04 โ Docker¶
4.1 Masalah yang Diselesaikan Docker¶
Keluhan klasik: "Di laptop saya jalan kok!" ๐คท
Aplikasi butuh environment spesifik: versi PHP/Node tertentu, extension, library sistem, konfigurasi OS. Perbedaan kecil antar mesin = bug misterius yang menghabiskan waktu berhari-hari.
Docker membungkus aplikasi + seluruh environment-nya ke dalam satu paket yang berjalan identik di mana pun โ laptop developer, laptop QA, server staging, server production.
Analogi: kontainer pengiriman barang. Isi kontainer bisa apa saja (furnitur, elektronik, makanan), tapi karena bentuknya standar, semua kapal/truk/crane bisa mengangkutnya tanpa peduli isinya. Sebelum kontainer distandarkan, bongkar-muat kapal butuh berhari-hari; sesudahnya, hitungan jam. Docker melakukan hal yang sama untuk software.
4.2 Container vs Virtual Machine¶
Keduanya "mengisolasi environment", tapi caranya berbeda:
VIRTUAL MACHINE CONTAINER
โโโโโโโโโโโโโฌโโโโโโโโโโโโ โโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโ
โ App A โ App B โ โ App A โ App B โ App C โ
โโโโโโโโโโโโโผโโโโโโโโโโโโค โโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโค
โ Guest OS โ Guest OS โ โ berat! โ Docker Engine โ
โ (GB-an) โ (GB-an) โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โโโโโโโโโโโโโดโโโโโโโโโโโโค โ Host OS โ
โ Hypervisor โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โโโโโโโโโโโโโโโโโโโโโโโโโค โ Hardware โ
โ Host OS โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโค
โ Hardware โ Berbagi kernel host OS โ
โโโโโโโโโโโโโโโโโโโโโโโโโ ringan (MB-an), start dalam detik
| VM | Container | |
|---|---|---|
| Ukuran | GB-an (OS lengkap) | MB-an (hanya aplikasi + dependency) |
| Waktu start | Menit | Detik |
| Isolasi | Sangat kuat (OS terpisah) | Cukup kuat (proses terisolasi) |
| Kapan dipakai | Butuh OS berbeda, isolasi maksimal | Default untuk aplikasi modern |
Catatan konteks: platform virtualisasi seperti Proxmox menjalankan VM; di dalam VM itu biasanya berjalan banyak container Docker. Keduanya saling melengkapi, bukan bersaing.
4.3 Konsep Inti¶
| Istilah | Definisi | Analogi |
|---|---|---|
| Image | Template/blueprint aplikasi + environment (read-only) | Resep kue |
| Container | Instance yang berjalan dari sebuah image | Kue yang dipanggang dari resep โ satu resep, banyak kue |
| Dockerfile | Skrip untuk membangun image | Tulisan resepnya |
| Registry | Tempat menyimpan & berbagi image (Docker Hub, GHCR, ECR) | Toko buku resep |
| Tag | Versi sebuah image (node:20-alpine, myapp:1.4.2) |
Edisi cetakan buku resep |
| Volume | Penyimpanan data persisten di luar container | Lemari es terpisah dari dapur |
| Network | Jaringan virtual antar container | Interkom antar ruangan |
| Port mapping | Menghubungkan port host ke port container | Nomor ekstensi telepon kantor |
๐ Wajib paham โ container itu ephemeral (sekali pakai): filesystem di dalam container hilang saat container dihapus/dibuat ulang. Data yang harus hidup lebih lama dari container-nya (database, file upload) wajib di volume. Ini penyebab klasik "data database hilang setelah update".
4.4 Dockerfile¶
# Mulai dari image dasar โ SELALU pakai versi spesifik, bukan :latest
FROM node:20-alpine
# Folder kerja di dalam container
WORKDIR /app
# Copy file dependency DULUAN, baru install
# โ memanfaatkan layer caching (lihat penjelasan di bawah)
COPY package*.json ./
RUN npm ci --omit=dev
# Baru copy seluruh kode aplikasi
COPY . .
# Dokumentasi port yang dipakai aplikasi
EXPOSE 3000
# Jalankan sebagai user non-root (keamanan)
USER node
# Perintah saat container dijalankan
CMD ["node", "server.js"]
Layer caching โ kenapa urutan baris itu penting¶
Setiap instruksi Dockerfile membentuk layer yang di-cache. Kalau sebuah layer tidak berubah, build berikutnya memakai cache-nya:
COPY package*.json ./ โ jarang berubah โ cache dipakai
RUN npm ci โ hanya diulang kalau package.json berubah
COPY . . โ sering berubah (setiap edit kode)
Kalau COPY . . ditaruh sebelum npm ci, setiap edit kode sekecil apa pun memaksa install ulang semua dependency โ build 5 menit alih-alih 10 detik. Ini salah satu kesalahan yang paling sering dibuat AI.
.dockerignore¶
Seperti .gitignore tapi untuk build Docker โ mencegah file tak perlu (dan berbahaya) masuk ke image:
node_modules
.git
.env โ PENTING: secret jangan sampai terbawa ke image!
*.log
Multi-stage build (sekilas)¶
Untuk aplikasi yang perlu di-build (Next.js, Go, dsb.): stage pertama berisi semua tool build (berat), stage akhir hanya berisi hasil build (ringan):
FROM node:20-alpine AS builder # stage 1: build
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM node:20-alpine # stage 2: hanya hasil akhirnya
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev
CMD ["npm", "start"]
Hasil: image production kecil, cepat di-deploy, dan permukaan serangannya lebih sempit.
4.5 Perintah Dasar¶
# --- Image ---
docker build -t my-app:1.0 . # bangun image dari Dockerfile, beri tag
docker images # daftar image lokal
docker pull postgres:16 # unduh image dari registry
# --- Container ---
docker run -d -p 8080:3000 --name web my-app:1.0
# โ โ โ
# โ โ โโโ beri nama biar gampang dirujuk
# โ โโโ port host 8080 โ port container 3000
# โโโ detached (jalan di background)
docker ps # container yang berjalan
docker ps -a # termasuk yang sudah berhenti
docker stop web && docker rm web # hentikan lalu hapus
# --- Debugging (tiga perintah paling sering dipakai) ---
docker logs web # lihat log โ TEMPAT PERTAMA saat ada masalah
docker logs -f --tail 100 web # ikuti log live, 100 baris terakhir
docker exec -it web sh # "masuk" ke dalam container
docker inspect web # semua detail konfigurasi container
docker stats # penggunaan CPU/RAM per container
4.6 Volume & Network¶
Volume โ dua cara umum¶
# Named volume โ dikelola Docker, untuk data aplikasi (database!)
docker run -v db_data:/var/lib/postgresql/data postgres:16
# Bind mount โ folder host dipetakan ke container, untuk development
docker run -v ./src:/app/src my-app # edit kode di host, langsung terasa di container
Network โ cara container saling bicara¶
Container dalam satu network Docker saling memanggil lewat nama service, bukan localhost:
โโโโโโโโโโโโโโโโ network: myapp_default โโโโโโโโโโโโโโโโ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ
โ โ app โ โโโโโโโบ โ db โ โ redis โ โ
โ โโโโโโโโโโโ "db:5432"โโโโโโโโโโ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๏ธ Red flag klasik: kode di dalam container memakai
DB_HOST=localhost. Di dalam container,localhost= container itu sendiri, bukan mesin host, bukan container database! Yang benar: nama service (DB_HOST=db). Ini penyebab errorconnection refusednomor satu bagi pemula.
4.7 Docker Compose โ Multi-Container¶
Aplikasi nyata butuh beberapa service sekaligus. Docker Compose mendefinisikan semuanya dalam satu file:
# docker-compose.yml
services:
app:
build: .
ports:
- "8080:3000"
environment:
DB_HOST: db # nama service = hostname
DB_PASSWORD: ${DB_PASSWORD} # diambil dari file .env
depends_on:
db:
condition: service_healthy # tunggu db BENAR-BENAR siap
restart: unless-stopped # auto-restart kalau crash
db:
image: postgres:16 # versi spesifik, bukan :latest
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck: # definisi "siap" untuk service ini
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
db_data:
redis_data:
Perintah harian:
docker compose up -d # jalankan semua service
docker compose ps # status semua service
docker compose logs -f app # log service tertentu
docker compose down # hentikan & hapus container (volume AMAN)
docker compose down -v # โ ๏ธ hapus SEKALIGUS VOLUME โ data hilang!
docker compose up -d --build # rebuild setelah kode berubah
๐ Perhatikan depends_on + healthcheck: tanpa healthcheck, depends_on hanya menunggu container start, bukan siap โ aplikasi bisa crash karena mencoba konek ke database yang masih booting. AI sering melewatkan ini.
4.8 Environment Variable & File .env¶
Konfigurasi yang berbeda antar environment (dan semua secret) hidup di environment variable, bukan di kode:
.env โ nilai asli โ MASUK .gitignore
.env.example โ template tanpa nilai asli โ DI-COMMIT ke Git
# isi .env.example:
DB_HOST=db
DB_PASSWORD= โ kosong; tiap orang/environment mengisi sendiri
APP_ENV=local
Rantainya: .env โ dibaca Docker Compose โ dioper ke container โ dibaca aplikasi.
โ ๏ธ Red flag: - Password/API key di-hardcode di Dockerfile, docker-compose.yml, atau kode. -
.envtidak ada di.gitignore(dan.dockerignore). - AI menaruh nilai asli di.env.example.
4.9 Troubleshooting Umum¶
| Gejala | Kemungkinan Penyebab | Cek Dengan |
|---|---|---|
| Container langsung mati setelah start | Aplikasi crash saat boot; env variable kurang | docker logs <nama> |
port is already allocated |
Port host sudah dipakai proses lain | Ganti port mapping, mis. 8081:3000 |
connection refused ke database |
Pakai localhost alih-alih nama service; atau DB belum siap |
Cek env DB_HOST; tambah healthcheck |
| Data hilang setelah update | Data di dalam container, bukan di volume | docker inspect bagian Mounts |
| Perubahan kode tidak muncul | Image belum di-rebuild | docker compose up -d --build |
| Disk server penuh | Timbunan image/container lama | docker system df, lalu docker system prune (hati-hati) |
4.10 Docker dalam Alur Kerja Tim¶
Developer Git push CI/CD (GitHub Actions dsb.) Server
โโโโโโโโโบ commit โโโโโโโโโบ build image โ test โ push ke registry โโโโโโโโโบ pull image โ jalankan
Karena yang berpindah adalah image yang identik, apa yang dites di CI = apa yang jalan di production. Inilah fondasi mengapa staging dan production bisa dipercaya konsisten.
Kenapa ini relevan untuk non-developer?¶
- Designer/QA: jalankan seluruh aplikasi di laptop dengan satu perintah
docker compose up -dโ tanpa install PHP/Node/MySQL manual. Mengecek hasil desain/fitur tidak perlu menunggu deploy. - PM: paham kenapa "environment parity" mengurangi bug kelas "cuma muncul di production", dan kenapa setup project baru bisa sangat cepat.
- Semua orang: saat ada masalah,
docker logsadalah informasi pertama yang berguna untuk dilaporkan.
4.11 Prompting AI untuk Docker¶
๐ก Setup baru: "Buatkan Dockerfile (multi-stage) dan docker-compose.yml untuk project Laravel 11 + MySQL 8 + Redis. Syarat: data MySQL di named volume; semua credential lewat .env (sertakan .env.example); healthcheck untuk MySQL dan app menunggu MySQL sehat; versi image spesifik, bukan latest; jalankan app sebagai user non-root; sertakan .dockerignore."
๐ก Debugging: "Container
apprestart terus-menerus. Ini outputdocker logs app50 baris terakhir: [tempel log]. Diagnosis penyebabnya dan beri langkah perbaikan."๐ก Review: "Review Dockerfile & compose ini: cek secret hardcoded, penggunaan :latest, urutan layer untuk caching, data penting yang tidak di volume, dan tidak adanya healthcheck."
๐งช Latihan: minta AI membuatkan docker-compose.yml berisi satu service nginx di port 8080, jalankan dengan docker compose up -d, buka http://localhost:8080, lihat lognya dengan docker compose logs, lalu matikan dengan docker compose down. Selamat โ Anda baru saja menjalankan server web dalam container.
4.12 ๐งช TUTORIAL PRAKTIK: Stack Pertamamu dalam 30 Menit¶
Prasyarat: Docker Desktop terpasang (docker.com/products/docker-desktop). Verifikasi:
docker --version # Docker version 2x.x
docker compose version # Docker Compose version v2.x
Langkah 1 โ Hello, container (5 menit)¶
docker run -d -p 8080:80 --name web nginx:1.27-alpine
Buka http://localhost:8080 โ halaman "Welcome to nginx!". Anda baru saja menjalankan web server tanpa menginstal apa pun di OS Anda. Sekarang bedah:
docker ps # lihat container 'web' berjalan, mapping 8080->80
docker logs web # log akses nginx โ refresh browser, lihat log bertambah
docker exec -it web sh # "masuk" ke dalamnya
ls /usr/share/nginx/html # file HTML yang disajikan
exit
docker stop web && docker rm web
Langkah 2 โ Buktikan sendiri bahwa container itu ephemeral (5 menit)¶
Ini pelajaran paling penting bab ini โ alami langsung supaya tidak pernah lupa:
docker run -d --name db1 -e POSTGRES_PASSWORD=rahasia postgres:16-alpine
docker exec -it db1 psql -U postgres -c "CREATE TABLE catatan (isi TEXT); INSERT INTO catatan VALUES ('data penting!');"
docker exec -it db1 psql -U postgres -c "SELECT * FROM catatan;" # data ada โ
docker rm -f db1 # hapus container
docker run -d --name db1 -e POSTGRES_PASSWORD=rahasia postgres:16-alpine
docker exec -it db1 psql -U postgres -c "SELECT * FROM catatan;" # ๐ฅ ERROR: tabel tidak ada
docker rm -f db1
Data hilang total. Sekarang ulangi dengan volume (-v db_data:/var/lib/postgresql/data di kedua docker run) โ data akan selamat melewati penghapusan container. Inilah kenapa checklist bab 06 mewajibkan volume.
Langkah 3 โ Compose stack mini (10 menit)¶
Buat folder baru berisi dua file:
# docker-compose.yml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
adminer: # GUI database ringan, buat eksplorasi
image: adminer:4
ports:
- "8081:8080"
depends_on:
db:
condition: service_healthy
volumes:
db_data:
# .env
DB_PASSWORD=rahasia-lokal-saja
Jalankan dan verifikasi:
docker compose up -d
docker compose ps # kedua service "running", db "(healthy)"
Buka http://localhost:8081 โ login Adminer: server db (nama service โ bukan localhost!), user postgres, password dari .env. Anda kini punya database + GUI-nya, tanpa instalasi apa pun.
Langkah 4 โ Simulasi insiden & pemulihan (10 menit)¶
Latih refleks troubleshooting (ยง4.9):
- Ubah
portsadminer jadi"8081:9999"(port dalam yang salah) โdocker compose up -dโ buka browser โ gagal. Diagnosis:docker compose ps+docker compose logs adminer. Kembalikan. - Ganti
DB_PASSWORDdi.envlaludocker compose up -dโ Adminer tidak bisa login dengan password baru. Kenapa? Password Postgres ditetapkan saat volume pertama kali dibuat โ env baru tidak mengubah data lama. Pelajaran: konfigurasi vs data itu dua hal berbeda. (Pemulihan di latihan ini:docker compose down -vlalu up ulang โ dan sadari bahwa-vbarusan menghapus semua data. Di production, langkah ini = bencana.)
Langkah 5 โ Serahkan ke AI dengan konteks penuh¶
Sekarang Anda punya vocabulary dan intuisi untuk menilai. Tes AI:
๐ก "Tambahkan service
redis:7-alpineke compose ini [tempel file]: dengan volume, healthcheck, dan tanpa mengekspos port ke host (hanya diakses antar-container). Jelaskan kenapa tidak perlu port mapping."
Verifikasi jawabannya dengan pemahaman dari Langkah 1โ4. Kalau AI mengekspos port Redis ke host tanpa alasan โ Anda kini tahu itu perlu dipertanyakan.
Rangkuman Bab¶
- Docker = environment identik di mana pun; solusi "di laptop saya jalan".
- Image = blueprint; container = instance; container itu sekali pakai โ data wajib di volume.
- Urutan instruksi Dockerfile menentukan kecepatan build (layer caching).
- Antar container: panggil lewat nama service, bukan
localhost. - Compose mendefinisikan semua service dalam satu file;
down -vmenghapus data โ hati-hati. - Secret lewat env variable +
.env(di-gitignore);.env.examplesebagai template. docker logsadalah langkah pertama semua debugging.