노드 셧다운

노드 셧다운 (Node Shutdown)

쿠버네티스 클러스터에서 노드는 계획된 정상 종료 또는 정전이나 다른 외부 요인 같은 이유로 예상치 못하게 셧다운될 수 있어요. 셧다운 전에 노드가 드레인되지 않으면 노드 셧다운이 워크로드 실패로 이어질 수 있습니다. 노드 셧다운은 정상(graceful) 또는 **비정상(non-graceful)**일 수 있어요.

Debian의 unattended-upgrades 패키지는 기본 구성에서 노드 정상 셧다운과 충돌해요. 서버 셧다운 유예 기간을 커스터마이즈하는 unattended-upgrades 기본 구성을 사용하면, kubelet이 셧다운 이벤트를 제대로 처리하는 데 필요한 잠금을 얻지 못할 수 있습니다.

이는 shutdownGracePeriod 값이 30초보다 클 때 발생해요. 이를 피하려면 /etc/systemd/logind.conf.d/unattended-upgrades-logind-maxdelay.conf/dev/null에 대한 심볼릭 링크로 만들어 unattended-upgrades 구성의 일부를 억제할 수 있습니다.

자세한 내용은 logind.conf 문서를 참고하세요.

출처: 문서

본문

정상 노드 셧다운

kubelet은 노드 시스템 셧다운을 감지하려 시도하고 노드에서 실행 중인 파드를 종료해요.

kubelet은 노드 셧다운 동안 파드가 정상 파드 종료 프로세스를 따르도록 보장해요. 노드 셧다운 동안 kubelet은 새 파드를 허용하지 않습니다(그 파드들이 이미 노드에 바인딩되어 있어도).

정상 노드 셧다운 활성화

Linux에서 정상 노드 셧다운 기능은 1.21에서 기본으로 활성화된 GracefulNodeShutdown 피처 게이트로 제어돼요.

정상 노드 셧다운 기능은 주어진 시간 동안 노드 셧다운을 지연시키는 systemd inhibitor locks를 활용하므로 systemd에 의존합니다.

Windows에서 정상 노드 셧다운 기능은 1.32에서 알파 기능으로 도입된 WindowsGracefulNodeShutdown 피처 게이트로 제어돼요. 쿠버네티스 1.34에서 이 기능은 Beta이고 기본으로 활성화됩니다.

Windows 정상 노드 셧다운 기능은 kubelet이 Windows 서비스로 실행되는 것에 의존하며, 그렇게 되면 주어진 시간 동안 preshutdown 이벤트를 지연시키는 등록된 서비스 제어 핸들러를 가집니다.

Windows 정상 노드 셧다운은 취소할 수 없어요.

kubelet이 Windows 서비스로 실행되지 않으면 Preshutdown 이벤트를 설정·모니터링할 수 없고, 노드는 위에서 언급한 비정상 노드 셧다운 절차를 거쳐야 합니다.

Windows 정상 노드 셧다운 기능이 활성화되었지만 kubelet이 Windows 서비스로 실행되지 않는 경우, kubelet은 실패하는 대신 계속 실행됩니다. 그러나 Windows 서비스로 실행되어야 한다는 오류를 로그로 남깁니다.

정상 노드 셧다운 구성

기본적으로 아래 설명된 두 구성 옵션 shutdownGracePeriodshutdownGracePeriodCriticalPods는 모두 0으로 설정되어 정상 노드 셧다운 기능을 활성화하지 않는다는 점을 유의하세요. 기능을 활성화하려면 두 옵션 모두 적절히 구성하고 0이 아닌 값으로 설정해야 해요.

kubelet이 노드 셧다운을 통보받으면 Node에 NotReady 상태를 설정하고 reason"node is shutting down"으로 설정돼요. kube-scheduler는 이 상태를 존중해 영향을 받는 노드에 어떤 파드도 스케줄하지 않습니다. 다른 서드파티 스케줄러도 같은 논리를 따를 것으로 기대됩니다. 즉 새 파드는 그 노드에 스케줄되지 않으므로 어떤 것도 시작되지 않습니다.

kubelet은 진행 중인 노드 셧다운이 감지되면 PodAdmission 단계에서 파드도 거부해서, node.kubernetes.io/not-ready:NoSchedule에 대한 허용 오염(toleration)이 있는 파드도 그곳에서 시작되지 않습니다.

kubelet이 API를 통해 그 노드에 이 상태를 설정할 때, kubelet은 로컬에서 실행 중인 파드도 종료하기 시작해요.

정상 셧다운 동안 kubelet은 두 단계로 파드를 종료합니다:

  1. 노드에서 실행 중인 일반 파드를 종료한다.
  2. 노드에서 실행 중인 중요 파드를 종료한다.

