Container #
Container adalah wujud hidup dari Docker. Saat developer dan operator berbicara tentang “menjalankan Docker”, yang sebenarnya mereka jalankan, hentikan, scale, dan deploy adalah container — bukan image, bukan daemon, bukan CLI. Image adalah cetakan; container adalah objek nyata yang sedang berjalan. Memahami container secara mendalam berarti memahami bagaimana Linux mengisolasi proses dengan namespaces, membatasi resource dengan cgroups, dan menyimpan perubahan runtime dengan union filesystem.
Artikel ini membahas container dari pondasi kernel sampai perilaku operasional. Kita akan melihat bagaimana container benar-benar bekerja di level sistem operasi, bagaimana ia berinteraksi dengan dunia luar lewat network namespace, dan bagaimana kamu bisa mengelolanya secara disiplin di production. Setelah membaca artikel ini, container bukan lagi “kotak hitam” — ia adalah proses yang terisolasi dan dapat dijelaskan.
Container sebagai Runtime Instance #
Container adalah proses Linux biasa yang berjalan di host dengan namespace dan cgroup tertentu. Ia tidak membawa kernel sendiri, tidak hypervisor, tidak ada abstraksi perangkat keras. Yang membedakannya dari proses biasa adalah batas-batas virtual yang diterapkan oleh kernel.
flowchart TB
subgraph HOST["Host OS (Linux Kernel)"]
subgraph NS1["Namespace Container A"]
PID1["PID: 1 = nginx"]
NET1["eth0: 172.17.0.2"]
MNT1["/ mounted"]
UTS1["hostname: container-a"]
end
subgraph NS2["Namespace Container B"]
PID2["PID: 1 = redis"]
NET2["eth0: 172.17.0.3"]
MNT2["/ mounted"]
UTS2["hostname: container-b"]
end
subgraph NS3["Namespace Container C"]
PID3["PID: 1 = postgres"]
NET3["eth0: 172.17.0.4"]
MNT3["/ mounted"]
UTS3["hostname: container-c"]
end
CG1["cgroup: 256 MB RAM, 0.5 CPU"]
CG2["cgroup: 512 MB RAM, 1.0 CPU"]
CG3["cgroup: 1 GB RAM, 2.0 CPU"]
end
NS1 --> CG1
NS2 --> CG2
NS3 --> CG3
Tiga karakteristik utama container:
- Isolasi — namespace membuat proses di dalam container merasa punya “sistem sendiri” (PID, network, filesystem, hostname, user, IPC).
- Pembatasan resource — cgroup memastikan container tidak menghabiskan semua CPU/RAM host.
- Lapisan filesystem — union filesystem (OverlayFS) menggabungkan image layer (read-only) dengan container layer (writable) menjadi satu mount yang dilihat container sebagai
/.
Saat kamu menjalankan docker run -d nginx, Docker (melalui daemon dan runc) hanya melakukan hal berikut: membuat PID namespace baru, NET namespace baru, MNT namespace baru, dan seterusnya, lalu execve() binary nginx di dalam namespace tersebut. Tidak ada hypervisor, tidak ada boot sequence, tidak ada init system. Karena itu container start dalam hitungan milidetik, bukan menit.
Cara mengingat: container adalah “sistem yang tidak ada” — apa yang dilihat proses di dalam container adalah ilusi yang diciptakan oleh kernel Linux. Semua resource yang ia “miliki” (PID 1, eth0, hostname) sebenarnya adalah resource host yang diberi view berbeda. Begitu proses di dalam container itu exit, namespace-nya di-remove dan ilusi itu hilang.
Namespace: Pondasi Isolasi #
Namespace adalah fitur kernel Linux yang membuat sekumpulan proses melihat resource sistem secara berbeda. Ada delapan jenis namespace yang dipakai container modern; ketiganya adalah yang paling relevan untuk Docker.
Jenis Namespace dan Efeknya #
| Namespace | Mengisolasi | Efek Visual dari Dalam Container |
|---|---|---|
PID |
Process ID | Container melihat PID sendiri mulai dari 1 |
NET |
Network interface, routing table, port | Container punya NIC virtual (eth0) sendiri |
MNT |
Filesystem mount points | Container melihat filesystem terbatas |
UTS |
Hostname dan domain name | Container bisa punya hostname sendiri |
IPC |
Inter-process communication (System V IPC, POSIX mq) | Container tidak bisa kirim signal ke proses host |
USER |
User dan group ID | Container punya root yang berbeda dari host |
CGROUP |
View ke cgroup hierarchy | Container hanya melihat cgroup-nya sendiri |
TIME |
System clock (Linux 5.6+) | Container bisa punya waktu berbeda (jarang dipakai) |
Namespace dalam Aksi #
Untuk melihat efek namespace secara langsung, kamu bisa masuk ke dalam container yang sedang berjalan dan “mengecek” lingkungannya:
# Masuk ke dalam container
docker exec -it mycontainer sh
# Di dalam container:
ps aux # Hanya melihat proses container (PID 1, dst)
hostname # "mycontainer" - bukan hostname host
ip addr # eth0 dengan IP 172.17.0.2
ls / # Filesystem image, bukan host
cat /proc/1/cgroup # cgroup container
Setiap output di atas adalah view virtual yang diciptakan oleh namespace. Dari sisi host, semua proses container terlihat sebagai PIDs biasa (PID 12345, 12346, dst), dengan hostname yang sama dengan host, dan terhubung ke bridge docker0.
Perintah untuk Melihat Namespace dari Host #
# Lihat namespace apa yang dipakai container
docker inspect mycontainer --format '{{json .State.Pid}}'
# Output: 12345
# Lihat semua namespace PID 12345
ls -la /proc/12345/ns/
# Output:
# lrwxrwxrwx 1 root root 0 Feb 7 10:00 ipc -> ipc:[4026532...]
# lrwxrwxrwx 1 root root 0 Feb 7 10:00 mnt -> mnt:[4026532...]
# lrwxrwxrwx 1 root root 0 Feb 7 10:00 net -> net:[4026532...]
# lrwxrwxrwx 1 root root 0 Feb 7 10:00 pid -> pid:[4026532...]
# lrwxrwxrwx 1 root root 0 Feb 7 10:00 user -> user:[4026532...]
# lrwxrwxrwx 1 root root 0 Feb 7 10:00 uts -> uts:[4026532...]
Setiap symlink di /proc/<pid>/ns/ menunjuk ke inode namespace tertentu. Inode yang sama berarti namespace yang sama; inode berbeda berarti namespace berbeda. Ini adalah representasi low-level dari “isolasi” yang sering kita bicarakan.
Detail menarik: namespace sudah ada di Linux sejak 2002 (mount namespace), tapi hanya dipakai oleh container secara mainstream sejak Docker 0.9 (2014). Sebelum itu, container kustom (LXC) sudah menggunakan namespace, tapi Docker-lah yang membuat teknologi ini mainstream untuk developer.
cgroups: Pembatasan Resource #
cgroups (control groups) adalah fitur kernel Linux yang membatasi dan mengukur resource usage dari sekumpulan proses. Jika namespace menjawab “apa yang dilihat container?”, cgroup menjawab “berapa banyak resource yang boleh dipakai container?”.
Resource yang Bisa Dibatasi #
| Resource | cgroup Controller | Contoh Flag Docker |
|---|---|---|
| CPU | cpu, cpuacct |
--cpus=1.5, --cpu-shares=512 |
| Memory | memory |
--memory=512m, --memory-swap=1g |
| Block I/O | blkio |
--device-read-bps, --device-write-iops |
| Network | net_cls, net_prio |
(via tc, bukan flag Docker) |
| PIDs | pids |
--pids-limit=100 |
| Devices | devices |
--device=/dev/snd (whitelist device) |
| Freezer | freezer |
(untuk docker pause) |
Contoh Penerapan #
# Container dengan batas memory 256 MB dan 0.5 CPU core
docker run -d --memory=256m --cpus=0.5 --name web nginx
# Container dengan limit pada jumlah PIDs (mencegah fork bomb)
docker run -d --pids-limit=100 myapp
# Container dengan prioritas CPU lebih rendah
docker run -d --cpu-shares=512 myapp # default 1024
cgroups v1 vs cgroups v2 #
Sejak kernel 5.x dan Docker 20.10+, Docker secara bertahap bermigrasi ke cgroups v2 — unified hierarchy yang lebih sederhana dan konsisten.
flowchart TB
subgraph V1["cgroups v1 (legacy)"]
V1A["/sys/fs/cgroup/cpu"]
V1B["/sys/fs/cgroup/memory"]
V1C["/sys/fs/cgroup/blkio"]
V1D["/sys/fs/cgroup/pids"]
V1E["/sys/fs/cgroup/devices"]
end
subgraph V2["cgroups v2 (modern)"]
V2A["/sys/fs/cgroup/<br/>(unified)"]
end
cgroups v2 punya beberapa keunggulan:
- Hierarki tunggal (tidak ada duplikasi kontroler).
- PSI (Pressure Stall Information) untuk monitoring yang lebih akurat.
- Kontrol yang lebih granular untuk memory dan IO.
- Support yang lebih baik untuk rootless mode.
Cara mengecek versi cgroup di host:
# Cek mount point cgroup
mount | grep cgroup
# Output v1: tmpfs on /sys/fs/cgroup type tmpfs
# cgroup on /sys/fs/cgroup/cpu type cgroup ...
# Output v2: cgroup2 on /sys/fs/cgroup type cgroup2
Anti-pattern: menjalankan container tanpa batas resource di production. Tanpa--memorydan--cpus, satu container bisa menghabiskan semua resource host dan membuat semua container lain kelaparan. Selalu set limit eksplisit, kecuali kamu benar-benar paham trade-off-nya.
Lifecycle Container #
Container punya state machine yang jelas. Memahami state-state ini penting untuk scripting dan monitoring.
stateDiagram-v2
[*] --> created: docker create
created --> running: docker start
running --> paused: docker pause
paused --> running: docker unpause
running --> exited: process exit / docker stop
created --> exited: never started
exited --> running: docker start
exited --> [*]: docker rm
running --> restarting: restart policy
restarting --> running: success
restarting --> exited: failed
State dan Arti Masing-Masing #
| State | Arti | Cara Mencapai |
|---|---|---|
created |
Container dibuat, filesystem siap, proses belum jalan | docker create atau sebelum docker start |
running |
Proses utama (PID 1) sedang berjalan | docker start |
paused |
Proses dihentikan sementara (sigstop ke semua thread) | docker pause |
exited |
Proses selesai (exit code 0 = normal, lain = error) | exit natural atau docker stop |
restarting |
Docker sedang me-restart container (sesuai policy) | restart policy triggered |
dead |
Container tidak bisa di-restart, harus dihapus | cleanup failure atau force remove |
Restart Policy #
# no : container tidak auto-restart
# on-failure: restart hanya jika exit code != 0
# always : restart selalu, termasuk saat Docker daemon start
# unless-stopped: restart kecuali user manual stop
docker run -d --restart=always nginx
docker run -d --restart=on-failure:5 myapp
# 5 = max retry, lalu state = exited
Perintah Lifecycle yang Paling Sering Dipakai #
# Lihat container yang sedang jalan
docker ps
# Lihat semua container (termasuk yang exited)
docker ps -a
# Inspect detail container
docker inspect mycontainer
# Lihat log
docker logs mycontainer
docker logs -f --tail 100 mycontainer # follow + tail
# Masuk ke container yang sedang jalan
docker exec -it mycontainer sh
docker exec -it mycontainer bash # jika image punya bash
# Kirim signal
docker stop mycontainer # SIGTERM, lalu SIGKILL setelah 10 detik
docker kill mycontainer # SIGKILL langsung
docker kill -s SIGUSR1 mycontainer # kirim signal custom
# Hapus container (harus stopped dulu, atau pakai -f)
docker rm mycontainer
docker rm -f mycontainer # paksa hapus meskipun running
# Hapus semua container yang sudah stopped
docker container prune
Detail menarik: docker stop mengirim SIGTERM dan menunggu 10 detik sebelum SIGKILL. Ini memberikan waktu pada aplikasi untuk graceful shutdown — menyelesaikan request yang sedang berjalan, flush buffer, tutup koneksi database. Aplikasi yang baik menangani SIGTERM dengan benar; aplikasi yang buruk akan langsung mati dan kehilangan state. Selalu test SIGTERM handling sebelum deploy ke production.
Container Networking #
Setiap container yang berjalan punya minimal satu network interface yang terpasang ke sebuah namespace network tertentu. Network ini menentukan bagaimana container berkomunikasi — dengan host, dengan container lain, dan dengan dunia luar.
Tipe Network Driver #
| Driver | Use Case | Karakteristik |
|---|---|---|
| bridge | Default untuk container standalone | Container dapat IP private, NAT ke host |
| host | Performa tinggi, semua port langsung ke host | Tidak ada network isolation |
| none | Container benar-benar terisolasi (no network) | Hanya loopback interface |
| overlay | Multi-host (Swarm, K8s) | VXLAN tunnel antar host |
| macvlan | Container punya MAC address sendiri | Berguna untuk legacy app yang butuh L2 access |
| ipvlan | Container share MAC host, beda IP | Mirip macvlan, lebih hemat MAC address pool |
Default Bridge vs Custom Bridge #
Docker otomatis membuat default bridge (docker0) yang dipakai semua container kecuali kamu membuat custom network.
flowchart LR
subgraph HOST["Host"]
DOCKER0[docker0 bridge<br/>172.17.0.1/16]
end
subgraph DEFAULT["Default bridge"]
C1["container-A<br/>172.17.0.2"]
C2["container-B<br/>172.17.0.3"]
C3["container-C<br/>172.17.0.4"]
end
subgraph CUSTOM["Custom network: app-net"]
C4["api<br/>172.18.0.2"]
C5["db<br/>172.18.0.3"]
C6["cache<br/>172.18.0.4"]
end
DOCKER0 --> C1
DOCKER0 --> C2
DOCKER0 --> C3
C4 --- C5
C5 --- C6
C4 --- C5
Custom bridge network punya keunggulan signifikan dibanding default:
- DNS otomatis — container bisa saling menyebut dengan nama, bukan IP.
- Isolasi — container di network yang berbeda tidak bisa saling komunikasi kecuali di-attach eksplisit.
- Better attach/detach — container bisa di-attach/detach dari network saat berjalan.
# Buat custom network
docker network create app-net
# Jalankan container di network ini
docker run -d --name api --network app-net myapi
docker run -d --name db --network app-net postgres
# Di dalam container 'api', kamu bisa:
ping db # resolves ke IP container 'db'
psql -h db -U postgres # juga works
Port Mapping (Publish) #
Container di default bridge tidak terekspos ke host. Untuk membuat container bisa diakses dari luar, kamu perlu publish port.
# Format: -p HOST_PORT:CONTAINER_PORT
docker run -d -p 8080:80 nginx
# 8080 di host → 80 di container
# Listen di IP tertentu
docker run -d -p 127.0.0.1:8080:80 nginx
# Publish range port
docker run -d -p 8000-8100:8000-8100 myapp
# Publish semua port yang dideklarasikan di EXPOSE
docker run -d -P nginx
Anti-pattern: docker run --network host kecuali kamu benar-benar paham konsekuensinya. Mode host menghilangkan network isolation — container bisa listen di port mana pun tanpa publish, dan port-nya bisa bentrok dengan service host. Untuk hampir semua use case, custom bridge network adalah pilihan yang lebih aman dan manageable.
Container Storage — Writable Layer, Volume, Bind Mount #
Setiap container yang berjalan punya writable layer di atas image layer-nya. Layer ini menyimpan semua perubahan runtime — log, cache, file yang ditulis aplikasi. Tapi layer ini sementara (ephemeral): saat container dihapus, layer hilang.
flowchart TB
subgraph IMAGE["Image Layers (read-only)"]
L1[Layer 1: base OS]
L2[Layer 2: dependencies]
L3[Layer 3: app code]
end
subgraph CONTAINER["Container"]
L4["Writable Layer<br/>(runtime changes)"]
end
subgraph HOST_FS["Host Filesystem"]
VOL["Named Volume<br/>(/var/lib/docker/volumes/)"]
BIND["Bind Mount<br/>(/host/path)"]
end
L1 --> L2 --> L3 --> L4
L4 -.->|"docker run -v"| VOL
L4 -.->|"docker run -v"| BIND
Tiga Mekanisme Persistence #
| Mekanisme | Syntax | Lokasi Fisik | Use Case |
|---|---|---|---|
| Writable layer | (default) | OverlayFS top layer | Perubahan runtime temporer |
| Named volume | -v mydata:/var/lib/data |
/var/lib/docker/volumes/ |
Database, app data — di-manage Docker |
| Bind mount | -v /host/path:/container/path |
Path host apa pun | Config, log, code dev |
Kapan Menggunakan Masing-Masing #
# Named volume: untuk data persisten yang di-manage Docker
docker run -d -v db_data:/var/lib/postgresql/data postgres
# Docker yang membuat, mount, dan menghapus direktori
# Bind mount: untuk share file antara host dan container
docker run -d -v /home/user/app:/app myapp
# File di host tersedia langsung di container
# tmpfs: untuk data rahasia yang tidak boleh masuk disk
docker run -d --tmpfs /run/secrets:rw,noexec,nosuid myapp
# Data hanya di RAM, hilang saat container stop
Panduan praktis: gunakan named volume untuk data aplikasi (database, upload file, log) dan bind mount untuk konfigurasi dan development. Jangan simpan data penting di writable layer container — ia akan hilang saat container dihapus atau di-restart tanpa volume.
Container vs Virtual Machine #
Pemahaman yang sering salah adalah menganggap container dan VM adalah dua pilihan yang setara. Padahal mereka bekerja di lapisan yang sangat berbeda.
flowchart TB
subgraph CONTAINER_STACK["Container Stack"]
APP1["App A"]
RUNTIME1["Bins/Libs"]
CONTAINER_ENGINE1["Container Engine (Docker)"]
OS1["Host OS"]
HW1["Hardware"]
end
subgraph VM_STACK["VM Stack"]
APP2A["App A"]
RUNTIME2A["Bins/Libs"]
GUEST_OS2A["Guest OS"]
HYPERVISOR2["Hypervisor"]
HOST_OS2["Host OS"]
HW2["Hardware"]
APP2B["App B"]
RUNTIME2B["Bins/Libs"]
GUEST_OS2B["Guest OS"]
end
APP1 --> RUNTIME1 --> CONTAINER_ENGINE1 --> OS1 --> HW1
APP2A --> RUNTIME2A --> GUEST_OS2A --> HYPERVISOR2 --> HOST_OS2 --> HW2
APP2B --> RUNTIME2B --> GUEST_OS2B --> HYPERVISOR2
Perbandingan Detail #
| Aspek | Container | Virtual Machine |
|---|---|---|
| Lapisan isolasi | Kernel (namespace + cgroups) | Hardware abstraction (hypervisor) |
| OS di dalam unit | Tidak ada (shared kernel) | Guest OS lengkap |
| Ukuran image | MB (10–500 MB) | GB (1–50 GB) |
| Waktu start | Milidetik–detik | Menit (30–120 detik) |
| Overhead RAM | MB (hanya app) | 500 MB – 4 GB (guest OS) |
| Overhead CPU | < 1% (native) | 1–5% (emulasi) |
| Density per host | 50–500 | 5–20 |
| Kernel | Shared dengan host | Sendiri per VM |
| Isolasi keamanan | Lebih lemah (shared kernel) | Sangat kuat (kernel terpisah) |
| Use case ideal | Microservices, CI/CD, cloud-native | OS berbeda, compliance ketat, legacy |
Kapan Memilih yang Mana #
Pilih container ketika:
- Aplikasi modern berbasis microservice atau stateless.
- Butuh deployment cepat dan auto-scaling.
- Ingin density tinggi di satu host.
- Butuh environment development yang konsisten dengan production.
Pilih VM ketika:
- Butuh menjalankan OS berbeda di satu host (misal Linux + Windows).
- Regulasi compliance secara eksplisit meminta hardware isolation.
- Aplikasi legacy yang bergantung pada kernel module atau driver tertentu.
- Multi-tenant dengan trust boundary yang sangat kuat.
Kombinasi keduanya adalah pola paling umum: VM sebagai infrastructure unit (yang di-provision oleh cloud), container sebagai application unit (yang berjalan di dalam VM). Kubernetes node pada dasarnya adalah VM (atau bare metal), dan pod adalah sekumpulan container yang berjalan di atas node itu.
Container Best Practice untuk Production #
Satu Container, Satu Proses #
# ANTI-PATTERN: banyak proses dalam satu container (via supervisor)
docker run -d myimage
# /etc/supervisor/conf.d/:
# [program:nginx]
# command=/usr/sbin/nginx
# [program:php-fpm]
# command=/usr/sbin/php-fpm
# [program:redis]
# command=redis-server
# BENAR: satu proses utama per container, diorkestrasi Compose/K8s
docker run -d nginx
docker run -d php-fpm
docker run -d redis
Mengapa satu proses per container lebih baik:
- Logging terstruktur — stdout/stderr langsung ke proses utama, mudah dikoleksi.
- Restart independen — kalau
php-fpmcrash,nginxtetap jalan. - Resource limit akurat — cgroup tahu persis proses mana yang harus dibatasi.
- Skalabilitas — tinggal replicate container yang butuh scaling.
Tangani Signal dengan Benar #
Aplikasi di dalam container harus bisa menangani SIGTERM dengan graceful. Ini memungkinkan docker stop melakukan shutdown yang bersih, bukan langsung SIGKILL.
# ANTI-PATTERN: tidak handle SIGTERM
import time
while True:
process_request()
time.sleep(1)
# BENAR: handle SIGTERM
import signal
import time
def shutdown(signum, frame):
print("Shutting down gracefully...")
# flush buffers, close connections, dll
exit(0)
signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)
while True:
process_request()
time.sleep(1)
Jangan Simpan State di Container #
Container adalah ephemeral — harus bisa dihapus dan diganti tanpa kehilangan data penting. Selalu gunakan volume atau external storage.
JANGAN dilakukan:
✗ Menyimpan database file di /var/lib/postgres di dalam
container.
✗ Menyimpan uploaded file di /app/uploads.
✗ Menyimpan konfigurasi runtime di /etc/myapp.conf
(gunakan configmap/secret/volume mount).
✗ Menulis log ke file di /var/log (gunakan stdout/stderr).
SELALU dilakukan:
✓ Named volume untuk database storage.
✓ Object storage (S3/GCS) untuk uploaded files.
✓ External config (file/env/secret) yang di-mount atau
di-pass sebagai env var.
✓ stdout/stderr untuk log, dikoleksi oleh logging driver.
User Non-Root #
Secara default, container berjalan sebagai root di dalam namespace-nya sendiri. Root di dalam user namespace bukan root di host (terima kasih kepada user namespace), tapi tetap prinsip least privilege menyarankan kita untuk tidak run sebagai root.
# Di Dockerfile
RUN adduser -D -u 1000 appuser
USER appuser
CMD ["./myapp"]
Atau di runtime:
docker run -d --user 1000:1000 myapp
Catatan tentang user namespace: sejak Docker 20.10 dengan userns-remap, UID root di container dipetakan ke UID non-root di host. Jadi bahkan tanpa --user, container root tidak punya akses setara host root. Tapi kombinasi userns-remap + USER nonroot di Dockerfile tetap praktik terbaik.
Health Check #
Tambahkan HEALTHCHECK di Dockerfile agar Docker tahu apakah container benar-benar siap melayani request, bukan hanya prosesnya jalan.
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
docker ps akan menampilkan status health:
CONTAINER ID STATUS NAMES
abc123def456 Up 5 minutes (healthy) myapi
789ghi012jkl Up 10 minutes (unhealthy) myapi-broken
Container yang unhealthy bisa di-restart otomatis atau di-exclude dari load balancer (terutama di Docker Swarm / K8s).
Decision Tree — Container vs VM, atau Kombinasi? #
flowchart TD
A{Aplikasi butuh<br/>OS berbeda?}
A -- Ya --> VM["Gunakan VM<br/>(atau multi-VM K8s cluster)"]
A -- Tidak --> B{Compliance<br/>mewajibkan hardware isolation?}
B -- Ya --> VM
B -- Tidak --> C{Butuh scaling<br/>dan startup cepat?}
C -- Ya --> CONT[Container + Orchestrator]
C -- Tidak --> D{Trust boundary<br/>antar workload kuat?}
D -- Ya --> VM_AT_EDGE["VM per workload<br/>+ container di dalamnya"]
D -- Tidak --> CONT
Untuk 90% aplikasi cloud-native modern, container adalah pilihan yang benar. Untuk 10% sisanya (legacy, compliance, OS-spesifik), VM tetap relevan. Kombinasi VM sebagai host + container sebagai unit aplikasi adalah pola paling umum di production modern.
Ringkasan #
- Container adalah proses Linux yang berjalan di namespace dan cgroup tertentu. Ia tidak membawa kernel sendiri — semua container di satu host berbagi kernel host. Inilah yang membuat container ringan dan cepat start.
- Namespace mengisolasi persepsi container (apa yang dilihatnya): PID, network, filesystem, hostname, user, IPC. cgroups mengisolasi resource (berapa banyak yang boleh dipakai): CPU, memory, I/O, PIDs.
- Lifecycle container: created → running → paused → exited → (restarting) → removed. Setiap state punya transisi yang jelas. Selalu test SIGTERM handling agar container bisa shutdown dengan graceful.
- Writable layer di atas image layer menyimpan perubahan runtime, tapi sementara — hilang saat container dihapus. Untuk data persisten, gunakan named volume atau bind mount.
- Network container punya beberapa driver: bridge (default), host, none, overlay, macvlan, ipvlan. Custom bridge memberikan DNS otomatis dan isolasi yang lebih baik dari default bridge.
- Best practice production: satu container satu proses, run sebagai non-root, tahan SIGTERM, gunakan health check, log ke stdout/stderr, simpan data di volume/eksternal storage.
- Container ≠ VM. Container mengisolasi di level kernel, VM di level hardware. Container lebih ringan dan cepat, VM lebih kuat dalam isolasi. Pola paling umum: VM sebagai host, container sebagai application unit.
- Orkestrasi container (Compose, Swarm, Kubernetes) mengelola banyak container di banyak host. Untuk setup kecil, Compose cukup. Untuk multi-host, kamu butuh orchestrator yang proper.