TheKoguryo's 기술 블로그

 Version 2024.04.01

1.3.3 Cluster Autoscaler

Kubernetes Cluster Autoscaler는

  • 리소스가 요구하는 자원이 있는 Pod에 대해, Pod에게 할당할 자원이 부족하여 Pod를 스케줄할 수 없는 경우, 해당 노드 풀에 Worker Node를 추가합니다.
  • 장시간 동안 Worker Node의 활용도가 낮고 Pod를 다른 노드에 배치할 수 있는 경우, 노드 풀에서 Worker Node를 제거합니다.

Kubernetes Cluster Autoscaler는 매뉴얼하게 직접 설치하거나, Cluster Add-On으로 설치할 수 있습니다. 여기서는 직접 설치하는 것을 기준합니다.

Step 1: Cluster Autoscaler가 노드 풀에 접근할 수 있도록, Instance Principal 설정하기

Cluster Autoscaler가 필요한 OCI 자원을 관리할 수 있도록 권한을 부여합니다. Instance Principal 또는 Workload Identity Principal을 사용할 수 있습니다. Basic Cluster에서 사용할 수 있는 Instance Principal을 여기서는 편의상 사용합니다.

  1. OCI 콘솔에 로그인합니다.

  2. 좌측 상단 햄버거 메뉴에서 Identity & Security > Identity > Compartments로 이동합니다.

  3. OKE 클러스터가 있는 Compartment의 OCID를 확인합니다.

  4. 좌측 Dynamic Group 메뉴로 이동하여 아래 규칙을 가진 Dynamic Group을 만듭니다.

    • Name: 예, oke-cluster-autoscaler-dyn-grp
    instance.compartment.id = '<compartment-ocid>'
    
  5. 좌측 상단 햄버거 메뉴에서 Identity & Security > Identity > Policies로 이동합니다.

  6. 아래 규칙을 가진 Policy를 만듭니다

    • Name: 예, oke-cluster-autoscaler-dyn-grp-policy
    • dynamic-group-name: 앞서 만든 dynamic group 이름, 예, oke-cluster-autoscaler-dyn-grp
    • compartment-name: 대상 OKE Cluster가 위치한 compartment 이름
    Allow dynamic-group <dynamic-group-name> to manage cluster-node-pools in compartment <compartment-name>
    Allow dynamic-group <dynamic-group-name> to manage instance-family in compartment <compartment-name>
    Allow dynamic-group <dynamic-group-name> to use subnets in compartment <compartment-name>
    Allow dynamic-group <dynamic-group-name> to read virtual-network-family in compartment <compartment-name>
    Allow dynamic-group <dynamic-group-name> to use vnics in compartment <compartment-name>
    Allow dynamic-group <dynamic-group-name> to inspect compartments in compartment <compartment-name>
    
Step 2: Cluster Autoscaler 설정파일 설정하기
  1. 설정 파일 샘플을 다운로드 받습니다.

    wget https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/oci/examples/oci-nodepool-cluster-autoscaler-w-principals.yaml -O cluster-autoscaler.yaml
    
  2. 설정 파일 변경

    ...
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: cluster-autoscaler
      namespace: kube-system
      labels:
        app: cluster-autoscaler
    spec:
      ...
      template:
        metadata:
          ...
        spec:
          serviceAccountName: cluster-autoscaler
          containers:
            - image: iad.ocir.io/oracle/oci-cluster-autoscaler:{{ image tag }}
              name: cluster-autoscaler
              resources:
                limits:
                  cpu: 100m
                  memory: 300Mi
                requests:
                  cpu: 100m
                  memory: 300Mi
              command:
                - ./cluster-autoscaler
                - --v=4
                - --stderrthreshold=info
                - --cloud-provider=oci
                - --max-node-provision-time=25m
                - --nodes=1:5:{{ node pool ocid 1 }}
                - --nodes=1:5:{{ node pool ocid 2 }}
                - --scale-down-delay-after-add=10m
                - --scale-down-unneeded-time=10m
                - --unremovable-node-recheck-timeout=5m
                - --balance-similar-node-groups
                - --balancing-ignore-label=displayName
                - --balancing-ignore-label=hostname
                - --balancing-ignore-label=internal_addr
                - --balancing-ignore-label=oci.oraclecloud.com/fault-domain
              imagePullPolicy: "Always"
              env:
              - name: OKE_USE_INSTANCE_PRINCIPAL
                value: "true"
              - name: OCI_SDK_APPEND_USER_AGENT
                value: "oci-oke-cluster-autoscaler"
    
    • - --cloud-provider=oci를 OKE 클러스터 버전이 1.27이 아닌, 1.26, 1.25, 또는 1.24인 경우 oci-oke로 변경합니다.

      - --cloud-provider=oci-oke
      
    • - image: iad.ocir.io/oracle/oci-cluster-autoscaler:{{ image tag }}

      Frankfurt, London, Ashburn, Phoenix 리전에 있는 이미지 중에서 가까운 곳에 있는 이미지를 사용하거나, 테넌시 OCIR에 별도로 push하여 사용합니다. 여기서는 Ashburn 리전의 이미지를 사용하겠습니다.

      Image LocationKubernetes VersionImage Path
      US East (Ashburn)Kubernetes 1.25iad.ocir.io/oracle/oci-cluster-autoscaler:1.25.0-6
      US East (Ashburn)Kubernetes 1.26iad.ocir.io/oracle/oci-cluster-autoscaler:1.26.2-11
      US East (Ashburn)Kubernetes 1.27iad.ocir.io/oracle/oci-cluster-autoscaler:1.27.2-9
      US East (Ashburn)Kubernetes 1.28iad.ocir.io/oracle/oci-cluster-autoscaler:1.28.0-5
    • - --nodes=1:5:{{ node pool ocid 1 }}

      포맷은 아래와 같습니다. nodepool-ocid에 Cluster Autoscaler가 관리할 Node Pool의 OCID를 입력합니다.

      --nodes=<min-nodes>:<max-nodes>:<nodepool-ocid>
      
    • - --nodes=1:5:{{ node pool ocid 2 }}

      관리할 두번째 Node Pool이 있는 경우, 설정해 사용하고 아닌 경우 해당 줄은 삭제합니다. 셋 이상인 Node Pool을 관리자 하는 경우 같은 형식으로 추가합니다.

  3. 설정파일을 저장합니다.

