L
JavaScript • Literally Web ID

Arsitektur Zero-Downtime Deployment dengan Docker & GitHub Actions

⏱️ 15 menit baca • 📅 12 Oct 2026 • 👁️ 1 views
Arsitektur Zero-Downtime Deployment dengan Docker & GitHub Actions

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap infrastruktur web modern, ketersediaan tinggi (high availability) dan pengiriman fitur tanpa gangguan (zero-downtime deployment) bukan lagi sekadar kemewahan, melainkan mandat bisnis yang mutlak. Setiap detik sistem mengalami pemadaman (downtime) saat proses pembaruan (deployment) berarti hilangnya pendapatan, rusaknya pengalaman pengguna (user experience), dan potensi pelanggaran perjanjian tingkat layanan (SLA).

Secara historis, administrator sistem melakukan deployment manual menggunakan skrip shell sederhana untuk menghentikan aplikasi, menarik kode sumber terbaru, memperbarui dependensi, dan menghidupkan kembali layanan. Pendekatan ini menghasilkan downtime linier yang durasinya sebanding dengan kompleksitas proses build. Ketika arsitektur beralih ke kontainerisasi menggunakan Docker, proses ini memang menjadi lebih konsisten, namun masalah penghentian kontainer lama sebelum kontainer baru siap melayani trafik tetap menjadi akar masalah utama.

Bottleneck klasik dalam ekosistem web modern (seperti PHP-FPM, Node.js, Go, dan Nginx) terjadi pada transisi koneksi. Ketika server web utama dihentikan mendadak untuk diganti dengan versi baru, koneksi TCP yang sedang aktif terputus secara paksa (hard reset), menghasilkan error 502 Bad Gateway atau 504 Gateway Timeout bagi pengguna akhir. Selain itu, manajemen database yang berjalan berdampingan atau terpisah sering kali mengalami kegagalan migrasi skema yang merusak kompatibilitas mundur (backward compatibility).

Perbandingan Kondisi: Sebelum vs Sesudah Penerapan

Parameter Sebelum (Traditional & Naive Docker) Sesudah (Zero-Downtime Blue-Green & GitHub Actions)
Waktu Downtime 5 hingga 30 detik per deployment 0 detik (Continuous Availability)
Penanganan Trafik Koneksi terputus paksa (Dropped Connections) Graceful draining & Reverse proxy routing
Rollback Strategy Manual, rentan human error, memakan waktu Otomatis atau instan via pergantian pointer upstream
Konsistensi Lingkungan Rentan perbedaan konfigurasi lokal vs server Identis mutlak menggunakan immutable Docker image

[!WARNING] Jangan pernah melakukan pull dan restart langsung pada kontainer tunggal di lingkungan produksi tanpa mekanisme reverse proxy health-checking. Hal ini dijamin akan menyebabkan downtime yang merusak reputasi sistem Anda.


2. Konsep Inti & Deep Dive Arsitektur

Untuk mencapai zero-downtime deployment, kita mengadopsi pola Blue-Green Deployment yang dikombinasikan dengan reverse proxy (Nginx/Caddy) dan manajemen kontainer terorkestrasi menggunakan Docker Compose. Inti dari pola ini adalah menjaga dua lingkungan identik secara paralel: Lingkungan Blue (yang sedang aktif melayani trafik produksi) dan Lingkungan Green (lingkungan baru yang memuat versi aplikasi termutakhir).

