[Infra] 쿠버네티스(kubernetes)(5) 퍼시스턴트 볼륨 스토리지
업데이트:
퍼시스턴트 볼륨 스토리지(persistent volume storage)
참고링크
참고 링크
- (0)도커의 개념
- (1)도커 설치하기, hello world!
- (2)도커 컨테이너 조작 기초(pull, run, ps)
- (3)도커 이미지 변경 후 저장(commit)
- (4)도커 컨테이너 네트워크
- (5)도커 API 이미지 빌드 및 컨테이너 실행
- (6)도커 컴포즈를 활용한 여러 개의 Flask, Nginx 배포
- (7)도커 볼륨을 이용한 데이터 관리
- (8)도커 장고 이미지 살펴보기
- (9)도커 PostgreSQL 컨테이너 배포
- (1)쿠버네티스 구조
- (2)쿠버네티스 설치
- (3)쿠버네티스 서비스 배포하기
- (4)컨테이너 IP와 파드 IP의 관계
- (5)퍼시스턴트 볼륨 스토리지
- (6)쿠버네티스 에러 메시지
1. 볼륨(volume)의 필요성
쿠버네티스를 이용해 배포한 애플리케이션의 데이터를 보존하기 위해서는 내부 혹은 외부의 스토리지 시스템과 연결해 퍼시스턴트 볼륨(Persistent Volume)을 이용해야 합니다. 예를 들어 DB같은 경우 컨테이너 내에서만 데이터가 생성된다면 컨테이너를 내렸을 경우 컨테이너 안에 있던 데이터도 함께 사라집니다. 따라서 데이터를 보존해야하는 앱의 경우 외부의 볼륨과 연결시켜놔야 컨테이너를 내렸다 올렸다 반복해도 데이터가 유지 됩니다. 이때 퍼시스턴트(persistent)단어는 데이터가 유지된다는 사실을 강조하기 위해 사용한 단어입니다.
2. 볼륨 종류
볼륨은 크게 쿠버네티스 내부에 두느냐 외부에 두느냐로 나눌수 있고, 쿠버네티스 내부에 두는 경우 emptyDir 이냐 hostPath로 나눠집니다.
2.1. emptyDir
먼저 emptyDir의 경우 파드가 종료되면 emptyDir이 사라집니다.
그리고 위 그림과 같이 파드 내 컨테이너 들만 공유하기때문에 파드가 다른 경우에는 사용할 수 없습니다. (추가적으로 확인해봐야할 사항은 emptyDir이 노드 내 경로에도 할당하는지는 알아봐함)
2.2. hostPath
두번째로 hostPath인데 hostPath는 노드내에 볼륨을 둠으로써 서로 다른 파드가 떠있다고 할지라도 hostPath를 통해 데이터를 공유할 수 있습니다.
위 그림과 같이 서로다른 파드의 데이터를 hostPath에 저장할수 있습니다. 그러나 파드가 떠있는 노드가 달라질 경우 hostPath를 사용할 수는 없습니다.
2.3. PersistentVolume(PV)
PV는 외부 스토리지 시스템에 볼륨을 둠으로써 서로다른 노드에 떠있는 파드들의 데이터를 관리할 수 있는 방법입니다.
위 그림을 보면 서로 다른 노드에 떠있는 파드 속 컨테이너 데이터를 관리 할 수 있는 것을 알 수 있습니다.
3. PV?, PVC?
PV는 Persistent Volume의 약자로 볼륨 그 자체를 의미합니다. 즉, PV는 파드와는 별개로 존재합니다. PVC는 Persistent Volume Claim의 약자로 PV에 보내는 요청입니다. PVC는 사용하고 싶은 용량, 모드(읽기/쓰기) 설정 등을 적어서 PV에 요청을 보냅니다. 즉, 파드는 PV에 다이렉트로 요청하는 것이 아니라 중간에 PVC를 거쳐서 요청하게 됨으로써 의존성이 줄어들게 됩니다.
4. hostPath 실습
4.0. 준비물
컨테이너를 만들 이미지가 준비되어 있어야합니다. 여기서는 nginx-lipaco, flask-lipaco 이미지가 필요합니당
4.1. yml 파일 생성
hostPath는 앞서 언급한데로 노드 내부에 볼륨을 둠으로써 파드들의 데이터를 관리합니다.
그럼 지금부터 위 그림과 같이 hostPath를 만들고 파드와 연결시키는 실습을 해보겠습니다.
위와 같은 구조를 만들기 위해 우리가 작성해야하는 파일은 pv.yml
와 pvc.yml
파일 입니다.
그리고 볼륨과 파드를 연결시키기 위해 deployment.yml
파일도 수정합니다.
그리고 최종적으로는 아래 그림과 같이 작동 됩니다.
그럼 pv.yml
파일부터 작성해보겠습니다.
다음 코드는 pv.yml
파일의 코드입니다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: "data1"
labels:
name: pv-data
spec:
accessModes:
- "ReadWriteOnce"
capacity:
storage: 2Gi
hostPath:
path: /tmp/k8s-pv
다음으로는 pvc.yml
파일을 작성해보겠습니다.
다음 코드는 pvc.yml
파일의 코드입니다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: lipaco-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2G
storageClassName: ""
selector:
matchLabels:
name: pv-data
마지막으로 볼륨과 연결하기 위해 기존에 있던 deployment.yml
파일을 다음과 같이 수정하겠습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deploy
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
volumes:
- name: hostpath-pv
persistentVolumeClaim:
claimName: lipaco-pvc
containers:
- name: nginx-lipaco
image: living_paradise_nginx
imagePullPolicy: Never
ports:
- containerPort: 8004
- name: flask-lipaco
image: living_paradise_predict_product_demand
imagePullPolicy: Never
volumeMounts:
- name: hostpath-pv
mountPath: /mnt
ports:
- containerPort: 5004
위 코드에서 수정 된 부분은 다음과 같습니다.
4.2. pv.yml 작동 시킴
위에서 작성한 파일을 동작시켜보겠습니다.
가장 먼저 pv.yml
을 작동시킵니다.
$ kubectl apply -f pv.yml
persistentvolume/data1 created
pv가 제대로 생성되었는지 확인해보겠습니다. 다음 결과를 보면 제대로 생성된 것을 알 수 있습니다.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
data1 2Gi RWO Retain Bound default/lipaco-pvc 9s
4.3. pvc.yml 작동 시킴
다음으로는 pvc.yml
을 작동시킵니다.
$ kubectl apply -f pvc.yml
persistentvolumeclaim/lipaco-pvc created
그리고 pvc가 제대로 생성되었는지 확인해보겠습니다.
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
lipaco-pvc Bound data1 2Gi RWO 9s
참고로 다음 커맨드와 같이 pv와 pvc를 한번에 확인할 수 있습니다.
$ kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/data1 2Gi RWO Retain Bound default/lipaco-pvc 29h
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/lipaco-pvc Bound data1 2Gi RWO 29h
4.4. deployment.yml 작동 시킴
다음으로는 deployment.yml
을 실행시켜 파드를 올리고 볼륨과 연결시킵니다.
$ kubectl apply -f deployment.yml
deployment.apps/web-deploy created
파드가 잘 돌아가는지 확인해봅시다.
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
web-deploy-6c594786ff-cnb7k 2/2 Running 0 4s
web-deploy-6c594786ff-l297d 2/2 Running 0 4s
web-deploy-6c594786ff-rw4mq 2/2 Running 0 4s
4.5. service.yml 작동 시킴
마지막으로 service.yml
을 실행해 로드밸런서를 작동시킵니다.
$ kubectl apply -f service.yml
service/web-service created
다음 코드로 서비스가 잘 작동하는지 확인해보겠습니다.
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 49d
web-service LoadBalancer 10.97.129.167 <pending> 80:30004/TCP 4s
그리고 다음과 같이 접속을 해보면 잘 되는 것을 볼 수 있습니다.
부록1. 작동 정지 시키는 법
작동 중인 친구들을 정지 시키고 싶다면 다음과 같이 입력합시다.
(마지막 pv.yml
은 입력안해도 될듯)
$ kubectl delete -f service.yml
$ kubectl delete -f deployment.yml
$ kubectl delete -f pvc.yml
$ kubectl delete -f pv.yml
부록2. pv 삭제하고 싶을때(pv terminating 문제 해결)
pv를 삭제하기 위해 다음 커맨드를 실행했습니다.
$ kubectl delete -f pv.yml
persistentvolume "data1" deleted
그런데 종료가 되지 않는 것이었습니다. 이게뭐지 싶어서 확인을 해보니 다음 코드와 같이 Terminating 이라는 상태만 뜨고 종료되지 않았습니다.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
data1 2Gi RWO Retain Terminating default/lipaco-pvc 5d22h
저는 pv를 완전히 삭제하고 싶었는데 이는 다음과 같은 명령어를 사용하면 됩니다.
$ kubectl patch pv <pv_name> -p '{"metadata": {"finalizers": null}}'
$ kubectl delete pv <pv_name> --grace-period=0 --force
제가 만든 pv의 이름은 data1 이므로 다음과 같이 입력합니다.
$ kubectl patch pv data1 -p '{"metadata": {"finalizers": null}}'
persistentvolume/data1 patched
$ kubectl delete pv data1 --grace-period=0 --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
Error from server (NotFound): persistentvolumes "data1" not found
그리고 나서 pv를 확인하면 사라진 것을 볼수 있습니다.
$ kubectl get pv
No resources found
부록3. 파드 속 컨테이너 확인법
$ Kubectl get pods.
$ Kubectl describe pod.
$ Kubectl logs [-f] POD [-c CONTAINER]
$ Kubectl top pod POD_NAME --containers.