8 Search results

For the term "nodeless".

EKS-NodeLess-08-AWS-Karpenter-topologySpreadConstraints

topologySpreadConstraints 을 사용한 Karpenter 테스트를 진행하겠다.

topologySpreadConstraints 테스트는 Topology Aware Hint 를 예비한 테스트다.

https://aws.amazon.com/ko/blogs/tech/amazon-eks-reduce-cross-az-traffic-costs-with-topology-aware-hints/

참고할 부분이 있다면 이 글을 참고하길 바란다.

간략하게 설명하자면 Kubernetes 에서 Cross Zone Traffic 의 문제로 비용이 막대하게 발생할수 있다. 또한 Cross-AZ로 인하여 약간의 레이턴시가 발생할수도 있기때문에 Topology Aware Hint는 여러 문제점들을 줄여주는 역할을 한다.

조건은 몇가지가 있는데, Service 로 연결되 AZ가 수평적으로 동일하게 노드가 배포되어있고 서비스에

apiVersion: v1
kind: Service
metadata:
  name: service
  annotations:
    service.kubernetes.io/topology-aware-hints: auto

다음과 같은 annotations 붙어있어야 한다.

그럼먼저 우리는 Provisioner가 자동으로 노드를 Deprovisioning 하도록 설정하자.

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
spec:
  consolidation:
    enabled: true
  requirements:
    - key: karpenter.k8s.aws/instance-category
      operator: In
      values: [ t, m, c ]
  providerRef:
    name: default
---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: default
spec:
  subnetSelector:
    karpenter.sh/discovery: "${CLUSTER_NAME}"
  securityGroupSelector:
    karpenter.sh/discovery: "${CLUSTER_NAME}"

consolidation enabled 옵션은 Pod 의 리소스 요구조건에 따라서 Karpenter 가 알아서 노드를 스케줄링한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: host-spread
spec:
  replicas: 20
  selector:
    matchLabels:
      app: host-spread
  template:
    metadata:
      labels:
        app: host-spread
    spec:
      containers:
      - image: public.ecr.aws/eks-distro/kubernetes/pause:3.2
        name: host-spread
        resources:
          requests:
            cpu: "1"
            memory: 256M
      topologySpreadConstraints:
      - labelSelector:
          matchLabels:
            app: host-spread
        maxSkew: 2
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
      - labelSelector:
          matchLabels:
            app: host-spread
        maxSkew: 5
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: DoNotSchedule

topologyKey: kubernetes.io/zone maxSkew: 2는 하나의 호스트 즉 노드간 파드의 차이는 maxSkew: 2 를 초과할수 없다. topologyKey: topology.kubernetes.io/zone maxSkew: 5 zone 간 pod의 갯수는 maxSkew: 5 초과 할수 없으므로 하나의 zone에서만 pod가 스케줄링된다면 20개의 replicas 를 요청한다 해도 5개의 pod 만 스케줄링 된다.

AZ별로 제공하는 유형의 인스턴스 페일리가 달라서 특정 유형만 사용하려고 하면 프로비저닝 조건에 걸려서 스케줄링이 쉽지 않다.

karpenter 의 강력함은 테스트중에 확인할수 있는데, 42s 만에 Pod 가 Running 된다. 42초안에 Node도 프로비저닝 된다는 말이다.

2023-05-20T11:03:34.806Z	ERROR	controller.provisioner	Could not schedule pod, incompatible with provisioner "default", no instance type satisfied resources {"cpu":"1","memory":"256M","pods":"1"} and requirements karpenter.k8s.aws/instance-category In [c m t], kubernetes.io/os In [linux], kubernetes.io/arch In [amd64], karpenter.sh/provisioner-name In [default], karpenter.sh/capacity-type In [on-demand], topology.kubernetes.io/zone In [ap-northeast-2a]	{"commit": "d7e22b1-dirty", "pod": "default/host-spread-fbbf7c9d9-x4lfd"}

