Skip to main content

Karpenter 설치 및 노드 구성

Kubernetes Engine 클러스터에 Karpenter Provider를 설치하고, 워커 노드가 자동으로 생성·회수되도록 설정합니다.

설치 전 확인 사항​

다음 항목을 준비합니다.

  • kr-central-2 리전의 실행 중인 Kubernetes Engine 클러스터와 Karpenter 컨트롤러를 실행할 관리형 Virtual Machine 노드 1대 이상
  • 클러스터 관리자 권한의 kubeconfig, kubectl, Helm 3.8.0 이상
  • 프로젝트 ID, 클러스터 이름, 워커 서브넷 ID, Kubernetes Engine 워커 보안 그룹 ID
  • Karpenter 전용 IAM 액세스 키

Step 1. Secret 생성​

IAM 액세스 키의 권한은 발급 계정에 할당된 프로젝트 역할을 따릅니다. Karpenter가 사용할 계정에 다음 작업이 가능한 역할을 할당한 뒤 IAM 액세스 키를 발급합니다.

서비스필요한 권한
IAM토큰 발급
Virtual Machine 인스턴스생성, 조회, 설명 수정, 삭제
인스턴스 유형조회
키 페어조회(spec.keyName을 사용하는 경우)
VPC서브넷 조회
Network보안 그룹 조회
Kubernetes Engine워커 이미지와 지원 Kubernetes 버전, 클러스터 및 노드 풀 조회

인스턴스 설명 수정 권한은 소유권 정보를 기록하는 데 필요합니다. 인스턴스 삭제 권한도 필요하므로 Secret 조회 권한을 제한합니다.

Karpenter 전용 네임스페이스와 API 호출에 사용할 Secret을 생성합니다.

자격 증명 Secret 생성
kubectl create namespace karpenter-kakaocloud --dry-run=client -o yaml \
| kubectl apply -f -

export APP_CRED_ID='<IAM_ACCESS_KEY_ID>'
export APP_CRED_SECRET='<IAM_ACCESS_KEY_SECRET>'

kubectl -n karpenter-kakaocloud create secret generic kakaocloud-credentials \
--from-literal=applicationCredentialId="$APP_CRED_ID" \
--from-literal=applicationCredentialSecret="$APP_CRED_SECRET" \
--from-literal=region=kr-central-2

unset APP_CRED_ID APP_CRED_SECRET

Secret에 필요한 키가 생성되었는지 확인합니다. 출력에는 applicationCredentialId, applicationCredentialSecret, region이 모두 포함되어야 합니다.

자격 증명 Secret 키 확인
kubectl -n karpenter-kakaocloud get secret kakaocloud-credentials \
-o go-template='{{range $key, $value := .data}}{{$key}}{{"\n"}}{{end}}'
서명 키 보관

서명 키는 Karpenter 인스턴스의 소유권 확인과 삭제에 사용합니다. 클러스터마다 한 번만 생성하여 백업합니다. Helm 업그레이드나 GitOps 동기화 중에 다시 생성하면 기존 인스턴스를 회수하지 못할 수 있습니다.

인스턴스 소유권 확인에 사용할 32바이트 서명 키를 생성하고 Secret에 저장합니다.

서명 키 Secret 생성
KEY=$(head -c 32 /dev/urandom | base64)
printf '{"activeKeyID":"k1","keys":[{"id":"k1","key":"%s"}]}\n' "$KEY" > keyring.json

kubectl -n karpenter-kakaocloud create secret generic karpenter-sigkey \
--from-file=keyring.json=./keyring.json

unset KEY

keyring.json을 안전한 장소에 백업한 후 작업 디렉터리에서 삭제합니다. activeKeyID는 최대 7자이며, 데이터 키 이름은 keyring.json을 사용해야 합니다.

Step 2. Karpenter 설치​

설치 전에 컨트롤러와 생성할 노드의 통신 경로를 확인합니다. 프라이빗 서브넷에서는 NAT를 통해 필요한 외부 서비스에 접근할 수 있어야 합니다. Karpenter 노드는 HTTP 프록시를 통한 부트스트랩을 지원하지 않습니다.

네트워크 접근 대상 확인

생성할 노드의 서브넷에는 다음 통신 경로가 필요합니다.

  • Kubernetes API 서버와 카카오클라우드 OpenAPI
  • 컨테이너 이미지 레지스트리와 시간 동기화 서버
  • 메타데이터 서비스 169.254.169.254
  • 클러스터 내부 도메인 이름 확인과 CNI 통신

