사전 준비
- EKS 클러스터
- EKS의 노드그룹
- Helm
1. Karpenter 설치
먼저 Karpenter를 설치하기 전, Karpenter-Controller가 사용할 IRSA가 필요합니다.
1-1. IRSA 생성을 위한 IAM Role 생성
먼저 EKS의 ID 제공업체 확인해줍니다.
# 클러스터 정보 확인
aws eks describe-cluster \
--name <CLUSTER_NAME> \
--query "cluster.identity.oidc.issuer" \
--output text
이제 Role 생성할 때 필요한 신뢰 관계를 json 파일로 만들어 줍니다.
cat > role.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<OIDC_ID>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<region>.amazonaws.com/id/<OIDC_ID>:sub": "system:serviceaccount:karpenter:karpenter"
}
}
}
]
}
EOF
이제, Role을 생성 해줍니다.
aws iam create-role \
--role-name KarpenterControllerRole \
--assume-role-policy-document file://role.json
그리고 정책을 붙여주면 되는데 저는 아래 사진과 같이 붙여주었습니다.
KarpenterController-EC2NodeClass
를 제외한 Policy는 원래 노드 그룹에서 사용하던 정책을 그대로 가져왔습니다.
KarpenterController-EC2NodeClass
정책은 아래와 같습니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2ReadForDiscoveryAndPricing",
"Effect": "Allow",
"Action": [
"ec2:DescribeImages",
"ec2:DescribeInstanceTypes",
"ec2:DescribeInstanceTypeOfferings",
"ec2:DescribeSpotPriceHistory",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DescribeSecurityGroups",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeLaunchTemplates",
"ec2:DescribeLaunchTemplateVersions"
],
"Resource": "*"
},
{
"Sid": "SSMParameterReadForAMIResolution",
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
"Resource": "*"
},
{
"Sid": "AWSPriceListRead",
"Effect": "Allow",
"Action": [
"pricing:GetProducts"
],
"Resource": "*"
},
{
"Sid": "InstanceProfileManageForKarpenter",
"Effect": "Allow",
"Action": [
"iam:GetInstanceProfile",
"iam:CreateInstanceProfile",
"iam:DeleteInstanceProfile",
"iam:AddRoleToInstanceProfile",
"iam:RemoveRoleFromInstanceProfile",
"iam:TagInstanceProfile"
],
"Resource": [
"arn:aws:iam::<ACCOUNT_ID>:instance-profile/<CLUSTER_NAME>_*",
"arn:aws:iam::<ACCOUNT_ID>:instance-profile/karpenter-*"
]
},
{
"Sid": "PassNodeRoleToEC2",
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": "arn:aws:iam::308910977993:role/dev-outcode-eks-node-group-role",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ec2.amazonaws.com"
}
}
},
{
"Sid": "ProvisionInstances",
"Effect": "Allow",
"Action": [
"ec2:CreateFleet",
"ec2:RunInstances",
"ec2:CreateTags",
"ec2:DeleteTags",
"ec2:TerminateInstances"
],
"Resource": "*"
},
{
"Sid": "CreateSpotServiceLinkedRoleIfMissing",
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "spot.amazonaws.com"
}
}
},
{
"Sid": "EC2LaunchTemplateRead",
"Effect": "Allow",
"Action": [
"ec2:DescribeLaunchTemplates",
"ec2:DescribeLaunchTemplateVersions"
],
"Resource": "*"
},
{
"Sid": "ManageLaunchTemplates",
"Effect": "Allow",
"Action": [
"ec2:CreateLaunchTemplate",
"ec2:DeleteLaunchTemplate",
"ec2:CreateLaunchTemplateVersion",
"ec2:DeleteLaunchTemplateVersions"
],
"Resource": "*"
},
{
"Sid": "ManageENI",
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:DetachNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses"
],
"Resource": "*"
}
]
}
이렇게 정책을 넣어주면 Role 생성은 완료 입니다.
1-2. Karpenter 설치
저는 우선 Karpenter 공식 홈페이지에 나와있는 방법대로 EKS 클러스터 에서 Helm을 사용해서 설치를 하니, Controller의 이미지 Pull 하는 과정에서 자꾸 401 에러가 나왔습니다.
그래서 values.yaml
파일에 나와있는 public.ecr.aws/karpenter/controller:1.6.3
이미지를 제 로컬 노트북에 설치한 후, ECR에 Push 한 후 ECR에서 이미지를 Pull 하는 식으로 설치하였습니다.
위 주소에서 values.yaml
파일을 확인 하실 수 있습니다.
앞서 생성한 Role의 ARN과 ECR 이미지 주소를 values.yaml
파일에 추가해서 설치를 진행하였습니다.
- values.yaml 파일에서 수정한 부분
serviceAccount: create: true name: "karpenter" annotations: eks.amazonaws.com/role-arn: <앞서 생성한 Role의 ARN> controller: image: repository: <자신이 사용하는 이미지 레포지터리 주소> tag: <변경한 태그>
이제 설치를 진행 해주면 됩니다.
helm upgrade --install --namespace karpenter --create-namespace \
karpenter oci://public.ecr.aws/karpenter/karpenter -f values.yaml
정상적으로 파드가 생긴 것을 확인 할 수 있습니다.
2. Spot 인스턴스 생성을 위한 EC2NodeClass 생성
EC2NodeClass 란?
Karpenter가 EC2 인스턴스를 띄울 때, 어떤 AWS 리소스(Subnet, SecurityGroup, AMI, IAM Role 등)를 사용할 지 정의하는 리소스 입니다.
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: <원하는 이름>
spec:
amiFamily: AL2023 # Amazon Linux 2023, 해당 부분은 사용자가 원하는 OS 타입을 사용하면 됩니다.
amiSelectorTerms:
- alias: al2023@v20250821 # 저는 기존 EKS에서 동작하던 워커노드와 버전을 맞추었습니다.
role: <기존 EKS의 노드 그룹에서 사용하던 Role의 이름>
subnetSelectorTerms:
- tags:
kubernetes.io/cluster/dev-outcode-eks-cluster: shared # Karpenter가 새로 EC2 노드를 만들 때 어떤 Subnet에 노드를 띄울지 결정하는 조건, 해당 태그가 존재하는 서브넷에 EC2 생성함
securityGroupSelectorTerms:
- tags:
kubernetes.io/cluster/dev-outcode-eks-cluster: owned # EC2 인스턴스가 사용할 Security Group 선택하는 조건, 해당 태그가 존재하는 SG를 생성된 EC2가 사용함
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
metadataOptions:
httpTokens: optional
httpPutResponseHopLimit: 2
httpEndpoint: enabled
tags:
Name: <원하는 태그 명>
3. 2. Spot 인스턴스 생성을 위한 NodePool 생성
NodePool 이란 ?
arpenter에서 클러스터의 노드 확장 정책을 정의하는 리소스입니다.
즉, “어떤 Pod이 들어오면 어떤 조건의 노드를 띄워줄지”를 결정하는 스케줄링 단위 입니다.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: <원하는 이름>
spec:
template:
metadata:
# 생성될 노드에 추가할 라벨입니다. 'kubectl get nodes -l node-group-type=cache-nodes' 와 같이 노드를 선택할 때 유용합니다.
labels:
node-group-type: spot
spec:
# expireAfter: 노드가 생성된 후 지정된 시간이 지나면 자동으로 교체되도록 설정합니다.
# 'Never'로 설정하면 시간 경과에 따른 자동 교체 기능을 사용하지 않습니다.
expireAfter: 'Never'
# nodeClassRef: 노드의 AWS 관련 상세 설정(AMI, 인스턴스 프로파일, 서브넷, 보안 그룹 등)을 정의한 EC2NodeClass를 연결합니다.
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: <앞서 생성한 EC2NodeClass 이름>
# requirements: 생성될 노드가 충족해야 할 요구사항을 정의합니다. 파드의 요구사항과 조합하여 최종 노드 스펙이 결정됩니다.
requirements:
# 인스턴스 타입을 t3a.large로 설정.
- key: node.kubernetes.io/instance-type
operator: In
values: ["t3a.large"]
# 스팟 인스턴스만 사용하도록 설정합니다. (온디맨드도 가능)
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
# taints: 생성될 노드에 특정 테인트(taint)를 적용합니다.
# 이 테인트를 용인(toleration)하는 파드만 이 노드에 스케줄링될 수 있습니다.
taints:
- key: spot
value: "only"
effect: "NoSchedule"
# disruption: 노드를 축소하거나 교체하는 '중단(disruption)' 관련 정책을 정의합니다.
disruption:
# consolidationPolicy: 노드 통합(축소) 정책을 정의합니다.
# 'WhenEmptyOrUnderutilized': 노드가 비어있거나(Empty) 또는 충분히 활용되지 않을 때(Underutilized) Karpenter가 노드를 통합(삭>제)하려고 시도합니다.
# 'WhenEmpty': 노드가 완전히 비었을 때만 삭제합니다.
consolidationPolicy: WhenEmptyOrUnderutilized
# consolidateAfter: 노드가 통합 대상으로 고려되기까지 대기하는 시간입니다.
# 예를 들어, 워크로드가 잠시 줄었다가 다시 늘어나는 경우를 대비해 너무 빨리 노드를 삭제하지 않도록 합니다.
consolidateAfter: 30s
# limits: 이 NodePool이 생성할 수 있는 전체 리소스의 양을 제한합니다. 비용 관리 및 안전장치 역할을 합니다.
limits:
cpu: "16" # 이 NodePool은 최대 16개의 vCPU까지만 생성할 수 있습니다.
이렇게 리소스들을 생성하고 파드의 toleration, nodeSelector에 아래와 같이 조건을 주면 karpenter가 알아서 노드를 생성하게 됩니다.
nodeSelector:
"node-group-type": spot
tolerations:
- key: "spot"
operator: "Equal"
value: "only"
effect: "NoSchedule"
이상 입니다.
'AWS' 카테고리의 다른 글
Packer 사용해서 AWS ami 커스텀 해보기 (0) | 2025.09.30 |
---|---|
GCP <-> AWS VPN 연결 (1) | 2025.06.25 |
EKS 노드에서 생성할 수 있는 POD 개수 제한 해제 (0) | 2025.06.17 |
AWS Secret Manager 에서 생성한 Secret을 EKS 에서 사용하기 (0) | 2025.06.04 |
AWS ALB(Application LoadBalancer) Controller 설치 하기 (0) | 2025.06.02 |