Overview
CloudNet@ AEWS 스터디 2주차는 EKS Networking이다.
Kubernetes에서 제일 이해하기 어려운 내용중 하나가 Networking인 것 같다.
이번 과제를 통해 자세히 공부를 해보려고 한다.
스터디원 이지오님께서 스터디 요약내용을 간단하게 정리해 주셨다.
0. 실습 환경 배포
Amazon EKS 원클릭 배포
스터디를 진행하시는 가시다님께서 스터디원들이 아주 쉽게 CloudFormation을 사용해 배포할 수 있도록 작성해 놓으셨다.
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick.yaml
# CloudFormation 스택 배포
# aws cloudformation deploy --template-file eks-oneclick.yaml --stack-name myeks --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 MyIamUserAccessKeyID=<IAM User의 액세스키> MyIamUserSecretAccessKey=<IAM User의 시크릿 키> ClusterBaseName='<eks 이름>' --region ap-northeast-2
예시) aws cloudformation deploy --template-file eks-oneclick.yaml --stack-name myeks --parameter-overrides KeyName=somaz-key SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 MyIamUserAccessKeyID=AKIA5... MyIamUserSecretAccessKey='CVNa2...' ClusterBaseName=myeks --region ap-northeast-2
# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text
3.38.211.116
# 마스터노드 SSH 접속
ssh -i ~/.ssh/somaz-key.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
[root@myeks-bastion-EC2 ~]#
대략시간은 20분 정도 걸리고, 배가 완료가 되었다면 아래와 같이 전부 생성된다.
설치 확인
# 설치 확인
kubectl cluster-info
Kubernetes control plane is running at https://CC6D303D24394AD25143287D6C65BCB8.gr7.ap-northeast-2.eks.amazonaws.com
CoreDNS is running at https://CC6D303D24394AD25143287D6C65BCB8.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
# 노드 정보 확인
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
NAME STATUS ROLES AGE VERSION
INSTANCE-TYPE CAPACITYTYPE ZONE
ip-192-168-1-191.ap-northeast-2.compute.internal Ready <none> 4m6s v1.24.11-eks-a59e1f0 t3.medium ON_DEMAND ap-northeast-2a
ip-192-168-2-62.ap-northeast-2.compute.internal Ready <none> 4m6s v1.24.11-eks-a59e1f0 t3.medium ON_DEMAND ap-northeast-2b
ip-192-168-3-20.ap-northeast-2.compute.internal Ready <none> 4m7s v1.24.11-eks-a59e1f0 t3.medium ON_DEMAND ap-northeast-2c
# krew 플러그인 확인
kubectl krew list
PLUGIN VERSION
ctx v0.9.4
df-pv v0.3.0
get-all v1.3.8
krew v0.4.3
ns v0.9.4
기본 설정
기본설정 부분은 아주 중요하다. 노드 IP를 변수로 지정한다.
# default 네임스페이스 적용
kubectl ns default
# 노드 IP 확인 및 PrivateIP 변수 지정
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
192.168.1.191, 192.168.2.62, 192.168.3.20
# 노드 보안그룹 ID 확인
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32
# 워커 노드 SSH 접속
ssh ec2-user@$N1 hostname
ssh ec2-user@$N2 hostname
ssh ec2-user@$N3 hostname
ssh ec2-user@$N1
Last login: Tue Apr 11 23:23:29 2023 from 205.251.233.109
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
No packages needed for security; 7 packages available
Run "sudo yum update" to apply all updates.
설치 정보 확인
# 이미지 정보 확인
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort | uniq -c
3 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.12.6-eksbuild.1
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.9.3-eksbuild.3
3 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/kube-proxy:v1.24.10-minimal-eksbuild.2
# eksctl 설치/업데이트 addon 확인
eksctl get addon --cluster $CLUSTER_NAME
2023-04-30 20:27:23 [ℹ] Kubernetes version "1.24" in use by cluster "myeks"
2023-04-30 20:27:23 [ℹ] getting all addons
2023-04-30 20:27:25 [ℹ] to see issues for an addon run `eksctl get addon --name <addon-name> --cluster <cluster-name>`
NAME VERSION STATUS ISSUES IAMROLE UPDATE AVAILABLE CONFIGURATION VALUES
coredns v1.9.3-eksbuild.3 ACTIVE 0
kube-proxy v1.24.10-eksbuild.2 ACTIVE 0
vpc-cni v1.12.6-eksbuild.1 ACTIVE 0 arn:aws:iam::611841095956:role/eksctl-myeks-addon-vpc-cni-Role1-NX9J2DSO6SQZ
1. AWS VPC CNI(Container Network Interface)란?
AWS에서 쿠버네티스를 설치하면, 네트워크 설정과 동작은 AWS 네트워크 환경에 영향을 받게된다. 따라서 AWS와 쿠버네티스 사이 적절한 중계자가 필요하다. 그 역할을 수행하는 모듈이 AWS VPC CNI이다.
AWS VPC CNI는 파드의 IP를 할당해준다. 그리고 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가능하다.
아래의 그림은 CNI 플러그인을 통해 ENI(Elastic Network Interface)를 관리하는 과정이다.
노드가 프로비저닝되면 CNI 플러그인은 노드의 서브넷에서 기본 ENI로 Pool(IPs or Prefix's)를 자동으로 할당해준다.
Pool의 크기는 노드의 인스턴스 유형에 따라 결정된다. CNI는 일반적으로 파드 수에 해당하는 필요한 슬롯 수에 따라 더 많은 ENI를 인스턴스에 연결한다.
K8S Calico CNI와 AWS VPC CNI의 차이
AWS VPC CNI는 네트워크 통신의 최적화(성능, 지연)을 위해서 노드와 파드의 네트워크 대역을 동일하게 설정한다.
그리고 파드간 통신 시 일반적으로 K8S CNI는 오버레이(VXLAN, IP-IP 등) 통신을 하고, AWS VPC CNI는 동일 대역으로 직접 통신한다.
[실습] 네트워크 기본 정보 확인
# 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
---------------------------------------------------------------------
| DescribeInstances |
+-------------------+-----------------+------------------+----------+
| InstanceName | PrivateIPAdd | PublicIPAdd | Status |
+-------------------+-----------------+------------------+----------+
| myeks-ng1-Node | 192.168.3.20 | 54.180.80.24 | running |
| myeks-ng1-Node | 192.168.2.62 | 15.165.233.53 | running |
| myeks-bastion-EC2| 192.168.1.100 | 3.38.211.116 | running |
| myeks-ng1-Node | 192.168.1.191 | 13.125.230.229 | running |
+-------------------+-----------------+------------------+----------+
# 파드 IP 확인
kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase
NAME IP STATUS
aws-node-2xfc6 192.168.1.191 Running
aws-node-mp5k6 192.168.3.20 Running
aws-node-qmvmq 192.168.2.62 Running
coredns-6777fcd775-8s89l 192.168.2.153 Running
coredns-6777fcd775-t87kh 192.168.1.196 Running
kube-proxy-9bcs8 192.168.1.191 Running
kube-proxy-b8qhf 192.168.2.62 Running
kube-proxy-p9b9h 192.168.3.20 Running
# 파드 이름 확인
kubectl get pod -A -o name
pod/aws-node-2xfc6
pod/aws-node-mp5k6
pod/aws-node-qmvmq
pod/coredns-6777fcd775-8s89l
pod/coredns-6777fcd775-t87kh
pod/kube-proxy-9bcs8
pod/kube-proxy-b8qhf
pod/kube-proxy-p9b9h
# 파드 갯수 확인
kubectl get pod -A -o name | wc -l
8
노드의 네트워크 정보도 확인할 수 있다.
# 노드에 툴 설치
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
/var/log/aws-routed-eni
├── egress-v4-plugin.log
├── ipamd.log
└── plugin.log
0 directories, 3 files
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
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 192.168.1.191/24 fe80::23:4fff:fe97:3a8c/64
enid10c5eb0b26@if3 UP fe80::d4e5:bfff:fe1a:179c/64
eth1 UP 192.168.1.17/24 fe80::f4:1eff:fedf:fbec/64
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
2. 노드에서 기본 네트워크 정보 확인
워커 노드 1의 기본 네트워크 구성
- 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 과 연결되어 있다
[실습] 보조 IPv4 주소를 파드가 사용하는지 확인
# coredns 파드 IP 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-6777fcd775-8s89l 1/1 Running 0 14m 192.168.2.153 ip-192-168-2-62.ap-northeast-2.compute.internal <none> <none>
coredns-6777fcd775-t87kh 1/1 Running 0 14m 192.168.1.196 ip-192-168-1-191.ap-northeast-2.compute.internal <none> <none>
# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh ec2-user@$N1 sudo ip -c route
default via 192.168.1.1 dev eth0
169.254.169.254 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.191
192.168.1.196 dev enid10c5eb0b26 scope link
ssh ec2-user@$N2 sudo ip -c route
default via 192.168.2.1 dev eth0
169.254.169.254 dev eth0
192.168.2.0/24 dev eth0 proto kernel scope link src 192.168.2.62
192.168.2.153 dev eni127692e64e3 scope link
ssh ec2-user@$N3 sudo ip -c route
[실습] 테스트용 파드 생성 - nicolaka/netshoot
# [터미널1~3] 노드 모니터링
ssh ec2-user@$N1
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
ssh ec2-user@$N2
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
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
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
netshoot-pod-7757d5dd99-5jcpv 1/1 Running 0 2m3s 192.168.1.7 ip-192-168-1-191.ap-northeast-2.compute.internal <none> <none>
netshoot-pod-7757d5dd99-8r8jn 1/1 Running 0 2m3s 192.168.3.130 ip-192-168-3-20.ap-northeast-2.compute.internal <none> <none>
netshoot-pod-7757d5dd99-tzx5c 1/1 Running 0 2m3s 192.168.2.37 ip-192-168-2-62.ap-northeast-2.compute.internal <none> <none>
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME IP
netshoot-pod-7757d5dd99-5jcpv 192.168.1.7
netshoot-pod-7757d5dd99-8r8jn 192.168.3.130
netshoot-pod-7757d5dd99-tzx5c 192.168.2.37
파드가 생성되면, 워커 노드에 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
----------------
- 테스트용 파드 접속(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
3. 노드 간 파드 통신
파드간 통신 시 tcpdump 내용을 확인하고 통신 과정을 알아본다.
파드간 통신 흐름
AWS VPC CNI 경우 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능하다
파드 간 통신 과정 참고
[실습] 파드간 통신 테스트 및 확인 : 별도의 NAT 동작 없이 통신 가능!
# 파드 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
PING 192.168.3.130 (192.168.3.130) 56(84) bytes of data.
64 bytes from 192.168.3.130: icmp_seq=1 ttl=62 time=1.48 ms
64 bytes from 192.168.3.130: icmp_seq=2 ttl=62 time=1.30 ms
# 파드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
0: from all lookup local
512: from all to 192.168.1.196 lookup main
512: from all to 192.168.1.7 lookup main
1024: from all fwmark 0x80/0x80 lookup main
32766: from all lookup main
32767: from all lookup default
# routing table management 확인
ip route show table local
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 192.168.1.0 dev eth0 proto kernel scope link src 192.168.1.191
broadcast 192.168.1.0 dev eth1 proto kernel scope link src 192.168.1.17
local 192.168.1.17 dev eth1 proto kernel scope host src 192.168.1.17
local 192.168.1.191 dev eth0 proto kernel scope host src 192.168.1.191
broadcast 192.168.1.255 dev eth0 proto kernel scope link src 192.168.1.191
broadcast 192.168.1.255 dev eth1 proto kernel scope link src 192.168.1.17
# 디폴트 네트워크 정보를 eth0 을 통해서 빠져나간다
ip route show table main
default via 192.168.1.1 dev eth0
...
4. 파드에서 외부 통신
파드에서 외부 통신 흐름
iptable 에 SNAT 을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신된다.
VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT 하거나 혹은 SNAT 없이 통신을 할 수 있다 - 링크
[실습] 파드에서 외부 통신 테스트 및 확인
파드 shell 실행 후 외부로 ping 테스트 & 워커 노드에서 tcpdump 및 iptables 정보 확인한다.
# 작업용 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
13.125.230.229
# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
## The right way to check the weather - 링크
kubectl exec -it $PODNAME1 -- curl -s ipinfo.io/ip ; echo
13.125.230.229
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
0: from all lookup local
512: from all to 192.168.1.196 lookup main
512: from all to 192.168.1.7 lookup main
1024: from all fwmark 0x80/0x80 lookup main
32766: from all lookup main
32767: from all lookup default
ip route show table main
default via 192.168.1.1 dev eth0
169.254.169.254 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.191
192.168.1.7 dev eni5f69ed21af2 scope link
192.168.1.196 dev enid10c5eb0b26 scope link
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.191 --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 변경되어 나간다!
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
다음 실습을 위해서 파드 삭제
kubectl delete deploy netshoot-pod
5. 노드에 파드 생성 갯수 제한
Secondary IPv4 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.medium 사용 시
# 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 | c5d.12xlarge |
| 30 | 8 | c5a.4xlarge |
| 10 | 3 | c5n.large |
| 10 | 3 | c5d.large |
| 30 | 8 | c5.4xlarge |
| 15 | 4 | c5n.2xlarge |
| 50 | 15 | c5d.24xlarge |
| 30 | 8 | c5a.12xlarge |
| 15 | 4 | c5a.xlarge |
| 50 | 15 | c5.24xlarge |
| 30 | 8 | c5d.4xlarge |
| 15 | 4 | c5.xlarge |
| 50 | 15 | c5a.24xlarge |
| 15 | 4 | c5d.2xlarge |
| 30 | 8 | c5.12xlarge |
| 30 | 8 | c5n.4xlarge |
| 50 | 15 | c5d.18xlarge |
| 30 | 8 | c5n.9xlarge |
| 15 | 4 | c5a.2xlarge |
| 15 | 4 | c5.2xlarge |
| 50 | 15 | c5d.metal |
| 10 | 3 | c5.large |
| 15 | 4 | c5d.xlarge |
| 30 | 8 | c5a.8xlarge |
| 50 | 15 | c5n.metal |
| 50 | 15 | c5n.18xlarge |
| 50 | 15 | c5a.16xlarge |
| 50 | 15 | c5.metal |
| 30 | 8 | c5.9xlarge |
| 50 | 15 | c5.18xlarge |
| 15 | 4 | c5n.xlarge |
| 10 | 3 | c5a.large |
| 30 | 8 | c5d.9xlarge |
+----------+----------+----------------+
# 파드 사용 가능 계산 예시 : 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
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-6fb79bc456-2f8pp 1/1 Running 0 2m14s 192.168.2.155 ip-192-168-2-62.ap-northeast-2.compute.internal <none> <none>
nginx-deployment-6fb79bc456-qqzfg 1/1 Running 0 2m14s 192.168.3.122 ip-192-168-3-20.ap-northeast-2.compute.internal <none> <none>
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME IP
nginx-deployment-6fb79bc456-2f8pp 192.168.2.155
nginx-deployment-6fb79bc456-qqzfg 192.168.3.122
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 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-6fb79bc456-6jn4c 0/1 Pending 0 69s
nginx-deployment-6fb79bc456-8v95z 0/1 Pending 0 69s
nginx-deployment-6fb79bc456-ffwnh 0/1 Pending 0 69s
nginx-deployment-6fb79bc456-jqw2g 0/1 Pending 0 69s
nginx-deployment-6fb79bc456-k9hg4 0/1 Pending 0 69s
nginx-deployment-6fb79bc456-zgxtx 0/1 Pending 0 69s
nginx-deployment-6fb79bc456-zsdzl 0/1 Pending 0 69s
...
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
해결 방안 : Prefix Delegation, WARM & MIN IP/Prefix Targets, Custom Network ⇒ 2주차 도전과제
워커노드의 최대 생성 파드 갯수 늘리기(도전과제)
워커 노드의 최대 생성 파드 갯수를 늘리는 방법은 두가지가 있다.
- Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정
- IPv4 Prefix Delegation : IPv4 28bit 서브넷(prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정
이전 PKOS Study에서 IPv4 Prefix Delegation을 사용하여 해결한 경험이 있다.
아래의 링크의 [과제2]번에 정리해 놓았다.
2023.01.16 - [교육, 커뮤니티 후기] - PKOS 쿠버네티스 스터디 2주차 - 쿠버네티스 네트워크
6. Service & AWS LoadBalancer Controller
Kubernetes 서비스에는 ClusterIP / NodePort / LoadBalancer가 있다.
해당 내용 관련해서는 인프런 강의를 들으며 정리한 내용이 있다.
2022.08.29 - [교육, 커뮤니티 후기] - <인프런> 대세는 쿠버네티스 [초급] - No.9 Object - Service
ClusterIP
NodePort
LoadBalancer (기본 모드) : NLB 인스턴스 유형
Service(LoadBalancer Controller)
- AWS Load Balancer Controller + NLB IP 모드 동작 with AWS VPC CNI
이렇게 사용하면, NodePort Service를 Bypass로 바로 파드의 IP와 통신가능하다.
따라서 LoadBalancer의 iptables 룰을 사용하지 않기 때문에 latency가 줄어든다.
NLB 모드 전체 정리
인스턴스 유형
- externalTrafficPolicy : ClusterIP ⇒ 2번 분산 및 SNAT으로 Client IP 확인 불가능 ← LoadBalancer 타입 (기본 모드) 동작한다.
- externalTrafficPolicy : Local ⇒ 1번 분산 및 ClientIP 유지, 워커 노드의 iptables 사용한다.
이전에 externalTrafficPolicy Local에 대해서 테스트한 적이 있다. 해당 내용도 마찬가지로 인프런 강의를 들으며 정리한 내용이 있다.
2022.08.31 - [교육, 커뮤니티 후기] - <인프런> 대세는 쿠버네티스 [초급] - No.10 Object - Service 실습
외부 클라이언트가 '로드밸런서' 접속 시 부하분산 되어 노드 도달 후 iptables 룰로 목적지 파드와 통신된다.
AWS LoadBalancer Controller 배포 with IRSA
# 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 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
## 서비스 어카운트 확인
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
NAME CREATED AT
eniconfigs.crd.k8s.amazonaws.com 2023-04-30T11:09:21Z
ingressclassparams.elbv2.k8s.aws 2023-04-30T13:18:07Z
securitygrouppolicies.vpcresources.k8s.aws 2023-04-30T11:09:23Z
targetgroupbindings.elbv2.k8s.aws 2023-04-30T13:18:07Z
kubectl get deployment -n kube-system aws-load-balancer-controller
NAME READY UP-TO-DATE AVAILABLE AGE
aws-load-balancer-controller 2/2 2 2 26s
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]
서비스/파드 배포 테스트 with NLB
# 모니터링
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
k get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-echo-5c4856dfd6-k5rbj 1/1 Running 0 2m7s 192.168.1.19 ip-192-168-1-191.ap-northeast-2.compute.internal <none> <none>
deploy-echo-5c4856dfd6-pnlwm 1/1 Running 0 2m7s 192.168.3.32 ip-192-168-3-20.ap-northeast-2.compute.internal <none> <none>
# 확인
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
Hostname: deploy-echo-5c4856dfd6-k5rbj
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.0 - lua: 10008
Request Information:
client_address=192.168.2.11
method=GET
real path=/
query=
request_version=1.1
request_uri=http://k8s-default-svcnlbip-e22aeb39f6-9af1845910af8e93.elb.ap-northeast-2.amazonaws.com:8080/
Request Headers:
accept=*/*
host=k8s-default-svcnlbip-e22aeb39f6-9af1845910af8e93.elb.ap-northeast-2.amazonaws.com
user-agent=curl/7.88.1
Request Body:
-no body in request-
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
57 Hostname: deploy-echo-5c4856dfd6-k5rbj
43 Hostname: deploy-echo-5c4856dfd6-pnlwm
# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
while true; do curl -s --connect-timeout 1 $NLB | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
AWS NLB의 대상 그룹 확인 : IP를 확인해보자
- 파드 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
75 Hostname: deploy-echo-5c4856dfd6-pnlwm
16 Hostname: deploy-echo-5c4856dfd6-xlqrx
9 Hostname: deploy-echo-5c4856dfd6-nw9nm
# [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
실습 리소스 삭제
kubectl delete deploy deploy-echo; kubectl delete svc svc-nlb-ip-type
7. Ingress
클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS) - Web Proxy 역할이다.
서비스/파드 배포 테스트 with Ingress(ALB)
# 게임 파드와 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-bd60172616 service-2048 80 ip 44s
# 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 }'
Game URL = http://k8s-game2048-ingress2-70d50ce3fd-942502217.ap-northeast-2.elb.amazonaws.com
# 파드 IP 확인
kubectl get pod -n game-2048 -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deployment-2048-6bc9fd6bf5-5qpzq 1/1 Running 0 2m 192.168.1.10 ip-192-168-1-191.ap-northeast-2.compute.internal <none> <none>
deployment-2048-6bc9fd6bf5-kdqb7 1/1 Running 0 2m 192.168.3.122 ip-192-168-3-20.ap-northeast-2.compute.internal <none> <none>
ALB 대상 그룹에 등록된 대상 확인 : ALB에서 파드 IP로 직접 전달
파드 3개로 증가시켜본다.
# 터미널1
watch kubectl get pod -n game-2048
# 터미널2 : 파드 3개로 증가
kubectl scale deployment -n game-2048 deployment-2048 --replicas 3
파드 1개로 감소해본다.
# 터미널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
8. ExternalDNS
K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53), Azure(DNS), GCP(Cloud DNS) 에 A 레코드(TXT 레코드)로 자동 생성/삭제한다.
ExternalDNS CTRL 권한 주는 방법 3가지가 있다.
- Node IAM Role, Static credentials, IRSA
AWS Route 53 정보 확인 & 변수 지정 : Public 도메인 소유
# 자신의 도메인 변수 지정 : 소유하고 있는 자신의 도메인을 입력하시면 됩니다
MyDomain=<자신의 도메인>
MyDomain=somaz.link
# 자신의 Route 53 도메인 ID 조회 및 변수 지정
aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." | jq
aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Name"
"somaz.link."
aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text
/hostedzone/Z03204211VEUZG9O0RLE5
MyDnzHostedZoneId=`aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text`
echo $MyDnzHostedZoneId
/hostedzone/Z03204211VEUZG9O0RLE5
# (옵션) NS 레코드 타입 첫번째 조회
aws route53 list-resource-record-sets --output json --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'NS']" | jq -r '.[0].ResourceRecords[].Value'
# (옵션) A 레코드 타입 모두 조회
aws route53 list-resource-record-sets --output json --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']"
# A 레코드 타입 조회
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A'].Name" | jq
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A'].Name" --output text
# A 레코드 값 반복 조회
while true; do aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq ; date ; echo ; sleep 1; done
ExternalDNS 설치
# EKS 배포 시 Node IAM Role 설정되어 있음
# eksctl create cluster ... --external-dns-access ...
#
MyDomain=<자신의 도메인>
MyDomain=somaz.link
# 자신의 Route 53 도메인 ID 조회 및 변수 지정
MyDnzHostedZoneId=`aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text`
# 변수 확인
echo $MyDomain, $MyDnzHostedZoneId
somaz.link, /hostedzone/Z03204211VEUZG9O0RLE5
# 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
NAME READY STATUS RESTARTS AGE
external-dns-d56b55b67-w2wj2 1/1 Running 0 15s
# 로그 모니터링
kubectl logs deploy/external-dns -f
Service(NLB) + 도메인 연동(ExternalDNS)
# 터미널1 (모니터링)
watch -d 'kubectl get pod,svc'
kubectl logs deploy/external-dns -f
# 테트리스 디플로이먼트 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: tetris
labels:
app: tetris
spec:
replicas: 1
selector:
matchLabels:
app: tetris
template:
metadata:
labels:
app: tetris
spec:
containers:
- name: tetris
image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
name: tetris
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-backend-protocol: "http"
#service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "80"
spec:
selector:
app: tetris
ports:
- port: 80
protocol: TCP
targetPort: 80
type: LoadBalancer
loadBalancerClass: service.k8s.aws/nlb
EOF
# 배포 확인 : CLB 배포 확인
kubectl get deploy,svc,ep tetris
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/tetris 1/1 1 1 9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/tetris LoadBalancer 10.100.111.240 k8s-default-tetris-23600a201f-64fddb3aad6625ff.elb.ap-northeast-2.amazonaws.com 80:31738/TCP 9s
NAME ENDPOINTS AGE
endpoints/tetris 192.168.2.241:80 9s
# NLB에 ExternanDNS 로 도메인 연결
kubectl annotate service tetris "external-dns.alpha.kubernetes.io/hostname=tetris.$MyDomain"
# Route53에 A레코드 확인
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A'].Name" | jq .[]
"tetris.somaz.link."
# 확인
dig +short tetris.$MyDomain @8.8.8.8
dig +short tetris.$MyDomain
3.39.35.101
3.37.154.134
3.37.189.204
# 도메인 체크
echo -e "My Domain Checker = https://www.whatsmydns.net/#A/tetris.$MyDomain"
My Domain Checker = https://www.whatsmydns.net/#A/tetris.somaz.link
# 웹 접속 주소 확인 및 접속
echo -e "Tetris Game URL = http://tetris.$MyDomain"
Tetris Game URL = http://tetris.somaz.link
아래와 같이 자동으로 레코드가 생성된다.
리소스 삭제 ← 삭제 시 externaldns 에 의해서 A레코드도 같이 삭제
kubectl delete deploy,svc tetris
리소스를 삭제하면 자동으로 레코드가 사라진다.
Service(NLB + TLS) + 도메인 연동(ExternalDNS)
AWS의 Certificate Manager로 도메인 인증서는 발급해놓은 상태여야 한다.
아래의 PKOS 스터디를 하면서 인증서를 미리 발급해놨다.
2023.01.16 - [교육, 커뮤니티 후기] - PKOS 쿠버네티스 스터디 2주차 - 쿠버네티스 네트워크
방법은 아까와 크게 다르지 않다.
# 사용 리전의 인증서 ARN 확인
aws acm list-certificates --max-items 10
{
"CertificateSummaryList": [
{
"CertificateArn": "arn:aws:acm:ap-northeast-2:611841095956:certificate/75e6fb4f-5999-4701-80f2-ab94e015bc9b",
"DomainName": "*.somaz.link",
"SubjectAlternativeNameSummaries": [
"*.somaz.link",
"somaz.link"
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN
arn:aws:acm:ap-northeast-2:611841095956:certificate/75e6fb4f-5999-4701-80f2-ab94e015bc9b
# 테트리스 디플로이먼트 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: tetris
labels:
app: tetris
spec:
replicas: 1
selector:
matchLabels:
app: tetris
template:
metadata:
labels:
app: tetris
spec:
containers:
- name: tetris
image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
name: tetris
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-backend-protocol: "http"
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "${CERT_ARN}"
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"
external-dns.alpha.kubernetes.io/hostname: "tetris.${MyDomain}" # Replace with your domain
spec:
selector:
app: tetris
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 80
type: LoadBalancer
loadBalancerClass: service.k8s.aws/nlb
EOF
# 배포 확인 : CLB 배포 확인
kubectl get deploy,svc,ep tetris
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/tetris 1/1 1 1 50s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/tetris LoadBalancer 10.100.154.43 k8s-default-tetris-3bb158f2f9-67ba9f6fb06d60d3.elb.ap-northeast-2.amazonaws.com 80:31437/TCP,443:31039/TCP 50s
NAME ENDPOINTS AGE
endpoints/tetris 192.168.2.216:80,192.168.2.216:80 50s
# Route53에 A레코드 확인
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq
aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A'].Name" | jq .[]
"tetris.somaz.link."
# 웹 접속 주소 확인 및 접속
echo -e "Tetris Game URL = https://tetris.$MyDomain"
Tetris Game URL = https://tetris.somaz.link
리소스 삭제 ← 삭제 시 externaldns 에 의해서 A레코드도 같이 삭제
kubectl delete deploy,svc tetris
Ingress(ALB + HTTPS) + 도메인 연동(ExternalDNS)
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN
arn:aws:acm:ap-northeast-2:611841095956:certificate/75e6fb4f-5999-4701-80f2-ab94e015bc9b
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: tetris
labels:
app: tetris
spec:
replicas: 1
selector:
matchLabels:
app: tetris
template:
metadata:
labels:
app: tetris
spec:
containers:
- name: tetris
image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
name: tetris
spec:
selector:
app: tetris
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tetris
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn: "${CERT_ARN}"
external-dns.alpha.kubernetes.io/hostname: tetris.${MyDomain}
spec:
rules:
- host: tetris.${MyDomain}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tetris
port:
number: 80
EOF
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/tetris 1/1 1 1 59s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/tetris ClusterIP 10.100.242.255 <none> 80/TCP 59s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/tetris <none> tetris.somaz.link k8s-default-tetris-d74062c3c8-532279177.ap-northeast-2.elb.amazonaws.com 80 3m54s
NAME ENDPOINTS AGE
endpoints/tetris 192.168.2.139:80 59s
리소스 삭제 ← 삭제 시 externaldns 에 의해서 A레코드도 같이 삭제
kubectl delete deploy,svc,ingress tetris
(실습 완료 후) 자원 삭제
공통 : AWS CloudFormation 에 loadbalancer 관련 IRSA 스택 삭제
aws cloudformation delete-stack --stack-name eksctl-$CLUSTER_NAME-addon-iamserviceaccount-kube-system-aws-load-balancer-controller
삭제 방안1 : 장점(1줄 명령어로 완전 삭제), 단점(삭제 실행과 완료까지 SSH 세션 유지 필요)
eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME
4개의 스택이 순서대로 삭제된다.
삭제 방안2 : 장점(eks 삭제 실행 후 SSH 세션이 중지되도 eks는 삭제 완료됨), 단점(eks 삭제 후 작업용 EC2 관련 CloudFormation 스택 삭제 필요)
# EKS 클러스터 삭제 (Shell 종료되어도 삭제가 진행됩니다!)
nohup eksctl delete cluster --name $CLUSTER_NAME 1> /root/delete-eks.log 2>&1 &
# (옵션) 삭제 로그 확인
tail -f /root/delete-eks.log
# 삭제완료 후 cloudfromation 스택 삭제
aws cloudformation delete-stack --stack-name myeks
Reference
'교육, 커뮤니티 후기 > AEWS 스터디' 카테고리의 다른 글
AEWS 스터디 6주차 - EKS Security (4) | 2023.06.03 |
---|---|
AEWS 스터디 5주차 - EKS Autoscaling (0) | 2023.05.27 |
AEWS 스터디 4주차 - EKS Observability (0) | 2023.05.17 |
AEWS 스터디 3주차 - EKS Storage & Node 관리 (1) | 2023.05.11 |
AEWS 스터디 1주차 - AWS EKS 설치 및 기본 사용 (2) | 2023.04.30 |