Overview #

Docker bukan sekadar tool yang menerima perintah docker run lalu menjalankan container. Di balik CLI yang sederhana itu, ada platform berlapis dengan komponen yang saling berinteraksi — daemon, REST API, container runtime, image, container itu sendiri, dan registry. Memahami arsitektur Docker bukan tujuan akhir; ini adalah fondasi untuk semua hal lain yang akan kamu bangun di atasnya, mulai dari optimasi build image sampai debugging container yang tiba-tiba mati di production.

Artikel ini memberikan gambaran tingkat tinggi dari seluruh arsitektur Docker. Tujuannya bukan menjelaskan setiap komponen secara mendalam (itu dilakukan oleh artikel-artikel berikutnya di section ini), melainkan menunjukkan peta besarnya — siapa berbicara dengan siapa, dalam urutan apa, dan mengapa setiap lapisan ada. Setelah membaca artikel ini, kamu akan punya kerangka berpikir yang membuat artikel-artikel teknis terasa saling menyambung, bukan potongan-potongan yang terpisah.

Kapan Perlu Memahami Arsitektur Docker? #

Tidak semua orang perlu tahu detail internal Docker. Tapi ada momen-momen di mana pemahaman arsitektur berubah dari “nice to know” menjadi “critical to know” — dan momen itu sering datang saat masalah sudah terjadi.

HARUS paham arsitektur Docker jika:
  ✓ Kamu mendeploy container ke production dan harus debug
    isu yang tidak muncul di development (resource limit,
    network, IPC).
  ✓ Kamu menulis Dockerfile yang build-nya lambat dan ingin
    оптимизировать layer caching.
  ✓ Kamu menggunakan Docker di CI/CD dan ingin tahu kenapa
    satu pipeline lambat padahal imagenya kecil.
  ✓ Kamu mengelola banyak host Docker (Swarm, K8s) dan
    perlu tahu bagaimana komunikasi remote bekerja.
  ✓ Kamu menghadapi masalah keamanan dan harus mengerti
    attack surface dari docker daemon.

TIDAK HARUS paham mendalam jika:
  ✗ Kamu hanya menjalankan `docker run` sekali-kali untuk
    eksplorasi dan tidak mendeploy apa pun.
  ✗ Kamu menggunakan layanan managed (ECS, Cloud Run, Fargate)
    yang menyembunyikan daemon dari kamu.
  ✗ Kamu hanya jadi pengguna akhir aplikasi tanpa mengurus
    infrastrukturnya.

Kabar baiknya, pemahaman arsitektur bersifat kumulatif. Kamu tidak harus menghafal semua diagram dalam sekali jalan. Yang penting adalah memiliki peta yang cukup akurat di kepala, lalu memperdalam satu area saat membutuhkannya. Artikel ini adalah peta itu.

Cara membaca section ini: enam artikel di section Architecture saling melengkapi. Artikel ini memberikan peta besarnya. Artikel Engine & Daemon mendalami inti platform. Image menjelaskan blueprint. Container membahas runtime. Registry menjelaskan distribusi. Dan How It Works menyatukan semuanya di level kernel. Baca berurutan, atau loncat ke artikel yang paling relevan dengan masalahmu.


Tiga Lapis Arsitektur Docker #

Docker mengikuti pola client–server klasik, tapi dengan satu lapis tambahan di tengah: runtime. Untuk kebanyakan interaksi, kamu hanya perlu tahu tiga lapis ini.

flowchart LR
    USER[Developer / CI / Tooling]
    subgraph CLIENT["Client Layer"]
        CLI[docker CLI]
        API[Docker SDK]
    end
    subgraph ENGINE["Engine Layer"]
        DAEMON[dockerd]
        RUNTIME[containerd + runc]
    end
    subgraph DELIVERY["Delivery Layer"]
        REG[Registry]
        IMAGES[(Image Storage)]
    end

    USER --> CLI
    CLI -->|"REST API<br/>(unix socket / TCP)"| DAEMON
    API --> DAEMON
    DAEMON --> RUNTIME
    DAEMON <-->|"pull / push"| REG
    REG --- IMAGES

Client Layer adalah apa yang kamu sentuh setiap hari — docker CLI di terminal, atau library SDK di aplikasi (misalnya Docker SDK untuk Python atau Go). Client tidak punya logika bisnis Docker; ia hanya menerjemahkan perintah menjadi request HTTP berformat REST API, lalu mengirimkannya ke daemon.

Engine Layer adalah tempat semua keputusan dibuat. Di sinilah dockerd (Docker Daemon) berjalan sebagai service background, menerima request dari client, memutuskan apa yang harus dilakukan, dan mendelegasikan eksekusi ke runtime yang lebih rendah (containerd lalu runc). Engine layer ini yang paling kompleks dan paling banyak artikel ini dan section ini akan bahas.