topologySpreadConstraints 옵션을 테스트하면서 느꼈는데, 여러 요인들로 잘 스케줄링하지 못한다.

두가지 조건에 의해서 pod는 모두다 스케줄링 되지 못하는데, 노드를 스케줄링하지 못해서 걸리기도 한다. 조건은 확실히 걸리긴한다.

k get node --show-labels | grep -v fargate | awk -F"topology.kubernetes.io/" '{print $3}' | sort
zone=ap-northeast-2a
zone=ap-northeast-2a
zone=ap-northeast-2a
zone=ap-northeast-2b
zone=ap-northeast-2b
zone=ap-northeast-2b
zone=ap-northeast-2c

다음과같이 노드가 a 3대 b 3대 c 1대 스케줄링 되면 모두 14개의 pod가 스케줄링된다. C zone때문에 두번째 조건에 걸리기 때문이다. 다양한 조건을 사용하면 이와 같이 균등하게 zone 에 스케줄링 하긴 어려운점이 있다. 적당히 조건을 걸어주면 잘 작동한다.

      - labelSelector:
          matchLabels:
            app: host-spread
        maxSkew: 2
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule

이 조건을 삭제하면 topology.kubernetes.io/zone maxSkew 5 만 남겨서 프로비저닝 해보면 비대칭으로 az 에 node가 프로비저닝 되지만 조건에만 맞는다면 pod를 모두 생성한다.

k get pod
NAME                          READY   STATUS    RESTARTS   AGE
host-spread-dd5f6c569-49ps7   1/1     Running   0          115s
host-spread-dd5f6c569-8772p   1/1     Running   0          115s
host-spread-dd5f6c569-9q2hn   1/1     Running   0          115s
host-spread-dd5f6c569-b68k2   1/1     Running   0          115s
host-spread-dd5f6c569-bfhv5   1/1     Running   0          115s
host-spread-dd5f6c569-bqqz2   1/1     Running   0          116s
host-spread-dd5f6c569-bsp8m   1/1     Running   0          115s
host-spread-dd5f6c569-dh8wx   1/1     Running   0          115s
host-spread-dd5f6c569-ffjdg   1/1     Running   0          115s
host-spread-dd5f6c569-jghmr   1/1     Running   0          115s
host-spread-dd5f6c569-jhbxg   1/1     Running   0          116s
host-spread-dd5f6c569-kf69q   1/1     Running   0          115s
host-spread-dd5f6c569-ksktv   1/1     Running   0          115s
host-spread-dd5f6c569-lbqmv   1/1     Running   0          115s
host-spread-dd5f6c569-mbf2g   1/1     Running   0          116s
host-spread-dd5f6c569-pd92p   1/1     Running   0          115s
host-spread-dd5f6c569-pgphc   1/1     Running   0          115s
host-spread-dd5f6c569-ph59g   1/1     Running   0          115s
host-spread-dd5f6c569-sdp7d   1/1     Running   0          115s
host-spread-dd5f6c569-tf8v9   1/1     Running   0          115s
(user-linuxer@myeks:default) [root@myeks-bastion-EC2 EKS]# k get node --show-labels | grep -v fargate | awk -F"topology.kubernetes.io/" '{print $3}' | sort
zone=ap-northeast-2a
zone=ap-northeast-2a
zone=ap-northeast-2a
zone=ap-northeast-2b
zone=ap-northeast-2b
zone=ap-northeast-2c

AZ 별로 균등하게 node를 프로비저닝 해야하는 방법이 필요하다.

https://github.com/aws/karpenter/issues/2572

일단 테스트한 결과와 git issues 를 보면 karpenter 가 topologySpreadConstraints 에 적절히 대응되지 않는것을 느낄수 있었다. 따라서 minDomains 옵션으로 3개의 zone을 지정도 해보았으나 썩 좋은 결과는 없었다.

