Kubernetesにおけるプログレッシブデリバリー: FlaggerとIstioによるカナリアリリースの自動化

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

本番環境デプロイにおける真の課題

かつては毎週金曜日のリリースを迎えるたび、胃がキリキリと痛むような感覚に襲われていました。標準のローリングアップデートは理論上素晴らしく思えますが、表面化しにくい「サイレント障害」には無力です。バージョン2.0をロールアウトし、Podが基本的なReadiness Probeに合格すると、Kubernetesは即座にユーザーのトラフィックを100%新バージョンへルーティングします。そして間もなく、500エラーの嵐が吹き荒れるのです。

ステージング環境でエッジケースのバグを検知するのは困難です。ある時、注文処理ワーカーのメモリリークが、本番環境で800の同時リクエストを受けるまで発覚しませんでした。その結果、アラートに誰かが気づく前のわずか4分間で、アクティブな決済処理の30%が失敗してしまいました。この惨事を機に、私たちはFlaggerとIstioを用いたプログレッシブデリバリーを導入し、手動によるトラフィック管理を完全に撤廃することを決断しました。

未検証のコードに全ユーザーを一度に晒す代わりに、まずは本番トラフィックのごく一部(例えば5%)だけを新バージョンに振り向けます。そして、エラーの急増やレイテンシの悪化がないかを実際のテレメトリで監視します。カナリアが健全であれば段階的にトラフィックの割合を増やし、万が一エラーが検知されれば、人手を介さずシステムが瞬時に自動ロールバックを実行します。

各コンポーネントの連携の仕組み

プログレッシブデリバリー vs. 標準のローリングアップデート

プログレッシブデリバリーは、段階的なトラフィック移行と自動分析ループを組み合わせたアプローチです。標準のローリングアップデートは、コンテナがクラッシュするかHTTPヘルスチェックに失敗した場合にしか停止しません。それに対し、プログレッシブデリバリーではHTTPエラー率やP99レイテンシといった実際のアプリケーションメトリクスを監視しながら、ユーザーのトラフィックを段階的にシフトしていきます。

Flagger: 自動化コントローラー

Flaggerは、カナリアリリースのライフサイクルを最初から最後まで管理するKubernetes Operatorです。Deploymentのコンテナイメージが更新されると、Flaggerがリリースプロセスをインターセプトし、隔離されたカナリア用ReplicaSetを作成して評価ループを開始します。

  • トラフィックの初期5%〜10%をカナリアデプロイにルーティングします。
  • 設定されたSLO閾値に基づき、60秒ごとにPrometheusへクエリを実行します。
  • 評価ウィンドウが成功するごとにトラフィックの重み(Weight)を引き上げます。
  • エラー閾値を超えた場合は、ルーティングを安定版のPrimary Podに即座に戻します。

Istio: サービスメッシュのデータプレーン

Kubernetesの標準的なClusterIP Serviceは、一致するPodラベルに対してトラフィックをランダムに分散させるため、90対10のような割合でのトラフィック分割はできません。Istioは、VirtualServiceとDestinationRuleリソースを利用してレイヤー7でこの問題を解決します。FlaggerがこれらのIstioルーティングルールを動的に操作するため、エンジニアが手動でルーティング比率を調整する必要はありません。

環境構築手順

前提条件

バージョン1.24以上のKubernetesクラスター、kubectlのアクセス権限、およびhelm v3がインストールされている必要があります。また、サービスメッシュのテレメトリを収集するPrometheusが必要ですが、これは標準のkube-prometheus-stackチャートで簡単に導入できます。

Istioのインストール

curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.20.0 sh -
cd istio-1.20.0
export PATH=$PWD/bin:$PATH

istioctl install --set profile=default -y

# 対象の名前空間でEnvoyサイドカーの自動インジェクションを有効化
kubectl label namespace default istio-injection=enabled --overwrite

Flaggerのインストール

helm repo add flagger https://flagger.app
helm repo update

helm upgrade -i flagger flagger/flagger \
  --namespace=istio-system \
  --set meshProvider=istio \
  --set metricsServer=http://kube-prometheus-stack-prometheus.monitoring:9090

metricsServerにはクラスター内のPrometheusエンドポイントを直接指定します。Flaggerは毎回の分析インターバルでこのアドレスにクエリを送信し、カナリアの健全性を評価します。

自動カナリアリリースの設定

ベースとなるDeploymentとServiceの作成

まずは標準的なDeploymentマニフェストを使ってアプリケーションをデプロイします。Flaggerはこのリソースをmy-app-primary(安定版ベースライン)として複製し、カナリアインスタンスを自動管理します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-registry/my-app:1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: default
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080

Canaryカスタムリソースの定義

Canaryカスタムリソースでは、進行ルール、トラフィック増加幅、およびロールバック基準を設定します。

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: my-app
  namespace: default
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  progressDeadlineSeconds: 120
  service:
    port: 80
    targetPort: 8080
    gateways:
    - istio-system/ingressgateway
    hosts:
    - my-app.example.com
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 1m