Delivery Layer adalah tempat image disimpan dan didistribusikan. Registry bisa publik (Docker Hub) atau privat (ECR, GHCR, self-hosted). Image yang sudah di-push ke registry adalah artifact yang bisa diambil oleh host mana pun yang punya akses.

Pemisahan ini penting karena menjelaskan banyak perilaku Docker yang terlihat misterius. Misalnya, kenapa docker build dan docker run bisa dilakukan dari mesin yang berbeda? Karena client dan engine adalah proses terpisah yang berkomunikasi lewat network. Kenapa image kecil tapi container yang berjalan besar? Karena image adalah paketan, container adalah instansi yang sedang berjalan dan menambahkan layer writable di atasnya. Semua jawaban ini kembali ke tiga lapis di atas.

Anatomi Docker Engine #

Engine adalah jantung Docker. Memahami anatominya di tingkat tinggi akan membuat semua istilah yang sering muncul — daemon, runtime, API — punya tempat di kepalamu.

flowchart TB
    subgraph ENGINE["Docker Engine"]
        DAEMON["dockerd<br/>(Docker Daemon)<br/><br/>• Image management<br/>• Container lifecycle<br/>• Network & volume<br/>• REST API server"]
        API["Docker REST API<br/><br/>• /v1.41/...<br/>• Default: unix:///var/run/docker.sock"]
        CLI["docker CLI<br/><br/>• build, run, pull, push<br/>• ps, logs, exec"]
        RUNTIME["Container Runtime<br/><br/>• containerd (high-level)<br/>• runc (low-level, OCI)"]
    end

    CLI -->|"HTTP request"| API
    API --> DAEMON
    DAEMON --> RUNTIME
    RUNTIME -->|"syscall"| KERNEL[Linux Kernel]

dockerd adalah proses utama yang berjalan terus-menerus di background. Ia adalah otak Docker: menerima request, mengelola state image dan container, mengatur network dan volume, dan memutuskan apa yang harus dijalankan. Ketika kamu mengetik docker ps, daemon yang menjawab dengan daftar container. Ketika kamu menjalankan docker run nginx, daemon yang menyiapkan segalanya.

Docker REST API adalah jembatan antara client dan daemon. Secara default API ini diakses lewat Unix socket di /var/run/docker.sock, yang berarti hanya proses di mesin yang sama yang bisa bicara ke daemon. Tapi daemon juga bisa dikonfigurasi untuk listen di TCP socket, dan di sinilah remote management serta orkestrasi (Swarm, K8s) mulai berperan.

docker CLI adalah interface yang paling sering kamu gunakan. Ia tidak punya logika internal yang berarti — semua keputusan terjadi di daemon. CLI hanya memformat input, mengirim request, dan menampilkan response. Karena itulah semua Docker SDK resmi (Go, Python, Node) pada dasarnya adalah pembungkus untuk endpoint REST API yang sama.

Container Runtime adalah lapis eksekusi. Sejak Docker 1.11, daemon tidak lagi menjalankan container secara langsung. Ia mendelegasikan ke containerd (high-level runtime yang mengelola image snapshot dan lifecycle container), yang kemudian memanggil runc (low-level runtime yang benar-benar membuat proses di namespace dan cgroup baru sesuai standar OCI). Pemisahan ini memberi dua keuntungan besar: stabilitas (jika runc crash, daemon tidak ikut crash) dan standar (siapa pun bisa menulis runtime OCI-compliant, tidak harus Docker).

Detail menarik: sejak Docker 1.11 (awal 2016), semua logika runtime ada di dalam dockerd. Ketika runc di-fork menjadi proyek terpisah dan containerd didonasikan ke CNCF, daemon menjadi jauh lebih ramping. Dampak praktisnya: Docker Engine hari ini kurang lebih adalah orchestrator ringan di atas runtime standar industri. Itulah kenapa Podman, CRI-O, dan containerd bisa menjalankan image Docker tanpa Docker Engine sama sekali.


Image, Container, dan Registry — Tiga Konsep Inti #

Di atas tiga lapis tadi, ada tiga objek yang selalu muncul dalam pembicaraan Docker: image, container, dan registry. Ketiganya punya hubungan yang erat, dan memahami hubungan ini akan mencegah banyak kebingungan.

flowchart LR
    DOCKERFILE[Dockerfile] -->|docker build| IMAGE[Docker Image]
    IMAGE -->|docker push| REG[Registry]
    REG -->|docker pull| IMAGE
    IMAGE -->|docker run| CONTAINER[Docker Container]
    CONTAINER -->|logs, exec, stop| CONTAINER

