Kita sudah memahami apa itu container dan bagaimana container berdiri di samping mesin virtual. Sekarang mari kita buka kap mesinnya: ketika kita mengetik docker run di terminal, tidak ada sihir yang terjadi — ada sekumpulan komponen yang bekerja berlapis dan saling berbicara melalui antarmuka yang jelas. Memahami arsitektur ini membuat galat di masa depan terasa masuk akal, karena kita tahu komponen mana yang sedang bersuara, dan membuat istilah seperti containerd atau runtime tidak lagi terdengar asing. Chapter ini membedah arsitektur client-server Docker dari CLI di ujung jari sampai proses container di dalam kernel.

Arsitektur Client-Server #

Docker berarsitektur client-server. Perintah docker yang kita ketik di terminal hanyalah klien CLI yang ringan: klien tidak membuat container maupun mengunduh image dengan caranya sendiri. Semua pekerjaan berat dilakukan dockerd — daemon Docker yang berjalan di latar belakang — dan klien berbicara dengan daemon tersebut melalui REST API yang secara default dapat diakses lewat Unix socket /var/run/docker.sock, atau lewat TCP untuk koneksi jarak jauh.

Di dalam host, dockerd pun tidak bekerja sendirian. Pengelolaan siklus hidup container didelegasikan ke containerd, runtime tingkat tinggi yang juga menjadi standar di luar Docker — Kubernetes memakainya juga. Untuk benar-benar melahirkan proses container, containerd memanggil runc: runtime tingkat rendah yang membentuk namespace dan cgroups sesuai spesifikasi OCI, lalu menyerahkan proses yang sudah hidup kembali ke containerd untuk diawasi. Pada sisi distribusi image berdiri registry seperti Docker Hub yang menyimpan image siap unduh. Arsitektur berlapis inilah yang digambarkan diagram berikut.

Diagram Arsitektur #

Mari kita telaah arti setiap panah pada diagram di atas:

  1. docker CLI ke dockerd — satu-satunya jalur komunikasi klien adalah REST API; klien boleh berada di mesin yang sama dengan daemon maupun di mesin jauh yang mengarah ke daemon lewat jaringan.
  2. dockerd ke registry dan sebaliknya — saat image belum ada di lokal, dockerd meminta manifest dan layer image dari registry (pull); ke arah sebaliknya, hasil build dikirim ke registry untuk disimpan (push).
  3. dockerd ke containerd — daemon tidak memegang container dengan tangannya sendiri; seluruh siklus hidup container diserahkan ke containerd.
  4. containerd ke runc — untuk melahirkan proses, containerd menyiapkan rootfs dan konfigurasi proses, lalu memanggil runc.
  5. runc ke proses container — runc membentuk isolasi lalu mundur setelah proses hidup; containerd lah yang terus mengawasi proses tersebut melalui komponen shim.

Perjalanan docker run nginx #

Teori di atas akan lebih melekat jika kita telusuri satu perintah nyata. Mari kita ikuti perjalanan docker run nginx — perintah yang akan sering kita pakai sepanjang Course ini — dalam lima langkah:

  1. CLI mengirim permintaan — klien docker mengurai argumen nginx, lalu mengirim permintaan pembuatan container ke dockerd melalui REST API.
  2. Daemon menarik image — dockerd memeriksa penyimpanan image lokal; karena image nginx belum ada, daemon menghubungi registry Docker Hub, membaca manifest, dan mengunduh layer-layer yang belum dimiliki.
  3. containerd menyiapkan rootfs — dockerd menyerahkan pekerjaan ke containerd; containerd menyusun rootfs dari layer image dan menyiapkan konfigurasi proses yang akan dijalankan.
  4. runc menghidupkan proses — containerd memanggil runc; runc membentuk namespace dan cgroups, memulai nginx sebagai proses dengan PID 1 di dalam container, lalu runc selesai dan kontrol kembali ke containerd.
  5. CLI menerima hasil — daemon mengirim ID container kembali ke CLI dan mengalirkan keluaran aplikasi ke terminal; nginx kini hidup sebagai proses terisolasi di host.

Membedah docker version #

Perintah docker version membaca versi setiap komponen tanpa menyentuh container apa pun, sehingga aman dijalankan kapan saja. Outputnya sekaligus menjadi foto keluarga arsitektur yang baru kita bahas: bagian Client untuk CLI beserta plugin-nya, dan bagian Server untuk daemon beserta runtime di bawahnya. Mari kita jalankan:

bash
$ docker version

Output yang Dihasilkan:

