無知

갈 길이 먼 공부 일기

기술 공부/쿠버네티스

쿠버네티스 (6) | 볼륨, 퍼시스턴트 스토리지

moozii 2022. 2. 23. 19:15

핵심 내용

  • 다중 컨테이너 파드 생성
  • 컨테이너 간 디스크 스토리지 공유용 볼륨 생성
  • 파드 내부 깃 리포지터리 사용
  • 파드 내 GCE 퍼시스턴트 디스크 등 퍼시스턴트 스토리지 연결
  • 사전 프로비저닝된 퍼시스턴트 스토리지
  • 퍼시스턴트 스토리지의 동적 프로비저닝

 

https://medium.com/analytics-vidhya/volumes-in-kubernetes-fa9f50ce4c8f

볼륨 소개

파드는 내부에 프로세스를 실행하고, 여러 리소스를 공유하는 논리적 호스트이지만, 디스크를 공유하지 못한다.
파드 내 각 컨테이너는 컨테이너 이미지에서 제공하는, 고유하게 분리된 파일시스템을 가진다.
새로운 컨테이너가 시작할 때 이미지 빌드 시 추가해둔 파일을 갖고 시작하기 때문에, 재시작시마다 파일시스템이 이미지대로 초기화된다. 즉, 이전에 종료된 컨테이너의 데이터를 가진 디렉터리를 보존하기 위해서는, 쿠버네티스 내에서 스토리지 볼륨을 정의해야 한다.

쿠버네티스 볼륨은, 파드 스펙에서 정의된 파드 구성 요소로,
파드 내 모든 컨테이너에서 사용 가능하지만, 접근하고자 하는 컨테이너 각각 마운트되어야 사용 가능하다.

 

각 컨테이너가 서로 간 디스크 스토리지를 공유하도록 하기 위해서,
파드에 볼륨을 추가하고, 각각 올바른 경로로 마운트시킬 수 있다.

  • publicHTML 볼륨 : 콘텐츠 생성기 내용을 웹서버가 서비스
    • 웹서버 컨테이너의 /var/htdocs에 마운트
    • Content Agent, 콘텐츠 생성기 컨테이너에 /var/html로 마운트
  • logVol 볼륨 : 로그를 저장
    • 웹서버 컨테이너 /var/logs 마운트
    • 로그 처리 컨테이너 /var/logs 마운트

 

볼륨 유형

  1. emptyDir : 일시적인 데이터를 저장하는 빈 디렉토리
  2. hostPath : 워커 노드 파일시스템을 파드 디렉토리로 마운트
  3. gitRepo : 깃 레포 콘텐츠를 체크아웃해 초기화
  4. nfs : NFS 공유를 파드에 마운트
  5. gcePersistentDisk / awsElasticBlock / azureDisk : CSP 전용 스토리지 마운트
  6. cinder, cephfs, iscsi, flocker, glusterfs, quobyte, rbd, flexVolume, vsphereVolume, photonPersistentDisk, scaleIO : 다른 유형의 네트워크 스토리지를 마운트
  7. configMap, secret, downwardAPI : 쿠버네티스 리소스 및 클러스터 정보를 파드에 노출하는 데에 사용
  8. persistentVolumeClaim : 사전에 혹은 동적으로 프로비저닝된 퍼시스턴트 스토리지를 사용
네트워크 파일 시스템(Network File System, NFS)은 1984년에 썬 마이크로시스템즈가 개발한 프로토콜이다. 클라이언트 컴퓨터의 사용자가 네트워크 상의 파일을 직접 연결된 스토리지에 접근하는 방식과 비슷한 방식으로 접근하도록 도와 준다. 다른 수많은 프로토콜과 마찬가지로 ONC RPC 시스템을 기반으로 한다. 네트워크 파일 시스템은 RFC에 정의된 오픈 표준이므로 누구나 구현할 수 있다.
https://ko.wikipedia.org/wiki/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%ED%8C%8C%EC%9D%BC_%EC%8B%9C%EC%8A%A4%ED%85%9C 

