인프라/시스템 구축

[ EKS ] karpenter

김붕어87 2023. 4. 6. 16:28
반응형
개요
karpenter 이란 ?
karpenter은 WorkerNode(노드)의 수를 조절하는 기능이다

- Watching : 예약 불가능한 (Pending) pod를 감시한다.
- Provisioning : Pending pod의 요구사항에 맞는 노드를 배포하고 배포된 노드에 pod를 스케쥴링 해준다.
- Removing : 노드가 더 이상 필요하지 않을 때 노드 제거한다.
- AWS 환경만 지원


ClusterAutoscaler(CA) 차이점 
- ClusterAutoscaler은 ASG를 사용하기 때문에 nodegroup을 구성/관리 해야하고, 노드 증설/축소 시 많은 단계가 필요해서 속도가 느리다.
- ClusterAutoscaler은 예약 불가능한 pod를 감시하고, AWS 리소스를 생성 요청하기 때문에 노드 확장하는대 시간이 오래 걸림.
- Karpenter은 ASG을 사용하지 않고 POD의 요구사항에 맞는 노드를 증설/축소 하기 때문에 속도가 빠르다.
   (group-less auto scaling)
- karpenter은  빠른 프로비저닝 가능


ClusterAutoscaler는 Node생성되는데 5분이상 걸림
Karpenter는                  Node생성되는데 1~2분 걸림

 

 

 

[ 아키텍처 ]

 

 

[ karpenter 프로세스 ]

Node Provisioning (Scale-out) 정책

  • Unschedulable pod, pending 상태의 POD가 발생하면 Resource Request량 기준으로 Node가 증설됨.
  • 신규 노드가 15분 동안 NotReady 상태면 종료하고 재생성.

Node Removing (Sale-in) 정책

  • 예약된 Pod가 없는 Node가 있으면 cordon & drain 이후 terminate 진행.
    • karpenter.sh/do-not-evict 설정으로 삭제 방지.

 


[ Required 작업 ]

  • AWS Infra 구성
    • EKS Cluster 설치
    • IAM OIDC 생성

 

 


[ karpenter IAM 설정 ]

 

IAM Role 내용 정리
- KarpenterControllerRole은 karpenter가 WorkerNode를 생성할 수 있는 AWS 권한 설정
- KarpenterInstanceNodeRole은 Karpenter가 생성한 WorkerNode가 EKS에 조인될 수 있는 Role 권한 설정

 

 

 

1. iam role 생성 - KarpenterInstanceNodeRole

  • "KarpenterInstanceNodeRole" IAM 역할 생성
    • KarpenterInstanceNodeRole 이란?
      • karpenter로 생성한 NODE에 연결 할 IAM 역할입니다.
      • EKS에서 NODE를 관리하기 위해서는 IAM 역할이 필요합니다.
echo '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}' > node-trust-policy.json

aws iam create-role --role-name KarpenterInstanceNodeRole \
    --assume-role-policy-document file://node-trust-policy.json

 

 

2. iam role에 policy 연결

  • "KarpenterInstanceNodeRole" IAM 역할에 Policy을 연결합니다.
    • AmazonEKSWorkerNodePolicy
    • AmazonEKS_CNI_Policy
    • AmazonEC2ContainerRegistryReadOnly
    • AmazonSSMManagedInstanceCore
aws iam attach-role-policy --role-name KarpenterInstanceNodeRole \
    --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy

aws iam attach-role-policy --role-name KarpenterInstanceNodeRole \
    --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy

aws iam attach-role-policy --role-name KarpenterInstanceNodeRole \
    --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

aws iam attach-role-policy --role-name KarpenterInstanceNodeRole \
    --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

 

 

3. iam Policy 생성 - KarpenterControllerPolicy

  • "KarpenterControllerRole"  IAM 역할 생성
    • KarpenterControllerRole 이란?
      • karpenter가 AWS의 EC2(NODE)를 생성할 수 있는 권한을 설정합니다.

  • controller-policy.json 파일 생성
echo '{
    "Statement": [
        {
            "Action": [
                "ssm:GetParameter",
                "iam:PassRole",
                "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"
        }
    ],
    "Version": "2012-10-17"
}' > controller-policy.json

 

 

  • controller-policy.json 파일을 참조해서 IAM Policy 생성
aws iam create-policy --policy-name KarpenterControllerPolicy --policy-document file://controller-policy.json

 

 

4. iam Role 생성 - KarpenterControllerRole

  • oidc 추출
cluster=${eks-cluster}
aws eks describe-cluster --name $cluster --query "cluster.identity.oidc.issuer" --output text

 

 

 

  • rust-policy.json 파일 생성
    • xxx으로 되어 있는 값을 채워야합니다.
    • oidc, accountid
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::123xxx:oidc-provider/oidc.eks.ap-southeast-1.amazonaws.com/id/EXAM1234"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.ap-southeast-1.amazonaws.com/id/EXAM1234:sub": "system:serviceaccount:karpenter:karpenter",
                    "oidc.eks.ap-southeast-1.amazonaws.com/id/EXAM1234:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}

 

 

  • Role 생성 - KarpenterControllerRole
    • trust-policy.json 파일을 참조해서 IAM Role 생성
