쿠버네티스 인 액션 - 볼륨 : 컨테이너에 디스크 스토리지 연결 (5)

hyeokjin·2022년 9월 9일
0

kubernetes

목록 보기
5/9
post-thumbnail

앞의 세 개 장에서 파드와 레플리케이션컨트롤러, 레플리카셋, 데몬셋, 잡 서비스와 같은 파드와 상호작용하는 쿠버네티스 리소스를 소개했다. 이제 파드 내부로 다시 돌아가 컨테이너가 어떻게 외부 디스크 스토리지에 접근하는지, 어떻게 컨테이너 간에 스토리지를 공유하는지를 살펴보자.

볼륨소개

쿠버네티스 볼륨은 파드의 구성 요소로 컨테이너와 동일하게 파드 스펙에서 정의된다. 볼륨은 독립적인 쿠버네티스 오브젝트가 아니므로 자체적으로 생성, 삭제될 수 없다.
볼륨은 파드의 모든 컨테이너에서 사용 가능하지만 접근하려는 컨테이너에서 각각 마운트돼야 한다. 각 컨테이너에서 파일시스템의 어느 경로에나 볼륨을 마운트할 수 있다.

예제의 볼륨 생성

컨테이너 세 개가 있는 파드를 가정해보자.

첫번째 컨테이너(웹서버)는 /var/htdocs 디렉터리에서 HTML 페이지를 서비스하고 /var/logs에 액세스 로그를 지정하는 웹 서버를 실행한다.

두번째 컨테이너(콘텐츠에이전트)는 /var/html에 HTML 파일을 생성하는 에이전트를 실행한다.

세번째 컨테이너(로그순환기)는 /var/logs 디렉터리의 로그를 처리한다.

여기서 publicHTML이라는 볼륨을 생성하고 첫번째 컨테이너의 /var/htdocs 와 두번째 컨테이너의 /var/html 에 마운트 시킨다.
이 단일 볼륨을 이런 방식으로 마운트하면 콘텐츠 생성기가 작성한 내용을 웹 서버가 서비스할 수 있다.
마찬가지로 첫번째 컨테이너의 /var/logs와 세번째 컨테이너의 /var/logs 을 볼륨logVol을 만들어 마운트 시킨다.
컨테이너에서 접근하려면 파드에서 볼륨을 정의하는 것만으로 충분하지 않고 VolumeMount를 컨테이너 스펙에 정의해야한다.

이 예제의 두 볼륨은 빈 상태로 초기화되므로 emptyDir 유형의 볼륨을 사용할 수 있다.
쿠버네티스는 볼륨을 초기화하며 외부 소스의 내용을 채우거나, 볼륨 내부에 기존에 존재하는 디렉터리를 마운트하는 것과 같은 다른 유형의 볼륨도 지원한다.
볼륨을 채우거나 마운트하는 프로세스는 파드의 컨테이너가 시작되기 전에 수행된다.

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

단일 컨테이너에서도 볼륨은 유용하지만 먼저 하나의 파드에 있는 여러 컨테이너에서 데이터를 공유하는 방법을 살펴보자.

emptyDir 볼륨 사용

가장 간단한 볼륨 유형은 emptyDir 볼륨으로 어떻게 파드에 볼륨을 정의하는지 첫 번째 예제에서 살펴보자.
이름에서 알 수 있듯이 볼륨이 빈 디렉터리로 시작된다. 파드에 실행중인 애플리케이션은 어떤 파일이든 볼륨에 쓸 수 있다. 볼륨의 라이프사이클이 파드에 묶여 있으므로 파드가 삭제되면 볼륨의 콘텐츠는 사라진다.

앞에 설명한 예제에서 웹 서버, 콘텐츠 에이전트, 로그 순환기가 볼륨 두 개를 공유한다고 했다.
이를 좀 더 단순화 해보자.
단지 웹 서버 컨테이너와 콘텐츠 에이전트, HTML을 위한 단일 볼륨으로 파드를 구성해보자.

Nginx를 웹 서버로 사용하고 유닉스 fortune 명령으로 HTML 콘텐츠를 생성한다.
Fortune 명령은 실행할 때마다 임의의 인용문을 출력한다. 매 10초마다 fortune 명령을 실행하고 출력을 index.html에 저장하는 스크립트를 생성한다.

도커허브에서 사용가능한 Nginx 이미지를 찾아 직접 fortune 이미지를 생성하거나, 미리 생성해놓은 이미지로 도커 허브에 푸시해놓은 luksa/fortune 이미지를 사용할 수 있다.

Fortune 컨테이너 이미지는 다음과 같다.

