Post

쿠버네티스 컨트롤러를 이용한 애플리케이션 스케일링

쿠버네티스 컨트롤러를 이용한 애플리케이션 스케일링

애플리케이션 스케일링의 기본 개념

쿠버네티스에서 애플리케이션의 처리 용량을 늘리는 가장 기본적인 방법은 파드(Pod)의 수를 늘리는 것이다 동일한 애플리케이션이 동작하는 여러 개의 파드를 레플리카(Replica)라고 부르며, 이 레플리카들은 클러스터의 여러 노드에 분산 배치되어 부하를 나누어 처리한다

하지만 우리는 파드를 직접 하나씩 생성하고 관리하는 경우는 거의 없음 대신, 파드를 효과적으로 관리하고 스케일링을 자동화하는 더 높은 수준의 리소스, 즉 컨트롤러(Controller)를 사용한다 컨트롤러는 내부에 파드 템플릿(Pod Template)을 가지고 있어, 이 템플릿을 바탕으로 파드를 생성, 복제, 복구하는 모든 작업을 책임진다

 

레플리카셋(ReplicaSet): 파드의 수를 보장

레플리카셋의 역할은 지정된 수의 파드 레플리카가 항상 실행되도록 보장하는 것

만약 레플리카셋이 관리하는 파드 중 하나가 비정상적으로 종료되거나 삭제되면, 레플리카셋은 즉시 파드 템플릿을 사용해 새로운 파드를 생성하여 지정된 레플리카 수를 유지한다

레플리카셋 YAML 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# replicaset-example.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: whoami-web
spec:
  replicas: 3 # 이 레플리카셋은 항상 3개의 파드를 유지합니다.
  selector:
    matchLabels:
      app: whoami-web # 이 레이블을 가진 파드를 관리 대상으로 삼습니다.
  template: # 파드를 생성할 때 사용할 템플릿입니다.
    metadata:
      labels:
        app: whoami-web # selector의 레이블과 반드시 일치해야 합니다.
    spec:
      containers:
      - name: whoami-container
        image: kiamol/ch02-whoami
  • replicas: 유지할 파드의 수를 정의
  • selector: 어떤 레이블을 가진 파드를 자신의 관리 대상으로 삼을지 결정
  • template: selector와 연결될 파드를 어떻게 만들지에 대한 명세서 템플릿에 정의된 레이블은 selectormatchLabels와 반드시 일치해야 한다

📖 간단 실습: 레플리카셋의 자동 복구 확인하기

  1. 위 YAML 파일을 replicaset-example.yaml로 저장하고 클러스터에 적용

    1
    
    kubectl apply -f replicaset-example.yaml
    
  2. 파드가 3개 생성되었는지 확인

    1
    2
    3
    4
    5
    
    kubectl get pods
    # NAME                 READY   STATUS    RESTARTS   AGE
    # whoami-web-abcde     1/1     Running   0          10s
    # whoami-web-fghij     1/1     Running   0          10s
    # whoami-web-klmno     1/1     Running   0          10s
    
  3. 파드 중 하나를 강제로 삭제

    1
    
    kubectl delete pod whoami-web-abcde
    
  4. 잠시 후 다시 파드 목록을 확인하면, 레플리카셋이 즉시 새로운 파드를 생성하여 다시 3개를 유지하는 것을 볼 수 있다

    1
    2
    3
    4
    5
    
    kubectl get pods
    # NAME                 READY   STATUS    RESTARTS   AGE
    # whoami-web-fghij     1/1     Running   0          50s
    # whoami-web-klmno     1/1     Running   0          50s
    # whoami-web-pqrst     1/1     Running   0          5s  <-- 새로 생성된 파드
    

 

스케일링은 어떻게 동작할까?

