Kubernetes

PodDisruptionBudget을 활용한 Application 보호

Operation CWAL 2021. 9. 22. 01:16

K8s의 고가용성 보장

K8s엔 서비스의 고가용성(HA; High Availability)을 보장하기 위해 다양한 Workload(ex: Deployment, Statefulset, Daemonset 등)이 존재한다. Replica 중 Pod 하나가 죽더라도, 다른 Pod이 살아있다면 계속해서 서비스를 제공할 수 있다. 또한 부족해진 replica 갯수는 K8s Controller를 통해 자동으로 다시 채워지며 이를 Reconciliation이라 한다.

 

하지만 특정 Usecase의 경우, 무결성 등의 이유로 반드시 정해진 갯수 이상의 Pod이 반드시 Running 상태로 존재해야 할 필요가 있다. 예를 들어 etcd, zookeeper와 같이 여러개 Instance가 클러스터를 구성하여 Stateful 서비스를 제공하는 경우, 정상적인 동작을 위해 과반수 이상의 Pod이 실행중이어야 한다.

물론 해당 Application의 Owner라면 자신이 담당하는 서비스의 특성을 잘 이해하고 있기 Pod을 함부로 제거하는 경우는 없을 것이다. 하지만 클러스터 관리자가 업그레이드나 설정 변경 등을 위해 Node를 Drain시킨다면, Pod이 Eviction되는 과정에 수 초에서 수 분 정도 이 조건을 깨뜨릴 수 있다. 

이러한 불상사를 막기 위해 K8s는 v1.7부터 PodDisruptionBudget(이하 PDB) 이라는 리소스를 beta로 제공하기 시작했으며, v1.21부터는 정식으로 사용 가능하다.

 

비자발적 중단과 자발적 중단

비자발적 중단(Involuntary Disruption)은 커널 패닉이나 H/W 장애 등으로 Node가 죽거나 VM 삭제와 같은 이유로 해당 Node에 위치한 Pod이 중단되는 상태를 의미한다. 이는 K8s 영역 밖에서 일어나기 때문에, 부족해진 Pod 갯수를 채우는 일 외에는 딱히 대응할 수단은 없다.

 

우리가 집중해야할 부분은 자발적 중단(Voluntary Disruption)이다. 클러스터 관리자가 Node를 업그레이드 하기 위해 Drain 작업(kubectl drain)을 수행하거나, Cluster Autoscaling에 의해 Scale-in으로 임의의 Node를 삭제해야 하는 경우, K8s 내부적으로 해당 Node에 존재하는 Pod을 제거하고 다른 Node에 다시 생성하는 Eviction 작업을 수행한다. PDB는 이런 자발적 중단에 한해 실행중인 Pod의 갯수를 지정한 숫자만큼 보호할 수 있다.

 

 

PDB Manifest 구성

PDB의 spec은 아래와 같이 3개의 필드로 구성된다.

  • .spec.selector: 반드시 입력해야 하는 field이며, 해당 PDB를 적용할 Pod의 Label을 명시한다.
  • .spec.maxUnavailable: Eviction 발생시, 사용불가 상태를 허용할 수 있는 Pod의 갯수를 명시한다. 절대값 또는 퍼센티지로 표기한다.
  • .spec.minAvailable: Eviction 발생하여도, 사용 가능한 Pod의 갯수이다. 절대값 또는 퍼센티지로 표기한다.

 

참고로 PDB 정의시, maxUnavailable과 minAvailable 필드 둘 중에 하나를 선택해야 하며, 하나의 PDB에서 두 필드를 동시에 사용 할 수는 없다.

 

PDB Manifest 예시

테스트를 위해 우선 다음과 같이 Nginx를 Deployment 형식으로 배포한다. 4개의 replica로 구성된 것을 알 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: nginx
  name: nginx-deploy
spec:
  replicas: 4
  selector:
    matchLabels:
      run: nginx
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
       - image: nginx
         name: nginx

 테스트 환경은 Master 1개, Worker 2개로 구성되어 있으며, 위 Nginx Deployment를 통해 Nginx Pod이 각 Worker에 2개씩 생성되었다.

다음은 minAvailable=3인 PDB 예시이다. .spec.selector는 위 Deployment에서 정의한 Pod Label과 동일한 것을 볼 수 있다. Namespace는 따로 지정하지 않았으며, 모두 default를 사용한다.

참고로 아래 Manifest는 K8s v1.21 기준이며 이전 버전을 사용 중이라면, apiVersion을 policy/v1에서 policy/v1beta1으로 수정해야 한다.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: nginx-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      run: nginx

클러스터 관리자에 의해 자발적 중단이 발생하여도, 반드시 3개 이상의 Pod이 사용가능(available)한 상태로 존재해야 한다.

모든 준비가 되었으니, 이번엔 직접 자발적 중단을 일으켜보자. 다름 아니라, 아래 명령어로 Worker 하나를 Drain시키는 일이다. 

kubectl drain worker-2

worker-2를 Cordon 처리하고, 기존 Pod들을 Eviction하지만, 특정 Pod(ex: nginx-deploy-xxx-yyy)들은 PDB에 의해 보호되는 것을 알 수 있다. 문제가 되는 Pod들은 다른 Node에 새로 생성되어 Running 상태가 될 때까지 eviction 되지 않는다. 시간이 지나 다른 Worker에 Pod이 생성되어, PDB의 minAvailable=3 조건을 만족시키면 PDB에 의해 보호받던 Pod들을 삭제할 수 있으므로 Drain 작업이 완료된다.

 

적용할만한 Application

다음은 K8s에서 PodDisruptionBudget 적용을 권장하는 Application 타입이다.

  • Stateless Frontend
    • 요구사항: 서비스 가용량이 10% 이상 감소해선 안됨
      • 해결책: PDB의 minAvailable을 90%로 설정
  • Single Instance Stateful Application
    • 요구사항: Application Owner와 협의없이 Pod을 삭제하지 말 것
      • 해결책: 우선 PDB의 maxUnavailable을 0으로 설정; 클러스터 관리자가 요청할 경우 Pod Eviction 가능하도록 해당값을 1로 변경. Downtime이 발생하므로 이에 대비해야 한다.
  • Multi Instance Stateful Application(ex: Zookeeper, Etcd, Consul)
    • 요구사항: Replica 갯수가 과반수(Quorum) 미만으로 감소하면 안됨(write fail 발생)
      • 해결책: PDB의 maxUnavailable을 1로 설정하거나, minAvailable을 quorom 크기값으로 정의

 

결론

만약 Application Owner와 Cluster Operator가 구분되는 환경이라면, 운영중인 서비스의 고가용성을 보완하기 위해 PDB를 적용하는 것이 바람직할 것이다. 미리 예고되지 않은 클러스터 업그레이드나 변경 작업 등으로부터 Application을 보호할 수 있기 때문이다. 또한 그 사용 방법이 매우 직관적이고 단순하기 때문에 어렵지 않게 적용할 수 있다.

 

참고

 

'Kubernetes' 카테고리의 다른 글

Kubernetes v1.24 릴리즈  (0) 2022.05.06
Kubernetes: The Documentary  (0) 2022.02.04
ETCD Backup&Restore  (0) 2021.05.29
Open Policy Agent  (0) 2021.05.05
ETCD Encryption  (0) 2021.04.21