Skip to main content

쿠버네티스 스토리지

Storage

  • StorageClass 적용한 PV, PVC 생성하여 pod에 적용

Volume Mount - emptyDir

  • volume은 k8s 스토리지의 추상화 개념
  • 컨테이너는 Pod의 바인딩 되는 볼륨을 마운트하고 마치 로컬 파일시스템에 있는 것처럼 스토리지에 접근
  • 파드가 실행되는 호스트의 디스크를 임시로 컨테이너에 볼륨으로 할당해서 사용하는 방법
  • 파드가 사라지면 emptyDir에 할당해서 사용했던 볼륨의 데이터도 함께 사라진다.
  • 주로 메모리와 디스크를 함께 이용하는 대용량 데이터 계산에 사용
  • 문제가 발생해서 컨테이너가 재시작되더라도 파드는 살아있으므로 emptyDir에 저장해둔 데이터를 계속 이용 가능
  • 볼륨 선언과 컨테이너에서 마운트하는 부분을 분리해서 설정

Kubernetes 스토리지

volumes:
- name: html
hostPath:
path: /hostdir_or_file

컨테이너 단위로 마운트

volumeMounts:
- name: html
mountPath: /webdata

Volume을 통해 컨테이너 간 데이터 공유

  • emptyDir 볼륨은 빈 디렉토리로 시작
  • Pod 내부에서 실행중인 애플리케이션은 필요한 모든 파일을 작성
  • Pod를 삭제하면 볼륨의 내용이 손실됨
  • 동일한 Pod에서 실행되는 컨테이너 간에 파일을 공유할 때 유용
note

다음 조건에 맞춰서 nginx 웹서버 Pod가 생성한 로그파일을 받아서 STDOUT으로 출력하는 busybox 컨테이너를 운영하세요.

  • Pod name: weblog
  • Web container:
    • image: nginx:1.17
    • Volume mount: /var/log/nginx
    • readwrite
  • Log container:
    • image: busybox
    • Command: /bin/sh, -c, "tail -n+1 -f /data/access.log:
    • Volume mount: /data
    • readonly
  • emptyDir 볼륨을 통한 데이터 공유
# 컨텍스트 스위칭
kubectl config use-context k8s
kubectl config current-context

# weblog 파드 생성 및 yaml 파일 생성
kubectl run weblog --image=nginx:1.17 --dry-run=client -o yaml
kubectl run weblog --image=nginx:1.17 --dry-run=client -o yaml > weblog.yaml

# vi weblog.yaml
apiVersion: v1
kind: Pod
metadata:
name: weblog
spec:
containers:
- image: nginx:1.17
name: web
volumeMounts:
- mountPath: /var/log/nginx
name: log-volume
- image: busybox
name: log
command: ['sh', '-c', 'while true; do tail -n+1 -f /data/access.log; sleep 3; done']
volumeMounts:
- mountPath: /data
name: log-volume
readOnly: true
volumes:
- name: log-volume
emptyDir: {}


# 파드 생성
kubectl apply -f weblog.yaml

# 파드 생성 확인
kubectl get pods

# 파드 상세 확인
kubectl describe pod weblog

kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
campus-01 1/1 Running 2 (2d17h ago) 3d14h 192.168.166.150 node1 <none> <none>
custom-app 1/1 Running 2 (2d17h ago) 3d14h 192.168.166.152 node1 <none> <none>
web-8b7cdf948-jv5bg 1/1 Running 0 18h 192.168.104.3 node2 <none> <none>
web-8b7cdf948-sbnps 1/1 Running 0 18h 192.168.166.170 node1 <none> <none>
weblog 2/2 Running 0 32s 192.168.104.34 node2 <none> <none>

# 워커노드2 접속
ssh node2

# 접속확인 (nginx 페이지)
curl 192.168.104.34

# weblog 파드 안 log 컨테이너의 로그 확인
kubectl logs weblog -c log

# weblog 파드 안 web 컨테이너의 로그 확인
kubectl logs weblog -c web

Troubleshooting

note
Warning BackOff 2m17s (x6 over 3m20s) kubelet Back-off restarting failed container log in pod weblog_default(90723944-4a46-4a0f-87c2-1e0b9f5ab7ad)
  • /bin/sh, -c, “tail -n+1 -f /data/access.log 실행시 에러
  • 서버가 아닌 busybox를 무한 루프로 만들기 위해서 ['sh', '-c', 'while true; do tail -n+1 -f /data/access.log; sleep 3; done'] 변경

