AWES[1기](Amzaon EKS Study)

[AWES] 2주차 - EKS Networking

Joon0464 2023. 5. 3. 22:48

 

1. 사전 준비

저번주와의 차이점은 워커노드가 2대에서 3대로 증가하였다는 것입니다. 좀 더 실제 EKS 운영환경에 가까운 느낌입니다. 3개의 가용영역을 사용하여 지난주보다 좀 더 높은 가용성을 보장 받을 수 있습니다.
지난주와 동일하게 CloudFormation을 통해 원클릭 배포를 진행합니다. 소요시간은 20분 내외입니다.
지난주와 동일하게 Xshell에 pem키를 이용하여 접속되는것을 볼 수 있습니다.

2. 배포 확인 및 설정

# cloud-init 실행 과정 로그 확인
tail -f /var/log/cloud-init-output.log

# cloud-init 정상 완료 후 eksctl 실행 과정 로그 확인
tail -f /root/create-eks.log

EKS가 생성되는 로그를 확인할 수 있습니다. 실제 배포중일 때는 tail -f 옵션으로 실시간 확인이 가능할 것입니다.

# KUBECONFIG PATH 및 파일 확인
$ k get node -v=6
$ ls ~/.kube/config

KUBECONFIG 파일 위치도 다음과 같이 확인할 수 있습니다.

변수 설정


# 워커 노드 IP를 변수로 설정
$ N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
$ N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
$ N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
$ echo "export N1=$N1" >> /etc/profile
$ echo "export N2=$N2" >> /etc/profile
$ echo "export N3=$N3" >> /etc/profile

# 설정된 변수 확인
$ echo $N1, $N2, $N3

# 보안 그룹 ID 변수 설정
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
echo $NGSGID

변수 설정을 완료하면 위와 같이 echo 로 변수 설정을 확인할 수 있습니다.

Security Group 설정


Bastion Host -> Worker node 접속 가능하도록 보안 그룹 설정이 필요합니다.

# 노드 보안그룹에 eksctl-host 에서 노드(파드)에 접속 가능하게 룰(Rule) 추가 설정
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32

# eksctl-host(Bastion Host) -> 워커노드 Ping Test 
ping -c 1 $N1 && ping -c 1 $N2 && ping -c 1 $N3

ping test 결과 정상적으로 통신 가능한 것을 볼 수 있습니다.

 

# 각 호스트에 호스트 네임을 조회하여 연결할 것인지에 대한 yes 응답을 미리 입력해둡니다.
$ ssh -o ec2-user@$N1 hostname
-> yes 입력 후 Enter
$ ssh -o ec2-user@$N2 hostname
-> yes 입력 후 Enter
$ ssh -o ec2-user@$N3 hostname
-> yes 입력 후 Enter

위와 같이 설정하면 배포 확인 및 설정이 완료됩니다.

Add-On(추가 기능) 정보 확인


CoreDNS, kube-proxy 등의 Add-on 들이 저번 실습때 사용한 yaml 과 다르게 설치되어있습니다.

# 클러스터 배포 YAML 에서 Add-on 명시된 것 확인
$ grep addons -A8 ~/myeks.yaml | yh

# eks add-on 확인
$ eksctl get addon --cluster $CLUSTER_NAME

콘솔에서 클러스터 배포 yaml 내용을 확인하면 설치된 add-on에 대한 정보가 작성되어있습니다.  eksctl 명령어를 사용하여 위 콘솔과 동일한 정보 확인 가능합니다.

 

 

3. 이론

AWS VPC CNI란?

  • 파드의 IP를 할당
  • 파드의 IP 네트워크 대역과 노드의 IP 대역이 같아 직접 통신이 가능 -> 통신 구조가 간결하고 효율화
  • 쿠버네티스 <-> AWS 사이의 중계자 역할
  • VPC와 완벽히 통합 
  • VPC ENI 에 미리 할당된 IP(Local-IPAM Warm IP Pool)를 파드에서 사용 가능

L-IPAM -> 파드에 할당 가능한 IP를 미리 생성해둔 후 파드가 생성되면 할당해주는 역할

 

우선 EKS 클러스터가 배포되어 노드가 생성되면 EC2의 Primary Subnet에 Primary ENI를 생성하고 연결합니다. Pod가 생성되면 Host Network 모드로 실행되어 Primary ENI의 IP를 부여받고 호스트(노드)와 동일한 Network Namespace를 공유합니다. 또한, CNI가 자동으로 Primary ENI에 Primary Subnet의 IP 또는 Prefix warm pool을 할당합니다. 할당하는 IP 또는 Prefix warm pool의 사이즈는 노드의 인스턴스 타입에 따라 결정됩니다. 이후 노드에서 실행되는 Pod 수에 따라 Secondary ENI를 추가해 나가는 방식으로 동작합니다.

 

 

