Kubernetes/EKS Study

[5주차] EKS Autoscaling

백곰곰 2023. 5. 24. 22:59
728x90
반응형

이번 주는 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주차 내용 정리를 마치겠습니다.

해당 문서에 포함되지 않은 실습은 추후에 별도 글로 정리해보겠습니다.

 

참고 문서

728x90

'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