Alur Kerja Eksekusi (Workflow Under the Hood)

  1. Trigger: Developer melakukan push atau merge kode ke branch main pada repositori GitHub.
  2. CI Pipeline: GitHub Actions Runner memvalidasi kode, menjalankan pengujian otomatis (unit & integration tests), melakukan build image Docker, dan mendorong (push) image tersebut ke container registry (GitHub Container Registry / Docker Hub).
  3. CD Pipeline: GitHub Actions melakukan koneksi SSH aman ke server produksi.
  4. Inisialisasi Lingkungan Pasif: Skrip deployment mendeteksi lingkungan mana yang sedang aktif (misalnya Blue). Jika Blue aktif, maka Green dipilih sebagai target deployment.
  5. Health Check: Kontainer Green dihidupkan melalui Docker Compose. Nginx internal atau skrip pemantau melakukan pengujian kesehatan (HTTP health check pada endpoint /healthz) hingga aplikasi merespons dengan status 200 OK.
  6. Traffic Switching: Konfigurasi Nginx diubah secara dinamis untuk mengarahkan trafik dari upstream Blue ke upstream Green, diikuti dengan perintah reload Nginx secara graceful (nginx -s reload).
  7. Cleanup: Kontainer Blue dimatikan dan dibersihkan untuk mempersiapkan siklus deployment berikutnya.
[ Client / Internet ]
         │
         ▼
┌────────────────────────────────────────┐
│      Nginx Reverse Proxy (Edge)        │
│  (Dynamic Upstream: Blue or Green)     │
└───────────┬────────────────┬───────────┘
            │ (Active)       │ (Standby)
            ▼                ▼
   ┌─────────────────┐  ┌─────────────────┐
   │  Blue Container │  │ Green Container │
   │   (v1.0.0)      │  │   (v1.1.0)      │
   └─────────────────┘  └─────────────────┘

3. Perbandingan Komparatif / Analisis Pendekatan

Dalam membangun sistem deployment otomatis, terdapat tiga pendekatan utama yang sering dipertimbangkan oleh para arsitek infrastruktur:

Analisis Pendekatan Deployment

  1. Rolling Update (Docker Swarm / Kubernetes)

    • Mekanisme: Mengganti instans kontainer secara bertahap satu demi satu.
    • Kelebihan: Efisiensi penggunaan sumber daya (tidak memerlukan duplikasi kapasitas server).
    • Kekurangan: Kompleksitas tinggi dalam manajemen state; risiko ketidakcocokan skema database sementara versi lama dan baru berjalan bersamaan.
    • Trade-off: Menghemat RAM/CPU, namun meningkatkan risiko race condition pada basis data.
  2. Canary Deployment

    • Mekanisme: Mengarahkan sebagian kecil trafik (misal 5%) ke versi baru, lalu meningkatkannya secara bertahap.
    • Kelebihan: Sangat aman untuk mendeteksi bug tersembunyi di produksi dengan dampak minimal.
    • Kekurangan: Membutuhkan infrastruktur traffic shaping yang kompleks (Service Mesh, Istio, atau Load Balancer tingkat lanjut).
    • Trade-off: Kompleksitas operasional yang tinggi, tidak cocok untuk infrastruktur server tunggal berbasis VPS/Docker Compose.
  3. Blue-Green Deployment (Docker Compose + Nginx)

    • Mekanisme: Membawa seluruh sistem baru secara penuh berdampingan dengan sistem lama, lalu melakukan switch instan.
    • Kelebihan: Sederhana, transisi bersih, rollback instan jika terjadi kegagalan dengan membalikkan arah proxy.
    • Kekurangan: Membutuhkan sumber daya komputasi ganda (dua kali lipat kapasitas RAM/CPU saat proses deployment berlangsung).
    • Trade-off: Penggunaan sumber daya sesaat lebih tinggi, namun memberikan kepastian operasional dan kesederhanaan arsitektur.

[!TIP] Untuk arsitektur berbasis VPS atau klaster kecil (skala menengah kebawah), kombinasi Docker Compose dan Nginx Blue-Green adalah solusi paling pragmatis, andal, dan mudah dipelihara dibandingkan kompleksitas Kubernetes.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum menyelam ke dalam implementasi teknis, pastikan server produksi dan lingkungan pengembangan Anda memenuhi spesifikasi berikut:

Spesifikasi Minimum Server Produksi

  • OS: Ubuntu Server 22.04 LTS / Debian 12 (Disarankan kernel Linux >= 5.15)
  • RAM: Minimal 2 GB (Disarankan 4 GB untuk alokasi Blue-Green simultan)
  • CPU: Minimal 2 vCore
  • Software:
    • Docker Engine (Versi >= 24.0)
    • Docker Compose V2
    • Nginx (Sebagai reverse proxy utama di host)
    • Git & OpenSSH Server

