ステータス:ドラフト(2026-10-17)、一部実測。 基準版 argoproj/argo-cd v3.5.1(コミット 109ca7ca7113)、argoproj/argo-workflows v4.1.5(コミット 6c80c7e56ae6)。 Argo CD は小さなクラスターが同じ版を実行しているため、2026-10-17 に 読み取りのみ で確認しました(Pod・Applicationステータス・設定キー・CronJobステータスフィールド)。 Argo Workflows はそのクラスターに存在しないためコードのみを読みました(未実測)。 作成ルール:実測データが基本であり、測定できなかったものは「未実測」、確認できなかったものは「未確認」と表示します。
目次
- 1. 一行まとめ:「比較するループ」と「Podで働くワーカー」
- 2. 構成図①:Argo CD の比較ループ
- 3. コードマップ
- 4. コアパスをたどる — Argo CD
- 5. コアパスをたどる — Argo Workflows
- 6. 筆者の貢献とつながる場所
- 7. 実測:小さなクラスターの Argo CD
- 8. どこから読むか
- 9. 最初のエントリコントリビューションに向いている場所
- 10. 制限
- 参考
1. 一行まとめ:「比較するループ」と「Podで働くワーカー」
同じArgoという名前を使っていますが、両プロジェクトの性質は異なります。
- Argo CD は サーモスタットです。gitに記述された目標温度(desired state)と室温(クラスターの実際の状態)を継続的に比較し、異なっていれば通知するか(OutOfSync)、合わせにい(sync)。この比較がエンドレスに回るループがコードの中心です。
- Argo Workflows は 工場作業指示書です。ステップを順序・依存関係でまとめたWorkflowを受け取り、ステップごとにPodを1つずつ立ち上げ、ステップ間でやり取りするファイル(アーティファクト)はS3などのストレージを経由して受け渡します。
| コンポーネント | 役割 | どこで動くか |
|---|---|---|
| argocd-repo-server | git·helm 저장소를 받아 helm/kustomize/plain 으로 매니페스트 렌더 | Deployment |
| argocd-application-controller | アプリごとの比較・ステータス判定・sync。クラスターキャッシュを保持 | StatefulSet(シャーディング可能) |
| argocd-server | API・UI・CLIの入口、認証 | Deployment |
| redis | レンダリング結果・アプリリソースツリーキャッシュ | Deployment |
| applicationset-controller | ジェネレーターでApplicationを生成 | Deployment |
| workflow-controller | Workflowのノードステータスマシン、Pod作成 | Deployment |
| argoexec | ステップPod内のinit・wait(またはsupervisor)コンテナ、mainをラップするemissary | ステップPodごと |
2. 構成図①:Argo CD の比較ループ
図の読み方:
- 上から下への1回分の「リフレッシュ」です。repo-serverがレンダリングした desired stateとクラスターキャッシュが保持する actual stateをコントローラーが比較し(②)、リソースごとにステータスを判定して(③)Applicationに書き込みます。
- 同期は別途動作する2番目のキュー(④・⑤)です。比較と適用を異なるキューに分けたのは、時間がかかるsyncが多数のアプリのステータス更新をブロックしないようにするためです。
- クラスターキャッシュはAPIサーバーを毎回照会するのではなく、watchで全リソースをメモリ上に保持しています。比較コストが安くならない代わりに、コントローラーのメモリ消費が大きくなります。
- 一番下のボックスが本記事セクション4.3のテーマ、ステータス判定スクリプトを選択する順序です。
3. コードマップ
v3.5のargo-cdリポジトリには、かつて別個にあった gitops-engine が gitops-engine/ フォルダに取り込まれて います(go.mod の replace … => ./gitops-engine)。
比較・sync・クラスターキャッシュの核心はここにあります。
| パス | 内容 | 最初に読むファイル |
|---|---|---|
argo-cd controller/ |
アプリコントローラー:キュー、比較、ステータス、sync | appcontroller.go, state.go, health.go, sync.go |
argo-cd controller/cache/ |
クラスターキャッシュをアプリの観点でラップしたレイヤー | cache.go |
argo-cd gitops-engine/pkg/ |
cache(watchキャッシュ)、 diff, sync(ウェーブ・フック)、 health(Go内蔵チェック) |
sync/sync_context.go, health/health.go |
argo-cd reposerver/repository/ |
マニフェスト生成、レンダリングキャッシュ | repository.go |
argo-cd resource_customizations/ |
リソース種別ごとのLua(ステータス・アクション)、テストデータ | batch/CronJob/health.lua |
argo-cd util/lua/ |
Luaランナー、スクリプト選択 | lua.go |
argo-workflows workflow/controller/ |
Workflowコントローラー、operator(ステータスマシン)、Pod作成 | controller.go, operator.go, workflowpod.go |
argo-workflows workflow/executor/ |
Pod内で動作するランナー(アーティファクトの保存・ロード) | executor.go |
argo-workflows workflow/artifacts/ |
ストレージドライバー(s3, gcs, azure, oss, hdfs, http, git …) | artifacts.go |
argo-workflows cmd/argoexec/commands/ |
argoexec init·wait·emissary コマンド |
emissary.go |
4. コアパスをたどる — Argo CD
4.1 リフレッシュ:いつ再確認するか
コントローラーにはキューが2つあります(controller/appcontroller.go:118 appRefreshQueue、
121行目 appOperationQueue). appcontroller.go:955 Run これらの2つのキューにワーカーゴルーチンを別々にアタッチします。
- リフレッシュワーカーは
appcontroller.go:1775processAppRefreshQueueItemです。 まずappcontroller.go:2105needRefreshAppStatusで「本当に再比較するタイミングか」を問います。 ユーザーがリフレッシュを押した、管理するリソースが変わった、あるいは指定した周期(デフォルト120秒にジッター最大60秒、くargocd_application_controller.go:45)が経過していれば比較します。 - 比較は
controller/state.go:633CompareAppStateです。この関数が望ましい状態をstate.go:206GetRepoObjsで repo-server から gRPC で取得し、実際の状態をcontroller/cache/cache.go:729GetManagedLiveObjsでキャッシュから取り出した後、 gitops-engine のpkg/diff/diff.go:76Diffでリソースごとに比較します。 - 同じ関数の最後で状態を判定します(
state.go:1051でsetApplicationHealth呼び出し)。4.3節で詳しく見ていきます。 - 自動 sync が有効な場合、
appcontroller.go:2322autoSyncが Application に operation を書き込み、 すると2つ目のキューがそのアプリを取り上げます。
4.2 レンダリングと適用:repo-server と sync
- repo-server の入り口は
reposerver/repository/repository.go:631GenerateManifestです。 キャッシュを先に見る(repository.go:1016getManifestCacheEntry)、なければリポジトリを指定のコミットに合わせた後repository.go:1714GenerateManifestsがrepository.go:1921GetAppSourceTypeで helm・kustomize・plain・プラグインのいずれかを選択してレンダリングします。 レンダリングを別サービスとして切り出した理由はセキュリティとリソースです。helm・kustomize・プラグインは他人のリポジトリの内容を実行する処理であるため、クラスターの認証情報を持つコントローラーから切り離し、 負荷の高いレンダリングは repo-server のみをスケールさせて対応します。 - 適用は
controller/sync.go:102SyncAppStateが gitops-engine のpkg/sync/sync_context.go:478Syncを呼び出します。ここでsync_context.go:954getSyncTasksがリソースをステージ(PreSync・Sync・PostSync フック)とウェーブ番号順に並べ、 1つのウェーブがヘルシーになってから次のウェーブに進みます。「ヘルシーになって」の判断を行うのが、再び 4.3 の状態判定です。 - 実際の状態のキャッシュは、
gitops-engine/pkg/cache/cluster.go:1094syncが最初にすべての API リソースをリストとして取得し、cluster.go:792watchEventsがその後の変更を watch で受け取って維持します。
4.3 状態判定:どのスクリプトが選択するのか
1つのリソースの状態は gitops-engine の pkg/health/health.go:70 GetResourceHealth が決定します。
削除中の場合は Progressing、そうでなければ オーバーライド(override)を先 に確認し、回答がない場合のみ Go で書かれた内蔵チェック
(health.go:104 GetHealthCheckFunc、Deployment・Pod・Job など)へと進みます。
Argo CD 側の「オーバーライド」が Lua です。 util/lua/lua.go:169 GetHealthScript の順序を簡略化すると次のようになります。
key := GetConfigMapKey(obj.GroupVersionKind())
if script, ok := vm.ResourceOverrides[key]; ok && script.HealthLua != "" {
return script.HealthLua, script.UseOpenLibs, nil // argocd-cm 에 사용자가 적은 것
}
// 와일드카드 항목 → 내장 Lua 파일 → 내장 와일드카드 순으로 찾는다
builtInScript, err := vm.getPredefinedLuaScripts(key, healthScriptFile)
出所: argoproj/argo-cd v3.5.1 util/lua/lua.go(GetHealthScript 要約)、Apache-2.0。
内蔵 Lua は resource_customizations/<그룹>/<종류>/health.lua ファイルであり、 resource_customizations/embed.go の
go:embed によってバイナリにまるごと取り込まれます。同じフォルダの health_test.yaml と testdata/ がそのスクリプトのテストです。 新しい CRD の状態判定に貢献する最も簡単な方法は、
このフォルダに1つの Lua とテストデータを追加することです。Go コードを知らなくてもよく、テストは util/lua/health_test.go がフォルダ内の health_test.yaml をすべて見つけて実行します。
このように判定されたリソースの状態は controller/health.go:20 setApplicationHealth が集約してアプリ全体のステータスにします。
最も悪い状態がアプリの状態となり、リソースに argocd.argoproj.io/ignore-healthcheck: "true" アノテーションがある場合は集約から除外されます(41行目)。
v3 ではリソースごとの状態を Application オブジェクトではなく redis のリソースツリーに 格納します。 --persist-resource-health のデフォルトが false だからです
(argocd_application_controller.go:293)。Application が巨大化して etcd への書き込みが重くなるのを避けるための選択です。
4.4 CronJob の事例:ドキュメントと Lua が異なるところ
CronJob の内蔵 Lua は resource_customizations/batch/CronJob/health.lua の56行のコードです。判定は5つの分岐に分かれています。
| 条件(上から順に最初に一致するもの) | 判定 | 行 |
|---|---|---|
spec.suspend == true |
Healthy(「CronJob is Suspended」) — 課題 #24428 が解決されるまでの暫定対応 | 3 |
一度も成功したことがない(lastSuccessfulTime がない) |
Healthy(「never completed successfully」) | 15 |
現在実行中の Job がある(status.active) |
Healthy(「job is running」) — 課題 #24429 | 27 |
| 最後の成功 < 最後のスケジュール | Degraded | 35 |
| その他(スケジュールされたことがない場合を含む) | Healthy | 41·50 |
v3.5.1 版のドキュメント(docs/operator-manual/health.md)では、CronJob のセクションで「最後の Job が失敗した場合は Degraded」、「最後の Job が実行中の場合は Progressing」と記載されており、
Suspended ステータスの例として「停止した CronJob」が挙げられています。コードと比較すると、この3点はすべて食い違っています。失敗した後に次の Job が実行されている場合は Healthy に戻り、実行中の間は Progressing ではなく Healthy であり、停止した CronJob は Suspended ではなく Healthy です。
5. コアパスをたどる — Argo Workflows
workflow/controller/controller.go:337Runが informer とワーカーを起動し、controller.go:922processNextItemが Workflow を1つ取り出して operator を作成します。workflow/controller/operator.go:212operateが1回のリコンサイル(reconcile)となります。 まずoperator.go:1216podReconciliationによってすでに起動しているポッドの状態をノード状態に反映し、operator.go:2235executeTemplateによってエントリーテンプレートから再度たどりながら次にすべきことを決定します。 テンプレートの種類に応じてsteps.go:42executeSteps,dag.go:278executeDAG,operator.go:3435executeContainerに分岐します。毎回最初から辿り直すがすでに完了したノードはスキップする方式であるため、 コントローラーが再起動しても Workflow のstatus.nodesさえあれば処理を継続できます。- コンテナステップは
workflowpod.go:341createWorkflowPodがポッドとして作成します。ユーザーコンテナmainの隣にinit(workflowpod.go:1092newInitContainers)とwaitをアタッチし、mainのコマンドはargoexec emissaryでラップします (cmd/argoexec/commands/emissary.go:46)。v4ではinitなしでsupervisorコンテナ1つがinitとwaitの仕事を一緒に行う方式も導入されました (workflowpod.go:1127newSupervisorContainer,initlessPod設定)。 - Pod内で
waitはmainが終了するのを待ってから(workflow/executor/executor.go:1355Wait),executor.go:449SaveArtifactsで出力ファイルをストレージにアップロードします。 - ストレージドライバーは
workflow/artifacts/artifacts.go:29NewDriverがアーティファクトに記述された種類に応じて選択します。 S3が一番最初(38行目)にあり、アクセスキーはその場でSecretを読み込んで設定します。MinIOはS3ドライバーとして接続します。 - アーティファクトにストレージが記述されていない場合、
workflow/artifactrepositories/artifactrepositories.go:38Resolveが ワークフロー名前空間のデフォルトConfigMap、その次にコントローラー設定のデフォルトストレージの順に探します。
なぜこのように分けたのか。 コントローラーはクラスターに1つですが、ステップは数千個に及びます。重いファイル転送をコントローラーではなくステップPod内の argoexec に任せれば、転送量が増えてもステップPodのみが増加します。その代わりPodごとにサイドカーコンテナが追加されるコストが発生し、v4のsupervisor方式はそのコストを削減しようとする試みと読み取れます(設計意度はドキュメントで確認できず、未確認)。
6. 執筆者の貢献と関連箇所
- argoproj/argo-cd のissue #30209 → #30243。 最初のissue(#30209)は4.4の「稼働していればProgressing」という記述に関するもので、他の貢献者による#30212によってその1行が修正されクローズされました。
残りの3つ(失敗のあとに次のJobが稼働すればHealthy、成功したことがなければHealthy、停止中のCronJobはSuspendedではない)を新しいissue #30243として起票しました。
根拠はいずれも4.4の表、
health.luaの3・15・27・35行目です。 - argoproj/argo-workflows#17246(レビュー中)。アーティファクトストレージのドキュメントのMinIOインストールのセクションが、現在取得できないイメージや大きすぎるデフォルト値を使用しているため、
リポジトリに既に存在するquick-startのMinIO構成(
manifests/quick-start/base/minio)を使うように変更するPRです。第5節の5番(S3ドライバーでMinIOに接続する方法)がドキュメントの説明対象です。 ドキュメントのスペルチェック(cspell)がイメージ名を認識できず、事前に単語を追加する対応もありました。
7. 実測:小規模クラスターにおけるArgo CD
2026-10-17、読み取りコマンドのみを使用しました。アプリ名やリポジトリのアドレスは記載していません。
| 項目 | 取得値 |
|---|---|
| コンポーネント | application-controller(StatefulSet 1)、repo-server、server、applicationset-controller、notifications-controller、redis、dex — すべて v3.5.1(dex・redisを除く) |
| repo-serverの再起動 | 4回(残りは0) |
| Application | 5件、すべて Synced·Healthy |
status.resourceHealthSource |
5件すべて appTree — 4.3の「リソース状態はredisのリソースツリーに」の通り |
argocd-cm の resource.customizations.* キー |
ignoreResourceUpdates.* が8個のみ存在し、 health.* はなし → CronJobは組み込みLuaで判定 |
Applicationの status.resources においてCronJobは44件であり、その health の欄はすべて空でした(上記の appTree のため)。そのため、CronJob 44件の実ステータスフィールド
(spec.suspend, status.lastScheduleTime, lastSuccessfulTime, active)を読み込み、 4.4の表のルールをPythonに移植して 分類しました。入力は実測値であり、判定はLuaを移植した計算によるものです。
| 該当する分岐 | 件数 | 判定 |
|---|---|---|
停止中(suspend: true) |
27 | Healthy |
| 現在Jobが稼働中 | 3 | Healthy |
| 前回の実行が成功 | 14 | Healthy |
| 前回の実行が失敗(稼働中のJobなし) | 0 | Degraded |
44件のうち27件が停止中のCronJobでした。ドキュメント通りであればUIでSuspendedと表示されるべきものがすべてHealthyに混ざっているということであり、 「Suspendedでフィルタリングして確認する」ことでは1件も見つけることができません。この分類はissue #30243の3番目を実際の数値で裏付けています。
8. どこから読むべきか
resource_customizations/batch/CronJob/— Luaの56行目とテストデータ。最も小さな入口です。util/lua/lua.go:169GetHealthScript→gitops-engine/pkg/health/health.go:70.controller/appcontroller.go:1775processAppRefreshQueueItem— 1回のリフレッシュ全体。controller/state.go:633CompareAppState— 長文ですが比較のためのすべての材料が揃います。- Workflowsは
workflow/controller/operator.go:212operateとworkflowpod.go:341createWorkflowPodの2箇所を押さえれば全体像がつかめます。 - 最後に
workflow/artifacts/artifacts.go:29— ドライバー1つ(s3)を選んで最後まで追いかけてみる。
9. 最初のエントリポイントにおすすめの箇所
resource_customizationsにLuaとテストを追加する。 まだステータス判定のないCRDが多くあります。health.lua+health_test.yaml+testdata/*.yamlの3つだけで十分であり、すべてのテストを実行しなくてもgo test ./util/lua/の1つで確認できます(4.3)。- CronJobドキュメントの整理(#30243)。 4.4の表に合わせてドキュメントを修正し、上書きの方法(
resource.customizations.health.batch_CronJob)とignore-healthcheckのコメントを併せて案内する作業です。 メンテナーがissueを受け入れてくれれば、そのままPRにつなげることができます。 - Luaの古い文法。 組み込みLuaの8箇所が
table.getnを使用しています(CronJobの27行目を含む)。実行エンジンがサポートしているため現在は問題ありませんが、#の長さ演算子に変更するリファクタリングは影響範囲が小さいです。 ただし、動作が変わらないことを示すテストを一緒に提出しなければマージされないでしょう。 - Workflowsのドキュメント。 4.1.5のinitなしsupervisor方式はコードに分かりやすい説明が付いていますが、ユーザー向けドキュメントでこの設定を見つけるのは困難でした(今回はドキュメントフォルダを取得していないため未確認)。
- ルール:Argoプロジェクトには生成AIポリシー(argoproj/argoprojの
community/genai.md)があり、Argo WorkflowsのPRテンプレートにはAIの使用を明記する### AIセクションがあります。両方のリポジトリでDCO署名(Signed-off-by)が求められ、 Argo CDにはPRタイトルのフォーマットチェック(.github/pr-title-checker-config.json)と、「テストのないPRはマージされにくい」というチェックリストの項目があります。
10. 制限事項
- Argo Workflowsは小規模クラスターに存在しないため実測していません。supervisor方式、アーティファクトプラグイン、アーカイブ(persist/)はコードの確認のみです。
- Argo CDのシャーディング、ApplicationSetジェネレーター、通知、サーバー側diff、ハイドレーター(commitserver)は扱っていません。
- CronJobの分類はLuaを移植した計算によるものです。Argo CD API経由でリソースツリーの実際の判定を読み込んではいません(ログインが必要なため未実施、未実測)。
ティーティーの一言。 休憩中の選手に「元気です!」とだけ書いておくと、監督が「誰が休んでいるんだ?」と探したときに誰も出てきません。27人も休んでいたのに!
参考
閲覧日はすべて2026-10-17です。
- argoproj/argo-cd v3.5.1:
controller/,gitops-engine/pkg/,reposerver/repository/repository.go,util/lua/lua.go,resource_customizations/batch/CronJob/health.lua,docs/operator-manual/health.md - argoproj/argo-workflows v4.1.5:
workflow/controller/,workflow/executor/executor.go,workflow/artifacts/artifacts.go,workflow/artifactrepositories/artifactrepositories.go - 公開issue・PR:argoproj/argo-cd#30209・#30212・#30243・#24428・#24429、argoproj/argo-workflows#17246
- 引用したコード断片は上記リポジトリのものであり、すべてApache License 2.0です。本記事では短く省略して掲載しています。