Tampilan
Kenapa penting
Di modul ini kamu sudah punya image Docker, konfigurasi lewat environment, Actuator, metrics, dan tracing. Semuanya belum berarti banyak bila rilis masih dilakukan dengan mvn package di laptop lalu menyalin JAR ke server lewat scp. Cara manual seperti itu lambat, rawan lupa menjalankan test, dan sulit dilacak: "versi mana yang sekarang jalan di production?" CI/CD mengubah setiap perubahan kode menjadi proses yang otomatis, berulang, dan teraudit: test berjalan di setiap pull request, image dibangun dengan tag yang jelas, dan deploy bisa di-rollback dalam hitungan menit. Di hampir semua lowongan backend, "terbiasa dengan CI/CD" tercantum sebagai syarat — dan saat insiden, pipeline yang baik adalah alat penyelamatmu.
Konsep inti
CI, continuous delivery, dan continuous deployment
Tiga istilah ini sering tertukar. Bedanya ada di seberapa jauh otomatisasi berjalan.
| Istilah | Yang diotomatisasi | Rilis ke production |
|---|---|---|
| Continuous integration | Build + test setiap push/PR, merge sering ke main | Tidak dibahas |
| Continuous delivery | CI + artefak (image) siap rilis kapan saja, deploy staging | Manual (tombol/izin) |
| Continuous deployment | Semua di atas, termasuk deploy production | Otomatis bila lulus |
Inti CI adalah umpan balik cepat: kalau test gagal, kamu tahu dalam beberapa menit, bukan saat rilis. Karena itu pipeline CI harus cepat dan stabil — test yang kadang lulus kadang gagal (flaky) merusak kepercayaan tim pada pipeline.
Anatomi workflow GitHub Actions
Workflow adalah file YAML di .github/workflows/. Strukturnya: trigger (on) → job (berjalan di runner terpisah) → step (perintah atau action siap pakai).
yaml
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read # default seminimal mungkin
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "25"
cache: maven # cache ~/.m2/repository berdasarkan hash pom.xml
- name: Build & test
run: ./mvnw -B verifyBeberapa detail yang sering dilupakan:
./mvnwmemakai Maven Wrapper sehingga versi Maven di CI sama dengan di laptop. Pastikan file-nya executable di Git (git update-index --chmod=+x mvnw).-B(batch mode) membuat log Maven ringkas tanpa progress bar unduhan.verifymenjalankan unit test (Surefire) dan integration test (Failsafe) —packagesaja melewatkan integration test.cache: mavendisetup-javamenghemat menit unduhan dependency di setiap run.
Testcontainers di runner
Runner ubuntu-latest milik GitHub sudah memiliki Docker, jadi integration test berbasis Testcontainers (PostgreSQL, Redis, Kafka) berjalan tanpa konfigurasi tambahan. Yang perlu kamu jaga adalah durasi: tarik image yang sama dengan production (misalnya postgres:17-alpine) dan gunakan kembali container per kelas test, bukan per method. Runner macOS dan Windows tidak menyediakan Docker Linux secara default, jadi tetap pakai Ubuntu untuk job ini.
Unggah laporan test saat gagal
Tambahkan langkah actions/upload-artifact dengan if: failure() untuk menyimpan target/surefire-reports dan target/failsafe-reports. Saat test gagal di CI tetapi lulus di laptop, laporan ini yang kamu butuhkan.
Build & push image ke GHCR dengan tag SHA
Setelah test lulus di main, bangun image dari Dockerfile multi-stage (bab Docker) dan dorong ke GitHub Container Registry (ghcr.io). Tag utama sebaiknya SHA commit — unik, tidak berubah, dan langsung menunjuk ke kode sumbernya. Tag latest boleh ada sebagai kemudahan, tetapi jangan pernah deploy berdasarkan latest karena kamu tidak tahu persis versi apa yang berjalan.
yaml
image:
needs: test # hanya jalan bila job test lulus
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # izin push ke GHCR
steps:
- uses: actions/checkout@v5
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
ghcr.io/${{ github.repository }}:latestneeds membentuk urutan antar job; tanpa itu semua job berjalan paralel. Nama image di GHCR harus huruf kecil — bila nama akun atau repo-mu mengandung huruf besar, pakai docker/metadata-action untuk menghasilkan tag yang valid.
Secret dan izin di Actions
- Simpan kredensial (password database staging, kunci SSH deploy, token API) di Settings → Secrets and variables → Actions, lalu akses lewat ekspresi
secrets.NAMA(ditulis di dalam sintaks ekspresi Actions seperti pada contoh YAML di atas). Nilai yang tidak rahasia (URL, nama region) taruh di variables dan akses lewatvars.NAMA. GITHUB_TOKENdibuat otomatis per run dan kedaluwarsa setelah job selesai. Batasi izinnya dengan blokpermissionsper job.- Pakai environment (
environment: production) untuk secret khusus production plus aturan required reviewers — job deploy berhenti menunggu persetujuan. - Secret tidak diberikan ke workflow yang dipicu pull request dari fork. Jangan "mengakali" ini dengan
pull_request_targetyang menjalankan kode dari fork — itu celah klasik pencurian secret. - Untuk cloud (AWS, GCP, Azure), utamakan OIDC (
permissions: id-token: write) daripada menyimpan access key jangka panjang.
Scanning dependency dan keamanan
Sebagian besar kode di aplikasimu berasal dari dependency. Pipeline yang baik memeriksanya otomatis:
- Dependabot (
.github/dependabot.yml) membuka PR saat ada versi baru atau advisory keamanan untuk Maven, Docker, dan GitHub Actions. - Dependency review (
actions/dependency-review-action) menggagalkan PR yang menambah dependency dengan kerentanan yang diketahui. - Code scanning (CodeQL) mencari pola kode berbahaya seperti SQL injection.
- Scan image (misalnya Trivy) memeriksa paket OS di base image — JRE yang lama atau
glibcyang rentan juga celah.
yaml
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: maven
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weeklyBranch protection dan required checks
Pipeline tidak berguna bila orang bisa langsung push ke main. Atur branch protection rule (atau ruleset) di main: wajib lewat pull request, minimal satu review, dan required status checks — misalnya job test harus hijau sebelum tombol merge aktif. Aktifkan juga "require branches to be up to date" agar PR diuji terhadap main terbaru, bukan versi lama.
Strategi deploy dan rollback
| Strategi | Cara kerja | Kelebihan | Kekurangan |
|---|---|---|---|
| Recreate | Matikan semua versi lama, nyalakan versi baru | Paling sederhana | Ada downtime |
| Rolling | Ganti instance satu per satu | Tanpa downtime, hemat resource | Dua versi hidup bersamaan sementara |
| Blue-green | Dua environment identik; pindahkan seluruh trafik sekaligus | Rollback instan (pindah balik) | Butuh resource dua kali lipat |
| Canary | Sebagian kecil trafik ke versi baru, naikkan bertahap per metrik | Risiko terbatas, berbasis data | Butuh routing trafik & metrik matang |
Apa pun strateginya, rollback harus sama mudahnya dengan deploy: karena image ditandai SHA, rollback cukup men-deploy ulang SHA sebelumnya. Readiness probe dari bab Actuator memastikan trafik baru masuk hanya setelah instance siap, dan graceful shutdown memastikan request yang sedang berjalan selesai sebelum instance lama mati.
Migrasi database di pipeline
Kode bisa di-rollback dalam detik; skema database tidak semudah itu. Aturan praktisnya:
- Migrasi Flyway dijalankan sekali per rilis — sebagai langkah pipeline terpisah atau saat aplikasi start (Flyway memakai lock sehingga instance paralel tidak menjalankan migrasi ganda).
- Setiap migrasi harus kompatibel mundur dengan versi aplikasi sebelumnya, karena selama rolling update kedua versi berjalan bersamaan.
- Pakai pola expand–contract: rilis 1 menambah kolom baru (nullable), rilis 2 mengisi dan mulai memakai kolom itu, rilis 3 menghapus kolom lama.
- Jangan pernah mengedit file migrasi yang sudah berjalan; buat migrasi baru.
Contoh di dunia backend
Layanan pesanan toko online memakai satu workflow: test di setiap PR, image di setiap merge ke main, lalu deploy ke staging otomatis dan ke production setelah disetujui. Perhatikan bahwa job deploy production memakai environment dengan required reviewer dan men-deploy SHA yang sama dengan yang sudah diuji di staging.
yaml
# .github/workflows/release.yml — potongan
jobs:
# job test & image seperti di atas ...
deploy-staging:
needs: image
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy ke staging
run: ./scripts/deploy.sh staging "${{ github.sha }}"
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
- name: Smoke test
run: |
curl --fail --retry 10 --retry-delay 5 \
"${{ vars.STAGING_URL }}/actuator/health/readiness"
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # menunggu persetujuan reviewer
concurrency: production # cegah dua deploy production berjalan bersamaan
steps:
- name: Deploy ke production
run: ./scripts/deploy.sh production "${{ github.sha }}"
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Saat rilis baru membuat error rate POST /orders melonjak di dashboard Grafana, jalur rollback-nya jelas: jalankan workflow deploy manual (workflow_dispatch) dengan input SHA rilis sebelumnya. Karena migrasi rilis tersebut bersifat expand (hanya menambah kolom nullable), versi lama tetap bisa berjalan di atas skema baru.
yaml
# .github/workflows/rollback.yml — potongan
on:
workflow_dispatch:
inputs:
sha:
description: SHA commit yang ingin di-deploy ulang
required: true
jobs:
rollback:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v5
- run: ./scripts/deploy.sh production "${{ inputs.sha }}"
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Pertanyaan interview
1. Apa beda continuous delivery dan continuous deployment?
Keduanya membuat setiap perubahan di main lulus build dan test serta menghasilkan artefak yang siap rilis. Pada continuous delivery, rilis ke production masih diputuskan manusia (tombol atau approval). Pada continuous deployment, setiap perubahan yang lulus pipeline otomatis masuk production — butuh test, monitoring, dan rollback yang sangat matang.
2. Kenapa image sebaiknya ditandai dengan SHA commit, bukan hanya latest?
SHA bersifat unik dan tidak berubah, sehingga kamu selalu tahu kode persis yang sedang berjalan, bisa men-deploy artefak yang sama ke staging lalu production, dan rollback cukup dengan men-deploy SHA sebelumnya. latest terus berpindah, sehingga dua server bisa menjalankan "latest" yang berbeda.
3. Bagaimana menjalankan migrasi database yang menghapus kolom tanpa downtime?
Pakai expand–contract. Pertama rilis kode yang berhenti membaca dan menulis kolom itu, sementara kolom tetap ada. Setelah semua instance memakai versi baru dan rollback ke versi lama tidak lagi diperlukan, baru rilis migrasi yang menghapus kolom. Dengan begitu tidak ada saat di mana versi aplikasi yang hidup bergantung pada kolom yang sudah hilang.
Jebakan umum
- Menjalankan
./mvnw packagedi CI sehingga integration test (Failsafe) tidak pernah berjalan — gunakanverify. - Deploy berdasarkan tag
latest, sehingga tidak jelas versi apa yang jalan dan rollback menjadi tebak-tebakan. - Memberi
permissions: write-allatau tidak membatasiGITHUB_TOKEN; satu action pihak ketiga yang disusupi bisa menyalahgunakan semua izin itu. Batasi izin per job dan pertimbangkan mem-pin action pihak ketiga ke SHA commit. - Mencetak secret ke log, atau memakai
pull_request_targetyang menjalankan kode dari fork dengan akses secret. - Migrasi yang tidak kompatibel mundur (rename/drop kolom langsung), sehingga instance versi lama gagal selama rolling update dan rollback mustahil.
- Membiarkan test flaky; tim mulai menekan "re-run" tanpa membaca kegagalan dan bug nyata ikut lolos.
Ringkasan
- CI = build + test otomatis di setiap perubahan; continuous delivery = selalu siap rilis dengan rilis manual; continuous deployment = rilis otomatis.
- Workflow Maven standar:
checkout→setup-java(Temurin 25,cache: maven) →./mvnw -B verify; Testcontainers berjalan di runner Ubuntu. - Bangun image setelah test lulus dan tandai dengan SHA commit; push ke GHCR butuh
packages: write. - Lindungi
maindengan PR wajib, review, dan required checks; scan dependency dengan Dependabot, dependency review, dan scan image. - Pilih strategi deploy (rolling, blue-green, canary) sesuai kebutuhan, dan pastikan rollback cukup dengan men-deploy SHA sebelumnya.
- Migrasi database harus kompatibel mundur (expand–contract) dan secret disimpan di Actions/environment, bukan di kode.