본문 바로가기
IaC/Terraform

[Terraform] 7-1. Karpenter controller를 EKS Fargate로 실행하기

by okms1017 2024. 7. 28.
728x90

✍ Posted by Immersive Builder  Seong

 

 

1. 실습 소개 

이번 실습에서는 Fargate Profile을 사용하여 Karpenter controller를 서버리스 클러스터로 구성합니다. 

 

Karpenter on EKS Fargate

 


▶ Karpenter 기본 개념

 

[AEWS2] 5-2. Karpenter

✍ Posted by Immersive Builder Seong Karpenter 란? Karpenter는 AWS로 구축된 오픈 소스 고성능 쿠버네티스 클러스터 오토스케일러입니다. 어플리케이션 워크로드의 변화에 대응하여 적절한 크기의 컴퓨팅 리

okms1017.tistory.com

 


2. Karpenter controller를 EKS Fargate로 실행하기

Fargate 란?

Fargate는 서버리스 컴퓨팅 엔진으로 컨테이너 기반의 어플리케이션을 빠르게 실행하기에 적합합니다. 

특히, EKS Control Plane과 Fargate를 조합하면 완전한 AWS 관리형 서비스를 구성할 수 있습니다. 

 

 

Fargate Profile

Fargate Profile은 파드를 배포할 대상 서브넷, 네임스페이스, 레이블 조건을 지정하여 지정한 파드가 Fargate에서 동작하도록 합니다. 참고로 Fargate는 프라이빗 서브넷에서만 동작하며, 퍼블릭 서브넷에서는 동작하지 않습니다. 

 


        fargate_profiles = {
          karpenter = {
            selectors = [
              { namespace = "karpenter" }
            ]
          }
          kube_system = {
            name = "kube-system"
            selectors = [
              { namespace = "kube-system" }
            ]
          }
        }
 

 

위와 같이 EKS 클러스터 모듈 내에 Fargate Profile을 명시함으로써 'karpenter'와 'kube-system' 네임스페이스에 지정한 파드가 각각 배포되도록 합니다. 

 

Fargate Profile - karpenter
Fargate Profile - kube_system
Fargate Profile

 

Karpenter controller on EKS Fargate

  • Template 준비 

Amazon EKS Blueprints for Terraform 에서 공식으로 제공하는 Karpenter controller Template을 사용합니다. 

 

$ git clone https://github.com/aws-ia/terraform-aws-eks-blueprints
$ cd terraform-aws-eks-blueprints/patterns/karpenter
$ tree
.
├── README.md
├── example.yaml
├── karpenter.yaml
├── main.tf
├── outputs.tf
├── variables.tf
└── versions.tf

 

  • versions.tf

테라폼 버전과 AWS, Helm, Kubernetes 프로바이더 소스 및 버전 정보를 명시합니다. 

Helm 프로바이더는 Karpenter를 설치하기 위해 필요하며, Kubernetes 프로바이더는 자격증명을 통해 Kubernetes 리소스를 프로비저닝합니다. 

 


        terraform {
          required_version = ">= 1.3"

          required_providers {
            aws = {
              source  = "hashicorp/aws"
              version = ">= 5.34"
            }
            helm = {
              source  = "hashicorp/helm"
              version = ">= 2.9"
            }
            kubernetes = {
              source  = "hashicorp/kubernetes"
              version = ">= 2.20"
            }
          }
 

 

  • main.tf

AWS 리전은 서울 리전과 버지니아 북부 리전을 명시합니다. 서울 리전은 EKS 클러스터가 실제 배포되는 환경으로 로컬 변수로 선언되고, 버지니아 북부 리전은 퍼블릭 ECR(공개 레지스트리)으로부터 컨테이너 이미지를 가져오기 위해 선언됩니다. 그리고 Helm과 Kubernetes 프로바이더에서 EKS 인증/인가를 위한 토큰을 요청하는 명령어를 실행합니다. 

 


        provider "aws" {
          region = local.region
        }
 
        provider "aws" {
          region = "us-east-1"
          alias  = "virginia"
        }

        provider "kubernetes" {
          host                   = module.eks.cluster_endpoint
          cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)

          exec {
            api_version = "client.authentication.k8s.io/v1beta1"
            command     = "aws"
            args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name]
          }
        }

        provider "helm" {
          kubernetes {
            host                   = module.eks.cluster_endpoint
            cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)

            exec {
              api_version = "client.authentication.k8s.io/v1beta1"
              command     = "aws"
              args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name]
            }
          }
        }
 

 

