1-2. docker run - コンテナを動かして体感する

所要時間: 40-50分(がっつりなら2セッション分) ゴール: docker run の主要オプションを使いこなし、コンテナがプロセスであることを体感する コミット内容: 操作ログを ~/log/docker_day02.log に保存。お試しで作ったコンテナは全部消す


このレッスンのゴール

  • docker run -d -p ... --name ... <イメージ> を指が覚えて打てる
  • -it --rm -v -e の意味と使いどころが言える
  • コンテナの起動・停止・削除・ログ確認・exec の流れを止まらず一巡できる
  • 「コンテナ=プロセス」の感覚で docker psps の延長として読める

なぜ学ぶか(実務悩みベース)

  • 「DB だけサッと立ち上げて検証したい」が docker run で30秒でできる
  • 同僚に「postgres 立てたいんだけど」と聞かれたら即答できる
  • CI/CD やローカル開発で docker run を呼ぶ箇所が頻出
  • 「コンテナが起動しない」「ログが見えない」初動デバッグの土台になる

前章とのつながり

1-1 では「コンテナ=namespaces+cgroups で隔離された Linux プロセス」と理屈で押さえた。今回はその理屈を 手で打って確かめる 回。docker exec が「動いているプロセスの namespace に入る」操作だと体で分かれば、もう抽象論は不要になる。


大前提: 手を動かしてコンテナの感覚を掴む

前回はコンテナ=隔離プロセスという話をした。今日はその「プロセス」を実際に起動・停止・覗き込む。

このレッスンの裏テーマは:

「コンテナはプロセスである」を体で覚える

具体的には:

  • docker run でコンテナが起動 = Linux プロセスがフォークされる、と同じ感覚
  • docker ps = ps コマンドのDocker版
  • docker exec = 動いているプロセスに「入る」(実態は新しいプロセスを namespace 内に作る)
  • docker stop = プロセスに SIGTERM を送る

ここを押さえれば、後の Dockerfile やボリュームも「プロセスを扱う延長」として理解できる。


セッション①: docker run の基本(25-30分)

0. 録画スタート

mkdir -p ~/log
script ~/log/docker_day02.log

1. 一番シンプルな docker run

# Hello World 的なコンテナ
docker run hello-world

docker run の本質

「イメージを元にコンテナを作って起動する」 コマンド。1コマンドで実は4つの操作をしている:

  1. ローカルにイメージがあるか確認(無ければ Docker Hub から pull)
  2. イメージを元にコンテナを作成docker create と同等)
  3. コンテナのプロセスを起動docker start と同等)
  4. 出力をターミナルに繋ぐ-it がある場合)

使い所: 「ちょっと動かしたい」「お試し」に最適。本番運用では Compose や K8s 経由で起動するが、開発・検証では docker run を毎日叩く。

# 起動したコンテナ一覧(停止中も含めて)
docker ps -a

docker ps の本質

ps(Linuxのプロセス一覧コマンド)の Docker 版。起動中のコンテナ一覧 を表示する。

オプション意味
(無し)実行中のコンテナだけ
-a停止中も含めて全部
-qコンテナIDだけ(スクリプトで使う)
--formatカスタム表示(JSON出力など)

実務ユースケース

  • 「あれ、さっき起動したコンテナどこ?」→ docker ps
  • 「停止したコンテナの残骸を整理したい」→ docker ps -a で確認 → docker rm で削除
  • スクリプトで「動いてるかチェック」→ docker ps -q --filter name=myapp

2. 終了後に自動削除(—rm)

# --rm を付けると終了したコンテナが自動で消える
docker run --rm hello-world
docker ps -a   # さっきと違って残骸が増えない

--rm の意味と使い所

コンテナが終了した瞬間に docker rm を自動実行する。

使い所

  • お試し・ワンショット実行: 「alpine イメージで ls を実行してすぐ捨てたい」など
  • CI のテスト用コンテナ: テスト終了したら残骸を残さない
  • 対話セッション: docker run --rm -it ubuntu bash でシェルに入って、抜けたら自動削除

落とし穴

  • 本番では使わない。停止しただけで消えてしまうので、コンテナのログを後から見られない
  • データが消える: コンテナ内のファイルは全部消える。永続化は必ずボリュームで(後の章で扱う)

3. nginx を立ち上げてみる

# nginx を「バックグラウンド」で起動、ポート8080をコンテナの80に繋ぐ
docker run -d -p 8080:80 --name web1 nginx

ブラウザで http://localhost:8080 を開く。nginx のデフォルトページが見えれば成功。

-d (detach) の本質

バックグラウンド起動。コンテナをフォアグラウンドで動かす(ターミナル占有)のではなく、デーモンとして動かす。

