쿠버네티스에서 컨테이너 파일 시스템이 구축되는 과정EmptyDir(공디렉터리)볼륨과 마운트로 노드에 데이터 저장하기 (호스트경로 HostPath)노드의 파일 시스템을 최소한으로 노출전체에서 접근 가능하도록 데이터 저장하기 : 영구볼륨과 클레임로컬 볼륨 정의 후, 노드와 연결, PVC 정의스토리지의 유형과 동적 볼륨 프로비저닝스토리지 유형스토리지를 선택할 때 고려할 점연습문제
쿠버네티스에서 컨테이너 파일 시스템이 구축되는 과정
파드 속 컨테이너의 파일 시스템은 여러 가지의 출처를 합쳐 구성됨
먼저 컨테이너 이미지가 파일 시스템의 초기 내용을 제공하고, 이 위에 컨테이너가 기록 가능한 레이어(writable layer)가 얹혀진다. 이미지에 들어 있던 파일을 수정한다거나 새로운 파일을 기록하는 작업이 바로 이 레이어에서 일어난다. ( 도커 이미지는 읽기 전용이기에 이미지에 들어 있던 파일을 수정하는 것은 이 기록 가능 레이어에서 해당 파일의 사본을 수정하는 것)


- 컨테이너의 파일 시스템은 컨피그맵이나 비밀값 등을 특정 경로에 마운트하는 방식으로 확장할 수 있다.
- 볼륨도 컨테이너 파일 시스템을 구성하는 수단 중 하나로, 볼륨은 파드 수준에서 정의되며 읽기 전용 또는 쓰기 가능으로 설정될 수 있다.
EmptyDir
(공디렉터리)
- 컨테이너 안에서 빈 디렉터리로 초기화되는 유형의 볼륨인 공디렉터리. 폴더 같지만 파드 수준의 스토리지임(이미지나 컨테이너 레이어에 속하지 않음)
- 저장된 데이터는 컨테이너가 재시작되더라도 유지됨
- 파드가 종료되었다가 다시 시작하면 데이터가 날아감
apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}
볼륨과 마운트로 노드에 데이터 저장하기 (호스트경로 HostPath
)
호스트경로 볼륨은 유상태 애플리케이션을 쿠버네티스로 처음 도입할 때 유리함
사용하기 쉽고, 모든 클러스터에서 동일하게 동작하는 것이 정상
그러나 상태가 임시 저장될 수 있기에 영구적으로 저장이 필요하면 클러스터 내 임의의 노드에서 접근할 수 있는 유형의 볼륨을 사용해야 함
이 상황에서는 데이터를 특정 노드에 고정시킬지 말지를 결정해야 하기 때문에 데이터를 다루기가 좀 더 까다로워짐
데이터가 특정 노드에 고정된다는 것은 대체 파드가 이전 파드와 동일한 노드에만 배치되도록 해야 한다는 의미임(반대로 데이터를 특정 노드에 고정시키지 않는다면 어떤 노드에도 파드를 배치할 수 있다)
- 호스트경로 볼륨 역시 파드에 정의되며 컨테이너 파일 시스템에 마운트되는 형태로 쓰이고 실제 데이터는 노드의 디스크에 기록됨 → 파드가 교체되더라도 대체 파드가 동일한 노드에 배치된다면 데이터를 그대로 사용할 수 있음
apiVersion: apps/v1 kind: Deployment metadata: name: pi-proxy labels: app: pi-proxy spec: selector: matchLabels: app: pi-proxy template: metadata: labels: app: pi-proxy spec: containers: - image: nginx:1.17-alpine name: nginx ports: - containerPort: 80 name: http volumeMounts: - name: config mountPath: "/etc/nginx/" readOnly: true - name: cache-volume mountPath: /data/nginx/cache # 프록시의 캐시 저장 경로 volumes: - name: config configMap: name: pi-proxy-configmap - name: cache-volume hostPath: # 노드의 디렉터리를 사용함 path: /volumes/nginx/cache # 사용할 노드의 디렉터리 type: DirectoryOrCreate # 디렉터리가 없으면 생성
- 노드가 하나뿐일 때는 이 데이터가 계속 저장되어 있음. 새로 생성된 대체 파드는 시작할 때 호스트경로 볼륨을 읽어들임
- 호스트경로 볼륨의 가장 큰 문제는 노드가 두 개 이상인 클러스터에서 생김
- 또한 호스트경로 볼륨을 잘못 사용하면 노드의 파일 시스템 전체가 노출되는 문제가 생길 수 있음
노드의 파일 시스템을 최소한으로 노출
apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep volumeMounts: - name: node-root mountPath: /pod-logs subPath: var/log/pods - name: node-root mountPath: /container-logs subPath: var/log/containers volumes: - name: node-root hostPath: path: / type: Directory
subPath
를 활용함으로써volumes
의 모든 경로에 대해 다 접근하는 것이 아닌 그 안에서 특정 하위 디렉터리만 대상할 수 있음
전체에서 접근 가능하도록 데이터 저장하기 : 영구볼륨과 클레임
Pod → PVC → PV (PV는 로컬 스토리지가 될수도, 리모트 스토리지가 될수도 있음)
PV 에는 스토리지 시스템에 대한 볼륨 정의가 들어가 있음
PVC에 필요한 요구사항을 기술하면 그에 맞는 PV가 할당됨
- 쿠버네티스 클러스터는 리소스가 가득 담긴 수영장과 같다. 여러 개의 노드가 있고 각각의 노드에는 클러스터에 cpu 및 메모리용량을 제공함. 그리고 이 CPU 및 메모리 용량을 사용하여 쿠버네티스에서 애플리케이션을 실행함
- 파드는 일반적인 volumes, volumeMounts 형태로 분산 스토리지를 사용함
- 모든 노드가 같은 분산 스토리지에 연결되고, 분산 스토리지로는 온프레미스(NFS, 글러스터FS(GlusterFS)), AKS 클러스터( 애저 파일스), EKS 클러스터 (Elastic Block Store)등을 사용할 수 있다. 따라서 파드는 어떤 노드에 실행되더라도 볼륨에 접근할 수 있다.
- 이들 시스템은 필수 설정이 각각 달라서 이 설정을 파드 정의에 기술할 수도 있다. 그러나 이러면 특정 스토리지 솔루션에 대한 의존도가 높아지기에 쿠버네티스에서는 스토리지 솔루션과의 결합을 느슨하게 유지하는 유연성 있는 수단을 제공함
Pod
는 컴퓨팅 계층의 추상,Service
는 네트워크 계층의 추상, 스토리지 계층의 추상 → PV(Persistence Volume), PVC(Persistence VolumeClaim)- 영구 볼륨(PV)은 사용 가능한 스토리지의 조각을 정의한 쿠버네티스 리소스이고, 각각의 영구볼륨에는 이를 구현하는 스토리지 시스템에 대한 볼륨 정의가 들어가 있다.
apiVersion: v1 kind: PersistentVolume metadata: name: pv01 spec: capacity: storage: 50Mi # 볼륨 용량 accessModes: # 파드의 접근 유형 - ReadWriteOnce # 파드 하나에서만 사용 가능 nfs: # NFS 스토리지를 사용하는 볼륨 server: nfs.my.network # NFS 서버의 도메인 네임 path: "/kubernetes-volumes" # 스토리지 경로
로컬 볼륨 정의 후, 노드와 연결, PVC 정의
kubectl label node $(kubectl get nodes -o jsonpath='{.items[0].metadata.name}' ) kiamol=ch05 kubectl get nodes -l kiamol=ch05
apiVersion: v1 kind: PersistentVolume metadata: name: pv01 spec: capacity: storage: 50Mi accessModes: - ReadWriteOnce local: path: /volumes/pv01 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kiamol operator: In values: - ch05
- 이렇게 영구볼륨을 정의했지만 파드가 영구볼륨을 직접 사용하지는 못하고 영구볼륨클레임 이란 것을 사용하여 볼륨 사용을 요청해야 함
- 영구볼륨클레임(PVC)은 파드가 사용하는 스토리지의 추상임
- 애플리케이션에서 사용할 스토리지를 요청하는 역할을 함
- 쿠버네티스에서 PVC는 요구 조건이 일치하는 PV와 함께 사용됨. 다만 상세한 볼륨 정보는 PV에 맡김
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc # 애플리케이션은 PVC를 통해 PV를 사용함 spec: accessModes: # 접근 유형은 필수 설정 - ReadWriteOnce resources: requests: storage: 40Mi # 요청하는 스토리지 용량 storageClassName: "" # 스토리지 유형을 지정하지 않음
- PVC에 스토리지 유형을 지정하지 않으면 쿠버네티스가 현존하는 PV 중 요구 사항과 일치하는 것을 찾아 줌. PV와 PVC의 관계는 일대일이며, PVC에 연결된 PV는 다른 PVC와 추가로 연결될 수 없음
# PVC 배치하기 전 PV kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pv01 50Mi RWO Retain Available <unset> 82s # PVC(postgres-pvc) 배치하고 난 후 PV kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pv01 50Mi RWO Retain Bound default/postgres-pvc <unset> 9m44s
- 이러한 방식은 PV를 명시적으로 생성해야 하는 정적 프로비저닝 방식으로, 요구사항이 일치하는 PV가 없을 때 PVC를 배치하면 PVC가 생성되기는 하지만 스토리지는 사용할 수 없다. 사용불가능한 PVC 를 사용하려는 파드는 보류 상태로 남아있게 된다.
kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE postgres-pvc Bound pv01 50Mi RWO <unset> 3m22s postgres-pvc-toobig Pending <unset> 4s
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc-toobig spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi storageClassName: ""
- PVC를 사용하는 파드 정의
apiVersion: v1 kind: Service metadata: name: todo-db spec: ports: - port: 5432 targetPort: 5432 selector: app: todo-db type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: todo-db spec: selector: matchLabels: app: todo-db template: metadata: labels: app: todo-db spec: containers: - name: db image: postgres:11.6-alpine env: - name: POSTGRES_PASSWORD_FILE value: /secrets/postgres_password volumeMounts: - name: secret mountPath: "/secrets" - name: data mountPath: /var/lib/postgresql/data volumes: - name: secret secret: secretName: todo-db-secret defaultMode: 0400 items: - key: POSTGRES_PASSWORD path: postgres_password - name: data persistentVolumeClaim: claimName: postgres-pvc
- 실제 클러스터에서는 노드에 로그인할 권한이 없는 경우가 있기에, 호스트경로 마운트로 노드의 루트 디렉터리를 마운트한 파드를 이용하여 노드의 파일 시스템에 디렉터리 생성
스토리지의 유형과 동적 볼륨 프로비저닝
- 지금까지의 방식은 명시적으로 PV와 pVC를 생성해서 연결하는
정적 볼륨 프로비저닝
방식
- 정적 볼륨 프로비저닝 방식은 모든 쿠버네티스 클러스터에서 사용할 수 있다는 점이 장점, 스토리지 접근 제약이 큰 조직에서 선호함
- 하지만 대부분의 쿠버네티스 플랫폼에서는 동적 볼륨 프로비저닝이라는 더 간단한 방식을 제공
- 동적 볼륨 프로비저닝 은 PVC만 생성하면 그에 맞는 PV 를 클러스터에서 동적으로 생성해 주는 방식임
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc-dynamic spec: accessModes: - ReadWriteOnce resources: requests: storage: 100M스토리지 유형 # storageClassName 필드가 없으면 기본 유형이 사용됨
- 쿠버네티스 플랫폼이 동적 볼륨 프로비저닝을 지원하고 기본 스토리지 유형이 지정되어 있다면, 이 스토리지 유형이 쓰인 영구볼륨이 생성되어 앞서 배치한 영구볼륨 클레임에 연결될 것
- 도커 데스크텁의 기본 스토리지 유형은 호스트경로 볼륨
스토리지 유형
스토리지 유형이 어떻게 동작할지를 결정하는 세가지 필드
provisioner
: 영구볼륨이 필요해질 때, PV를 만드는 주체. 플랫폼에 따라 관리 주체가 달라짐. 예로 AKS 는 함께 통합된 애저 파일스가 스토리지를 만듦
reclaimPolicy
: 연결되었던 클레임이 삭제되었을 때 남아 있는 볼륨을 어떻게 처리할지 지정한다. 볼륨을 함께 삭제할 수도 있고, 그대로 남겨 둘 수도 있따.
volumeBindingMode
: PVC가 생성되자마자 PV를 생성해서 연결할지, 아니면 해당 PVC를 사용하는 파드가 생성될 때 PV를 생성할지 선택할 수 있음
# 스토리지 유형 목록 확인. 이를 통해 기본 스토리지 유형이 어떻게 설정되었는지도 볼 수 있음 kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE hostpath (default) docker.io/hostpath Delete Immediate false 3d9h kiamol docker.io/hostpath Delete Immediate false 17s
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc-kiamol spec: accessModes: - ReadWriteOnce storageClassName: kiamol resources: requests: storage: 100M
스토리지를 선택할 때 고려할 점
- 데이터베이스 같은 유상태 애플리케이션도 쿠버네티스에서 실행해야 할까? 데이터 관리는 쿠버네티스에서도 쉬운 일이 아니며 유상태 애플리케이션은 이런 문제의 일부에 지나지 않음
- 쿠버네티스로 데이터베이스 서버를 이전하더라도 여전히 어떻게 데이터를 백업하고 스냅샷을 남기며 또 이를 복원할지 고려해야 함. 반면 클라우드환경의 매니지드 데이터베이스 서버는 이런 고민의 대부분을 덜어줌
- 그럼에도 전체 기술 스택을 쿠버네티스 매니페스트로 옮기고 싶다면 컨테이너 플랫폼을 위해 만들어진 몇 가지 현대식 데이터베이스 서버를 고려해 볼 수 있다(TiDB 나 CockroachDB)
연습문제
- PVC 부터 일단 만듦
- 해당 PVC를 Pod들에 Volume으로 설정해주고 VolumeMount 하여 PVC를 사용하도록 함