同じ設定を書き直す二つのコントローラー
一言でいうと
ApplicationSetが作ったApplicationを直接直すと、親が再び元に合わせてしまうことがあります。操作を止めるには、まずそのフィールドを誰が所有しているのかを確認する必要があります。
なぜ必要なのか
障害を調査するために、自動同期を切りました。画面で切れたことを確認して、少し席を外したところ、また有効になっています。 ほかの同僚が有効にしたのかと思って尋ねてみても、誰も触っていないといいます。コントローラーを再起動したり、同じボタンを 何度も押したりしても、問題を説明できません。設定を変えた対象が、最上位の元のデータではなかった可能性があるからです。
GitOpsの調整ループは、1つだけではありません。Application controllerは、Applicationが指定したGitの目標を 実際のリソースに合わせます。ApplicationSet controllerは、generatorとtemplateで作ったApplicationの目標を 実際のApplicationオブジェクトに合わせます。運用者が子のApplicationの自動化フィールドを変更すると、2つ目のループがそれを ドリフトと見なして復元することがあります。Gitから読んだConfigMapを直すことと、Application自体を直すことは、 別の層の変更です。
どう動くのか
このラボでは、list generatorにalphaという項目を1つだけ置きます。サービスの数を増やすラボではなく、所有の関係を 絞って観測するラボです。ApplicationSetのテンプレートはalphaのApplicationを作り、そのApplicationは内部の Gitのapps/alphaディレクトリを読んで小さなConfigMapを作ります。比較の対象として、独立したApplicationと別の ConfigMapも置きます。独立した対象まで一緒に変われば、範囲の取り方が間違っています。
| 調整器 | 目標を読む場所 | 変更する対象 | メンテナンスのときの問い |
|---|---|---|---|
| ApplicationSet controller | generatorとtemplate | Applicationの設定 | 誰がこの自動化の値を書き直しているのか |
| Application controller | Applicationが指定したGit | ConfigMapのような実際のリソース | どのGitコミットを実際に適用しているのか |
ownerReferencesは、1つ目の関係の重要な手がかりです。子のApplicationのownerReferencesでkind、 name、uid、controllerを確認すれば、どのApplicationSetにつながっているかが分かります。名前だけを比較すると、 同じ名前で作り直された親を見逃すことがあるので、UIDも一緒に突き合わせます。ConfigMapは、Argo CDの追跡用の アノテーションを読んで、どのApplicationが管理しているかを区別します。この2種類の所有情報を、同じものとして扱わないでください。
自動同期を明示的に切る値は、spec.syncPolicy.automated.enabledのfalseです。nullやフィールドの 欠落を、falseと同じ意味に読んではいけません。pruneとselfHealがtrueでも、enabled=falseなら自動 同期は切れた状態です。この環境は、enabledを常にブール値で明示して、デフォルト値の推測を避けています。
子にenabled=falseを書いたときに、APIがその変更を受け入れたことと、その状態が維持されることも別です。 このラボでは、patchの応答のfalseとresourceVersionを先に保存し、あとの観測で、同じApplication UIDの enabledがtrueに戻ったことを確認します。最初からfalseが反映されていないリクエストを、復元の実験と 呼ばないためです。resourceVersionは、観測のバージョンを区別する不透明な文字列であり、時間や全体の通し番号を 計算する数値としては使いません。
親のテンプレートでenabled=falseを宣言すると、生成される子の目標そのものが変わります。子のfalseが、もう 親との差ではなくなるので、維持されることがあります。テンプレートは複数の子に共通で適用されることがあるので、実際の チームの環境では、変更の前に、generatorの結果と影響を受けるApplicationの一覧から確認する必要があります。 このラボは子1つに範囲を限定しますが、この事実を、大規模な運用でも1つだけが変わるという意味に一般化してはいけません。
現場での姿
開発環境は自動でデプロイし、検証環境は手動で確認してからデプロイする組織を考えてみてください。1つのテンプレートをすべての 環境に適用したあと、検証環境でだけボタンで切っておくと、元のデータと実際の状態が衝突します。チームが保証しようとしている 運用の方式を、テンプレートや明示的な例外ポリシーで表現する必要があります。誰かが覚えていて、毎回切り直す手順は、 望ましい状態の宣言ではなく、繰り返される手作業です。
調査するときは、次のように、異なる層のドキュメントを読みます。すべて個人のラボVMの中で実行するコマンドです。
kubectl -n argocd get applicationset cgoa-appset-ownership -o json
kubectl -n argocd get application cgoa-appset-ownership-alpha -o json
kubectl -n cgoa-appset-ownership get configmap alpha-config -o json
最初のドキュメントではtemplateとgenerator、2つ目ではownerReferencesと自動化のポリシー、3つ目では実際のデータを 見てください。自動化が切れたという説明が1行あるだけでは、どの調整ループが止まったのかは分かりません。
次のラボですること
まず、APIが子の変更を受け入れた記録と、親が元に戻した記録を比較します。次に、親のテンプレートを直して、 子の設定とGitの変更が適用されたかどうかを別々に見ます。ApplicationSet自体は、ラボのブートストラップ用の宣言ファイルを ヘルパーが適用し、上位のApplicationがそれまでGitから調整する3つ目のループは、今回の範囲には入れません。 この境界を明示して初めて、ラボで直接適用した宣言と、運用での全体的なGitOpsの配線を混同せずに済みます。
公式ドキュメント
- ApplicationSetの変更制御: 子の変更と例外ルールを区別してください。
- 自動同期: enabledのtrue・false・nullの意味を確認してください。