티스토리 뷰

개발/Cloud (AWS)

AWS Node Termination Handler

Jaeyeon Baek 2024. 7. 27. 00:33

AWS에서 spot을 사용해서 컴퓨팅 비용을 절감하는 방식은 너무 유명합니다. 하지만 spot은 언제든 빼앗길 여지가 있는데요. EKS에서 AutoScaler로 카펜터(karpenter)를 사용 중이고 인스턴스 타입으로 spot을 사용 중이라면 NTH(Node Termination Handler) 사용을 고려하셔야 합니다. 마치 애플리케이션이 종료되기 전에 시그널을 받아서 처리(signal handler)하는 것처럼 NTH는 노드가 종료되기 전에 "어떤" 이벤트를 받고 종료 전에 필요한 것들을 처리할 수 있도록 돕습니다.

spot 인스턴스는 종료되기 2분 전에 EC2 Spot Instance interruption notice를 수신하는데요. 관련된 내용은 아래와 같이 cloudtrail에서 이벤트 이름을 BidEvictedEvent 필터 해서 찾을 수 있습니다.

 

아무튼, 이렇게 발행되는 이벤트를 카펜터는 현재(2024-07-26) 기준으로 처리하지 못합니다. 그 말인즉, 카펜터는 영문도 모른 채 노드를 빼앗긴다는 거죠. 그렇게 되면 PodDisruptionBudget 같은 처리를 해줬다고 한들 갑작스러운 노드 종료에 대응하지 못하게 됩니다. ( 아래 이미지에 보이는 도구는 k9s 입니다 )

노드가 종료 경고를 받고 대응하지 않다가 2분후에 NotReady 상태로 빠진다

 

서비스의 가용성을 유지하려면 AWS Node Termination Handler를 설치하고 사용해 주면 좋습니다. 헬름으로 구현된 예제는 인터넷에 많으니 여기서는 테라폼 코드를 사용하겠습니다.

resource "helm_release" "aws_node_termination_handler" {
  name       = "aws-node-termination-handler"
  repository = "oci://public.ecr.aws/aws-ec2/helm"
  chart      = "aws-node-termination-handler"
  version    = "0.24.0"
  namespace  = "kube-system"
  values = [templatefile("./manifests/aws-node-termination-handler/values.tftpl", {
    webhook_url = "https://hooks.slack.com/services/xxxxxx/xxxxxx/xxxxxx"
  })]
  provider = helm.eks_helm
  depends_on = [
    module.eks_addon
  ]
}

 

templatefile에 인자로 넣은 values.tftpl은 아래와 같은 내용이 담겨있습니다. 

daemonsetNodeSelector:
  karpenter.sh/capacity-type: spot
webhookURL: ${webhook_url}

 

여기서는 daemonsetNodeSelector를 통해 karpenter.sh/capacity-type이 spot인 경우로 범위를 지정했습니다. 아키텍처 상황에 따라 on-demand 노드에도 aws-node-termination-handler를 설치해야 하는 경우도 있겠습니다만 저는 karpenter가 실행한 spot에만 적용하면 충분한 상황이었습니다.

아래 보이는 것처럼 aws-node-termination-handler는 두 가지 타입으로 제공됩니다. 기본 설정은 IMDS(Instance Metadata Service) Processor를 따라가게 됩니다. 

IMDS는 DaemonSet 기반이고 Queue는 Deployment 입니다

 

이번 글에서는 IMDS Processor만 다루겠습니다. IMDS 모니터는 각 노드에 DaemonSet 형태로 파드를 실행하고 /spot 또는 /events와 같은 IMDS 경로를 모니터링합니다. 그럼 어떤 옵션으로 IMDS를 사용할 수 있을까요? values.tftpl 쪽에 어떤 설정을 넣을 수 있는지는 여기 페이지를 참고하시면 됩니다. 보통 아래 두 가지 옵션을 많이 사용하는데 기본값이 true이기 때문에 굳이 설정하지 않아도 됩니다. ( 저는 명시적으로 선언해 주는 것을 선호하는 편입니다 )

맨 오른쪽 컬럼이 기본값

 

설정을 마치고 배포하면 이후부터 EC2 Spot Instance interruption notice에 대해 반응합니다. 

SchedulingDisabled 상태가 된다

 

