Saat kamu menjalankan sebuah container, rasanya benar-benar seperti punya komputer mungil yang berdiri sendiri. Dia punya berkas sendiri, proses sendiri, alamat jaringan sendiri, dan pemahamannya sendiri soal apa saja yang terpasang. Intip ke dalamnya, kamu bakal yakin sedang berada di mesin yang terpisah. Padahal bukan — kamu sedang berbagi komputer fisik yang sama, dan kernel sistem operasi yang sama, dengan setiap container lain yang jalan di sebelahmu. Tipuan inilah yang disebut isolasi, dan inilah satu ide yang membuat container layak dipakai.
Seluruh alasan kenapa container bisa ringan, cepat, dan sekali pakai adalah karena dia tidak menggendong sistem operasi utuh ke mana-mana. Sebagai gantinya, dia meminjam kernel milik host, lalu cukup minta dibuatkan beberapa dinding pembatas. Artikel ini membahas dinding-dinding tadi: terbuat dari apa, apa yang sebenarnya dipisahkan, dan — yang sama pentingnya — di bagian mana dindingnya jauh lebih tipis daripada yang orang bayangkan.
Arti “isolasi” yang sebenarnya
Isolasi adalah sifat yang membuat beberapa program bisa jalan di satu mesin sambil berlagak seakan-akan masing-masing memegang mesin itu sendirian. Sebuah container yang terisolasi dengan baik percaya bahwa filesystem-nya adalah satu-satunya filesystem, prosesnya adalah satu-satunya proses, dan jatah CPU serta memorinya adalah seluruh kuenya. Tidak ada satu pun dari itu yang benar — semuanya ilusi yang sengaja dijaga oleh kernel.
Bayangkan sebuah gedung apartemen. Semua penghuni berbagi pondasi yang sama, saluran air yang sama, jaringan listrik yang sama dari jalan. Tapi tiap unit punya pintu terkuncinya sendiri, ruangannya sendiri, kotak suratnya sendiri. Kamu bisa hidup seumur hidup di dalam unitmu tanpa pernah berpapasan dengan tetangga, padahal kalian semua ada dalam satu bangunan. Sebuah container itu ibarat satu unit apartemen. Kernel adalah gedungnya.
Inilah inti perbedaan antara container dan virtual machine. VM membawa sistem operasinya sendiri secara utuh dan berpura-pura jadi gedungnya sendiri. Container berbagi kernel milik host dan cuma jadi satu unit tersegel di dalamnya. Kalau bedanya masih kabur, ada baiknya kamu baca dulu container vs virtual machine — artikel ini melanjutkan persis dari titik itu berhenti, lalu menggali satu tingkat lebih dalam soal bagaimana dinding unit apartemen tadi dibangun.
Isolasi bukan satu dinding, tapi banyak dinding
Tidak ada satu sakelar pun yang bertuliskan “isolasikan ini”. Isolasi sebuah container adalah hasil penjumlahan dari beberapa mekanisme terpisah yang bekerja bersama — satu untuk filesystem, satu untuk proses, satu untuk jaringan, satu untuk batas sumber daya, dan seterusnya. Kamu bisa punya isolasi yang kuat di satu sisi tapi lemah di sisi lain pada saat yang sama. Mengingat hal ini menjelaskan banyak nasihat soal keamanan container yang kalau tidak, terdengar saling bertentangan.
Fitur kernel yang melakukan pekerjaannya
Container bukan satu fitur tunggal yang kamu pasang. Dia adalah gabungan dari kemampuan kernel Linux biasa yang sudah ada bertahun-tahun, dikemas jadi satu supaya mudah dipakai. Dua yang paling penting adalah namespace dan cgroup.
Namespace — mengatur apa yang boleh dilihat sebuah proses
Namespace mengubah apa yang boleh ditangkap oleh sebuah proses. Biasanya tiap proses di sebuah mesin melihat daftar global yang sama: daftar proses yang sama, antarmuka jaringan yang sama, titik mount yang sama. Namespace memberi sebuah proses versi pribadinya sendiri dari salah satu daftar tadi.
Ada beberapa jenis, dan masing-masing mengisolasi dimensi yang berbeda:
- PID namespace — pohon proses sendiri. Di dalamnya, proses pertama container adalah PID 1, dan dia bahkan tidak bisa melihat proses di luar namespace-nya. Bagi container, sisa mesin itu seolah-olah memang tidak ada.
- Mount namespace — sudut pandang filesystem sendiri. Container melihat direktori root-nya sendiri dan mount-nya sendiri, bukan milik host.
- Network namespace — tumpukan jaringan sendiri: antarmuka, alamat IP, dan tabel routing-nya sendiri. Inilah kenapa sebuah container bisa mendengarkan di port 80 secara internal tanpa bentrok dengan host atau container lain.
- UTS namespace — hostname sendiri.
- User namespace — pemetaan user ID sendiri, sehingga “root di dalam container” bisa jadi user biasa yang tidak berbahaya di sisi host.
- IPC namespace — saluran komunikasi antar-proses sendiri, supaya container tidak bisa saling mengirim pesan ke proses container lain lewat shared memory.
Tumpuk semua itu, dan sebuah proses benar-benar yakin dia sendirian. Dia tidak bisa melihat daftar proses di luar, tidak bisa melihat berkas di luar, tidak bisa menyentuh antarmuka jaringan di luar — bukan karena dia sopan dan memilih untuk tidak, tapi karena kernel memang tidak pernah memperlihatkannya.
SATU KERNEL LINUX (dipakai bersama)
┌──────────────────────────────────────────────┐
│ Container A Container B │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ PID ns: 1,2 │ │ PID ns: 1,2 │ ← masing-masing
│ │ mount ns: / │ │ mount ns: / │ mengira ia
│ │ net ns: eth0 │ │ net ns: eth0 │ punya PID 1 & /
│ └──────────────┘ └──────────────┘ │
│ ▲ ▲ │
│ └──── kernel yang sama ┘ │
└──────────────────────────────────────────────┘
Cgroup — mengatur apa yang boleh dipakai sebuah proses
Namespace menentukan apa yang boleh dilihat sebuah proses. Control group (cgroup) menentukan apa yang boleh dikonsumsi. Namespace mencegah Container A mengintip Container B; cgroup mencegah Container A melahap seluruh CPU sampai Container B kelaparan.
Cgroup membuat host bisa memasang batas keras pada sekelompok proses:
- CPU — “container ini paling banyak boleh memakai setengah dari satu inti.”
- Memori — “container ini dapat jatah 512 MB; kalau minta lebih, dia di-kill, bukannya menyeret seluruh mesin ikut tumbang.”
- I/O — batas seberapa besar bandwidth baca/tulis disk yang boleh dirampas sebuah container.
- Jumlah proses — plafon berapa banyak proses yang boleh dia munculkan, supaya loop yang lepas kendali tidak terus fork sampai mesin ambruk.
Tanpa cgroup, satu container yang berkelakuan buruk bisa menghabiskan seluruh sumber daya dan menjatuhkan semua hal lain di mesin itu. Cgroup-lah yang membuatnya aman untuk menumpuk puluhan container di satu server sambil tetap yakin tidak ada satu pun yang bisa merusak pesta. Kalau kata “proses” dan “batas” masih terasa goyah, artikel soal proses dan service sudah menyiapkan fondasinya.
Filesystem — root pribadinya sendiri
Bagian besar yang terakhir adalah filesystem. Lewat mount namespace ditambah fitur pengubahan root bergaya chroot, tiap container dibekali direktori root-nya sendiri. Apa yang terlihat sebagai / di dalam container sebenarnya cuma sebuah folder di host, yang disajikan seolah-olah itu adalah puncak dunia.
Root pribadi tadi dibangun dari image milik container — cetakan read-only yang jadi titik awal container. Image menyediakan kumpulan berkas minimal (sebuah shell, beberapa library, aplikasimu), dan container jalan seolah-olah itulah seluruh sistemnya. Karena image bersifat read-only, container menambahkan satu lapisan tipis yang bisa ditulis di atasnya untuk setiap perubahan yang dia buat, dan lapisan itu lenyap begitu container dihancurkan. Inilah kenapa container itu sekali pakai: tidak ada apa pun di dalamnya yang bertahan kecuali kamu sengaja menyimpannya di luar.
Sebagian besar cuma fitur Linux lama yang dirapikan jadi satu
Tidak satu pun dari mekanisme ini diciptakan khusus untuk container. Namespace, cgroup, dan pengubahan root semuanya sudah ada di kernel Linux sebagai alat yang terpisah-pisah. Yang dilakukan container runtime adalah merangkai semuanya di balik satu perintah sederhana, sehingga kamu dapat seluruh isolasi sekaligus tanpa harus menyetel tiap bagian satu per satu. Memahami bahwa semuanya adalah komponen terpisah itulah yang membuatmu bisa menalar isolasi secara jujur — dan mengencangkan bagian mana pun saat kamu butuh.
Apa yang dilindungi isolasi — dan apa yang tidak
Di sinilah banyak intuisi pemula tersesat. Container terasa seperti kotak tersegel, jadi orang mengira ia sama terpisahnya dengan dua mesin fisik yang berbeda. Padahal tidak, dan jaraknya itu penting.
Yang ditangani isolasi dengan baik:
- Dua aplikasi yang butuh versi library yang sama tapi saling bentrok bisa jalan berdampingan, masing-masing dengan berkasnya sendiri, tanpa pernah sadar ada yang lain.
- Sebuah crash, kebocoran memori, atau proses yang lepas kendali di satu container tetap berada dalam batas container itu dan tidak ikut menjatuhkan tetangganya.
- Tiap container dapat identitas jaringannya sendiri, jadi bentrok port dan tabrakan penamaan sebagian besar lenyap.
- Menghapus container itu bersih dan tuntas — saat lapisan tulisnya dihapus, dia tidak meninggalkan apa pun di host.
Di mana dindingnya lebih tipis daripada yang terlihat:
- Kernel dipakai bersama. Setiap container berbicara dengan kernel yang sama. Sebuah celah serius di kernel, secara teori, bisa membuat proses di satu container memengaruhi host atau container lain. VM tidak berbagi kernel, dan itulah kenapa VM tetap dianggap batas keamanan yang lebih kuat.
- “Root” di dalam itu bukan tanpa arti. Kecuali kamu memakai user namespace untuk memetakannya ulang, user root di dalam container bisa terlalu dekat dengan root di host. Menjalankan container sebagai user non-root adalah salah satu langkah pengamanan paling sederhana sekaligus paling ampuh.
- Isolasi itu bisa diatur, dan setelan bawaannya beragam. Kamu bisa sengaja melubanginya — berbagi jaringan host, mount sebuah direktori host, memberi hak istimewa tambahan. Masing-masing menukar isolasi demi kemudahan, dan gampang sekali memberi lebih banyak daripada yang kamu maksud.
Kesimpulan jujurnya: container memberimu isolasi operasional yang sangat bagus — aplikasi tidak saling sandung, dan sumber daya tetap berpagar. Sebagai batas keamanan, dia bagus tapi tidak kedap rapat, karena semua berbagi satu kernel. Untuk beban kerja yang tidak tepercaya atau berniat jahat, orang menambahkan lapisan ekstra (sandbox yang lebih ketat, atau VM utuh di bawahnya) justru karena kernel yang dipakai bersama itu.
KEKUATAN ISOLASI (model mental kasar)
mesin terpisah ████████████████ paling kuat
virtual machine ██████████████
container █████████ bagus, kernel bersama
proses yang sama ██ paling lemah
Model mental yang bisa kamu pegang
Saat kamu menalar sebuah container, ajukan tiga pertanyaan, satu untuk tiap mekanisme:
- Apa yang bisa dia lihat? Itu namespace. Prosesnya sendiri, filesystem-nya sendiri, jaringannya sendiri — sudut pandang terpisah atas satu mesin.
- Apa yang bisa dia pakai? Itu cgroup. Sepotong jatah CPU, memori, dan I/O yang dibatasi, supaya tidak ada satu container pun yang bisa membuat sisanya kelaparan.
- Dari apa dia dibangun? Itu image dan root read-only-nya, ditambah lapisan tulis sekali-buang yang membuat container jadi sekali pakai.
Beresin ketiga hal itu, dan hampir semua perilaku container berhenti jadi misterius. Kenapa container jauh lebih ringan daripada VM? Karena dia melewati sistem operasi kedua dan cuma meminjam kernel host. Kenapa data hilang saat container di-restart? Karena lapisan tulisnya memang dirancang sementara. Kenapa container bukan dinding keamanan yang sempurna? Karena kernel di bawahnya dipakai bersama.
Penutup
Ini gambaran utuhnya dalam satu tempat:
- Isolasi adalah ilusi bahwa tiap container memegang mesin itu sendirian, yang sengaja dijaga oleh kernel. Inilah ide inti yang membuat container berguna.
- Dia bukan satu dinding, melainkan banyak mekanisme terpisah yang bekerja bersama — apa yang dilihat, apa yang dipakai, dan dari apa dibangun.
- Namespace mengatur apa yang boleh dilihat sebuah proses: prosesnya, filesystem-nya, jaringannya, hostname-nya, dan user ID-nya sendiri.
- Cgroup mengatur apa yang boleh dipakai sebuah proses: batas keras untuk CPU, memori, I/O, dan jumlah proses.
- Tiap container dapat filesystem root pribadinya sendiri dari image read-only, ditambah lapisan tulis sekali-buang — itulah kenapa container ringan dan sekali pakai.
- Isolasi hebat untuk menjaga aplikasi agar tidak saling injak, tapi karena kernel dipakai bersama, dia adalah batas keamanan yang lebih lemah daripada virtual machine utuh — sesuatu yang harus kamu hormati saat menjalankan kode yang tidak tepercaya.
Berikutnya, ada baiknya kita lihat bagaimana dunia-dunia mungil yang terisolasi ini sebenarnya saling berbicara dan terhubung ke luar, dan itulah inti dari jaringan container — bagaimana network namespace yang terpisah-pisah dirangkai jadi sesuatu yang bekerja sebagai satu sistem.