Cloud Architect/Kubernetes

Karpenter 소개 [Part 01]

"Everything about infra" 2025. 7. 25. 17:42

▶ 지난 포스팅

AutoScaling [Part 01] — 하늘을 나는 펭귄

 

AutoScaling [Part 01]

01. EKS (Elastic Kubernetes Service) 설치💁‍♂️ EKS 소개Kubernetes Control plane을 구성할 필요 없이 AWS에서 Kubernetes를 제공하는 관리형 서비스.장점은 아래와 같습니다.리팩토링이 필요없다.EKS환경에서

ssunghwan.tistory.com

AutoScaling [Part 02] — 하늘을 나는 펭귄

 

AutoScaling [Part 02]

▶ 지난 포스팅AutoScaling [Part 01] — 하늘을 나는 펭귄 AutoScaling [Part 01]01. HPA(Horizon Pod Autoscaler)Pod에 대한 Client Connection이 많아지거나 적어지면 리소스 정보를 확인하여 Pod를 증가시키거나 축소시

ssunghwan.tistory.com

 


01. Karpenter (AutoScaling tool)

💁‍♂️Karpenter란?

Cluster AutoScaler(CA)와 유사하게 Kubernetes 클러스터에서 자동 스케일링을 관리하기 위한 도구.

 

CA와 Karpenter 동작방식 비교

Cluster AutoScaler Architecture

동작 방식은 아래와 같다.

  • Cluster AutoScaler는 AWS AutoScaling Group과 같이 사용된다.
  • CA는 Kubernets의 Pod의 요구사항에 따라 노드 수를 조정하며, ASG는 시스템 지표(CPU, Network Traffic)에 따라 인스턴스 수를 조정한다.
  • CA는 AWS 리소스에 대한 정보를 IAM ServiceAccount를 생성하여 접근할 수 있고, IAM 역할에서 신뢰 정책에 옵션을 추가하여 Auto Scaling Group을 관리할 수 있다.

Karpenter Architecture

동작 방식은 아래와 같다.

  • 구성도를 보면 알 수 있듯이, Karpenter가 Pending 상태의 Pod를 감지하여 새로운 worker node를 생성
  • Cluster AutoScaler 기능 + AWS AutoScaling Group 기능을 Karpenter 혼자서 관리한다.

즉, ASG와는 독립적으로 노드를 프로비저닝 하고 관리한다. ASG에 의존하지 않기 때문에 더 세밀하게 제어함.

 

💁‍♂️Karpenter의 장단점

 

장점

1. 신속한 Node AutoScaling

  • CA대비 단순한 구조이며, ASG의 역할까지 Karpenter가 대신함으로써 autoscaling 작업이 훨씬 빠르다.

2. 효율적인 자원관리, 운영 부담 절감

 

CA를 사용할때 Node의 운영 요구사항을 반영하기 위해 Launch template를 따로 관리하고 만약 수정하려면

수정 후 노드를 재생성 해주어야 하는 번거로움이 있었다.

 

또한, 적은 리소스로 부족으로 인해 Scale-out이 진행되어 노드가 증가하게 되면 기존 instance type 으로 생성되어

자원 낭비가 발생할 수 있다.

 

하지만, karpenter는 사용할 instance type을 여러개 지정할 수 있을 뿐더러 현재 자원 상황을 고려하여

type을 선택 후 노드를 생성해준다.

 

1. AWS AutoScaling Group 사용이 불가능하다.

  • ASG를 사용하지 않기 때문에 노드 인스턴스에 대한 최소, 최대 개수를 지정할 수 없으며 Karpenter의 cpu, memory 설정으로 스케일링을 제한한다.

2. 운영환경에 대한 전문성이 필요

  • Karpenter는 아직 소개된지 얼마 되지 않아서 베타버전에 가깝다는 점
  • 국내 레퍼런스가 없어서 Karpenter의 작동방식, 설정을 이해하기에 많은 시간과 노력을 투자해야 한다는 점

1-1. 사전 준비물

  • awscli, eksctl, kubectl은 지난 포스팅 (part 01) 참고 바랍니다.
  • helm repo 설치