아직 노드가 종료된 상태가 아니므로 노드에 접속해서 aws-node-termination-handler의 로그를 살펴보면 이벤트를 받고 노드를 draining, pod를 evict 하는 과정을 볼 수 있습니다. 2분이라는 짧은 시간 동안 pods이 옮겨져야 하는데요. karpenter를 사용한다면 (경우에 따라서는 어려움도 있겠지만) 정말 불가능한 시간이 아닙니다. 

cordon까지 적용되기 때문에 해당 노드에 pod이 배치되지 않는다

 

얼마나 많은 spot 종료 경고 이벤트가 발생하는지는 EventBridge에 AutoScalingManagedRule 룰을 찾아서 보시면 됩니다. AWS Managed로 기본 생성되어 있는 정책이라 아래와 같이 Interruption과 함께 rebalance recommendation이 함께 포함되어 있습니다. 구분해서 보려면 별도의 룰을 생성하시면 됩니다.

{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance Rebalance Recommendation", "EC2 Spot Instance Interruption Warning"]
}

 

아래 그래프를 통해 어느 빈도로 spot이 이벤트를 받는지 확인할 수 있습니다. 위에서 언급한 것처럼 rebalance recommendation이 포함된 수치기 때문에 실제 종료된 spot 개수보다 더 많이 확인될 겁니다.

하루 기준 데이터

 

rebalance recommendation를 받고 처리하는 것도 가능합니다.

그런데 이 설정을 사용할 때는 몇 가지 주의사항이 있습니다. 우선 첫 번째로 rebalance recommendation 이벤트는 "권고" 항목입니다. 이벤트가 발생한다고 해서 실제로 spot이 종료되지 않을 수 있다는 거죠. 두 번째로 이벤트가 전송되는 시점이 고정이 아닙니다. interruption notice(2분)와 함께 수신될 수도 있습니다. 어쩌면 필요 이상으로 많은 이벤트에 karpenter가 대응하게 될 수도 있는 거죠.

 

# 마무리

karpenter로 spot을 운영할 때 여러 가지 어려움이 있을 텐데 그중에 하나가 갑작스러운 노드 종료일 겁니다. NTH를 통해 조금은 더 가용성 있게 클러스터를 운영하실 수 있기를 바랍니다. 이 글에서는 굳이 언급하지 않았지만 webhookURL을 등록해서 슬랙으로 알림 받는 것도 가능합니다. 슬랙을 알림을 받는다고 해서 뭔가 추가적인 대응을 할 수 있는 건 아니지만 기록용으로 남겨두면 언제 어떤 이벤트가 발생했는데 추적이 용이하니까요( 아 물론 데이터독이나 스플렁크 와탭 등 Observability 도구를 사용하는 것도 아주 좋은 선택지입니다 )

카펜터 공식 문서에 AWS Node Termination Handler (NTH) interactions 부분을 살펴보면 아래와 같은 문구가 나옵니다. ( 첫 문장에 two components는 NTH와 Karpenter 입니다 )

These two components do not share information between each other, meaning if you have drain and terminate functionality enabled on NTH, NTH may remove a node for a spot rebalance recommendation. Karpenter will replace the node to fulfill the pod capacity that was being fulfilled by the old node; however, Karpenter won't be aware of the reason that that node was terminated. This means that Karpenter may launch the same instance type that was just deprovisioned, causing a spot rebalance recommendation to be sent again. This can result in very short-lived instances where NTH continually removes nodes and Karpeneter re-launches the same instance type over and over again.

 

저는 이게 아주 작은 확률의 문제라고 생각합니다. NTH가 노드를 정리하고 해당 노드에 있던 pods를 위해 karpenter가 신규 노드를 올릴텐데요. spot을 빼앗기는 주기가 얼마나 짧을 것인지가 관건일겁니다. spot을 주기적으로 분/시간 단위로 빼앗기는 경우는 없기 때문에 별로 문제가 아니라고 생각합니다. 단, rebalance recommendation 옵션을 활성화하면 문제가 생길 수도 있습니다. 클러스터에 얼마나 많은 노드가 붙어있냐에 따라서 말이죠(이벤트 받는 주기/확률이 높아질 것이므로). 당연한 이야기지만 규모가 큰 서비스일수록 신규 기능을 붙일 때 주의가 필요합니다. 인터넷의 대부분의 예제와 글은 노드를 수백 수천대 운영하는 경우가 아닐테니까요. NTH 사용을 고민하시는 분은 이런 부분을 고려해서 사용하시길 바랍니다. 

댓글
최근에 올라온 글
최근에 달린 댓글
글 보관함
Total
Today
Yesterday