Volume Mount - hostPath

  • 여기서 호스트는 노드라고 생각하고 해당 파드를 실행하는 노드의 디렉토리에 저장된다.
  • 주로 노드의 로그를 수집하는데 많이 사용 (로그 에이전트)
  • 노드의 파일시스템의 디렉토리나 파일을 컨테이너에 마운트
  • 노드에 디렉토리나 파일을 생성하여 마운트 가능
  • hostPath는 type 지시어를 이용해 mount 구성의 요구를 추가할 수 있다.
  • 파드가 실행된 호스트의 파일이나 디렉토리를 파드에 마운트
  • emptyDir과 다르게 호스트에 있는 실제 파일이나 디렉터리를 마운트한다.
  • 파드가 재시작했을 때도 호스트에 데이터가 남아있음
  • 파드가 재시작되어서 새로운 노드에서 시작할 경우, 새로운 노드의 hostPath를 사용함
volumes:
- name: html
hostPath:
type: FileOrCreate
path: /hostdir_or_file
  • DirectoryOrCreate: 주어진 경로에 아무것도 없다면, 필요에 따라 kubelet의 소유권 권한을 0755로 설정한 빈 디렉토리를 생성한다.
  • Directory: 주어진 경로에 디렉토리가 있어야 한다.
  • FireOrCreate: 주어진 경로에 아무것도 없다면, 필요에 따라 kubelet의 소유권, 권한을 0755로 설정한 file을 생성한다.
  • File: 주어진 경로에 파일이 있어야 한다.
note
  • 작업 클러스터: k8s
  • /data/cka/fluentd.yaml 파일에 다음 조건에 맞게 볼륨 마운트를 설정하세요.
  • 워커 노드의 도커 컨테이너 디렉토리를 동일 디렉토리로 파드에 마운트하세요.
  • 워커 노드의 /var/log 디렉토리를 fluentd Pod에 동일 이름의 디렉토리로 마운트하세요.

데몬셋(DaemonSet)

데몬셋은 클러스터 전체 노드에 특정 파드를 실행할 때 사용하는 컨트롤러입니다. 클러스터 안에 새롭게 노드가 추가되었을 때 데몬셋이 자동으로 해당 파드를 실행시킵니다. 반대로 노드가 클러스터에서 빠졌을 때는 해당 노드에 있던 파드는 그대로 사라질 뿐 다른 곳으로 옮겨가서 실행되지는 않습니다. 따라서 데몬셋은 보통 로그 수집기를 실행하거나 노드를 모니터링하는 데몬 등 클러스터 전체에 항상 실행해두어야 하는 파드에 사용합니다.

kubectl config use-context k8s
kubectl config current-context

kubectl get nodes

# vi /data/cka/fluentd.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
containers:
- name: fluentd
image: fluentd
volumeMounts:
- mountPath: /var/lib/docker/containers
name: dockercontainerdir
- mountPath: /var/log
name: varlogdir
volumes:
- name: dockercontainerdir
hostPath:
path: /var/lib/docker/containers
- name: varlogdir
hostPath:
path: /var/log

cd /data/cka
# 데몬셋 생성
kubectl apply -f fluentd.yaml


# 생성된 데몬셋 확인
kubectl get ds
# 생성된 파드 확인
kubectl get pod

# 볼륨 마운트 Path 확인
kubectl describe pod fluentd-cwtp8

# fluentd 컨테이너 접속
kubectl exec fluentd-cwtp8 -c fluentd -it -- /bin/sh

PV/PVC의 생명주기

storage1

PV는 볼륨 자체를 뜻합니다. 클러스터 안에서 자원으로 다룹니다. 파드와는 별개로 관리되며 별도의 생명 주기가 있습니다. PVC는 사용자가 PV에게 하는 요청입니다. 사용하고 싶은 용량은 얼마인지, 읽기/쓰기는 어떤 모드로 설정하고 싶은지 등을 정해서 요청합니다.

쿠버네티스 볼륨을 파드에 직접 할당하는 방식이 아니라 중간에 PVC를 두어 파드와 파드가 사용할 스토리지를 분리했습니다. 이런 구조는 파드 각각의 상황에 맞게 다양한 스토리지를 사용할 수 있게 합니다.