Karpenter 컨트롤러는 다음 카카오클라우드 OpenAPI 호스트에 HTTPS(443)로 접근할 수 있어야 합니다.

서비스호스트
IAMiam.kakaocloud.com
Virtual Machinebcs.kr-central-2.kakaocloud.com
VPCvpc.kr-central-2.kakaocloud.com
Networknetwork.kr-central-2.kakaocloud.com
Imageimage.kr-central-2.kakaocloud.com
Volumevolume.kr-central-2.kakaocloud.com
Kubernetes Enginekubernetes-engine.kr-central-2.kakaocloud.com

컨트롤러 이미지와 Helm 차트를 가져오려면 ke-container-registry.kr-central-2.kcr.dev에도 접근할 수 있어야 합니다. 보안 그룹에는 기존 Kubernetes Engine 워커 노드와 동일한 클러스터 및 CNI 통신 규칙을 적용합니다.

Helm OCI 차트를 사용하여 Karpenter Provider를 설치합니다.

Karpenter Provider 설치
export KARPENTER_CHART_VERSION='0.1.0'

helm upgrade --install karpenter-provider-kakaocloud \
oci://ke-container-registry.kr-central-2.kcr.dev/ke-helm-public/karpenter-provider-kakaocloud \
--version "$KARPENTER_CHART_VERSION" \
--namespace karpenter-kakaocloud \
--create-namespace \
--set clusterName='<KE_CLUSTER_NAME>' \
--set projectID='<PROJECT_ID>' \
--set credentials.secretName=kakaocloud-credentials \
--set sigKey.secretName=karpenter-sigkey \
--wait \
--timeout 10m

차트에 포함된 컨트롤러 이미지를 사용하므로 controller.image.*, region 또는 clusterUID 값을 별도로 지정하지 않습니다.

Helm 차트의 기본 affinity는 컨트롤러가 Karpenter 노드에 배치되지 않도록 설정되어 있습니다. 사용자 정의 values 파일에서 affinity를 변경하더라도 karpenter.sh/nodepool 레이블이 있는 노드를 제외하는 조건을 유지합니다.

컨트롤러 상태를 확인합니다.

컨트롤러 상태 확인
kubectl -n karpenter-kakaocloud get deployment karpenter-provider-kakaocloud
kubectl -n karpenter-kakaocloud get pods

Deployment의 READY가 1/1이고 파드가 Running 상태인지 확인합니다.

Step 3. KCNodeClass 생성​

KCNodeClass에서 Karpenter가 생성할 노드의 네트워크, 루트 볼륨 및 Kubernetes 버전을 설정합니다.

Kubernetes 버전은 클러스터 세부 정보에서 확인합니다. KCNodeClass에는 패치 번호를 제외하고, 컨트롤 플레인과 같거나 한 단계 낮은 마이너 버전을 지정합니다. 예를 들어 클러스터 버전이 1.35라면 Kubernetes Engine에서 제공하는 1.35 또는 1.34를 지정합니다.

기존 워커 노드의 버전도 함께 확인합니다. 다음 명령의 출력이 v1.35.2라면 마이너 버전은 1.35입니다.

워커 노드 Kubernetes 버전 확인
kubectl get nodes -o custom-columns='NODE:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion'

kcnodeclass.yaml 파일을 생성합니다.

kcnodeclass.yaml
apiVersion: karpenter.kakaocloud.com/v1alpha1
kind: KCNodeClass
metadata:
name: karpenter-default
spec:
subnetSelectorTerms:
- id: <SUBNET_ID>
securityGroupSelectorTerms:
- id: <WORKER_SECURITY_GROUP_ID>
rootVolume:
sizeGiB: 50
deleteOnTermination: true
bootstrap:
mode: Kubeadm
kubernetesVersion: "<KUBERNETES_MAJOR.MINOR>"

subnetSelectorTerms에 지정할 ID는 서브넷 목록에서 확인합니다. securityGroupSelectorTerms에는 기존 노드 풀에서 사용하는 워커 보안 그룹을 포함합니다.

다른 보안 그룹이 필요하면 항목을 추가합니다. SSH 키 페어를 사용하는 경우에는 spec.keyName에 이름을 지정합니다.

rootVolume.sizeGiB는 30GiB 이상으로 설정합니다. deleteOnTermination을 false로 변경하면 인스턴스 삭제 후에도 루트 볼륨과 요금이 남습니다.

지원 범위를 벗어난 버전을 지정하면 KCNodeClass가 Ready=False가 되고 노드를 생성할 수 없습니다. 지원 종료된 버전의 기존 구성은 지원 종료된 Kubernetes 버전 관리를 참고하시기 바랍니다.

