Server yang sehat adalah server yang adil: database, web server, dan cron backup berbagi CPU dan memori tanpa ada yang menyengsarakan yang lain. Kenyataannya, satu proses yang bocor memori bisa melapalkan seluruh RAM dan memaksa OOM killer memilih korban secara acak. Mekanisme control group (cgroup) adalah jawaban kernel untuk masalah ini: mengelompokkan sejumlah proses, lalu membatasi konsumsi CPU, memori, I/O, bahkan jumlah proses dari kelompok tersebut secara agregat. Pada chapter ini kita bedah cara kerja cgroups versi 2, controller yang tersedia, dan bagaimana systemd serta container memakainya setiap hari tanpa kita sadari.
Masalah Pembatasan per Kelompok Proses #
Alat klasik pembatasan yang sudah kita kenal ternyata tidak cukup. nice dan renice hanya mengatur prioritas penjadwalan satu proses, sedangkan ulimit dari chapter resource limits
membatasi per proses dan per atribut seperti jumlah file terbuka. Keduanya sama-sama tidak bisa menjawab pertanyaan nyata administrator: “batasi total memori seluruh proses milik layanan web ini sampai 512 MiB”. Sebuah service modern terdiri dari belasan proses pekerja — membatasi masing-masing 512 MiB justru mengizinkan total 6 GiB. cgroups menyelesaikannya dengan membalik sudut pandang: batas ditempelkan pada kelompok, dan kernel otomatis menghitung akumulasi seluruh anggotanya, termasuk proses yang baru lahir di kemudian hari.
Konsep Dasar cgroup #
Sebuah cgroup adalah node dalam hierarki direktori yang memuat daftar proses dan kenop pembatas. Dua berkas pentingnya sederhana: cgroup.procs berisi daftar PID anggota kelompok, sementara file seperti memory.max atau cpu.max menampung nilai batas. Ketika proses menulis malloc melebihi memory.max kelompoknya, kernel yang menolak — bukan aplikasi, bukan systemd. Karena penegakan terjadi di dalam kernel, tidak ada proses yang bisa lolos walau ia anak dari anak dari proses pertama, selama masih berada di kelompok yang sama. Hierarkinya juga bisa disarangkan: grup web-app boleh punya anak web-app/php-fpm dan web-app/nginx, dan batas induk tetap berlaku untuk seluruh keturunannya.
Dari cgroups v1 ke cgroups v2 #
cgroups lahir tahun 2008 sebagai versi 1 dengan satu kelemahan desain: setiap controller memasang hierarki direktorinya sendiri. Satu kelompok proses bisa berada di /sys/fs/cgroup/memory/web/ sekaligus /sys/fs/cgroup/cpu/web/ — dua tempat berbeda yang harus tetap sinkron manual. Versi 2 (sejak kernel 4.5) menyatukan semuanya menjadi satu hierarki tunggal:
| Aspek | cgroups v1 | cgroups v2 |
|---|---|---|
| Hierarki | terpisah per controller (memory, cpu, …) | satu hierarki bersama |
| Mengaktifkan controller | otomatis saat mount | eksplisit via cgroup.subtree_control |
| Proses di grup induk | boleh | dilarang (induk khusus pengatur) |
| Nama file batas | memory.limit_in_bytes | memory.max |
Distribusi modern beralih ke v2 — Fedora sejak rilis 31 dan Ubuntu sejak 21.10 — dan dokumentasi lengkapnya ada di panduan admin cgroup v2 . Sistem latihan pada buku ini memasang hierarki v1, dan justru itu kesempatan bagus untuk mengenali bentuk lamanya:
echo "== isi /sys/fs/cgroup =="
ls /sys/fs/cgroup
echo "== jenis filesystem =="
stat -fc %T /sys/fs/cgroupOutput yang Dihasilkan:
== isi /sys/fs/cgroup ==
blkio
cpu
cpu,cpuacct
cpuacct
cpuset
devices
freezer
hugetlb
memory
name=systemd
net_cls
net_cls,net_prio
net_prio
perf_event
pids
systemd
== jenis filesystem ==
tmpfsPerhatikan bahwa direktori-direktori di output — memory, cpu, pids, blkio — adalah hierarki terpisah per controller khas v1. Pada sistem v2, isi direktorinya berupa file kontrol seperti cgroup.controllers dan cgroup.subtree_control. Perintah stat -fc %T membedakan keduanya: filesystem v1 dilaporkan sebagai tmpfs, sedangkan hierarki v2 terlaporkan sebagai cgroup2fs — cara cepat memeriksa versi di mesin mana pun.
Controller Utama dan Batasnya #
Setiap controller menjaga satu jenis resource. Berikut empat yang paling sering dipakai:
| Controller | File batas | Peran |
|---|---|---|
cpu | cpu.max | kuota CPU dalam format kuota periode — 20000 100000 berarti 20% dari satu core |
memory | memory.max | plafon memori keras; memory.high versi lunak yang hanya menunda (throttle) |
io | io.max | batas laju baca/tulis per perangkat blok |
pids | pids.max | jumlah maksimum proses dalam kelompok — tameng dari fork bomb |
Controller pids bisa kita lihat bekerja di sistem latihan ini melalui hierarki v1-nya:
cat /sys/fs/cgroup/pids/pids.maxOutput yang Dihasilkan:
maxNilai max berarti tanpa batas — kelompok pids di sistem ini tidak membatasi jumlah proses, tetapi strukturnya sudah siap dipakai. Pada v2, file dengan nama yang sama hidup di dalam tiap node hierarki. Ketika batas memory.max terlampaui dan kernel tidak lagi bisa mereklamasi memori, keputusan keras diambil: proses pelanggar dibunuh oleh OOM killer dan peristiwanya dicatat di memory.events pada penghitung oom dan oom_kill — angka yang pantau untuk tahu seberapa sering layanan kita kehabisan memori.
Membaca dan Menghitung Anggota Grup #
Untuk merasakan cara data cgroup tersusun, file daftar-group.txt pada bundle chapter ini berisi snapshot kolom PID dan nama cgroup dari sekumpulan proses — format serupa keluaran ps -eo pid,cgroup yang sudah dipadatkan. Skrip awk berikut menghitung jumlah proses per kelompok:
awk '{ jumlah[$2]++ } END { for (g in jumlah) printf "%-24s %d proses\n", g, jumlah[g] }' daftar-group.txt | sortOutput yang Dihasilkan:
backup-latihan.service 1 proses
db.service 2 proses
web-app.slice 4 prosesBedah skripnya baris-per-baris:
{ jumlah[$2]++ }: kolom kedua (nama cgroup) dipakai sebagai kunci array asosiatif; tiap baris menambah hitungan kelompoknya.END { for (g in jumlah) ... }: setelah seluruh baris habis, awk berkeliling ke semua kunci array lalu mencetak nama kelompok rata kiri selebar 24 karakter dan hitungannya.| sort: urutan keluaranforpada awk tidak dijamin, sehinggasortmenjadikan hasilnya deterministik — kelompok backup 1 proses, database 2, web-app 4.
Mengelola cgroup Secara Manual #
Di sistem v2, membuat kelompok baru semudah membuat direktori — kernel otomatis mengisi cgroup.procs dan kenop batas di dalamnya. Urutan lengkapnya dijalankan sebagai root:
cd /sys/fs/cgroup
echo "+memory" > cgroup.subtree_control
mkdir latihan-web
echo 536870912 > latihan-web/memory.max
echo $$ > latihan-web/cgroup.procsBaris demi baris: tulis nama controller ke cgroup.subtree_control untuk mengaktifkannya bagi anak-anak hierarki; buat kelompok latihan-web; pasang plafon memori 512 MiB (536.870.912 byte); lalu pindahkan shell kita sendiri ($$) ke dalamnya. Sejak saat itu, setiap malloc dari shell dan seluruh anak cucunya dihitung terhadap plafon 512 MiB tersebut. Prinsip v1 berbeda sedikit — controller sudah aktif di hierarkinya masing-masing, tinggal mkdir di dalamnya.
systemd Mengelola cgroup untuk Kita #
Praktiknya, administrator jarang menyentuh /sys/fs/cgroup langsung karena systemd sudah menjadi manajer cgroup bawaan. Setiap service, scope, dan slice yang kita pantau lewat systemctl hidup sebagai node hierarki cgroup, dan pembatasan cukup dideklarasikan di unit — MemoryMax=512M, CPUQuota=20%, TasksMax=100 — sebagaimana dibahas pada chapter menulis unit systemd
. Pohon lengkapnya terlihat dengan perintah systemd-cgls; berikut contoh tampilannya di sistem desktop:
Control group /:
-.slice
├─user.slice
│ └─user-1000.slice
│ └─[email protected]
│ ├─gnome-terminal-server.service
│ └─bash …
├─system.slice
│ ├─nginx.service
│ │ ├─1204 nginx: master process
│ │ └─1205 nginx: worker process
│ └─sshd.service
│ └─980 sshd: /usr/sbin/sshd -DStruktur pohon pada contoh tampilan itu adalah cgroup sungguhan: nginx.service dengan dua prosesnya adalah satu kelompok, dan satu baris MemoryMax=512M pada unit-nya membatasi keduanya sekaligus. Docker dan Podman memakai mekanisme yang persis sama di balik flag --memory dan --cpus, sebagaimana dibahas pada chapter container concepts
— dan karena cgroups adalah fasilitas kernel, peluang delegasinya ke container juga diatur resmi di dokumentasi systemd
.
cgroups menjawab pertanyaan berapa banyak resource yang boleh dipakai sebuah kelompok proses. Pertanyaan pasangannya adalah apa saja yang boleh dilihat proses tersebut: PID berapa, interface jaringan mana, mount apa. Jawaban itu datang dari mekanisme kembar bernama namespaces, yang kita bedah pada chapter berikutnya.