반응형
Storage
Storage in Docker (건너뛰어도 됨)
- 쿠버네티스 같은 컨테이너 오케스트레이션 도구의 스토리지를 이해하려면 먼저 Docker에서 스토리지가 어떻게 작동하는지 이해하면 더 쉬워질 것이다.
- Docker에서는 스토리지 드라이버와 볼륨 드라이버가 있다.
- Docker를 설치하면 기본적으로 /var/lib/docker/ 경로에 폴더 구조를 생성한다.
- aufs, containers, image, volumes 등 여러 개의 폴더가 있다. 여기가 docker가 기본ㄱ밧으로 모든 데이터를 저장하는 곳이다.
- 여기서 데이터란 Docker 호스트에서 실행되는 이미지 및 컨테이너와 관련된 파일을 말한다.
- 먼저 Docker의 계층 구조를 이해해보자
- Docker는 이미지를 구축할 때 레이어드 아키텍처에 구축한다. Docker 파일의 각각의 지시 사항 줄은 Docker 이미지에 새 레이어를 만든다. (이전 레이어에서 변경된 것만)
- 예시로 Dockerfile1이 있다. 이렇게 한줄 한줄 마다를 레이어 구조로 저장하게 된다.
- 오른쪽 Dockerfile2 처럼 Dockerfile1에 있는 내용을 그대로 사용하는 경우에는 이미 Docker host에 이미지에 대한 구성 정보가 저장되어 있기 때문에 (캐싱) 도커는 다른 이미지를 생성할 때 더 빨리 만들고 디스크 공간을 효율적으로 절약할 수 있다.
- 그렇다면 컨테이너를 직접 실행하는 경우는 어떨까?
- 위와 같은 이미지 레이어를 이용해서 docker run 명령어로 컨테이너를 실행시킨 뒤 컨테이너 레이어 내에서 파일을 변경하는 경우 컨테이너가 파괴되면 이 레이어와 그 안에 저장된 모든 변화도 파괴된다.
- 컨테이너 내부의 파일을 수정할 수 있지만 수정한 파일을 저장하기 전에 Docker는 자동으로 해당 파일의 복사본을 RW 레이어에 만들고 RW 레이어의 파일에 수정 사항을 생성하게 된다.
- 이를 Copy-On-Write 메커니즘이라고 한다.
- docker build 명령어로 이미지를 재 구축하기 전까지는 기존에 Read Only로 구성된 이미지는 변경되지 않으며 애초에 Read Only로 구성된 이미지가 변경되는 것이 아닌 RW로 구성된 이미지 레이어에 있는 파일을 변경시킨 후 RW 이미지 레이어에 있는 파일을 Read Only 이미지에 포함시키는 것으로 생각하면 될것이다.
- 컨테이너를 제거하면 어떻게 될까? 컨테이너 레이어에 저장된 모든 데이터도 삭제가 된다.
- 그럼 컨테이너를 run하고 안에서 변경한 데이터를 유지하려면 어떻게 해야 하는가? 컨테이너에 영속적인 볼륨을 추가하면 된다.
- 이를 위해 먼저 docker volume create 명령을 사용해 볼륨을 만들면 된다. /var/lib/docker/volumes 디렉토리 아래에 볼륨이 생성되게 된다. 그 다음 docker run -v 옵션으로 생성했던 볼륨을 지정하면 docker 컨테이너의 쓰기 가능한 레이어 내에 이 볼륨을 마운트할 수 있다.
docker run -v <volume name>:<mount path> <image name>
- 위와 같이 명령어를 사용하면 되고, 만약 docker volume create로 볼륨이 생성되지 않은 상태에서 run -v 명령어를 사용하는 경우 자동으로 볼륨을 생성하고 이를 컨테이너에 마운트하기 때문에 걱정하지 않아도 된다.
- 생성된 볼륨은 /var/lib/docker/volumes 폴더에 있기 때문에 해당 디렉터리를 확인하면 모든 볼륨을 확인할 수 있다.
- 컨테이너에 스토리지를 마운트하는 방법은 볼륨 마운트와 바인드 마운트가 있다.
- 볼륨 마운트는 위에서 이야기한 것이고 바인드 마운트를 설명하고자 한다.
- docker host에 /data에 스토리지가 있고 기본 /var/lib/docker/bolumes 폴더가 아닌 해당 볼륨에 데잍터를 저장하고 싶다면 docker run -v 명령을 사용하지만 볼륨의 이름이 아닌 절대 경로로서 적어줘야 한다.
docker run -v <volume path>:<mount path> <image name>
# 예시
docker run -v /data/mysql:/var/lib/mysql mysql
- -v 옵션을 사용하는 것은 오래된 방식이라고 한다. 그래서 --mount 옵션을 대신해서 사용한다.
- mount 옵션은 type, source, target 의 옵션이 사용된다.
docker run --mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql
- 그렇다면 모든 작업을 수행하는 주체는 누구인가?
- 계층화된 아키텍처를 유지하고 읽기-쓰기 레이어를 생성하고 파일을 레이어 간에 이동하여 복사 및 쓰기를 활성화하는 등의 작업을 수행하는 주체는 Storage Driver이다.
- Docker는 계층화된 아키텍처를 가능하게 하는 데 storage driver를 사용한다. 일반적인 저장 드라이버에는 aufs, vtrfs, zfs, device mapper, overlay 및 overlay2가 있다.
- storage driver의 선택은 사용중인 기본 OS에 따라 달라진다. 주로 overlay2를 사용하는 듯하다.
https://docs.docker.com/storage/storagedriver/select-storage-driver/
Volume Driver Plugins in Docker
- 볼륨을 생성하고 스토리지를 유지하려면 볼륨을 만들어야 한다.
- 볼륨은 볼륨 드라이버 플러그인에 의해 처리되며, 기본 볼륨 드라이버 플러그인은 로컬이다.
- 로컬 볼륨 플러그인은 Docker 호스트에 볼륨을 생성하고 해당 데이터를 /var/lib/docker/bolumes 디렉터리에 저장한다.
- 여러 다른 볼륨 드라이버 플러그인이 있으며 이를 사용해 Azure FS, Convoy, DigiralOcean Block Storage 기타 등등 타사 솔루션에 볼륨을 생성할 수 있다.
- 이러한 볼륨 드라이버 중 일부는 다양한 스토리지 공급자를 지원한다. 예를들어 REX-Ray 스토리지 드라이버는 AWS EBS, S3 등등 스토리지를 프로비저닝하는 데 사용될 수도 있다. 이렇게 하면 컨테이너가 종료되어도 데이터가 클라우드에 안전하게 유지된다.
Container Storage Interface (CSI)
- 과거에는 kubernetes가 단독으로 docker를 컨테이너 런타임으로 사용하였지만 Rocket, CRI-O 와 같은 다른 컨테이너 런타임이 도입되면서 다양한 컨테이너 런타임과 작동하도록 지원을 확장하게 되었다.
- 이것이 컨테이너 런타임 인터페이스(CRI)가 탄생한 이유이다.
- 컨테이너 스토리지 인터페이스는 kubernetes와 같은 오케스트레이션 솔루션이 Docker와 같은 컨테이너 런타임과 통신하는 방식을 정의하는 표준이다. 따라서 앞으로 새로운 컨테이너 런타임 인터페이스가 개발되어도 CRI 표준만 따른다면 kubernetes와 함께 작동할 수 있게 되었다.
- CSI를 사용하면 자신만의 스토리지 드라이버를 작성하여 kubernetes와 통합할 수 있다.
- portworx, amazon EBS, Azure desk 등등 모두 자체의 CSI 드라이버를 갖추고 있다. CSI는 kubernetes 특정 표준이 아니고 범용 표준이며, 구현되면 어떤 컨테이너 오케스트레이션 도구든 지원 가능한 플러그인을 갖추게 된다.
- CSI는 컨테이너 오케스트레이터에 의해 호출되는 일련의 원격 프로시저 호출(RPC)을 정의하며 이러한 RPC는 스토리지 드라이버에 의해 구현되어야 한다.
- 예를 들어 CSI는 파드가 생성되고 볼륨이 필요한 경우 kubernetes가 볼륨 이름과 같은 세부 정보를 전달하여 create volume RPC를 호출해야 한다고 말한다.
- 스토리지 드라이버는 이 RPC를 구현하고 해당 요청을 처리하며 스토리지 어레이에 새로운 볼륨을 프로비저닝하고 작업의 결과를 반환해야 한다.
- 마찬가지로 컨테이너 오케스트레이터는 볼륨을 삭제할 때 delete volume RPC를 호출해야 하며 스토리지 드라이버는 이 호출이 발생할 때 어레이에서 볼륨을 제거하는 코드를 구현해야한다.
- 명령에 대한 호출 및 솔루션에 의해 전송되어야 하는 매개 변수 및 교환되어야 하는 오류 코드에 대한 명세는 CSI 명세어에서 정확히 설명 중이다.
Volumes
- PV를 알아보기 전에 Docker에서 Volume을 살펴보자
- Docker 컨테이너는 단기간 동안만 지속되어야 하는 휘발성이 있는 성격을 가지고 있다. 필요한 경우 데이터를 처리하도록 호출되고 처리가 완료되면 삭제된다.
- 컨테이너에서 처리된 데이터를 유지하기 위해 컨테이너를 생성할 때 해당 컨테이너에 볼륨을 연결하고, 컨테이너에서 처리된 데이터는 볼륨에 저장되어 영구적으로 유지된다. 심지어 컨테이너가 삭제 되더라도 컨테이너 내에서 생성 또는 처리된 데이터는 남아 있다.
- 쿠버네티스에서는 어떨까?
- Docker와 마찬가지로 쿠버네티스에서 생성된 파드는 휘발성이 있다. 이를 위해 파드에 볼륨을 연결한다.
- 예를 들어 단일 노드 쿠버네티스 클러스터에서 /data 디렉터리에 볼륨을 생성하고 파드 컨테이너 내의 /opt 디렉터리에 마운트 하고자 한다.
- 이 경우 hostPath(다양한 옵션이 있지만 지금은 간단히) 디렉터리를 사용하여 /data 경로를 지정한다.
- 이렇게 하면 볼륨에 생성된 파일이 노드의 data 디렉터리에 저장된다. 볼륨이 생성되면 컨테이너에서 해당 볼륨에 액세스하기 위해 볼륨을 컨테이너 내의 디렉터리에 마운트한다.
- volumeMounts를 이용해서 /opt 디렉터리에 마운트되어 컨테이너 내에서 파일을 변경한 작업이 호스트의 /data 디렉터리에 해당하는 /data 볼륨에 저장된다.
- 파드가 삭제되더라도 해당 컨테이너에서의 파일은 호스트에 남아 있게 된다.
- 다시 돌아가서 볼륨 스토리지 옵션을 살펴보게되면, 위에선 hostPath 옵션을 사용했었지만 단일 노드에서는 잘 작동하지만 다중 노드 클러스터에서는 이를 사용하지 않는 것이 권장된다.
- 파드가 모든 노드에서 /data 디렉터리를 사용하고 모든 노드가 동일하고 동일한 데이터를 가지고 있다고 기대하기 때문이다. 각각의 노드는 서로 다른 서버이므로 사실 같지 않다. 어떤 외부 복제 클러스터 스토리지 솔루션을 구성하지 않는 한 말이다.
- 쿠버네티스는 다양한 유형의 저장소 솔루션을 지원한다. NFS, ClusterFS, Flocker, AWS EBS, Azure Disk file 등이 있다.
- 예를 들어 AWS EBS 볼륨을 볼륨의 스토리지 옵션으로 구성하려면 볼륨의 hostPath 필드를 awsElasticBlockStore 로 대체하고 볼륨 ID와 파일 시스템 유형을 지정하면 된다.
Persistent Volumes
- 이전 섹션에서 볼륨에 대한 얘기를 했고, persistent volume에 대한 이야기를 해보자
- 볼륨을 생성할 때 볼륨을 파드 정의 파일 내에서 구성했으므로 볼륨에 대한 스토리지를 구성하는 데 필요한 모든 구성 정보가 파드 정의 파일 내에 들어가게 된다.
- 이제 많은 사용자가 많은 파드를 배포하는 대규모 환경이 있다고 가정해보자
- 사용자는 각 파드마다 스토리지를 매번 구성해야 하고, 사용되는 스토리지 솔루션이 무엇이든 사용자는 그것을 자신의 환경의 모든 파드 정의 파일에서 구성해야 한다. 또한 변경 사항이 있을 때마다 사용자는 자신의 모든 파드에서 이를 적용해야 한다.
- 위와 같은 상황에서 스토리지를 더 중앙에서 관리하고자 하면 어떻게 해야할까?
- 관리자가 대량의 스토리지 풀을 생성하고 사용자가 필요할 때 조각을 낼 수 있도록 구성하면 되지 않을까
- PV는 관리자가 클러스터에서 응용 프로그램을 배포하는 사용자가 사용할 수 있도록 구성한 클러스터 전체의 스토리지 볼륨 풀이다.
- 사용자는 Persistent Voulume Claim (PVC)를 이용해서 PV라는 풀에서 스토리지를 선택할 수 있다.
- PV를 생성해보자
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol1
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: "/mnt/data"
- accessMode를 지정할 때 ReadOnlyMany, ReadWriteOnce 또는 ReadWriteMany 모드를 설정 가능하다. 이는 볼륨을 호스트에 어떻게 마운트할지를 정의하는 것이다.
- storageClassName 의 경우 스토리지 클래스의 네임을 지정해 pv와 pvc의 해당 네임이 같을 경우 특정 스토리지 클래스를 가진 pv가 동일한 스토리지 클래스를 가진 pvc하고 연결된다. pv에 storageClassName이 없는 경우 storageClassName이 없는 pvc에만 연결된다.
- capacity.storage에 스토리지의 용량을 설정할 수 있다.
- hostPath 옵션은 노드의 로컬 디렉터리에서 스토리지를 사용하게 되는데, 이 옵션은 프로덕션 환경에서 사용하는 것을 지양해야한다.
kubectl get pv # persistent volume 은 pv로 줄일 수 있다.
- hostPath 옵션을 awsElasticBlockStore로 변경 가능하다.
Persistent Volume Claims
- 이전 강의에서 PV를 생성했는 데, 해당 스토리지를 노드에서 사용가능하도록 PVC를 생성해보겠다.
- PV 및 PVC는 쿠버네티스 네임스페이스 내의 두 개의 별개의 객체이다. 관리자는 PV set를 만들고 사용자는 PVC를 사용해 해당 스토리지를 사용한다.
- PVC가 생성되면 쿠버네티스는 볼륨을 클레임에 요청 및 속성에 따라 바인딩한다. 모든 PVC는 단일 PV에 바인딩된다.
- 바인딩 프로세스 중에 쿠버네티스는 클레임이 요청한 용량과 액세스 모드, 볼륨 모드, 스토리지 클래스 등과 같은 다른 요청 속성을 충족하는 PV를 찾으려고 한다.
- 그러나 단일 PVC에 대해 여러 가능한 일치 항목이 있는 경우 특정 볼륨을 사용하려면 여전히 레이블 및 셀렉터를 사용해 올바른 볼륨에 바인딩할 수 있다.
- 마지막으로 작은 클레임이 다른 기준이 일치하고 더 나운 옵션이 없는 경우 큰 볼륨에 바인딩될 수 있음에 유의해야한다.
- 클레임과 볼륨 간에는 일대일 관계라서 다른 클레임이 볼륨의 남은 용량을 활용할 수 없다.
- 볼륨이 사용 가능하지 않으면 PVC는 클러스터에 사용 가능한 새로운 볼륨이 제공될 때까지 대기 상태로 유지된다.
- 새로운 볼륨이 사용 가능하게 되면 클레임은 자동으로 새로운 사용 가능한 볼륨에 바인딩된다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
- kubectl create 명령어로 PVC를 생성하게되면 클레임이 생성되고 클레임은 대기 상태이다.
- 클레임이 생성되면 쿠버네티스는 이전에 생성된 볼륨을 확인한다. 액세스 모드가 일치하고 요청된 용량은 500MiB 이지만 볼륨은 1GiB 로 구성되어 있다. 다른 볼륨이 없으므로 PVC가 PV에 바인딩 된다.
kubectl get pvc
kubectl delete pvc
- PVC를 삭제하면 기본 PV에는 어떤 일이 발생하는가? 볼륨에 대해 어떻게 처리할 지 선택할 수 있다. 기본값은 유지되도록 설정 되어 있다. (Retain)
- PV는 관리자에 의해 수동으로 삭제될 때까지 유지되고, 다른 클레임에서 재사용할 수 없다. 또는 자동으로 삭제할 수 있다.
- persistentVolumeReclaimPolicy : Retain / Delete / Recycle
- Delete로 설정하게되면 클레임이 삭제되는 즉시 볼륨이 삭제되어 최종 스토리지 장치의 스토리지가 해제 된다.
- 또한 Recycle 옵션도 있다. 이 경우 데이터 볼륨의 데이터가 다른 클레임에서 사용할 수 있도록 기존 데이터를 모두 삭제 후 깨끗한 상태로 재활용 할 수 있게끔 된다.
Using PVCs in Pods
- PVC를 생성한 후에는 볼륨 섹션의 PVC 섹션 아래 PVC 클레임 이름을 지정하여 파드 정의 파일에서 사용할 수 있다.
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
- Replicaset이나 Deployments 에서도 마찬가지로 사용 가능하다.
Storage Class
- 이전 강의에서는 PV를 생성하고, 그 다음 PVC를 생성하여 스토리지를 요청하고, PVC를 파드 정의 파일에서 볼륨으로 사용하는 방법을 알아보았다.
- GCP 에서는 어플리케이션이 스토리지를 필요로 할 때마다 먼저 GCP에서 디스크를 수동으로 프로비저닝하고, 그 다음 디스크 이름과 동일한 이름을 가진 PV 정의 파일을 수동으로 만들어야 하는 것이 Static Provisioning Volumes 라고 한다.
- 어플리케이션이 필요할 때 볼륨이 자동으로 프로비저닝되면 어떨까? 이럴 때 스토리지 클래스가 사용된다.
- 스토리지 클래스를 사용하면 클레임이 생성될 때 자동으로 스토리지를 프로비저닝하고 해당 스토리지를 파드에 연결할 수 있다. 이를 볼륨의 동적 프로비저닝이라고 한다.
- provisioner: kubernetes.io/gce-pd 를 이용해서 프로비저너를 지정해주고 스토리지 클래스 객체를 생성해준다.
- 위와같이 생성하게되면 더 이상 PV 정의가 필요하지 않다.
- PVC가 생성될때 해당 스토리지 클래스는 정의된 프로비저너를 사용해 GCP에 필요한 크기의 새 디스크를 프로비저닝하고, PV를 생성하고, PVC를 해당 볼륨에 바인딩한다.
- 기억해야할 것은 여전히 PV가 생성되지만 더 이상 PV를 수동으로 생성하지 않아도 된다는 것이다. 스토리지 클래스가 자동으로 생성하기 때문이다.
- GCE 프로비저너 뿐만 아니라 AWS EBS, Azure File 등등 다양한 프로비저너가 있다.
- 각 프로비저너는 디스크 유형, 복제 유형 등과 같은 추가 매개변수를 전달할 수 있다.
- GCE에서는 silver, gold. platinum과 같은 스토리지 클래스가 있다.
- AWS EBS의 경우 아래와 같다.
# 샘플
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
반응형
'자격증 > Kubernetes CKA' 카테고리의 다른 글
[CKA] Networking - 2 (1) | 2023.12.30 |
---|---|
[CKA] Networking - 1 (1) | 2023.12.26 |
[CKA] Security - 3 (0) | 2023.12.18 |
[CKA] Security - 2 (0) | 2023.12.13 |
[CKA] Security - 1 (0) | 2023.12.12 |