NFS(Network File System)란 리눅스 머신에서 이더넷 기반으로 동작하는 가장 기본적인 공유 파일 시스템 중의 하나로 서버/클라이언트 모델로 동작하는 스토리지 프로토콜입니다. 각 클라이언트들이 NFS 서버의 공유 폴더(Export Path)를 마운트하게 되면 자신의 로컬 파일 시스템을 사용하는 것처럼 여러 클라이언트와 파일을 공유하여 사용할 수 있다.
https://cloud.kt.com/portal/ktcloudportal.epc.productintro.ucloud_server_image.nfs.html

 

볼륨을 사용한 컨테이너 간 데이터 공유

emptyDir

빈 디렉터리로 시작하는 볼륨으로, 파드에 바인딩되어 파드 삭제 시 사라진다.
동일 파드 내 컨테이너 간 파일 공유 시 유용하고, 임시 데이터를 디스크에 쓰고자 할 때도 사용 가능하다.
컨테이너의 파일 시스템이 쓰기가 불가한 경우가 발생해 볼륨 사용이 대안이 될 수도 있다.

$ cat fortune/fortuneloop.sh 
#!/bin/bash
trap "exit" SIGINT
mkdir /var/htdocs

while :
do
  echo $(date) Writing fortune to /var/htdocs/index.html
  /usr/games/fortune > /var/htdocs/index.html
  sleep 10
done

유닉스 fortune 명령으로 HTML 콘텐츠를 생성한다.
(해당 명령어는 실행 시마다 임의의 인용문을 출력하는데,
10초마다 실행하여 그 출력을 index.html에 저장하는 스크립트이다.)

$ cd fortune 
$ cat Dockerfile 
FROM ubuntu:latest

RUN apt-get update ; apt-get -y install fortune
ADD fortuneloop.sh /bin/fortuneloop.sh

ENTRYPOINT /bin/fortuneloop.sh

$ docker build -t joon0615/fortune . 
[+] Building 1.1s (8/8) FINISHED                                                
 => [internal] load build definition from Dockerfile                       0.0s
 => => transferring dockerfile: 184B                                       0.0s
 => [internal] load .dockerignore                                          0.0s
 => => transferring context: 2B                                            0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest           1.0s
 => [1/3] FROM docker.io/library/ubuntu:latest@sha256:669e010b58baf5beb28  0.0s
 => [internal] load build context                                          0.0s
 => => transferring context: 36B                                           0.0s
 => CACHED [2/3] RUN apt-get update ; apt-get -y install fortune           0.0s
 => CACHED [3/3] ADD fortuneloop.sh /bin/fortuneloop.sh                    0.0s
 => exporting to image                                                     0.0s
 => => exporting layers                                                    0.0s
 => => writing image sha256:5fd169984e13a75ba9b3d88802ae20ed7ead1a227a723  0.0s
 => => naming to docker.io/joon0615/fortune                                0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

$ docker push joon0615/fortune      
Using default tag: latest
The push refers to repository [docker.io/joon0615/fortune]
587d7b2ee6a4: Pushed 
825dea5881e8: Pushed 
36ffdceb4c77: Pushed 
latest: digest: sha256:62b3ac19f205cd6425144d98356b970663d458c13a2a2effe7ec3b59a670e341 size: 948

fortune 디렉토리 내 도커파일을 생성해, 이미지를 빌드하고 푸시한다.

$ cat fortune-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: fortune
spec:
  containers:
  - image: joon0615/fortune
    name: html-generator
    volumeMounts:
    - name: html
      mountPath: /var/htdocs
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html
    emptyDir: {}

이후 파드 매니페스트를 yaml 파일 기반으로 생성한다.

해당 파드를 포트포워딩으로 노출시켜 접근해보면, 생성기가 계속 주기적으로 새로운 문장을 만들어, 매번 다른 메시지를 받음을 확인할 수 있다.

$ kubectl create -f fortune-pod.yaml 
pod/fortune created

