세미나 & 교육 & Tech

[Amazon EKS STUDY] #2주차 (도전과제1~7)

jih0ssang 2025. 2. 18. 22:11

[도전과제1] 

EKS Max pod 개수 증가 - Prefix Delegation + WARM & MIN IP/Prefix Targets : EKS에 직접 설정 후 파드 150대 생성해보기

참조 : https://kimalarm.tistory.com/51

 

 

1.1 IPv4 Prefix Delegation (접두사 위임) 활성화

# Prefix Delegation(접두사 위임) 활성화
kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true

Prefix Delegation (접두사 위임)을 활성화합니다.

 

 

1.2 150개의 Pod 배포

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 150
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80

 

 

 

Pod 배포 실패

 

Prefix Delegation(접두사 위임) 활성화를 하였으나, 40개의 Pod만 배포된 것을 알 수 있습니다.

원래 17+17+17=51개 정도 배포될 것으로 예상하였습니다..

 

배포 실패 원인

배포 가능한 Pod 수보다 더 많은 Pod를 배포하여 스케줄링 실패한 모습입니다.

 

 

1.2.1 Worker Node 의 max-pod 값 변경

# worker node의 max pod 개수 설정 확인
kubectl get node -o yaml | grep allocatable -A7

# worker node(192.168.1.78) max pod 설정 확인
kubectl get node ip-192-168-1-78.ap-northeast-2.compute.internal -o yaml | grep allocatable -A7

t3.medium 기준

 

150개의 Pod를 띄우기에 부족한 숫자입니다.

 

왜 17개 일까?
# 현재 인스턴스 타입의 ENI 및 IP 제한 확인
aws ec2 describe-instance-types --instance-types t3.medium --query "InstanceTypes[0].NetworkInfo"

  • MaximumNetworkInterfaces: 3  (최대 ENI 개수)
  • Ipv4AddressesPerInterface: 6  (ENI당 IPv4 개수)
  • 1 (Node 자체의 IP)
  • 2 (EKS에서 기본적으로 2개의 추가 IP를 예약해서 사용함.   기본 노드 ENI 1개 + 추가 ENI 1개)
최대 Pod 수 계산
ㆍ{최대 ENI 개수 * (ENI당 IPv4 개수 - 1)} + 2
ㆍex.t3.medium 기준 = { 3 * (6-1) } + 2 = 17 개

 

 

 

1.2.2 Worker Node 의 max-pod 값 변경

Worker Node 접속

ssh ec2-user@192.168.1.52
cd /etc/eks

 

 

bootstrap을 활용하여 Node의 Max-pod 설정 변경

# bootstrap.sh 설정 변경
sudo /etc/eks/bootstrap.sh myeks --use-max-pods false --kubelet-extra-args '--max-pods=110'

 

ENI 개수, ENI당 IPv4 개수가 아닌 --max-pods 수를 확대하면 생기는 일
ㆍAWS VPC CNI는 Pod를 위해 기본적으로 ENI를 미리 확보해 둠
ㆍ--max-pods 변경하면 kubelet이 미리 확보해 둔 IP를 더 많이 활용 가능합니다.

 

 

kubelet 재시작

sudo systemctl restart kubelet

 

!! 주의 !!  kubelet을 재기동 해야만 실제 바뀐 설정이 적용됨 !!

 

 

 

Node의 max-pods 설정 확인

# worker node(192.168.1.25) max pod 설정 확인
kubectl get node ip-192-168-1-25.ap-northeast-2.compute.internal -o yaml | grep allocatable -A7

Node의 max-pods 가 110으로 늘어났음을 확인할 수 있습니다.

 

17+17+17에서 17+17+110=134개 Pod가 배포되었음을 확인할 수 있습니다.

Node 하나를 더 위의 방식으로 17개 → 110개로 변경해두면 모두 배포가 가능합니다.

 

150개의 Pod 가 모두 정상적으로 배포된 것을 알 수 있습니다.

 

 

배포 성공

 

 

[도전과제2] 

EKS Max pod 개수 증가 - Custom Network : EKS에 직접 설정 후 파드 150대 생성해보기

참조 : https://docs.aws.amazon.com/eks/latest/best-practices/custom-networking.html

 

 

2.1 환경변수 설정

kubectl set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true

custom network를 사용하기 위해 AWS CNI 환경 변수를 다음과 같이 설정합니다.

 