일반적으로 서비스 운영 환경에서는 변수를 variables.tf 파일에 선언하여 별도로 관리하지만, EKS Blueprints Template 은 패턴별 아키텍처를 빠르게 구성하는데 목적이 있으므로 로컬 변수를 사용합니다. 로컬 변수로 서울 리전(ap-northeast-2)과 VPC 대역(10.10.0.0/16), 그리고 가용영역을 선언합니다. 가용영역은 slice 내장함수를 사용하여 3개의 영역(ap-northeast-2a, ap-northeast-2b, ap-northeast-2c)을 순서대로 지정합니다. 

 


        locals {
          name   = "ex-${basename(path.cwd)}"
          region = "ap-northeast-2"

          vpc_cidr = "10.10.0.0/16"
          azs      = slice(data.aws_availability_zones.available.names, 0, 3)

          tags = {
            Blueprint  = local.name
            GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints"
          }
        }

 

다음은 VPC 모듈 구성 정보로 모듈 소스 및 버전 정보와 가용영역, 서브넷, NAT-GW 등 네트워크 정보를 포함합니다. 

for 반복문과 cidrsubnet 내장함수를 사용하여 3개의 가용영역에 프라이빗 서브넷(10.10.0.0/20, 10.10.16.0/20, 10.10.32/20)과 퍼블릭 서브넷(10.10.48.0/24, 10.10.49.0/24, 10.10.50.0/24)을 구성합니다. 대부분의 어플리케이션 파드가 프라이빗 서브넷에 배포되는 점을 감안하여 보안 요건이 적은 퍼블릭 서브넷보다 Prefix 값을 크게 할당합니다.  

 


        module "vpc" {
          source  = "terraform-aws-modules/vpc/aws"
          version = "~> 5.0"

          name = local.name
          cidr = local.vpc_cidr

          azs             = local.azs
          private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
          public_subnets  = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)]

          enable_nat_gateway = true
          single_nat_gateway = true

          public_subnet_tags = {
            "kubernetes.io/role/elb" = 1
          }

          private_subnet_tags = {
            "kubernetes.io/role/internal-elb" = 1
            # Tags subnets for Karpenter auto-discovery
            "karpenter.sh/discovery" = local.name
          }

          tags = local.tags
        }
 

 

VPC 정보 조회 1
VPC 정보 조회 2
VPC 배포 확인
서브넷 정보 조회
서브넷 배포 확인
NAT-GW 배포 확인

 

이제 EKS 클러스터를 배포하기 위한 모듈 구성 정보입니다. 

