Azure의 AKS에서 karpenter 사용

Karpenter는 쿠버네티스 클러스터 자동 확장에 정말 유용한 도구로 많이 알려져 있지만 Azure 환경에서의 정보는 거의 없는 것 같습니다.

 

저 또한 공식 문서에도 자세한 내용이 없어서 막막했습니다.

그래서 이번 글에서는 AKS에서 Karpenter를 구성하고 활용하는 방법에 대해 공유해 보려고 합니다.

 


1. 사전 설정

소프트웨어 버전

WSL Ubuntu Ubuntu 22.04.3 LTS
Azure CLI 2.64.0
Helm v3.15.4
AKS 1.29.7

Azure vm vCPU(수량) 메모리(GB)

Standard_D2_v4 2 8

 

제한 사항 및 지원되지 않는 기능 (Azure docs참고)

 

제한 사항

  • 허용되는 유일한 네트워크 구성은 Cilium 제공 Azure CNI 오버레이입니다.
  • 노드 풀에 클러스터 자동 크기 조정기가 사용하도록 설정된 클러스터에서는 사용하도록 설정할 수 없습니다.

지원되지 않는 기능

  • Windows 노드 풀
  • 노드 kubelet에 사용자 지정 구성 적용
  • IPv6 클러스터
  • 서비스 주체시스템이 할당한 관리 ID 또는 사용자가 할당한 관리 ID를 사용할 수 있습니다.
  • 참고
  • 디스크 암호화 집합
  • CustomCATrustCertificates
  • 시작 중지 모드
  • HTTP 프록시
  • OutboundType 변형입니다. 모든 OutboundType은 지원되지만 만든 후에는 변경할 수 없습니다.

 

2. AKS 상에서 작업

# 명령에 사용될 변수 값 지정
export CLUSTER_NAME=karpenter
export RG=karpenter
export LOCATION='koreacentral'
export KARPENTER_NAMESPACE=kube-system

# 리소스 그룹 생성
$ az group create --name ${RG} --location ${LOCATION}
{
  "id": "/subscriptions/<Subscription ID>/resourceGroups/karpenter",
  "location": "koreacentral",
  "managedBy": null,
  "name": "karpenter",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}

# karpentermsi이름으로 Managed Identity 생성
$ KMSI_JSON=$(az identity create --name karpentermsi --resource-group "${RG}" --location "${LOCATION}")

# Microsoft.ContainerService 제공자를 등록
$ az provider register --namespace Microsoft.ContainerService

# aks 생성
$ AKS_JSON=$(az aks create \
  --name "${CLUSTER_NAME}" --resource-group "${RG}" \
  --node-count 2 --generate-ssh-keys \
  --network-plugin azure --network-plugin-mode overlay --network-dataplane cilium \
  --enable-managed-identity \
  --enable-oidc-issuer --enable-workload-identity)
  
# aks에서 작업할 수 있도록 자격증명 등록 역할 할당
$ az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RG}" --overwrite-existing
Merged "karpenter" as current context in /home/ubuntu/.kube/config

# 인증 사용을 위해 karpenter 서비스 계정에 연결된 연합 자격 증명 생성.
$ az identity federated-credential create \
  --name KARPENTER_FID --identity-name karpentermsi --resource-group "${RG}"  \
  --issuer "$(jq -r ".oidcIssuerProfile.issuerUrl" <<< "$AKS_JSON")"  \
   --subject system:serviceaccount:${KARPENTER_NAMESPACE}:karpenter-sa \ 
   --audience api://AzureADTokenExchange
{
  "audiences": [
    "api://AzureADTokenExchange"
  ],
  "id": "/subscriptions/<구독 id>/resourcegroups/karpenter/providers/Microsoft.ManagedIdentity/userAssignedIdentities/karpentermsi/federatedIdentityCredentials/KARPENTER_FID",
  "issuer": "https://koreacentral.oic.prod-aks.azure.com/2c667446-26d0-430d-8fe5-373c81417422/87e6b018-64f9-4531-9b73-ead801527cb4/",
  "name": "KARPENTER_FID",
  "resourceGroup": "karpenter",
  "subject": "system:serviceaccount:kube-system:karpenter-sa",
  "systemData": null,
  "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
}

# karpenter가 vm, 네트워크 리소스를 관리할 수 있도록 역할 할당
$ KARPENTER_USER_ASSIGNED_CLIENT_ID=$(jq -r '.principalId' <<< "$KMSI_JSON")
RG_MC=$(jq -r ".nodeResourceGroup" <<< "$AKS_JSON")
RG_MC_RES=$(az group show --name "${RG_MC}" --query "id" -otsv)
for role in "Virtual Machine Contributor" "Network Contributor" "Managed Identity Operator"; do
  az role assignment create --assignee "${KARPENTER_USER_ASSIGNED_CLIENT_ID}" --scope "${RG_MC_RES}" --role "$role"
