[AEWS] EKS 아키텍처와 Private EKS 클러스터 배포하기

1
2
AWS EKS Workshop Study (=AEWS)는 EKS Workshop 실습 스터디입니다.
CloudNet@ Gasida(가시다)님이 진행하시며,공개된 AWS EKS Workshop을 기반으로 진행하고 있습니다.

EKS ?

Amazon Elastic Kubernetes Service(EKS)는 AWS에서 제공하는 관리형 Kubernetes 서비스다. EKS를 사용하면 Kubernetes 클러스터를 생성, 운영 및 유지 관리할 수 있다.

<a href="https://catalog.us-east-1.prod.workshops.aws/workshops/9c0aa9ab-90a9-44a6-abe1-8dff360ae428/ko-KR/10-intro/200-eks">https://catalog.us-east-1.prod.workshops.aws/workshops/9c0aa9ab-90a9-44a6-abe1-8dff360ae428/ko-KR/10-intro/200-eks</a>

https://catalog.us-east-1.prod.workshops.aws/workshops/9c0aa9ab-90a9-44a6-abe1-8dff360ae428/ko-KR/10-intro/200-eks

EKS의 주요 특징은 다음과 같다.

  • 관리형 서비스: EKS는 Kubernetes 컨트롤 플레인이나 데이터 플레인를 설치, 운영 및 유지 관리시켜주는 서비스이다. 사용자가 인프라 설치, 운영, 유지 관리를 할 필요가 없다.
  • 높은 가용성: EKS는 여러 AWS 가용 영역(데이터 센터의 물리적 위치)에 걸쳐 컨트롤 플레인과 데이터 플레인를 분산시켜 서비스의 안정성을 제공한다. 이는 단일 장재 지점을 제거하도록 설계되었으며 자동 관리되는 컨트롤 플레인의 경우 리전 내 개별 가용 영역에서 최소 2개이상의API 서버 노드를 실행한다.
  • AWS 서비스 통합: 타 AWS ECR(도커 레지스트리), ELB(네트워크 로드밸런싱), IAM(보안), VPC(네트워크)와 같은 AWS 서비스와 통합되어 컨테이너 이미지 저장, 로드 밸런싱, 인증 및 격리를 쉽게 관리할 수 있다.
  • 오픈 소스 Kubernetes 호환성: 최신 버전의 오픈 소스 Kubernetes를 사용하여 기존 플러그인과 도구를 그대로 활용할 수 있다.
  • 지원 버전 : 23년 4월 현재 kubernetes 버전 중 1.22~1.26 지원을 지원 중이다. 버전 출시 주기는 연 3회이며 각 버전은 12개월 동안 지원된다. 지원이 끝난 버전들은 자동으로 EKS가 업데이트시킨다.



EKS 아키텍처

EKS 아키텍처는 크게 컨트롤 플레인(마스터 노드)과 데이터 플레인(워커 노드)로 나뉜다. 아키텍처 그림은 스터디에서 공유해주신 2022 AWS 마이그레이션 요점 정리 pdf를 기반으로 살펴보겠다.

컨트롤 플레인

EKS에서는 Kubernetes 컨트롤 플레인의 가용성과 내구성을 손상시킬 수 있는 단일 장애 지점을 제거하도록 설계되었다. 컨트롤 플레인은 API 서버 노드, etcd 클러스터로 구성된다.

control2.png

  • API 서버: 쿠버네티스 클러스터의 모든 작업을 처리하고 상태를 저장하는 중앙 허브이다. RESTful API를 통해 통신하며, 사용자 요청에 따라 클러스터의 상태를 변경하거나 정보를 반환시켜준다.
  • etcd: etcd는 쿠버네티스 클러스터에서 사용하는 분산 키-값 저장소이다. 쿠버네티스의 모든 설정 데이터와 클러스터 상태 정보를 저장한다.
  • 각 API 서버와 etcd는 개별 가용 영역에서 최소 2개 이상의 클러스터로 구성되어 실행된다. 이를 통해 단일 가용 영역의 이벤트가 EKS 클러스터 가용성에 영향을 미치지 않는다.
  • 각 가용 영역에서는 NAT 게이트웨이를 통해 프라이빗 서브넷에서 실행되며 사용자는 해당 노드에 접근할 수 없다. 또한 API 서버는 NLB로 ETCD는 ELB를 사용하여 컨트롤 플레인 서버의 부하를 분산시킨다.
  • API 서버와 ETCD 클러스터는 오토스케일링 그룹으로 구성되어 조건(클러스터 규모 , API 서버 및 etcd에 대한 요청 증가)에 따라 자동으로 리소스가 확장된다.

데이터 플레인

데이터 플레인은 쿠버네티스 클러스터 내에서 워크로드를 실행하고 관리되는 서버다. 아키텍처는 다음과 같다.