-d 無しで docker run nginx を実行すると、nginx のログがターミナルに流れ続けて、Ctrl+C で止めるとコンテナも止まる。

使い所: サーバー系のコンテナ(nginx, postgres, redis)はほぼ常に -d を付ける。逆に bash を起動する時は -d 付けない(対話したいので)。

-p (publish) の本質

ホストのポートをコンテナのポートに転送する。書式は -p ホスト側:コンテナ側

-p 8080:80
    │    │
    │    └─ コンテナ内のプロセスが LISTEN しているポート(nginx は 80)
    └─ ホスト(あなたのMac)からアクセスする時のポート

これが無いとコンテナにアクセスできない。コンテナは隔離されたネットワーク空間にいるため、外(ホスト)から見えない。-p でホストの穴を開けて転送する。

複数ポートも書ける: -p 8080:80 -p 8443:443

ユースケース

  • 開発中: ホストの 3000 番をコンテナの 80 番に向ける → localhost:3000 で確認
  • 本番(Linuxの場合): 80 と 443 をホストに公開してリバースプロキシなしで運用も可

-p の落とし穴

  • ホスト側ポートが既に使われていると起動失敗: 別のプロセスが 8080 を使っているとエラー。lsof -i :8080 で誰が使ってるか調査
  • ホスト側を省略すると全インターフェイスで公開: -p 80 ではなく -p 8080:80 のように明示する。本番でうっかり全公開すると事故
  • docker port web1 で確認できる: 何のポートが何にマップされているかを後から確認可能

--name の意味

コンテナに名前を付ける。指定しないと Docker が自動生成(affectionate_einstein のような可愛い名前)。

名前を付けておくと:

  • docker stop web1 のようにIDではなく名前で操作できる
  • スクリプトでハードコードしやすい

落とし穴: 同名のコンテナは1つしか作れない。docker rm web1 で消してから再作成 or --rm で自動削除。

4. コンテナの中身を見る(logs / exec)

# nginx のアクセスログを見る
docker logs web1
 
# リアルタイムで追従
docker logs -f web1
 
# コンテナの中に入って bash を起動
docker exec -it web1 bash
 
# 中で色々試す
ls /
ps aux           # nginx だけ動いていることを確認
cat /etc/os-release  # コンテナ内は Debian だったりする
exit             # コンテナから出る

docker logs の本質

コンテナの標準出力・標準エラーを表示する

Docker は「コンテナ内の PID 1 プロセスの stdout/stderr」を自動収集している。これを docker logs で取り出す。

オプション意味
-f (follow)リアルタイム追従(tail -f と同じ感覚)
--tail 100末尾100行だけ
--since 1h1時間前から
-t (timestamps)タイムスタンプ付き

実務ユースケース: 本番でアプリのエラー調査の入口。「あれ、エラー出てる?」→ docker logs -f --tail 100 myapp

落とし穴: アプリがログをファイルに書いている場合は docker logs には出ない。stdout/stderr に出すのが原則。これは Dockerfile のベストプラクティスでも触れる。

docker exec の本質

動いているコンテナの中で、追加でコマンドを実行する

重要な勘違い: 「コンテナに入る」というより「コンテナの namespace の中で新しいプロセスを起動する」が正しい。だから docker exec で起動した bash を抜けても、コンテナ本体は動き続ける。

オプション意味
-i (interactive)標準入力を繋ぐ
-t (tty)疑似端末を割り当てる(プロンプトが表示される)
-it上記2つの組み合わせ。シェル起動の鉄板セット

実務ユースケース

  • デバッグ: 動いているコンテナの中で lscat /etc/config.yml を確認
  • DB のシェルに入る: docker exec -it db psql -U postgres
  • ワンショットコマンド: docker exec web1 nginx -t(設定ファイルの構文チェック)

docker exec の落とし穴

  • コンテナが停止していると使えない: 停止中のコンテナには exec できない。docker start してから
  • 新規プロセスなので、シェルで設定した変数は残らない: 抜けたら消える。永続的な変更はイメージ側に入れる
  • 本番では極力避ける: 「コンテナを手で改造する」のは事故の元。設定変更はイメージ再ビルドが正道

5. コンテナを止める・消す

# 停止
docker stop web1
 
# 起動中のものを確認(web1 はもう出てこない)
docker ps
 
# 停止中も含める
docker ps -a   # web1 が "Exited" 状態で残ってる
 
# 削除
docker rm web1
 
# 一気に: 動いてるコンテナを止めずに削除(強制)
docker rm -f web1
 
# 停止中のコンテナを全部一掃
docker container prune

stop / kill / rm の違い

