本記事には広告(アフィリエイトリンク)が含まれます。

Docker基本操作・ライフサイクル解説【CKAD第2回】

広告
広告

第2回スコープ・学習目標・今ここマップ

動作確認バージョン: Docker CE 29.4.3 / containerd 2.2.3 / runc 1.3.5 / nginx:1.27-alpine(実体: nginx 1.27.5)/ AlmaLinux 10.1(kernel 6.12.0-124.55.3.el10_1)(2026-05-09 時点・k8s-ops 実機検証済・SP_vol1-pre-03 起点)

本回は Kubernetes 実践教科書 第1巻(CKAD 対応・全 19 回)の第2回です。第1部「コンテナと Docker」の全 4 回のうち、第2回はイメージの取得からコンテナの起動・停止・削除・観察まで一通りの基本操作を扱います。

CKAD D1(Application Design and Build)と D3(Application Observability and Maintenance)の前提知識を固めるセクションです。

第1回からの継承状態確認(SP_vol1-pre-02 状態・第1回完走スナップショット SP_vol1-pre-01 を起点として SP_vol1-pre-02 取得後に演習を開始):

  • k8s-ops(AlmaLinux 10.1・2 vCPU / 6 GB / 40 GB)に Docker CE 29.4.3 が導入済み
  • developer ユーザーが docker グループに所属しており sudo なしで docker コマンドを実行できる
  • docker run hello-world で hello-world イメージの取得と実行が確認済み

今ここマップ(第1巻 19 回中の現在位置):

第1部 コンテナとDocker
    第1回: コンテナ技術概念 + Docker環境準備  [完了]
  ★ 第2回: Docker基本操作  ← 今ここ
    第3回: Dockerfile + マルチステージビルド + JDK 25/Payara Micro
    第4回: コンテナレジストリ + Trivy スキャン

第2部 Kubernetes基礎(第5〜6回)
第3部 アプリリソース(第7〜11回)
第4部 ワークロード戦略(第12〜14回)
第5部 セキュリティ基礎(第15〜16回)
第6部 パッケージ管理 + HTTPS公開(第17〜19回)

第2回を終えると、以下を習得した状態になります。

  • docker pull / docker run / docker stop / docker rm を一人で実施できる
  • コンテナライフサイクル(Created → Running → Paused → Stopped → Removed)を図示して説明できる
  • docker ps / docker logs / docker exec / docker inspect の基本操作ができる(D3 前提知識)
  • Volume と bind mount の違いを説明し、用途に合わせて使い分けられる(Docker コンテナのログ設計・stdout/stderr symlink 実装を含む)
  • Nginx コンテナを起動してホストブラウザからアクセスできる

コンテナライフサイクル

コンテナには明確な状態(ライフサイクル)があります。状態遷移を理解することで、「コンテナが動かない」「出力が見えない」といった問題の原因を特定する際の思考の基礎になります。

コンテナの5つの状態

状態説明主な遷移トリガー
Createddocker create でコンテナが作成されたが、まだ実行されていないdocker create 実行後
Runningコンテナが起動して内部プロセスが実行中docker start / docker run 実行後
Pausedcgroup freezer でプロセスが一時停止された状態(メモリは保持・実務での使用機会は限定的:メモリダンプ取得や開発デバッグ等の特殊用途で稀に使用)docker pause 実行後
Stopped(Exited)コンテナ内のプロセスが終了した状態(コンテナのファイルシステムは残る)docker stop / プロセス自然終了
Removeddocker rm でコンテナが削除された状態(ファイルシステムも消える)docker rm 実行後
Docker コンテナのライフサイクル状態遷移図 - Created / Running / Paused / Stopped (Exited) / Removed の 5 状態と、docker create / start / pause / unpause / stop / kill / rm / rm -f コマンドによる遷移を示す。Running 状態を強調色で表現し、docker rm -f は Stopped を経由せず Running から直接 Removed に遷移する斜め矢印で表現。

状態遷移の流れをテキストで確認します。