$ kubectl port-forward fortune 8080:80
\Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
$ curl http://localhost:8080
You own a dog, but you can only feed a cat.
$ curl http://localhost:8080
Your supervisor is thinking about you.
$ curl http://localhost:8080
You will live to see your grandchildren.
$ curl http://localhost:8080
Your fly might be open (but don't check it just now).

볼륨으로 사용한 emptyDir은 워커 노드 디스크에 생성되므로 노드 디스크에 따라 성능이 결정된다.

하지만 쿠버네티스에 emptyDir을 디스크가 아닌 메모리를 사용하는 tmpfs 파일시스템으로 생성하도록 요청할 수 있다.

tmpfs는 수많은 유닉스 계열 운영 체제의 임시 파일 스토리지 기능을 일컫는 이름이다. 마운트된 파일 시스템처럼 보이지만 영구적인 기억 장치가 아닌 휘발성 메모리에 저장된다. 가상 디스크 드라이브처럼 보이면서도 디스크 파일 시스템을 호스팅하는 램 디스크와 구조가 비슷하다.
https://ko.wikipedia.org/wiki/Tmpfs
  volumes:
  - name: html
    emptyDir: 
      medium: Memory

 

깃 리포지터리 볼륨으로 사용하기

깃레포 볼륨은 emptyDir 볼륨이며, 파드 시작 시 깃 레포를 복제하고 특정 리비전을 체크아웃해 데이터로 채운다.

 

볼륨 생성 이후에는 레포와 동기화가 이루어지지 않지만, 파드 삭제 후 재생성 시에는 생성 시점에 맞춰 갱신된다.
따라서 레포에 변경사항이 푸시될 때마다 새로 서비스하기 위해 기존 파드가 삭제되어야 한다.

생성 시 볼륨을 깃레포로 명시하고 레포 주소 등을 기록하여 생성한다.

$ cat gitrepo-volume-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: gitrepo-volume-pod
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:
  - name: html
    gitRepo:
      repository: https://github.com/joon0615/kubia-website-example.git
      revision: master
      directory: .

$ kubectl create -f gitrepo-volume-pod.yaml
Warning: spec.volumes[0].gitRepo: deprecated in v1.11
pod/gitrepo-volume-pod created

$ kubectl port-forward gitrepo-volume-pod 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
$ curl http://localhost:8080
<html>
<body>
Hello there.
</body>
</html>

 

gitRepo 볼륨 타입 오류
https://github.com/kubernetes/kubernetes/issues/60999

경고: gitRepo 볼륨 유형은 사용 중단되었다. git repo가 있는 컨테이너를 프로비전 하려면 초기화 컨테이너(InitContainer)에 EmptyDir을 마운트하고, 여기에 git을 사용해서 repo를 복제하고, EmptyDir을 파드 컨테이너에 마운트 한다.
https://kubernetes.io/ko/docs/concepts/storage/volumes/

>> initContainer를 포함한 yaml 파일을 만들어 파드를 생성하는 실습을 진행했으나, 마운트 주소를 설정하는 과정에서 오류가 발생해 일단 실습을 넘어갔다.

 

사이드카 컨테이너

보조 역할을 수행하는 별개의 컨테이너를 의미한다. 파드의 주 컨테이너를 보완해 기존 이미지의 재사용성을 높인다.
깃 동기화를 수행하는 이미지를 도커 허브에서 활용하여 파일 동기화를 진행하는 사이드카 컨테이너를 생성할 수 있다.
프라이빗 깃 레포를 복제하기 위해서도 깃 동기화 과정에서는 사이드카를 활용해야 한다고 이야기한다.

 

워커 노드 파일시스템의 파일 접근

hostPath 볼륨

일반적으로는 파드가 호스트 노드를 인식하지 못하기 때문에 노드 파일 시스템에 접근이 불가하지만,
노드 파일시스템을 사용해야 할 경우 이용하는 볼륨이 hostPath 볼륨이다.
노드 파일 시스템의 특정 파일 혹은 디렉터리를 가리키는 볼륨이다.

emptyDir과 달리, 파드가 종료되어도 삭제되지 않고,
다음 파드가 동일 경로를 가리키는 볼륨을 사용하고, 동일한 노드에 파드가 스케줄링되면
이전 파드가 남긴 항목을 모두 확인할 수 있다.

특정 노드의 파일시스템에 콘텐츠를 저장한다는 측면에서, 파드가 다른 노드로 스케줄링될 수 있으므로, 데이터 디렉터리로 사용하기에는 적합하지 않다.

$ kubectl get pod s --namespace kube-system
Error from server (NotFound): pods "s" not found

$ kubectl get pod --namespace kube-system 
NAME                               READY   STATUS    RESTARTS   AGE
coredns-64897985d-w9lm5            1/1     Running   4          22d
etcd-minikube                      1/1     Running   5          22d
kube-apiserver-minikube            1/1     Running   5          22d
kube-controller-manager-minikube   1/1     Running   5          22d
kube-proxy-gqm9x                   1/1     Running   5          22d
kube-scheduler-minikube            1/1     Running   5          22d
storage-provisioner                1/1     Running   6          22d

$ kubectl describe pod kube-scheduler-minikube --namespace kube-system 
Name:                 kube-scheduler-minikube
Namespace:            kube-system
...
Volumes:
  kubeconfig:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/kubernetes/scheduler.conf
    HostPathType:  FileOrCreate
...

$ kubectl describe pod kube-controller-manager-minikube --namespace kube-system 
Name:                 kube-controller-manager-minikube
Namespace:            kube-system
...
Volumes:
  ca-certs:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/ssl/certs
    HostPathType:  DirectoryOrCreate
  flexvolume-dir:
    Type:          HostPath (bare host directory volume)
    Path:          /usr/libexec/kubernetes/kubelet-plugins/volume/exec
    HostPathType:  DirectoryOrCreate
  k8s-certs:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/minikube/certs
    HostPathType:  DirectoryOrCreate
  kubeconfig:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/kubernetes/controller-manager.conf
    HostPathType:  FileOrCreate
  usr-share-ca-certificates:
    Type:          HostPath (bare host directory volume)
    Path:          /usr/share/ca-certificates
    HostPathType:  DirectoryOrCreate
...

hostPath 볼륨은, 자체 데이터를 저장하기 위함이 아니라, 노드 데이터에 접근하기 위해 사용되는 것이 주 목적이다.

혹은 이후 등장할 퍼시스턴트 스토리지의 테스트용으로도 사용된다.

 

퍼시스턴트 스토리지

파드 애플리케이션이 디스크에 데이터를 유지하고, 다른 노드로 스케줄링되어도 동일한 데이터를 유지하려면,
어떤 노드에서도 접근 가능하도록 NAS 유형에 저장되어야 한다.

네트워크 결합 스토리지(network-attached storage, NAS)는 컴퓨터 네트워크에 연결된 파일 수준의 컴퓨터 기억 장치이며 서로 다른 네트워크 클라이언트에 데이터 접근 권한을 제공한다.
https://ko.wikipedia.org/wiki/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%EA%B2%B0%ED%95%A9_%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80

영구 데이터를 허용하는 볼륨을 위해 MongoDB를 실행하는 파드를 생성하고자 한다.

Minikube 상에 실습하므로, mongo-pod-hostpath.yaml을 활용하여 hostPath를 사용하여 생성한다.

$ cat mongodb-pod-hostpath.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mongodb 
spec:
  volumes:
  - name: mongodb-data
    hostPath:
      path: /tmp/mongodb
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP

파드를 생성하고, 컨테이너 내부에서 MongoDB 셸을 실행해 데이터 스토리지에 데이터를 작성해보자.

JSON 도큐먼트를 단일 속성으로 추가해 추가한 도큐먼트를 확인한다.

$ kubectl create -f mongodb-pod-hostpath.yaml
pod/mongodb created

$ kubectl exec -it mongodb mongo
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
error: unable to upgrade connection: container not found ("mongodb")

$ kubectl exec -it mongodb -- mongo
MongoDB shell version v5.0.6
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("a945d09c-f422-43b9-8194-9bd2d82bfa70") }
MongoDB server version: 5.0.6
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
...
> use mystore
switched to db mystore

