← Semua pembelajaran / Go Nol → Enterprise
Fase 10 · Deploy di AWS

Deploy tanpa jeda & observability produksi

Semua bagian sudah ada: graceful shutdown, health check, migrasi kompatibel dua arah. Materi ini merangkainya jadi deploy yang tidak menghasilkan satu pun respons non-200 — dan memastikan kamu tahu kalau ia gagal.

Intisari

  • minimumHealthyPercent: 100 + maximumPercent: 200 = task baru siap sebelum yang lama berhenti.
  • Nyalakan deployment circuit breaker dengan rollback: true — deploy yang gagal kembali sendiri.
  • Rantai lengkapnya: readiness → deregistration delay → Shutdown → stopTimeout. Semua harus selaras.
  • Log JSON dengan request_id membuat CloudWatch Logs Insights bisa dipakai sungguhan.
  • Delapan alarm menutup sebagian besar insiden yang bisa diperkirakan.

Konfigurasi service

{
  "serviceName": "toko-server",
  "desiredCount": 4,

  "deploymentConfiguration": {
    "minimumHealthyPercent": 100,   // jangan pernah turun di bawah 4 task sehat
    "maximumPercent": 200,          // boleh sampai 8 task selama transisi

    "deploymentCircuitBreaker": {
      "enable": true,
      "rollback": true              // gagal → kembali ke revisi sebelumnya
    }
  },

  "healthCheckGracePeriodSeconds": 45,

  "loadBalancers": [{
    "targetGroupArn": "arn:aws:elasticloadbalancing:...",
    "containerName": "server",
    "containerPort": 8080
  }]
}
SetelanKalau salah
minimumHealthyPercent: 50Separuh kapasitas hilang selama deploy — latensi naik saat trafik tinggi
maximumPercent: 100Task lama harus mati dulu sebelum yang baru mulai — ada jeda
Circuit breaker matiDeploy rusak terus mencoba sampai kamu menyadarinya
healthCheckGracePeriod terlalu pendekTask dibunuh saat masih memuat

Rantai waktu shutdown

Semua angka ini harus selaras, atau permintaan akan terpotong:

  Health check ALB    : interval 10 dtk × ambang 2  = terdeteksi dalam 20 dtk
  Jeda readiness      : 10 dtk   (aplikasi mulai membalas 503 di /siap)
  Deregistration delay: 30 dtk   (ALB menyelesaikan yang sedang berjalan)
  srv.Shutdown(ctx)   : 20 dtk
  stopTimeout ECS     : 60 dtk   ← harus > 10 + 20 + margin
                                    kalau tidak: SIGKILL

  Aturannya: stopTimeout > jeda + Shutdown + penghentian worker

Semua ini sudah kamu tulis di Fase 3. Yang ditambahkan di sini cuma angkanya, dan syarat bahwa angka di infrastruktur harus lebih longgar daripada angka di aplikasi. Kalau stopTimeout lebih kecil, ECS mengirim SIGKILL di tengah Shutdown — dan transaksi yang belum di-commit, pesan antrean yang belum di-ack, serta permintaan yang sedang berjalan semuanya mati begitu saja.

Log yang bisa dikueri

// Log JSON ke stdout (Fase 5), dikumpulkan driver awslogs.
log.InfoContext(ctx, "permintaan",
	"metode", r.Method,
	"rute", polaRute,          // POLA, bukan URL mentah
	"status", ww.status,
	"durasi_ms", ms,
	"request_id", RequestID(ctx),
	"trace_id", traceID,
	"pengguna_id", u.ID)
-- CloudWatch Logs Insights

-- p95 per rute
fields @timestamp, rute, durasi_ms
| filter ispresent(durasi_ms)
| stats pct(durasi_ms, 95) as p95, pct(durasi_ms, 99) as p99, count(*) as n
    by rute
| sort p95 desc

-- semua baris untuk satu permintaan yang gagal
fields @timestamp, @message
| filter request_id = "01HQ8XZ4K2..."
| sort @timestamp asc

-- endpoint mana yang paling banyak menghasilkan 5xx
fields rute, status
| filter status >= 500
| stats count(*) as gagal by rute
| sort gagal desc

Kueri pertama itu tidak mungkin ditulis kalau log-mu berupa kalimat. Log terstruktur bukan soal kerapian — ia yang membuat CloudWatch berubah dari tempat menyimpan teks jadi alat investigasi. Dan request_id di setiap baris adalah yang membuat kueri kedua berguna saat insiden.

Delapan alarm

AlarmAmbangArtinya
HTTPCode_Target_5XX> 1% selama 5 menitAplikasi gagal
HTTPCode_ELB_5XX> 0 selama 5 menitTidak ada target sehat
TargetResponseTime p95> 2× baseline, 10 menitMelambat
UnHealthyHostCount> 0 selama 5 menitTask bermasalah
CPU / memori task> 85% selama 10 menitPerlu skala atau ada kebocoran
DatabaseConnections> 80% dari batasAritmetika pool salah (Fase 4)
Umur pesan tertua SQS> 5 menitWorker tertinggal (Fase 5)
Kedalaman DLQ> 0Ada job yang gagal permanen

Alarm yang berbunyi tanpa ada yang bisa dilakukan akan diabaikan, dan alarm yang diabaikan sama dengan tidak ada alarm. Setiap alarm butuh satu kalimat runbook: apa yang harus diperiksa pertama. Kalau sebuah alarm tidak punya jawaban untuk itu, ia belum layak jadi alarm.

Daftar periksa sebelum deploy pertama ke produksi

  1. Aplikasi — graceful shutdown, timeout di setiap lapisan, log JSON ke stdout, endpoint /sehat dan /siap terpisah.
  2. Konfigurasi — semua rahasia lewat secrets, GOMEMLIMIT tersetel, gagal-cepat saat konfigurasi kurang.
  3. Image — multi-stage, distroless, non-root, tag SHA, dipindai.
  4. Database — MaxOpenConns dihitung untuk jumlah task maksimum, ConnMaxLifetime tersetel, migrasi sebagai langkah pipeline.
  5. Jaringan — task di subnet privat, security group saling merujuk, TLS ke RDS.
  6. Deploy — OIDC tanpa access key, circuit breaker aktif, rollback pernah diuji.
  7. Observability — delapan alarm terpasang, dasbor p95, trace tersambung ke log.
  8. Cache — CloudFront di depan, header Cache-Control benar untuk halaman publik dan personal.

Latihan: jalankan deploy sambil membebani aplikasimu dengan hey -z 120s -c 20 dan hitung berapa respons non-200 yang muncul. Kalau lebih dari nol, telusuri rantai waktu shutdown di atas dan temukan angka mana yang tidak selaras. Lalu deploy image yang sengaja rusak dan buktikan circuit breaker mengembalikannya sendiri.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.