Step 3: OKE 클러스터에 Cluster Autoscaler 배포하기
  1. Kubernetes Cluster Autoscaler을 OKE 클러스터에 배포합니다.

    kubectl apply -f cluster-autoscaler.yaml
    
  2. 배포결과를 확인하기 위해 로그를 확인합니다.

    kubectl -n kube-system logs -f deployment.apps/cluster-autoscaler
    
    • 배포가 성공하면 다음과 같은 로그가 보입니다. Pod 3개중 하나가 리더가 되고, lock을 잡습니다. 관련 로그가 지속적으로 보이는 것을 볼 수 있습니다.

      $ kubectl -n kube-system logs -f deployment.apps/cluster-autoscaler
      ...
      Found 3 pods, using pod/cluster-autoscaler-544c49444b-45gdt
      ...
      I0128 12:35:14.650472       1 main.go:474] Cluster Autoscaler 1.26.2
      I0128 12:35:14.668348       1 leaderelection.go:248] attempting to acquire leader lease kube-system/cluster-autoscaler...
      ...
      I0128 12:35:32.071336       1 leaderelection.go:352] lock is held by cluster-autoscaler-544c49444b-jjzk7 and has not yet expired
      I0128 12:35:32.071357       1 leaderelection.go:253] failed to acquire lease kube-system/cluster-autoscaler
      I0128 12:35:35.723678       1 leaderelection.go:352] lock is held by cluster-autoscaler-544c49444b-jjzk7 and has not yet expired
      I0128 12:35:35.724233       1 leaderelection.go:253] failed to acquire lease kube-system/cluster-autoscaler
      ...
      
  3. Kubernetes Cluster Autoscaler Pod 세 개 중 어느 Pod가 실제 동작하고 있는 지 확인해 봅니다.

    $ kubectl get pod -l app=cluster-autoscaler -n kube-system
    NAME                                  READY   STATUS    RESTARTS   AGE
    cluster-autoscaler-544c49444b-45gdt   1/1     Running   0          7m55s
    cluster-autoscaler-544c49444b-gzx64   1/1     Running   0          7m55s
    cluster-autoscaler-544c49444b-jjzk7   1/1     Running   0          7m55s
    
    $ kubectl -n kube-system get lease cluster-autoscaler
    NAME                 HOLDER                                AGE
    cluster-autoscaler   cluster-autoscaler-544c49444b-jjzk7   34m
    
  4. Kubernetes Cluster Autoscaler의 상태를 확인하기 위해 Config Map을 확인해 봅니다.

    kubectl -n kube-system get cm cluster-autoscaler-status -oyaml
    
