🔄

セッション管理を考慮したBlue-Greenデプロイメントの実装

に公開

はじめに

この記事で学べること

  • セッション管理を考慮したBlue-Greenデプロイメントの設計方法
  • 進行中リクエストを追跡するGolangアプリケーションの実装
  • ダウンタイムゼロを実現するデプロイスクリプトの作成
  • オンプレミス環境でのゼロダウンタイムデプロイの実践手法

対象読者

  • オンプレミス環境でAI関連システムを運用している方
  • 長時間実行されるリクエストを持つアプリケーションを開発している方
  • Blue-Greenデプロイメントの基本を理解している方
  • DockerとDocker Composeの基本的な使い方を知っている方

クラウドサービスの利用を最優先に

クラウド環境でアプリケーションを運用する場合、Blue-Greenデプロイメントは各クラウドプロバイダーが提供するマネージドサービスを利用することを強く推奨します。

プロバイダー サービス 特徴
AWS ECS Blue/Green Deployment CodeDeployと連携した自動デプロイ
Google Cloud Cloud Deploy GKEへの継続的デリバリー
Azure App Service デプロイスロット スロット間のトラフィック制御

これらのサービスは、トラフィック制御やロールバック機能が組み込まれており、運用負荷を最小限に抑えられます。自前で実装する前に、まずはこれらのマネージドサービスの利用を検討してください。

なぜオンプレミスなのか

近年のAI関連システムでは、以下の理由からオンプレミス環境への回帰が顕著になっています。

  • データの機密性: 学習データや推論結果を外部に出せない
  • レイテンシ要件: リアルタイム推論で数ミリ秒の遅延も許容できない
  • GPU資源の効率: 専用GPUサーバーを継続的に稼働させる方がコスト効率が良い
  • カスタマイズ性: 特殊なハードウェアやネットワーク構成が必要

オンプレミス環境では、マネージドサービスを利用できないため、自前でBlue-Greenデプロイメントを実装する必要があります。本記事では、そのような環境でダウンタイムをゼロに近づける実装方法を解説します。

第1章:Blue-Greenデプロイメントの基礎と課題

1.1 Blue-Greenデプロイメントとは

Blue-Greenデプロイメントは、2つの本番環境(BlueとGreen)を用意し、トラフィックを切り替えることでダウンタイムを最小化するデプロイ手法です。

  • 基本的な流れ
  1. Blue環境で現行バージョンが稼働中
  2. Green環境に新バージョンをデプロイ
  3. Green環境の動作確認
  4. トラフィックをGreen環境に切り替え
  5. Blue環境を削除または待機状態に

問題が発生した場合は即座に元の環境に戻せるため、リスクを抑えられます。

1.2 従来の実装の問題点

一般的なBlue-Greenデプロイメントでは、新しいコンテナがヘルスチェックをパスしたら古いコンテナを削除します。しかし、この方法には以下の問題があります。

問題 具体例 影響
長時間リクエストの中断 AI推論処理(数秒〜数分) ユーザーへのエラー応答
長時間接続の切断 WebSocket、ロングポーリング リアルタイム通信の断絶
トランザクションの中断 データベース更新、ファイル処理 データ不整合の発生

これらの問題は、ヘルスチェックだけでは検出できません。古いコンテナに進行中のリクエストが残っていないことを確認してから削除する必要があります。

1.3 本記事の実装方針

本記事では、以下の4つのステップで実装を進めます。

  1. メトリクス公開: Golangアプリケーションで進行中リクエスト数を追跡し、エンドポイントで公開する
  2. 遅延シミュレート: リクエストにランダムな遅延を追加し、セッション状態をシミュレートする
  3. セッション待機: デプロイスクリプトで古いコンテナの進行中リクエストが0になるまで待機する
  4. 安全な削除: 進行中リクエストが0になったことを確認してからコンテナを削除する

この実装により、AI推論サーバーなどの長時間実行リクエストを持つアプリケーションでも、ダウンタイムをゼロに近づけることができます。

