쿠버네티스 일반/Network

Karpenter CoreDNS on Spot Instance 장애

Jerry_이정훈 2024. 6. 4. 11:30
728x90

이번 포스트에서는 최근에 발생한 CoreDNS 장애 관련 내용을 공유합니다. 당연한 것들인데 새롭게 배운 것들이 많습니다. Karpenter, CoreDNS 사용하는 분들에게 참고가 되었으면 합니다.

 

장애 현상

일부 파드에서 DNS 조회 실패(dns resolve fail) 현상이 발생하였습니다.

장애 원인

Karpenter 설정에서 노드 그룹을 On-Demand와 Spot 두 가지로 운영하고 있었지만, interruptionQueue 설정이 누락되어 있었습니다. interruptionQueue 설정이 없으면 Spot 노드 종료 시 실행하고 있는 파드가 Graceful Shutdown하게 기존 세션을 종료하고 정상 종료되지 않아 해당 노드에 실행 중인 CoreDNS 파드 등에 문제가 발생합니다.

 

장애 조치

1. Karpenter interruptionQueue 설정

3가지 작업이 필요합니다. 순서는 CloudFormation 이용 SQS 생성 - IAM Role SQS 권한 추가 - Karpenter Helm interruptionQueue 추가입니다.

 

SQS 생성 CloudFormation 코드

----------------------------------------------------------------------------------------------------------------
AWSTemplateFormatVersion: "2010-09-09"
Description: Resources used by https://github.com/aws/karpenter 
Parameters:
  ClusterName:
    Type: String
    Description: "EKS cluster name"
Resources:
  KarpenterInterruptionQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: !Sub "${ClusterName}"
      MessageRetentionPeriod: 300
  KarpenterInterruptionQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref KarpenterInterruptionQueue
      PolicyDocument:
        Id: EC2InterruptionPolicy
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - events.amazonaws.com
                - sqs.amazonaws.com
            Action: sqs:SendMessage
            Resource: !GetAtt KarpenterInterruptionQueue.Arn
  ScheduledChangeRule:
    Type: 'AWS::Events::Rule'
    Properties:
      EventPattern:
        source:
          - aws.health
        detail-type:
          - AWS Health Event
      Targets:
        - Id: KarpenterInterruptionQueueTarget
          Arn: !GetAtt KarpenterInterruptionQueue.Arn
  SpotInterruptionRule:
    Type: 'AWS::Events::Rule'
    Properties:
      EventPattern:
        source:
          - aws.ec2
        detail-type:
          - EC2 Spot Instance Interruption Warning
      Targets:
        - Id: KarpenterInterruptionQueueTarget
          Arn: !GetAtt KarpenterInterruptionQueue.Arn
  RebalanceRule:
    Type: 'AWS::Events::Rule'
    Properties:
      EventPattern:
        source:
          - aws.ec2
        detail-type:
          - EC2 Instance Rebalance Recommendation
      Targets:
        - Id: KarpenterInterruptionQueueTarget
          Arn: !GetAtt KarpenterInterruptionQueue.Arn
  InstanceStateChangeRule:
    Type: 'AWS::Events::Rule'
    Properties:
      EventPattern:
        source:
          - aws.ec2
        detail-type:
          - EC2 Instance State-change Notification
      Targets:
        - Id: KarpenterInterruptionQueueTarget
          Arn: !GetAtt KarpenterInterruptionQueue.Arn
----------------------------------------------------------------------------------------------------------------

 

IAM Role

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "sqs:DeleteMessage",
                "sqs:GetQueueAttributes",
                "sqs:GetQueueUrl",
                "sqs:ReceiveMessage"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:sqs:ap-northeast-2:{ACCOUNT_ID}:<SQS-NAME>"
        }
    ]
}

 

Karpenter Helm 수정

- Values.yaml 추가 내역 (0.33.1 이전 버전)

settings:
  aws:
    interruptionQueueName: my-dev  # Cluster Name 지정

 

Karpenter interruptionQueue 적용 전 로그

karpenter-6d877b79fc-bjq4f controller 2024-05-28T05:27:16.799Z  INFO    controller.termination  cordoned node   {"commit": "xxx-dirty", "node": "ip-10-76-38-71.ap-northeast-2.compute.internal"}
karpenter-6d877b79fc-bjq4f controller 2024-05-28T05:27:24.433Z  INFO    controller.termination  deleted node    {"commit": "xxxx-dirty", "node": "ip-xxxx.ap-northeast-2.compute.internal"}

