Contents
Flask アプリの基本構成とローカル動作確認
このセクションでは、Docker 化に入る前段階として Flask アプリがローカルで正しく起動するか を確認します。最小限のファイルだけで構成できれば、後続のコンテナビルドや CI/CD の設計がシンプルになります。まずはディレクトリ構造と必須コードを用意し、python -m flask run が問題なく動作することを確かめましょう。
プロジェクトディレクトリ構成
以下のようなシンプルなレイアウトを目安にしてください。ファイルが少ないほどビルドコンテキストが小さくなり、Docker イメージのキャッシュ効率も向上します。
|
1 2 3 4 5 6 7 8 9 |
my_flask_app/ ├─ app.py ├─ requirements.txt ├─ .env # 開発用環境変数(本番には含めません) ├─ static/ │ └─ style.css └─ templates/ └─ index.html |
app.py と requirements.txt の作成
ポイント:アプリ本体は app.py、依存パッケージは requirements.txt に列挙します。
理由:Docker ビルド時に pip install -r requirements.txt を実行すれば、環境差異がなく確実に同一ライブラリがインストールされます。
app.py
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from flask import Flask, render_template import os app = Flask(__name__) @app.route("/") def index(): # .env で設定した GREETING があれば使用、なければデフォルト文字列 greeting = os.getenv("GREETING", "Hello") return render_template("index.html", message=greeting) if __name__ == "__main__": # ホスト全体にバインドし、ポートは環境変数で上書き可能にする app.run(host="0.0.0.0", port=int(os.getenv("PORT", 5000))) |
requirements.txt
|
1 2 3 |
Flask>=3.0,<4 python-dotenv>=1.0 |
静的資産とテンプレートの役割
- static ディレクトリ:CSS・JavaScript・画像など、クライアントに直接配信するファイルを格納します。Flask は自動的に
/staticパスで提供します。 - templates ディレクトリ:Jinja2 形式の HTML テンプレートを置き、
render_templateから呼び出すことで動的ページが生成されます。
重要:これらのディレクトリは Docker イメージに必ずコピーする対象です。
.dockerignoreに誤って除外しないよう注意してください。
Docker 化の実装 ― Dockerfile と .dockerignore のベストプラクティス
この章では、Flask アプリを 軽量かつ高速にビルド できる Dockerfile を作成します。加えて、ビルドコンテキストを最小化する .dockerignore の設定ポイントと、キャッシュ機能を有効にする前提条件についても解説します。
BuildKit の有効化方法
--mount=type=cache は Docker BuildKit がオンになっている場合のみ利用可能です。Docker デーモンの設定や CLI オプションで以下のいずれかを実行してください。
|
1 2 3 4 5 6 |
# 1. 環境変数で一時的に有効化(推奨) export DOCKER_BUILDKIT=1 # 2. Docker Desktop の Settings → Docker Engine に "features": {"buildkit": true} を追加 # 3. daemon.json に { "features": { "buildkit": true } } を記載して再起動 |
Tip:CI 環境(GitHub Actions 等)でも
DOCKER_BUILDKIT=1を環境変数として設定すれば、同様にキャッシュマウントが利用できます。
マルチステージ Dockerfile の構成
以下は Python 3.12‑slim イメージをベースにした ビルドステージ と ランタイムステージ を分離した例です。コメントで各行の意図を明示していますので、プロジェクト固有の要件に合わせてカスタマイズしてください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# -------------------------------------------------------------- # ① ビルドステージ(依存関係インストール専用) # -------------------------------------------------------------- FROM python:3.12-slim-bullseye AS builder # BuildKit のキャッシュディレクトリを利用して pip の再実行を高速化 RUN --mount=type=cache,target=/root/.cache \ pip install --upgrade pip setuptools wheel WORKDIR /app COPY requirements.txt . # 依存パッケージのインストール。--no-cache-dir とキャッシュマウントで最適化 RUN --mount=type=cache,target=/root/.cache \ pip install --no-cache-dir -r requirements.txt # -------------------------------------------------------------- # ② ランタイムステージ(実行環境はできるだけ小さく) # -------------------------------------------------------------- FROM python:3.12-slim-bullseye AS runtime ENV PYTHONUNBUFFERED=1 \ FLASK_APP=app.py \ PORT=5000 WORKDIR /app COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY . . # 非特権ユーザーを作成し、セキュリティリスクを低減 RUN groupadd -r app && useradd -r -g app app \ && chown -R app:app /app USER app EXPOSE 5000 CMD ["flask", "run", "--host=0.0.0.0"] |
主な改善ポイント
| 項目 | 従来の記述 | 改善後の記述 |
|---|---|---|
| キャッシュ活用 | pip install のみ |
--mount=type=cache で pip キャッシュを共有 |
| イメージサイズ | ビルド依存がランタイムに残る | マルチステージで最小化 |
| 実行ユーザー | デフォルトの root | 非特権 app ユーザーで実行 |
正しい .dockerignore の設定例
.dockerignore はビルドコンテキストを削減し、機密情報がイメージに混入するリスクを防ぎます。Dockerfile 自体は除外しないことが最重要です(除外するとビルド時にファイルが見つからずエラーになります)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# Python のキャッシュやバイトコード __pycache__/ *.py[cod] # ローカル環境変数・シークレット .env # バージョン管理関連 .git .gitignore # 開発用仮想環境 venv/ .idea/ # ドキュメント類(イメージに不要なもの) *.md |
注意:
Dockerfileとdocker-compose.ymlは除外リストに入れないようにしてください。これでビルドエラーを防げます。
ローカル開発環境の構築 ― Docker Compose の活用とセキュリティ配慮
この章では、Web アプリ本体 + PostgreSQL + Redis を一括起動できる docker-compose.yml を作成します。特に本番環境で不要なポート公開を防ぐ設定や、Compose が提供するネットワーク分離の利点についても触れます。
Docker Compose の基本コマンドとビルドフロー
Dockerfile が完成したら、次の手順でローカルイメージを作成しコンテナを起動します。開発中はコード変更だけで再ビルドが高速に行える点が大きなメリットです。
|
1 2 3 4 5 6 7 8 9 |
# 1. ビルド(タグは任意) docker build -t myflask:latest . # 2. 単体コンテナの起動例 docker run -d --name flask_app \ -p 5000:5000 \ --env-file .env \ myflask:latest |
docker compose up -d を利用すれば、データベースやキャッシュサーバーと同時に起動でき、開発フローがさらにシンプルになります。
Docker Compose ファイル(本番志向の設定)
以下は Web・PostgreSQL・Redis の 3 サービスを定義した例です。Redis は外部から直接アクセスさせないように expose のみでポート公開を抑制しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
version: "3.9" services: web: build: . ports: - "5000:5000" # ホスト側へ公開するポートは Web のみ env_file: - .env # 開発用環境変数。実運用ではシークレットに置き換える depends_on: - db - redis db: image: postgres:16-alpine environment: POSTGRES_USER: flask_user POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: flask_db volumes: - pg_data:/var/lib/postgresql/data # データ永続化はボリュームで管理 redis: image: redis:7-alpine expose: - "6379" # コンテナ間通信のみ許可、ホストへの公開はしない # 必要に応じてメモリ制限や永続化設定を追加可能 volumes: pg_data: |
変更点の解説
- Redis のポート非公開:
ports:を削除しexpose:に置き換えることで、同一 Compose ネットワーク内からのみアクセスできるようにしています。これにより本番環境で不必要な外部露出を防げます。 - シークレットの扱い:
POSTGRES_PASSWORDは.envではなく GitHub Actions の Secrets やクラウドシークレットストアから注入することが推奨です(後述の CI/CD 節参照)。
本番デプロイと自動化 ― CI/CD パイプラインの構築
本セクションでは、Docker 化した Flask アプリを 主要クラウドサービス にデプロイする手順と、GitHub Actions を用いた完全自動化フローを示します。ブランドトーンに合わせ、読者が「すぐにでも試せる」感覚を得られるよう具体的なコード例とベストプラクティスを交えて解説します。
推奨デプロイ先と選定基準(2026 年版)
| サービス | 主な特長 | 典型的なユースケース |
|---|---|---|
| AWS ECS/Fargate | 完全マネージド、サーバーレス実行、ECR 連携がシームレス | 高トラフィック・自動スケーリングが必要な API |
| Google Cloud Run | リクエスト単位課金、Zero‑Config デプロイ、Knative ベース | 短時間のスパイクやイベントドリブン処理 |
| Azure Container Apps | Dapr 統合・マイクロサービス指向、柔軟なスケール設定 | Azure エコシステムに統合された社内システム |
| Heroku Docker Deploy | CLI が直感的でセットアップが簡単、無料プランあり | プロトタイプや PoC 迅速構築 |
選定ポイント:運用コスト、スケーラビリティ要件、既存インフラとの親和性を総合評価し、最適なサービスを選びましょう。
GitHub Actions によるマルチプラットフォームビルド & デプロイ
以下は GitHub Container Registry へイメージをプッシュし、その後 Google Cloud Run へデプロイするワークフローです。AWS や Azure 用に差し替えるだけで同様に利用できます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
name: CI / CD on: push: branches: [ main ] pull_request: types: [ opened, synchronize ] env: IMAGE_NAME: ghcr.io/${{ github.repository }}:latest jobs: build-and-push: runs-on: ubuntu-latest permissions: packages: write # Container Registry への書き込み権限 contents: read steps: - name: Checkout repository uses: actions/checkout@v4 # BuildKit が有効な環境でマルチプラットフォームビルドを実行 - name: Set up QEMU (multi‑arch support) uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ env.IMAGE_NAME }} # BuildKit のキャッシュマウントを有効化 build-args: | DOCKER_BUILDKIT=1 deploy-to-cloudrun: needs: build-and-push runs-on: ubuntu-latest environment: production steps: - name: Deploy to Cloud Run uses: google-github-actions/deploy-cloudrun@v2 with: service: flask-app image: ${{ env.IMAGE_NAME }} region: us-central1 env: GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GCP_SA_KEY }} |
重要ポイント
- BuildKit の有効化:
docker/setup-buildx-actionが自動で BuildKit を利用しますが、明示的にDOCKER_BUILDKIT=1を渡すと確実です。 - マルチプラットフォーム対応:
setup-qemu-actionにより ARM と x86 の両方のイメージを同時生成できます。 - シークレット管理:クラウド認証情報は GitHub Secrets に格納し、ワークフロー内で環境変数として注入します。
環境変数・シークレットの安全な取り扱い
| 方法 | メリット | デメリット |
|---|---|---|
.env(ローカル限定) |
手軽に設定可能、開発者間で共有しやすい | 本番イメージに含めると情報漏洩リスク |
| Docker secret(Swarm/ECS/Fargate) | コンテナ起動時に暗号化されたファイルとしてマウント | 初期設定がやや手間 |
| クラウドシークレットストア(AWS Secrets Manager・GCP Secret Manager・Azure Key Vault) | 完全マネージド、ローテーション機能あり | 追加コストが発生 |
実運用では必ず Docker secret または各クラウドのシークレットサービスを利用し、.env は開発環境だけに留める方針を徹底してください。
運用・モニタリング ― ログとメトリクスで可視化
本番運用では ログ と メトリクス の二層構造が不可欠です。Docker が標準出力に書き込むだけで、Grafana Loki や Prometheus に取り込み可能になる仕組みを紹介します。
標準出力ベースのロギングと Loki 連携
- Flask 側:
logging.StreamHandlerを使用し JSON 形式で stdout に出力。 - Compose にサイドカー:
grafana/loki:2.9コンテナを追加し、webサービスのロギングドライバをjson-fileに設定。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
services: web: ... logging: driver: "json-file" options: max-size: "10m" max-file: "3" loki: image: grafana/loki:2.9 ports: - "3100:3100" |
Prometheus Exporter の導入
prometheus-flask-exporter をインストールし、/metrics エンドポイントでリアルタイムのリクエスト数やレイテンシを取得します。
|
1 2 |
pip install prometheus-flask-exporter |
|
1 2 3 4 5 |
from prometheus_flask_exporter import PrometheusMetrics app = Flask(__name__) metrics = PrometheusMetrics(app) # 自動で /metrics を公開 |
Grafana に Loki と Prometheus のデータソースを登録すれば、ログ検索とメトリクスグラフが同一ダッシュボードに統合され、障害解析が格段に速くなります。
典型的な障害シナリオと対策チェックリスト
| 障害 | 主因 | 推奨対策 |
|---|---|---|
| ポート競合 | ホスト側で同一ポート使用中 | docker ps・lsof -i :5000 で確認し、Compose の ports: を別に割り当てる |
| ファイル権限エラー | 非特権ユーザーが書き込み不可 | Dockerfile の RUN chown -R app:app /app と、ボリュームマウント時の UID/GID 統一 |
| キャッシュが古い | requirements.txt 変更後にキャッシュが残存 |
--no-cache ビルドやレイヤー順序見直しで再取得を保証 |
| 環境変数未設定 | .env がマウントされていない |
docker compose up --env-file .env、または Secrets へ移行 |
| DB 接続失敗 | 起動順序やネットワーク遅延 | depends_on + アプリ側のリトライロジック(例: tenacity) |
デプロイ直後の確認項目
- コンテナが期待ポートでリスニングしているか (
docker ps) - 環境変数が正しく注入されているか (
docker exec web env | grep VAR_NAME) - Loki にログが流れているか(Grafana のクエリ実行)
/metricsが 200 を返すか (curl http://localhost:5000/metrics)- DB 接続テストが成功するか(アプリ起動後のヘルスチェック)
まとめ
- ローカルでの確認:
app.py,requirements.txt,static/,templates/の最小構成でまずは Flask が動くことを検証します。 - Dockerfile のベストプラクティス:マルチステージビルド、BuildKit キャッシュ、非特権ユーザー実行によりイメージサイズとセキュリティを最適化しました。
.dockerignoreからはDockerfileを除外しないよう修正しています。 - ローカル開発環境:
docker compose up -dによって Web、PostgreSQL、Redis の統合スタックが数分で立ち上がります。Redis はポート非公開にして本番リスクを低減しました。 - 本番デプロイと自動化:AWS/ECS, GCP/Cloud Run, Azure Container Apps から選択し、GitHub Actions によるマルチプラットフォームビルド・シークレット管理で CI/CD を実現します。
- 運用・モニタリング:標準出力+Grafana Loki/Prometheus の構成でログとメトリクスを一元可視化し、典型的な障害に対するチェックリストで迅速復旧が可能です。
これらの手順とベストプラクティスに沿って実装すれば、安全・高速・拡張性の高い Flask コンテナデプロイ が実現できます。ぜひ本記事を参考に、ご自身のプロジェクトで試してみてください!