#!/bin/bash
trap "exit" SIGINT
while :
do
  echo $(data) Writing fortune to /var/htdocs/index.html
  /usr/games/fortune > /var/htdocs/index.html
  sleep 10
done

그런 다음 동일 디렉터리에서 Dokerfile을 생성하고 다음 내용을 작성한다.

FROM ubuntu:latest
RUN apt-get update ; apt-get -y install fortune
ADD fortuneloop.sh /bin/fortuneloop.sh
ENTRYPOINT /bin/fortuneloop.sh

이미지가 실행되면 fortuneloop.sh 스크립트가 실행된다.

다음으로 이미지를 빌드하고, 도커 허브에 업로드한다.
$ docker build -t luksa/fortune .
$ docker push luksa/fortune

파드 생성하기

이제 파드 매니페스트를 생성하자.

fortune-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: fortune
spec:
  containers:
  - image: luksa/fortune	# 첫번째 컨테이너는 html-generator라고 이름을 짓고 luksa/fortune 이미지를 실행시킨다.
    name: html-generator
    volumeMounts:		# html이라는 이름의 볼륨을 컨테이너의 /var/htdocs에 마운트한다.
    - name: html
      mountPath: /var/htdocs	
  - image: nginx:alpine		# 두번째 컨테이너는 web-server라고 이름을 짓고 nginx:alpine 이미지를 실행시킨다.
    name: web-server
    volumeMounts:		# 위와 동일한 볼륨을 /usr/share/nginx/html에 읽기 전용으로 마운트한다.
    - name: html
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:			# html이라는 단일 emptyDir 볼륨을 위의 컨테이너 두 개에 마운트 한다.
  - name: html
    emptyDir: {}

파드는 컨테이너 두 개와 각 컨테이너에 각기 다른 경로로 마운트된 단일 볼륨을 갖는다.
html-generator 컨테이너가 시작하면 매 10초마다 fortune 명령의 결과를 /var/htdocs/index.html에 쓰기 시작한다. 볼륨이 /var/htdocs에 마운트 됐으므로 index.html 파일은 컨테이너의 최상단 레이어가 아닌 볼륨에 쓰여진다.
web-server 컨테이너가 시작 하자마자 컨테이너는 /usr/share/nginx/html 디렉터리의 HTML 파일을 서비스하기 시작한다. 볼륨이 정확한 경로에 마운트됐으므로, Nginx는 fortune 루프를 실행하는 컨테이너가 작성한 index.html 파일을 서비스한다.
결과적으로 클라이언트가 파드의 포트 80으로 보낸 HTTP 요청은 fortune 메시지를 응답으로 받는다.

실행 중인 파드 보기

Fortune 메시지를 보려면 파드의 접근을 활성화해야 한다. 이 작업은 로컬 머신의 포트를 파드로 포워딩하면 된다.

$ kubectl port-forward fortune 8080:80
Forwarding from 127.0.0.1:8080 -> 80

로컬 머신의 포트 8080으로 Nginx 서버에 접근할 수 있다.

$ curl http://localhost:8080
You will live to see your grandchildren.

emptyDir을 사용하기 위한 매체 지정하기

볼륨으로 사용한 emptyDir은 파드를 호스팅하는 워커 노드의 실제 디스크에 생성되므로 노드 디스크가 어떤 유형인지에 따라 성능이 결정됐다.
반면 쿠버네티스에 emptyDir을 디스크가 아닌 메모리를 사용하는 fmpfs 파일시스템으로 생성하도록 요청할 수 있다. 이 작업을 위해 다음과 같이 emptyDir의 medium을 Memory로 지정한다.

volumes:
  - name: html
    emptyDir:
	  medium: Memory # 이 emptyDir의 파일들은 메모리에 저장될 것이다.

emptyDir 볼륨은 가장 단순한 볼륨의 유형이며, 빈 디렉터리가 생성된 후 데이터로 채워진다.

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

대부분의 파드는 호스트 노드를 인식하지 못하므로 노드의 파일시스템에 있는 어떤 파일에도 접근하면 안 된다.
그러나 특정 시스템 레벨의 파드는 노드의 파일을 읽거나 파일시스템을 통해 노드 디바이스를 접근하기 위해 노드의 파일시스템을 사용해야 한다. 쿠버네티스는 hostPath 볼륨으로 가능케 한다.

hostPath 볼륨 소개

hostPath 볼륨은 노드 파일시스템의 특정 파일이나 디렉터리를 가리킨다.
동일 노드에 실행 중인 파드가 hostPath 볼륨의 동일 경로를 사용 중이면 동일한 파일이 표시된다.
hostPath 볼륨의 콘텐츠는 파드가 종료되면 삭제되지 않는다. 파드가 삭제되면 다음 파드가 호스트의 동일 경로를 가리키는 hostPath 볼륨을 사용하고, 이전 파드와 동일한 노드에 스케줄링된다는 조건에서 새로운 파드는 이전 파드가 남긴 모든 항목을 볼 수 있다.