2.2 ENIConfig(CRD) yaml 작성

AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true 가 되었다면

AWS VPC CNI는 ENIConfig 파일에 정의된 subnet으로부터 Pod IP 주소를 할당합니다.

apiVersion : crd.k8s.amazonaws.com/v1alpha1
kind : ENIConfig
metadata:
  name: us-west-2a
spec:
  securityGroups:
    - sg-0dff111a1d11c1c11
  subnet: subnet-011b111c1f11fdf11

 

Pod의 IP를 별도 서브넷에서 할당 받습니다.해당 서브넷은 EKS 클러스터에 연결된 서브넷 또는 새로운 서브넷입니다.

 

 

2.3 Prefix Delegation (접두사 위임) 활성화

1번 문제 과정을 참고하여 접두사 위임 활성화 및 max-pods 증가 진행하면 150개 Pod 생성이 가능합니다.

 

2.4 배포 완료

Custom Networking
ㆍ기본 네트워크 설정만으로 대규모 Pod 배포 시 IP 부족 문제가 발생할 수 있음
ㆍCustom Networking + Prefix Delegation을 진행하면 해결

 

 

 

[도전과제3] 

Security Group for Pod : 파드별 보안그룹 적용해보기

참조 : https://kimalarm.tistory.com/51

 

2.1 Security Groups Per Pod 활성화

kubectl describe daemonsets aws-node -n kube-system | grep ADDITIONAL_ENI_TAGS: -B1 -A26

 

ENABLE_POD_ENI 옵션이 Security Groups Per Pod 기능입니다.

T 타입의 EC2는 Security Groups Per Pod 기능을 사용하지 못하므로 m5.large 타입으로 테스트 진행하였습니다.

 

kubectl set env daemonset aws-node -n kube-system ENABLE_POD_ENI=true

ENABLE_POD_ENI를 활성화합니다.

 

ENABLED_POD_ENI가 활성화되었음을 확인할 수 있습니다.

 

2.2 Security Group Policy 설정

kubectl get crd

Security Group Per Pod 가 활성화 되면, securitygrouppolicies.vpcresources.k8s.aws 라는 이름의 CRD 를 생성할 수 있습니다. 해당 CRD 로 Pod 별 SG 를 설정할 수 있게됩니다.

 

2.2.1 SG for Pods 를 위한 SG 생성

SG를 생성하여 SG ID를 복사합니다.

 

2.2.2 EKS Cluster SG 수정

EKS Cluster SG에 복사한 SG ID를 소스에 붙여넣고 DNS(UDP), DNS(TCP) 규칙을 추가합니다.

Pod와 EKS Cluster 간 CoreDNS 질의를 위함입니다.

 

2.3 CRD yaml 생성

# my-security-group-policy.yaml
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
  name: my-security-group-policy
  namespace: my-namespace
spec:
  podSelector: 
    matchLabels:
      role: my-role
  securityGroups:
    groupIds:
      - sg-0e # 생성한 SG ID 입력

 

role: my-role의 label을 가진 Pod에 보안 그룹을 적용하는 CRD 입니다.

 

 

2.4 CRD 배포 및 확인

 

 

 

2.5 샘플 Application 배포

# sample-application.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  namespace: my-namespace
  labels:
    app: my-app
spec:
  replicas: 4
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
        role: my-role
    spec:
      terminationGracePeriodSeconds: 120
      containers:
      - name: nginx
        image: public.ecr.aws/nginx/nginx:1.23
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: my-namespace
  labels:
    app: my-app
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

 

CRD의 Label 과 일치하는 Label을 지닌 Pod가 배포됩니다.

이 Pod는 CRD 설정의 적용 대상이므로, Pod에 SG가 걸릴 것입니다.

 

2.6 Security Group 테스트

# 배포한 pod 내 container 접속
kubectl exec -it -n my-namespace my-deployment-5cb557c48b-lwnfv -- /bin/bash

# application 실행 테스트
curl my-app

 

 

정상적으로 응답합니다.

통신이 안될 경우, 생성한 보안그룹을 다시 살펴봐야 합니다.

 

 

생성 성공

 

[도전과제4] 

게임서버의 트래픽(UDP)를 서비스(NLB)를 통해 인입 설정

참조 : https://themapisto.tistory.com/284

 

 

 

UDP 트래픽 및 고정성(Stickness)이 설정된 대상 그룹에는 수동 상태 확인이 지원되지 않습니다

