본문 바로가기

Cloud-native/Kubernetes

[Kubernetes]클러스터의 안정성을 최대화할 수 있는 여러 설정과 구성요소

이번 글에서는 Kubernetes의 안정성을 향상시키는 주요 설정과 구성요소에 대해 다룹니다. 이러한 설정과 구성요소는 파드의 생명주기 관리, 자원 할당 및 사용, 스케줄링 전략, 노드 관리 등 여러 범주에 걸쳐 있습니다.

특히 다음 주제에 초점을 맞춥니다.

  1. 파드의 생명주기 및 상태 관리: 파드의 정상 작동을 지속적으로 확인하고 필요에 따라 복구 또는 재시작하는 메커니즘.
  2. 스케줄링 전략: 파드가 클러스터 내에서 어떻게 배치되며, 고가용성과 효율성을 동시에 달성하기 위한 전략.
  3. 리소스 관리: 클러스터의 리소스를 효율적으로 할당하고 사용하는 방법.
  4. 노드 관리 및 안정성: 노드의 상태를 모니터링하고, 장애 발생 시 자동 복구를 위한 전략.

Pod Lifecycle

Probe

개요:

Kubernetes에서 Pod의 상태를 정기적으로 확인하는 메커니즘입니다.

설명:

Liveness, Readiness 및 Startup Probe를 통해 컨테이너가 제대로 작동하고 있는지, 준비되었는지, 시작되었는지를 확인합니다. 이를 통해 문제가 발생한 Pod를 자동으로 재시작하거나 새로운 요청을 받지 않도록 할 수 있습니다.

예시: 애플리케이션 헬스 체크

시나리오: 웹 애플리케이션의 상태를 주기적으로 확인하고, 문제가 발생하면 자동으로 재시작하려 한다.

목적: 웹 애플리케이션의 건강 상태를 보장하고 문제가 생기면 빠르게 대응한다.

해결: liveness probe를 사용하여 애플리케이션의 건강 상태를 주기적으로 검사한다.

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
    - name: my-container
      image: my-image
      livenessProbe:
        httpGet: # 응답의 상태 코드가 200 이상 400 미만이면 진단이 성공한 것으로 간주
          path: /health
          port: 8080
        initialDelaySeconds: 10
        periodSeconds: 5
      readinessProbe:
        tcpSocket: # 포트가 활성화되어 있다면 진단이 성공한 것으로 간주
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 10
      startupProbe:
        exec: # 상태 코드 0으로 종료되면 진단이 성공
          command:
            - cat
            - /tmp/initialized
        failureThreshold: 30
        periodSeconds: 1

Pod Scheduling

podAntiAffinity

개요:

특정 조건을 만족하는 Pod들이 동일한 토폴로지 도메인 (예: 노드, 존, 레이블 등)에서 함께 실행되는 것을 방지하는 제약조건입니다.

설명:

이 설정을 사용하면 하나의 노드에서 문제가 발생했을 때, 동일한 애플리케이션의 모든 인스턴스에 영향을 미치지 않도록 분산시켜 가용성과 성능을 향상시킬 수 있습니다.

예시: 동일한 데이터 센터 내의 랙(Rack) 배치

시나리오: 대규모 데이터 센터에서 각 랙에는 여러 노드가 있으며, 각 랙은 독립적인 전원과 네트워크 스위치를 가지고 있다.

목적: 한 랙에 문제가 발생하면 그 랙에 있는 모든 노드가 영향을 받을 수 있다. 따라서 고가용성을 위해 특정 애플리케이션의 여러 인스턴스를 서로 다른 랙에 배치하려 한다.

해결: podAntiAffinity를 사용하여 특정 애플리케이션의 Pod들이 동일한 랙에 위치하지 않도록 한다.

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
    - name: my-container
      image: my-image
  affinity:
    podAntiAffinity: # 4. 해당 노드에 Pod를 배포하지 마라
      requiredDuringSchedulingIgnoredDuringExecution: # 3. 가급적(preferred), 반드시(required)
        - labelSelector:
            matchExpressions: # 2. 해당 라벨을 가진 pod가 이미 실행 중이라면
              - key: app
                operator: In
                values:
                  - my-app
          topologyKey: kubernetes.io/hostname # 1. 해당 라벨을 가진 노드에

