[CAS, Karpenter] 쿠버네티스 노드 오토스케일러

zzery·2024년 5월 12일

일지(2022~2024)

목록 보기
25/25

최근 EKS에 대해 여러가지를 찾아보았다.

EKS Best Practices

  • https://aws.github.io/aws-eks-best-practices/
  • (배경 지식) EKS의 Managed Node Group(관리형 노드 그룹)
    • EKS 워커노드를 매우 쉽게 생성할 수 있다.
    • taint, label등 워커노드에 부가적으로 필요한 정보도 설정 가능하다.
    • 최소 대수, 원하는 대수, 최대 대수를 설정할 수 있다. (ASG와 연동)
    • 하나의 노드그룹에는 한 종류의 노드 스펙만 설정 가능하다.
      • AMI 정보
      • 인스턴스 타입
      • 디스크 설정

여기에서 클러스터의 워커노드 오토스케일링 방법에 대해 알아본다.

  • Cluster Autoscaler
  • Karpenter (⭐️)

두 기술 모두 기존 워커노드에 Pod를 스케줄링할 수 없는 상황일 때, 새로운 워커노드를 생성하고 관리한다.

공통점

  • 확장성: 일정 주기로 클러스터에 Pending 상태의 Pod가 있는지 확인하고, 발견될 경우 새로운 워커노드를 프로비저닝한다.
  • 축소: 워커노드의 리소스 사용량을 확인하고, 불필요한 노드로 판단될 경우 노드를 삭제한다.

Cluster Autoscaler (CAS)

동작 구조

  1. Pending 상태의 Pod가 있는지 주기적으로 확인한다. (있을 경우 새로운 워커노드를 생성하게 됨)
  2. 클라우드 환경의 오토스케일링 서비스의 원하는 서버 대수를 증가시킨다.
  3. 오토스케일링 서비스에 의해 새 워커노드가 생성되며, 이곳에 Pod가 스케줄링된다.

Managed Node Group 문서에 따르면

관리형 노드 그룹의 일부로 시작된 노드는 Kubernetes Cluster Autoscaler에서 자동 검색할 수 있도록 태그가 자동으로 지정됩니다. 노드 그룹을 사용하여 Kubernetes 레이블을 노드에 적용하고 언제든지 업데이트할 수 있습니다.

문제점

오토 스케일링 과정 자체가 클라우드 벤더의 오토스케일링 서비스에 종속된다.

  • AWS의 경우, AWS의 ASG(Auto Scaling Group) 리소스를 통해 실제 노드 확장을 수행하게 된다.
  • ASG를 통한 EC2 생성 시간이 느리다.

EKS의 규모가 커질수록, 다수의 ASG와 launchTemplate에 대한 관리에 어려움이 생긴다.


Karpenter

Cluster Autoscaler를 사용했을 때 생기는 여러 어려운 점을 해소하기 위해, AWS에서 새로 개발한 k8s 노드 오토스케일링 오픈소스. 현재 지원되는 환경은 AWS, Azure 2개이다.

특징

노드 오토스케일링 정책을 k8s Custom Resource를 통해 정의한다.

  • Provisioner: 오토 스케일링 정책을 선언화
  • AWSNodeTemplate: EC2 구성 내용을 선언화
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: sample-provisioner
spec:
  requirements:
    - key: node.kubernetes.io/instance-type # 여러 타입 정의 가능
      operator: In
      values: [ r5.xlarge, r5.4xlarge ]
    - key: topology.kubernetes.io/zone # 여러 AZ에서 할당
      operator: In
      values: [ ap-northeast-2a, ap-northeast-2b, ap-northeast-2c ]
    - key: karpenter.sh/capacity-type # on-demand vs spot
      operator: In
      values: [ on-demand ]
  providerRef:
    name: sample-template # AWSNodeTemplate과 연동
  ttlSecondsAfterEmpty: 30
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: sample-template
spec:
  # EC2는 아래 라벨을 가진 subnet에서 생성됨.
  subnetSelector:
    kubernetes.io/role/internal-elb: "1"
    kubernetes.io/cluster/my-eks: "shared"
  # EC2는 아래 라벨을 가진 SG와 연동됨.
  securityGroupSelector:
    aws:eks:cluster-name: "my-eks"
  # EC2의 instance profile (Cluster API와 동일하게 사용 가능)
  instanceProfile: "nodes.cluster-api-provider-aws.sigs.k8s.io"
  amiSelector:
    aws-ids: "ami-085d85b69823fe795"
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100G
        volumeType: gp3
        iops: 3000
        deleteOnTermination: true
  # EC2는 아래의 태그를 포함하여 생성됨.
  tags:
    Name: managed-by-karpenter

Provisioner에는 워커노드의 생명주기를 정의하는 옵션이 포함된다.

  • ttlSecondsAfterEmpty(int): 해당 노드에 DaemonSet을 제외하고 아무 파드도 없는 경우, n초뒤에 해당 노드를 삭제
  • ttlSecondsUntilExpired(int): 해당 시간만큼 노드가 유지되었다면, 삭제 후 동일 스펙으로 새로 생성
  • consolidation(bool): 해당 노드에 배포된 Pod를 다른 노드로 이동시켰을 때 이 노드를 삭제할 수 있다면, Pod를 이동시킴. (ttlSecondsAfterEmpty와 동시에 사용 불가)

