이전에는 Node 확장을 위해 Cluster Autoscaler을 사용했습니다. 그러나 Cluster Autoscaler는 AutoScaling Group을 통해 Node를 확장합니다. Pod의 Pending 상태를 확인하고, ASG의 Desired Capacity 값을 늘려 Node를 확장하는데는 많은 시간이 걸립니다. 또한, 워크로드별 인스턴스 요구사항이 다를 경우 여러 ASG를 운영해야 하기 때문에 운영 부담이 늘어납니다.
이러한 Cluster Autoscaler의 문제점을 해결하기 위해 Karpenter가 등장했습니다. Karpenter는 ASG에 의존하지 않고, EC2 Instance를 즉시 실행시켜 신속하게 Node를 확장시킬 수 있습니다. Provisioner을 설정해 워크로드별 인스턴스 요구사항에 대응할 수 있고, Pod들의 리소스를 계산하여 하나의 노드에 재배치 해주기도 합니다.
오늘은 Karpenter을 설치하고 사용하여 부하 증가에 대처해봅시다.
1. 설치
Addon Node Group이 있는 상태로 시작합니다.
자주 쓰이는 변수는 미리 등록해 두겠습니다.
CLUSTER_NAME=$(eksctl get clusters -o json | jq -r '.[0].Name')
AWS_DEFAULT_REGION="ap-northeast-2"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
Karpenter 동작에 필요한 SQS, EventBridge Rule 등을 생성합니다.
KARPENTER_VERSION=$(curl -sL "https://api.github.com/repos/aws/karpenter/releases/latest" | jq -r ".tag_name")
TEMPOUT=$(mktemp)
curl -fsSL https://raw.githubusercontent.com/aws/karpenter/"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > $TEMPOUT \
&& aws cloudformation deploy \
--stack-name "Karpenter-${CLUSTER_NAME}" \
--template-file "${TEMPOUT}" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides "ClusterName=${CLUSTER_NAME}"
Karpenter로 프로비저닝한 Node가 잘 동작할 수 있도록 Karpenter Node Role을 aws-auth configmap에 등록합니다.
eksctl create iamidentitymapping \
--username system:node:{{EC2PrivateDNSName}} \
--cluster "${CLUSTER_NAME}" \
--arn "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}" \
--group system:bootstrappers \
--group system:nodes
Karpenter가 AWS 서비스를 조정할 수 있도록 OIDC를 발급한 후 IAM Role과 연결된 serviceaccount를 생성합니다.
eksctl utils associate-iam-oidc-provider --cluster ${CLUSTER_NAME} --approve
eksctl create iamserviceaccount \
--cluster "${CLUSTER_NAME}" --name karpenter --namespace karpenter \
--role-name "${CLUSTER_NAME}-karpenter" \
--attach-policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}" \
--role-only \
--approve
Helm을 통해 Karpenter을 설치합니다.
KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"
helm upgrade karpenter oci://public.ecr.aws/karpenter/karpenter \
--install --version ${KARPENTER_VERSION} --namespace karpenter --create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
--set settings.aws.clusterName=${CLUSTER_NAME} \
--set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
--set settings.aws.interruptionQueueName=${CLUSTER_NAME} \
--wait
Node가 배치될 Subnet과 사용할 Security Group에 태그(karpenter.sh/discovery: ${CLUSTER_NAME})를 추가해줍니다. Security Group은 Addon Node Group이 생성될 때 만들어진 것을 사용했습니다.
2. Provisioner, AWSNodeTemplate
Provisioner를 작성합니다. Provisioner는 Karpenter에 의해 생성될 노드와 해당 노드에서 실행될 Pod에 대한 제약 조건을 설정합니다. 공식 문서를 보고 자세하게 설정할 수 있습니다.
https://karpenter.sh/preview/concepts/provisioners
Provisioners
Learn about Karpenter Provisioners
karpenter.sh
# provisioner.yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
labels: # Node의 Label
node: app
taints: # Node의 Taint
- key: node
value: app
effect: NoSchedule
limits: # Node 리소스 제한
resources:
cpu: 1000
providerRef: # Node Template 이름 설정
name: default
consolidation: # 노드 통합을 통한 deprovisioning
enabled: true
AWSNodeTemplate을 작성합니다. AWSNodeTemplate은 Node가 생성되는 AWS 환경을 정의한 것입니다. 태그를 통해 Subnet과 Security Group을 설정하겠습니다. 공식 문서를 보고 다른 환경을 더 정의할 수 있습니다.
https://karpenter.sh/preview/concepts/node-templates/
Node Templates
Configure AWS specific settings
karpenter.sh
cat <<EOF > node_template.yaml
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
name: default
spec:
subnetSelector: # Node가 배치될 Subnet이 가지는 태그
karpenter.sh/discovery: ${CLUSTER_NAME}
securityGroupSelector: # Node가 사용할 Security Group이 가지는 태그
karpenter.sh/discovery: ${CLUSTER_NAME}
EOF
Provisioner와 AWSNodeTemplate을 배포합니다. 배포를 해도 Node가 즉시 추가되지는 않습니다.
kubectl apply -f provisioner.yaml
kubectl apply -f node_template.yaml
3. Node 생성과 제거
Deployment를 배포하여 Karpenter에 의해 Node가 생성되는지 보겠습니다.
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
spec:
replicas: 2
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
nodeSelector:
node: app
tolerations:
- key: node
operator: Equal
value: app
effect: NoSchedule
containers:
- name: nginx
image: nginx:1.14.2
resources:
requests:
cpu: 500m
memory: 128Mi
limits:
cpu: 1
memory: 256Mi
ports:
- containerPort: 80
kubectl apply -f deployment.yaml
Karpenter에 의해 Node 하나가 생성된 것을 볼 수 있습니다. 인스턴스 타입을 c5a.large로 설정해줬습니다.
[ec2-user@ip-10-0-11-131 ~]$ kubectl get nodes -l node=app
NAME STATUS ROLES AGE VERSION
ip-10-0-159-243.ap-northeast-2.compute.internal Ready <none> 7m31s v1.27.5-eks-43840fb
Node가 자동으로 제거되는지 보기 위해 deployment를 삭제하겠습니다.
kubectl delete deployment demo
1분도 안 되서 Node가 제거되었습니다. 확실히 Cluster Autoscaler보다 높은 성능을 보입니다.
[ec2-user@ip-10-0-11-131 ~]$ kubectl get nodes -l node=app
No resources found
오늘의 글은 여기까지입니다. 감사합니다!
참고
https://devblog.kakaostyle.com/ko/2022-10-13-1-karpenter-on-eks/
EKS클러스터 Karpenter 적용기
안녕하세요! 카카오스타일 SRE팀 네사입니다. 오늘은 카카오스타일 SRE팀에서 올해 EKS 클러스터 이전을 하며 새롭게 도입 했던 AWS Karpenter 에 대해 공유를 해보려 합니다.
devblog.kakaostyle.com
'AWS' 카테고리의 다른 글
[AWS] RDS의 개념과 기본 실습 (0) | 2023.09.29 |
---|---|
[AWS] EKS - AWS Load Balancer Controller (1) | 2023.09.26 |
[AWS] aws-nuke로 모든 리소스 삭제 (0) | 2023.09.24 |
[AWS] EKS Node Metadata (IMDS) - EC2 (1) | 2023.09.22 |
[AWS] ArgoCD를 통한 애플리케이션 배포 (with CodeCommit) (0) | 2023.09.19 |