Docker 基礎 #3 イメージとコンテナ — build, run, ps, logs, exec

読了 8分

#2 Dockerfile を初めて書く でイメージを直接焼いて一度立ち上げてみました。この記事は Docker CLI コマンド群 を本格的に整理します。日常作業の 90% はこのコマンドで終わります。

Docker 基礎 シリーズでこの記事の位置:

  • #1 コンテナとは
  • #2 Dockerfile を初めて書く
  • #3 イメージとコンテナ — build, run, ps, logs, exec ← この記事
  • #4 ボリュームとネットワーク
  • #5 レジストリ — Docker Hub, GHCR, push/pull
  • #6 .dockerignore とビルドコンテキスト

コンテナのライフサイクル #

コマンドを並べる前に、一つのコンテナが経る状態を絵で掴んでから入りましょう。

コンテナライフサイクル
   ┌──────────┐
   │  image   │
   └────┬─────┘
        │ docker run
   ┌──────────┐  docker stop   ┌──────────┐
   │ running  │ ─────────────▶ │  exited  │
   │          │ ◀───────────── │          │
   └────┬─────┘  docker start  └────┬─────┘
        │                           │
        │  docker pause             │ docker rm
        ▼                           ▼
   ┌──────────┐                ┌──────────┐
   │  paused  │                │ (gone)   │
   └──────────┘                └──────────┘

イメージから run でコンテナが作られて、メインプロセスが生きている間 running 状態。メインプロセスが終わるか stop で信号を受けると exited になり、rm で消えます。この五つの状態と矢印さえ頭に入れておけばコマンドの役割が自然に整理されます。

docker build — イメージを焼く #

最も単純な形は #2 で見たそのまま:

基本ビルド
docker build -t hello-docker .

よく使うフラグをまとめると:

フラグ意味
-t name:tagイメージに名前とタグを付ける。:tag 省略時は latest。複数回指定で複数タグ可
-f Dockerfile.devデフォルト名 (Dockerfile) ではない別のファイルを使うとき
--no-cacheレイヤーキャッシュを無視して最初からビルド
--pullベースイメージを毎回取り直す (古いキャッシュ回避)
--build-arg KEY=valueDockerfile の ARG に値を注入
--target stageマルチステージビルドで特定ステージだけ
--platform linux/amd64特定アーキテクチャ向けにビルド
--progress=plainBuildKit の一行サマリーではなくフルログを見たいとき

同じビルドにタグを二つ付けるパターンはよく使います。

タグを二つ付ける
docker build -t myapp:1.2.0 -t myapp:latest .

リリースバージョンと latest を同時に固める形です。push のときは両方 push します。

docker images — キャッシュされたイメージを見る #

イメージ一覧
docker images
# REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
# hello-docker latest    a1b2c3d4e5f6   2 minutes ago  148MB
# python       3.14-slim 9a8b7c6d5e4f   3 days ago     147MB

よく使う変形:

便利な形
docker images -q             # ID だけ出力 (スクリプト用)
docker images --filter dangling=true   # タグが外れた (<none>) 中間イメージ
docker image inspect myapp   # JSON でメタデータ全体
docker history myapp         # レイヤーごとにどのコマンドが作ったか

docker history は最初に見ると面白い — イメージがどんなレイヤーで積まれたか、各レイヤーのサイズがいくらかが一目で見えます。イメージが膨れた原因を追うときによく使います。

docker run — 主要フラグ #

docker run はオプションが本当に多いですが、日常で出会うのは一握りです。