구성 요소


  • CNI Binary
    • Pod network를 구성하여 Pod간 통신을 가능하게 하는 역할
    • 노드의 root 파일 시스템에서 실행됨
    • Pod가 추가되거나 노드에서 삭제될 때 kubelet에 의해 호출됨
  • ipadm (long-running node-local IP Address Management daemon)
    • 노드의 ENI 관리
    • 가용 IP 주소/prefix의 warm-pool 관리


Calico CNI vs AWS VPC CNI


 위 그림에서처럼 Calico CNI를 사용하는 Kubernetes 클러스터에서 노드의 IP대역과 파드의 IP 대역이 서로 다릅니다. 즉 서로 다른 노드에 존재하는 파드간 통신을 하기 위해서는 원본 패킷 아래 Outer 패킷이 Encapsulation 과정을 통해 감싸지는 과정을 거치게 됩니다.

 하지만 AWS VPC CNI는 노드의 IP 대역과 파드의 IP 대역이 같습니다. 이와 같은 과정을 AWS VPC CNI에서는 Encapsulation, Decapsulation 과정이 생략되어 오버레이 경로도 최적화되어 훨씬 단순화되고 효율적인 네트워크 통신이 가능하게 됩니다.

 

 

Amazon VPC CNI Prefix 할당 모드


 

VPC CNI는 Secondary IP 모드가 디폴트 설정입니다. 2021년 8월 Amazon VPC CNI 플러그인에 AWS Nitro 기반의 EC2 인스턴스 유형인 경우 노드당 더 많은 파드를 실행할 수 있도록 "접두사(prefix) 할당 모드"가 추가되었습니다. 즉 노드에 더 많은 수의 Pod를 실행시킬 필요가 있다면 prefix 모드로 변경이 필요합니다. Prefix 할당 모드에서는 prefix 28bit (16개의 IP 주소) IPv4 주소 접두사를 할당하여 노드당 더 많은 IP주소를 파드에 할당할 수 있게 되었습니다. 

  1. Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 개수와 할당 가능 IP 수를 조합하여 선정
  2. IPv4 Prefix Delegation : IPv4 28bit 서브넷(prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정
# /28 사이즈의 IP CIDR을 ENI에 할당하기 위한 옵션 설정
$ kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true

 

네트워크 기본 정보 확인


 

# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
amazon-k8s-cni-init:v1.12.6-eksbuild.1
amazon-k8s-cni:v1.12.6-eksbuild.1

# kube-proxy config 확인 : 모드 iptables 사용 >> ipvs 모드 사용하지 않는 이유???
kubectl describe cm -n kube-system kube-proxy-config
...
mode: "iptables"
...

# 노드 IP 확인
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

# 파드 IP 확인
kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase

# 파드 이름 확인
kubectl get pod -A -o name

# 파드 갯수 확인
kubectl get pod -A -o name | wc -l

AWS VPC CNI는 kubernetes daemonset 리소스로 각 노드에 1대씩 동작하게 됩니다. 명령어를 통해 AWS VPC CNI의 이미지 정보 및 kube-proxy 모드를 확인합니다. 현재 kube-proxy의 mode는 "iptables" 인 것을 볼 수 있습니다.

$ ssh ec2-user@{node01 ip} tree /var/log/aws-routed-eni

노드1 에서 확인해보면,&nbsp; /var/log/aws-routed-eni 경로에 CNI 관련 로그가 쌓이는 것을 확인할 수 있습니다.
노드의 IP주소와 현재 동작중인 파드의 IP주소를 확인해봅니다.

노드의 Private IP주소 kube-proxy, aws-node POD의 IP 주소가 같다는 것을 알 수 있습니다.

# 노드에 툴 설치
ssh ec2-user@$N1 sudo yum install links tree jq tcpdump -y
ssh ec2-user@$N2 sudo yum install links tree jq tcpdump -y
ssh ec2-user@$N3 sudo yum install links tree jq tcpdump -y

# CNI 정보 확인
ssh ec2-user@$N1 tree /var/log/aws-routed-eni
ssh ec2-user@$N1 cat /var/log/aws-routed-eni/plugin.log | jq
ssh ec2-user@$N1 cat /var/log/aws-routed-eni/ipamd.log | jq
ssh ec2-user@$N1 cat /var/log/aws-routed-eni/egress-v4-plugin.log | jq

# 네트워크 정보 확인 : eniY는 pod network 네임스페이스와 veth pair
ssh ec2-user@$N1 sudo ip -br -c addr
ssh ec2-user@$N1 sudo ip -c addr
ssh ec2-user@$N1 sudo ip -c route
ssh ec2-user@$N1 sudo iptables -t nat -S
ssh ec2-user@$N1 sudo iptables -t nat -L -n -v

노드의 라우팅 테이블 및 네트워크 인터페이스 정보를 확인합니다.

 

4. 노드에서 기본 네트워크 정보 확인

  • Network 네임스페이스는 호스트(Root)와 파드 별(Per Pod)로 구분된다
  • 특정한 파드(kube-proxy, aws-node)는 호스트(Root)의 IP를 그대로 사용한다
  • t3.medium 의 경우 ENI 마다 최대 6개의 IP를 가질 수 있다
  • ENI0, ENI1 으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질수 있다
  • coredns 파드는 veth 으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다

노드3 을 기준으로 확인

노드3 인스턴스의 프라이빗 IP와 보조 프라이빗 IP 확인

# coredns 파드 IP 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -owide

# 노드의 라우팅 및 이더넷 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh ec2-user@$N3 sudo ip -c route
ssh ec2-user@$N3 sudo ip -c addr

 

표시한 부분을 살펴보면 노드3의 coredns 파드의 IP는 노드3 인스턴스의 보조 프라이빗&nbsp; IP에서 할당된 것을 볼 수 있습니다.

 

라우팅 테이블을 확인해보면 192.168.3.181 IP는 노드3 인스턴스의 프라이빗 IPv4 주소 중 하나입니다.
노드 3의 이더넷 IP주소를 확인해보면 프라이빗 IPv4 주소와 동일합니다.

테스트용 파드 생성

# [터미널1~3] 노드 모니터링
ssh ec2-user@$N3
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

# 테스트용 파드 netshoot-pod 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

# 파드 이름 변수 지정
PODNAME1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].metadata.name})
PODNAME2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].metadata.name})
PODNAME3=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[2].metadata.name})

# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

 

Deployment 배포 전&nbsp;노드3에서의 라우트 테이블 및 이더넷 정보
배포된 파드 정보를 확인합니다.
Deployment 배포 후 노드3에서의 라우트 테이블 및 이더넷 정보

Deployment 배포 후 노드 3 라우팅 테이블에 "192.168.3.168" 의 경로가 추가된 것을 볼 수 있습니다. 해당 IP는 "보조 프라이빗 IPv4 주소"에서 확인했었던 IP입니다.

파드가 생성되면, 워커 노드eniY@ifN 추가되고 라우팅 테이블에도 정보가 추가됩니다.

 

테스트용 파드 eniY 정보 확인 - 워커 노드 EC2

# 노드3에서 네트워크 인터페이스 정보 확인
ssh ec2-user@$N3
----------------
ip -br -c addr show
ip -c link
ip -c addr
ip route # 혹은 route -n

# 마지막 생성된 네임스페이스 정보 출력 -t net(네트워크 타입)
sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1

# 마지막 생성된 네임스페이스 net PID 정보 출력 -t net(네트워크 타입)를 변수 지정
MyPID=$(sudo lsns -o PID,COMMAND -t net | awk 'NR>2 {print $1}' | tail -n 1)

# PID 정보로 파드 정보 확인
sudo nsenter -t $MyPID -n ip -c addr
sudo nsenter -t $MyPID -n ip -c route

exit
----------------

노드에 접속한 후 PID를 활용해서 네임스페이스에 대해 조금 더 상세한 정보를 볼 수 있습니다.

테스트용 파드 접속(exec) 후 확인

# 테스트용 파드 접속(exec) 후 Shell 실행
kubectl exec -it $PODNAME1 -- zsh

# 아래부터는 pod-1 Shell 에서 실행 : 네트워크 정보 확인
----------------------------
ip -c addr
ip -c route
route -n
ping -c 1 <pod-2 IP>
ps
cat /etc/resolv.conf
exit
----------------------------

# 파드2 Shell 실행
kubectl exec -it $PODNAME2 -- ip -c addr

# 파드3 Shell 실행
kubectl exec -it $PODNAME3 -- ip -br -c addr

파드에 접속하여 네트워크 정보를 확인하면 위와 동일한 정보를 확인할 수 있습니다.

 

 

 

5. 노드간 파드 통신 확인

여기서부터 EKS 클러스터를 새로 배포하고 진행하였기 때문에 일부 파드의 IP주소등이 다를 수 있습니다.


파드간 통신 ->  AWS VPC CNI 경우 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능

파드간 통신 시 과정

# 파드 IP 변수 지정
PODIP1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].status.podIP})
PODIP2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].status.podIP})
PODIP3=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[2].status.podIP})

# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2

# 파드2 Shell 에서 파드3로 ping 테스트
kubectl exec -it $PODNAME2 -- ping -c 2 $PODIP3

# 파드3 Shell 에서 파드1로 ping 테스트
kubectl exec -it $PODNAME3 -- ping -c 2 $PODIP1

# 워커 노드 EC2 : TCPDUMP 확인 - 왜지???? 패킷 덤프 확인이 되나요?
sudo tcpdump -i any -nn icmp
sudo tcpdump -i eth1 -nn icmp
sudo tcpdump -i eth0 -nn icmp

[워커 노드1]
# routing policy database management 확인
ip rule

# routing table management 확인
ip route show table local