karpenter-6d877b79fc-bjq4f controller 2024-05-28T05:28:15.073Z  INFO    controller.deprovisioning       deprovisioning via consolidation delete, terminating 1 machines ip-xxxx.ap-northeast-2.compute.internal/m7i-flex.xlarge/spot        {"commit": "d7e22b1-dirty"}
karpenter-6d877b79fc-bjq4f controller 2024-05-28T05:28:15.112Z  INFO    controller.termination  cordoned node   {"commit": "d7e22b1-dirty", "node": "ip-xxxx.ap-northeast-2.compute.internal"}
karpenter-6d877b79fc-bjq4f controller 2024-05-28T05:28:27.657Z  INFO    controller.termination  deleted node    {"commit": "xxxx-dirty", "node": "ip-xxxx.ap-northeast-2.compute.internal"}

 

적용 후

karpenter-dc946fdf9-2q8fn controller {"level":"INFO","time":"2024-05-30T02:56:04.309Z","logger":"controller.interruption","message":"starting controller","commit":"e719109"}
(생략)
karpenter-dc946fdf9-2q8fn controller {"level":"INFO","time":"2024-05-30T03:12:19.013Z","logger":"controller.interruption","message":"initiating delete from interruption message","commit":"e719109","queue":"baas-dev-01","messageKind":"SpotInterruptionKind","nodeclaim":"default-2r9k5","action":"CordonAndDrain","node":"ip-10-62-71-224.ap-northeast-2.compute.internal"}
karpenter-dc946fdf9-2q8fn controller {"level":"INFO","time":"2024-05-30T03:12:19.076Z","logger":"controller.node.termination","message":"tainted node","commit":"e719109","node":"ip-10-62-71-224.ap-northeast-2.compute.internal"}
karpenter-dc946fdf9-2q8fn controller {"level":"INFO","time":"2024-05-30T03:12:20.159Z","logger":"controller.provisioner","message":"found provisionable pod(s)","commit":"e719109","pods":"elastic-system/es6-es-es6-2, eventstore/eventstore-4","duration":"89.178932ms"}

 

로그를 확인하면 기존과 다르게 controller.interruption 이라는 새로운 컨트롤러를 확인할 수 있습니다. 카펜터 interruption 컨트롤러가 AWS SQS에서 Spot 노드 종료 이벤트를 수신하면 노드에 실행 중인 파드를 Graceful 하게 종료하면서 다른 노드로 파드를 이전합니다.

 

참고로 interruption 컨트롤러는 kubectl drain {NODE_NAME} 명령어를 실행한 것과 동일한 과정을 실행합니다. 먼저 Cordon 명령어로 해당 노드에 새로운 파드가 실행하지 않게하고 Spot 인스턴스가 종료되는 2분 동안 기존 파드가 정상 종료되도록 파드의 graceful shutdown 설정 등을 존중(?)하고 새로운 노드에 기존 파드가 실행하도록 합니다.
(interruption 컨트롤러 없이도 동일한 프로세스일 것 같은데, 아닌 것은 의아합니다.)

 

2. EKS Add-on CoreDNS 설정을 수정하여 On-Demand 노드에 실행하도록 수정

CoreDNS를 Add-on 방식으로 설치하여도 헬름 등으로 설치한 것과 동일하게 On-Demand 노드에 실행하도록 설정 가능합니다. 그리고 최근에 Add-on으로 CoreDNS 오토스케일링 설정(1)이 가능합니다.

아래 설정을 EKS - Add-on 메뉴의 'Advanced configuration'에 추가합니다.

{
  "autoScaling": {
    "enabled": true,
    "minReplicas": 3,
    "maxReplicas": 10
  },
  "nodeSelector": {
      "nodegroup": "on-demand"
  }
}
  • autoscaling min, max replicas는 임의로 지정할 수 있습니다.
  • nodegroup 이름은 각자 변경하면 됩니다.

 

3. CoreDNS Graceful Shutdown 설정

CoreDNS가 파드 종료(SIGTERM) 시 기존 DNS 요청을 좀 더 안정적으로 완료할 수 있도록 lameduck(2) 설정을 5초에서 30초로 변경했습니다. lameduck 설정은 파드의 graceful shutdown 설정과 동일합니다. 

아래와 같이 ConfigMap의 lameduck 설정을 변경하면 됩니다.

$ kubectl describe cm coredns -n kube-system
Name:         coredns
Namespace:    kube-system
Labels:       eks.amazonaws.com/component=coredns
              k8s-app=kube-dns
Annotations:  <none>

Data
====
Corefile:
----
.:53 {
    errors
    health {
        lameduck 30s
      }
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
      pods insecure
      fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}

 

참조

  1. CoreDNS Autoscaling - https://docs.aws.amazon.com/eks/latest/userguide/coredns-autoscaling.html 
  2. CoreDNS lameduck - https://coredns.io/plugins/health/
반응형