레플리카 수를 늘렸을 때 어떻게 새로운 파드에 거의 즉시 트래픽이 전달될 수 있을까? 여기에는 두 가지 핵심 원리가 있다

  1. 컨테이너 이미지 캐싱: 실습 환경처럼 단일 노드 클러스터에서는 애플리케이션의 도커 이미지가 노드에 이미 다운로드(캐싱)되어 있다 따라서 새 파드를 만들 때 이미지를 다시 내려받을 필요가 없어 매우 빠르게 컨테이너를 시작할 수 있다 만약 이미지가 없는 다른 노드에 파드가 배치된다면, 이미지 다운로드 시간 때문에 조금 더 느려질 수 있음
  2. 서비스와 파드의 느슨한 결합 (Loose Coupling): 서비스(Service)는 특정 파드를 직접 가리키는 것이 아니라, 레이블 셀렉터(Label Selector)를 통해 조건에 맞는 파드 그룹 전체를 바라본다
    • 레플리카셋의 레플리카 수를 늘리면, 서비스의 레이블 셀렉터와 일치하는 파드의 수도 늘어난다
    • 서비스는 새로 생긴 파드를 자동으로 감지하고 자신의 엔드포인트(Endpoint) 목록에 추가
    • 이후 서비스로 들어오는 요청은 기존 파드와 새로 생긴 파드 모두에게 고르게 분배(로드 밸런싱)된다

이처럼 서비스와 파드가 직접적인 관계가 아닌 레이블을 통해 느슨하게 연결되어 있기 때문에, 파드의 수가 동적으로 변하더라도 서비스는 중단 없이 트래픽을 분배할 수 있음

 

디플로이먼트(Deployment): 더 높은 수준의 관리자

사실 우리는 레플리카셋을 직접 사용하는 경우는 거의 없다 대신, 레플리카셋을 관리하는 컨트롤러인 디플로이먼트를 주로 사용

디플로이먼트는 레플리카셋이 가진 ‘파드 개수 유지’ 기능은 물론, 애플리케이션의 업데이트와 롤백이라는 매우 중요한 기능을 추가로 제공한다

디플로이먼트의 스케일링과 업데이트

  • 스케일링: 디플로이먼트의 replicas 필드 값을 변경하면, 디플로이먼트는 자신이 관리하는 레플리카셋의 replicas 값을 수정하여 파드 수를 조절한다
  • 업데이트: 만약 디플로이먼트의 파드 템플릿(예: 컨테이너 이미지 버전 변경)을 수정하면, 디플로이먼트는 다음과 같이 동작
    1. 새로운 파드 템플릿을 가진 새로운 레플리카셋을 생성
    2. 새로운 레플리카셋의 파드 수를 점진적으로 늘리는 동시에, 기존 레플리카셋의 파드 수는 점진적으로 줄인다
    3. 업데이트가 완료되면 기존 레플리카셋의 파드는 0개가 되고, 새로운 레플리카셋만 남게 된다 (이전 레플리카셋은 롤백을 위해 삭제되지 않고 남아있음)

📖 디플로이먼트 스케일링 실습

  1. 아래 내용으로 deployment-example.yaml 파일을 생성 replicas 필드는 선택사항이며, 생략 시 기본값은 1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    # deployment-example.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:1.14.2
            ports:
            - containerPort: 80
    
  2. 디플로이먼트를 생성

    1
    
    kubectl apply -f deployment-example.yaml
    
  3. kubectl scale 명령을 사용하여 YAML 파일을 수정하지 않고 직접 스케일링할 수 있다

    1
    2
    
    # 레플리카 수를 4개로 늘리기
    kubectl scale deployment nginx-deployment --replicas=4
    
  4. 디플로이먼트와 그에 의해 생성된 레플리카셋, 파드를 모두 확인

    1
    
    kubectl get deployment,replicaset,pod
    

결과

 

데몬셋(DaemonSet): 모든 노드에 파드를 배치하는 방법

데몬셋(DaemonSet)은 이름 그대로, 리눅스의 ‘데몬(Daemon)’ 프로세스처럼 동작하는 것을 목표로 하는 컨트롤러 데몬이란 시스템 백그라운드에서 항상 실행되며 특정 기능을 수행하는 프로세스를 의미

