Dockerfile Go Best Practice
Pendahuluan
Membuat container untuk aplikasi Go bukan hanya soal menjalankan docker build. Dalam production environment, tujuannya adalah menghasilkan Docker image yang sangat kecil, cepat di-build (dalam hitungan detik), dan sangat aman (lulus audit keamanan).
Ada empat pilar utama untuk Dockerfile Go yang efisien dan aman.
1. Multi-Stage Builds (Build Bertahap)
Ini adalah teknik paling fundamental. Kita tidak ingin mengirim Go SDK (yang berukuran >1GB) ke produksi
Tahap 1 (
builder): Gunakan image lengkap sepertigolang:1.25-alpineuntuk meng-install dependensi dan mengkompilasi aplikasi Go AndaTahap 2 (
final): Gunakan image kosong sepertiscratchataudistroless. Kemudian, salin hanya hasil binary yang sudah dikompilasi dari tahapbuilder.
Hasil: Image final hanya berisi binary aplikasi Anda, mengurangi ukuran dari ~1GB menjadi di bawah beberapa MB.
2. Base Image Minimal (scratch vs distroless)
Menggunakan base image seperti ubuntu adalah kesalahan umum. Image besar ini mengandung ribuan paket dan library yang tidak Anda perlukan, yang masing-masing meningkatkan attack surface dan paparan CVE (kerentanan keamanan).
scratch: Image ini benar-benar kosong (0MB). Ini adalah yang paling minimal.distroless: Image ini (sepertigcr.io/distroless/static-debian12) hanya berisi hal-hal esensial, seperti sertifikat CA dan data zona waktu, tetapi tidak memiliki shell atau package manager. Ini adalah pilihan yang sangat baik untuk keamanan.
3. Binary Statis (CGO_ENABLED=0)
Untuk menjalankan aplikasi Go di image scratch atau distroless, aplikasi tersebut harus berupa static binary (tidak memiliki dependensi eksternal seperti libc).
Anda dapat mencapainya dengan mengatur variabel lingkungan CGO_ENABLED=0 saat melakukan go build. Ini juga membantu mengurangi ukuran binary.
4. Jalankan sebagai Non-Root
Menjalankan container sebagai root adalah risiko keamanan besar. Jika terjadi container breakout, penyerang bisa mendapatkan akses root di host machine. Kepatuhan seperti SOC2 dan PCI-DSS secara eksplisit melarang eksekusi container sebagai root.
Cara Mudah: Gunakan base image
distroless:nonrootyang sudah dikonfigurasi untuk berjalan sebagai non-root secara otomatis.Cara Manual (jika pakai
scratch): Buat user di tahapbuilder(RUN adduser -D appuser)lalu salin file/etc/passwdke tahap final, dan gunakanUSER appuser.
2. Mengoptimalkan Kecepatan Build (Layer Caching)
Kecepatan build 2 detik vs 2 menit bergantung pada seberapa baik Anda memanfaatkan Docker layer caching. Aturan utamanya adalah: urutan perintah COPY dan RUN sangat penting.
Strukturkan Dockerfile Anda dari lapisan yang paling jarang berubah ke yang paling sering berubah.
Dependensi Sistem: Install
ca-certificatesatautzdata(jarang berubah).Dependensi Go: Salin
go.moddango.sumterlebih dahulu.Download Dependensi: Jalankan
go mod download. Docker akan menyimpan lapisan ini.Salin Kode Sumber: Baru salin sisa kode sumber aplikasi Anda (
COPY . .).Build: Jalankan
go build.
Dengan cara ini, jika Anda hanya mengubah kode aplikasi (Langkah 4) tanpa mengubah dependensi (Langkah 2), Docker akan menggunakan kembali cache dari Langkah 3 (go mod download) dan tidak perlu mengunduh ulang semua library.
3. Optimasi Tambahan
Mengurangi Ukuran Binary
Gunakan ldflags saat membangun untuk menghapus simbol debug dan informasi DWARF, yang dapat mengurangi ukuran binary secara signifikan.
go build -ldflags="-w -s"
Build-Time Variables
Suntikkan informasi versi, commit, atau waktu build ke dalam binary Anda menggunakan ARG dan ldflags. Ini sangat berguna untuk monitoring dan debugging.
ARG VERSION=dev
ARG COMMIT=unknown
...
RUN go build -ldflags="-X main.Version=${VERSION} -X main.Commit=${COMMIT}" ...4. Dockerfile Produksi Lengkap
Berikut adalah Dockerfile lengkap yang menggabungkan semua praktik terbaik: multi-stage, distroless:nonroot, caching dependensi, binary statis, dan build-time variables.
# --- TAHAP 1: BUILDER ---
# Gunakan image alpine yang ringan untuk build
FROM golang:1.25-alpine AS builder
# Argumen untuk build-time variables
ARG VERSION=dev
ARG COMMIT=unknown
ARG BUILD_TIME
# Install dependensi sistem: sertifikat (untuk HTTPS) dan timezone
RUN apk add --no-cache git ca-certificates tzdata
WORKDIR /build
# --- Caching Dependensi ---
# 1. Salin file mod dan sum
COPY go.mod go.sum ./
# 2. Download dependensi (akan di-cache)
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
# 3. Salin sisa kode sumber
COPY . .
# 4. Build static binary
# - CGO_ENABLED=0 untuk static build
# - ldflags "-w -s" untuk mengurangi ukuran binary
# - ldflags "-X" untuk menyuntikkan build-time variables
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-w -s \
-X main.version=${VERSION} \
-X main.commit=${COMMIT} \
-X main.buildTime=${BUILD_TIME}" \
-o app ./cmd/server
# --- TAHAP 2: FINAL (PRODUKSI) ---
# Gunakan image distroless non-root: super minimal dan aman
FROM gcr.io/distroless/static-debian12:nonroot
# Salin data timezone dari builder
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# Salin binary aplikasi dari builder
COPY --from=builder /build/app /app
# Expose port
EXPOSE 8080
# Perintah untuk menjalankan aplikasi
ENTRYPOINT ["/app"]
Hasil Akhir
Dengan mengikuti struktur ini, Anda akan mendapatkan image Docker untuk aplikasi Go Anda yang:
Berukuran 8-12MB (dibandingkan 850MB+).
Build dalam 12 detik (setelah cache awal).
Sangat aman, berjalan sebagai non-root, dan memiliki attack surface minimal yang lolos audit SOC2 dan ISO27001.
Last updated