따라서 이 블로그에 언급된 샘플 게임 서버는 포트 80에서 수신 대기하는 sidecar로 nginx를 실행합니다.

게임 서버 상태 확인의 정확성을 높이려면 Liveness Probe 구성이 좋습니다.

대상 그룹에서 비정상 임계값 수에 도달하면 요청이 다른 정상 Pod로 리다이렉션됩니다.

 

UDP 기반 LoadBalancer Service는 NLB 상태 확인 구성과 같이 명심해야 할 사항이 몇 가지 있습니다.
ㆍLiveness Probe(활성 프로브) 간 간격은 NLB 상태 확인 간격보다 작아야 합니다.
ㆍ기본 로드밸런서의 헬스 체크의 비정상 임계값은 3회로 지정되어있습니다.

 

Liveness Probe
ㆍk8s에서 애플리케이션이 정상적으로 실행되는지 확인하는 기능
ㆍ애플리케이션이 정상적으로 동작하지 않을 때 자동 감지하여 재시작하도록 하는 헬스 체크 매커니즘

 

 

4.1 로드밸런서 컨트롤러 배포

AWS Load Balancer Controller는 AWS K8S용 ELB를 관리합니다.

 

# OIDC 조회
aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text
aws iam list-open-id-connect-providers | jq

 

 

4.1.1 IAM Policy 생성

##### IAM Policy (AWSLoadBalancerControllerIAMPolicy) 생성 #####
# AWS Load Balancer Controller가 필요한 권한(IAM Policy) 다운로드
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json

# 다운로드 파일 기반 정책(IAM Policy) 생성
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json

## 혹시 이미 IAM 정책이 있지만 예전 정책일 경우 아래의 명령어와 같이, 업데이트 ##
# aws iam update-policy ~~~


# 생성된 IAM Policy Arn 확인
aws iam list-policies --scope Local | jq
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy | jq
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --query 'Policy.Arn'

 

AWS LoadBalaner가 EC2, ELB, IAM 관련 리소스에 대한 권한이 필요합니다.

AWS LoadBalacner를 위한 IAM Policy 생성을 진행합니다.

 

 

4.1.2 Service Account(SA) 생성

# IAM 역할 생성. AWS Load Balancer Controller의 kube-system 네임스페이스에 aws-load-balancer-controller라는 Kubernetes 서비스 계정을 생성하고 IAM 역할의 이름으로 Kubernetes 서비스 계정에 주석을 답니다
eksctl create iamserviceaccount --cluster=$CLUSTER_NAME --namespace=kube-system --name=aws-load-balancer-controller --role-name AmazonEKSLoadBalancerControllerRole \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve

# IRSA 정보 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# Service Account(SA) 확인
kubectl get serviceaccounts -n kube-system aws-load-balancer-controller -o yaml | yh

 

K8S LoadBalancer Controller가 AWS IAM Role을 사용하도록 Service Account(SA)를 생성합니다.

EKS에서는 IRSA(IAM Roles for Service Accounts)를 사용해서 K8S 서비스가 AWS IAM Role을 사용할 수 있습니다.

 

eksctl 를 활용하면 자동으로 매칭되는 IAM Role 을 CloudFormation 으로 생성됩니다.

 

4.1.3 AWS LoadBalacner Controller 배포

# Helm Chart 저장소 추가
helm repo add eks https://aws.github.io/eks-charts

# Helm 저장소 업데이트(최신화)
helm repo update

# AWS LoadBalancer Controller 설치
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

 

Helm Chart를 사용하면 Controller의 설치, 업데이트, 삭제를 쉽게 관리할 수 있습니다. 

AWS Load Balancer Controller를 쿠버네티스 클러스터에 배포하는 과정입니다.

 

 

4.1.4 AWS LoadBalacner Controller 배포 확인

## 설치 확인 : aws-load-balancer-controller:v2.7.1 ##

# CRD(Custom Resource Definitions) 목록 조회
kubectl get crd

# LoadBalancer Controller 상태 확인(상세정보 조회)
kubectl get deployment -n kube-system aws-load-balancer-controller
kubectl describe deploy -n kube-system aws-load-balancer-controller
kubectl describe deploy -n kube-system aws-load-balancer-controller | grep 'Service Account'

 

LoadBalancer 가 설치한 CRD를 확인하고 LoadBalancer Controller 상세정보를 조회합니다.

 

 

4.2 EKS Cluster 배포

 

## 기 존재하는 EKS Cluster가 있어서 Nodegroup만 배포하였음 ##

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: myeks
  region: ap-northeast-2

managedNodeGroups:
  - instanceType: "c6g.xlarge"
    amiFamily: AmazonLinux2
    name: arm-ng-1
    desiredCapacity: 1
    maxSize: 10
    minSize: 1
    tags:
      project: supertuxkart
    labels:
      project: supertuxkart

현재 상황에 맞게 수정하여 위의 eks-arm64-nodegroup-spec.yaml 파일을 작성하였습니다.

 

######## 내 설정에 맞게 수정된 코드 ########
# app 다운로드
git clone https://github.com/aws-samples/containerized-game-servers.git
cd containerized-game-servers/udp-nlb-sample

# cluster 배포
eksctl create nodegroup -f eks-arm64-nodegroup-spec.yaml



######## 원본 코드 ########
# app 다운로드
git clone https://github.com/aws-samples/containerized-game-servers.git
cd containerized-game-servers/udp-nlb-sample

# 환경변수 설정
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=us-west-2

# cluster 배포
cd containerized-game-servers/udp-nlb-sample
eksctl create cluster -f eks-arm64-cluster-spec.yaml

제 상황과 다르게 Cluster 구성이 없는 분들은 아래의 원본 코드를 참조하여 배포하면 되겠습니다.

 

 

4.3 supertuxkart 게임 이미지 배포

# stk 배포
cd containerized-game-servers/udp-nlb-sample/stk
sh ecr-repos.sh
sh build.sh

# 어플리케이션 sidecar 배포
cd containerized-game-servers/udp-nlb-sample/nginx-static-sidecar/
sh ecr-repos.sh
sh build.sh

#nlb-sidecar 배포
cd containerized-game-servers/nginx-static-sidecar/
cat stknlb-static-tcphealth-sidecar.yaml | envsubst | kubectl apply -f -

 

4.3.1 ECR 배포

### ecr-repo.sh ###
#!/bin/sh

echo "Creating docker image repositories"
aws cloudformation create-stack --stack-name ecr-repos-stk --template-body file://./ecr-repos.json

 

AWS ECR Repository를 배포하는 CloudFormation Stack 생성하는 명령어입니다.

 

4.3.2 supertuxkart 게임 이미지 배포

#!/bin/bash
  
account=$(aws sts get-caller-identity --output text --query Account)
region=${AWS_REGION}
repo="stk"
ver="arm0.12.0"

repo_name='.dkr.ecr.'$region'.amazonaws.com/'$repo':'$ver
repo_url=$account$repo_name

aws ecr get-login-password --region $region | docker login --username AWS --password-stdin $repo_url
docker build -t $repo .
docker tag $repo:latest $repo_url
docker push $repo_url

위의 stk 게임 이미지가 현재 제공되지 않아서(2년 전 이미지) 테스트는 여기까지만 진행하였습니다.

 

UDP 기반 NLB 생성 절차
ㆍAWS IAM Policy 생성
ㆍAWS LoadBalancer가 사용할 Service Account 생성 및 앞서 생성한 IAM Policy 연결

 

 

[도전과제5] 

Multiple Ingress pattern : 여러 Ingress 를 하나의 ALB에서 처리 할 수 있게 설정

참조 : https://themapisto.tistory.com/284

 

 

ㆍ기본적으로 각 Ingress는 별도의 ALB 생성
ㆍ하지만 여러 Ingress가 하나의 ALB를 공유하는 IngressGroup 기능 사용 가능
ㆍ실습 전 AWS-loadbalancer-controller 배포 필요
ㆍingress 배포 → 확인

 

 

6.1 AWS LoadBalancer Controller 배포

도전과제 5 참조

 

6.2 Namespace 생성

kubectl create namespace service-a
kubectl create namespace service-b

각 서비스별 namespace인 catalog, ui를 생성합니다.

전혀 다른 서비스인 catalog, ui가 하나의 ALB를 공유하도록 구성하겠습니다.

 

 

6.3 서비스 배포

