Argo CD(ArgoCD)とは、Gitリポジトリに置いたKubernetesのマニフェストを「あるべき状態」とみなし、クラスターの実際の状態をそれに合わせ続けるGitOps用の継続的デリバリーツールです。デプロイの作業が「Gitにコミットする」ことに置き換わり、誰かが手で変えた設定も自動で元に戻せます。
ここでは執筆時点の最新版Argo CD v3.5.3を、k3s v1.36.4の1ノードクラスターに入れ、自前のGitサーバーに置いたマニフェストから小さなWebアプリを自動で同期させるところまでを、使い捨ての検証環境で確かめました。インストールで出たエラー、手で変えたレプリカ数が戻るまでの時間、メモリの使用量も実測値で載せています。
この記事でわかること
- Argo CDとGitOpsの仕組み、CIから
kubectl applyする方式との違い - k3sへのインストール手順と、そのままでは失敗するCRDの適用エラーの回避策
- Applicationマニフェストの書き方と、自動同期・prune・selfHealの意味
- Helmとは何か、HelmチャートをArgo CDで扱うときの注意
- 初期パスワード・公開範囲・メモリ量など、自宅クラスターで運用するときの注意
Argo CDとは:GitOpsとpush型デプロイの違い
CIのジョブからkubectl applyでデプロイする方式は、CI側がクラスターへの書き込み権限を持ち、変更を押し込む「push型」です。一方のArgo CDはクラスターの中で動き、Gitを定期的に見に行って差分を取り込む「pull型」で、クラスターの認証情報をCIに渡す必要がありません。
| 観点 | push型(CIからkubectl) | pull型(Argo CD) |
|---|---|---|
| クラスターの認証情報 | CIのシークレットに置く | クラスターの外に出さない |
| 誰かが手で変えた設定 | 次のデプロイまで放置される | 差分として検出し、設定しだいで自動で戻す |
| 今の状態の確認 | CIのログをたどる | 画面とCLIで「Gitとの差分」として見える |
| 導入の手間 | ワークフローに数行足すだけ | Argo CD自体の運用(更新・認証・メモリ)が増える |
中身は複数のコンポーネントに分かれています。repo-serverがGitを取得してマニフェストを組み立て(素のYAML、Kustomize、Helmに対応)、application-controllerがクラスターの状態と比べて同期し、argocd-serverが画面とAPIを提供します。Gitの変更は既定で2分ごと(最大1分のゆらぎ付き)に確かめに行くポーリングで検出され、間隔はargocd-cmのtimeout.reconciliationで変えられます(執筆時点の公式ドキュメントの既定値)。すぐに反映させたい場合は、GitHubなどからArgo CDのwebhookを呼ぶ設定を足します。
ArgoCDのインストール:k3sに入れて画面を開く
前提は、kubectl get nodesが通るk3sのクラスターです。まだなければk3sで作る自宅Kubernetesクラスタの手順で用意します。本体は専用の名前空間argocdに、公式のマニフェスト1枚で入ります。
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/v3.5.3/manifests/install.yaml
kubectl -n argocd get podsNAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 98s
argocd-applicationset-controller-84549767db-42cbg 1/1 Running 0 98s
argocd-dex-server-6cc5dd7c9d-8pfp9 1/1 Running 2 (71s ago) 98s
argocd-notifications-controller-57d4c66f69-w9kbh 1/1 Running 0 98s
argocd-redis-c55679569-vlhtf 1/1 Running 0 98s
argocd-repo-server-7f9fdfbb74-w854z 1/1 Running 0 98s
argocd-server-5f785dd555-wtht7 1/1 Running 0 98s--server-side --force-conflictsを付けているのには理由があります。付けずに普通のkubectl applyをすると、検証では次のエラーでApplicationSetのCRDだけが作られませんでした。
The CustomResourceDefinition "applicationsets.argoproj.io" is invalid: metadata.annotations: Too long: may not be more than 262144 bytes通常の(クライアント側の)kubectl applyは、適用した内容を丸ごとkubectl.kubernetes.io/last-applied-configurationという注釈に保存します。ApplicationSetのCRDは定義が大きく、注釈の上限である262144バイトを超えてしまいます。サーバー側で差分を管理するserver-side applyはこの注釈を使わないため、上限に当たりません。URLは版を固定したv3.5.3のものにしており、stableのURLにすると適用した時点の最新版が入るので、再現性が必要な環境では版を明示します。
初期パスワードはSecretに入っています。画面はクラスターの外に公開せず、手元のPCからポートフォワードで開きます。
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d; echo
kubectl -n argocd port-forward svc/argocd-server 8080:443
# ブラウザで https://localhost:8080 を開き、ユーザー admin と上のパスワードでログイン証明書は自己署名なので、ブラウザの警告を確認して進みます。ログインしたら、画面のUser Infoかargocd account update-passwordでパスワードを変え、使い終えたargocd-initial-admin-secretはkubectl -n argocd delete secret argocd-initial-admin-secretで消します(メニュー名は執筆時点のもの)。
検証のために、k3sを本番サーバー上のDockerの特権コンテナで動かすのは避けてください。特権コンテナでは/proc/sysがホストのものになり、kubeletが起動時にホストのカーネルパラメータ(vm.overcommit_memory=1、kernel.panic=10、kernel.panic_on_oops=1)を書き換えます。筆者の環境でも、検証用コンテナを動かしただけでホストの値が変わりました。試すなら専用のVMか検証用のマシンを使い、どうしてもコンテナで動かす場合はk3sに--kubelet-arg=protect-kernel-defaults=trueを渡します。
Gitリポジトリにマニフェストを置く
デプロイするものは、リクエストの内容をそのまま返す小さなWebアプリ(traefik/whoami)にします。リポジトリhomelab-appsのapps/whoami/に、DeploymentとServiceの2ファイルを置いてpushします。
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
labels:
app: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:v1.12.0
ports:
- containerPort: 80
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 32MiapiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app: whoami
ports:
- port: 80
targetPort: 80公開リポジトリなら、Argo CD側の設定はこれだけで読めます。非公開リポジトリの場合は、読み取り専用のデプロイキーを作り、argocd repo addか、ラベルargocd.argoproj.io/secret-type: repositoryを付けたSecretで接続情報を登録します。書き込み権限のあるトークンを渡す必要はありません。
Applicationマニフェストで自動同期する
Argo CDに「どのGitのどのディレクトリを、どのクラスターのどの名前空間へ同期するか」を伝えるのが、Applicationというカスタムリソースです。これ自体もYAMLなので、同じリポジトリに入れておくとArgo CDの設定もGitで履歴が追えます。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: whoami
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/USER/homelab-apps.git
targetRevision: main
path: apps/whoami
destination:
server: https://kubernetes.default.svc
namespace: whoami
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true$ kubectl apply -f whoami-app.yaml
application.argoproj.io/whoami created
$ kubectl -n argocd get applications
NAME SYNC STATUS HEALTH STATUS
whoami Synced Healthy検証(リポジトリは自前のGitea)では、applyから45秒以内にSynced・Healthyになりました。destination.serverのhttps://kubernetes.default.svcは「Argo CDが動いているのと同じクラスター」を表します。syncPolicyの各項目は、有効にしたときとしないときで次のように動作が変わります。
| 設定 | 有効にしたとき | 書かないとき |
|---|---|---|
automated | Gitの変更を検出したら自動で同期する | 差分はOutOfSyncと表示されるだけで、画面やCLIで手動同期する |
prune: true | Gitから消したリソースをクラスターからも削除する | 消したリソースはクラスターに残り続ける |
selfHeal: true | クラスター側を手で変えても、Gitの状態に戻す | 次にGitが変わるまで手作業の変更が残る |
CreateNamespace=true | 宛先の名前空間がなければ作る | 名前空間を先に作っておく必要がある |
prune: trueは、Gitからファイルを消すとクラスターのリソースも消える、という強い設定です。リポジトリのディレクトリ名を変えたつもりがpathの指す場所が空になり、アプリがまるごと消えることもあります。最初はpruneを付けずにOutOfSyncの表示で差分を確かめ、動きに慣れてから有効にするのが安全です。
差分が自動で戻ることを確かめる:selfHeal
GitOpsの効果が一番わかりやすいのは、クラスターを手で変えたときです。Gitではreplicas: 2のDeploymentを、kubectl scaleで5に増やしてみました。
$ kubectl -n whoami scale deploy whoami --replicas=5
deployment.apps/whoami scaled
$ kubectl -n whoami get deploy whoami -o jsonpath='{.spec.replicas}'; echo
25秒後に確認した時点で、レプリカ数はGitの2に戻っていました。selfHealはクラスター側の変更を監視しているため、Gitのポーリング間隔を待たずに戻します。障害対応で一時的に台数を増やしたい場合は、Gitのreplicasを変えてpushするか、そのアプリのselfHealを一時的に外します。「手で直せない」ことが、そのまま「誰がいつ何を変えたかがGitに残る」ことになります。
Gitの側を変えた場合は、次のポーリング(既定で2〜3分以内)で差分が検出されて同期されます。待てないときは画面のRefreshボタンか、argocd CLIのargocd app get whoami --refreshで即座に確認させられます。同期の履歴はargocd app history whoamiで、コミットのハッシュとともに一覧できます(CLIはGitHubのリリースページから入手、執筆時点)。
Helmとは:チャートをArgo CDで使うときの注意
Helmとは、Kubernetesのマニフェスト一式をテンプレートと設定値(values.yaml)にまとめた「チャート」として配布・導入するパッケージマネージャーです。helm installは、テンプレートを展開してクラスターに適用し、その記録をリリースとして残します。cert-managerやGrafana、監視用のPrometheusなど、多くのソフトウェアがHelmチャートで配布されています。
Argo CDはHelmチャートも同期元にできますが、内部ではチャートを展開して得たマニフェストを適用するだけで、Helmのリリースとしては管理しません。そのためhelm listにはアプリが表示されず、ロールバックもHelmではなくArgo CD(Gitを戻す)で行います。値はhelm.valuesObjectにYAMLのまま書けます。次は公開されているpodinfoチャート(執筆時点の最新は6.15.0)を使う例です。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: podinfo
namespace: argocd
spec:
project: default
source:
repoURL: https://stefanprodan.github.io/podinfo
chart: podinfo
targetRevision: 6.15.0
helm:
valuesObject:
replicaCount: 1
destination:
server: https://kubernetes.default.svc
namespace: podinfo
syncPolicy:
syncOptions:
- CreateNamespace=trueチャートの版はtargetRevisionで固定し、上げるときはこの値を変えてコミットします。automatedを付けていないので、この例では同期は画面かCLIから手動で行います。外部のチャートは中身が大きく変わることがあるため、自動同期にする前に一度差分を目で確かめる運用にしています。
自宅クラスターで運用するときの注意
メモリは見積もっておきます。traefik・metrics-server・servicelbを無効にしたk3sの1ノードにArgo CD一式を入れた直後、アプリを1つも同期していない状態で、ノード全体の使用量は約923MiBでした(検証用のコンテナで測った値)。メモリが2GBのミニPCに載せるとアプリの分の余裕がほとんど残りません。画面が不要なら、argocd-serverを含まないcore-install.yaml(application-controller・applicationset-controller・repo-server・redisだけ)も用意されています。
argocd-serverはクラスターのほぼすべてを変更できる管理画面なので、インターネットに直接公開しません。自宅の外から見たいときは、VPNかゼロトラスト型のトンネルの内側に置きます。チームで使う場合は、defaultプロジェクトのままにせず、AppProjectで同期元のリポジトリと宛先の名前空間を絞ると、設定を間違えたApplicationがkube-systemを書き換える、といった事故を防げます。
つまずきポイント
| 症状・エラーメッセージ | 原因 | 対処 |
|---|---|---|
The CustomResourceDefinition "applicationsets.argoproj.io" is invalid: metadata.annotations: Too long: may not be more than 262144 bytes | クライアント側のkubectl applyが大きなCRDを注釈に保存しようとした | --server-side --force-conflictsを付けて適用し直す |
インストール直後にargocd-dex-serverのRESTARTSが増える | 他のコンポーネントの起動待ちで再起動することがある(検証では2回で安定) | 数分待ってRunningが続くか確認する。増え続けるならkubectl -n argocd logsで原因を見る |
kubectl scaleやkubectl editの変更がすぐ元に戻る | selfHeal: trueの正常な動作 | Gitの側を変えてpushする。緊急時はそのアプリのselfHealを外す |
SYNC STATUSがUnknownのまま進まない | リポジトリに接続できない(URLの誤り、非公開リポジトリの認証情報がない) | 画面のアプリ詳細でエラー内容を確認し、URLとargocd repo addの登録を見直す |
動作確認
Applicationが同期済みか、実際にPodとServiceができているかを確かめます。SYNC STATUSがSynced、HEALTH STATUSがHealthyで、DeploymentのREADYがGitに書いたレプリカ数と一致していれば完了です。
$ kubectl -n argocd get applications
NAME SYNC STATUS HEALTH STATUS
whoami Synced Healthy
$ kubectl -n whoami get deploy,svc,pods
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/whoami 2/2 2 2 43s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/whoami ClusterIP 10.43.43.110 <none> 80/TCP 43s
NAME READY STATUS RESTARTS AGE
pod/whoami-54b747cf5c-984kl 1/1 Running 0 43s
pod/whoami-54b747cf5c-mzmb8 1/1 Running 0 43sよくある質問
ArgoCDのSyncとは?
Gitに書かれたマニフェストをクラスターに適用し、実際の状態をGitに一致させる操作です。一致していればSynced、ずれていればOutOfSyncと表示されます。syncPolicy.automatedを設定すると差分の検出時に自動で行われ、設定しなければ画面のSYNCボタンやCLIで手動で実行します。
ArgoCDのPruneとは?
Gitから削除したリソースを、同期のときにクラスターからも削除する機能です。既定では無効で、消したリソースはクラスターに残り、OutOfSyncとして表示されます。automated.prune: trueで自動の削除が有効になりますが、パスの指定ミスでアプリ全体が消えることもあるため、慣れてから有効にします。
GitOpsとは何ですか?
システムのあるべき状態をすべてGitに書き、Gitへのコミットを唯一の変更手段とする運用の考え方です。クラスターの中のツール(Argo CDなど)がGitを監視して状態を合わせ続けるため、変更の履歴・レビュー・取り消しがGitの仕組みでそのまま行えます。
DevOpsとGitOpsの違いは何ですか?
DevOpsは、開発と運用が協力して変更を速く安全に届けるための文化や進め方の総称です。GitOpsはその中の具体的なやり方の1つで、インフラやアプリの状態をGitで管理し、ツールで自動的に反映させる方法を指します。GitOpsを採らないDevOpsの実践もあります。
まとめ
Argo CDの導入は、--server-side付きでマニフェストを適用し、Applicationを1つ書くところまでは30分ほどで済みます。難しいのはその後の運用で、pruneとselfHealをどこまで自動にするか、画面をどこまで公開するか、メモリをどれだけ割けるかを先に決めておくと、自宅クラスターでも無理なく続けられます。
次に読む
- GitHub Actionsで始めるCI/CD:イメージのビルドはCI、デプロイはArgo CDと役割を分ける
- SREとは?DevOps・プラットフォームエンジニアリングとの違い:GitOpsを支える運用の考え方
- Tailscaleで自宅サーバーに安全にリモート接続する:Argo CDの画面を外に公開せずに使う