Belajar Backend Developer - Deployment & CI/CD Backend
Episode 15 of 28

Belajar Backend Developer - Deployment & CI/CD Backend

Mengirim image Docker ke produksi dengan aman dan berulang: pipeline CI/CD dengan GitHub Actions, memisahkan environment development, staging, dan production, strategi zero-downtime deployment, serta rollback yang cepat

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Di episode 14 kalian punya image Docker yang siap jalan. Tapi mengirim image ke server dengan ssh + docker run secara manual adalah kebiasaan berbahaya: tidak terdokumentasi, tidak bisa diulang, dan tidak bisa di-audit. Inilah yang diperbaiki CI/CD — pipeline otomatis yang mengetes, membangun, dan men-deploy kode setiap kali berubah.

Mengapa CI/CD penting? Karena deployment manual adalah sumber kegagalan terbesar di tim kecil menengah: "kemarin saya lupa menjalankan satu perintah". Pipeline otomatis menjamin setiap deployment melewati langkah yang sama, ke environment yang tepat, dan bisa di-rollback kapan saja. Episode ini membangun pipeline GitHub Actions lengkap, memisahkan environment, dan menerapkan zero-downtime deployment.

Alur CI/CD

100%
  • CI (Continuous Integration) — otomatis test dan build setiap perubahan kode. Menangkap bug sebelum merge.
  • CD (Continuous Deployment/Delivery) — otomatis mengirim artefak ke environment. Deployment ke production bisa otomatis penuh atau butuh persetujuan manual.

GitHub Actions: Dasar

Setiap pipeline didefinisikan sebagai workflow dalam file YAML di .github/workflows/:

.github/workflows/ci.yml
name: CI
 
on:
  push:
    branches: [main, staging]
  pull_request:
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
        with:
          bun-version: "1.1"
 
      - run: bun install --frozen-lockfile
      - run: bun run lint
      - run: bun run test
 
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Build Docker image
        run: docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
 
      - name: Push ke GHCR
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}

Perhatikan bagian penting:

  • Trigger (on) — workflow berjalan saat push ke main/staging dan saat pull request.
  • Job dependency — job build menunggu test lulus (needs: test).
  • Secrets — token GitHub (GITHUB_TOKEN) dipakai untuk login registry; jangan pernah menulis secret ke dalam file.

Note

Image di-tag dengan commit SHA (github.sha) — setiap commit menghasilkan image unik. Ini membuat deployment bisa dilacak balik ke kode yang persis, dan rollback cukup men-deploy tag lama. Prinsip immutable image dari episode 14 terwujud di sini.

Environment: Staging vs Production

Deployment ke production langsung tanpa uji adalah resiko. Pisahkan environment dengan kebijakan berbeda:

.github/workflows/deploy.yml
name: Deploy
 
on:
  push:
    branches: [main]
 
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Deploy ke staging
        run: ./script/deploy.sh staging ghcr.io/${{ github.repository }}:${{ github.sha }}
 
  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy ke production
        run: ./script/deploy.sh production ghcr.io/${{ github.repository }}:${{ github.sha }}

Di pengaturan GitHub, environment bisa diatur dengan protection rules — misalnya production butuh persetujuan (approved deployments), dan staging harus lulus dulu. Environment juga membawa secrets terpisah: DATABASE_URL staging berbeda dengan production.

Zero-Downtime Deployment

Menjalankan docker rm -f lalu docker run baru berarti downtime — user terganggu, payment gagal. Strategi zero-downtime yang paling sederhana adalah rolling update: jalankan container baru, tunggu sehat, baru matikan yang lama.

Rolling update sederhana
#!/usr/bin/env bash
set -euo pipefail
 
IMAGE="$1"
CONTAINER="shop-api"
 
# 1. Jalankan container baru dengan port baru
docker run -d --name "${CONTAINER}-new" -p 3001:3000 "$IMAGE"
 
# 2. Tunggu sampai sehat (healthcheck episode 14)
for i in {1..30}; do
  if curl -sf http://localhost:3001/health > /dev/null; then
    echo "New container healthy"
    break
  fi
  sleep 2
done
 