[docker create]
      |
  [Created]
      | [docker start]
  [Running] <--- [docker restart]
      |                      |
      | [docker pause]       | [docker stop / SIGTERM]
      v                      v
  [Paused]          [Stopped/Exited]
      |                      |
      | [docker unpause]     | [docker start]
      +----------------------+
                             |
                             | [docker rm]
                         [Removed]

補足:docker run = docker create + docker start

通常は docker run を使うことがほとんどですが、内部では createstart の2ステップで動作しています。状態管理の観点から、この分解を知っておくと、docker ps -a(停止中も含む全コンテナ表示)の意味がわかりやすくなります。

K8s との橋渡し:Kubernetes の Pod ライフサイクル(Pending → Running → Succeeded / Failed)は、コンテナのライフサイクルを Pod という単位に昇華させたものです。第7回(Pod + Multi-container パターン)でこの関係を掘り下げます。

イメージとコンテナの関係

イメージとコンテナは似ているようで異なります。

  • イメージ:コンテナを作成するための「テンプレート(設計図)」。読み取り専用のレイヤー構造。Docker Hub 等から docker pull で取得する
  • コンテナ:イメージを元に作成した「実行インスタンス」。書き込み可能なレイヤーが追加されて動作する

1つのイメージから複数のコンテナを起動できます。

nginx:1.27-alpine イメージ(テンプレート)
       |---> コンテナ A(名前: nginx-a / ポート: 8080)
       |---> コンテナ B(名前: nginx-b / ポート: 8081)
       +---> コンテナ C(名前: nginx-c / ポート: 8082)

イメージ自体は変わらず、コンテナごとに異なる設定(ポート・環境変数・Volume マウント先等)で起動できます。

Docker 29.x の docker images 出力フォーマット変更(重要)

Docker CE 29.x(containerd snapshotter ベース)では docker images の出力列が変わっています。

旧フォーマット(Docker 27.x 以前):

REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
nginx         latest    xxxxxxxx       2 weeks ago    193MB

新フォーマット(Docker 29.x・containerd snapshotter 使用時):

IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
nginx:1.27-alpine    65645c7bb6a0         73MB         21.9MB
hello-world:latest   f9078146db2e       21.8kB         9.49kB   U
WARNING: This output is designed for human readability. For machine-readable output, please use --format.

主な変更点:

  • REPOSITORY/TAGIMAGEname:tag 形式で一列に統合)
  • SIZEDISK USAGE / CONTENT SIZE / EXTRA の3列に分割
  • WARNING 行が末尾に追加される

この変更は第1回の progress.md に記録した技術発見です。本記事のコマンド出力例はすべて Docker 29.x 形式で記述します。

やってみよう ① docker pull / run / ps / stop / rm の基本操作

前提状態:SP_vol1-pre-02 状態(Docker CE 29.4.3 インストール済・developer ユーザーで sudo なし操作可能)

ステップ1:イメージを pull する

実行コマンド:

$ docker pull nginx:1.27-alpine

実行結果:

197eb75867ef: Pull complete
39c2ddfd6010: Pull complete
Digest: sha256:65645c7bb6a0661892a8b03b89d0743208a18dd2f3f17a54ef4b76fb8e2f2a10
Status: Downloaded newer image for nginx:1.27-alpine
docker.io/library/nginx:1.27-alpine

出力中の Digest: sha256:... はイメージの内容に基づく不変識別子(ダイジェスト)です。タグ 1.27-alpine は同じ名前でも内容が更新されることがありますが、ダイジェストはイメージのバイト列が変わらない限り同じ値となり、本番環境で「先週と同じイメージか?」を確実に検証する手段になります。

タグ戦略・latest タグの罠については第4回で詳述します。

ステップ2:イメージの確認

実行コマンド:

$ docker images

実行結果(Docker 29.x フォーマット):

IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
nginx:1.27-alpine    65645c7bb6a0         73MB         21.9MB
hello-world:latest   f9078146db2e       21.8kB         9.49kB   U
WARNING: This output is designed for human readability. For machine-readable output, please use --format.

ステップ3:コンテナをバックグラウンドで起動(ポート公開)

実行コマンド:

$ docker run -d --name nginx-test -p 8080:80 nginx:1.27-alpine

実行結果(コンテナ ID が返る):

