DevOps BootCamp/쿠버네티스

볼륨과 스테이트풀셋

cloudmaster 2023. 5. 22. 10:07
파드 : Stateless
 : 파드는 일시적이며, 언제나 삭제될 수 있음을 감안해야 함 -> 파드 그 자체는 Stateless 헤야 함
 : 파드의 교체와 배치를 담당하는 것이 디플로이먼트 
 -> 디플로이먼트는 레플리카셋을 통해 파드를 scale out 하며, 이때 만들어지는 파드들은 상호 대체 가능

  

파드가 사라져도, 데이터를 남기고 싶다면

 : 파드 그 자체에 상태(데이터)를 남겨야만 하는 Stateful 애플리케이션 -> MySQL, mongoDB, redis와 같은 데이터베이스가 있음

 : 데이터베이스 애플리케이션이 담긴 파드가 사라질 때, 데이터가 함께 사라지도록 두어서는 안 될 것

 

  -> 쿠버네티스에도 영속적인(Persistence) 데이터(프로그램의 실행이 종료되어도 사라지지 않는 데이터)를 저장하기 위해 볼륨(Volume)을 연결할 수 있음

 

파드가 사용할 수 있는 볼륨 유형

볼륨 유형

[이미지 출처] https://tech.gluesys.com/blog/2022/06/21/CSI.html

 

글루시스 기술 블로그

A simple yet classy theme for your Jekyll website or blog.

tech.gluesys.com

 

Q. 볼륨과 퍼시스턴스 볼륨(Persistence Volume)은 어떤 차이가 있나요?
 -> 볼륨은 파드와 연결되어 파드가 사라지면 없어지지만, Persistence Volume은 파드와 직접 연결되지 않고, 분리되어 있기 때문에 파드가 사라져도 데이터가 영구적으로 남습니다.

 

Stateful 한 애플리케이션을 관리하려면

: 파드 명세에 PV를 정의해서 직접 연결하는 것은 좋은 방법이 아님

 

Q. 왜 파드와 PV를 직접 연결하는 것이 좋지 않은가요?
1. 파드의 이식성 문제: 파드는 여러 노드 사이에서 스케줄링될 수 있습니다. 직접적으로 PV에 연결된 파드는 특정 노드에 의존하게 되어 이식성이 저하됩니다. 노드 장애 또는 리소스 부족으로 인해 파드를 이동시켜야 할 때 문제가 발생할 수 있습니다.
2. 스토리지 유연성 부족: PV는 스토리지의 추상화 계층으로 작동하여 다양한 스토리지 프로바이더와 통합될 수 있습니다. PV와 파드를 직접 연결하면 특정 스토리지 프로바이더에 종속되게 됩니다. 이로 인해 스토리지 유연성이 제한되며, 스토리지 프로바이더를 변경하거나 업그레이드하는 것이 어려워집니다.
3. 동시에 여러 파드 사용 문제: PV는 여러 파드에서 동시에 사용될 수 있습니다. PV를 직접 파드에 연결하면 해당 PV를 단 하나의 파드만 사용할 수 있게 됩니다. 그러나 PV를 볼륨 클레임(VolumeClaim)을 통해 동적으로 할당하면 여러 파드에서 동시에 사용할 수 있습니다.

 

 -> 이때 이러한 의존도를 줄이기 위해, 퍼시스턴스 볼륨 클레임(Persistence Volume Claim, 이하 PVC)을 이용하여 PV와 연결함

 -> PVC는 파드가 볼륨의 세부 사항을 몰라도 볼륨을 사용할 수 있게 도와줌

  • PV는 실제로 데이터가 저장되는 공간
  • PVC는 PV를 선택, 연결해 주는 요청 그 자체

 

Stateful 한 애플리케이션이 수평 확장한다면

: 파드를 정의할 때 PVC를 통해 볼륨에 연결할 수가 있음

: 그냥 이러한 파드를 디플로이먼트로 배포해도, PVC를 통해 연결되는 볼륨은 하나이기 때문에, 결국 같은 스토리지를 사용하는 모양새가 됨

[이미지 출처] https://tech.gluesys.com/blog/2022/06/21/CSI.html

 

글루시스 기술 블로그

A simple yet classy theme for your Jekyll website or blog.

tech.gluesys.com

 

-> 파드마다 별도의 데이터를 저장할 수 있게 만들려면, 수평확장되는 파드에 서로 다른 PV가 연결되어야 함

-> 파드가 PV를 동적으로 요청해서 그때그때 볼륨이 생성되게 하면 어떨까? 스토리지 클래스(StorageClass) 리소스는 사용자가 요청할 때, 자동으로 PV를 프로비저닝 할 수 있게 도움

 

