기본적으로 container는 stateless application에 적합합니다. stateful한 database를 container로 사용하려면 데이터를 어떻게 저장하는지가 관건인데 kubernetes에서는 PV(persistent volumes)를 통해 방법을 제시합니다.
Pod의 spec.volumes[].persistentVolumeClaime
에서 pvc를 지정합니다.
PVC, PV를 용량, rw여부 등에 따라 설정을 합니다.
어떤 Storage Class를 사용하느냐에 따라서 kubernetes cluster 내에 addon으로 추가 설치가 필요할 수도 있습니다.
GKE 의 경우에는 gcp의 pd에 대해서 GKE 버전 1.18.10-gke.2100 이상 또는 1.19.3-gke.2100 이상 부터는 기본 사용 설정돼있습니다.
설정 예시
클러스터 생성 시
gcloud container clusters create CLUSTER-NAME \
--addons=GcePersistentDiskCsiDriver \
--cluster-version=VERSION
기존 클러스터에서
gcloud container clusters update CLUSTER-NAME \
--update-addons=GcePersistentDiskCsiDriver=ENABLED
disable
gcloud container clusters update CLUSTER-NAME \
--update-addons=GcePersistentDiskCsiDriver=ENABLED
kind: Pod
apiVersion: v1
metadata:
name: POD_NAME
spec:
volumes:
- name: VOLUME_NAME
persistentVolumeClaim:
claimName: PV_CLAIM_NAME
containers:
- name: CONTAINER_NAME
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: VOLUME_NAME
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: PV_NAME
spec:
storageClassName: "STORAGE_CLASS_NAME"
capacity:
storage: DISK_SIZE
accessModes:
- ReadWriteOnce
claimRef:
namespace: default
name: PV_CLAIM_NAME
csi:
driver: pd.csi.storage.gke.io
volumeHandle: DISK_ID
fsType: FS_TYPE
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: default
name: PV_CLAIM_NAME
spec:
storageClassName: "STORAGE_CLASS_NAME"
volumeName: PV_NAME
accessModes:
- ReadWriteOnce
resources:
requests:
storage: DISK_SIZE
apiVersion: v1
kind: Pod
metadata:
name: web-server
spec:
containers:
- name: web-server
image: nginx
volumeMounts:
- mountPath: /var/lib/www/html
name: mypvc
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: podpvc
readOnly: false
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: podpvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard-rwo
resources:
requests:
storage: 6Gi
이렇게 생성하면
pv 까지 자동으로 생성됩니다.
컴퓨팅 > 스토리지에 가보면 pd가 생성된걸 볼 수 있습니다.
pvc 를 삭제하면 pd까지 삭제되는걸 확인할 수 있습니다.
참고
pv로 생성된 pd의 스냅샷을 생성하면 kubectl delete 명령어로 삭제를 해도 pv는 삭제되지 않습니다.
desc = Failed to delete disk: googleapi: Error 400: The disk resource 'projects/hivelab-prj-1/zones/asia-northeast3-b/disks/pvc-bd54ff2c-f5bd-482f-98cf-498b05044018' is already being used by 'projects/hivelab-prj-1/zones/asia-northeast3-b/instantSnapshots/instant-snapshot-1', resourceInUseByAnotherResource
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-demo
spec:
storageClassName: "standard-rwo"
capacity:
storage: 15Gi
accessModes:
- ReadWriteOnce
claimRef:
namespace: default
name: pvc-demo
csi:
driver: pd.csi.storage.gke.io
volumeHandle: projects/hivelab-prj-1/zones/asia-northeast3-a/disks/demo-pd
fsType: ext4
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: default
name: pvc-demo
spec:
storageClassName: "standard-rwo"
volumeName: pv-demo
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 15Gi
배포가 되지 않았습니다.
kubectl describe pod 로 로그를 살펴봅니다.
AttachVolume.Attach failed for volume "pv-demo" : rpc error: code = Internal desc = Failed to Attach: failed cloud service attach disk call: googleapi: Error 400: Invalid value for field 'resource.source': 'https://compute.googleapis.com/compute/v1/projects/hivelab-prj-1/zones/asia-northeast3-a/disks/demo-pd'. Disk must be in the same zone as the instance.
정적으로 생성한 pd는 zone-a 였지만. 우리의 포드가 올라갈 노드는 zone-a,b,c 중에 어디에 배치될지 모릅니다. 그래서 볼륨 마운트에 실패합니다.
pd와 노드의 위치를 맞춰주니 pod가 제대로 구동됩니다.
이제 마운트한 볼륨에서 한 작업이 정말로 persistent 한지 확인합니다.
index.html 파일을 만듭니다.
kubernetes 오브젝트들을 삭제후 다시 만들고 아까 만든 파일의 경로로 가보면 우리가 만든 파일이 있는걸 볼 수 있습니다.
kubernetes는 stateful한 어플리케이션을 위해서 deployments와 유사하다고도 볼 수 있는 statefulset을 제공합니다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-demo
spec:
selector:
matchLabels:
app: mysql-demo
serviceName: "SERVICE_NAME"
replicas: 3
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: mysql-demo
spec:
containers:
- name: CONTAINER_NAME
image: ...
ports:
- containerPort: 80
name: PORT_NAME
volumeMounts:
- name: PVC_NAME
mountPath: ...
volumeClaimTemplates:
- metadata:
name: PVC_NAME
annotations:
...
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
끝
참고
https://cloud.google.com/compute/docs/disks/persistent-disks?hl=ko
https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistent-volumes
https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/gce-pd-csi-driver?hl=ko
https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/preexisting-pd?hl=ko
https://cloud.google.com/kubernetes-engine/docs/concepts/persistent-volumes?hl=ko