Web Artisan
Chapter 06 · Docker

Studi Kasus & Jalan ke Produksi Cloud

Chapter penutup menyatukan semuanya: satu stack skincare-api yang mirip produksi dijalankan dengan satu perintah, lima jebakan khas dibongkar, lalu peta jelas dari image lokal ke CI/CD dan AWS.

Stack end-to-endPeta ke CI/CD & AWS~23 menit baca

Lima chapter sebelumnya memberi kepingan-kepingannya: mental model, image multi-stage, config dan jaringan dan volume, Compose, lalu registry dan pengerasan produksi. Chapter penutup ini merangkainya jadi satu gambar utuh, sebuah studi kasus skincare-api yang realistis, membongkar pitfall yang paling sering menjatuhkan pendatang dari JS/PHP, dan memetakan langkah dewasa berikutnya menuju cloud, sebelum menutup dengan ringkasan keseluruhan course.

01

Studi Kasus: Containerize Go API Skincare

Stack lengkap plus pitfalls khas developer JS/PHP

Saatnya menyatukan semua konsep menjadi satu stack lokal yang realistis: API Go, PostgreSQL, dan Redis, dijalankan dengan satu perintah.

Target kita adalah skincare-api, backend online shop skincare yang sudah kamu kenal sepanjang course ini. API ini membaca produk dari PostgreSQL, men-cache hasilnya di Redis, dan mengekspos GET /healthz agar Compose tahu kapan container siap melayani. Tujuannya bukan sekadar “bisa jalan”, tapi menyusun stack yang mirip produksi: build kecil dengan multi-stage, config lewat env, dan dependency yang digerbangi healthcheck.

Dockerfile final skincare-api

Kita pakai pola multi-stage dari Chapter 2: stage golang:1.26 untuk compile, lalu runtime distroless yang ramping dan non-root. Biner Go statis (CGO_ENABLED=0) muat di distroless/static-debian12 tanpa shell, jadi permukaan serangan tipis.

Dockerfile
# --- build --- FROM golang:1.26 AS build WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app ./cmd/server # --- runtime --- FROM gcr.io/distroless/static-debian12:nonroot COPY --from=build /app /app USER nonroot:nonroot EXPOSE 8080 ENTRYPOINT ["/app"]

Perhatikan COPY go.mod go.sum lebih dulu sebelum COPY . .. Itu bukan gaya, melainkan trik cache layer: selama dependency tidak berubah, go mod download tidak dijalankan ulang walau kode handler kamu berubah ratusan kali.

Config env dan health endpoint

Aplikasi membaca semuanya dari environment, tidak ada nilai hardcode. Health endpoint dibuat ringan, tidak menyentuh database, agar cepat dan tidak ikut menumbangkan API saat DB lambat.

cmd/server/main.go
package main import ( "net/http" "os" "github.com/kamu/skincare-backend/internal/server" ) func main() { cfg := server.Config{ Addr: ":" + envOr("PORT", "8080"), DatabaseURL: os.Getenv("DATABASE_URL"), RedisAddr: os.Getenv("REDIS_ADDR"), } http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"ok"}`)) }) server.Run(cfg) }
🌉Jembatan: dari .env Laravel ke env container

Di Laravel, .env dibaca proses PHP saat boot; di sini env disuntik Compose ke proses container, jadi satu image yang sama bisa jalan beda config tanpa rebuild.

Arsitektur stack final

flowchart LR
  Dev["curl :8080"] --> API["skincare-api\n(EXPOSE 8080)"]
  API -->|appnet DNS: db:5432| DB[("postgres:17")]
  API -->|appnet DNS: cache:6379| R[("redis:7")]
  DB -.named volume.-> V[("pgdata")]

Stack lokal skincare. API bicara ke db dan cache lewat nama service, bukan IP; data Postgres bertahan di named volume pgdata.

Compose stack final

compose.yaml
services: api: build: . ports: - "8080:8080" environment: PORT: "8080" DATABASE_URL: "postgres://app:secret@db:5432/skincare?sslmode=disable" REDIS_ADDR: "cache:6379" depends_on: db: condition: service_healthy cache: condition: service_started db: image: postgres:17 environment: POSTGRES_USER: app POSTGRES_PASSWORD: secret POSTGRES_DB: skincare volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U app"] interval: 5s timeout: 3s retries: 5 cache: image: redis:7 volumes: pgdata:

Tidak ada version: di atas. Atribut itu sudah usang dan diabaikan Compose modern, malah memunculkan peringatan. Service bernama db dan cache, persis nama yang dipakai API di DATABASE_URL dan REDIS_ADDR, karena Compose membuat user-defined network dengan DNS antar service otomatis.

Lima pitfalls khas dan cara debug

Inilah lima jebakan yang paling sering menjatuhkan developer yang baru pindah dari npm run dev atau php artisan serve. Sebagian besar bukan bug Docker, melainkan beda mental model antara “proses di laptop” dan “proses di dalam container”, tema yang kita tanam sejak Chapter 1.

PitfallGejalaSebabSolusi
Localhost trapAPI gagal connect DB walau Postgres “jalan”Kode pakai localhost:5432; di dalam container, localhost = container itu sendiri, bukan hostPakai nama service: db:5432, andalkan DNS Compose
Stale imagePerubahan kode tidak muncul setelah upLupa rebuild; Compose pakai image lama yang sudah ter-cachedocker compose up --build atau docker compose build api
Wrong port mappingcurl :8080 connection refusedApp listen :3000 tapi mapping 8080:8080, atau urutan host:container terbalikSamakan ports dengan port app; ingat format host:container
Missing envApp panic atau koneksi kosong saat startDATABASE_URL tidak diset, os.Getenv mengembalikan string kosongDefinisikan di environment/env_file; cek docker compose config
Volume shadowingFile yang sudah di-build hilang di containerBind mount menimpa direktori berisi artefak image dengan folder host kosongJangan mount over path build; mount hanya source yang memang perlu
⚠️Localhost trap adalah jebakan nomor satu

Di dalam container, 127.0.0.1 menunjuk ke container itu sendiri. Postgres ada di container lain, jadi alamatnya db:5432, bukan localhost:5432.

💡Debug cepat: config dan logs

Jalankan docker compose config untuk melihat env final yang ter-resolve, dan docker compose logs -f api untuk membaca alasan crash sebelum menebak-nebak.

Hands-on: bangun, jalankan, cek health

Build image API

Jalankan docker compose build api agar Dockerfile multi-stage dieksekusi dan layer dependency masuk cache untuk build berikutnya.

Jalankan seluruh stack

docker compose up -d menyalakan db, cache, lalu api; depends_on: condition: service_healthy menahan API sampai pg_isready lulus.

Cek health endpoint

curl localhost:8080/healthz harus mengembalikan {"status":"ok"}; bila refused, periksa docker compose ps dan logs api.

🔗Satu perintah, satu lingkungan

Compose ke stack ini seperti resep dapur: satu file mendeklarasikan bahan (image), takaran (env), dan urutan masak (depends_on), lalu up memasaknya identik di laptop siapa pun.

Dengan ini kamu punya backend skincare yang berjalan lokal layaknya produksi mini: build ramping, dependency tergerbang sehat, dan config yang bisa dipindah tanpa menyentuh image. Inilah fondasi yang sama yang nanti diangkat ke cloud.

02

Topik Lanjutan dan Peta ke Deploy

Dari image lokal ke CI/CD dan AWS

Image yang jalan di laptop adalah setengah cerita; setengah lainnya adalah membawanya ke registry dan menjalankannya di cloud secara otomatis.

Setelah skincare-api containerized, langkah dewasa berikutnya adalah menghapus tahap manual. Alih-alih docker build lalu docker push dari laptop, sebuah CI pipeline melakukannya setiap kali kamu merge ke main: build image, jalankan test, lalu push ke registry. Dari registry, platform orkestrasi menarik image dan menjalankannya. Pola “build sekali, deploy artefak yang sama” ini, yang kita pelajari di Chapter 5, sudah jadi standar tim backend modern. Course ini berhenti di gerbang itu, tapi penting kamu lihat petanya supaya tahu ke mana arah berikutnya.

Peta pipeline ke AWS

flowchart LR
  Push["git push main"] --> CI["CI: build + test"]
  CI --> Img["docker build\n-t ...:sha"]
  Img --> ECR["push ke AWS ECR\n(registry)"]
  ECR --> ECS["deploy ECS Fargate\n(jalankan container)"]
  ECS --> RDS[("RDS PostgreSQL")]

Dari commit ke produksi. CI membangun dan menguji, mendorong image ke ECR, lalu ECS Fargate menjalankan container yang sama, terhubung ke database RDS terkelola.

Potongan-potongan yang akan kamu temui

Beberapa istilah AWS akan muncul, dan masing-masing memetakan rapi ke konsep yang sudah kamu kuasai. ECR hanyalah registry privat (seperti GHCR, tapi milik AWS). ECS Fargate adalah cara menjalankan container tanpa mengurus server. RDS adalah Postgres terkelola, pengganti container db lokalmu. Untuk konteks resmi, lihat dokumentasi Amazon ECR dan panduan AWS Fargate.

🌉Jembatan: dari Vercel/Forge ke ECS

Kalau terbiasa deploy Next.js di Vercel atau Laravel via Forge, ECS Fargate adalah ide serupa untuk container: kamu serahkan artefak (image), platform yang menjalankan dan menskalakannya.

Akselerasi build: BuildKit dan cache mounts

BuildKit sudah jadi builder default, jadi kamu otomatis menikmati build paralel dan cache layer. Lebih jauh, cache mount membuat cache modul Go bertahan antar build tanpa masuk ke image final, memangkas waktu go mod download di CI.

Dockerfile (cache mount)
RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 go build -o /app ./cmd/server
💡compose watch untuk loop dev

docker compose watch menyinkronkan perubahan source ke container dan me-rebuild saat perlu, mendekati pengalaman hot-reload npm run dev tanpa meninggalkan stack Compose.

Arah lanjutan

CI/CD pipeline

Otomatiskan build, test, dan push image bertag digest tiap merge, agar deploy reproducible dan bebas langkah manual dari laptop.

Registry dan ECR

Login ke ghcr.io atau ECR, push tag semver, dan referensikan via image@sha256: untuk image yang immutable di produksi.

ECS Fargate dan RDS

Jalankan container tanpa server, sambungkan ke Postgres terkelola RDS, dan atur scaling tanpa menyentuh OS host.

Untuk runtime, ingat kembali pilihan base image ramping seperti distroless dari Google: image kecil non-root mempercepat pull di CI dan ECS sekaligus mengecilkan permukaan serangan. Referensi perintah dan praktik terbaik lain selalu bisa kamu cek di docs.docker.com.

📝Course ini fondasi, deploy AWS di jalur lanjutan

Tujuan course ini menanamkan fondasi container yang kokoh. Detail end-to-end ECR, ECS Fargate, dan RDS dibahas tuntas di jalur deploy lanjutan.

03

Ringkasan dan Poin Penting

Checklist Docker untuk backend Go

Kamu sekarang bisa mengubah biner Go menjadi container yang ramping, aman, dan reproducible, lalu menjalankannya sebagai bagian dari stack multi-service, dari laptop sampai gerbang cloud.

Mari petakan ulang perjalanan seluruh course. Kita mulai dari membedakan image (cetakan immutable) dan container (instance yang berjalan), lalu menulis Dockerfile yang sadar cache layer. Multi-stage memisahkan toolchain build dari runtime sehingga image akhir hanya berisi biner dan sertifikat. Config masuk lewat env, bukan hardcode. Networking mengandalkan DNS antar service di user-defined network. Volume menjaga data Postgres tetap hidup melewati restart. Compose merangkai semuanya, dengan healthcheck menggerbangi urutan start. Terakhir, tag dan registry membuat image bisa dibagikan, sementara non-root dan scanning menjaga keamanan.

Yang Wajib Menempel

  • Image adalah cetakan immutable; container adalah instance berjalan dari image, dan registry tempat image dibagikan.
  • Urutkan Dockerfile dari yang jarang berubah ke yang sering: COPY go.mod go.sum lalu go mod download sebelum COPY . ..
  • Multi-stage plus CGO_ENABLED=0 menghasilkan biner statis yang muat di distroless/static-debian12, kecil dan tanpa shell.
  • Config selalu lewat env (environment/env_file); jangan hardcode kredensial atau alamat ke dalam image.
  • Di dalam container, localhost adalah container itu sendiri; service lain dipanggil lewat nama via DNS user-defined network.
  • Named volume menjaga data stateful (Postgres) bertahan lintas docker compose down dan restart.
  • Compose plus healthcheck dan depends_on: condition: service_healthy memastikan API start setelah DB benar-benar siap.
  • docker compose logs, exec, dan flag resource (--memory, --cpus) adalah alat debug dan pembatas dasar.
  • Pin tag semver dan referensi digest untuk deploy reproducible; jalankan sebagai non-root dan pindai dengan docker scout atau trivy.
💡Aturan emas image produksi

Image akhir yang baik: kecil, non-root, tanpa shell bila bisa, bertag immutable, dan tidak membawa satu pun secret di dalam layer-nya.

Tiga arah langkah berikutnya

CI/CD

Pindahkan build, test, dan push image ke pipeline otomatis agar setiap merge menghasilkan artefak yang konsisten dan teruji.

AWS ECR dan ECS

Simpan image di registry ECR, lalu jalankan container di ECS Fargate yang tersambung ke Postgres terkelola RDS.

Observability dan scaling

Tambahkan metrik, log terpusat, dan health probe agar container bisa di-scale dan dipantau dengan percaya diri.

Dari sini, lanjutkan ke Docker Compose tingkat lanjut untuk environment dev yang lebih kaya, lalu rakit CI pipeline yang membangun dan menguji image setiap commit. Setelah itu, bawa skincare-api ke produksi nyata lewat AWS ECR sebagai registry dan ECS Fargate sebagai runtime, dengan RDS sebagai database. Fondasi container yang kamu kuasai di course ini adalah tiket masuk ke seluruh jalur deploy itu.