DevOps & Cloud

ArgoCD: GitOps for Kubernetes

GRATIS

Kuasai ArgoCD untuk GitOps di Kubernetes β€” Application CRD, sync strategies, RBAC, notifications, ApplicationSet untuk multi-env deployment, dan best practices production

1. Prinsip GitOps

GitOps adalah paradigma deployment di mana Git repository menjadi single source of truth untuk infrastruktur dan aplikasi. Perubahan pada sistem dilakukan melalui pull request, dan tool otomatis (seperti ArgoCD) memastikan bahwa state aktual di cluster sesuai dengan yang didefinisikan di Git.

GitOps Workflow dengan ArgoCD
πŸ‘¨β€πŸ’»
Developer
Push code & update
manifest di Git
β†’ Push β†’
πŸ“
Git Repository
Helm charts / Kustomize
Single source of truth
← Poll/Pull β†’
πŸ”„
ArgoCD
Compare & Sync
Drift detection
Self-healing
β†’ Apply β†’
☸️
Kubernetes
Actual state
sama dengan
desired state

1.1 Empat Prinsip GitOps

  • Declarative: Semua config didefinisikan secara deklaratif (YAML/JSON)
  • Versioned & Immutable: Semua perubahan tercatat di Git history
  • Pulled Automatically: Agent (ArgoCD) menarik perubahan dari Git, bukan push dari CI
  • Continuously Reconciled: Agent terus memastikan actual state = desired state

2. Instalasi ArgoCD

Terminal β€” Install ArgoCD
# Buat namespace
kubectl create namespace argocd

# Install ArgoCD menggunakan manifest resmi
kubectl apply -n argocd \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Tunggu semua pods ready
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s

# Cek status pods
kubectl get pods -n argocd

# Ambil initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

# Install ArgoCD CLI
curl -sSL -o argocd-linux-amd64 \
  https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
rm argocd-linux-amd64

# Login via CLI
argocd login localhost:8080 --username admin --password <password>

# Port-forward untuk akses web UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

2.1 Production Installation dengan Helm

Helm values β€” argo-cd-values.yaml
# values.yaml untuk production ArgoCD
global:
  domain: argocd.example.com

server:
  ingress:
    enabled: true
    ingressClassName: nginx
    tls: true
    annotations:
      cert-manager.io/cluster-issuer: letsencrypt-prod
  replicas: 2
  resources:
    limits:
      cpu: 500m
      memory: 512Mi

controller:
  replicas: 1
  resources:
    limits:
      cpu: "1"
      memory: 1Gi

repoServer:
  replicas: 2
  resources:
    limits:
      cpu: 500m
      memory: 512Mi

redis-ha:
  enabled: true

configs:
  params:
    server.insecure: false  # HTTPS only
  cm:
    url: https://argocd.example.com
    # Enable status badge
    statusbadge.enabled: "true"
Terminal β€” Install via Helm
# Tambahkan ArgoCD Helm repository
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

# Install dengan custom values
helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --values argo-cd-values.yaml \
  --version 7.3.0

3. Application CRD

Application adalah Custom Resource Definition (CRD) utama di ArgoCD yang mendefinisikan hubungan antara Git repository dan target cluster/namespace.

3.1 Basic Application

application.yaml β€” Kustomize App
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
  # Finalizer untuk cascade delete
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://github.com/my-org/k8s-manifests.git
    targetRevision: main
    path: apps/my-app/overlays/production
    # Kustomize specific
    kustomize:
      images:
        - my-app=ghcr.io/my-org/my-app:v1.2.3
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app
  syncPolicy:
    automated:
      prune: true       # Hapus resource yang tidak ada di Git
      selfHeal: true     # Revert manual changes
      allowEmpty: false  # Jangan prune semua resource
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
      - ServerSideApply=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

3.2 Application dengan Helm

application-helm.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx-ingress
  namespace: argocd
spec:
  project: infrastructure
  source:
    repoURL: https://kubernetes.github.io/ingress-nginx
    chart: ingress-nginx
    targetRevision: 4.8.0
    helm:
      releaseName: ingress-nginx
      values: |
        controller:
          replicaCount: 3
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
          service:
            type: LoadBalancer
            annotations:
              service.beta.kubernetes.io/aws-load-balancer-type: nlb
          metrics:
            enabled: true
        defaultBackend:
          enabled: true
      # Values file dari repo
      valueFiles:
        - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: ingress-nginx
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

3.3 Multi-Source Application

application-multi-source.yaml
# Multi-source: Helm chart dari satu repo, values dari repo lain
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: default
  sources:
    # Source 1: Helm chart
    - repoURL: https://charts.my-org.com
      chart: my-app
      targetRevision: 2.0.0
      helm:
        valueFiles:
          - $values/apps/my-app/values-production.yaml
    # Source 2: Values dari repo Git
    - repoURL: https://github.com/my-org/gitops-config.git
      targetRevision: main
      ref: values  # Reference name untuk $values
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app

4. Sync Strategies

