はじめに
「ジャンルなしオンラインもくもく会 Advent Calendar 2025」の11日目の記事です。
昨日(12/10)はHelmとKustomizeの使い分けでマニフェスト管理を学びました。今日は、GitOpsの実践としてArgoCDを導入し、Gitへのpushだけでクラスタが自動更新される世界を構築します。
本シリーズの全体構成
前回までのおさらい
GitOpsとは?
定義
GitをKubernetesクラスタの唯一の信頼できる情報源(Single Source of Truth)として扱う運用手法
GitOpsの4原則
- 宣言的(Declarative): 望ましい状態をマニフェストで宣言
- バージョン管理・不変(Versioned and Immutable): Gitで全ての変更を追跡し、履歴を改変しない
- 自動取得(Pulled Automatically): クラスタがGitから自動的に状態を取得
- 継続的な調整(Continuously Reconciled): Gitとクラスタの状態を常に一致させる
従来のCI/CDとの違い
【従来のCI/CD】
Git commit → CI/CD Pipeline → kubectl apply → Cluster
↑
認証情報が必要(セキュリティリスク)
【GitOps】
Git commit → Git Repository ← Argo CD (Pull) → Cluster
↑
クラスタ内で実行(認証情報不要)
メリット
- ✅ CI/CDパイプラインにクラスタ認証情報が不要
- ✅ Git履歴 = デプロイ履歴
- ✅ Git revert = ロールバック
- ✅ PRレビュー = 変更レビュー
Argo CD の準備
本記事では、Argo CD がすでにクラスタにインストールされている前提で進めます。
インストール方法について
Argo CD のインストールはkubectl applyや Helm など複数の方法があります。詳細は Argo CD 公式ドキュメント - Getting Started を参照してください。
1. インストール確認
# Pod起動確認
kubectl get pods -n argocd
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 2m
argocd-applicationset-controller-7c4d5d5d9f-xxxxx 1/1 Running 0 2m
argocd-redis-7c4d5d5d9f-xxxxx 1/1 Running 0 2m
argocd-repo-server-7c4d5d5d9f-xxxxx 1/1 Running 0 2m
argocd-server-7c4d5d5d9f-xxxxx 1/1 Running 0 2m
2. 初期パスワード取得
# admin初期パスワード取得
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# 出力例: xYz123AbC456
3. Argo CD UIアクセス
ブラウザで以下にアクセス
- URL: https://argocd.homelab.local
- Username: admin
- Password: (上記で取得したパスワード)
4. Argo CD CLIインストール(オプション)
# macOS
brew install argocd
# Linux
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd
sudo mv argocd /usr/local/bin/
# ログイン
argocd login argocd.homelab.local --username admin --password <your-password>
# パスワード変更
argocd account update-password
GitOps実践: サンプルアプリケーションのデプロイ
シナリオ
Week 1で構築したTalos Linuxクラスタに、デモアプリケーション(Nginx)をGitOpsでデプロイします。
本シリーズで使用しているマニフェストは公開リポジトリで確認できます
https://github.com/r4sd/k8s-advent-2025/tree/main/kubernetes/apps
デモアプリケーションの構成
kubernetes/apps/demo-app.yaml には以下のリソースが含まれています
---
apiVersion: v1
kind: Namespace
metadata:
name: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: nginx-demo-html
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: demo
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-demo
namespace: demo
spec:
ingressClassName: nginx
rules:
- host: demo.homelab.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo
port:
number: 80
Argo CD Application定義
このデモアプリをArgo CDで管理するApplication定義を作成します
argocd/demo-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-app
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/r4sd/k8s-advent-2025
targetRevision: main
path: kubernetes/apps
destination:
server: https://kubernetes.default.svc
namespace: demo
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Argo CDへアプリケーション登録
# Argo CD Applicationをクラスタに適用
kubectl apply -f argocd/demo-app.yaml
# または、Argo CD CLIで登録
argocd app create demo-app \
--repo https://github.com/r4sd/k8s-advent-2025 \
--path kubernetes/apps \
--dest-server https://kubernetes.default.svc \
--dest-namespace demo \
--sync-policy automated \
--auto-prune \
--self-heal
# アプリケーション一覧確認
argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY
demo-app https://kubernetes.default.svc demo default Synced Healthy Auto-Prune
# アプリケーション詳細確認
argocd app get demo-app
動作確認: GitOps フロー
1. Gitリポジトリを変更
# demo-app.yaml のreplica数を変更
vi kubernetes/apps/demo-app.yaml
# replicas: 2 → replicas: 4 に変更
# Commit & Push
git add .
git commit -m "Scale demo-app to 4 replicas"
git push origin main
2. Argo CDが自動検知・デプロイ
# Argo CDは定期的(デフォルト3分)にGitをポーリング
# 変更を検知すると自動的にsync
# リアルタイム確認
watch kubectl get pods -n demo
NAME READY STATUS RESTARTS AGE
nginx-demo-5d8b9d8d9f-xxxxx 1/1 Running 0 5m
nginx-demo-5d8b9d8d9f-yyyyy 1/1 Running 0 5m
nginx-demo-5d8b9d8d9f-zzzzz 1/1 Running 0 10s # 新規追加
nginx-demo-5d8b9d8d9f-aaaaa 1/1 Running 0 10s # 新規追加
3. Argo CD UIで確認
Argo CD UIでは以下が可視化されます
- Application: アプリケーション全体の状態
- Resources: 各Kubernetesリソースの状態(Deployment, Pod, Service, Ingress)
- Sync Status: Gitとクラスタの同期状態
- Health Status: リソースの健全性(Healthy, Progressing, Degraded)
- Events: 同期履歴
実践: homelab環境でのセットアップ
ここからは、実際のhomelab環境でArgo CDをセットアップした手順を紹介します。
1. プライベートリポジトリの登録
GitHubのプライベートリポジトリを使用する場合、認証情報の設定が必要です。
GitHub Personal Access Token (PAT) の選択
| PAT種類 | メリット | デメリット |
|---|---|---|
| Classic | 設定が簡単 | 権限が広範囲 |
| Fine-grained | リポジトリ単位で権限制御 | 設定が細かい |
セキュリティの観点から、Fine-grained PAT を推奨します。
Fine-grained PAT の作成
GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
必要な権限
- Repository access: 対象リポジトリのみ選択
- Permissions:
- Contents: Read-only(必須)
- Metadata: Read-only(自動付与)
⚠️ よくあるエラー
Write access to repository not grantedエラーが出る場合、ContentsのRead権限が付与されていません。GitHubでPATの権限を確認してください。
ArgoCD CLIでリポジトリ登録
# ArgoCD にログイン
argocd login argocd.homelab.local --username admin
# プライベートリポジトリを登録
argocd repo add https://github.com/your-username/your-repo \
--username your-username \
--password ghp_xxxxxxxxxxxx # Fine-grained PAT
# 登録確認
argocd repo list
TYPE NAME REPO INSECURE ... STATUS
git https://github.com/your-username/your-repo false ... Successful
2. Application作成(CLI)
既存のマニフェストをArgoCD管理下に置きます。
# Application作成
argocd app create demo-app \
--repo https://github.com/r4sd/homelab-infra \
--path kubernetes/apps \
--dest-server https://kubernetes.default.svc \
--dest-namespace demo \
--sync-policy none
# 作成確認
argocd app get demo-app
出力例
Name: argocd/demo-app
Project: default
Server: https://kubernetes.default.svc
Namespace: demo
URL: https://argocd.homelab.local/applications/demo-app
Source:
- Repo: https://github.com/r4sd/homelab-infra
Path: kubernetes/apps
Sync Status: OutOfSync from (010c683)
Health Status: Missing
GROUP KIND NAMESPACE NAME STATUS HEALTH
ConfigMap demo nginx-demo-html OutOfSync Missing
Namespace demo OutOfSync Missing
Service demo nginx-demo OutOfSync Missing
apps Deployment demo nginx-demo OutOfSync Missing
networking.k8s.io Ingress demo nginx-demo OutOfSync Missing
ArgoCD がGitリポジトリからマニフェストを読み取り、5つのリソースを検出しました。
3. Sync実行
argocd app sync demo-app
出力例
Name: argocd/demo-app
Sync Status: Synced to (010c683)
Health Status: Progressing
Operation: Sync
Sync Revision: 010c683c41fbef2fdc2957c9b08e2e2489fd8596
Phase: Succeeded
Start: 2025-12-03 19:41:06 +0900 JST
Finished: 2025-12-03 19:41:07 +0900 JST
Duration: 1s
Message: successfully synced (all tasks run)
GROUP KIND NAMESPACE NAME STATUS HEALTH MESSAGE
Namespace demo demo Running Synced namespace/demo created
ConfigMap demo nginx-demo-html Synced configmap/nginx-demo-html created
Service demo nginx-demo Synced Healthy service/nginx-demo created
apps Deployment demo nginx-demo Synced Healthy deployment.apps/nginx-demo created
networking.k8s.io Ingress demo nginx-demo Synced Progressing ingress created
1秒でSync完了し、全リソースが作成されました。
4. Discord通知の確認
通知コントローラのログで、Discordへの通知送信を確認
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-notifications-controller --tail=20
出力例
{"level":"info","msg":"Start processing","resource":"argocd/demo-app","time":"2025-12-03T10:41:07Z"}
{"level":"info","msg":"Trigger on-sync-succeeded result: [{[0].xxx true}]","resource":"argocd/demo-app"}
{"level":"info","msg":"Sending notification about condition 'on-sync-succeeded' to '{discord}'"}
POST https://discord.com/api/webhooks/xxxxxxxxx/yyyyyyyy
{"level":"info","msg":"Processing completed","resource":"argocd/demo-app"}
Discordの #gitops チャンネルに以下のような通知が届きます
✅ Sync Succeeded
Application demo-app synced successfully
Application: demo-app
Namespace: demo
Revision: 010c683
5. 最終状態確認
# Application状態
argocd app get demo-app
# Sync Status: Synced
# Health Status: Healthy
# Pod確認
kubectl get pods -n demo
NAME READY STATUS RESTARTS AGE
nginx-demo-7d57987576-2fd4r 1/1 Running 0 2m
nginx-demo-7d57987576-xkjs6 1/1 Running 0 2m
これでArgoCD による GitOps 運用の基盤が整いました。以降はGitリポジトリへの変更がArgoCD で検知され、Sync時にDiscord通知が送信されます。
高度な設定
1. Webhook設定(即時同期)
デフォルトでは3分ごとのポーリングですが、Webhookを設定することで即時同期が可能です。
📝 homelab環境での注意
GitHub WebhookはGitHubからArgo CDへHTTPリクエストを送る仕組みです。
homelab.localのようなローカルドメインはGitHubから到達できないため、外部公開環境向けの設定です。
GitHub Webhook設定(外部公開環境向け)
# Argo CD Webhook URLを取得
echo "https://argocd.example.com/api/webhook"
# GitHubリポジトリの Settings → Webhooks → Add webhook
# Payload URL: https://argocd.example.com/api/webhook
# Content type: application/json
# Secret: (任意のシークレット)
# Events: Just the push event
2. ApplicationSet: 複数環境の一括管理
複数環境(develop, staging, production)を一つのマニフェストで管理:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: frontend-all-envs
namespace: argocd
spec:
generators:
- list:
elements:
- env: develop
replicas: 1
- env: staging
replicas: 2
- env: production
replicas: 5
template:
metadata:
name: 'frontend-{{env}}'
spec:
project: default
source:
repoURL: https://github.com/your-org/k8s-gitops-demo
targetRevision: main
path: 'applications/frontend/overlays/{{env}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{env}}'
syncPolicy:
automated:
prune: true
selfHeal: true
# ApplicationSet適用
kubectl apply -f argocd/frontend-applicationset.yaml
# 自動的に3つのApplicationが作成される
argocd app list
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH
frontend-develop https://kubernetes.default.svc develop default Synced Healthy
frontend-staging https://kubernetes.default.svc staging default Synced Healthy
frontend-production https://kubernetes.default.svc production default Synced Healthy
3. Progressive Delivery: 段階的ロールアウト
RollingUpdate戦略で環境ごとに段階的にデプロイ
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: frontend-progressive
namespace: argocd
spec:
generators:
- list:
elements:
- env: develop
- env: staging
- env: production
strategy:
type: RollingSync
rollingSync:
steps:
# Step 1: develop環境を先にデプロイ
- matchExpressions:
- key: env
operator: In
values:
- develop
# Step 2: develop成功後、staging環境をデプロイ
- matchExpressions:
- key: env
operator: In
values:
- staging
# Step 3: staging成功後、production環境をデプロイ
- matchExpressions:
- key: env
operator: In
values:
- production
template:
metadata:
name: 'frontend-{{env}}'
labels:
env: '{{env}}'
spec:
project: default
source:
repoURL: https://github.com/your-org/k8s-gitops-demo
targetRevision: main
path: 'applications/frontend/overlays/{{env}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{env}}'
syncPolicy:
automated:
prune: true
selfHeal: true
フロー:
Git Push → dev環境デプロイ → 成功 → staging環境デプロイ → 成功 → production環境デプロイ
4. Sync Waves: リソース作成順序の制御
依存関係のあるリソース(例: Database → Migration → Application)を順番にデプロイ
# 1. Database(Wave 0)
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql
annotations:
argocd.argoproj.io/sync-wave: "0" # 最初にデプロイ
spec:
# ...
---
# 2. Database Migration(Wave 1)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
annotations:
argocd.argoproj.io/sync-wave: "1" # DBの後にデプロイ
spec:
# ...
---
# 3. Application(Wave 2)
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
annotations:
argocd.argoproj.io/sync-wave: "2" # Migrationの後にデプロイ
spec:
# ...
5. Sync Hooks: デプロイ前後の処理
# Pre-Sync Hook: デプロイ前にバックアップ
apiVersion: batch/v1
kind: Job
metadata:
name: database-backup
annotations:
argocd.argoproj.io/hook: PreSync # Sync前に実行
argocd.argoproj.io/hook-delete-policy: HookSucceeded # 成功後削除
spec:
template:
spec:
containers:
- name: backup
image: postgres:15
command: ["pg_dump", "-h", "postgresql", "-U", "admin", "-d", "app"]
restartPolicy: Never
---
# Post-Sync Hook: デプロイ後にテスト
apiVersion: batch/v1
kind: Job
metadata:
name: smoke-test
annotations:
argocd.argoproj.io/hook: PostSync # Sync後に実行
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: test
image: curlimages/curl
command: ["curl", "http://frontend/health"]
restartPolicy: Never
トラブルシューティング
Application が Synced にならない
# Application状態確認
argocd app get demo-app
# 詳細ログ確認
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller
# 手動Sync
argocd app sync demo-app
# Diff確認(Gitとクラスタの差分)
argocd app diff demo-app
Application が Degraded になる
# 各リソースの健全性確認
argocd app resources demo-app
# Podログ確認
kubectl logs -n demo -l app=nginx-demo
# Event確認
kubectl get events -n demo --sort-by='.lastTimestamp'
Git Repository接続エラー
# Repository接続確認
argocd repo list
# 認証情報再設定(HTTPSの場合)
argocd repo add https://github.com/your-org/k8s-gitops-demo \
--username your-username \
--password your-token
# 認証情報再設定(SSHの場合)
argocd repo add git@github.com:your-org/k8s-gitops-demo.git \
--ssh-private-key-path ~/.ssh/id_rsa
Argo CD UI Tips
1. Resource Tree View
Argo CD UIでは、Applicationのリソースがツリー構造で可視化されます:
Application: demo-app
├── Deployment: nginx-demo
│ ├── ReplicaSet: nginx-demo-7d57987576
│ │ ├── Pod: nginx-demo-7d57987576-xxxxx
│ │ └── Pod: nginx-demo-7d57987576-yyyyy
├── Service: nginx-demo
└── Ingress: nginx-demo
2. Sync Options
- Sync: Gitの状態をクラスタに適用
- Refresh: Gitから最新状態を取得
- Hard Refresh: キャッシュをクリアして強制再取得
3. Rollback
Argo CD UIでSync履歴を確認し、任意のリビジョンにロールバック可能:
History:
- Revision 3 (main:abc123) - Scale to 5 replicas (Current)
- Revision 2 (main:def456) - Update image to v1.2.0
- Revision 1 (main:ghi789) - Initial deployment
→ Revision 2にロールバック可能
Discord通知連携
Argo CDのSync状態をDiscordに通知することで、デプロイ状況をリアルタイムで把握できます。
1. Notificationsの有効化
Helm values.yamlで通知機能を有効化
notifications:
enabled: true
# Discord webhook Secret
secret:
items:
discord-webhook-url: "YOUR_DISCORD_WEBHOOK_URL"
# 通知サービス設定
notifiers:
service.webhook.discord: |
url: $discord-webhook-url
headers:
- name: Content-Type
value: application/json
# 通知テンプレート
templates:
template.app-sync-succeeded: |
webhook:
discord:
method: POST
body: |
{
"embeds": [{
"title": "✅ Sync Succeeded",
"description": "Application **{{.app.metadata.name}}** synced successfully",
"color": 3066993,
"fields": [
{"name": "Application", "value": "{{.app.metadata.name}}", "inline": true},
{"name": "Namespace", "value": "{{.app.spec.destination.namespace}}", "inline": true},
{"name": "Revision", "value": "{{.app.status.sync.revision | substr 0 7}}", "inline": true}
]
}]
}
template.app-sync-failed: |
webhook:
discord:
method: POST
body: |
{
"embeds": [{
"title": "❌ Sync Failed",
"description": "Application **{{.app.metadata.name}}** sync failed",
"color": 15158332,
"fields": [
{"name": "Application", "value": "{{.app.metadata.name}}", "inline": true},
{"name": "Message", "value": "{{.app.status.operationState.message}}", "inline": false}
]
}]
}
template.app-health-degraded: |
webhook:
discord:
method: POST
body: |
{
"embeds": [{
"title": "⚠️ Health Degraded",
"description": "Application **{{.app.metadata.name}}** health is degraded",
"color": 15105570,
"fields": [
{"name": "Application", "value": "{{.app.metadata.name}}", "inline": true},
{"name": "Health Status", "value": "{{.app.status.health.status}}", "inline": true}
]
}]
}
# トリガー設定
triggers:
trigger.on-sync-succeeded: |
- when: app.status.operationState.phase in ['Succeeded']
send: [app-sync-succeeded]
trigger.on-sync-failed: |
- when: app.status.operationState.phase in ['Error', 'Failed']
send: [app-sync-failed]
trigger.on-health-degraded: |
- when: app.status.health.status == 'Degraded'
send: [app-health-degraded]
# デフォルトトリガー(全Applicationに適用)
subscriptions:
- recipients:
- discord
triggers:
- on-sync-succeeded
- on-sync-failed
- on-health-degraded
2. 通知の種類
| イベント | 通知内容 | 用途 |
|---|---|---|
| Sync Succeeded | ✅ デプロイ成功 | 正常確認 |
| Sync Failed | ❌ デプロイ失敗 | 即座に対応 |
| Health Degraded | ⚠️ 健全性低下 | Pod起動失敗等 |
3. Discordでの表示例
✅ Sync Succeeded
Application demo-app synced successfully
Application: demo-app
Namespace: demo
Revision: abc1234
まとめ
GitOpsのメリット
- ✅ Declarative: 望ましい状態をGitで宣言
- ✅ Auditable: 全ての変更がGit履歴に記録
- ✅ Reversible: Git revertで即座にロールバック
- ✅ Secure: CI/CDパイプラインにクラスタ認証情報が不要
- ✅ Collaborative: PRレビューで変更を検証
Argo CDの強み
- ✅ UI/CLIの両方で操作可能
- ✅ ApplicationSet で複数環境を一括管理
- ✅ Sync Waves/Hooks で柔軟なデプロイ制御
- ✅ Health Status で各リソースの健全性を監視
- ✅ Webhook/Pollingで自動同期
所感
- 1つの記事でまとめるにはちょっとつらいが、ぼちぼちやっていければと思いました
- 自宅環境をGitOpsにできたことで今まで何をどこまでやったか?から脱却できたのは嬉しいです!!
次回予告
次回(12/17)は、Kubernetesクラスタのセキュリティベストプラクティスを実装します。Pod Security Standards、Network Policy、RBAC、Secrets管理など、本番運用に必須のセキュリティ設定を解説します!