第1章のチェックリスト

  • Blue-Greenデプロイメントの基本的な流れを理解した
  • 従来の実装の問題点を把握した
  • 本記事の実装方針を確認した
  • セッション管理の重要性を理解した

Golangアプリケーションの実装

まず、アプリケーション側で進行中リクエスト数を追跡する機能を追加します。

進行中リクエスト数の追跡

アトミック変数を使って、スレッドセーフに進行中リクエスト数をカウントします。

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "math/rand"
    "net/http"
    "os"
    "sync/atomic"
    "time"
)

var version = getEnv("APP_VERSION", "1.0.0")
var requestCounter uint64
var activeRequests int64  // 進行中リクエスト数
var startTime = time.Now()

func getEnv(key, defaultValue string) string {
    if value := os.Getenv(key); value != "" {
        return value
    }
    return defaultValue
}

2.2 リクエストハンドラーの実装

各リクエストの開始時にカウンターをインクリメントし、終了時にデクリメントします。

  • 実装のポイント
    • deferを使うことで、パニックが発生した場合でも確実にデクリメントする
    • ランダム遅延でAI推論などの長時間処理をシミュレートする
    • レスポンスに遅延時間を含めることで、動作確認が容易になる
func rootHandler(w http.ResponseWriter, r *http.Request) {
    // 進行中リクエスト数をインクリメント
    atomic.AddInt64(&activeRequests, 1)
    defer atomic.AddInt64(&activeRequests, -1)
    
    // リクエストカウンターをインクリメント
    count := atomic.AddUint64(&requestCounter, 1)
    
    // ランダム遅延(1秒〜5秒)でセッション状態をシミュレート
    delayMs := rand.Intn(4000) + 1000
    time.Sleep(time.Duration(delayMs) * time.Millisecond)
    
    // レスポンス生成
    hostname, _ := os.Hostname()
    response := map[string]interface{}{
        "version":  version,
        "hostname": hostname,
        "message":  fmt.Sprintf("Request #%d - delayed %dms", count, delayMs),
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
    
    log.Printf("[%s] Request #%d (delayed %dms)", hostname, count, delayMs)
}

2.3 統計エンドポイントの実装

進行中リクエスト数を外部から取得できるように、統計エンドポイントを実装します。

  • このエンドポイントの役割
    • デプロイスクリプトが進行中リクエスト数を確認するために使用します
    • 運用時のモニタリングにも活用できます
    • JSON形式で構造化されたメトリクスを提供します
func statsHandler(w http.ResponseWriter, r *http.Request) {
    hostname, _ := os.Hostname()
    
    stats := map[string]interface{}{
        "version":         version,
        "hostname":        hostname,
        "uptime":          time.Since(startTime).Round(time.Second).String(),
        "total_requests":  atomic.LoadUint64(&requestCounter),
        "active_requests": atomic.LoadInt64(&activeRequests),  // 進行中リクエスト数
        "started_at":      startTime.Format(time.RFC3339),
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(stats)
}

2.4 メイン関数

進行中のセッションのシミュレーションのために、乱数シードを初期化し、各エンドポイントを登録します。

  • 初期化の流れ
  1. 乱数シードを初期化します(遅延時間をランダムにするため)
  2. 環境変数からポート番号を取得します
  3. 各エンドポイントをルーティングに登録します
  4. サーバーを起動します
func main() {
    rand.Seed(time.Now().UnixNano())
    
    port := getEnv("PORT", "8080")
    
    http.HandleFunc("/", rootHandler)
    http.HandleFunc("/health", healthHandler)
    http.HandleFunc("/stats", statsHandler)
    
    hostname, _ := os.Hostname()
    log.Printf("Starting server version %s", version)
    log.Printf("Container: %s", hostname)
    log.Printf("Listening on port %s", port)
    
    addr := fmt.Sprintf(":%s", port)
    if err := http.ListenAndServe(addr, nil); err != nil {
        log.Fatal(err)
    }
}

第2章のチェックリスト

  • アトミック変数による進行中リクエスト数の追跡を実装した
  • deferを使った確実なカウンターのデクリメントを理解した
  • ランダム遅延でセッション状態をシミュレートした
  • 統計エンドポイントでメトリクスを公開した
  • 実装したコードが正しくビルドできることを確認した

第3章:デプロイスクリプトの実装

本章では、セッション終了を待機する機能を持つデプロイスクリプトを作成します。

3.1 新コンテナ起動時の重要な注意点

新しいイメージをビルドした後にコンテナを起動する際、--no-recreateオプションが重要な役割を果たします。

--no-recreateオプションの役割

  • 既存のコンテナを再作成せずに、新しいコンテナのみを追加する
  • 進行中リクエストを持つ古いコンテナを保護する
  • Blue-Greenデプロイメントの根幹を成す

このオプションが必要な理由

Step 1で新しいイメージをビルドしているため、Docker Composeは「イメージが変更された」と認識します。--no-recreateオプションがない場合、docker compose upは既存のコンテナも停止・再作成しようとし、進行中のリクエストが中断されてしまいます。

# ❌ 悪い例(既存コンテナも再作成される可能性)
docker compose up -d --scale app=$NEW_COUNT

# ✅ 良い例(新しいコンテナのみ追加)
docker compose up -d --scale app=$NEW_COUNT --no-recreate

使い分けのガイドライン

--no-recreateが必要な場合:

  1. ✅ Blue-Greenデプロイメントでスケールアップする場合
  2. ✅ 新しいイメージをビルドした直後
  3. ✅ 進行中リクエストを保護したい場合
  4. ✅ 既存コンテナを維持したまま新規追加する場合

--no-recreateが不要な場合:

  1. ❌ 初回デプロイ(既存コンテナがない)
  2. ❌ 意図的に全コンテナを再作成したい場合
  3. ❌ 設定変更を既存コンテナに反映させたい場合
  4. --force-recreateと併用する場合(互換性なし)

3.2 セッション終了待機処理

古いコンテナを停止する前に、進行中リクエストが0になるまで待機します。重要なのは、すぐに削除せずに停止状態で保持することです。

  • 待機処理の設計方針
    • 2秒ごとに進行中リクエスト数をチェックする
    • 最大60秒まで待機する(タイムアウト設定)
    • タイムアウト時は警告を出して強制停止する
    • 進行中リクエスト数をログに出力して可視化する
    • 停止後もコンテナとイメージは保持する(ロールバック用)
# Step 2: 新コンテナを起動(スケールアップ)
log_info "Step 2/5: 新しいバージョンのコンテナを起動中..."
docker compose up -d --scale app=$NEW_COUNT --no-recreate

# Step 4: 古いコンテナの停止(セッション終了待機)
log_info "Step 4/5: 古いコンテナのセッション終了待機中..."

# 全コンテナのIDを取得し、古いものから停止
ALL_CONTAINERS=$(docker compose ps -q app 2>/dev/null)
OLD_CONTAINERS=$(echo "$ALL_CONTAINERS" | head -n $CURRENT_COUNT)

# 古いコンテナの情報を保存(ロールバック用)
OLD_CONTAINER_IDS=""
OLD_IMAGE_IDS=""

for container_id in $OLD_CONTAINERS; do
    CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $container_id 2>/dev/null | sed 's/\///')
    OLD_IMAGE_ID=$(docker inspect --format='{{.Image}}' $container_id 2>/dev/null)
    
    log_info "セッション終了待機: $CONTAINER_NAME"
    
    # 進行中リクエストが0になるまで待機(最大60秒)
    SESSION_WAIT=0
    MAX_SESSION_WAIT=60
    
    while [ $SESSION_WAIT -lt $MAX_SESSION_WAIT ]; do
        # コンテナの/statsエンドポイントから進行中リクエスト数を取得
        ACTIVE_REQUESTS=$(docker exec $container_id wget -qO- http://localhost:8080/stats 2>/dev/null | grep -o '"active_requests":[0-9]*' | grep -o '[0-9]*' || echo "0")
        
        if [ "$ACTIVE_REQUESTS" -eq 0 ]; then
            log_success "セッション終了確認: $CONTAINER_NAME (active_requests=0)"
            break
        fi
        
        printf "  進行中リクエスト: %d\n" "$ACTIVE_REQUESTS"
        sleep 2
        SESSION_WAIT=$((SESSION_WAIT + 2))
    done
    
    if [ $SESSION_WAIT -ge $MAX_SESSION_WAIT ]; then
        log_warning "タイムアウト: $CONTAINER_NAME (強制停止)"
    fi
    
    # コンテナを停止するが削除はしない(ロールバック用に保持)
    log_info "停止中: $CONTAINER_NAME (イメージは保持)"
    docker stop $container_id >/dev/null 2>&1 || true
    
    OLD_CONTAINER_IDS="$OLD_CONTAINER_IDS $container_id"
    OLD_IMAGE_IDS="$OLD_IMAGE_IDS $OLD_IMAGE_ID"
done

log_success "古いコンテナ停止完了(ロールバック可能な状態)"
  • なぜ削除せずに停止するのか
    • 新バージョンに問題が発生した場合、即座にロールバックできる
    • イメージを保持することで、再ビルド不要で旧バージョンに戻せる
    • 停止状態のコンテナはリソースをほとんど消費しない

3.3 新バージョンの安定稼働確認

古いコンテナを停止した後、新バージョンが安定稼働していることを確認します。

# 新コンテナの安定稼働確認
log_info "=== 新バージョンの安定稼働確認 ==="
log_info "30秒間、新バージョンの動作を監視します..."
log_info "問題があれば Ctrl+C で中断してロールバックしてください"

STABILITY_CHECK=0
STABILITY_DURATION=30

while [ $STABILITY_CHECK -lt $STABILITY_DURATION ]; do
    # 新コンテナの状態確認
    HEALTHY_COUNT=$(docker compose ps app 2>/dev/null | grep -c "(healthy)" || echo 0)
    
    if [ "$HEALTHY_COUNT" -lt 1 ]; then
        log_error "新コンテナが異常終了しました"
        log_warning "ロールバックが必要です: ./deploy.sh rollback"
        exit 1
    fi
    
    printf "."
    sleep 1
    STABILITY_CHECK=$((STABILITY_CHECK + 1))
done

log_success "新バージョンが安定稼働しています"
  • 安定稼働確認の重要性
    • デプロイ直後は正常でも、時間経過で問題が発生する可能性がある
    • メモリリーク、接続プールの枯渇、タイムアウトなどを検出
    • 30秒間は停止中の古いコンテナを保持しているため、即座にロールバック可能

3.4 古いコンテナとイメージの削除

安定稼働が確認できたら、停止中の古いコンテナとイメージを削除します。

# 古いコンテナとイメージの削除
if [ -n "$OLD_CONTAINER_IDS" ]; then
    log_info "=== 古いコンテナとイメージの削除 ==="
    
    for container_id in $OLD_CONTAINER_IDS; do
        CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $container_id 2>/dev/null | sed 's/\///' || echo "unknown")
        if [ "$CONTAINER_NAME" != "unknown" ]; then
            log_info "削除中: $CONTAINER_NAME"
            docker rm $container_id >/dev/null 2>&1 || true
        fi
    done
    
    # 古いイメージの削除(使用されていないイメージのみ)
    for image_id in $OLD_IMAGE_IDS; do
        # イメージが他のコンテナで使用されていないか確認
        IMAGE_IN_USE=$(docker ps -a --filter "ancestor=$image_id" -q | wc -l | tr -d ' ')
        
        if [ "$IMAGE_IN_USE" -eq 0 ]; then
            IMAGE_TAG=$(docker inspect --format='{{range .RepoTags}}{{.}} {{end}}' $image_id 2>/dev/null || echo "")
            log_info "イメージ削除: $IMAGE_TAG"
            docker rmi $image_id >/dev/null 2>&1 || true
        fi
    done
    
    log_success "クリーンアップ完了"
fi

3.5 デプロイフロー全体

デプロイスクリプト全体の流れは以下のようになります。

ステップ 処理内容 目的
Step 1 新しいイメージをビルド 最新コードを含むDockerイメージを作成
Step 2 新コンテナを起動(--no-recreate付き) 既存コンテナを保護しつつGreen環境を追加
Step 3 新コンテナのヘルスチェック待機 Green環境が正常に起動したことを確認
Step 4 古いコンテナのセッション終了待機 Blue環境の進行中リクエストが完了するまで待機
Step 5 古いコンテナを停止(削除しない) ロールバック可能な状態を維持
Step 6 新バージョンの安定稼働確認(30秒) 問題がないことを確認
Step 7 古いコンテナとイメージを削除 リソースをクリーンアップ

この流れにより、進行中のリクエストを中断することなく、かつ安全にロールバック可能な状態を維持しながら、デプロイを実行できます。

第3章のチェックリスト

  • --no-recreateオプションの重要性を理解した
  • 既存コンテナを保護する仕組みを把握した
  • セッション終了待機処理の仕組みを理解した
  • 古いコンテナを停止するが削除しない理由を理解した
  • 安定稼働確認の重要性を把握した
  • デプロイフロー全体の流れを確認した

第4章:動作確認

実装が完了したら、実際に動作確認を行います。本章では、3つのテストケースで実装の正しさを検証します。

4.1 単一リクエストの確認

まず、単一リクエストでランダム遅延が動作することを確認します。

  • 確認項目
    • レスポンスに遅延時間が含まれている
    • 実際の実行時間と遅延時間が一致している
    • 1〜5秒の範囲でランダムに遅延している
$ time curl -s http://localhost/ | jq -r '.message'
Request #1 - delayed 2247ms
curl -s http://localhost/  0.00s user 0.00s system 0% cpu 2.262 total
  • 結果の確認
    • レスポンス: 2247msの遅延
    • 実行時間: 2.262 total
    • 遅延時間と実行時間が一致している ✅

4.2 並行リクエストの確認

次に、複数のリクエストを並行で送信し、進行中リクエスト数が正しくカウントされることを確認します。

  • 確認項目
    • 5つの並行リクエストが正しくカウントされる
    • アトミック変数が正しく動作している
    • 統計エンドポイントが正確な値を返す
$ for i in {1..5}; do curl -s http://localhost/ > /dev/null & done; \
  sleep 0.5; \
  curl -s http://localhost/stats | jq '.active_requests'
5
  • 結果の確認
    • 5つのリクエストをバックグラウンドで送信
    • 0.5秒後に統計エンドポイントを確認
    • active_requests: 5が返される ✅

5つの並行リクエストが正しくカウントされています。

4.3 デプロイ時の動作確認

最後に、実際にデプロイを実行して、セッション終了待機が動作することを確認します。

  • テストシナリオ
  1. バックグラウンドで継続的にリクエストを送信
  2. デプロイスクリプトを実行
  3. 古いコンテナの進行中リクエストが0になるまで待機することを確認
  4. すべてのリクエストが正常に完了することを確認
# バックグラウンドで継続的にリクエストを送信
$ for i in {1..20}; do
  curl -s http://localhost/ > /dev/null &
  sleep 0.3
done &

# デプロイ実行
$ ./deploy.sh 2.0.0
  • デプロイ実行時の出力
[INFO] Step 4/5: 古いコンテナのセッション終了待機中...
[INFO] セッション終了待機: blue-green-demo-app-1
  進行中リクエスト: 1
[SUCCESS] セッション終了確認: blue-green-demo-app-1 (active_requests=0)
[INFO] 停止中: blue-green-demo-app-1
  • 結果の分析
  1. デプロイスクリプトが進行中リクエストを検出 ✅
  2. 進行中リクエスト: 1と表示され、待機状態に入る ✅
  3. リクエスト完了後、active_requests=0を確認 ✅
  4. 確認後、古いコンテナを安全に削除 ✅

進行中リクエストが0になってから古いコンテナが削除されるため、リクエストが中断されることはありません。

第4章のチェックリスト

  • 単一リクエストでランダム遅延が動作することを確認した
  • 並行リクエストで進行中リクエスト数が正しくカウントされることを確認した
  • デプロイ時にセッション終了待機が動作することを確認した
  • すべてのリクエストが正常に完了することを確認した

第5章:本番環境での考慮事項

本記事で紹介した実装を本番環境で利用する際の重要なポイントを解説します。

5.1 タイムアウト時間の調整

デフォルトでは60秒でタイムアウトしますが、アプリケーションの特性に応じて調整が必要です。

アプリケーション種別 推奨タイムアウト 理由
Webアプリケーション 60秒 ユーザーリクエストは通常数秒で完了
AI推論サーバー 300秒(5分) 複雑なモデルの推論に時間がかかる
バッチ処理 600秒(10分) 大量データの処理に時間がかかる
# AI推論サーバーの場合
MAX_SESSION_WAIT=300  # 5分に延長

5.2 メトリクスの監視

進行中リクエスト数だけでなく、以下のメトリクスも監視することを推奨します。

  • 監視すべきメトリクス
  1. リクエストの平均処理時間: 異常に遅いリクエストを検出
  2. エラー率: 新バージョンの品質を確認
  3. リソース使用率: CPU、メモリの使用状況を監視
  4. 進行中リクエスト数の推移: デプロイ時の待機時間を予測

これらのメトリクスを組み合わせることで、より安全なデプロイが可能になります。

5.3 ロールバック戦略

新バージョンに問題が発生した場合、即座にロールバックできる仕組みを用意しておくことが重要です。本実装では、デプロイスクリプトに組み込まれたロールバック機能を使用します。

ロールバックコマンド

デプロイスクリプトは、rollbackコマンドをサポートしています。

# ロールバック実行
./deploy.sh rollback

ロールバックの動作

ロールバック機能は、以下の2つのケースに対応します。

  • ケース1: 停止中のコンテナがある場合

安定稼働確認中(30秒間)に問題を発見した場合、停止中の古いコンテナを再起動します。

# 稼働中のコンテナを取得
RUNNING_CONTAINERS=$(docker compose ps -q app 2>/dev/null)

# 停止中のコンテナを取得
STOPPED_CONTAINERS=$(docker compose ps -aq app 2>/dev/null | grep -v -F "$RUNNING_CONTAINERS")

# 停止中のコンテナを再起動
for container_id in $STOPPED_CONTAINERS; do
    CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $container_id 2>/dev/null | sed 's/\///')
    log_info "再起動中: $CONTAINER_NAME"
    docker start $container_id >/dev/null 2>&1 || true
