Chapter 13

멀티컨테이너와 오토스케일링

  • 13.1 멀티컨테이너 파드
  • 13.2 멀티컨테이너 디자인 패턴
  • 13.3 오토스케일링 개요
  • 13.4 HPA
  • 13.5 인플레이스 파드 리사이즈
  • 13.6 VPA

하나의 파드에 여러 컨테이너를 넣는 멀티컨테이너 패턴부터, 부하에 따라 파드를 자동으로 늘리고 줄이는 HPA, 파드 리소스 자체를 조정하는 VPA와 인플레이스 리사이즈까지 쿠버네티스의 확장 전략을 다뤄.

멀티 컨테이너 파드는 하나의 파드 안에 여러 컨테이너를 함께 넣는 거야. 마이크로서비스 아키텍처에서는 각 서비스를 독립적으로 개발하고 배포하는 게 원칙이지만, 때로는 두 서비스가 항상 함께 동작해야 하는 경우가 있거든.

예를 들어 웹 서버와 메인 앱이 있다고 하자. 메인 앱 인스턴스 하나당 웹 서버 하나가 필요한 상황이야. 코드를 합쳐서 하나로 만들면 부풀어 오르고 관리가 힘들어지니까 별도로 개발하되, 함께 배포하고 함께 확장/축소하고 싶은 거지.

멀티 컨테이너 파드가 바로 이걸 해결해줘. 같은 파드에 있는 컨테이너들은 동일한 라이프사이클을 공유해서 함께 생성되고 함께 소멸돼. 같은 네트워크 공간을 공유하니까 서로를 localhost로 접근할 수 있고, 같은 스토리지 볼륨에도 접근할 수 있어. 별도로 서비스를 만들거나 볼륨 공유를 설정할 필요가 없는 거야.

파드 정의 파일에서 멀티 컨테이너를 만드는 건 간단해. spec 아래의 containers 섹션이 배열이잖아. 거기에 컨테이너를 하나 더 추가하면 돼.

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
spec:
  containers:
    - name: web-app
      image: web-app
    - name: main-app
      image: main-app

containers가 배열인 이유가 바로 이거야. 단일 파드에 여러 컨테이너를 허용하기 위해서.

이런 멀티 컨테이너 파드에는 세 가지 디자인 패턴이 있어. 공동 배치(Co-located) 컨테이너, 초기화(Init) 컨테이너, 사이드카(Sidecar) 컨테이너야. 이 셋의 차이를 정확히 이해하는 게 중요해.

공동 배치 컨테이너는 가장 기본적인 형태야. 하나의 파드에서 두 컨테이너가 동시에 시작되고 파드 수명 내내 함께 실행돼. 두 서비스가 서로 종속적일 때 쓰는 건데, 어떤 컨테이너가 먼저 시작되는지 순서를 지정할 수 없어. 그냥 다 같이 시작되는 거야.

초기화 컨테이너는 메인 애플리케이션이 시작되기 전에 해야 할 준비 작업이 있을 때 써. 예를 들어 데이터베이스가 준비될 때까지 기다린다거나, 다른 API 서비스가 올라올 때까지 기다리는 거야. 초기화 컨테이너는 작업을 수행하고 나면 종료되고, 그 다음에 메인 컨테이너가 시작돼. 여러 개의 초기화 컨테이너를 정의하면 순차적으로 실행돼. 첫 번째가 끝나면 두 번째가 시작되고, 그것도 끝나면 메인 앱이 시작되는 식이야.

파드 정의 파일에서는 containers가 아니라 initContainers라는 별도의 속성에 정의해.

spec:
  initContainers:
    - name: wait-for-db
      image: busybox
      command: ['sh', '-c', 'until nslookup db; do sleep 2; done']
    - name: api-checker
      image: busybox
      command: ['sh', '-c', 'until nslookup api; do sleep 2; done']
  containers:
    - name: main-app
      image: main-app

사이드카 컨테이너는 공동 배치와 초기화의 특성을 결합한 거야. 메인 앱보다 먼저 시작되지만, 초기화 컨테이너처럼 종료되는 게 아니라 파드 수명 내내 계속 실행돼. 메인 앱이 종료된 후에야 종료되고. 로그 수집기 같은 게 대표적인 사례야. 메인 앱의 시작 로그부터 종료 로그까지 전부 잡아야 하니까, 메인 앱보다 먼저 시작해서 나중에 종료되어야 하거든.