Docker Image adalah template yang immutable. Ia adalah kumpulan layer filesystem plus metadata (perintah default, port yang diekspos, environment variable). Image tidak berjalan — ia hanya cetakan. Image dibuat dengan docker build dari Dockerfile, dan didistribusikan lewat registry.

Docker Container adalah instance runtime dari image. Ketika kamu menjalankan docker run nginx, Docker mengambil image nginx, menambahkan satu layer writable di atasnya, lalu menjalankan proses utama yang didefinisikan di image. Satu image bisa dipakai untuk membuat banyak container, dan tiap container punya state sendiri (file yang ditulis, proses yang berjalan, network interface).

Docker Registry adalah gudang image. Registry menyimpan image dalam repository, dan tiap repository bisa punya banyak tag (versi). Docker Hub adalah registry publik terbesar, tapi ada banyak alternatif — AWS ECR, Google Artifact Registry, GitHub Container Registry, Harbor, dan self-hosted Docker Registry. Registry adalah titik temu antara yang membuat image (developer, CI) dan yang menjalankan (server, Kubernetes node).

Hubungan ketiganya membentuk lifecycle yang akan kamu temui di mana-mana:

Tahap Peran Contoh Perintah
Build Dockerfile → Image lokal docker build -t myapp:1.0 .
Push Image lokal → Registry docker push myapp:1.0
Pull Registry → Image lokal di host lain docker pull myapp:1.0
Run Image → Container hidup docker run -d -p 80:80 myapp:1.0
Stop/Remove Container dimatikan dan dihapus docker stop + docker rm

Perhatikan bahwa image tetap, container berubah. Image adalah kontrak — apa yang kamu build, itu yang dijalankan di mana-mana. Container adalah ekspresi sementara dari image itu di host tertentu, dengan state lokal yang akan hilang saat container dihapus (kecuali data disimpan di volume).

Anti-pattern umum: memperlakukan container seperti VM dengan menyimpan data di filesystem container. Container dirancang untuk immutable dan ephemeral; setiap restart atau replacement akan kehilangan data yang disimpan di writable layer. Selalu gunakan named volume atau bind mount untuk data yang harus bertahan.

Alur End-to-End: Apa yang Terjadi Saat docker run #

Salah satu cara terbaik untuk memahami arsitektur Docker adalah menelusuri satu perintah dari ujung ke ujung. Perhatikan apa yang terjadi ketika kamu mengetik docker run -d -p 80:80 nginx:latest di terminal.

sequenceDiagram
    participant U as User Terminal
    participant CLI as docker CLI
    participant D as dockerd
    participant R as containerd
    participant RC as runc
    participant K as Linux Kernel
    participant REG as Registry

    U->>CLI: docker run -d -p 80:80 nginx:latest
    CLI->>D: POST /containers/create<br/>(REST API, unix socket)
    D->>REG: HEAD nginx:latest
    REG-->>D: manifest info
    D->>D: cek image lokal
    alt image tidak ada
        D->>REG: GET /v2/nginx/manifests/latest
        REG-->>D: manifest
        D->>REG: GET /v2/nginx/blobs/...
        REG-->>D: layer data
        D->>D: simpan image ke local storage
    end
    D->>R: Create container spec
    R->>R: snapshot writable layer
    R->>RC: bundle OCI
    RC->>K: clone() + unshare()
    K-->>RC: namespace ready
    RC->>K: execve(nginx)
    K-->>U: nginx listening on port 80
    D-->>CLI: container ID
    CLI-->>U: print container ID

Mari kita pecah tahapannya:

  1. Client menerima perintah. CLI mem-parse argumen -d -p 80:80 nginx:latest, menyusun request JSON sesuai Docker REST API, lalu mengirimnya ke dockerd lewat Unix socket di /var/run/docker.sock.

  2. Daemon memvalidasi request. dockerd menerima request, melakukan sanity check (apakah port 80 tersedia, apakah image nginx:latest ada di format yang benar), dan memutuskan langkah selanjutnya.

  3. Image resolution. Daemon mengecek apakah image nginx:latest sudah ada di local image cache. Jika tidak, ia menghubungi registry (default: Docker Hub) untuk mengambil manifest dan layer yang diperlukan. Setiap layer disimpan di local image storage dan di-cache untuk penggunaan berikutnya.

  4. Container spec disusun. Daemon membuat container specification — deskripsi lengkap tentang image mana yang dipakai, environment variable apa, port apa yang di-map, volume apa yang di-mount, namespace dan cgroup apa yang harus di-setup. Spec ini mengikuti standar OCI.

  5. Runtime menyiapkan eksekusi. Spec diteruskan ke containerd. containerd menambahkan writable layer di atas image, mengkonfigurasi network virtual (bridge, veth pair), dan meneruskan bundle ke runc.

  6. runc membuat proses. runc menggunakan system call clone() dengan flag namespace untuk membuat proses baru yang berjalan di namespace terpisah (PID, NET, MNT, UTS, IPC, USER). Ia juga mendaftarkan proses ke cgroup untuk pembatasan resource.

  7. Container hidup. Proses utama (di case ini nginx) berjalan sebagai PID 1 di dalam namespace container-nya sendiri. Dari sudut pandangnya, ia adalah satu-satunya proses di sebuah “sistem” yang baru dibuat. Port 80 sudah di-bind di network namespace container, dan Docker di sisi host mem-forward traffic dari port 80 host ke port 80 container.

  8. Response kembali ke user. Daemon mengembalikan container ID ke CLI, yang mencetaknya ke terminal. Container berjalan di background (-d), dan kamu bisa berinteraksi dengannya lewat docker logs, docker exec, docker stop, dan seterusnya.

