Episode ini merangkai semuanya menjadi arsitektur production: layout project modular, graceful shutdown, konfigurasi env, dan logging terpusat, lalu containerization dengan Docker multi-stage, pipeline CI/CD di GitHub Actions, serta deployment zero-downtime di Kubernetes.

Semua pelajaran dari episode 0 sampai 20 sekarang disatukan. Episode 21 ini membangun production-ready architecture: layout project modular, graceful shutdown, konfigurasi yang aman, logging terpusat, containerization dengan Docker multi-stage, pipeline CI/CD di GitHub Actions, dan deployment dengan strategi zero-downtime.
Tujuannya bukan sekadar membuat kode berjalan, tetapi membuat kode bertahan di production: mudah dibangun ulang, mudah diverifikasi oleh mesin, mudah dipantau, dan mudah diganti tanpa downtime. Setiap bagian di episode ini adalah praktik industri yang saling melengkapi.
Production-ready dimulai dari struktur kode. Susun ulang semua yang sudah kalian pelajari menjadi satu kesatuan:
belajar-gin/
├── cmd/server/main.go
├── internal/
│ ├── config/
│ ├── router/
│ ├── user/
│ ├── order/
│ ├── middleware/
│ └── observability/
├── migrations/
├── Dockerfile
├── .github/workflows/ci.yml
└── go.modFolder internal/ menjaga semua package privat (episode 8). migrations/ menyimpan skema versioned (episode 9). config/ memuat environment dengan struct (episode 10), observability/ berisi metrik dan tracing (episode 18). Struktur ini bukan kewajiban absolut, tetapi pola yang terbukti.
Seluruh aplikasi mengikuti disiplin yang sudah dipelajari: handler tidak menyentuh database, service memuat business logic, repository memakai ctx, error mengalir ke satu middleware, dan log selalu terstruktur. Ketika disiplin ini konsisten di semua domain, menambah fitur baru terasa sama seperti menambah fitur lama.
Build image dengan multi-stage: satu stage untuk kompilasi, satu stage ramping untuk runtime:
FROM golang:1.25 AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /server /server
EXPOSE 8080
USER nonroot
ENTRYPOINT ["/server"]CGO_ENABLED=0 GOOS=linux go build ... menghasilkan binary statis tanpa dependensi C. Stage runtime memakai image distroless nonroot yang hanya berisi binary — kecil dan minim permukaan serangan. Berkas .env tidak pernah disalin; konfigurasi masuk lewat variabel lingkungan saat container dijalankan.
docker build -t belajar-gin:local .
docker run --rm -p 8080:8080 \
-e PORT=8080 \
-e DATABASE_URL="postgres://arman:rahasia@host.docker.internal:5432/belajargin?sslmode=disable" \
belajar-gin:localdocker build -t belajar-gin:local . membangun image dari Dockerfile. Flag -e menyuntikkan environment — persis variabel yang dibaca config.Load() dari episode 10. Jalankan image ini di mana saja: laptop, VM, atau Kubernetes, dengan konfigurasi yang sama.
Pipeline menjalankan test di setiap push, lalu membangun image di rilis:
name: ci
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.25"
- name: Run tests
run: go test ./...
- name: Build binary
run: go build ./cmd/serveractions/setup-go dengan go-version: "1.25" menyiapkan toolchain di runner. go test ./... dari episode 17 berjalan untuk setiap push dan pull request, sehingga regresi tertangkap sebelum masuk production. Langkah go build memastikan aplikasi terkompilasi di lingkungan bersih.
Saat code di-merge ke main, pipeline membangun image dengan tag versi lalu mendorongnya ke registry:
docker tag belajar-gin:latest ghcr.io/devvnull/belajar-gin:$VERSION
docker push ghcr.io/devvnull/belajar-gin:$VERSIONdocker push ghcr.io/devvnull/belajar-gin:$VERSION mengunggah image yang sudah diuji. Gunakan SHA commit atau semver sebagai tag agar setiap versi bisa di-rollback dengan pasti — image yang tidak bisa dilacak sumbernya adalah liabilitas.
Kubernetes memperbarui pod secara bertahap tanpa downtime. Siapkan deployment dengan strategi RollingUpdate dan probe dari episode 18:
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
spec:
containers:
- name: api
image: ghcr.io/devvnull/belajar-gin:$VERSION
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /readyz
port: 8080
livenessProbe:
httpGet:
path: /healthz
port: 8080maxUnavailable: 0 dan maxSurge: 1 menjamin selalu ada pod yang melayani selama proses pembaruan. Pod baru hanya menerima traffic setelah readinessProbe ke /readyz sukses. Ini pola zero-downtime: saat satu pod diperbarui, yang lain tetap melayani.
kubectl set image deployment/belajar-gin \
api=ghcr.io/devvnull/belajar-gin:v1.21.0
kubectl rollout status deployment/belajar-ginkubectl rollout status deployment/belajar-gin memantau pembaruan sampai selesai. Jika sesuatu rusak, kubectl rollout undo mengembalikan versi sebelumnya dengan cepat. Gabungkan dengan grace period yang panjang pada preStop hook agar request aktif selesai sebelum pod dihentikan — melengkapi graceful shutdown dari episode 10.
Inti yang harus dibawa pulang:
internal/, migrations, dan observability terpisah.go test ./... di setiap push dan pull request.Di episode 22 selanjutnya, episode terakhir, kita akan membedah ekosistem alternatif & refleksi akhir — membandingkan Gin dengan Echo, Fiber, chi, dan net/http polos, kapan memilih masing-masing, serta rekap dan checklist REST API production-grade untuk menutup perjalanan Belajar Gin.