Membedah arsitektur client-server Docker: Docker Client, daemon (dockerd), dan Registry; tumpukan runtime modern (containerd, runc) serta peran standard OCI; hingga empat komponen inti — Image, Container, Volume, dan Network — beserta alur perintah dari CLI ke runc.

Setelah di episode 1 sebelumnya kita memahami mengapa Docker lahir — dari evolusi bare-metal → VM → kontainer, masalah "It works on my machine!", hingga teknologi kernel Linux (cgroups, namespaces, chroot) yang menjadi fondasinya — pada episode kali ini kita membedah mesinnya dari dalam: bagaimana Docker bekerja, siapa melakukan apa, dan bagaimana sebuah perintah sederhana berjalan sepanjang rantai komponen.
Memahami arsitektur bukan sekadar pengetahuan akademik. Ketika error muncul — misalnya Cannot connect to the Docker daemon, OCI runtime create failed, atau unauthorized: access token has expired — kalian harus tahu di level mana masalah itu terjadi. Apakah di client, di daemon, di containerd, atau di registry? Kemampuan memetakan error ke komponen adalah skill diagnostik pertama yang membedakan engineer yang memahami sistem dari yang hanya menghafal perintah.
Di episode ini kita akan membedah arsitektur client-server Docker (client, daemon, registry), tumpukan runtime modern yang berjalan di belakang layar (containerd, runc, dan standard OCI), empat komponen inti Docker (Image, Container, Volume, Network), dan menutup dengan melacak perjalanan satu perintah dari CLI hingga runc menciptakan proses kontainer.
Docker dibangun dengan pola client-server — tiga aktor utama yang saling terpisah:
docker yang kalian ketik di terminal. Ia hanya mengirim perintah (lewat REST API) dan menampilkan respons. Ia tidak menjalankan atau menyimpan apa pun.dockerd) berjalan. Daemon inilah otak operasional: menerima permintaan API, mengelola image, menjalankan dan menghentikan kontainer, mengelola network dan volume.Yang paling penting untuk dipahami: client dan daemon tidak harus berada di mesin yang sama. Client berbicara ke daemon lewat API — secara default lewat Unix socket /var/run/docker.sock, atau lewat jaringan (misalnya docker -H tcp://server:2375). Inilah kenapa kalian bisa mengelola server produksi yang berjarak ribuan kilometer hanya dengan mengetik perintah docker di laptop. Konsep ini juga yang membuat Docker Context (di episode 24) bisa beralih antar host dari satu CLI.
┌─────────────┐ REST API ┌─────────────┐ pull/push ┌──────────────┐
│ Client │ ───────────► │ Daemon │ ─────────────► │ Registry │
│ (docker) │ │ (dockerd) │ │ Docker Hub │
└─────────────┘ └─────────────┘ └──────────────┘Note
Ketika docker pull atau docker run sepertinya "tidak melakukan apa-apa" — misalnya error Cannot connect to the Docker daemon at unix:///var/run/docker.sock — itu artinya client tidak bisa menjangkau daemon. Periksa urutan ini: daemon jalan? (systemctl status docker) → socket ada? → grup docker kalian benar? Pada episode 0 kita sudah menyiapkan semuanya.
Arsitektur Docker tidak berhenti di dockerd. Sejak dirilisnya Docker Engine 1.11 (2016), runtime dipecah menjadi komponen modular yang terpisah. Memahami lapisan ini adalah kunci membaca log error produksi.
containerd adalah daemon pengelola siklus hidup kontainer — daemon yang menciptakan, menjalankan, menghentikan, dan menghapus kontainer. Ia adalah standar industri yang sebenarnya: selain dipakai Docker, containerd juga dipakai Kubernetes (via CRI) dan banyak runtime lain. dockerd tidak lagi mengelola kontainer secara langsung — ia mendelegasikan pekerjaan itu ke containerd.
Bayangkan containerd sebagai manajer proyek: ia tidak membangun gedung sendiri, tapi ia yang mengatur seluruh proses — kapan mulai, kapan berhenti, siapa mengerjakan apa.
runc adalah runtime tingkat rendah (low-level) yang benar-benar menciptakan dan menjalankan proses kontainer menggunakan primitives kernel: namespaces untuk isolasi, cgroups untuk pembatasan resource. Ketika sebuah kontainer "diciptakan", yang bekerja di level paling bawah adalah runc — ia memanggil kernel untuk membuat proses baru yang terisolasi.
Lanjutkan analoginya: runc adalah kontraktor yang menembok dan memasang pintu — eksekutor di lapangan yang tangannya menyentuh bahan baku (kernel).
OCI adalah organisasi standar (di bawah Linux Foundation) yang dibentuk Docker bersama komunitas untuk memastikan teknologi kontainer tidak lagi terkunci pada satu vendor. OCI mendefinisikan dua spesifikasi utama:
config.json-nya, dan bagaimana filesystem dan proses diatur.Inilah mengapa ekosistem kontainer begitu kaya: image Docker bisa dijalankan oleh Kubernetes, containerd, Podman, atau CRI-O — karena semuanya mematuhi standar yang sama. Ini analogi dengan steker dan soket standar: selama mengikuti spesifikasi, alat dari produsen mana pun cocok.
dockerd ──► containerd ──► runc ──► kernel (cgroups + namespaces)
(API) (lifecycle) (create/run)Selain arsitektur proses, Docker memperkenalkan empat abstraksi yang menjadi bahasa keseharian. Kuasai keempatnya — semua episode berikutnya hanya memutar di sekitar konsep ini.
Image adalah cetakan (blueprint) aplikasi yang read-only dan tidak berubah: berisi base OS (misal Ubuntu minimal), runtime (Node.js, Python), library, kode aplikasi, dan konfigurasi default. Image disusun dari lapisan-lapisan (layers) yang saling menumpuk — semakin dalam kita bahas di episode 23. Karena read-only, image aman dibagikan: tidak ada yang bisa mengubahnya secara tidak sengaja.
Bayangkan image sebagai cetakan kue: resep tetap, hasilnya konsisten setiap kali. Kalian bisa mencetak seribu kue identik dari satu cetakan.
Container adalah instance yang berjalan dari sebuah image. Saat image dijalankan, Docker menambahkan lapisan read-write di atas lapisan-lapisan read-only image — inilah "kertas kerja" tempat proses kontainer menulis. Perubahan yang ditulis di sini hilang saat kontainer dihapus — ini fakta penting yang akan menjadi masalah besar di episode 8 (storage).
Analogi paling pas: image adalah cetakan kue, container adalah kue jadi yang bisa dimakan — dan setiap perubahan (topping yang ditambahkan) hanya ada di kue itu, bukan di cetakannya. Hapus kuenya, topping ikut hilang.
Volume adalah mekanisme penyimpanan data yang hidup di luar siklus hidup kontainer — data tetap ada meskipun kontainer dihapus dan diganti. Ia dipasang (mount) ke direktori tertentu di dalam kontainer, tapi data fisiknya disimpan di host (atau remote storage). Database di produksi harus memakai volume, atau semua data hilang saat kontainer di-recreate.
Bayangkan volume sebagai lemari arsip yang berdiri di luar kamar: kamar (kontainer) boleh dibongkar pasang, tapi arsipnya tetap aman di lemari.
Network mengatur bagaimana kontainer berkomunikasi — antar kontainer, dan dengan dunia luar. Secara default, kontainer bergabung ke network bridge yang memberi setiap kontainer IP internal, sehingga kontainer bisa saling terhubung tapi terisolasi dari host. Port yang kalian petakan (-p 8080:80) adalah jembatan dari dunia luar ke IP internal itu.
Bayangkan network sebagai gang-gang kecil antar rumah: tetangga (kontainer) di gang yang sama bisa saling berkunjung; orang luar harus lewat gerbang utama (port) yang dipetakan.
Sekarang mari satukan semuanya. Apa yang sebenarnya terjadi ketika kalian menjalankan perintah paling sederhana:
docker run hello-worldPerhatikan bahwa satu perintah ini melewati enam langkah di empat komponen berbeda — namun kalian hanya melihat satu baris output. Inilah yang dimaksud arsitektur yang baik: kompleksitas disimpan, antarmuka dipermudah. Tetap penting untuk tahu apa yang terjadi di setiap langkah, karena di episode-episode berikutnya kita akan berulang kali "membuka kap" rantai ini — saat error, saat tuning performa, saat menghubungkan kontainer ke storage dan network.
Urutan kejadian di balik satu baris ini:
docker) menerima perintah, memaknai argumen, dan mengirim request REST ke daemon lewat socket /var/run/docker.sock. "Jalankan image hello-world."dockerd) mengecek apakah image hello-world sudah ada di cache lokal. Jika tidak, daemon mengunduhnya dari registry (Docker Hub) dan menyimpannya ke penyimpanan image.docker run hello-world
│ 1. request API
▼
dockerd ──► cek image lokal ──► (jika belum ada) pull dari registry
│ 2. minta create + start
▼
containerd ──► siapkan spesifikasi OCI
│ 3. minta runc
▼
runc ──► kernel: namespaces + cgroups ──► proses kontainer berjalanKalian tidak akan pernah melihat sebagian besar rantai ini secara langsung — tetapi saat error muncul, peta ini menjadi alat diagnosis. Cannot connect = masalah di langkah 1. pull access denied = masalah di langkah 2 (registry). OCI runtime create failed = masalah di langkah 4–5 (runc/kernel). Error docker inspect di episode 4 akan menunjukkan detail dari setiap level ini.
Important
Jangan bingung antara "Docker Engine" dan ekosistem sekitarnya. Docker Engine (yang kalian install di episode 0) mencakup client, dockerd, dan integrasi containerd/runc. Docker Desktop adalah aplikasi yang membungkus Engine untuk macOS/Windows dengan tambahan VM, GUI, dan fitur. Containerd bisa berdiri sendiri tanpa Docker sama sekali — itulah yang dipakai Kubernetes. Semua berbagi fondasi OCI yang sama.
Manfaat praktis terbesar dari memahami arsitektur adalah kemampuan menentukan lokasi error dalam hitungan detik. Setiap pesan error Docker mengindikasikan lapisan mana yang gagal — dan tiap lapisan punya resep pemecahannya sendiri:
| Error Umum | Lapisan yang Gagal | Tindakan Pertama |
|---|---|---|
Cannot connect to the Docker daemon at unix:///var/run/docker.sock | Client → daemon (langkah 1) | Cek daemon jalan (systemctl status docker), socket ada, grup docker benar |
pull access denied for <image>, repository does not exist or may require 'docker login' | Daemon → registry (langkah 2) | Cek nama image, atau docker login untuk image privat |
unauthorized: authentication required | Registry (token/credential) | docker login, cek token belum kedaluwarsa |
OCI runtime create failed: ... container_linux.go ... | containerd/runc (langkah 4–5) | Cek storage driver, ruang disk, dan kernel host |
port is already allocated | Network / daemon | ss -tlnp untuk menemukan pemakai port, lalu ganti port host |
Kontainer Exited berulang tanpa sebab | Proses aplikasi | docker logs + docker inspect State.ExitCode |
Tip
Kebiasaan yang menyehatkan: baca error dari klausa terdalam — bagian yang menyebut file, baris, atau nama sistem paling spesifik biasanya menunjukkan akar masalah, bukan bagian luar yang hanya membungkusnya. OCI runtime create failed terdengar mengerikan, tapi klausa setelah tanda titik dua (misal mount ...: no space left on device) justru yang menjawab: disk penuh.
Pada episode 2 ini kalian telah memahami bahwa Docker adalah arsitektur client-server tiga aktor: client (docker) yang mengirim perintah lewat REST API, daemon (dockerd) yang menjadi otak operasional, dan registry (Docker Hub dan lainnya) sebagai penyimpanan image yang portabel. Di balik layar, dockerd mendelegasikan siklus hidup kontainer ke containerd (manajer lifecycle standar industri), yang pada gilirannya meminta runc (runtime low-level) menciptakan proses kontainer menggunakan namespaces dan cgroups — semuanya distandarkan oleh OCI melalui image-spec dan runtime-spec.
Inti yang harus kalian bawa:
dockerd → containerd → runc: dari API, ke manajemen lifecycle, ke eksekusi proses kernel.Sekarang kalian tahu bagaimana Docker bekerja dari dalam. Di episode 3 selanjutnya kita langsung praktik: menjalankan kontainer pertama — docker run hello-world, docker run -it ubuntu bash, mode detached, manajemen kontainer (ps, stop, start, rm), docker exec dan docker logs, hingga port mapping yang menghubungkan kontainer ke dunia luar. Saatnya mengotori tangan! Sampai jumpa di episode 3.