aws iam create-role --role-name KarpenterControllerRole \
    --assume-role-policy-document file://trust-policy.json

 

  • IAM Role에 Policy 연결
    • "KarpenterControllerRole" IAM 역할에 Policy을 연결합니다.
      • KarpenterControllerPolicy
aws iam attach-role-policy --role-name KarpenterControllerRole \
    --policy-arn arn:aws:iam:xxx:aws:policy/KarpenterControllerPolicy

 

 

 

 

 

5. subnet에 TAG 추가

  • NODE에 설정된 subnet 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

 

 

6. SecurityGroup에 TAG 추가

  • NODE에 연결된 SG TAG 추가
    • Key = karpenter.sh/discovery
    • Value = ${Cluster-name}
# 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)

aws ec2 create-tags \
    --tags "Key=karpenter.sh/discovery,Value=${CLUSTER_NAME}" \
    --resources ${SECURITY_GROUPS}

 

 

 

 

7. EC2 Instance profile 생성 및 ROLE 연결

  • karpenter 설치할 때 Instance profile 지정이 필요합니다.
  • Instance profile은 karpenter가 NODE을 생성할 때 IAM 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 "KarpenterInstanceNodeRole"

 

 

 

8. ConfigMap "aws-auth" 내용 추가

  • karpenter가 만든 NODE가 EKS 권한을 얻을 수 있도록 "aws-auth"에 내용 추가
    • KarpenterInstanceNodeRole

data:

  mapRoles:

    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::xxx:role/KarpenterInstanceNodeRole
      username: system:node:{{EC2PrivateDNSName}}

 

kubectl edit cm aws-auth -n kube-system

apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::xxx:role/xxx-eks-node-group
      username: system:node:{{EC2PrivateDNSName}}
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::xxx:role/KarpenterInstanceNodeRole
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::xxx:user/xxx@xxx.com
      username: xxx@xxx.com

... 중간 생략 ...

 

 


[ karpenter 설치 ]

 

 

1. karpenter 설치

 

 

설치 변수

  • ${KARPENTER_IAM_ROLE_ARN}
    • 위에서 생성한 KarpenterControllerRole 입력
    • arn:aws:iam::xxx:role/KarpenterControllerRole
  • ${CLUSTER_ENDPOINT}
    • clustern endpoint 정보
    • aws eks describe-cluster --name ${cluster_name} --query "cluster.endpoint" --output text
  • KarpenterNodeInstanceProfile-${CLUSTER_NAME}
    • KarpenterNodeInstanceProfile 정보 입력
helm upgrade --install --namespace karpenter --create-namespace \
  karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version v0.27.1 \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
  --set settings.aws.clusterName=${CLUSTER_NAME} \
  --set settings.aws.clusterEndpoint=${CLUSTER_ENDPOINT} \
  --set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
  --set nodeSelector.nodegroupname=infra-ng 
#  --set settings.aws.interruptionQueueName=${CLUSTER_NAME} \

 

 

2. Provisioner 설치

 

  • karpenter-provisioner.yaml 파일 생성
vi karpenter-provisioner.yaml

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: karpenter-provisioner
spec:    
  requirements:
    - key: node.kubernetes.io/instance-type
      operator: In
      values: ["t2.small"]  
    - key: "topology.kubernetes.io/zone"
      operator: In
      values: ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"]      
    - key: karpenter.sh/capacity-type
      operator: In
      values: ["on-demand"]
    - key: nodegroupname
      operator: In
      values:
      - test-ng      
  limits:
    resources:
      cpu: "500"
      memory: 1000Gi
  ttlSecondsAfterEmpty: 30
  labels:
    role: ops
    provision: karpenter
  provider:
    securityGroupSelector:
      karpenter.sh/discovery: ${cluster-name}
    subnetSelector:
      karpenter.sh/discovery: ${cluster-name}
    tags:
      Name: karpenter.sh/provisioner-name/karpenter-provisioner
      karpenter.sh/discovery: ${cluster-name}

 

  • karpenter-provisioner.yaml 배포
kubectl apply -f karpenter-provisioner.yaml

 

 


[ karpenter 테스트 ]

 

1. Pod 생성

  • php-apache pod 생성
  • nodeSelector을 "nodegroupname: test-ng" 설정
vi php-apache.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache-dw
spec:
  selector:
    matchLabels:
      run: php-apache-dw
  template:
    metadata:
      labels:
        run: php-apache-dw
    spec:
      containers:
      - name: php-apache-dw
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m
      nodeSelector:
        nodegroupname: test-ng
        
        
# php-apache 배포
kubectl apply -f php-apache.yaml

 

 