よく使う形
docker run -d -p 8000:8000 --name myapp -e DEBUG=1 --rm hello-docker
フラグ意味
-d (--detach)バックグラウンド実行。コンテナ ID が出力されてプロンプトが戻る
-p HOST:CONTAINERポートマッピング。-p 8080:8000 はホスト 8080 → コンテナ 8000
-P (大文字)EXPOSE されたポート全てを任意のホストポートにマップ
--name Nコンテナ名。指定しないと Docker が bold_curie のような任意名
-e KEY=val環境変数注入。複数回可
--env-file .env.env ファイルから一括で読む
--rm終了したら自動削除。開発・一回限りの実行に便利
-itインタラクティブ + TTY。シェルに入るとき
-v src:dstボリューム / バインドマウント (#4)
--network Nネットワーク指定 (#4)
--restart unless-stoppedコンテナが死んだら自動再起動
-w /pathworking dir のオーバーライド
-u 1000:1000UID/GID のオーバーライド

フラグは イメージ名の前 に来ます。イメージ名の後ろはコンテナの中で実行するコマンド (あれば) です。

フラグ → イメージ → コマンド (オプション)
docker run [フラグ] <image> [コマンド]
Ubuntu の中で一回限りのコマンド実行
docker run --rm ubuntu:24.04 echo hello
# hello

-d--rm を一緒に使わない #

よく混乱するポイントです。-d --rm でバックグラウンドに立ち上げると終了即削除でログを見る時間がありません。どちらか一方:

  • 開発・体験: --rm (フォアグラウンド、終了時に片付け)
  • 運用・デモ: -d (バックグラウンド、明示的に rm で片付け)

docker ps — 実行中のコンテナ #

今立ち上がっているもの
docker ps
# CONTAINER ID   IMAGE          STATUS         PORTS                    NAMES
# a1b2c3d4...    hello-docker   Up 3 minutes   0.0.0.0:8000->8000/tcp   myapp
全コンテナ (終了したものまで)
docker ps -a

よく使う変形:

便利な形
docker ps -q                              # ID だけ
docker ps --filter status=exited          # 終了したものだけ
docker ps --filter ancestor=hello-docker  # 特定イメージから作られたものだけ
docker ps --format '{{.Names}}\t{{.Status}}'  # フォーマットカスタム

docker logs — stdout/stderr を見る #

-d で立ち上げたコンテナのログを見るには:

ログを見る
docker logs myapp                # これまでの出力全体
docker logs -f myapp             # follow — tail -f のように
docker logs --tail 100 myapp     # 最後の 100 行だけ
docker logs --since 10m myapp    # 直近 10 分
docker logs --timestamps myapp   # タイムスタンプ込み

Docker はコンテナの stdout/stderr をホストのログファイルに溜めます。だからアプリが標準出力にだけログを出力しても docker logs で全部見えます。コンテナの中でログファイルを作らず stdout に流すパターン が推奨される理由がこれです。

docker exec — 立ち上がっているコンテナに入る #

run は新しいコンテナを作り、execすでに立ち上がっているコンテナの中で コマンドを実行します。

シェルに入る
docker exec -it myapp bash
# または sh (alpine のように bash がないベース)
docker exec -it myapp sh

入ってファイルを見たり、DB クライアントを叩いたり、ps でプロセスを確認できます。デバッグの第一歩です。

単発コマンド
docker exec myapp ls /app
docker exec myapp env | grep DB_

-it なしで単発コマンドだけ実行する形もよく使います。

runexec の違いをもう一度 #

最初は二つが似て見えます。大きな違いは:

  • docker run -it ubuntu bash新しいコンテナ を作ってその中で bash。exit するとコンテナ終了。
  • docker exec -it myapp bashすでに立ち上がっている myapp の中で bash。exit しても myapp はそのまま生きています。

運用ではほぼ常に exec です。run で入った Ubuntu でやった作業はそのコンテナ終了とともに消えるので、「環境を少し触ってみる」以外には意味がありません。

docker stop / start / restart #

ライフサイクルを直接扱うコマンド。

止める
docker stop myapp
# コンテナに SIGTERM → 10 秒待つ → 終わらなければ SIGKILL
docker stop -t 30 myapp   # 待ち時間を 30 秒に

stopグレースフル終了 がデフォルトです。Docker が SIGTERM を送り、アプリが綺麗に片付ける時間 (デフォルト 10 秒) を与えます。この時間内に終了しないと SIGKILL が落ちます。

#2CMD を exec form で書くべきと言った理由がここにあります。shell form で書くと SIGTERM がシェルで止まってアプリに届かず、常に SIGKILL で死にます。DB 接続が綺麗に閉じなかったり、進行中の作業が切られたりします。

再起動 / 止まっているコンテナを起こす
docker start myapp     # exited 状態から再び running に
docker restart myapp   # stop → start
docker kill myapp      # SIGKILL 即時 (グレースフルなし)

docker rm / docker rmi — 片付け #

コンテナを消す
docker rm myapp                # 終了したコンテナを削除
docker rm -f myapp             # 立ち上がっていても強制削除 (kill + rm)
docker rm $(docker ps -aq)     # 全コンテナ削除 (危険)
イメージを消す
docker rmi hello-docker
docker rmi $(docker images -q --filter dangling=true)  # タグが外れたもの

<none>:<none> と表示される dangling イメージはビルドを繰り返すとどんどん溜まります。たまに片付けてください。

docker system prune — 一気に掃除 #

複数のコマンドを一つずつ叩かず、一度に使っていないものを片付けるコマンド:

軽い掃除
docker system prune
# 止まったコンテナ + dangling イメージ + 使っていないネットワークを片付け

docker system prune -a
# 上 + どのコンテナでも使われていないイメージまで

docker system prune -a --volumes
# 上 + 使っていないボリュームまで (データが消える可能性 — 注意)

CI マシンや開発マシンのディスクが埋まり始めたとき、このコマンドからまず叩くことになります。ただし --volumes は DB データまで吹き飛ぶ可能性があるので、必ずもう一度確認してから実行してください。

現在のディスク占有を見る
docker system df
# TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
# Images          18        4         3.2GB     2.1GB (65%)
# Containers      6         1         12MB      5MB (41%)
# Local Volumes   8         3         420MB     280MB (66%)

docker inspect — メタデータを掘る #

問題を追うときに一度ずつ呼ぶコマンド。コンテナ / イメージ / ネットワーク / ボリューム何でも JSON で出してくれます。

コンテナの全情報
docker inspect myapp

docker inspect --format '{{.State.Status}}' myapp
# running

docker inspect --format '{{.NetworkSettings.IPAddress}}' myapp
# 172.17.0.2

--format は Go テンプレート文法で、一行で特定の値だけ取り出したいときにとても便利です。

一サイクル — まとめて #

ここまでのコマンドで一つのコンテナを最初から最後まで通す流れ:

開始から片付けまで
# 1. イメージビルド
docker build -t myapp .

# 2. バックグラウンド実行
docker run -d --name myapp -p 8000:8000 -e DEBUG=1 myapp

# 3. 状態確認
docker ps
docker logs -f myapp

# 4. 中に入って点検
docker exec -it myapp sh

# 5. グレースフル終了
docker stop myapp

# 6. 片付け
docker rm myapp
docker rmi myapp

最初は長く見えても、すぐに手に馴染みます。CI スクリプト、Makefile、シェル alias によく入るパターンです。

まとめ #

この記事で掴んだ絵:

  • コンテナライフサイクル: image → running → exited → (gone)。コマンドはこの矢印の上に位置を取る
  • docker build のよく使うフラグ: -t-f--no-cache--build-arg--target--platform
  • docker run の日常フラグ: -d-p--name-e--rm-it
  • docker ps / images で現在の状態を、docker logs / exec で中を覗く
  • docker stop は SIGTERM グレースフル、docker kill は SIGKILL 即時
  • docker system prune は一気に掃除、docker inspect --format は精密診断

次の記事 (#4 ボリュームとネットワーク) ではコンテナが死ぬと一緒に消えていたデータをどう活かすか (ボリューム)、そしてコンテナ同士 / ホストとどう通信するか (ネットワーク) の二つを掴みます。運用段階に一歩踏み込む内容です。

X