지속
적으로 신규 pod의 상태
를 확인합니다.Worker node
의 배포/삭제도 직접 수행
합니다.kube-scheduler를 대신
하여 pod를 특정 worker 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 대비 단순한 구조
로 훨씬 빠르게
처리 가능합니다.
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}
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
eksctl create iamserviceaccout \
--cluster $CLUSTER_NAEM --name karpenter --namespace karpenter \
--attach-policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/KarpenterControllerPolicy-$CLUSTER_NAME \
--approve
aws eks --region ap-northeast-2 update-kubeconfig --name recap-karpenter-demo
kubectl get ns
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"}}'
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
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