쿠버네티스에서 데몬셋의 역할은 “클러스터 내 모든 노드(또는 특정 조건을 만족하는 일부 노드)에 파드를 하나씩만 실행하도록 보장하는 것” 이다

레플리카셋이나 디플로이먼트가 ‘클러스터 전체에서 총 몇 개의 파드를 유지할 것인가’에 초점을 맞춘다면, 데몬셋은 ‘각 노드마다 하나씩 배치할 것인가’ 에 초점을 맞춘다 클러스터에 새로운 노드가 추가되면 데몬셋은 해당 노드에도 자동으로 파드를 배치하고, 노드가 제거되면 해당 파드도 함께 정리

주로 다음과 같은 인프라 수준의 기능을 구현할 때 많이 사용된다

  • 로그 수집기: 모든 노드의 로그를 수집하는 에이전트 (예: Fluentd, Logstash)
  • 모니터링 에이전트: 모든 노드의 성능 지표를 수집하는 에이전트 (예: Prometheus Node Exporter)
  • 네트워크 프록시: 모든 노드에서 네트워크 트래픽을 처리하는 프록시

데몬셋 YAML 예제

각 노드에서 리버스 프록시 역할을 할 Nginx 파드를 하나씩 실행하는 데몬셋 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# nginx-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-proxy
spec:
  selector:
    matchLabels:
      app: nginx-proxy # 이 레이블을 가진 파드를 관리합니다.
  template:
    metadata:
      labels:
        app: nginx-proxy
    spec:
      # nodeSelector: # 만약 특정 노드에만 실행하고 싶다면 이 부분을 활성화합니다.
      #   disktype: ssd # disktype=ssd 레이블이 부여된 노드에만 파드를 배치합니다.
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

📖 데몬셋 실습

  1. 위 YAML 파일을 nginx-daemonset.yaml로 저장하고 적용

    1
    
    kubectl apply -f nginx-daemonset.yaml
    
  2. -o wide 옵션을 사용하여 파드가 어느 노드에 배치되었는지 확인 후 클러스터의 모든 노드에 nginx-proxy 파드가 하나씩 실행되는 것을 볼 수 있음

    1
    
    kubectl get pods -o wide
    

 

쿠버네티스 객체 간의 소유권 관계

컨트롤러는 레이블 셀렉터를 이용해 자신의 관리 대상을 결정하고, 관리 대상 리소스는 메타데이터의 ownerReferences 필드에 자신을 관리하는 소유자(Owner) 정보를 기록한다 이 관계 덕분에 디플로이먼트를 삭제하면 관련된 레플리카셋과 파드가 연쇄적으로 삭제(Cascading Deletion)될 수 있다

📖 소유권 관계 확인하기

  1. 앞서 생성한 디플로이먼트가 만든 파드 중 하나의 이름을 확인

    1
    
    kubectl get pods -l app=nginx # app=nginx 레이블을 가진 파드 조회
    
  2. 해당 파드의 YAML 정의를 출력하여 ownerReferences 필드를 확인

    1
    2
    
    # <pod-name>을 실제 파드 이름으로 변경
    kubectl get pod <pod-name> -o yaml
    

    YAML 출력 결과에서 metadata 섹션을 보면 다음과 같은 ownerReferences 정보를 찾을 수 있다 이 파드는 nginx-deployment라는 이름의 레플리카셋에 의해 관리되고 있음을 알 수 있음

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    metadata:
      ownerReferences:
      - apiVersion: apps/v1
        blockOwnerDeletion: true
        controller: true
        kind: ReplicaSet
        name: nginx-deployment-86dcfdf4c6
        uid: b5fe9340-cd56-45f6-82d3-37ccc5c92c33
      resourceVersion: "1263039"
      uid: 3e47507d-b915-4a78-9d40-4169b708ed92
    
This post is licensed under CC BY 4.0 by the author.