CKA #8 Storage

· Infra, Kubernetes, CKA

Index

Storage Drivers vs. Volume Drivers

Docker Storage Driver와 파일 시스템

Docker는 로컬 파일 시스템을 어떻게 저장하는가?

도커를 설치하면 /var/lib/docker/docker/ 디렉토리가 생성된다. 도커는 기본적으로 모든 데이터를 이 디렉토리에 저장한다. 데이터란 호스트에서 실행되는 이미지 및 컨테이너 관련 파일을 말한다.

모든 파일은 컨테이너 폴더에 저장되고 이미지와 관련된 파일은 이미지 폴더에 저장된다. 도커 컨테이너에 의해 생성된 모든 볼륨은 볼륨 폴더 아래에 저장된다.

도커가 자신의 파일과 포맷을 어디에 저장하는지 이해해보자.

도커의 레이어 구조를 이해해야 한다. 도커는 이미지를 구축할 때 레이어드 아키텍쳐에 구축한다. 도커 파일의 각각의 명령어는 레이어를 생성한다.

그리고, 이미지를 컨테이너로 실행하면, 이미지 위에 컨테이너 레이어를 얹는다. 이미지 레이어의 파일들은 ReadOnly이다. 컨테이너 레이어는 Read와 Write가 가능하다. 이미지 레이어는 여러 컨테이너 사이에서 공유될 수 있기 때문이다. 이미지 레이어의 파일을 수정하고 싶다면, CopyOnWrite 형태로, 이미지 레이어의 파일은 컨테이너 레이어로 가져와서 수정할 수 있게 된다.

컨테이너를 지우면, 컨테이너 레이어가 삭제되면서 컨테이너 레이어 내의 모든 파일이 삭제된다. 만약 이 파일들을 유지하고 싶다면, 볼륨을 추가할 수 있다.

docker volume create data_volume
# /var/lib/docker/volumes/docker_volume 디렉토리가 생성됨

docker run -v data_volume:/var/lib/mysql mysql
docker run -v 볼륨이름:컨테이너디렉토리 이미지이름

docker run --mount type=bind, source=볼륨위치, target=컨테이너내부디렉토리 이미지이름

Docker’s Storage Drivers

Docker’s Volume Drivers

docker run -it \
  --name mysql
  --volume-driver rexray/ebs
  --mount src=ebs-vol, target=/var/lib/mysql
  mysql

Rex-Ray EBS를 쓰면 아마존 EBS를 볼륨으로 쓸 수 있다.

Container Storage Interface

Volumes in k8s

Docker 컨테이너는 일시적인 용도로 사용된다. 데이터 처리가 필요할 때 호출되고, 끝나면 폐기된다. 컨테이너 안의 데이터도 마찬가지다. 컨테이너의 데이터는 파괴되는 것이 보통이다. 볼륨을 이용하면 컨테이너의 데이터를 영구적으로 보존할 수 있다. 컨테이너가 삭제되어도 데이터는 남아 있다. 쿠버네티스에서는 어떻게 동작할까?

쿠버네티스에서 생성된 파드는 일시적이다. 파드가 데이터를 처리하고 파드가 삭제될 때 데이터도 삭제된다. 파드에 볼륨을 부착하여 이를 해결한다. 파드에서 생성된 데이터가 볼륨에 저장되고 파드가 삭제되도 볼륨은 남아있다.

apiVersion: v1
kind: Pod
metadata:
  name: random-number-generator 
spec:
  containers:
  - image: alpine
    name: alpine
    command: ["/bin/sh", "-c"]
    args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
  volumes:
  - name: data-volume
    hostPath:
      path: /data
      type: Directory

Volume 관계이미지 출처: Certified Kubernetes Administrator (CKA) with Practice Tests(Udemy)

싱글 노드에서 로컬 디렉토리로 볼륨을 마운트한다. 이제 파드가 삭제되어도 노드에 볼륨 데이터가 남아있다.

그런데 다중 노드 클러스터에서 이 방법을 쓸 수 없다. 파드가 각기 다른 노드에서 /data 디렉토리를 쓰면서 동일한 데이터가 저장되어 있길 기대하기 때문이다.

그래서 쿠버네티스 레벨에서는 다양한 유형의 저장소 솔루션을 지원한다.

volumes:
- name: data-volume
awsElasticBlockStore:
    volumeID: <volume-id>
    fsType: ext4

Persistent Volumes