Strategy Deskripsi Penggunaan
ManualSync hanya saat diminta manualProduction kritis
Auto (prune)Sync otomatis + hapus resource lamaStaging/Dev
Auto (selfHeal)Sync otomatis + revert manual changesEnforcing GitOps
Auto (full)prune + selfHeal + retryDevelopment cepat
Sync wave dan hooks
# Sync Waves β€” urutan sinkronisasi berdasarkan annotation
# Wave default = 0, semakin tinggi semakin akhir di-sync

# Database harus di-deploy duluan
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: my-app
  annotations:
    argocd.argoproj.io/sync-wave: "-1"  # Sync duluan
spec:
  # ...

---
# Migration harus setelah database ready
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  namespace: my-app
  annotations:
    argocd.argoproj.io/sync-wave: "0"   # Sync kedua
    argocd.argoproj.io/hook: PreSync    # Jalankan sebelum sync utama
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: migrate
          image: my-app:latest
          command: ["./migrate", "up"]
      restartPolicy: Never

---
# App deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: my-app
  annotations:
    argocd.argoproj.io/sync-wave: "1"   # Sync terakhir
spec:
  # ...

# Sync Hooks yang tersedia:
# PreSync    β€” Sebelum apply manifests
# Sync       β€” Selama apply (jarang digunakan)
# PostSync   β€” Setelah semua manifests berhasil
# SyncFail   β€” Saat sync gagal
# Skip       β€” Tidak pernah di-apply ArgoCD
⚠️ Automated Sync di Production

Hindari automated sync untuk production critical apps. Gunakan manual sync dengan approval process. Jika Anda mengaktifkan selfHeal dan seseorang melakukan perubahan manual via kubectl, ArgoCD akan langsung merubahnya kembali. Ini bisa berbahaya jika ada emergency fix yang perlu dilakukan langsung.

5. RBAC & SSO

