Docker: Dockerfile の高度な命令
最終更新:2026-08-26
本番グレードの Dockerfile は動作するだけでなく、安全で、設定可能、かつ監視可能である必要があります — これらはすべて高度な命令によって実現されます。
1. 学べること
- COPY と ADD の選択原則
- ARG: ビルドパラメータの受け渡し
- ENV ランタイム環境変数
- HEALTHCHECK ヘルスチェック設定
- 非 root セキュリティポリシー
2. セキュリティ監査についての物語
(1) 問題点: root でコンテナを実行することは高リスクの脆弱性
Charlie のセキュリティ監査レポートには、「コンテナが root として実行されている」ことが高リスク脆弱性として記載されています。攻撃者がアプリケーションの脆弱性を通じてコンテナにアクセスした場合、root 権限を持ち、システムファイルの変更やホストディレクトリのマウントが可能になります。監査スコアはわずか 45 点です。
(2) Dockerfile のセキュリティ強化ソリューション
Alice は Dockerfile に USER appuser の 1 行を追加し、セキュリティスコアを 45 から 85 に引き上げました。
DOCKERFILE
# セキュリティ強化: 非 root ユーザーで実行
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 非 root ユーザーを作成して切り替え
RUN useradd -m appuser
USER appuser
CMD ["python", "app.py"]
(3) メリット: セキュリティスコアが倍に
わずか 2 行(RUN useradd + USER)を追加しただけで、セキュリティスコアは 45 から 85 へと上がり、監査に合格しました。
3. COPY vs ADD
(1) 機能比較
| 機能 | COPY |
ADD |
|---|---|---|
| ローカルファイルをイメージにコピー | ✅ | ✅ |
| URL からファイルをダウンロード | ❌ | ✅ |
| tar/gzip/bzip2/xz を自動展開 | ❌ | ✅ |
マルチステージビルドの --from |
✅ | ✅ |
(2) 選択原則
📌 重要: ベストプラクティスは
COPY のみを使用することです。ADD の自動展開と URL ダウンロードの動作は予測不能で、エラーが発生しがちです。展開が必要な場合は、明示的に RUN tar コマンドを使うと透過性と制御性が高まります。
▶ サンプル: COPY と ADD の動作の違い (難易度: ⭐⭐)
DOCKERFILE
# COPY: tar ファイルをそのままコピー
COPY archive.tar.gz /tmp/
# 結果: /tmp/archive.tar.gz(まだ圧縮されている)
# ADD: tar ファイルを自動展開
ADD archive.tar.gz /tmp/
# 結果: /tmp/archive/(展開された内容)
▶ サンプル: マルチステージビルドと COPY --from (難易度: ⭐⭐⭐)
DOCKERFILE
# マルチステージ: ビルダーステージからアーティファクトをコピー
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o server
FROM alpine:3.19
COPY --from=builder /src/server /usr/local/bin/server
CMD ["server"]
4. ARG と ENV
(1) 適用範囲の比較
| 観点 | ARG |
ENV |
|---|---|---|
| 利用可能な段階 | ビルド時(docker build) |
実行時(docker run) |
| 永続性 | イメージメタデータに書き込まれない | イメージメタデータに書き込まれる |
| 上書き可能 | --build-arg |
docker run -e |
| CMD/ENTRYPOINT で使用可能 | ❌ | ✅ |
| セキュリティ | 実行時には公開されない | 実行時に公開される(docker inspect で表示可能) |
graph TB
BUILD["ビルド時"] -->|ARG| LAYER["イメージレイヤー"]
RUN["実行時"] -->|ENV| CONTAINER["コンテナ環境変数"]
BUILD -->|ENV 永続化| CONTAINER
▶ サンプル: ARG バージョン番号ビルド (難易度: ⭐⭐)
DOCKERFILE
# ビルド時変数に ARG を使用
ARG PYTHON_VERSION=3.12
FROM python:${PYTHON_VERSION}-slim
ARG APP_VERSION=1.0
LABEL version="${APP_VERSION}"
WORKDIR /app
COPY . .
CMD ["python", "app.py"]
BASH
# カスタム Python バージョンでビルド
docker build --build-arg PYTHON_VERSION=3.11 -t myapp:3.11 .
# カスタムアプリバージョンでビルド
docker build --build-arg APP_VERSION=2.0 -t myapp:v2.0 .
▶ サンプル: ENV 環境変数設定 (難易度: ⭐⭐)
DOCKERFILE
# ENV: 実行時に利用可能
FROM python:3.12-slim
ENV APP_PORT=5000 \
APP_HOST=0.0.0.0 \
LOG_LEVEL=info
WORKDIR /app
COPY . .
CMD ["python", "app.py"]
BASH
# 実行時に ENV を上書き
docker run -d -e APP_PORT=8080 -e LOG_LEVEL=debug myapp:1.0
⚠️ 注意: 機密情報(パスワード/キー)を ENV 変数に保存しないでください。イメージメタデータに書き込まれ、誰でも
docker inspect で閲覧できます。機密情報は docker run -e で実行時に注入するか、Docker Secrets を使用してください。
5. WORKDIR 作業ディレクトリ
▶ サンプル: WORKDIR — 作業ディレクトリの設定 (難易度: ⭐)
DOCKERFILE
# WORKDIR は後続の命令の作業ディレクトリを設定する
FROM python:3.12-slim
# 各 WORKDIR はディレクトリが存在しない場合に作成する
WORKDIR /app
COPY . .
# WORKDIR は相対パスも指定可能(前の WORKDIR からの相対パス)
WORKDIR src
RUN pwd # 出力: /app/src
| ルール | 説明 |
|---|---|
| 絶対パスを使用 | WORKDIR /app(WORKDIR app ではなく) |
| 自動作成 | ディレクトリが存在しない場合は自動作成 |
| RUN/CMD/ENTRYPOINT/COPY に影響 | 後続のコマンドは WORKDIR に基づいて実行される |
RUN cd は使わない |
RUN cd /app && ... は現在の RUN でのみ有効; WORKDIR は持続する |
6. 非 root ユーザーのセキュリティ原則
(1) root でのコンテナ実行のリスク
| リスク | 説明 |
|---|---|
| 権限昇格 | コンテナの脆弱性 + root 権限 = 攻撃者がホスト上で root 権限を取得 |
| ファイル変更 | コンテナ内のすべてのファイル(システムファイルを含む)を変更可能 |
| マウントリスク | ホストディレクトリをマウントする際、root コンテナはホストのファイルを変更可能 |
| CIS ベンチマーク | 「コンテナを非 root として実行」は CIS Docker セキュリティベースラインの必須要件 |
▶ サンプル: 非 root USER での実行 (難易度: ⭐⭐)
DOCKERFILE
# ベストプラクティス: 専用ユーザーを作成してそれに切り替える
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 特定の UID/GID を持つ非 root ユーザーを作成
RUN groupadd -r appgroup && \
useradd -r -g appgroup -u 1000 -m appuser
# アプリユーザーがアプリケーションディレクトリの所有権を持つようにする
RUN chown -R appuser:appgroup /app
# 非 root ユーザーに切り替え
USER appuser
CMD ["python", "app.py"]
🔒 セキュリティ:
USER appuser 以降のすべてのコマンド(RUN/CMD/ENTRYPOINT)は appuser として実行されます。攻撃者がコンテナにアクセスしても、root 権限は持ちません。
7. EXPOSE ポート宣言
▶ サンプル: EXPOSE ポート宣言 (難易度: ⭐)
DOCKERFILE
# EXPOSE はコンテナがリッスンするポートをドキュメント化する
FROM python:3.12-slim
WORKDIR /app
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
⚠️ 注意: EXPOSE はドキュメント宣言であり、実際にはポートを公開しません。コンテナポートマッピングには引き続き
docker run -p 5000:5000 が必要です。EXPOSE の目的: ① ドキュメント化; ② docker run -P で EXPOSE 宣言されたポートをランダムマッピング対象に自動選択。
8. HEALTHCHECK ヘルスチェック
HEALTHCHECK により、Docker はコンテナ内のアプリケーションが健全かどうかを自動的にチェックし、健全でない場合は自動的に再起動できます。
(1) ヘルスチェック構文
DOCKERFILE
HEALTHCHECK [OPTIONS] CMD command
| オプション | デフォルト | 説明 |
|---|---|---|
--interval |
30s | チェック間隔 |
--timeout |
30s | タイムアウト |
--start-period |
0s | コンテナ起動猶予期間 |
--retries |
3 | X 回連続失敗後に「unhealthy」とマーク |
▶ サンプル: HEALTHCHECK curl チェック (難易度: ⭐⭐)
DOCKERFILE
# curl でのヘルスチェック
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
CMD ["python", "app.py"]
BASH
# ヘルスステータスを確認
docker inspect myapp --format='{{.State.Health.Status}}'
💻 出力:
TEXT
📖 参照専用
healthy
(2) ヘルスステータスの説明
| ステータス | 意味 | Docker の動作 |
|---|---|---|
starting |
猶予期間中(start-period) | カウント対象外 |
healthy |
検査合格 | 正常に動作中 |
unhealthy |
連続失敗がリトライ回数に達した | アラートをトリガー; 再起動ポリシーと組み合わせれば自動再起動可能 |
9. LABEL メタデータ
▶ サンプル: LABEL でメタデータを追加 (難易度: ⭐)
DOCKERFILE
# LABEL はイメージにメタデータを追加する
LABEL maintainer="alice@example.com"
LABEL version="1.0"
LABEL description="Flask web application for production"
LABEL org.opencontainers.image.source="https://github.com/example/app"
10. 完全なサンプル: Spring Boot アプリケーション用セキュアな Dockerfile の作成
DOCKERFILE
# ============================================
# Spring Boot 用 本番グレード Dockerfile
# 機能: 非 root ユーザー、HEALTHCHECK、ARG、LABEL
# ============================================
# JAR ファイル名用のビルド引数
ARG JAR_FILE=app.jar
# ステージ 1: ビルド(将来はマルチステージ、ここでは単純にコピー)
FROM eclipse-temurin:21-jre-alpine
# メタデータ
LABEL maintainer="dev-team@example.com"
LABEL version="2.0"
# 非 root ユーザーを作成
RUN addgroup -S appgroup && \
adduser -S appuser -G appgroup
# 作業ディレクトリを設定
WORKDIR /app
# JAR ファイルをコピー(柔軟性のために ARG を使用)
COPY target/${JAR_FILE} app.jar
# アプリユーザーがファイルを所有するようにする
RUN chown -R appuser:appgroup /app
# 非 root ユーザーに切り替え
USER appuser
# アプリケーションポートを公開
EXPOSE 8080
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
# アプリケーションを実行
ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["--server.port=8080"]
BASH
# カスタム JAR 名でビルド
docker build --build-arg JAR_FILE=myapp-2.0.jar -t spring-app:2.0 .
# 環境変数オーバーライドで実行
docker run -d -p 8080:8080 --name spring spring-app:2.0
# ヘルスステータスを確認
docker inspect spring --format='User: {{.Config.User}}, Health: {{.State.Health.Status}}'
💻 出力:
TEXT
📖 参照専用
User: appuser, Health: healthy
❓ よくある質問
Q ADD は tar アーカイブを自動展開しますか?
A はい、ただしこれは「トラップ機能」です。tar ファイルを単にコピーしたい場合(展開なし)、ADD は予期せず展開してしまいます。ベストプラクティス: COPY のみを使用し、展開が必要な場合は明示的に
RUN tar -xzf file.tar.gz を使って、透過的で制御可能な動作を確保してください。Q ARG と ENV の違いは何ですか?
A ARG はビルドプロセス中のみ利用可能でイメージに書き込まれません; ENV はイメージに書き込まれ、実行時にも表示されます。経験則: 実行時にアクセスが必要なもの(ポート設定など)は ENV、ビルド時にのみ使うもの(バージョン番号やダウンロード URL など)は ARG を使用します。パスワードの保存には ENV ではなく、実行時の
-e を使用してください。Q HEALTHCHECK を誤検知しないように設定するには?
A 3 つの重要ポイント: ①
--start-period を設定してアプリケーションの起動時間を確保する(Java アプリでは少なくとも 30 秒); ② アプリケーションの実際のヘルスエンドポイント(例: /health)を確認する; 単にポートにアクセスできるかをチェックするのではなく; ③ --timeout が --interval の半分を超えないようにし、チェックのバックログが発生しないようにします。Q なぜコンテナを root として実行してはいけないのですか?
A コンテナが root で動作している場合、アプリケーションの脆弱性を通じてコンテナにアクセスした攻撃者は root 権限を取得します。カーネルの脆弱性と組み合わさると、ホストへのエスケープにつながる可能性があります。非 root ユーザーでの実行により攻撃対象領域が大幅に縮小されます — CIS Docker ベンチマークは「非 root として実行」を必須要件として挙げています。
Q
EXPOSE は実際にポートを公開しますか?A いいえ。
EXPOSE は単にドキュメント宣言であり、コンテナがどのポートを使用する予定かを伝えるだけです。実際のマッピングには docker run -p または -P が必要です。-P(大文字)は EXPOSE で宣言されたポートをホストのランダムポートに自動的にマッピングします。Q コンテナが非 root ユーザーで実行されていることを確認するには?
A
docker exec <コンテナ> whoami が root 以外のユーザー名を返すはずです。または docker inspect <コンテナ> --format='{{.Config.User}}' を使って設定されたユーザーを確認できます。📖 まとめ
- COPY vs ADD: COPY のみを使う — その動作は透過的で制御可能; ADD の自動展開は予測不能な「トラップ」
- ARG: ビルド時変数(イメージに書き込まれない); ENV: ランタイム変数(イメージに書き込まれる)
- WORKDIR は作業ディレクトリを設定する;
RUN cdより信頼性が高く永続的 - 非 root ユーザーへの切り替えはセキュリティのベストプラクティス — わずか 2 行のコードで実現可能
- HEALTHCHECK により Docker はアプリケーションの健全性を自動監視し、再起動ポリシーと組み合わせて自己修復を実現
- EXPOSE はポート宣言; 実際のマッピングには
-pパラメータが必要
📝 練習問題
- 基本問題 (難易度: ⭐): レッスン 7 の Dockerfile に非 root ユーザーを追加してください。イメージビルド後、
docker execを使ってwhoamiが「root」を出力しないことを確認してください。 - 応用問題 (難易度: ⭐⭐): HEALTHCHECK 命令を追加し、curl を使ってアプリケーションのヘルスエンドポイントをチェックしてください。アプリケーションをビルドして実行した後、
docker inspectを使って Health.Status を確認してください。 - 挑戦問題 (難易度: ⭐⭐⭐): レッスン 7 の Flask アプリケーション用に、本番グレードの完全な Dockerfile を書いてください: ARG バージョン番号 + ENV 設定 + 非 root ユーザー + HEALTHCHECK + LABEL を含むこと。その後、
docker scout quickviewまたはtrivy imageを使ってイメージのセキュリティ脆弱性をスキャンしてください。