# 디폴트 네트워크 정보를 eth0 을 통해서 빠져나간다
ip route show table main
default via 192.168.1.1 dev eth0
...

햇갈림을 방지하고자 조금 귀찮지만 위와 같이 직접 변수 할당하였습니다.
$PODNAME1에서 192.168.1.190 노드의 파드 IP로 Ping test
노드1, 노드2에서의 tcpdump 결과
POD1, POD2에서의 tcpdump 결과

Node01(192.168.1.190)의 pod(netshoot-pod-7757d5dd99-9qdn2, 192.168.1.9)에서 Node02(192.168.2.6)의 pod(netshoot-pod-7757d5dd99-knk8, 192.168.2.138) 로 ping 테스트를 진행하였습니다.

 각 노드 및 파드에서 tcpdump 결과를 살펴보면 노드와 파드 모두eth0 에서 패킷이 보이고, pod간 통신 시에 노드의 eni가 관여하는 것을 볼 수 있고 각 파드가 존재하는 노드의 eth0, eth1에는 패킷이 도달하지 않고 pod의 네트워크 인터페이스로 직접 통신하는 것을 확인할 수 있습니다.

 

6. 파드에서 외부 통신


파드에서 외부 통신 흐름 : iptable 에 SNAT 을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신됨

# 작업용 EC2 : pod-1 Shell 에서 외부로 ping
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com

# 워커 노드 EC2 : TCPDUMP 확인
sudo tcpdump -i any -nn icmp
sudo tcpdump -i eth0 -nn icmp

# 워커 노드 EC2 : 퍼블릭IP 확인
curl -s ipinfo.io/ip ; echo

# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
## The right way to check the weather - 링크
kubectl exec -it $PODNAME1 -- curl -s ipinfo.io/ip ; echo
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul?format=3
kubectl exec -it $PODNAME1 -- curl -s wttr.in/Moon
kubectl exec -it $PODNAME1 -- curl -s wttr.in/:help

# 워커 노드 EC2
## 출력된 결과를 보고 어떻게 빠져나가는지 고민해보자!
ip rule
ip route show table main
sudo iptables -L -n -v -t nat
sudo iptables -t nat -S

# 파드가 외부와 통신시에는 아래 처럼 'AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1' 룰(rule)에 의해서 SNAT 되어서 외부와 통신!
# 참고로 뒤 IP는 eth0(ENI 첫번째)의 IP 주소이다
# --random-fully 동작 - 링크1  링크2
sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'
-A AWS-SNAT-CHAIN-0 ! -d 192.168.0.0/16 -m comment --comment "AWS SNAT CHAIN" -j AWS-SNAT-CHAIN-1
-A AWS-SNAT-CHAIN-1 ! -o vlan+ -m comment --comment "AWS, SNAT" -m addrtype ! --dst-type LOCAL -j SNAT --to-source 192.168.1.251 --random-fully

## 아래 'mark 0x4000/0x4000' 매칭되지 않아서 RETURN 됨!
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully
...

# 카운트 확인 시 AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1 에 매칭되어, 목적지가 192.168.0.0/16 아니고 외부 빠져나갈때 SNAT 192.168.1.251(EC2 노드1 IP) 변경되어 나간다!
sudo iptables -t filter --zero; sudo iptables -t nat --zero; sudo iptables -t mangle --zero; sudo iptables -t raw --zero
watch -d 'sudo iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-0; echo ; sudo iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-1; echo ; sudo iptables -v --numeric --table nat --list KUBE-POSTROUTING'

# conntrack 확인
sudo conntrack -L -n |grep -v '169.254.169'
conntrack v1.4.5 (conntrack-tools): 
icmp     1 28 src=172.30.66.58 dst=8.8.8.8 type=8 code=0 id=34392 src=8.8.8.8 dst=172.30.85.242 type=0 code=0 id=50705 mark=128 use=1
tcp      6 23 TIME_WAIT src=172.30.66.58 dst=34.117.59.81 sport=58144 dport=80 src=34.117.59.81 dst=172.30.85.242 sport=80 dport=44768 [ASSURED] mark=128 use=1

Node01(192.168.1.190)의 pod(netshoot-pod-7757d5dd99-9qdn2, 192.168.1.9)에서 8.8.8.8(외부 구글 DNS)로 ping 테스트를 진행하였습니다. 좌측 이미지의 Node01에서 tcpdump를 확인해보면 Node01의 IP인 192.168.1.190를 거쳐 외부와 통신한다는 것을 볼 수 있습니다. 이는 iptable에 SNAT을 통하여 노드의 eth0 IP로 변경되어 외부와 통신하기 때문입니다.

 

# Deployment 삭제
$ kubectl delete deploy netshoot-pod

다음 실습을 위해 디플로이먼트를 삭제합니다.

 

7. 노드에 파드 생성 갯수 제한