a3f8b2c1d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1

オプションの意味:

  • -d:バックグラウンド(detached)モードで起動
  • --name nginx-test:コンテナに名前を付ける(指定しない場合は自動生成)
  • -p 8080:80:ホストの 8080 ポートをコンテナの 80 ポートに転送

ステップ4:起動中のコンテナを確認

実行コマンド:

$ docker ps

実行結果:

CONTAINER ID   IMAGE              COMMAND                  CREATED         STATUS         PORTS                                   NAMES
a3f8b2c1d4e5   nginx:1.27-alpine  "/docker-entrypoint.…"   10 seconds ago  Up 9 seconds   0.0.0.0:8080->80/tcp, :::8080->80/tcp   nginx-test

ステップ5:ホストブラウザからアクセス確認

k8s-ops の IP アドレスは環境により異なります。ip addr show または hostname -I | awk '{print $1}' で確認してください(本書著者環境では 192.168.1.122・読者向け Vagrantfile では 192.168.56.10 想定)。ホスト OS のブラウザで http://<k8s-ops の IP>:8080 にアクセスします(著者環境例: http://192.168.1.122:8080)。

「Welcome to nginx!」のデフォルトページが表示されれば Nginx コンテナが正常に動作しています。

curl でも確認できます(k8s-ops 上から):

実行コマンド:

$ curl -s http://localhost:8080 | head -5

実行結果(Nginx デフォルトページの冒頭):

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

ステップ6:コンテナを停止する

docker stop はデフォルトでコンテナのメインプロセスに SIGTERM を送り、10 秒待機しても終了しなければ SIGKILL で強制終了します(猶予秒数は --time / -t オプションで調整可能)。

これは Kubernetes の Pod 削除時に動作する terminationGracePeriodSeconds(デフォルト 30 秒)と同じ仕組みで、グレースフルシャットダウン設計の前提知識として第7回 Pod ライフサイクルで再度詳述します。

実行コマンド:

$ docker stop nginx-test

実行結果:

nginx-test

ステップ7:停止中のコンテナを確認

docker ps は起動中のコンテナのみ表示するため、停止中は -a オプションを付けます。-a は all の略で、Created / Exited / Paused 状態のコンテナも含めて全件を一覧化します。

実行コマンド:

$ docker ps -a

実行結果:

CONTAINER ID   IMAGE              COMMAND                  CREATED        STATUS                     PORTS   NAMES
a3f8b2c1d4e5   nginx:1.27-alpine  "/docker-entrypoint.…"   1 minute ago   Exited (0) 10 seconds ago          nginx-test

nginx-test が消えています。第1回で実行した hello-world コンテナが Exited 状態で残っているのが見えます(CONTAINER ID と NAMES は環境ごとに異なる値が表示されます)。これは第1回演習の残骸です。不要になった停止コンテナは適宜 docker rm で削除します。

ステップ8:コンテナを削除する

実行コマンド:

$ docker rm nginx-test

実行結果:

nginx-test

実行コマンド(削除確認):

$ docker ps -a

実行結果(nginx-test が消えている):

CONTAINER ID   IMAGE           COMMAND    CREATED        STATUS                   PORTS   NAMES
353de0e97213   hello-world     "/hello"   (第1回実行時)   Exited (0) (第1回時刻)          condescending_mirzakhani

補足:起動中コンテナの強制削除

停止してから削除する場合は docker stopdocker rm の2ステップが基本です。docker rm -f で起動中のコンテナを強制削除することもできますが、実行中のプロセスを突然終了させるためデータ損失のリスクがあります(ヒヤリハットセクションで詳述)。

docker logs / exec / inspect でコンテナを観察する

コンテナが動いているとき、あるいはエラーが出たとき、「コンテナの中で何が起きているか」を確認する手段が3つあります。これらは CKAD D3(Application Observability and Maintenance)の前提知識であり、第6回の kubectl logs / kubectl exec / kubectl describe への橋渡しになります。

docker logs — コンテナの標準出力・標準エラーを確認する

まず Nginx コンテナを再度起動してからログを確認します。

実行コマンド:

