# Zero-Downtime GitOps Deployment dengan ArgoCD di Kubernetes

> Diterbitkan oleh Literally Web ID pada 21 Oct 2026
> Sumber: https://literally.web.id/articles/zero-downtime-gitops-deployment-dengan-argocd-di-kubernetes

---

Pernahkah kalian mengalami malam jumat yang tenang, tiba-tiba pager bernyanyi melengking, dan dashboard Grafana berubah menjadi lautan merah terang? Ya, malam itu sebuah skrip CI/CD konvensional yang berjalan di GitLab mendadak *stuck* di tengah jalan, meninggalkan setengah pod Kubernetes menjalankan versi v1.2 dan setengah lagi v1.3. Layanan web frontend (Next.js/React) yang dibungkus Nginx tersendat, koneksi WebSocket berjatuhan, API backend Go mencatat error 502 Bad Gateway secara massal, dan database MySQL mengalami lonjakan koneksi akibat retry bad requests. Panik? Tentu saja. Saat itu saya sadar, mengandalkan *push-based deployment* dari luar klaster adalah resep bencana yang menunggu waktu meledak.

Cerita horor di atas bukan sekadar dongeng pengantar tidur bagi DevOps engineer. Di dunia nyata yang menuntut ketersediaan 99.99%, infrastruktur web modern kita—mulai dari edge caching Cloudflare, reverse proxy Nginx, service mesh, hingga pod Node.js dan PHP-FPM—membutuhkan kepastian absolut. Kita butuh sebuah mekanisme di mana klaster Kubernetes itu sendiri yang secara aktif menarik perubahan dari *single source of truth*, menjaga agar kondisi aktual di produksi selalu sinkron dengan deklarasi di repositori Git tanpa intervensi manual yang rentan <i>human error</i>.

Perjalanan menuju kedamaian operasional ini menuntut kita membuang jauh-jauh ego skrip Bash kustom yang rapuh. Mari kita bedah bagaimana cara merancang, membangun, dan mempertahankan *Zero-Downtime GitOps Deployment* menggunakan ArgoCD di Kubernetes, dari teori arsitektur tingkat tinggi hingga baris konfigurasi YAML paling krusial yang menyelamatkan tidur malam kita.

## 1. Titik Awal Masalah: Mengapa Arsitektur Lama Sering Ambruk di Produksi

Bertahun-tahun lalu, standar industri kita berkutat pada *push-based deployment*: Jenkins, GitLab CI, atau GitHub Actions menembakkan perintah `kubectl apply` atau `helm upgrade` langsung ke dalam klaster Kubernetes produksi menggunakan kredensial `kubeconfig` yang disimpan sebagai secret di luar klaster. Masalahnya? Kredensial itu punya hak akses *Cluster Admin* yang maha luas. Sekali token tersebut bocor atau ada kesalahan sintaks pada skrip CI, seluruh klaster bisa rata dalam hitungan detik.

> [!WARNING]
> Menyimpan kredensial `kubeconfig` produksi di dalam sistem CI/CD pihak ketiga adalah pelanggaran keamanan fatal (anti-pattern). Ini membuka celah *lateral movement* bagi penyerang yang berhasil menyusup ke pipeline Anda.

Selain masalah keamanan, *drift state* menjadi hantu harian. Berapa kali Anda mendapati seseorang melakukan *hotfix* manual menggunakan `kubectl edit` di production, yang kemudian tertimpa atau malah merusak siklus deployment otomatis berikutnya? Keadaan klaster akhirnya menjadi "state of confusion" yang hanya bisa dipahami oleh segelintir orang. Kita butuh perubahan radikal: balik arah panahnya. Jangan biarkan CI mendorong perubahan ke klaster, butarkan ArgoCD di dalam klaster untuk menarik (*pull*) perubahan secara konsisten dari Git.

## 2. Di Balik Layar: Bagaimana Sistem Ini Bekerja Tanpa Sihir

ArgoCD beroperasi dengan prinsip dasar *Continuous Delivery* berbasis Kubernetes Controller. Bayangkan ArgoCD sebagai seorang satpam super teliti yang berdiri di dalam klaster Anda, memegang buku catatan berisi *desired state* (kondisi ideal yang didefinisikan dalam repositori Git Anda) dan membandingkannya secara real-time dengan *live state* (kondisi aktual pod, service, ingress, dan database di dalam klaster).

