EC2 を1台立てて、その上に Docker を入れ、Redis のコンテナを動かしました。
やってみて一番意外だったのは、EC2 側で ps aux を打つと、コンテナの中で動いているプロセスがそのまま出てくることでした。
$ ps aux | grep redis-server
systemd+ 29928 0.1 0.3 176992 24924 ? Ssl 05:32 0:00 redis-server *:6379
29928 は EC2 側の番号です。コンテナの中で同じものを見ると、番号は 1 でした。同じ1つのプロセスを、外と中から別の番号で見ているということになります。
「コンテナは軽い仮想マシン」という説明を先に読んでいたので、外から中が見えるとは思っていませんでした。仮想マシンなら、外から ps aux を打っても中のプロセスは1つも出ません。
この記事では、EC2 を立てるところから、Docker を入れ、Redis を動かし、Python からつなぎ、最後に全部消すまでを 10章に分けて書きます。「仮想マシンではない」と分かる場面が3回あるので、そこは数字を残しました。
この記事でできること
EC2 が1台ある状態から、Docker で Redis のコンテナを立て、EC2 の側で動く Python からその Redis を読み書きできるようにします。
- 対象読者:コードは書けるけれど Docker はこれからの方
- かかるお金:約20円(EC2 が動いていたのは32分でした)
- 試した日:2026年9月17日
- 試した環境:EC2(t3.large・Amazon Linux 2023)/Docker 25.0.14/Redis 8.10.1/Python 3.9.25/東京リージョン(ap-northeast-1)
この記事は前の4本の続きです。EC2 を立てる部分は Terraform で書いた4本目をそのまま使いました。
言葉の整理(ここだけ言い換えます)
Docker の説明で最初につまずいたのが、イメージとコンテナの区別でした。自分の中で腹に落ちた言い方を1枚だけ置いておきます。以降の章では実物の名前で書きます。
| 言い換え | 実物 | 確かめるコマンド |
|---|---|---|
| 空き地 | EC2 |
ssh で入る |
| 地面 | Linux カーネル | uname -r |
| クリップボード | イメージの置き場 | docker images |
| コピーされた家 | イメージ(redis・146MB) |
docker pull redis |
| 貼って建った家 | コンテナ | docker run |
| 玄関の番地 | ポート番号(6379) | -p 6379:6379 |
| 家財道具 | Redis に入れた値 | KEYS * |
大事なのは、この3つが別の操作だということです。
-
コピーする(
docker pull)=クリップボードに入るだけ。まだ何も建っていない -
貼る(
docker run)=コンテナが起動する -
撤収する(
docker rm -f)=コンテナは消える。クリップボードは残る
コピー元は減りません
クリップボードのコピー&ペーストと同じで、貼っても元は残ります。あとの8章で、2軒目を建ててもイメージが1行・146MB のままであることを実際に確かめます。
1章 EC2 を立てる
4本目で書いた main.tf をそのまま使いました。
terraform apply -auto-approve
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
33秒でした。前回は31秒だったので、ほぼ同じです。
outputs.tf が ssh のコマンドを出してくるので、それを打って入ります。
ssh -i ~/.ssh/aws-study-key.pem ec2-user@203.0.113.45
入ったところで、Docker がまだ無いことを確かめておきます。
$ which docker
/usr/bin/which: no docker in (/home/ec2-user/.local/bin:...)
何も入っていません。ここから始めます。
ここから課金が始まります
EC2 が1時間あたり約19円、ディスク30GB が1日あたり約14円です。この記事の作業は32分で終わったので、合わせて約20円でした。最後の10章で全部消します。
2章 Docker を入れる
sudo dnf install -y docker
Installed:
container-selinux-4:2.245.0-1.amzn2023.noarch
containerd-2.2.7-1.amzn2023.0.1.x86_64
docker-25.0.16-1.amzn2023.0.4.x86_64
iptables-libs-1.8.8-3.amzn2023.0.2.x86_64
...(全部で11個)
docker だけでなく11個入りました。中で効いているのは2つです。
| 入ったもの | 役目 |
|---|---|
containerd |
実際にコンテナを作って動かす部分 |
runc |
カーネルの機能を呼んでコンテナを起動する部分 |
docker |
打つコマンドそのもの |
打つのは docker だけで、下の2つは意識しなくて済みます。
入れただけでは動きません。確かめるとこうなります。
$ systemctl is-active docker
inactive
起動させて、次回の再起動でも自動で立ち上がるようにします。
sudo systemctl start docker
sudo systemctl enable docker
$ systemctl is-active docker
active
$ systemctl is-enabled docker
enabled
3章 sudo なしで打てるようにする
ここで一度つまずきます。docker は入って動いているのに、コマンドが通りません。
$ docker ps
permission denied while trying to connect to the Docker daemon socket
at unix:///var/run/docker.sock: ... connect: permission denied
/var/run/docker.sock が、コマンドと containerd をつなぐ窓口です。この窓口を触れるのは root と docker グループだけで、ec2-user はどちらでもありません。
グループに入れます。
sudo usermod -aG docker ec2-user
ところが、これだけでは通りません。同じ ssh のまま打つと、まだ permission denied のままです。
面白いのは、設定ファイル側は先に書き換わっていることです。
$ id ec2-user
uid=1000(ec2-user) gid=1000(ec2-user) groups=1000(ec2-user),4(adm),10(wheel),190(systemd-journal),993(docker)
993(docker) が入っています。ところが、いま自分が持っているグループを見るとこうです。
$ groups
ec2-user adm wheel systemd-journal
docker がありません。ログイン中のセッションは、自分の所属グループの一覧をログインした時点でコピーして握ったままで、あとから書き換わらないためです。
ssh を一度切って、繋ぎ直します。
$ groups
ec2-user adm wheel systemd-journal docker
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
通りました。これは Docker の都合ではなく Linux のログインの仕組みなので、他のグループを足したときも同じことが起きます。
ここで、始まりの状態を見ておきます。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
両方0件です。ここが基準線になります。
4章 コピーする(pull)と、貼る(run)は別
Redis のイメージを取ってきます。
$ docker pull redis
6310eb16bf42: Pull complete
9b30195f536d: Pull complete
db0606565b61: Pull complete
87650244ec3e: Pull complete
5b7942a2dab1: Pull complete
4f4fb700ef54: Pull complete
b1de9b9d5a81: Pull complete
Status: Downloaded newer image for redis:latest
real 0m4.084s
4.0秒でした。7つに分かれて落ちてきているのは、イメージが7枚の層でできているからです。
docker history で中身が見えます。
$ docker history redis --format "table {{.Size}}\t{{.CreatedBy}}"
SIZE CREATED BY
0B CMD ["redis-server"]
0B EXPOSE map[6379/tcp:{}]
0B ENTRYPOINT ["docker-entrypoint.sh"]
4.86kB COPY docker-entrypoint.sh /usr/local/bin/
0B WORKDIR /data
66.9MB RUN |2 REDIS_DOWNLOAD_URL=https://github.com…
0B ENV REDIS_VERSION=8.10.1
4.51kB RUN /bin/sh -c set -eux; apt-get update; ...
下の2行が、このあと効いてきます。
-
EXPOSE 6379:コンテナ側で 6379 を使うという申告だけ。これだけでは外から入れません -
CMD ["redis-server"]:コンテナが起動したときに走るコマンド。5章で何も指定しないのに Redis が動き出す理由です
ここで一度、docker ps を打っておきます。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0件です。pull した時点ではクリップボードに入っただけで、まだ何も起動していません。docker images のほうだけ1行になります。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 923b31bc216c 3 weeks ago 146MB
146MB の中身
上の docker history を足すと、Debian の土台と共通ライブラリで約76MB、Redis 本体で66.9MB です。これで146MB になります。ps コマンドすら入っていない(6章で確かめます)ので、この大きさで収まっています。
5章 コンテナを起動する
$ time docker run -d -p 6379:6379 --name redis1 redis
62ec8235dff02d9c06351a72a22555a33694ec78eb424dfd1a0dab84a69c31fc
real 0m0.277s
0.277秒です。ここが仮想マシンとの一番わかりやすい違いで、OS の起動から始まらないぶん、この速さになります。
付けたオプションは3つです。
| オプション | 意味 |
|---|---|
-d |
バックグラウンドで動かす(付けないと画面がログで埋まります) |
-p 6379:6379 |
左が EC2 側のポート、右がコンテナ側のポート |
--name redis1 |
名前を付ける。付けないと 64文字の ID か、Docker が決めた名前で呼ぶことになります |
ログを見ると、Redis が起動しています。
$ docker logs redis1
1:M 17 Sep 2026 05:32:14.151 * Server initialized
1:M 17 Sep 2026 05:32:14.151 * Ready to accept connections tcp
1:M 17 Sep 2026 05:32:14.151 # WARNING: Redis does not require authentication and is not
protected by network restrictions. Redis will accept connections from any IP address on
any network interface.
最後の1行が重要です。パスワードが無く、届く相手なら誰でも読み書きできる状態です。この記事で外から触れないのは、4本目で作ったセキュリティグループが 6379 を閉じているからです。
6379 をインターネットに開けないでください
既定でパスワードがありません。セキュリティグループで 6379 を 0.0.0.0/0 に開けると、そのまま読み書きされます。この記事では EC2 の中からだけ触ります。
docker ps が1行になりました。
$ docker ps
NAMES IMAGE STATUS PORTS
redis1 redis Up 1 minute 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp
PORTS の 0.0.0.0:6379->6379/tcp が、4章で見た EXPOSE 6379 に -p を足した結果です。EC2 側でも実際にポートが開いています。
$ ss -tln | grep 6379
LISTEN 0 4096 0.0.0.0:6379 0.0.0.0:*
この行が出るのは -p を付けたからです。-p を外すと、コンテナの中では 6379 が開いているのに、この行は出ません。
ディスクの使われ方も変わりました。
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 145.6MB 0B (0%)
Containers 1 1 0B 0B
RECLAIMABLE が 100% から 0% になり、Containers のサイズは 0B です。コンテナ自体は容量を使っておらず、イメージの146MB を読んでいるだけです。
6章 コンテナは仮想マシンではない
ここがこの記事の山です。3つ確かめます。
① EC2 側からコンテナの中のプロセスが見える
$ ps aux | grep [r]edis-server
systemd+ 29928 0.1 0.3 176992 24924 ? Ssl 05:32 0:00 redis-server *:6379
EC2 側で打っているのに、コンテナの中の redis-server が出てきます。docker top でコンテナ側から見ても同じ番号です。
$ docker top redis1
UID PID PPID C STIME TTY TIME CMD
systemd+ 29928 29903 0 05:32 ? 00:00:00 redis-server *:6379
同じ 29928 です。別々に2つ動いているのではなく、1つを2方向から見ています。
② カーネルは1つしかない
$ uname -r
6.18.48-107.148.amzn2023.x86_64
$ docker exec redis1 uname -r
6.18.48-107.148.amzn2023.x86_64
1文字も違いません。コンテナは自分のカーネルを持たず、EC2 のカーネルをそのまま使っています。
ところが OS の名前は違います。
$ cat /etc/os-release | head -1
NAME="Amazon Linux"
$ docker exec redis1 cat /etc/os-release | head -1
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
カーネルは1つ、その上の作りは別。これがコンテナの正体でした。だからコンテナの中で ps を打つと、こうなります。
$ docker exec redis1 ps aux
OCI runtime exec failed: ... exec: "ps": executable file not found in $PATH: unknown
Debian 側に ps が入っていません。必要なものだけ載せてあるので、146MB で収まっています。
③ 見えるのは片側だけ
外から中は見えますが、中から外は見えません。プロセスの数を数えました。
# EC2 側
$ ls /proc | grep -E "^[0-9]+$" | wc -l
152
# コンテナの中
$ docker exec redis1 sh -c 'ls /proc | grep -E "^[0-9]+$" | tr "\n" " "'
1 50 59 60 61 62
外から152個、中から6個です。しかも中から見た redis-server の番号は 1番。コンテナの中からは、自分が最初のプロセスに見えています。
まとめると、透明なのは片側だけということになります。
| 見えるか | |
|---|---|
| EC2 側 → コンテナの中 | 見える(152個の中に含まれる) |
| コンテナの中 → EC2 側 | 見えない(6個だけ) |
| カーネル | 共有(同じ 6.18.48) |
| OS の作り | 別(Amazon Linux と Debian) |
仮想マシンなら、外から ps aux を打っても出るのは仮想マシン1個ぶんのプロセスだけです。ここが違いました。
7章 Redis を触る(SQL が無い)
コンテナの中に入って、Redis のコマンドを打ちます。
docker exec -it redis1 redis-cli
docker exec は、動いているコンテナの中でコマンドを1つ動かすものです。redis-cli は Redis に付いてくる道具で、146MB のイメージの中に一緒に入っています。EC2 側にはありません。
打ったものと返ってきたものです。
127.0.0.1:6379> SET user:1 mitsuhashi
OK
127.0.0.1:6379> GET user:1
"mitsuhashi"
127.0.0.1:6379> INCR count
(integer) 1
127.0.0.1:6379> INCR count
(integer) 2
127.0.0.1:6379> INCR count
(integer) 3
127.0.0.1:6379> SET tmp hello EX 10
OK
127.0.0.1:6379> TTL tmp
(integer) 10
127.0.0.1:6379> KEYS *
1) "tmp"
2) "count"
3) "user:1"
127.0.0.1:6379> DBSIZE
(integer) 3
SQL が1文字も出てきません。SELECT も WHERE も JOIN もなく、キーを指定して値を1つ取るだけです。
Oracle を長く見てきた立場で並べると、こうなります。
| Oracle | Redis | |
|---|---|---|
| 探し方 |
WHERE で条件を書く |
キーを知らないと取れない |
| 結合 |
JOIN できる |
できない |
| 置き場所 | ディスク | メモリ |
| 応答 | ミリ秒 | マイクロ秒 |
EX 10 が Redis らしいところでした。10秒経つと値が自動的に消えます。12分後に同じキーを見るとこうなります。
127.0.0.1:6379> TTL tmp
(integer) -2
-2 は「そのキーは無い」という意味です。ログイン中の利用者の情報を「30分で消える」形で置くのがこの機能で、Oracle なら消す処理を自分で書くところです。
INCR も自作すると面倒なものです。読んで+1して書き戻す間に別の処理が同じことをすると数が狂いますが、Redis は1つずつ処理するので狂いません。
exit で redis-cli を抜けます。抜けてもコンテナは動いたままです。
$ docker ps --format "table {{.Names}}\t{{.Status}}"
NAMES STATUS
redis1 Up 12 minutes
exit で終わったのは redis-cli だけで、redis-server(29928)はそのまま動いています。
8章 コンテナの外から 6379 を叩く
実際にアプリから使うときは、人が redis-cli を打つのではなく、プログラムが Redis につなぎます。ここでは EC2 側に Python を用意して、コンテナの外からつなぎました。
sudo dnf install -y python3-pip
pip3 install redis
Successfully installed async-timeout-5.0.1 redis-7.0.1
入った場所を見ておきます。
$ python3 -c "import redis; print(redis.__file__)"
/home/ec2-user/.local/lib/python3.9/site-packages/redis/__init__.py
sudo を付けずに pip3 install したので、/home/ec2-user/.local/ の下に入りました。OS 全体の Python には手を付けていません。
書いたコードです。
import redis
# EC2 側から、コンテナのポート 6379 につなぐ
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
print("user:1 =", r.get("user:1"))
print("count =", r.get("count"))
r.set("from_python", "こんにちは")
print("from_python =", r.get("from_python"))
print("count を +1 =", r.incr("count"))
user:1 = mitsuhashi
count = 3
from_python = こんにちは
count を +1 = 4
7章で redis-cli からコンテナの中で入れた値が、コンテナの外の Python から読めています。count も3から4へ、続きの番号になりました。
つなぐのに書いたのは1行だけです。
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
| 部分 | 意味 |
|---|---|
host="localhost" |
EC2 の中なので localhost。コンテナ名ではありません |
port=6379 |
5章の -p 6379:6379 の左側。これが無いとつながりません |
decode_responses=True |
返り値を文字列として扱う(付けないとバイト列になります) |
接続文字列もユーザー名もパスワードも要りませんでした。5章の警告のとおり、誰でもつなげるということでもあります。
値の置き場所を変えると何が変わるか
Python を2プロセス同時に動かして、2種類の置き方を比べました。
proc_a.py:
import redis, os, time
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
print(f"[A] 私のプロセス番号 = {os.getpid()}")
# ① Python の変数に持つ
my_own = "Aだけが持っている値"
print(f"[A] 自分の変数に持った: {my_own}")
# ② Redis に持つ
r.set("shared", "Aが入れた値")
print("[A] Redis に入れた: Aが入れた値")
time.sleep(3)
print("[A] 3秒後に Redis を読む:", r.get("shared"))
print("[A] 3秒後に自分の変数を読む:", my_own)
proc_b.py:
import redis, os
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
print(f"[B] 私のプロセス番号 = {os.getpid()}")
try:
print("[B] A の my_own を読む:", my_own)
except NameError:
print("[B] A の my_own を読む: 見えない(NameError)")
print("[B] Redis を読む:", r.get("shared"))
r.set("shared", "Bが書き換えた値")
print("[B] Redis を書き換えた: Bが書き換えた値")
A を先に走らせ、1秒後に B を走らせます。
python3 proc_a.py & sleep 1; python3 proc_b.py; wait
[B] 私のプロセス番号 = 31434
[B] A の my_own を読む: 見えない(NameError)
[B] Redis を読む: Aが入れた値
[B] Redis を書き換えた: Bが書き換えた値
[A] 私のプロセス番号 = 31432
[A] 自分の変数に持った: Aだけが持っている値
[A] Redis に入れた: Aが入れた値
[A] --- B を待つ(3秒) ---
[A] 3秒後に Redis を読む: Bが書き換えた値
[A] 3秒後に自分の変数を読む: Aだけが持っている値
プロセスは別(31432 と 31434)です。
-
Python の変数:B からは
NameError。A は最後まで自分の値を持ったまま - Redis に置いた値:A が入れた値を B が読み、B が書き換えた値を A が読んだ
同じ1本のプログラムの中に両方を並べて書いたので、置き場所を変えただけで見え方がこれだけ変わることが1回の実行で出ました。
業務アプリを書いてきた人向けの補足
クライアント/サーバー型のアプリなら、値をクライアント側(各PCのメモリ)に持てます。Web になるとそれができないので、置き場所がサーバー側の1か所に移ります。その置き場所として使われるのが Redis です。役目は同じで、置き場所が逆になります。
9章 2軒目を建てる、そして壊す
同じイメージからもう1つ起動する
$ time docker run -d -p 6380:6379 --name redis2 redis
faa8be45f5bb9b2eacd6b7ce293297181c79b37c27d6ad3adb90331d7812b596
real 0m0.326s
0.326秒。ダウンロードの行が1つも出ていません。イメージは既にあるので、起動するだけで済みます。
ポートは2つ開きました。
$ ss -tln | grep -E "6379|6380"
LISTEN 0 4096 0.0.0.0:6380 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:6379 0.0.0.0:*
-p 6380:6379 の左右がここで効きます。
| EC2 側のポート | コンテナ側のポート | |
|---|---|---|
| 1つ目 | 6379 | 6379 |
| 2つ目 | 6380 | 6379 |
コンテナ側はどちらも 6379 です。イメージが同じなので変えられません。変えたのは EC2 側だけで、1台のマシンに同じポートは2つ作れないためです。
一覧はこうなりました。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 923b31bc216c 3 weeks ago 146MB
$ docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES STATUS PORTS
redis2 Up 2 minutes 0.0.0.0:6380->6379/tcp
redis1 Up 23 minutes 0.0.0.0:6379->6379/tcp
docker images は1行のままです。docker system df の Images も 145.6MB のまま増えていません。
2つがどのイメージから起動したか見ると、同じでした。
$ docker inspect redis1 redis2 --format "{{.Name}} → イメージ {{.Image}}"
/redis1 → イメージ sha256:923b31bc216c39d25b1c984d86704f49dbd518...
/redis2 → イメージ sha256:923b31bc216c39d25b1c984d86704f49dbd518...
1文字も違いません。イメージが2つに増えたのではなく、1つを2つのコンテナが使っています。
中身は別
Python から両方につないで確かめました。
r1 = redis.Redis(host="localhost", port=6379, decode_responses=True)
r2 = redis.Redis(host="localhost", port=6380, decode_responses=True)
for key in ["user:1", "count", "from_python", "shared"]:
print(f"{key:12} 6379={r1.get(key)!r:25} 6380={r2.get(key)!r}")
user:1 6379='mitsuhashi' 6380=None
count 6379='4' 6380=None
from_python 6379='こんにちは' 6380=None
shared 6379='Bが書き換えた値' 6380=None
2つ目は全部 None です。キーの数も 4個と0個でした。同じイメージから起動しても、中のデータは別々です。
INFO server で身元を比べると、こうなります。
6379 の run_id = a3ad85169f6b1529
6380 の run_id = a3083c5483404d58
6379 の process_id = 1
6380 の process_id = 1
run_id は別、process_id はどちらも 1。6章で見たとおり、それぞれのコンテナの中では自分が1番です。
壊して起動し直すと値が消える
この記事で一番押さえておきたいところです。
$ docker rm -f redis1
redis1
壊した直後、ポートも消えます。
$ ss -tln | grep 6379
(何も出ない)
5章とまったく同じコマンドで起動し直します。
$ docker run -d -p 6379:6379 --name redis1 redis
ca13e4585fa48431490445f1f128ca657e16d175317a62752d357f26a65cf190
中を見ます。
127.0.0.1:6379> KEYS *
(empty array)
127.0.0.1:6379> GET user:1
(nil)
127.0.0.1:6379> DBSIZE
(integer) 0
空です。7章で入れた mitsuhashi も、8章で4になった count も残っていません。
コンテナの ID も変わっています。
| ID | |
|---|---|
| 5章で起動したもの | 62ec8235dff0 |
| 起動し直したもの | ca13e4585fa4 |
名前は同じ redis1 でも、別のコンテナです。イメージのほうは146MB のまま無傷なので、すぐ起動し直せます。
ここから決まることが1つあります。
- コンテナの中に置いていいもの:消えても作り直せるもの(ログイン中の利用者の情報、計算結果の一時置き)
- 置いてはいけないもの:注文、顧客、売上
消えて困る値を Redis のコンテナに残したいときは docker run -v(ボリューム)でデータをコンテナの外に置きますが、この記事では扱いません。
10章 全部消す
コンテナを2つとも消します。
$ docker rm -f redis1 redis2
redis1
redis2
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker ps ではなく docker ps -a で見ているのは、止めただけのコンテナが残っていないか確かめるためです。docker stop だけだと docker ps は0件でも -a には残ります。rm -f は「止めて消す」を1回でやるので、残りません。
イメージは残っています。
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 0 145.6MB 145.6MB (100%)
Containers 0 0 0B 0B
ACTIVE が 0 に戻り、RECLAIMABLE が 100% になりました。3章で見た状態と同じです。
最後に EC2 ごと消します。
$ time terraform destroy -auto-approve
aws_instance.study: Destroying... [id=i-055679d2...]
aws_instance.study: Still destroying... [00m10s elapsed]
aws_instance.study: Still destroying... [00m20s elapsed]
aws_instance.study: Destruction complete after 30s
aws_security_group.study: Destroying...
aws_security_group.study: Destruction complete after 0s
aws_iam_role.study: Destruction complete after 1s
Destroy complete! Resources: 7 destroyed.
real 0m43.189s
消える順番に意味があります。EC2 が消えきるまでセキュリティグループは消せず、インスタンスプロファイルが消えるまで IAM ロールは消せません。使っているものがある間は消せない、という順番を Terraform 側が判断しています。
destroy の出力を信じずに、AWS 側にも直接聞きます。
$ aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running,stopped" \
--query 'Reservations[].Instances[].[InstanceId,State.Name]' --output text
(何も出ない)
$ aws ec2 describe-volumes --query 'Volumes[].[VolumeId,Size,State]' --output text
(何も出ない)
0台・0個です。terminated の状態で1台出てくることがありますが、これは消し終わった記録なので課金されません。stopped だった場合はディスク代だけ発生し続けるので、そこだけ見分けます。
この日の請求は 約20円でした。EC2 が動いていたのは32分です。
つまずいた3か所
① ヒアドキュメントの中の Python が SyntaxError
ssh 越しに Python のファイルを書こうとして、こうなりました。
File "/home/ec2-user/proc_a.py", line 14
print(f"[A] 3秒後に Redis を読む: {r.get(\"shared\")}")
SyntaxError: f-string expression part cannot include a backslash
ssh のコマンド全体をシングルクォートで囲んでいるところへ、f-string の中に \" を書いたためです。手元のマシンでファイルを作って scp で送る形に変えて通しました。
scp -i ~/.ssh/aws-study-key.pem proc_a.py proc_b.py ec2-user@203.0.113.45:~/
② EC2 の中で書いたファイルを destroy で失った
8章の最初に書いた test_redis.py は EC2 の中で直接書いたので、terraform destroy で EC2 ごと消えました。9章で学んだ「コンテナを消すと中の値が消える」と同じことが、一段大きいところでも起きます。以降のファイルは手元で書いて scp で送る形にしました。
③ ps aux | grep が自分自身を拾った
10章でコンテナを全部消したあと、残っていないか確かめようとして打った行です。
$ ps aux | grep redis-server
ec2-user 32474 0.0 0.0 223140 3548 ? Ss 06:02 0:00 bash -c ... grep redis-server ...
1行出たので焦りましたが、打ったコマンド自身でした。コマンド文字列の中に redis-server という文字が入っているためです。角括弧を1つ入れると自分は引っかかりません。
$ ps aux | grep [r]edis-server
(何も出ない)
まとめ
「コンテナは軽い仮想マシン」ではありませんでした。EC2 の上で確かめた数字を並べます。
| 確かめたこと | 結果 |
|---|---|
| EC2 側から中のプロセスが見えるか | 見える(PID 29928) |
| カーネルは別か | 同じ(6.18.48-107.148.amzn2023.x86_64) |
| OS の作りは同じか | 別(Amazon Linux 2023 と Debian 13) |
| 見えるプロセスの数 | 外から152個、中から6個 |
| 起動にかかる時間 | 0.277秒 |
| 2つ目を起動したときのイメージ | 1行・146MB のまま |
| コンテナを消して起動し直したとき |
中のデータは空(DBSIZE が 0) |
Redis のほうは、SET / GET / INCR / TTL の4つで今回やりたいことが全部できました。SQL は使いません。
かかったお金は 約20円、作業時間は EC2 が動いていたぶんで32分でした。
次は RDS で MySQL を立てて、同じ Python からつなぐところを書く予定です。