사이드카 컨테이너는 initContainers에 정의하되 restartPolicy: Always를 설정해서 계속 실행되게 만들어. 이렇게 하면 메인 앱 시작 전에 사이드카가 먼저 올라오고, 메인 앱이 죽은 후에도 사이드카가 종료 로그를 캡처할 수 있어.

공동 배치와 사이드카가 헷갈릴 수 있는데, 둘 다 파드 수명 내내 실행된다는 점은 같아. 핵심 차이는 시작 순서야. 공동 배치는 순서를 보장할 수 없고, 사이드카는 메인 앱보다 먼저 시작되는 게 보장돼. 사이드카의 실제 사례로는 Filebeat 같은 로그 수집기를 앱과 함께 배포해서 Elasticsearch로 로그를 보내는 구성이 있어. Filebeat가 앱보다 먼저 시작해서 시작 로그를 캡처하고, 앱이 종료된 후에도 종료 로그를 캡처해서 문제 진단에 활용할 수 있는 거야.

멀티컨테이너로 파드 내부 구성을 다뤘으니, 이제 파드 자체를 늘리고 줄이는 오토스케일링으로 넘어가자.

오토스케일링을 이해하려면 먼저 스케일링의 기본 개념부터 알아야 해. 쿠버네티스를 잠시 잊고 물리 서버 시절로 돌아가보자.

서버에서 앱을 돌리다가 부하가 늘어나면 두 가지 방법이 있었어. 하나는 기존 서버에 CPU나 메모리를 더 꽂는 **수직 확장(Vertical Scaling)**이야. 서버를 내리고 부품 추가하고 다시 켜는 거지. 다른 하나는 서버를 더 추가해서 부하를 분산하는 **수평 확장(Horizontal Scaling)**이야.

쿠버네티스에서도 이 두 가지 개념이 그대로 적용되는데, 두 가지 차원에서 스케일링이 일어나. 하나는 워크로드 스케일링이고 다른 하나는 클러스터 인프라 스케일링이야.

클러스터 인프라를 수평 확장한다는 건 노드를 더 추가하는 거고, 수직 확장은 기존 노드의 리소스를 늘리는 거야(근데 이건 서버를 내렸다 올려야 하니까 실용적이지 않아). 워크로드를 수평 확장한다는 건 파드를 더 만드는 거고, 수직 확장은 기존 파드에 할당된 CPU/메모리를 늘리는 거야.

스케일링 방법도 수동과 자동이 있어. 수동으로 클러스터를 수평 확장하려면 새 노드를 프로비저닝하고 kubeadm join으로 클러스터에 추가하면 돼. 워크로드를 수동으로 수평 확장하려면 kubectl scale 명령으로 파드 수를 조절하고, 수직 확장은 kubectl edit 명령으로 리소스 요청/제한을 변경하면 돼.

자동으로 하는 게 진짜 오토스케일링인데, 세 가지가 있어. 클러스터 인프라를 자동으로 수평 확장하는 Cluster Autoscaler, 워크로드를 자동으로 수평 확장하는 HPA(Horizontal Pod Autoscaler), 워크로드를 자동으로 수직 확장하는 VPA(Vertical Pod Autoscaler). 이 세 가지를 하나씩 살펴보자.

HPA의 핵심은 파드의 리소스 사용량을 지속적으로 모니터링하다가 임계값을 넘으면 자동으로 파드를 추가하고, 줄어들면 파드를 제거하는 거야.

수동으로 한다고 생각해봐. 관리자가 컴퓨터 앞에 앉아서 kubectl top pod 명령으로 리소스 사용량을 계속 지켜보다가, CPU가 임계값에 근접하면 kubectl scale 명령을 실행해서 파드를 늘리는 거지. 당연히 이건 비현실적이야. 24시간 모니터링해야 하고, 갑작스러운 트래픽 급증에 빠르게 대응하기도 어렵잖아.

HPA가 이걸 자동으로 해줘. CPU, 메모리 또는 사용자 정의 메트릭에 따라 배포, 스테이트풀셋, 레플리카셋의 파드 수를 자동으로 조절해. 사용량이 높아지면 파드를 추가하고, 낮아지면 제거해서 리소스를 절약하는 거야.

HPA를 만드는 명령형 방법은 이래.

kubectl autoscale deployment myapp --min=1 --max=10 --cpu-percent=50

