Argo CDとAWS Load Balancer Controllerに起因するWorkflow失敗
登場人物
Workflow = Argo Workflowsによって利用可能なカスタムリソースで、バッチ処理に使っている
できごとの流れ
- AWS Load Balancer ControllerをデプロイするApplicationのキャッシュミス
- AWS Load Balancer Controllerチャートの
helm template
実行 -
helm template
の結果、自動生成される証明書に差分が生じ、Syncが必要だとArgo CDが判断 - Syncが実行され完了する
- コンテナ内の証明書ファイルが更新される前にPod作成リクエストが送られ、Webhook呼び出しが失敗し、Podの作成が拒否される
失敗防止の方法
- Webhookの
objectSelector
/namespaceSelector
で本当に必要なPodのみWebhook呼び出しの対象にする - 一時的なエラーのためWorkflow側で自動リトライする
- ただし、1回だけのリトライでは早すぎてまだコンテナ内の証明書が更新されていなかった。複数回リトライするのがよさそう
-
retryStrategy.backoff
も活用する - https://argoproj.github.io/argo-workflows/retries/
-
微妙)WebhookのFailure PolicyをIgnore
にする- 今回作成を拒否されたPodはPod Readiness Gateを利用する意図はなかったので、Workflowの失敗は流れ弾ではある(別にそのWebhookが失敗しようがどうでもいいからPod作成させてよと)。たしかにFailure Policyが
Ignore
であればWorkflowが失敗させられることはなかったが、Ignore
に設定してしまうと今度はReadiness Gateを利用したいPodで機能が有効にならないリスクが生じる。 - 一度は微妙と書いたが、「Pod作成できない」と「Pod作成できるがReadiness Gateは有効ではない」だと前者のほうが困り度が高いので悪くはない
- 今回作成を拒否されたPodはPod Readiness Gateを利用する意図はなかったので、Workflowの失敗は流れ弾ではある(別にそのWebhookが失敗しようがどうでもいいからPod作成させてよと)。たしかにFailure Policyが
- 微妙)証明書の自動生成を利用せず自前で管理する
- 面倒
-
管理する証明書が多ければcert-managerを使ってしっかりやるのもあり- ただcert-managerを使うだけでは証明書が無効な期間がゼロになるわけではない
アラート
WorkflowのイベントWorkflowFailed
を監視していたところアラートが鳴った。
WorkflowNodeError
-> WorkflowNodeFailed
-> WorkflowFailed
の流れで発生したようだ。
発端のWorkflowNodeError
の内容はこうだった。
Internal error occurred: failed calling webhook "mpod.elbv2.k8s.aws": Post "https://aws-load-balancer-webhook-service.kube-system.svc:443/mutate-v1-pod?timeout=10s": x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "aws-load-balancer-controller-ca")
同じ内容がworkflow-controllerのログにも出力されていた(WARNログ)。
これはAWS Load Balancer ControllerによるMutating Admission Webhookが失敗したためにPodの作成が拒否され、Workflowが進行不能となったことを意味する。
Podの作成が拒否された理由
WebhookのFailure PolicyがFail
に設定されているため(Ignore
であった場合はWebhookが失敗してもPodの作成は拒否されない)。
これはMutatingWebhookConfiguration
のwebhooks.failurePolicy
フィールドに書かれている。
Webhookの失敗理由
クライアント(kube-apiserver)が期待するauthorityと、サーバー(aws-load-balancer-controller)が提示した証明書のauthorityが違っていたため。
クライアントが期待するauthorityとは、MutatingWebhookConfiguration
のwebhooks.clientConfig.caBundle
フィールドのこと。
サーバーが提示する証明書のauthorityは、aws-load-balancer-tls
というSecret
のdata.ca.crt
フィールドのこと(コンテナでこのSecretをマウントしている)。
authorityが違っていた理由
AWS Load Balancer ControllerのWebhook用証明書の管理方法にはいくつか選択肢があるが、MutatingWebhookConfiguration
のcaBundle
とSecret
のca.crt
の中身は一致するようにHelmチャートは作られているので、これらの内容が違っていることは本来はないはずである。
ただ、この2つが一致していてもエラーが発生しうるケースがある。
SecretやConfigMapをコンテナ内にファイルとしてマウントすると、コンテナの再作成をしなくてもSecretやConfigMapの更新は自動的にコンテナ内のファイルへ反映されるが、反映には1分ほどのタイムラグがある。
今回、このラグの間にWebhook呼び出しが行われたことにより、エラーを引き起こした。
以下のログ時系列から、そのように判断した。
-
Sep 04 15:02:57.184
argocd-application-controllerでApplicationのSync中のログ- 具体的なログ
updated 'アプリケーション名' operation (phase: Running)
- 具体的なログ
-
Sep 04 15:02:58.184
ApplicationのSync完了のログ- 具体的なログ
updated 'アプリケーション名' operation (phase: Succeeded)
- 具体的なログ
-
Sep 04 15:03:00.886
workflow-controllerでfailed calling webhook
のログ
ただし、9月4日は日曜日で、ApplicationをSyncした覚えはない。
休日に勝手にSyncされた理由
証明書が生成される仕組み
AWS Load Balancer ControllerをHelmチャートを利用してデプロイする場合、特定の条件下ではWebhookに使用する証明書が自動的に生成される。
特定の条件とは、
.Values.webhookTLS.caCert
.Values.webhookTLS.cert
.Values.webhookTLS.key
を入力していない
.Values.keepTLSSecret
を入力していない(※追加条件あり)
我々の環境は「特定の条件」に合致していた(=上記どちらの分岐にも入らなかった)ので、デプロイのたびに証明書は再生成されることになる。
追加条件について
.Values.keepTLSSecret
はv2.3.0でデフォルトfalse
で導入されたフラグで、v2.4.0でデフォルトtrue
に変更された。
我々はデフォルト値のまま使用していたので、エラー発生時点ではフラグはtrueだった。
フラグがtrue
であり、Helmのtemplate function lookup
がクラスター上のSecretを取得できる場合には証明書の生成が行われず、すでにあるものが利用されるが、
lookup
はHelmコマンドを直接利用する場合のうち、さらに限定されたケースでのみ機能するため、Argo CDを使ってHelmチャートをデプロイする我々の環境では常に再生成が行われていた。
具体的にはArgo CDがhelm template
を実行するたびに再生成され、「DiffがあるのでSyncしますね」となる。
helm templateが実行された理由
ApplicationのtargetRevision
にはブランチ名を指定しており、先頭のコミットが更新された場合はキャッシュが無効化されhelm template
が実行されるが、日曜日なので誰もpushしていない。
application-controllerのapp-hard-resync
オプションも使っていない。
が、repo-serverのログを見てみると普通にキャッシュミスが起きただけだった。
-
Sep 04 15:02:55.038
manifest cache miss
ログ -
Sep 04 15:02:57.025
helm template
実行ログ
キャッシュに関してデフォルトの設定で利用しており、リポジトリ全体のキャッシュ(repo-serverの--default-cache-expiration
フラグ)は24時間、リビジョンのキャッシュ(argocd-cmのtimeout.reconciliation
)は3分で期限切れになる。
関連