KCNodeClass를 생성하고 준비 상태를 확인합니다.

KCNodeClass 생성 및 확인
kubectl apply -f kcnodeclass.yaml

kubectl get kcnodeclass karpenter-default \
-o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\n"}{end}'

KubernetesVersionReady, ImagesReady, SubnetsReady, SecurityGroupsReady, KeyPairReady, Ready 조건이 모두 True인지 확인합니다.

Step 4. NodePool 생성​

NodePool에서 노드 생성 조건과 리소스 상한, 회수 정책을 설정합니다.

클러스터의 CNI를 확인합니다.

CNI 확인
kubectl get daemonset -A | grep -E 'cilium|calico-node'
  • cilium DaemonSet이 있으면 Cilium을 사용합니다.
  • calico-node DaemonSet이 있으면 Calico를 사용합니다.

nodepool.yaml 파일을 생성합니다.

nodepool.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: karpenter-default
spec:
template:
metadata:
labels:
workload-type: karpenter
kakaoi.io/kke-nodepool: karpenter-default
spec:
requirements:
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
nodeClassRef:
group: karpenter.kakaocloud.com
kind: KCNodeClass
name: karpenter-default
expireAfter: 720h
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 10m
limits:
cpu: "40"
memory: 160Gi

Cilium을 사용하는 클러스터에서는 spec.template.spec 아래에 다음 startup taint를 추가합니다. Calico를 사용하는 클러스터에는 추가하지 않습니다.

Cilium startup taint
startupTaints:
- key: node.cilium.io/agent-not-ready
value: "true"
effect: NoExecute

NodePool을 생성하고 상태를 확인합니다.

NodePool 생성 및 확인
kubectl apply -f nodepool.yaml
kubectl get nodepool karpenter-default

READY가 True인지 확인합니다. 인스턴스 유형이나 가용 영역을 제한하는 방법은 노드 생성 및 회수 정책 설정을 참고하시기 바랍니다.

kakaoi.io/kke-nodepool 레이블은 카카오클라우드 콘솔에서 Karpenter 노드를 식별하기 위한 값입니다. 노드 생성과 회수는 콘솔의 노드 풀 기능이 아닌 Karpenter 리소스를 통해 관리합니다.

NodePool 이름은 38자 이내로 입력합니다. Karpenter가 생성하는 Virtual Machine 이름에는 클러스터 식별자와 NodeClaim 이름이 함께 포함되므로, 제한을 초과하면 인스턴스 생성이 실패할 수 있습니다.

관리형 노드 풀의 오토 스케일링과 Karpenter가 같은 워크로드를 대상으로 하지 않도록 구분합니다. Karpenter에서 실행할 워크로드에는 아래 예제처럼 nodeSelector로 workload-type: karpenter 레이블을 선택합니다.

관리형 노드 풀의 레이블과 taint는 노드 풀 생성을 참고하시기 바랍니다.

Step 5. 노드 자동 생성 확인​

Karpenter가 NodeClaim과 워커 노드를 생성하는지 확인하기 위해 테스트 워크로드를 배포합니다. NodeClaim은 Karpenter가 생성한 노드를 나타내는 리소스입니다.

karpenter-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: karpenter-demo
spec:
replicas: 3
selector:
matchLabels:
app: karpenter-demo
template:
metadata:
labels:
app: karpenter-demo
spec:
nodeSelector:
workload-type: karpenter
containers:
- name: pause
image: registry.k8s.io/pause:3.9
resources:
requests:
cpu: "1"
memory: 2Gi
테스트 워크로드 배포 및 확인
kubectl apply -f karpenter-demo.yaml
kubectl get nodeclaims
kubectl get nodes -l karpenter.sh/nodepool=karpenter-default
kubectl get pods -l app=karpenter-demo -o wide

NodeClaim이 생성되고 새 노드가 Ready, 테스트 파드가 Running 상태인지 확인합니다. 카카오클라우드 콘솔의 Virtual Machine > 인스턴스 메뉴에서도 Karpenter가 생성한 인스턴스를 확인합니다.

확인이 끝나면 테스트 워크로드를 삭제합니다.

테스트 워크로드 삭제
kubectl delete -f karpenter-demo.yaml

consolidateAfter가 지난 뒤 비어 있는 노드와 NodeClaim이 회수되는지 확인합니다. 카카오클라우드 콘솔에서도 인스턴스 삭제 여부를 확인합니다.

Karpenter를 제거하려면 Karpenter 운영 및 삭제를 참고하시기 바랍니다.