Argo RolloutsによるKubernetesプログレッシブデリバリー:カナリアとブルー/グリーンデプロイメント完全ガイド

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

クイックスタート — Argo Rolloutsを5分で動かす

深夜2時。チームがホットフィックスをプッシュしたばかりで、標準のkubectl rolloutでは冷や汗が止まらない — 1つのPodが壊れただけでトラフィックが落ちる。その経験、私にもある。あの夜がArgo Rolloutsを真剣に使い始めたきっかけだった。

Argo RolloutsはKubernetesコントローラーで、リリース中のトラフィック移行を細かく制御できる。カナリア分割、ブルー/グリーン切り替え、メトリクス自動分析、そして障害時の自動ロールバックが可能だ。標準のKubernetes Deploymentオブジェクトでは、これらをネイティブに実現することはできない。

まずコントローラーとkubectlプラグインをインストールする:

# Argo Rolloutsコントローラーをインストール
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# kubectlプラグインをインストール(macOS/Linux)
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts

# バージョン確認
kubectl argo rollouts version

次に、DeploymentマニフェストをRolloutオブジェクトに置き換える。Podのスペックはそのままで、主にkindの変更とstrategyブロックの追加だけでよい:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
spec:
  replicas: 5
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-registry/my-app:v1.0.0
        ports:
        - containerPort: 8080
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {duration: 2m}
      - setWeight: 50
      - pause: {duration: 5m}
      - setWeight: 100

適用する:

kubectl apply -f rollout.yaml
kubectl argo rollouts get rollout my-app --watch

これでカナリアロールアウトが動作する。トラフィックの20%が新バージョンに流れ、2分待機後に50%へ拡大、さらに5分待機してから100%へ移行する。カスタムロジックを1行も書かずに実現できる。

詳細解説 — Argo Rolloutsの仕組み

カナリア戦略

内部的には、Argo Rolloutsは2つのReplicaSetを管理している。stableセット(現行バージョン)とcanaryセット(新バージョン)だ。トラフィックの重み付けはレプリカ数の調整によって行われる — 5つのレプリカで20%の重みを設定すると、カナリアPodが1つ、stableが4つになる。

レプリカベースではなく実際のHTTPレベルでのトラフィック分割には、IngressコントローラーまたはサービスメッシュとのIntegrationが必要だ。NGINX Ingressを使う場合:

strategy:
  canary:
    canaryService: my-app-canary
    stableService: my-app-stable
    trafficRouting:
      nginx:
        stableIngress: my-app-ingress
    steps:
    - setWeight: 10
    - pause: {duration: 10m}
    - setWeight: 30
    - pause: {duration: 10m}
    - setWeight: 100

Argo Rolloutsが管理する2つのServiceを作成する:

---
apiVersion: v1
kind: Service
metadata:
  name: my-app-stable
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-canary
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080

Argo RolloutsはNGINXアノテーションを自動的に注入し、Pod数ベースではなくプロキシレベルでトラフィックを分割する。「おおよそ10%」と「厳密に10%」の違いがここにある。

ブルー/グリーン戦略

ブルー/グリーンは概念的にシンプルだ。新バージョン(グリーン)を旧バージョン(ブルー)と並行して起動し、一気に切り替える。段階的なトラフィック移行はなく全か無かだが、切り替え前にグリーンを検証できる。

strategy:
  blueGreen:
    activeService: my-app-active
    previewService: my-app-preview
    autoPromotionEnabled: false   # 手動プロモーションが必要
    scaleDownDelaySeconds: 30     # プロモーション後30秒間ブルーを維持

新しいイメージをデプロイすると、グリーンPodがmy-app-previewの裏側で起動する。QAチームはプレビューエンドポイントに直接アクセスして検証できる。確認が取れたら:

# グリーンをアクティブにプロモート(切り替え実行)
kubectl argo rollouts promote my-app

# または中止してブルーへロールバック
kubectl argo rollouts abort my-app

深夜2時の作業ではscaleDownDelaySecondsに注目してほしい。プロモーション後も古いPodを短時間維持し、処理中のリクエストが正常に完了するまでの猶予を与える。この時間があれば、ブルーPodが消える前に問題を発見して中止することもできる。

応用編 — 自動分析とロールバック

小規模なチームなら手動プロモーションで十分だ。しかし1日に何度もリリースする場合は、プロセス自体がメトリクスを確認してくれる仕組みが必要になる。Argo RolloutsのAnalysisTemplateは、Prometheus、Datadog、New Relic、または任意のHTTPエンドポイントにクエリを投げ、続行するかロールバックするかを自動的に判断できる。