Step 4: 클러스터 오토스케일링 동작 확인해 보기
  1. 현재 Worker Node 상태를 확인합니다.

    $ kubectl get nodes
    NAME          STATUS   ROLES   AGE    VERSION
    10.0.10.158   Ready    node    10d    v1.26.7
    10.0.10.42    Ready    node    2d5h   v1.26.7
    10.0.10.43    Ready    node    2d5h   v1.26.7
    
  2. 샘플 애플리케이션 배포 파일 예시입니다.

    requests.cpu를 기본 200 밀리코어까지 사용할 수 있게 지정하였습니다. 0.2 코어로 설정한 예시

    # nginx.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
    spec:
      selector:
        matchLabels:
          app: nginx
      replicas: 2
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
            resources:
              requests:
                cpu: 200m            
    
  3. 샘플을 배포합니다.

    kubectl apply -f nginx.yaml
    
  4. Pod의 수를 늘립니다.

    kubectl scale deployment nginx-deployment --replicas=40
    
  5. Deployment 상태를 확인합니다. 배포하다가, 자원을 다 쓰고 더 이상 Pod를 생성하지 못하고 멈춰있게 됩니다.

    $ kubectl get deployment nginx-deployment --watch
    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   20/40   40           20          60s
    nginx-deployment   22/40   40           22          60s
    nginx-deployment   23/40   40           23          61s
    nginx-deployment   24/40   40           24          61s
    
  6. 이벤트 로그를 확인해 보면, CPU 부족으로 Pod 스케줄링에 실패한 것을 볼 수 있습니다. 이로 인해 Cluster Scale 이벤트가 발생하고, 노드가 3개에서 5개로 늘어납니다.

    $ kubectl get events --sort-by=.metadata.creationTimestamp
    ...
    5m1s        Warning   FailedScheduling          pod/nginx-deployment-694bc9bdb8-bgqww                                           0/3 nodes are available: 3 Insufficient cpu. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod..
    ...
    40s         Normal    TriggeredScaleUp    pod/nginx-deployment-694bc9bdb8-bgqww                                           pod triggered scale-up: [{ocid1.nodepool.oc1.ap-chuncheon-1.aaaaaaaabtavqjthmpeivjj5dj7i4yttl74y7ncnvoxgxn7kqna6xzvlolbq 3->5 (max: 5)}]
    
    $ kubectl get nodes 
    NAME          STATUS     ROLES    AGE    VERSION
    10.0.10.132   NotReady   <none>   5s     v1.26.7
    10.0.10.158   Ready      node     10d    v1.26.7
    10.0.10.167   NotReady   node     13s    v1.26.7
    10.0.10.42    Ready      node     2d5h   v1.26.7
    10.0.10.43    Ready      node     2d5h   v1.26.7
    
  7. 확장된 Worker Node가 Ready 상태가 되면 나머지 Pod에 대한 스케줄링이 진행됩니다.

    $ kubectl get nodes 
    NAME          STATUS   ROLES   AGE     VERSION
    10.0.10.132   Ready    node    3m52s   v1.26.7
    10.0.10.158   Ready    node    10d     v1.26.7
    10.0.10.167   Ready    node    4m      v1.26.7
    10.0.10.42    Ready    node    2d5h    v1.26.7
    10.0.10.43    Ready    node    2d5h    v1.26.7
    
    $ kubectl get deployment nginx-deployment --watch
    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   24/40   40           24          61s
    ...
    ...
    ...
    nginx-deployment   25/40   40           25          7m18s
    ...
    nginx-deployment   40/40   40           40          7m51s
    
Step 5: 정리 및 Scale In 확인하기
  1. 배포한 샘플 애플리케이션을 삭제합니다.

    kubectl delete deployment nginx-deployment
    
  2. scale-down-unneeded-time=10m 설정값에 따라 10분 뒤에 ScaleDown 이벤트가 발생한 것을 확인할 수 있습니다.

    $ kubectl get events --sort-by=.metadata.creationTimestamp -A
    ...
    default         12m         Normal    Killing                   pod/nginx-deployment-694bc9bdb8-t44jp                                           Stopping container nginx
    default         105s        Normal    ScaleDown                 node/10.0.10.167                                                                marked the node as toBeDeleted/unschedulable
    ...
    default         105s        Normal    ScaleDown                 node/10.0.10.132                                                                marked the node as toBeDeleted/unschedulable
    ...
    default         89s         Normal    NodeNotSchedulable        node/10.0.10.167                                                                Node 10.0.10.167 status is now: NodeNotSchedulable
    ...
    default         82s         Normal    ScaleDown                 node/10.0.10.132                                                                marked the node as toBeDeleted/unschedulable
    
  3. 노드의 상태를 조회해봅니다. 2개 노드가 스케줄링에서 제외되었습니다.

    $ kubectl get nodes
    NAME          STATUS                        ROLES   AGE    VERSION
    10.0.10.132   Ready,SchedulingDisabled      node    22m    v1.26.7
    10.0.10.158   Ready                         node    10d    v1.26.7
    10.0.10.167   NotReady,SchedulingDisabled   node    22m    v1.26.7
    10.0.10.42    Ready                         node    2d5h   v1.26.7
    10.0.10.43    Ready                         node    2d5h   v1.26.7
    
  4. 이후 해당 노드가 삭제되고 원래대로 3개의 노드로 남았습니다.

    Cluster Autoscaler 배포시 <min-nodes>을 1로 설정해도, Node Pool 생성시 지정한 수가 더 큰 경우, 그 수 만큼은 유지하는 것으로 보입니다.

    $ kubectl get nodes
    NAME          STATUS   ROLES   AGE    VERSION
    10.0.10.158   Ready    node    10d    v1.26.7
    10.0.10.42    Ready    node    2d6h   v1.26.7
    10.0.10.43    Ready    node    2d6h   v1.26.7
    


이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.

Last updated on 28 Jan 2024