Seluruh proses ini biasanya terjadi dalam hitungan detik, terutama untuk image yang sudah di-cache. Tapi di balik kecepatan itu ada orkestrasi banyak komponen yang masing-masing punya tanggung jawab spesifik.

Decision Tree — Memilih Topologi Deployment Docker #

Arsitektur Docker tidak berdiri sendiri. Cara kamu mendeploy menentukan komponen mana yang perlu kamu pahami secara mendalam.

flowchart TD
    A{Deployment<br/>target?}
    A -- Laptop dev --> L[Docker Desktop / CLI lokal]
    A -- Single server --> B[Docker Engine + Compose]
    A -- Multi-host --> C[Orchestrator?]
    C -- Swarm --> S[Docker Swarm]
    C -- Kubernetes --> K[Kubernetes]
    C -- Managed cloud --> M[ECS / Cloud Run / Fargate]

    L --> LV[Bottleneck:<br/>resource laptop]
    B --> BV[Bottleneck:<br/>konfigurasi manual]
    S --> SV[Belajar:<br/>swarm mode, service]
    K --> KV[Belajar:<br/>pod, deployment, ingress]
    M --> MV[Belajar:<br/>IAM, scaling policy]

Untuk pengembangan di laptop, kamu cukup paham CLI dan daemon lokal. Untuk server tunggal dengan beberapa service, Docker Compose adalah teman terbaik. Begitu kamu harus menjalankan container di banyak host sekaligus, kamu masuk ke dunia orkestrasi — Docker Swarm (native) atau Kubernetes (de facto standard industri). Layanan managed (ECS, Cloud Run, Fargate) menyembunyikan daemon dan runtime dari kamu sepenuhnya.

Section ini fokus pada Engine itu sendiri — apa yang terjadi dari CLI sampai container hidup. Topologi deployment spesifik (Swarm, Compose, K8s) dibahas di section lain.

Catatan versioning: section ini menjelaskan arsitektur Docker yang relevan untuk Docker Engine 20.10 ke atas (rilis 2020–2024). Sejak Moby Project mem-fork komponen internal, detail kecil seperti nama proses dan path socket bisa sedikit berbeda antar distribusi, tapi model arsitekturnya tetap sama.


Ringkasan #

  • Arsitektur Docker terdiri dari tiga lapis utama — Client Layer (CLI/SDK), Engine Layer (daemon + runtime), dan Delivery Layer (registry). Pemisahan ini menjelaskan kenapa Docker bisa dikontrol dari jarak jauh, kenapa image bisa di-share, dan kenapa komponen-komponennya bisa di-swap (misalnya Podman untuk daemon).
  • Docker Engine bukan satu proses — ia adalah paket yang terdiri dari dockerd (daemon), REST API, CLI, dan container runtime (containerd + runc). Sejak Docker 1.11, daemon tidak lagi menjalankan container langsung; ia mendelegasikan ke runtime OCI-compliant.
  • Tiga objek sentral Docker — image (template immutable), container (instance runtime), registry (gudang distribusi). Lifecycle-nya: buildpushpullrunremove.
  • Image ≠ Container — image adalah blueprint, container adalah blueprint plus writable layer plus proses yang berjalan. Image bisa dipakai untuk membuat banyak container. Container bisa dihapus tanpa memengaruhi image.
  • Alur docker run melewati banyak komponen: CLI → REST API → daemon → image resolution → runtime → kernel. Memahami alur ini adalah dasar untuk debug masalah Docker apapun.
  • Arsitektur menentukan tooling — untuk deployment single-host, cukup Engine + Compose. Untuk multi-host, kamu butuh orchestrator (Swarm atau K8s). Untuk managed environment, daemon tersembunyi dari kamu.
  • Section ini membahas Engine secara mendalam — artikel-artikel berikutnya mendalami masing-masing komponen: engine & daemon, image, container, registry, dan cara kerja di level kernel.

← Sebelumnya: Use Case Docker   Berikutnya: Engine & Daemon →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact