🖥️애플리케이션의 정상적인 동작을 위해서는 무엇이 필요할까?
애플리케이션의 정상적인 동작을 위해서 애플리케이션의 데이터를 저장하고 유지하기 위한 📁데이터 지속성이 필요하다.
즉, 컨테이너, 파드, 노드의 생명주기와 무관한 영구적으로 데이터를 저장할 수 있는 스토리지가 필요하다.
데이터 스토리지의 종류를 살펴보자.
임시 파일 시스템의 경우 컨테이너에 종속적이므로, 컨테이너가 삭제된다면 함께 삭제된다. 파드가 살아있어도 파드 내 컨테이너가 죽는 경우 임시 파일 시스템 또한 삭제되는 것이다.
임시 파일 시스템에서 조금 더 나아가 임시 볼륨(emptydir)에 대해서 알아보자.
파드 내 임시 볼륨을 생성하면 한 파드내 컨테이너가 함께 볼륨을 공유할 수 있다.
하지만, 파드의 생명주기에 의존하게 되므로 파드가 삭제되면 함께 삭제되게 된다.
이젠 노드(host)의 생명주기를 따라가는 저장 공간인 hostpath에 대해서 알아보자.
노드에 대해서 종속적이므로, 해당 노드 내 파드들이 공유해서 사용할 수 있고, 파드가 죽는다 하더라도 여전히 노드가 살아있는 한 해당 볼륨은 유지된다.
이는 도커의 bindMount와 거의 동일하다.
만일 해당 죽었던 파드가 전혀 다른 새로운 노드에 다시 생성되면 어떻게 될까?
해당 노드에는 기존 파드와 연결되어있던 hostpath가 존재하지 않으므로 당연히 연결할 수 없게 된다.
이를 위해 노드가 구성되면 hostpath를 구성하고 서로간의 마운트를 해주면 된다.
이렇게 되면 hostpath를 이용해서 해당 파드가 여전히 기존 노드의 볼륨을 사용할 수 있다
하지만 이렇게 hostpath를 노드간에 서로↔️ 마운트하여 데이터 지속성을 유지할 수는 있겠으나, 아무래도 서로 마운트를 해줘야하는 불편함이 있다.✔️
💡 그렇기에 우리는
PV
와PVC
를 사용한다!
PV란 📁데이터 지속성을 위한 영구적인 볼륨을 의미한다. PVC는 이러한 영구적인 볼륨을 요청하기 위한 일종의 요청사항을 기록한 파일이다.
(보다 상세한 내용은 해당 글에서 확인 가능)
먼저 PV와 PVC의 기본적인 할당 과정에 대해서 알아보도록 하자. 이를 정적 프로비저닝 이라고 한다.
<👤관리자 영역>
관리자는 EBS, EFS와 같은 실제 스토리지를 생성한다.
이후 관리자는 PV를 생성한다.
PV는 실제 스토리지를 나타내는 볼륨 객체로 쿠버네티스 내에서 사용하게 된다. 이 때, PV에 연결될 실제 스토리지의 유형(EBS, EFS)과 크기를 지정한다.
PV를 생성할 때 액세스 모드에 대해서도 함께 지정한다.
액세스 모드에 대한 유형은 아래와 같다.
EBS 볼륨의 경우 ReadWriteOnce만을 지원하고, EFS의 경우 ReadWriteMany까지 지원한다.
<👨👩👧👦사용자 영역>
파드와 PV간의 직접적인 연결은 불가능하므로 파드와 PV를 바인드하는 PVC를 생성한다.
5.파드를 생성
할 때 이 PVC를 이용하여 파드와 PV를 바인드하게 된다.
파드를 생성한다.
사용자는 파드를 생성할 때, PVC를 정의하여 파드에게 PV를 바인드한다.
핵심은 그림에서 보다시피 PV는 컨테이너, 파드, 노드 그 어디의 생명주기에도 종속되어 있지 않다.
따라서 이들의 생명주기와는 연관없이 PV는 말 그대로 Persistent💎
하게 영구적으로 보존된다.
⚠️ Local Path Provisioner
단, Local Path Provisioner를 사용하면 노드의 HostPath를 이용해서 PV가 생성되므로 노드에 종속된다.
(파드도 해당 노드에 있어야지만 PV 이용 가능)
귀찮게 PV와 PVC를 왜 분리할까? 그냥 쓰면 PV만들고 바로 파드에 마운트하면 안될까?
💡어허! 관리자와 사용자의 역할은 분리해줘야죠!
K8S를 공부하며 관리자와 사용자의 역할을 둘 다 짬뽕해서 사용해 왔겠지만, 실제로는 PVC를 생성하는 것은 쿠버네티스에 애플리케이션을 배포하는 사용자
의 역할이고, PV를 생성하는 것은 실제 스토리지를 생성하고 관리하는 관리자
의 역할이기 때문이다.
위의 예시처럼 정적으로 프로비저닝하게 되는 경우, PV를 관리자가 직접 미리 생성하고 관리해야므로 시간적으로나 공간적으로 불편함이 많다.
따라서 Storage Class
와 Provisoner
를 통해 동적으로 스토리지를 프로비저닝하고 스토리지와 연결된 PV를 생성하는 🆙동적 프로비저닝을 사용한다.
<👤관리자 영역>
1. Provisioner라는 객체를 생성하여 동적으로 스토리지를 생성하고 PV를 생성할 수 있도록 한다.
<👨👩👧👦사용자 영역>
사용자가 원하는 PVC를 생성(정의)한다.
PVC에 생성한 Storage Class를 연결한다.
파드를 생성한다.
사용자는 파드를 생성할 때, PVC를 정의하여 파드에게 PV를 바인드한다.
최종 결과
이렇게 되면 파드 생성 시, PVC에 있는 Storage Class를 바탕으로 Provisioner가 스토리지를 실제로 프로비저닝하고, PV를 생성하여 PVC와 마운트한다.
스토리지 대상을 외부 스토리지 공급자로 하는 경우와 노드 내부에 구성하는 경우
노드 내부에 구성하게 되는 경우에는 노드가 죽는 경우, 해당 스토리지 hostpath가 실제로 죽어버리게되므로 이는 PV라고 할 수 없지 않는가?
CSI 드라이버💿란 쿠버네티스와 스토리지의 연결을 손쉽게할 수 있도록 도와주는 Add-on
이다.
쿠버네티스와 스토리지 공급자(ex. AWS EBS, EFS) 사이에서 인터페이스 형태로 연결을 돕는다.
쿠버네티스 CSI 드라이버는 기본적으로 프로비저너(Provisioner)
, 어태쳐(Attacher)
, 컨트롤러(Controller)
, 노드서버(NodeServer)
로 이루어진다.
프로비저너(Provisioner)
클러스터의 PVC를 모니터링하고 PVC가 생성되면 PV를 생성
어태쳐(Attacher)
파드에 정의된 PVC에 따라 컨테이너에 실제 볼륨을 마운트
컨트롤러(Controller)
컨테이너가 사용하는 볼륨을 실제 스토리지 서버에서 생성 및 삭제
노드서버(NodeServer)
파드가 스토리지 볼륨에 마운트할 수 있게 환경을 만드는 역할
AWS에서 제공하는 AWS CSI 드라이버💿를 살펴보자.
쿠버네티스 클러스터에 AWS CSI 드라이버를 설치하게 되면 csi 컨트롤러 파드와 csi 노드파드가 생성된다.
(csi 컨트롤러 파드는 디플로이먼트 형태로, csi 노드파드는 데몬셋 형태로 생성)
CSI 컨트롤러 파드에는 앞선 4개의 프로비저너(Provisioner)
, 어태쳐(Attacher)
, 컨트롤러(Controller)
, 노드서버(NodeServer)
구성요소가 컨테이너로 들어가 있고
추가적으로 볼륨의 스냅샷을 만드는 snapshotter
와 볼륨의 크기를 재조정하는 resizer
, 컨테이너의 생명주기를 담당하는 liveness-probe
가 있다.
Reference📎 | CloudNet@와 함께하는 Amazon EKS 기본 강의