Contents
1. プロジェクト構成と依存管理
📌 ポイント
- 明確なディレクトリ構造+Poetry による単一ソース(
pyproject.toml)の依存管理で、保守性・テスト容易性を最大化する。
📂 推奨ディレクトリレイアウト
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
myservice/ ├─ src/ # アプリケーションコード │ └─ myservice/ │ ├─ __init__.py │ ├─ main.py # FastAPI エントリポイント │ └─ api/ │ ├─ __init__.py │ └─ v1.py ├─ tests/ # ユニットテスト │ └─ test_main.py ├─ pyproject.toml # Poetry 設定ファイル(依存・ビルド情報) ├─ poetry.lock # 正確なバージョンロック └─ .dockerignore # Docker ビルド除外リスト |
pyproject.toml の抜粋(バージョンは 変数 で管理)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[tool.poetry] name = "myservice" version = "0.1.0" description = "Python microservice example" authors = ["Your Name <you@example.com>"] packages = [{include = "myservice", from = "src"}] [tool.poetry.dependencies] python = "^3.11" fastapi = "^{{ fastapi_version }}" # ← CI で自動更新 uvicorn = {extras = ["standard"], version = "^{{ uvicorn_version }}"} # 必要に応じて追加パッケージをここへ [tool.poetry.dev-dependencies] pytest = "^{{ pytest_version }}" |
※ バージョンは
{{ xxx_version }}のようにテンプレート化し、
Dependabot や Renovate と連携させることで定期的に最新安定版へ自動更新できます(後述)。
Poetry での操作例
|
1 2 3 4 5 6 |
# 依存関係をロックファイルに書き込みつつインストール poetry install # 仮想環境をアクティベートしてテスト実行 poetry run pytest |
2. Dockerfile の安全・軽量設計
📌 ポイント
- BuildKit + マルチステージビルドで、ビルド時のツールやキャッシュを本番イメージに持ち込まない。
site‑packagesディレクトリ全体をコピーせず、requirements.txtから再インストールすることで不要ファイルを除外。
改訂版 Dockerfile
|
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 |
# syntax = docker/dockerfile:1.4 ############################ # ① ビルドステージ (builder) ############################ FROM python:3.11-slim AS builder ARG POETRY_VERSION=1.8.2 # 必要に応じて変数化 # Poetry のインストール(キャッシュマウントで高速化) RUN --mount=type=cache,target=/root/.cache \ pip install --no-cache-dir "poetry==$POETRY_VERSION" WORKDIR /app COPY pyproject.toml poetry.lock ./ # 依存関係を requirements.txt に変換(ハッシュ除去で再現性確保) RUN --mount=type=cache,target=/root/.cache \ poetry export -f requirements.txt --without-hashes -o requirements.txt ############################ # ② ランタイムステージ (runtime) ############################ FROM python:3.11-slim AS runtime ARG USER_ID=10001 ENV PYTHONUNBUFFERED=1 # 非 root ユーザー作成 RUN addgroup --system app && \ adduser --system --uid $USER_ID --ingroup app appuser WORKDIR /app # ビルドステージで生成した requirements.txt をコピーし、再インストールだけ実行 COPY --from=builder /app/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # アプリコードのみをコピー COPY src/ ./src/ USER appuser EXPOSE 8080 CMD ["uvicorn", "myservice.main:app", "--host", "0.0.0.0", "--port", "8080"] |
主な改善点
| 項目 | 従来の実装 | 改訂版 |
|---|---|---|
| site‑packages のコピー | COPY --from=builder /usr/local/lib/python3.11/site-packages/ … |
依存リスト (requirements.txt) を再インストールし、ビルドキャッシュは残さない |
| 不要ファイル | ビルダーのキャッシュが混入する可能性あり | --no-cache-dir とマウントキャッシュでビルド時のみ保持 |
| ユーザー権限 | USER appuser はそのままだが、作成手順が明示的に分離 |
同上だがコメントで意図を明記 |
3. ローカル開発環境(Compose & DevContainer)
📌 ポイント
- Docker Compose v2 によるネットワーク・ヘルスチェックの自動管理。
- VS Code の DevContainer でローカルと CI が同一イメージを使用でき、開発者ごとの環境差異が解消。
docker-compose.yml
|
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 |
version: "3.9" services: api: build: context: . target: runtime # Compose でもマルチステージ活用 ports: - "8080:8080" env_file: - .env.api depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 5s retries: 3 db: image: postgres:15-alpine environment: POSTGRES_USER: myuser POSTGRES_DB: mydb volumes: - pg_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"] interval: 15s timeout: 5s retries: 5 volumes: pg_data: |
DevContainer 設定 (.devcontainer/devcontainer.json)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "name": "Python Microservice (DevContainer)", "dockerComposeFile": ["../docker-compose.yml"], "service": "api", "workspaceFolder": "/workspaces/myservice", "remoteUser": "appuser", "features": { "ghcr.io/devcontainers/features/python:1": { "version": "3.11" } }, "postCreateCommand": "poetry install" } |
Tip
docker-compose.ymlと同じ Dockerfile(マルチステージ)を利用しているため、ローカルでも CI と同様のイメージが生成されます。
4. CI/CD パイプライン(GitHub Actions)
📌 ポイント
- BuildKit、Trivy による脆弱性診断、テスト実行、GHCR へのプッシュを一連のジョブで完結。
- バージョン固定・再現性確保のため、外部 Action はタグまたは SHA‑1 でロック。
dependabot.yml(自動バージョン更新)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
version: 2 updates: - package-ecosystem: "pip" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" |
.github/workflows/ci-cd.yml
|
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
name: CI / CD on: push: branches: [ main ] pull_request: permissions: contents: read packages: write id-token: write # OIDC for cloud providers jobs: build-test: runs-on: ubuntu-latest env: DOCKER_BUILDKIT: 1 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up QEMU (multi‑arch) uses: docker/setup-qemu-action@v3 # ---------- Docker Hub / GHCR ログイン ---------- - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # ---------- ビルド(キャッシュ活用) ---------- - name: Build Docker image (runtime target) run: | docker build \ --target runtime \ -t ghcr.io/${{ github.repository }}:${{ github.sha }} \ . # ---------- コンテナ内でユニットテスト ---------- - name: Run tests in container run: | docker run --rm ghcr.io/${{ github.repository }}:${{ github.sha }} pytest # ---------- Trivy スキャン(安定タグでロック) ---------- - name: Scan image with Trivy uses: aquasecurity/trivy-action@v0.12.0 # ← 固定バージョン with: image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }} format: table exit-code: "1" # 高/クリティカルで失敗させる severity: "HIGH,CRITICAL" ignore-unfixed: true # ---------- イメージを GHCR にプッシュ ---------- - name: Push image to GHCR run: | docker push ghcr.io/${{ github.repository }}:${{ github.sha }} deploy: needs: build-test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Checkout repository (for deployment scripts) uses: actions/checkout@v4 # ---------- AWS ECS/Fargate ---------- - name: Deploy to AWS ECS/Fargate uses: aws-actions/amazon-ecs-deploy-task-definition@v2 with: task-definition: ecs/task-def.json service: myservice cluster: prod-cluster image: ghcr.io/${{ github.repository }}:${{ github.sha }} wait-for-service-stability: true # ---------- Azure Container Apps ---------- - name: Deploy to Azure Container Apps uses: azure/container-apps-deploy@v1 # 正式アクション名 with: resource-group: rg-prod containerapp-name: myservice image: ghcr.io/${{ github.repository }}:${{ github.sha }} ingress: external target-port: 8080 # ---------- GCP Cloud Run ---------- - name: Deploy to Cloud Run uses: google-github-actions/deploy-cloudrun@v0 with: service: myservice image: ghcr.io/${{ github.repository }}:${{ github.sha }} region: asia-northeast1 |
主な改善点
| 項目 | 従来の記述 | 改訂版 |
|---|---|---|
| Trivy Action | aquasecurity/trivy-action@master(ブランチ指定) |
aquasecurity/trivy-action@v0.12.0(安定タグ) |
| Azure デプロイ | azure/aci-deploy@v1(旧名称・誤記) |
azure/container-apps-deploy@v1(正式) |
| バージョン固定 | FastAPI・Uvicorn のハードコード | {{ fastapi_version }} 等テンプレート化し Dependabot が自動更新 |
| 再現性 | 依存はロックされているが Action バージョン未固定 | 全 Action にタグ/SHA を明示 |
5. バージョン管理と自動更新の仕組み
📌 なぜ「バージョンを固定」しつつ「定期的に最新へ」?
- 再現性:CI が同一コード・依存で常に同じイメージを生成できる。
- セキュリティ:脆弱性が修正された新バージョンは速やかに取り込める。
実装例
- Poetry のバージョン変数化
pyproject.toml内の依存は{{ fastapi_version }}などプレースホルダーで記述。 - GitHub Actions のテンプレート置換
yaml -
name: Render version placeholders
id: render
run: |
FASTAPI=$(curl -s https://pypi.org/pypi/fastapi/json | jq -r '.info.version')
UVICORN=$(curl -s https://pypi.org/pypi/uvicorn/json | jq -r '.info.version')
echo "fastapi_version=$FASTAPI" >> $GITHUB_OUTPUT
echo "uvicorn_version=$UVICORN" >> $GITHUB_OUTPUT
env
その後に注入し、sed等でpyproject.toml を書き換えてビルド。 -
Dependabot の活用
- 上記
dependabot.ymlが自動的に PR を作成。 -
CI がマージ前にテスト・スキャンを走らせ、問題なければ自動マージ(
auto-merge: true)も設定可能。 -
GitHub Actions のバージョンロック
yaml
uses: docker/setup-qemu-action@v3 # タグで固定
uses: aquasecurity/trivy-action@v0.12.0 # タグまたは SHA-1
6. 主要クラウドへのデプロイ比較
| 項目 | AWS ECS/Fargate | Azure Container Apps | GCP Cloud Run |
|---|---|---|---|
| CLI / Action | aws-actions/amazon-ecs-deploy-task-definition@v2 |
azure/container-apps-deploy@v1(正式) |
google-github-actions/deploy-cloudrun@v0 |
| デプロイ単位 | タスク定義 + サービス | コンテナアプリ+環境 | Service (Revision) |
| シークレット管理 | AWS Secrets Manager / Parameter Store → ECS task definition | Azure Key Vault → env vars(container‑app) | Secret Manager → env vars |
| 料金モデル | vCPU・メモリ使用量に従量課金 | 実行時間とリソース予約で従量課金 | リクエスト数+実行時間で従量課金 |
| 主な設定ファイル例 | ecs/task-def.json(JSON) |
az containerapp env create …(CLI) |
gcloud run deploy …(CLI) |
デプロイ手順の共通パターン
- コンテナレジストリにプッシュ → GHCR / ECR / ACR など
- GitHub Actions のステップで対象クラウド用 CLI/Action を呼び出す
- 環境変数・シークレットは各クラウドのマネージドサービスから注入
ポイント:同一ワークフロー内に
if: contains(matrix.cloud, 'aws')等で分岐させれば、1 つの YAML ですべてのプロバイダーへデプロイ可能。
7. 運用・監視・セキュリティの基本設定
📌 ロギングとメトリクス(OpenTelemetry + Prometheus)
docker-compose.yml に追加
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
otel-collector: image: otel/opentelemetry-collector-contrib:0.93.0 command: ["--config", "/etc/otel-config.yaml"] volumes: - ./otel/otel-config.yaml:/etc/otel-config.yaml prometheus: image: prom/prometheus:v2.50.1 ports: - "9090:9090" volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml |
otel/otel-config.yaml(抜粋)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
receivers: otlp: protocols: grpc: exporters: prometheus: endpoint: "0.0.0.0:9464" service: pipelines: metrics: receivers: [otlp] exporters: [prometheus] |
アプリ側依存とコード例
|
1 2 |
poetry add opentelemetry-sdk opentelemetry-exporter-otlp |
src/myservice/metrics.py
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from opentelemetry import metrics from opentelemetry.exporter.otlp.proto.grpc.metrics_exporter import OTLPMetricExporter from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader exporter = OTLPMetricExporter(endpoint="http://otel-collector:4317") reader = PeriodicExportingMetricReader(exporter, export_interval_millis=5_000) provider = MeterProvider(metric_readers=[reader]) metrics.set_meter_provider(provider) meter = metrics.get_meter(__name__) request_counter = meter.create_counter( "api_requests_total", description="Total number of API requests" ) def record_request(endpoint: str): request_counter.add(1, {"endpoint": endpoint}) |
📌 非 root 実行とイメージスキャン
- Dockerfile で
USER appuserを必ず設定(上記参照)。 - CI の Trivy スキャンは「高・クリティカル」だけ CI を失敗させ、低リスクは issue として残す。
|
1 2 3 4 5 6 7 |
- name: Scan image with Trivy (high/critical only) uses: aquasecurity/trivy-action@v0.12.0 with: image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }} severity: "HIGH,CRITICAL" ignore-unfixed: true |
📌 定期的な脆弱性データベース更新
Trivy はデフォルトで毎日 DB を取得しますが、自前のキャッシュレイヤーを作ることでビルド時間短縮も可能です。
CI の最初に以下を実行すると、GitHub Actions のキャッシュ機能と組み合わせられます。
|
1 2 3 4 5 6 7 8 |
- name: Cache Trivy DB uses: actions/cache@v4 with: path: ~/.cache/trivy key: trivy-db-${{ runner.os }}-${{ hashFiles('**/Dockerfile') }} restore-keys: | trivy-db- |
8. まとめ
| 項目 | ベストプラクティス |
|---|---|
| プロジェクト構造 | src/ 配下にコード、Poetry が唯一の依存管理ツール |
| Dockerfile | BuildKit + マルチステージ → ビルドキャッシュはランタイムに持ち込まない。非 root ユーザーで実行 |
| ローカル開発 | Compose v2 + DevContainer で本番と同一イメージを使用し、ヘルスチェック・ネットワーク自動管理 |
| CI/CD | GitHub Actions にてビルド・テスト・Trivy スキャン・GHCR プッシュ+各クラウドへデプロイ。Action はすべてタグ固定で再現性確保 |
| バージョン更新 | Dependabot + {{ xxx_version }} テンプレート → 定期的に PR が作成され、マージすると自動で最新ライブラリになる |
| クラウドデプロイ比較 | AWS/ECS‑Fargate・Azure Container Apps・GCP Cloud Run を同一ワークフローで切り替え可能 |
| 観測性 & セキュリティ | OpenTelemetry → Prometheus、非 root コンテナ、Trivy 高リスクスキャン、GitHub Secrets/クラウド KMS でシークレット管理 |
このガイドラインを自プロジェクトに取り込むことで、「コードの書きやすさ」⇔「本番運用の堅牢性」 の両輪が揃い、開発スピードと安全性を同時に向上できます。
次のステップ
1. 本リポジトリでdependabot.ymlを有効化 → 依存自動更新を開始。
2. Dockerfile と CI のバージョン固定が完了したら、GitHub Actions のワークフロー実行で一度ビルド・テスト・スキャンを走らせる。
3. 任意のクラウド(AWS / Azure / GCP)へデプロイし、OpenTelemetry ダッシュボードでメトリクスが可視化されていることを確認。
以上、2026 年時点の 最新・安全・再現性 を担保した Python マイクロサービス構築手順でした。 🚀