[쿠버네티스] State (feat. longhorn)

최동혁·2023년 5월 2일
0

쿠버네티스

목록 보기
7/7
post-thumbnail

State

Stateless와 Stateful

  • 스테이트풀(Stateful)과 스테이트리스(Stateless)는 상호 작용 상태가 얼마나 오래 기록 되는지, 해당 정보가 어떤 식으로 저장되는지를 기준으로 구별

chat gpt 답변

쿠버네티스에서 StatefulSet은 stateful한 애플리케이션을 배포하기 위한 컨트롤러입니다. StatefulSet은 각각의 파드에 고유한 식별자를 할당하고, 일관된 네트워크 식별자를 유지함으로써 stateful한 애플리케이션에 대한 지속적인 데이터 보존성을 제공합니다.

StatefulSet은 다음과 같은 상황에서 사용될 수 있습니다.

데이터베이스 서비스 (MySQL, PostgreSQL 등)
메시징 큐 (Kafka, RabbitMQ 등)
분산 파일 시스템 (Ceph, GlusterFS 등)
반면에 stateless한 애플리케이션은 상태를 유지하지 않는 애플리케이션입니다. Stateless 애플리케이션은 새로운 요청이 들어올 때마다 동일한 응답을 반환하는 것이 목적입니다. 예를 들어, 웹 서버 애플리케이션은 주로 stateless하게 설계됩니다.

stateful과 stateless의 차이점은 주로 상태의 유무에 있습니다. stateful한 애플리케이션은 데이터를 유지하고, 일관된 상태를 유지하며, 네트워크 식별자를 유지합니다. 반면에, stateless한 애플리케이션은 데이터를 유지하지 않고, 각 요청에 대해 새로운 응답을 반환합니다. 이러한 차이점으로 인해, stateful한 애플리케이션은 주로 데이터베이스나 분산 시스템과 같은 백엔드 서비스에서 사용되며, stateless한 애플리케이션은 주로 웹 서버와 같은 프론트엔드 서비스에서 사용됩니다.

Stateless

  • 일반적으로 똑같은 서버끼리 역할이 나눠져 있지 않은 것
    • ex) 웹서버

Stateful

  • 일반적으로 똑같은 서버끼리 역할이 나눠져 있는 것
    • ex) DB 클러스터에서 마스터와 슬레이브

Stateful한 앱을 위한 볼륨

Longhorn

  • 원래 PV/PVC를 생성해 db 볼륨을 지정해주는게 정적 프로비저닝
  • 그래서 PV를 생성안해주면 안됐는데, 이건 동적으로 프로비저닝해줌.
  • 컨테이너는 휘발성이기 때문에 한번 꺼지면 데이터 전부 날아감.
  • 근데 볼륨을 추가해주면, master 노드에 특정 디렉토리를 만들어서 해당 데이터들을 저장해줌.
  • 그래서 컨테이너가 생성되면 이걸 바라봐서 데이터를 끄내오기 때문에 휘발성이여도, master 노드의 디렉토리에 생성되어 있기 때문에 문제없음.
  • Longhorn은 쿠버네티스를 위한 스토리지 시스템임.
  • Longhorn - Cloud native distributed block storage for kubernetes
    • 분산이 중요한 말
    • 데이터를 분산해서 저장하면 속도가 더 빨라짐.
    • 엔진에서 레플리카에 연결해서 알아서 저장소를 나눠서 연결해줌.
    • pv, pvc를 직접 생성하지 않고 자동으로 해준다는 뜻.

설치

  • 파드들이 생성되고 있는것을 확인할 수 있다.

  • longhorn-system 네임스페이스에 파드들이 다 생성된것을 확인할 수 있다.