data2.png

  • kubelet: 노드 에이전트이다. kubelet은 API 서버로부터 파드를 실행하고 관리해야 하는 명령을 받아 서버에 반영시켜준다. 또 실행 중인 파드와 컨테이너를 관리하고, 상태를 모니터링하며, 필요한 경우 API 서버에 상태 정보를 보고하는 역할을 수행한다.
  • kube-proxy: 네트워크 프록시 및 로드 밸런서이다. kube-proxy는 쿠버네티스 서비스에 대한 요청을 적절한 파드로 전달하고, 파드 간의 통신을 관리한다. EKS 의 기본 네트워크 CNI로는 VPC-CNI가 실행된다.


또한, 사용자의 요구에 맞게 데이터 플레인의 구성 옵션을 설정할 수 있다. 구성 옵션별 세부 정보는 다음 그림과 같다.

SRM-MNG.jpeg

<a href="https://aws.github.io/aws-eks-best-practices/reliability/docs/">https://aws.github.io/aws-eks-best-practices/reliability/docs/</a>

https://aws.github.io/aws-eks-best-practices/reliability/docs/

그림에서 비교한 것과 같이 사용자 책임 레벨에 따라 3가지로 구분된다.

  • Self Managed Workers :사용자가 직접 노드를 구성하고 관리하는 방식이다. 구체적으로는 Custom AMI를 통해 작업자 노드를 생성하며 AMI와 노드의 패치 및 업그레이드를 사용자가 직접 관리한다.
  • Managed Node groups : AWS가 노드를 자동으로 프로비저닝하고, 업데이트 및 유지 관리를 처리한다.
  • EKS Fargate : 서버리스 컴퓨팅 옵션이다. Micro VM를 이용하여 Pod별 VM이 할당된다. AWS가 설정에 맞게 파드 레벨의 스케일링을 자동으로 처리한다.




Cluster Endpoint Access

EKS 클러스터의 API 서버에 대한 접근을 제어하는 설정이다. 클러스터 엔드포인트를 설정함으로써, 클러스터에 대한 접근을 필요한 범위로 제한하여 보안을 강화할 수 있다. 옵션은 3가지 존재한다.

  • Public : 클러스터 엔드포인트가 Public IP로 할당되어 인터넷에서 접근이 가능한 설정이다. 클러스터 내부에서도 IGW 를 통해 Public IP와 통신한다.

    public.png

    해당 옵션은 AWS 콘솔에서 확인 가능하다. Public 설정시 다음과 같이 접근이 가능하다.

    public2.png

    public1.png

    위와 같이 인터넷을 통해 접근이 가능하여 접근성을 높일 수 있다.

  • Public Private : 워크노드 내부에서는 Private(VPC 내부망에서만 접근 가능)로, API 접근은 Public(인터넷을 통한 접근 가능)으로만 통신이 가능하다. 워크 노드에서 민감데이터 노출을 막기 위해 Private subnet(사설망)에 위치시킨 경우 사용할 수 있는 옵션이다.

    public-private.png

  • Private : API 서버 엔드포인트 또한 허용된 VPC에서만 접근이 가능한 설정이다.

    private.png

    해당 옵션으로 사용시 API Server URL은 Route53 privated hosted zone으로 변경되어 VPC 내부에서만 통신이 가능하다. 해당 URL은 AWS 콘솔의 EKS나 kubectl -v=6 옵션으로 확인이 가능하다.

    1
    2
    3
    4
    
    # API Server URL(Route53 privated hosted zone)
    (terraform-eks@my-eks:N/A) [root@myeks-host example]# kubectl get pods -A  -v=6
    I0428 14:26:36.609385   27821 loader.go:374] Config loaded from file:  /root/.kube/config
    I0428 14:26:37.276259   27821 round_trippers.go:553] GET https://232B71CC97744BC94C09D40801D073B0.yl4.ap-northeast-2.eks.amazonaws.com/api/v1/pods?limit=500 200 OK in 661 milliseconds
    

    [https://232B71CC97744BC94C09D40801D073B0.yl4.ap-northeast-2.eks.amazonaws.com](https://232B71CC97744BC94C09D40801D073B0.yl4.ap-northeast-2.eks.amazonaws.com) 가 API server URL이다. 해당 URL은 EKS 컨트롤 노드가 소유하고 있는 네트워크 인터페이스를 통해 통신된다. 통칭 EKS owned ENI는 AWS 콘솔에서 확인이 가능하다.

    eni.png

    네트워크 인터페이스에서 클러스터를 배포한 VPC를 입력하여 인스턴스 ID가 존재하지 않고(- 표시), 설명에 Amazone EKS cluster-name 으로 표시된 것이 EKS owned ENI 이다. 인스턴스 소유자 또한 AWS 소유자 번호가 다른 것을 확인할 수 있는데 AWS가 직접 관리하는 컨트롤 노드의 소유자 번호이기 때문이다.




EKS 배포 방식

EKS 배포 방식은 Manual, Command line utility, Infrastructure as Code 에 따라 구성할 수 있다.

  • Manual : AWS Management Console 에서 직접 구성
  • Command line utility : 커맨드 명령어를 통한 구성 (AWS CLI, eksctl)
  • Infrastructure as Code : 코드를 통한 구성(Terrafrom, AWS CDK 등)

이번 블로그 글에서는 eksctl 를 통해 클러스터를 구성하겠다.

eksctl ?

Amazon EKS (Elastic Kubernetes Service) 클러스터를 생성하고 관리하기 위한 명령줄 인터페이스(CLI) 툴이다. eksctl를 통해 클러스터를 구성 및 삭제, 업데이트할 수 있으며 VPC, 서브넷, 리소스 생성을 위한 정책 생성 등의 작업을 쉽게 처리할 수 있다. 공식 문서를 통해 다양한 인프라 환경에서의 EKS 구성을 참고할 수 있다.

아쉬운 점은 EKS에서만 구축이 된다는 점이다. 추후 다른 클러스터나 멀티 클러스터 구축시 다른 툴을 사용하자.

추가로 사용해보니.. eksctl는 형상관리가 불가능하다. 한 번 배포 이후에는 eksctl 구성 파일을 통해 추가 작업이 불가능하다. eksctl를 통해 기존의 리소스 변경이 불가능하며 추가만 가능하다.

eksctl 를 통한 EKS 배포

eksctl를 통해 EKS Private Cluster 를 배포하겠다. 구성 아키텍처는 다음과 같다.

eks-arch.png

노드 그룹은 unmanaged node group 으로, Cluster endpoint 는 Private로 구성하였다. 아키텍처에서 화살표는 통신 아키텍처이다.

  • EKS Admin(빨간선) : 베스천 서버를 통해 EKS API Server로 접근하여 EKS를 관리하는 과정이다.
  • Private Worker node(보라선): Private cluster 로 구성되어 EKS control node ENI를 통해 EKS Control node와 통신한다.

배포 과정은 다음과 같이 진행하겠다.

  1. cloudformation를 통한 베스천 서버 배포
  2. eksctl를 통한 EKS 배포



1. AWS Cloudformation를 통한 베스천 서버 배포

AWS Cloudformation을 통해 VPC 구성 및 베스천 서버를 배포하고 eksctl 설치 및 관리 패키지를 설치하겠다. Cloudformation 스크립트는 스터디에서 공유해주신 것을 기반으로 내용을 추가하였다.

1
2
# yaml 파일 다운로드
curl -O https://github.com/HanHoRang31/blog-share/blob/main/aews-eksctl/cloudformation-bastion.yaml

구성 코드에서 Private 클러스터 구축을 위한 중요 부분과 베스천 서버의 패키지 구성 정보를 확인하겠다. 먼저 Private 클러스터 구축을 위해 필요한 보안 그룹은 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# cloudformation-bastion
# 파라미터 
...
# 베스천 서버 보안 그룹 설정 
# EKSCTL-Host
  EKSEC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: eksctl-host Security Group
      VpcId: !Ref EksVPC
      Tags:
        - Key: Name
          Value: !Sub ${ClusterBaseName}-HOST-SG
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp: !Ref SgIngressSshCidr

# 베스천 서버에서 EKS 배포를 위한 보안 그룹 설정 
  ControlPlaneSecurityGroup:
    DependsOn:
      - EKSEC2SG
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Cluster communication with worker nodes
      VpcId: !Ref EksVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          SourceSecurityGroupId: !Ref EKSEC2SG
      Tags:
        - Key: Name
          Value: !Sub ${ClusterBaseName} Control Plane Security Group
...

Private Cluster 구축으로 중요 사항은 두 가지이다.

  • EKSEC2SG : 베스천 서버에 대한 보안 그룹 설정 부분이다. 해당 부분을 통해 베스천 서버에 접근할 수 있는 IP 범위를 제한할 수 있다. 코드 내 SgIngressSshCidr 에서 지정한 IP를 제한하며 해당 값은 배포 명령어로 override 을 통해 값을 수정할 수 있다.
  • ControlPlaneSecurityGroup : 컨트롤 플레인과 워크 노드간 통신을 위한 보안 그룹 설정 부분이다. 코드에서는 베스천 서버와의 통신을 위해 443 포트의 보안 그룹을 추가하였다.

userdata를 확인하면 베스천 서버에 설치되는 패키지를 확인할 수 있다. 설치 명령어는 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
...
UserData:
Fn::Base64:
!Sub |
  #!/bin/bash
  hostnamectl --static set-hostname "${ClusterBaseName}-host"

  # Config convenience
  echo 'alias vi=vim' >> /etc/profile
  echo "sudo su -" >> /home/ec2-user/.bashrc

  # Change Timezone
  sed -i "s/UTC/Asia\/Seoul/g" /etc/sysconfig/clock
  ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

  # Install Packages
  cd /root
  yum -y install tree jq git htop lynx

  # Install kubectl & helm
  #curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.26.2/2023-03-17/bin/linux/amd64/kubectl
  curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.25.7/2023-03-17/bin/linux/amd64/kubectl
  install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
  curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

  # Install eksctl
  curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
  mv /tmp/eksctl /usr/local/bin

  # Install aws cli v2
  curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
  unzip awscliv2.zip >/dev/null 2>&1
  sudo ./aws/install
  complete -C '/usr/local/bin/aws_completer' aws
  echo 'export AWS_PAGER=""' >>/etc/profile
  export AWS_DEFAULT_REGION=${AWS::Region}
  echo "export AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" >> /etc/profile

  # Install YAML Highlighter
  wget https://github.com/andreazorzetto/yh/releases/download/v0.4.0/yh-linux-amd64.zip
  unzip yh-linux-amd64.zip
  mv yh /usr/local/bin/

  # Install krew
  curl -LO https://github.com/kubernetes-sigs/krew/releases/download/v0.4.3/krew-linux_amd64.tar.gz
  tar zxvf krew-linux_amd64.tar.gz
  ./krew-linux_amd64 install krew
  export PATH="$PATH:/root/.krew/bin"
  echo 'export PATH="$PATH:/root/.krew/bin"' >> /etc/profile

  # Install kube-ps1
  echo 'source <(kubectl completion bash)' >> /etc/profile
  echo 'alias k=kubectl' >> /etc/profile
  echo 'complete -F __start_kubectl k' >> /etc/profile

  git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1
  cat <<"EOT" >> /root/.bash_profile
  source /root/kube-ps1/kube-ps1.sh
  KUBE_PS1_SYMBOL_ENABLE=false
  function get_cluster_short() {
    echo "$1" | cut -d . -f1
  }
  KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
  KUBE_PS1_SUFFIX=') '
  PS1='$(kube_ps1)'$PS1
  EOT

  # Install krew plugin
  kubectl krew install ctx ns get-all  # ktop df-pv mtail tree

  # Install Docker
  amazon-linux-extras install docker -y
  systemctl start docker && systemctl enable docker
  
  # Install nerdctl 
  wget https://github.com/containerd/nerdctl/releases/download/v1.3.1/nerdctl-1.3.1-linux-amd64.tar.gz
  tar -xzf nerdctl-1.3.1-linux-amd64.tar.gz 
  sudo mv nerdctl /usr/local/bin/

  # CLUSTER_NAME
  export CLUSTER_NAME=${ClusterBaseName}
  echo "export CLUSTER_NAME=$CLUSTER_NAME" >> /etc/profile

  # Create SSH Keypair
  ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa
...
  • 몇 가지 alias와 환경 설정을 추가하고, 시스템의 시간대를 Asia/Seoul로 변경한다.
  • 필요한 패키지들(tree, jq, git, htop, lynx, kubectl, helm, eksctl, AWS CLI v2, yh, krew, docker, nerdctl, kube-ps1)을 설치한다.
  • 클러스터 이름을 설정하고, SSH 키 페어를 생성한다.

배포는 다음과 같은 파라미터를 통해 진행하였다.

1
aws cloudformation deploy --template-file myeks-1week.yaml --stack-name myeks --parameter-overrides KeyName=eks-terraform-key --region ap-northeast-2

배포 완료 후 다음의 명령어를 통해 베스천 서버에 접근하고 EKS 배포 과정으로 넘어가겠다.

1
ssh -i eks-terraform-key.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)



2. eksctl를 통한 EKS 배포

베스천 서버에 접속하여 AWS 인증 정보를 입력하자.

1
2
3
4
5
[root@myeks-host example]# aws configure 
AWS Access Key ID [None]: ACCESS-KEY
AWS Secret Access Key [None]: SECRET-KEY
Default region name [None]:  ap-northeast-2
Default output format [None]: json

AWS 인증 정보 입력 후 eksctl를 통해 private cluster 구성하자. 구성 내용은 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: my-eks
  region: ap-northeast-2 # 지역 설정(서울) 
  version: "1.25"  # 클러스터 버전

vpc:
  id: vpc-05a960a0837da1328 # 베스천 서버 VPC ID (아래 내용 참고)
  cidr: 192.168.0.0/16 # # 베스천 서버 VPC CIDR (아래 내용 참고)
  securityGroup: sg-0c59ddf1a9a73edc9
  nat:
    gateway: HighlyAvailable

  subnets:
    public: 
      public-2a:
        id: subnet-06391e7ab56a8ae9c # 서브넷 ID (아래 내용 참고)
        cidr: 192.168.1.0/24 # 서브넷 CIDR (아래 내용 참고)
      public-2c:
        id: subnet-00c193bd6e515a79b # 서브넷 ID (아래 내용 참고)
        cidr: 192.168.2.0/24 # 서브넷 CIDR (아래 내용 참고) 
    private:
      private-2a:
        id: subnet-02d592518f7ae0755 # 서브넷 ID (아래 내용 참고) 
        cidr: 192.168.3.0/24 # 서브넷 CIDR (아래 내용 참고) 
      private-2c:
        id: subnet-0dcfc3b165e7b355d # 서브넷 ID (아래 내용 참고) 
        cidr: 192.168.4.0/24 # 서브넷 CIDR (아래 내용 참고) 

  clusterEndpoints:  # 클러스터 엔드포인트 액세스 설정 부분 
    publicAccess: false  # 공용 액세스 비활성화
    privateAccess: true  # 사설 액세스 활성화