따라서 다이나믹하게 Node를 프로비저닝하면서 사용할수는 없을것같고, 미리 Node를 프로비저닝 하는 구성에선 될법한데, 그건 Karpenter 의 패턴은 아니라고 느꼈다.

EKS-NodeLess-07-AWS-Karpenter-CRD

이제 카펜터의 CRD를 정리하고 어떻게 사용해야 하는지 이야기를 해보려고 한다. 거의 끝에 다왔다.

Karpenter Component 를 설명할때 CRD를 설명했었다.

다시 이야기 하자면 provisioners.karpenter.sh - provisioners - / awsnodetemplates.karpenter.k8s.aws -awsnodetemplates - 두가지를 셋팅해야 한다.

먼저 provisioners 를 이야기 해볼까 한다.

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
spec:
  requirements:
    - key: karpenter.k8s.aws/instance-category
      operator: In
      values: [c, m, r]
    - key: karpenter.k8s.aws/instance-generation
      operator: Gt
      values: ["2"]
  providerRef:
    name: default

https://karpenter.sh/v0.27.3/concepts/provisioners/

https://github.com/aws/karpenter/tree/main/examples

provisioners 의 사용은 이 두가지 링크를 참고하면 대부분 할수있는데, 간단하게 provisioners를 설명하자면 노드를 만들기 위한 조건을 정의하는거다.

예를 들자면 인스턴스 페밀리 / 인스턴스 CPU 갯수 / 하이퍼바이저 유형 / AZ / kubeletConfiguration 등을 설정할수 있다. 프로비저너는 노드를 생성하고 관리하는데 사용되는 CRD다

https://karpenter.sh/v0.27.5/concepts/node-templates/

apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: default
spec:
  subnetSelector:
    karpenter.sh/discovery: "${CLUSTER_NAME}"
  securityGroupSelector:
    karpenter.sh/discovery: "${CLUSTER_NAME}"

awsnodetemplates 은 필수 요소들이 있다. 그것이 Subnet / SecurityGroup 다.
서브넷 셀렉터는 subnet-id 혹은 서브넷에 연결된 특정 태그로 동작한다.
보안그룹또한 ID혹은 특정 태그다.

awsnodetemplates은 ami / userdata ebs 등들을 컨트롤하려 원하는 노드의 OS를 선택할수도 있다.

EKS-NodeLess-06-AWS-Karpenter-Install

이번 포스팅은 Karpenter 설치다.

설치하다 막히면 가이드를 보자.

https://karpenter.sh/v0.27.3/getting-started/migrating-from-cas/

karpenter 를 설치하기 전에 먼저 셋팅해야 할것들이 있다.

CLUSTER_NAME=myeks # your clouster name
AWS_PARTITION="aws" # aws or aws-gov or aws-cn
AWS_REGION="$(aws configure list | grep region | tr -s " " | cut -d" " -f3)"
OIDC_ENDPOINT="$(aws eks describe-cluster --name ${CLUSTER_NAME} \
    --query "cluster.identity.oidc.issuer" --output text)"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' \
    --output text)
export KARPENTER_VERSION=v0.27.3 # latast version

환경 변수 설정이다.

클러스터 이름 / 리전 / OIDC ENDPOINT / 어카운트 넘버 / karpenter 버전이 그것이다.

Karpenter 를 설치할때는 많은 권한을 요구로 한다.

echo '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}' > node-trust-policy.json
aws iam create-role --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
    --assume-role-policy-document file://node-trust-policy.json
aws iam attach-role-policy --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
    --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
aws iam attach-role-policy --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
    --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
aws iam attach-role-policy --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
    --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
aws iam attach-role-policy --role-name "KarpenterNodeRole-${CLUSTER_NAME}" \
    --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam create-instance-profile \
    --instance-profile-name "KarpenterNodeInstanceProfile-${CLUSTER_NAME}"