hostPath 볼륨을 사용하는 시스템 파드 검사하기

hostPath를 적절하게 사용하는 방법을 살펴보자. 새로운 파드를 생성하는 대신 이미 이 볼륨 유형을 사용하는 시스템 전역 파드가 있는지 확인해보자.
여러개의 시스템 파드가 kube-system 네임스페이스에서 실행 중인 것을 볼 수 있다.

$ kubectl get pods --namespace kube-system
NAME                                              READY   STATUS    RESTARTS   AGE
fluentbit-gke-hdbqp                               2/2     Running   0          3d6h
fluentbit-gke-jlb9w                               2/2     Running   0          3d6h
fluentbit-gke-r5r65                               2/2     Running   0          3d6h
gke-metrics-agent-2zzwp                           1/1     Running   0          3d6h
gke-metrics-agent-g5zhn                           1/1     Running   0          3d6h
gke-metrics-agent-rt78t                           1/1     Running   0          3d6h
konnectivity-agent-859b9dc845-cf6cn               1/1     Running   0          46h
konnectivity-agent-859b9dc845-k2sjc               1/1     Running   0          3d6h
konnectivity-agent-859b9dc845-wgrm8               1/1     Running   0          3d6h
konnectivity-agent-autoscaler-555f599d94-zsrr4    1/1     Running   0          46h
kube-dns-85df8994db-7hcbm                         4/4     Running   0          3d6h
kube-dns-85df8994db-nnf9d                         4/4     Running   0          3d6h
kube-dns-autoscaler-f4d55555-t7gqb                1/1     Running   0          46h
kube-proxy-gke-kubia-default-pool-c2f41b01-37p5   1/1     Running   0          3d6h
kube-proxy-gke-kubia-default-pool-c2f41b01-3gd0   1/1     Running   0          3d6h
kube-proxy-gke-kubia-default-pool-c2f41b01-wgt5   1/1     Running   0          3d6h
l7-default-backend-69fb9fd9f9-68wcr               1/1     Running   0          3d6h
metrics-server-v0.4.5-fb4c49dd6-ctqbm             2/2     Running   0          3d6h
pdcsi-node-dvchv                                  2/2     Running   0          3d6h
pdcsi-node-qrx7q                                  2/2     Running   0          3d6h
pdcsi-node-xj92s                                  2/2     Running   0          3d6h
event-exporter-gke-5479fd58c8-j7b8z               2/2     Running   0          46h

첫번째로 조회된 파드가 어떤 종류의 볼륨을 사용하는지 살펴보자

$ kubectl describe po fluentbit-gke-hdbqp --namespace kube-system
Name:                 fluentbit-gke-hdbqp
Namespace:            kube-system
...
Volumes:
  varrun:
    Type:          HostPath (bare host directory volume)
    Path:          /var/run/google-fluentbit/pos-files
    HostPathType:
  varlog:
    Type:          HostPath (bare host directory volume)
    Path:          /var/log
    HostPathType:
  varlibkubeletpods:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/kubelet/pods
    HostPathType:
  varlibdockercontainers:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/docker/containers
    HostPathType:
 ...

파드가 노드의 /var/log와 /var/lib/docker/containers 디렉터리등 접근하기 위해 hostPath를 사용한다.
다른 파드를 살펴보면 대부분이 노드의 로그파일이나 kubeconfig(쿠버네티스 구성파일), CA 인증서를 접근하기 위해 이 유형의 볼륨을 사용한다는 것을 볼 수 있다.

퍼시스턴트 스토리지 사용

파드에서 실행 중인 애플리케이션이 디스크에 데이터를 유지해야 하고 파드가 다른 노드로 재스케줄링된 경우에도 동일한 데이터를 사용해야 한다면
지금까지 언급한 볼륨 유형은 사용할 수 없다. 이러한 데이터는 어떤 클러스터 노드에서도 접근이 필요하기 때문에 NAS 유형에 저장돼야 한다.

영구 데이터를 허용하는 볼륨을 알아보기 위해 MongoDB를 실행하는 파드를 생성해보자

GCE 퍼시스턴트 디스크를 파드 볼륨으로 사용하기

GCE에 클러스터 노드가 실행 중인 구글 쿠버네티스 엔진에서 예제를 실행한다면 GCE 퍼시스턴트 디스크를 기반 스토리지 매커니즘으로 사용한다.
쿠버네티스 기반 스토리지를 자동 프로비저닝이 가능하지만 먼저 수동으로 프로비저닝하는 방법을 살펴보면서 내부에 무슨 일이 일어나는지 살펴보자.