Konfigurasi Hak Akses & Keamanan Dasar

Pastikan Anda membuat pengguna khusus untuk keperluan deployment (bukan menggunakan root secara langsung demi keamanan):

# Membuat user deployment
sudo useradd -m -s /bin/bash deployer
sudo usermod -aG docker deployer
sudo usermod -aG sudo deployer

# Konfigurasi SSH key untuk GitHub Actions
sudo mkdir -p /home/deployer/.ssh
sudo chmod 700 /home/deployer/.ssh

5. Panduan Implementasi Langkah demi Langkah

Berikut adalah konfigurasi lengkap berbasis file nyata yang siap diterapkan di lingkungan produksi.

5.1. Struktur Direktori Proyek

/var/www/my-webapp/
├── docker-compose.blue.yml
├── docker-compose.green.yml
├── nginx/
│   ├── conf.d/
│   │   └── app.conf
│   └── nginx.conf
└── .env

5.2. Konfigurasi Docker Compose (Blue & Green)

Kita memisahkan file konfigurasi Docker Compose menjadi dua agar dapat mengontrol port dan nama kontainer secara independen.

File docker-compose.blue.yml:

version: '3.8'

services:
  web_blue:
    image: ghcr.io/username/my-webapp:latest
    container_name: web_app_blue
    restart: always
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DB_HOST=${DB_HOST}
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
    ports:
      - "3001:3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
    networks:
      - app_network

networks:
  app_network:
    external: true

File docker-compose.green.yml:

version: '3.8'

services:
  web_green:
    image: ghcr.io/username/my-webapp:latest
    container_name: web_app_green
    restart: always
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DB_HOST=${DB_HOST}
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
    ports:
      - "3002:3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
    networks:
      - app_network

networks:
  app_network:
    external: true

5.3. Konfigurasi Nginx Reverse Proxy

Nginx bertindak sebagai pengarah trafik utama (upstream switcher). Buat file pointer upstream yang akan dimodifikasi secara otomatis saat deployment.

File /etc/nginx/conf.d/upstream.conf:

# Default mengarah ke Blue (Port 3001)
upstream backend_servers {
    server 127.0.0.1:3001;
    keepalive 32;
}

File /etc/nginx/sites-available/my-webapp.conf:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://backend_servers;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

5.4. Pipeline GitHub Actions (.github/workflows/deploy.yml)

name: Zero-Downtime Deployment

on:
  push:
    branches: [ "main" ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels)
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            latest
            type=sha,format=short

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest

    steps:
      - name: Deploy to Production via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.PRODUCTION_HOST }}
          username: ${{ secrets.PRODUCTION_USER }}
          key: ${{ secrets.PRODUCTION_SSH_KEY }}
          script: |
            cd /var/www/my-webapp
            
            # Login ke GHCR di server produksi
            echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
            
            # Tentukan lingkungan aktif saat ini (blue atau green)
            if docker ps --format '{{.Names}}' | grep -q 'web_app_blue'; then
              CURRENT_ENV="blue"
              NEXT_ENV="green"
              NEXT_PORT="3002"
              OLD_COMPOSE="docker-compose.blue.yml"
              NEW_COMPOSE="docker-compose.green.yml"
            else
              CURRENT_ENV="green"
              NEXT_ENV="blue"
              NEXT_PORT="3001"
              OLD_COMPOSE="docker-compose.green.yml"
              NEW_COMPOSE="docker-compose.blue.yml"
            fi

            echo "Lingkungan aktif saat ini: $CURRENT_ENV"
            echo "Melakukan deployment ke lingkungan baru: $NEXT_ENV"

            # Tarik image terbaru
            docker compose -f $NEW_COMPOSE pull

            # Jalankan lingkungan baru
            docker compose -f $NEW_COMPOSE up -d

            # Health check manual dengan retry
            echo "Menunggu kesehatan aplikasi pada port $NEXT_PORT..."
            RETRIES=30
            until curl -s http://localhost:$NEXT_PORT/healthz | grep -q "status" || [ $RETRIES -eq 0 ]; do
              sleep 2
              RETRIES=$((RETRIES-1))
            done

            if [ $RETRIES -eq 0 ]; then
              echo "Health check gagal! Membatalkan deployment."
              docker compose -f $NEW_COMPOSE down
              exit 1
            fi

            # Ubah upstream Nginx secara dinamis
            echo "upstream backend_servers { server 127.0.0.1:$NEXT_PORT; keepalive 32; }" | sudo tee /etc/nginx/conf.d/upstream.conf
            
            # Reload Nginx secara graceful tanpa memutus koneksi aktif
            sudo nginx -t && sudo nginx -s reload

            # Matikan lingkungan lama
            echo "Membersihkan lingkungan lama ($CURRENT_ENV)..."
            docker compose -f $OLD_COMPOSE down

            echo "Deployment Zero-Downtime berhasil diselesaikan!"