done

 

 

3. helm install

curl -sO <https://raw.githubusercontent.com/Azure/karpenter-provider-azure/main/hack/deploy/configure-values.sh>
chmod +x ./configure-values.sh && ./configure-values.sh ${CLUSTER_NAME} ${RG} karpenter-sa karpentermsi
  • configure-values.sh의 마지막줄 yq 대신 envsubst < karpenter-values-template.yaml > karpenter-values.yaml로 수정
$ export KARPENTER_VERSION=0.5.1

$ helm upgrade --install karpenter oci://mcr.microsoft.com/aks/karpenter/karpenter \\
  --version "${KARPENTER_VERSION}" \\
  --namespace "${KARPENTER_NAMESPACE}" --create-namespace \\
  --values karpenter-values.yaml \\
  --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
  
$ k get po -n kube-system | grep karpenter
karpenter-5cf5cc5d5b-k5f9q  1/1  Running   0   2m10s

 

4. NodePool, AKSNodeClass 생성

nodeclass 생성 시 imageVersion을 명시하지 않으면 node를 감지하지 못해 aks 노드그룹에 추가되지 않는 오류 발생

$ cat <<EOF | kubectl apply -f -
---
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: general-purpose
  annotations:
    kubernetes.io/description: "General purpose NodePool for generic workloads"
spec:
  template:
    spec:
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: kubernetes.io/os
          operator: In
          values: ["linux"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.azure.com/sku-family
          operator: In
          values: [D]
      nodeClassRef:
        name: default
  limits:
    cpu: 100
  disruption:
    consolidationPolicy: WhenUnderutilized
    expireAfter: Never
---
apiVersion: karpenter.azure.com/v1alpha2
kind: AKSNodeClass
metadata:
  name: default
  annotations:
    kubernetes.io/description: "General purpose AKSNodeClass for running Ubuntu2204 nodes"
spec:
  imageFamily: Ubuntu2204
  imageVersion: 202408.21.0 
EOF
nodepool.karpenter.sh/general-purpose created
aksnodeclass.karpenter.azure.com/default created

$ k get nodepools.karpenter.sh 
NAME              NODECLASS
general-purpose   default
$ k get aksnodeclasses.karpenter.azure.com 
NAME      AGE
default   14s

아래와 같은 로그 발생하며 vm이 생성됨

{"level":"DEBUG","time":"2024-09-17T16:16:40.560Z","logger":"controller.nodeclaim.lifecycle","message":"Created virtual machine /subscriptions/356ce9af-1ab0-44d1-bfb6-2295e4f0175c/resourceGroups/MC_karpenter_karpenter_koreacentral/providers/Microsoft.Compute/virtualMachines/aks-general-purpose-j7m94","commit":"46b4276","nodeclaim":"general-purpose-j7m94"}
{"level":"DEBUG","time":"2024-09-17T16:16:40.560Z","logger":"controller.nodeclaim.lifecycle","message":"Creating virtual machine AKS identifying extension for aks-general-purpose-j7m94","commit":"46b4276","nodeclaim":"general-purpose-j7m94"}

 

karpenter로 인해 추가된 가상머신

 

 

5. 스케일링 테스트

cpu requests 가 1인 pod를 생성해서 스케일링 테스트

$ 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: mcr.microsoft.com/oss/kubernetes/pause:3.6
          resources:
            requests:
              cpu: 1
EOF

$ kubectl scale deployment inflate --replicas 2

# 로그 확인 명령
$ kubectl logs -f -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter -c controller

 

 

 

aks 상에 추가된 노드 확인

 

 

오류해결

subscription ID not set 오류로 karpenter pod crash 발생

 

values 파일의 34번째 줄에 구독 id 추가

 

AKSNodeClass에 이미지 버전 명시하지 않으면 아래의 로그가 계속해서 발생하고, vm만 생성되며 이를 감지하지 못해 클러스터에 포함되지 않는 문제 발생

apiVersion: karpenter.azure.com/v1alpha2
kind: AKSNodeClass
metadata:
  name: default
  annotations:
    kubernetes.io/description: "General purpose AKSNodeClass for running Ubuntu2204 nodes"
spec:
  imageFamily: Ubuntu2204
  imageVersion: 202408.21.0

 

{"level":"DEBUG","time":"2024-09-17T16:33:38.359Z","logger":"controller.disruption","message":"waiting on cluster sync","commit":"46b4276"} {"level":"DEBUG","time":"2024-09-17T16:33:39.360Z","logger":"controller.disruption","message":"waiting on cluster sync","commit":"46b4276"}

 

 

참고문서

github  https://github.com/Azure/karpenter-provider-azure

Azure docs 노드 자동 프로비전(미리 보기) - Azure Kubernetes Service

karpenter docs Getting Started with Karpenter