가시다님 EKS 스터디 참여하고 내용을 정리합니다.
EKS 처음 설치한다고 가정하면 어떻게 설치하는것이 Best Practice일까 고민을 많이 한다. 항상 그렇듯이 현재 설정은 무언가 아쉽고 불만이다. 하지만 막상 변경하려고 하면 이런저런 고민을 핑계로 미룬다.
처음할 때 제대로 하는게 당연히 좋다. 내 경험이 비록 제한이 많지만 그래도 실제 경험은 항상 소중하니, 혹시 새롭게 구성하는 사람들에게 작은 도움이 될까봐 정리한다.
애플리케이션 중요도에 따라 3개의 노드풀로 나누어서 운영한다. 예를 들어 Prometheus 파드와 서비스 관련 파드가 하나의 노드에 배포되어 성능에 영향을 미치는 케이스를 예방할 수 있다. 물론, Resources Limits, Requests 설정을 잘하면 예방할 수 있으나 안되어 있는 경우가 있으니 2,3중 조치를 한다.
카펜터의 기본은 언제든지 노드가 교체된다는 것이다. 노드가 교체되어도 서비스에 영향이 없도록 파드에 PDB, karpenter.sh/do-not-disrupt, Pod Anti-Affinity 설정 등이 먼저 되어야 한다.
1분, 10분, 매시 정각 등 일회성 배치 성 파드들이 많다. 해당 파드들은 한번은 실패해도 재실행하면 되어 Spot 노드에 배치한다.
Spot 노드
90%까지 비용을 줄일 수 있다고 하나, 대부분 On-Demand에 비하여 70% 정도만 싸다. On-Demand 노드도 Saving Plan을 적용하면 30%까지 비용 절감할 수 있으니 약 40% 정도 차이난다고 생각하면 된다.
그리고 카펜터는 On-Demand 노드는 배포 시 비용이 가장 저렴한 노드를 배포하지만 Spot 노드는 비용 뿐만이 회수 가능성까지 고려하여 오래 실행되는 노드도 고려 대상이다. 따라서 비용이 저렴하지 않은 C6in, M5n 등 노드를 배포하는 경우가 있다. 그렇다고 많이 사용하는 M7i(Flex)-xlarge 등을 특정 노드 타입을 지정하면 노드를 뺏기는 경우가 굉장히 잦고 심지어 노드를 배포하지 못하는 경우도 있다. 그래서 Spot 노드를 이전처럼 적극적으로 활용하지는 않는다. 개발 환경에 전부 Spot 노드를 사용하였으나 지금은 위와 같이 1회성 배치 작업 노드에만 사용한다.
Node - Taint, Pod - Toleration 설정으로 서로 다른 노드에 스케줄 되지 않도록 한다. Platform 노드는 Taint 설정을 풀어서 Toleration 설정을 하지 않은 Public 헬름 차트로 설치하는 파드들이 디폴트로 스케줄되도록 한다.
노드 CPU 아키텍처는 Arm 사용한다. OS 이미지는 특정한 버전을 지정하지 않고 최신 AWS 이미지를 사용하여 보안 안정성을 높인다.
NodePool 코드
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: low-priority-arm
spec:
template:
metadata:
labels:
priority: low-arm
spec:
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: low-priority
requirements:
- key: kubernetes.io/arch
operator: In
values: ["arm64"]
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot"] # spot or on-demand
- key: "karpenter.k8s.aws/instance-category"
operator: In
values: ["m", "r"]
- key: "karpenter.k8s.aws/instance-cpu"
operator: In
values: ["8"]
- key: "karpenter.k8s.aws/instance-generation"
operator: Gt
values: ["5"]
taints:
- key: low-priority-arm
effect: NoSchedule
value: "true"
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 15m
expireAfter: 168h
---
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: platform-priority-arm
spec:
template:
metadata:
labels:
priority: platform-arm
spec:
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: platform-priority
requirements:
- key: kubernetes.io/arch
operator: In
values: ["arm64"]
- key: "karpenter.sh/capacity-type"
operator: In
values: ["on-demand"] # spot or on-demand
- key: "karpenter.k8s.aws/instance-category"
operator: In
values: ["m", "r"]
- key: "karpenter.k8s.aws/instance-cpu"
operator: In
values: ["4", "8"]
- key: "karpenter.k8s.aws/instance-generation"
operator: Gt
values: ["6"]
taints:
- key: platform-priority-arm
effect: NoSchedule
value: "true"
disruption:
consolidationPolicy: WhenUnderutilized
아직 0.32 version 사용하고 있다. 최신이 1.3인데, 아직은 큰 필요성을 못느껴 업그레이드 고려하지 않는다.
1회성 배치 작업 실행 및 개발 환경에 애플리케이션을 자주 배포하면 불필요한 노드의 교체가 너무 많다. 카펜터 옵션으로 70% ~ 80% 까지면 배포하는 옵션이 있으면 좋겠다. 지금은 거의 95% 이상 Full로 파드를 배치한다.
대시보드 Karpenter Capacity v1-New 사용한다. 노드 교체 시간, 현재 사용량 등을 확인할 수 있어 편리하다.
HPA는 CPU 기준으로 사용하고 사용하였으나 아무래도 실제 부하를 반영하기 어렵고 속도도 늦은 편이다. 커넥션 숫자 등 실제 부하와 관련된 Custom Metric 사용을 권한다.
업무의 성격 상 특정 시간에 이벤트를 하고 해당 시간대 부하가 증가한다. Keda를 사용해서 해당 시간에 파드를 증가하는 Cronjob 오토스케일링을 잘 사용하고 있다. 사전에 개발팀과 협의하여 Keda 설정을 할 수 있고 시간에 따라 자동으로 Scale-Out, Scale-In 되어 매우 편리하다.