대시보드 설치

  • 서비스에 frontend 서비스가 있음.
  • 들어가서 수정해야함.
  • nodeport를 이용해 외부에서 접속 가능하게 열어주는것.

  • type을 위의 사진처럼 NodePort로 바꿔줘야함.

  • 타입이 클러스터가 아닌 노드포트로 바뀐것을 확인할 수 있음.
  • 오른쪽에 31099라는 포트가 쓰여져있음.
  • 필자가 할당받은 포트임.
  • master 노드 : 할당받은 포트로 들어가보면?

  • 이런식으로 대시보드가 나옴.

  • 만약 node 카테고리에 들어갔는데 위의 그림처럼 schedulale이 아닌게 뜨면 용량 문제임
  • 연결되어 있는 node1과 node2로 들어가서 df -h 명령어 입력

  • 만약 75% 이상이면 안됨.
  • 그런 경우 docker images 명령어를 이용해 이미지들이 있는지 확인

  • 이런식으로 이미지들이 많다면, docker image prune -a 명령어를 이용해 전부 삭제
  • 그러면 용량 문제 해결

스토리지 클래스 생성

  • 스토리지클래스는 관리자가 제공하는 스토리지의 "classes"를 설명할 수 있는 방법을 제공
  • 스토리지 운영자는 PV로 사용할 볼륨을 수동으로 프로비저닝해 함(Static Provisioning 방식)
  • 이런 불편함을 해결하기 위해 자동으로 볼륨을 생성, 할당하는 StorageClass를 사용(Dynamic Provisioning 방식)
  • provisioner에는 kubernetes.io/aws-ebs와 같은 퍼블릭 클라우드의 스토리지가 올 수도 있고 driver.longhorn.io과 로컬 환경이 될 수도 있다.
  • parameters에는 provisioner가 동적으로 볼륨을 생성할 때 필요한 옵션들을 지정
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: driver.longhorn.io
parameters:
  dataLocality: disabled
  fromBackup: ""
  fsType: ext4
  numberOfReplicas: "1"
  staleReplicaTimeout: "30"
  • 자신이 작업하고 있는 네임스페이스에 해당 코드 적용
  • stateful한 앱을 올리기 위해 이 짓을 하고 있는 건데, 그러기 위해서는 데이터가 날라가지 않아야함.
  • 그러기 위해 복제본을 여러개 설정해놓는것임.

  • 그러고 나서 스토리지 클래스에 들어가보면 이렇게 fast라는 스토리지 클래스가 생성되어 있는 것을 볼 수 있다.
  • 이렇게 하면 파드가 생성이 될 때 스토리지 클래스 안에 알아서 pv와 pvc가 자동생성된다.

PV 생성 (수동)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 1G
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /hostpath
    type: DirectoryOrCreate
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 2G
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /hostpath
    type: DirectoryOrCreate

  • 용량이 다른 pv 두개 생성

PVC 생성 (수동)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-01
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1G
  storageClassName: ""

  • pv1에 연결이 된다.
  • 근데 여기서 storageClassName에 아무것도 안썼다.
  • 여기에 만약 우리가 만든 fast라는 스토리지 클래스를 지정해준다면?
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-02
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1G
  storageClassName: "fast"

  • pv가 알아서 생성되면서 연결이 됨.

  • 근데 여기에 스토리지 클래스를 아예 지정안해버리면

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-03
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 2G

  • longhorn이라는 기본 스토리지 클래스로 생김.

실습

pvc 생성 (fast 스토리지 클래스)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1G
  storageClassName: "fast"

파드 생성 (생성한 pvc를 사용하는)

apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath
spec:
  containers:
  - name: container
    image: ddarahakit2023/hello:8000
    volumeMounts:
    - name: hostpath
      mountPath: /mount1
  volumes:
  - name : hostpath
    persistentVolumeClaim:
      claimName: pvc

  • 위의 그림처럼 Volume 카테고리에 들어가 보면 잘 동작하는것을 확인할 수 있다.

  • 서로 다른 노드에서 실행이 되는데, 같은 볼륨을 공유하고 싶다면, ReadWriteMany로 설정.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 2G
  storageClassName: "fast"
  • 그리고 파드를 생성할 때 nodeselector를 이용해 서로 다른 노드에 각각 생성하면?
apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath
spec:
  nodeSelector:
    kubernetes.io/hostname: node1
  containers:
  - name: container
    image: ddarahakit2023/hello:8000
    volumeMounts:
    - name: hostpath
      mountPath: /mount1
  volumes:
  - name : hostpath
    persistentVolumeClaim:
      claimName: pvc
apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath2
spec:
  nodeSelector:
    kubernetes.io/hostname: node2
  containers:
  - name: container
    image: ddarahakit2023/hello:8000
    volumeMounts:
    - name: hostpath
      mountPath: /mount1
  volumes:
  - name : hostpath
    persistentVolumeClaim:
      claimName: pvc

서비스(headless)

  • 쿠버네티스 서비스 생성 시 .spec.clusterIP 필드 값을 None으로 설정하면 클러스터 IP가 없는 서비스를 만들 수 있다.
  • 이런 서비스를 '헤드리스 서비스(Headless Service)'라고 한다.
  • 서비스를 통하지 않고 직접 파드를 호출하기 위해서 사용
  • 이게 뭐냐면 서비스를 통해서 파드를 호출하는건 같은 공유기를 사용하는 사설ip들끼리 공인 ip로 통신하고 있는것과 같다.
  • 이렇게 되면 통신 단계가 1단계 더 늘어나기 때문에, 그걸 줄이기 위함.
  • 그런데 여기서 드는 의문점은 서비스를 통하지 않고, 파드 이름을 통해 통신을 하게 되면 해당 파드가 지워지고 다시 생기게 되었을 때, 우리가 흔히 배운 레플리카셋 컨트롤러에서는 랜덤한 이름으로 파드를 생성하기 때문에 다시 연결해주는게 힘듬
  • 그래서 사용하는게 레플리카셋 컨트롤러가 아닌 스테이트풀셋 컨트롤러이다.
kind: Service
metadata:
  name: headless1
spec:
  selector:
    svc: headless
  ports:
    - port: 8000
      targetPort: 8080    
  clusterIP: None
  • 일단 headless 서비스를 생성하는 코드이다.

컨트롤러(StatefulSet)

  • 흔히 웹 서비스는 Stateless한 서비스이다.

  • 하지만 db는 Stateful한 서비스이다.

  • 컨트롤러에서 우리가 흔히 사용한 것이 레플리카 셋으로서, 자동으로 복제품을 만들어 서버를 늘리는 방식을 사용했다.

  • 하지만 이렇게 statefulset으로 만들면 파드를 생성하더라도 이름이 랜덤하게 생기지 않음.

  • Replicaset은 파드의 이름이 랜덤으로 생성되고 순서가 없지만 Statefulset은 이름에 인덱스 번호가 붙고 순서를 가진다.

  • Replicaset은 파드가 삭제되고 다시 생성되면 대부분의 정보가 새로 생성되지만 Statefulset은 유지된다.

  • Replicaset은 PVC로 볼륨을 연결할 때 PVC가 미리 생성되어있어야 하지만 Statefulset은 동적으로 PVC 생성 가능

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replica-test
spec:
  replicas: 1
  selector:
    matchLabels:
      type: web
  template:
    metadata:
      labels:
        type: web
    spec:
      containers:
      - name: container
        image: ddarahakit2023/hello:8000
      terminationGracePeriodSeconds: 10
  • 이건 레플리카셋 컨트롤러를 만드는 코드이다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test
spec:
  replicas: 1
  selector:
    matchLabels:
      type: db
  template:
    metadata:
      labels:
        type: db
    spec:
      containers:
      - name: container
        image: ddarahakit2023/hello:8000
      terminationGracePeriodSeconds: 10
  • 이건 statefulset 컨트롤러를 만드는 코드이다.

  • 흔히 레플리카셋 컨트롤러를 이용하는 서버는 웹이다.

  • statefulset 컨트롤러를 이용하는 서버는 db이다.

  • 생성된 파드 이름을 보면 레플리카셋으로 만들어진 파드는 우리가 정한 이름 뒤에 랜덤한 문자가 붙는다.
  • 하지만 statefulset으로 만들어진 파드는 index 개념으로 붙는다.

  • 만약 레플리카 수를 3개로 늘리면 나머지 2개가 생성이 되는데, 이름도 무작위이고, 생성이 동시에 된다.
  • 물론 삭제도 동시에 된다.

  • statefulset의 레플리카 수를 3개로 늘리면, 2개가 생성이 되는데 차례대로 생성이 된다.
  • 삭제도 순서대로 삭제가 되며, 이름도 index 식으로 붙는다.
profile
항상 성장하는 개발자 최동혁입니다.

0개의 댓글