プロジェクトデプロイとローンチ — 開発から本番へ
製品のローンチはロケットの打ち上げのようなもの - 最初の24レッスンは設計と製造で, このレッスンはカウントダウンと打ち上げです。打ち上げ前には長いチェックリストがあり, すべての項目にチェックが入るまでエンジンに点火できません。チェックリストなしのローンチはギャンブルです。
1. 学ぶ内容
- 本番環境チェックリスト:セキュリティ, パフォーマンス, モニタリングの3次元評価
- 段階的ロールアウト戦略:ブルーグリーンデプロイとカナリアデプロイの実装アプローチ
- データベースマイグレーションの安全性:ゼロダウンタイムマイグレーションのためのAlembicベストプラクティス
- ログ集約:構造化ログ + ELK / Loki統合
- Alice:最終章 - PriceTracker正式ローンチ
2. Aliceの実話
(1) 悩み:デプロイ時のインシデントが頻発
PriceTrackerは最初の2回のデプロイで問題が発生しました:1回目はHTTPSの設定忘れ, 2回目はデータベースマイグレーション時のテーブルロックによる10分間のサービス停止, 3回目はログが集約されておらず, 問題発生後にCharlieが5台のサーバーのログを2時間かけて捜索しました。Aliceには, すべてのデプロイが安全で制御可能であることを保証する体系的なデプロイプロセスが必要です。
(2) デプロイチェックリストによる解決策
デプロイは「コードをプッシュして終わり」ではなく, 一連のチェックリストです:セキュリティ強化, パフォーマンス検証, マイグレーション戦略, 段階的ロールアウト, モニタリング検証 - 各項目にチェックが入ってから次のステップに進みます。
(3) 成果
3回目のデプロイ:チェックリストの全項目にチェックが入り, HTTPSが設定され, マイグレーションはゼロダウンタイムで完了, ローリングリリースでまず10%のトラフィックをルーティングして検証し, ログは自動的にLokiに集約 - デプロイプロセス全体が滞りなく完了し, Charlieのモニタリングダッシュボードはずっと緑色のままでした。
3. 本番環境チェックリスト
(1) ローンチプロセス
flowchart TD
A[コードフリーズ] --> B[セキュリティ監査]
B --> C[負荷テスト]
C --> D[Stagingデプロイ]
D --> E[スモークテスト]
E --> F[カナリアリリース 10%]
F --> G{メトリックOK?}
G -->|Yes| H[フルロールアウト 100%]
G -->|No| I[ロールバック]
I --> J[調査]
J --> A
H --> K[1時間監視]
K --> L[✓ Live]
(2) 3次元チェックリスト
| 次元 | チェック項目 | ステータス |
|---|---|---|
| セキュリティ | HTTPS証明書の設定 | ☐ |
| CORSは本番ドメインのみ許可 | ☐ | |
| Rate Limitingの有効化 | ☐ | |
| JWT SECRET_KEYの変更 | ☐ | |
| ハードコードされたキーの不存在 | ☐ | |
| セキュリティスキャン合格 | ☐ | |
| パフォーマンス | Uvicorn Workers ≥ 4 | ☐ |
| 適切なDBコネクションプールサイズ | ☐ | |
| Redisキャッシュの有効化 | ☐ | |
| GZip圧縮の有効化 | ☐ | |
| 負荷テスト合格 (目標QPS) | ☐ | |
| モニタリング | /healthエンドポイントが正常 | ☐ |
| Prometheusメトリック収集 | ☐ | |
| Grafanaダッシュボード準備完了 | ☐ | |
| アラートルールの設定 | ☐ | |
| ログ集約の稼働 | ☐ |
4. 段階的ロールアウト戦略
(1) ブルーグリーンデプロイ
flowchart LR
LB[Load Balancer] -->|100% traffic| Blue[Blue v1]
subgraph Deploy
Green[Green v2]
end
LB -.->|Switch| Green
style Blue fill:#4caf50
style Green fill:#2196f3
| ステップ | アクション | ロールバック |
|---|---|---|
| 1 | Green (v2)環境をデプロイ | - |
| 2 | Greenでスモークテストを実行 | - |
| 3 | LBをGreenに切り替え | Blueに戻す |
| 4 | Greenを30分間監視 | Blueに戻す |
| 5 | 安定確認後, Blueをオフライン | - |
(1) ▶ サンプル:Nginxブルーグリーン設定
NGINX
# nginx/conf.d/pricetracker.conf
upstream pricetracker_blue {
server api-blue:8000;
}
upstream pricetracker_green {
server api-green:8000;
}
# 現在Blueを配信中
server {
listen 80;
server_name api.pricetracker.example.com;
location / {
proxy_pass http://pricetracker_blue;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# proxy_passのターゲットを変更してGreenに切り替え
# その後:docker compose -f docker-compose.green.yml up -d
出力:
TEXT
// 実行成功
(2) カナリアリリース
| 戦略 | トラフィック配分 | 観察期間 | ロールバック |
|---|---|---|---|
| 10% | 1 v2 + 9 v1 | 15分 | v1に戻す |
| 30% | 3 v2 + 7 v1 | 15分 | v1に戻す |
| 50% | 5 v2 + 5 v1 | 15分 | v1に戻す |
| 100% | 全てv2 | 30分 | イメージバージョンをロールバック |
5. ゼロダウンタイムデータベースマイグレーション
(1) 安全なマイグレーションの原則
| 原則 | 説明 | 例 |
|---|---|---|
| 追加のみ, 削除しない | まず新しいカラムを追加, 古いカラムは削除しない | ALTER TABLE ADD COLUMN new_col |
| デュアルライト互換 | 新旧両方のコードが動作 | 新コードは2つのカラムに書き込み, 旧コードは旧カラムのみ読み取り |
| 遅延クリーンアップ | 安定性確認後にのみ古いカラムを削除 | v2デプロイ7日後に古いカラムを削除 |
| 段階的マイグレーション | 一度に1つだけ変更 | 1つのマイグレーションでカラム追加とデータ型変更を同時にしない |
(1) ▶ サンプル:安全なカラム追加マイグレーション
PYTHON
# alembic/versions/xxx_add_wholesale_price.py
"""productsにwholesale_priceカラムを追加
安全なマイグレーション:ADD COLUMNはPostgreSQLで非ブロッキング
"""
def upgrade():
op.add_column(
"products",
sa.Column("wholesale_price", sa.Float(), nullable=True),
)
# 既存行にデフォルト値を設定 (パフォーマンスのため別ステートメント)
op.execute("UPDATE products SET wholesale_price = base_price * 0.6 WHERE wholesale_price IS NULL")
# コードデプロイ後の次のマイグレーションでNOT NULLにする
def downgrade():
op.drop_column("products", "wholesale_price")
出力:
TEXT
# 関数定義成功
(2) リスクの高いマイグレーション vs 安全なマイグレーション
| マイグレーションタイプ | 安全性 | テーブルロックのリスク | 推奨事項 |
|---|---|---|---|
| ADD COLUMN | 安全 | なし | オンラインで実行可能 |
| CREATE INDEX | 比較的安全 | あり | CREATE INDEX CONCURRENTLYを使用 |
| DROP COLUMN | リスクあり | あり | コードがもう使用していないことを先に確認 |
| ALTER COLUMN TYPE | リスクあり | 高い | 段階的マイグレーション (新カラム追加 → データ移行 → 旧カラム削除) |
| RENAME COLUMN | 危険 | 中程度 | デュアルライト互換を有効にしてからリネーム |
6. ログ集約
(1) 構造化ログ
(1) ▶ サンプル:構造化ログ設定
PYTHON
# app/core/logging.py
import logging
import json
from datetime import datetime
class JSONFormatter(logging.Formatter):
def format(self, record):
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
"function": record.funcName,
"line": record.lineno,
}
# リクエストコンテキストがあれば追加
if hasattr(record, "request_id"):
log_entry["request_id"] = record.request_id
if hasattr(record, "user_id"):
log_entry["user_id"] = record.user_id
return json.dumps(log_entry)
# ログ設定
logger = logging.getLogger("pricetracker")
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)
出力:
TEXT
# 関数定義成功
(2) ログ集約ソリューションの比較
| ソリューション | 利点 | 欠点 | 推奨シナリオ |
|---|---|---|---|
| ELK (Elasticsearch + Logstash + Kibana) | 機能が充実, 強力な検索機能 | リソース消費が大きい | 大規模プロジェクト |
| Loki+Grafana+Promtail | 軽量, Grafanaと統合 | 検索機能が限定的 | 中小規模プロジェクト |
| CloudWatch Logs | メンテナンス不要 | AWS依存 | AWSデプロイ |
(2) ▶ サンプル:Docker ComposeにLokiを追加
YAML
# docker-compose.ymlに追加
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki_data:/loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log:ro
- ./docker/promtail.yml:/etc/promtail/config.yml
depends_on:
- loki
出力:
TEXT
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
7. PriceTracker正式ローンチ
(1) プロジェクト完了マイルストーン
gantt
title PriceTracker 開発タイムライン
dateFormat YYYY-MM-DD
section Phase 1
FastAPI Intro :p1a, 2026-01-01, 1d
Installation & UV :p1b, after p1a, 1d
Path & Query Params :p1c, after p1b, 1d
Request Body & Pydantic :p1d, after p1c, 1d
Response Models :p1e, after p1d, 1d
Phase 1 Capstone :p1f, after p1e, 2d
section Phase 2
Middleware :p2a, after p1f, 1d
Dependency Injection :p2b, after p2a, 1d
Database SQLAlchemy :p2c, after p2b, 2d
CRUD Operations :p2d, after p2c, 1d
Authentication JWT :p2e, after p2d, 2d
Phase 2 Capstone :p2f, after p2e, 2d
section Phase 3
WebSocket :p3a, after p2f, 1d
Celery :p3b, after p3a, 2d
File Upload :p3c, after p3b, 1d
Testing :p3d, after p3c, 1d
Caching :p3e, after p3d, 1d
OpenAPI :p3f, after p3e, 1d
section Phase 4
Docker :p4a, after p3f, 1d
Performance :p4b, after p4a, 1d
Monitoring :p4c, after p4b, 1d
CI/CD :p4d, after p4c, 1d
section Phase 5
Project Design :p5a, after p4d, 1d
Project Development :p5b, after p5a, 2d
Project Deployment :p5c, after p5b, 1d
(2) デプロイ検証チェックリスト
| 検証項目 | 検証方法 | 合格基準 |
|---|---|---|
| API可用性 | curl /health |
{"status": "healthy"} |
| フロントエンド統合 | BobがフロントエンドからAPIを呼び出し | 全エンドポイントが正常に動作 |
| 認証 | JWTログイン + 保護エンドポイント | ログイン成功, 未認証時は401を返す |
| 権限 | Free/Pro/Enterpriseのレート制限 | プランに応じて正しく制限 |
| WebSocket | 接続 + 価格フィード | リアルタイム更新を受信 |
| キャッシュ | 人気製品のキャッシュヒット率 | > 80% |
| モニタリング | Grafanaダッシュボード | メトリックが正常に表示 |
| アラート | P99急増のシミュレーション | アラート通知がトリガー |
| ログ | Lokiの照会 | 構造化ログが検索可能 |
| CI/CD | tagプッシュによるデプロイトリガー | 自動デプロイが成功 |
❓ よくある質問
Q ブルーグリーンデプロイにはサーバーが2倍必要ですか?
A はい, 切り替え中は2セットの環境が必要です。K8s環境では代わりにローリングアップデートが使用できるため, 追加リソースは不要です。
Q カナリアリリースはどうトラフィックを配分しますか?
A Nginxは
weightパラメータを使用し (server v2:8000 weight=1; server v1:8000 weight=9;), K8sはcanary Deploymentでレプリカ数を調整します。Q データベースマイグレーション中にテーブルロックが発生した場合はどうしますか?
A
CREATE INDEX CONCURRENTLYを使用し (テーブルをロックせずにインデックスを作成), ピーク時のDDL文実行を避けます。大きなテーブルはバッチでマイグレーションしてください。Q 構造化ログと通常のログの違いは何ですか?
A 構造化ログはJSON形式で, 各ログエントリに固定フィールド (timestamp, level, message, request_id)が含まれ, 機械によるパースと検索が可能です。通常のログは人間が読めるテキストで, 機械によるパースが困難です。
Q デプロイ後の最初の1時間は何を監視すべきですか?
A Grafanaダッシュボードで3つのコアメトリックを監視します:QPS (正常か?), P99レイテンシ (急増していないか?), エラー率 (> 1%か?)。また, ログ集約でERRORメッセージがないか確認します。
Q ロールバックにはどのくらいかかりますか?
A Dockerのロールバックは約30秒です (旧イメージバージョンに切り替えて再起動)。データベースのロールバックはマイグレーションの複雑さに依存し, シンプルなカラム削除は数秒, 複雑なマイグレーションはデータ修復が必要になる場合があります。
📖 まとめ
- デプロイチェックリストの3次元:セキュリティ (HTTPS/CORS/RateLimit), パフォーマンス (Workers/コネクションプール/キャッシュ), モニタリング (メトリック/アラート/ログ)
- ブルーグリーンデプロイ:2環境間の切り替えで秒単位のロールバック, カナリアデプロイ:段階的スケーリング—10% → 30% → 50% → 100%
- ゼロダウンタイムマイグレーションの原則:追加のみ, 削除しない, デュアルライト互換, 遅延クリーンアップ, 段階的マイグレーション
- 構造化JSONログ + Loki/Grafana集約,
request_idでリクエストを追跡 - PriceTracker正式ローンチ:Bobのフロントエンド統合は成功, Charlieのモニタリングシステムは緑色の信号, 数百万の価格データが安定して流れている
📝 練習問題
- 基本問題 (難易度 ⭐):PriceTrackerのデプロイチェックリスト (セキュリティ, パフォーマンス, モニタリング各5項目)を作成し, デプロイ環境を項目ごとに検証してください。ヒント:この記事のチェックリスト表を参照。
- 応用問題 (難易度 ⭐⭐):構造化JSONロギング (timestamp, level, message, request_idを含む)を実装し, リクエストIDを各リクエストに注入するロギングミドルウェアを追加し, Docker ComposeとLokiを設定してログを集約してください。ヒント:JSONFormatter +
logging.getLogger("pricetracker") - チャレンジ (難易度 ⭐⭐⭐):完全なデプロイプロセス - 安全なマイグレーションスクリプト (「追加のみ, 削除しない」原則に従う)を作成し, Nginxブルーグリーンデプロイを設定し, Grafanaアラートルール (P99 > 500 ms + エラー率 > 5%)を設定し, カナリアリリース検証 (まず10%のトラフィックをv2にルーティングし, その後完全に切り替え)を実行してください。ヒント:
CREATE INDEX CONCURRENTLY+ Nginx upstream切り替え
---| 目次に戻る



