TT Lab
はじめる
学ぶ 学習パス コース

CAPA — Argoプロジェクト認定アソシエイト

Argoはなぜ四つに割れたのか

TT Labで続きを見る

一言でいうと

Argoは、デプロイツール1つではなく、4つのCNCFプロジェクトの集まりです。Argo CDは「クラスターがGitと同じ状態か」、Workflowsは「これらの作業を決まった順序で最後まで実行せよ」、Eventsは「外で何かが起きたらそのとき」、Rolloutsは「新しいバージョンをどれだけゆっくり出すか」を、それぞれ解決します。4つを1つにまとめなかった理由は、それぞれが扱う時間の性質が違うからです。

なぜ必要なのか

GitOps以前のデプロイは、ほとんどがプッシュでした。CIサーバーがビルドを終えて、kubectl applyを叩きます。この構造には、3つの代償が隠れています。1つ目は、CIサーバーが本番クラスターの認証情報を持っている必要があることです。Jenkinsが1台破られると、すべてのクラスターが破られます。2つ目は、適用したあとに誰かが手でreplicasを変えても、誰も気づかないことです。Gitはデプロイ時点の記録にすぎず、現在の状態の根拠ではありません。3つ目は、クラスターが複数になった瞬間に、パイプラインがクラスター数の分だけ増えることです。

プルモデルは、この3つを一度に覆します。クラスターの中に住むコントローラーがGitを読んで、自分で合わせます。認証情報は外へ出ず、コントローラーが常に比較するのでドリフトが状態として表れ、クラスターが増えてもGitリポジトリは1つです。Argo CDが行うのがまさにこれで、残りの3つは、この軸を中心に付いたサテライトです。

どう動くのか

プロジェクト 解決する問題 時間の性質
Argo CD 宣言した状態と実際の状態の差を、消し続けます 終わらないループ(デフォルトは180秒周期)
Argo Workflows 複数のコンテナ作業をDAGでつなぎ、1回完走させます 始まりと終わりのある有限の実行
Argo Events 外部のシグナルを受けて、何かを開始させます いつ来るかわからないイベント
Argo Rollouts 新しいバージョンをメトリクスを見ながら少しずつ増やします 人の判断が入る数分から数時間

まとめなかった理由は、ここから見えます。Argo CDは「差が0になるまで」回り続けるものなので、完了の概念がありません。Workflowsは、逆に完了がすべてです。この2つを1つのコントローラーに入れると、リトライの意味も、失敗の処理も、メトリクス名も、すべて衝突します。Eventsが別にあるのも同じ理由です。Argo CDにWebhookの処理を入れ始めると、GitHub、S3、Kafka、カレンダーまでが、すべてデプロイのコントローラーの中に入ってきます。Eventsは、そのアダプターの泥沼を、EventSource → Sensor → Triggerという別の軸として切り出したものです。

Argo CDはCNCF Graduatedのプロジェクトです。Graduatedのレベルが意味する実務的な含意は「このAPIが、次の四半期に作り直されることはない」ということで、そのため、Applicationマニフェストを組織の標準にしてもよいという判断の根拠になります。

現場での姿

筆者のホームラボは、コントロールプレーン3台とGPUワーカー4台の、合わせて7ノードです。MetalLBが10.0.0.200から215までをL2で割り当て、その上でGiteaが10.0.0.200、ArgoCDが10.0.0.201、Harborが10.0.0.202で動いています。ソースとGitOpsコントローラーとレジストリが、同じアドレス帯の中に並んでいる形ですが、この配置の本当の価値は、障害が起きたときに表れました。

このクラスターは、controlPlaneEndpointがVIPではなく、最初のコントロールプレーンの物理IPで固定されていました。etcdメンバーは3つなのでクォーラムは健全でしたが、そのノードが落ちると、kubectlもkubeletもすべて接続できなくなります。コントロールプレーンは生きているのに、誰も扉を見つけられない状態です。ここで学んだことが2つあり、1つは「状態がReadyであることと、実際に動作することは別の命題」だということ、もう1つは、クラスターが丸ごと見えない間も、Gitリポジトリには望ましい状態がそのまま残っていたことです。プッシュのパイプラインであれば、復旧手順は「パイプラインを組み直す」でしたが、プルモデルでは、クラスターを復活させた瞬間に、コントローラーが自分で追いつきます。GitOpsを導入する理由を、デプロイの便利さで説明する文章は多いですが、実際に価値が表れる瞬間は、このような復旧の時点です。

4つのプロジェクトが実際に重なる場所

Argoの4つのプロジェクトは、別々に使えますが、一緒に使うと、境界があいまいになる場所ができます。試験でも現場でも、ここで分かれます。

CDとRolloutsは、所有権をめぐってぶつかります。Rolloutsがカナリアを進めている間、Deploymentのreplicasやイメージが変わりますが、CDはそれをGitとの差と見なします。ignoreDifferencesでそのフィールドを除外しないと、CDがカナリアを元に戻すループができます。

WorkflowsとEventsは、「何が引き金か」で分かれます。Workflowsは、ステップのある作業を実行するエンジンで、Eventsは、外部のイベント(Webhook、キューのメッセージ、スケジュール)を受けて、そのエンジンを呼び出す層です。EventsなしでWorkflowsだけを使うと、人が開始する必要があり、WorkflowsなしでEventsだけを使うと、受けたイベントで行うことが単純なものにとどまります。

同期の順序が必要な場所には、フックとウェーブ(wave)を使います。ネームスペースがあってはじめてCRDを入れられ、CRDがあってはじめて、それを使うリソースを入れられます。順序を指定しないと、最初の同期が失敗し、リトライでたまたま成功します。argocd.argoproj.io/sync-waveで順序を書き、マイグレーションのように1回だけ実行すべきものは、PreSyncフックに置きます。

アプリを作るアプリ(App of Apps)は便利ですが、元に戻すのが難しいです。親アプリを削除すると、子も一緒に削除されるかどうかがfinalizerにかかっており、これを知らずに削除して、クラスター全体が空になることが実際に起きます。親と子の削除ポリシーを、明示的に決めておきます。

どちらにしても、本当の難しさは権限です。CDが複数のクラスターにデプロイするには、そのクラスターの権限を持っている必要があり、それはCDを破られるとすべて破られるという意味です。プロジェクトごとに、対象のネームスペースとリソースの種類を制限するAppProjectが、そのため、選択ではなく基本です。

次のクイズで確認すること

このモジュールは、概念だけを扱います。すぐ続くモジュールで、Applicationマニフェストを自分で書きながら、いま述べた「差を消し続けるループ」がどのフィールドで組み立てられるかを、手で確認します。