Ketika seorang developer melakukan merge PR ke branch `main` yang mengubah tag image Docker dari aplikasi Go backend kita dari `v1.2.0` ke `v1.3.0`, ArgoCD mendeteksi perbedaan hash commit tersebut melalui webhook atau polling berkala. Tanpa perlu campur tangan manusia, ArgoCD mengeksekusi sinkronisasi. Namun, proses ini bukan sekadar lempar data mentah. ArgoCD bekerja sama erat dengan mekanisme *Rolling Update* bawaan Kubernetes.

Deployment Kubernetes diatur dengan `maxSurge` dan `maxUnavailable`. Ketika pod baru (`v1.3.0`) dibuat, Kubernetes menunggu hingga probe kesehatan (`livenessProbe` dan `readinessProbe`) menyatakan pod tersebut benar-benar siap menerima traffic HTTP. Selama masa transisi ini, load balancer Nginx atau Ingress Controller secara mulus mengalihkan koneksi baru ke pod baru, sementara pod lama (`v1.2.0`) menyelesaikan request yang sedang berjalan (graceful shutdown). Pengguna akhir yang sedang membuka website, melakukan transaksi via API, atau streaming data via WebSocket tidak akan merasakan secuil pun gangguan. Inilah definisi nyata dari *Zero-Downtime*.

## 3. Pilihan Pendekatan & Kompromi yang Harus Diambil

Sebelum mengetikkan perintah `helm install`, kita harus memilih pola arsitektur repositori GitOps. Di lapangan, ada dua aliran utama yang sering diperdebatkan dengan sengit di ruang rapat arsitek:

1. **Monorepo untuk Application Code & Kubernetes Manifests**: Kode sumber aplikasi (React, Node.js, Go) berada dalam satu repositori yang sama dengan folder `deploy/k8s/`. 
   * *Kelebihan*: Sangat mudah bagi developer junior untuk melihat perubahan infrastruktur berdampingan dengan kodenya.
   * *Kekurangan*: Siklus build CI (yang mem-build Docker image) dan CD (yang mengupdate manifest) sering saling mengunci, memicu konflik merge yang menyebalkan.

2. **Separated Repositories (App Repo & GitOps Config Repo)**: Kode aplikasi berada di repo A, sementara manifest Kubernetes (Deployment, Service, Ingress, ArgoCD Application CRD) berada di repo B yang murni dikhususkan untuk konfigurasi klaster.
   * *Kelebihan*: Keamanan ketat. Token CI aplikasi tidak memiliki akses ke repo infrastruktur. Audit trail sangat bersih.
   * *Kekurangan*: Membutuhkan otomatisasi tambahan (biasanya skrip kecil di CI app repo) untuk otomatis mengupdate tag image di repo GitOps config.

> [!TIP]
> Untuk skala enterprise dan lingkungan produksi multi-cluster, selalu pilih **Separated Repositories**. Pisahkan urusan *coding* dari urusan *infrastructure state*. Ini mencegah error aplikasi merusak stabilitas konfigurasi klaster.

## 4. Meracik Lingkungan: Persiapan & Prasyarat Lapangan

Mari kita siapkan tangan kita untuk mengotori terminal. Asumsinya, Anda sudah memiliki klaster Kubernetes yang berjalan (bisa menggunakan Minikube untuk lokal, atau AWS EKS / Google GKE untuk produksi) serta *command line tool* `kubectl` dan `helm` yang terkonfigurasi dengan benar.

Pertama, kita buat *namespace* khusus untuk tempat tinggal sang penguasa GitOps, ArgoCD:

```bash
# Membuat namespace argocd di dalam klaster Kubernetes
kubectl create namespace argocd

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

Setelah proses instalasi pod selesai (pastikan semua pod di namespace `argocd` berstatus `Running`), saatnya mengakses antarmuka web ArgoCD. Secara default, ArgoCD menggunakan tipe servis `ClusterIP`. Untuk memudahkan akses awal di lingkungan staging atau local, kita bisa melakukan *port-forwarding* melalui terminal lokal Anda:

```bash
# Melakukan port forwarding service argocd-server ke localhost:8080
kubectl port-forward svc/argocd-server -n argocd 8080:443
```

Buka browser Anda dan arahkan ke `https://localhost:8080`. Password awal untuk akun `admin` di-generate secara otomatis oleh Kubernetes dan disimpan dalam sebuah secret. Kita bisa mengambilnya dengan perintah satu baris yang elegan ini:

