Karpenter

강재민·2023년 2월 1일
1

Kubernetes

목록 보기
21/29

Karpenter의 Node 확장 방식

  • Karpenter는 지속적으로 신규 pod의 상태를 확인합니다.
  • 필요하다면 Worker node의 배포/삭제도 직접 수행합니다.
  • kube-scheduler를 대신하여 pod를 특정 worker node 쪽으로 바인딩 되도록 요청도 수행합니다.


Karpenter node 자동확장 시나리오

Worker node에 pod는 가득 차 있고 더이상 pod를 배포할 수 있는 가용 공간이 없는 상태를 가정해봅시다.
이때, 신규 pod 생성 요청이 왔습니다.

EKS cluster의 kube-scheduler는 신규 pod를 배치할 적정 worker node를 선정합니다.
이때 다양한 label정보를 통해 pod가 배치될 대상 worker node를 filtering한 후,
최종으로 pod 가 자리잡을 node를 선정하게 됩니다.
하지만, 이 경우에는 적당한 worker node 선정에 실패하게 됩니다.
worker node 가용 리소스가 없기 때문입니다

이때 karpenter 또한 pod 의 상태를 확인합니다.
pod 가 Worker node 를 할당 받지 못한 unschedulable 상태가 지속되는 것이 확인 되면,
karpenter 는 신규 Worker node 배포를 결정하게 됩니다.

신규 Worker node를 생성할 때 어떤 instance type을 선택하여 배포 할 것인지(Spot 사용 여부 등) 은
Karpenter 에서 제공되는 Custom Resource인 Provisioner에 의해서 결정됩니다.
모든 Worker node는 karpenter에 의해 lifecycle이 결정됩니다.

신규 Worker node가 배포된 이후 EKS Cluster에서 해당 node가 가용상태(Ready) 가 되면,
karpenter는 직접 pod 를 신규 worker node에 배포될 수 있도록 바인딩 요청을 합니다.

Auto Scaling 발생시 일어나는 많은 부분을 Karpenter에서 직접 처리하여
기존 AutoScaler 대비 단순한 구조로 훨씬 빠르게 처리 가능합니다.


Hands-On Lab with EKS


클러스터 생성

export CLUSTER_NAME=recap-karpenter-demo
export AWS_DEFAULT_REGION=ap-northeast-2
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo ${AWS_ACCOUNT_ID}

vi cluster.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadate:
  name: recap-karpenter-demo
  region: ap-northeast-2
  version: "1.21"
managedNodeGroups:
  - instanceType: m5.large
    amiFamily: AmazonLinux2
    name: recap-karpenter-demo-ng
    desiredCapacity: 1
    minSize: 1
    maxSize: 10
availabilityZones:
  - ap-northeast-2a
  - ap-northeast-2b
  - ap-northeast-2c
  - ap-northeast-2d
iam:
  withOIDC: true
eksctl create cluster -f cluster.yaml

서브넷 태그 설정

SUBNET_IDS=$(aws cloudformation describe-stacks \
  --stack-name eksctl-${CLUSTER_NAME}-cluster \
  --query 'Stacks[].Outputs[?OutputKey==`SubnetsPrivate`].OutputValue' \
  --output text)
echo ${SUBNET_IDS}
aws ec2 create-tags \
  --resources $(echo $SUBNET_IDS | tr ',' '\n') \
  --tags Key="kubernetes.io/cluster/${CLUSTER_NAME}",Value=
TEMPOUT=$(mktemp)
curl -fsSL https://karpenter.sh/docks/getting-started/cloudformation.yaml > $TEMPOUT \
&& aws cloudformation deploy \
  --stack-name Karpenter-${CLUSTER_NAME} \
  --template-file ${TEMPOUT} \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides ClousterName=${CLUSTER_NAME}

Cluster 접근 권한 부여

eksctl create iamidentitymapping \
  --username system:node:{{EC2PrivateDNSName}} \
  --cluster ${CLUSTER_NAME} \
  --arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME} \
  --group system:bootstrappers \
  --group system:nodes

Karpenter에게 IAM Role 부여

eksctl create iamserviceaccout \
--cluster $CLUSTER_NAEM --name karpenter --namespace karpenter \
--attach-policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/KarpenterControllerPolicy-$CLUSTER_NAME \
--approve

