카이도스의 Tech Blog
EKS 2주차 - EKS Networking 본문
CloudNet@-가시다(Gasida)님의 EKS 스터디를 기준으로 작성됩니다.
지난글 : 2023.04.26 - [EKS] - EKS 1주차 - Amzaon EKS 설치 및 기본 사용
비슷한글 : 2023.01.26 - [KOPS] - PKOS(쿠버네티스)2주차 - 쿠버네티스 네트워크
0. 실습 환경 배포
0-1. Amazon EKS 윈클릭 배포 & 기본 설정(CloudFormation으로 진행함)
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick.yaml
# CloudFormation 스택 배포 (20~25분 소요)
# 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=PJH-aws-test SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 MyIamUserAccessKeyID=AKIATA.... MyIamUserSecretAccessKey='SX+o7l.....' 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.151.162
# 작업용EC2 SSH 접속
ssh -i ~/PJH-aws-test.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
0-2. 기본 설정
# default 네임스페이스 적용
kubectl ns default
# 노드 정보 확인
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-249.ap-northeast-2.compute.internal Ready <none> 3m32s v1.24.11-eks-a59e1f0 t3.medium ON_DEMAND ap-northeast-2a
ip-192-168-2-25.ap-northeast-2.compute.internal Ready <none> 3m29s v1.24.11-eks-a59e1f0 t3.medium ON_DEMAND ap-northeast-2b
ip-192-168-3-240.ap-northeast-2.compute.internal Ready <none> 3m32s v1.24.11-eks-a59e1f0 t3.medium ON_DEMAND ap-northeast-2c
eksctl get iamidentitymapping --cluster myeks
# 노드 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.249, 192.168.2.25, 192.168.3.240
# 노드 보안그룹 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
0-3. 설치 정보 확인
# 이미지 정보 확인
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
NAME VERSION STATUS ISSUES IAMROLE
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::911283464785:role/eksctl-myeks-addon-vpc-cni-Role1-1TZPXJONK9XGE
1. AWS VPN CNI 소개
1-1. K8S CNI : Container Network Interface는 k8s 네트워크 환경을 구성해준다. - 링크, 다양한 플러그인 존재 - 링크
- AWS VPC CNI : 파드의 IP를 할당해준다. 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가능하다.
같은대역에 있기때문에 통신의 최적화(성능, 지연) 가능
- K8S Calico CNI vs AWS VPC CNI
1-2. 워커노드에 생성 가능한 최대 파드 갯수 - 링크
1-3. 실습 : 네트워크 기본 정보 확인
# 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.240 | 43.200.177.106 | running |
| myeks-ng1-Node | 192.168.2.25 | 3.38.189.164 | running |
| myeks-bastion-EC2| 192.168.1.100 | 3.38.151.162 | running |
| myeks-ng1-Node | 192.168.1.249 | 3.36.111.41 | 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-4dxzb 192.168.1.249 Running
aws-node-svbrx 192.168.3.240 Running
aws-node-sz772 192.168.2.25 Running
coredns-6777fcd775-7v7gq 192.168.2.27 Running
coredns-6777fcd775-jj7l8 192.168.1.163 Running
kube-proxy-6l6kg 192.168.2.25 Running
kube-proxy-t57tv 192.168.1.249 Running
kube-proxy-tfdcv 192.168.3.240 Running
# 파드 이름 확인
kubectl get pod -A -o name
# 파드 갯수 확인
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.249/24 fe80::e0:92ff:fe58:bc28/64
eni93873172f14@if3 UP fe80::4a:c6ff:fe6f:695d/64
eth1 UP 192.168.1.59/24 fe80::19:66ff:fe60:8ba4/64
ssh ec2-user@$N1 sudo ip -c addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 02:e0:92:58:bc:28 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.249/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 2879sec preferred_lft 2879sec
inet6 fe80::e0:92ff:fe58:bc28/64 scope link
valid_lft forever preferred_lft forever
3: eni93873172f14@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 02:4a:c6:6f:69:5d brd ff:ff:ff:ff:ff:ff link-netns cni-baeb4b56-0665-2367-3ca0-abc3059c5531
inet6 fe80::4a:c6ff:fe6f:695d/64 scope link
valid_lft forever preferred_lft forever
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 02:19:66:60:8b:a4 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.59/24 brd 192.168.1.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::19:66ff:fe60:8ba4/64 scope link
valid_lft forever preferred_lft forever
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.249
192.168.1.163 dev eni93873172f14 scope link
ssh ec2-user@$N1 sudo iptables -t nat -S
ssh ec2-user@$N1 sudo iptables -t nat -L -n -v
2. 노드에서 기본 네트워크 정보 확인
2-1. 워커노드 기본 네트워크 구성
- Network 네임스페이스는 호스트(Root)와 파드 별(Per Pod)로 구분된다.
- 특정한 파드(kube-proxy, aws-node)는 호스트(Root)의 IP를 그대로 사용한다 → 파드의 Host Network 옵션 - 참고링크
- t3.medium의 경우 ENI 마다 최대 6개의 IP를 가질 수 있다.
- ENI0, ENI1 으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질 수 있다.
- coredns 파드는 veth으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0과 연결되어 있다.
2-2. 실습 : 보조 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-7v7gq 1/1 Running 0 10m 192.168.2.27 ip-192-168-2-25.ap-northeast-2.compute.internal <none> <none>
coredns-6777fcd775-jj7l8 1/1 Running 0 10m 192.168.1.163 ip-192-168-1-249.ap-northeast-2.compute.internal <none> <none>
# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh ec2-user@$N1 sudo ip -c route
...
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.249
...
ssh ec2-user@$N2 sudo ip -c route
...
192.168.2.0/24 dev eth0 proto kernel scope link src 192.168.2.25
...
ssh ec2-user@$N3 sudo ip -c route
...
192.168.3.0/24 dev eth0 proto kernel scope link src 192.168.3.240
2-3. 실습 : 테스트용 파드 생성 - 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
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
- 파드가 생성되면, 워커 노드에 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. 노드 간 파드 통신
3-1. 파드간 통신 흐름 : AWS VPN CNI의 경우 별도의 오버레이 통신기술 없이, VPC Native하게 파드간 직접 통신이
가능하다.
- 파드간 통신 시 과정 참고
3-2. 실습 : 파드간 통신 테스트 및 확인 - 별도의 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
# 파드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
...
4. 파드에서 외부 통신
4-1. 파드에서 외부 통신 흐름 : iptable에 SNAT을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신됨.
- VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT 하거나
혹은 SNAT 없이 통신을 할 수 있다 - 링크
4-2. 실습 : 파드에서 외부 통신 테스트 및 확인
##파드 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
# 작업용 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
##다음 실습을 위해서 파드 삭제
kubectl delete deploy netshoot-pod
5. 노드에 파드 생성 갯수 제한
PKOS 2주차 참고
6. Service & AWS LoadBalancer Controller
* 서비스 종류
- ClusterIP 타입
- NodePort 타입
- LoadBalancer 타입 (기본 모드) : NLB 인스턴스 유형
- Service (LoadBalancer Controller) : AWS Load Balancer Controller + NLB IP 모드 동작 with AWS VPC CNI
NLB 모드 전체 정리
1. 인스턴스 유형
a. externalTrafficPolicy : ClusterIP ⇒ 2번 분산 및 SNAT으로 Client IP 확인 불가능 ← LoadBalancer 타입 (기본 모드) 동작
b. externalTrafficPolicy : Local ⇒ 1번 분산 및 ClientIP 유지, 워커 노드의 iptables 사용함
2. IP 유형 ⇒ 반드시 AWS LoadBalancer 컨트롤러 파드 및 정책 설정이 필요함!
a. Proxy Protocol v2 비활성화 ⇒ NLB에서 바로 파드로 인입, 단 ClientIP가 NLB로 SNAT 되어 Client IP 확인 불가능
b. Proxy Protocol v2 활성화 ⇒ NLB에서 바로 파드로 인입 및 ClientIP 확인 가능
(→ 단 PPv2 를 애플리케이션이 인지할 수 있게 설정 필요)
6-1. 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 정책이 있지만 예전 정책일 경우 아래 처럼 최신 업데이트 할 것
# 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
NAME CREATED AT
eniconfigs.crd.k8s.amazonaws.com 2023-05-12T03:58:15Z
ingressclassparams.elbv2.k8s.aws 2023-05-12T04:37:25Z
securitygrouppolicies.vpcresources.k8s.aws 2023-05-12T03:58:17Z
targetgroupbindings.elbv2.k8s.aws 2023-05-12T04:37:25Z
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 22s
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 Role 신뢰 관계 확인
6-2. 서비스/파드 배포 테스트 with NLB - 링크 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
# 확인
kubectl get deploy,pod
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deploy-echo 2/2 2 2 6s
NAME READY STATUS RESTARTS AGE
pod/deploy-echo-5c4856dfd6-k464z 1/1 Running 0 6s
pod/deploy-echo-5c4856dfd6-zjnf8 1/1 Running 0 6s
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 }'
Pod Web URL = http://k8s-default-svcnlbip-2f4fd5473d-7b161f30932ae391.elb.ap-northeast-2.amazonaws.com
# 파드 로깅 모니터링
kubectl logs -l app=deploy-websrv -f
192.168.2.64 - - [12/May/2023:04:47:39 +0000] "GET / HTTP/1.1" 200 1098 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
192.168.2.64 - - [12/May/2023:04:47:39 +0000] "GET /favicon.ico HTTP/1.1" 200 1120 "http://k8s-default-svcnlbip-2f4fd5473d-7b161f30932ae391.elb.ap-northeast-2.amazonaws.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
...
# 분산 접속 확인
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
54 Hostname: deploy-echo-5c4856dfd6-zjnf8
46 Hostname: deploy-echo-5c4856dfd6-k464z
# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
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
87 Hostname: deploy-echo-5c4856dfd6-k464z
# 작업용 EC2 - 파드 3개 설정
kubectl scale deployment deploy-echo --replicas=3
# 확인
kubectl get deploy,pod,svc,ep
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/deploy-echo 3/3 3 3 8m57s
...
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
37 Hostname: deploy-echo-5c4856dfd6-h27lw
32 Hostname: deploy-echo-5c4856dfd6-k464z
31 Hostname: deploy-echo-5c4856dfd6-xwbrq
#
kubectl describe deploy -n kube-system aws-load-balancer-controller | grep 'Service count'
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
# 실습 리소스 삭제
kubectl delete deploy deploy-echo; kubectl delete svc svc-nlb-ip-type
6-3. NLB IP Target & Proxy Protocol v2 활성화 : NLB에서 바로 파드로 인입 및 ClientIP 확인 설정 - 링크 image 참고
# 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: pjh-web
spec:
replicas: 1
selector:
matchLabels:
app: pjh-web
template:
metadata:
labels:
app: pjh-web
spec:
terminationGracePeriodSeconds: 0
containers:
- name: pjh-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: pjh-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/pjh-web -- apachectl -t -D DUMP_MODULES
kubectl exec deploy/pjh-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
<html><body><h1>It works!</h1></body></html>
# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
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 pjh-web; kubectl delete svc svc-nlb-ip-type-pp
7. Ingress
인그레스 소개 : 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS) - Web Proxy 역할AWS Load Balancer Controller + Ingress (ALB) IP 모드 동작 with AWS VPC CNI
7-1. 서비스/파드 배포 테스트 with Ingress(ALB) - 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-c42c83a982 service-2048 80 ip 8s
# 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-124911108.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-5q2r5 1/1 Running 0 54s 192.168.1.234 ip-192-168-1-84.ap-northeast-2.compute.internal <none> <none>
deployment-2048-6bc9fd6bf5-q4h29 1/1 Running 0 54s 192.168.3.172 ip-192-168-3-145.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
8-1. AWS Route 53 정보 확인 & 변수 지정 : Public 도메인 소유하고있어야 함!
# 자신의 도메인 변수 지정 : 소유하고 있는 자신의 도메인을 입력하시면 됩니다
MyDomain=<자신의 도메인>
MyDomain=pjhtest.click
# 자신의 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"
aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text
MyDnzHostedZoneId=`aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text`
echo $MyDnzHostedZoneId
# (옵션) 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
8-2. ExternalDNS 설치 - 링크
# EKS 배포 시 Node IAM Role 설정되어 있음
# eksctl create cluster ... --external-dns-access ...
#
MyDomain=<자신의 도메인>
MyDomain=pjhtest.click
# 자신의 Route 53 도메인 ID 조회 및 변수 지정
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
# 변수 확인
echo $MyDomain, $MyDnzHostedZoneId
# ExternalDNS 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
cat externaldns.yaml | yh
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
# 확인 및 로그 모니터링
kubectl get pod -l app.kubernetes.io/name=external-dns -n kube-system
NAME READY STATUS RESTARTS AGE
external-dns-c75c58f7d-8fqtp 1/1 Running 0 6s
kubectl logs deploy/external-dns -n kube-system -f
....
time="2023-05-12T07:55:40Z" level=info msg="Instantiating new Kubernetes client"
time="2023-05-12T07:55:40Z" level=info msg="Using inCluster-config based on serviceaccount-token"
time="2023-05-12T07:55:40Z" level=info msg="Created Kubernetes client https://10.100.0.1:443"
time="2023-05-12T07:55:42Z" level=info msg="Applying provider record filter for domains: [pjhtest.click. .pjhtest.click.]"
time="2023-05-12T07:55:42Z" level=info msg="All records are already up to date"
8-3. Service(NLB) + 도메인 연동(ExternalDNS) - 도메인체크
# 터미널1 (모니터링)
watch -d 'kubectl get pod,svc'
kubectl logs deploy/external-dns -n kube-system -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
# 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.pjhtest.click."
# 확인
dig +short tetris.$MyDomain @8.8.8.8
3.34.7.52
52.79.161.201
3.37.230.41
dig +short tetris.$MyDomain
# 도메인 체크
echo -e "My Domain Checker = https://www.whatsmydns.net/#A/tetris.$MyDomain"
My Domain Checker = https://www.whatsmydns.net/#A/tetris.pjhtest.click
# 웹 접속 주소 확인 및 접속
echo -e "Tetris Game URL = http://tetris.$MyDomain"
Tetris Game URL = http://tetris.pjhtest.click
# 리소스 삭제 ← 삭제 시 externaldns 에 의해서 A레코드도 같이 삭제됨
kubectl delete deploy,svc tetris
9. Istio
Skip
10~12 skip
13. kube-ops-view
13-1. kube-ops-view : 노드의 파드 상태 정보를 웹 페이지에서 실시간으로 출력 - 링크
# 설치
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
kubectl annotate service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=kubeopsview.$MyDomain"
# 접속 주소 확인 : 각각 1배, 1.5배, 3배 크기
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080"
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=1.5"
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=3.0"
Kube Ops View URL = http://kubeopsview.pjhtest.click:8080
# nginx 파드 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
kubectl apply -f nginx-dp.yaml
kubectl scale deployment nginx-deployment --replicas 15
kubectl scale deployment nginx-deployment --replicas 40
kubectl delete -f nginx-dp.yaml
# 삭제
helm uninstall kube-ops-view -n kube-system
[심화] NLB+ExternalDNS+SSL 테트리스 배포
# 자신의 도메인, acm 변수 지정
MyDomain=<자신의 도메인>
MyDomain=pjhtest.click
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $MyDomain $CERT_ARN
# 테트리스 배포
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
ports:
- containerPort: 80
---
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"
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "${CERT_ARN}"
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
external-dns.alpha.kubernetes.io/hostname: "tetris.${MyDomain}"
spec:
selector:
app: tetris
ports:
- port: 80
protocol: TCP
targetPort: 80
name: http
- port: 443
targetPort: 80
protocol: TCP
name: https
type: LoadBalancer
loadBalancerClass: service.k8s.aws/nlb
EOF
# 배포 확인 : CLB 배포 확인
kubectl get deploy,svc,ep tetris
# 웹 접속 주소 확인 및 접속
echo -e "Tetris Game URL = https://tetris.$MyDomain"
Tetris Game URL = https://tetris.pjhtest.click
'EKS' 카테고리의 다른 글
EKS CloudFormation (0) | 2024.03.03 |
---|---|
5주차 - EKS Autoscaling (0) | 2023.05.30 |
EKS 4주차 - EKS Observability (0) | 2023.05.16 |
EKS 3주차 - EKS Storage & Node 관리 (0) | 2023.05.12 |
EKS 1주차 - Amzaon EKS 설치 및 기본 사용 (0) | 2023.04.26 |