Secondary lPv4 addresses -> 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정합니다.

 

  • 인스턴스 타입 별 ENI 최대 개수와 할당 가능한 최대 IP 개수에 따라서 가능한 파드 배치 수가 결정됩니다.
  • aws-node 와 kube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외합니다.

최대 파드 생성 갯수 = (Number of network interfaces for the instance type × (the number of IP addressess per network interface - 1)) + 2

# t3 타입의 정보(필터) 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
 --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
 --output table
--------------------------------------
|        DescribeInstanceTypes       |
+----------+----------+--------------+
| IPv4addr | MaxENI   |    Type      |
+----------+----------+--------------+
|  15      |  4       |  t3.2xlarge  |
|  6       |  3       |  t3.medium   |
|  12      |  3       |  t3.large    |
|  15      |  4       |  t3.xlarge   |
|  2       |  2       |  t3.micro    |
|  2       |  2       |  t3.nano     |
|  4       |  3       |  t3.small    |
+----------+----------+--------------+

# c5 타입의 정보(필터) 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=c5*.* \
 --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
 --output table
 
 ----------------------------------------
|         DescribeInstanceTypes        |
+----------+----------+----------------+
| IPv4addr | MaxENI   |     Type       |
+----------+----------+----------------+
|  30      |  8       |  c5a.4xlarge   |
|  30      |  8       |  c5d.12xlarge  |
|  15      |  4       |  c5n.2xlarge   |
|  10      |  3       |  c5n.large     |
|  10      |  3       |  c5d.large     |
|  30      |  8       |  c5.4xlarge    |
|  50      |  15      |  c5a.24xlarge  |
|  50      |  15      |  c5.24xlarge   |
|  30      |  8       |  c5d.4xlarge   |
|  15      |  4       |  c5a.xlarge    |
|  30      |  8       |  c5.12xlarge   |
|  30      |  8       |  c5a.12xlarge  |
|  30      |  8       |  c5n.9xlarge   |
|  50      |  15      |  c5d.18xlarge  |
|  50      |  15      |  c5d.24xlarge  |
|  30      |  8       |  c5n.4xlarge   |
|  15      |  4       |  c5a.2xlarge   |
|  15      |  4       |  c5.xlarge     |
|  15      |  4       |  c5d.2xlarge   |
|  50      |  15      |  c5a.16xlarge  |
|  50      |  15      |  c5n.18xlarge  |
|  15      |  4       |  c5d.xlarge    |
|  50      |  15      |  c5.metal      |
|  15      |  4       |  c5.2xlarge    |
|  50      |  15      |  c5n.metal     |
|  50      |  15      |  c5d.metal     |
|  10      |  3       |  c5.large      |
|  30      |  8       |  c5.9xlarge    |
|  30      |  8       |  c5a.8xlarge   |
|  15      |  4       |  c5n.xlarge    |
|  50      |  15      |  c5.18xlarge   |
|  30      |  8       |  c5d.9xlarge   |
|  10      |  3       |  c5a.large     |
+----------+----------+----------------+
 

