이번 주는 EKS를 조금 더 유연하게 사용할 수 있는 scaling 방법에 대해 알아보았습니다.
EKS에서 scaling 대상은 node와 pod가 있습니다. 각각 어떤 솔루션이 있는지 살펴보겠습니다.
실습 시 제일 하단에 있는 '사전 설치 툴'을 설치한 뒤 진행이 필요합니다.
Node
Cluster Autoscaler
- metric server에서 제공하는 metric을 기반으로 노드그룹의 ASG 수를 조정합니다.
- pending 상태인 pod가 생기면 노드 수를 늘리고 사용량이 낮다면 노드 수를 줄입니다.
Cluster Autoscaler 설치
설치 전에 먼저 nodegroup의 ASG 최대값을 조정합니다.
$ export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].AutoScalingGroupName" --output text)
$ aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${ASG_NAME} --min-size 3 --desired-capacity 3 --max-size 4
$ aws autoscaling describe-auto-scaling-groups --query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize,DesiredCapacity]" --output table
-----------------------------------------------------------------
| DescribeAutoScalingGroups |
+------------------------------------------------+----+----+----+
| eks-ng1-bcc426b3-fc49-263c-9632-9f264a271b6d | 3 | 3 | 4 |
+------------------------------------------------+----+----+----+
이제 Cluster Autoscaler를 설치해보겠습니다.
$ curl -s -O https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
$ sed -i "s/<YOUR CLUSTER NAME>/$CLUSTER_NAME/g" cluster-autoscaler-autodiscover.yaml
$ kubectl apply -f cluster-autoscaler-autodiscover.yaml
serviceaccount/cluster-autoscaler created
clusterrole.rbac.authorization.k8s.io/cluster-autoscaler created
role.rbac.authorization.k8s.io/cluster-autoscaler created
clusterrolebinding.rbac.authorization.k8s.io/cluster-autoscaler created
rolebinding.rbac.authorization.k8s.io/cluster-autoscaler created
deployment.apps/cluster-autoscaler created
Cluster Autoscaler의 정상 동작 여부는 Over-provisioning을 설정하며 확인해보겠습니다.
Over-provisioning
- Cluster Autoscaler를 사용할 때 노드가 생성되는 시간이 있어 신규 pod는 pending 상태에 머물게 됩니다. 이때 더 빠르게 pod 배포가 필요할 때 미리 노드를 띄워두는 방법을 사용할 수 있습니다.
- PriorityClass 라는 객체를 활용해서 pod에 우선순위를 정하고 우선순위가 낮은 pod를 미리 만들어 노드를 확보하는 방식입니다.
- 실제 리소스가 부족할 때 우선 순위가 낮은 pod가 evict되고 우선 순위가 높은 pod가 배포됩니다.
Over-provisioning 실습
먼저 우선순위가 낮은 PriorityClass와 deployment를 배포합니다.
Cluster Autoscaler의 동작도 확인하기 위해 pod가 노드의 메모리를 많이 차지할 수 있게 설정했습니다.
$ cat <<EOT > low-priority-class.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: pause-pods
value: -1
globalDefault: false
description: "Priority class used by pause-pods for overprovisioning."
EOT
$ kubectl apply -f low-priority-class.yaml
$ cat <<EOT > low-priority-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pause-pods
spec:
replicas: 3
selector:
matchLabels:
run: pause-pods
template:
metadata:
labels:
run: pause-pods
spec:
priorityClassName: pause-pods
containers:
- name: reserve-resources
image: registry.k8s.io/pause
resources:
requests:
memory: "3Gi" ## 사용 중인 nodegroup ec2 스펙에 맞게 조정
EOT
$ kubectl apply -f low-priority-deployment.yaml
pod를 배포한 후 노드의 자원이 부족한 것을 확인할 수 있습니다.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
pause-pods-69f8b747b4-6m5bt 1/1 Running 0 4s
pause-pods-69f8b747b4-hnwpr 1/1 Running 0 4s
pause-pods-69f8b747b4-szmjq 0/1 Pending 0 4s
$ ./eks-node-viewer --resources cpu,memory
ip-192-168-1-43.ap-northeast-2.compute.internal cpu ███████████░░░░░░░░░░░░░░░░░░░░░░░░ 32% (10 pods) t3.medium/$0.0520 On-Demand - Ready
memory ██████████░░░░░░░░░░░░░░░░░░░░░░░░░ 28%
ip-192-168-3-11.ap-northeast-2.compute.internal cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12% (8 pods) t3.medium/$0.0520 On-Demand - Ready
memory █████████████████████████████████░░ 95%
ip-192-168-2-62.ap-northeast-2.compute.internal cpu ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 6% (4 pods) t3.medium/$0.0520 On-Demand - Ready
memory ████████████████████████████████░░░ 93%
시간이 조금 지나면 노드가 3개에서 4개로 증가하고 pod도 배포되는 것을 볼 수 있습니다.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
pause-pods-69f8b747b4-6m5bt 1/1 Running 0 82s
pause-pods-69f8b747b4-hnwpr 1/1 Running 0 82s
pause-pods-69f8b747b4-szmjq 1/1 Running 0 82s
$ ./eks-node-viewer --resources cpu,memory
ip-192-168-1-43.ap-northeast-2.compute.internal cpu ███████████░░░░░░░░░░░░░░░░░░░░░░░░ 32% (10 pods) t3.medium/$0.0520 On-Demand - Ready
memory ██████████░░░░░░░░░░░░░░░░░░░░░░░░░ 28%
ip-192-168-3-11.ap-northeast-2.compute.internal cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12% (8 pods) t3.medium/$0.0520 On-Demand - Ready
memory █████████████████████████████████░░ 95%
ip-192-168-2-62.ap-northeast-2.compute.internal cpu ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 6% (4 pods) t3.medium/$0.0520 On-Demand - Ready
memory ████████████████████████████████░░░ 93%
ip-192-168-3-244.ap-northeast-2.compute.internal cpu ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 6% (4 pods) t3.medium/$0.0520 On-Demand - Ready
memory ████████████████████████████████░░░ 93%
그 다음 우선순위가 높은 PriorityClass와 deployment를 배포합니다.
$ cat <<EOT > high-priority-class.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "Priority class used for high priority Pods only."
EOT
$ kubectl apply -f high-priority-class.yaml
$ cat <<EOT > high-priority-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
resources:
requests:
memory: "500Mi"
ports:
- containerPort: 80
priorityClassName: high-priority # Priority Class specified
EOT
$ kubectl apply -f high-priority-deployment.yaml
노드의 자원이 부족해지는 상황을 만들기 위해 deployment의 replica 수를 조정합니다.
이미 ASG의 최대값과 동일한 노드 수가 실행중이기 때문에 노드의 자원이 부족할 때 우선순위가 낮은 pod가 evict되는 것을 예상할 수 있습니다.
$ kubectl scale deployment nginx-deployment --replicas 5
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-deployment-5c98788955-5rr2g 1/1 Running 0 19s
nginx-deployment-5c98788955-pz656 1/1 Running 0 19s
nginx-deployment-5c98788955-rppdv 1/1 Running 0 87s
nginx-deployment-5c98788955-rq4qs 1/1 Running 0 19s
nginx-deployment-5c98788955-zr8g2 1/1 Running 0 87s
pause-pods-69f8b747b4-6m5bt 1/1 Running 0 4m22s
pause-pods-69f8b747b4-hnwpr 1/1 Running 0 4m22s
pause-pods-69f8b747b4-vtxfc 0/1 Pending 0 19s
예상과 같이 기존 우선순위가 낮은 pod가 pending 상태로 변하고 우선순위가 높은 신규 pod가 생성되는 것을 볼 수 있습니다.
Pending 상태인 pod에는 아래와 같은 메세지를 확인할 수 있습니다.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal NotTriggerScaleUp 3m50s cluster-autoscaler pod didn't trigger scale-up: 1 max node group size reached
Warning FailedScheduling 21s (x4 over 3m55s) default-scheduler 0/4 nodes are available: 4 Insufficient memory. preemption: 0/4 nodes are available: 4 No preemption victims found for incoming pod.
Karpenter
- Cluster Autoscaler 보다 노드 생성 시간이 적게 걸려 최근에 많이 사용되는 autoscaler입니다.
- 다양한 노드 타입을 활용하여 현재 필요한 사용량에 맞게 비용 최적화된 노드 구성을 해줍니다.
Karpenter 실습
karpenter는 신규 클러스터를 생성하여 실습을 진행하겠습니다. ( vpc, ec2 등 기본 네트워크 설정 : cloudformation yaml)
export | egrep 'ACCOUNT|AWS_|CLUSTER' | egrep -v 'SECRET|KEY'
# 환경변수 설정
export KARPENTER_VERSION=v0.27.5
export TEMPOUT=$(mktemp)
echo $KARPENTER_VERSION $CLUSTER_NAME $AWS_DEFAULT_REGION $AWS_ACCOUNT_ID $TEMPOUT
# CloudFormation 스택으로 IAM Policy, Role, EC2 Instance Profile 생성
curl -fsSL https://karpenter.sh/"${KARPENTER_VERSION}"/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}"
클러스터 생성
eksctl create cluster -f - <<EOF
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ${CLUSTER_NAME}
region: ${AWS_DEFAULT_REGION}
version: "1.24"
tags:
karpenter.sh/discovery: ${CLUSTER_NAME}
iam:
withOIDC: true
serviceAccounts:
- metadata:
name: karpenter
namespace: karpenter
roleName: ${CLUSTER_NAME}-karpenter
attachPolicyARNs:
- arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy-${CLUSTER_NAME}
roleOnly: true
iamIdentityMappings:
- arn: "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}"
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
managedNodeGroups:
- instanceType: m5.large
amiFamily: AmazonLinux2
name: ${CLUSTER_NAME}-ng
desiredCapacity: 2
minSize: 1
maxSize: 10
iam:
withAddonPolicies:
externalDNS: true
## Optionally run on fargate
# fargateProfiles:
# - name: karpenter
# selectors:
# - namespace: karpenter
EOF
karpenter 설치
$ export CLUSTER_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output text)"
$ export KARPENTER_IAM_ROLE_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${CLUSTER_NAME}-karpenter"
$ echo $CLUSTER_ENDPOINT $KARPENTER_IAM_ROLE_ARN
$ helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter --create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=${KARPENTER_IAM_ROLE_ARN} \
--set settings.aws.clusterName=${CLUSTER_NAME} \
--set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME} \
--set settings.aws.interruptionQueueName=${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
$ kubectl get-all -n karpenter
NAME NAMESPACE AGE
configmap/config-logging karpenter 37s
configmap/karpenter-global-settings karpenter 37s
configmap/kube-root-ca.crt karpenter 37s
endpoints/karpenter karpenter 37s
pod/karpenter-6c6bdb7766-5mcpn karpenter 37s
pod/karpenter-6c6bdb7766-5vj2t karpenter 37s
secret/karpenter-cert karpenter 37s
secret/sh.helm.release.v1.karpenter.v1 karpenter 37s
serviceaccount/default karpenter 37s
serviceaccount/karpenter karpenter 37s
service/karpenter karpenter 37s
deployment.apps/karpenter karpenter 37s
replicaset.apps/karpenter-6c6bdb7766 karpenter 37s
lease.coordination.k8s.io/karpenter-leader-election karpenter 29s
endpointslice.discovery.k8s.io/karpenter-2hjxb karpenter 37s
poddisruptionbudget.policy/karpenter karpenter 37s
rolebinding.rbac.authorization.k8s.io/karpenter karpenter 37s
role.rbac.authorization.k8s.io/karpenter karpenter 37s
provisioner 생성
$ cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
limits:
resources:
cpu: 1000
providerRef:
name: default
ttlSecondsAfterEmpty: 30
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
name: default
spec:
subnetSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
securityGroupSelector:
karpenter.sh/discovery: ${CLUSTER_NAME}
EOF
$ kubectl get awsnodetemplates,provisioners
NAME AGE
awsnodetemplate.karpenter.k8s.aws/default 17s
NAME AGE
provisioner.karpenter.sh/default 17s
이제 pod를 생성하여 karpetner로 노드가 생성되는지 확인해보겠습니다.
$ 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
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
EOF
$ kubectl scale deployment inflate --replicas 5
$ kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller
...
2023-05-24T13:10:08.014Z DEBUG controller.provisioner.cloudprovider created launch template {"commit": "698f22f-dirty", "provisioner": "default", "launch-template-name": "karpenter.k8s.aws/3594526214815891012", "launch-template-id": "lt-0a274807620307b36"}
2023-05-24T13:10:10.717Z INFO controller.provisioner.cloudprovider launched instance {"commit": "698f22f-dirty", "provisioner": "default", "id": "i-00429eff0a28e5bd5", "hostname": "ip-192-168-109-187.ap-northeast-2.compute.internal", "instance-type": "c4.2xlarge", "zone": "ap-northeast-2c", "capacity-type": "spot", "capacity": {"cpu":"8","ephemeral-storage":"20Gi","memory":"14208Mi","pods":"58"}}
2023-05-24T13:06:45.504Z DEBUG controller discovered kube dns {"commit": "698f22f-dirty", "kube-dns-ip": "10.100.0.10"}
2023-05-24T13:06:45.504Z DEBUG controller discovered version {"commit": "698f22f-dirty", "version": "v0.27.5"}
2023/05/24 13:06:45 Registering 2 clients
2023/05/24 13:06:45 Registering 2 informer factories
2023/05/24 13:06:45 Registering 3 informers
2023/05/24 13:06:45 Registering 5 controllers
2023-05-24T13:06:45.505Z INFO controller Starting server {"commit": "698f22f-dirty", "path": "/metrics", "kind": "metrics", "addr": "[::]:8080"}
2023-05-24T13:06:45.505Z INFO controller Starting server {"commit": "698f22f-dirty", "kind": "health probe", "addr": "[::]:8081"}
I0524 13:06:45.606733 1 leaderelection.go:248] attempting to acquire leader lease karpenter/karpenter-leader-election...
2023-05-24T13:06:45.628Z INFO controller Starting informers... {"commit": "698f22f-dirty"}
신규 노드가 spot 타입으로 생성된 것을 볼 수 있습니다.
$ kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type,node.kubernetes.io/instance-type
NAME STATUS ROLES AGE VERSION CAPACITYTYPE CAPACITY-TYPE INSTANCE-TYPE
ip-192-168-109-187.ap-northeast-2.compute.internal Ready <none> 77s v1.24.13-eks-0a21954 spot c4.2xlarge
ip-192-168-28-174.ap-northeast-2.compute.internal Ready <none> 13m v1.24.13-eks-0a21954 ON_DEMAND m5.large
ip-192-168-76-145.ap-northeast-2.compute.internal Ready <none> 13m v1.24.13-eks-0a21954 ON_DEMAND m5.large
이번에는 fargate를 사용해서 pod를 띄워보겠습니다.
iam role 생성
$ cat << EOF > AmazonEKSFargatePodExecutionRole-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn:aws:eks:ap-northeast-2:$AWS_ACCOUNT_ID:fargateprofile/$CLUSTER_NAME/*"
}
},
"Principal": {
"Service": "eks-fargate-pods.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
$ aws iam create-role \
--role-name AmazonEKSFargatePodExecutionRole \
--assume-role-policy-document file://AmazonEKSFargatePodExecutionRole-policy.json
$ aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy \
--role-name AmazonEKSFargatePodExecutionRole
fargate profile 생성
$ export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="eksctl-$CLUSTER_NAME-cluster/SubnetPrivateAPNORTHEAST2A" --query "Subnets[0].[SubnetId]" --output text)
$ export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="eksctl-$CLUSTER_NAME-cluster/SubnetPrivateAPNORTHEAST2C" --query "Subnets[0].[SubnetId]" --output text)
$ export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="eksctl-$CLUSTER_NAME-cluster/SubnetPrivateAPNORTHEAST2D" --query "Subnets[0].[SubnetId]" --output text)
## 테스트용 namespace 생성
$ kubectl create ns karpenter-test
$ aws eks create-fargate-profile \
--fargate-profile-name karpenter-test \
--cluster-name $CLUSTER_NAME \
--pod-execution-role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/AmazonEKSFargatePodExecutionRole \
--subnets $PubSubnet1 $PubSubnet2 $PubSubnet3 \
--selectors namespace=karpenter-test
karpenter-test ns에 pod를 배포합니다.
$ cat <<EOT > pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: karpenter-test
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
EOT
$ kubectl apply -f pod.yaml
$ kubectl get po -n karpenter-test
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 3m17s
$ kubectl describe po nginx -n karpenter-test
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning LoggingDisabled 34s fargate-scheduler Disabled logging because aws-logging configmap was not found. configmap "aws-logging" not found
Normal Scheduled 1s fargate-scheduler Successfully assigned karpenter-test/nginx to fargate-ip-192-168-173-121.ap-northeast-2.compute.internal
노드를 확인해보면 fargate 노드가 생성된 것을 확인할 수 있습니다.
$ kubectl get node --label-columns=eks.amazonaws.com/capacityType,karpenter.sh/capacity-type,node.kubernetes.io/instance-type
NAME STATUS ROLES AGE VERSION CAPACITYTYPE CAPACITY-TYPE INSTANCE-TYPE
fargate-ip-192-168-173-121.ap-northeast-2.compute.internal Ready <none> 3m54s v1.24.12-eks-f4dc2c0
ip-192-168-28-174.ap-northeast-2.compute.internal Ready <none> 53m v1.24.13-eks-0a21954 ON_DEMAND m5.large
ip-192-168-76-145.ap-northeast-2.compute.internal Ready <none> 53m v1.24.13-eks-0a21954 ON_DEMAND m5.large
EKS에서 add-on으로 제공되는 coredns, LB Controller 등도 fargate에 배포가 가능합니다.
추후에 해당 실습도 진행해보도록 하겠습니다.
Pod
HPA
- metric을 기반으로 pod 수를 늘리고 줄입니다.
- Deployment, StatefulSet에 적용이 가능합니다.
KEDA
- metric이 아닌 이벤트 기반으로 scaling이 가능합니다.
- 특정 시간에 pod 수를 조정하는 것도 가능합니다.
VPA
- 필요에 따라 pod의 cpu, memory 스펙을 조정합니다.
- pod가 재생성되니 주의가 필요합니다. (pod evict)
VPA 설치
$ git clone https://github.com/kubernetes/autoscaler.git
$ cd ~/autoscaler/vertical-pod-autoscaler/
# openssl 버전 확인 및 업그레이드
$ openssl version
$ OpenSSL 1.0.2k-fips 26 Jan 2017
$ yum install openssl11 -y
$ openssl11 version
OpenSSL 1.1.1g FIPS 21 Apr 2020
# 스크립트파일내에 openssl11 수정
$ sed -i 's/openssl/openssl11/g' ~/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/gencerts.sh
# Vertical Pod Autoscaler 설치
$ ./hack/vpa-up.sh
$ kubectl get crd | grep autoscaling
verticalpodautoscalercheckpoints.autoscaling.k8s.io 2023-05-23T13:01:30Z
verticalpodautoscalers.autoscaling.k8s.io 2023-05-23T13:01:30Z
VPA 예제
예제를 아래와 같이 배포했을 때 pod가 vpa에 의해 재생성되는 것을 볼 수 있습니다.
$ cd ~/autoscaler/vertical-pod-autoscaler/
$ kubectl apply -f examples/hamster.yaml && kubectl get vpa -w
verticalpodautoscaler.autoscaling.k8s.io/hamster-vpa created
deployment.apps/hamster created
NAME MODE CPU MEM PROVIDED AGE
hamster-vpa Auto 1s
hamster-vpa Auto 587m 262144k True 9s
hamster-vpa Auto 587m 262144k True 69s
$ kubectl get deploy -o wide -w
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
hamster 2/2 2 2 28s hamster registry.k8s.io/ubuntu-slim:0.1 app=hamster
hamster 1/2 1 1 68s hamster registry.k8s.io/ubuntu-slim:0.1 app=hamster
hamster 1/2 2 1 68s hamster registry.k8s.io/ubuntu-slim:0.1 app=hamster
hamster 2/2 2 2 69s hamster registry.k8s.io/ubuntu-slim:0.1 app=hamster
위 예제에서는 pod가 순차적으로 vpa에 의해 재생성되지만 동시에 기존 pod가 종료될 가능성도 있기 때문에 추가적인 설정을 해보겠습니다.
방법 1) updatePolicy
$ vi examples/hamster.yaml
---
apiVersion: "autoscaling.k8s.io/v1"
kind: VerticalPodAutoscaler
metadata:
name: hamster-vpa
spec:
# recommenders field can be unset when using the default recommender.
# When using an alternative recommender, the alternative recommender's name
# can be specified as the following in a list.
# recommenders:
# - name: 'alternative'
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: hamster
updatePolicy:
updateMode: "Auto"
minReplicas: 1
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
memory: 50Mi
maxAllowed:
cpu: 1
memory: 500Mi
controlledResources: ["cpu", "memory"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hamster
spec:
selector:
matchLabels:
app: hamster
replicas: 2
template:
metadata:
labels:
app: hamster
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65534 # nobody
containers:
- name: hamster
image: registry.k8s.io/ubuntu-slim:0.1
resources:
requests:
cpu: 100m
memory: 50Mi
command: ["/bin/sh"]
args:
- "-c"
- "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done"
replica 수가 2개인 deployment에 대해 vpa를 설정했고, 최소 1개의 pod를 유지하기 위해 아래 설정을 추가했습니다.
updatePolicy:
updateMode: "Auto"
minReplicas: 1
방법 2) PDB
pod가 evict 될 때 최소 pod를 유지하기 위해 PDB를 사용하는 것처럼 VPA에 pdb를 같이 사용해보겠습니다.
먼저 아래와 같이 pdb를 생성합니다.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: hamster-pdb
spec:
minAvailable: 1
selector:
matchLabels:
app: hamster
그리고 기존 example/hamster.yaml 파일로 배포한 자원을 삭제하고 updatePolicy 부분을 삭제한 뒤에 다시 배포하겠습니다.
$ kubectl delete -f examples/hamster.yaml
$ vi examples/hamster.yaml
---
apiVersion: "autoscaling.k8s.io/v1"
kind: VerticalPodAutoscaler
metadata:
name: hamster-vpa
spec:
# recommenders field can be unset when using the default recommender.
# When using an alternative recommender, the alternative recommender's name
# can be specified as the following in a list.
# recommenders:
# - name: 'alternative'
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: hamster
# updatePolicy:
# updateMode: "Auto"
# minReplicas: 1
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
memory: 50Mi
maxAllowed:
cpu: 1
memory: 500Mi
controlledResources: ["cpu", "memory"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hamster
spec:
selector:
matchLabels:
app: hamster
replicas: 2
template:
metadata:
labels:
app: hamster
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65534 # nobody
containers:
- name: hamster
image: registry.k8s.io/ubuntu-slim:0.1
resources:
requests:
cpu: 100m
memory: 50Mi
command: ["/bin/sh"]
args:
- "-c"
- "while true; do timeout 0.5s yes >/dev/null; sleep 0.5s; done"
$ kubectl apply -f examples/hamster.yaml && kubectl get vpa -w
위 예제에서는 별도의 설정없이도 pod가 동시에 종료되는 현상이 없어서 updatePolicy와 PDB를 테스트하기에 적합하지는 않지만, 두 방법 모두 정상 동작하는 것을 확인했습니다.
CPA
- 클러스터 규모에 따라 pod 수를 늘릴 필요가 있는 컴포넌트들에 사용합니다.
- 노드 수에 따라 pod 수를 자동으로 증가하게 설정할 수 있습니다.
사전 설치 툴
실습 중 사전에 설치가 필요한 툴 설치 방법입니다.
EKS Node Viewer 설치 및 사용 예
$ yum install -y go
$ go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest
$ cd ~/go/bin
$ ./eks-node-viewer
3 nodes (675m/5790m) 11.7% cpu █████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ $0.156/hour | $113.880/month
9 pods (0 pending 9 running 9 bound)
ip-192-168-1-195.ap-northeast-2.compute.internal cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12% (3 pods) t3.medium/$0.0520 On
ip-192-168-2-244.ap-northeast-2.compute.internal cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12% (3 pods) t3.medium/$0.0520 On
ip-192-168-3-150.ap-northeast-2.compute.internal cpu ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12% (3 pods) t3.medium/$0.0520 On
Press any key to quit
metric server 설치
$ kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
이상으로 5주차 내용 정리를 마치겠습니다.
해당 문서에 포함되지 않은 실습은 추후에 별도 글로 정리해보겠습니다.
참고 문서
'Kubernetes > EKS Study' 카테고리의 다른 글
[6주차] EKS Security (0) | 2023.06.11 |
---|---|
[4주차] EKS Observability (0) | 2023.05.21 |
[3주차] EKS Storage (0) | 2023.05.09 |
EKS에서 볼륨 snapscheduler 설치하기 (0) | 2023.05.09 |
EKS에서 pvc로 생성한 ebs에 태그 추가하기 (0) | 2023.05.09 |