6. Bedah Kode & Penjelasan Detail Baris Kunci

Mari kita bedah beberapa komponen paling krusial dari skrip deployment di atas:

  1. Deteksi Lingkungan Otomatis (docker ps): Skrip memeriksa kontainer mana yang sedang berjalan menggunakan perintah docker ps --format '{{.Names}}'. Jika web_app_blue terdeteksi aktif, skrip secara otomatis mengalihkan target ke green (port 3002). Logika ini membuat pipeline bersifat stateless dan aman dijalankan berulang kali (idempotent).

  2. Health Check Loop & Retry: Perintah until curl -s http://localhost:$NEXT_PORT/healthz memastikan bahwa kontainer baru tidak hanya 'menyala' di tingkat proses Docker, tetapi aplikasi web di dalamnya benar-benar siap menerima HTTP request. Jika aplikasi gagal merespons dalam waktu 60 detik (30 iterasi x 2 detik), skrip membatalkan proses dan mematikan kontainer baru tanpa mengganggu trafik produksi yang masih dipegang oleh lingkungan lama.

  3. Graceful Nginx Reload (nginx -s reload): Perintah ini memerintahkan Nginx untuk memuat ulang file konfigurasi upstream.conf tanpa menutup koneksi TCP yang sedang berlangsung (active worker connections). Prosesor Nginx lama akan menyelesaikan request yang sedang berjalan, sementara prosesor baru langsung menerima koneksi masuk yang baru.


7. Skenario Produksi & Studi Kasus Dunia Nyata

Kasus: Menangani Beban Tinggi (High Concurrency) pada Web Backend Go & Database MySQL

Bayangkan sebuah platform e-commerce dengan trafik puncak mencapai 15.000 request per second (RPS). Saat deployment dilakukan, dua tantangan besar muncul:

  1. Koneksi Database Terlalu Padat (Connection Pooling Exhaustion): Ketika lingkungan Blue dan Green berjalan secara bersamaan selama fase transisi, jumlah maksimum koneksi ke database MySQL berlipat ganda.

    • Solusi: Terapkan PgBouncer (untuk PostgreSQL) atau ProxySQL (untuk MySQL) di antara aplikasi dan database. ProxySQL bertindak sebagai penampung koneksi (connection pooler) terpusat, memastikan bahwa penambahan kontainer tidak melebihi batas max_connections pada server database utama.
  2. Manajemen Sesi Pengguna (Session State): Jika aplikasi menyimpan sesi di memori lokal (in-memory session), pengguna akan mengalami logout saat berpindah dari Blue ke Green.

    • Solusi: Pisahkan status sesi dari kontainer aplikasi. Gunakan Redis Cluster eksternal atau penyimpanan terdistribusi sehingga kontainer Blue dan Green bersifat stateless sepenuhnya.

8. Common Pitfalls, Anti-Patterns & Solusi