コマンド何をするプロセスへの影響
docker stopグレースフルに停止(SIGTERM → 10秒待ち → SIGKILL)アプリが終了処理を実行できる
docker kill即座に強制終了(SIGKILL)終了処理は走らない
docker rmコンテナを削除(停止中のみ。-f で起動中も)プロセス無し、ファイルシステムも削除

stop と kill の違いは本番で重要:

  • DB 系コンテナを kill するとデータ破損のリスク
  • 通常は stop(10秒で終了するなら綺麗に)、緊急時のみ kill

docker container prune でお掃除

停止中のコンテナをすべて削除する。本番では使うべきではないが、開発機ではしょっちゅう叩く。

関連:

  • docker image prune: 未使用イメージ削除
  • docker volume prune: 未使用ボリューム削除
  • docker system prune -a: 全部一括(コンテナ・イメージ・ネットワーク・ビルドキャッシュ)。Mac のディスクを開放する時の鉄板

セッション②: 環境変数とインタラクティブ実行(20分)

6. 環境変数を渡す(-e)

# postgres を起動。パスワードを環境変数で渡すのが定番
docker run -d \
  --name db1 \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  postgres:16
 
# 動いているか確認
docker logs db1
 
# DB に入ってみる
docker exec -it db1 psql -U myuser -d mydb
# 抜ける: \q

-e (env) の本質と「12factor」

コンテナに環境変数を渡すオプション。

なぜ環境変数で渡すか:

  • 設定をコードから分離する という設計思想(The Twelve-Factor App の “Config” 原則)
  • イメージは1個、環境ごとに変数だけ変えれば動く(開発はパスワード secret、本番は強い値、など)
  • パスワード等を Dockerfile に書かない(イメージに焼き付くと漏洩リスク)

書き方バリエーション

  • -e KEY=VALUE: 直接指定
  • -e KEY: ホストの環境変数をそのまま転送(同名で)
  • --env-file .env: ファイルから一括読み込み

落とし穴: 環境変数はコンテナ起動時に固定される。後から変えるには再起動が必要。

7. インタラクティブにシェルを使う

# ubuntu イメージで bash を起動、終了したら自動削除
docker run --rm -it ubuntu bash
 
# 中で色々試す
apt list --installed 2>/dev/null | head
echo "ここはコンテナの中"
exit

-it の正体

-i (interactive) + -t (tty) の組み合わせ。

  • -i: 標準入力を繋ぐ(あなたのキー入力をコンテナに渡す)
  • -t: 疑似端末を割り当てる(プロンプトが表示される、補完が効く)

両方無いと: コマンドを打っても反応しない、Ctrl+C も効かない、というハマりが起きる シェル起動時は必ず -it: bash, sh, psql, mysql などインタラクティブなコマンドはこのセット

逆に -d-it は同時にあまり使わない(バックグラウンド × 対話は矛盾)

8. 「コンテナはプロセスである」を体感する

# ターミナル1: nginx をフォアグラウンドで起動
docker run --rm --name fg_nginx nginx
# (ログがターミナルに流れる)
 
# ターミナル2(別タブで開く):
docker ps               # fg_nginx が動いてる
ps aux | grep nginx     # ホスト側からも見える!
docker exec -it fg_nginx ps aux  # コンテナ内のプロセス一覧

観察ポイント

  • ホストの ps aux | grep nginx で、nginx プロセスがホスト側にも見えている → カーネル共有の証拠
  • コンテナ内の ps aux では nginx だけ(PID 1)。ホストの他プロセスは見えない → namespace 隔離の証拠
  • ホスト側でも見える、しかし内側からはホストが見えない → これがコンテナの隔離

ターミナル1で Ctrl+C → コンテナ停止 → --rm で自動削除。

9. お試し用の便利コマンド集

# alpine(5MB の極小Linux)で ls だけして終了
docker run --rm alpine ls /
 
# alpine の sh に入る
docker run --rm -it alpine sh
 
# Python のワンライナー
docker run --rm python:3.12-alpine python -c "print('Hello from container')"
 
# Node の REPL
docker run --rm -it node:20-alpine node

イメージタグについて

nginx だけだと nginx:latest と同じ意味。postgres:16node:20-alpine のように バージョンとベースOSを明示する のが本番では鉄則。

  • latest は時間とともに中身が変わる → 「数ヶ月後にビルドしたら壊れた」事故
  • -alpine 付きは Alpine Linux ベースで超軽量(10-50MB)
  • 詳細は 次回

練習課題

# 1. nginx を「web2」という名前で 8081 にマップして起動
docker run -d --name web2 -p 8081:80 nginx
 
# 2. ブラウザで http://localhost:8081 を確認
 
# 3. コンテナの中に入って、index.html がどこにあるか調べる
docker exec -it web2 sh
# 中で: find / -name index.html 2>/dev/null
# 中で: cat /usr/share/nginx/html/index.html | head
# 抜ける: exit
 