argocd-rbac-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  # Default policy: read-only
  policy.default: role:readonly

  # Policy definitions
  policy.csv: |
    # Admin β€” full access
    p, role:admin, applications, *, */*, allow
    p, role:admin, clusters, *, *, allow
    p, role:admin, repositories, *, *, allow
    p, role:admin, projects, *, *, allow

    # Developer β€” deploy ke staging, view production
    p, role:developer, applications, get, staging/*, allow
    p, role:developer, applications, sync, staging/*, allow
    p, role:developer, applications, get, production/*, allow
    p, role:developer, logs, get, staging/*, allow

    # SRE β€” full access ke semua environment
    p, role:sre, applications, *, */*, allow
    p, role:sre, clusters, get, *, allow
    p, role:sre, logs, get, */*, allow

    # Assign groups dari SSO
    g, devops-team, role:admin
    g, backend-team, role:developer
    g, sre-team, role:sre

  # Scopes β€” claim yang dibaca dari JWT
  scopes: '[groups, email]'

---
# SSO Configuration (OIDC)
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  url: https://argocd.example.com
  oidc.config: |
    name: Google
    issuer: https://accounts.google.com
    clientID: $argo-cd-oidc:clientID
    clientSecret: $argo-cd-oidc:clientSecret
    requestedScopes:
      - openid
      - profile
      - email
      - groups
    requestedIDTokenClaims:
      groups:
        essential: true

6. Notifications

argocd-notifications-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  # Service configurations
  service.slack: |
    token: $slack-token
    signingSecret: $slack-signing-secret

  service.teams: |
    recipientUrls:
      my-channel: $teams-webhook-url

  # Template definitions
  template.app-sync-succeeded: |
    message: |
      βœ… Application {{.app.metadata.name}} sync berhasil!
      Revision: {{.app.status.sync.revision}}
      Environment: {{.app.spec.destination.namespace}}
    slack:
      attachments: |
        [{
          "color": "#18be52",
          "fields": [{
            "title": "Repository",
            "value": "{{.app.spec.source.repoURL}}",
            "short": true
          },{
            "title": "Revision",
            "value": "{{.app.status.sync.revision}}",
            "short": true
          }]
        }]

  template.app-sync-failed: |
    message: |
      ❌ Application {{.app.metadata.name}} sync GAGAL!
      Error: {{.app.status.operationState.message}}
    slack:
      attachments: |
        [{
          "color": "#f4c542",
          "fields": [{
            "title": "Error",
            "value": "{{.app.status.operationState.message}}",
            "short": false
          }]
        }]

  # Trigger definitions
  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-sync-failed]

---
# Aktifkan notifikasi pada Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
  annotations:
    notifications.argoproj.io/subscribe.on-sync-succeeded.slack: "#deployments"
    notifications.argoproj.io/subscribe.on-sync-failed.slack: "#alerts"
    notifications.argoproj.io/subscribe.on-health-degraded.teams: my-channel
spec:
  # ... konfigurasi application

7. ApplicationSet

ApplicationSet adalah controller yang memungkinkan Anda membuat banyak ArgoCD Application secara dinamis dari satu template. Ini sangat berguna untuk multi-environment dan multi-cluster deployment.

applicationset-multi-env.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-app-multi-env
  namespace: argocd
spec:
  generators:
    # List generator β€” define environments secara manual
    - list:
        elements:
          - env: dev
            cluster: https://dev-cluster.example.com
            revision: develop
            autoSync: "true"
          - env: staging
            cluster: https://staging-cluster.example.com
            revision: main
            autoSync: "true"
          - env: production
            cluster: https://prod-cluster.example.com
            revision: main
            autoSync: "false"
  template:
    metadata:
      name: 'my-app-{{env}}'
      annotations:
        notifications.argoproj.io/subscribe.on-sync-succeeded.slack: '#deploy-{{env}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/my-org/k8s-manifests.git
        targetRevision: '{{revision}}'
        path: apps/my-app/overlays/{{env}}
      destination:
        server: '{{cluster}}'
        namespace: my-app
      syncPolicy:
        automated:
          prune: '{{autoSync}}'
          selfHeal: '{{autoSync}}'
        syncOptions:
          - CreateNamespace=true
  # Strategy untuk rollout update
  strategy:
    type: RollingSync
    rollingSync:
      steps:
        - matchExpressions:
            - key: env
              operator: In
              values:
                - dev
        - matchExpressions:
            - key: env
              operator: In
              values:
                - staging
        - matchExpressions:
            - key: env
              operator: In
              values:
                - production

7.1 Git Directory Generator

applicationset-git-directory.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-apps
  namespace: argocd
spec:
  generators:
    # Scan direktori di Git β€” otomatis buat app
    - git:
        repoURL: https://github.com/my-org/gitops.git
        revision: main
        directories:
          - path: apps/*
          - path: apps/legacy-*  # Exclude pattern
            exclude: true
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/my-org/gitops.git
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

8. Best Practices Production

  • App of Apps / ApplicationSet: Gunakan pattern ini untuk mengelola banyak aplikasi dari satu tempat
  • Separate Config Repo: Pisahkan repo kode aplikasi dengan repo manifest/deployment
  • RBAC Ketat: Jangan berikan admin access ke semua tim
  • Notifications: Setup Slack/Teams notifications untuk sync events
  • Resource Limits: Selalu set resource limits di ArgoCD components
  • Disable Admin User: Setelah SSO aktif, disable admin user default
  • Monitoring: Expose ArgoCD metrics ke Prometheus
Monitoring ArgoCD dengan Prometheus
# ArgoCD sudah expose metrics secara default
# Port-forward untuk melihat metrics:
kubectl port-forward svc/argocd-metrics -n argocd 8083:8083

# ServiceMonitor untuk Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: argocd-metrics
  namespace: argocd
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-metrics
  endpoints:
    - port: metrics
      interval: 30s

# Contoh alert rules
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: argocd-alerts
  namespace: argocd
spec:
  groups:
    - name: argocd
      rules:
        - alert: ArgoAppOutOfSync
          expr: |
            argocd_app_info{sync_status="OutOfSync"} == 1
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "App {{ $labels.name }} out of sync"
        - alert: ArgoAppDegraded
          expr: |
            argocd_app_info{health_status="Degraded"} == 1
          for: 5m
          labels:
            severity: critical

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut:

Pertanyaan 1: Apa fungsi utama selfHeal di ArgoCD sync policy?

a) Menghapus resource yang tidak ada di Git
b) Mengembalikan (revert) perubahan manual di cluster agar sesuai kembali dengan yang di Git
c) Membuat backup otomatis
d) Meng-update image tag secara otomatis

Pertanyaan 2: Apa perbedaan prune dan selfHeal di ArgoCD?

a) Keduanya sama
b) Prune menghapus resource yang dihapus dari Git; selfHeal membatalkan perubahan manual pada resource yang masih ada
c) Prune hanya untuk Helm, selfHeal untuk Kustomize
d) Prune bekerja otomatis, selfHeal harus manual

Pertanyaan 3: Apa fungsi ApplicationSet di ArgoCD?

a) Menggabungkan beberapa cluster menjadi satu
b) Membuat banyak Application secara dinamis dari satu template dengan generator
c) Mengatur RBAC untuk semua aplikasi
d) Monitoring semua aplikasi sekaligus

Pertanyaan 4: Apa yang dimaksud dengan sync wave di ArgoCD?

a) Frekuensi polling Git repository
b) Antrian notifikasi yang dikirim ke Slack
c) Mekanisme untuk mengontrol urutan sinkronisasi resource berdasarkan annotation numerik
d) Jumlah retry saat sync gagal

Pertanyaan 5: Mengapa disarankan tidak menggunakan automated sync untuk production di ArgoCD?

a) Karena ArgoCD tidak mendukung automated sync untuk production
b) Karena automated sync bisa langsung deploy perubahan tanpa approval, berisiko menyebabkan downtime
c) Karena automated sync membutuhkan resource lebih banyak
d) Karena automated sync hanya untuk Helm charts
← Sebelumnya GitLab CI/CD Advanced Pipeline Selanjutnya β†’ Istio Service Mesh
πŸ” Zoom
100%
🎨 Tema