이 명령을 실행하면 쿠버네티스가 myapp 배포에 대한 HPA를 생성해. 먼저 파드에 설정된 CPU 제한(예: 500밀리코어)을 읽고, 메트릭 서버를 통해 사용량을 지속적으로 모니터링하다가 50%를 초과하면 파드를 늘리고, 내려가면 줄여. 최소 1개에서 최대 10개 사이로 조절하는 거야.

kubectl get hpa로 현재 상태를 확인할 수 있어. 현재 CPU 사용률, 임계값, 최소/최대값, 현재 복제본 수를 볼 수 있지.

선언형으로도 만들 수 있어.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50

scaleTargetRef가 모니터링 대상이고, metrics에서 어떤 메트릭을 기준으로 스케일링할지 정의해.

HPA는 쿠버네티스 1.23부터 기본 내장이라 별도 설치가 필요 없어. 다만 메트릭 서버가 전제 조건이야. 메트릭 서버가 없으면 현재 리소스 사용률을 알 수 없으니까. 기본 메트릭 서버 외에도 사용자 정의 메트릭 어댑터나 Datadog, Dynatrace 같은 외부 소스도 참조할 수 있지만, CKA 시험 범위에서는 기본 메트릭 서버면 충분해.

HPA가 더 이상 필요 없으면 kubectl delete hpa <이름>으로 삭제하면 돼.

HPA가 파드 수를 조절하는 거라면, 파드를 재시작하지 않고 리소스를 바꾸는 방법도 있어. 바로 인플레이스 리사이즈야.

파드 리소스의 인플레이스 크기 조정이란, 파드를 죽이지 않고도 CPU나 메모리 할당량을 변경할 수 있는 기능이야. 기본적으로 쿠버네티스에서 배포의 리소스 요구 사항을 변경하면 기존 파드를 삭제하고 새 파드를 만드는 게 기본 동작이거든. 특히 상태 저장 워크로드에서는 이게 꽤 문제가 될 수 있잖아.

이 기능은 현재 쿠버네티스 1.33 기준으로 알파 버전이야. 기본적으로 활성화되어 있지 않고, InPlacePodVerticalScaling이라는 기능 플래그를 true로 설정해야 사용할 수 있어. 향후 베타나 안정 단계로 올라가면 기본 활성화될 예정이지만 아직은 아니야.

이 기능이 활성화되면 파드 정의에서 resizePolicy라는 새로운 옵션을 쓸 수 있어. 각 리소스별로 변경 시 재시작이 필요한지 아닌지를 지정하는 거야.

spec:
  containers:
    - name: my-app
      image: my-app
      resizePolicy:
        - resourceName: cpu
          restartPolicy: NotRequired
        - resourceName: memory
          restartPolicy: RestartContainer

이 예시에서는 CPU를 변경해도 파드를 재시작하지 않지만, 메모리를 변경하면 재시작이 필요하다고 정의한 거야. 그래서 CPU를 1코어로 업데이트하면 파드를 죽이지 않고 그대로 적용돼.

다만 제한 사항이 있어. CPU와 메모리 리소스에 대해서만 동작하고, 파드의 QoS 클래스는 변경할 수 없어. 초기화 컨테이너나 임시 컨테이너의 크기는 조정할 수 없고, 리소스 요청과 제한은 한번 설정하면 위치를 바꿀 수 없어(요청이 제한보다 커지는 식으로는 안 됨). 컨테이너 메모리 제한을 현재 사용량보다 낮게 줄일 수도 없어. 만약 줄이려고 하면 크기 조정 상태가 "진행 중"으로 유지되면서 가능해질 때까지 기다려. 그리고 현재로서는 Windows 파드의 크기 조정은 지원하지 않아.

인플레이스 리사이즈가 아직 알파 단계인 만큼, 현재 프로덕션에서 파드 리소스를 자동으로 조정하려면 VPA를 쓰게 돼.

VPA는 파드의 CPU, 메모리 할당량을 자동으로 조정해주는 오토스케일러야. HPA가 파드 수를 늘리고 줄이는 거라면, VPA는 기존 파드의 리소스 크기를 키우거나 줄이는 거야.

수동으로 한다고 생각해보면, kubectl top pod으로 리소스 사용량을 모니터링하다가 임계값에 도달하면 kubectl edit deployment 명령으로 들어가서 리소스 요청과 제한을 직접 바꿔주는 거야. 그러면 기존 파드가 죽고 새 리소스 설정의 파드가 올라와. 이걸 VPA가 자동으로 해주는 거지.