# 파드 사용 가능 계산 예시 : aws-node 와 kube-proxy 파드는 host-networking 사용으로 IP 2개 남음
((MaxENI * (IPv4addr-1)) + 2)
t3.medium 경우 : ((3 * (6 - 1) + 2 ) = 17개 >> aws-node 와 kube-proxy 2개 제외하면 15개

# 워커노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 pods 에 17개 정보 확인
kubectl describe node | grep Allocatable: -A7
Allocatable:
  attachable-volumes-aws-ebs:  25
  cpu:                         1930m
  ephemeral-storage:           27905944324
  hugepages-1Gi:               0
  hugepages-2Mi:               0
  memory:                      3388360Ki
  pods:                        17

최대 파드 생성 확인

# 워커 노드 EC2 - 모니터링
while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

# 작업용 EC2 - 터미널1
watch -d 'kubectl get pods -o wide'

# 작업용 EC2 - 터미널2
# 디플로이먼트 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
kubectl apply -f nginx-dp.yaml

# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=8

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=15

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=30

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=50

# 파드 생성 실패!
kubectl get pods | grep Pending
nginx-deployment-7fb7fd49b4-d4bk9   0/1     Pending   0          3m37s
nginx-deployment-7fb7fd49b4-qpqbm   0/1     Pending   0          3m37s
...

kubectl describe pod <Pending 파드> | grep Events: -A5
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  45s   default-scheduler  0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Too many pods. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod.

# 디플로이먼트 삭제
kubectl delete deploy nginx-deployment

워커 노드 EC2 - 모니터링
작업용 EC2 - 터미널1
디플로이먼트 생성 후 파드 생성 확인
replicas 15 설정
replicas 50 설정 후 Pending 상태에 머무르는 파드들이 발생
해당 파드의 이벤트 정보 확인

파드의 개수를 50개로 Scale up하면 위와 같이 더 이상 파드에 IP를 할당할 수 없게 되어 파드가 Pending 상태에 머무르게 됩니다. 이를 해결하기 위해서는 Prefix Delegation, WARM & MIN IP/Prefix Targets, Custom Network(2주차 도전과제) 설정이 필요합니다.

 

 

설정 방법

# 접두사 할당 모드 활성화하기
$ kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true
$ kubectl set env daemonset aws-node -n kube-system WARM_PREFIX_TARGET=1

# 설정 적용 확인
$ kubectl describe daemonset -n kube-system aws-node | grep -e ENABLE_PREFIX_DELEGATION -e WARM_PREFIX_TARGET

설정 완료 후 보조 프라이빗 IPv4 주소 확인
해당 노드의 eni 정보에서 IPv4 접두사가 할당된 것을 볼 수 있습니다.

 

 

8. Service & AWS LoadBalancer Controller

ClusterIP 타입
NodePort 타입
LoadBalancer 타입 (기본 모드) : NLB 인스턴스 유형
Service (LoadBalancer Controller : AWS Load Balancer Controller &nbsp;+&nbsp; NLB &nbsp; IP 모드 &nbsp;동작 with AWS VPC CNI

service (LoadBalancer Controller : AWS Load Balancer Controller  +  NLB   IP 모드  동작 with AWS VPC CNI 의 특징

  • NodePort Service 를 Bypass로 파드의 IP와 통신합니다.
  • 즉 LoadBalancer의 iptables 룰을 사용하지 않아 latency가 감소됩니다.

NLB모드 전체 정리


1. 인스턴스 유형

  • externalTrafficPolicy : ClusterIP ⇒ 2번 분산 및 SNAT으로 Client IP 확인 불가능 ← LoadBalancer 타입 (기본 모드) 동작
  • externalTrafficPolicy : Local ⇒ 1번 분산 및 ClientIP 유지, 워커 노드의 iptables 사용함

  • 노드는 외부에 공개되지 않고 로드밸런서만 외부에 공개되어, 외부 클라이언트는 로드밸랜서에 접속을 할 뿐 내부 노드의 정보를 알 수 없다
  • 로드밸런서가 부하분산하여 파드가 존재하는 노드들에게 전달한다, iptables 룰에서는 자신의 노드에 있는 파드만 연결한다 (externalTrafficPolicy: local)
  • DNAT 2번 동작 : 첫번째(로드밸런서 접속 후 빠져 나갈때), 두번째(노드의 iptables 룰에서 파드IP 전달 시)
  • 외부 클라이언트 IP 보존(유지) : AWS NLB 는 타켓인스턴스일 경우 클라이언트 IP를 유지, iptables 룰 경우도 externalTrafficPolicy 로 클라이언트 IP를 보존

부하분산 최적화 : 노드에 파드가 없을 경우 '로드밸런서'에서 노드에 헬스 체크(상태 검사)가 실패하여 해당 노드로는 외부 요청 트래픽을 전달하지 않는다

2. 인스턴스 유형 ⇒ AWS LoadBalancer 컨트롤러 파드 및 정책 설정이 필요

  1. Proxy Protocol v2 비활성화 ⇒ NLB에서 바로 파드로 인입, 단 ClientIP가 NLB로 SNAT 되어 Client IP 확인 불가능
  2. Proxy Protocol v2 활성화 ⇒ NLB에서 바로 파드로 인입 및 ClientIP 확인 가능(→ 단 PPv2 를 애플리케이션이 인지할 수 있게 설정 필요)
# OIDC 확인
aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text
aws iam list-open-id-connect-providers | jq

# IAM Policy (AWSLoadBalancerControllerIAMPolicy) 생성
curl -o iam_policy.json 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

# 혹시 이미 IAM 정책이 있지만 예전 정책일 경우 아래 처럼 최신 업데이트 할 것
# aws iam update-policy ~~~

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

# AWS Load Balancer Controller를 위한 ServiceAccount를 생성 >> 자동으로 매칭되는 IAM Role 을 CloudFormation 으로 생성됨!
# 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 \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --override-existing-serviceaccounts --approve

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

## 서비스 어카운트 확인
kubectl get serviceaccounts -n kube-system aws-load-balancer-controller -o yaml | yh

# Helm Chart 설치
helm repo add eks https://aws.github.io/eks-charts
helm repo update
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

## 설치 확인
kubectl get crd
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'
  Service Account:  aws-load-balancer-controller
 
# 클러스터롤, 롤 확인
kubectl describe clusterrolebindings.rbac.authorization.k8s.io aws-load-balancer-controller-rolebinding
kubectl describe clusterroles.rbac.authorization.k8s.io aws-load-balancer-controller-role
...
PolicyRule:
  Resources                                     Non-Resource URLs  Resource Names  Verbs
  ---------                                     -----------------  --------------  -----
  targetgroupbindings.elbv2.k8s.aws             []                 []              [create delete get list patch update watch]
  events                                        []                 []              [create patch]
  ingresses                                     []                 []              [get list patch update watch]
  services                                      []                 []              [get list patch update watch]
  ingresses.extensions                          []                 []              [get list patch update watch]
  services.extensions                           []                 []              [get list patch update watch]
  ingresses.networking.k8s.io                   []                 []              [get list patch update watch]
  services.networking.k8s.io                    []                 []              [get list patch update watch]
  endpoints                                     []                 []              [get list watch]
  namespaces                                    []                 []              [get list watch]
  nodes                                         []                 []              [get list watch]
  pods                                          []                 []              [get list watch]
  endpointslices.discovery.k8s.io               []                 []              [get list watch]
  ingressclassparams.elbv2.k8s.aws              []                 []              [get list watch]
  ingressclasses.networking.k8s.io              []                 []              [get list watch]
  ingresses/status                              []                 []              [update patch]
  pods/status                                   []                 []              [update patch]
  services/status                               []                 []              [update patch]
  targetgroupbindings/status                    []                 []              [update patch]
  ingresses.elbv2.k8s.aws/status                []                 []              [update patch]
  pods.elbv2.k8s.aws/status                     []                 []              [update patch]
  services.elbv2.k8s.aws/status                 []                 []              [update patch]
  targetgroupbindings.elbv2.k8s.aws/status      []                 []              [update patch]
  ingresses.extensions/status                   []                 []              [update patch]
  pods.extensions/status                        []                 []              [update patch]
  services.extensions/status                    []                 []              [update patch]
  targetgroupbindings.extensions/status         []                 []              [update patch]
  ingresses.networking.k8s.io/status            []                 []              [update patch]
  pods.networking.k8s.io/status                 []                 []              [update patch]
  services.networking.k8s.io/status             []                 []              [update patch]
  targetgroupbindings.networking.k8s.io/status  []                 []              [update patch]

 

iam Policy 생성
생성된 IAM Policy Arn 확인
AWS Load Balancer Controller를 위한 ServiceAccount를 생성 및 IRSA 정보, SA 정보 확인
설치 확인

생성된 IAM Role 신뢰 관계 확인

# 모니터링
watch -d kubectl get pod,svc,ep

# 작업용 EC2 - 디플로이먼트 & 서비스 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/echo-service-nlb.yaml
cat echo-service-nlb.yaml | yh
kubectl apply -f echo-service-nlb.yaml

# 확인
kubectl get deploy,pod
kubectl get svc,ep,ingressclassparams,targetgroupbindings
kubectl get targetgroupbindings -o json | jq

# AWS ELB(NLB) 정보 확인
aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text

# 웹 접속 주소 확인
kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Pod Web URL = http://"$1 }'

# 파드 로깅 모니터링
kubectl logs -l app=deploy-websrv -f

# 분산 접속 확인
NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
  52 Hostname: deploy-echo-55456fc798-2w65p
  48 Hostname: deploy-echo-55456fc798-cxl7z

# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
while true; do curl -s --connect-timeout 1 $NLB | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

작업용 EC2 - 디플로이먼트 & 서비스 생성
배포된 리소스 확인
AWS ELB(NLB) 정보 확인
웹 접속 주소 확인
분산 접속 확인

 

파드 2개 → 1개 → 3개 설정 시 동작 : auto discovery ← 어떻게 가능한지 확인


# 작업용 EC2 - 파드 1개 설정 
kubectl scale deployment deploy-echo --replicas=1

# 확인
kubectl get deploy,pod,svc,ep
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

# 작업용 EC2 - 파드 3개 설정 
kubectl scale deployment deploy-echo --replicas=3

# 확인
kubectl get deploy,pod,svc,ep
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

#
kubectl describe deploy -n kube-system aws-load-balancer-controller | grep "Service Account"
  Service Account:  aws-load-balancer-controller

# [AWS LB Ctrl] 클러스터 롤 바인딩 정보 확인
kubectl describe clusterrolebindings.rbac.authorization.k8s.io aws-load-balancer-controller-rolebinding

# [AWS LB Ctrl] 클러스터롤 확인 
kubectl describe clusterroles.rbac.authorization.k8s.io aws-load-balancer-controller-role

작업용 EC2 - 파드 1개 설정 후 확인
작업용 EC2 - 파드 3개 설정 후 확인

# 리소스 삭제
$ kubectl delete deploy deploy-echo; kubectl delete svc svc-nlb-ip-type

 

 

NLB 대상 타켓을 Instance mode 로 설정해보기


# 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gasida-web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gasida-web
  template:
    metadata:
      labels:
        app: gasida-web
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: gasida-web
        image: gasida/httpd:pp
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: svc-nlb-ip-type-pp
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
  selector:
    app: gasida-web
EOF

# 확인
kubectl get svc,ep
kubectl describe svc svc-nlb-ip-type-pp
kubectl describe svc svc-nlb-ip-type-pp | grep Annotations: -A5

# apache에 proxy protocol 활성화 확인
kubectl exec deploy/gasida-web -- apachectl -t -D DUMP_MODULES
kubectl exec deploy/gasida-web -- cat /usr/local/apache2/conf/httpd.conf

# 접속 확인
NLB=$(kubectl get svc svc-nlb-ip-type-pp -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB

# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
while true; do curl -s --connect-timeout 1 $NLB; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

# 로그 확인
kubectl logs -l app=gasida-web -f

# 삭제
kubectl delete deploy gasida-web; kubectl delete svc svc-nlb-ip-type-pp

배포 및 배포된 리소스 확인
접속 시도 및 로그 확인

9. Ingress


Ingress -> 클러스터 내부 서비스를 외부로 노출하기 위한 쿠버네티스 리소스, Web Proxy 역할

# 게임 파드와 Service, Ingress 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ingress1.yaml
cat ingress1.yaml | yh
kubectl apply -f ingress1.yaml

# 모니터링
watch -d kubectl get pod,ingress,svc,ep -n game-2048

# 생성 확인
kubectl get-all -n game-2048
kubectl get ingress,svc,ep,pod -n game-2048
kubectl get targetgroupbindings -n game-2048
NAME                               SERVICE-NAME   SERVICE-PORT   TARGET-TYPE   AGE
k8s-game2048-service2-e48050abac   service-2048   80             ip            87s

# Ingress 확인
kubectl describe ingress -n game-2048 ingress-2048

# 게임 접속 : ALB 주소로 웹 접속
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Game URL = http://"$1 }'

# 파드 IP 확인
kubectl get pod -n game-2048 -owide

# 터미널1
watch kubectl get pod -n game-2048

# 터미널2 : 파드 3개로 증가
kubectl scale deployment -n game-2048 deployment-2048 --replicas 3

# 터미널2 : 파드 1개로 감소
kubectl scale deployment -n game-2048 deployment-2048 --replicas 1

# 실습 리소스 삭제
kubectl delete ingress ingress-2048 -n game-2048
kubectl delete svc service-2048 -n game-2048 && kubectl delete deploy deployment-2048 -n game-2048 && kubectl delete ns game-2048

배포된 ingress, s리소스 확인
인그세스 상세정보 확인
해당 로드벨런서의 접근 가능한 URL을 확인합니다.
실제로 접근해보면 게임에 정상적으로 접속됩니다.

10. ExternalDNS


- K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53), Azure(DNS), GCP(Cloud DNS) 에 A 레코드(TXT 레코드)로 자동 생성/삭제해주는 기능 제공

- ExternalDNS CTRL 권한 주는 방법 3가지 : Node IAM Role, Static credentials, IRSA

사전 준비

사전 설정으로 도메인 구매 -> Route53에 도메인 등록 -> 도메인 네임서버 설행을 진행하였습니다.

 

 

# EKS 배포 시 Node IAM Role 설정되어 있음
# eksctl create cluster ... --external-dns-access ...

# 도메인 변수로 설정
MyDomain=hyunjoon.store

# 자신의 Route 53 도메인 ID 조회 및 변수 지정
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)

# 변수 확인
echo $MyDomain, $MyDnzHostedZoneId

# externaldns.yaml 파일 생성
cat <<EOT> externaldns.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  labels:
    app.kubernetes.io/name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
  labels:
    app.kubernetes.io/name: external-dns
rules:
  - apiGroups: [""]
    resources: ["services","endpoints","pods","nodes"]
    verbs: ["get","watch","list"]
  - apiGroups: ["extensions","networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
  labels:
    app.kubernetes.io/name: external-dns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
  - kind: ServiceAccount
    name: external-dns
    namespace: default # change to desired namespace: externaldns, kube-addons
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  labels:
    app.kubernetes.io/name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app.kubernetes.io/name: external-dns
  template:
    metadata:
      labels:
        app.kubernetes.io/name: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
        - name: external-dns
          image: registry.k8s.io/external-dns/external-dns:v0.13.4
          args:
            - --source=service
            - --source=ingress
            - --domain-filter=$MyDomain # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
            - --provider=aws
            #- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
            - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
            - --registry=txt
            - --txt-owner-id=$MyDnzHostedZoneId
          env:
            - name: AWS_DEFAULT_REGION
              value: ap-northeast-2 # change to region where EKS is installed
EOT

# yaml 파일 내용 확인 및 배포 및 확인
cat externaldns.yaml | yh
kubectl apply -f externaldns.yaml
kubectl get pod -l app.kubernetes.io/name=external-dns

# 로그 모니터링
kubectl logs deploy/external-dns -f

도메인 및 Route53에 등록된 도메인ID를 변수로 지정합니다.
External DNS 파드를 배포합니다.

에러 발생

external dns 파드 로그를 확인해보니 설정한 도메인에 대해 timeout이 발생하여 테스트가 제대로 이루어지지 않았습니다.

추후 다시 테스트하여 내용 업데이트하겠습니다.