[출처] 코드스테이츠

 

스테이트풀셋

: 애플리케이션 구성(파드)을 복제하더라도, 스토리지 클래스를 이용해 파드가 필요로 하는 볼륨을 자동으로 프로비저닝 하여 연결

-> 첫 번째 파드를 primary(master), 두 번째 파드를 secondary 복제본처럼 구성하는 것도 가능

 

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는 삭제하지 않음

 

스테이트풀셋은 컨테이너 애플리케이션의 상태를 관리하는 데 사용하는 컨트롤러

 

디플로이먼트와 스테이트풀셋 둘 다 동일한 컨테이너 스펙을 기반으로 둔 파드들을 관리한다는 점에는 같지만, 가장 결정적인 차이점은 다음과 같다

스테이트풀셋은 파드의 순서고유성을 보장
-> 가축과 애완동물의 비유를 떠올리면 좋다. 스테이트풀셋이 생성한 파드는 상호 대체할 수 없는 파드

  • 파드 배포
  • 파드의 복제본
  • 스케일링
  • 파드의 순서
  • 파드의 고유성 (이름, 네트워크, 스토리지)
  • 각 파드의 고유한 볼륨

 

스테이트풀셋을 사용할 때의 주의사항
  • 파드에 지정된 스토리지는 관리자에 의해 퍼시스턴트 볼륨 프로비저너를 기반으로 하는 storage class를 요청해서 프로비전 하거나 사전에 프로비전이 되어야 함
  • 헤드리스 서비스가 필요 - 파드의 고유한 네트워크 신원을 제공하기 위함
  • 예를 들어 일반적인 서비스라면, 서비스는 기본적으로 레이블 셀렉터가 일치하는 랜덤한 포드를 선택해 트래픽을 전달하기 때문에 스테이트풀셋의 랜덤한 포드들에게 요청을 분산될 것.
  • 하지만, 이것은 스테이트풀셋이 원하는 동작이 아님. 스테이트풀셋의 각 포드는 고유하게 식별되야하며, 포드에 접근할 때에도 '랜덤한 포드'가 아닌 '개별 포드'에 접근해야 함.
  • 이런 경우 헤드리스 서비스는 서비스의 이름으로 포드의 접근 위치를 알아내기 위해 사용되며, 서비스의 이름과 포드의 이름을 통해서 포드에 직접 접근할 수 있음.

[출처] https://nearhome.tistory.com/107

 

[쿠버네티스] 쿠버네티스 스테이트풀셋 (StatefulSet) 소개 및 관리

들어가며: 기존 컨트롤러의 문제점 스테이트풀셋(StatefulSet) 소개 스테이트풀셋 이란? 스테이트풀셋의 주의사항 스테이트풀셋의 파드 이름 규칙 스테이트풀셋의 파드 DNS 주소 규칙 스테이트풀셋

nearhome.tistory.com

 

Q. 네트워크 트래픽에 따라 요청이 랜덤하게 분산되는 일반적인 서비스에 비해, 헤드리스 서비스는 특정 스테이트풀셋에게 트래픽을 보냅니다. 왜 그렇게 해야 하나요?
상태 유지(Statefulness): 헤드리스 서비스는 일반적으로 상태를 유지해야 하는 애플리케이션에 사용됩니다. 스테이트풀 애플리케이션은 각각의 인스턴스에 고유한 상태를 가지고 있으며, 파드에 직접 접근하여 상태를 조작해야 할 수 있습니다. 헤드리스 서비스를 사용하면 클라이언트가 특정 파드에 직접 접근할 수 있으므로 상태 조작이 가능해집니다.
직접 연결과 성능: 헤드리스 서비스는 DNS를 통해 각 파드의 직접적인 IP 주소를 제공합니다. 이는 클라이언트가 특정 파드에 직접 연결할 수 있도록 합니다. 트래픽이 특정 파드로 직접 분산되므로 로드 밸런서 등의 추가 네트워크 계층이 필요하지 않으며, 더 효율적인 통신이 가능해집니다. 또한, 상태 유지 애플리케이션의 경우 일관된 연결을 유지해야 하므로 직접 연결이 중요합니다.
강력한 식별자와 검색 기능: 헤드리스 서비스는 파드에 고유한 DNS 항목을 제공합니다. 이는 파드에 강력한 식별자를 부여하고, 클라이언트가 DNS 조회를 통해 원하는 파드를 찾을 수 있게 합니다. 특히 스테이트풀 애플리케이션의 경우 파드의 식별이 중요하며, 헤드리스 서비스는 이를 지원하는 기능을 제공합니다.

 

pvc와 pv 실습

perisistent volume 생성

 

perisistent volume claim 생성

 