nodeGroups: 
  - name: ng-1
    instanceType: m5.xlarge  # 인스턴스 유형
    desiredCapacity: 3  # 원하는 노드 수
    privateNetworking: true  # 사설 네트워크 사용
    ssh:
      publicKeyName: ec2-key # ec2 보안 키 
    availabilityZones:
      - ap-northeast-2a
      - ap-northeast-2c
    iam:
      withAddonPolicies:
        imageBuilder: true  # 이미지 빌더 정책 활성화
        albIngress: true  # ALB 인그레스 정책 활성화
        cloudWatch: true  # CloudWatch 정책 활성화
        autoScaler: true  # 오토 스케일러 정책 활성화
    instanceName: EKS-WORKER-TEST
    volumeSize: 30  # 볼륨 크기 설정

구성 내용 중 VPC, Subnet ID, CIDR은 AWS Console에서 확인할 수 있다. VPC → Resource Road Map 을 통해 연결된 서브넷 및 라우팅 정보를 확인할 수 있다. 정보가 필요한 리소스를 클릭하면 바로 넘어가진다.

vpc-.png

subnet.png

보안 그룹 ID 는 EC2→ 보안 그룹에서 Name myeks Control Plane Security Group 에서 확인할 수 있다.

control-sg.png

구성 입력 후 eksctl 명령어를 통해 클러스터를 구축하겠다.

1
eksctl create cluster -f private-cluster.yaml

create-cluster.png

배포는 약 20분정도 소요된다. 구성 후 클러스터 API 서버 접근을 위한 인증 정보가 필요하다. 인증 정보는 다음의 eksctl 명령어를 통해 저장할 수 있다.

1
2
3
(terraform-eks@my-eks-2:N/A) [root@myeks-host example]# eksctl utils write-kubeconfig --name my-eks --region ap-northeast-2
Flag --name has been deprecated, use --cluster
2023-04-29 19:59:36 []  saved kubeconfig as "/root/.kube/config"

배포 확인을 위해 파드 리스트를 확인해보자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(terraform-eks@my-eks:N/A) [root@myeks-host example]# kubectl get pods -A 
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
kube-system   aws-node-4kms4             1/1     Running   0          109m
kube-system   aws-node-8r2fq             1/1     Running   0          109m
kube-system   aws-node-hsgmr             1/1     Running   0          109m
kube-system   aws-node-nf758             1/1     Running   0          109m
kube-system   coredns-76b4dcc5cc-4mbw2   1/1     Running   0          127m
kube-system   coredns-76b4dcc5cc-vfpgp   1/1     Running   0          127m
kube-system   kube-proxy-68vgc           1/1     Running   0          109m
kube-system   kube-proxy-lgltk           1/1     Running   0          109m
kube-system   kube-proxy-lqngn           1/1     Running   0          109m
kube-system   kube-proxy-vl4tb           1/1     Running   0          109m

앞서 공유한 베스천 서버 인프라 구성 파일과 eks 구성 파일을 통해 진행하면 아무 문제 없이 구성이 완료될 것이다. 아래 내용은 필자가 private cluster 구성 중 생긴 에러로 트러블슈팅한 내용이다. 혹시 구성 중 에러가 발생한다면 다음 내용을 참고하자.




트러블슈팅

클러스터 배포 중 timeout 발생과 워크 노드 클러스터 조인