> db.foo.insert({name:'foo'})
WriteResult({ "nInserted" : 1 })

> db.foo.find()
{ "_id" : ObjectId("620d25f99f07914ea504c1af"), "name" : "foo" }

> exit
bye

위에 작성하여 저장한 데이터를, 파드 재생성한 후에도 확인 가능한지 실습을 진행한다.

$ kubectl delete pod mongodb 
pod "mongodb" deleted

$ kubectl create -f mongodb-pod-hostpath.yaml
pod/mongodb created

$ kubectl get po -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
mongodb   1/1     Running   0          24s   172.17.0.2   minikube   <none>           <none>

$ kubectl exec -it mongodb -- mongo
...
> use mystore
switched to db mystore

> db.foo.find()
{ "_id" : ObjectId("620d25f99f07914ea504c1af"), "name" : "foo" }

minikube 환경이라 동일 노드라서 크게 의미 있는 실습은 아니지만, 삭제 후 재생성해도, 확인 가능하다.
우리는 생성시 hostPath 볼륨을 사용했고, 동일 노드였기 때문에 가능했다고 추측한다.

 

기반 퍼시스턴트 스토리지로 다른 유형 볼륨 사용하기

다른 CSP 환경에서는 각각 그에 맞는 퍼시스턴트 스토리지 볼륨을 사용해야 함에 대한 안내이다. (SKIP)

 

NFS 볼륨 사용하기

그외로 NFS 볼륨을 사용하는 방법을 소개한다.
클러스터가 여러 대의 서버로 실행되는 경우, 외장 스토리지를 볼륨에 마운트하기 위한 옵션을 사용해야 하는데, NFS 서버와 서버에서 익스포트 경로(대체로 /etc/exports 로 지정)를 지정하면 된다고 한다. 이를 통해 서버의 특정 경로로 외부에서 마운트가 가능하게 한다.

$ cat mongodb-pod-nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mongodb-nfs
spec:
  volumes:
  - name: mongodb-data
    nfs:
      server: 1.2.3.4
      path: /some/path
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP

다른 스토리지 기술 사용하기

  • ISCSI 디스크 리소스 마운트 용 iscsi
  • GlusterFS 마운트용 glusterfs
  • RADOS 블록 디바이스용 rdb

등.. 다양하게 존재한다.

다만, 개발자가 파드 생성 시 인프라 관련 스토리지 세부사항을 직접 처리한다거나, NFS 서버 이름을 지정한다거나, 인프라 정보를 파드 정의에 포함하는 것은 파드 정의가 특정 클러스터에 의존하게 되는 위험성이 존재하고, 기본적인 사용 철학과도 어긋나기 때문에 다른 방법을 다음 절에서 소개할 예정이다.

 

기반 스토리지 기술과 파드 분리

파드 개발자가 인프라 세부사항을 걱정하지 않고도 이식 가능하도록,
개발자가 애플리케이션에 일정량의 퍼시스턴트 스토리지를 필요로 하면 쿠버네티스에 요청하고,
동일하게 파드 생성시 CPU나 메모리와 같은 다른 리소스도 요청이 가능해야 한다.

 

퍼시스턴트 볼륨과 퍼시스턴트 볼륨 클레임

인프라 세부사항을 처리하지 않고 애플리케이션이 스토리지를 요청하는 데에 사용하는 2가지 리소스이다.

 
  1. 클러스터 관리자(비-개발자)가 기반 스토리지를 설정한다
    (NFS 익스포트와 같은 네트워크 스토리지 유형 설정)
  2. 클러스터 관리자가 쿠버네티스 API 서버로 퍼시스턴트 볼륨 리소스를 생성하여 쿠버네티스에 등록한다.
    (PV 디스크립터를 API 서버에 게시해 PV를 생성)
    클러스터 관리자는 생성된 퍼시스턴트 볼륨의 크기와 지원가능한 접근 모드를 지정한다.
  3. 클러스터 사용자가 퍼시스턴트 볼륨 클레임 매니페스트를 작성해 최소 크기와 필요 접근 모드를 명시한다.
  4. 클러스터 사용자가 퍼시스턴트 볼륨 클레임 매니페스트를 API 서버에 게시한다.
  5. 쿠버네티스가 적절한 퍼시스턴트 볼륨을 찾아, 클레임에 볼륨을 바인딩한다.
  6. 바인딩된 퍼시스턴트 볼륨을 파드 내부 볼륨 중 하나로 사용한다.
  7. 볼륨 클레임의 바인딩을 삭제해 릴리스될 때까지 다른 사용자의 해당 볼륨 사용을 금한다.
$ cat mongodb-pv-hostpath.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mongodb-pv
spec:
  capacity: 
    storage: 1Gi       // 퍼시스턴트볼륨 사이즈
  accessModes:
    - ReadWriteOnce    // 단일 클라이언트의 읽기, 쓰기용
    - ReadOnlyMany     // 여러 클라이언트의 읽기 전용
  persistentVolumeReclaimPolicy: Retain   // 클레임 해제 후 퍼시스턴트 볼륨 유지 (삭제 X)
  hostPath:
    path: /tmp/mongodb  // 이전에 생성한 몽고디비 볼륨 기반

$ kubectl create -f mongodb-pv-hostpath.yaml
persistentvolume/mongodb-pv created

$ kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mongodb-pv   1Gi        RWO,ROX        Retain           Available                                   17s

퍼시스턴트 볼륨은 특정 네임스페이스에 속하지 않는다. 노드와 같은 클러스터 수준 리소스이다.
Available 상태는 아직 클레임이 생성되어 바인딩되지 않았음을 의미한다.

 

볼륨 클레임을 생성하면, 그 조건에 맞는 볼륨을 바로 바인딩한다. (클레임이 있는 네임스페이스의 파드에서만 사용 가능)
(볼륨 자체는 클러스터 수준 리소스이므로 특정 네임스페이스 내 생성이 불가하다)

$ cat mongodb-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc 
spec:
  resources:
    requests:
      storage: 1Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: ""  // 이 부분은 동적 프로비저닝에서 배운다

$ kubectl create -f mongodb-pvc.yaml 
persistentvolumeclaim/mongodb-pvc created

$ kubectl get pv                    
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
mongodb-pv   1Gi        RWO,ROX        Retain           Bound    default/mongodb-pvc                           3m44s

$ kubectl get pvc                   
NAME          STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongodb-pvc   Bound    mongodb-pv   1Gi        RWO,ROX                       13s

접근모드는 다음과 같이 동시 사용 가능한 워커 노드 수를 중심으로 구분된다.

  • RWO, ReadWriteOnce : 단일 노드가 읽기, 쓰기용으로 마운트
  • ROX, ReadOnlyMany : 다수 노드가 읽기용으로 마운트
  • RWX, ReadWriteMany : 다수 노드가 읽기, 쓰기용으로 마운트

이제 해당 볼륨을 사용하는 파드를 만들어 데이터를 확인해보자.
파드 볼륨에서 퍼시스턴트 볼륨 클레임을 참조하도록 설정한다.

$ cat mongodb-pod-pvc.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mongodb 
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: mongodb-data
    persistentVolumeClaim:
      claimName: mongodb-pvc  // 클레임 참조

$ kubectl create -f mongodb-pod-pvc.yaml 
pod/mongodb created

$ kubectl exec -it mongodb -- mongo
MongoDB shell version v5.0.6
...

> use mystore
switched to db mystore

> db.foo.find()
{ "_id" : ObjectId("620d25f99f07914ea504c1af"), "name" : "foo" }

> exit
bye

 

퍼시스턴트 볼륨 클레임과 퍼시스턴트 볼륨의 장점