정상 노드 셧다운 기능은 두 가지 KubeletConfiguration 옵션으로 구성됩니다:

  • shutdownGracePeriod: 노드가 셧다운을 지연해야 하는 총 시간을 지정한다. 이것은 일반 파드와 중요 파드 모두에 대한 파드 종료의 총 유예 기간입니다.

  • shutdownGracePeriodCriticalPods: 노드 셧다운 동안 중요 파드를 종료하는 데 사용되는 시간을 지정한다. 이 값은 shutdownGracePeriod보다 작아야 합니다.

노드 종료가 시스템(또는 관리자가 수동으로)에 의해 취소되는 경우가 있어요. 두 상황 모두에서 노드는 Ready 상태로 돌아갑니다. 그러나 이미 종료 과정을 시작한 파드는 kubelet이 복원하지 않으며 다시 스케줄되어야 합니다.

예를 들어 shutdownGracePeriod=30s, shutdownGracePeriodCriticalPods=10s라면 kubelet은 노드 셧다운을 30초 지연시켜요. 셧다운 동안 처음 20(30-10)초는 일반 파드를 정상 종료하는 데 예약되고, 마지막 10초는 중요 파드 종료에 예약됩니다.

정상 노드 셧다운 동안 파드가 축출되면 shutdown으로 표시돼요. kubectl get pods를 실행하면 축출된 파드의 상태가 Terminated로 표시됩니다. 그리고 kubectl describe pod는 파드가 노드 셧다운 때문에 축출되었음을 나타냅니다:

Reason:         Terminated
Message:        Pod was terminated in response to imminent node shutdown.

Pod 우선순위 기반 정상 노드 셧다운

셧다운 동안 파드의 순서에 더 많은 유연성을 제공하기 위해, 클러스터에서 이 기능을 활성화했다면 정상 노드 셧다운은 Pod의 PriorityClass를 존중해요. 이 기능은 클러스터 관리자가 우선순위 클래스에 기반해 정상 노드 셧다운 동안 파드의 순서를 명시적으로 정의할 수 있게 해줍니다.

위에서 설명한 정상 노드 셧다운 기능은 비중요 파드 다음에 중요 파드를 종료하는 두 단계로 파드를 종료해요. 셧다운 동안 파드 순서를 더 세분화된 방식으로 명시적으로 정의할 추가 유연성이 필요하다면, 파드 우선순위 기반 정상 셧다운을 사용할 수 있습니다.

정상 노드 셧다운이 파드 우선순위를 존중하면 정상 노드 셧다운을 여러 단계로 수행할 수 있고, 각 단계는 특정 우선순위 클래스의 파드를 종료해요. kubelet은 정확한 단계와 단계별 셧다운 시간으로 구성될 수 있습니다.

클러스터에 다음 커스텀 파드 우선순위 클래스가 있다고 가정해요,

Pod priority class name Pod priority class value
custom-class-a 100000
custom-class-b 10000
custom-class-c 1000
regular/unset 0

kubelet 구성 안에서 shutdownGracePeriodByPodPriority 설정은 다음과 같을 수 있어요:

Pod priority class value Shutdown period
100000 10 seconds
10000 180 seconds
1000 120 seconds
0 60 seconds

해당하는 kubelet config YAML 구성은 다음과 같습니다:

shutdownGracePeriodByPodPriority:
  - priority: 100000
    shutdownGracePeriodSeconds: 10
  - priority: 10000
    shutdownGracePeriodSeconds: 180
  - priority: 1000
    shutdownGracePeriodSeconds: 120
  - priority: 0
    shutdownGracePeriodSeconds: 60

위 표는 priority 값이 100000 이상인 파드는 종료할 시간이 10초뿐이고, 값이 10000 이상 100000 미만인 파드는 180초, 값이 1000 이상 10000 미만인 파드는 120초를 얻는다는 것을 뜻해요. 마지막으로 다른 모든 파드는 종료할 시간이 60초입니다.

모든 클래스에 해당하는 값을 지정할 필요는 없어요. 예를 들어 대신 이 설정을 사용할 수 있습니다:

Pod priority class value Shutdown period
100000 300 seconds
1000 120 seconds
0 60 seconds

위 경우 custom-class-b 파드는 custom-class-c와 같은 버킷에 들어가 종료됩니다.

특정 범위에 파드가 없으면 kubelet은 그 우선순위 범위의 파드를 기다리지 않아요. 대신 kubelet은 다음 우선순위 클래스 값 범위로 즉시 건너뜁니다.

이 기능이 활성화되었지만 구성이 제공되지 않으면 순서 지정 작업이 수행되지 않습니다.

이 기능을 사용하려면 GracefulNodeShutdownBasedOnPodPriority 피처 게이트를 활성화하고 kubelet config에서 ShutdownGracePeriodByPodPriority를 파드 우선순위 클래스 값과 각각의 셧다운 기간을 담은 원하는 구성으로 설정해야 해요.

정상 노드 셧다운 동안 Pod 우선순위를 고려하는 기능은 쿠버네티스 v1.23에서 알파 기능으로 도입되었어요. 쿠버네티스 v1.37에서 이 기능은 Beta이고 기본으로 활성화됩니다.