$ docker run -d --name nginx-observe -p 8080:80 nginx:1.27-alpine

実行コマンド:

$ docker logs nginx-observe

実行結果(Nginx の起動ログ抜粋):

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/05/09 08:36:16 [notice] 1#1: using the "epoll" event method
2026/05/09 08:36:16 [notice] 1#1: nginx/1.27.5
2026/05/09 08:36:16 [notice] 1#1: built by gcc 14.2.0 (Alpine 14.2.0)
2026/05/09 08:36:16 [notice] 1#1: OS: Linux 6.12.0-124.52.3.el10_1.x86_64
2026/05/09 08:36:16 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1024:524288
2026/05/09 08:36:16 [notice] 1#1: start worker processes
2026/05/09 08:36:16 [notice] 1#1: start worker process 30
2026/05/09 08:36:16 [notice] 1#1: start worker process 31

--follow-f)オプションでリアルタイムにログを追うことができます。

実行コマンド:

$ docker logs -f nginx-observe

別ターミナルで curl を実行するとアクセスログがリアルタイムで流れることを確認できます。終了するには Ctrl+C を入力します。

K8s での対応kubectl logs <pod名> が同等の操作です。第6回の Observability セクションで本格的に扱います。

docker exec — 起動中のコンテナ内でコマンドを実行する

実行コマンド(コンテナ内のシェルに入る):

$ docker exec -it nginx-observe /bin/sh

実行結果(コンテナ内のシェルプロンプトが表示される):

/ #

オプションの意味:

  • -i:標準入力をオープンにする(interactive)
  • -t:疑似ターミナルを割り当てる(tty)

コンテナ内での確認例(コンテナ内で実行):

コンテナ内に入った後、Nginx の設定ファイルや DocumentRoot を確認できます。以下の順で実行します。

実行コマンド(Nginx 設定ファイルの中身を確認):

# cat /etc/nginx/nginx.conf

nginx 公式イメージ 1.27-alpine の標準設定(worker_processes / events / http ブロックなど)が表示されます。本回ではコンテナの中身を観察できることを確認するのが目的のため、内容の詳細は第3回(Dockerfile / nginx ベースイメージ)以降で扱います。

実行コマンド(DocumentRoot を確認):

# ls /usr/share/nginx/html/

実行結果:

50x.html    index.html

確認が終わったらコンテナから抜けます。

実行コマンド:

# exit

コマンド1つだけ実行してコンテナ内に入らない方法:

実行コマンド:

$ docker exec nginx-observe nginx -v

実行結果:

nginx version: nginx/1.27.5

K8s での対応kubectl exec -it <pod名> -- /bin/sh が同等の操作です。第6回で詳述します。

docker inspect — コンテナの詳細設定情報を JSON で確認する

実行コマンド:

$ docker inspect nginx-observe

実行結果(JSON 形式・一部抜粋):

[
  {
    "Id": "a3f8b2c1d4e5f6g7...",
    "Created": "2026-05-09T08:36:16.000000000Z",
    "State": {
      "Status": "running",
      "Running": true
    },
    "NetworkSettings": {
      "Networks": {
        "bridge": {
          "IPAddress": "172.17.0.2"
        }
      }
    },
    "Mounts": []
  }
]

特定のフィールドだけ取り出す場合は --format オプションが便利です。

【Docker 29.x 変更点】:Docker 29.x ではコンテナのネットワーク設定が NetworkSettings.Networks.<network 名> 配下にネスト化されました。旧バージョンで使われていた {{.NetworkSettings.IPAddress}}map has no entry for key "IPAddress" エラーになります。

デフォルトの bridge ネットワーク使用時は .NetworkSettings.Networks.bridge.IPAddress でアクセスします。複数ネットワーク接続時は range テンプレート({{range $k, $v := .NetworkSettings.Networks}}{{$k}}={{$v.IPAddress}}{{end}})が便利です。

実行コマンド(コンテナの IP アドレスだけ取得):

$ docker inspect nginx-observe --format '{{.NetworkSettings.Networks.bridge.IPAddress}}'

実行結果:

172.17.0.2

K8s での対応kubectl describe pod <pod名>kubectl get pod -o json が同等の操作です。第6回で詳述します。