done

# 新しいコンテナを削除
for container_id in $RUNNING_CONTAINERS; do
    CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $container_id 2>/dev/null | sed 's/\///')
    log_info "新バージョンのコンテナを削除中: $CONTAINER_NAME"
    docker stop $container_id >/dev/null 2>&1 || true
    docker rm $container_id >/dev/null 2>&1 || true
done
  • ケース2: 複数のコンテナが稼働中の場合

デプロイの途中段階(古いコンテナがまだ稼働中)の場合、最新のコンテナを削除します。

# 最新のコンテナを削除
LATEST_CONTAINER=$(echo "$RUNNING_CONTAINERS" | tail -n 1)
CONTAINER_NAME=$(docker inspect --format='{{.Name}}' $LATEST_CONTAINER 2>/dev/null | sed 's/\///')

log_info "新バージョンのコンテナを削除中: $CONTAINER_NAME"
docker stop $LATEST_CONTAINER >/dev/null 2>&1 || true
docker rm $LATEST_CONTAINER >/dev/null 2>&1 || true

ロールバックの判断基準

以下のメトリクスを監視し、異常を検知したら即座にロールバックします。

メトリクス 正常値 ロールバック基準
エラー率 1%以下 5%以上が5分間継続
レスポンスタイム 平均2秒以下 平均5秒以上が3分間継続
CPU使用率 70%以下 90%以上が5分間継続
メモリ使用率 80%以下 95%以上

