인프콘에서 karpenter 를 듣고 정리했던 내가..
카펜터를 설치하는 날이 오고야 말았다.
사실 해결하는데에 급급해서 많은 기록을 해두지는 못했지만 중요하게 여겨야 할 부분들을 적어보도록 하겠다.
일단 Karpenter란... 노드 오토스케일링을 알아서 척척 하는 아주 착한 도구이다.
비용 최적화도 도와주고,,,
장점에는 무엇이 있느냐 => 노드 그룹 불필요, 바로 노드 생성, 비용 최적화
장점들이 그득그득 하다. (( 이정도면 CA를 왜 쓰는지 모르겠는 (알아봐야겠다. )
정말 중요한 부분 : 버전
나는 하필이면 클러스터 가장 최신버전 1.29 에 karpenter 호환 버전을 찾아야만 했다.
사실 이걸 간과하고, 그냥 둘다 최신하면 되겠지~~ 싶어서
0.34.1 ( 현재 최신버전) 으로 설치했는데, 안되었다. ㅋㅋ
집단지성의 힘을 빌려 해결하고자 했다.
착한 클둥이 한분이 알려주신 버전 이슈 유레카...
공식 docs 를 살펴보면,
https://karpenter.sh/docs/upgrading/compatibility/
안내가 이렇게 되어있다.
(아니 이럴거면 34.1 은 왜.. )
특히 무슨무슨 버전부터는 기존에 많은 리소스들이 말하는 provisioner 와 nodetemplate 을 쓰지 않는다.
유의해야한다...
기억을 더듬어 해보자면, ( 순서가 조금 이상할 수도 있어요 )
0. 환경변수 설정
이건 docs 대로 해도된다.
어떤 리소스에서는 kube-system 에다가 하는곳도 있고 (docs대로)
혹은 karpenter 네임스페이스에다가 하던곳도 있던데
나는 한번 karpenter에 했다가 망했기 때문에
재시도에는 시키는 대로 kube-system 에 해봤다.
export KARPENTER_NAMESPACE=kube-system
export KARPENTER_VERSION=v0.34.0
export K8S_VERSION=1.29
export AWS_PARTITION="aws" # if you are not using standard partitions, you may need to configure to aws-cn / aws-us-gov
export CLUSTER_NAME="클러스터이름"
export AWS_DEFAULT_REGION="ap-northeast-2"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export TEMPOUT=$(mktemp)
확인
echo $KARPENTER_NAMESPACE $KARPENTER_VERSION $K8S_VERSION $CLUSTER_NAME $AWS_DEFAULT_REGION $AWS_ACCOUNT_ID $TEMPOUT
1. Role 생성후 연결 ( 중요 !!)
Role 은 크게 두개를 생성해야 한다.
1) NodeRole
2) Controller Role
근데 이거 일일이 추가하기는 어려우니까, 공식문서에서 알려주는 cloudformation 을 이용해 생성하면 편하다.
export KARPENTER_VERSION=v0.34.1
curl https://raw.githubusercontent.com/aws/karpenter-provider-aws/"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > cloudformation.yaml
아래는 cloudformation.yaml 파일이다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowScopedEC2InstanceAccessActions",
"Effect": "Allow",
"Resource": [
"arn:aws:ec2:ap-northeast-2::image/*",
"arn:aws:ec2:ap-northeast-2::snapshot/*",
"arn:aws:ec2:ap-northeast-2:*:security-group/*",
"arn:aws:ec2:ap-northeast-2:*:subnet/*",
"*"
],
"Action": [
"ec2:RunInstances",
"ec2:CreateFleet",
"sqs:GetQueueUrl",
"sqs:ListQueues",
"sqs:DeleteMessage",
"sqs:SendMessage",
"sqs:TagQueue"
]
},
{
"Sid": "AllowScopedEC2LaunchTemplateAccessActions",
"Effect": "Allow",
"Resource": "arn:aws:ec2:ap-northeast-2:*:launch-template/*",
"Action": [
"ec2:RunInstances",
"ec2:CreateFleet"
],
"Condition": {
"StringEquals": {
"aws:ResourceTag/kubernetes.io/cluster/ub-cwave-cluster": "owned"
},
"StringLike": {
"aws:ResourceTag/karpenter.sh/nodepool": "*"
}
}
},
{
"Sid": "AllowScopedEC2InstanceActionsWithTags",
"Effect": "Allow",
"Resource": [
"arn:aws:ec2:ap-northeast-2:*:fleet/*",
"arn:aws:ec2:ap-northeast-2:*:instance/*",
"arn:aws:ec2:ap-northeast-2:*:volume/*",
"arn:aws:ec2:ap-northeast-2:*:network-interface/*",
"arn:aws:ec2:ap-northeast-2:*:launch-template/*",
"arn:aws:ec2:ap-northeast-2:*:spot-instances-request/*"
],
"Action": [
"ec2:RunInstances",
"ec2:CreateFleet",
"ec2:CreateLaunchTemplate"
],
"Condition": {
"StringEquals": {
"aws:RequestTag/kubernetes.io/cluster/ub-cwave-cluster": "owned"
},
"StringLike": {
"aws:RequestTag/karpenter.sh/nodepool": "*"
}
}
},
{
"Sid": "AllowScopedResourceCreationTagging",
"Effect": "Allow",
"Resource": [
"arn:aws:ec2:ap-northeast-2:*:fleet/*",
"arn:aws:ec2:ap-northeast-2:*:instance/*",
"arn:aws:ec2:ap-northeast-2:*:volume/*",
"arn:aws:ec2:ap-northeast-2:*:network-interface/*",
"arn:aws:ec2:ap-northeast-2:*:launch-template/*",
"arn:aws:ec2:ap-northeast-2:*:spot-instances-request/*"
],
"Action": "ec2:CreateTags",
"Condition": {
"StringEquals": {
"aws:RequestTag/kubernetes.io/cluster/ub-cwave-cluster": "owned",
"ec2:CreateAction": [
"RunInstances",
"CreateFleet",
"CreateLaunchTemplate"
]
},
"StringLike": {
"aws:RequestTag/karpenter.sh/nodepool": "*"
}
}
},
{
"Sid": "AllowScopedResourceTagging",
"Effect": "Allow",
"Resource": "arn:aws:ec2:ap-northeast-2:*:instance/*",
"Action": "ec2:CreateTags",
"Condition": {
"StringEquals": {
"aws:ResourceTag/kubernetes.io/cluster/ub-cwave-cluster": "owned"
},
"StringLike": {
"aws:ResourceTag/karpenter.sh/nodepool": "*"
},
"ForAllValues:StringEquals": {
"aws:TagKeys": [
"karpenter.sh/nodeclaim",
"Name"
]
}
}
},
{
"Sid": "AllowScopedDeletion",
"Effect": "Allow",
"Resource": [
"arn:aws:ec2:ap-northeast-2:*:instance/*",
"arn:aws:ec2:ap-northeast-2:*:launch-template/*"
],
"Action": [
"ec2:TerminateInstances",
"ec2:DeleteLaunchTemplate"
],
"Condition": {
"StringEquals": {
"aws:ResourceTag/kubernetes.io/cluster/ub-cwave-cluster": "owned"
},
"StringLike": {
"aws:ResourceTag/karpenter.sh/nodepool": "*"
}
}
},
{
"Sid": "AllowRegionalReadActions",
"Effect": "Allow",
"Resource": "*",
"Action": [
"ec2:DescribeAvailabilityZones",
"ec2:DescribeImages",
"ec2:DescribeInstances",
"ec2:DescribeInstanceTypeOfferings",
"ec2:DescribeInstanceTypes",
"ec2:DescribeLaunchTemplates",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSpotPriceHistory",
"ec2:DescribeSubnets"
],
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-northeast-2"
}
}
},
{
"Sid": "AllowSSMReadActions",
"Effect": "Allow",
"Resource": "arn:aws:ssm:ap-northeast-2::parameter/aws/service/*",
"Action": "ssm:GetParameter"
},
{
"Sid": "AllowPricingReadActions",
"Effect": "Allow",
"Resource": "*",
"Action": "pricing:GetProducts"
},
{
"Sid": "AllowInterruptionQueueActions",
"Effect": "Allow",
"Resource": "arn:aws:sqs:ap-northeast-2:564131458637:ub-cwave-cluster",
"Action": [
"sqs:DeleteMessage",
"sqs:GetQueueUrl",
"sqs:GetQueueAttributes",
"sqs:ReceiveMessage",
"sqs:*"
]
},
{
"Sid": "AllowPassingInstanceRole",
"Effect": "Allow",
"Resource": "arn:aws:iam::564131458637:role/KarpenterNodeRole-ub-cwave-cluster",
"Action": "iam:PassRole",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ec2.amazonaws.com"
}
}
},
{
"Sid": "AllowScopedInstanceProfileCreationActions",
"Effect": "Allow",
"Resource": "*",
"Action": [
"iam:CreateInstanceProfile"
],
"Condition": {
"StringEquals": {
"aws:RequestTag/kubernetes.io/cluster/ub-cwave-cluster": "owned",
"aws:RequestTag/topology.kubernetes.io/region": "ap-northeast-2"
},
"StringLike": {
"aws:RequestTag/karpenter.k8s.aws/ec2nodeclass": "*"
}
}
},
{
"Sid": "AllowScopedInstanceProfileTagActions",
"Effect": "Allow",
"Resource": "*",
"Action": [
"iam:TagInstanceProfile"
],
"Condition": {
"StringEquals": {
"aws:ResourceTag/kubernetes.io/cluster/ub-cwave-cluster": "owned",
"aws:ResourceTag/topology.kubernetes.io/region": "ap-northeast-2",
"aws:RequestTag/kubernetes.io/cluster/ub-cwave-cluster": "owned",
"aws:RequestTag/topology.kubernetes.io/region": "ap-northeast-2"
},
"StringLike": {
"aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass": "*",
"aws:RequestTag/karpenter.k8s.aws/ec2nodeclass": "*"
}
}
},
{
"Sid": "AllowScopedInstanceProfileActions",
"Effect": "Allow",
"Resource": "*",
"Action": [
"iam:AddRoleToInstanceProfile",
"iam:RemoveRoleFromInstanceProfile",
"iam:DeleteInstanceProfile"
],
"Condition": {
"StringEquals": {
"aws:ResourceTag/kubernetes.io/cluster/ub-cwave-cluster": "owned",
"aws:ResourceTag/topology.kubernetes.io/region": "ap-northeast-2"
},
"StringLike": {
"aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass": "*"
}
}
},
{
"Sid": "AllowInstanceProfileReadActions",
"Effect": "Allow",
"Resource": "*",
"Action": "iam:GetInstanceProfile"
},
{
"Sid": "AllowAPIServerEndpointDiscovery",
"Effect": "Allow",
"Resource": "arn:aws:eks:ap-northeast-2:564131458637:cluster/ub-cwave-cluster",
"Action": "eks:DescribeCluster"
}
]
}
근데 좀 당황스럽다. 티스토리는 yaml 형식이 없나..? (모름)
이건 아마 Controller Role 에 쓰이는0 Policy 일것이다.
{
"Statement": [
{
"Action": [
"ssm:GetParameter",
"ec2:DescribeImages",
"ec2:RunInstances",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeLaunchTemplates",
"ec2:DescribeInstances",
"ec2:DescribeInstanceTypes",
"ec2:DescribeInstanceTypeOfferings",
"ec2:DescribeAvailabilityZones",
"ec2:DeleteLaunchTemplate",
"ec2:CreateTags",
"ec2:CreateLaunchTemplate",
"ec2:CreateFleet",
"ec2:DescribeSpotPriceHistory",
"pricing:GetProducts"
],
"Effect": "Allow",
"Resource": "*",
"Sid": "Karpenter"
},
{
"Action": "ec2:TerminateInstances",
"Condition": {
"StringLike": {
"ec2:ResourceTag/Name": "*karpenter*"
}
},
"Effect": "Allow",
"Resource": "*",
"Sid": "ConditionalEC2Termination"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::564131458637:role/KarpenterNodeRole-ub-cwave-cluster",
"Sid": "PassNodeIAMRole"
},
{
"Effect": "Allow",
"Action": "eks:DescribeCluster",
"Resource": "arn:aws:eks:ap-northeast-2:564131458637:cluster/ub-cwave-cluster",
"Sid": "EKSClusterEndpointLookup"
},
{
"Effect": "Allow",
"Action": [
"sqs:DeleteMessage",
"sqs:GetQueueUrl",
"sqs:GetQueueAttributes",
"sqs:ReceiveMessage",
"sqs:ListQueues",
"sqs:ListQueueTags",
"sqs:SendMessage",
"sqs:*"
],
"Resource": [
"arn:aws:sqs:ap-northeast-2:564131458637:haesong-ub-cwave-cluster",
"*"
]
}
],
"Version": "2012-10-17"
}
2. 카펜터가 생성할 노드의 서브넷, 보안그룹의 태그를 지정해줘야 한다.
안그럼 아래와 같은 오류 발생할 것이다 ( 설치 이후에 동작시킬때 직면한..)
{"level":"ERROR","time":"2024-02-25T05:40:00.150Z","logger":"controller","message":"Reconciler error","commit":"17d6c05","controller":"nodeclass","controllerGroup":"karpenter.k8s.aws","controllerKind":"EC2NodeClass",
"EC2NodeClass":{"name":"default"},"namespace":"","name":"default",
"reconcileID":"0e96b8ac-331c-48c3-8778-9ed34dd656b5",
"error":"no subnets exist given constraints [{map[karpenter.sh/discovery:ub-cwave-cluster] }]"}
태그는 어떻게 지정하느냐
- Key : karpenter.sh/discovery
- Value : 현재 사용중인 자신의 EKS 클러스터 이름을 찾아 자동 입력됨
요기 블로그에서는 AWS CLI 명령어로 한번에 태그추가를 해줬기에 나도 그대로 해봤다.
서브넷에 먼저 추가
for NODEGROUP in $(aws eks list-nodegroups --cluster-name ${CLUSTER_NAME} \
--query 'nodegroups' --output text); do aws ec2 create-tags \
--tags "Key=karpenter.sh/discovery,Value=${CLUSTER_NAME}" \
--resources $(aws eks describe-nodegroup --cluster-name ${CLUSTER_NAME} \
--nodegroup-name $NODEGROUP --query 'nodegroup.subnets' --output text )
done
보안그룹에 추가
$ NODEGROUP=$(aws eks list-nodegroups --cluster-name ${CLUSTER_NAME} \
--query 'nodegroups[0]' --output text)
$ LAUNCH_TEMPLATE=$(aws eks describe-nodegroup --cluster-name ${CLUSTER_NAME} \
--nodegroup-name ${NODEGROUP} --query 'nodegroup.launchTemplate.{id:id,version:version}' \
--output text | tr -s "\t" ",")
# If your EKS setup is configured to use only Cluster security group, then please execute -
$ SECURITY_GROUPS=$(aws eks describe-cluster \
--name ${CLUSTER_NAME} --query "cluster.resourcesVpcConfig.clusterSecurityGroupId" --output text)
# If your setup uses the security groups in the Launch template of a managed node group, then :
$ SECURITY_GROUPS=$(aws ec2 describe-launch-template-versions \
--launch-template-id ${LAUNCH_TEMPLATE%,*} --versions ${LAUNCH_TEMPLATE#*,} \
--query 'LaunchTemplateVersions[0].LaunchTemplateData.[NetworkInterfaces[0].Groups||SecurityGroupIds]' \
--output text)
$ aws ec2 create-tags \
--tags "Key=karpenter.sh/discovery,Value=${CLUSTER_NAME}" \
--resources ${SECURITY_GROUPS}
근데 나는 내가 원하는 것에는 안되어있어서 콘솔로 수작업을..
3. 이제 카펜터를 적용해보자.
1) 우여곡절
공식문서에서는 아래와 같은 명령어를 추천한다.
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/"${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}"
이거 즐대안된다.
너무 킹받았음
하다보면 아무리 해도 파드가 CrashBackOff 를 뿜어낼것이다..
이유는 크게 권한문제, sts,SQS 문제 그외에도 뭔가가 있었던 것 같은데
어쨌든 첫번째로 권한 문제를 어떻게 해결했냐!
- 권한,sts 문제 --- > 컨트롤러 Role 신뢰관계 !!! 참고
( 소소한 썰을 풀자면,, 권한 문제 해결해야 했는데 aws 측에서 갑자기 IAM 자체를 수리(?) 하는 상황이 발생해서 올 스탑이 되었었습니다.. 하하)
난 여태 Role 수정하면 그냥 Policy 추가하면되는 줄 알았는데,
신뢰 정책을 편집해야할 일이 은근 있다.
일단 oidc 가 정확한지 1차 확인해야하고,
2차로 노란색 표시한부분을 한번 더 확인해야한다.
네임스페이스 : karpenter
형식이어야 한다. 난 특히나 karpenter 네임스페이스가 아니라 kube-system에서 했기 때문에 이전에 했던 기록이 안바뀌고 있었다.
SQS 문제
이거는 꽤나 여러개를 시도해봐서 기억은 잘 안나지만,,
일단 sqs 를 만들어줬따.
그런담에.. 이미 컨트롤러 role policy 에서 sqs 이름을 추가(수정)해줬다.
4. 진짜로 카펜터 적용하는 방법
이 블로그가 날 살렸다.
helm template karpenter oci://public.ecr.aws/karpenter/karpenter \
--version v0.34.0 \
--namespace kube-system \
--set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile- \
--set settings.clusterName=ub-cwave-cluster \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:aws:iam::564131458637:role/KarpenterControllerRole-ub-cwave-cluster" \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--set controller.resources.limits.cpu=1 \
--set controller.resources.limits.memory=1Gi \
--set replicas=2 > karpenter.yaml
참 좋은 사람이시다.
여기서 필요한 요소들만 가지고 버전과 네임스페이스만 바꿔줬다.
karpenter.yaml 을 수정해서
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: karpenter.sh/provisioner-name
operator: DoesNotExist
+ - matchExpressions:
+ - key: eks.amazonaws.com/nodegroup
+ operator: In
+ values:
+ - YOUR_NODE_GROUP_NAME # 노드그룹 이름은 현재 사용중인 노드 그룹으로 수정하기
요거 추가해주고
nodepool 을 만들어준다. 원하는 사양중에 카펜터가 적절한 사양의 노드를 만들어주도록!
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["2"]
nodeClassRef:
name: default
limits:
cpu: 1000
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720h # 30 * 24h = 720h
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2 # Amazon Linux 2
role: KarpenterNodeRole-ub-cwave-cluster # replace with your cluster name
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: ub-cwave-cluster # replace with your cluster name
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: ub-cwave-cluster # replace with your cluster name
k apply -f nodepool.yaml
k apply -f karpenter.yaml 만 하면된다.
5. 카펜터가 잘 돌아가는지 확인하는법
이번에도 그 블로그인데..(물론 docs 에도 똑같은 명령어 있음 - 근데 신뢰잃음 말이안되긴하는데 말돼)
kubectl logs -f \
-n karpenter \
-c controller \
-l app.kubernetes.io/name=karpenter
카펜터가 동작하려면, pending 상태의 파드들이 존재해야한다.
알아서 노드 축소해보시길..
kubectl get node \
-L beta.kubernetes.io/instance-type
새 노드 확인 - 편안
모든 과정은 locust 로 요청을 보내고 있었기에 HPA 와 동시 동작했으며,
때문에 pending 파드가 증가했던겁니다.
또한,, 글은 하루만에 뚝딱 썼지만 수없이 많은
k get po ~~ -n kube-system
k describe po ~~ -n kube-system
k logs ~~ -n kube-system
의 과정이 있었다는...
눈물겹습니다. .
여러분도 홧팅
참고했던 리소스 ( 나의 한줄기 빛 )
가장 도움이 된 블로그 1위: https://younsl.github.io/blog/k8s/karpenter/
https://younsl.github.io/blog/k8s/karpenter/
https://flavono123.oopy.io/posts/from-karpenter-migration-to-contribute-to-that
https://karpenter.sh/docs/troubleshooting/
https://linuxdatahub.com/accessdenied-not-authorized-to-perform-stsassumerolewithwebidentity/
https://medium.com/dtevangelist/k8s-kubernetes의-hpa를-활용한-오토스케일링-auto-scaling-2fc6aca61c26
https://github.com/aws/karpenter-provider-aws/tree/main
https://linuxdatahub.com/accessdenied-not-authorized-to-perform-stsassumerolewithwebidentity/
https://techblog.gccompany.co.kr/karpenter-7170ae9fb677
노드 풀 설정
https://www.smileshark.kr/post/amazon-ec2-vs-amazon-rds-how-to-choose-right-hosted-database
'☁️2024 > Cloud' 카테고리의 다른 글
AWS ECS vs EKS 차이점 알기 ( 그리고 Fargate vs EC2) (2) | 2024.04.17 |
---|---|
우여곡절2 Kubecost 설치기... (0) | 2024.02.25 |
AWS Community Day HandsOn - DevOps Track (0) | 2024.02.06 |
CI/CD 작업을 해보자 with GCP (0) | 2024.02.06 |
CloudClub 활동 회고 및 정리 (2,3,4기를 마치며..) (1) | 2024.02.05 |