CKA 대비 kubernetes 스터디 - 7. Storage
Docker Storage
크게 두 가지 개념이 있다.
Storage Driver
docker는 데이터를 어디에 저장하는지, container에서 fileSystem을 어떻게 관리하는지.
- docker image를 실행하면, /var/lib/docker 라는 경로를 컨테이너 내부에 생성한다.
- aufs, containers, image, volumes 등 하위 경로도 같이 만들어진다. 여기까지가 docker에서 흔히 말하는 'data' 필드.
Docker의 Layered Architecture
- dockerfile의 맨 윗줄부터 하나씩 레이어를 쌓아가면서 이미지를 생성한다.
- 각 레이어는 이전 레이어에서 추가된 내용만을 저장하며, 로컬에 캐시된다.
- 따라서, 비슷한 dockerfile을 빌드하게 될 경우 layer를 새로 생성하는 대신, 이전에 캐시해둔 레이어를 재활용한다.
- 빌드 속도를 빠르게 가져갈 수 있고
- disk의 불필요한 중복저장을 최소화할 수 있다.
- 여러 layer가 합쳐져 만들어진 docker image는 READ Only, immutable 상태가 된다.
- 변경사항을 반영하려면 새로운 이미지를 빌드해야 함. (변경되지 않은 레이어는 재사용이 가능하다)
- docker run을 실행할 경우, image 위에 container layer가 생성된다.
- container layer는 컨테이너 위 애플리케이션에서 생성되는 데이터 - log file나 임시파일 등 사용자에 의해 변경된 모든 파일들 - 이 만들어지는 영역.
- 즉 READ / Write가 가능하지만, container layer는 해당 컨테이너가 실행될 때에만 임시로 존재하는 ephemeral 레이어다.
- 소스코드는 image layer에 있고, 여러 컨테이너에서 같은 layer를 공유해서 사용할 수 있다.
- 소스코드를 수정하는 로직을 실행할 경우, READ Only인 이미지 레이어에서 Container layer로 소스코드를 복사한 뒤, 복사한 코드를 수정한다. = CopyOnWrite Mechanism
- 이미지에 있는 파일 자체를 수정하는 게 아니라, 복사본을 떠서 ephemeral Layer에서 수정하는 것.
- 따라서 container layer에서 수정한 소스코드 파일은 컨테이너가 종료되면 같이 삭제된다.
- Layered Architecture를 관리하고
- Writable Layer를 생성하고
- file의 이동, 복사 등
을 관리하는 컴포넌트가 Storage Driver
- Storage Driver는 OS가 선택해 사용한다.
- 예컨대 ubuntu의 경우 AUFS를 사용하며, 다른 Storage Driver는 동작하지 않는다.
- docker는 해당 OS에서 사용하는 최선의 storage driver를 선택해 실행한다.
만약 컨테어너가 종료되더라도 특정 파일이나 변경사항이 남아있어야 할 경우, Persistent Volume을 사용해야 한다.
Volume Mounting. -> /var/lib/docker/volumes 경로에 마운트.
docker volume create data_volume
명령어를 실행한다.- /var/lib/docker/volumes 하위에 data_volume 디렉토리가 생성된다.
- 여기에 생성된 경로를 -v 옵션을 줘서 적용하면, rewrite layer로 해당 디렉토리를 활용할 수 있게 된다.
docker run -v data_volume:/var/lib/mysql mysql
: mysql이 데이터를 저장하는 기본 경로를 data_volume 디렉토리로 Mount한 뒤 mysql 이미지를 실행한다.- mysql 컨테이너에서 생성된 모든 변경사항이 /var/lib/docker/volumes/data_volume에 저장되는 방식.
- 미리 volume을 생성하지 않고 바로
docker run -v data_volume2:/var/lib/mysql mysql
형태로 실행하면, data_volume2 디렉토리를 생성하고 마운트한 뒤 mysql 이미지를 실행한다.
Bind Mounting. -> docker host의 어느 경로에도 마운트할 수 있음.
- 기존 디렉토리를 mount하고 싶다면
docker run -v /data/mysql:/var/lib/mysql mysql
처럼 full path를 입력해주면 된다.
cf. 자료 사진의 -v 옵션은 옛날 옵션이며, --mount를 사용하는 게 좋다고 안내함.
docker run --mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql
형태로 사용하는 것을 권장.
Volume Driver Plugin in Docker
storage Provider와 Volume Driver는 서로 다른 역할을 한다.
- volume driver의 기본값은 local. docker 로컬 경로에 volume을 생성하고 persistency를 관리한다.
- 퍼블릭 클라우드 스토리지를 지원하는 volume plugin을 사용하면, 데이터를 클라우드에 저장할 수 있다.
Container Storage Interface (CSI)
- 예전에는 k8s에 embedded docker가 유일한 runtime Engine이었다.
- 하지만 docker 외에 다른 container runtime과의 호환성을 위해 Container Runtime Interface를 사용하는 식으로 변경되었다.
- Orchestration Tool이 container Runtime과 통신하기 위한 표준 인터페이스라고 보면 됨.
Runtime과의 호환성을 위해 CRI가 생겼다면, 네트워크 솔루션과의 호환에는 CNI, Storage와의 호환성을 위해서는 CSI가 만들어졌다고 보면 된다.
CSI는 단순히 k8s뿐만 아니라 Orchestration tool 자체와 storage 간의 통신규약을 위한 프로토콜이다.
- ex) Orchestration tool에서 CreateVolume RPC로 volume 생성 요청 시 -> volume 이름 같은 파라미터를 필수로 전달
- Storage는 CreateVolume RPC의 Implementation이 있어야 함.
Volume
- container 자체는 short period of time 실행을 위한 것. 필요하면 프로세스로 올라와서 작업하고, 작업이 끝나면 사라진다.
- container 내부에서 생성되는 데이터도 마찬가지로 ephemeral.
- 컨테이너에서 사용한 데이터를 보존하고 유지하기 위해서는 컨테이너의 생성 시점에서 volume attach가 필요하다.
k8s에서는 pod에 volume attach를 사용해서 데이터의 영속성을 유지한다.
- volumes 필드로 host (node)에 데이터를 저장해둘 경로를 지정하고
- container의 volumeMounts 필드로 volumes를 연결한다.
- 위 예시의 경우, container의 /opt 디렉토리에 쓰이는 모든 파일은 node의 /data 디렉토리에 저장된다. 따라서 컨테이너가 종료되더라도 컨테이너에서 실행한 데이터는 남아 있다.
하지만 host의 Directory를 직접 사용하는 방식은 멀티 노드에서는 권장하지 않는다.
- 여러 pod가 돌고 있는 각각의 노드에서 전부 동일한 데이터를 유지한다는 보장이 없기 때문.
- 따라서 k8s에서는 다양한 방식의 volume mount storage를 제공하고 있다.
예컨대 AWS EBS를 storage로 정하고 싶다면 yaml 옵션을 위와 같이 변경하면 된다. 작업한 데이터는 전부 AWS EBS storage에 저장된다.
Persistent Volume
- pod에 volume을 마운트해서 사용하는 것까진 좋은데, 사용자가 많아서 pod을 여러 개 띄워야 하는 앱일 경우 각 pod마다 yaml파일에 일일이 정의해야 하는 문제가 있다.
- 경로를 수정하거나 할 경우, 기존 Pod의 yaml 파일을 빠짐없이 변경해야 하는 등 유지보수에 번거로움.
- 좀더 Centralized Storage 방식을 사용하는 게 Persistent Volume
- Large Pool of Storage를 만들어두고, pod에서 carved out pieces 하도록 만든 것.
- pod에서 Persistent Volume에 storage 할당을 요청하는 게 PVC (Persistent Volume Claim.)
- accessModes : volume이 마운트되는 방식을 정의함.
- ReadOnlyMany, ReadWriteOnce, ReadWriteMany 세 가지 옵션이 있다.
- hostPath를 지정하면 디렉토리를 설정할 수 있지만, production에서는 권장하지 않는다.
- 위의 예시는 AWS EBS를 사용해서 Persistent Volume을 생성하도록 하는 명령어.
Persistent Volume Claim
- PV와 PVC는 Namespace별로, 서로 별개의 k8s Object.
- Admin은 PV를 생성하고, user가 PVC를 생성해서 필요한 양의 storage를 요청 후 할당받는 식.
- 1 PVC -> bind to 1 PV. 일대일 관계.
기본적으로는 requested claim을 충족할 만큼의 capacity가 있는 Persistent Volume을 binding해 준다.
- 일단 용량부터 맞으면, 그 후에 access mode / volumen mode / storage class 등을 따져서 최종적으로 적합한 PV를 할당한다.
- 정 맞는 게 없으면, PVC보다 큰 용량의 PV가 할당될 수 있다. PVC와 PV는 1:1 관계이므로, 이 경우 PV의 용량이 남는다 해도 다른 요청을 위해 할당해줄 수 없다.
- 맞는 요청이 하나도 없다면, PVC는 Volume 여유분이 생길 때까지 Pending 상태로 남아 있다.
- 만약 하나의 요청에 부합하는 PV가 여러 개 있고, 그 중 특정 PV만 사용하고 싶다면 labelSelector를 사용하면 된다.
- PVC
- AccessMode : ReadWriteOnce
- Resource Request : 500Mi
- PV
- AccessMode : ReadWriteOnce -> 일치
- Resource Request : 1Gi -> 요청한 양보다 많지만, 다른 PV 선택지가 없으면 이 PV가 할당된다.
- Delete 요청 -> PVC와 Binding되어 있던 PV가 분리된다.
- ReclaimPolicy 옵션을 제공할 수 있음.
- Retain: 일시적으로 분리한 상태. 다른 PVC에게 할당할 수 없는 상태가 되며, Admin이 직접 삭제해야 지워진다.
- Delete: 자동으로 삭제. 따라서 PVC가 삭제되면 할당된 PV가 삭제되고, 디바이스에 여유공간이 남는 것.
- Recycle: data volume에 저장되어 있던 데이터만 날리는 것. 다른 PVC가 사용할 수 있는 상태가 된다.
Storage Class
Static Provisioning
GCP에 PV를 적용하기 위한 예시. PVC를 생성하기 전에
- google cloud에 PV가 만들어져 있어야 한다.
- PV definition yaml 파일이 생성되어 있어야 한다.
이렇게 PVC 생성하기 전에 직접 PV yaml파일을 생성하고, storage 공간을 미리 확보해두는 것을 Static Provisioning이라고 부른다.
Provision Automatically 해주면 편할 텐데, 그 기능을 지원하는 게 storage class.
Dynamic Provisioning
- 이렇게 될 경우, PV를 직접 정의할 필요가 없다. StorageClass가 사실상 PV 역할을 대신할 수 있는 object이기 때문.
- PVC의 definition에 storage class에서 정의한 metadata.name 필드를 추가해준다.
storageClassName: <storage-class-metadata-name>
- pod는 PVC의 metadata.name을 사용하고, PVC는 storageClassName 값으로 storageClass object를 찾는다.
- storageClass는 자동으로 PV를 생성하고, PVC - PV - Storage 간 연결을 지원해준다. storageClass라고 해서 PV를 안 쓰는 게 아님. storageClass가 대신 생성하고 관리하는 거다.
- provider마다 제공할 수 있는 옵션이 다를 수 있음. 그건 parameter 필드에서 확인 가능하다.
- gcp의 경우 type으로 storage의 기본 설정값 (standard, ssd), replicationType으로 regional-pd 등의 옵션을 줄 수 있다.
- 옵션을 토대로, 여러 개의 storage class를 생성해서 비즈니스 로직에도 활용할 수 있다.