6.3.1 service-a 배포

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${SERVICE_NAME}
  namespace: ${NS}
  labels:
    app.kubernetes.io/name: ${SERVICE_NAME}
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ${SERVICE_NAME}
  replicas: 1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ${SERVICE_NAME}
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: ${SERVICE_NAME}
          image: hashicorp/http-echo
          imagePullPolicy: IfNotPresent
          args:
            - -listen=:3000
            - -text=${SERVICE_NAME}
          ports:
            - name: app-port
              containerPort: 3000
          resources:
            requests:
              cpu: 0.125
              memory: 50Mi
---
apiVersion: v1
kind: Service
metadata:
  name: ${SERVICE_NAME}
  namespace: ${NS}
  labels:
    app.kubernetes.io/name: ${SERVICE_NAME}
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: ${SERVICE_NAME}
  ports:
    - name: svc-a-port
      port: 8080
      targetPort: app-port
      protocol: TCP

 

6.3.2 service-b 배포

---
apiVersion: v1
kind: Service
metadata:
  name: ${SERVICE_NAME}
  namespace: ${NS}
  labels:
    app.kubernetes.io/name: ${SERVICE_NAME}
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: ${SERVICE_NAME}
  ports:
    - name: svc-b-port
      port: 8082
      targetPort: app-port
      protocol: TCP

service-b는 service-a와 deployment 내용은 동일하되, service 내용 일부만 다릅니다.

 

각 Service는 동일한 노드에 있어도 namespace가 다를 경우 논리적으로 분리되어있으므로 동일 구성(name, port) 가능

 

SERVICE_NAME=service-a NS=service-a envsubst < service.yaml | kubectl apply -f -
SERVICE_NAME=service-b NS=service-b envsubst < service.yaml | kubectl apply -f -

service name과 namespace는 다르고 구성은 동일하도록 서비스를 2번 배포하므로, 환경 변수를 활용하여 배포하였습니다.

 

 

kubectl get pod,svc -n service-a
kubectl get pod,svc -n service-b

파드와 서비스가 정상적으로 배포되었는지 확인합니다.

 

 

6.4 Ingress 배포

6.4.1 service-a-Ingress 배포

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ${NS}-ingress
  namespace: ${NS}
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/liveness
    alb.ingress.kubernetes.io/group.name: retail-app-group
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 8080}]'
spec:
  ingressClassName: alb
  rules:
    - host: first.com
      http:
        paths:
          - path: /first
            pathType: Prefix
            backend:
              service:
                name: first
                port:
                  name: svc-a-port
metadata.annotations 설정에서 alb.ingress.kubernetes.io/group.name retail-app-group을 지정하면 특정 ALB의 대상그룹으로 생성됩니다.

 

NS=service-a envsubst < service-a-ingress.yaml | kubectl apply -f -

 

namespace service-a 에 생성하도록 환경변수를 지정합니다.

 

 

 

6.4.2 service-b-ingress.yaml 파일 배포

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ${NS}-ingress
  namespace: ${NS}
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/liveness
    alb.ingress.kubernetes.io/group.name: retail-app-group
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 8082}]'
spec:
  ingressClassName: alb
  rules:
    - host: second.com
      http:
        paths:
          - path: /second
            pathType: Prefix
            backend:
              service:
                name: second
                port:
                  name: svc-b-port

 

 

NS=service-b envsubst < service-b-ingress.yaml | kubectl apply -f -

 

namespace service-b 에 생성하도록 환경변수를 지정합니다.

 

Ingress 수정해도 제대로 반영이 되지 않는 경우, 아래의 명령어를 통해 로그 확인 가능
kubectl get deployment -n kube-system aws-load-balancer-controller -o yaml | grep image

 

6.5 AWS LoadBalancer Console 확인

AWS Console 상에서 AWS LoadBalancer가 1개 생성된 것을 확인할 수 있습니다.

  • Path prefix의 /catalogue 요청은 서비스 카탈로그의 대상 그룹으로 전송됩니다.
  • 이외의 다른 요청은 서비스 ui의 대상 그룹으로 전송됩니다.
  • 에러가 발생하는 모든 요청에는 404가 발생합니다.

 

 

 

생성 성공

 

 

 

[도전과제6] 

How to rapidly scale your application with ALB on EKS (without losing traffic)

참조 : https://www.eksworkshop.com/docs/fundamentals/exposing/ingress/multiple-ingress

https://themapisto.tistory.com/284

 



5.1 IPv

 

 

5.2 IPv

 

 

[도전과제7] 

Expose Amazon EKS pods through cross-account load balancer

 

1.1 IPv