topologySpreadConstraints

개요:

지정된 토폴로지 도메인에서 Pod들의 균등한 분포를 보장하는 것입니다. 즉, Pod의 수가 각 토폴로지 도메인에서 균등하게 분포되도록 합니다.

설명:

이를 사용하면 특정 레이블을 가진 pod들의 분포가 토폴로지 도메인(예: 노드, 존, 레이블 등) 마다 균일하게 유지되어, 특정 영역에 장애가 발생해도 애플리케이션의 가용성이 유지될 수 있습니다.

예시: 다중 존(Multi-Zone) 배치

시나리오: 클라우드 제공 업체에서 제공하는 존 기반의 클러스터에서 작동하는 애플리케이션.

목적: 특정 존에 장애가 발생해도 애플리케이션의 가용성을 유지하기 위해, 애플리케이션의 인스턴스를 여러 존에 균등하게 분포시키고 싶다.

해결: topologySpreadConstraints를 사용하여 애플리케이션의 Pod들이 여러 존에 균등하게 분포되도록 한다.

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
    - name: my-container
      image: my-image
    topologySpreadConstraints:
      - maxSkew: 1 # 노드 간에 허용되는 최대 차이를 지정
        topologyKey: kubernetes.io/zone # 분산 제약 조건을 적용할 기준 키를 지정, 여기서는 노드의 가용영역을 기준
        whenUnsatisfiable: ScheduleAnyway # 제약 조건을 만족시키지 못할 경우 어떻게 처리할지를 지정
        labelSelector:
          matchLabels:
            app: my-app

nodeAffinity - Topology Affinity

개요:

Pod가 특정 노드 또는 토폴로지 도메인에 속해야 할 경우, 토폴로지 어피니티를 사용하여 해당 제약 조건을 설정할 수 있습니다.

설명:

이를 통해 성능, 가용성 또는 기타 요구 사항에 따라 Pod가 특정 노드에만 배치되거나 특정 토폴로지 영역에서만 실행되도록 제어할 수 있습니다.

예시: 메모리가 높은 노드에만 pod를 스케줄링

Pod가 hardware-type=high-memory 레이블을 가진 노드에만 배치되도록 토폴로지 어피니티를 설정하고 있습니다. 따라서, Pod는 high-memory 토폴로지 영역에 속한 노드에만 스케줄링될 수 있습니다.

시나리오: 메모리가 많은 노드와 일반 노드가 데이터 센터에 혼합되어 있다.

목적: 메모리 집약적인 애플리케이션을 메모리가 많은 노드에 배치한다.

해결: nodeAffinity 설정을 사용하여 메모리 집약적인 애플리케이션을 높은 메모리 노드에만 스케줄링한다.

apiVersion: v1
kind: Pod
metadata:
  name: memory-intensive-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: hardware-type
            operator: In
            values:
            - high-memory
  containers:
  - name: memory-intensive-container
    image: memory-intensive-image

Resource Management

서비스 품질 클래스(QoS)

Configure Quality of Service for Pods https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/

개요:

Pod의 리소스 요청 및 제한을 기반으로 한 서비스 품질을 정의합니다.

설명:

Guaranteed, Burstable, BestEffort 세 가지 클래스로, 리소스 확보와 스케줄링 우선순위에 영향을 줍니다. 우선순위 Guaranteed > Burstable > BestEffort

Guaranteed: 다른 Pod들에 의해 리소스를 선점당하지 않으며, 노드의 리소스를 보장받는 우선 순위가 높은 Pod에 사용됩니다.

Burstable: Pod는 요구 사항을 충족하는 경우에는 노드의 리소스를 보장받지만, 다른 Pod들이 리소스를 요청할 경우 리소스를 선점당할 수 있습니다.

