Dockerfileは、Dockerイメージを作る手順を上から順に書いたテキストファイルです。この記事では、拡張子なしのファイル名と置き場所、FROM・RUN・COPY・CMDなどの主要命令、RUN・CMD・ENTRYPOINTとCOPY・ADDの違い、1行目の # syntax=docker/dockerfile:1 が実際にどの版を指すのか、そして書き方の誤りを docker build --check で機械的に見つける方法までを扱います。掲載したDockerfileはすべて、Docker Engine 29.5.2・buildx v0.38.0 の環境で実際にビルドして動作を確かめています。
まとめ:Dockerfileの要点
- Dockerfileはイメージの設計図です。
docker buildでイメージを作り、docker runでコンテナとして起動します。 - ファイル名は拡張子なしの Dockerfile。
Dockerfile.txtやContainerfileは既定では読まれず、-fで明示すれば任意の名前を使えます。 - Pythonアプリを組み込む基本例は
FROM → WORKDIR → COPY → RUN → CMDの順です。依存ファイルを先にCOPYしてからソースをCOPYすると、ビルドのキャッシュが効きます。 - RUNはビルド時、CMD・ENTRYPOINTは起動時に動きます。CMDはexec形式(
["python", "app.py"])にすると、アプリが停止シグナルを直接受け取れます。シェルがシグナルを転送しない構成では、停止がタイムアウトまで遅れる場合があります(後述の実測では、シェル形式が10秒、exec形式が0秒)。 - 1行目の
# syntax=docker/dockerfile:1は、Docker Engineを更新せずに新しい構文を使うための行です。2026年10月28日時点で:1は1.27.1を指し、10月7日公開の1.28.0にはまだ追随していません。 - 書き方の点検は
docker build --check .で済みます。20種類のルールで判定し、違反があれば終了コード1を返すので、CIにそのまま組み込めます。
Dockerfileとイメージ・コンテナ・Composeの関係
Dockerfileに書くのは「どのベースイメージに、何をインストールし、どのファイルを置き、起動時に何を実行するか」です。命令は上から順に処理され、結果がイメージとして保存されます。役割は3つに分かれます。Dockerfileが設計図、イメージが設計図から作った読み取り専用のテンプレート、コンテナがイメージから起動した実行中のプロセスです。
設計図がテキストなので、Gitで差分を追え、レビューにも乗せられます。「自分のマシンでは動く」問題が減るのはこのためです。Docker自体の位置づけやDesktopとEngineの違いはDockerとは|仮想マシンとの違い・必要スペック・Desktop/Engineの違いとインストール手順、namespacesやcgroupsによる隔離の原理はDockerの仕組みを図解:containerd・runc・namespaces・ネットワーク・Composeの内部で扱っています。
Docker Composeとの違いもよく聞かれます。Dockerfileは1つのイメージの作り方を定義し、Composeは compose.yaml で複数のコンテナの構成と起動順をまとめます。Composeでは build.context にビルドコンテキストを指定し、既定以外のDockerfileを使う場合は build.dockerfile にファイルのパスを指定して併用します。Composeの書き方はdocker-composeとは?複数コンテナをymlで定義し一括管理する仕組みにまとめています。
Dockerfileのファイル名と置き場所
拡張子なしの「Dockerfile」と、読まれない名前
ファイル名は拡張子を付けずに Dockerfile とします。docker build は -f を省くと、ビルドコンテキストの直下からこの名前を探します。紛らわしい名前で試した結果は次のとおりです(ファイルを1つだけ置いて docker build . を実行)。
| 置いたファイル名 | -f なし | 結果 |
|---|---|---|
| Dockerfile | ビルド成功 | 既定の名前 |
| dockerfile(小文字) | ビルド成功 | 小文字へのフォールバックあり |
| Dockerfile.txt | 失敗 | open Dockerfile: no such file |
| Containerfile | 失敗 | 同上(Podmanの既定名) |
| Dockerfile.txt を -f で指定 | ビルド成功 | 名前は自由になる |
小文字の dockerfile が通るのは、大文字小文字を区別しないmacOSのファイルシステムだからではありません。Linux上でも成功し、BuildKitのDockerfileフロントエンドが Dockerfile が見つからないときに小文字の名前を探し直す実装になっています(moby/buildkit の frontend/dockerui/config.go)。ただし公式の表記は Dockerfile なので、新しく作るなら大文字で始めてください。
失敗例で多いのは、Windowsのメモ帳で保存して Dockerfile.txt になっているケースです。エクスプローラーが拡張子を隠していると気づけません。PodmanやBuildahの既定名である Containerfile は、docker build では -f Containerfile を付けないと読まれません。用途別に複数持つときは Dockerfile.prod のように接尾辞を付け、-f で指定します。
# 用途別のDockerfileを指定してビルドする
docker build -f docker/Dockerfile.prod -t myapp:prod .
Dockerfileの配置とビルドコンテキストの指定
慣例はプロジェクトのルートです。docker build の末尾に渡す . は「ビルドコンテキスト」で、ビルド時にDockerへ送られるファイル群の起点を表します。COPY が参照できるのはこの中だけで、COPY ../config.yaml . のようにコンテキストの外は取り込めません。上の例のように -f でDockerfileを別ディレクトリに置いても、コンテキストは末尾のパスで決まります。
ビルドコンテキストのルートには .dockerignore を置きます。Dockerfileごとに除外設定を分ける場合は、そのDockerfileと同じディレクトリに、ファイル名へ .dockerignore を付けた専用ファイルを置きます。.git、node_modules、ローカルの .env を除外しないと、COPY . . が認証情報ごとイメージに取り込み、コンテキストの転送も遅くなります。
# .dockerignore
.git
.env
node_modules
__pycache__
Dockerfileの書き方:最小構成からビルド・起動まで
Pythonアプリの基本構成
Pythonのスクリプトを動かす最小構成です。ベースの python:3.15-slim は、2026年10月9日にリリースされたPython 3.15の公式イメージです。
# syntax=docker/dockerfile:1
FROM python:3.15-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
requirements.txt だけを先にCOPYしているのは、アプリのソースを1行直しただけで pip install が再実行されるのを避けるためです。命令ごとにキャッシュが作られ、ある命令の入力が変わるとそれ以降がすべて作り直されます。変更の少ないものを上に、よく変わるものを下に置くのが基本の並べ方で、Dockerの公式ベストプラクティスも同じ順序を勧めています。
docker build と docker run の基本オプション
以下は、Dockerfileと同じディレクトリに requirements.txt と app.py を用意した場合のコマンド例です。ポート公開の例では、app.py がコンテナ内の 0.0.0.0:8000 で待ち受けることを前提とします。
# カレントディレクトリをコンテキストにしてビルドし、タグを付ける
docker build -t myapp:1.0 .
# 後述のARG PYTHON_VERSIONと変数参照付きFROMを使うDockerfile向け
# 直前の固定FROMの例では、この引数でPythonの版は変わらない
docker build --build-arg PYTHON_VERSION=3.14 -t myapp:py314 .
# キャッシュを使わずに作り直す
docker build --no-cache -t myapp:1.0 .
# 起動する(ホストの8000番をコンテナの8000番へ)
docker run --rm -p 8000:8000 myapp:1.0
-t でタグを付けておかないと、イメージは <none> のまま溜まり、後から追えなくなります。起動後の start・exec の使い分けや「コンテナがすぐ終了する」ときの切り分けはDockerコンテナ起動・実行コマンドの使い分け|run・start・execと起動しない時の対処を参照してください。
Dockerfileの主要命令一覧
実務の大半は FROM・WORKDIR・COPY・RUN・CMD の5つで書けます。残りは必要になったときに足すものです。
| 命令 | 効くタイミング | 役割 | 記述例 |
|---|---|---|---|
| FROM | ビルド | ベースイメージ | FROM python:3.15-slim |
| WORKDIR | ビルド・起動 | 作業ディレクトリ | WORKDIR /app |
| COPY | ビルド | ファイルの取り込み | COPY . . |
| ADD | ビルド | URL取得・tar展開付きの取り込み | ADD app.tar.gz /opt/ |
| RUN | ビルド | コマンド実行 | RUN apt-get update |
| CMD | 起動 | 既定のコマンド・引数 | CMD [“python”, “app.py”] |
| ENTRYPOINT | 起動 | 固定の実行ファイル | ENTRYPOINT [“gunicorn”] |
| ARG | ビルドのみ | ビルド引数 | ARG PYTHON_VERSION=3.15 |
| ENV | ビルド・起動 | 環境変数 | ENV APP_ENV=production |
| EXPOSE | (宣言のみ) | 使うポートの記録 | EXPOSE 8000 |
| USER | ビルド・起動 | 実行ユーザー | USER appuser |
| HEALTHCHECK | 起動 | 正常性チェック | HEALTHCHECK CMD curl -f http://localhost/ |
| LABEL | ビルド | メタデータ | LABEL org.opencontainers.image.version=”1.0″ |
FROMのタグ固定とslim・alpineの選び方
FROMは先頭に書きます。前に置けるのはパーサーディレクティブ、コメント、そしてARGだけです。Dockerfileリファレンスも「ARGはFROMより前に置ける唯一の命令」と明記しており、ベースイメージの版を変数にするときに使います。
ARG PYTHON_VERSION=3.15
FROM python:${PYTHON_VERSION}-slim
タグは latest を避け、python:3.15-slim のように系列を指定します。latest は中身が予告なく入れ替わるので、昨日通ったビルドが今日壊れます。ただし系列のタグも、パッチ更新やベースOSの更新で中身が変わります。内容まで固定するなら python:3.15-slim@sha256:... とダイジェストまで固定しますが、こうするとセキュリティ修正も自動では入らなくなるため、RenovateやDependabotで更新のプルリクエストを出す仕組みとセットにしてください。
サイズを削るために alpine 系を選ぶのは、無条件には勧めません。alpine はC標準ライブラリにglibcではなくmusl libcを使うため、PythonのC拡張やglibc前提のバイナリでビルド失敗や実行時エラーが起きます。数十MBの差のためにビルドの調査で時間を失うなら、slim 系のほうが総コストは低く済みます。alpine が向くのは、Go の静的バイナリや nginx のように musl で動作確認されたものを載せる場合です。
ARG・ENVの有効範囲とEXPOSEの宣言機能
ARGはビルド中だけの変数で、できたコンテナには残りません。ENVはイメージに焼き込まれ、実行中のコンテナからも読めます。どちらも秘密情報には使えません。値は docker history で読めてしまい、後述の docker build --check も SecretsUsedInArgOrEnv として、秘密情報を示唆する変数名を警告します。ただし、任意の秘密値をすべて検出できるわけではありません。秘密情報は後の章のシークレットマウントで渡します。
EXPOSEはポートを開ける命令ではなく、「このイメージはこのポートで待ち受ける」という記録です。外から届くようにするのは docker run -p 8000:8000 の側で、EXPOSEが無くても -p があれば通信できます。
RUN・CMD・ENTRYPOINTの違いとシグナルの実測
3つとも「コマンドを書く」命令ですが、動くタイミングが違います。RUNはビルド時、CMDとENTRYPOINTはコンテナの起動時です。
| 命令 | 実行タイミング | レイヤ | 上書き方法 | 主な用途 |
|---|---|---|---|---|
| RUN | ビルド時 | 作る | なし | インストール・構築 |
| CMD | 起動時 | 作らない | docker run の引数 | 既定のコマンド・引数 |
| ENTRYPOINT | 起動時 | 作らない | –entrypoint | 固定の実行ファイル |
CMDは docker run myapp:1.0 python other.py のように引数を渡すと丸ごと置き換わり、複数書いても有効なのは最後の1つです。ENTRYPOINTを書くと、docker run の引数はその後ろに追加されます。実行ファイルをENTRYPOINTで固定し、既定の引数をCMDで与える組み合わせが定番です。
ENTRYPOINT ["gunicorn", "app:app"]
CMD ["--bind", "0.0.0.0:8000", "--workers", "2"]
書き方は必ずexec形式(JSON配列)にしてください。シェル形式で書くと /bin/sh -c が親プロセスになり、docker stop が送るSIGTERMがアプリに届きません。SIGTERMを受けたら終了するPythonスクリプトを python:3.15-slim(/bin/sh はdash)で動かし、docker stop にかかる時間を測った結果が次の表です。
| CMDの書き方 | PID 1のプロセス | docker stop の所要 |
|---|---|---|
| CMD [“python”, “app.py”] | python app.py | 0秒 |
| CMD python app.py | /bin/sh -c python app.py | 10秒 |
| CMD cd /app && python app.py | /bin/sh -c cd /app && … | 11秒 |
シェル形式では、SIGTERMを受けたシェルがアプリに転送しないまま待ち、10秒の猶予が切れた時点でSIGKILLで強制終了されます。強制終了ではアプリの終了処理(接続の切断や書きかけのファイルの保存)が走りません。シェルの機能(環境変数の展開や &&)がどうしても要るときは、起動スクリプトの最後を exec python app.py にして、アプリをPID 1に置き換えます。
COPYとADDの違い
ファイルを取り込む命令は2つありますが、通常はCOPYを使います。ADDはローカルのtarを自動で展開し、リモートのURLやGitリポジトリも取得できるため、「tarを置きたいだけなのに展開された」という事故が起きます。
| 観点 | COPY | ADD |
|---|---|---|
| ローカルファイル | そのままコピー | そのままコピー |
| ローカルのtar | 展開しない | 自動で展開する |
| リモートURL・Git | 不可 | 可(–checksum で検証) |
| 使う場面 | 通常はこちら | 展開・取得が必要なときだけ |
公式のベストプラクティスも、ADDはチェックサム検証付きのリモート取得やtarの展開が必要なときに限るよう案内しています。ADD --checksum はDockerfile 1.6で入ったオプションで、取得した内容のハッシュが一致しなければビルドを止めます。1.27.0 からは zstd 圧縮のアーカイブもADDで扱えるようになりました。マルチステージビルドで前段の成果物を移すときも COPY --from= を使います。
syntaxディレクティブ:Dockerfileの「バージョン」の決まり方
「Dockerfileのバージョン」を決めているのは、ファイルの1行目に書く syntax パーサーディレクティブです。Dockerfileの解釈はBuildKitの「フロントエンド」が担い、その版をこの行で指定します。
# syntax=docker/dockerfile:1
この行が無い場合、BuildKitに同梱された版のフロントエンドが使われます。書いておけば、ビルドのたびに指定したタグのフロントエンドをDocker Hubから取得するため、BuildKitやDocker Engineを更新しなくても新しい構文が使えます(Dockerfileリファレンスの syntax の節)。パーサーディレクティブはコメントや空行より前、つまりファイルの最初に置かないと、ただのコメントとして扱われます。
:1・1.28・labs のタグと指している版
タグの付け方はCustom Dockerfile syntaxで決まっています。2026年10月28日にDocker Hubの docker/dockerfileのダイジェストを突き合わせた結果が次の表です。
| 指定 | 公式の説明 | 2026-10-28 の実体 |
|---|---|---|
| docker/dockerfile:1 | 1.x の最新安定版 | 1.27.1(9月30日) |
| docker/dockerfile:latest | 最新版 | 1.27.1(9月30日) |
| docker/dockerfile:1.28 | 1.28.x の最新パッチ | 1.28.0(10月7日) |
| docker/dockerfile:1.28.0 | 固定(更新されない) | 1.28.0 |
| docker/dockerfile:1-labs | :1 に試験的機能を追加 | 1.28.0-labs |
公式は :1 を「常に最新の安定版を指す」と説明していますが、実際には最新リリースより遅れることがあります。docker build のログにも docker.io/docker/dockerfile:1@sha256:4edf897a… と出ており、これは1.27.1のダイジェストです。1.28.0で入った変更(COPY --chown で既定グループを指す書き方の追加など)を確実に使いたいなら、:1 ではなく docker/dockerfile:1.28 と書いてください。逆に、普段は :1 のままで困りません。
Dockerfile 1.24〜1.28の主な構文変更
2026年に入ってからの主な変更をmoby/buildkit のリリースノートから拾うと、次のとおりです。
- 1.24.0:
ARG SOURCE_DATE_EPOCH=contextで、Gitのコミット時刻などをイメージのタイムスタンプに使える(再現可能ビルド向け) - 1.25.0:ビルド時にCPU・メモリの上限を指定すると、すべてのRUNに適用される(BuildKit v0.31.0以降)
- 1.27.0:
RUN --deviceが試験版から安定版へ。zstd圧縮のアーカイブをADDやビルドコンテキストに使える - 1.28.0:
COPY --chownで既定グループ(default group)を指す書き方の追加と、--chownと--linkの無効な組み合わせの検出
BuildKit前提の書き方:キャッシュ・シークレット・ヒアドキュメント
BuildKitは、Docker Engine 23.0のリリースノートのとおりLinuxでも既定のビルダーになり、docker build は docker buildx build の別名になりました。設定なしで次の構文が使えます。この章では、通常のRUNに加え、BuildKitのマウント構文とヒアドキュメントを扱います。仕組みやマルチアーキテクチャのビルドまで知りたい場合はBuildKitとは|Dockerの新ビルドバックエンドの仕組み・キャッシュ/secret/マルチアーキと採用判断を読んでください。
次のDockerfileは、ヒアドキュメント・キャッシュマウント・シークレットマウント・非rootユーザーを1本にまとめた例です。check=error=true と組み合わせるので、syntaxは :1 ではなく 1.28 に固定しています(理由は build checks の章で説明します)。
# syntax=docker/dockerfile:1.28
# check=error=true
ARG PYTHON_VERSION=3.15
FROM python:${PYTHON_VERSION}-slim
WORKDIR /app
RUN <<EOF
set -eux
apt-get update
apt-get install -y --no-install-recommends ca-certificates
rm -rf /var/lib/apt/lists/*
EOF
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
RUN --mount=type=secret,id=api_token,env=API_TOKEN \
test -n "$API_TOKEN" && echo "token length: ${#API_TOKEN}"
RUN useradd --uid 10001 --no-create-home --shell /usr/sbin/nologin appuser
COPY --chown=appuser . .
USER appuser
CMD ["python", "app.py"]
export API_TOKEN=dummy-secret-123
docker build --secret id=api_token,env=API_TOKEN -t myapp:1.0 .
キャッシュマウント(--mount=type=cache)は、pipやnpmのダウンロード済みパッケージをビルドをまたいで保持します。requirements.txtの変更で命令のキャッシュが外れても、pipが利用できるダウンロード済みデータがキャッシュマウントに残っていれば、再取得を減らせます。マウントした中身はイメージに入らないので、--no-cache-dir を付けなくてもイメージは膨らみません。
シークレットマウント(--mount=type=secret)は、トークンをそのRUNの間だけ渡します。env= で環境変数として受け取る指定はDockerfile 1.10.0以降で使えます。上の例をビルドすると、ビルドログには token length: 16 と出る一方、docker history --no-trunc にもコンテナの環境変数にもトークンは残りませんでした。プライベートなパッケージリポジトリの認証は、ARGやENVではなくこの方法で渡します。
ヒアドキュメント(RUN <<EOF)は、バックスラッシュでつなぐ長いRUNを素直なスクリプトとして書けます。途中の単純コマンドの失敗で停止させるには、先頭に set -e を指定します。未定義変数を検出する -u と、展開後のコマンドをログへ出す -x は用途に応じて追加します(-x は値もログに出すので、シークレットを扱うRUNでは付けません)。ヒアドキュメントの中身は1つのシェルスクリプトとして実行され、成否は最後のコマンドの終了コードだけで決まります。途中に false を置いて試すと、set -e が無い場合は後続の echo まで進んでビルドが成功し、set -eux を付けると exit code: 1 で止まりました。apt-getのinstallが失敗していても気づかないまま、壊れたイメージができるということです。
apt-get update と apt-get install を別々のRUNに分けるのも典型的な失敗です。updateの結果がキャッシュされたまま残り、installが古いパッケージ一覧を見に行きます。同じRUNの中にまとめ、最後に /var/lib/apt/lists を消します。
マルチステージビルドによる最終イメージの軽量化
マルチステージビルドは、FROMを複数書いてステージを分け、最終ステージに必要な成果物だけを持ち込む書き方です。FROM ... AS 名前 でステージに名前を付け、COPY --from=名前 で成果物を引き継ぎます。ビルドツールやソースコードが最終イメージに残らないので、サイズと攻撃面が同時に減ります。
# syntax=docker/dockerfile:1
FROM node:26-slim AS builder
WORKDIR /src
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
FROM nginx:1.30-alpine
COPY --link --from=builder /src/dist /usr/share/nginx/html
EXPOSE 80
ビルド用のベースにはNode.js 26を使っています。Node.jsのリリース計画では、26のActive LTS開始日は2026年10月28日、24のメンテナンスLTS開始日は10月20日とされています。配信側の nginx:1.30-alpine はnginxの安定版ブランチで、Docker Hubの stable-alpine と同じダイジェストです。最終イメージにはNode.jsのランタイムも node_modules も入らず、nginxと配信ファイルだけになります。
COPY --link はコピーで作るレイヤを前段から独立させる指定です。ベースの nginx:1.30-alpine が更新されても、このレイヤは作り直されずに再利用されます。コピー元の dist が変われば当然作り直されます。JavaやSpring Bootでのマルチステージとレイヤー展開はSpring BootのDockerfileの書き方|マルチステージ・レイヤー展開・AOTキャッシュを4.1.1で検証、VS Codeの開発環境をコンテナにする構成はDev Container(devcontainer)のメリットとは|devcontainer.jsonの書き方・lockfile・Compose連携で扱っています。
docker build –checkによるDockerfileの静的検査
Dockerfileのレビューで指摘される誤りの多くは、BuildKitに組み込まれたbuild checksで機械的に見つかります。Dockerfile 1.8で入った機能で、buildx 0.15.0以降なら docker build --check . で使えます。イメージは作らず、20種類のルールに照らして違反だけを報告します。
わざと誤りを入れた次のDockerfileで試します。
# syntax=docker/dockerfile:1
from python:3.15-slim as Builder
ARG API_TOKEN
ENV DB_PASSWORD=changeme
WORKDIR app
COPY . .
CMD python app.py
$ docker build --check .
WARNING: StageNameCasing - Stage name 'Builder' should be lowercase
WARNING: ConsistentInstructionCasing - Command 'from' should match the case of the command majority (uppercase)
WARNING: SecretsUsedInArgOrEnv - ... (ARG "API_TOKEN")
WARNING: SecretsUsedInArgOrEnv - ... (ENV "DB_PASSWORD")
WARNING: WorkdirRelativePath - Relative workdir "app" can have unexpected results ...
WARNING: JSONArgsRecommended - JSON arguments recommended for CMD ...
Check complete, 6 warnings have been found!
$ echo $?
1
出力は要点だけに縮めています。6件の警告と終了コード1が返りました。注意したいのは、--check を付けない通常の docker build では同じ警告が出てもビルドは成功し、終了コードは0になる点です。警告を見落とさないためには、Dockerfileの2行目に check=error=true を書きます。
# syntax=docker/dockerfile:1
# check=error=true
この指定で通常のビルドも lint violation found for rules: StageNameCasing, ... で失敗するようになります。既存のDockerfileで一部のルールだけ外したいときは # check=skip=JSONArgsRecommended;error=true のように、検査対象から外すルールとエラー扱いの設定を1行にまとめます。複数のルール名はカンマで区切ります。CIでイメージをビルドするジョブに組み込む方法はGitHub ActionsでDockerイメージをビルドする|push先とキャッシュ・マルチアーキの設計を参照してください。
build checksとhadolintの検出範囲の比較
同じDockerfileをhadolint v2.15.1(2026年7月31日リリース)に通すと、検出は次のように分かれました。
| 誤り | docker build –check | hadolint v2.15.1 |
|---|---|---|
| ARG・ENVの秘密情報 | 検出(2件) | 検出(DL3064・2件) |
| WORKDIRの相対パス | 検出(警告) | 検出(DL3000・error) |
| CMDのシェル形式 | 検出 | 検出(DL3025) |
| 命令・ステージ名の大文字小文字 | 検出(2件) | 検出なし |
| 終了コード | 1 | 1 |
build checksは追加の導入が要らず、Dockerfileの構文そのものの誤りに強いのが利点です。hadolintは apt-get install の版固定(DL3008)のように、RUNの中のシェルコマンドまで踏み込んで検査します。まず # check=error=true を入れ、RUN内の追加検査が必要ならhadolintを併用します。CIで新しい検査ルールによる突然の失敗を避ける場合は、syntaxの版も固定して計画的に更新します。脆弱性の検出はどちらの守備範囲でもないので、できたイメージはTrivy(トリヴィ)の使い方|インストールからイメージ・IaCスキャン、CI設定までのようなスキャナーにかけます。
Dockerfile運用のユーザー権限・責務分離・更新方針
ここまでの章で扱わなかった点を、効果の大きい順に挙げます。
- 非rootユーザーで動かす:既定のユーザーはrootです。UIDを明示してユーザーを作り、
USERで切り替えます。useraddはDebian系(python:3.15-slimなど)のコマンドで、Alpine系ではadduser -D -u 10001 appuserを使います。上の例ではidの結果がuid=10001(appuser)になることを確認しています。 - 1コンテナ1責務にする:Webサーバーとデータベースを1つのイメージに入れず、別のコンテナに分けてComposeでつなぎます。
- RUNを何でも1行に詰め込まない:レイヤ数を減らす目的で無関係なコマンドまでつなぐと、読みにくくなり、1か所の変更でキャッシュがすべて外れます。同じRUNにまとめる必要があるのは、apt-getのupdateとinstall、一時ファイルの作成と削除のように、分けると壊れるものだけです。
- ベースイメージを定期的に作り直す:タグを固定していても、
docker build --pullでベースの修正版を取り込まないと、脆弱性の修正が入りません。
コンテナのビルドや配布をAWS上のCI/CDやECS・EKSまで含めて設計したい場合は、AWS・Google Cloud・Azureのインフラ構築支援でもご相談を受け付けています。
よくある質問
Dockerfileの拡張子は何ですか?
拡張子はありません。ファイル名は Dockerfile です。Dockerfile.txt は既定では読まれず、open Dockerfile: no such file or directory で失敗します。用途別に分けるときは Dockerfile.prod のような名前にして、docker build -f で指定します。
Dockerfileはどこに置けばいいですか?
プロジェクトのルートが慣例です。条件は、COPYしたいファイルがすべてビルドコンテキスト(docker build の末尾に渡すパス)の中にあることです。Dockerfile自体は -f を使えば別のディレクトリに置けます。
syntax=docker/dockerfile:1 の行は必須ですか?
必須ではありません。書かなければBuildKitに同梱された版で解釈されます。Docker Engineを更新しなくても新しい構文を使えるようになるため、書いておくことを勧めます。最新の1.28系の機能を確実に使いたい場合は :1 ではなく docker/dockerfile:1.28 と書きます。
DockerfileとDocker Composeの違いは何ですか?
Dockerfileは1つのイメージの作り方、Composeは複数コンテナの構成と起動をまとめる compose.yaml です。Composeの build: でDockerfileを指定して併用します。
Dockerfileが実行されるのはいつですか?
docker build を実行したときに、命令が上から順に処理されます。ただしCMDとENTRYPOINTはビルド時には動かず、docker run でコンテナを起動したときに実行されます。ARGはビルド中だけ存在し、ENVはイメージに残って起動後も参照できます。