StatefulSet 에서 로컬 디스크 사용
이 문서는 StatefulSet 에서 로컬 디스크 사용 에 대한 글이다. Kubernetes 에서 디스크 사용은 어려운감이 있다. PersistentVolume 과 PersistentVolumeClaim 이라는 것을 알아야하고 이를 알고나서도 여러가지 속성들때문에 헷깔리는 경우가 많다.
PersistentVolume 관련해서 문서를 보면 대부분 클라우드가 제공하는 스토리지를 이용하는 예제가 많다. 그것이 아니라면 nfs 를 이용하는 경우가 많아서 나처럼 집에 컴퓨터를 이용하는 경우에 실습해 보기가 쉽지 않은게 사실이다.
StatefulSet 의 경우가 바로 이런 경우였다. StatefulSet 을 연구하기 위해서 여러가지 예제를 찾아봤고 다음과 같은 파일을 찾아냈다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
--- apiVersion: v1 kind: Service metadata: name: nginx namespace: perf-poc labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web namespace: perf-poc spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 2 template: metadata: labels: app: nginx spec: terminationGracePeriodSeconds: 10 containers: - name: nginx image: k8s.gcr.io/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html nodeSelector: kubernetes.io/hostname: knode volumeClaimTemplates: - metadata: name: www namespace: perf-poc spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi |
StatefulSet 은 비상태(stateless) 자원을 생성하는게 아니라 상태를 가지는 자원을 생성하게 해주는 Kubernetes 의 객체다. 대부분 Persistent Data 를 위한 것이기에 PersistentVolume 을 대부분 필요로 한다.
위 예제를 가지고 생성을 해보자.
1 2 3 |
]$ kubectl apply -f web.yaml service/nginx created statefulset.apps/web created |
정상적으로 생성이 됐다고 나온다. 확인을 해보자.
1 2 3 4 5 6 7 8 9 10 |
]$ kubectl get po,svc,sts NAME READY STATUS RESTARTS AGE pod/web-0 0/1 Pending 0 2m7s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d service/nginx ClusterIP None <none> 80/TCP 2m7s NAME READY AGE statefulset.apps/web 0/2 2m7s |
Pod 생성이 Pending 상태를 보이고 있다. 왜 이럴까? 앞에서 객체 생성을 정의한 yaml 파일을 자세히 보면 Persistent Volume 을 요청하고 있다. volumeClaimTemplates 이 바로 그것이다. 이것은 PersistentVolumeClaim 을 요청하는 것으로 나타난다. 확인해 보자.
1 2 3 |
]$ kubectl get po,pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Pending 5m29s |
persistentvolumeclaim 이 Pending 상태이다. 결국에는 이것으로 인해서 Pod 생성도 Pending 이 된 것이다.
해결방안
PersistentVolumeClaim 은 PersistentVolume 이 있어야 한다. 그리고 이것을 묶어줘야하는데 이것을 Bound 라고 한다. 문제는 web.yaml 을 실행했을때에 생성되는 PersistentVolumeClaim 과 PersistentVolume 을 어떻게 연결할 것인가?
좀더 정확하게 말하면 StatefulSet 이 자동으로 생성하는 PersistentVolumeClaim 과 PersistentVolume 을 어떻게 연결할 것인가?
StatefulSet 의 특징은 생성하는 객체의 이름이 임의대로 정해지지 않는다. 정확하게 사람이 인식하기 쉬운 이름으로 결정된다. 앞에서보면 Pod 의 이름이 web-0 이다. 이는 Pod 이름에 Replicas 의 첫번째인 0 을 붙여서 만든다.
StatefulSet 에서 PersistentVolumeClaim 이름은 예측 가능한 패턴을 따른다. volumeclaimtemplates-name–statefulset-name–replica-index. 그래서 생성된 PersistentVolumeClaim 의 이름이 www-web-0 가 된 것이다.
PersistentVolumeClaim 은 PersistentVolume 을 필요로 한다. PersistentVolume 을 생성할때에 claimRef.name 을 PersistentVolumeClaim 이름으로 지정한다. 이렇게 하면 PersistentVolumeClaim 이 생성될때에 PersistentVolume 이 이름과 연결되어 자동으로 Bound 되어진다. 다음과 같이 PersistentVolume 을 만든다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
apiVersion: v1 kind: PersistentVolume metadata: name: pv-statefulset-demo-0 namespace: perf-poc spec: storageClassName: "example-storageclass" capacity: storage: 2Gi volumeMode: Filesystem accessModes: - ReadWriteOnce claimRef: namespace: perf-poc name: www-web-0 persistentVolumeReclaimPolicy: Delete hostPath: path: /tmp/k8s-pv-statefulset-demo type: DirectoryOrCreate nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: "kubernetes.io/hostname" operator: "In" values: - knode |
이것은 Kubernetes Worker 호스트에 파일시스템에 특정 디렉토리를 PersistentVolume 으로 생성한다. 만일 디렉토리가 존재하지 않을 경우에는 생성하도록 한다. 그리고 PersistentVolumeClaim 이 삭제되면 이 볼륨도 함께 삭제되도록 했다.
여기서 중요한 것이 claimRef 필드이다. name 속성에 PersistentVolumeClaim 이름을 지정해준다.
또, nodeAffinity 필드를 이용해서 특정한 Worker 에서 생성되도록 했다.
StatefulSet 에서 replicas 를 2 로 했기 때문에 위 PersistentVolume 생성은 claimRef.name 이 www-web-0, www-web-1 로 두개가 필요하다.
두개의 PersistentVolume 을 생성하고 시간을 갖고 기다리면 다음과 같은 결과를 얻게 된다.
1 2 3 4 5 6 7 8 9 10 11 12 |
]$ kubectl get po,pv,pvc NAME READY STATUS RESTARTS AGE pod/web-0 1/1 Running 0 21m pod/web-1 1/1 Running 0 33s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pv-statefulset-demo-0 2Gi RWO Delete Bound default/www-web-0 example-storageclass 57s persistentvolume/pv-statefulset-demo-1 2Gi RWO Delete Bound default/www-web-1 example-storageclass 23s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pv-statefulset-demo-0 2Gi RWO 21m persistentvolumeclaim/www-web-1 Bound pv-statefulset-demo-1 2Gi RWO 33s |
Worker 호스트에 PersistentVolume 에서 생성한 디렉토리가 있는지 확인해 보자.
1 2 3 4 |
$ ls -lh /tmp/ total 0 drwxr-xr-x 2 root root 6 Aug 8 21:42 k8s-pv-statefulset-demo-0 drwxr-xr-x 2 root root 6 Aug 8 21:44 k8s-pv-statefulset-demo-1 |
이렇게 문제를 해결할 수 있다.