Backup menjawab musibah yang datang tanpa diundang; rilis adalah musibah yang kita jadwalkan sendiri. Setiap docker compose up -d yang mengganti image produksi adalah momen ketika toko bertaruh versi baru di depan pelanggan sungguhan, dan cara kita menekan taruhan itu menentukan apakah rilis terasa seperti ritual yang tenang atau lemparan dadu di jam sibuk. Kabar baiknya: seluruh infrastruktur yang kita bangun sepanjang section ini — image bertag di registry dengan digest yang bisa diverifikasi, healthcheck tiga lapis, restart policy, proxy dengan resolver dinamis, backup yang berjalan tiap malam — ada persis untuk momen ini. Chapter ini menyusun urutannya: rilis paling sederhana lewat pull dan up -d, pola blue-green untuk nol detik henti, rollback dalam hitungan menit, lalu dua disiplin yang membedakan rilis dewasa dari pertaruhan — menghormati migrasi database yang tidak mau mundur sendiri, dan membukti-bukti rilis dengan verifikasi terukur sebelum menyebutnya selesai.
Rilis Versi Baru: pull dan up -d #
Siti menyelesaikan fitur pesanan bernomor, CI membangun dan mendorong registry.example.com/toko/backend:1.5.0, dan satu commit di repositori toko memperbarui tag image pada compose.yaml. Seluruh perubahan itu berakhir di server sebagai dua perintah:
$ cd /srv/toko
$ git pull
$ docker compose pullOutput yang Dihasilkan (dipotong):
1.5.0: Pulling from toko/backend
a1b2c3d4e5f6: Pull complete
7e8f9a0b1c2d: Pull complete
Digest: sha256:7a2e9c4b1d6f83025a7c9e1b3d5f7092a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2d6
Status: Downloaded newer image for registry.example.com/toko/backend:1.5.0
[+] Pulling 5/5
✔ db Pulled 3.1s
✔ cache Pulled 1.8s
✔ proxy Pulled 2.4s
✔ frontend Pulled 2.0s
✔ backend Pulled 6.2sLima service ikut diperiksa ke registry, tetapi hanya backend yang membawa layer baru — empat lainnya dijawab up-to-date dalam hitungan detik, bukti lagi bahwa content-addressable membuat registry tahu persis blob mana yang belum dimiliki mesin ini. Giliran penggantian:
$ docker compose up -dOutput yang Dihasilkan:
[+] Running 5/5
✔ Container toko-db-1 Running 0.0s
✔ Container toko-cache-1 Running 0.0s
✔ Container toko-backend-1 Recreated 1.8s
✔ Container toko-frontend-1 Running 0.0s
✔ Container toko-proxy-1 Running 0.0sMari kita telaah kosakata status pada keluaran tersebut — dua kata yang membawa dua nasib berbeda:
Runninguntuk empat service — Compose membandingkan cetakbiru compose.yaml dengan container yang ada; image dan konfigurasi yang tak berubah membuat Compose membiarkan container tetap hidup. Database tidak disentuh sama sekali: koneksi pool, isi volume, dan statushealthysemuanya meneruskan hidupnya.Recreateduntuk backend — satu-satunya service yang image-nya berganti digest; container lama menerimaSIGTERMdengan masa tenggangstop_grace_period, dihapus, lalu container baru lahir dengan namatoko-backend-1yang sama.- Jeda yang terasa hanya 1-2 detik pada jalur
/api/— selama jeda itu proxy mendapat502sesaat; begitu container baru terdaftar di DNS Compose, resolver127.0.0.11danproxy_passberbasis variabel dari Chapter Reverse Proxy dan TLS menemukan alamat barunya tanpa perlu me-restart proxy. - Gerbang tetap berlaku untuk bayi baru —
depends_ondengancondition: service_healthymenahan backend baru sampai db membuktikan sehat, dan healthcheck backend sendiri menaikkan status(healthy)dalam belasan detik.
Status akhirnya dibaca dalam satu tabel:
$ docker compose psOutput yang Dihasilkan:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
toko-proxy-1 nginx:1.29-alpine "/docker-entrypoint.…" proxy 7 days ago Up 7 days 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, ...
toko-frontend-1 registry.example.com/toko/frontend:1.4.0 "/docker-entrypoint.…" frontend 7 days ago Up 7 days 80/tcp
toko-backend-1 registry.example.com/toko/backend:1.5.0 "docker-entrypoint.s…" backend About a minute ago Up About a minute (healthy) 3000/tcp
toko-db-1 postgres:18-alpine "docker-entrypoint.s…" db 7 days ago Up 7 days (healthy) 127.0.0.1:5432->5432/tcp
toko-cache-1 redis:7-alpine "docker-entrypoint.s…" cache 7 days ago Up 7 days 6379/tcpEmpat baris berumur tujuh hari dan satu baris berumur semenit — itulah seluruh jejak rilis pada tabel produksi yang sehat. Kolom CREATED yang mencampur umur panjang dan umur semenit adalah tanda bahwa penggantian mengenai sasaran: aplikasi berganti, fondasi tetap.
Blue-Green: Dua Warna Berdampingan #
Jeda 1-2 detik pada rilis sederhana itu bisa diterima toko pada hari biasa — tetapi bukan saat promosi weekend dengan ratusan keranjang aktif. Untuk momen itu, pola blue-green menaruh versi baru berdampingan dengan versi lama, menguji yang baru secara privat, lalu memindahkan pintu proxy dalam satu detik tanpa memutus satu koneksi pun. Desainnya membagi stack menjadi tiga project Compose: project toko tetap memegang db, cache, dan proxy — wasit yang tidak pernah berganti — sementara aplikasi hidup di project berwarna toko-biru dan toko-hijau yang bergantian menjadi yang aktif. Berkas compose.warna.yaml adalah lapisan yang membuat pola ini bekerja:
services:
backend:
networks:
backend-net:
aliases:
- backend-${WARNA}
frontend:
networks:
frontend-net:
aliases:
- frontend-${WARNA}
networks:
backend-net:
external: true
name: toko_backend-net
frontend-net:
external: true
name: toko_frontend-netMari kita telaah tiga keputusan pada berkas kecil tersebut:
- Alias
backend-${WARNA}— container backend project warna apapun mendaftarkan dua nama di DNS network: nama biasa dan alias berwarna; proxy memanggil nama berwarna sehingga berganti warna tidak pernah menimpa nama lawannya. external: truedenganname:eksplisit — kedua network dimiliki projecttoko(nama aslinyatoko_backend-netdantoko_frontend-net); project warna hanya menumpang, sehingga container hijau dan biru hidup di halaman yang sama dengan proxy dan db — pola klaim nama eksplisit yang kita tanam di Chapter Persistent Storage Production, kini dipakai untuk network.- Variabel
WARNA— satu berkas yang sama melahirkan dua project berbeda lewat lingkungan; tidak ada compose.yaml duplikat yang harus disinkronkan.
Adopsi pola ini dari model tunggal hanya butuh dua gerakan: lahirkan warna biru dengan versi yang sedang berjalan, lalu cabut aplikasi dari project induknya.
$ WARNA=biru docker compose -f compose.yaml -f compose.warna.yaml -p toko-biru up -d backend frontend
$ docker compose rm -sf backend frontendOutput yang Dihasilkan:
[+] Running 2/2
✔ Container toko-biru-backend-1 Started
✔ Container toko-biru-frontend-1 Started
[+] Running 2/2
✔ Container toko-backend-1 Removed
✔ Container toko-frontend-1 RemovedProxy kini menunjuk ke backend-biru lewat satu kata pada nginx.conf (set $backend_host backend-biru; dan pasangannya set $frontend_host frontend-biru;) — perubahan yang sama dengan yang akan kita lakukan saat ganti warna. Sepekan kemudian, rilis 1.6.0 datang dan hijau naik panggung tanpa menyentuh biru:
$ git pull
$ WARNA=hijau docker compose -f compose.yaml -f compose.warna.yaml -p toko-hijau up -d backend frontendOutput yang Dihasilkan:
[+] Running 2/2
✔ Container toko-hijau-backend-1 Started
✔ Container toko-hijau-frontend-1 StartedDua warna kini hidup bersamaan — biru melayani pelanggan, hijau menyala tapi belum menerima satu pengunjung pun. Ujian terhadap hijau dilakukan dari dalam network, jauh sebelum pelanggan bisa tersesat ke sana:
$ docker run --rm --network toko_backend-net curlimages/curl:8.14.1 \
-fsS http://backend-hijau:3000/healthzOutput yang Dihasilkan:
okContainer uji memakai image curl murni, hidup sesaat di toko_backend-net, dan mengetuk alias backend-hijau — nama yang hanya dijawab oleh container hijau; flag -f membuat curl keluar galat bila jawaban bukan 2xx, sehingga jawaban ok di atas adalah bukti sehat, bukan dugaan. Uji lebih dalam tinggal mengulang pola yang sama ke rute sungguhan seperti /api/produk.json. Puncaknya satu kata di nginx.conf diganti — backend-biru menjadi backend-hijau, frontend-biru menjadi frontend-hijau — lalu proxy membaca ulang konfigurasinya:
$ docker compose exec proxy nginx -t
$ docker compose exec proxy nginx -s reloadOutput yang Dihasilkan:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
2026/10/31 10:12:03 [notice] 3011#3011: signal process startedBiru yang telanjur kosong melenggang turun tanpa menyentuh siapa-siapa:
$ docker compose -p toko-biru downOutput yang Dihasilkan:
[+] Running 2/2
✔ Container toko-biru-backend-1 Removed
✔ Container toko-biru-frontend-1 RemovedPerintah down pada project warna hanya membawa container warnanya sendiri — db, cache, proxy, dan kedua volume produksi tidak tersentuh karena semuanya milik project lain. Satu harga yang harus dibayar jujur: selama masa tumpang tindih, host menanggung dua kali beban aplikasi; resource limit per service yang kita pasang dari Chapter CPU dan Memory Limit menjaga masa itu tetap terukur, dan durasinya hanya seleksi uji — beberapa menit, bukan beberapa jam.
Rollback dalam Hitungan Menit #
Rilis yang buruk menampar cepat: error rate naik, docker compose logs --since 5m dipenuhi kalimat merah, healthcheck bolak-balik. Kabar baiknya, arah mundur adalah urutan yang sama dengan arah maju — dengan tag yang lebih tua:
$ git checkout v1.4.0
$ docker compose pull
$ docker compose up -dOutput yang Dihasilkan:
[+] Running 5/5
✔ Container toko-db-1 Running 0.0s
✔ Container toko-cache-1 Running 0.0s
✔ Container toko-backend-1 Recreated 1.5s
✔ Container toko-frontend-1 Running 0.0s
✔ Container toko-proxy-1 Running 0.0sPerintah git checkout v1.4.0 mengembalikan compose.yaml ke titik rilis lama, pull memastikan image 1.4.0 ada, dan up -d melakukan Recreated ke arah yang kita maksud. Tiga generasi image di host menjadi jaminan bahwa arah mundur tidak menunggu jaringan:
$ docker images registry.example.com/toko/backendOutput yang Dihasilkan:
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.example.com/toko/backend 1.5.0 9d2f6a1c8e4b 3 hours ago 142MB
registry.example.com/toko/backend 1.4.0 6b8e0d2f4a7c 6 days ago 139MB
registry.example.com/toko/backend 1.3.0 3a5c7e9b1d0f 3 weeks ago 131MBTabel itu menampung rollback lokal instan; registry menjadi lapis kedua saat host berganti atau disk di-prune; dan kebijakan retensi tag di registry — tag lama tidak dihapus, minimal N-3 generasi — adalah lapis ketiga. Untuk rilis yang benar-benar kritis, kunci deployment tidak pada tag melainkan pada digest yang tidak bisa dimutasi, persis pelajaran Chapter Image Digest:
services:
backend:
image: registry.example.com/toko/backend@sha256:3f8c1a9d2e4b607159c28ad3f0b4e6d8a1c3e5f7b9d0f2a4c6e8b0d2f4a6c8e0Bentuk registry/repo@sha256:... meminum image versi 1.4.0 apa adanya selamanya — tag 1.4.0 bisa ditimpa siapa pun yang punya hak push, digest tidak; itulah alasan CHANGELOG kita mencatat digest di setiap rilis.
Empat pertanyaan berikut dijawab sebelum tombol deploy ditekan — tabel uji pra-rilis yang membuat rollback sebagai rencana, bukan harapan:
| Pertanyaan sebelum rilis | Cara mengujinya |
|---|---|
| Apakah skema baru aman bagi kode lama? | jalankan image lama melawan skema baru di staging — expand-contract menjamin jawaban ya |
| Apakah config lama masih dibaca tanpa galat? | variabel baru punya nilai default; variabel lama dihapus satu rilis kemudian |
| Apakah image versi lama masih tersedia? | docker images lokal menampilkan generasi; tag lama masih terdaftar di registry |
| Apakah runbook rollback tertulis dan pernah dilatih? | lembar drill terakhir berumur kurang dari satu kuartal |
Verifikasi Pasca-Deploy #
Rilis baru dinyatakan sehat oleh bukti, bukan oleh ketiadaan keluhan. Kerangka lima langkah Chapter Strategi Diagnostik menuntun bentuk buktinya: gejala presisi, lalu fakta terukur. Tiga perintah menutup setiap rilis toko. Yang pertama sudah kita lihat — docker compose ps dengan CREATED semenit dan STATUS (healthy). Yang kedua mengukur jalur publik lima kali berturut:
$ for i in 1 2 3 4 5; do curl -fsSk -o /dev/null -w "%{http_code} %{time_total}s\n" https://localhost/api/produk.json; sleep 2; doneOutput yang Dihasilkan:
200 0.042s
200 0.039s
200 0.041s
200 0.038s
200 0.040sLima jawaban 200 dengan latensi stabil di bawah 50 milidetik adalah denyut sehat yang tidak menurun — satu permintaan yang lambat bisa kebetulan, lima yang stabil adalah pola. Perintah ketiga menyisir log jendela lima menit untuk kata yang tak boleh hadir:
$ docker compose logs --since 5m backend | grep -ci errorOutput yang Dihasilkan:
0Angka nol dari grep -ci menutup ritual: jalur publik hijau, container sehat, log bersih — rilis boleh disebut selesai dan CHANGELOG boleh ditulis.
Changelog dan Jejak Rilis #
Jejak tertulis membuat rilis berikutnya lebih pintar dari rilis sebelumnya. Tag beranotasi menandai titik mundur yang persis:
$ git tag -a v1.5.0 -m "pesanan: nomor pesanan + trace_id di log"
$ git tag -lOutput yang Dihasilkan:
v1.3.0
v1.4.0
v1.5.0CHANGELOG.md merangkum tiap tag dengan informasi yang dibutuhkan saat panik — apa yang berubah, image apa yang terpasang, dan seberapa aman arah mundurnya:
## v1.5.0 — 2026-10-24
- Pesanan kini bernomor urut dan mencatat trace_id di log backend.
- Migrasi: kolom nomor_pesanan DITAMBAHKAN (nullable — tahap expand).
- image backend: registry.example.com/toko/backend@sha256:3f8c1a9d2e4b6071...
- Rollback: aman ke v1.4.0 (kolom baru nullable; image 1.4.0 masih di registry).Seluruh chapter ini terdestilasi menjadi tujuh langkah rilis yang bisa ditempel di dinding:
| # | Langkah | Gerbang lulus |
|---|---|---|
| 1 | Build dan test di mesin CI | seluruh test hijau sebelum image dibuat |
| 2 | Scan image hasil build | tidak ada critical/high baru |
| 3 | Push image bertag, catat digest | digest masuk CHANGELOG |
| 4 | Perbarui compose.yaml, commit, git tag -a | tag menunjuk commit yang tepat |
| 5 | docker compose pull | digest yang ditarik sama dengan yang dicatat |
| 6 | docker compose up -d | hanya service yang berubah yang Recreated |
| 7 | Verifikasi: ps healthy, loop curl, grep error | lima 200 stabil, hitungan error nol |
Tujuh langkah itu berjalan di atas satu mesin bernama srv-toko yang sejauh ini kita perlakukan seperti sudah ada — padahal seluruh section ini dimulai dari server yang masih kosong. Bagaimana VPS dikeraskan sebelum Docker dipasang, mengapa ufw bisa dibocorkan Docker diam-diam, bagaimana stack pertama dideploy sampai curl menjawab HTTP/2, dan ritual pemeliharaan apa yang menjaga server itu sehat berbulan-bulan — semuanya adalah isi Chapter Docker di VPS.
Referensi #
- Docker Compose CLI reference
— perilaku
pull,up,down, dan flag-puntuk project terpisah. - Compose file reference — services
— kunci
imagedengan tag maupun digest dannetworksdengan alias. - nginx documentation
— kontrol sinyal
nginx -s reloaddan pergantian worker tanpa putus. - docker image ls — daftar image beserta tag dan ukurannya untuk audit generasi.