Volume と bind mount の違い

コンテナのファイルシステムは「コンテナのライフサイクルと同一」です。docker rm でコンテナを削除すると、コンテナ内に書き込んだデータもすべて消えます。データを永続化したり、ホストとファイルを共有したりするために Volumebind mount の2つの手段があります。

Volume vs bind mount 比較表

比較項目Volumebind mount
管理主体Docker が管理(/var/lib/docker/volumes/ 以下)ホスト OS のパスを直接指定
データの場所Docker 管理下(ホストパスを意識しない)ホスト OS の任意のパス
作成方法docker volume create または -v volume名:コンテナ内パス-v /ホストパス:コンテナ内パス または --mount type=bind,...
ポータビリティ高い(ホスト OS のディレクトリ構造に依存しない)低い(ホスト OS のパスに依存)
主な用途DB データ永続化・コンテナ間データ共有設定ファイルの注入・開発時のソースコード共有
本番向き推奨設定ファイル注入目的なら可
Volume と bind mount のアーキテクチャ対比図 - 左列の Volume は Docker 管理エリア /var/lib/docker/volumes/ 配下に nginx-logs/_data として格納されコンテナ削除後もデータが残る。右列の bind mount はホスト OS の任意パス /home/developer/nginx-html を直接コンテナにマウントする。

K8s との橋渡し

  • Docker Volume → Kubernetes PersistentVolume(PVC)/ emptyDir(第9回で詳述)
  • bind mount → Kubernetes ConfigMap / Secret(設定ファイル注入)/ hostPath(第9回で詳述)
  • 注意: K8s の emptyDir は Pod 削除と同時にデータが消える一時ストレージです。Docker Volume はコンテナを docker rm で削除しても残るため、両者は「永続性」の意味が大きく異なります。混同しやすいポイントなので、第9回 Volume / PVC の学習時に再度確認します。

Volume の基本操作(概念確認)

実行コマンド(Volume を作成する):

$ docker volume create nginx-data

実行結果:

nginx-data

実行コマンド(Volume の一覧確認):

$ docker volume ls

実行結果:

DRIVER    VOLUME NAME
local     nginx-data

実行コマンド(Volume の詳細確認):

$ docker volume inspect nginx-data

実行結果(JSON 形式):

[
    {
        "CreatedAt": "2026-05-09T19:50:38+09:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/nginx-data/_data",
        "Name": "nginx-data",
        "Options": null,
        "Scope": "local"
    }
]

Mountpoint フィールドでホスト OS 上の実際のディレクトリパスを確認できます。

やってみよう ② Volume + bind mount でデータ永続化

前提状態:H2-5 の nginx-observe コンテナが起動中。

シナリオ:Nginx のデフォルトページをカスタムし(bind mount)、Volume のマウントで Docker コンテナのログ設計を発見する。

ステップ1:nginx-observe コンテナを停止・削除(クリーンアップ)

実行コマンド:

$ docker stop nginx-observe && docker rm nginx-observe

実行結果:

nginx-observe
nginx-observe

ステップ2:カスタム HTML ファイルを作成(bind mount 用)

まず bind mount するためのディレクトリをホスト OS 上に作成します。

実行コマンド:

$ mkdir -p /home/developer/nginx-html

実行結果:(出力なし。成功時は何も表示されません)

続いて、ヒアドキュメントでカスタム HTML ファイルを書き込みます。

実行コマンド:

$ cat > /home/developer/nginx-html/index.html << 'EOF'


カスタム Nginx ページ

第2回 Docker 基本操作 - bind mount 動作確認

EOF

実行結果:(出力なし。成功時は何も表示されません)

ステップ3:Volume を作成

実行コマンド:

$ docker volume create nginx-logs

実行結果:

nginx-logs

ステップ4:bind mount + Volume を指定してコンテナを起動

実行コマンド:

$ docker run -d \
  --name nginx-vol-test \
  -p 8080:80 \
  -v /home/developer/nginx-html:/usr/share/nginx/html:ro \
  -v nginx-logs:/var/log/nginx \
  nginx:1.27-alpine

