Cheat sheet menyelesaikan masalah kecepatan satu orang, tetapi repository yang dirawat lima developer selama tiga tahun menghadapi masalah yang berbeda: tanpa kesepakatan tertulis, setiap Dockerfile tumbuh dengan gaya penulisnya masing-masing — ada yang menulis instruksi dengan huruf kecil, ada yang menumpuk lima RUN padahal bisa satu, ada yang lupa .dockerignore sehingga context membesar diam-diam. Perbedaan gaya bukan sekadar estetika: urutan dan bentuk instruksi menentukan umur cache, ukuran image, dan keamanan runtime, sehingga Dockerfile yang tidak konsisten adalah Dockerfile yang mahal. Chapter ini merumuskan konvensi penulisan yang bisa diadopsi tim dalam satu halaman: aturan gaya, lima blok logis, label dan pin versi, .dockerignore baku, tiga aturan keras, anggaran ukuran, dan satu contoh refactor menyeluruh.

Aturan Gaya Penulisan #

Empat aturan berikut membuat Dockerfile mudah dibaca siapa pun tanpa membuka dokumentasi. Contoh penerapannya sekaligus memperlihatkan bentuk akhirnya:

dockerfile
# syntax=docker/dockerfile:1
# Blok metadata: base image yang hampir tidak pernah berubah

FROM node:22-alpine

# Blok dependensi: manifest dulu supaya cache awet
COPY package.json package-lock.json ./
RUN npm ci --omit=dev \
 && npm cache clean --force
  1. Instruksi ditulis HURUF BESAR — FROM, COPY, RUN — agar kontras dengan argumen dan nama berkas di sebelah kanannya.
  2. Parser directive di baris pertama — # syntax=docker/dockerfile:1 memastikan builder memakai fitur Dockerfile terbaru secara konsisten lintas mesin.
  3. Continuation backslash untuk aksi panjang — rangkaian perintah yang harus menjadi satu layer (instalasi plus pembersihan) disambung dengan \, bukan dipecah menjadi beberapa RUN.
  4. Komentar per blok, bukan per baris acak — komentar menjelaskan mengapa sebuah blok ada (misalnya “manifest dulu supaya cache awet”), bukan mengulangi isi perintahnya.

Lima Blok Logis Urutan Standar #

Setiap Dockerfile yang rapi dapat dibaca sebagai lima blok dari atas ke bawah. Urutan ini bukan selera — ia adalah urutan frekuensi berubah, sama seperti tabel pada Cheat Sheet Dockerfile, dan mengikuti aturan emas Chapter Build Cache: yang jarang berubah di atas.

BlokIsi khasFrekuensi berubah
1. Metadataparser directive, judul komentar, FROM, LABELsangat jarang
2. LingkunganWORKDIR, ENV, ARG, instalasi paket sistemjarang
3. DependensiCOPY manifest, RUN instalasi dependensijarang
4. AplikasiCOPY kode sumber, langkah build, pengaturan hak aksessetiap commit
5. RuntimeUSER, EXPOSE, HEALTHCHECK, CMDhampir tidak pernah

LABEL dan Pin Versi Tiga Tingkat #

Metadata image mengikuti kunci standar org.opencontainers.image.* sehingga bisa dibaca oleh registry, alat scan, dan pipeline yang sama. Pin versi diterapkan bertingkat sesuai seberapa ketat reproduksi yang dibutuhkan:

dockerfile
LABEL org.opencontainers.image.title="katalog-web" \
      org.opencontainers.image.description="API katalog toko" \
      org.opencontainers.image.source="https://github.com/budi/toko" \
      org.opencontainers.image.licenses="MIT"
dockerfile
FROM node:22-alpine                                                    # tingkat 1: tag minor
FROM node:22-alpine@sha256:3f763443c2b783aef6301f03ffa73d0ddd3307c4f4976f8aec9593056ac7da8d   # tingkat 2: digest
RUN apt-get install -y --no-install-recommends curl=8.14.1-*           # tingkat 3: paket apt dipin
  1. Tag minor (node:22-alpine) — mudah dibaca dan mendapat perbaikan patch otomatis; cocok untuk pengembangan.
  2. Digest (@sha256:...) — image dibekukan persis bit demi bit; wajib untuk produksi dan build yang harus ter-reproduksi, pola yang kita bedah di chapter Image Digest.
  3. Paket apt dipin (curl=8.14.1-*) — sintaks nama=versi mencegah versi paket bergeser diam-diam antar build walau base image tetap.

.dockerignore Minimal Baku #

Dua belas entri berikut menutup kebocoran context yang paling sering: folder VCS, rahasia, artefak build, dan berkas pengembang. Baris terakhir adalah negasi yang mengembalikan contoh environment agar tetap ikut terkirim.

