Docker adalah alat yang mempopulerkan container sekaligus standar de facto cara memakainya sejak 2013. Konsep layer, image, dan registry yang kita bangun di Chapter Container Concepts
semua dijalankan Docker lewat satu baris perintah yang nyaman: docker run, dan seisi dunia mulai memakainya. Chapter ini membedah praktik sehari-harinya — arsitektur client-server di balik perintah itu, siklus hidup container, penulisan Dockerfile, dan volume untuk data yang harus bertahan. Karena daemon Docker tidak terpasang di lingkungan latihan, kita bekerja dengan berkas konfigurasi sungguhan yang ditulis dan dibedah langsung, ditambah simulasi siklus hidup di terminal.
Arsitektur Client-Server #
Perintah docker yang kita ketik hanyalah klien tipis. Klien berbicara lewat Unix socket /var/run/docker.sock kepada dockerd — daemon yang berjalan sebagai root di host dan mengerjakan semua pekerjaan berat: membuat container, menjalankan image, mengunduh dari registry, membangun jaringan virtual. Pemisahan ini yang membuat docker CLI tetap bisa mengelola host jauh dengan docker -H ssh:budi@server-kantor ... — klien di laptop, daemon di server. Berikut potongan contoh tampilan docker version, yang mencetak kedua sisi sekaligus:
Client: Docker Engine - Community
Version: 27.3.1
OS/Arch: linux/amd64
Server: Docker Engine - Community
Engine Version: 27.3.1Bagian Server kosong atau error berarti daemon tidak berjalan — atau klien tidak punya izin ke socket-nya. Akses socket diatur lewat group docker, dan keputusan ini serius:
Image dan Container: Kelas dan Instansi #
Hubungan image dan container mengikuti hubungan program dan proses yang kita kenal di Chapter Process Concepts
: image adalah cetak biru baca-saja yang diam di disk, container adalah instance yang sedang berjalan di memori. Satu image bisa melahirkan banyak container sekaligus — tiga container dari image nginx:1.27 adalah tiga proses terpisah yang masing-masing punya lapisan tulisnya sendiri, tetapi berbagi layer baca-saja yang sama. Menghapus container membuang lapisan tulisnya saja; image tetap utuh dan siap melahirkan container baru.
Menulis Dockerfile #
Dockerfile adalah resep membangun image: satu berkas berisi instruksi yang dieksekusi dari atas ke bawah, dan setiap instruksi menghasilkan satu layer baru. Kita tulis resep untuk server web sederhana:
cat > Dockerfile <<'EOF'
FROM debian:12-slim
RUN apt-get update && apt-get install -y --no-install-recommends nginx \
&& rm -rf /var/lib/apt/lists/*
COPY index.html /var/www/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF
cat DockerfileOutput yang Dihasilkan:
FROM debian:12-slim
RUN apt-get update && apt-get install -y --no-install-recommends nginx \
&& rm -rf /var/lib/apt/lists/*
COPY index.html /var/www/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Mari kita bedah instruksi demi instruksi:
- Baris 1 (
FROM debian:12-slim): menetapkan image dasar — titik awal tumpukan layer kita;12-slimadalah varian Debian 12 yang dipangkas sehingga image dasarnya kecil. - Baris 2–3 (
RUN apt-get ...): perintah yang dieksekusi saat build, bukan saat container berjalan — memasang nginx lalu membersihkan cache apt. Keduanya digabung dalam satuRUNdengan&&agar menjadi satu layer: layer bersifat tetap, jadi membersihkan cache di instruksi terpisah tidak akan mengecilkan layer sebelumnya. - Baris 4 (
COPY index.html ...): menyalin berkas dari folder proyek (disebut build context) ke dalam image — satu layer baru berisi hanya berkas ini. - Baris 5 (
EXPOSE 80): dokumentasi bahwa container mendengarkan port 80 — murni catatan, tidak menerbitkan port apa pun. - Baris 6 (
CMD [...]): perintah default saat container menyala; format array menjadikan nginx langsung PID 1 tanpa shell perantara, dandaemon off;menjaga nginx tetap di foreground — container hidup selama proses utamanya hidup.
Saat docker build . dijalankan, tiap instruksi mencetak satu langkah beserta ID layer yang dihasilkannya — berikut potongan contoh tampilannya:
Step 1/5 : FROM debian:12-slim
---> 830c8d477f88
Step 2/5 : RUN apt-get update && apt-get install -y --no-install-recommends nginx && rm -rf /var/lib/apt/lists/*
---> Running in 4f9a1c2b3d10
---> 5d1e8a7c9f21
Step 3/5 : COPY index.html /var/www/html/index.html
---> a2f7b3c8e9d0
Step 4/5 : EXPOSE 80
---> 6c4d9e1a7b52
Step 5/5 : CMD ["nginx" "-g" "daemon off;"]
---> 9a1b7f3c5e28
Successfully built 9a1b7f3c5e28Baris ---> berisi ID layer hasil tiap langkah. Build kedua yang tidak mengubah Dockerfile memakai ulang seluruh ID itu dalam sekejap — inilah cache berlapis yang membuat iterasi build cepat: ubah hanya index.html, maka RUN yang mahal tidak diulang.
.dockerignore: Menyaring Build Context #
Sebelum build dimulai, klien mengirim seluruh isi folder proyek kepada daemon sebagai build context. Tanpa penyaring, berkas rahasia dan sampah ikut terkirim — dan bila ter-COPY, ikut tertanam di image. Berkas .dockerignore di akar context mendaftarkan pola yang dikecualikan. Kita buat proyek kecil lengkap dengan barang-barang yang seharusnya tidak ikut:
mkdir -p .git
printf 'print("halo")\n' > app.py
printf 'CONTOH_PASSWORD_KUAT\n' > .env
printf 'log lama\n' > debug.log
printf 'kunci-rahasia\n' > kunci-privat.pem
printf 'ref: utama\n' > .git/HEAD
cat > .dockerignore <<'EOF'
.git
*.log
.env
kunci-privat.pem
EOF
echo "== seluruh isi folder proyek =="
find . -type f | sort
echo "== yang dikirim sebagai build context =="
for berkas in $(find . -type f | sort); do
case "$berkas" in
./.git/*|./.env|./.dockerignore|*.log|./kunci-privat.pem) : ;;
*) echo "$berkas" ;;
esac
doneOutput yang Dihasilkan:
== seluruh isi folder proyek ==
./.dockerignore
./.env
./.git/HEAD
./app.py
./debug.log
./kunci-privat.pem
== yang dikirim sebagai build context ==
./app.pyMari kita bedah baris demi baris:
- Baris 1–6 (
mkdirdanprintf): menyiapkan proyek mini berisi enam berkas — termasuk.envberisi password dankunci-privat.pemyang tidak boleh pernah keluar dari laptop. - Baris 7–12 (
cat > .dockerignore): menulis daftar pengecualian — satu pola per baris, format yang sama dengan.gitignoreyang kita kenal di proyek git. - Baris 14–15 (
find . -type f | sort): mendaftar seluruh isi folder sebagaimana dilihat mata telanjang — semuanya ada. - Baris 16–21 (
case): mensimulasikan penyaringan daemon: pola./.git/*meniru entri.git,*.logmeniru*.log, dan seterusnya — hanya./app.pyyang lolos.
Tanpa .dockerignore, COPY . . di Dockerfile akan menyematkan password dan kunci privat ke dalam image yang mungkin didorong ke registry publik — kesalahan yang sudah menjadi legenda di dunia nyata. Daftar ini juga menjaga context tetap kecil sehingga build jauh lebih cepat.
docker run dan Flag Penting #
docker run image memulai container baru; kekuatan sesungguhnya ada di flag-nya. Flag yang paling sering dipakai:
| Flag | Peran |
|---|---|
-d | menjalankan container di background (detach) |
-p 8080:80 | menerbitkan port — kiri untuk host, kanan untuk container |
-v web-data:/var/www/html | mengaitkan volume bernama web-data |
--name web | memberi nama agar mudah dipanggil perintah lain |
--rm | membuang container secara otomatis saat berhenti |
-e POSTGRES_PASSWORD=CONTOH_PASSWORD_KUAT | menyetel environment variable di container |
-it | interactive dengan terminal — untuk sesi shell di dalam |
Maka menjalankan server web lengkap dengan nama, port, dan volume berbunyi docker run -d --name web -p 8080:80 -v web-data:/var/www/html nginx:1.27. Mekanisme penerbitan port dan jaringan antar-container dibedah tuntas di Chapter Container Networking
.
Siklus Hidup Container #
Container punya siklus kehidupan yang pendek: dibuat, menyala, berhenti, dan dihapus. Daemon Docker mencatat statusnya di database internal; untuk melihat mekanismenya tanpa daemon, kita tiru catatan status itu dengan berkas — satu berkas mewakili satu container:
mkdir -p containers
printf 'image:nginx:1.27\nstatus:running\n' > containers/web
printf 'image:postgres:16\nstatus:running\n' > containers/db
echo "== docker ps =="
printf '%-8s %-16s %s\n' NAMA IMAGE STATUS
for f in containers/*; do
printf '%-8s %-16s %s\n' "$(basename "$f")" \
"$(sed -n 's/^image://p' "$f")" "$(sed -n 's/^status://p' "$f")"
done
echo "== docker stop db =="
sed -i 's/^status:running/status:exited/' containers/db
for f in containers/*; do
[ "$(sed -n 's/^status://p' "$f")" = running ] || continue
printf '%-8s %-16s %s\n' "$(basename "$f")" \
"$(sed -n 's/^image://p' "$f")" "$(sed -n 's/^status://p' "$f")"
done
echo "== docker rm web, lalu docker ps -a =="
rm containers/web
ls containersOutput yang Dihasilkan:
== docker ps ==
NAMA IMAGE STATUS
db postgres:16 running
web nginx:1.27 running
== docker stop db ==
web nginx:1.27 running
== docker rm web, lalu docker ps -a ==
dbMari kita bedah baris demi baris:
- Baris 1–3 (
mkdirdanprintf): dua container “menyala” — daemon sungguhan mencatatnya sebagai dua proses; kita cukup mencatat nama image dan statusnya. - Baris 4–9 (
docker ps): mendaftar yang berstatus running — dengan kolom NAMA, IMAGE, dan STATUS sepertidocker pssungguhan. - Baris 10–12 (
docker stop db): mengubah statusdbmenjadiexited— container masih ada dan muncul didocker ps -a, tetapi prosesnya sudah mati. - Baris 13–17: daftar kedua hanya menampilkan yang running —
websendirian setelahdbberhenti. - Baris 18–20 (
docker rm web): menghapuswebsepenuhnya — hanyadbyang tersisa, menunggu dihidupkan lagi dengandocker start db.
Perintah pendampingnya satu keluarga: docker logs web membaca seluruh keluaran proses utama, docker exec -it web bash membuka shell di dalam container yang sedang berjalan, dan docker start db menyalakan kembali container yang berhenti tanpa membuat yang baru.
Volume: Data yang Hidup Lebih Lama #
Semua tulisan di container hidup di lapisan tulis yang dibuang bersama containernya — sempurna untuk stateless, fatal untuk database. Volume memutus kaitan itu: docker volume create web-data membuat penyimpanan bernama yang dikelola daemon di bawah /var/lib/docker/volumes/, dan flag -v web-data:/var/www/html mengaitkannya ke path di dalam container. Menghapus container tidak menyentuh volume; container baru yang memakai volume sama menemukan datanya utuh. Varian bind mount (-v /home/budi/situs:/usr/share/nginx/html) mengaitkan folder host apa adanya — pilihan untuk development, ketika file diedit di host dan langsung terlihat di container. Seluruh peta storage ini dibedah di Chapter Container Storage
.
Docker Hub dan Registry #
Sumber image bawaan Docker adalah Docker Hub: docker pull nginx:1.27 mengunduh image resmi nginx beserta layer-nya, otomatis memakai layer yang sudah ada di disk. Untuk berbagi image sendiri, alurnya tiga langkah: docker login, docker tag web:1.4 budi/web:1.4, lalu docker push budi/web:1.4 — dan image itu bisa ditarik di server mana pun dengan docker pull budi/web:1.4.
Satu container per perintah terasa efisien untuk satu aplikasi — sampai aplikasi kita butuh web, database, dan cache sekaligus yang saling terhubung. Mengetik tiga docker run panjang dengan jaringan buatan tangan adalah tanda kita sudah siap untuk alat berikutnya: Docker Compose, satu berkas YAML yang menyusun aplikasi multi-container — topik Chapter Docker Compose
.
Referensi lanjut: referensi CLI docker sebagai kamus semua perintah, referensi Dockerfile untuk daftar instruksi lengkap, serta storage dan volume untuk detail jenis mount.