Belajar Chi - Production-Ready Architecture
Series/Belajar Chi/Episode 21
Episode 21 of 23

Belajar Chi - Production-Ready Architecture

Episode ini merangkai seluruh pola menjadi arsitektur siap produksi: modular router sebagai http.Handler, dependency injection, config env, dan centralized logging. Kalian juga membangun container dengan Docker multi-stage, pipeline CI/CD dengan GitHub Actions, deployment ke Kubernetes, dan strategi zero-downtime.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

Menulis route itu mudah; menjalankan aplikasi selama bertahun-tahun itu sulit. Episode 21 menggabungkan semua pola dari episode 8 sampai 20 menjadi satu arsitektur yang siap production: bagaimana aplikasi chi dikemas, diuji, di-deploy, dan dioperasikan tanpa downtime. Setelah episode ini, kalian punya cetak biru lengkap — dari main.go yang bersih sampai manifest Kubernetes dan pipeline yang otomatis.

Modular Router sebagai http.Handler

Blueprint

Blueprint router modular
func NewServer(cfg Config) http.Handler {
    pool := newPool(cfg.DBURL)
    userRepo := repository.NewUserRepo(pool)
    userSvc := service.NewUserService(userRepo)
    userH := handler.NewUserHandler(userSvc)
 
    r := chi.NewRouter()
    r.Use(middleware.RequestID)
    r.Use(middleware.Recoverer)
    r.Use(middleware.Timeout(10 * time.Second))
 
    r.Mount("/users", userH.Routes())
    r.Get("/metrics", prometheusHandler)
    return r
}

NewServer(cfg Config) http.Handler membangun dependency dari config, lalu merangkai middleware dan rute. Seluruh konstruksi terjadi sekali di main; hasilnya bisa diuji dan di-mount ulang.

Dependency Injection, Config, dan Logging

Alur Konfigurasi

Config dari environment
type Config struct {
    Port        string
    DBURL       string
    JWTSecret   string
    RedisAddr   string
}
 
func loadConfig() Config {
    return Config{
        Port:      viper.GetString("PORT"),
        DBURL:     viper.GetString("DATABASE_URL"),
        JWTSecret: viper.GetString("JWT_SECRET"),
        RedisAddr: viper.GetString("REDIS_ADDR"),
    }
}

loadConfig() membaca environment sekali dan membagikannya ke seluruh lapisan lewat Config. Tidak ada handler yang memanggil os.Getenv langsung.

Centralized Logging

Centralized logging
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelInfo,
}))
slog.SetDefault(logger)

slog.NewJSONHandler(os.Stdout, ...) menulis semua log sebagai JSON ke stdout — format yang langsung diproses agregator log. Tidak ada log ke file lokal yang bisa hilang saat container di-restart.

Containerization dengan Docker Multi-stage

Dockerfile yang Ramping

Dockerfile multi-stage
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /server ./cmd/server
 
FROM alpine:3.20
RUN adduser -D appuser
COPY --from=builder /server /usr/local/bin/server
USER appuser
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/server"]

CGO_ENABLED=0 go build menghasilkan binary statis tanpa dependency C. adduser -D appuser lalu USER appuser menjalankan server tanpa hak root — praktik keamanan standar. Image dijalankan dengan docker build -t chi-service . lalu docker run dengan environment yang diinjeksi.

CI/CD dan Deployment

Pipeline GitHub Actions

Pipeline CI/CD
name: ci
on:
  push:
    branches: [main, staging]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: "1.24"
      - run: go build ./...
      - run: go test ./...
      - run: go vet ./...

go build ./... lalu go test ./... memastikan kode selalu hijau sebelum lanjut. go vet menangkap pola mencurigakan lebih awal.

Deployment ke Kubernetes

Deployment Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chi-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: chi-service
  template:
    metadata:
      labels:
        app: chi-service
    spec:
      containers:
        - name: chi-service
          image: registry.example.com/chi-service:1.0.0
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080

replicas: 3 menjalankan tiga instance; readinessProbe memakai /healthz dari episode 18. Deployment dengan replica dan probe adalah fondasi zero-downtime.

Rolling Update

Deployment baru dijalankan bertahap: pod lama tetap melayani sampai pod baru siap (readiness hijau), lalu traffic dipindahkan dan pod lama di-terminate setelah selesai menangani request.

Rollout baru
kubectl set image deployment/chi-service \
  chi-service=registry.example.com/chi-service:1.0.1

kubectl set image deployment/chi-service ... memicu rolling update. Kombinasi srv.Shutdown dari episode 10 dan readiness probe memastikan tidak ada request yang terpotong.

Penutup

Inti yang harus dibawa pulang:

  • Router production adalah func NewServer(cfg Config) http.Handler.
  • Config dan dependency diinjeksi sekali, dipakai di semua lapisan.
  • Semua log ditulis JSON ke stdout lewat satu logger.
  • Docker multi-stage menghasilkan image ramping tanpa root.
  • Pipeline CI/CD menjalankan build, test, dan vet otomatis.
  • Kubernetes deployment plus readiness dan graceful shutdown = zero-downtime.

Di episode 22 selanjutnya, episode penutup series: ekosistem alternatif dan refleksi akhir — perbandingan chi dengan Gin, Echo, Fiber, dan net/http, kapan memilih masing-masing, rekap perjalanan episode 0 sampai 21, serta checklist service production-grade dan masa depan chi di ekosistem Go.

Belajar Chi - Production-Ready Architecture | Belajar Chi