BestEffort : 다른 Pod들이 리소스를 요청하는 경우에는 할당된 리소스가 제한적일 수 있습니다. BestEffort 클래스는 클러스터의 남은 리소스를 활용하고, 높은 우선 순위를 가지는 Pod들에게 할당될 수 있는 제한적인 리소스를 사용합니다.

예시: 우선 순위가 높은 애플리케이션

나리오: 서로 다른 우선 순위의 애플리케이션을 동일한 클러스터에서 실행하고 있다.

: 중요한 애플리케이션에 자원을 보장하며, 다른 애플리케이션에 영향을 주지 않는다.

해결: QoS 클래스를 사용하여 pod에 자원 우선순위를 부여한다.

apiVersion: v1
kind: Pod
metadata:
  name: guaranteed-pod
spec:
  containers:
  - name: guaranteed-container
    image: some-image
    resources:
      requests:
        memory: "1Gi"
        cpu: "500m"
      limits:
        memory: "2Gi"
        cpu: "1"

HPA (Horizontal Pod Autoscaler)

개요:

CPU, 메모리 사용량 등의 메트릭을 기반으로 Pod의 인스턴스 수를 자동으로 조정하는 기능입니다.

설명:

부하가 증가하면 자동으로 Pod의 수를 늘려서 처리능력을 향상시키며, 부하가 감소하면 자동으로 줄여 리소스를 효율적으로 사용합니다.

예시: 트래픽 증가 시 자동으로 scale out

시나리오: 웹 애플리케이션에 대한 트래픽이 변동적이다.

목적: 트래픽 증가 시 자동으로 pod 인스턴스를 늘려서 요청을 처리한다.

해결: HPA를 설정하여 CPU 사용률 또는 다른 메트릭을 기반으로 pod 인스턴스를 자동으로 조절한다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 80

Node Management

PodDisruptionBudget

개요:

동시에 중단될 수 있는 Pod의 최대 수나 비율을 지정하는 설정입니다.

설명:

유지보수 또는 업데이트 중에도 서비스 중단을 최소화하고 애플리케이션의 가용성을 유지할 수 있습니다.

예시: 높은 가용성이 필요한 애플리케이션

시나리오: 회사의 핵심 웹 애플리케이션은 높은 가용성이 필요하다.

목적: 유지 보수나 업데이트 중에도 항상 최소한의 파드 인스턴스가 작동하도록 보장한다.

해결: PodDisruptionBudget를 설정하여 동시에 다운되어서는 안 되는 최소 파드의 수나 비율을 지정한다.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-pdb
spec:
  minAvailable: 2   # 동시에 다운될 수 없는 파드의 최소 개수
  selector:
    matchLabels:
      app: my-app

Karpenter

개요:

Karpenter는 여러 클라우드 공급자와 통합되며, 실시간으로 노드를 프로비저닝하고 사용되지 않는 노드를 종료하여 클러스터의 효율성을 향상시킵니다.

설명:

실제 리소스 사용을 기반으로 클러스터의 크기를 자동으로 조정하여 리소스 사용 효율성을 최적화합니다.

Karpenter는 파드 요구 사항에 따라 동적으로 노드를 프로비저닝합니다.

왜 필요한가?

  • 리소스 요청이 많아진다면, Karpenter는 자동으로 노드를 확장하여 사용자의 요청을 처리할 수 있습니다.
  • 노드의 활용도를 극대화하며 리소스 낭비를 최소화합니다.

예시: 클러스터의 리소스를 모두 사용하여 pod가 pending 상태로 scale out을 하지 못하고 있다.

시나리오: 클러스터 내의 파드 요청이 증가하여 더 많은 노드가 필요하다.

목적: 파드 요청에 따라 노드를 자동으로 프로비저닝하고, 사용하지 않을 때는 자동으로 종료하여 비용을 절약한다.

해결: Karpenter 프로비저너를 설치하고 설정하여 파드 요청에 따라 자동으로 노드를 프로비저닝하도록 한다.