Deploy tanpa jeda & observability
Deploy tanpa jeda bukan satu fitur yang dinyalakan, melainkan hasil dari beberapa setelan yang selaras. Kalau salah satu tidak sinkron, penggunamu melihat 502 setiap kali kamu merilis.
Intisari
- Rolling update: task baru menyala dan lulus health check sebelum task lama dihentikan.
- Deployment circuit breaker mengembalikan versi sebelumnya otomatis saat task baru gagal menyala.
- Empat setelan harus selaras: health check, deregistration delay,
stopTimeout, dan SIGTERM. - Migrasi harus kompatibel dua arah β kode lama dan baru berjalan bersamaan saat deploy.
- Log JSON ke CloudWatch, dengan
request_idsupaya satu permintaan bisa ditelusuri utuh.
Anatomi rolling update
minimumHealthyPercent = 100 maximumPercent = 200
(desiredCount = 4)
1. ECS menyalakan 4 task BARU β total 8 task
2. Task baru lulus health check ALB β mulai menerima lalu lintas
3. ALB berhenti mengirim ke task lama, menunggu deregistration delay
4. ECS mengirim SIGTERM ke task lama
5. Task lama menyelesaikan pekerjaannya dalam stopTimeout, lalu berhenti
β total kembali 4 task
| Setelan | Nilai | Artinya |
|---|---|---|
minimumHealthyPercent | 100 | Kapasitas tidak pernah turun selama deploy |
maximumPercent | 200 | Boleh menjalankan dua kali lipat sementara |
| Keduanya 100/100 | β | Task lama dimatikan dulu β ada jeda |
| 50/100 | β | Hemat, tapi kapasitas separuh saat deploy |
Empat setelan yang harus selaras
ALB health check : interval 15s, healthy threshold 2 β ~30 detik untuk sehat
ALB deregistration delay : 30 detik
ECS stopTimeout : 60 detik (web) / 120 detik (worker)
Aplikasi : menangani SIGTERM dengan benar
Aturannya: stopTimeout > deregistration delay. Kalau ECS membunuh task sebelum ALB
selesai menunggu, permintaan yang sedang berjalan terputus β dan pengguna melihat 502 tepat pada saat kamu
merilis. Ini penyebab paling umum dari "deploy kita selalu menghasilkan sedikit error".
Circuit breaker
aws ecs update-service --cluster portal --service portal-web \
--deployment-configuration '{
"deploymentCircuitBreaker": { "enable": true, "rollback": true },
"minimumHealthyPercent": 100,
"maximumPercent": 200
}'
Dengan ini, kalau task baru gagal menyala atau gagal health check berulang kali, ECS menghentikan deploy dan mengembalikan task definition sebelumnya secara otomatis. Kamu mendapat rollback tanpa harus ada orang yang memperhatikan dasbor.
Circuit breaker hanya menangkap kegagalan yang terlihat oleh health check. Bug yang membuat aplikasi tetap menjawab 200 tapi menghasilkan halaman yang salah tidak akan tertangkap. Untuk itu, tambahkan alarm pada tingkat error 5xx dan latensi setelah deploy β dan pemeriksaan asap di akhir pipeline.
Migrasi yang aman selama deploy
| Perubahan | Aman saat kode lama masih jalan? |
|---|---|
| Tambah kolom nullable | Ya |
| Tambah tabel | Ya |
| Tambah index | Ya (perhatikan penguncian di tabel besar) |
Tambah kolom NOT NULL tanpa default | Tidak β insert kode lama gagal |
| Ganti nama kolom | Tidak |
| Hapus kolom | Tidak |
| Ubah tipe kolom | Biasanya tidak |
Untuk baris-baris "tidak", pecah jadi tiga deploy sesuai pola expand & contract dari Fase 2. Aturannya tidak berubah, tapi di sini alasannya jadi konkret: selama beberapa menit, dua versi kodemu benar-benar berjalan bersamaan di atas satu database.
Log terstruktur
// app/Http/Middleware/KonteksLog.php
public function handle(Request $request, Closure $next): Response
{
$id = $request->header('X-Amzn-Trace-Id') ?? (string) Str::uuid();
Log::withContext([
'request_id' => $id,
'user_id' => $request->user()?->id,
'rute' => $request->route()?->getName(),
]);
return $next($request)->header('X-Request-Id', $id);
}
{"message":"pesanan dibuat","level":"info","request_id":"Root=1-abc-def",
"user_id":42,"rute":"pesanan.store","pesanan_id":1183,"ms":84}
Log::withContext() membuat setiap baris log dalam satu permintaan bisa disatukan. Saat
ada keluhan, kamu punya X-Request-Id dari respons, dan satu query Logs Insights mengembalikan
seluruh jejak permintaan itu β termasuk baris log dari job yang dipicunya, kalau ID-nya ikut diteruskan.
CloudWatch Logs Insights
fields @timestamp, level, message, rute, ms
| filter level = "error"
| sort @timestamp desc
| limit 50
fields rute, ms
| filter ispresent(ms)
| stats count(*) as jumlah,
avg(ms) as rata,
pct(ms, 95) as p95,
pct(ms, 99) as p99
by rute
| sort p95 desc
| limit 20
Alarm yang benar-benar perlu
| Alarm | Ambang | Tingkat |
|---|---|---|
ALB HTTPCode_ELB_5XX | > 10 dalam 5 menit | Bangunkan orang |
ALB TargetResponseTime p95 | > 2 detik | Bangunkan orang |
ECS RunningTaskCount | < minimum | Bangunkan orang |
| SQS pesan di DLQ | > 0 | Bangunkan orang |
Aurora CPUUtilization | > 80% selama 10 menit | Peringatan |
| Redis memori | > 75% | Peringatan |
| Kedalaman antrean SQS | > 1000 selama 10 menit | Peringatan |
| Tingkat error aplikasi | Naik tajam setelah deploy | Bangunkan orang |
Alarm yang terlalu banyak sama buruknya dengan tidak ada alarm. Kalau setiap lonjakan kecil memicu notifikasi, orang berhenti membacanya β dan alarm yang sungguhan ikut terlewat. Delapan alarm di atas menutup hampir semua kegagalan yang benar-benar merugikan pengguna. Tambahkan yang lain hanya setelah sebuah insiden membuktikan bahwa kamu memang membutuhkannya.
Pemeriksaan asap setelah deploy
set -e
BASE=https://portal.example.com
test "$(curl -s -o /dev/null -w '%{http_code}' $BASE/up)" = "200"
test "$(curl -s -o /dev/null -w '%{http_code}' $BASE/)" = "200"
curl -sI $BASE/ | grep -qi 'x-cache'
curl -s $BASE/api/v1/artikel | jq -e '.data | length > 0' > /dev/null
echo "Pemeriksaan asap lolos."
Latihan: selaraskan keempat setelan deploy, lalu jalankan
while true; do curl -s -o /dev/null -w '%{http_code}\n' https://portalβ¦/; sleep 0.2; done sambil
memicu deploy. Hitung berapa respons yang bukan 200. Targetnya nol; setiap angka di atas itu menunjuk ke satu
setelan yang belum sinkron.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.