워크 노드 조인 중 에러가 생긴 에러이다. 에러 메세지는 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(N/A:N/A) [root@myeks-host example]# eksctl create cluster -f private-cluster.yaml 
2023-04-28 12:24:04 []  eksctl version 0.138.0
2023-04-28 12:24:04 []  using region ap-northeast-2
2023-04-28 12:24:04 [!]  warning, having public access disallowed will subsequently interfere with some features of eksctl. This will require running subsequent eksctl (and Kubernetes) commands/API calls from within the VPC.  Running these in the VPC requires making updates to some AWS resources.  See: https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html for more details
2023-04-28 12:24:05 []  using existing VPC (vpc-06210c8560709c761) and subnets (private:map[private-2a:{subnet-0cceb4f7117a82214 ap-northeast-2a 192.168.3.0/24 0 } private-2c:{subnet-0c19b6b58320fce0a ap-northeast-2c 192.168.4.0/24 0 }] public:map[public-2a:{subnet-023a838543987d725 ap-northeast-2a 192.168.1.0/24 0 } public-2c:{subnet-09e710a10ef0fb5ef ap-northeast-2c 192.168.2.0/24 0 }])
2023-04-28 12:24:05 [!]  custom VPC/subnets will be used; if resulting cluster doesn't function as expected, make sure to review the configuration of VPC/subnets
2023-04-28 12:24:05 [ℹ]  nodegroup "ng-2" will use "ami-0fdcb707922882aef" [AmazonLinux2/1.25]
2023-04-28 12:24:05 [ℹ]  using EC2 key pair "eks-terraform-key"
2023-04-28 12:24:05 [ℹ]  using Kubernetes version 1.25
2023-04-28 12:24:05 [ℹ]  creating EKS cluster "my-eks" in "ap-northeast-2" region with un-managed nodes
2023-04-28 12:24:05 [ℹ]  1 nodegroup (ng-2) was included (based on the include/exclude rules)
2023-04-28 12:24:05 [ℹ]  will create a CloudFormation stack for cluster itself and 1 nodegroup stack(s)
2023-04-28 12:24:05 [ℹ]  will create a CloudFormation stack for cluster itself and 0 managed nodegroup stack(s)
2023-04-28 12:24:05 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-2 --cluster=my-eks'
2023-04-28 12:24:05 [ℹ]  Kubernetes API endpoint access will use provided values {publicAccess=false, privateAccess=true} for cluster "my-eks" in "ap-northeast-2"
2023-04-28 12:24:05 [ℹ]  CloudWatch logging will not be enabled for cluster "my-eks" in "ap-northeast-2"
2023-04-28 12:24:05 [ℹ]  you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=ap-northeast-2 --cluster=my-eks'
2023-04-28 12:24:05 []  
2 sequential tasks: { create cluster control plane "my-eks", 
    2 sequential sub-tasks: { 
        wait for control plane to become ready,
        create nodegroup "ng-2",
    } 
}
2023-04-28 12:24:05 []  building cluster stack "eksctl-my-eks-cluster"
2023-04-28 12:24:05 []  deploying stack "eksctl-my-eks-cluster"
2023-04-28 12:24:35 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:25:05 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:26:05 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:27:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:28:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:29:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:30:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:31:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:32:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:33:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:34:06 []  waiting for CloudFormation stack "eksctl-my-eks-cluster"
2023-04-28 12:36:06 []  building nodegroup stack "eksctl-my-eks-nodegroup-ng-2"
2023-04-28 12:36:06 []  --nodes-min=2 was set automatically for nodegroup ng-2
2023-04-28 12:36:06 []  --nodes-max=2 was set automatically for nodegroup ng-2
2023-04-28 12:36:07 []  deploying stack "eksctl-my-eks-nodegroup-ng-2"
2023-04-28 12:36:07 []  waiting for CloudFormation stack "eksctl-my-eks-nodegroup-ng-2"
2023-04-28 12:39:43 []  waiting for CloudFormation stack "eksctl-my-eks-nodegroup-ng-2"
2023-04-28 12:39:43 []  waiting for the control plane to become ready
2023-04-28 12:39:44 []  saved kubeconfig as "/root/.kube/config"
2023-04-28 12:39:44 []  no tasks
2023-04-28 12:39:44 []  all EKS cluster resources for "my-eks" have been created
2023-04-28 12:39:44 []  adding identity "arn:aws:iam::955963799952:role/eksctl-my-eks-nodegroup-ng-2-NodeInstanceRole-283XKKCXM9GT" to auth ConfigMap
2023-04-28 12:39:44 []  nodegroup "ng-2" has 0 node(s)
2023-04-28 12:39:44 []  waiting for at least 2 node(s) to become ready in "ng-2"

Error: timed out waiting for at least 2 nodes to join the cluster and become ready in "ng-2": context deadline exceeded

보통 타임에러로 표시되며 kubectl 명령어를 확인하면 워크 노드가 조인이 안되어 파드가 정상적으로 배포되지 않는 것을 확인할 수 있다.

error2.png

해당 내용은 깃 이슈에서 참고할 수 있지만 답을 찾을 수 없어 며칠을 고생했다. 원인은 EKS 보안 그룹 설정이였다. 앞서 베스천서버 구성시 EKS 통신을 위한 보안 그룹을 설정하였는데 추가를 안하면 EKS 워크 노드와 베스천 서버와의 통신이 안되어 조인이 안된다. EKS 보안 그룹을 설정하고 다시 EKS를 배포하자.



API 서버 접근이 안되는 경우

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(N/A:N/A) [root@myeks-host example]# eksctl create cluster -f private-cluster.yaml 
2023-04-27 19:45:28 []  eksctl version 0.138.0
2023-04-27 19:45:28 []  using region ap-northeast-2
2023-04-27 19:45:28 [!]  warning, having public access disallowed will subsequently interfere with some features of eksctl. This will require running subsequent eksctl (and Kubernetes) commands/API calls from within the VPC.  Running these in the VPC requires making updates to some AWS resources.  See: https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html for more details
2023-04-27 19:45:28 []  setting availability zones to [ap-northeast-2a ap-northeast-2b ap-northeast-2d]
2023-04-27 19:45:28 []  subnets for ap-northeast-2a - public:192.168.0.0/19 private:192.168.96.0/19
2023-04-27 19:45:28 []  subnets for ap-northeast-2b - public:192.168.32.0/19 private:192.168.128.0/19
2023-04-27 19:45:28 []  subnets for ap-northeast-2d - public:192.168.64.0/19 private:192.168.160.0/19
2023-04-27 19:45:28 []  nodegroup "EKS-PRIVATE-NODE" will use "ami-0fdcb707922882aef" [AmazonLinux2/1.25]
2023-04-27 19:45:28 []  using Kubernetes version 1.25
2023-04-27 19:45:28 []  creating EKS cluster "my-private-eks" in "ap-northeast-2" region with un-managed nodes
...
Error: getting auth ConfigMap: Get "https://AA1694EDA538EFE2ADC5FCCABBB4F745.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/configmaps/aws-auth": dial tcp 192.168.175.32:443: i/o timeout

(terraform-eks@my-private-eks:N/A) [root@myeks-host example]# kubectl get pods -A 
Unable to connect to the server: dial tcp 192.168.134.188:443: i/o timeout

→ 앞에도 다뤘지만 Timeout 의 대부분의 원인이 보안 그룹이다. 이 경우는 클러스터 구성 파일에서 베스천 서버의 보안그룹을 설정하지 않아서 생긴 문제였다. 클러스터 구성에서 베스천 서버에 대한 보안 그룹(아웃 바운드 베스천서버 443 포트)을 설정하면 된다. 클러스터 구성에서 보안 그룹 설정은 AWS 콘솔 → EKS에서 가능하다.

eks-sgh.png



AWS CLI 및 eksctl 결과 i/o timeout

1
2
(N/A:N/A) [root@myeks-host example]# eksctl get cluster --region ap-northeast-2
Error: checking AWS STS access – cannot get role ARN for current session: operation error STS: GetCallerIdentity, https response error StatusCode: 0, RequestID: , request send failed, Post "https://sts.ap-northeast-2.amazonaws.com/": dial tcp 52.95.192.98:443: i/o timeout

마찬가지로 보안 그룹 문제였다. 베스천 서버의 아웃바운드에 0.0.0.0/0를 추가하면 해결된다.



콘솔에서 컴퓨팅 리소스 확인이 안되는 경우

다음 그림과 같이 EKS 배포이후 AWS 콘솔에서 워크 노드가 표시되지 않는 경우이다.

configmap-eks.png

원인은 EKS 클러스터 사용자 정보에 AWS 사용자 정보가 없기 때문이다. 다음과 같이 추가하도록 하자.

1
kubectl edit cm/aws-auth -n kube-system
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::000000000:role/eksctl-my-eks-nodegroup-ng-1-NodeInstanceRole-ZG7JVL5Z4IPU
      username: system:node:{{EC2PrivateDNSName}}    
  mapUsers: |
    - userarn: arn:aws:iam:000000000:user/hanhorang  # AWS 인증에서 사용한 IAM 사용자 arn를 입력하자.
      username: hanhorang
      groups:
        - system:masters    
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
  • mapUsers에서 IAM 사용자 arn을 추가하면 해결된다.



클러스터 구성 확인

클러스터 구성 후 클러스터 정보와 인스턴스 정보를 확인하겠다.


클러스터 구성 확인

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 클러스터 확인 
(terraform-eks@my-eks:N/A) [root@myeks-host example]# eksctl get cluster 
NAME            REGION          EKSCTL CREATED
my-eks          ap-northeast-2  True

# 클러스터 노드 그룹 확인 
(terraform-eks@my-eks:N/A) [root@myeks-host example]# eksctl get  nodegroup --cluster my-eks
CLUSTER NODEGROUP       STATUS          CREATED                 MIN SIZE        MAX SIZE        DESIRED CAPACITY        INSTANCE TYPE   IMAGE ID                ASG NAMETYPE
my-eks  ng-1            CREATE_COMPLETE 2023-04-29T09:05:31Z    4               4               4                       m5.xlarge       ami-0fdcb707922882aef   eksctl-my-eks-nodegroup-ng-1-NodeGroup-IWPRDQX6J0CG     unmanaged

# 클러스터 접근 정보 확인 및 노드 확인 
(terraform-eks@my-eks:N/A) [root@myeks-host example]# kubectl get nodes -v6
I0429 20:21:39.855710    5753 loader.go:374] Config loaded from file:  /root/.kube/config
I0429 20:21:40.631509    5753 round_trippers.go:553] GET https://8F53A6D3D93C1D751527688A7CB07659.yl4.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 200 OK in 769 milliseconds
NAME                                               STATUS   ROLES    AGE    VERSION
ip-192-168-3-31.ap-northeast-2.compute.internal    Ready    <none>   131m   v1.25.7-eks-a59e1f0
ip-192-168-3-59.ap-northeast-2.compute.internal    Ready    <none>   131m   v1.25.7-eks-a59e1f0
ip-192-168-4-195.ap-northeast-2.compute.internal   Ready    <none>   131m   v1.25.7-eks-a59e1f0
ip-192-168-4-250.ap-northeast-2.compute.internal   Ready    <none>   131m   v1.25.7-eks-a59e1f0

# 클러스터 정보 확인
kubectl cluster-info dump
...

베스천 서버에서 워크 노드 접근을 위한 보안 그룹 설정

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 인스턴스 IP 확인 
terraform-eks@my-eks:N/A) [root@myeks-host example]# aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

-------------------------------------------------------------------
|                        DescribeInstances                        |
+-----------------+-----------------+------------------+----------+
|  InstanceName   |  PrivateIPAdd   |   PublicIPAdd    | Status   |
+-----------------+-----------------+------------------+----------+
|  EKS-WORKER-TEST|  192.168.4.230  |  None            |  running |
|  EKS-WORKER     |  192.168.4.195  |  None            |  running |
|  EKS-WORKER     |  192.168.4.250  |  None            |  running |
|  EKS-WORKER     |  192.168.3.59   |  None            |  running |
|  EKS-WORKER     |  192.168.3.31   |  None            |  running |
|  myeks-host     |  192.168.1.100  |  43.201.102.195  |  running |
+-----------------+-----------------+------------------+----------+

# 워크 노드로 Ping을 하나 안된다. 
(terraform-eks@my-eks:N/A) [root@myeks-host example]# ping 192.168.3.31 
PING 192.168.3.31 (192.168.3.31) 56(84) bytes of data. 

# 노드 보안그룹 ID 확인하여 베스천 서버 IP를 추가하자
(terraform-eks@my-eks:N/A) [root@myeks-host example]# aws ec2 describe-security-groups --filters Name=group-name,Values=*nodegroup* --query "SecurityGroups[*].[GroupId]" --output text

sg-0bf33458f7e193841
sg-0f4cd52a07786b9fb

# --group-id 에 위 보안 그룹 하나 입력 
(terraform-eks@my-eks:N/A) [root@myeks-host example]# aws ec2 authorize-security-group-ingress --group-id sg-0f4cd52a07786b9fb  --protocol '-1' --cidr 192.168.1.100/32
{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-01bca8eecf69c86b6",
            "GroupId": "sg-0f4cd52a07786b9fb",
            "GroupOwnerId": "955963799952",
            "IsEgress": false,
            "IpProtocol": "-1",
            "FromPort": -1,
            "ToPort": -1,
            "CidrIpv4": "192.168.1.100/32"
        }
    ]
}

# 접근 확인 
(terraform-eks@my-eks:N/A) [root@myeks-host example]# ping 192.168.3.31
PING 192.168.3.31 (192.168.3.31) 56(84) bytes of data.
64 bytes from 192.168.3.31: icmp_seq=1 ttl=255 time=0.176 ms
64 bytes from 192.168.3.31: icmp_seq=2 ttl=255 time=0.152 ms
64 bytes from 192.168.3.31: icmp_seq=3 ttl=255 time=0.144 ms

인스턴스 정보 확인

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# kubelet 확인 
systemctl status kubelet
● kubelet.service - Kubernetes Kubelet
   Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/kubelet.service.d
           └─10-kubelet-args.conf, 30-kubelet-extra-args.conf
   Active: active (running) since Sat 2023-04-29 09:08:56 UTC; 2h 49min ago
     Docs: https://github.com/kubernetes/kubernetes
  Process: 3176 ExecStartPre=/sbin/iptables -P FORWARD ACCEPT -w 5 (code=exited, status=0/SUCCESS)
 Main PID: 3178 (kubelet)
    Tasks: 16
   Memory: 77.3M
   CGroup: /runtime.slice/kubelet.service
           └─3178 /usr/bin/kubelet --config /etc/kubernetes/kubelet/kubelet-config.json --kubeconfig /var/lib/kubelet/kubeconfig --container-runtime-endpoint unix://...

Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.377073    3178 kubelet.go:2117] "SyncLoop ADD" source="api" pods=...sncki]
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.377103    3178 topology_manager.go:205] "Topology Admit Handler"
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.513465    3178 reconciler.go:357] "operationExecutor.VerifyControllerAt...
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.614154    3178 reconciler.go:269] "operationExecutor.MountVolume starte...
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.632046    3178 operation_generator.go:730] "MountVolume.SetUp succeeded...
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.703987    3178 util.go:30] "No sandbox for pod can be found. Need...sncki"
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.791915    3178 provider.go:102] Refreshing cache for provider: *c...ovider
Apr 29 11:52:06 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:06.792001    3178 provider.go:82] Docker config file not found: coul...t   /]
Apr 29 11:52:07 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:07.235202    3178 kubelet.go:2155] "SyncLoop (PLEG): event for pod" ...a6526}
Apr 29 11:52:11 ip-192-168-3-31.ap-northeast-2.compute.internal kubelet[3178]: I0429 11:52:11.242075    3178 kubelet.go:2155] "SyncLoop (PLEG): event for pod" ...4351a}
Hint: Some lines were ellipsized, use -l to show in full.

# 볼륨 확인 
lsblk
NAME          MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1       259:0    0  30G  0 disk 
├─nvme0n1p1   259:1    0  30G  0 part /
└─nvme0n1p128 259:2    0   1M  0 part 

# 컨테이너 런타임 확인 
ps axf |grep /usr/bin/containerd
 3013 ?        Ssl    0:46 /usr/bin/containerd
 4269 ?        Sl     0:10 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id 0ea51c82eb135fca4bdae99f89e8f5804d1e144efb857e8fae310a9d7039e21a -address /run/containerd/containerd.sock
 4270 ?        Sl     0:02 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id f723a90d2f436d77f37789bc29e26fcdbca82ca575bfacde43be2a0978573634 -address /run/containerd/containerd.sock
24478 ?        Sl     0:00 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id c1657f716d01283e8e7aa183f6583ec4c96af3d1d19b081004d1c565015a6526 -address /run/containerd/containerd.sock
26752 ?        S+     0:00          \_ grep --color=auto /usr/bin/containerd 



참고

https://awskoreamarketingasset.s3.amazonaws.com/2022 Summit/pdf/T14S4_Amazon EKS 마이그레이션 요점 정리.pdf

https://341123.tistory.com/m/6