Di episode ini kita akan coba bahas Kubernetes Volume untuk persistent data storage. Kita akan mempelajari different volume type, cara mount volume di Pod, dan best practice untuk data persistence.

Catatan
Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 20 di bawah ini
Di episode sebelumnya kita sudah belajar tentang Multi-Container Pod dan design pattern untuk running multiple container together. Selanjutnya di episode 21 kali ini, kita akan coba bahas Volume, mekanisme untuk persisting data di Kubernetes.
Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.
By default, container filesystem adalah ephemeral - ketika container restart, semua data hilang. Volume solve problem ini dengan menyediakan persistent storage yang survive container restart dan bisa shared antara container di Pod.
Volume adalah directory yang accessible ke container di Pod. Tidak seperti ephemeral container filesystem, volume persist data beyond container restart dan bisa shared antara multiple container.
Bayangkan volume seperti external hard drive - sementara internal storage komputer kalian di wipe ketika reinstall OS, external drive keep data kalian safe. Similarly, volume preserve data ketika container restart atau crash.
Karakteristik kunci Volume:
Volume solve beberapa critical storage challenge:
Tanpa volume, kalian akan lose semua data setiap kali container restart, making it impossible run stateful application seperti database.
Memahami volume lifecycle sangat crucial:
Kebanyakan volume tied ke Pod lifecycle:
Beberapa volume persist beyond Pod lifecycle:
Kubernetes support banyak volume type untuk different use case.
Temporary storage yang exist selama Pod exist.
Use case:
Contoh:
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
containers:
- name: writer
image: busybox:1.36
command:
- sh
- -c
- while true; do date >> /data/log.txt; sleep 5; done
volumeMounts:
- name: shared-data
mountPath: /data
- name: reader
image: busybox:1.36
command:
- sh
- -c
- tail -f /data/log.txt
volumeMounts:
- name: shared-data
mountPath: /data
volumes:
- name: shared-data
emptyDir: {}emptyDir dengan memory:
volumes:
- name: cache
emptyDir:
medium: Memory
sizeLimit: 128MiIni create tmpfs (RAM-backed filesystem) untuk high-performance temporary storage.
Mount file atau directory dari host node filesystem.
Use case:
Warning
Warning: hostPath volume tidak portable across node dan pose security risk. Gunakan hanya ketika necessary.
Contoh:
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: host-data
mountPath: /usr/share/nginx/html
volumes:
- name: host-data
hostPath:
path: /data/web
type: DirectoryOrCreatehostPath type:
DirectoryOrCreate - Create directory jika tidak existDirectory - Harus existing directoryFileOrCreate - Create file jika tidak existFile - Harus existing fileSocket - Harus existing Unix socketCharDevice - Harus existing character deviceBlockDevice - Harus existing block deviceMount ConfigMap data sebagai file di container.
Use case:
Contoh:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
app.conf: |
server {
listen 80;
server_name localhost;
}
database.conf: |
host=db.example.com
port=5432
---
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: config
mountPath: /etc/config
volumes:
- name: config
configMap:
name: app-configKonfigurasi tersebut akan membuat file app.conf dan database.conf di dalam container pada path /etc/config. Untuk melihat nya kalian bisa masuk ke dalam container dengan perintah sudo kubectl exec -it configmap-pod -- sh dan melihat file tersebut dengan perintah cat /etc/config/app.conf dan cat /etc/config/database.conf.
Mount specific key:
volumes:
- name: config
configMap:
name: app-config
items:
- key: app.conf
path: nginx.confMount Secret data sebagai file di container.
Use case:
Contoh:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: admin
password: secretpassword
---
apiVersion: v1
kind: Pod
metadata:
name: secret-pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: credentials
mountPath: /etc/secrets
readOnly: true
volumes:
- name: credentials
secret:
secretName: db-credentialsFile created:
/etc/secrets/username (contains "admin")/etc/secrets/password (contains "secretpassword")Reference PersistentVolumeClaim untuk durable storage.
Use case:
Contoh:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: app
image: postgres:15
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvcKita akan cover PersistentVolume dan PersistentVolumeClaim secara detail di episode selanjutnya.
Expose Pod metadata sebagai file.
Use case:
Contoh:
apiVersion: v1
kind: Pod
metadata:
name: downwardapi-pod
labels:
app: myapp
version: v1.0
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- while true; do cat /etc/podinfo/*; sleep 10; done
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: name
fieldRef:
fieldPath: metadata.name
- path: namespace
fieldRef:
fieldPath: metadata.namespace
- path: labels
fieldRef:
fieldPath: metadata.labelsCombine multiple volume source ke single directory.
Use case:
Contoh:
apiVersion: v1
kind: Pod
metadata:
name: projected-pod
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: all-config
mountPath: /etc/config
volumes:
- name: all-config
projected:
sources:
- configMap:
name: app-config
- secret:
name: db-credentials
- downwardAPI:
items:
- path: pod-name
fieldRef:
fieldPath: metadata.nameCustomize bagaimana volume di mount di container.
Prevent container dari modifying volume data:
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: trueMount specific file atau subdirectory dari volume:
apiVersion: v1
kind: Pod
metadata:
name: subpath-pod
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-configIni mount hanya file nginx.conf, bukan entire ConfigMap.
Gunakan environment variable di subPath:
volumeMounts:
- name: data
mountPath: /var/data
subPathExpr: $(POD_NAME)
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.nameapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-content-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
volumeMounts:
- name: content
mountPath: /usr/share/nginx/html
volumes:
- name: content
persistentVolumeClaim:
claimName: web-content-pvcapiVersion: v1
kind: Pod
metadata:
name: multi-volume-app
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
# Application config dari ConfigMap
- name: config
mountPath: /etc/app/config
readOnly: true
# Database credential dari Secret
- name: secrets
mountPath: /etc/app/secrets
readOnly: true
# Persistent data storage
- name: data
mountPath: /var/lib/app
# Temporary cache
- name: cache
mountPath: /tmp/cache
# Log shared dengan sidecar
- name: logs
mountPath: /var/log/app
- name: log-shipper
image: fluent/fluentd:v1.16
volumeMounts:
- name: logs
mountPath: /var/log/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config
- name: secrets
secret:
secretName: app-secrets
- name: data
persistentVolumeClaim:
claimName: app-data-pvc
- name: cache
emptyDir:
medium: Memory
sizeLimit: 256Mi
- name: logs
emptyDir: {}apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-pvcapiVersion: v1
kind: Pod
metadata:
name: init-volume-pod
spec:
initContainers:
- name: setup
image: busybox:1.36
command:
- sh
- -c
- |
echo "Downloading configuration..."
wget -O /config/app.conf https://config-server/app.conf
echo "Setup complete"
volumeMounts:
- name: config
mountPath: /config
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config
mountPath: /etc/app
volumes:
- name: config
emptyDir: {}Different volume support different access mode:
Volume bisa di mount read-write oleh single node:
accessModes:
- ReadWriteOncePaling common untuk block storage (AWS EBS, GCE PD).
Volume bisa di mount read-only oleh many node:
accessModes:
- ReadOnlyManyUseful untuk shared configuration atau static content.
Volume bisa di mount read-write oleh many node:
accessModes:
- ReadWriteManyRequire network filesystem (NFS, CephFS, GlusterFS).
Volume bisa di mount read-write oleh single Pod:
accessModes:
- ReadWriteOncePodKubernetes 1.22+ feature untuk strict single-Pod access.
Problem: Data lost ketika Pod deleted.
Solusi: Gunakan PersistentVolumeClaim untuk data yang harus survive Pod deletion:
# Buruk: emptyDir untuk database
volumes:
- name: data
emptyDir: {}
# Bagus: PVC untuk database
volumes:
- name: data
persistentVolumeClaim:
claimName: db-pvcProblem: Container bisa modify sensitive data.
Solusi: Selalu mount secret sebagai read-only:
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: trueProblem: Volume tidak bisa mounted di multiple node.
Solusi: Gunakan ReadWriteMany untuk multi-node access:
accessModes:
- ReadWriteMany # Untuk shared accessProblem: emptyDir bisa fill up node disk.
Solusi: Set sizeLimit untuk emptyDir:
emptyDir:
sizeLimit: 1GiProblem: Tidak portable, security risk, node dependency.
Solusi: Gunakan PersistentVolume instead:
# Hindari di production
hostPath:
path: /data
# Gunakan instead
persistentVolumeClaim:
claimName: data-pvcPilih right volume type untuk use case kalian:
# Temporary data
emptyDir: {}
# Configuration
configMap:
name: app-config
# Sensitive data
secret:
secretName: credentials
# Persistent data
persistentVolumeClaim:
claimName: data-pvcSelalu limit emptyDir size:
emptyDir:
sizeLimit: 500MiMount volume sebagai read-only ketika possible:
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: trueGroup related volume logically:
volumeMounts:
# Configuration volume
- name: app-config
mountPath: /etc/app
- name: nginx-config
mountPath: /etc/nginx
# Data volume
- name: data
mountPath: /var/lib/app
# Temporary volume
- name: cache
mountPath: /tmp/cacheSubPath bisa cause issue dengan update:
# ConfigMap update tidak reflect dengan subPath
volumeMounts:
- name: config
mountPath: /etc/app/config.yml
subPath: config.yml # Block updateMount entire volume ketika possible.
Add comment explaining volume usage:
volumes:
# Application configuration file
- name: config
configMap:
name: app-config
# Database credential
- name: secrets
secret:
secretName: db-credentials
# Persistent application data
- name: data
persistentVolumeClaim:
claimName: app-data-pvcsudo kubectl get pod <pod-name> -o jsonpath='{.spec.volumes[*].name}'sudo kubectl describe pod <pod-name>Show semua volume dan mount mereka.
sudo kubectl exec <pod-name> -- df -hsudo kubectl exec <pod-name> -- ls -la /path/to/mountCheck Pod event:
sudo kubectl describe pod <pod-name>Look for mount error di event.
Check volume permission dan security context:
securityContext:
fsGroup: 1000
runAsUser: 1000Verify resource exist:
sudo kubectl get configmap <name>
sudo kubectl get secret <name>Check disk usage:
sudo kubectl exec <pod-name> -- df -hIncrease sizeLimit atau clean up data.
Pada episode 21 ini, kita telah membahas Volume di Kubernetes secara mendalam. Kita sudah belajar tentang different volume type, cara mount mereka di Pod, dan best practice untuk data persistence.
Key takeaway:
Volume essential untuk running stateful application di Kubernetes. Dengan memahami different volume type dan use case mereka, kalian bisa design robust storage solution untuk application kalian.
Bagaimana, makin jelas kan tentang Volume di Kubernetes? Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!
Catatan
Untuk kalian yang ingin melanjutkan ke episode 21.1 di mana kita membahas secara mendalam PersistentVolume dan PersistentVolumeClaim, bisa click thumbnail Episode 21.1 di bawah ini