ロールバックのタイミング

ロールバックは、以下のタイミングで実行できます。

  1. 安定稼働確認中(30秒間): Ctrl+Cで中断して./deploy.sh rollbackを実行
  2. デプロイ完了後: 問題を発見したら即座に./deploy.sh rollbackを実行
  3. 自動ロールバック: ヘルスチェック失敗時は自動的にロールバック

ロールバック後の確認

ロールバック実行後、自動的に以下の情報が表示されます。

# 現在の状態
docker compose ps app

# アプリケーション情報
curl -s http://localhost/stats | jq '{version, active_requests}'

これにより、旧バージョンに正しく戻ったことを確認できます。

5.4 ログの保存

デプロイ時のログは必ず保存し、問題発生時に原因を特定できるようにしておきます。

  • 保存すべきログ
    • デプロイ開始・完了時刻
    • 各ステップの実行時間
    • 進行中リクエスト数の推移
    • エラーメッセージ

特に進行中リクエスト数の推移を記録しておくと、タイムアウト時間の調整に役立ちます。

第5章のチェックリスト

  • アプリケーションの特性に応じてタイムアウト時間を調整した
  • 監視すべきメトリクスを理解した
  • ロールバック戦略を準備した
  • デプロイ時のログ保存を設定した

まとめ

