Day 5: Dockerfileでイメージを作成
今日学ぶこと
- Dockerfileとは何か
- 基本的なDockerfile命令
- docker buildでイメージをビルド
- ビルドコンテキストの理解
- 実践的なDockerfileの作成
Dockerfileとは
Dockerfileは、Dockerイメージを構築するための命令を記述したテキストファイルです。レシピのように、ベースイメージから始まり、アプリケーションの設定、依存関係のインストール、起動コマンドなどを順番に記述します。
flowchart LR
Dockerfile["Dockerfile\n(命令書)"] --> |"docker build"| Image["イメージ"]
Image --> |"docker run"| Container["コンテナ"]
style Dockerfile fill:#3b82f6,color:#fff
style Image fill:#22c55e,color:#fff
style Container fill:#8b5cf6,color:#fff
なぜDockerfileを使うのか
| メリット | 説明 |
|---|---|
| 再現性 | 同じDockerfileから同じイメージを何度でも作成可能 |
| バージョン管理 | Gitで変更履歴を管理できる |
| 自動化 | CI/CDパイプラインに組み込める |
| ドキュメント | イメージの構成が明確に記述される |
Dockerfileの基本構造
# ベースイメージを指定
FROM python:3.11-slim
# メタデータを追加
LABEL maintainer="your@email.com"
# 作業ディレクトリを設定
WORKDIR /app
# ファイルをコピー
COPY requirements.txt .
# コマンドを実行
RUN pip install -r requirements.txt
# アプリケーションをコピー
COPY . .
# ポートを公開
EXPOSE 8000
# コンテナ起動時のコマンド
CMD ["python", "app.py"]
主要な命令
FROM - ベースイメージの指定
すべてのDockerfileは FROM から始まります。
# 公式Pythonイメージを使用
FROM python:3.11
# 軽量版を使用
FROM python:3.11-slim
# 超軽量Alpine版を使用
FROM python:3.11-alpine
# 特定のバージョンを固定
FROM python:3.11.7-slim-bookworm
WORKDIR - 作業ディレクトリの設定
# 作業ディレクトリを作成して移動
WORKDIR /app
# 以降の命令はすべて /app 内で実行される
COPY - ファイルのコピー
ホストのファイルをイメージにコピーします。
# 単一ファイルをコピー
COPY requirements.txt .
# 複数ファイルをコピー
COPY package.json package-lock.json ./
# ディレクトリをコピー
COPY src/ ./src/
# すべてをコピー
COPY . .
ADD - 高度なコピー
COPY に似ていますが、追加機能があります。
# URLからダウンロード
ADD https://example.com/file.tar.gz /app/
# tarファイルを自動展開
ADD archive.tar.gz /app/
推奨: 通常は COPY を使用。ADD は特別な機能が必要な場合のみ。
RUN - コマンドの実行
イメージ構築時にコマンドを実行します。
# パッケージのインストール
RUN apt-get update && apt-get install -y curl
# Pythonパッケージのインストール
RUN pip install --no-cache-dir -r requirements.txt
# 複数コマンドを1つのRUNにまとめる(レイヤー数を減らす)
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
CMD - デフォルトコマンド
コンテナ起動時のデフォルトコマンドを指定します。
# exec形式(推奨)
CMD ["python", "app.py"]
# shell形式
CMD python app.py
ENTRYPOINT - エントリーポイント
コンテナをコマンドとして使用する場合に便利です。
ENTRYPOINT ["python"]
CMD ["app.py"]
# app.py を実行
docker run myapp
# 別のスクリプトを実行
docker run myapp other.py
ENV - 環境変数
ENV APP_ENV=production
ENV PORT=8000
# 複数を一度に
ENV APP_ENV=production \
PORT=8000
EXPOSE - ポートの公開
# ポート8000を公開(ドキュメント目的)
EXPOSE 8000
# 複数ポート
EXPOSE 80 443
注意: EXPOSE は実際にポートを開くわけではありません。docker run -p で指定が必要です。
ARG - ビルド時引数
ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}
ARG APP_ENV=production
ENV APP_ENV=${APP_ENV}
# ビルド時に引数を渡す
docker build --build-arg PYTHON_VERSION=3.10 .
命令の流れ
flowchart TB
FROM["FROM: ベースイメージ"] --> WORKDIR["WORKDIR: 作業ディレクトリ"]
WORKDIR --> COPY1["COPY: 依存ファイル"]
COPY1 --> RUN["RUN: 依存インストール"]
RUN --> COPY2["COPY: アプリケーション"]
COPY2 --> EXPOSE["EXPOSE: ポート公開"]
EXPOSE --> CMD["CMD: 起動コマンド"]
style FROM fill:#3b82f6,color:#fff
style RUN fill:#f59e0b,color:#fff
style CMD fill:#22c55e,color:#fff
docker build コマンド
基本構文
docker build [オプション] パス
基本的な使い方
# カレントディレクトリのDockerfileをビルド
docker build .
# タグを指定してビルド
docker build -t myapp:v1.0 .
# 別のDockerfileを指定
docker build -f Dockerfile.prod -t myapp:prod .
ビルドコンテキスト
docker build の最後の引数(.)はビルドコンテキストです。
flowchart LR
subgraph Context["ビルドコンテキスト"]
Dockerfile["Dockerfile"]
App["app.py"]
Req["requirements.txt"]
Other["その他のファイル"]
end
Context --> |"送信"| Daemon["Docker Daemon"]
Daemon --> |"ビルド"| Image["イメージ"]
style Context fill:#3b82f6,color:#fff
style Daemon fill:#8b5cf6,color:#fff
ビルドコンテキスト内のすべてのファイルがDockerデーモンに送信されます。
.dockerignore
不要なファイルをビルドコンテキストから除外します。
# .dockerignore
node_modules/
.git/
*.log
.env
__pycache__/
.pytest_cache/
*.pyc
実践:Pythonアプリをコンテナ化
Step 1: アプリケーションの作成
# プロジェクトディレクトリを作成
mkdir myapp && cd myapp
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from Docker!'
@app.route('/health')
def health():
return 'OK'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
requirements.txt
flask==3.0.0
Step 2: Dockerfileの作成
Dockerfile
# ベースイメージ
FROM python:3.11-slim
# 作業ディレクトリ
WORKDIR /app
# 依存関係をまずコピー(キャッシュ効率化)
COPY requirements.txt .
# 依存関係のインストール
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションをコピー
COPY app.py .
# ポートを公開
EXPOSE 8000
# 起動コマンド
CMD ["python", "app.py"]
Step 3: イメージのビルド
docker build -t myapp:v1.0 .
出力例:
[+] Building 15.2s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> [internal] load .dockerignore
=> [internal] load metadata for docker.io/library/python:3.11-slim
=> [1/5] FROM docker.io/library/python:3.11-slim
=> [2/5] WORKDIR /app
=> [3/5] COPY requirements.txt .
=> [4/5] RUN pip install --no-cache-dir -r requirements.txt
=> [5/5] COPY app.py .
=> exporting to image
=> => naming to docker.io/library/myapp:v1.0
Step 4: コンテナの実行
docker run -d -p 8000:8000 --name myapp myapp:v1.0
Step 5: 動作確認
# ブラウザまたはcurlで確認
curl http://localhost:8000
# Hello from Docker!
curl http://localhost:8000/health
# OK
Step 6: クリーンアップ
docker stop myapp
docker rm myapp
ビルドキャッシュ
Dockerは各レイヤーをキャッシュします。変更がないレイヤーは再利用されます。
flowchart TB
subgraph FirstBuild["初回ビルド"]
F1["FROM python:3.11-slim"] --> F2["WORKDIR /app"]
F2 --> F3["COPY requirements.txt"]
F3 --> F4["RUN pip install"]
F4 --> F5["COPY app.py"]
end
subgraph SecondBuild["app.py変更後の2回目"]
S1["FROM (キャッシュ)"] --> S2["WORKDIR (キャッシュ)"]
S2 --> S3["COPY req (キャッシュ)"]
S3 --> S4["RUN pip (キャッシュ)"]
S4 --> S5["COPY app.py (再実行)"]
end
style F4 fill:#f59e0b,color:#fff
style S4 fill:#22c55e,color:#fff
style S5 fill:#f59e0b,color:#fff
重要: 変更頻度が低いものを上に、高いものを下に配置することでキャッシュを最大限活用できます。
実践:Node.jsアプリをコンテナ化
package.json
{
"name": "node-docker",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
index.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello from Node.js in Docker!');
});
app.listen(port, '0.0.0.0', () => {
console.log(`App listening on port ${port}`);
});
Dockerfile
FROM node:20-slim
WORKDIR /app
# package.jsonとpackage-lock.jsonをコピー
COPY package*.json ./
# 依存関係のインストール
RUN npm ci --only=production
# アプリケーションをコピー
COPY index.js .
EXPOSE 3000
CMD ["npm", "start"]
ビルドと実行
docker build -t node-app:v1.0 .
docker run -d -p 3000:3000 --name node-app node-app:v1.0
curl http://localhost:3000
まとめ
| 命令 | 説明 |
|---|---|
FROM |
ベースイメージを指定 |
WORKDIR |
作業ディレクトリを設定 |
COPY |
ファイルをイメージにコピー |
RUN |
ビルド時にコマンドを実行 |
ENV |
環境変数を設定 |
EXPOSE |
公開するポートを宣言 |
CMD |
デフォルト起動コマンドを指定 |
ENTRYPOINT |
エントリーポイントを設定 |
重要ポイント
FROMは必ず最初に記述- 変更頻度の低いものを上に配置してキャッシュを活用
.dockerignoreで不要ファイルを除外RUNコマンドは可能な限りまとめてレイヤーを減らす- 本番では
slimやalpineベースを検討
練習問題
問題1: Dockerfileの作成
以下の要件でDockerfileを作成してください:
- Python 3.11-slim ベース
/appを作業ディレクトリにrequirements.txtをコピーしてパッケージをインストール- すべてのファイルをコピー
- ポート5000を公開
python main.pyで起動
問題2: 命令の理解
COPY と ADD の違いを説明してください。どのような場合に ADD を使うべきでしょうか?
チャレンジ問題
静的なHTMLファイルを提供するNginxコンテナを作成してください:
index.htmlを作成(内容は自由)- Dockerfileで
nginx:alpineをベースに使用 index.htmlを/usr/share/nginx/html/にコピー- ビルドして実行、ブラウザで確認
参考リンク
次回予告: Day 6では「コンテナとデータの永続化」について学びます。ボリュームとバインドマウントを使って、コンテナのデータを永続化する方法をマスターしましょう。