1. はじめに
このガイドでは、基本的なDockerfileの書き方を解説します。Dockerを利用することで、アプリケーションの環境設定が簡単になり、開発から本番環境への移行がスムーズになります。
成果物のリポジトリはこちらです。
Dockerとは何か?
Dockerは、アプリケーションとその依存関係を単一の軽量で移植可能なコンテナにパッケージングするためのオープンソースプラットフォームです。従来の仮想マシンとは異なり、オペレーティングシステム全体ではなく、アプリケーションに必要なものだけを含んでいるため、迅速かつ効率的な実行が可能です。
Dockerは、開発者、システム管理者、および運用担当者によって、次のようなさまざまな目的に使用されています。
- アプリケーションの開発とテスト: 開発者は、Dockerを使用して、アプリケーションとその依存関係を分離された環境で実行できます。これにより、開発サイクルの効率が向上し、テストが容易になります
- アプリケーションのデプロイ: システム管理者は、Dockerを使用して、アプリケーションを本番環境に迅速かつ簡単にデプロイできます
- アプリケーションの運用: 運用担当者は、Dockerを使用して、アプリケーションをスケーリングし、監視し、トラブルシューティングすることができます
Dockerfileの役割と利点
Dockerfileは、Dockerイメージを作成するための設計図となるテキストファイルです。Dockerイメージは、コンテナの作成に使用されるテンプレートであり、Dockerfileには、イメージを構築するために必要なすべてのコマンドが記述されています。
Dockerfileを使用する利点は次のとおりです。
- 再現性: Dockerfileを使用すると、さまざまな環境で同じDockerイメージを作成できます。これは、アプリケーションのデプロイと更新を容易にするため、非常に重要です
- 移植性: Dockerイメージは、オペレーティングシステムに依存しないため、あらゆる環境で実行できます
- 効率性: Dockerイメージは軽量で、従来の仮想マシンよりも迅速に起動および実行できます
参考文献
2. Dockerfileの基本構文
FROM: ベースイメージの指定
Dockerfileの最初に記述する命令で、どのベースイメージを使用するかを指定します。これにより、ベースイメージ上に新しいレイヤーを追加して独自のイメージを作成します。
FROM python:3.11.9-slim-bookworm
RUN: コマンドの実行
イメージのビルド時に実行されるコマンドを指定します。複数のRUN命令を記述することで、各ステップごとに新しいレイヤーが作成されます。
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
wget=1.21.3-1+b2 \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/*
COPY: ファイルの複製
ホストマシン上のファイルやディレクトリをイメージ内にコピーします。ビルドコンテキストから指定したパスの内容をコンテナにコピーします。
COPY requirements.txt ./
WORKDIR: 作業ディレクトリの指定
RUNやCMD、ENTRYPOINT命令が実行される作業ディレクトリを指定します。指定したディレクトリが存在しない場合は自動的に作成されます。
WORKDIR /app
ENV: 環境変数の設定
コンテナ内で使用する環境変数を設定します。これにより、アプリケーションの設定や動作を環境変数で調整できます。
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ARG: 変数定義の設定
Dockerfile内でビルド時に使用する変数を定義するために使用されます。これにより、ビルドプロセス中にカスタマイズ可能なパラメータを指定することができます。
ARG VERSION=1.0
ARGとENVの違い
- ARG: ビルド時にのみ有効な変数。コンテナ実行時には使用不可
- ENV: ビルド時およびコンテナの実行時に有効な環境変数
EXPOSE: ポートの公開
コンテナがリスンするネットワークポートを指定します。この命令自体はポートを開くものではなく、情報提供の役割を果たします。
EXPOSE 8000
コンテナの実行時に実際にポートを公開するには、
docker run -p フラグを使い、公開用のポートと割り当てるポートを指定します。
docker run -d -p 8000:8000 -it apsrver:latest
CMD: デフォルトコマンドの設定
コンテナが起動した際に実行されるデフォルトのコマンドを指定します。複数のCMD命令がある場合、最後に記述したものが有効になります。コマンドライン引数で上書き可能です。
CMD ["/app/opt/main.py"]
以下の場合、このコマンドを実行すると、コンテナはデフォルトの/app/opt/main.pyを実行する代わりに、/bin/bashシェルを起動します。
docker run -p 8000:8000 -it apsrver:latest /bin/bash
ENTRYPOINT: エントリーポイントの設定
コンテナが起動した際に実行されるコマンドを指定します。ENTRYPOINTで指定されたコマンドは固定されており、コマンドライン引数で上書きされません。
ENTRYPOINT ["./docker-entrypoint"]
#!/usr/bin/env bash
set -euo pipefail
function tmp_file_exits() {
if [ "$(ls -A /tmp/nitro/)" ]; then
rm /tmp/nitro/*
echo "delete successful tmp file"
else
echo "file doesn't exits"
fi
}
function main() {
tmp_file_exits
npm run dev
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
参考文献
3. ベストプラクティスとセキュリティ
Dockerfileの整理と可読性
Dockerfileの整理と可読性を上げると、メンテナンス性が向上し、チームメンバー全員の理解度が上がります。以下はそのための基本的なポイントです。
- コメントを活用する:Dockerfile内の各セクションやコマンドについての説明をコメントとして追加します。これにより、他の開発者が意図を理解しやすくなります
- 適切な命名規則:変数やディレクトリ名は、意味が明確で一貫性のある名前を使用します
- コマンドの分離:複数の操作を一つのRUNコマンドでまとめず、適切に分けることで可読性を高めます。ただし、これはレイヤー数とトレードオフになります
レイヤーの最適化
Dockerイメージはレイヤーを重ねた構造で、各命令(RUN、COPY、ADDなど)ごとに新しいレイヤーが生成されます。このレイヤーを最適化することで、イメージの軽量化とビルド時間の短縮を実現できます。
- 不要なファイルを含めない:Dockerイメージを小さくするには、不要なファイルを持ち込まないことが重要です。.dockerignoreというファイルを使って、コピーしたくないファイルやディレクトリを指定することで、無駄なファイルがイメージに含まれるのを防ぎましょう
- RUNコマンドの統合:関連する操作は一つのRUNコマンドにまとめます。例えば、複数のパッケージをインストールし、キャッシュをクリアする場合、一つのRUNコマンドにまとめて書くことで、不要なレイヤーを減らせます
悪い例
RUN apt-get update
RUN apt-get install -y --no-install-recommends wget=1.21.3-1+b2
RUN apt-get -y clean
RUN rm -rf /var/lib/apt/lists/*
良い例
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
wget=1.21.3-1+b2 \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/*
ビルドキャッシュの活用
Dockerはビルド時にキャッシュを利用します。これを有効に活用することで、ビルド時間を大幅に短縮できます。
- 頻繁に変わらない部分を上部に配置:Dockerfileの上部にキャッシュを利用できる部分(例:基盤イメージの取得や共通ライブラリのインストール)を配置し、頻繁に変更される部分(例:ソースコードの追加)は下部に配置します
FROM python:3.11.9-slim-bookworm(頻繁に変わらない)
WORKDIR /app(頻繁に変わらない)
COPY requirements.txt ./(頻繁に変わらない)
RUN pip install --no-cache-dir -r requirements.txt(頻繁に変わらない)
COPY . .(頻繁に変わる)
マルチステージビルド
マルチステージビルドを使用することで、最終的なイメージに不要なビルドツールや中間生成物を含めず、より小さくセキュアなイメージを作成できます。
# 開発用ステージ
FROM python:3.11.9-slim-bookworm AS developer
# ビルドステージ
FROM python:3.11.9-slim-bookworm AS builder
# 本番ステージ
FROM gcr.io/distroless/python3-debian12:nonroot AS production
Docker Composeの利用
Docker Composeは、マルチコンテナのDockerアプリケーションを定義して実行するためのツールです。複数のサービス(コンテナ)をまとめて管理でき、開発環境を簡単に構築し、実行することができます。
services:
api:
restart: always
build:
context: ./apserver
target: developer
container_name: "api_ec_site"
environment:
DATABASE_NAME: ecsiteproject
DATABASE_USER: pythonMySql1
DATABASE_PASSWORD: pythonMySql1
DATABASE_HOST: db
DATABASE_PORT: 3306
DEBUG_MODE: "true"
command: ["python", "opt/main.py"]
volumes:
- ./apserver:/app
tty: true
ports:
- 8000:8000
ボリュームを使ったホットリロード
開発効率をさらに向上させるために、ボリュームを使ったホットリロードを利用することができます。ホットリロードとは、ソースコードの変更をリアルタイムで反映させる機能です。これにより、コードを変更するたびにコンテナを再起動する必要がなくなります。
volumes:
- ./apserver:/app
lintツールを使う
Dockerfileの品質を維持することは、Dockerイメージのビルドやデプロイの成功に不可欠です。hadolintを利用することで、Dockerfile内の問題を事前に発見し、品質を向上させることができます。
セキュリティ対策
Dockerイメージのセキュリティを確保するためのベストプラクティスも重要です。
- 公式イメージの使用:可能な限り公式のベースイメージを使用し、信頼性を確保します
- 不要なパッケージをインストールしない:最小限のパッケージのみをインストールし、攻撃対象領域を減らします
- 脆弱性スキャン:定期的にイメージの脆弱性スキャンを行い、セキュリティリスクを低減します。例えば、ClairやTrivyなどのツールを使用します
- 最小権限の原則:コンテナ内で実行されるプロセスは、必要最小限の権限で実行するようにします。例えば、ユーザーをrootではなく専用の非特権ユーザーに変更します
詳しくは以下の記事を参考にして下さい。
4. PythonアプリケーションのDockerfile作成
ベースイメージの選択
まず、Pythonアプリケーションを実行するために適切なベースイメージを選択する必要があります。Pythonアプリケーションを構築する場合、公式のPythonイメージが最適です。最新のPythonイメージを使用することをお勧めします。ベータ版の場合仮想環境の設定がされていないことがあるので注意が必要。
FROM python:3.11.9-slim-bookworm
ベースイメージの違い
- python: Pythonのデフォルトのベースイメージです。最新の安定版が含まれています
- python:alpine: Alpine Linuxは軽量かつセキュアなLinuxディストリビューションで、Dockerイメージのサイズを最小限に抑えることができます
- python:slim: DebianベースのPythonイメージのスリム版です
- python:buster: Debian BusterをベースにしたPythonイメージです。Debianは安定性と広範なパッケージのサポートを提供し、Python開発に必要な多くの追加パッケージが含まれています
- bookworm: bookwormイメージは、最新のDebianテスト版を使用しており、最新の機能やパッケージにアクセスできますが、安定版よりも安定性に欠ける可能性があります
- bullseye: bullseyeイメージは、Debianのテスト版であり、安定性と将来の互換性を考慮しています
- distroless: Distrolessイメージは、ディストリビューションレス(Distroless)として知られる、非常に軽量でセキュアなDockerイメージです。これらのイメージには、基本的なOSパッケージが含まれていません
必要な依存関係のインストール
アプリケーションが依存するパッケージやライブラリをインストールするために、pipを使用します。
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
requirements.txtは、Pythonアプリケーションが必要とするすべてのパッケージを含むテキストファイルです。このファイルは、pipを使用してインストールできます。
Flask==3.0.3
Flask-Cors==4.0.1
アプリケーションコードの追加
アプリケーションのコードをDockerイメージに追加します。
COPY . .
実行コマンドの設定
Dockerコンテナが起動したときに実行されるコマンドを指定します。
CMD ["python", "opt/main.py"]
環境変数の設定
アプリケーションに必要な環境変数がある場合は、Dockerfile内で設定することができます。
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
環境変数の例は以下の通りです。
- PYTHONDONTWRITEBYTECODE: この環境変数は、Pythonがバイトコード(.pycファイル)を生成しないようにします。それにより、ディレクトリが綺麗なままになります
- PYTHONUNBUFFERED: この環境変数は、Pythonの標準出力と標準エラー出力をバッファリングせずに直ちに出力するように指示します。これにより、Pythonプログラムの出力がリアルタイムに表示されます
- PIP_NO_CACHE_DIR: この環境変数は、pipがキャッシュディレクトリを使用しないようにします。キャッシュディレクトリは、pipがパッケージをダウンロードしてキャッシュする場所です。この変数を設定すると、pipはキャッシュを無視し、常に最新のパッケージをダウンロードします
- PIP_DISABLE_PIP_VERSION_CHECK: この環境変数は、pipが自身のバージョンをチェックしないようにします。通常、pipは実行時に自身のバージョンをチェックし、新しいバージョンが利用可能かどうかを確認します。しかし、この変数を設定すると、そのチェックを無効にします
- PYTHONPATH: Pythonがモジュールを検索するパスを指定します。特定のディレクトリを指定することで、Pythonがそのディレクトリ内のモジュールをインポートできるようにします。
参考文献
Pythonコードの実行
これでDockerfileが完成しました。
# 開発用ステージ
FROM python:3.11.9-slim-bookworm AS developer
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
wget=1.21.3-1+b2 \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# ビルドステージ
FROM python:3.11.9-slim-bookworm AS builder
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 本番ステージ
FROM gcr.io/distroless/python3-debian12:nonroot AS production
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11 /usr/local/lib/python3.11
ENV PYTHONPATH=/usr/local/lib/python3.11/site-packages
COPY --from=builder /app/ .
EXPOSE 8000
USER nonroot
CMD ["/app/opt/main.py"]
Flask==3.0.3
Flask-Cors==4.0.1
PyMySQL==1.1.1
Jinja2==3.1.4
requests==2.32.3
SQLAlchemy==2.0.30
Flask-SQLAlchemy==3.1.1
pytest==8.2.2
pytest-mock==3.14.0
pytz==2024.1
bcrypt==4.1.3
# Git
.git
.gitignore
.gitattributes
# Docker
docker-compose.yml
compose.yaml
Dockerfile
.dockerignore
# Byte-compiled / optimized / DLL files
**/__pycache__/
**/*.py[cod]
# PyInstaller
*.manifest
*.spec
# Virtual environment
.env
.venv/
venv/
# VS Code
.vscode/
# macOS
.DS_Store
.AppleDouble
.LSOverride
# macOS icon file
Icon?
._*
# logfile
*.log
# cache file
*.cache/
*.pytest_cache/
*.coverage
以上です。