Cluster API AWS Provider와 호환성

Cluster API

AWS Provider의 기본 구조

  • CloudFormation을 통해 Controller/MasterNode/WorkerNode IAM 설정을 정형화한다.
  • 기본 설정 기준, 아래의 11개의 리소스가 생성된다.
- AWSIAMInstanceProfileControlPlane
- AWSIAMInstanceProfileControllers
- AWSIAMInstanceProfileNodes
- AWSIAMManagedPolicyCloudProviderControlPlane
- AWSIAMManagedPolicyCloudProviderNodes
- AWSIAMManagedPolicyControllers
- AWSIAMManagedPolicyControllersEKS
- AWSIAMRoleControlPlane
- AWSIAMRoleControllers
- AWSIAMRoleEKSControlPlane
- AWSIAMRoleNodes

Controller는 k8s에 Pod로 배포되고, AWS의 리소스를 Reconcile할 때 위의 Controller 네이밍의 IAM 리소스들이 사용된다.

Karpenter와 호환성 여부

  • Worker Node 관리 측면
    • 오토스케일링을 적용할 노드 -> karpenter로 관리
    • 오토스케일링이 적용되지 않은 노드 -> Cluster API로 관리
  • AWS 리소스 조회 및 관리 측면
    • 태깅: karpenter는 태그 기반으로 AWS 리소스를 추적함. 이 태깅 정보를 Cluster API와 호환해서 사용 가능
    • IAM: Cluster API에서 CloudFormation으로 생성한 리소스 설정 활용 가능

설치

IAM 설정

EKS와 관련된 AWS 리소스를 조회/수정하기 위한 권한이 필요하다.

Cluster API의 IAM 리소스와 대부분 호환 가능.
아래 설정만 따로 Policy를 만들어 기존 CAPI Controller Role에 추가하면 된다.

{
	"Statement": [
		{
			"Action": [
				"ec2:DescribeInstanceTypeOfferings",
				"ec2:CreateFleet",
				"ec2:DescribeSpotPriceHistory",
				"pricing:GetProducts"
			],
			"Effect": "Allow",
			"Resource": "*",
			"Sid": "Karpenter"
		},
		{
			"Effect": "Allow",
			"Action": "eks:DescribeCluster",
			"Resource": "arn:aws:eks:ap-northeast-2:*:cluster/*",
			"Sid": "EKSClusterEndpointLookup"
		}
	],
	"Version": "2012-10-17"
}

IRSA (Identity Role as a Service Account) 설정

Helm으로 배포

export KARPENTER_VERSION="v0.29.0"
export CLUSTER_ENDPOINT="EKS 엔드포인트 URL"
export CLUSTER_NAME="EKS 이름"
export AWS_ACCOUNT_ID="EKS가 배포된 AWS 계정 ID"
export ROLE_NAME="controllers.cluster-api-provider-aws.sigs.k8s.io" # karpenter가 사용할 IAM Role (Cluster API Role과 호환 가능)

# karpenter helm repo 추가
helm repo add karpenter https://charts.karpenter.sh/
helm repo update

# karpenter 배포 manifest 구성
helm template karpenter oci://public.ecr.aws/karpenter/karpenter --version ${KARPENTER_VERSION} --namespace karpenter \
    --set settings.aws.defaultInstanceProfile="nodes.cluster-api-provider-aws.sigs.k8s.io" \
    --set settings.aws.clusterEndpoint="${CLUSTER_ENDPOINT}" \
    --set settings.aws.clusterName=${CLUSTER_NAME} \
    --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:aws:iam::${AWS_ACCOUNT_ID}:role/${ROLE_NAME}" \
    --set replicas=1 > karpenter.yaml
# 기본 설정은 replicas=2 이다.

# karpenter 배포
kubectl create namespace karpenter
kubectl apply -f https://raw.githubusercontent.com/aws/karpenter/${KARPENTER_VERSION}/pkg/apis/crds/karpenter.sh_provisioners.yaml
kubectl apply -f https://raw.githubusercontent.com/aws/karpenter/${KARPENTER_VERSION}/pkg/apis/crds/karpenter.k8s.aws_awsnodetemplates.yaml
kubectl apply -f https://raw.githubusercontent.com/aws/karpenter/${KARPENTER_VERSION}/pkg/apis/crds/karpenter.sh_machines.yaml
kubectl apply -f karpenter.yaml

동작 테스트 방법

  • 기존 Worker Node에 모두 Cordon을 건다.
  • 기존 Worker Node에 배포됐던 Pod 하나를 삭제하여 새 Pod를 생성시킨다. (이때 모두 Cordon이 걸려 있기에 Pending 상태가 됨)
  • Karpenter에서 이를 감지하고 Provisioner 리소스 정책에 맞는 워커노드를 새로 추가한다. (약 1분 소요)
  • Karpenter를 통해 추가된 워커노드에 Pod가 스케줄링된다.

기타 운영시 고려할 사항

profile
이 블로그의 모든 글은 수제로 짜여져 있습니다...

0개의 댓글