Merapikan infrastruktur lokal tokokita: memahami Docker network dan DNS antar service, menulis compose yang benar dengan depends_on, healthcheck, dan named volumes, serta mengamankan image container dengan multi-stage build dan non-root user

Sampai episode ini kita menjalankan service lewat bun dev di host dan dependency infra di Docker Compose. Sekarang mari memindahkan seluruh layanan ke container — seperti itulah mereka akan hidup di Kubernetes nanti. Ini episode jembatan: networking dan compose yang benar di sini menjadi fondasi manifest K8s di episode 17.
Mengapa episode ini penting? Karena kebanyakan masalah "works on my machine" di microservices lahir dari networking dan env yang tidak terkendali. Container yang saling salah DNS, env yang hardcode, image yang membengkak — semua bisa dicegah dengan disiplin yang kita susun sekarang.
Docker menyediakan bridge network per Compose project secara otomatis. Di dalam network itu, tiap container bisa diakses lewat nama service sebagai DNS:
auth-service:3001 → 127.0.0.1:5432? Tidak — `postgres:5432`
order-service → call → product-service:3002 (bukan localhost:3002)Aturan penting: jangan expose port internal ke host kecuali yang harus dijangkau luar (api-gateway, database untuk psql lokal, Mailpit UI). Port 5432 PostgreSQL tetap di-publish agar psql lokal nyaman, tetapi port service internal seperti 3002 cukup di dalam network.
Dalam network: semua layanan saling bicara via nama service.
Ke host: hanya api-gateway (3000), PG (5432:psql), Mailpit (8025).depends_on saja hanya menunggu container start, bukan ready. Pasangkan dengan healthcheck agar order yang benar benar-benar berarti:
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: tokokita
POSTGRES_PASSWORD: tokokita
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U tokokita"]
interval: 5s
timeout: 3s
retries: 10
auth-service:
build:
context: ../services/auth-service
environment:
DATABASE_URL: postgres://tokokita:tokokita@postgres:5432/tokokita
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3001:3001"Dengan condition: service_healthy, auth-service baru dimulai setelah PostgreSQL benar-benar siap menerima koneksi.
docker compose down.postgres:16-alpine, redis:7-alpine, redpandadata/redpanda:v24.x) — jangan latest yang bisa bergeser diam-diam dan merusak reproduksibilitas.Semua nilai rahasia (password, secret) lewat variabel env; .env dipakai Compose untuk interpolasi; nilai dev default boleh di .env, jangan di commit (gunakan .env.example sebagai template). Secret sungguhan di produksi lewat secrets management (episode 19 dan 23).
Image production sebaiknya hanya berisi runtime — bukan toolchain build. Penghematannya nyata:
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
FROM oven/bun:1 AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN bun build src/index.ts --target=bun --outfile=dist/index.js
FROM oven/bun:1 AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER bun
EXPOSE 3001
CMD ["bun", "run", "dist/index.js"]Tiga tahap: deps (install), build (bundling TypeScript), runtime (hanya hasil build + node_modules). Runtime pun tidak menyimpan source code — hanya artefak.
Konvensi keamanan dasar: jangan jalankan container sebagai root. Itulah USER bun di Dockerfile — image oven/bun menyediakan user bun yang non-root.
Jangan salin .git, node_modules, .next, atau .env* ke konteks build — selain mempercepat build, ini mencegah secret ikut ke image:
node_modules
dist
.env*
.git
DockerfileWarning
Bahaya latest: image yang sama mungkin berisi bug fix (bagus) atau breaking change (buruk) tanpa terduga. Satu aturan yang tak bisa ditawar: pinned version di compose dan manifest. Untuk image produksi tokokita selanjutnya selalu pakai tag semver (misal ghcr.io/tokokita/auth-service:v1.4.2), bukan latest — akan dikonsistenkan di episode 23 saat CI/CD.
Setelah compose lengkap:
docker compose up --build -d
docker compose ps
docker network ls
# dari dalam container auth-service, cek DNS product-service
docker compose exec auth-service ping -c 1 postgresTip
Kalau sebuah layanan "tidak bisa connect ke database", cek urutan: DNS salah? (pakai nama service, bukan localhost) — port salah? — healthcheck belum selesai? — secret/env salah? Sembilan dari sepuluh kasus di compose adalah salah satu dari empat ini. Gunakan docker compose logs <service> dan docker compose exec <service> env untuk diagnosa cepat.
Episode 16 merapikan infrastruktur container:
USER bun), .dockerignore.Di episode 17 selanjutnya, kita deploy tokokita ke Kubernetes — manifest Deployment, Service, HPA, PVC untuk PostgreSQL, ConfigMap & Secret, probes, ingress yang hanya mengekspos gateway, serta strategi rolling update yang aman. Sampai jumpa di episode 17!