aws iam add-role-to-instance-profile \
    --instance-profile-name "KarpenterNodeInstanceProfile-${CLUSTER_NAME}" \
    --role-name "KarpenterNodeRole-${CLUSTER_NAME}"
cat << EOF > controller-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_ENDPOINT#*//}"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "${OIDC_ENDPOINT#*//}:aud": "sts.amazonaws.com",
                    "${OIDC_ENDPOINT#*//}:sub": "system:serviceaccount:karpenter:karpenter"
                }
            }
        }
    ]
}
EOF

aws iam create-role --role-name KarpenterControllerRole-${CLUSTER_NAME} \
    --assume-role-policy-document file://controller-trust-policy.json

cat << EOF > controller-policy.json
{
    "Statement": [
        {
            "Action": [
                "ssm:GetParameter",
                "ec2:DescribeImages",
                "ec2:RunInstances",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeLaunchTemplates",
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceTypes",
                "ec2:DescribeInstanceTypeOfferings",
                "ec2:DescribeAvailabilityZones",
                "ec2:DeleteLaunchTemplate",
                "ec2:CreateTags",
                "ec2:CreateLaunchTemplate",
                "ec2:CreateFleet",
                "ec2:DescribeSpotPriceHistory",
                "pricing:GetProducts"
            ],
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "Karpenter"
        },
        {
            "Action": "ec2:TerminateInstances",
            "Condition": {
                "StringLike": {
                    "ec2:ResourceTag/karpenter.sh/provisioner-name": "*"
                }
            },
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "ConditionalEC2Termination"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}",
            "Sid": "PassNodeIAMRole"
        },
        {
            "Effect": "Allow",
            "Action": "eks:DescribeCluster",
            "Resource": "arn:${AWS_PARTITION}:eks:${AWS_REGION}:${AWS_ACCOUNT_ID}:cluster/${CLUSTER_NAME}",
            "Sid": "EKSClusterEndpointLookup"
        }
    ],
    "Version": "2012-10-17"
}
EOF

aws iam put-role-policy --role-name KarpenterControllerRole-${CLUSTER_NAME} \
    --policy-name KarpenterControllerPolicy-${CLUSTER_NAME} \
    --policy-document file://controller-policy.json

환경 설정과 적절한 권한이 주어져 있다면 이과정에서 에러는 나지 않는다.

IAM까지 했다면 거의 다한거다.

IAM에는 KarpenterNodeRole 을 만들고 권한부여하고 KarpenterNodeInstanceProfile 을 Role에 추가한다. 이름과도 같이 프로비저닝된 Karpenter Node가 가지게 될 Role 이다.

또 KarpenterControllerRole 은 IRSA로 Karpenter Pod에 부여될 Role이다. 그다음에 필수로 있어야하는건 서브넷과 보안그룹이다. 인스턴스가 프로비저닝 되기위한 필수 조건이 바로 이것이다.

이 실습에선 Karpenter의 기본설정을 따라가지만 이해를 돕기위해 직접 태깅을 추가한다. 만일 NodeGroup를 사용하고 있고 전환하길 원한다면 가이드에 나온 스크립트를 사용해도 좋다.

나는 이렇게 추가했다 karpenter.sh/discovery = myeks

그다음엔 보안그룹에 태그를 추가해준다

서브넷과 동일하게 했다. 실제로 사용할때에 AWSNodeTemplate 을 작성할때 사용할 태그이다.

이렇게 진행했다면 이제 Karpenter 에서 프로비저닝한 노드가 클러스터에 Join이 가능하도록 허용해줘야 한다.

kubectl edit configmap aws-auth -n kube-system
apiVersion: v1
data:
  mapRoles: |
  - groups:
    - system:bootstrappers
    - system:nodes
    rolearn: arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME}
    username: system:node:{{EC2PrivateDNSName}}

mapRoles 아래에 넣는다. 변수부분 수정해서 넣어야한다.