클라우드 서비스를 사용할 때는 본인이 사용하는 클라우드 서비스에서 제공해주는 볼륨 서비스를 사용할 수도 있고 직접 구축한 스토리지를 사용할 수도 있습니다. 이렇게 다양한 스토리지를 PV로 사용할 수 있지만 파드에 직접 연결하는 것이 아니라 PVC를 거쳐서 사용하므로 파드는 어떤 스토리지를 사용하는지 신경 쓰지 않아도 됩니다.

Storage Class

  • 기본 스토리지 운영 환경을 분리
    • 관리자: 스토리지 구성
    • 개발자: 스토리지가 필요한 만큼 요구

각 스토리지 클래스에는 해당 스토리지 클래스에 속하는 퍼시스턴트 볼륨을 동적으로 프로비저닝할 때 사용되는 provisioner, parameters와 reclaimPolicy 필드가 포함된다.

Static Provisioning

정적으로 PV를 프로비저닝할 때는 클러스터 관리자가 미리 적정 용량의 PV를 만들어 두고 사용자의 요청이 있으면 미리 만들어둔 PV를 할당합니다. 사용할 수 있는 스토리지 용량에 제한이 있을 때 유용합니다. 사용하도록 미리 만들어 둔 PV의 용량이 100GB라면 150GB를 사용하려는 요청들은 실패합니다. 1TB 스토리지를 사용하더라도 미리 만들어 둔 PV 용량이 150GB 이상인 것이 없으면 요청이 실패합니다.

Dynamic Provisioning

  • 온-디맨드 방식으로 스토리지 볼륨을 생성
  • 사용자가 스토리지를 요청하면 자동으로 프로비저닝
  • 하나 이상의 Storage Class 오브젝트를 사전 생성

Binding (바인딩)

바인딩은 프로비저닝으로 만든 PV를 PVC와 연결하는 단계입니다. PVC에서 원하는 스토리지의 용량과 접근방법을 명시해서 요청하면 거기에 맞는 PV가 할당됩니다. 이 때 PVC에서 원하는 PV가 없다면 요청은 실패합니다. 하지만 PVC는 원하는 PV가 생길 때까지 대기하다가 바인딩합니다. PV와 PVC의 매핑은 1대1 관계입니다. PVC 하나가 여러 개 PV에 할당될 수 없습니다.

Using

PVC는 파드에 설정되고 파드는 PVC를 볼륨으로 인식해서 사용합니다. 할당된 PVC는 파드를 유지하는 동안 계속 사용하며 시스템에서 임의로 삭제할 수 없습니다. 이 기능을 'Storage Object in Use Protection' (사용중인 스토리지 오브젝트 보호)라고 합니다. 사용중인 데이터 스토리지를 임의로 삭제하는 경우 치명적인 결과가 발생할 수 있으므로 이런 보호 기능을 사용하는 것입니다.

Reclaiming

사용이 끝난 PVC는 삭제되고 PVC를 사용하던 PV를 초기화(reclaim)하는 과정을 거칩니다. 이를 Reclaiming(반환)이라고 합니다. 초기화 정책에는 Retain, Delete, Recycle이 있습니다.

Retain

Retain은 PV를 그대로 보존합니다. PVC가 삭제되면 사용 중이던 PV는 해제(released) 상태여서 다른 PVC가 재사용할 수 없습니다. 단순히 사용 해제 상태이므로 PV 안의 데이터는 그대로 남아있습니다. 이 PV를 재사용하려면 관리자가 다음 순서대로 직접 초기화해줘야 합니다.

    1. PV 삭제, 만약 PV가 외부 스토리지와 연결되었다면 PV는 삭제되더라도 외부 스토리지의 볼륨은 그대로 남아 있습니다.
    1. 스토리지에 남은 데이터를 직접 정리
    1. 남은 스토리지의 볼륨을 삭제하거나 재사용하려면 해당 볼륨을 이용하는 PV 재생성

Delete

PV를 삭제하고 연결된 외부 스토리지 쪽의 볼륨도 삭제합니다. 프로비저닝할 때 동적 볼륨 할당 정책으로 생성된 PV들은 기본 반환 정책(Reclaim Policy)이 Delete입니다. 상황에 따라 처음에 Delete로 설정된 PV의 반환 정책을 수정해서 사용해야 합니다.

Recycle

Recycle은 PV의 데이터들을 삭제하고 다시 새로운 PVC에서 PV를 사용할 수 있도록 합니다. 특별한 파드를 만들어 두고 데이터를 초기화하는 기능도 있지만 동적 볼륨 할당을 기본 사용할 것을 추천합니다.