kubectl 설정

aws eks --region ap-northeast-2 update-kubeconfig --name recap-karpenter-demo
kubectl get ns

helm 차트를 활용한 karpenter 설치

helm repo add karpenter https://charts.karpenter.sh
helm repo update karpenter
helm upgrade --install karpenter karpenter/karpenter --namespace karpenter \
--create-namespace --set serviceAccount.create=false --version 0.5.3 \
--set controller.clusterName=${CLUSTER_NAME} \
--set controller.clusterEndpoint=$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output json) \
--wait
kubectl get deploy -n karpenter
kubectl get po -n karpenter
kubectl path configmap config-logging -n karpenter --patch '{"data":{"loglevel.controller":"debug"}}'

karpenter privisioner 배포

vi provisioner.yaml

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
spec:
  requirements:
    - key: karpenter.sh/capacity-type
      operator: In
      values: ["spot"]
limits:
  resoutces:
    cpu: 1000
provider:
  instanceProfile: KarpenterNodeInstanceProfile-recap-karpenter-demo
ttlSecondsAfterEmpty: 30
kubectl apply -f provisioner.yaml
kubectl get provisioner -n karpenter

karpenter를 활용한 node autoscaling 테스트

vi pause.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 0
  selector:
    matchLabels:
      app: inflate
  template:
    metadate:
      labels:
        app: inflate
    spec:
      terminateionGracePeriodSeconds: 0
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.2
          resources:
            requests:
              cpu: 1
kubectl get deploy
kubectl scale deployment inflate --replicas 5
kubectl get deploy
NAEM	READY	UP-TO-DATE	AVAILABE	AGE
inflate	0/5		5			0			45s
kubectl get po
kubectl describe pod {{pod이름}}
Events:
  Type		Reason				Age					From				Message
  ----		-----				----				----				-------
  Warning	FailedScheduling	25s (x2 over 26s)	default-scheduler	0/1 nodes are available: 1 Insufficient cpu.
kuebectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -i name)
2023-02-01T21:34:36.019Z	DEBUG	controller.provisioning Created launch template, Karpenter
-recap-karpenter-demo-2173619431287057541		{"commit": "5047f3c", "provisioner": "default"}
2023-02-01T21:34:39.030Z	INFO	controller.provisioning Launched instance: i-025e6b55e9096
968d, hostname: ip-192-168-86-90.ec2.internal, type: c5d.2xlarge, zone: ap-northeast-2a, 
capacityType: spot	{"commit": "5047f3c", "provisioner": "default"}
kubectl get no
NAME							STATUS	ROLES	AGE		VERSION
ip-192-168-60-60.ec2.internal	Ready	<none>	44m		v1.21.5-eks-bc4871b
ip-192-168-86-90.ec2.internal	Ready	<none>	106s	v1.21.5-eks-bc4871b
kubectl get po
NAME						READY	STATUS		RESTARTS	AGE
inflate-6b88c9fb68-4sqv9	1/1		Running		0			117s
inflate-6b88c9fb68-f9sv6	1/1		Running		0			117s
inflate-6b88c9fb68-gsdj2	1/1		Running		0			117s
inflate-6b88c9fb68-jm9ln	1/1		Running		0			117s
inflate-6b88c9fb68-q65v4	1/1		Running		0			117s
kubectl delete deployment inflate
kubectl get po
No resources found in default namespace.
kubectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -o name)
2023-02-01T21:37:34.616Z	INFO	controller.node Triggering termination after 30s for empty
node	{"commit": "5047f3c", "node": "ip-192-168-86-90.ec2.internal"}
2023-02-01T21:37:34.647Z	INFO	controller.termination	Cordoned node	{"commit": "5047f3
c", "node": "ip-192-168-86-90.ec2.internal"}
2023-02-01T21:37:34.873Z	INFO	controller.termination	Deleted node	{"commit": "5047f3
c", "node": "ip-192-168-86-90.ec2.internal"}
kubectl get no
NAME							STATUS	ROLES	AGE		VERSION
ip-192-168-60-60.ec2.internal	Ready	<none>	44m		v1.21.5-eks-bc4871b

0개의 댓글