Berikut adalah kesalahan fatal yang sering dilakukan oleh pengembang saat merancang sistem deployment otomatis:

  • Anti-Pattern 1: Menggunakan tag latest tanpa kontrol versi spesifik di produksi

    • Dampak: Sulit melakukan rollback akurat jika image terbaru mengalami kegagalan.
    • Solusi: Selalu gunakan tag berbasis Git Commit SHA atau Semantic Versioning pada image Docker.
  • Anti-Pattern 2: Mengabaikan Migrasi Database yang Merusak (Breaking Database Schema Changes)

    • Dampak: Versi aplikasi baru menghapus kolom tabel yang masih diakses oleh versi lama saat masa transisi Blue-Green.
    • Solusi: Terapkan prinsip Expand-Contract Pattern. Lakukan migrasi database dalam beberapa tahap:
      1. Tambahkan kolom/tabel baru (kompatibel dengan versi lama).
      2. Deploy aplikasi baru yang menggunakan kolom baru.
      3. Hapus kolom lama pada deployment berikutnya setelah dipastikan tidak ada lagi dependensi.
  • Anti-Pattern 3: Hardcode Port di dalam Kode Aplikasi

    • Dampak: Kegagalan binding port saat menjalankan dua instans (Blue dan Green) di mesin yang sama.
    • Solusi: Selalu baca port melalui variabel lingkungan (Environment Variables) seperti process.env.PORT atau os.Getenv("PORT").

9. Benchmark, Metrik Kinerja & Tuning Performa

Untuk memastikan sistem zero-downtime Anda benar-benar tahan banting, lakukan pengujian beban (load testing) menggunakan tool modern seperti k6 atau wrk selama proses deployment berlangsung.

Skrip Uji Beban k6 (load-test.js)

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

export const options = {
  stages: [
    { duration: '30s', target: 500 }, // Ramp-up ke 500 virtual users
    { duration: '1m', target: 500 },  // Pertahankan beban selama deployment
    { duration: '30s', target: 0 },   // Ramp-down
  ],
};

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

Jalankan pengujian di terminal saat GitHub Actions sedang melakukan proses deployment:

k6 run load-test.js

Tuning Parameter Sistem Linux (/etc/sysctl.conf)

Untuk menangani trafik tinggi dan mencegah socket exhaustion pada Nginx dan Docker, tambahkan konfigurasi kernel berikut:

# Meningkatkan batas port ephemeral
net.ipv4.ip_local_port_range = 1024 65535

# Mengaktifkan reuse TCP sockets
net.ipv4.tcp_tw_reuse = 1

# Meningkatkan ukuran backlog jaringan
net.core.somaxconn = 65535

Terapkan perubahan dengan perintah:

sudo sysctl -p

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum meluncurkan arsitektur Zero-Downtime Deployment ini ke lingkungan produksi tingkat enterprise, verifikasi seluruh item pada checklist berikut:

  • Docker Network: Pastikan jaringan eksternal Docker (app_network) telah dibuat secara manual di server (docker network create app_network).
  • Health Check Endpoint: Pastikan aplikasi memiliki endpoint /healthz yang mengembalikan kode status HTTP 200 hanya ketika koneksi database dan cache aktif.
  • SSL/TLS & Certbot: Konfigurasi sertifikat SSL Let's Encrypt terpasang dengan benar pada blok server Nginx utama.
  • Secret Management: Seluruh kredensial sensitif (kunci database, token GitHub, SSH Key) disimpan di dalam GitHub Secrets, bukan di dalam kode sumber.
  • Log Rotation: Konfigurasi rotasi log Docker (/etc/docker/daemon.json) diaktifkan untuk mencegah kehabisan ruang disk akibat log kontainer yang membengkak.
  • Backup Otomatis: Skrip backup database harian berjalan sebelum pipeline deployment dieksekusi.

Dengan menerapkan arsitektur Blue-Green berbasis Docker Compose, Nginx, dan GitHub Actions ini, tim Anda tidak hanya mengeliminasi downtime operasional secara total, tetapi juga membangun budaya rekayasa perangkat lunak yang tangguh, dapat diandalkan, dan siap menghadapi skala bisnis global masa depan.

🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server