# 4. コンテナのログを表示
docker logs web2
 
# 5. ホスト側からアクセスして、ログがリアルタイムで増えるか確認
docker logs -f web2 &
curl http://localhost:8081/
curl http://localhost:8081/no-such-page  # 404 が出るはず
 
# 6. お片付け
docker stop web2
docker rm web2
 
# 7. 念のため、起動中・停止中とも何も残っていないか
docker ps -a

締め: 振り返り(10分)

1. セッション録画を終了

exit   # script を抜ける

2. 今日の発見

- 「コンテナはプロセス」を体感できた瞬間:
- -p / -d / -it / --rm でどれが一番直感的じゃなかった?:
- docker exec で何ができることが分かった?:
- 詰まったところ:
- 次回やりたいこと:

やらかし事例: docker run でハマる定番

事例1: -p を忘れてブラウザから見えない

docker run -d nginx で起動したのに localhost:80 にアクセスできない。コンテナのポートはホストに自動で開かない-p 8080:80 が必須。

事例2: docker execbash がない

docker exec -it alpine-container bashexecutable file not found。Alpine には bash が入ってない(sh のみ)。ベースイメージのシェルは何かを確認する習慣 を持つ。

事例3: 残骸が積もって disk full

--rm を付けずに毎回 docker run していると、停止コンテナが溜まり続けて数GB食う。docker ps -a で確認し、docker container prune で定期掃除。

事例4: 名前衝突で起動失敗

--name web で起動したコンテナを docker stop だけして docker rm し忘れ、再度 --name web で起動 → Conflict. The container name "/web" is already in use-f 付き削除か、--rm で自動掃除。

事例5: -it を忘れて Ctrl+C が効かない

docker run nginx をフォアグラウンドで起動 → Ctrl+C で抜けようとしても効かない。-it で TTY を割り当てるか、-d でデタッチが正解。

対比表: docker run のオプション組み合わせパターン

用途典型コマンド解説
ちょい試し(1回限り)docker run --rm -it alpine sh終了で自動削除、対話シェル
常駐サービスdocker run -d -p 8080:80 --name web nginxバックグラウンド、ポート公開、名前付き
ファイル共有開発docker run -it -v $(pwd):/app node shカレントディレクトリをマウント
環境変数で設定注入docker run -d -e DB_URL=... myapp12 Factor App パターン
ワンライナー実行docker run --rm python:3.12 python -c "print(1+1)"言語処理系を1コマンド呼出

自己評価チェックリスト

知識レベル

  • -d / -it / --rm の使い分けが言える
  • -p HOST:CONTAINER の左右どちらがホスト側か即答できる
  • docker execdocker run の違いを説明できる

実行レベル

  • docker run -d -p 8080:80 nginx でブラウザから見えた
  • docker psdocker ps -a の違いが分かった
  • docker exec -it <名前> bash でコンテナの中に入れた
  • docker logs -f でログをリアルタイム追従した
  • docker stopdocker rm を順番に実行できた
  • --rm の挙動を理解した
  • お試しコンテナを全部消した(docker ps -a が空)

メタ認知

  • 「打ったコマンド」と「裏で起きた namespace 操作」を結び付けて考えられた
  • 詰まった時に「どのオプションが原因か」を切り分ける癖がついた

詰まった時のチートシート

やりたいことコマンド
イメージから起動docker run <イメージ>
バックグラウンド起動docker run -d <イメージ>
ポート公開docker run -p 8080:80 <イメージ>
名前を付けるdocker run --name web1 <イメージ>
環境変数docker run -e KEY=VALUE <イメージ>
終了後自動削除docker run --rm <イメージ>
対話シェルdocker run -it <イメージ> bash
一覧(実行中)docker ps
一覧(全部)docker ps -a
中に入るdocker exec -it <名前> bash
ログdocker logs <名前>
ログ追従docker logs -f <名前>
停止docker stop <名前>
削除docker rm <名前>
強制削除docker rm -f <名前>
残骸一掃docker container prune

「実務OK」基準

  • docker run -d -p ... --name ... <イメージ> が指が覚えてる
  • docker logs / exec / stop / rm が呼吸のように出る
  • コンテナがプロセスである感覚を持っている
  • -it を bash 起動時につけ忘れない

ここを越えれば、開発中の「ちょっと postgres 立ち上げて検証」が30秒でできるようになる。次は イメージ の構造を覗く回。


次のレッスン

1-3. イメージとレイヤー - Copy-on-Write の仕組み へ。

コンテナの元になる「イメージ」が何なのか、なぜキャッシュが効くのか、なぜ alpine が軽いのか、を解き明かす。