카카오스타일 SRE팀이 EKS 클러스터를 이전하며 기존 Cluster Autoscaler 를 AWS Karpenter 로 교체한 기록이다. ASG 의존으로 느리던 노드 확장이 1분 반 수준으로 줄었고, Provisioner CRD 하나로 인스턴스 타입과 스토리지, IAM 을 관리하게 됐다. 도입하며 밟은 CoreDNS, Security Group 함정도 같이 적는다.
핵심 포인트- Cluster Autoscaler 는 ASG 의 Desired Capacity 를 고쳐 노드를 늘리는 구조라 AWS 리소스 의존도가 높고 느리다. EBS 를 gp2 에서 gp3 로 바꾸느라 노드 10여 대를 재배포할 때 한 대씩 rolling 되는 걸 1시간 지켜만 봐야 했다.
- Karpenter 는 unscheduled Pod 를 직접 관찰해 노드를 띄우고, kube-scheduler 를 대신해 Pod binding 까지 수행한다.
- Provisioner CRD 에 인스턴스 타입 목록, zone, capacity-type 을 정의하면 ASG 와 Launch Template 관리가 사라진다.
- ttlSecondsAfterEmpty 로 빈 노드를 빠르게 정리하고(카카오스타일은 30초), ttlSecondsUntilExpired 를 1209600 으로 둬 14일마다 노드를 롤링해 보안 패치를 반영한다.
- 실측 결과 Pod 가 pending 된 뒤 55초 만에 NodeReady, 78초 안에 정상 구동까지 갔다.
상세 정리- 배경: 주요 마이크로서비스가 EKS 위에 있고 사용자 이용 시간대 패턴이 뚜렷해 하루에도 몇 번씩 노드가 늘고 준다.
- CA 동작 순서: HPA 로 Pod 를 늘리다 배정 실패해 pending, CA 가 ASG Desired Capacity 수정, ASG 가 노드 추가, kube-scheduler 가 Pod 할당. 단계마다 AWS 리소스를 거친다.
- CA 의 운영 부담: 노드에 custom userdata 를 넣으려면 Launch Template 을 따로 관리해야 하고, 워크로드별 인스턴스 요구가 다르면 관리형 Node Group 과 ASG 를 여러 벌 운영해야 한다.
- Karpenter 동작 순서: pending Pod 관찰, 노드 직접 배포, Ready 되면 binding. 모든 워커 노드의 lifecycle 을 Karpenter 가 쥔다.
- 인스턴스 타입 유연성: Launch Template 은 타입을 하나만 지정할 수 있지만 Provisioner requirements 에는 m6i, c6i 계열 여러 타입을 나열할 수 있다. 리전 내 가용성 걱정이 줄고 bin packing 문제도 개선된다.
- 설치: Terraform 의 helm_release 로 karpenter v0.16.0 차트를 EKS 생성 시 함께 배포한다. replicas 2, IRSA role arn, clusterName, clusterEndpoint, defaultInstanceProfile 을 set 으로 넘긴다.
- Pod 구성: Controller 는 Pod 상태를 감시하고 노드를 증감시키며 Node selector 불일치로 할당 불가일 때 에러 로그가 여기 남는다. Webhook 은 Provisioner CRD 유효성 검사와 기본값 지정을 맡는다.
- Provisioner 예시: taints 로 networkNode=true:NoSchedule, requirements 로 c5n.4xlarge, ap-northeast-2a/2c, on-demand 를 지정하고 providerRef 로 템플릿을 연결한 뒤 ttlSecondsAfterEmpty 30.
- AWSNodeTemplate 예시: subnetSelector, securityGroupSelector, instanceProfile, blockDeviceMappings(gp3 20Gi, iops 3000, throughput 125), userData 로 ipvsadm 설치와 ip_vs 모듈 로드. Provisioner 와는 providerRef 로 묶인다.
- 워크로드 쪽은 nodeSelector 와 tolerations 만 맞추면 된다. 노드가 뜰 때 controller 로그에 "Computed 1 new node(s) will fit 1 pod(s)", "Launching node with 1 pods ... from types c5n.2xlarge" 가 찍힌다.
- 함정 1: 클러스터 생성 시 만들어지는 default 노드에 CoreDNS Pod 가 올라간다. 인스턴스 타입을 너무 작게 잡으면 클러스터 DNS 처리가 흔들린다.
- 함정 2: securityGroupSelector 는 태그가 일치하는 SG 를 전부 가져와 노드에 붙인다. 같은 태그를 가진 SG 가 2개 이상이면 AWS Load Balancer controller 가 SG 하나만 지원해 Ingress 가 생성되지 않는다. aws:eks:cluster-name 태그로 지정해 회피했다.
- 함정 3: Provisioner yaml 을 고쳐 다시 apply 해도 이전 설정으로 뜬 노드는 자동 교체되지 않는다. 즉시 반영하려면 별도로 롤링해야 한다.
- 버전 함정: podAffinity 와 podAntiAffinity 가 v0.9.0 에서야 추가돼 그 전 버전에서는 의도대로 동작하지 않았다. 2021년 11월 GA 후 2022년 9월까지 60여 번 릴리즈될 만큼 변화가 빨라 최신 버전 사용을 권한다.
왜 읽나EKS 노드 확장 속도와 Node Group 운영 부담에 지친 SRE 에게 Karpenter 전환의 실제 설정값과 먼저 밟은 함정을 보여주는 글.