앞선 방법은 파드 정의 yaml 파일에 volumes에 대한 구성 정보가 들어가게 된다. 그런데 큰 환경에서 많은 파드를 배포한다고 가정했을 때, 파드에 따라 매번 저장소를 구성하고 설정해주어야 하는 문제가 발생한다. 사용자는 해당 파드를 배포할 때 파드 정의 파일에 저장소를 구성해야 한다. 수정사항도 작성해주어야 하는 문제가 있다.

이러한 문제를 해결하고자, 중앙에서 거대한 저장소 하나를 관리하는 방법이 있다. 이것이 Persistent Volume이다.

PVC와 PV이미지 출처: Certified Kubernetes Administrator (CKA) with Practice Tests(Udemy)

# pv-definition.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vol1
spec:
  persistentVolumeReclaimPolicy: Retain
  accessModes:
  - ReadWriteOnce # (ReadOnlyMany, ReadWriteOnce, ReadWriteMany)
  capacity:
    storage: 1Gi
  hostPath:
    path: /tmp/data #  <Node Local Directory>를 적는 곳. 상용에서는 안씀
  awsElasticBlockStore: # 실제로는 이런 것을 위주로 ...
    volumeID: <volume-id>
    fsType: ext4
kubectl create -f pv-definition.yaml
kubectl get persistentvolume

Persistent Volume Claims

Persistent Volume을 사용하기 위해 Persistent Volume Claims를 만든다.

Persistent Volume Claim이 생성되면 쿠버네티스는 Persistent Volume Claim의 요청과 속성을 읽고, Persistent Volume Claim을 Persistent Volume에 Bind한다. 모든 PVC은 단일 PV에 바인딩된다. 쿠버네티스는 요구대로 충분한 Capacity를 확보하고자 한다.

요구사항에는 다음과 같은 것들이 있다.

특정한 볼륨을 쓰고 싶다면, 라벨과 셀렉터를 이용해 바인딩할 수 있다.

# in PV
labels:
  name: my-pv
  
# in PVC
selector:
  matchLabels:
    name: my-pv
# pvc-definition.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi

--
# pv-definition.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vol1
spec:
  storageClassName: local-storage
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 1Gi
  awsElasticBlockStore:
    volumeID: <volume-id>
    fsType: ext4
kubectl create -f pvc-definition.yaml

PVC가 생성되면 쿠버네티스는 PV 리스트를 확인하고, 할당가능한 볼륨을 바인딩한다.

kubectl delete persistentvolumeclaim myclaim

만약 PVC를 삭제한다면, PV는 어떻게 될까? → 옵션 선택에 따라 달라진다.

persistentVolumeReclaimPolicy: Retain | Delete | Recycle

Using PVC in Pods

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

PV, PVC, Pod Definitions이미지 출처: Certified Kubernetes Administrator (CKA) with Practice Tests(Udemy)

Storage Class

만약 클라우드 플랫폼의 스토리지를 사용하려고 한다면, PV가 생성되기 전에 클라우드 플랫폼의 디스크를 프로비저닝해야 한다는 문제가 있다.

gcloud beta compute disks create --size 1GB --region us-east1 pd-disk

이러한 작업을 정적 프로비저닝 볼륨이라고 한다. 그런데 자동으로 프로비저닝시킬 수 있다면? 그게 바로 Storage Class다.

구글 클라우드 같은 곳에서 저장소를 자동으로 프로비저닝하고, 요구사항에 따라 파드에 연결하는 것을 볼륨의 동적 프로비저닝이라고 한다.

# sc-definition.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: google-storage
provisioner: kubernetes.io/gce-pd

StorageClass가 있어서 PV가 필요하지 않아졌다. StorageClass가 알아서 다 생성해줄 것이기 때문. PVC가 PV가 아닌 StorageClass를 쓰려면 정의 파일에 storageClassName을 추가해주어야 한다.

# pvc-definition.yaml
...
spec:
  storageClassName: google-storage
  ...

PVC가 생성되면, PVC에 적힌 스토리지 클래스는 프로비저너를 이용해 클라우드 플랫폼에 요구되는 사이즈의 새 디스크를 프로비저닝하고, PV를 생성해서 PV를 PVC와 연결한다.

여기서 중요한 것은, PV를 자동으로 생성한다는 점이다. PV를 정의하지 않았다고 해서 쓰지 않는 것이 아니다. 여전히 PV를 사용한다.

프로비저너는 여러 종류가 있다. AzureFile, AzureDisk, Cinder, AWSElasticBlockStore, GCEPersistentDisk…