helm (v3.8.0 이상 권장) 설치

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

chmod 700 get_helm.sh
./get_helm.sh --version v3.8.0

helm version --short
v3.8.0+gd141386

 

 EKS 설치 이전에 간단한 Architecture 설명

AWS 인프라 내부에서 실제 운용중인 EC2 Instance에 설치하는 것 보다, Kubernetes Node를 관리하고 그 위에

Pod 형태로 구동되는 Karpenter를 관리하기 편하게 관리용도로 Private EC2 Instance를 별도로 생성하자.

EKS Managed Server에서 Karpenter 및 EKS Cluster가 구동된다.

 

 

EKS Cluster 설치  [yaml 형태로 build]

managed group으로 생성하여 AWS에서 node group을 관리할 수 있게 생성

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: prd-eks-cluster
  region: ap-northeast-2
vpc:
  id: 'vpc-02172b18928eebff8'     
  subnets:
    private:
      ap-northeast-2a:
        id: 'subnet-09fd28d637e11b029'
      ap-northeast-2c:
        id: 'subnet-0ee462c61cd9b9395'

managedNodeGroups:
  - name: prd-eks-dataplane1
    labels: { role: workers }
    instanceType: t2.medium
    desiredCapacity: 1
    privateNetworking: true
    subnets:
      - subnet-09fd28d637e11b029
  - name: prd-eks-dataplane2
    labels: { role: workers }
    instanceType: t2.medium
    desiredCapacity: 1
    privateNetworking: true
    subnets:
      - subnet-0ee462c61cd9b9395
    iam:
      withAddonPolicies:
        imageBuilder: true
iam:
  withOIDC: true

 


 

02. Configuration (환경 구성)

 환경변수 설정

  • karpenter, kubernetes에 대한 version 설정
export KARPENTER_NAMESPACE=karpenter
export KARPENTER_VERSION=v0.34.1
export K8S_VERSION=1.29.0
  • Karpenter가 AWS API에 접근하여 읽을수 있도록 AWS 환경변수 설정
export AWS_PARTITION="aws" # if you are not using standard partitions, you may need to configure to aws-cn / aws-us-gov
export AWS_REGION="ap-northeast-2" your aws region
export CLUSTER_NAME="prd-eks-cluster" # your cluster name
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"

OIDC_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} \
    --query "cluster.identity.oidc.issuer" --output text)"
    
CLUSTER_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} \
    --query "cluster.endpoint" --output text)"
  • 출력값 확인.

아래와 같이 echo 명령을 사용하였을때 결과값이 추출되면 잘 적용된것.

echo $KARPENTER_NAMESPACE $KARPENTER_VERSION $K8S_VERSION $CLUSTER_NAME $AWS_REGION $AWS_ACCOUNT_ID
karpenter v0.34.1 1.27.9 prd-eks-cluster ap-northeast-2 339712771495

echo $OIDC_ENDPOINT $CLUSTER_ENDPOINT
https://oidc.eks.ap-northeast-2.amazonaws.com/id/4F92A25FA10F98F239811FC0A7386160 https://4F92A25FA10F98F239811FC0A7386160.gr7.ap-northeast-2.eks.amazonaws.com

 


2-1. Node IAM Role 생성

EKS에서 동작하는 Karpenter가 추후에 Node들을 AutoScaling 하기 위해 AWS에서 IAM Role, Policy 부여

 

1. Role의 신뢰관계 생성

echo '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}' > node-trust-policy.json

 

2. Karpenter로 생성한 노드에서 사용할 IAM Role을 생성

aws iam create-role \
  --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
  --assume-role-policy-document file://node-trust-policy.json    # 전에 만든 신뢰관계 적용

 

3. EKS Cluster의 Node가 동작할때 필요한 필수적 권한 추가

aws iam attach-role-policy \
  --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
  --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy

aws iam attach-role-policy \
  --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
  --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy

aws iam attach-role-policy \
  --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
  --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

aws iam attach-role-policy \
  --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