```bash
# Mengambil password admin awal ArgoCD dan mendecodenya dari base64
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo
```

## 5. Langkah Implementasi: Membangun Solusi yang Tangguh

Sekarang ArgoCD sudah siap siaga di dalam klaster. Saatnya mendaftarkan aplikasi web kita (misalnya sebuah layanan backend Go berperforma tinggi yang melayani REST API dan WebSocket) ke dalam manajemen ArgoCD.

Alih-alih menggunakan CLI atau klik-klik di UI web (yang melanggar prinsip deklaratif), kita akan mendefinisikan aplikasi menggunakan Custom Resource Definition (CRD) `Application`. Buat file bernama `app-backend.yaml`:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-backend-api
  namespace: argocd
  # Menambahkan finalizer agar ArgoCD membersihkan resource terkait jika aplikasi dihapus
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: 'https://github.com/perusahaan-anda/k8s-gitops-config.git'
    targetRevision: HEAD
    path: manifests/backend-api
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: production
  syncPolicy:
    automated:
      prune: true     # Menghapus resource di klaster jika dihapus dari Git
      selfHeal: true  # Mengembalikan kondisi klaster jika ada yang mengubah manual via kubectl
    syncOptions:
      - CreateNamespace=true
```

Terapkan manifest tersebut ke klaster:

```bash
# Mendaftarkan deklarasi aplikasi ke controller ArgoCD
kubectl apply -f app-backend.yaml
```

Begitu file ini di-apply, ArgoCD akan langsung meluncur ke repositori GitHub yang ditentukan, mengambil manifest di folder `manifests/backend-api`, dan langsung membangun seluruh resource di namespace `production` secara otomatis!

## 6. Membedah Baris Kode Krusial yang Kerap Menjebak

Banyak pemula terjebak mengira bahwa dengan memasang ArgoCD, urusan *zero-downtime* otomatis beres. Padahal, sihir sebenarnya terletak pada konfigurasi manifest Kubernetes di dalam folder GitOps Anda—khususnya pada strategi deployment dan probe kesehatan aplikasi web.

Mari kita bedah manifest `Deployment` untuk aplikasi backend Go atau Node.js kita yang menjamin tidak ada satupun request pengguna yang terputus saat rilis versi baru:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
  namespace: production
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1       # Boleh membuat 1 pod ekstra di atas kapasitas normal saat update
      maxUnavailable: 0 # HARAM mematikan pod lama sebelum pod baru benar-benar sehat
  selector:
    matchLabels:
      app: backend-api
  template:
    metadata:
      labels:
        app: backend-api
    spec:
      containers:
      - name: api
        image: registry.perusahaan.com/backend:v1.3.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 3
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 10
        lifecycle:
          preStop:
            exec:
              command: ["sh", "-c", "sleep 10"]
```

> [!IMPORTANT]
> Perhatikan blok `lifecycle.preStop` dan `maxUnavailable: 0`. Perintah `sleep 10` memberikan jeda waktu bagi load balancer (seperti Nginx Ingress Controller) untuk memperbarui endpoint list mereka sebelum wadah aplikasi benar-benar dimatikan oleh Kubernetes. Ini adalah rahasia dapur utama untuk menghilangkan error `502 Bad Gateway` secara permanen saat deployment.

## 7. Bertahan di Bawah Tekanan: Skenario Traffic Nyata

Bayangkan skenario nyata: Hari belanja nasional (Harbolnas), traffic melompat 500% dari biasanya. Kunjungan membanjiri CDN Cloudflare, menembus Nginx Ingress, dan menghujani pod backend kita. Tiba-tiba, tim produk mendorong rilis hotfix kecil untuk memperbaiki bug teks di halaman checkout.

Dalam arsitektur GitOps kita, developer hanya perlu mengubah tag image di repo config, lalu commit. ArgoCD mendeteksi perubahan tersebut dalam hitungan detik. Karena kita menggunakan strategi *Rolling Update* dengan `maxSurge: 1`, Kubernetes secara sabar menaikkan satu pod baru versi hotfix, menguji kesehatannya lewat endpoint `/healthz`, memasukkannya ke dalam rotasi traffic load balancer, baru kemudian mematikan satu pod lama dengan anggun.

