Dua pekan data pemantauan kini tergeletak di folder tim sebagai berkas-berkas CSV, dan isinya membuka jendela yang tidak pernah kita punya sebelumnya: puncak nyata setiap service, jam datangnya, dan seberapa sering ia kembali. Pertanyaan yang sepanjang section ini dijawab dengan tebakan — “berapa limit yang benar untuk service ini?” — kini punya jawaban yang bisa dihitung. Chapter penutup ini merangkai seluruh perangkat menjadi praktik produksi yang utuh: right-sizing dari data p95, kebijakan anggaran per kelas service, perhitungan yang jujur sampai ke tagihan cloud, peluncuran limit baru secara bertahap, sampai runbook saat kegentingan datang — lalu menutup Section Manajemen Resource dengan satu checklist yang bisa diaudit siapa pun.
Right-sizing: P95 dan Headroom #
Kesalahan klasik penetapan limit adalah bertanya ke rata-rata. Rata-rata pemakaian backend tercatat 120 MB — angka yang manis dan menenangkan — padahal puncak jam sibuknya menembus 380 MB, dan OOM tidak terjadi di rata-rata melainkan di puncak. Ukuran yang tepat untuk keputusan limit adalah p95: nilai pemakaian yang hanya dilampaui 5% dari waktu pengamatan. P95 mengabaikan pencilan satu detik yang tidak berarti, tetapi tetap memegang kebenaran bahwa aplikasi harus selamat dari jam sibuk tiap hari.
Formula turunannya sederhana dan simetris: reservasi ditulis kira-kira sebesar p95 — jaminan penempatan yang jujur untuk penjadwal Swarm dari Chapter Resource di Swarm — sedangkan limit ditulis sekitar p95 dikali 1,3 sebagai langit-langit dengan ruang napas 30%, lalu dibulatkan ke angka yang sehat. Dari data pemantauan tim, tabelnya terbaca sebagai berikut:
| Service | p95 terukur | Reservasi | Limit final | Catatan angka |
|---|---|---|---|---|
| backend | 380 MB | 400m | 512m | 380 × 1,3 ≈ 494, dibulatkan ke 512m |
| db | 2,1 GB | 2g | 4g | p95 × 1,3 ≈ 2,7; dinaikkan ke 4g karena tren cache naik tiap pekan |
| cache | 90 MB | 128m | 192m | maxmemory 128mb ditambah overhead proses Redis |
| proxy dan frontend | di bawah 20 MB | 64m | 128m | berkas statis, puncaknya pun kecil |
Tiga catatan melengkapi tabel itu. Pertama, db di staging sengaja diberi limit longgar 8g selama masa pengukuran — setelah kejadian flash sale pada Chapter Overcommit dan Noisy Neighbor, tim menaikkan angkanya darurat lalu membiarkannya longgar agar p95 yang terbaca adalah kebutuhan sungguhan, bukan pantulan langit-langit yang sempit; p95 2,1 GB adalah hasil pengukuran itu, dan 4g dipilih dengan ruang tumbuh karena tren cache PostgreSQL yang mendaki setiap pekan. Kedua, p95 bukan monopoli memori: backend yang menunjukkan 22% throttle berkelanjutan pada kuota cpus: "1.0" dinaikkan ke 1.5 — data throttle adalah argumen, bukan perasaan. Ketiga, cache mendapat pagar ganda yang selaras: maxmemory mengatur evict kunci di dalam Redis, limit cgroup 192m menjaga kernel tetap mengenal batasnya.
Kebijakan per Kelas Service #
Menetapkan angka satu per satu untuk tiap service baru adalah pekerjaan yang tidak skalabel. Kebijakan per kelas memotong pekerjaan itu: setiap service baru tinggal memilih kelasnya, lalu menyalin pola angkanya sebagai titik awal sebelum datanya sendiri berkumpul:
| Kelas | Contoh di toko | cpus | Memory limit | Reservasi memori |
|---|---|---|---|---|
| frontend statis | proxy, frontend | 0,25 | 128m | 64m |
| api stateless | backend | 1,5 | 512m | 400m |
| data berat | db | 2,0 | 4g | 2g |
| pekerja latar | worker-gambar | 1,0 | 1g | 512m |
Untuk node produksi 16 GB yang direncanakan tim, kebijakan itu menjelma anggaran yang bisa diaudit:
| Penghuni node 16 GB | Reservasi | Limit |
|---|---|---|
| db | 2g | 4g |
| backend 2 replika | 2 × 400m | 2 × 512m |
| worker-gambar | 512m | 1g |
| cache | 128m | 192m |
| proxy dan frontend | 2 × 64m | 2 × 128m |
| Total | ±3,5g | ±6,7g |
Total reservasi 3,5 GB berarti penjadwal cluster masih melihat ruang lega untuk task tambahan, dan total limit 6,7 GB terhadap RAM 16 GB adalah overcommit ringan yang tertib — puncak bersama yang terukur 5,5 GB masih menyisakan ruang aman. Angka-angka ini ditulis dalam deploy.resources di compose.yaml produksi, sehingga kebijakan dan pelaksanaannya tidak pernah berpisah.
Cloud: Ukur dari Reservasi, Bukan dari Limit #
Di cloud, anggaran resource berubah wujud menjadi tagihan bulanan, dan satu kekeliruan membaca tabel di atas bisa menggandakannya. Ukuran instance yang tepat dihitung dari total reservasi ditambah puncak terukur ditambah ruang napas — bukan dari total limit. Dengan reservasi 3,5 GB dan puncak nyata 5,5 GB, instance 8 GB sudah memadai; mengukur dari total limit 6,7 GB lalu menambah “jaga-jaga” akan mendorong tim ke instance 12–16 GB, dan selisihnya terbayar setiap bulan untuk puncak yang tidak pernah datang.
Bedahnya perlu satu kalimat yang tegas: limit bukan tagihan. Menulis memory: 4G pada db tidak menagih apa pun — limit hanyalah langit-langit yang boleh tidak pernah tersentuh. Yang menuntut kapasitas sungguhan — dan dengan itu tagihan — adalah reservasi (yang menuntut penjadwal menyiapkan tempat) dan puncak nyata (yang benar-benar memakai RAM). Karena itu limit yang membengkak tidak langsung menagih, tetapi menggoda tim menaikkan ukuran mesin “untuk jaga-jaga”, dan di situlah uang terbakar diam-diam. Jalan keluarnya dua macam: turunkan limit yang memang tidak pernah tersentuh agar buku anggaran jujur, atau — untuk beban yang memang bergelombang — serahkan penambahan kapasitas pada autoscaling horizontal: orchestrator menambah replika saat beban naik dan memangkasnya saat turun, sehingga kapasitas mengikuti kurva permintaan. Mekanika autoscaling adalah materi Section Orchestration dan Ekosistem pada Chapter Pengantar Container Orchestration; yang penting bagi anggaran hari ini adalah prinsipnya — replika ber-reservasi kecil yang datang dan pergi lebih murah daripada mesin besar yang menganggur.
Meluncurkan Limit Baru Secara Bertahap #
Limit yang berubah adalah eksperimen pada sistem hidup, dan eksperimen tidak dijalankan pada seluruh pengguna sekaligus. Urutan peluncuran yang dipakai tim toko:
| Tahap | Tindakan | Bukti sehat sebelum lanjut |
|---|---|---|
| 1. Kandidat | terapkan limit baru pada satu replika atau satu host uji | container hidup, tidak ada restart tak terduga |
| 2. Pengamatan | biarkan 1–2 hari: throttle, MEM %, oom di memory.events, dan error rate aplikasi | throttle di bawah 20%, MEM % di bawah 85%, tidak ada oom baru |
| 3. Rollout penuh | angkat ke seluruh replika lewat docker compose up -d | stats merata antar replika, error rate stabil |
| 4. Mundur | bila memburuk: kembalikan angka lama dari Git, darurat lewat docker update | — |
Dua hal pada tabel itu sering terlewat. Pertama, pengamatan tahap dua mencakup error rate aplikasi — galat 5xx yang naik tipis dan latensi p95 yang melebar adalah gejala kelaparan resource yang tidak pernah tampil di docker stats. Kedua, di dunia Compose murni, “satu replika” didekati lewat host uji atau project berdampingan dengan flag -p; canary yang mengalirkan trafik sungguhan ke sebagian kecil pengguna adalah fitur bawaan orchestrator, dan itu satu lagi alasan menunggu Section Orchestration dan Ekosistem.
Aplikasi Berat: JVM dan Limit Container #
Saat tim menambahkan service pelaporan berbasis Java, satu kelas masalah baru ikut masuk: runtime yang mengatur dirinya sendiri di atas memori. JVM menentukan besar heap-nya saat menyala, dan container limit tidak dihitung ke dalam keputusan itu kecuali JVM diberi tahu. JVM modern menyediakan persentase yang dihitung dari batas container:
CMD ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "laporan.jar"]Baris tersebut menyuruh JVM mengalokasikan heap maksimal 75% dari limit container — sisanya 25% disisakan untuk metaspace, thread stack, dan buffer di luar heap yang tetap dihitung cgroup. Jalur eksplisit yang lebih tua juga sah: java -Xmx384m mematok heap 384 MB pada container ber-limit 512m, dengan syarat seluruh pemakaian di luar heap ikut diperhitungkan.
Runtime lain kebanyakan sudah sadar container — Node.js dan Go membaca limit cgroup dengan benar — sehingga JVM tua adalah pengecualian paling terkenal, bukan aturan umum. Pelajarannya tetap berlaku luas: aplikasi yang mengelola memori sendiri harus diberi tahu tentang pagar, karena cgroup membatasi tanpa bernegosiasi.
Runbook: Container Lambat #
Sisa satu perangkat adalah prosedur tertulis untuk kegentingan — karena kegentingan bukan waktu yang baik untuk berpikir dari nol. Ini runbook mini tim toko untuk keluhan paling umum, “API-nya lambat”, tiap langkahnya memakai perangkat yang sudah kita kuasai:
| Langkah | Perintah atau tindakan | Yang dibaca |
|---|---|---|
| 1 | docker stats --no-stream | siapa yang CPU %-nya menempel kuota, MEM % di atas 90% |
| 2 | docker compose exec backend cat /sys/fs/cgroup/cpu.stat | nr_throttled yang menanjak → kunci CPU |
| 3 | free -m dan swapon --show di host | swap yang aktif membengkak → kunci memori host |
| 4 | docker update --cpus 1.5 toko-backend-1 | darurat: kuota dinaikkan tanpa recreate — senjata Chapter Docker Update |
| 5 | sunting compose.yaml lalu commit | permanen: darurat adalah jembatan, bukan tempat tinggal |
Langkah keempat dan kelima adalah pasangan yang tidak boleh berpisah. docker update menyelamatkan malam itu tanpa mematikan layanan, tetapi perbaikan itu hidup hanya selama container-nya — beginilah cara kerjanya yang kita pelajari pada Chapter Docker Update dan alasan seluruh perjalanan section ini berawal. Keesokan paginya, angka yang sama harus sudah duduk di compose.yaml, direview, dan di-commit; runbook yang berakhir di langkah empat hanyalah utang yang menunggi.
Checklist Akhir Section #
Tujuh pertanyaan berikut merangkum seluruh Section Manajemen Resource sebagai audit yang bisa dijalankan siapa pun terhadap stack mana pun:
- Setiap service punya limit
cpusdanmemoryeksplisit di compose.yaml — tidak ada yang mengandalkan kebetulan host-nya besar. - Reservasi ditulis dari p95 terukur, bukan dari limit yang dipotong separuh secara buta.
- Total reservasi per node di bawah kapasitas dengan ruang napas, dan total limit diperiksa terhadap puncak bersama yang terukur.
- Overcommit memori dijaga konservatif — puncak bersama di bawah sekitar 80% RAM — sementara overcommit CPU diperbolehkan dan dipantau lewat throttle.
- Pemantauan tren berjalan: ambang throttle,
MEM %, PIDS, danoompunya pemilik, ritme, dan kanal alert. - Aplikasi sadar batasnya — heap JVM dan buffer aplikasi lain disetel di bawah limit container, bukan di bawah RAM host.
- Jalur darurat pernah dilatih dan setiap darurat selalu berakhir dengan perbaikan compose.yaml yang di-commit.
Section Manajemen Resource menutup perjalanannya di sini, dan jarak tempuhnya layak direnungkan sejenak: kita berangkat dari satu flag --memory di Section Konfigurasi Container, membedah cgroups sampai ke berkas-berkasnya, memasang pagar CPU, memori, proses, dan I/O, memindahkan semuanya ke compose.yaml, meminjam mata penjadwal Swarm, menakar overcommit dan tetangga yang berisik, merekam tren, dan berakhir di kebijakan anggaran yang bisa dijelaskan sampai ke tagihan cloud. Resource kini dikuasai penuh — dari flag tunggal sampai keputusan produksi. Tetapi toko yang sebenarnya belum berdiri: masih ada sertifikat TLS yang harus terpasang, backup yang harus terjadwal, image produksi yang harus ramping, dan checklist go-live yang harus lulus audit. Seluruh bekal teknis sudah di tangan; yang tersisa adalah membawanya ke medan sesungguhnya. Section Deployment dan Production membuka medan itu, dimulai dari Chapter Persiapan Production Image.
Referensi #
- Runtime options with Memory, CPUs, and GPUs — dasar seluruh limit dan reservasi yang kita tetapkan.
- The Compose Specification
—
deploy.resourcessebagai bahasa bersama Compose dan Swarm. - Services in Swarm mode — reservasi yang menuntut kapasitas node di cluster.
- docker container update — jalur darurat sebelum perbaikan permanen di compose.yaml.