EKS Node 그룹


1. Amazon Linux 2

EKS Node Bootstrap

  1. AWS에서 제공하는 eks-node AMI를 사용하며 /etc/eks/bootstrap.sh 호출

  2. Bootstrap populates several files and make API queries to EKS

  3. Kubelet starts connects to the API server.

  4. Kubelet tries to register itself, role must match

  5. Kubelet creates Certificate Signing Request(CSR) and wait for it to be approved by EKS signer

  6. Certificated is signed, kubelet downloads it and serve traffic using 10250 port

    • https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/


EKS Node AMI

기본적으로 AMI가 설정된 Custom Launch Template을 사용하게되면 Userdata는 사용자에서 지정해야 한다. 그렇지 않은 모든 경우에는 EKS 서비스에서 자동으로 Injection 해준다.

Customized AMI Launch Template Userdata

Origin

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
set -ex
/etc/eks/bootstrap.sh my-cluster \
  --b64-cluster-ca certificate-authority \
  --apiserver-endpoint api-server-endpoint \
  --dns-cluster-ip service-cidr.10 \
  --container-runtime containerd \
  --kubelet-extra-args '--max-pods=my-max-pods-value' \
  --use-max-pods false

--==MYBOUNDARY==--

Optional을 제외한 적용가능 Userdata - 클러스터명만 들어가면 모두 bootstrap.sh 에서 찾음

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="

--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

set -ex
/etc/eks/bootstrap.sh bys-dev-eks-main

--==MYBOUNDARY==--

kubelet에 추가적인 파라미터 설정을 위서 kubelet-extra-args

/etc/eks/bootstrap.sh cluster_name --kubelet-extra-args '--node-labels=something=hello,somethingelse=bye --register-with-taints=taint1=true'

Bootstrap.sh에서는 기본적으로 kubelet을 구동하기 위한 설정 및 값들이 적용된다.
버전에 따라 적용되는 내용이 다를 수 있으니 해당 내용을 꼭 참고한다.



## 2. Amazon Linux 2023

  • AL2023은 IMDSv2가 기본 설정
  • AL2023은 cgroupv2 가 사용됨
  • AL2023 부터는 bootstrap.sh 을 사용하지 않으며 nodeadm 프로세스를 통해서 node initialization 프로세스가 시작된다. bootstrap.sh 에서는 describe를 통해 필수 파라미터를 조회하였지만 AL2023에서는 Throttling 방지를 위해 제공하지 않는다. 따라서 아래의 필수 파라미터를 모두 설정해야 한다.

Requirement Configuration

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    name: my-cluster
    apiServerEndpoint: https://example.com
    certificateAuthority: Y2VydGlmaWNhdGVBdXRob3JpdHk=
    cidr: 172.20.0.0/16

Example

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"

--BOUNDARY
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    name: my-cluster
    apiServerEndpoint: https://example.com
    certificateAuthority: Y2VydGlmaWNhdGVBdXRob3JpdHk=
    cidr: 172.20.0.0/16

--BOUNDARY--
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  kubelet:
    config:
      shutdownGracePeriod: 30s
      featureGates:
        DisableKubeletCloudCredentialProviders: true

--BOUNDARY--

Injected Userdata

# cat user-data.txt
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
  cluster:
    apiServerEndpoint: https://1111122222.sk1.ap-northeast-2.eks.amazonaws.com
    certificateAuthority: Y2VydGlmaWNhdGVBdXRob3JpdHk==
    cidr: 172.20.0.0/16
    name: my-cluster
  kubelet:
    config:
      shutdownGracePeriod: 30s
      featureGates:
        DisableKubeletCloudCredentialProviders: true
      maxPods: 17
      clusterDNS:
      - 172.20.0.10
    flags:
    - "--node-labels=eks.amazonaws.com/nodegroup-image=ami-057051082a0861071,eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup=ng-al2023"
  # instance:
  #   localStorage: 
  #     strategy: 
  # containerd:
  #   config: |
  #     [plugins."io.containerd.grpc.v1.cri".containerd]
  #     discard_unpacked_layers = false

--//--



EKS managed node upgrade

EKS 클러스터 버전을 업데이트 한 이 후, EKS managed node group에 대한 버전 업데이트를 진행하면 다음과 같은 스텝으로 진행된다.

  1. 노드 그룹의 maximum unavailable 설정 값 까지 업그레이드해야 하는 노드를 무작위로 선택한다.
  2. 노드에서 Pods를 Draining 한다. Pods가 15분 이내에 노드를 떠나지 않고 force 플래그가 없으면 PodEvictionFailure 오류와 함께 업그레이드 단계가 실패한다. 이 경우에는 force 플래그를 적용하여 Pods를 강제로 삭제할 수 있다.
  3. 모든 Pod가 eviction되면 노드를 Cordon하고 60초를 기다린다. 이 작업은 service controller가 이 노드에 새 요청을 보내지 않고 active 노드 목록에서 이 노드를 제거하도록 하기 위해 수행된다.
  4. Cordon 노드를 위해 Auto Scaling 그룹에 종료 요청을 보냅니다.
  5. 이전 버전의 시작 템플릿으로 배포된 노드 그룹에 노드가 없을 때까지 이전 업그레이드 단계를 반복합니다.

이 단계에서 PodEvictionFailure 오류가 발생하는 이유는 다음과 같을 수 있다.

  • Aggressive PDB (Aggressive PDB가 존재하는 경우)
    Aggressive PDB is defined on the Pod or there are multiple PDBs pointing to the same Pod.
  • Deployment tolerating all the taints (모든 Taint를 허용하는 Toleration을 가진 Pod가 존재하는 경우)
    Once every Pod is evicted, it’s expected for the node to be empty because the node is tainted in the earlier steps. However, if the deployment tolerates every taint, then the node is more likely to be non-empty, leading to Pod eviction failure.
  • Subnet의 IP부족으로 인하여 신규 파드가 생성이 되지 않는 경우에는 Pod eviction이 실패할 수 있다.
    NodeCreationFailure Couldn’t proceed with upgrade process as new nodes are not joining node group ng-v1



2. Bottlerocket

기본적으로 다음과 같은 ng-bottlerocket.yaml 파일을 생성하여 eksctl create nodegroup -f ng-bottlerocket.yaml 커맨드를 통해 생성할 수 있다.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: bys-dev-eks-sec
  region: ap-northeast-2

vpc:
  id: "vpc-0ca96cd5c37d3bae8"
  subnets:
    private:
      bys-dev-sbn-az1-app:
          id: "subnet-0ea5be4984975e8ed"
      bys-dev-sbn-az2-app:
          id: "subnet-0b4076508ce121c27"

managedNodeGroups:
  - name: ng-bottlerocket
    amiFamily: Bottlerocket
    instanceType: m5.large
    volumeSize: 20
    minSize: 2
    maxSize: 4
    desiredCapacity: 2
    privateNetworking: true
    subnets:
      - bys-dev-sbn-az1-app
      - bys-dev-sbn-az2-app
    ssh:
      allow: true
      publicKeyName: "bys-console"
    tags:
      auto-delete: "no"

Custom AMI를 사용하는 LT를 통해 Bottlerocket을 이용할 때는 다음과 같이 userdata를 정의할 수 있다. 참고. Description of settings

settings.kubernetes.cluster-name = 'bys-dev-eks-sec'
settings.kubernetes.api-server = 'https://A2FAB60F0DBB94CDCD057CE2227F2252.yl4.ap-northeast-2.eks.amazonaws.com'
settings.kubernetes.cluster-certificate = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1ESXdNekF5TWpjeU4xb1hEVE16TURFek1UQXlNamN5TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUM2CktwVURDVVRobUpaZjlKaVd3Ulg2cFREa1M3aWpFTVdpTWJoU0g1UlR5UDBHaVJCSWxYM1c4ZmZ4VlJTclN3T1YKQWJTU3FCV2NUUVcvM08vM3NUdGpORzZuUy94ZjhOaXZlVkRhOUtvY3FCZWVDODZjeEk5MU5EMnVoeUxGZmo5ZApCNDVJamdMeVc2N2ovRTdLQUxkaXhNd2lBMGhvbk44SVFZM0NJUWUrM0tHNWxCbkkzOFVlemJ3TUVFNmRVTGJGCkFZWERjMzhTQ3Byc1VxUkUzS1FlQ21TUk85andKbGJEcUhPWjF6djI0ekdseUZDS2hvdVJWVHQzQU9RT2ppZUkKYWNHUmNDK2ZoRDZlK1FpbHJyR05TcUdtaEI1RUZISVFYT2Zac0JBLytaTmkvNm82NEZOS3NsZFVlNGplNzJDYQp3Sng3TTBOZEpRNkNtYzJZT2IwQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZHSDdOOWJydDBjVVpLN1I1eHVaZHhNeVU5UEpNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRW9sQkJyaUdIc2RCRXlTU0xycQp1UjNmUnFOMW02c3BPR1oyZjVybzFmaExCMXV0LzVEdnNqKzUxaDlzOWpIampDcGhyUnQxVURMdWsyTlMweTFWCkhSMDdIU1dKL0tXSjBRRWNUQmxoYm1jbEYrTDNvcEtIcXZZYmx0OW1sdGszQXZEQlA5dzBQRG4wZ3E5dm85YXgKb0FJbGJHQTNtZ0xCZmlUQi9hR3VTQkd2Ry8vVXhxNWlsRWVGdGJKcU5yRkMxZEpleGU3U0l6THVmOUtSbmxOOApvSTd0UFRDUE9mcDQyLzFSVGE5VXBxL0hzN2JlaWxYQ05wNFhPRzNzYkdvNm5ZYnhUQlE3Q3BuTnE4NEFDeHNYCk9yMm56ek1GbHB0T0lob0VETFNJSVZCMEZ2Sm9oeTZXaXQvZjRrQkRYQlkvalYrVWRZK0ZNcmk5SkxGWTZidWkKMDhBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=='
settings.kubernetes.cluster-dns-ip = '172.20.0.10'
settings.kubernetes.max-pods = 20
settings.kubernetes.node-labels.'eks.amazonaws.com/sourceLaunchTemplateVersion' = '1'
settings.kubernetes.node-labels.'alpha.eksctl.io/cluster-name' = 'bys-dev-eks-sec'
settings.kubernetes.node-labels.'alpha.eksctl.io/nodegroup-name' = 'ng-bottlerocket'
settings.kubernetes.node-labels.'eks.amazonaws.com/nodegroup-image' = 'ami-0c0c53d11a3f898ea'
settings.kubernetes.node-labels.'eks.amazonaws.com/capacityType' = 'ON_DEMAND'
settings.kubernetes.node-labels.'eks.amazonaws.com/nodegroup' = 'ng-bottlerocket'
settings.kubernetes.node-labels.'eks.amazonaws.com/sourceLaunchTemplateId' = 'lt-05fd1822cf902ddbe'
settings.host-containers.admin.enabled = true

Bottlerocket log 확인하는 방법

# Access admin container  
$ apiclient exec admin bash

# Get a full root shell
$ sudo sheltie

# Obtain an archive of log 
$ logdog
......
logs are at: /var/log/support/bottlerocket-logs.tar.gz



5. EKS Windows node

Windows 노드를 사용하기 위해서는 Windows 문서를 확인할 필요가 있다.

Summary

  • 하나 이상의 Linux 노드를 반드시 포함해야한다.
  • Windows 노드의 group으로 eks:kube-proxy-windows 반드시 포함되어야 한다.

    mapRoles:
    ----
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::558846430793:role/eksctl-bys-dev-eks-win-nodegroup-NodeInstanceRole-1MIR40S58HUY7
      username: system:node:
    - groups:
      - system:bootstrappers
      - system:nodes
      - eks:kube-proxy-windows
      rolearn: arn:aws:iam::558846430793:role/eksctl-bys-dev-eks-win-nodegroup-NodeInstanceRole-VD0XFSFTRF2Q
      username: system:node:
    
  • Can’t use SGP
  • Can’t use custom networking
  • Can’t use IPv6
  • Number of Pods: Number of private IPv4 addresses for each interface on the node - 1

  • amazon-vpc-cni ConfigMap 배포 (중요)
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: amazon-vpc-cni
      namespace: kube-system
    data:
      enable-windows-ipam: "true"
    
    • 이 작업을 진행하지 않으면 파드에 IP가 할당되지 않으며 아래와 같은 오류메세지가 확인된다
      Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6a614a0ee5842556403f696a9d6b33347e622a1e5442ca5e07c29c90f925954d": plugin type="vpc-bridge" name="vpc" failed (add): failed to parse Kubernetes args: failed to get pod IP address windows-server-iis-797bf57f76-vwxgz: error executing k8s connector: error executing connector binary: exit status 1 with execution error: pod windows-server-iis-797bf57f76-vwxgz does not have label vpc.amazonaws.com/PrivateIPv4Address
      
    • IIS 컨테이너 기동시 약 1분 30초 정도의 시간이 소요 되는 것을 확인

Creation

eksctl create nodegroup \
    --region ap-northeast-2 \
    --cluster eks-win \
    --name ng-win-v1 \
    --node-type m5.large \
    --nodes 2 \
    --nodes-min 2 \
    --nodes-max 2 \
    --managed=false \
    --node-ami-family WindowsServer2022FullContainer

managedNodeGroups:
  - name: ng-win-v1
    amiFamily: WindowsServer2022FullContainer
    instanceType: m5.large
    volumeSize: 80
    minSize: 2
    maxSize: 2
    desiredCapacity: 2
    privateNetworking: true
    subnets:
      - subnet1
      - subnet2
    ssh:
      allow: true
      publicKeyName: "keypair"
    tags:
      auto-delete: "no"




  • References
    1. https://kubernetes.io/docs/concepts/architecture/nodes/
    2. Kubelet flags - https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet
    3. Kube-controller-manager flags - https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/
    4. Bottlerocket - https://github.com/bottlerocket-os/bottlerocket/blob/develop/README.md

Tag: [ aws  cloud  eks  node  al2023  ]