FlaggerはDeploymentがどうなったらトラフィックシフトを始めてくれるのか？

Kubernetes環境でのカナリーリリースを自動化するFlaggerは非常に便利なツールですが、「Flaggerがどのような条件でカナリーデプロイへのトラフィックシフトを開始するのか」という内部の挙動が気になり調査してみました。本記事では、Flaggerが`HTTPRoute`に対するカナリートラフィックシフト（重み付け）を開始するための条件を、実際のコードから詳しく解説します。


## FlaggerによるHTTPRouteトラフィックシフトの開始条件

トラフィックシフト開始の鍵となるのは、カナリーDeploymentが「Ready」状態であるかどうかです。この検証は以下のフローで行われます：

1. スケジューラーが`advanceCanary`関数を実行（`pkg/controller/scheduler.go`）
2. カナリーの状態を確認
3. `IsCanaryReady`関数によるDeploymentのレディネスチェック（`pkg/canary/deployment_ready.go`）
4. レディネスチェックに合格した場合のみ、トラフィックシフトの検討が開始される

実際のコードでは、以下のように検証が行われています：

```go
// pkg/canary/deployment_ready.go
func (c *DeploymentController) isDeploymentReady(deployment *appsv1.Deployment, deadline int, readyThreshold int) (bool, error) {
    // ...
    
    readyThresholdRatio := float32(readyThreshold) / float32(100)
    readyThresholdUpdatedReplicas := int32(float32(deployment.Status.UpdatedReplicas) * readyThresholdRatio)

    if progress != nil && progress.Reason == "ProgressDeadlineExceeded" {
        return false, fmt.Errorf("deployment %q exceeded its progress deadline", deployment.GetName())
    } else if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas {
        return retriable, fmt.Errorf("waiting for rollout to finish: %d out of %d new replicas have been updated",
            deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas)
    } else if deployment.Status.Replicas > deployment.Status.UpdatedReplicas {
        return retriable, fmt.Errorf("waiting for rollout to finish: %d old replicas are pending termination",
            deployment.Status.Replicas-deployment.Status.UpdatedReplicas)
    } else if deployment.Status.AvailableReplicas < readyThresholdUpdatedReplicas {
        return retriable, fmt.Errorf("waiting for rollout to finish: %d of %d (readyThreshold %d%%) updated replicas are available",
            deployment.Status.AvailableReplicas, readyThresholdUpdatedReplicas, readyThreshold)
    }
    
    // ...
    return true, nil
}
```


このコードから、Deploymentの準備状態チェックにおいて以下の条件が順番にチェックされていることがわかります：

1. プログレスデッドラインを超えていないこと
   - `ProgressDeadlineExceeded`のステータスになっていないこと
   - これに失敗すると、カナリーデプロイは即座にロールバックされます（non-retriable error）
2. すべてのレプリカが最新バージョンに更新されていること
   - `UpdatedReplicas` = `*deployment.Spec.Replicas`
   - すべてのポッドが新しいバージョンでスケジュールされている必要があります
3. 古いレプリカが終了処理中でないこと
   - `deployment.Status.Replicas` = `deployment.Status.UpdatedReplicas`
   - 古いバージョンのポッドが残っていないことを確認します
4. 指定された割合のレプリカが利用可能状態（`Available`）であること
   - `AvailableReplicas` >= `readyThresholdUpdatedReplicas`
   - 実行中のポッドのうち、指定された割合のポッドが`Ready`状態である必要があります

### `canaryReadyThreshold`

最後の条件に関して、重要なカスタマイズポイントとして`canaryReadyThreshold`パラメータがあります：

```yaml
apiVersion: flagger.app/v1beta1
kind: Canary
spec:
  analysis:
    # カナリーレプリカの何%が利用可能である必要があるか
    canaryReadyThreshold: 75
```

このパラメータは、カナリーDeploymentの何パーセントのポッドが「Available」状態になる必要があるかを指定します。デフォルト値は100%です。

例えば：
- レプリカ数10、`canaryReadyThreshold: 75`の場合
- 少なくとも7つのポッド（10×0.75=7.5→7）が`Available`状態である必要があります

## トラフィックシフトが始まらない一般的な原因

以上の分析をふまえると、Deploymentが「Ready」判定されず、トラフィックシフトが開始されないケースには以下のようなものがありそうです。

### 1. コンテナの起動失敗

- `ImagePullBackOff`
- `CrashLoopBackOff`
- `ReadinessProbe`失敗

### 2. リソース不足

- `Pending`状態のポッド (ノードのリソース枯渇など)
- `Unschedulable`

### 3. 設定ミス

- 必要なボリュームがマウントできない
- アプリケーションが起動に必要な環境変数がない
- サービスアカウントの権限不足

### 4. デプロイ進行の停滞

- `ProgressDeadlineExceeded` (デプロイの進行が一定時間内に完了しない)
- 古いレプリカの終了遅延 (前のバージョンのポッドが`Graceful Shutdown`に時間がかかっている)


## おわり

以上、「FlaggerはDeploymentがどうなったらトラフィックシフトを始めてくれるのか？」を調べてみた記事でした。最後までお読みくださりありがとうございました。

Kubernetesエンジニアを募集しているので、よかったら見てみてください！

https://herp.careers/v1/c16e