이제 드디어 카펜터를 설치한다. 이과정에는 헬름이 필수다.

 helm template karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version ${KARPENTER_VERSION} \
 --namespace karpenter   \
 --set clusterName=${CLUSTER_NAME}   \
 --set settings.aws.clusterName=${CLUSTER_NAME}     \
 --set clusterEndpoint=${CLUSTER_ENDPOINT}     \
 --set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-${CLUSTER_NAME}     \
 --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:${AWS_PARTITION}:iam::${AWS_ACCOUNT_ID}:role/KarpenterControllerRole-${CLUSTER_NAME}"     \
 --set controller.resources.requests.cpu=1     \
 --set controller.resources.requests.memory=1Gi     \
 --set controller.resources.limits.cpu=1     \
 --set controller.resources.limits.memory=1Gi > karpenter.yaml

settings.aws.clusterName / clusterName 이두가지 옵션은 다른옵션이다. 헷갈리지 말자.

우리는 NodeLess 를 진행중이기 때문에 여기서 Karpneter 가이드와 다르게 간다.

aws eks create-fargate-profile --fargate-profile-name karpenter --cluster-name myeks --pod-execution-role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/AmazonEKSFargatePodExecutionRole --subnets "subnet-1" "subnet-2" "subnet-3"

이렇게 karpenter Fargate Profile을 생성하였다면 이제 Karpenter의 컴포넌트와 CRD를 같이 배포해줄 때다.

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

이렇게 배포하면 파게이트에 배포된 Karpenter 를 만날수 있다.

k get pod -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                                                        NOMINATED NODE   READINESS GATES
karpenter-5bffc6f5d8-p2pxh   1/1     Running   0          9d    192.168.12.183   fargate-ip-192-168-12-183.ap-northeast-2.compute.internal   <none>           <none>
karpenter-5bffc6f5d8-qgcwn   1/1     Running   0          9d    192.168.13.157   fargate-ip-192-168-13-157.ap-northeast-2.compute.internal   <none>           <none>

Karpenter는 버전에 따라 Pod내에 Container 가 2개인 경우가 있다. 이경우엔 컨트롤러와 웹훅용도의 컨테이너가 두개가 동작한다. 일정버전 이상에서만 Fargate에 프로비저닝 된다. 그냥 v0.27.3버전 이상쓰자.

하다가 안되면 대부분 유저 문제다.

https://karpenter.sh/v0.27.3/troubleshooting/

을보자.

설치가 드디어 완료됬다. 다음은 Karpenter 의 두가지 CRD에 대한 설명을 할것이다.

EKS-NodeLess-05-AWS-Karpenter-component

이제야 드디어 Karpenter까지 왔다. Karpenter의 구성요소부터 살펴보자!

  1. PodDisruptionBudget: PodDisruptionBudget은 클러스터의 안정성을 보장하기 위해 사용된다. 특정 서비스를 중단하지 않고 동시에 종료할 수 있는 Pod의 최대 수를 지정한다.
  2. ServiceAccount: Karpenter가 동작하려면 해당 권한을 가진 Kubernetes의 ServiceAccount가 필요하다. ServiceAccount는 Kubernetes 리소스에 대한 API 접근 권한을 제공한다.
  3. Secret-webhook-cert: Karpenter의 웹훅에 사용되는 TLS 인증서를 저장하는 Secret이다. 이를 통해 웹훅이 안전하게 통신할 수 있다.
  4. ConfigMap-logging: Karpenter의 로깅 설정을 저장하는 ConfigMap이다. 로깅 수준, 출력 형식 등을 지정할 수 있다.
  5. ConfigMap: Karpenter의 기본 설정을 저장하는 ConfigMap입니다. 예를 들어, 프로비저닝 로직과 관련된 설정을 저장할 수 있다.
  6. RBAC-related components: Karpenter의 동작에 필요한 권한을 정의하는 역할(Role), 클러스터 역할(ClusterRole), 역할 바인딩(RoleBinding), 클러스터 역할 바인딩(ClusterRoleBinding) 등이 포함된다. 이러한 리소스들은 Karpenter가 Kubernetes API를 사용하여 필요한 작업을 수행할 수 있도록 한다.
  7. Service: Karpenter 컨트롤러의 API를 노출하는 Kubernetes Service이다. 이를 통해 웹훅 및 기타 클라이언트가 Karpenter에 접속할 수 있다.
  8. Deployment: Karpenter의 컨트롤러 구성 요소를 정의하는 Deployment이다. 컨트롤러는 Karpenter의 주요 기능을 실행하는 프로세스이다.
  9. Webhooks: Karpenter는 웹훅을 사용하여 다양한 요청을 처리하고, 배포 전에 검증 또는 변형 작업을 수행한다. 이 웹훅을 설정하는 데 사용되는 리소스가 여기에 포함되어 있다.

