Tampilan
Kenapa penting
Menulis Deployment dan ConfigMap baru separuh jalan; pekerjaan sehari-hari di tim backend adalah mengirim versi baru ke cluster dengan aman — tanpa downtime, bisa dibatalkan dalam hitungan detik, dan konsisten antara environment dev, staging, dan production. Di bab ini kamu menjalankan seluruh siklus itu di laptop dengan kind (Kubernetes in Docker): build image, memuatnya ke cluster, membuka akses lewat Ingress, melakukan rolling update dan rollback, lalu mengenal alat yang dipakai tim nyata untuk mengelola manifest (Helm, Kustomize, GitOps) serta cara menjalankan migrasi database saat deploy.
Konsep inti
Alur deploy ke kind
kind menjalankan node Kubernetes sebagai container Docker. Artinya image yang kamu build di Docker lokal belum terlihat oleh node; kamu harus memuatnya dengan kind load docker-image. Alurnya:
bash
# 1. Buat cluster (sekali saja); config dengan port mapping untuk Ingress, lihat bagian berikutnya
kind create cluster --name jalur --config kind-config.yaml
# 2. Build image aplikasi Spring Boot (Dockerfile multi-stage atau Buildpacks)
./mvnw -q package -DskipTests
docker build -t order-service:1.0.0 .
# alternatif tanpa Dockerfile: ./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=order-service:1.0.0
# 3. Muat image ke node kind
kind load docker-image order-service:1.0.0 --name jalur
# 4. Apply manifest (Deployment, Service, ConfigMap dari bab sebelumnya)
kubectl apply -f k8s/
kubectl rollout status deployment/order-service
kubectl get pods -l app=order-serviceSelalu pakai tag versi (1.0.0, atau SHA commit), bukan latest. Untuk tag latest, default imagePullPolicy adalah Always, sehingga kubelet mengabaikan image yang sudah dimuat dan mencoba menarik dari registry — hasilnya ErrImagePull. Tag versi juga membuat rollback bermakna: kamu tahu persis versi mana yang sedang berjalan.
Ingress: pintu HTTP dari luar cluster
Service bertipe ClusterIP hanya bisa diakses dari dalam cluster. Ingress adalah aturan routing HTTP (host/path → Service), dan perlu Ingress controller yang benar-benar memproses lalu lintas. Untuk kind, cluster dibuat dengan port mapping agar port 80 laptop diteruskan ke node:
yaml
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443Setelah controller terpasang (ikuti panduan Ingress di dokumentasi kind), definisikan aturannya:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
spec:
ingressClassName: nginx
rules:
- host: shop.localtest.me
http:
paths:
- path: /api/orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
- path: /api/products
pathType: Prefix
backend:
service:
name: catalog-service
port:
number: 8080curl http://shop.localtest.me/api/orders kini sampai ke order-service. Satu Ingress bisa melayani banyak service, menangani TLS, dan mengganti kebutuhan satu LoadBalancer per service.
Ingress dan Gateway API
API Ingress stabil dan masih umum dipakai, tetapi Kubernetes kini mengarahkan routing baru ke Gateway API (Gateway, HTTPRoute) yang lebih ekspresif. Proyek controller ingress-nginx juga sudah diumumkan pensiun. Untuk belajar konsep, Ingress cukup; di proyek baru, cek controller dan API yang dipakai tim.
Rolling update dan rollback
Mengubah image di Deployment memicu rolling update: ReplicaSet baru dibuat, pod baru dinaikkan bertahap, pod lama diturunkan setelah pod baru Ready. Kecepatannya diatur maxSurge (berapa pod ekstra boleh ada) dan maxUnavailable (berapa pod boleh tidak siap).
yaml
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # kapasitas tidak pernah turun selama rolloutbash
docker build -t order-service:1.1.0 . && kind load docker-image order-service:1.1.0 --name jalur
kubectl set image deployment/order-service app=order-service:1.1.0
kubectl rollout status deployment/order-service # tunggu sampai selesai / gagal
kubectl rollout history deployment/order-service # daftar revisi
kubectl rollout undo deployment/order-service # kembali ke revisi sebelumnya
kubectl rollout undo deployment/order-service --to-revision=2Rollout hanya aman bila readiness probe jujur: pod baru yang belum siap tidak menerima trafik, dan rollout berhenti alih-alih mengganti semua pod dengan versi rusak. kubectl set image praktis untuk latihan, tetapi di tim nyata perubahan tag sebaiknya lewat manifest di Git agar tercatat.
Helm: paket manifest dengan template
Helm membungkus manifest menjadi chart: folder templates/ berisi YAML bertemplate (sintaks Go template), values.yaml berisi nilai default, dan Chart.yaml berisi metadata. Satu chart bisa dipasang berkali-kali dengan nilai berbeda.
yaml
# templates/deployment.yaml (potongan)
spec:
replicas: { { .Values.replicaCount } }
template:
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"bash
helm upgrade --install order-service ./charts/order-service \
-n shop --create-namespace -f values-staging.yaml --set image.tag=1.1.0
helm history order-service -n shop
helm rollback order-service 1 -n shopHelm unggul untuk mendistribusikan software (memasang PostgreSQL, Kafka, atau Prometheus dari chart komunitas). Kelemahannya: template YAML mudah jadi sulit dibaca bila terlalu banyak logika.
Kustomize: base + overlay tanpa template
Kustomize (bawaan kubectl apply -k) mengambil manifest polos sebagai base, lalu tiap environment punya overlay yang menambal field tertentu.
text
k8s/
base/ deployment.yaml, service.yaml, kustomization.yaml
overlays/
staging/ kustomization.yaml (replicas 1)
prod/ kustomization.yaml (replicas 3, tag versi rilis)yaml
# k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop-prod
resources:
- ../../base
images:
- name: order-service
newTag: 1.4.0
patches:
- target:
kind: Deployment
name: order-service
patch: |-
- op: replace
path: /spec/replicas
value: 3kubectl kustomize k8s/overlays/prod menampilkan hasil render tanpa meng-apply — biasakan memeriksanya dulu.
GitOps: Git sebagai sumber kebenaran
Dengan GitOps, pipeline CI hanya membangun image dan memperbarui tag di repo manifest. Controller di dalam cluster (Argo CD atau Flux) terus membandingkan state cluster dengan Git dan menyamakannya. Keuntungannya: riwayat deploy = riwayat commit, rollback = git revert, dan tidak ada kredensial cluster di CI.
yaml
# Application Argo CD (potongan)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/contoh/shop-manifests.git
targetRevision: main
path: k8s/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: shop-prod
syncPolicy:
automated:
prune: true # hapus resource yang sudah tidak ada di Git
selfHeal: true # kembalikan perubahan manual ke state GitDatabase: managed vs StatefulSet
Aplikasi stateless cocok untuk Deployment; database stateful tidak. Pilihannya:
| Opsi | Kelebihan | Kekurangan |
|---|---|---|
| Managed (Cloud SQL, RDS, Azure Database) | backup, patch, failover, replika diurus provider | biaya, sedikit kontrol, terikat vendor |
StatefulSet + PersistentVolume (atau operator) | jalan di mana saja, cocok untuk dev/kind | backup, upgrade, failover jadi tanggung jawabmu |
StatefulSet memberi identitas pod stabil (postgres-0) dan volume per pod yang tidak hilang saat pod di-restart. Untuk production, kebanyakan tim memilih database managed atau operator matang (mis. CloudNativePG); di kind, StatefulSet satu replika sudah cukup untuk latihan.
Migrasi Flyway saat deploy
Secara default Spring Boot menjalankan Flyway saat startup. Dengan banyak replika, semua pod mencoba migrasi bersamaan; Flyway memakai lock sehingga hanya satu yang jalan, tetapi pod lain menunggu dan migrasi panjang bisa memicu probe me-restart pod. Dua pola yang lebih terkendali:
- Init container — migrasi berjalan sebelum container aplikasi start, di setiap pod (tetap dikunci Flyway).
- Job terpisah — satu
Jobmigrasi dijalankan sebelum Deployment diperbarui (di Helm lewat hookpre-upgrade, di Argo CD lewat sync hook/wave). Aplikasi menonaktifkan Flyway denganspring.flyway.enabled=false.
yaml
apiVersion: batch/v1
kind: Job
metadata:
name: order-db-migrate-1-4-0
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: flyway
image: flyway/flyway:11
args: ["migrate"]
env:
- name: FLYWAY_URL
value: jdbc:postgresql://postgres:5432/orders
- name: FLYWAY_USER
valueFrom:
secretKeyRef: { name: order-db, key: username }
- name: FLYWAY_PASSWORD
valueFrom:
secretKeyRef: { name: order-db, key: password }
volumeMounts:
- name: sql
mountPath: /flyway/sql
volumes:
- name: sql
configMap:
name: order-migrationsApa pun polanya, migrasi harus kompatibel ke belakang (expand–contract): selama rolling update, pod versi lama dan baru berjalan bersamaan di atas skema yang sama. Menghapus atau mengganti nama kolom dilakukan di rilis berikutnya, setelah tidak ada lagi kode yang memakainya.
Contoh di dunia backend
Tim e-commerce merilis order-service 1.4.0 yang menambah kolom discount_code. Urutan rilis yang aman:
bash
# CI: build & tag dengan versi rilis
docker build -t registry.example.com/shop/order-service:1.4.0 .
docker push registry.example.com/shop/order-service:1.4.0
# CI: perbarui tag di repo manifest (Kustomize overlay prod), lalu commit
cd shop-manifests/k8s/overlays/prod
kustomize edit set image order-service=registry.example.com/shop/order-service:1.4.0
git commit -am "order-service 1.4.0" && git push
# Argo CD: sync wave 0 menjalankan Job migrasi (ADD COLUMN discount_code, nullable)
# sync wave 1 memperbarui Deployment -> rolling update maxSurge 1, maxUnavailable 0Lima menit kemudian dashboard menunjukkan error 500 naik. Karena migrasinya hanya menambah kolom nullable, rollback aman: git revert commit tag (atau kubectl rollout undo sebagai langkah darurat, lalu samakan Git agar Argo CD tidak mengembalikannya). Versi 1.3.x tetap berjalan normal di skema baru karena kolom tambahan diabaikan.
Pertanyaan interview
1. Apa yang terjadi di balik kubectl rollout undo?
Deployment menyimpan ReplicaSet dari revisi sebelumnya (jumlahnya diatur revisionHistoryLimit). rollout undo mengembalikan template pod ke revisi tersebut, lalu controller melakukan rolling update biasa ke arah sebaliknya: pod lama dinaikkan lagi, pod versi bermasalah diturunkan. Tidak ada downtime bila readiness probe dan maxUnavailable benar. Rollback ini hanya menyangkut pod — skema database tidak ikut kembali.
2. Kapan memilih Helm dan kapan Kustomize?
Helm cocok untuk paket yang didistribusikan dan dikonfigurasi banyak pihak (chart PostgreSQL, Kafka), punya versi chart dan riwayat rilis sendiri. Kustomize cocok untuk manifest internal tim: YAML tetap polos dan mudah dibaca, perbedaan antar environment berupa patch kecil. Banyak tim memakai keduanya — chart komunitas via Helm, aplikasi sendiri via Kustomize — dan menyerahkan apply ke Argo CD atau Flux.
3. Bagaimana menjalankan migrasi database dengan aman di Kubernetes?
Pisahkan migrasi dari startup aplikasi (Job atau hook sebelum Deployment diperbarui) agar tidak bergantung pada probe dan tidak membuat semua pod menunggu lock. Tulis migrasi kompatibel ke belakang dengan pola expand–contract karena versi lama dan baru berjalan bersamaan selama rolling update. Kredensial diambil dari Secret, dan Job dibuat idempoten — Flyway sendiri melewati migrasi yang sudah tercatat.
Jebakan umum
- Memakai tag
latestdi kind atau production —ErrImagePulldi kind, dan kamu tidak tahu versi mana yang sedang berjalan. - Lupa
kind load docker-imagesetelah build ulang; pod tetap memakai image lama atau gagal menarik image. - Membuat Ingress tanpa memasang Ingress controller — resource tercipta tapi tidak ada yang melayani trafik.
- Rolling update tanpa readiness probe: pod dianggap siap seketika, versi rusak menggantikan semua pod lama.
- Migrasi yang menghapus/mengganti nama kolom dalam satu rilis, sehingga pod versi lama langsung error dan rollback tidak lagi aman.
kubectl editlangsung di cluster yang dikelola GitOps — perubahan hilang dikembalikan oleh self-heal.
Ringkasan
- Di kind: build image bertag versi →
kind load docker-image→kubectl apply→kubectl rollout status. - Ingress merutekan HTTP berdasarkan host/path ke Service dan butuh controller; Gateway API adalah arah penerusnya.
- Rolling update diatur
maxSurge/maxUnavailabledan bergantung pada readiness probe;kubectl rollout undountuk rollback cepat. - Helm = chart bertemplate + values; Kustomize = base + overlay tanpa template; GitOps (Argo CD/Flux) menyamakan cluster dengan Git terus-menerus.
- Database production umumnya managed;
StatefulSetmemberi identitas dan volume stabil untuk self-hosted. - Jalankan Flyway lewat Job/init container dan tulis migrasi expand–contract agar rollback tetap aman.