text
.git
.github
.vscode
.idea
.env*
node_modules
__pycache__
*.pyc
dist
build
target
tests
!.env.example

Entri .env* memblokir seluruh keluarga berkas environment sungguhan, lalu !.env.example membuat pengecualian untuk templatnya — pola yang kita pakai di project WordPress pada Section Proyek. Tanpa baris ini, docker build mengirim .env berisi rahasia123 ke daemon sebagai bagian dari context meskipun Dockerfile tidak pernah COPY berkas tersebut.

Tiga Aturan Keras #

Tiga aturan berikut bukan gaya — melanggarnya membuka celah nyata atau merusak perilaku runtime, jadi statusnya wajib di setiap Dockerfile produksi.

AturanWujud di DockerfileAlasan
Berjalan non-rootUSER node atau USER 1000:1000 sebelum CMDproses root di container punya daya jangkau berlebih terhadap volume dan host
Exec formCMD ["node", "src/server.js"]SIGTERM sampai ke aplikasi dan exit code terbaca akurat saat docker stop
Ada HEALTHCHECKHEALTHCHECK ... CMD wget -qO- ... probe /healthzdaemon tahu container sakit sebelum pelanggan yang melapor

Anggaran Ukuran Image per Jenis Aplikasi #

Angka berikut ilustratif dan berfungsi sebagai patokan cepat: bila image kita jauh melampaui anggarannya, ada sesuatu yang bocor — biasanya toolchain builder yang ikut terkirim atau cache paket yang lupa dibersihkan.

Jenis aplikasiBase imageAnggaran ukuran
Situs statisnginx:1.29-alpinesekitar 50MB
API Go multi-stagealpine:3.2010 sampai 20MB
API Nodenode:22-alpine130 sampai 190MB
API Pythonpython:3.12-slim150 sampai 250MB
CMS siap pakaiwordpress:6.8-apache250MB ke atas

Contoh Refactor Sebelum dan Sesudah #

Dockerfile buruk berikut asli keluaran “cukup jalan”: base penuh, COPY segalanya di awal, paket terpasang tanpa alasan, dan CMD bentuk shell. Mari kita lihat dulu wujud sebelumnya:

dockerfile
FROM node:22
COPY . .
RUN npm install
RUN apt-get update && apt-get install -y curl
ENV APP_ENV production
CMD node src/server.js

Hasil refactornya mengikuti lima blok konvensi, dan panjangnya hanya bertambah beberapa baris:

dockerfile
# syntax=docker/dockerfile:1

# Blok 1: metadata
FROM node:22-alpine
LABEL org.opencontainers.image.title="katalog-web"

# Blok 2: lingkungan
WORKDIR /app
ENV NODE_ENV=production

# Blok 3: dependensi
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# Blok 4: aplikasi
COPY src/ ./src/

# Blok 5: runtime
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD wget -qO- http://localhost:3000/healthz || exit 1
CMD ["node", "src/server.js"]

Mari kita telaah lima perubahan yang paling berdampak pada hasil refactor di atas:

  1. node:22 menjadi node:22-alpine — runtime fungsi yang sama dengan ratusan megabita lebih ramping, sekaligus mengurangi jumlah paket yang bisa punya celah keamanan.
  2. COPY . . pindah ke bawah dan menyempit — kode sumber tidak lagi meruntuhkan layer npm ci; manifest disalin lebih dulu sehingga rebuild setelah commit hanya menyentuh dua layer terakhir.
  3. curl hilang dari image — probe kesehatan cukup memakai wget bawaan busybox; paket sistem yang tidak dipakai aplikasi tidak berhak tinggal di image produksi.
  4. npm install menjadi npm ci --omit=dev — instalasi kini persis mengikuti lock file dan tanpa dependensi pengembangan, sehingga image sama di mesin siapa pun.
  5. CMD pindah ke exec form dan USER node ditambahkan — sinyal docker stop sampai ke proses Node, exit code akurat, dan aplikasi tidak lagi berjalan sebagai root; HEALTHCHECK melengkapi ketiganya dengan status kesehatan yang bisa dipantau daemon.

Konvensi ini menjaga isi Dockerfile, tetapi Dockerfile hanya satu berkas di dalam repository: letaknya relatif terhadap kode, context, dan compose.yaml juga merupakan keputusan yang berdampak pada cache, kecepatan CI, dan keamanan. Keputusan penempatan itulah yang dibahas chapter berikutnya melalui tiga pola Struktur Project Container — aplikasi tunggal, monorepo, hingga project yang membawa infrastrukturnya sendiri.

Referensi #