이컴포넌트들은 helm 명령어로 karpenter의 template 을 outout 했을때 나온 manifast를 나열한것이다. 이 9가지의 컴포넌트 외에 두개의 CRD - CustomResourceDefinition - 가 있다.

  1. provisioners.karpenter.sh CRD는 Karpenter가 새로운 노드를 프로비저닝할 때 사용하는 정책을 정의한다. 이 CRD는 클러스터의 노드를 동적으로 스케일링하기 위한 설정을 포함하며, 예를 들어 어떤 유형의 인스턴스를 사용할지, 어떤 가용 영역에서 노드를 프로비저닝할지, 최대 노드 수는 얼마인지 등의 정보를 포함할 수 있다.
    Provisioner 오브젝트는 Karpenter에게 필요한 리소스 요구사항을 알려주고, Karpenter는 이 정보를 사용하여 클러스터를 효과적으로 관리하고 노드를 프로비저닝한다. Provisioner는 공급 업체, 가용 영역, 인스턴스 유형, 노드 수량 등에 대한 세부 정보를 포함하여 워크로드 요구사항에 가장 적합한 노드를 선택하는 데 도움이 된다.
    Karpenter는 이러한 Provisioner 오브젝트를 감시하고, 워크로드의 요구사항에 따라 적절한 시기에 새 노드를 프로비저닝한다. 수동으로 노드를 추가하거나 제거할 필요가 없다.
  2. awsnodetemplates.karpenter.k8s.aws CRD는 Karpenter가 AWS 클러스터에서 노드를 프로비저닝할 때 사용 한다. 이 CRD는 AWS에서 노드를 프로비저닝하는 데 필요한 세부 정보와 구성을 제공한다. 예를 들자면, 사용할 EC2 인스턴스 유형, 가용 영역, 보안 그룹, IAM 역할, 사용자 데이터, 노드 그룹 레이블 등의 정보가 포함될 수 있다. awsnodetemplates 를 사용하면 Karpenter가 AWS에서 노드를 프로비저닝하는 방법을 세밀하게 제어하고 구성할 수 있다. 이를 통해 클러스터의 노드가 클러스터의 워크로드 요구사항과 AWS의 특정 요구사항에 가장 잘 맞도록 조정할 수 있다.

