Azure Kubernetes Service - Version Upgrade(서비스 영향 최소화하기)

개요

재직중인 회사에서는 AKS(Azure Kubernetes Service)를 통해 수많은 API들을 배포, 운영하고 있습니다. 쿠버네티스의 경우 코로나 이전에는 분기별로 신규 버전이 출시되었으나, 코로나 이후에는 4개월 텀으로 1년에 3개 버전이 출시되고 있습니다. 클라우드 3대장(Azure, AWS, GCP)에서는 자사 직원들을 통해 쿠버네티스 SIG(Special Interest Group)에 참여하여 자신들의 클라우드에 호환성을 가질수 있도록 기여합니다. 이에 쿠버네티스 버전 업그레이드에는 클라우드 공급 업체들의 호환성 개선도 포함된다고 할수있기에 주기적인 업그레이드는 필수이기도 합니다. 이에 이 포스트에서는 AKS를 사용하는 사용자를 대상으로 서비스 영향도를 최소화 하는 버전 업그레이드를 하는 방법을 소개하고자 합니다.

Azure Kubernetes Service - Upgrade

Control Plane 업그레이드

Azure Portal에 접속하여 업그레이드 대상을 보면 컨트롤 플레인(Control Plane)만 업그레이드 하는 것과 노드 풀(Node Pool)을 함께 업그레이드하는 2가지 방식이 있는 것을 확인할 수 있습니다. 여기서 업그레이드는 컨트롤 플레인만 진행합니다. 컨트롤 플레인만 진행하는 이유는 다음과 같습니다.

  • 노드 풀이 여러개이고, 노드 풀당 운영중인 노드수가 많은 경우에 업그레이드에 시간소요가 많이 발생함.
  • 특정 노드에 POD가 몰려있는 경우 업그레이드시 동시 다발적으로 다른 노드로 축출되어 서비스 영향이 발생할수 있음

신규 NodePool 추가 및 Overprovisioning 배포

컨트롤 플레인을 업그레이드가 완료된 상태에서 신규 노드 풀을 추가합니다. 신규 노드풀의 경우 버전 지정을 하지 않는 경우 컨트롤 플레인의 버전에 따라 생성되기 때문에 버전 지정에는 신경쓰지 않고 노드풀을 추가합니다.

az aks nodepool add \
--cluster-name {cluster_name} \
--name k8spool4 \ # nodepool name
--resource-group {cluster_name}-rg \
--enable-cluster-autoscaler \
--min-count 3 \
--max-count 100 \
--max-pods 60 \
--mode System \
--node-count 3 \
--node-osdisk-type Ephemeral \
--node-osdisk-size 512 \
--node-vm-size Standard_F32s_v2 \
--vnet-subnet-id {subnet_id} \
--debug

이후 노드 풀 제한이 설정된 Overprovisioning을 배포하여 노드를 확보합니다.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: overprovisioning-{nodepool #}
  namespace: default
spec:
  replicas: 7
  selector:
    matchLabels:
      run: overprovisioning-{nodepool #}
  template:
    metadata:
      labels:
        run: overprovisioning-{nodepool #}
    spec:
      priorityClassName: overprovisioning
      containers:
      - name: reserve-resources
        image: k8s.gcr.io/pause
        resources:
          requests:
            cpu: "28"
            memory: "55Gi"
      nodeSelector:
        agentpool: {nodepool name}
EOF

기존 노드 풀의 AutoScaler 중지 및 기존 overprovisiong 수량 축소

기존 노드 풀은 제거 대상이 되기 때문에 Deployment/StatefulSet 리소스들이 기존 노드 풀에 배치되지 않도록 AutoScaler 중지 및 기존 overprovisioning의 수량을 조정합니다.

az aks nodepool update --debug \
--cluster-name s{cluster_name} \
--name k8spool1 \
--resource-group s{cluster_name}-rg \
--disable-cluster-autoscaler

이후 수량 조절도 진행합니다.

kubectl scale --replicas=0 deployment/overprovisioning-pool1

기존 노드 풀에 대한 cordon 처리

노드를 개별적으로 drain 처리를 해도 되지만, 이렇게 되면 POD들이 축출되며 타 노드에 배치시 일시적인 서비스 지연 또는 장애가 발생할수 있습니다. 이에 cordon 처리를 통해 node에 POD만 추가 배치가 되지 않도록 설정해서 서비스 영향도를 최소화 합니다.

kubectl get nodes -A | awk '{print $1}' | grep -v NAME
--------
kubectl cordon {nodename}

배포된 리소스들을 모두 rollout restart 진행

기존 노드에 배포되어있는 POD들을 신규 노드풀로 변경하기 위해 rollout restart를 진행합니다.

kubectl get deployments -A | awk '{print $1}' | grep -v NAME
-------
kubectl rollout restart deployment/{deployment_name}

기존 노드 풀을 drain 및 delete 처리

모든 리소스들이 신규 노드풀로 이전된 것을 확인한 다음에 기존 노드풀에 속한 노드들에 대한 drain 처리 및 delete 처리를 진행합니다.

kubectl get nodes -A | awk '{print $1}' | grep -v NAME | grep -v '{신규 노드풀 이름}'
---------
kubectl drain {nodename}
kubectl delete node {nodename}

이후 Azure CLI를 이용해서 노드풀 삭제를 진행합니다.

az aks nodepool delete --debug \
  --name {nodepool_name} \
  --resource-group {nodepool_name}-rg \
  --cluster-name {cluster_name}

마무리

지금까지 AKS이용시 버전 업그레이드를 안전하게 하는 방법에 대해 간략하게 정리했습니다. 서비스 가용성을 최대치(99.99~~~%)로 매우 안정적으로 운영하기 위해서는 많은 고민이 필요합니다. 이 고민에서 나온것이 해당 업그레이드 방법이기에 이것이 최선이다라고 하기에는 부족할수 있지만, 하나의 방안이 되었으면 좋겠습니다.