実行結果(コンテナ ID が返る):

b4e9f3a2c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2

オプションの説明:

  • -v /home/developer/nginx-html:/usr/share/nginx/html:ro:ホストの HTML ディレクトリを Nginx のドキュメントルートに bind mount(:ro = 読み取り専用)
  • -v nginx-logs:/var/log/nginxnginx-logs Volume を Nginx のログディレクトリにマウント(Nginx の /var/log/nginx/ ディレクトリには、Docker 公式イメージのデフォルトでログファイルではなく /dev/stdout / /dev/stderr への symbolic link が配置される。Volume にマウントすることで「コンテナがどのようなログ設計を持つか」を確認する演習として活用する)

ステップ5:カスタムページの表示確認

実行コマンド:

$ curl -s http://localhost:8080

実行結果(カスタム HTML が返る):

<!DOCTYPE html>
<html>
<head><title>カスタム Nginx ページ</title></head>
<body>
<h1>第2回 Docker 基本操作 - bind mount 動作確認</h1>
</body>
</html>

カスタム HTML が表示されれば bind mount が正常に機能しています。

ステップ6:「Nginx ログは Volume に保存されない」の発見演習

注:alpine イメージが未取得の場合、Docker Hub から自動でダウンロードされます(初回実行時のみ・圧縮転送サイズ 約 4 MB / 展開後ディスク使用量 約 12 MB)。

実行コマンド:

$ docker run --rm -v nginx-logs:/data alpine ls -la /data

実行結果(alpine イメージを初回取得する場合は pull 出力が先に表示される):

Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
6a0ac1617861: Pulling fs layer
a63813f19ccb: Download complete
4e0cb4ac63e7: Download complete
Digest: sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11
Status: Downloaded newer image for alpine:latest

実行結果(ls -la の本体・symlink のみが表示される):

total 0
drwxr-xr-x    2 root     root            41 May  9 08:39 .
drwxr-xr-x    1 root     root            18 May  9 08:39 ..
lrwxrwxrwx    1 root     root            11 Apr 16  2025 access.log -> /dev/stdout
lrwxrwxrwx    1 root     root            11 Apr 16  2025 error.log -> /dev/stderr

ステップ7:Docker ログ設計の解説

Volume 内に access.logerror.log のファイルは存在しますが、いずれも実体ではなく /dev/stdout / /dev/stderr への symbolic link です。Nginx 公式イメージは Docker コンテナベストプラクティスに従い、ログを /dev/stdout / /dev/stderr に流す symbolic link として /var/log/nginx/ を実装しています。これにより docker logs <コンテナ名> でログを取得でき、コンテナがステートレスに保たれます。

nginx.conf の access_log ディレクティブはすでに /var/log/nginx/access.log を指していますが、その先が symlink になっているため、Volume にマウントしても symlink のみが保存されます。ログ実体を Volume に保存するには、nginx.conf の access_log 先をコンテナ内の実ファイルパス(例: /tmp/access.log)に変更するか、symlink を含まないカスタムイメージを作成する必要があります(本書スコープ外)。

実用上は Loki / Fluent Bit / syslog 集約などのログアグリゲータで集めるのが標準です(第2巻第13回で扱う Prometheus/Loki スタックに繋がる前提知識)。

ステップ8:コンテナを削除して Volume が残ることを確認

実行コマンド:

$ docker stop nginx-vol-test
$ docker rm nginx-vol-test

実行コマンド(Volume がまだ存在することを確認):

$ docker volume ls

実行結果(Volume が残っている):

DRIVER    VOLUME NAME
local     nginx-data
local     nginx-logs

コンテナを削除しても Volume は残ります。これが Volume を使う意義です。

ステップ9:Volume と bind mount のクリーンアップ

実行コマンド:

$ docker volume rm nginx-data nginx-logs

実行結果:

nginx-data
nginx-logs

bind mount 用ディレクトリも削除します。

実行コマンド:

$ rm -rf /home/developer/nginx-html

実行コマンド(演習② 終了時点のイメージ一覧確認):

$ docker images

実行結果:

IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
nginx:1.27-alpine    65645c7bb6a0         73MB         21.9MB
alpine:latest        5b10f432ef3d       12.7MB         3.95MB
hello-world:latest   f9078146db2e       21.8kB         9.49kB   U
WARNING: This output is designed for human readability. For machine-readable output, please use --format.

演習② で pull した alpine:latest が追加されていることが確認できます。

現場ヒヤリハット

事例1:docker rm -f で稼働中 DB コンテナを削除してデータが消えた

入社4ヶ月目の B さんは、開発環境の Docker コンテナが増えて混乱してきたため、不要なコンテナをまとめて削除しようとしました。docker ps -a を実行し、停止中のコンテナを中心に docker rm を実行しようとしましたが、誤って -f(強制削除)オプションを付けたままコンテナ名を間違えて実行しました。

削除したコンテナは MySQL のデータベースコンテナでした。Volume を使わずにコンテナ内のファイルシステム(/var/lib/mysql/)に直接データを書き込んでいたため、docker rm -f の実行と同時に開発中のデータが全件消失しました。

翌朝、チームメンバーが開発環境で動作確認しようとしたところ、DB が空になっていることが発覚。バックアップは1週間前のものが最後で、その間に蓄積した開発データは復旧不能でした。

教訓と対処法

  • コンテナのデータは必ず Volume か bind mount で永続化する:データを持つコンテナ(DB 等)は、コンテナを削除してもデータが消えないように Volume を必ず指定する
  • docker rm -f は運用中には原則使用しない:正しい手順は docker stopdocker rm の2ステップ。-f は緊急時のみ
  • 削除前に docker inspect でマウント情報を確認する:削除対象コンテナの Mounts フィールドを確認し、データが保存されている場所を把握する

事例2:bind mount の順序を誤ってホスト OS の重要ファイルを上書きしそうになった

入社8ヶ月目の C さんは、コンテナ内の設定ファイルを外部から注入するために bind mount を設定しようとしました。-v オプションの順序は「ホストパス:コンテナ内パス」ですが、手元のメモを見間違えて「コンテナ内パス:ホストパス」の順序で記述してしまいました。さらに、マウント先として /etc に近い重要なディレクトリを指定してしまいました。

コマンドを実行する直前に、同席していた先輩エンジニアがコマンドの引数を見て「それは逆では」と気付き、実行前に停止できました。もし実行されていれば、ホスト OS の設定ディレクトリがコンテナ内から参照でき、最悪の場合は書き換えられる危険がありました。

危険な例(絶対に実行しないこと)

$ docker run --rm -v /etc:/etc nginx:1.27-alpine rm /etc/passwd

上記は、ホスト OS の /etc/passwd が削除されます。bind mount の -v オプションでは「ホストパス:コンテナ内パス」の順序・パスを必ず慎重に確認してください。

防止策

  • 設定ファイルの注入目的の bind mount は原則 :ro(読み取り専用)を付ける
  • -v の前に --mount type=bind,source=...,target=...,readonly 形式で書くと視認性が上がり、誤記が減る
  • コマンド実行前に --dry-run 相当の確認をする(コマンドを一度 echo で確認する等)

CKAD D3 前提知識ブリッジ — Docker コマンドと kubectl の対応

本回で学習した docker logs / docker exec / docker inspect は、Kubernetes 環境では対応するコマンドに置き換わります。Docker レベルの操作を習得しておくことで、第6回以降の kubectl 操作に移行する際の概念的なハードルを下げることが目的です。

Docker → Kubernetes コマンド対応表

操作Docker コマンドkubectl 対応コマンド対応回
コンテナのログ確認docker logs <コンテナ名>kubectl logs <Pod名>第6回
リアルタイムログ追跡docker logs -f <コンテナ名>kubectl logs -f <Pod名>第6回
コンテナ内でコマンド実行docker exec -it <コンテナ名> /bin/shkubectl exec -it <Pod名> -- /bin/sh第6回
コンテナの詳細情報確認docker inspect <コンテナ名>kubectl describe pod <Pod名>第6回
起動中コンテナの一覧docker pskubectl get pods第5回
全コンテナの一覧(停止中含む)docker ps -akubectl get pods --field-selector=status.phase!=Running(停止中相当を抽出)第5回
全 Namespace のコンテナ一覧—(Docker に Namespace 概念なし)kubectl get pods -A-A は all-namespaces の意・状態フィルタではない)第5回