필수 권한 4가지 설명
AmazonEKSWorkerNodePolicy : EKS 워커노드에 대한 기본 정책
  • EKS 노드가 EKS API와 통신하고, ECR에서 이미지를 가져오는 등의 작업을 허용
AmazonEKS_CNI_Policy : EKS 클러스터의 CNI 플러그인을 위한 정책
  • CNI 플러그인이 VPC에서 리소스를 관리하고 ENI를 할당, 해제하는 등의 작업을 허용
AmazonEC2ContainerRegistryReadOnly : ECR에서 이미지를 읽어올 수 있는 권한을 부여
  • ECR에 저장되어 있는 Container Image를 EKS 노드에서 실행할 수 있다.
AmazonSSMManagedInstanceCore : AWS Systems Manager 에이전트 관리
  • SSM가 관리되는 인스턴스에서 실행하는 작업을 위한 권한 부여

4. EC2 instance profile 생성

Karpenter가 배포한 EC2 인스턴스에서 해당 Role을 사용할 수 있도록 하기 위함.

aws iam create-instance-profile \
  --instance-profile-name "KarpenterNodeInstanceProfile-${CLUSTER_NAME}"

aws iam add-role-to-instance-profile \
  --instance-profile-name "KarpenterNodeInstanceProfile-${CLUSTER_NAME}" \
  --role-name "KarpenterNodeRole-${CLUSTER_NAME}"

 


2-2. Controller IAM Role 생성

새 인스턴스를 프로비저닝할때 사용할 IAM 역할 생성

  • IRSA (IAM Role for Service Account) 방식으로 권한을 얻고, EC2를 생성, 삭제 한다.
  • Karpenter Controller가 AWS API를 호출할 수 있도록 권한 부여

1. Role의 신뢰관계 생성

$ cat << EOF > controller-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_ENDPOINT#*//}"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "${OIDC_ENDPOINT#*//}:aud": "sts.amazonaws.com",
                    "${OIDC_ENDPOINT#*//}:sub": "system:serviceaccount:karpenter:karpenter"
                }
            }
        }
    ]
}
EOF

 

2. Karpenter Controller에서 사용할 IAM Role 생성

aws iam create-role \
  --role-name KarpenterControllerRole-${CLUSTER_NAME} \
  --assume-role-policy-document file://controller-trust-policy.json

 

3. Karpenter Controller의 IAM Policy 생성

$ cat << EOF > controller-policy.json
{
    "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/karpenter.sh/nodepool": "*"
                }
            },
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "ConditionalEC2Termination"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}",
            "Sid": "PassNodeIAMRole"
        },
        {
            "Effect": "Allow",
            "Action": "eks:DescribeCluster",
            "Resource": "arn:${AWS_PARTITION}:eks:${AWS_REGION}:${AWS_ACCOUNT_ID}:cluster/${CLUSTER_NAME}",
            "Sid": "EKSClusterEndpointLookup"
        },
        {
            "Sid": "AllowScopedInstanceProfileCreationActions",
            "Effect": "Allow",
            "Resource": "*",
            "Action": [
            "iam:CreateInstanceProfile"
            ],
            "Condition": {
            "StringEquals": {
                "aws:RequestTag/kubernetes.io/cluster/${CLUSTER_NAME}": "owned",
                "aws:RequestTag/topology.kubernetes.io/region": "${AWS_REGION}"
            },
            "StringLike": {
                "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass": "*"
            }
            }
        },
        {
            "Sid": "AllowScopedInstanceProfileTagActions",
            "Effect": "Allow",
            "Resource": "*",
            "Action": [
            "iam:TagInstanceProfile"
            ],
            "Condition": {
            "StringEquals": {
                "aws:ResourceTag/kubernetes.io/cluster/${CLUSTER_NAME}": "owned",
                "aws:ResourceTag/topology.kubernetes.io/region": "${AWS_REGION}",
                "aws:RequestTag/kubernetes.io/cluster/${CLUSTER_NAME}": "owned",
                "aws:RequestTag/topology.kubernetes.io/region": "${AWS_REGION}"
            },
            "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/${CLUSTER_NAME}": "owned",
                "aws:ResourceTag/topology.kubernetes.io/region": "${AWS_REGION}"
            },
            "StringLike": {
                "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass": "*"
            }
            }
        },
        {
            "Sid": "AllowInstanceProfileReadActions",
            "Effect": "Allow",
            "Resource": "*",
            "Action": "iam:GetInstanceProfile"
        }
    ],
    "Version": "2012-10-17"
}
EOF

 

