[Kubernetes] 무중단 배포

문린이·2024년 3월 11일
0

Pod의 종료

종료 과정

1. 종료 요청

  • 종료 요청 발생: 사용자가 kubectl delete pod [POD_NAME] 명령을 실행하거나, Pod의 수명 주기 정책(Lifecycle Policy)에 의해 Pod가 종료되어야 할 때, kube-apiserver는 종료 요청을 받는다.
  • API 서버 처리: kube-apiserver는 해당 Pod에 대한 Terminating 상태를 설정하고, 이 정보를 etcd에 저장한다.

2. SIGTERM 신호 전송

  • preStop : preStop Hook이 정의된 경우, 컨테이너에 SIGTERM 신호를 보내기 전에 preStop Hook을 실행한다. 이는 HTTP 요청 또는 임의의 쉘 명령 실행을 포함할 수 있으며, 외부 서비스와의 연결을 안전하게 종료하거나, 중요한 데이터를 백업하는 등의 작업에 사용될 수 있다.
  • kubelet 알림: kube-apiserver로부터 종료 요청을 받은 후, 해당 노드의 kubelet은 Pod 종료 요청을 감지한다.
  • SIGTERM 신호 발송: kubelet은 Pod 내의 각 컨테이너에 SIGTERM 신호를 보내 종료 프로세스를 시작한다. 이 신호는 애플리케이션이 종료를 준비하라는 알림이다.

3. Graceful Shutdown 처리

  • 정리 작업 실행: 컨테이너는 SIGTERM 신호를 받은 후, 정해진 terminationGracePeriodSeconds 동안 정리 작업을 수행한다. 이는 데이터베이스 연결 종료, 임시 파일 정리, 중요한 데이터의 디스크 쓰기 등을 포함할 수 있다.
  • Grace Period: terminationGracePeriodSeconds는 Pod 스펙에서 설정할 수 있으며, 기본 값은 30초이다. 이 시간 동안 컨테이너는 정상적으로 종료될 수 있다.

4. SIGKILL 신호 전송

  • Grace Period 만료: terminationGracePeriodSeconds가 만료되고 컨테이너가 여전히 실행 중인 경우, kubelet은 강제 종료를 위해 SIGKILL 신호를 컨테이너에 보냅다.
  • 강제 종료 처리: SIGKILL 신호는 컨테이너를 즉시 종료시킨다. 이 단계는 Graceful Shutdown이 실패했을 때 실행된다.

5. Pod 상태 업데이트 및 정리

  • 종료 상태 반영: 모든 컨테이너가 종료되면, kubelet은 Pod의 상태를 Terminated로 업데이트하고, kube-apiserver에 상태 변경을 보고한다.
  • 리소스 정리: Pod 리소스와 관련된 모든 항목은 Kubernetes 클러스터에서 정리되며, Pod는 시스템에서 완전히 제거된다.

종료 테스트

Service는 같은 파일을 쓰고 Deployment를 변경하면서 테스트를 진행
테스트 환경은 minikube, 사용자의 트래픽은 Locust를 사용

nginx- service.yaml

  apiVersion: v1
  kind: Service
  metadata:
    name: nginx-service
  spec:
    type: LoadBalancer
    selector:
      app: nginx
    ports:
      - protocol: TCP
        port: 8080
        targetPort: 80

nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          resources:
            requests:
              cpu: 200m
              memory: 250Mi
            limits:
              cpu: 1000m
              memory: 500Mi
          ports:
            - containerPort: 80

종료 테스트는 kubectl rollout restart deployment nginx-deployment 로 진행
(StrategyType: RollingUpdate)

설정X, replicas: 1 (기본값)

downtime 발생

설정X, replicas: 2

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2 # 2로 변경

downtime 발생

설정X, replicas: 4

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 4 # 4로 변경

downtime 발생