HPA와 다르게 VPA는 쿠버네티스에 기본 내장이 아니야. 별도로 설치해야 해. GitHub 리포지토리에 있는 VPA 정의 파일을 kubectl apply로 적용하면 kube-system 네임스페이스에 세 가지 구성 요소가 배포돼. 추천자(Recommender), 업데이터(Updater), **어드미션 컨트롤러(Admission Controller)**야.

추천자는 메트릭 API에서 리소스 사용량을 지속적으로 모니터링하고, 과거 및 실시간 데이터를 수집해서 최적의 CPU/메모리 값을 권장해. 추천자 자체는 파드를 직접 수정하지 않고 제안만 해. 업데이터는 추천자의 정보를 기반으로 최적이 아닌 리소스로 실행 중인 파드를 감지하고, 업데이트가 필요하면 해당 파드를 퇴출(종료)시켜. 어드미션 컨트롤러는 새 파드가 생성될 때 개입해서 추천자의 권장값으로 파드 사양을 변경해, 올바른 리소스 설정으로 시작되게 만들어.

흐름은 이래. 추천자가 정보를 수집하고, 업데이터가 그 정보와 실제 파드를 비교해서 임계값을 초과하면 파드를 죽이고, 배포가 자동으로 파드를 재생성할 때 어드미션 컨트롤러가 개입해서 새 리소스 값을 적용하는 거야.

VPA 정의 파일은 이렇게 생겼어.

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: myapp-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        minAllowed:
          cpu: 100m
        maxAllowed:
          cpu: 2
  updatePolicy:
    updateMode: "Auto"

updatePolicy의 mode가 네 가지 있어. Off는 권장 사항만 만들고 아무것도 안 해. 추천자만 동작하고 업데이터와 어드미션 컨트롤러는 작동 안 해. Initial은 파드가 새로 생성될 때만 리소스를 변경해. 이미 실행 중인 파드는 건드리지 않아서 업데이터가 파드를 죽이지 않아. Recreate는 리소스 사용량이 범위를 벗어나면 업데이터가 파드를 퇴출하고 새로 만들어. Auto는 현재로서는 Recreate와 동일하게 동작하지만, 향후 인플레이스 파드 리사이즈 기능이 안정화되면 파드를 죽이지 않고 리소스를 변경하는 게 기본 동작이 될 거야.

VPA의 권장 사항은 kubectl describe vpa <이름>으로 확인할 수 있어.

HPA와 VPA 중 뭘 써야 할지 고민될 수 있는데, 용도가 달라. VPA는 기존 파드의 리소스를 최적화하는 거고, HPA는 파드 수를 조절해서 부하를 분산하는 거야. VPA는 파드를 재시작해야 해서 다운타임이 생기지만, HPA는 기존 파드를 유지하면서 새 파드만 추가하니까 가용성이 유지돼. 갑작스러운 트래픽 급증에는 HPA가 훨씬 빠르게 대응할 수 있어.

VPA는 데이터베이스, JVM 기반 앱, AI 워크로드 같은 상태 저장이나 CPU/메모리를 많이 쓰는 애플리케이션에 적합해. 예를 들어 시작할 때 CPU를 많이 쓰지만 이후에는 적게 쓰는 앱이 있다면, VPA가 초기에는 높은 CPU를 할당하고 나중에 줄여주는 거야. HPA는 웹 애플리케이션, 마이크로서비스, API 서버, 메시지 큐 같은 상태 비저장 서비스에서 트래픽 변동에 따라 빠르게 확장/축소해야 할 때 이상적이야.


정리

13장 읽고 기억할 거 세 가지:

  1. 멀티컨테이너 파드는 공동 배치(순서 없이 동시 시작), 초기화(메인 앱 전에 실행 후 종료), 사이드카(메인 앱 전에 시작하고 끝까지 유지) 세 가지 패턴이 있고, 핵심 차이는 시작 순서와 수명이다.
  2. HPA는 메트릭 서버 기반으로 파드 수를 자동 조절하는 수평 확장이고, VPA는 추천자-업데이터-어드미션 컨트롤러 3종 세트로 파드 리소스를 자동 조정하는 수직 확장이다. HPA는 기본 내장, VPA는 별도 설치.
  3. 인플레이스 리사이즈는 파드를 죽이지 않고 CPU/메모리를 변경하는 알파 기능이며, VPA의 Auto 모드가 향후 이 기능과 통합될 예정이다.