애플리케이션 개발자에게 인프라 스토리지를 가져오는 간접적인 방식을 사용하도록 돕는다.
볼륨 클레임과 볼륨을 생성하는 추가 절차가 필요한 대신 스토리지 기술을 알지 않아도 된다.
또한, 다른 쿠버네티스 클러스터에서도 사용 가능하다. 접근모드와 스토리지 용량만 정해주면 요청이 가능하다.

 

퍼시스턴트 볼륨 재사용 실험

$ kubectl delete pod mongodb 
pod "mongodb" deleted

$ kubectl delete pvc mongodb-pvc 
persistentvolumeclaim "mongodb-pvc" deleted

$ kubectl get pvc               
No resources found in default namespace.

$ kubectl create -f mongodb-pvc.yaml 
persistentvolumeclaim/mongodb-pvc created

$ kubectl get pvc                   
NAME          STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongodb-pvc   Pending                                                     3s

$ kubectl get pv 
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM                 STORAGECLASS   REASON   AGE
mongodb-pv   1Gi        RWO,ROX        Retain           Released   default/mongodb-pvc                           14m

클레임과 파드를 삭제한 뒤에 클레임을 재생성하더라도, 기존 퍼시스턴트 볼륨이 해당 클레임과 다시 바인딩되지 않고, Released 상태가 된다. 이미 볼륨을 사용해 데이터를 가지고 있기 때문에, 클러스터 관리자가 기존 볼륨을 완전히 비우지 않으면 새로운 클레임에 바인딩할 수 없다는 것이다.

 

퍼시스턴트 볼륨 다시 클레임하기

  1. 수동으로 클레임하는 방식
    퍼시스턴트 볼륨이 클레임 해제 시에도 콘텐츠를 유지하게 하는 방법은,
    persistentVolumeClaimPolicy를 Retain으로 설정하는 방식이다.
    이럴 경우, 수동으로 재사용하기 위해서는 기존 퍼시스턴트 볼륨 리소스를 삭제 후 재생성해야 한다.
  2. 자동으로 클레임하는 방식
    1. Recycle
      다른 리클레임 정책 중 하나로, 볼륨의 콘텐츠를 삭제하고 리클레임되도록 사용가능하게 만드는 정책이다.
      여러번 재사용이 가능한 퍼시스턴트 볼륨이 된다.
    2. Delete
      다른 리클레임 정책 중 하나로 기반 스토리지를 삭제한다.

정책 옵션의 경우 각 상황별로 지원 가능 여부가 상이함에 유의하자.

 

동적 프로비저닝

클러스터 관리자 입장에서 실제 스토리지를 미리 프로비저닝 해두어야 했는데,
퍼시스턴트의 동적 프로비저닝을 활용하여 자동으로 수행하면 클러스터 관리자의 업무 부담도 감소한다.

직접 볼륨을 생성하지 않고, 퍼시스턴트 볼륨 프로비저너를 배포해,
사용자가 선택 가능한 볼륨 타입을 하나 이상의 스토리지클래스 오브젝트로 정의하면,
사용자가 퍼시스턴트볼륨클레임에서 스토리지클래스를 참조하여,
프로비저너가 이를 프로비저닝할 시에 처리한다.

즉, 사용자의 클레임 요청 시마다 자동으로 볼륨을 생성하여, 수급이 원활해진다.

또한, 스토리지 클래스는 클레임 이름으로 스토리지 클래스를 참조하기 때문에, 이름만 동일하면 다른 클러스터 간 이식이 가능하다는 장점이 있다.

$ cat storageclass-fast-hostpath.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath   // 프로비저닝 용 볼륨 플러그인
parameters:                             // 프로비저너 전달 파라미터
  type: pd-ssd

스토리지클래스 리소스가 볼륨 클레임의 요청 시 어떤 프로비저너가 사용될지를 지정한다.

$ kubectl create -f storageclass-fast-hostpath.yaml
storageclass.storage.k8s.io/fast created

$ cat mongodb-pvc-dp.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc 
spec:
  storageClassName: fast
  resources:
    requests:
      storage: 100Mi
  accessModes:
    - ReadWriteOnce

