Docker dari Nol: Install, Build Image, Jalankan Container, Volume, dan Network
Memasang Docker memang cuma satu baris perintah, tetapi image versus container, lapisan data yang hilang saat container dihapus, dan perbedaan jaringan bawaan dengan jaringan buatan sendiri adalah tempat pemula tersesat. Tutorial ini menjalankan seluruh perintahnya di satu mesin Ubuntu dan menampilkan output aslinya, sehingga yang Anda baca di sini adalah hasil yang bisa Anda ulang, bukan asumsi.
Ringkasan
- Image bersifat immutable dan tersusun dari lapisan read-only; container adalah proses terisolasi dengan lapisan tulis sendiri.
- Data di lapisan container hilang bersama container; volume bernama bertahan setelah container dihapus.
-p 8080:80mempublikasikan port ke semua alamat host, bukan hanya localhost.- Jaringan
bridgebawaan tidak me-resolve nama container; jaringan buatan memberi DNS di127.0.0.11. - Multi-stage build memangkas image Go dari 364 MB menjadi 25,8 MB pada mesin uji.
.dockerignoremembatasi apa yang dikirim ke builder dan menentukan efektivitas cache.
Prasyarat dan Lingkungan Pengujian
Semua perintah dan output di bawah berasal dari mesin uji berikut, bukan dari dokumentasi semata.
| Komponen | Nilai Teruji | Keterangan |
|---|---|---|
| Sistem Operasi | Ubuntu 26.04.1 LTS | Arsitektur x86_64 |
| Docker Engine | 29.8.1 | Klien dan daemon versi sama |
| Docker Compose | v5.5.1 | Plugin docker-compose-plugin |
| Storage driver | overlayfs | DockerRootDir di /var/lib/docker |
| Cgroup | systemd, cgroup v2 | Terlihat pada docker info |
| Image uji | nginx:alpine 94,4 MB, alpine:latest 13 MB, golang:1.26-alpine 364 MB | Nginx 1.31.6, Alpine 3.24.2 |
Perintah dan nama paket di artikel ini mengikuti dokumentasi resmi untuk Ubuntu 26.04, 24.04, dan 22.04 LTS. Pada distribusi lain, ganti repositori dan nama paket sesuai panduan masing-masing. Halaman panduan instalasi resmi Docker untuk Ubuntu adalah sumber acuan untuk langkah di bawah.
Langkah 1: Pasang Docker Engine dari Repository Resmi
Langkah ini memasang Docker Engine beserta CLI, containerd, Buildx, dan Compose dari repository resmi, lalu memverifikasi bahwa daemon berjalan.
- Tambahkan kunci GPG resmi Docker. Kunci disimpan di direktori khusus agar tidak menimpa keyring sistem:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc- Daftarkan repository dengan format deb822. Docker sekarang memakai berkas
docker.sources, bukan lagidocker.listyang dipakai tutorial lama. Tulis nama suite dari/etc/os-releaseagar otomatis cocok dengan rilis Anda:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update- Hapus paket Docker lama yang konflik, lalu pasang yang baru. Paket bawaan distribusi (
.docker.io) dan paket Compose v1 tidak boleh bercampur dengan paket resmi:
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin| Paket | Fungsi |
|---|---|
docker-ce | Daemon Docker Engine |
docker-ce-cli | Perintah docker |
containerd.io | Runtime container yang dipakai Engine |
docker-buildx-plugin | Builder BuildKit di balik docker build |
docker-compose-plugin | Compose v2 sebagai plugin |
- Verifikasi daemon dan jalankan container pertama.
hello-worldadalah uji pemasangan paling jujur, karena benar-benar menyentuh daemon:
sudo systemctl status docker
sudo docker run --rm hello-worldKeluaran hello-world merangkum seluruh alur kerja Docker dalam empat langkah: klien menghubungi daemon, daemon menarik image dari Docker Hub, daemon membuat container dari image itu, lalu mengalirkan output-nya kembali ke terminal Anda. Saya memakai --rm agar container percobaan hilang otomatis beserta volume anonimnya.
- Berikan hak akses tanpa
sudo(opsional). Grupdockermemberi hak setara root, jadi dokumentasi resmi memberi peringatan tegas soal ini:
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp dockerMemberikan akses ke grup
dockersama dengan memberi hak administratif penuh, karena pengguna dapat menjalankan container dengan hak root. Pada mesin produksi atau server bersama, biarkansudo dockerdipakai seperlunya, atau batasi siapa saja yang boleh masuk ke grup tersebut.
Langkah 2: Bedakan Image dan Container
Langkah ini menunjukkan batas yang sering disalahpahami: image adalah paket read-only, sedangkan container adalah proses berjalan dengan lapisan tulisnya sendiri.
- Tarik image dan lihat daftar image di mesin Anda. Perhatikan format keluaran Docker 29 yang memisahkan ukuran disk dan ukuran konten:
docker pull nginx:alpine
docker image lsIMAGE ID DISK USAGE CONTENT SIZE EXTRA
alpine:latest 294b683cb724 13MB 3.94MB
hello-world:latest 5e2309035332 25.9kB 9.49kB
nginx:alpine df221db836e1 94.4MB 27.2MB- Baca lapisan image dari atas ke bawah.
docker image historymenunjukkan instruksi yang membangun image. Dua baris teratas adalah lapisan yang saya tambahkan sendiri; sisanya warisan dari image resmi nginx:
docker image history nginx:alpineIMAGE CREATED CREATED BY SIZE COMMENT
f8a68025a558 About a minute EXPOSE [80/tcp] 0B buildkit.dockerfile.v0
<missing> About a minute COPY index.html /usr/share/ngin… 24.6kB buildkit.dockerfile.v0
<missing> 4 days ago RUN /bin/sh -c set -x && apkArch=… 52.4MB buildkit.dockerfile.v0
<missing> 4 days ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0- Buktikan bahwa lapisan tulis ikut hilang bersama container. Ini bukan teoretis; saya menulis berkas di dalam container, menghapus container-nya, lalu container baru dengan image yang sama tidak menemukan berkas tersebut:
docker run --name layer-test -d alpine sleep 300
docker exec layer-test sh -c 'echo "data sementara" > /tmp/ujicoba.txt'
docker rm -f layer-test
docker run --rm alpine ls -l /tmp/ujicoba.txtls: /tmp/ujicoba.txt: No such file or directory| Aspek | Image | Container |
|---|---|---|
| Sifat | Immutable, tidak bisa diubah setelah dibuat | Berjalan, bisa dihapus |
| Isi | Lapisan read-only berisi berkas, biner, pustaka, konfigurasi | Lapisan tulis di atas image, unik per container |
| Jumlah | Satu image dipakai banyak container | Satu container merujuk satu image |
| Daur hidup | Bertahan sampai dihapus eksplisit | Hilang saat container dihapus, kecuali datanya di volume |
Container bukan virtual machine ringan. Menurut dokumentasi resmi, “A container is simply an isolated process with all of the files it needs to run. If you run multiple containers, they all share the same kernel.” Semua container berbagi kernel host, dan proses container berjalan di namespace, cgroup, serta filesystem union milik container tersebut. Detail mekanisme lapisan ada di halaman storage.
Langkah 3: Build Image Sendiri dengan Dockerfile
Langkah ini membangun image web statis dari nol, lalu memperagakan secara konkret kapan cache build berlaku dan kapan ia dibatalkan.
- Buat konteks build berisi satu halaman HTML. Dockerfile hanya instruksi; semua berkas yang dirujuk harus ada di dalam konteks build:
mkdir -p website && cd website
printf '%s\n' '<!DOCTYPE html>' \
'<html lang="id">' \
'<head><meta charset="utf-8"><title>Demo Docker</title></head>' \
'<body><h1>Hola dari image Docker</h1></body>' \
'</html>' > index.html- Tulis Dockerfile tiga baris.
FROMmemilih basis,COPYmenaruh berkas,EXPOSEhanya mendokumentasikan port dan tidak membukanya di host:
cat > Dockerfile <<'DOCKERFILE'
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
DOCKERFILE| Instruksi | Fungsi | Catatan |
|---|---|---|
FROM | Menentukan image dasar dan memulai stage baru | Pin versi atau digest agar hasil reproducible |
COPY | Menyalin berkas dari konteks build | Lebih disukai daripada ADD untuk menyalin antar stage |
RUN | Menjalankan perintah saat build, hasilnya jadi lapisan | Gabung beberapa RUN agar cache tidak terpecah |
EXPOSE | Dokumentasi port yang didengarkan | Tidak membuka port di host |
USER | Menentukan user non-root saat container berjalan | Praktik keamanan dasar |
CMD | Perintah default saat container berjalan | Bentuk exec: CMD ["halo"] |
- Build image dan beri nama tag. Flag
-tmenamai hasil build agar bisa dijalankan tanpa menyebut ID panjang:
docker build -t demo-web:latihan .- Buktikan cache bekerja saat tidak ada perubahan. Build kedua memakai kembali lapisan yang sama, sehingga instruksi
COPYtampil sebagaiCACHED:
docker build -t demo-web:latihan .#5 [1/2] FROM docker.io/library/nginx:alpine@sha256:df221db836e1…
#5 CACHED
#6 [2/2] COPY index.html /usr/share/nginx/html/index.html
#6 CACHED- Ubah isi berkas, lalu build lagi. Lapisan
FROMtetap dari cache, lapisan setelahCOPYdibangun ulang. Sesuai aturan invalidasi cache, “Once the cache is invalidated, all subsequent Dockerfile commands generate new images”:
printf '%s\n' '<h1>Hola dari image Docker (v2)</h1>' >> index.html
docker build -t demo-web:latihan .#5 [1/2] FROM docker.io/library/nginx:alpine@sha256:df221db836e1…
#5 CACHED
#6 [2/2] COPY index.html /usr/share/nginx/html/index.html
#6 DONE 0.0sSentuh berkas tanpa mengubah isi (
touch index.html) tetap menghasilkan#6 CACHED. Dokumentasi resmi menegaskan bahwa untukADDdanCOPY, “The modification time of a file (mtime) is not taken into account”. Yang diperiksa adalah checksum isi berkas, bukan waktu modifikasinya.
- Batasi apa yang dikirim ke builder dengan
.dockerignore. Tanpa berkas ini, seluruh konteks ikut terkirim dan bisa ikut ter-cache bersamaCOPY . .:
printf '%s\n' 'rahasia.txt' '*.log' > .dockerignore
docker build -t demo-web:latihan . 2>&1 | grep 'transferring context'Ukuran konteks turun dari 224 byte menjadi 32 byte setelah rahasia.txt dikecualikan. Dokumentasi konteks build menjelaskan bahwa aturan .dockerignore berlaku untuk seluruh subdirektori, ** didukung, dan baris yang cocok terakhir yang menang.
Langkah 4: Perkecil Image dengan Multi-Stage Build
Langkah ini memisahkan tahap kompilasi dari tahap runtime, sehingga toolchain build tidak ikut terbawa ke image akhir.
- Tulis program Go minimal dan Dockerfile dua tahap. Tahap pertama membawa compiler; tahap kedua hanya menerima berkas binary hasil build:
mkdir -p halo && cd halo
cat > main.go <<'GO'
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
addr := ":8080"
if p := os.Getenv("PORT"); p != "" {
addr = ":" + p
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Halo dari container Go\n")
})
log.Fatal(http.ListenAndServe(addr, nil))
}
GOcat > Dockerfile <<'DOCKERFILE'
FROM golang:1.26-alpine AS build
WORKDIR /src
COPY main.go ./
RUN go mod init halo && go build -o /out/halo .
FROM alpine:3.22
COPY --from=build /out/halo /halo
EXPOSE 8080
USER 65534:65534
CMD ["/halo"]
DOCKERFILE- Build sekali, bandingkan ukuran image. Perbedaan pada mesin uji ini dapat diperiksa langsung:
docker build -t halo-app:latihan .
docker image ls --format '{{.Repository}}:{{.Tag}}\t{{.Size}}' | grep -E 'halo-app|golang:1.26'halo-app:latihan 25.8MB
golang:1.26-alpine 364MBImage final sekitar 14 kali lebih kecil karena tidak membawa compiler Go. Menurut panduan multi-stage build, setiap instruksi FROM memulai stage baru, dan hanya lapisan yang dibutuhkan oleh stage tujuan yang ikut terpasang di image akhir.
- Jalankan image hasil dan pastikan bukan root.
USER 65534:65534membuat proses berjalan sebagainobody:
docker run -d --name halo-app -p 8082:8080 halo-app:latihan
curl -sS http://localhost:8082/
docker exec halo-app idHalo dari container Go
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)- Bangun stage tertentu saja saat menelusuri masalah.
--target buildmenghentikan build di stage kompilasi tanpa mengemas image akhir. BuildKit hanya membangun stage yang dibutuhkan tahap tersebut:
docker build --target build -t halo-app:build-stage .
docker image ls --format '{{.Repository}}:{{.Tag}}\t{{.Size}}' | grep halo-apphalo-app:latihan 25.8MB
halo-app:build-stage 489MBImage build-stage justru lebih besar karena masih memuat toolchain. Gunakan ini untuk debugging, bukan untuk produksi.
Langkah 5: Jalankan Container dan Publikasikan Port
Langkah ini menjalankan web server di dalam container dan memperlihatkan secara konkrem perbedaan mempublikasikan port ke semua alamat host dengan hanya ke localhost.
- Jalankan container dalam mode detached. Flag
-dmengembalikan kontrol ke terminal,--namememberi nama yang mudah diketik, dan-pmemetakan port:
docker run -d --name web-nginx -p 8080:80 nginx:alpine- Periksa status dan uji dari host. Output
docker pslangsung menunjukkan pemetaan port, lalucurlmembuktikan layanannya benar-benar menjawab:
docker ps
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8080/CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a2050b359dba nginx:alpine "/docker-entrypoint.…" 2 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp web-nginx
HTTP 200- Perhatikan ke mana port sebenarnya terikat.
docker portmemperlihatkan ikatannya, danssmengonfirmasi socket mana yang benar-benar terbuka di host:
docker port web-nginx
ss -ltn | grep 808080/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:*
LISTEN 0 4096 [::]:8080 [::]:*
-p 8080:80memetakan port 8080 pada semua alamat host (0.0.0.0dan[::]), sehingga layanan bisa dijangkau dari jaringan lain. Dokumentasi resmi menyebutnya “insecure by default”. Untuk layanan yang hanya perlu diakses dari host, tulis alamatnya secara eksplisit dan verifikasi sendiri hasilnya:bashdocker run -d --name halo-local -p 127.0.0.1:8083:8080 halo-app:latihan ss -ltn | grep 8083textLISTEN 0 4096 127.0.0.1:8083 0.0.0.0:*
Bentuk -p | Arti |
|---|---|
-p 8080:80 | Port 8080 di semua alamat host ke port 80 container |
-p 127.0.0.1:8080:80 | Hanya host yang bisa mengakses |
-p 8080:80/udp | Publish protokol UDP |
-P | Publish semua port yang di-EXPOSE ke port acak |
- Baca log dan masuk ke dalam container yang sedang berjalan. Gunakan
docker exectanpa-ituntuk perintah satu kali;-ithanya diperlukan bila Anda ingin shell interaktif:
docker logs web-nginx
docker exec web-nginx nginx -v- Hentikan lalu hapus container. Menghapus image adalah langkah terpisah; menghapus container tidak menyentuh image maupun volume bernama:
docker stop web-nginx
docker rm web-nginxFlag docker run | Fungsi |
|---|---|
-d, --detach | Jalankan di latar belakang |
--name | Beri nama container |
-p, --publish | Publikasikan port container ke host |
-e, --env | Set variabel lingkungan container |
--rm | Hapus container beserta volume anonimnya saat berhenti |
--network | Tentukan jaringan yang dipakai |
--restart | Kebijakan mulai ulang: no, always, unless-stopped, on-failure |
Langkah 6: Simpan Data di Luar Container dengan Volume
Langkah ini membuktikan perlakuan data yang berbeda antara lapisan container, volume bernama, dan bind mount.
- Buat volume bernama dan isi dari container pertama. Volume dibuat otomatis jika belum ada, dan isinya bertahan di luar daur hidup container:
docker volume create data-app
docker run --rm --mount type=volume,src=data-app,dst=/data \
alpine sh -c 'echo "halo dari container" > /data/berkas.txt'- Baca kembali dari container kedua yang berbeda. Berkas masih ada karena volume, bukan lapisan container, yang menyimpannya:
docker run --rm --mount type=volume,src=data-app,dst=/data alpine cat /data/berkas.txt
docker volume inspect data-app --format 'Name={{.Name}} Driver={{.Driver}} Mountpoint={{.Mountpoint}}'halo dari container
Name=data-app Driver=local Mountpoint=/var/lib/docker/volumes/data-app/_data- Periksa mount yang terpasang pada container. Bagian
Mountsdidocker inspectmemberi tahu sumber, tujuan, dan mode baca-tulis:
docker inspect --format '{{json .Mounts}}' vol-test[{"Type":"volume","Name":"data-app","Source":"/var/lib/docker/volumes/data-app/_data","Destination":"/data","Driver":"local","Mode":"z","RW":true,"Propagation":""}]- Bandingkan dengan bind mount. Pada bind mount, berkas yang ditulis container langsung muncul di direktori host:
mkdir -p bind-test && echo "isi dari host" > bind-test/izin.txt
docker run --rm --mount type=bind,src=$PWD/bind-test,dst=/mnt \
alpine sh -c 'echo "ditulis container" > /mnt/dari-container.txt'
ls -l bind-test/-rw-r--r-- 1 root root 19 Sep 27 17:05 dari-container.txt
-rw-rw-r-- 1 <user> <user> 14 Sep 27 17:05 izin.txtBerkas yang dibuat container milik root, sedangkan berkas bawaan host tetap milik pengguna host; <user> di atas ditulis sebagai placeholder.
| Aspek | Volume bernama | Bind mount |
|---|---|---|
| Sumber | Dikelola Docker di /var/lib/docker/volumes/ | Direktori milik host yang Anda tentukan |
| Bertahan setelah container dihapus | Ya, kecuali docker volume rm | Ya, karena berkasnya milik host |
| Akses dari host | Lebih merepotkan, perlu docker cp atau mount | Langsung |
| Kepatuhan lintas OS | Bekerja di Linux dan Windows container | Terikat struktur dan OS host |
| Rekomendasi resmi | “the preferred mechanism for persisting data” | Dipakai bila berkas harus diakses host dan container |
Halaman volume menyatakan volume adalah mekanisme yang diutamakan untuk menyimpan data, dan kinerjanya lebih baik karena menulis langsung ke filesystem host, bukan ke lapisan container yang memakai union filesystem. Volume juga bisa diisi otomatis: saat volume kosong di-mount ke direktori yang sudah berisi berkas di image, isi direktori itu disalin ke volume. Bind mount punya risiko berbeda: “Bind mounts have write access to files on the host by default”, jadi batasi dengan
readonlybila tidak perlu menulis.
- Pahami risiko
--rmdandocker volume prune.--rmhanya menghapus volume anonim (tanpa nama); volume bernama tetap ada. Sebaliknyadocker volume prunemenghapus semua volume yang tidak dipakai container mana pun, termasuk milik proyek lain di host yang sama:
docker run --rm -v /anon -v data-app:/named alpine true
docker volume inspect data-app --format '{{.Name}} masih ada'Langkah 7: Hubungkan Container lewat Jaringan
Langkah ini memperagakan perbedaan yang paling sering mengejutkan: jaringan bawaan tidak menyediakan DNS antar container, sedangkan jaringan buatan yang Anda buat sendiri menyelesaikan nama antar container secara otomatis.
- Coba me-resolve nama pada jaringan bawaan. Dua container di
bridgebawaan hanya dapat saling menghubungi lewat alamat IP, bukan nama:
docker run -d --name srv1 alpine sleep 300
docker run --rm alpine nslookup srv1** server can't find srv1: NXDOMAIN- Buat jaringan bridge buatan sendiri lalu sambungkan container. Jaringan ini mengaktifkan DNS bawaan Docker di
127.0.0.11:
docker network create app-net
docker network connect app-net srv1
docker run --rm --network app-net alpine sh -c 'cat /etc/resolv.conf; nslookup srv1'# Generated by Docker Engine.
nameserver 127.0.0.11
search .
options edns0 trust-ad ndots:0
---
Name: srv1
Address: 172.21.0.2- Buktikan container saling memanggil lewat nama. Jalankan web server di jaringan itu, lalu panggil dari container lain dengan
wgetke hostname:
docker run -d --name web --network app-net nginx:alpine
docker run --rm --network app-net alpine wget -qO- http://web/ | head -3<!DOCTYPE html>
<html>
<head>| Perintah | Fungsi |
|---|---|
docker network create app-net | Buat jaringan bridge baru |
docker network connect app-net srv1 | Sambungkan container yang sudah jalan |
docker network disconnect app-net srv1 | Lepaskan container dari jaringan |
docker network inspect app-net | Lihat detail jaringan dan anggotaanya |
docker network ls | Daftar semua jaringan |
Bedakan dua galat DNS ini, karena penyebabnya berbeda.
NXDOMAINberarti tidak ada catatan nama sama sekali, biasanya karena container berada di jaringan bawaan.SERVFAILyang saya alami saat pengujian berarti resolver menjangkau Docker tetapi entri-nya tidak tersedia, yang sering terjadi karena container target sudah berhenti. Pada langkah di atas, percobaan pertama gagal denganSERVFAILkarenahttpdtidak ada di imagealpinesehingga containerweblangsung exit dengan kode 127.
Langkah 8: Rangkai Dua Service dengan Compose
Langkah ini memindahkan seluruh hasil langkah sebelumnya ke satu berkas deklaratif yang dapat dijalankan ulang.
- Tulis satu berkas
compose.yaml. Nama service menjadi hostname di jaringan yang sama, dandown -vmembersihkan volume serta jaringan milik proyek ini:
services:
web:
image: nginx:alpine
ports:
- "127.0.0.1:8084:80"
volumes:
- konten-situs:/usr/share/nginx/html
networks:
- aplikasi
api:
image: halo-app:latihan
environment:
PORT: "8080"
networks:
- aplikasi
volumes:
konten-situs:
networks:
aplikasi:
driver: bridge- Validasi berkas sebelum menjalankan apa pun. Perintah
confighanya mem-parsing dan memvalidasi, tidak menyentuh host:
docker compose -p latihan config --quiet- Jalankan, periksa, lalu panggil antar service. Service
apidapat memanggilwebcukup dengan menulishttp://web/:
docker compose -p latihan up -d
docker compose -p latihan ps
docker compose -p latihan exec -T api wget -qO- http://web/ | head -3
docker compose -p latihan down -vVolume konten-situs terisi otomatis dengan index.html dan 50x.html dari image nginx, karena volume kosong di-mount ke direktori yang sudah berisi berkas.
Langkah 9: Bersihkan Sisa Uji
Langkah ini membersihkan container, image, dan cache yang dibuat tutorial ini, tanpa menyentuh proyek lain di host.
- Lihat berapa ruang yang terpakai.
docker system dfmemisahkan image, container, volume, dan build cache:
docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 9 4 1.663GB 545.8MB (32%)
Containers 6 3 114.7kB 24.58kB (21%)
Local Volumes 8 1 949.5MB 884MB (93%)
Build Cache 25 0 244.7MB 125.1MB- Hapus hanya yang milik tutorial ini. Menyebut nama satu per satu lebih aman daripada
docker system prune -a, yang juga menyentuh image proyek lain:
docker stop halo-app halo-local srv1 web
docker rm halo-app halo-local srv1 web
docker image rm halo-app:latihan halo-app:build-stage demo-web:latihan
docker volume rm data-app
docker network rm app-net- Kosongkan build cache bila perlu.
docker builder prunemenghapus cache yang tidak dipakai image mana pun. Cache bersifat dapat dibangun ulang, tetapi membersihkannya membuat build berikutnya mengunduh ulang lapisan:
docker builder prunePada host yang menjalankan beberapa proyek Compose,
docker system prune -a --volumesdandocker volume prunebisa menghapus image serta volume milik proyek lain yang sedang tidak aktif. Gunakandocker system dfuntuk melihat rinciannya, lalu hapus per nama.
Jebakan yang Saya Temui Saat Menjalankan
| Gejala | Penyebab | Solusi |
|---|---|---|
cannot attach stdin to a TTY-enabled container because stdin is not a terminal | docker exec -it dijalankan di konteks tanpa terminal, misalnya dari pipeline atau sesi non-interaktif | Tambahkan -it hanya saat memakai shell interaktif; untuk perintah sekali jalan, cukup docker exec |
sh: httpd: not found, container langsung exit | Applet httpd tidak tersedia di image alpine, sehingga DNS akhirnya memberi SERVFAIL | Gunakan image yang memang menyediakan layanannya, dan periksa docker logs sebelum mengira masalah jaringan |
| Build kedua tetap ingin mengunduh layer | --no-cache hanya mematikan cache build, bukan mengambil image dasar baru | Gunakan --pull untuk memaksa ambil versi terbaru |
| Perubahan kecil memicu build ulang yang panjang | COPY . . membatalkan seluruh lapisan setelahnya | Salin berkas spesifik, dan buat .dockerignore |
permission denied saat menulis berkas dari container | Bind mount mewarisi kepemilikan UID/GID host, sedangkan container sering berjalan sebagai nobody | Samakan UID/GID di Dockerfile dengan UID pengguna host, atau tulis ke volume |
Data hilang setelah docker rm | Data ditulis ke lapisan container, bukan volume | Pasang volume bernama pada direktori data aplikasi |
Cheatsheet Perintah Sehari-hari
| Tujuan | Perintah |
|---|---|
| Lihat container berjalan | docker ps |
| Lihat container berhenti | docker ps -a |
| Lihat log container | docker logs -f <nama> |
| Masuk ke shell container | docker exec -it <nama> sh |
| Hentikan lalu hapus | docker stop <nama> && docker rm <nama> |
| Lihat image | docker image ls |
| Lapisan sebuah image | docker image history <image> |
| Build ulang tanpa cache | docker build --no-cache -t <nama>:<tag> . |
| Daftar volume | docker volume ls |
| Daftar jaringan | docker network ls |
| Bersihkan container berhenti | docker container prune |
Dengan semuanya ini, Anda sudah bisa menjalankan aplikasi Anda sendiri dalam container, menyimpan datanya di luar container, dan menyatakannya lewat jaringan internal. Langkah berikutnya biasanya adalah menulis image multi-stage untuk aplikasi nyata, lalu mengotomatiskan build dan deploy-nya di CI.