Referensi cepat GitOps dan Argo CD. Prinsip GitOps, Application spec, sync policy, health check, app-of-apps, dan penanganan secrets. Perfect buat platform engineer.
GitOps adalah cara mengelola infrastruktur di mana Git jadi satu-satunya sumber kebenaran. Kamu mendeklarasikan state yang diinginkan di repository, lalu sebuah controller yang berjalan di dalam cluster yang menyinkronkan state nyata ke deklarasi itu. Perubahan produksi terjadi lewat pull request, bukan lewat kubectl apply manual dari laptop seseorang.
Empat prinsip yang biasa dirujuk (dari OpenGitOps):
Push vs pull, ini perbedaan yang sering bikin bingung:
| Aspek | Push (CI-based) | Pull (GitOps) |
|---|---|---|
| Pemicu deploy | Job CI menjalankan kubectl | Controller di cluster menarik dari Git |
| Kredensial cluster | CI butuh akses ke cluster | Cluster yang punya akses ke Git, CI tidak |
| Audit trail | Tersebar di log CI | Revisi Git = revisi cluster |
| Drift detection | Tidak ada, manual | Bawaan, controller terus bandingkan |
| Rollback | Jalankan ulang job lama | git revert lalu sync |
Dengan model pull, CI hanya bertugas build, test, dan mengupdate manifest (misalnya image tag baru di repo config). Deploy adalah urusan controller di dalam cluster.
Argo CD adalah controller Kubernetes yang mengimplementasikan GitOps. Dia watch repo Git, bandingkan manifest dengan state live cluster, lalu tampilkan statusnya sebagai Synced atau OutOfSync. Instalasi standar lewat manifest resmi di namespace argocd.
kubectl create namespace argocd
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Password admin awal disimpan di secret ini
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -dCLI argocd dipakai untuk operasi sehari-hari: login, buat aplikasi, sync, dan lihat status.
argocd login localhost:8080 --username admin --insecure
argocd app list
argocd app get guestbook
argocd app sync guestbookApplication adalah CRD utama Argo CD. Satu Application memetakan satu path di repo Git ke satu destination namespace di cluster.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://gitlab.com/my-group/gitops-config.git
targetRevision: main
path: apps/guestbook
destination:
server: https://kubernetes.default.svc
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueTabel field penting di spec:
| Field | Fungsi |
|---|---|
source.repoURL | Repository tempat manifest hidup |
source.targetRevision | Branch, tag, atau commit SHA yang dipakai |
source.path | Path dalam repo ke direktori manifest |
destination.server | Cluster tujuan, kubernetes.default.svc untuk cluster lokal |
destination.namespace | Namespace tujuan di cluster |
project | AppProject yang membatasi repo dan cluster yang boleh dipakai |
syncPolicy | Aturan sync otomatis dan opsi perilaku |
Sumber manifest yang didukung: plain YAML directory (kustomize tanpa konfigurasi), Kustomize, Helm charts, Jsonnet, dan plugin lainnya via config management plugins. Untuk Helm, konfigurasi values bisa langsung di spec:
source:
repoURL: https://gitlab.com/my-group/charts.git
path: charts/guestbook
helm:
releaseName: guestbook
valueFiles:
- values.yaml
- values-prod.yaml
parameters:
- name: image.tag
value: v1.4.2Argo CD tidak render helm template berulang-ulang tiap reconcile. Dia merender sekali lalu membandingkan hasil render dengan cluster, jadi behavior helm upgrade (seperti state di Secret untuk release) tidak berlaku di sini.
Dua istilah ini beda dan sering tertukar. Sync status menjawab: apakah live state cocok dengan Git? Health status menjawab: apakah aplikasi itu hidup dan sehat?
| Status sync | Arti |
|---|---|
| Synced | Live state sama dengan target di Git |
| OutOfSync | Ada resource yang beda antara Git dan cluster |
| Unknown | Status tidak bisa ditentukan, biasanya masalah komunikasi |
| Status health | Arti |
|---|---|
| Healthy | Semua resource sehat |
| Progressing | Sedang menuju state sehat, masih menunggu |
| Degraded | Ada resource gagal, contoh pod CrashLoopBackOff |
| Suspended | Sengaja dihentikan, contoh CronJob suspended |
| Missing | Resource di Git tapi tidak ada di cluster |
Argo CD punya built-in health check untuk tipe standar: Deployment, StatefulSet, Service, DaemonSet, ReplicaSet, Pod, PDB, dan lainnya. Contoh Deployment dianggap Healthy kalau semua replica up to date dan tersedia. Untuk CRD (seperti Argo Rollouts atau cert-manager Certificate), kamu bisa definisikan health check sendiri via Lua script di ConfigMap argocd-cm:
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
resource.customizations.health.example.com_MyCRD: |
hs = {}
if obj.status ~= nil and obj.status.ready == true then
hs.status = "Healthy"
else
hs.status = "Progressing"
end
return hsHealth aggregate Application dihitung dari semua resource anaknya. Satu resource Degraded bikin seluruh app Degraded, dan status resource Missing dihitung lebih ringan daripada Degraded.
Sync policy menentukan seberapa banyak Argo CD bertindak otomatis. Defaultnya manual: kamu klik Sync di UI atau jalankan argocd app sync. Sync otomatis dinyalakan lewat syncPolicy.automated.
syncPolicy:
automated:
prune: true # hapus resource yang hilang dari Git
selfHeal: true # kembalikan perubahan manual di cluster
allowEmpty: falsePerilaku penting yang perlu kamu ingat:
prune: true, resource yang dihapus dari Git tidak dihapus dari cluster. App jadi OutOfSync terus dengan resource extra. Penghapusan baru terjadi saat manual sync dengan centang prune.selfHeal: true membatalkan perubahan manual di cluster (misalnya kubectl scale) dan mengembalikan ke state Git. Tanpa ini, edit manual bikin app OutOfSync tapi dibiarkan sampai sync berikutnya.allowEmpty: true mengizinkan app punya nol resource, misalnya saat path di Git dikosongkan sengaja. Defaultnya false sebagai pengaman supaya salah hapus file tidak menghapus semua workload.selfHeal aktif.Sync options mengatur perilaku detail saat sync berjalan, level Application atau per resource:
syncPolicy:
syncOptions:
- CreateNamespace=true
- PruneLast=true
- ApplyOutOfSyncOnly=true
- Validate=false
- PrunePropagationPolicy=foreground| Opsi | Efek |
|---|---|
CreateNamespace=true | Bikin namespace tujuan kalau belum ada |
PruneLast=true | Hapus resource setelah yang lain selesai diterapkan |
ApplyOutOfSyncOnly=true | Hanya apply resource yang OutOfSync, hemat waktu app besar |
PrunePropagationPolicy=foreground | Kebijakan penghapusan: foreground, background, atau orphan |
ServerSideApply=true | Pakai server-side apply, berguna untuk resource besar |
Validate=false | Lewati validasi kubectl client-side |
RespectIgnoreDifferences=true | Hormati ignoreDifferences saat apply, bukan hanya diff |
Untuk menghindari resource tertentu di-prune, annotasi di resource itu:
metadata:
annotations:
argocd.argoproj.io/sync-options: Prune=falseUntuk batasi kapan deploy boleh terjadi (misalnya cuma jam kerja), pakai sync windows yang didefinisikan di AppProject. Rollback ke versi sebelumnya bisa lewat UI atau CLI:
argocd app rollback guestbook 42 # rollback ke history id 42Catatan: rollback menonaktifkan auto-sync sementara supaya tidak langsung ditimpa kembali oleh Git.
Dalam satu sync, resource bisa diurutkan pakai annotasi argocd.argoproj.io/sync-wave. Angka kecil diterapkan lebih dulu, dan Argo CD menunggu resource sehat sebelum lanjut ke wave berikutnya. Default wave adalah 0.
# Namespace dan CRD dulu (wave -2, -1)
apiVersion: v1
kind: Namespace
metadata:
name: guestbook
annotations:
argocd.argoproj.io/sync-wave: "-2"
---
# Database sebelum aplikasi (wave 0)
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
annotations:
argocd.argoproj.io/sync-wave: "0"
---
# Aplikasi terakhir (wave 1)
apiVersion: apps/v1
kind: Deployment
metadata:
name: guestbook
annotations:
argocd.argoproj.io/sync-wave: "1"Sync hooks memakai annotasi argocd.argoproj.io/hook dengan fase PreSync, Sync, PostSync, SyncFail, atau Skip. Job dengan hook PreSync jalan sebelum resource lain diterapkan, cocok untuk migrasi database:
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
argocd.argoproj.io/hook: PreSyncKalau kamu punya puluhan Application, kelola semuanya dari satu Application induk yang berisi manifest Application lain. Ini namanya app-of-apps, dan induknya sering disebut root app.
# root-app.yaml: Application yang menunjuk ke direktori berisi Application lain
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: https://gitlab.com/my-group/gitops-config.git
targetRevision: main
path: bootstrap
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: trueDirektori bootstrap/ berisi file Application untuk tiap app (guestbook, monitoring, ingress, dan seterusnya). Root app mensync manifest tersebut, efeknya Argo CD membuat atau mengubah Application anak. Satu git push bisa menambah atau menghapus seluruh app di cluster.
Keterbatasan yang perlu kamu tahu: health check bawaan untuk CRD Application dihapus sejak Argo CD 1.8. Kalau kamu ingin root app menunggu anak-anaknya sehat (berguna digabung dengan sync wave), tambahkan Lua health check untuk argoproj.io/Application di ConfigMap argocd-cm, contohnya ada di docs bagian Resource Health.
Untuk membuat banyak app sekaligus dengan template (misalnya satu app per cluster atau per environment), ApplicationSet adalah penerus modern dari app-of-apps. Generatornya (list, git, cluster, matrix, dan lainnya) bikin satu ApplicationSet bisa menghasilkan ratusan Application dengan parameter berbeda.
AppProject membatasi apa yang bisa dilakukan sekumpulan Application: repo mana yang boleh jadi sumber, cluster mana yang boleh jadi tujuan, namespace mana yang diizinkan, dan objek Kubernetes apa saja yang bisa dibuat. Ini kontrol penting saat banyak tim berbagi satu Argo CD.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-frontend
namespace: argocd
spec:
description: Aplikasi milik tim frontend
sourceRepos:
- https://gitlab.com/frontend/*
destinations:
- server: https://kubernetes.default.svc
namespace: frontend-*
clusterResourceWhitelist:
- group: ''
kind: NamespaceArgo CD bisa mengelola banyak cluster sekaligus. Daftarkan cluster dengan argocd cluster add, lalu pilih destination di Application. Secrets koneksi cluster disimpan sebagai Secret di namespace argocd dengan label argocd.argoproj.io/secret-type: cluster.
argocd cluster add my-context --label env=prod
argocd cluster listMasalah klasik GitOps: kalau Git sumber kebenaran, dan secrets tidak boleh masuk Git, bagaimana secrets sampai ke cluster? Jawabannya jangan pernah commit secret polos ke repo config. Enkripsi atau injeksi saat runtime.
Pola yang umum dipakai:
| Tools | Cara kerja | Cocok untuk |
|---|---|---|
| Sealed Secrets (Bitnami) | Secret dienkripsi dengan kunci publik, hanya controller di cluster yang bisa dekrip | Repo publik atau tim yang sudah pakai kubectl |
| SOPS + age atau KMS | File terenkripsi di Git, Argo CD dekripsi via plugin atau KSOPS | Multi-cluster, kunci di KMS cloud |
| External Secrets Operator | SecretStore membaca dari vault eksternal (AWS Secrets Manager, Vault), bikin Secret di cluster | Secrets yang sering rotasi |
| Vault Agent Injector | Pod menerima secret lewat sidecar saat start | Aplikasi yang sudah pakai Vault |
Contoh paling sederhana pakai Sealed Secrets:
# Install controller dulu di cluster
kubectl apply -f \
https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.3/controller.yaml
# Bikin sealed secret yang aman di-commit
echo -n 's3cr3t-value' | kubectl create secret generic api-key \
--from-file=api-key=/dev/stdin --dry-run=client -o yaml > secret.yaml
kubeseal -f secret.yaml -w sealed-secret.yaml
# Commit sealed-secret.yaml ke repo GitOps, hapus secret.yamlArgo CD sendiri punya beberapa batasan soal secrets yang jarang diberitahu:
Prune=true atau selfHeal=true. Pola yang benar: kelola Secret lewat tool khusus (di atas), lalu exclude dari Argo CD dengan ignoreDifferences atau masukkan resource ke luar cakupan app.readOnly tidak melihat isi Secret.# Abaikan perbedaan field tertentu, misalnya yang diubah webhook lain
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicasargocd app get guestbook # ringkasan sync + health per resource
argocd app diff guestbook # lihat perbedaan Git vs live
argocd app wait guestbook # tunggu sampai synced dan healthy
argocd app history guestbook # riwayat sync untuk rollbackKasus yang sering muncul:
last-applied-configuration) atau HPA mengubah spec.replicas. Solusinya ignoreDifferences atau sync option RespectIgnoreDifferences=true.kubectl describe pod, penyebab umum image pull lama, resource node kurang, PVC tidak binding, atau init container tidak selesai.argocd app set guestbook --sync-policy automated.Alur lengkap dari kode sampai deploy dengan GitOps:
# 1. Developer push kode fitur, CI build dan test
# 2. CI build image dan push ke registry
docker build -t registry.example.com/app:v1.4.2 .
docker push registry.example.com/app:v1.4.2
# 3. CI (atau developer) update image tag di repo config
cd gitops-config/
yq -i '.images[0].newTag = "v1.4.2"' apps/guestbook/kustomization.yaml
git commit -am "Bump guestbook ke v1.4.2"
git push
# 4. Merge request direview, setelah merge Argo CD menarik perubahan
argocd app get guestbook # verifikasi Synced + HealthyLangkah 3 adalah inti GitOps: perubahan produksi selalu lewat commit yang ter-review. Image promotion hanyalah update satu baris tag di repo config.
prune: true dan selfHeal: true untuk environment non-kritis, manual untuk produksitargetRevision ke branch atau tag eksplisit, jangan HEAD dari mirror defaultApplyOutOfSyncOnly=true untuk app dengan ratusan resource| Istilah | Arti |
|---|---|
| GitOps | Praktik kelola infrastruktur dengan Git sebagai sumber kebenaran |
| Reconciliation | Proses membandingkan state live dengan state target lalu memperbaiki deviasi |
| Application | CRD Argo CD yang memetakan repo ke destination cluster |
| AppProject | CRD yang membatasi sumber, tujuan, dan resource yang diizinkan |
| Sync | Tindakan menerapkan manifest Git ke cluster |
| Prune | Menghapus resource yang sudah tidak ada di Git |
| Self-heal | Mengembalikan perubahan manual di cluster ke state Git |
| Drift | Perbedaan antara state Git dan state live |
| Sync wave | Urutan penerapan resource dalam satu sync |
| Sync hook | Job yang berjalan di fase tertentu (PreSync, PostSync) |
| App-of-apps | Pattern Application yang berisi Application lain |
| ApplicationSet | Generator yang membuat banyak Application dari template |
| Sealed Secret | Secret terenkripsi yang aman disimpan di Git |
| Root app | Application induk paling atas dalam app-of-apps |
| Health status | Penilaian kondisi aplikasi: Healthy, Degraded, Progressing |
| Sync status | Penilaian kesesuaian Git vs cluster: Synced, OutOfSync |
Login atau daftar akun gratis untuk membaca cheat sheet ini.