한번 그럼 다이어그램으로 그려 봤다.

  1. 파드 스케줄링 요청: 파드가 생성되면 쿠버네티스 스케줄러는 이를 적합한 노드에 스케줄링하려고 시도한다. 만약 충분한 리소스를 가진 노드가 없다면, 파드는 Pending 상태가 된다.
  2. 파드 요구사항 분석: Karpenter는 Pending 상태의 파드를 주기적으로 검사하여 각 파드의 요구사항을 분석한다. 요구사항에는 CPU, 메모리, GPU, EBS 등의 리소스 요구사항이 포함될 수 있다.
  3. 프로비저너 선택: Karpenter는 파드 요구사항에 가장 적합한 프로비저너를 선택한다. 프로비저너는 provisioners.karpenter.sh CRD에 의해 정의되며, 어떤 유형의 노드를 프로비저닝할지, 어떤 가용 영역에서 노드를 프로비저닝할지 등의 정책을 포함한다.
  4. 노드 템플릿 선택: 선택된 프로비저너는 적합한 노드 템플릿을 선택하거나 생성한다. 노드 템플릿은 awsnodetemplates.karpenter.k8s.aws와 같은 클라우드 공급자 특정 CRD에 의해 정의될 수 있으며, 사용할 EC2 인스턴스 유형, 보안 그룹, IAM 역할 등의 세부 정보를 포함할 수 있다.
  5. 노드 프로비저닝: 선택된 노드 템플릿에 기초하여 새 노드가 프로비저닝된다. 이 과정은 클라우드 공급자 API를 사용하여 수행한다.
  6. 노드 등록: 프로비저닝된 노드는 쿠버네티스 클러스터에 등록된다
  7. 파드 러닝: 스케줄러는 Pending 상태의 Pod를 노드에 스케줄링하여 노드는 Runing 상태로 변경된다.

컴포넌트를 설명했다. 이 다음은 이제 설치를 진행해 보겠다.

EKS-NodeLess-04-AWS-LoadBalancer-Controller

AWS LoadBalancer Controller 도 Fargate에 올려야 한다.

비교적 간단한데, Controller Pod에 annotations 한줄만 추가하면된다.

먼저 추가할때 Policy 를 생성한다.

VPC_NAME=myeks-VPC // VPC NAME 으로 변경
CLUSTER_NAME=myeks
REGION=ap-northeast-2
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.7/docs/install/iam_policy.json

aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam_policy.json

POLICY_ARN=`aws iam list-policies | grep Arn | grep AWSLoadBalancerControllerIAMPolicy | awk -F\" '{print $4}'`
VPC_ID=$(aws ec2 describe-vpcs --query 'Vpcs[?contains(Tags[?Key==`Name`].Value[], `'$VPC_NAME'`) == `true`].[VpcId]' --output text)

이 Policy를 이용하여 eksctl에서 사용할거다. POLICY_ARN은 전체 Policy 에서 AWSLoadBalancerControllerIAMPolicy 의 arn을 추출한다.

eksctl create iamserviceaccount \
  --cluster=myeks \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --role-name AmazonEKSLoadBalancerControllerRole \
  --attach-policy-arn=$POLICY_ARN \
  --approve
  helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set region=$REGION \
  --set vpcId=$VPC_ID

로 보통 AWS LoadBalancer Controller 를 설치해 줘야 하지만 우리는 Fargate annotations 추가 한단계를 더 거쳐야 한다.

kubectl patch deployment aws-load-balancer-controller -n kube-system --type=json -p='[{"op": "add", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1fargate-profile", "value":"kube-system"}]'
kubectl rollout restart deployment aws-load-balancer-controller -n kube-system

다음과 같이 patch 를 하고 rollout restart 까지 하면 pod가 Fargate 로 생성된다.

k get pod -o wide
NAME                                            READY   STATUS    RESTARTS   AGE     IP               NODE                                                        NOMINATED NODE   READINESS GATES
aws-load-balancer-controller-76db948d9b-qzpsd   1/1     Running   0          46s     192.168.11.65    fargate-ip-192-168-11-65.ap-northeast-2.compute.internal    <none>           <none>
aws-load-balancer-controller-76db948d9b-t26qt   1/1     Running   0          88s     192.168.11.180   fargate-ip-192-168-11-180.ap-northeast-2.compute.internal   <none>           <none>

시간날때마다 Fargate 로 추가되어야 하는 에드온들을 Fargate 로 생성하는 방법을 작성하겠다.

읽어줘서 감사하다!