모듈 소스와 버전 정보뿐만 아니라 쿠버네티스 버전은 v1.30 이고, 클러스터 엔드포인트는 퍼블릭 접근을 허용합니다. 

 


        module "eks" {
          source  = "terraform-aws-modules/eks/aws"
          version = "~> 20.11"

          cluster_name                   = local.name
          cluster_version                = "1.30"
          cluster_endpoint_public_access = true

          vpc_id     = module.vpc.vpc_id
          subnet_ids = module.vpc.private_subnets
          '''
          tags = merge(local.tags, {
            "karpenter.sh/discovery" = local.name
          })
        }
 

 

EKS 클러스터 배포 확인

kubernetes_network_config는 EKS 클러스터 내에서 통신을 가능하게 하는 설정으로 해당 네트워크 대역(172.20.0.0/16)을 사용할 것임을 명시합니다. 

 

kubernetes_network_config (terraform show로 확인)
EKS 클러스터 네트워킹 정보 확인

encryption_config는 Secrets encryption 기능을 활성화합니다. 해당 기능이 활성화되면 KMS 키를 생성하고, 키를 기반으로 etcd와 kubernetes secret 리소스를 암호화합니다. 여기서 KMS 키는 대칭키(symmetric)를 사용하여 암/복호화를 진행합니다. 

 

encryption_config (terraform show로 확인)
Secrets encryption 활성화
KMS 암호화 구성 정보 확인

EKS 액세스 인증 모드는 EKS API 방식과 ConfigMap 방식 2가지 방식을 함께 사용합니다.

그리고 EKS에 액세스 가능한 사용자 정보는 아래와 같습니다. 

 

access_config (terraform show로 확인)
EKS 사용자 자격 증명 정보
EKS 인증 모드

EKS Control plane 영역은 AWS에서 관리하기 때문에 직접 접속하여 볼 수 없습니다. 따라서 Control Plane 로깅 관련한 3가지 항목(API 서버, 감사, Authenticator)을 활성화합니다. 로그는 CloudWatach와 연동하여 로그 그룹의 로그 스트림에 쌓이게 됩니다. EKS Blueprints Template은 이벤트 발생 시 SQS 큐에 전달되도록 이미 구성이 되어 있고, 사용자는 필요에 따라 Slack/E-mail을 구독하여 알림을 보내거나 Lambda와 연동하여 특정 작업을 트리거하도록 구성할 수 있습니다. 

 

Control Plane 로깅 활성화
CloudWatch - 로그 스트림
Amazon EventBridge - 규칙
SQS queue 1
SQS queue 2
SQS queue 3

 

EKS 클러스터 애드온을 추가하기 위한 모듈 구성 정보입니다. 

기본적인 VPC-CNI, kube-proxy, CoreDNS 애드온을 활성화합니다. 해당 애드온들이 활성화되어야 노드와 파드의 정보를 조회할 수 있게 됩니다. 

 


        module "eks_blueprints_addons" {
          source  = "aws-ia/eks-blueprints-addons/aws"
          version = "~> 1.16"

          cluster_name      = module.eks.cluster_name
          cluster_endpoint  = module.eks.cluster_endpoint
          cluster_version   = module.eks.cluster_version
          oidc_provider_arn = module.eks.oidc_provider_arn
 
          create_delay_dependencies = [for prof in module.eks.fargate_profiles : prof.fargate_profile_arn]

          eks_addons = {
            coredns = {
              configuration_values = jsonencode({
                computeType = "Fargate"
                resources = {
                  limits = {
                    cpu = "0.25"
                    memory = "256M"
                  }
                  requests = {
                    cpu = "0.25"
                    memory = "256M"
                  }
                }
              })
            }
            vpc-cni    = {}
            kube-proxy = {}
          }

          enable_karpenter = true

          karpenter = {
            repository_username = data.aws_ecrpublic_authorization_token.token.user_name
            repository_password = data.aws_ecrpublic_authorization_token.token.password
          }

          karpenter_node = {
            iam_role_use_name_prefix = false
          }

          tags = local.tags
        }

        resource "aws_eks_access_entry" "karpenter_node_access_entry" {
          cluster_name      = module.eks.cluster_name
          principal_arn     = module.eks_blueprints_addons.karpenter.node_iam_role_arn
          kubernetes_groups = []
          type              = "EC2_LINUX"
        }

 

EKS Add-on 추가 확인

  • ouput.tf

로컬 환경에서 EKS 자격 증명 정보를 업데이트하기 위한 명령어를 화면에 출력합니다. 

 


        output "configure_kubectl" {
          description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig"
          value       = "aws eks --region ${local.region} update-kubeconfig --name ${module.eks.cluster_name}"
        }

 

하기 명령어를 통해 EKS 자격 증명 정보를 업데이트합니다. 

 

$ aws eks --region ap-northeast-2 update-kubeconfig --name ex-karpenter

 

  • 상태 정보 확인

State 파일의 전체 상태 정보를 하기 명령어를 통해 조회할 수 있습니다. 

 

$ terraform show

 

  • 노드 모니터링

노드 정보를 간단하게 명령어로 조회합니다. 

 

k get node -owide

컴퓨팅 노드는 Fargate 인스턴스 유형으로 총 4개가 배포된 것을 확인할 수 있습니다. 

 

EKS Fargate 배포 확인

eks-node-viewer를 설치하여 노드의 할당 가능한 용량과 스케줄링된 파드의 Request 리소스 정보를 모니터링합니다. 

 

# Go Install 
$ wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz 
$ echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
$ go version

# eks-node-viewer Install
$ go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest
$ echo 'export PATH=$PATH:/home/okms1017/go/bin' >> ~/.bashrc

$ eks-node-viewer --resources cpu,memory

 

eks-node-viewer

  • 파드 모니터링

파드 정보를 간단하게 명령어로 조회합니다. 

 

k get pod -o wide -A

 

kube-ops-view를 설치하여 노드의 파드 상태 정보를 웹 브라우저에서 실시간으로 모니터링합니다. 

 

# helm 배포
$ helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
$ helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system

# 포트포워딩
$ kubectl port-forward deployment/kube-ops-view -n kube-system 8080:8080 &

# URL 확인 
$ echo -e "KUBE-OPS-VIEW URL = http://localhost:8080/#scale=1.5"

 

kube-ops-view

 

Fargate는 파드 하나당 노드 하나가 할당됩니다. 결국 'karpenter'와 'kube-system' 파드가 Fargate 유형의 인스턴스로 하나씩 배포된 것을 알 수 있습니다. 

 

EKS Node Scale-Out/In using Karpenter

Karpenter controller를 사용하여 EKS Node를 Scale-Out/In 테스트를 진행합니다. 

 

  • karpenter.yaml : NodePool 정보(Instance Type, CPU, Hypervisor, Generation, limits, consolidation etc)

        ---
        apiVersion: karpenter.k8s.aws/v1beta1
        kind: EC2NodeClass
        metadata:
          name: default
        spec:
          amiFamily: AL2
          role: karpenter-ex-karpenter
          subnetSelectorTerms:
            - tags:
                karpenter.sh/discovery: ex-karpenter
          securityGroupSelectorTerms:
            - tags:
                karpenter.sh/discovery: ex-karpenter
          tags:
            karpenter.sh/discovery: ex-karpenter
        ---
        apiVersion: karpenter.sh/v1beta1
        kind: NodePool
        metadata:
          name: default
        spec:
          template:
            spec:
              nodeClassRef:
                name: default
              requirements:
                - key: "karpenter.k8s.aws/instance-category"
                  operator: In
                  values: ["c", "m", "r"]
                - key: "karpenter.k8s.aws/instance-cpu"
                  operator: In
                  values: ["4", "8", "16", "32"]
                - key: "karpenter.k8s.aws/instance-hypervisor"
                  operator: In
                  values: ["nitro"]
                - key: "karpenter.k8s.aws/instance-generation"
                  operator: Gt
                  values: ["2"]
          limits:
            cpu: 1000
          disruption:
            consolidationPolicy: WhenEmpty
            consolidateAfter: 30s

 

  • example.yaml : Deployment 

        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: public.ecr.aws/eks-distro/kubernetes/pause:3.7
                  resources:
                    requests:
                      cpu: 1
 

 

Replicas 수량을 조정하여 노드의 변화를 모니터링합니다. 

 

  • Replicas=3 

 

  • Deployment 삭제 

 

※ EKS Blue/Green 업데이트를 1년에 한 번씩 수행 필요 => 테라폼 코드를 이용한 수행 기법 활용 

 

리소스 삭제

실습을 완료하고 리소스를 하기 순서에 따라 삭제합니다. 

 

# kube-ops-view 삭제
$ helm uninstall kube-ops-view -n kube-system

# addon & karpenter helm 삭제
$ terraform destroy -target="module.eks_blueprints_addons" -auto-approve

# EKS 삭제 
$ terraform destroy -target="module.eks" -auto-approve

# VPC 삭제 
$ terraform destroy -auto-approve

# VPC 삭제 확인
$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=false' --output yaml

# kubeconfig 삭제
$ rm -rf ~/.kube/config

 

 


[출처]

1) CloudNet@, T1014 실습 스터디 

2) https://www.eksworkshop.com/docs/fundamentals/fargate/

 

Fargate | EKS Workshop

Leverage AWS Fargate, the serverless compute engine for containers, with Amazon Elastic Kubernetes Service.

www.eksworkshop.com

3) https://aws-ia.github.io/terraform-aws-eks-blueprints/

 

Amazon EKS Blueprints for Terraform

Amazon EKS Blueprints for Terraform Welcome to Amazon EKS Blueprints for Terraform! This project contains a collection of Amazon EKS cluster patterns implemented in Terraform that demonstrate how fast and easy it is for customers to adopt Amazon EKS. The p

aws-ia.github.io

728x90