replicas를 늘려도 downtime이 발생함을 알 수 있다.
이는 롤링 업데이트 전략을 적용하더라도 새로운 pod이 준비되지 않았거나 건강하지 않은 상태에서 트래픽을 받게 되어 downtime이 발생하는 상황이다.

Liveness, Readiness and Startup Probes

Liveness Probe

컨테이너가 정상적으로 작동하고 있는지 확인한다. Liveness Probe가 실패하면, kubelet은 컨테이너를 재시작한다. 이는 컨테이너가 데드락에 빠지거나 응답하지 않는 상태에서 자동으로 복구되도록 돕는다.

Readiness Probe

컨테이너가 트래픽을 받을 준비가 되었는지 확인한다. Readiness Probe가 실패하면, 쿠버네티스는 해당 포드를 서비스의 트래픽 대상에서 제외한다. 이는 컨테이너가 아직 초기화 중이거나, 일시적으로 트래픽을 처리할 수 없는 상태일 때 유용하다.

Startup Probe

컨테이너의 초기화가 완료되었는지 확인한다. Startup Probe는 애플리케이션의 긴 초기화 시간 동안 Liveness Probe의 조기 재시작을 방지한다. 초기화가 완료된 후에는 Liveness Probe가 컨테이너의 상태를 지속적으로 모니터링한다.

테스트

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:alpine
          resources:
            requests:
              cpu: 200m
              memory: 250Mi!
            limits:
              cpu: 1000m
              memory: 500Mi
          ports:
            - containerPort: 80
              name: http # 추가
          # 추가
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 15
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 15
            periodSeconds: 10

downtime 발생

Probe를 설정해도 downtime이 발생함을 알 수 있다.
이는 기존 Pod에 대한 요청이 아직 처리 중인데 Pod이 삭제되면, 연결이 강제로 종료되어 오류가 발생할 수 있다.

preStop

애플리케이션 레벨에서 Graceful Shutdown 로직을 구현할 수 있지만 preStop으로도 비슷한 효과를 낼 수 있다.

앞에서 말한 Pod의 종료 과정에서 preStop hook을 설정하면 컨테이너에 SIGTERM 신호를 보내기 전에 실행되므로 진행 중인 요청을 처리하고 리소스를 정리하는 등의 작업을 수행할 수 있다.

테스트

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:alpine
          resources:
            requests:
              cpu: 200m
              memory: 250Mi
            limits:
              cpu: 1000m
              memory: 500Mi
          ports:
            - containerPort: 80
              name: http
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 15
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 15
            periodSeconds: 10
          # 추가
          lifecycle:
            preStop:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - sleep 20

zero-downtime 성공

preStop hook의 시간이 30초 이상 걸린다면 terminationGracePeriodSeconds의 시간을 조절해 줘야 한다.

AWS EKS

Pod readiness gate

Pod readiness gate 공식문서

AWS Load Balancer Controller를 사용할때, 로드 밸런서의 타깃 그룹 등록 상태를 Pod의 준비 상태로 반영할 수 있도록 해주는 설정이다.

AWS Load Balancer Controller는 타깃 그룹에 Pod가 성공적으로 등록되면 해당 Pod의 readinessGate를 성공 상태로 업데이트한다. 이는 Pod가 외부 로드 밸런서에 의해 트래픽을 받을 준비가 되었다는 것을 나타낸다.

Kubernetes 클러스터와 외부 로드 밸런서 간의 상태를 동기화하여, Pod가 실제로 외부 트래픽을 처리할 준비가 되었는지 보다 정확하게 반영한다.

$ kubectl create namespace readiness
namespace/readiness created

$ kubectl label namespace readiness elbv2.k8s.aws/pod-readiness-gate-inject=enabled
namespace/readiness labeled

$ kubectl describe namespace readiness
Name:         readiness
Labels:       elbv2.k8s.aws/pod-readiness-gate-inject=enabled
Annotations:  <none>
Status:       Active
profile
Software Developer

0개의 댓글