StorageClass

  • 스토리지의 "classes"를 설명
  • StorageClass에는 해당 StorageClass에 속하는 PV를 동적으로 프로비저닝할 때 사용되는 provisioner, parameters와 reclaimPolicy 필드가 포함
  • (동적 볼륨 할당 정책) reclaimPolicy가 지정되지 않으면 기본값은 Delete
  • (정적 볼륨 할당 정책) reclaimPolicy가 지정되지 않으면 기본값은 Retain
  • 스토리지 클래스에 속하는 볼륨을 설명하는 parameters

프로비저너

  • 각 스토리지 클래스에는 PV 프로비저닝에 사용되는 볼륨 플러그인을 결정하는 프로비저너가 있다. 이 필드는 반드시 지정해야 한다.

Persistent Volume

apiVersion: v1
kind: PersistentVolume
metadata:
name: pvname
spec:
capacity:
storage: <storage_size>
accessModes:
- ReadWriteOnce
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: <NFS_SERVER>
path: <Share_Storage>
note
  • 작업 클러스터: hk8s
  • pv001라는 이름으로 size 1Gi, access mode ReadWriteMany를 사용하여 persistent volume을 생성
  • volume type은 hostPath이고 위치는 /tmp/app-config입니다.
# 컨텍스트 스위칭
kubectl config use-context hk8s
kubectl config current-context

# vi pv001.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
hostPath:
path: /tmp/app-config
storageClassName: app-hostpath-sc

# 퍼시스턴트 볼륨 생성
kubectl apply -f pv001.yaml

# 기본 정책: RETAIN / STATUS: Available 확인
kubectl get pv

Persistent Volume Claim

# 1. PVC 생성
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc이름
spec:
resources:
requests:
storage: 사이즈
accessModes:
- ReadWriteOnce
- ReadOnlyMany
storageClassName: 스토리지 클래스

# 2. 파드 생성
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- image: 이미지명
name: 컨테이너명
volumeMounts:
- name: 볼륨명
mountPath: 마운트 디렉토리
volumes:
- name: 볼륨명
persistentVolumeClaim:
claimName: pvc이름

PVC를 사용하는 애플리케이션 파드 운영

note
  • 작업 클러스터: k8s
  • 다음의 조건에 맞는 새로운 PersistentVolumeClaim을 생성하세요.
    • name: pv-volume
    • Class: app-hostpath-sc
    • Capacity: 10Mi
  • 앞서 생성한 pv-volume PersistentVolumeClaim을 mount하는 Pod를 생성하세요.
    • name: web-server-pod
    • image: nginx
    • mount path: /usr/share/nginx/html

volume에서 ReadWriteMany 액세스 권한을 가지도록 구성합니다.

Claim 생성

# 컨텍스트 스위칭
kubectl config use-context k8s
kubectl config current-context

# vi pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pv-volume
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
storageClassName: app-hostpath-sc

# 퍼시스턴트 볼륨 요청
kubectl apply -f pvc.yaml

# 요청 확인
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pv-volume Bound pv1 1Gi ROX,RWX app-hostpath-sc 3s

# 퍼시스턴트 볼륨 확인
kubectl get pv
...
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWX Retain Available 24m
pv1 1Gi ROX,RWX Recycle Bound default/pv-volume app-hostpath-sc 3d17h
pv2 1Gi ROX,RWX Recycle Available app-data-sc 3d17h

파드 생성

# vi pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web-server-pod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: pv-volume

# web-server-pod 파드 내 nginx 컨테이너 접속
kubectl exec web-server-pod -c nginx -it -- /bin/sh

# 리소스 정리
kubectl get pvc
...
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWX Retain Available 85m
pv1 1Gi ROX,RWX Recycle Bound default/pv-volume app-hostpath-sc 7d12h
pv2 1Gi ROX,RWX Recycle Available app-data-sc 7d12h

# 퍼시스턴트 볼륨 클레임 삭제 (Released -> Available)
kubectl delete pvc pv-volume
...
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWX Retain Available 86m
pv1 1Gi ROX,RWX Recycle Released default/pv-volume app-hostpath-sc 7d12h
pv2 1Gi ROX,RWX Recycle Available app-data-sc 7d12h
...
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWX Retain Available 86m
pv1 1Gi ROX,RWX Recycle Available app-hostpath-sc 7d12h
pv2 1Gi ROX,RWX Recycle Available app-data-sc 7d12h

# 퍼시스턴트 볼륨 삭제
kubectl delete pv pv001
kubectl get pv