CKAD D3(Application Observability and Maintenance)との対応

CKAD の D3 ドメインでは「コンテナログの活用」と「Kubernetes でのデバッグ」が出題範囲に含まれます。第2回で Docker レベルの観察手段を習得しておくことで、第6回の kubectl 操作へ移行する際の概念的なハードルを下げられます。

第3回(Dockerfile + マルチステージビルド)では、本回で起動した Nginx コンテナのような「出来合いのイメージを使う」ではなく、自分でイメージを作るステップに進みます。fanclub-api の Backend(Java JDK 25 + Payara Micro 7.2026.4)のイメージを Dockerfile で定義し、docker build でビルドします。

今回のまとめ + 理解度チェック

第2回では以下を実施しました。

  • コンテナのライフサイクル(Created → Running → Paused → Stopped → Removed)と状態遷移を確認した
  • イメージとコンテナの関係(イメージ = テンプレート / コンテナ = 実行インスタンス)を整理した
  • Docker 29.x の docker images 出力フォーマット変更(DISK USAGE / CONTENT SIZE / EXTRA)を確認した
  • Nginx コンテナを起動(docker run -d -p 8080:80)してホストブラウザからアクセスした
  • docker logs / docker exec / docker inspect でコンテナを観察する手段を習得した
  • Volume と bind mount の違い(管理主体・用途・ポータビリティ)を整理し、実際にコンテナに適用した
  • Nginx 公式イメージのログ設計(/var/log/nginx//dev/stdout / /dev/stderr への symbolic link)を実機で発見した(Docker コンテナベストプラクティス・ログアグリゲータ連携への前提知識)
  • Volume のデータ(および symlink)はコンテナ削除後も残ることを確認した

理解度チェック(○×形式・全 9 問)

問題文解答
1docker run コマンドは内部的に docker createdocker start の2ステップを実行する
2docker ps は起動中のコンテナと停止中のコンテナを両方表示する×
3docker stop を実行するとコンテナのファイルシステムも削除される×
4docker logs コマンドはコンテナの標準出力(stdout)と標準エラー(stderr)を表示する
5Volume はホスト OS の任意のパスを直接指定してコンテナにマウントする手段である×
6bind mount に :ro オプションを付けると、コンテナ内からそのマウントパスへの書き込みを禁止できる
7docker rm -f は起動中のコンテナを強制削除できる
8Docker 29.x の docker images 出力では SIZE 列の代わりに DISK USAGE / CONTENT SIZE / EXTRA の3列が表示される
9Nginx 公式 Docker イメージでは、/var/log/nginx/access.log は実ファイルとして存在している×

解説(重要問のみ)

  • 問2:docker ps は起動中(Running)のコンテナのみ表示します。停止中も含めて表示するには docker ps -a を使います。
  • 問3:docker stop はコンテナを停止(Exited 状態)にするだけです。コンテナのファイルシステムは docker rm を実行するまで残ります。
  • 問5:これは bind mount の説明です。Volume は Docker が管理するストレージ領域(/var/lib/docker/volumes/ 配下)に作成されます。ホスト OS の任意パスを指定するのは bind mount です。

次回予告第3回「Dockerfile + マルチステージビルド + JDK 25 / Payara Micro イメージビルド」では、出来合いのイメージを使うのではなく、自分でコンテナイメージを作るステップに進みます。Dockerfile の記法(FROM / COPY / RUN / CMD / ENTRYPOINT / EXPOSE / ENV)、マルチステージビルド(Maven ビルドステージ → JDK 25 + Payara Micro 実行ステージ)、そして fanclub-api Backend(v0.1.0)の初登場を扱います。

シリーズ一覧

第1部:コンテナと Docker

第2部:Kubernetes 基礎

第3部:アプリリソース

第4部:ワークロード戦略

第5部:セキュリティ基礎

第6部:パッケージ管理 + HTTPS 公開

広告
kubernetes
スポンサーリンク