主要な設定項目の説明:

  • interval (1m): Flaggerが1分ごとにメトリクスを収集・評価します。
  • threshold (5): メトリクスチェックが5回連続で失敗すると、即座にロールバックが実行されます。
  • maxWeight (50): Primaryへの完全プロモーション前に、カナリアへ流すトラフィックの上限を50%に制限します。
  • stepWeight (10): チェックが成功するごとにトラフィックを10%ずつ引き上げます(10%、20%、30%、40%、50%)。
  • request-success-rate: Envoyテレメトリに基づき、HTTP成功率99%以上を維持することを要求します。
  • request-duration: P99レイテンシが500ミリ秒未満であることを要求します。

リリースのトリガー

Canaryマニフェストを適用し、Flaggerの初期化完了を待ちます。Primaryデプロイの準備が整ったら、コンテナイメージを更新してリリースを開始します。

kubectl set image deployment/my-app \
  my-app=my-registry/my-app:2.0.0 \
  -n default

ターミナルで進捗をモニタリングします:

kubectl get canary my-app -n default -w

# 名前     ステータス     重み   最終遷移時刻
# my-app   Progressing   10       2024-01-15T10:00:00Z
# my-app   Progressing   20       2024-01-15T10:01:00Z
# my-app   Progressing   30       2024-01-15T10:02:00Z
# my-app   Progressing   40       2024-01-15T10:03:00Z
# my-app   Progressing   50       2024-01-15T10:04:00Z
# my-app   Promoting     0        2024-01-15T10:05:00Z
# my-app   Succeeded     0        2024-01-15T10:06:00Z

ロールバック機構の検証

意図的に失敗させて検証していないロールバックポリシーを決して信用してはいけません。リクエストの10%で500エラーを返すように設定したイメージをデプロイし、Flaggerコントローラーのログを確認してみましょう。

kubectl logs -n istio-system \
  -l app.kubernetes.io/name=flagger \
  --tail=100 -f | grep my-app

エラーがPrometheusに記録されると、Flaggerは違反を検知してリリースの進行を停止します。

my-app.default の昇格を停止: request-success-rate 94.20 < 99
my-app.default の昇格を停止: request-success-rate 93.80 < 99
my-app.default をロールバック中: 失敗チェックの閾値に達しました (5/5)
カナリア失敗! my-app.canary をスケールダウンしています

Istioは即座にトラフィックのルーティングをmy-app-primaryの100%へとリセットします。クラスター全体がダウンする事態を回避し、問題のあるリリースによる影響をわずか5分間・ユーザーの10%のみに抑え込むことができました。

カスタムビジネスメトリクスゲートの作成

HTTPステータスコードだけではシステムの健全性を完全に把握できません。MetricTemplateを利用することで、決済処理の失敗率やデータベースのクエリレイテンシなど、アプリケーション固有のPrometheusメトリクスをデプロイの判定基準に設定できます。

apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: checkout-error-rate
  namespace: default
spec:
  provider:
    type: prometheus
    address: http://kube-prometheus-stack-prometheus.monitoring:9090
  query: |
    sum(rate(http_requests_total{
      app="my-app",
      path="/checkout",
      status=~"5.."
    }[1m]))
    /
    sum(rate(http_requests_total{
      app="my-app",
      path="/checkout"
    }[1m])) * 100

作成したメトリクステンプレートをCanaryの定義に関連付けます。

  analysis:
    metrics:
    - name: checkout-error-rate
      templateRef:
        name: checkout-error-rate
        namespace: default
      thresholdRange:
        max: 0.5
      interval: 1m

いずれかのステップで決済失敗率が0.5%を超えた場合、Flaggerは即座にリリースを凍結します。

本番運用における実践Tips

Prometheusのスクレイピング間隔とFlaggerの分析サイクルを同期させましょう。Prometheusが30秒ごとにスクレイプしている場合、Flaggerのインターバルを1分に設定することで、評価ごとに少なくとも2つの最新データポイントが確保されます。60秒のスクレイプ間隔に対して60秒のインターバルを設定すると、古いメトリクスに基づいて計算してしまう恐れがあります。

トラフィックの少ないマイクロサービスでは、擬似トラフィックの生成(シンセティックトラフィック)が必要です。リクエストが毎秒10件未満のサービスでは、Flaggerのメトリクスが大きく乱高下する傾向があります。Flaggerに組み込まれているwebhooks機能を活用し、分析中にghzやheyなどの負荷テストツールを実行してカナリアPodに負荷をかけると安定します。

段階的リリースではなく一括切り替えを好むチームでも、ブルーグリーンデプロイメントが可能です。CanaryスペックでstepWeight: 100を設定すると、Flaggerは隔離された候補環境に対してヘルスチェックを行い、問題がないことを確認してからトラフィックを一括で切り替えます。

カナリア分析を自動化することで、デプロイパイプラインから属人性と推測を排除できます。信頼性の基準をあらかじめコードとして定義し、ルーティング制御はIstioに、ロールバックの自動管理はFlaggerに任せましょう。

Share: