Chapter Docker Update berakhir dengan disiplin yang tuntas: kuota backend dinaikkan lewat docker update di tengah kampanye flash sale tanpa satu kali recreate, lalu angka yang sama dituliskan ke compose.yaml di hari itu juga, direview, dan diterapkan lewat recreate di jendela maintenance. Darurat dan permanen akhirnya berbicara satu bahasa — untuk satu service. Selebihnya stack toko masih berjalan tanpa satu pagar pun: db, cache, proxy, dan frontend mengandalkan kebetulan host-nya lapang, dan empat kunci hasil suntingan darurat (cpus, mem_limit, memswap_limit, pids_limit) baru menempel pada satu service yang kebetkan panik lebih dulu. Chapter ini melengkapi sisanya: memindahkan seluruh perangkat Section Manajemen Resource ke kunci service yang bisa di-commit, membedah bentuk kanonik deploy.resources yang akan dibaca dua dunia, lalu menata dua lapis anggaran yang berbeda antara laptop Linux Budi dan Docker Desktop milik Siti.

Dari Flag ke Kunci Service #

Docker Compose menerjemahkan kunci service menjadi flag docker run persis seperti yang kita tulis sendiri sepanjang chapter-chapter sebelumnya — tidak ada mekanisme penegakan baru, hanya bentuk tulis yang berbeda untuk angka yang sama. Kunci berikut adalah padanan langsung dari flag yang sudah kita kuasai, dan tabel ini layak menjadi lembar contekan resmi tim:

Flag docker runKunci servicePeran
--memory 2gmem_limit: 2glangit-langit keras memori — ditabrak berarti OOM kill
--memory-reservation 1gmem_reservation: 1gjaminan lunak saat memori host tertekan
--memory-swap 1gmemswap_limit: 1gtotal RAM plus swap yang boleh dipakai container
--cpus 1.5cpus: 1.5kuota CPU rata-rata — kelebihannya di-throttle
--cpuset-cpus 0-1cpuset: "0-1"sematkan container pada core tertentu
--pids-limit 200pids_limit: 200jumlah maksimum proses dan thread
--shm-size 256mshm_size: 256mbesar /dev/shm di dalam container

Mekanisme di balik tiap barisnya tetap kosakata Chapter Resource Limit: Docker menulis angka ke cgroups, dan kernel-lah yang menegakkannya. Compose hanya memastikan angka itu ditulis ulang setiap kali container diciptakan — sehingga recreate yang dulu menghapus perbaikan kini justru menjadi cara perbaikan itu bertahan.

Service db dengan Set Kunci Lengkap #

Budi memulai dari service terberat: db. Potongan compose.yaml berikut menampilkan seluruh kunci resource yang kini menemani konfigurasi lamanya (environment, volume, dan healthcheck tidak berubah):

yaml
services:
  db:
    image: postgres:18-alpine
    environment:
      POSTGRES_USER: toko
      POSTGRES_PASSWORD: rahasia123
      POSTGRES_DB: toko
    volumes:
      - db-data:/var/lib/postgresql/data
    mem_limit: 2g
    mem_reservation: 1g
    cpus: 1.5
    cpuset: "0-1"
    pids_limit: 200
    shm_size: 256m

Mari kita telaah enam kunci yang baru saja turun ke berkas:

  1. mem_limit: 2g — padanan --memory 2g. Langit-langit keras 2 GiB; bila PostgreSQL menabraknya, korban terkurung di dalam cgroup db, bukan menyebar ke seluruh host — janji utama pembatasan memori yang kita uji saksikan pada Chapter CPU dan Memory Limit.
  2. mem_reservation: 1g — padanan --memory-reservation 1g. Jaminan lunak setengah dari langit-langit: kernel berusaha menjaga porsi itu tersedia saat memori host menipis, tanpa pernah membunuh siapa pun — pola pasangan limit-reservasi yang kita rancang sejak Chapter Resource Limit.
  3. cpus: 1.5 — padanan --cpus 1.5. Kuota rata-rata satu setengah core; kelebihannya di-throttle penjadwal CFS, bukan ditolak.
  4. cpuset: "0-1" — padanan --cpuset-cpus 0-1. Container db hanya pernah dijadwalkan pada dua core pertama laptop delapan core; enam core sisanya tidak tersentuh gigitan kueri besar — alat isolasi yang kita pinjam dari Chapter CPU Limit dan Reservation.
  5. pids_limit: 200 — padanan --pids-limit 200. PostgreSQL melahirkan satu proses per koneksi, dan pagar 200 task memastikan ledakan koneksi yang tidak terkendali berhenti sebagai kegagalan sopan alih-alih fork bomb di host — peristiwa yang kita pukul tuntas pada Chapter PIDs Limit dan Ulimit.
  6. shm_size: 256m — padanan --shm-size 256m. Container bawaannya hanya mendapat /dev/shm 64 MB, dan PostgreSQL menaruh memori bersama untuk worker kueri paralel di sana.

Kunci terakhir itu bukan hiasan. Tanpa shm_size, kueri paralel besar di PostgreSQL kerap gagal di tengah jalan dengan galat klasik yang membingungkan karena container-nya sendiri tampak sehat:

text
FATAL:  could not resize shared memory segment "/PostgreSQL.180401" to 33554432 bytes: No space left on device

Galat tersebut bukan keluhan tentang disk yang penuh — No space left on device mengacu pada /dev/shm yang habis di angka bawaan 64 MB, sementara 33554432 adalah 32 MB yang diminta satu worker paralel. Kunci satu baris shm_size: 256m menghilangkan seluruh kelas kegagalan itu.

Anggaran turun ke berkas, dan container dibuat ulang untuk membacanya:

bash
$ docker compose up -d

Output yang Dihasilkan (dipotong):

text
[+] Running 9/9
 ✔ Network toko_frontend-net    Running
 ✔ Network toko_backend-net     Running
 ✔ Container toko-db-1          Recreated
 ✔ Container toko-db-1          Healthy
 ...
 ✔ Container toko-backend-1     Recreated
 ✔ Container toko-backend-1     Started
 ...

Dua baris Recreated adalah bukti mekanismenya bekerja: konfigurasi service berubah, jadi container lama dibongkar dan container baru lahir membawa limit yang dibaca dari berkas. Kamera resource langsung membenarkan angkanya:

bash
$ docker stats --no-stream

Output yang Dihasilkan (baris toko-frontend-1 dipotong):

text
CONTAINER ID   NAME             CPU %     MEM USAGE / LIMIT     MEM %     NET I/O         BLOCK I/O      PIDS
b4e2f8a1c6d9   toko-proxy-1     0.07%     5.312MiB / 128MiB     4.15%     1.46MB / 2.31MB 12.6MB / 0B    3
3e9a5c8f1d27   toko-backend-1   3.42%     96.84MiB / 512MiB     18.91%    9.87MB / 11.2MB 3.14MB / 0B    11
5c8e2a7f9d1b   toko-cache-1     0.19%     9.84MiB / 512MiB      1.92%     1.02MB / 0B     0B / 0B        4
7a1d9e4b2c8f   toko-db-1        2.03%     41.2MiB / 2GiB        2.01%     1.2MB / 12MB    88.3MB / 0B    9
...

Mari kita telaah kolom MEM USAGE / LIMIT yang kini berbunyi seperti buku anggaran:

  1. db membaca 2GiB — angka dari mem_limit: 2g telah menjadi langit-langit cgroup milik container ini semata.
  2. backend membaca 512MiB — angka darurat flash sale dari Chapter Docker Update yang kini menetap sebagai anggaran resmi; rekap sudah ditulis ulang agar memproses per batch, dan 512 MiB memberi ruang puncaknya.
  3. cache membaca 512MiB — pagar ganda yang selama ini hilang — maxmemory 128mb milik Redis tetap mengatur evict kunci, sedangkan limit cgroup kini menjadi pagar yang benar-benar terbaca kernel; melunasi peringatan Chapter Docker Stats bahwa batas aplikasi saja tidak pernah sampai ke kernel.
  4. proxy membaca 128MiB — penyaji berkas statis tidak membutuhkan lebih.

Verifikasi terakhir menyusuri jejak angka sampai ke tempat kernel membacanya:

bash
$ docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}} {{.HostConfig.PidsLimit}} {{.HostConfig.ShmSize}}' toko-db-1

Output yang Dihasilkan:

text
2147483648 1500000000 200 268435456
bash
$ docker compose exec db cat /sys/fs/cgroup/memory.max

Output yang Dihasilkan:

text
2147483648

Angka 2147483648 berdiri di tiga tempat sekaligus: ditulis manusia di compose.yaml, tersimpan di HostConfig milik Docker, dan dibaca kernel dari berkas cgroups — satu sumber kebenaran dengan dua turunan. NanoCpus 1500000000 adalah 1,5 core dalam nanodetik per detik penjadwalan, PidsLimit 200 dan ShmSize 268435456 (256 MiB) melengkapi arsipnya — kosakata yang sama dengan flag, hanya kemasannya yang kini deklaratif.

Bentuk Kanonik deploy.resources #

Backend kini memegang angka hasil disiplin Chapter Docker Update — cpus: 2.0, mem_limit: 512m, memswap_limit: 1g, pids_limit: 300 — semuanya dalam kunci klasik hasil suntingan darurat. Budi memindahkan pasangan CPU-memorinya ke bentuk kedua: bentuk kanonik dari spesifikasi Compose yang dirancang untuk dibaca dua dunia:

yaml
  backend:
    image: toko-api:dev
    pids_limit: 300
    memswap_limit: 1g
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 512M
        reservations:
          cpus: "1.0"
          memory: 256M

Mari kita telaah migrasi bentuk yang tampak pada potongan itu:

  1. deploy.resources.limits — cpus: "2.0" dan memory: 512M menggantikan cpus: 2.0 dan mem_limit: 512m; angkanya persis angka darurat Siti, hanya berpindah kemasan. Saat container diciptakan, keduanya dipetakan ke flag --cpus 2.0 dan --memory 512m yang sama.
  2. reservations.memory: 256M — padanan --memory-reservation: jaminan lunak separuh langit-langit, pola yang sama dengan service db.
  3. reservations.cpus: "1.0" — di Compose non-Swarm nilai ini ditafsirkan sebagai bobot penjadwalan CPU, keluarga --cpu-shares; makna penuhnya sebagai alat penempatan baru hidup di Swarm, dan itu pokok bahasan chapter berikutnya.
  4. pids_limit: 300 dan memswap_limit: 1g tetap menjadi kunci klasik — dua knob ini tidak punya padanan di deploy.resources, sehingga keduanya tetap ditulis sebaris dengan blok kanoniknya; satu berkas, dua bentuk, tanpa konflik.

Rekomendasi untuk toko, dan pola yang kita pakai sepanjang sisa Course: tulis cpus dan memory dalam bentuk deploy.resources karena bentuk itu dipahami Compose dan Swarm sekaligus — bekal portabel saat cluster datang. Untuk empat tombol yang tidak punya padanan deploy sama sekali (pids_limit, memswap_limit, shm_size, cpuset), kunci klasik adalah satu-satunya jalan — persis alasan service db menulis semuanya dalam satu keluarga kunci, dan alasan potongan backend di atas memuat dua bentuk sekaligus. Mencampur kedua bentuk dalam satu berkas sah-sah saja; yang penting setiap angka punya satu tempat tetap yang di-commit.

Dua Lapis Anggaran di Docker Desktop #

Pengguna Docker Desktop membawa satu lapis anggaran tambahan yang tidak dimiliki pengguna Linux. Seperti kita bahas pada Chapter Instalasi Docker Desktop: di Windows dan macOS, seluruh container hidup di dalam VM Linux internal, dan anggaran VM itu diatur dari luar — panel Resources di aplikasi Docker Desktop untuk macOS, atau berkas .wslconfig untuk Windows dengan backend WSL 2. Berkas milik Siti masih menyimpan angka yang ia tulis saat instalasi:

ini
[wsl2]
memory=6GB
processors=4

Angka pada panel dan berkas tersebut adalah batas total VM: seluruh service toko yang menyala di mesin itu, container alat, dan proses sistem VM berbagi satu kantong 6 GB. Limit per container yang kita tulis di compose.yaml hanya bermakna di dalam kantong itu — mem_limit: 2g pada db menjanjikan dua gigabyte dari anggaran VM, bukan dari laptop.

Siti mengalaminya sendiri. Percaya bahwa “angka besar berarti aman”, ia sempat menulis mem_limit: 8g pada service db di mesin Windows-nya — angka yang melebihi seluruh kantong VM 6 GB. Seminggu semuanya tampak baik, sampai malam Minggu ia menjalankan pemulihan backup besar dan pemakaian db memanjak tajam. Container db mati di tengah proses, dan kolom STATUS menyimpan saksinya:

bash
$ docker compose ps -a db

Output yang Dihasilkan:

text
NAME             IMAGE               COMMAND                  SERVICE   CREATED      STATUS                      PORTS
toko-db-1        postgres:18-alpine  "docker-entrypoint.s…"  db        3 days ago   Exited (137) 2 minutes ago  5432/tcp
bash
$ docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' toko-db-1

Output yang Dihasilkan:

text
true 137

Mari kita telaah kematian yang janggal itu:

  1. Exited (137) tanpa satu baris perpisahan — wajah OOM kill yang sudah kita kenal dari Chapter Debugging Container; SIGKILL tidak memberi PostgreSQL kesempatan mencatat apa pun.
  2. db belum menabrak limitnya sendiri — pemakaian baru menyentuh sekitar 5 GB dari janji 8 GB. Kernel di dalam VM membunuhnya bukan karena cgroup db terlanggar, melainkan karena kantong VM 6 GB habis lebih dulu; RSS db yang terbesar menjadikannya kandidat skor tertinggi — mekanisme pemilihan korban yang akan kita bedah pada Chapter Overcommit dan Noisy Neighbor.
  3. Perbaikannya bukan menaikkan angka — Siti menurunkan mem_limit db ke 2g, angka yang masuk akal terhadap kantong 6 GB setelah dipotong kebutuhan service lain dan sistem VM.

Anggaran yang Menyisakan Tempat Bernapas #

Angka-angka di compose.yaml Budi akhirnya membentuk satu buku anggaran untuk laptop 8 GB — dan bagian terpenting dari buku anggaran adalah kolom yang tidak ditulis: sisanya. Kebiasaan menutup service debug yang lupa menyala, yang kita latih pada Chapter Mengelola Environment Development, ikut menjaga kolom itu tetap lapang:

Penghuni laptop 8 GBAnggaranAlasan angka
db2gdata dan cache halaman tumbuh seiring pemakaian
backend512mangka darurat flash sale yang terbukti cukup untuk beban normal
cache512mmaxmemory 128mb ditambah overhead proses Redis
proxy dan frontend128m masing-masingberkas statis, nyaris kosong
OS, browser, editor, build±4g tidak di-container-kanlaptop tetap enak dipakai untuk bekerja

compose.yaml kini memuat buku anggaran yang dibaca sama oleh siapa pun yang menyalakan stack — Budi, Siti, dan server CI tanpa pengecualian. Tetapi di antara para pembaca itu ada satu pihak yang belum kita perkenalkan: penjadwal cluster, yang membaca blok deploy.resources dengan mata yang sama sekali berbeda — bukan untuk memasang pagar, melainkan untuk memutuskan di node mana sebuah container boleh hidup. Chapter Resource di Swarm memperkenalkan pembaca itu dan menunjukkan bagaimana reservasi berubah dari jaminan lunak menjadi bahasa resmi penempatan.

Referensi #