エラーレートを確認する分析を定義する:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: error-rate-check
spec:
  args:
  - name: service-name
  metrics:
  - name: error-rate
    interval: 1m
    count: 5
    successCondition: result[0] < 0.05   # エラー率5%未満であること
    failureLimit: 2
    provider:
      prometheus:
        address: http://prometheus.monitoring.svc:9090
        query: |
          sum(rate(http_requests_total{service="{{args.service-name}}",status=~"5.."}[2m]))
          /
          sum(rate(http_requests_total{service="{{args.service-name}}"}[2m]))

カナリア戦略に組み込む:

strategy:
  canary:
    steps:
    - setWeight: 20
    - analysis:
        templates:
        - templateName: error-rate-check
        args:
        - name: service-name
          value: my-app-canary
    - setWeight: 50
    - pause: {duration: 5m}
    - setWeight: 100

これにより、ロールアウトは20%で一時停止し、5分間にわたって5回のPrometheusクエリを実行する。2回以上のチェックでエラーレートが5%を超えた場合は自動的に中止される。深夜2時でも人手は不要だ。

ブルー/グリーンのバックグラウンド分析

ブルー/グリーンでは、本番トラフィックが新バージョンに到達する前に、プレビュー環境に対して分析を実行できる:

strategy:
  blueGreen:
    activeService: my-app-active
    previewService: my-app-preview
    autoPromotionEnabled: false
    prePromotionAnalysis:
      templates:
      - templateName: error-rate-check
      args:
      - name: service-name
        value: my-app-preview
    postPromotionAnalysis:
      templates:
      - templateName: error-rate-check
      args:
      - name: service-name
        value: my-app-active

プロモーション前の分析はプレビューに対して実行される。プロモーション後の分析は確認ウィンドウとしてアクティブに対して実行され、失敗した場合は自動ロールバックがトリガーされる。

本番環境での実践的なヒント

1. まず手動プロモーションから始める

最初から自動分析を導入しないこと。まずは仕組みに慣れることが大切だ — 手動でのpromoteコマンドとabortコマンドを体で覚える。自動化に任せる前に、これらの操作が反射的にできるようになっておく必要がある。このステップを省略したオペレーターは、自動化による失敗を読み誤り、本来通すべきでなかったロールバックを上書きしてしまう傾向がある。

2. scaleDownDelaySecondsは必ず設定する

デフォルトは30秒だ。毎秒1,000リクエスト以上の場合は60〜120秒に増やすことを推奨する。この設定に一度助けられたことがある。カナリアがメモリリークを静かに引き起こしていたとき、スパイクはすぐには現れなかった。しかしstableなPodが温かい状態で余分な2分間あったおかげで、リクエストを1つも落とさずにクリーンに中止できた。

strategy:
  blueGreen:
    scaleDownDelaySeconds: 120

3. ダッシュボードを活用する

Argo Rolloutsには読み取り専用のUIが付属しており、インシデント対応時に真価を発揮する:

# ダッシュボードを起動(localhost:3100にポートフォワード)
kubectl argo rollouts dashboard

ステップのビジュアルタイムライン、分析結果、レプリカ数が一目で確認できる。睡眠不足の状態でSlackの通話でデプロイ状況を説明しなければならないとき、色分けされたチャートは生のkubectl出力より何倍も役に立つ。

4. 段階的に移行する — 一度に全部書き直さない

まず1つのサービスを選ぶ。1スプリントの間Rolloutとして運用し、失敗しうるポイントを学んでから拡大する。Argo RolloutsはArgoCDと完全に互換性があり、GitOpsをすでに使っているなら、Rolloutマニフェストを同じリポジトリの他のファイルと並べて配置するだけでよい。

5. 一時停止の動作に注意する

duration未指定のpause: {}は、手動でプロモートするまで無期限に停止する。人間によるサインオフが必要なカナリアに有用だが、落とし穴もある。CI/CDパイプラインがロールアウトのステータスを確認せずにkubectl argo rollouts promoteを呼び出すと、メトリクスに関わらず即座にプロモートされてしまう。

プロモーションステップには必ずガードを設ける:

# プロモート前にロールアウトがhealthyまたはdegradedになるまで待機
kubectl argo rollouts status my-app --timeout 10m

# ステータスが"Paused"の場合のみプロモート
kubectl argo rollouts promote my-app

Argo RolloutsはKubernetesのデプロイメントを、二択のフリップから制御可能で可観測なプロセスへと変える。深夜3時に問題のあるカナリアが自動で中止・ロールバックされる様子を — まだSlackのアラートを読んでいる最中に — 目の当たりにしたとき、これがなぜ重要なのかを実感するだろう。

Share: