Deploy Go REST API di Kubernetes (K3s) dengan PostgreSQL, SSL Cloudflare, dan Autoscaling
Artikel ini akan membahas langkah end-to-end untuk membangun aplikasi Go REST API, membuat image Docker, lalu melakukan deployment di Kubernetes (K3s) lengkap dengan PostgreSQL sebagai database, SSL menggunakan Cloudflare DNS Challenge, hingga autoscaling dengan Horizontal Pod Autoscaler (HPA). Semua akan dijelaskan detail agar mudah dipahami. π₯
1οΈβ£ Persiapan Struktur Project
Sebelum memulai, silakan unduh script terlebih dahulu melalui repository berikut:
https://github.com/masdikaaa/go-rest-api-template
Struktur direktori project Go API kita adalah sebagai berikut:
.
βββ Caddyfile
βββ cmd
β βββ main.go
βββ config
β βββ postgres.go
βββ db
β βββ init.sql
βββ deployment
β βββ Dockerfile
βββ docker-compose.yaml
βββ go.mod
βββ go.sum
βββ internal
β βββ middleware
β β βββ jwt.go
β βββ news
β β βββ dependency_injection
β β β βββ news_di.go
β β βββ dto
β β βββ handler
β β βββ model
β β βββ repository
β β βββ service
β βββ user
β βββ dependency_injection
β βββ dto
β βββ handler
β βββ model
β βββ repository
β βββ service
βββ kubernetes
β βββ final.yaml
βββ pkg
βββ form-validation
βββ id-generate
βββ response
π File penting:
deployment/Dockerfileβ Docker build aplikasi Go.db/init.sqlβ Script untuk inisialisasi database PostgreSQL.kubernetes/final.yamlβ Manifests Kubernetes lengkap.
2οΈβ£ Build dan Push Docker Image
Sebelum masuk ke Kubernetes, kita perlu membuat Docker image:
docker build -t masdika/go-api:latest -f deployment/Dockerfile .
docker push masdika/go-api:latest
Image masdika/go-api:latest sekarang sudah siap dipakai di Kubernetes. π³
3οΈβ£ File Kubernetes Lengkap (final.yaml)
Berikut file final.yaml yang berisi semua resource Kubernetes: namespace, PostgreSQL dengan PVC, Go API Deployment, Service, Ingress (SSL), hingga HPA.
# =============================
# Namespace
# =============================
apiVersion: v1
kind: Namespace
metadata:
name: go-api
---
# =============================
# PersistentVolumeClaim (PVC) untuk Postgres
# =============================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: go-api
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-path # biar jalan di K3s
---
# =============================
# Deployment PostgreSQL
# =============================
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: go-api
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:17
env:
- name: POSTGRES_DB
value: go_rest_api
- name: POSTGRES_USER
value: masdika
- name: POSTGRES_PASSWORD
value: supersecretpassword
ports:
- containerPort: 5432
resources: # batasi resource
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
- name: init-sql
mountPath: /docker-entrypoint-initdb.d
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
- name: init-sql
hostPath:
path: /root/go-rest-api-template/db # auto-import init.sql
---
# =============================
# Service PostgreSQL
# =============================
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: go-api
spec:
ports:
- port: 5432
targetPort: 5432
selector:
app: postgres
---
# =============================
# Deployment Go API
# =============================
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-api
namespace: go-api
spec:
replicas: 1
selector:
matchLabels:
app: go-api
template:
metadata:
labels:
app: go-api
spec:
initContainers:
- name: wait-for-postgres
image: busybox
command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for postgres; sleep 2; done;']
containers:
- name: go-api
image: masdika/go-api:latest
env:
- name: POSTGRES_HOST
value: "postgres"
- name: POSTGRES_PORT
value: "5432"
- name: POSTGRES_USER
value: "masdika"
- name: POSTGRES_PASSWORD
value: "supersecretpassword"
- name: POSTGRES_DB
value: "go_rest_api"
- name: JWT_SECRET_KEY
value: "verylongrandomjwtsecretkey"
- name: CORS_ALLOW_ORIGINS
value: "*"
ports:
- containerPort: 8080
resources: # penting buat autoscaling
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
# =============================
# Service Go API
# =============================
apiVersion: v1
kind: Service
metadata:
name: go-api
namespace: go-api
spec:
ports:
- port: 8080
targetPort: 8080
selector:
app: go-api
---
# =============================
# PodDisruptionBudget API
# =============================
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: go-api-pdb
namespace: go-api
spec:
minAvailable: 1
selector:
matchLabels:
app: go-api
---
# =============================
# PodDisruptionBudget PostgreSQL
# =============================
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: postgres-pdb
namespace: go-api
spec:
minAvailable: 1
selector:
matchLabels:
app: postgres
---
# =============================
# ClusterIssuer untuk SSL
# =============================
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-cloudflare
spec:
acme:
email: me@masdika.biz.id
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-cloudflare-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
---
# =============================
# Secret Cloudflare API Token
# =============================
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: "ZJIgWpl5UddGLyoUfs2mxNJZbgxCeo4iAItbmKfL"
---
# =============================
# Ingress untuk Go API
# =============================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: go-api-ingress
namespace: go-api
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cloudflare
spec:
ingressClassName: traefik
rules:
- host: api.syslab.my.id
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: go-api
port:
number: 8080
tls:
- hosts:
- api.syslab.my.id
secretName: go-api-tls
---
# =============================
# HPA untuk Go API
# =============================
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: go-api-hpa
namespace: go-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: go-api
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
4οΈβ£ Deploy ke K3s
Jalankan perintah berikut untuk apply seluruh konfigurasi:
kubectl apply -f kubernetes/final.yaml
Cek pod apakah sudah jalan:
kubectl get pods -n go-api

Cek service:
kubectl get svc -n go-api

5οΈβ£ Verifikasi SSL
Gunakan kubectl describe certificate untuk memastikan SSL berhasil dibuat oleh cert-manager:
kubectl describe certificate go-api-tls -n go-api

Kalau sukses, coba akses API:
curl -k https://api.syslab.my.id/api/v1/users

6οΈβ£ Autoscaling
Kita sudah menambahkan HPA (HorizontalPodAutoscaler) pada Go API. Untuk melihat scaling berjalan:
kubectl get hpa -n go-api -w

Lalu lakukan load testing supaya CPU usage naik. Jika target CPU > 70%, maka pod akan otomatis bertambah. π―
π Kesimpulan
Dalam artikel ini kita sudah membahas:
- Build Go REST API dengan Docker
- Push image ke Docker Hub
- Deploy PostgreSQL + Go API ke Kubernetes (K3s)
- Tambah SSL dari Letβs Encrypt via Cloudflare DNS Challenge
- Terapkan PodDisruptionBudget untuk HA minimal
- Aktifkan autoscaling dengan HPA
Dengan setup ini, aplikasi Go REST API kamu sekarang production-ready: scalable, secure (HTTPS), dan resilient. π
Pendahuluan Bayangkan sebuah cluster Kubernetes yang kemarin masih berjalan normal. Semua service aktif, storage Longhorn stabil, dan aplikasi berjalan tanpa…
Panduan ini menjelaskan instalasi Jenkins production menggunakan Docker + Traefik (SSL otomatis via DNS Challenge Cloudflare), dengan fokus utama pada…