deployment에 pvc 볼륨 삽입

 

스테이트풀셋 실습

 

스테이트풀셋의 파드 이름

: 테이트풀셋의 각 파드 이름은 컨트롤러의 이름에 0부터 시작하는 순서 색인이 붙게 됨

 

  • 파드의 이름 형식
{StatefulSet-Name}-{Order}

 

  • 파드 이름 예
sanghyup-0
sanghyup-1

 

스테이트풀셋의 파드 DNS 주소

: 헤드리스 서비스와 스테이트풀셋을 같이 사용하는 경우 파드의 DNS 주소는 다음과 같음.

 

  • 파드의 DNS 주소 형식
{Pod_Name}.{Governing_Service_Domain}.{Namespace}.svc.cluster.local

 

  • 파드의 DNS 주소 예
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를 생성할 수 있음.

 

스테이트풀셋의 스케일링

  • 3개의 복제본이 있는 스테이트풀셋 파드는 0 -> 1 -> 2 순서대로 생성됩니다
  • 파드를 scale out 하기 전 기존 파드는 Running 및 Ready 상태여야 합니다
  • 파드가 scale in에 의해 삭제될 때는 역순을 진행됩니다

스테이트풀셋 관리

 - 스테이트풀셋 기본 살펴보기

//myapp-svc-headless.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-svc-headless
  labels:
    app: myapp-svc-headless
spec:
  ports:
  - name: http
    port: 80
  clusterIP: None
  selector:
    app: myapp-sts

 : 스테이트풀셋에 사용할 헤드리스 서비스 예제

 : 서비스의 이름은 myapp-svc-headless이며, .spec.clusterIP를 None으로 설정해 헤드리스 서비스를 구성합니다. 파드 레이블 셀렉터는 app=myapp-sts 레이블을 가지고 있는 파드를 지정

//myapp-sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: myapp-sts
spec:
  selector:
    matchLabels:
      app: myapp-sts
  serviceName: myapp-svc-headless
  replicas: 2
  template:
    metadata:
      labels:
        app: myapp-sts
    spec:
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb
        ports:
        - containerPort: 8080

: 스테이트풀셋 리소스의 기본 예제입니다.

: .spec.serviceName: 스테이트풀셋에 사용할 헤드리스 서비스 이름 지정

: 앞서 선언한 헤드리스 서비스를 스테이트풀셋이 사용하도록 .spec.serviceName 필드에 지정

 

헤드리스 서비스와 스테이트풀셋 리소스를 생성.

파드의 이름을 보면 0부터 시작해 인덱스가 순서대로 붙게 된 것을 확인할 수 있음.

 

스테이트풀셋의 복제본을 3개로 스케일링 해보겠습니다. Scale Out

다음 순서인 2번 인덱스가 붙은 파드를 확인할 수 있음.

 

파드의 IP

임시 파드를 생성해서 헤드리스 서비스에 접근

-> 헤드리스서비스 이름으로 DNS에 질의하면 앞서 살펴본 대로, 서비스의 ClusterIP가 아닌(어차피 없음) 각 파드의 IP로 응답하는 것을 확인할 수 있음.

 

첫 번째 파드 이름에 서비스 이름을 붙여 질의하면, 첫 번째 파드 IP만 응답하는 것을 확인할 수 있음.

 

스테이트풀셋의 볼륨 클레임 템플릿

: 스테이트풀셋은 각 파드의 고유한 볼륨을 제공

 

볼륨 클레임 템플릿

//myapp-sts-vol.yaml

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: myapp-sts-vol
spec:
  selector:
    matchLabels:
      app: myapp-sts-vol
  serviceName: myapp-svc-headless
  replicas: 2
  template:
    metadata:
      labels:
        app: myapp-sts-vol
    spec:
      containers:
      - name: myapp
        image: ghcr.io/c1t1d0s7/go-myweb:alpine
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: myapp-data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: myapp-data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
      storageClassName: rook-ceph-block

 

.spec.template: 파드의 템플릿

.spec.volumeClaimTemplates: 영구 볼륨 클레임(PVC) 템플릿

 

다른 컨트롤러와 다르게 파드별 고유 상태를 가지기 위해 각 파드에 연결할 볼륨 클레임 템플릿을 선언

 

[출처] https://nearhome.tistory.com/107

'DevOps BootCamp > 쿠버네티스' 카테고리의 다른 글

인그레스  (0) 2023.05.22
HealthCheck  (0) 2023.05.22
새 버전이 망가졌어요! 3.0에서 2.0으로 롤백하기  (1) 2023.05.20
쿠버네티스-선언적 접근  (0) 2023.05.20
쿠버네티스 service 타입의 비교  (0) 2023.05.18