404 Not Found

404 Not Found


nginx

プロジェクトデプロイとローンチ — 開発から本番へ

製品のローンチはロケットの打ち上げのようなもの - 最初の24レッスンは設計と製造で, このレッスンはカウントダウンと打ち上げです。打ち上げ前には長いチェックリストがあり, すべての項目にチェックが入るまでエンジンに点火できません。チェックリストなしのローンチはギャンブルです。

1. 学ぶ内容


2. Aliceの実話

(1) 悩み:デプロイ時のインシデントが頻発

PriceTrackerは最初の2回のデプロイで問題が発生しました:1回目はHTTPSの設定忘れ, 2回目はデータベースマイグレーション時のテーブルロックによる10分間のサービス停止, 3回目はログが集約されておらず, 問題発生後にCharlieが5台のサーバーのログを2時間かけて捜索しました。Aliceには, すべてのデプロイが安全で制御可能であることを保証する体系的なデプロイプロセスが必要です。

(2) デプロイチェックリストによる解決策

デプロイは「コードをプッシュして終わり」ではなく, 一連のチェックリストです:セキュリティ強化, パフォーマンス検証, マイグレーション戦略, 段階的ロールアウト, モニタリング検証 - 各項目にチェックが入ってから次のステップに進みます。

(3) 成果

3回目のデプロイ:チェックリストの全項目にチェックが入り, HTTPSが設定され, マイグレーションはゼロダウンタイムで完了, ローリングリリースでまず10%のトラフィックをルーティングして検証し, ログは自動的にLokiに集約 - デプロイプロセス全体が滞りなく完了し, Charlieのモニタリングダッシュボードはずっと緑色のままでした。


3. 本番環境チェックリスト

(1) ローンチプロセス

100%
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) ブルーグリーンデプロイ

100%
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) プロジェクト完了マイルストーン

100%
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秒です (旧イメージバージョンに切り替えて再起動)。データベースのロールバックはマイグレーションの複雑さに依存し, シンプルなカラム削除は数秒, 複雑なマイグレーションはデータ修復が必要になる場合があります。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度 ⭐):PriceTrackerのデプロイチェックリスト (セキュリティ, パフォーマンス, モニタリング各5項目)を作成し, デプロイ環境を項目ごとに検証してください。ヒント:この記事のチェックリスト表を参照。
  2. 応用問題 (難易度 ⭐⭐):構造化JSONロギング (timestamp, level, message, request_idを含む)を実装し, リクエストIDを各リクエストに注入するロギングミドルウェアを追加し, Docker ComposeとLokiを設定してログを集約してください。ヒント:JSONFormatter + logging.getLogger("pricetracker")
  3. チャレンジ (難易度 ⭐⭐⭐):完全なデプロイプロセス - 安全なマイグレーションスクリプト (「追加のみ, 削除しない」原則に従う)を作成し, Nginxブルーグリーンデプロイを設定し, Grafanaアラートルール (P99 > 500 ms + エラー率 > 5%)を設定し, カナリアリリース検証 (まず10%のトラフィックをv2にルーティングし, その後完全に切り替え)を実行してください。ヒント:CREATE INDEX CONCURRENTLY + Nginx upstream切り替え

---| 目次に戻る

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%