GCE 퍼시스턴트 디스크 생성하기

먼저 GCE 퍼시스턴트 디스크를 생성하는 것부터 시작한다. 쿠버네티스는 클러스터가 있는 동일한 영역에 생성한다.
어떤 영역에 클러스터를 생성했는지 기억하지 못한다면 다음 gcloud 명령으로 쿠버네티스 클러스터를 조회해보자.

$ gcloud container clusters list
NAME: kubia
LOCATION: us-west1-a
MASTER_VERSION: 1.22.11-gke.400
MASTER_IP: 34.105.79.56
MACHINE_TYPE: e2-medium
NODE_VERSION: 1.22.11-gke.400
NUM_NODES: 3
STATUS: RUNNING

명령의 결과는 클러스터가 us-west1-a 영역에 생성됐다는 것을 표시하므로 GCE 퍼시스턴트 디스크도 동일한 영역에 생성해야 한다.

$ gcloud compute disks create --size=10GiB --zone=us-west1-a mongodb
WARNING: You have selected a disk size of under [200GB]. This may result in poor I/O performance. For more information, see: https://developers.google.com/compute/docs/disks#performance.
Created [https://www.googleapis.com/compute/v1/projects/bright-vision-360512/zones/us-west1-a/disks/mongodb].

mongodb라고 이름 붙여진 10GiB 크기의 GCE 퍼시스턴트 디스크를 생성한다.

GCE 퍼시스턴트 디스크 볼륨을 사용하는 파드 생성하기

이제 물리 스토리지가 적절하게 구성됐으므로 MongoDB 파드에서 볼륨으로 사용할 수 있다.

mongodb-pod-gcepd.yaml


apiVersion: v1
kind: Pod
metadata:
  name: mongodb 
spec:
  volumes:
  - name: mongodb-data	# 볼륨의 이름 (볼륨을 마운트 할때 참조한다)
    gcePersistentDisk:	# 볼륨의 유형은 GCE 퍼시스턴트 디스크다
      pdName: mongodb	# 퍼시스턴트 디스크의 이름은 반드시 이전에 생성한 실제 퍼시스턴트 디스크와 일치해야 한다.
      fsType: ext4		# 파일시스템 유형은 EXT4(리눅스 파일시스템 유형 중 하나) 이다.
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db	# MongoDB가 데이터를 저장할 경로다.
    ports:
    - containerPort: 27017
      protocol: TCP

파드는 생성한 GCE 퍼시스턴트 디스크를 기반으로 한 단일 볼륨과 단일 컨테이너로 이뤄진다.
볼륨을 컨테이너 내부의 MongoDB가 데이터를 저장하는 /data/db에 마운트 한다.

MongoDB 데이터베이스에 도큐먼트를 추가해 퍼시스턴트 스토리지에 데이터 쓰기

파드를 생성해 컨테이너가 시작됐으므로 컨테이너 내부에 MongoDB 셸을 실행해 데이터 스토리지에 데이터를 쓰는 데 사용할 수 있다.

$ kubectl exec -it mongodb mongosh
Current Mongosh Log ID: 631b45f3e15c12d7777ee7fc
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.5.4
Using MongoDB:          6.0.1
Using Mongosh:          1.5.4
...

MongoDB는 JSON 도큐먼트 저장을 허용하므로 한 건의 데이터를 저장해 영구적으로 저장되는지와 파드가 다시 생성된 후에도 데이터를 가져올 수 있는지 살펴보자.

다음은 JSON 도큐먼트를 추가한다

test> use mystore
switched to db mystore

mystore> db.foo.insertOne({name: 'foo'})
{
  acknowledged: true,
  insertedId: ObjectId("631b46327e3da68f1b04cd18")
}

간단한 JSON 도큐먼트를 단일 속성으로 추가했다. 이제 find() 명령으로 추가한 도큐먼트를 확인한다.

mystore> db.foo.find()
[
  { _id: ObjectId("631b46327e3da68f1b04cd18"), name: 'foo' }
]

이제 도큐먼트는 GCE 퍼시스턴트 디스크에 저장돼 있어야 한다.

파드를 다시 생성하고 이전 파드가 저장한 데이터를 읽을 수 있는지 확인

MongoDB 셸을 종료하고 파드를 삭제하고 다시 생성해본다

> exit
$ kubectl delete pod mongodb
pod "mongodb" deleted

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

파드 컨테이너가 다시 가동되면 다시 MongoDB 셸을 실행해 이전에 저장한 도큐먼트를 검색해본다.

$ kubectl exec -it mongodb mongo
Current Mongosh Log ID: 631b4747265ff3b92bb57194
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.5.4
Using MongoDB:          6.0.1
Using Mongosh:          1.5.4
...

test> use mystore
switched to db mystore

mystore> db.foo.find()
[
  { _id: ObjectId("631b46327e3da68f1b04cd18"), name: 'foo' }
]

예상한 바와 같이 파드를 삭제하고 재 생성해도 데이터는 그대로 유지된다.

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

GCE 퍼시스턴트 디스크 볼륨을 생성했던 이유는 쿠버네티스 클러스터를 구글 쿠버네티스 엔진에서 실행 중이기 때문이다.
다른 곳에서 클러스터를 실행 중이라면 기반 인프라스트럭처에 따라 다른 유형의 볼륨을 사용해야 한다.

AWS Elastic Block Store 볼륨 사용하기

예를 들어 GCE 퍼시스턴트 볼륨 대신 awsElasticBlockStore를 사용하라면 아래와 같이 볼륨 정의를 변경해야한다.

mongodb-pod-aws.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mongodb-aws
spec:
  volumes:
  - name: mongodb-data
    awsElasticBlockStore:	# gcePersistentDisk 대신 awsElasticBlockStore 를 사용한다.
      volumeID: my-volume	# 생성한 EBS 볼륨의 ID를 지정
      fsType: ext4			# 파일시스템 유형은 EXT4로 이전과 같다.
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP

NFS 볼륨 사용하기

클러스터가 여러 대의 서버로 실행되는 경우 외장 스토리지를 볼륨에 마운트하기 위한 다양한 지원 옵션이 제공된다.
예를 들어 NFS 공유를 마운트하기 위해서 NFS 서버와 서버에서 익스포트 경로를 지정하면 된다.

mongodb-pod-nfs.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mongodb-nfs
spec:
  volumes:
  - name: mongodb-data
    nfs:				# 이 볼륨은 NFS 공유를 사용한다
      server: 1.2.3.4	# NFS 서버의 IP이다
      path: /some/path	# 서버의 익스포트된 경로다
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP

스토리지 기술 사용하기

각 볼륨 유형별로 필요한 속성의 세부 정보를 보려면 쿠버네티스 API 레퍼런스의 API 정의를 확인하거나 kubectl explain을 통해 정보를 찾아봐야 한다.
특정 스토리지 기술에 익숙하다면 explain 명령을 사용해 적절한 유형의 볼륨을 어떻게 마운트하고 파드에서 사용하는지 쉽게 확인할 수 있다.

인스트럭처 관련 정보를 파드 정의에 포함한다는 것은 파드 정의가 특정 쿠버네티스 클러스터에 밀접하게 연결됨을 의미한다.
동일한 파드 정의를 다른 클러스터에서는 사용할 수 없다. 이것이 바로 볼륨을 이런 방식으로 사용하는 것이 파드에 퍼시스턴트 스토리지를 연결하는 최적의 방법이 아닌 이유다
다음으로 넘어가 이를 어떻게 개선하는지 살펴보자

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

지금까지 살펴본 모든 퍼시스턴트 볼륨 유형은 파드 개발자가 실제 네트워크 스토리지 인프라스트럭처에 관한 지식이 있어야한다.
이상적으로는 쿠버네티스에 애플리케이션을 배포하는 개발자는 기저에 어떤 종류의 스토리지 기술이 사용되는지 알 필요가 없어야하고, 인프라스트럭처 관련 처리는 클러스터 관리자만의 영역이여야한다.
개발자가 애플리케이션을 위해 일정량 퍼시스턴트 스토리지를 필요로 하면 쿠버네티스에 요청할 수 있어야 한다. 시스템 관리자는 클러스터를 구성해 애플리케이션이 요구한 것을 제공할 수 있어야 한다.

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

인프라스트럭처의 세부 사항을 처리하지 않고 애플리케이션이 쿠버네티스 클러스터에 스토리지를 요청할 수 있도록 하기 위해 새로운 리소스 두 개가 도입됐다.
바로 퍼시스턴트볼륨과 퍼시스턴트볼륨클레임이다.

퍼시스턴트볼륨 생성

MongoDB 예제를 다시 살펴보자. 이전과 달리 파드에서 직접 GCE 퍼시스턴트 볼륨을 참조하지 않는다.
자신이 클러스터 관리자의 역할이라고 가정하고 GCE 퍼시스턴트 볼륨을 기반으로 한 퍼시스턴트볼륨을 생성한다.
그런 다음 애플리케이션 개발자의 역할이라 가정하고 퍼시스턴트볼륨을 클레임해서 이것을 파드에서 사용한다.

mongodb-pv-gcepd.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mongodb-pv
spec:
  capacity: 		# PersistentVolume 사이즈를 지정한다
    storage: 1Gi
  accessModes:		# 이 PersistentVolume(PV)는 단일 클라이언트의 읽기/쓰기용(ReadWriteOnce)이나 여러 클라이언트를 위한 읽기 전용(ReadOnlyMany)으로 마운트 한다.
    - ReadWriteOnce
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain		# 클레임이 해제된 후 퍼시스턴트볼륨을 유지하는 명령어다
  gcePersistentDisk:	# 퍼시스턴트볼륨은 이전에 생성한 GCE 퍼시스턴트 디스크를 기반으로 한다.
    pdName: mongodb
    fsType: ext4

퍼시스턴트볼륨을 생성할 때 관리자는 쿠버네티스에게 용량이 얼마나 되는지, 단일 노드나 동시에 다수 노드에 읽거나 쓰기가 가능한지 여부를 알려야 한다.
또한 쿠버네티스에게 퍼시스턴트볼륨이 해제되면 어떤 동작을 해야 할지 알려야 한다.

kubectl create 명령으로 퍼시스턴트볼륨을 생성하고 나면 클레임할 준비가 됐다.

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

아직 퍼시스턴트볼륨클레임을 생성하지 않았으므로 퍼시스턴트볼륨이 Available로 표시된다.

퍼시스턴트볼륨클레임 생성을 통한 퍼시스턴트볼륨 요청

퍼시스턴트볼륨클레임을 생성한다

mongodb-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc # 퍼시스턴트볼륨클레임의 이름으로 나중에 파드의 볼륨을 요청할 때 사용한다.
spec:
  resources:
    requests:
      storage: 1Gi	# 1GiB 스토리지를 요청한다.
  accessModes:		# 단일 클라이언트를 지원하는 스토리지로 읽기/쓰기를 모두 수행한다.
  - ReadWriteOnce
  storageClassName: ""	# 이 부분은 동적 프로비저닝 설정에 대해 추후에 설명한다.

퍼시스턴트볼륨클레임이 생성되자마자 쿠버네티스는 적절한 퍼시스턴트볼륨을 찾고 클레임에 바인딩한다.
퍼시스턴트볼륨의 용량은 퍼시스턴트볼륨클레임의 요청을 수용할 만큼 충분히 커야 한다.
추가로 볼륨 접근 모드는 클레임에서 요청한 접근 모드를 포함해야 한다. 이전에 요청한 퍼시스턴트 볼륨은 두 가지 요구 사항을 만족하므로 퍼시스턴트볼륨클레임에 바인딩 된다.

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

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

클레임이 퍼시스턴트볼륨에 Bound됐다고 나온다.

다음은 접근모드로 사용되는 약어다

  • RWO(ReadWriteOnce) : 단일 노드만이 읽기/쓰기용으로 볼륨을 마운트 할 수 있다.
  • ROX(ReadOnlyMany) : 다수 노드가 읽기으로 볼륨을 마운트 할 수 있다.
  • RWX(ReadWriteMany) : 다수 노드가 쓰기용으로 볼륨을 마운트 할 수 있다.

퍼시스턴트볼륨 조회하기

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

퍼시스턴트볼륨이 default/mongodb-pvc 쿨레임에 바인딩 됨을 보여준다.
default 부분은 클레임이 있는 네임스페이스다

파드에서 퍼시스턴트볼륨클레임 사용하기

이제 퍼시스턴트볼륨을 사용 중에 있다. 볼륨을 해제할 때까지 다른 사용자는 동일한 볼륨에 클레임을 할 수 없다.
파드 내부에서 볼륨을 사용하기 위해 파드 볼륨에서 이름으로 퍼시스턴트볼륨클레임을 참조한다.

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

계속해서 파드를 생성하고, 파드가 실제로 동일 퍼시스턴트볼륨과 기반 GCE 스토리지를 사용하는지 확인해본다.

$ kubectl exec -it mongodb mongo
Current Mongosh Log ID: 631b4747265ff3b92bb57194
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.5.4
Using MongoDB:          6.0.1
...

test> use mystore
switched to db mystore

mystore> db.foo.find()
[
  { _id: ObjectId("631b46327e3da68f1b04cd18"), name: 'foo' }
]

이전에 MongoDB에 저장한 도큐먼트를 가져올 수 있다.

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

개발자에게 인프라스트럭처 스토리지를 가져오는 간접적인 방식을 사용하여, 기저에 사용된 실제 스토리지 기술을 알 필요없다.
게다가 동일한 파드와 클레임 매니페스트는 인프라스트럭처와 관련된 어떤 것도 참조하지 않으므로 다른 쿠버네티스 클러스터에도 사용할 수 있다.
파드는 볼륨 중 하나에서 쉽게 해당 클레임을 이름으로 참조할 수 있다.

퍼시스턴트 볼륨 재사용

퍼시스턴트볼륨클레임을 삭제 후 다시 생성하면 어떻게 될까?

이미 볼륨을 사용했기 때문에 데이터를 가지고 있으므로 클러스터 관리자가 볼륨을 완전히 비우지 않으면 새로운 클레임에 바인딩할 수 없다. 클러스터 관리자가 볼륨을 비우지 않았다면 동일한 퍼시스턴트볼륨을 사용하는 새 파드는 다른 네임스페이스에서 클레임과 파드가 생성됐다고 할지라도 이전 파드가 저장한 데이터를 읽을 수 있다.

쿠버네티스에 persistentVolumeReclaimPolicy를 Retain으로 설정하면 퍼시스턴트볼륨이 이러한 동작을 할 수 있다. 쿠버네티스가 클레임이 해제돼도 볼륨과 콘텐츠를 유지하도록 한다.

다른 두 가지 리클레임 정책은 Recycle과 Delete다. Recycle은 볼륨의 콘텐츠를 삭제하고 볼륨이 다시 클레임될 수 있도록 볼륨을 사용 가능하게 만든다. 즉, 퍼시스트볼륨은 여러 번 다른 퍼시스턴트볼륨클레임과 다른 파드에서 재사용할 수 있다.
반대로 Delete 정책은 기반 스토리지를 삭제한다.

퍼시스턴트볼륨의 동적 프로비저닝

클러스터 관리자가 퍼시스턴트볼륨을 생성하는 대신 퍼시스턴트볼륨 프로비저너를 배포하고 시용자가 선택 가능한 퍼시스턴트볼륨클레임에서 스토리지클래스를 참조하면 프로비저너가 퍼시스턴트 스토리지를 프로비저닝할 때 이를 처리한다.

storageclass-fast-gcepd.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/gce-pd	# 퍼시스턴트볼륨 프로비저닝을 위해 사용되는 볼륨 플러그인이다.
parameters:		# 이런 파라미터가 프로비저너로 전달된다.
  type: pd-ssd

스토리지클래스 리소스는 퍼시스턴트볼륨클레임이 스토리지클래스에 요청할 때 어떤 프로비저너가 퍼시스턴트볼륨을 프로비저닝하는 데 사용돼야 할지를 지정한다.
스토리지 클래스에 정의된 파라미터들은 프로비저너에 전달되며, 파라미터는 각 프로비저너 플러그인마다 다르다.

퍼시스턴트볼륨클레임에서 스토리지 클래스 요청하기

스토리지클래스 리소스가 생성되면 사용자는 퍼시스턴트볼륨클레임의 이름에 스토리지클래스를 참조할 수 있다.

특정 스토리지클래스를 요청하는 PVC 정의를 생성해보자.
(PVC는 퍼시스턴트볼륨클레임, PV는 퍼시스턴트볼륨을 정의한다)

Mongdb-pvc 동적 프로비저닝을 사용하도록 수정한다.

mongodb-pvc-dp.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc 
spec:
  storageClassName: fast	# PVC는 사용자 정의 스토리지 클래스를 요청한다.
  resources:
    requests:
      storage: 100Mi
  accessModes:
    - ReadWriteOnce

크기와 접근 모드를 지정하는 것 외에도 퍼시스턴트볼륨클레임에 사용할 스토리지클래스를 지정해야 한다. 클레임을 생성하면 fast 스토리지클래스 리소스에 참조된 프로비저너가 퍼시스턴트볼륨을 생성한다.

다음으로 동적 프로비저닝된 PV와 생성된 PVC를 검사해보자

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

$ kubectl get pvc mongodb-pvc
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongodb-pvc   Bound    pvc-58a92f2e-58af-45d3-a046-928dc195a2bd   1Gi        RWO            fast           12s

VOLUME 열은 클레임에 바인딩된 퍼시스턴트볼륨을 표시한다. 퍼시스턴트볼륨을 조회해서 실제로 PV가 자동으로 생성됐는지 확인해본다.

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM                 STORAGECLASS   REASON   AGE
mongodb-pv                                 1Gi        RWO,ROX        Retain           Released   default/mongodb-pvc                           2d
pvc-58a92f2e-58af-45d3-a046-928dc195a2bd   1Gi        RWO            Delete           Bound      default/mongodb-pvc   fast                    21s

동적으로 프로비저닝된 퍼시스턴트볼륨을 볼 수 있다. 퍼시스턴트볼륨의 용량과 접근 모드가 PVC에서 요청한 것과 동일하다.

fast 스토리지클래스는 GCE 퍼시스턴트 디스크를 프로비저닝하는 kubernetes.io/gce-pd 프로비저너를 사용하도록 설정됐다.

다음 명령으로 디스크를 확인한다.

$ gcloud compute disks list
NAME: gke-kubia-default-pool-c2f41b01-37p5
LOCATION: us-west1-a
LOCATION_SCOPE: zone
SIZE_GB: 100
TYPE: pd-standard
STATUS: READY

NAME: gke-kubia-default-pool-c2f41b01-3gd0
LOCATION: us-west1-a
LOCATION_SCOPE: zone
SIZE_GB: 100
TYPE: pd-standard
STATUS: READY

NAME: gke-kubia-default-pool-c2f41b01-wgt5
LOCATION: us-west1-a
LOCATION_SCOPE: zone
SIZE_GB: 100
TYPE: pd-standard
STATUS: READY

NAME: mongodb
LOCATION: us-west1-a
LOCATION_SCOPE: zone
SIZE_GB: 10
TYPE: pd-standard
STATUS: READY

NAME: pvc-58a92f2e-58af-45d3-a046-928dc195a2bd
LOCATION: us-west1-a
LOCATION_SCOPE: zone
SIZE_GB: 1
TYPE: pd-ssd
STATUS: READY

확인한 바와 같이 마지막 퍼시스턴트 디스크의 이름은 동적으로 프로비저닝됐음을 나타내고 유형은 이전에 생성한 스토리지 클래스에서 지정한 대로 SSD임을 나타낸다.

스토리지 클래스 사용하는 법 이해하기

클러스터 관리자는 성능이나 기타 특성이 다른 여러 스토리지 클래스를 생성할 수 있다.
그런 다음 개발자는 생성할 각 클레임에 가장 적합한 스토리지 클래스를 결정한다.
스토리지클래스의 좋은 점은 클레임 이름으로 이를 참조한다는 것이다.
스토리지클래스를 배포하면 클러스터 사용자는 이전과 동일한 PVC 매니페스트와 파드 매니페스트를 배포할 수 있다.

스토리지클래스를 지정하지 않고 퍼시스턴트볼륨클레임 생성하기

storageClassName 속성을 지정하지 않고 PVC를 생성하면 구글 쿠버네티스 엔진에서는 pd-standard 유형의 GCE 퍼시스턴트 디스크가 프로비저닝된다. 예제를 살펴보자

mongodb-pvc-dp-nostorageclass.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc2 
spec:		# 이전 예제와 다르게 storageClassName 속성을 지정하지 않았다.
  resources:
    requests:
      storage: 100Mi
  accessModes:
    - ReadWriteOnce

이 PVC 정의는 단지 스토리지 사이즈와 의도된 접근 모드만을 지정하고 스토리지 클래스를 포함하지 않는다. PVC를 생성하면 기본값으로 표시된 스토리지 클래스가 사용된다

$ kubectl get pvc mongodb-pvc2
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mongodb-pvc2   Bound    pvc-009f9ff6-fe31-4e35-9f77-c155310b9a1f   1Gi        RWO            standard       6s

$ kubectl get pv pvc-009f9ff6-fe31-4e35-9f77-c155310b9a1f
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
pvc-009f9ff6-fe31-4e35-9f77-c155310b9a1f   1Gi        RWO            Delete           Bound    default/mongodb-pvc2   standard                43s

$ gcloud compute disks list
...
NAME: pvc-009f9ff6-fe31-4e35-9f77-c155310b9a1f
LOCATION: us-west1-a
LOCATION_SCOPE: zone
SIZE_GB: 1
TYPE: pd-standard
STATUS: READY

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

이 전 mongodb-pvc.yaml를 생성 할때 storageClassName 을 빈 문자열로 설정한 이유를 살펴보자

storageClassName 속성을 빈 문자열로 지정하지 않으면 미리 프로비저닝된 퍼시스턴트볼륨이 있다고 할지라도 동적 볼륨 프로비저너는 새로운 퍼시스턴트볼륨을 프로비저닝 할 것이다. 앞선 이 예제에서는 수동으로 미리 프로비저닝된 퍼시스턴트볼륨이 어떻게 클레임에 바인딩되는지를 보기 위한 것이므로, 동적 프로비저너가 간섭하는것을 막은 것이다.

요약

파드에 퍼시스턴트 스토리지를 연결하는 최적의 방법은 storageClassName을 명시적으로 지정하고 PVC와(PVC를 이름으로 참조한) 파드만 생성하는 것이다. 이외의 다른 모든 것은 동적 퍼시스턴트볼륨 프로비저너가 처리한다.

profile
노옵스를향해

0개의 댓글