text
Client: Docker Engine - Community
 Version:    29.9.0
 OS/Arch:    linux/amd64
 Context:    default
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.28.0
  compose: Docker Compose (Docker Inc.)
    Version:  v5.6.0

Server: Docker Engine - Community
 Engine:
  Version:          29.9.0
  API version:      1.52
  OS/Arch:          linux/amd64
 containerd:
  Version:          2.1.4
 runc:
  Version:          1.3.2

Output aslinya sedikit lebih panjang — setiap plugin juga mencantumkan baris Path, dan bagian Server mencantumkan Git commit serta waktu build — tetapi baris-baris tersebut kita potong karena tidak berpengaruh pada pembahasan. Mari kita telaah bagian-bagian pentingnya:

  1. Client Version 29.9.0 — versi CLI yang terpasang di mesin kita; versi CLI tidak harus sama dengan versi daemon, karena keduanya berkomunikasi lewat REST API yang dirancang kompatibel antar versi.
  2. Context default — context menentukan daemon mana yang dituju CLI; cara memiliki banyak context untuk banyak daemon sekaligus kita bahas pada Chapter Memahami Docker Context.
  3. Plugins buildx dan compose — dua plugin CLI yang menyatu pada perintah docker: docker buildx untuk build modern dan docker compose untuk mengelola stack multi-container.
  4. Server Engine 29.9.0 dengan API version 1.52 — ini versi dockerd dan REST API-nya; klien lama yang hanya memahami API lebih rendah tetap dapat diajak bicara oleh daemon baru.
  5. containerd 2.1.4 dan runc 1.3.2 — bukti nyata arsitektur berlapis pada sisi Server; keduanya adalah komponen terpisah yang diawasi dockerd, persis seperti diagram kita di atas.

Perintah ini juga berguna saat diagnosis: jika daemon mati, docker version hanya menampilkan bagian Client disertai galat koneksi. Kita akan sering memakainya lagi pada Section Instalasi dan Konfigurasi saat memverifikasi pemasangan.

Peran Setiap Komponen #

KomponenPeran
docker CLIKlien baris perintah yang menerima input kita dan menerjemahkannya menjadi panggilan REST API
REST APIAntarmuka resmi daemon — semua alat di ekosistem Docker berbicara melalui API ini
dockerdDaemon inti yang mengelola image, container, network, dan volume
containerdRuntime tingkat tinggi yang mengawasi siklus hidup container dari lahir sampai mati
runcRuntime tingkat rendah yang membentuk proses container sesuai spesifikasi OCI
registryLayanan penyimpanan dan distribusi image, contohnya Docker Hub

Enam komponen ini tidak perlu kita hafal urutan panggilannya setiap saat. Yang penting adalah ingat pembagiannya: CLI di sisi pengguna, dockerd sebagai pintu gerbang sekaligus otak, containerd dan runc sebagai pelaksana di level proses, serta registry di sisi distribusi. Pembagian peran inilah yang membuat Docker tetap rapi meski ekosistemnya terus membesar.

Namespace dan Cgroups #

Kita berulang kali menyebut namespace dan cgroups sebagai fondasi isolasi container; saatnya melihat apa saja yang sebenarnya diisolasi. Kernel Linux menyediakan beberapa jenis namespace, dan Docker mengaktifkannya secara otomatis setiap kali container dibuat:

NamespaceYang diisolasi
pidNomor dan hierarki proses — container melihat prosesnya sendiri, dan proses utamanya menjadi PID 1
netKartu jaringan, tabel routing, dan port — container memiliki stack jaringan sendiri
mntTitik mount dan sistem berkas yang terlihat oleh proses
utsHostname dan nama domain milik container
ipcFasilitas komunikasi antar proses seperti shared memory dan semaphore
userPemetaan UID dan GID — proses root di container dapat dipetakan ke pengguna biasa di host

Keenam namespace inilah yang membuat proses nginx pada section sebelumnya merasa hidup di mesinnya sendiri. Sementara namespace membatasi apa yang dilihat proses, cgroups membatasi berapa banyak resource yang boleh dipakai proses tersebut. Docker menyediakan flag siap pakai untuk kebutuhan ini: --cpus membatasi jumlah CPU dan --memory membatasi besaran memori sebuah container. Pembahasan penuh pembatasan resource menunggu Section Manajemen Resource.

Dengan arsitektur dan fondasi isolasi yang kini jelas, satu pertanyaan praktis tinggal menggantung: perangkat-perangkat ini kita dapatkan dari mana, dan dalam bentuk paket apa? Chapter Docker Engine dan Docker Desktop menjawabnya dengan membandingkan dua jalur distribusi resmi Docker.

Referensi #