Jika pada saat yang sama database MySQL mengalami lonjakan latensi query, Kubernetes `readinessProbe` akan otomatis mendeteksi bahwa pod baru gagal merespons endpoint kesehatan tepat waktu. ArgoCD dan Kubernetes akan menahan proses roll-out lebih lanjut, mengisolasi pod yang bermasalah, dan menyelamatkan keseluruhan sistem dari keruntuhan total.

## 8. Jebakan Klasik & Cara Menghindarinya Sebelum Meledak

Selama mengelola puluhan klaster Kubernetes berbasis ArgoCD di berbagai proyek skala besar, saya mencatat beberapa jebakan klasik yang sering menjebak para engineer pemula:

* **Jebakan Infinite Sync Loop**: Terjadi ketika manifest di Git Anda mengandung nilai dinamis (seperti timestamp otomatis atau chart Helm yang merender secret acak di setiap render). ArgoCD akan mengira selalu ada perubahan (*OutOfSync* terus-menerus) dan memicu sinkronisasi tanpa henti. 
  * *Solusi*: Gunakan fitur `ignoreDifferences` di konfigurasi ArgoCD Application untuk mengabaikan field yang sering berubah sendiri.
* **Ketiadaan Resource Limits**: Membiarkan pod berjalan tanpa batasan CPU dan Memory. Saat traffic melonjak, satu pod rakus memakan seluruh memori node, memicu *OOMKilled* massal pada pod lain di node yang sama.
  * *Solusi*: Selalu tetapkan `resources.requests` dan `resources.limits` yang ketat pada setiap kontainer.
* **Mengabaikan Database Migrations**: Menjalankan migrasi skema database secara langsung di dalam kontainer aplikasi saat startup tanpa strategi backward-compatible. Jika pod lama dan pod baru berjalan bersamaan (selama masa *rolling update*), versi lama bisa crash karena membaca struktur tabel baru yang belum sepenuhnya didukung.
  * *Solusi*: Pisahkan migrasi database menjadi Kubernetes `Job` terpisah yang dijalankan *sebelum* deployment utama melakukan rolling update.

## 9. Uji Nyali: Mengukur Performa di Titik Batas

Bagaimana kita tahu bahwa arsitektur GitOps dan Zero-Downtime kita benar-benar tahan banting? Jangan percaya pada teori; lakukan uji tekanan (*load testing*) langsung di lingkungan staging menggunakan tools standar industri seperti `k6` atau `Locust`.

Jalankan skrip pengujian k6 yang menghantam endpoint aplikasi web secara terus-menerus (misalnya 2000 request per detik), lalu di tengah-tengah pengujian, lakukan simulasi rilis versi baru melalui Git commit:

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 100,
  duration: '5m',
};

export default function () {
  const res = http.get('https://staging.perusahaan.com/api/v1/status');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 200ms': (r) => r.timings.duration < 200,
  });
  sleep(0.1);
}
```

Selama pengujian berjalan, pantau metrik di Prometheus/Grafana. Jika persentase error request (`http_req_failed`) tetap di angka `0.00%` saat ArgoCD melakukan sinkronisasi dan pergantian pod, selamat! Arsitektur Zero-Downtime Anda telah teruji di medan perang yang sesungguhnya.

## 10. Catatan Lapangan & Checklist Sebelum Rilis

Sebelum Anda menutup editor kode dan mendeklarasikan sistem siap meluncur ke produksi, luangkan waktu 5 menit untuk memeriksa *checklist* pamungkas dari pengalaman lapangan ini:

1. [ ] Apakah semua manifest Deployment memiliki `readinessProbe` dan `livenessProbe` yang akurat?
2. [ ] Apakah strategi `rollingUpdate` sudah diatur dengan `maxUnavailable: 0` dan `maxSurge > 0`?
3. [ ] Apakah script `preStop` sudah terpasang untuk mengantisipasi pemutusan koneksi mendadak oleh Ngress/Proxy?
4. [ ] Apakah fitur *Auto-Healing* dan *Prune* di ArgoCD Application sudah diaktifkan untuk mencegah *configuration drift*?
5. [ ] Apakah kredensial repositori GitOps terenkripsi dengan aman dan tidak mencampuradukkan token akses aplikasi?

Dengan fondasi GitOps yang disiplin, ArgoCD yang andal, dan konfigurasi Kubernetes yang matang, deployment bukan lagi momen yang menegangkan, melainkan sebuah rutinitas membosankan yang berjalan mulus di latar belakang—memungkinkan kita tidur nyenyak tanpa gangguan pager di tengah malam.