앞서 생성한 스토리지 클래스를 참고하도록 클레임을 작성한다.

$ kubectl create -f mongodb-pvc-dp.yaml 
persistentvolumeclaim/mongodb-pvc created

$ kubectl get pvc mongodb-pvc 
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongodb-pvc   Bound    pvc-04ef71a7-78a8-4545-9687-8e0ad439a52f   100Mi      RWO            fast           12s

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM                 STORAGECLASS   REASON   AGE
mongodb-pv                                 1Gi        RWO,ROX        Retain           Released   default/mongodb-pvc                           27m
pvc-04ef71a7-78a8-4545-9687-8e0ad439a52f   100Mi      RWO            Delete           Bound      default/mongodb-pvc   fast                    26s

그뒤 클레임을 생성하면, 스토리지클래스의 정보가 들어간 것을 볼 수 있고, 그에 맞게 퍼시스턴트 볼륨도 생성되었다.

스토리지 클래스를 지정하지 않은 동적 프로비저닝

$ kubectl get sc
NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
fast                 k8s.io/minikube-hostpath   Delete          Immediate           false                  6m42s
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  22d

$ kubectl get sc standard -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"labels":{"addonmanager.kubernetes.io/mode":"EnsureExists"},"name":"standard"},"provisioner":"k8s.io/minikube-hostpath"}
    storageclass.kubernetes.io/is-default-class: "true" // 스토리지 클래스를 기본값으로 표시한다. 기본 스토리지 클래스는 명시적으로 지정하지 않을 경우 동적 프로비저닝에 사용됨을 의미한다.
  creationTimestamp: "2022-01-25T08:15:19Z"
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
  name: standard
  resourceVersion: "331"
  uid: f69492e1-37c3-476b-bf64-1e2dbcf1d60d
provisioner: k8s.io/minikube-hostpath
reclaimPolicy: Delete
volumeBindingMode: Immediate

정의를 보면, 이 스토리지 클래스를 기본값으로 표시한다.
기본 스토리지 클래스는 명시적으로 지정하지 않을 경우 동적 프로비저닝에 사용됨을 의미한다.

$ cat mongodb-pvc-dp-nostorageclass.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc2 
spec:
  resources:
    requests:
      storage: 100Mi
  accessModes:
    - ReadWriteOnce

$ kubectl create -f mongodb-pvc-dp-nostorageclass.yaml
persistentvolumeclaim/mongodb-pvc2 created

$ kubectl get pvc mongodb-pvc2 
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongodb-pvc2   Bound    pvc-44a26cd7-083d-4c82-b044-f22534108fd6   100Mi      RWO            standard       16s

$ kubectl get pv pvc-44a26cd7-083d-4c82-b044-f22534108fd6
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
pvc-44a26cd7-083d-4c82-b044-f22534108fd6   100Mi      RWO            Delete           Bound    default/mongodb-pvc2   standard                29s

클레임에 명세되지 않았더니 기본값 스토리지클래스로 동적 프로비저닝이 이루어졌음이 확인 가능하다.

 

퍼시스턴트볼륨클레임을 미리 프로비저닝된 퍼시스턴트 볼륨으로 바인딩 강제화하기

$ cat mongodb-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc 
spec:
  resources:
    requests:
      storage: 1Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: ""

하단의 스토리지클래스 이름을 빈 문자열로 지정하면, 볼륨 클레임이 새로운 퍼시스턴트볼륨을 동적 프로비저닝하는 대신, 미리 프로비저닝된 퍼시스턴트 볼륨에 바인딩한다. 수동으로 바인딩하여 동적 프로비저너의 간섭을 막고 진행했다.

 

 

 

https://kubernetes.io/ko/docs/concepts/storage/volumes/

 

볼륨

컨테이너 내의 디스크에 있는 파일은 임시적이며, 컨테이너에서 실행될 때 애플리케이션에 적지 않은 몇 가지 문제가 발생한다. 한 가지 문제는 컨테이너가 크래시될 때 파일이 손실된다는 것

kubernetes.io