2. Pod Pending

  • "nodegroupname: test-ng" node가 존재하지 않아서 Pending 상태

  • 에러 메세지
    •   Warning  FailedScheduling  29m (x4 over 33m)     default-scheduler  0/14 nodes are available: 14 node(s) didn't match Pod's node affinity/selector.

 

 

3. Node 생성

 

  • karpenter가 node 생성 프로세스
    • karpenter가 Pending POD를 감지
    • Pending POD가 provisioner에 설정한 내용가 일치하면 Node 생성
    • Node NotReady 상태
    • Node Ready 상태
    • POD가 Node에 배포됨
    • node 생성 및 pod 배포까지 약 2분 30초 소요
 
  • karpenter log 메세지

INFO controller.provisioner found provisionable pod(s) {"commit": "7131be2-dirty", "pods": 1}

 

INFO controller.provisioner computed new machine(s) to fit pod(s) {"commit": "7131be2-dirty", "machines": 1, "pods": 1}

 

INFO controller.provisioner launching machine with 1 pods requesting {"cpu":"355m","memory":"120Mi","pods":"7"} from types t2.small {"commit": "7131be2-dirty", "provisioner": "karpenter-provisioner"}

 
INFO controller.provisioner.cloudprovider launched new instance {"commit": "7131be2-dirty", "provisioner": "karpenter-provisioner", "id": "i-057d88411cd946bd4", "hostname": "ip-10-223-69-192.ap-southeast-1.compute.internal", "instance-type": "t2.small", "zone": "ap-southeast-1a", "capacity-type": "on-demand"}


 
4. POD Running
  • Karpenter가 Node 증설 후 POD가 배포됨

 

  • pod log 메세지

  Normal   Nominated         12m                 karpenter          Pod should schedule on node: ip-10-223-70-189.ap-southeast-1.compute.internal

 

 

5. Node 축소

 

  • POD를 삭제하면 karpenter가 생성한 node에 배포된 pod가 없어서 자동으로 축소됨

 

  • karpenter log 메세지
INFO controller.deprovisioning deprovisioning via emptiness delete, terminating 1 machines ip-10-223-72-254.ap-southeast-1.compute.internal/t2.small/on-demand {"commit": "7131be2-dirty"}
 
INFO controller.termination cordoned node {"commit": "7131be2-dirty", "node": "ip-10-223-72-254.ap-southeast-1.compute.internal"}
 
INFO controller.termination deleted node {"commit": "7131be2-dirty", "node": "ip-10-223-72-254.ap-southeast-1.compute.internal"}

Provisioner 옵션 

 

[ Provisioner - spec.requirements 옵션 ]

- 옵션 링크 : https://karpenter.sh/v0.27.1/concepts/provisioners/

key 설명
node.kubernetes.io/instance-type AWS Node Type 설정하는 옵션
topology.kubernetes.io/zone a,b,c zone 선택하는 옵션
karpenter.sh/capacity-type on-demand, spot 옵션 선택
nodegroupname NodeSelector 내용과 일치했을 때 생성하는 옵션

 

 
[ Provisioner - 기타 옵션 ]
key 설명
spec.provider.securityGroupSelector AWS NODE 생성할 때 SG설정 옵션
(values에 지정한 tag 설정된 SG그룹)
spec.provider.subnetSelector AWS NODE 생성할 때 subnet 설정 옵션
(values에 지정한 tag 설정된 subnet)
spec.provider.tags AWS NODE 생성할 때 TAG 내용 추가
(karpenter가 생성한 EC2 TAG NAME 설정)
spec.labels AWS NODE 생성할 때 labels 내용 추가
spec.annotations AWS NODE 생성할 때 annotations 내용 추가
spec.taints AWS NODE 생성할 때 taints 내용 추가
spec.ttlSecondsAfterEmpty Karpenter가 생성한 Node가 사용하지 않으면 Node를 축소합니다.
값은 초 단위 설정
생략시 node 축소를 하지 않습니다.
spec.ttlSecondsUntilExpired Karpenter가 생성한 Node 만료일을 설정합니다.
만료가되면 Node가 삭제됩니다.
값은 초 단위 설정
spec.providerRef Node Template을 사용해서 더 다양한 옵션을 할때 사용한다.
https://karpenter.sh/v0.27.1/concepts/node-templates/
spec.limits.resources.cpu
spec.limits.resources.memory
Karpenter가 AWS Node를 무한대로 생성하지 못하도록 제한한다.
(AWS Node 생성할 수 있는 개수 limit(제한) 설정합니다.)

 

 

 

 

반응형

'인프라 > 시스템 구축' 카테고리의 다른 글

[ istio ] Install  (0) 2023.04.13
[ istio ] istio 이란?  (0) 2023.04.13
[ EKS ] ClusterAutoscaler (CA)  (0) 2023.04.06
[ EKS ] VPA  (0) 2023.04.06
[ EKS ] HPA  (0) 2023.04.04