# 3. Tukar traffic dan matikan yang lama
docker stop "${CONTAINER}-old" 2>/dev/null || true
docker rename "${CONTAINER}" "${CONTAINER}-old" 2>/dev/null || true
docker rename "${CONTAINER}-new" "${CONTAINER}"
docker start "${CONTAINER}"
docker rm "${CONTAINER}-old" 2>/dev/null || true

Prinsipnya: selalu ada container sehat yang melayani traffic. Kalau container baru gagal healthcheck, yang lama tetap hidup — deployment batal tanpa downtime. (Load balancer di episode 21 mengotomatiskan pola ini; di cloud, gunakan managed service.)

Migrasi Database Saat Deploy

Bagian paling berbahaya dari deployment adalah migrasi database — tabel berubah, dan API baru serta API lama harus tetap kompatibel.

Urutan deploy yang benar
# 1. Jalankan migrasi (bukan breaking) dulu
bun run migrate:up
 
# 2. Baru deploy kode baru
./script/deploy.sh production "$IMAGE"
 
# 3. Setelah semua instance baru sehat, baru cleanup
bun run migrate:cleanup

Aturan expand-migrate-contract: tambahkan kolom (expand) → deploy kode yang membaca kolom baru (migrate) → buang kolom lama (contract). Ini membuat migration backward compatible sehingga rollback tidak merusak data.

Warning

Jangan pernah menjalankan migrasi yang menghapus kolom di deployment yang sama dengan kode baru — saat rollback, kode lama membaca kolom yang sudah hilang dan aplikasi rusak. Migrasi breaking harus dipisah jadi beberapa release. Ini penyebab downtime paling umum di dunia nyata.

Rollback

Deployment yang baik selalu punya jalan keluar. Dua strategi:

  1. Re-deploy image lama — tag ${{ github.sha }} disimpan, deploy SHA sebelumnya. Paling sederhana dan selalu bisa.
  2. Blue-green — dua environment (blue dan green) saling tukar; rollback cukup tukar balik. Cocok untuk yang butuh switch instan.
Rollback ke commit sebelumnya
# image lama masih ada di registry
./script/deploy.sh production ghcr.io/devvnull/shop-api:abc123

Karena image immutable (episode 14), rollback selalu mungkin — tidak ada "state yang sudah terlanjur berubah di server".

Common Pitfalls

Deploy Manual di Produksi

Mengubah server produksi dengan tangan = perubahan tidak terdokumentasi. Semua harus lewat pipeline.

Secret di Workflow YAML

Menulis password langsung di deploy.yml = bocor di git. Selalu pakai GitHub Secrets (episode 17).

Deployment Tanpa Verifikasi

Pipeline yang deploy lalu selesai tanpa cek health = bom waktu. Selalu verifikasi setelah deploy: healthcheck + smoke test alur kritis.

Test Dijalankan Terlambat

CI yang hanya menjalankan test setelah merge di main tidak menangkap apa pun. Jalankan test di setiap pull request — itu inti "continuous".

Tip

Mulai dari yang sederhana: satu workflow ci.yml untuk lint + test di tiap PR, satu workflow deploy.yml untuk build image dan deploy ke staging, dan production dideploy manual-lewat-UI setelah staging disetujui. Jangan langsung membangun CD penuh sebelum staging benar-benar dipakai.

Penutup

Episode 15 mengirim image ke produksi dengan aman: alur CI/CD, pipeline GitHub Actions dengan image bertag SHA, pemisahan environment staging/production, rolling update zero-downtime, migrasi database yang backward compatible, dan strategi rollback.

Inti yang harus dibawa pulang:

  • CI mengetes dan membangun setiap perubahan; CD men-deploy dengan aman.
  • Image di-tag commit SHA agar deployment bisa dilacak dan di-rollback.
  • Environment staging dan production terpisah dengan secrets berbeda.
  • Rolling update: container baru sehat dulu, baru yang lama dimatikan.
  • Migrasi database: expand → migrate → contract; jangan breaking di satu release.
  • Rollback = deploy image lama; image immutable menjamin ini selalu mungkin.

Di episode 16 selanjutnya kita akan naik ke cloud: cloud services backend — compute, managed database, dan storage di AWS, GCP, dan Azure. Sampai jumpa di episode 16!

Belajar Backend Developer - Deployment & CI/CD Backend | Belajar Backend