일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- MFA 분실
- epxress-generator
- AWS
- 디스크 성능테스트
- Authenticator
- 테라폼 설치
- EC2
- 테라폼 맥
- EBS
- 리눅스
- 볼륨추가
- 컨테이너 터미널
- 컨테이너 터미널 로그아웃
- 볼륨 연결
- Mac Terraform
- docker -i -t
- /etc/fstab 설정
- 테라폼 캐시
- xfs_quota
- MFA 인증
- /etc/fstab 뜻
- docker 상태
- ebs 마운트
- 리눅스 시간대
- Terrafrom
- ebs 재부팅
- AWS EBS
- 테라폼 자동완성
- 텔레메트리란
- EBS 최적화
- Today
- Total
I got IT
EKS Autoscaling - 3 (Karpenter) 본문
8. Karpenter
8.1 Karpenter 란?
Karpenter는 Amazon EKS에서 노드 프로비저닝을 자동화하는 도구로, Kubernetes의 Cluster Autoscaler와 AWS의 Auto Scaling Group(ASG)의 한계를 극복하기 위해 설계되었습니다.
8.1.1 Karpenter 구성요소
- NodeClaim:
- 새로운 노드가 필요할 때 생성되는 요청 객체.
- Pending 상태의 Pod 요구 사항(리소스 요청, NodeSelector, Affinity 등)을 분석하여 적합한 노드를 프로비저닝합니다.
- AWS EC2 API를 호출해 필요한 인스턴스를 생성.
- NodePool:
- 노드에 대한 설정을 관리하는 추상화 계층.
- 특정 노드 유형(인스턴스 크기, AMI, 네트워크 설정 등)을 정의합니다.
- 다양한 워크로드를 효과적으로 관리할 수 있도록 지원.
- NodeClass:
- 특정 노드의 세부적인 속성을 정의하는 객체.
- 예: AMI 유형, 서브넷 선택, 보안 그룹, IAM 역할 등.
- Scheduling:
- Pending 상태의 Pod을 감지하고, Pod 요구 사항에 따라 적합한 노드를 선택하여 배치.
- 현재 클러스터 상태와 리소스 활용도를 고려해 최적화된 스케줄링을 수행.
- Consolidation:
- 클러스터 비용 절감을 위해 빈 노드를 제거하거나 워크로드를 재배치하여 더 적은 수의 노드로 통합.
- Spot 인스턴스 가격 변동을 감지하여 더 저렴한 인스턴스로 대체 가능.
- Interruption Handling:
- AWS Spot 인스턴스 종료 또는 EC2 유지보수 이벤트를 감지하고, 영향을 받는 노드를 미리 종료 및 대체하여 다운타임을 최소화.
- Simulation:
- 실제 리소스를 확장하기 전에 시뮬레이션을 실행하여 최적의 스케일링 결정을 내림.
- 워크로드 요구 사항, 인스턴스 크기, 가격 옵션 등을 고려해 가장 효율적인 리소스를 선택.
8.2 Karpenter 실습
해당 실습은 카펜터 공식 사이트의 튜토리얼을 토대로 하였습니다. Getting Started with Karpenter 실습 - Docs
8.2.0 Prequisite
실습을 위해 아래 유틸리티가 설치 되어있어야 합니다. 포스팅을 쭉 따라오셨다면 이전 과정을 통해 이미 설정되어 있으니 스킵해도 됩니다.
- AWS CLI : 자격증명 설정
kubectl
- the Kubernetes CLIeksctl
(>= v0.202.0) - the CLI for AWS EKShelm
- the package manager for Kuberneteseks-node-view
8.2.1 Karpenter 실습 환경 변수 설정
# 변수 설정
export KARPENTER_NAMESPACE="kube-system"
export KARPENTER_VERSION="1.2.1"
export K8S_VERSION="1.32"
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="gasida-karpenter-demo" # ${USER}-karpenter-demo
export AWS_DEFAULT_REGION="ap-northeast-2"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export TEMPOUT="$(mktemp)"
export ALIAS_VERSION="$(aws ssm get-parameter --name "/aws/service/eks/optimized-ami/${K8S_VERSION}/amazon-linux-2023/x86_64/standard/recommended/image_id" --query Parameter.Value | xargs aws ec2 describe-images --query 'Images[0].Name' --image-ids | sed -r 's/^.*(v[[:digit:]]+).*$/\1/')"
# 확인
echo "${KARPENTER_NAMESPACE}" "${KARPENTER_VERSION}" "${K8S_VERSION}" "${CLUSTER_NAME}" "${AWS_DEFAULT_REGION}" "${AWS_ACCOUNT_ID}" "${TEMPOUT}" "${ALIAS_VERSION}"
8.2.2 Karpenter 실습 환경 배포
- CloudFormation을 사용하여 EKS 클러스터에 필요한 인프라 설정
- CloudFormation 템플릿(
cloudformation.yaml
)을 사용해 Karpenter가 노드를 생성 및 관리할 수 있도록 클러스터를 부트스트랩합니다. - 자세한 내용은 CloudFormation 문서를 참조하세요.
- CloudFormation 템플릿(
- Kubernetes 서비스 계정 및 AWS IAM 역할 생성
- Kubernetes 서비스 계정과 AWS IAM 역할을 생성하고 IRSA(IAM Roles for Service Accounts)를 사용해 연결합니다. 이를 통해 Karpenter가 EC2 인스턴스를 시작할 수 있는 권한을 부여합니다.
aws-auth
ConfigMap에 Karpenter 노드 역할 추가- 생성된 노드가 클러스터에 연결될 수 있도록
aws-auth
ConfigMap에 Karpenter의 노드 역할을 추가합니다.
- 생성된 노드가 클러스터에 연결될 수 있도록
- EKS 관리형 노드 그룹 또는 Fargate 설정
- 기본적으로
kube-system
과karpenter
네임스페이스에 대해 관리형 노드 그룹을 사용합니다. - Fargate를 사용하려면
fargateProfiles
설정을 활성화하고,managedNodeGroups
설정을 비활성화합니다.
- 기본적으로
KARPENTER_IAM_ROLE_ARN
변수 설정- Karpenter가 사용할 IAM 역할의 ARN 값을 환경 변수로 지정합니다.
- Spot 인스턴스를 허용하는 역할 생성
- 비용 최적화를 위해 Spot 인스턴스를 사용할 수 있도록 관련 IAM 역할을 생성합니다.
- Helm을 사용해 Karpenter 설치
- Helm 차트를 실행하여 Karpenter를 클러스터에 배포합니다.
카펜터가 사용할 역할을 미리 세팅해 주어야 합니다. 이때 해당 역할은 워커노드의 인스턴스 프로파일 입니다.
# CloudFormation 스택으로 IAM Policy/Role, SQS, Event/Rule 생성 : 3분 정도 소요
## IAM Policy : KarpenterControllerPolicy-gasida-karpenter-demo
## IAM Role : KarpenterNodeRole-gasida-karpenter-demo
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${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}"
기본 네트워크 구성을 완료했으면 이제 클러스터를 생성하여 줍니다.
# 클러스터 생성 : EKS 클러스터 생성 15분 정도 소요
eksctl create cluster -f - <<EOF
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ${CLUSTER_NAME}
region: ${AWS_DEFAULT_REGION}
version: "${K8S_VERSION}"
tags:
karpenter.sh/discovery: ${CLUSTER_NAME}
iam:
withOIDC: true
podIdentityAssociations:
- namespace: "${KARPENTER_NAMESPACE}"
serviceAccountName: karpenter
roleName: ${CLUSTER_NAME}-karpenter
permissionPolicyARNs:
- arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}
iamIdentityMappings:
- arn: "arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}"
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
## If you intend to run Windows workloads, the kube-proxy group should be specified.
# For more information, see https://github.com/aws/karpenter/issues/5099.
# - eks:kube-proxy-windows
managedNodeGroups:
- instanceType: m5.large
amiFamily: AmazonLinux2023
name: ${CLUSTER_NAME}-ng
desiredCapacity: 2
minSize: 1
maxSize: 10
iam:
withAddonPolicies:
externalDNS: true
addons:
- name: eks-pod-identity-agent
EOF
💡eksctl 메타데이터에 태그를 넣으면 모든 리소스에 해당 태그가 입력이 됩니다.
배포 확인하기
# eks 배포 확인
eksctl get cluster
eksctl get nodegroup --cluster $CLUSTER_NAME
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
eksctl get addon --cluster $CLUSTER_NAME
#
kubectl ctx
kubectl config rename-context "<각자 자신의 IAM User>@<자신의 Nickname>-karpenter-demo.ap-northeast-2.eksctl.io" "karpenter-demo"
kubectl config rename-context "admin@gasida-karpenter-demo.ap-northeast-2.eksctl.io" "karpenter-demo"
# k8s 확인
kubectl ns default
kubectl cluster-info
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get pod -n kube-system -owide
kubectl get pdb -A
kubectl describe cm -n kube-system aws-auth
# EC2 Spot Fleet의 service-linked-role 생성 확인 : 만들어있는것을 확인하는 거라 아래 에러 출력이 정상!
# If the role has already been successfully created, you will see:
# An error occurred (InvalidInput) when calling the CreateServiceLinkedRole operation: Service role name AWSServiceRoleForEC2Spot has been taken in this account, please try a different suffix.
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true
8.2.3 Karpenter 설치
# Logout of helm registry to perform an unauthenticated pull against the public ECR
helm registry logout public.ecr.aws
# Karpenter 설치를 위한 변수 설정 및 확인
export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name "${CLUSTER_NAME}" --query "cluster.endpoint" --output text)"
export KARPENTER_IAM_ROLE_ARN="arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"
echo "${CLUSTER_ENDPOINT} ${KARPENTER_IAM_ROLE_ARN}"
# karpenter 설치
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}" --namespace "${KARPENTER_NAMESPACE}" --create-namespace \
--set "settings.clusterName=${CLUSTER_NAME}" \
--set "settings.interruptionQueue=${CLUSTER_NAME}" \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--set controller.resources.limits.cpu=1 \
--set controller.resources.limits.memory=1Gi \
--wait
# 확인
helm list -n kube-system
kubectl get-all -n $KARPENTER_NAMESPACE
kubectl get all -n $KARPENTER_NAMESPACE
kubectl get crd | grep karpenter
ec2nodeclasses.karpenter.k8s.aws 2025-03-02T06:11:47Z
nodeclaims.karpenter.sh 2025-03-02T06:11:47Z
nodepools.karpenter.sh 2025-03-02T06:11:47Z
- Karpenter는 ClusterFirst기본적으로 포드 DNS 정책을 사용합니다. Karpenter가 DNS 서비스 포드의 용량을 관리해야 하는 경우 Karpenter가 시작될 때 DNS가 실행되지 않음을 의미합니다.
- Karpenter는 노드 용량 추적을 위해 클러스터의 CloudProvider 머신과 CustomResources 간의 매핑을 만듭니다. 이 매핑이 일관되도록 하기 위해 Karpenter는 다음 태그 키를 활용합니다.
- karpenter.sh/managed-by
- karpenter.sh/nodepool
- kubernetes.io/cluster/${CLUSTER_NAME}
카펜터 설치가 확인 되었으면 다음 단계로 진행합니다.
8.2.4 Kapenter 동작확인을 위한 모니터링 설정
프로메테우스, 그라파나를 설치합니다. 이 때 이전처럼 prometheus-stack으로 한번에 설치하지 않고 프로메테우스와 그라파나 각각 설치해줍니다.
helm repo 등록
#
helm repo add grafana-charts https://grafana.github.io/helm-charts
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
kubectl create namespace monitoring
프로메테우스 설치
# 프로메테우스 설치
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/prometheus-values.yaml | envsubst | tee prometheus-values.yaml
helm install --namespace monitoring prometheus prometheus-community/prometheus --values prometheus-values.yaml
extraScrapeConfigs: |
- job_name: karpenter
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- kube-system
relabel_configs:
- source_labels:
- __meta_kubernetes_endpoints_name
- __meta_kubernetes_endpoint_port_name
action: keep
regex: karpenter;http-metrics
# 프로메테우스 얼럿매니저 미사용으로 삭제
kubectl delete sts -n monitoring prometheus-alertmanager
# 프로메테우스 접속 설정
export POD_NAME=$(kubectl get pods --namespace monitoring -l "app.kubernetes.io/name=prometheus,app.kubernetes.io/instance=prometheus" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace monitoring port-forward $POD_NAME 9090 &
open http://127.0.0.1:9090
- 카펜터 메트릭을 수집하기 위해 extraScrapeConfigs가 설정됨
오류 사항은 무시하셔도 됩니다.
프로메테우스 관리화면에 들어가보면 타겟 헬스에 카펜터가 등록된 것을 확인 가능합니다.
그라파나 설치
# 그라파나 설치
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v"${KARPENTER_VERSION}"/website/content/en/preview/getting-started/getting-started-with-karpenter/grafana-values.yaml | tee grafana-values.yaml
helm install --namespace monitoring grafana grafana-charts/grafana --values grafana-values.yaml
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
version: 1
url: http://prometheus-server:80
access: proxy
dashboardProviders:
dashboardproviders.yaml:
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /var/lib/grafana/dashboards/default
dashboards:
default:
capacity-dashboard:
url: https://karpenter.sh/preview/getting-started/getting-started-with-karpenter/karpenter-capacity-dashboard.json
performance-dashboard:
url: https://karpenter.sh/preview/getting-started/getting-started-with-karpenter/karpenter-performance-dashboard.json
# admin 암호
kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
17JUGSjgxK20m4NEnAaG7GzyBjqAMHMFxRnXItLj
# 그라파나 접속
kubectl port-forward --namespace monitoring svc/grafana 3000:80 &
open http://127.0.0.1:3000
그라파나에 접속해보면 대시보드가 자동으로 구성된 것을 확인할 수 있습니다.
8.2.5 Create NodePool
Node pool 이란
NodePool은 Karpenter가 노드를 생성할 때 참조하는 정책 및 설정 정보를 제공하며, Karpenter는 이를 활용해 클러스터의 리소스를 동적으로 확장합니다.
- NodePool의 역할: NodePool은 클러스터에서 노드의 특성을 정의하는 리소스입니다. 이는 Karpenter가 새 노드를 생성할 때 사용할 설정(예: 인스턴스 유형, 태그, 가용 영역 등)을 제공합니다.
- Karpenter와 NodePool의 상호작용:
- Karpenter는 NodePool을 "감시(watch)"하여 클러스터의 상태를 지속적으로 모니터링합니다.
- NodePool에 정의된 정책 및 설정을 기반으로 Karpenter는 Pending 상태의 Pod(스케줄되지 않은 Pod)의 요구사항을 충족하기 위해 적절한 노드를 생성합니다.
- NodeClaim 생성:
- Pending Pod가 감지되면 Karpenter는 NodePool 및 NodeClass(노드의 세부적인 설정) 정보를 참조하여 NodeClaim을 생성합니다.
- NodeClaim은 새 노드의 생성 요청을 나타내며, EC2 인스턴스와 같은 실제 리소스를 프로비저닝하는 데 사용됩니다.
- 자동화된 노드 프로비저닝:
- Karpenter는 NodeClaim을 기반으로 EC2 Fleet을 통해 적절한 EC2 인스턴스를 시작하고, 이를 Kubernetes 클러스터에 연결하여 필요한 리소스를 제공합니다.
노드풀 생성하기
#
echo $ALIAS_VERSION
v20250228
#
cat <<EOF | envsubst | kubectl apply -f -
apiVersion: karpenter.sh/v1
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: ["on-demand"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["2"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
expireAfter: 720h # 30 * 24h = 720h
limits:
cpu: 1000
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
---
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
role: "KarpenterNodeRole-${CLUSTER_NAME}" # replace with your cluster name
amiSelectorTerms:
- alias: "al2023@${ALIAS_VERSION}" # ex) al2023@latest
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "${CLUSTER_NAME}" # replace with your cluster name
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "${CLUSTER_NAME}" # replace with your cluster name
EOF
# 확인
kubectl get nodepool,ec2nodeclass,nodeclaims
헤당 노드풀의 requirements 설정을 보면 Karpenter가 프로비저닝할 노드에 대해 다음과 같은 조건을 만족하도록 제한합니다:
- x86_64 아키텍처와 Linux 운영 체제 사용
- 온디맨드 인스턴스로만 생성
- AWS 인스턴스 계열은 컴퓨팅 최적화(
c
), 범용(m
), 메모리 최적화(r
) 중 하나 - 3세대 이상의 최신 인스턴스를 사용
노드풀 설치 확인
8.2.6 Karpenter 동작 시키기
애플리케이션 배포. cpu를 1코어나 차지하는 걸로 보아 꽤 무거운 파드인걸로 보입니다.
# pause 파드 1개에 CPU 1개 최소 보장 할당할 수 있게 디플로이먼트 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 0
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
terminationGracePeriodSeconds: 0
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
securityContext:
allowPrivilegeEscalation: false
EOF
모니터링 화면 대기
# [신규 터미널] 모니터링
eks-node-viewer --resources cpu,memory
eks-node-viewer --resources cpu,memory --node-selector "karpenter.sh/registered=true" --extra-labels eks-node-viewer/node-age
파드 스케일 아웃(레플리카 증가) → 노드 부하 증가
# Scale up
kubectl get pod
kubectl scale deployment inflate --replicas 5
파드를 늘렸는데 노드가 2대에서 3대로 증가된 것을 볼 수 있습니다.
Karpenter Autoscaling 확인
# 출력 로그 분석해보자!
kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller
kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller | jq '.'
kubectl logs -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller | grep 'launched nodeclaim' | jq '.'
# 확인
kubectl get nodeclaims
#
kubectl get node -l karpenter.sh/registered=true -o jsonpath="{.items[0].metadata.labels}" | jq '.'
노드클레임이 하나 발생한걸 확인할 수 있습니다.
노드클레임을 describe 해보면 노드가 등록되는 과정을 볼 수 있습니다.
AWS 트레일을 살펴보면 이전에 CAS로 발생한 CreatFleet과 다르게 사용자 이름이 -karpenter 가 붙은 것을 볼 수 있습니다. 이는 IRSA 설정을 통해 카펜터에게 노드스케일링 권한을 부여했기 때문입니다.
해당 이벤트를 자세히 살펴보면 카펜터가 어떤식으로 동작했는지 알 수 있습니다.
온디맨드 요금의 경우 AllocationStrategy는 lowest-price가 default로 설정됩니다.
파드 스케일 인(레플리카 감소) → 노드 부하 감소
# Now, delete the deployment. After a short amount of time, Karpenter should terminate the empty nodes due to consolidation.
kubectl delete deployment inflate && date
# 출력 로그 분석해보자!
kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller | jq '.'
8.3 실습 리소스 삭제
# Karpenter helm 삭제
helm uninstall karpenter --namespace "${KARPENTER_NAMESPACE}"
# Karpenter IAM Role 등 생성한 CloudFormation 삭제
aws cloudformation delete-stack --stack-name "Karpenter-${CLUSTER_NAME}"
# EC2 Launch Template 삭제
aws ec2 describe-launch-templates --filters "Name=tag:karpenter.k8s.aws/cluster,Values=${CLUSTER_NAME}" |
jq -r ".LaunchTemplates[].LaunchTemplateName" |
xargs -I{} aws ec2 delete-launch-template --launch-template-name {}
# 클러스터 삭제
eksctl delete cluster --name "${CLUSTER_NAME}"
Appendix
- 쿠버네티스 자원 최적화 하기:
KRR
: Prometheus-based Kubernetes Resource Recommendations (링크)
'AWS > EKS' 카테고리의 다른 글
EKS Security - OPA 로 정책 적용하기 (0) | 2025.03.16 |
---|---|
EKS Security - ECR Enhanced Scanning (0) | 2025.03.16 |
EKS Autoscaling - 2 (Node level) (0) | 2025.03.09 |
EKS Autoscaling - 1 (Pod level) (0) | 2025.03.08 |
EKS 모니터링 (1) | 2025.03.02 |