kubelet 서브시스템에서 노드 셧다운을 모니터링하는 메트릭 graceful_shutdown_start_time_secondsgraceful_shutdown_end_time_seconds가 발행됩니다.

비정상 노드 셧다운 처리

노드 셧다운 동작이 kubelet의 Node Shutdown Manager에 감지되지 않을 수 있어요. 명령이 kubelet이 사용하는 inhibitor locks 메커니즘을 트리거하지 않기 때문이거나, ShutdownGracePeriod와 ShutdownGracePeriodCriticalPods가 제대로 구성되지 않은 사용자 오류 때문입니다. 자세한 내용은 위의 정상 노드 셧다운 섹션을 참고하세요.

노드가 셧다운됐지만 kubelet의 Node Shutdown Manager가 감지하지 못하면, StatefulSet의 일부인 파드는 셧다운된 노드에서 terminating 상태로 멈춰 새 실행 노드로 이동할 수 없어요. 이는 셧다운된 노드의 kubelet이 파드를 삭제할 수 없어 StatefulSet이 같은 이름의 새 파드를 만들 수 없기 때문입니다. 파드가 사용하는 볼륨이 있다면 원래 셧다운 노드에서 VolumeAttachments가 삭제되지 않아, 그 파드들이 사용하는 볼륨을 새 실행 노드에 연결할 수 없습니다. 결과적으로 StatefulSet에서 실행되는 애플리케이션은 제대로 기능하지 못해요. 원래 셧다운 노드가 다시 뜨면 kubelet이 파드를 삭제하고 다른 실행 노드에서 새 파드가 만들어집니다. 원래 셧다운 노드가 다시 뜨지 않으면 이 파드들은 셧다운 노드에서 영원히 terminating 상태로 멈춰 있습니다.

위 상황을 완화하기 위해 사용자는 node.kubernetes.io/out-of-service 오염(taint)을 NoExecute 또는 NoSchedule 효과와 함께 수동으로 추가해 노드를 서비스 불능(out-of-service)으로 표시할 수 있어요. 이 taint로 노드가 서비스 불능으로 표시되면, 일치하는 허용 오염(toleration)이 없고 노드에서 종료되는 파드에 대한 볼륨 분리 작업이 즉시 일어나며, 노드의 파드는 강제로 삭제됩니다. 이는 서비스 불능 노드의 파드가 다른 노드에서 빠르게 복구되게 해줍니다.

비정상 셧다운 동안 파드는 두 단계로 종료됩니다:

  1. 일치하는 out-of-service 허용 오염이 없는 파드를 강제 삭제한다.
  2. 그런 파드에 대해 볼륨 분리 작업을 즉시 수행한다.
  • node.kubernetes.io/out-of-service taint를 추가하기 전에 노드가 이미 셧다운 또는 전원 꺼짐 상태(재시작 중간이 아님)인지 확인해야 한다.
  • 사용자가 taint를 처음 추가했으므로 파드가 새 노드로 이동하고 셧다운 노드가 복구되었는지 확인한 뒤, out-of-service taint를 수동으로 제거해야 한다.

시간 초과 시 강제 스토리지 분리

파드 삭제가 6분 동안 성공하지 못하는 어떤 상황에서도, 노드가 그 순간 비정상 상태라면 쿠버네티스는 마운트 해제 중인 볼륨을 강제로 분리해요. 노드에서 계속 실행 중인 강제 분리된 볼륨을 사용하는 워크로드는 CSI 사양을 위반하게 되는데, 이 사양은 ControllerUnpublishVolume이 "볼륨에 대한 모든 NodeUnstageVolumeNodeUnpublishVolume이 호출되고 성공한 후에 호출되어야 한다"고 명시합니다. 이러한 상황에서 해당 노드의 볼륨은 데이터 손상을 겪을 수 있습니다.

강제 스토리지 분리 동작은 선택 사항이며, 사용자는 대신 "비정상 노드 셧다운" 기능을 사용하기로 선택할 수 있어요.

시간 초과 시 강제 스토리지 분리는 kube-controller-manager에서 disable-force-detach-on-timeout 구성 필드를 설정해 비활성화할 수 있어요. 시간 초과 시 강제 분리 기능을 비활성화하면 6분 이상 비정상인 노드에 호스팅된 볼륨의 관련 VolumeAttachment가 삭제되지 않습니다.

이 설정이 적용된 뒤에는 여전히 볼륨에 연결된 비정상 파드가 위에서 언급한 비정상 노드 셧다운 절차를 통해 복구되어야 해요.

  • 비정상 노드 셧다운 절차를 사용할 때는 주의해야 한다.
  • 위에 문서화된 단계에서 벗어나면 데이터 손상이 발생할 수 있다.

더 알아보기 (Learn more)

다음에 대해 더 알아보세요: