앞서 언급했던 것처럼, 파드는 일시적이며, 언제나 삭제될 수 있음을 감안해야 합니다. 따라서, 파드 그 자체는 Stateless 합니다. 이러한 파드의 교체와 배치를 담당하는 것이 디플로이먼트입니다. 디플로이먼트는 레플리카셋을 통해 파드를 scale out하며, 이때 만들어지는 파드들은 상호 대체 가능합니다.
파드 그 자체에 상태(데이터)를 남겨야만 하는 Stateful 애플리케이션으로는 MySQL, mongoDB, redis와 같은 데이터베이스가 있을 수 있습니다. 데이터베이스 애플리케이션이 담긴 파드가 사라질 때, 데이터가 함께 사라지도록 두어서는 안 될 것입니다.
그래서 쿠버네티스에도 영속적인(Persistence) 데이터(프로그램의 실행이 종료되어도 사라지지 않는 데이터)를 저장하기 위해 볼륨(Volume)을 연결할 수 있습니다. 여타 클라우드 서비스에서 볼륨을 따로 분리해 놓는 것과 마찬가지입니다.
Q. 볼륨과 퍼시스턴스 볼륨(Persistence Volume)은 어떤 차이가 있나요? AWS EC2에도 비슷한 개념이 있습니다. EBS와 인스턴스 스토어 볼륨의 차이점과 연결해서 설명해 주세요.
파드에 볼륨을 연결하면, 데이터를 영속적으로 저장하는 것이 가능합니다.
그런데, 파드 명세에 PV를 정의해서 직접 연결하는 것은 좋은 방법이 아닙니다.
Q. 왜 파드와 PV를 직접 연결하는 것이 좋지 않은가요?
이때 이러한 의존도를 줄이기 위해, 퍼시스턴스 볼륨 클레임(Persistence Volume Claim, 이하 PVC)을 이용하여 PV와 연결합니다. PVC는 파드가 볼륨의 세부 사항을 몰라도 볼륨을 사용할 수 있게 도와줍니다.
Q. PVC는 어떻게 작동되나요?
앞서 설명했다시피, 파드를 정의할 때 PVC를 통해 볼륨에 연결할 수가 있습니다. 그런데, 그냥 이러한 파드를 디플로이먼트로 배포해도, PVC를 통해 연결되는 볼륨은 하나이기 때문에, 결국 같은 스토리지를 사용하는 모양새가 됩니다.
파드마다 별도의 데이터를 저장할 수 있게 만들려면, 수평확장되는 파드에 서로 다른 PV가 연결되어야 합니다. 파드가 PV를 동적으로 요청해서 그때그때 볼륨이 생성되게 하면 어떨까요? 스토리지 클래스(StorageClass) 리소스는 사용자가 요청할 때, 자동으로 PV를 프로비저닝 할 수 있게 돕습니다.
Stateless 애플리케이션을 예로 들면 아파치, nginx, IIS 가 있습니다.
해당 애플리케이션에서 서비스가 죽으면 단순 복제로 대체해주면 됩니다.
볼륨의 경우 같은 내용을 서비스하기 때문에 필요하다면 하나의 볼륨에 다수가 접근하면 됩니다.
네트워크 트래픽에서는 서비스에 접근하면 부하를 방지하기 위해 각각 분산을 하게 됩니다.
Stateful 애플리케이션은 각각의 역할이 있는데 Primary 메인 DB가 있고 Secondary로 Primary가 죽으면 대체할 DB이 존재하고 이를 감시하는 Arbiter가 있습니다.
각각의 역할이 있기 때문에 아비터가 죽으면 아비터 역할을 살려줘야 합니다.
또한, 각각의 역할마다의 볼륨을 사용하기 때문에 원래 사용하던 볼륨에 접근해야 해당 역할을 이어갈 수 있습니다.
네트워크 트래픽에서는 대체로 내부 시스템들이 데이터베이스에 사용되는데 각 앱에 특징에 맞게 들어가야 합니다.
App1에는 메인DB로 Read/Write가 가능하므로 내부 시스템들이 CRUD를 모두 하려면 이곳으로 접근해야하고
App2는 Read 권한만 있기 때문에 조회만 할 때 트래픽 분산을 위해 사용할 수 있으며
App3은 Primary와 Secondary를 감시하고 있어야 하기 때문에 App1,2에 연결이 되어야 합니다.
쿠버네티스에서 마이크로서비스 구조로 동작하는 애플리케이션은 대부분 상태를 갖지 않는 경우(Stateless) 가 많습니다. 그러한 경우에는 디플로이먼트,레플리카셋을 통해 쉽게 애플리케이션을 배포할 수 있습니다.
하지만, 레플리케이션 컨트롤러나 레플리카셋을 제공하는 파드는 각 별도의 볼륨을 사용할 수 있는 방법을 제공해주지 않아 모두 같은 볼륨으로 같은 상태를 가질수 밖에 없습니다. 또한 데이터베이스처럼 상태를 갖는(Stateful) 애플리케이션을 쿠버네티스에서 실행하는 것은 매우 복잡한 일 입니다. 왜냐하면 Pod 내부의 데이터를 어떻게 관리해야 할지, 상태를 갖는 Pod에는 어떻게 접근할 수 있을지 등을 꼼꼼히 고려해야 하기 때문입니다.
쿠버네티스가 이에 대한 해결책을 완벽하게 제공하는 것은 아니지만, 스테이트 풀셋이라는 쿠버네티스 오브젝트를 통해 어느정도 해결할 수 있도록 제공하고 있습니다.
즉, 파드마다 각각 다른 스토리지를 사용해 각각 다른 상태를 유지하기 위해서는 스테이트풀셋 (StatefulSet) 리소스를 사용하면 됩니다.
또한, 목적에 따라 해당 파드에 연결하기 위한 Headless Service 를 달아주면 됩니다.
레플리카셋 | 스테이트풀셋 | |
---|---|---|
파드 생성시 이름 설정 | Random 이름으로 설정cf) Pod-ska25, Pod-dk15d ... | Ordinal index 이름으로 생성cf) Pod-0, Pod-1, Pod-2 ... |
파드 생성 시 순서 | 동시 생성 | 순차 생성. 0->1->2... |
파드 Recreate 시 | 파드 이름 변경cf) Pod-sdf34 -> Pod-vjng3 | 파드 이름 유지cf) Pod-2 -> Pod-2 |
파드 삭제 시 순서 | 동시 삭제 | 인덱스 높은 순부터 순차 삭제 2->1->0 |
볼륨 생성 하는 방법 | PVC를 직접 생성 | volumeClaimTemplates 을 통한 동적 생성 |
파드의 수를 늘리면 PVC는? | 1개의 PVC에 모두 연결 | 각각의 PV 를 생성한 뒤 연결 |
PVC 연결된 특정 파드를 죽으면? | NodeSelector 가 설정 되어 있다면 해당 노드에 동일한 서비스로 랜덤한 파드이름 생성(같은 노드에 PVC,파드가 생성되지 않으면 연결되지 않음) | 특정 파드와 동일한 파드를 생성 후 기존 PVC와 연결 |
PVC가 연결된 파드 수를 0으로 하면? | PVC도 삭제함 | PVC는 삭제하지 않음 |
cf ) 스테이트 풀셋은 애완동물로 비유가 됩니다. 가축에 비해 애완동물은 쉽게 대체될 수 없기 때문입니다.
레플리케이션 컨트롤러나 레플리카셋은 상태를 저장하지 않는 Stateless 이기 때문에 항상 실행하는 파드의 정보가 똑같을 필요는 없습니다. 병들어 죽은 가축처럼 얼마든지 교체할 수 있는 형태 입니다. 교체된 가축은 기존의 가축과 이름도 다르고(이름이 랜덤하게 붙음) 행동(IP)도 다를 수 있습니다.
스테이트풀셋이 관리하는 파드는 애완동물과 같아 애완동물이 죽으면 다른 애완동물로 대체할 수 없습니다.
애완동물을 대체하기 위해서는 (현실에서는 불가능하지만) 생김이나 행동 등 원래 있던 애완동물과 같아야 합니다.
스테이트 풀셋은 문제가 생긴 파드와 완벽하게 똑같은 파드로 대체합니다. 즉, 똑같은 이름과 똑같은 IP를 가진 파드로 교체한다는 의미입니다.
쿠버네티스에서 v1.4 까지 스테이트풀셋을 펫셋(PetSet)이라고 불렀습니다.
v1.5+ 부터 스테이트풀셋으로 변경
디플로이먼트 컨트롤러와 같이 파드를 배포하고 복제본을 제공하며 스케일링을 관리할 수 있습니다. 그러나 디플로이먼트와 다른 점은 파드의 순서 및 파드의 고유성을 보장하며, 동일한 스펙으로 생성되지만 각각 고유한 볼륨을 가지고 있습니다.
스테이트풀셋을 사용할 때 주의사항이 있습니다.
스테이트풀셋의 각 파드 이름은 컨트롤러의 이름에 0부터 시작하는 순서 색인이 붙게 됩니다.
{StatefulSet-Name}-{Order}
mysts-0
mysts-1
헤드리스 서비스와 스테이트풀셋을 같이 사용하는 경우 파드의 DNS 주소는 다음과 같습니다.
{Pod_Name}.{Governing_Service_Domain}.{Namespace}.svc.cluster.local
mysts-0.mysts.default.svc.cluster.local
mysts-1.mysts.default.svc.cluster.local
{Governing_Service_Domain}은 statefulset.spec.serviceName에 선언하며 해당 필드에 헤드리스 서비스의 이름을 지정합니다.
스테이트풀셋의 파드는 각각 고유한 PVC를 생성해 고유한 PV를 가집니다.
statefulset.spec.volumeClaimTemplates 필드에 선언하며, 미리 PV를 준비하거나, StorageClass를 통해 PV를 생성할 수 있습니다.
스테이트풀셋의 파드가 삭제되더라도 해당 볼륨은 안정적인 데이터 보존을 위해 자동으로 삭제되지 않습니다.