4. IAM Policy를 Role에 맵핑 (연동)

aws iam put-role-policy \
  --role-name KarpenterControllerRole-${CLUSTER_NAME} \
  --policy-name KarpenterControllerPolicy-${CLUSTER_NAME} \
  --policy-document file://controller-policy.json

 

지금까지 생성한 IAM Role, Policy 확인

# IAM Role
aws iam list-role-policies --role-name <your-role-name>

# IAM Policy
aws iam get-role-policy --role-name your-role-name --policy-name your-policy-name

# IAM Trust relationship
aws iam get-role --role-name your-role-name

 


2-3. Subnet & Security Group에 Tag값 추가

  • Karpenter Controller가 EC2 노드를 추가 및 생성할 시 Tag먼저 확인하여 작업한다.

 

참고사항

EKS 클러스터를 생성할 당시 managed nodegroup 옵션을 사용하여 node 생명주기 관리가 용이하다.

  • 즉, aws cli로 Tag작업이 가능하며 옵션을 사용하지 않고 self로 관리시 aws console에서 tag 추가해야한다.

Karpenter에서 사용할 Tag값은 아래와 같다.

Key: karpenter.sh/discovery
Value: [cluster name]

 

Subnet에 Tag 추가

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

 

Tag값 확인

aws ec2 describe-subnets --subnet-ids ${SUBNET_ID}

 

 Security Group에 Tag추가

  • EKS Cluster에서 Karpenter를 구동시킬 보안 그룹만 태그를 지정한다.
  • 그룹이 여러 개인 경우에 Karpenter가 사용해야 하는 그룹을 지정해야 하는데, 이유는 아래와 같다.

Karpenter가 가용중이고 노드들을 스케줄링 할 때 자신이 생성한 노드에 controller 등

중요한 Pod가 들어가는 경우 리소스가 정상적으로 돌아왔을 때 노드들을 제거하면서 중요한 Pod가 삭제될 수 있다.

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}

 

Tag값 확인

aws ec2 describe-tags --filters "Name=resource-id,Values=${SECURITY_GROUPS}"

# resource id로 해당 Securty Group 이름 출력
aws ec2 describe-security-groups --group-ids sg-0ffdc3de28c86c9a2 --query 'SecurityGroups[].GroupName' --output text

SecurityGroup에 Key,Vaule값으로 Tag값 추가 확인

 


2-4. aws-auth ConfigMap 업데이트

  • IAM 역할을 사용하는 EC2 노드가 EKS 클러스터에 가입하도록 허용해줌.
  • eks cluster를 생성할 때 자동으로 만들어지는데, 만약 없다면 수동으로 꼭 생성해줘야한다.
kubectl edit configmap aws-auth -n kube-system

기존 ASG로 관리되는 NodeGroup (AS-IS)

  • 위 mapRoles에 있는 IAM 역할은 기존 ASG로 관리되는 노드그룹이다. (기존 노드 그룹)
  • 바로 아래에 Karpenter Node IAM Role을 추가하자.
- groups:
  - system:bootstrappers
  - system:nodes
  rolearn: arn:aws:iam::339712771495:role/KarpenterNodeRole-prd-eks-cluster
  username: system:node:{{EC2PrivateDNSName}}

Karpenter NodeGroup을 추가 (To-BE)

 


 

너무 길게 포스팅이 되는것 같아서 Part별로 나누려고 합니다.

다음 포스팅 에서는 Karpenter 배포하는 실습내용으로 찾아뵙도록 하겠습니다

 

감사합니다 :)