1 — Kondisi saat ini (as-is)
HTTPS
NodePort 30625 (https) / 32144 (http)
routing per Host()
http://api:3000
DATABASE_URL
http://seaweedfs:8333
Users
Browser / dashboard
Host publik 187.77.112.39
2 VM Ubuntu 24.04 · 2 vCPU / 8 GB / node
wg0 WireGuard 10.10.10.0/24 (IP node)
Service traefik LoadBalancer masih EXTERNAL-IP <pending>
KUBERNETES CLUSTER — kubeadm v1.36.4 · containerd 2.2.1 · pod 192.168.0.0/16 · svc 10.96.0.0/12 · usia 36d
ingress layer — namespace traefik-system · pod berjalan di k8s-worker
Traefik v3.7.12
• entrypoints: web:8000 → redirect :443
• websecure:8443 · metrics:9100
• ACME Let's Encrypt (certResolver: le)
• Helm release v2 · PVC /data/acme.json
IngressRoute (CRD, 4 rute)
• development-api.kmtxpumit.my.id
• development.kmtxpumit.my.id
• development-ops / -vendor .kmtxpumit.my.id
• provider: kubernetescrd · IngressClass default
Middleware & transport
• cors-kmtxpumit (whitelist 3 origin)
• timeout-kmtxpumit (ServerTransport)
• TLS terminate di ingress (ACME)
• hanya rute api yang pakai CORS
k8s-master · control-plane · taint NoSchedule
Control plane (static pod)
• kube-apiserver :6443
• etcd (single node, /var/lib/etcd)
• kube-controller-manager
• kube-scheduler
• authZ Node,RBAC · NodeRestriction
• tanpa audit log · tanpa enkripsi at-rest
Node agent
• kubelet (webhook authZ, rotateCerts)
• containerd 2.2.1 · cgroup systemd
• kube-proxy (mode iptables)
• calico-node + csi-node-driver
• staticPodPath /etc/kubernetes/manifests
Titik kegagalan tunggal
• 1 control plane · 1 etcd lokal
• tanpa snapshot etcd terjadwal
• tidak menjalankan workload aplikasi
• master mati = klaster tidak bisa diubah
(pod yang sudah jalan tetap hidup)
k8s-worker · SEMUA workload aplikasi ada di sini
marketplace
svc :80 → :8080
image …-marketplace:development
ops
svc :80 → :8080
image …-ops:development
vendor
svc :80 → :8080
image …-vendor:development
api
svc :3000 · liveness + readiness
PVC artifacts 2Gi → /data
worker (queue/async)
tanpa probe · PVC artifacts 2Gi
DATABASE_URL + SERVICE_TOKEN
postgres-0
StatefulSet · 16-alpine
headless svc :5432 · 5Gi
seaweedfs-0
StatefulSet · 3.68 (S3)
headless svc :8333 · 5Gi
hermes-agent
nousresearch v2026.9.7
SA-nya = cluster-admin ⚠
PVC local-path (RWO, reclaim Delete): pgdata 5Gi · seaweed-data 5Gi · artifacts 2Gi · hermes-agent-data 2Gi
ConfigMap kmtxpumit-config (21 key) · Secret kmtxpumit-secrets · imagePullSecret ghcr-pull
http://api:3000 (INTERNAL_API_URL) · http://seaweedfs:8333 (STORAGE_ENDPOINT)
namespace kmtxpumit-development — semua Deployment/StatefulSet replicas: 1 · tanpa HPA / PDB / NetworkPolicy / quota
pod aplikasi tanpa securityContext (jalan sebagai root) · AUTH_LUAR_DEV=1 · tag image :development
platform & addons — cluster-wide (Calico dikelola Tigera Operator, bukan manifest statis)
Calico CNI v3.32.1
• VXLAN encap · iptables dataplane
• IP pool 192.168.0.0/16 (block /26)
• calico-typha 1 replika + PDB
• 0 NetworkPolicy (jaringan datar)
CoreDNS 1.14.2
• 2 replika · podAntiAffinity
• cache 30 · prometheus :9153
• readOnlyRootFilesystem ✓
• keduanya di k8s-worker
local-path-provisioner
• StorageClass local-path (default)
• WaitForFirstConsumer · Delete
• path node /opt/local-path-provisioner
• disk lokal node, bukan shared
kube-proxy · CSI · operator
• kube-proxy DaemonSet (iptables)
• csi-node-driver (Calico CSI)
• tigera-operator v1.42.3
• tanpa metrics-server
Let's Encrypt
ACME HTTP-01
Google OAuth
client secret (login eksternal)
GHCR
ghcr.io/pt-sagung-…
Legend
Frontend / ingress
Backend / service
Database / storage
Platform / infra node
Eksternal / tooling
Alur keluar (ACME, OAuth, pull image)
Batas klaster / node
Batas namespace aplikasi
2 — Target yang disarankan (to-be): GitOps + IaC + observability
push/PR
trigger
push
watch
commit manifest (image digest) → Git, bukan kubectl apply
sync / reconcile
bootstrap node
inventory
LANE A — aplikasi: source → CI → registry → CD (GitOps, tanpa kubectl manual)
Developer
• branch + pull request
• review, bukan apply
• commit = satu-satunya
cara mengubah produksi
Git repo (GitHub)
• main = kode aplikasi
• /deploy = manifest K8s
• Kustomize overlay dev/prod
• branch protection + CODEOWNERS
GitHub Actions
• lint · unit test · build
• Trivy scan (fail on HIGH)
• SBOM + cosign sign
• push GHCR by digest
GHCR
• tag immutable + digest
• ganti :development
• retention policy
• pull secret per namespace
Argo CD
• watch repo /deploy (auto-sync)
• deteksi drift + rollback
• ApplicationSet per environment
• Argo Rollouts (canary/blue-green)
LANE B — infrastruktur sebagai kode (dari "setup manual" ke reproducibility)
Terraform
• provision VM/VPS, volume, snapshot
• WireGuard wg0 + firewall rule
• DNS record (api / ops / vendor / dev)
• remote state (S3/MinIO seaweedfs)
• tls/ssh key material, bukan manual
Ansible
• kubeadm init / join (idempoten)
• containerd, cgroups, kernel module
• harden node (sysctl, firewall, user)
• install Calico / Traefik / storage
• jadi "runbook hidup" yang bisa diuji ulang
LANE C — observability & scaling
metrik, log, alert
• metrics-server → kubectl top + HPA
• kube-prometheus-stack + Grafana
• Loki/Vector untuk log container
• alertmanager → Telegram/Slack
• dashboard SLO: error rate, latency
TARGET CLUSTER — perbaikan wajib sebelum menambah lapisan baru
• backup etcd terjadwal (CronJob snapshot → object storage) + uji restore — ini yang paling mendesak
• RBAC: SA hermes-agent diturunkan dari cluster-admin ke Role spesifik · PSA restricted + NetworkPolicy default-deny
• replicas ≥ 2 + PDB + podAntiAffinity · secret terenkripsi at-rest · audit log apiserver aktif
• secret dari Sealed Secrets / External Secrets + SOPS (bukan file .env atau kubectl create secret manual)
• opsional: 3 control plane HA + HAProxy/VIP, MetalLB untuk menggantikan LoadBalancer <pending>
Komponen baru / diubah
Infrastruktur sebagai kode
Observability
Umpan balik GitOps (manifest, bukan kubectl)
• RBAC + Node authorization, admission NodeRestriction
• kubelet: anonymous auth off, authZ Webhook, rotateCertificates
• etcd TLS penuh + client/peer cert auth
• Calico dikelola operator (bukan manifest statis), typha punya PDB
• CoreDNS 2 replika + anti-affinity, readOnlyRootFilesystem
• ConfigMap untuk config, Secret untuk kredensial (tidak hardcoded)
• TLS otomatis Let's Encrypt + CORS middleware
• CoreDNS & Traefik sudah drop ALL capability + seccomp
• SA hermes-agent di-bind ke cluster-admin : satu pod = seluruh klaster
• 0 NetworkPolicy: semua pod bisa saling akses (network datar)
• Namespace tanpa label Pod Security Admission (efektif privileged)
• Pod aplikasi tanpa securityContext → jalan sebagai root
• Tanpa audit log apiserver: tidak ada jejak siapa mengubah apa
• Secret tidak terenkripsi at-rest di etcd
• AUTH_LUAR_DEV=1 + tag :development di domain publik
• 1 control plane + 1 etcd lokal, tanpa snapshot terjadwal
• Semua workload di satu node (k8s-worker)
• Semua Deployment/StatefulSet replicas: 1, tanpa PDB
• Tanpa HPA, tanpa metrics-server → tidak bisa scaling otomatis
• Traefik LoadBalancer <pending> → bergantung NodePort
• worker: liveness/readiness dan seaweedfs: tanpa probe
• Storage node-local (RWO) → pod tidak bisa pindah node bebas
• Users → host :443/:80 → NodePort 30625/32144
• Traefik → IngressRoute (4 Host rules) → Service → Pod
• api dipanggil internal via http://api:3000
• artefak disimpan ke S3 http://seaweedfs:8333 (bucket kmtxpumit)
• Postgres & SeaweedFS headless service, 1 replika masing-masing
• Login eksternal Google OAuth (client secret di Secret)
• Postgres 16 single instance, tanpa replika/backup terjadwal
• SeaweedFS 1 node (S3) — artefak & bucket kmtxpumit
• PVC local-path: hilang jika node/disk rusak (reclaim Delete)
• Tanpa Velero / pg_dump CronJob / snapshot etcd
• Uji restore belum pernah dilakukan → backup tanpa uji = asumsi
• Fase 0 — backup & restore etcd + Postgres (prasyarat semua fase lain)
• Fase 1 — Git + GitHub Actions: build, test, Trivy, push GHCR (digest)
• Fase 2 — Argo CD: manifest di Git, sync otomatis, hapus kebiasaan kubectl apply
• Fase 3 — Ansible (bootstrap node) lalu Terraform (VM, wg0, DNS, state)
• Fase 4 — observability, NetworkPolicy, PSA, RBAC diperkecil
• Fase 5 — HA control plane, managed DB, Velero, MetalLB