本記事では、セッション管理を考慮したBlue-Greenデプロイメントの実装方法を、5つの章に分けて解説しました。

実装の要点

内容 重要ポイント
第1章 基礎と課題 従来の実装では長時間リクエストが中断される
第2章 Golangアプリ アトミック変数で進行中リクエスト数を追跡
第3章 デプロイスクリプト セッション終了を待機してから削除
第4章 動作確認 3つのテストケースで実装を検証
第5章 本番環境 タイムアウト調整とメトリクス監視

クラウドとオンプレミスの使い分け

  • クラウド環境の場合

    • AWS、Google Cloud、Azureのマネージドサービスを最優先に検討
    • 手間をかけずにBlue-Greenデプロイメントを実現できる
    • トラフィック制御やロールバック機能が組み込み済み
  • オンプレミス環境の場合

    • AI関連システムでは、データの機密性やレイテンシ要件からオンプレミスが選択される
    • 本記事の実装により、ダウンタイムをゼロに近づけることができる
    • 適切な実装で、クラウドと同等の運用品質を実現可能

AI関連システムでの重要性

AI推論サーバーやバッチ処理システムなど、長時間実行されるリクエストを持つアプリケーションでは、進行中リクエストの管理が特に重要です。

  • ダウンタイムゼロの実現
    • ユーザー体験を損なわない
    • サービスの信頼性が向上する
    • 継続的なシステム改善が可能になる

オンプレミス環境でも、工夫次第でクラウドと同等の運用品質を実現できます。本記事で紹介した手法を参考に、自社の環境に適したデプロイ戦略を検討してください。

次のステップ

  1. リポジトリのクローン: 完全なソースコードを取得
  2. ローカル環境で実行: Docker Composeで動作確認
  3. 自社環境への適用: アプリケーションの特性に応じてカスタマイズ
  4. 本番環境への展開: メトリクス監視とロールバック戦略を準備

完全なソースコードは、GitHubリポジトリで公開しています。実際に動かして試してみてください。

Discussion