이번 게시글은 가시다님의 AEWS [2기] 스터디 내용을 정리한 포스트 입니다.
이번 게시글은 2 주차의 스터디 내용인 EKS Networking에 대해 살펴봅니다.
EKS 원클릭 배포
사전 준비
- AWS 계정
- SSH 키 페어
- IAM 계정 생성 후 키(Credentials)
전체 구성도
Cloud Formation으로 배포하기
- 여기를 클릭하면 Cloud Formation의 서울리전에 접속 됩니다.
- 스택 생성 메뉴에서 다음을 클릭합니다.
- 아래 사진의 체크 부분을 입력하고 default 값으로 두고 생성합니다.
- KeyName : bastion EC2 인스턴스 접근에 사용할 Key
- MyIamUserAccessKeyId: Iam User의 AccessKey
- MyIamUserSecretAccessKey: Iam User의 SecretAccessKey
확인
Bastion EC2 인스턴스의 공인 IP로 SSH 접근을 시도합니다. 공인 IP는 CloudFormation의 결과에 나오는 출력값 혹은 EC2 메뉴에서 bastion 호스트의 공인 IP를 확인합니다.
# ssh -i "Keyname" ec2-user@"Public IP"
ssh -i hj42700eks.pem ec2-user@3.36.133.15
아래 명령을 통해 EKS에 접근이 가능한지 확인합니다.
kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system aws-node-6b888 2/2 Running 0 106m
kube-system aws-node-glfdg 2/2 Running 0 106m
kube-system aws-node-n9ktr 2/2 Running 0 106m
kube-system coredns-55474bf7b9-j2lsq 1/1 Running 0 104m
kube-system coredns-55474bf7b9-jzjl6 1/1 Running 0 104m
kube-system kube-proxy-5wv9m 1/1 Running 0 105m
kube-system kube-proxy-9sdl7 1/1 Running 0 105m
kube-system kube-proxy-ttxnn 1/1 Running 0 105m
아래 명령을 통해 Bastion에서 EKS Worker Node에 접근 가능하도록 설정합니다.
# 노드 IP 확인 및 PrivateIP 변수 지정
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
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와 보안그룹 이름(Name아님을 주의!) 확인
aws ec2 describe-security-groups --query 'SecurityGroups[*].[GroupId, GroupName]' --output text
# 노드 보안그룹 ID 확인
aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
echo $NGSGID
echo "export NGSGID=$NGSGID" >> /etc/profile
# 노드 보안그룹에 eksctl-host 에서 노드(파드)에 접속 가능하게 룰(Rule) 추가 설정
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32
# eksctl-host 에서 노드의IP나 coredns 파드IP로 ping 테스트
ping -c 1 $N1
ping -c 1 $N2
ping -c 1 $N3
# 워커 노드 SSH 접속 : '-i ~/.ssh/id_rsa' 생략 가능
for node in $N1 $N2 $N3; do ssh -i ~/.ssh/id_rsa ec2-user@$node hostname; done
ssh ec2-user@$N1
exit
ssh ec2-user@$N2
exit
ssh ec2-user@$N3
exit
원클릭 배포를 통해 EC2 Bastion 호스트가 생성되고, Bastion에서 Ssh를 통한 Worker Node 접근, EKS 클러스터 생성 및 kubectl 인증 등록까지 자동화 되어 kubectl 연결이 가능한 상태임을 확인해야 합니다.
Add-On의 최신버전 설치
원클릭 배포를 통해 EKS 클러스터를 배포 시 먼저 Bastion 호스트를 생성하고 Bastion에서 eksctl 명령을 통해 배포하게 되어 있습니다.
eksctl create cluster -f myeks.yaml --verbose 4 --kubeconfig "/root/.kube/config" 1> /root/create-eks.log
여기서 myeks.yaml을 확인 해보면 특정 Add-On에 대한 버전을 지정할 수 있는데 latest로 적용하여 최신 버전의 Add-on을 설치한 것을 확인할 수 있습니다.
addons:
- name: vpc-cni # no version is specified so it deploys the default version
version: latest # auto discovers the latest available
attachPolicyARNs:
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
configurationValues: |-
enableNetworkPolicy: "true"
- name: kube-proxy
version: latest
- name: coredns
version: latest
따라서 아래와 같은 명령어로 Add-On Pod의 컨테이너 이미지 버전 조회 시 기본 설치 한 클러스터와 상이하게 최신 버전의 Add-On이 설치된 것을 확인할 수 있습니다.
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/aws-network-policy-agent:v1.0.8-eksbuild.1
3 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.16.4-eksbuild.2
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.10.1-eksbuild.7
3 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/kube-proxy:v1.28.6-minimal-eksbuild.2
# 아래는 기본 설치 시 버전
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.15.1-eksbuild.1
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/coredns:v1.10.1-eksbuild.4
2 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/kube-proxy:v1.28.2-minimal-eksbuild.2
최신 버전의 Add-On을 사용하면, 버그 패치, 새로운 기능을 사용할 수 있는 장점이 있습니다.
Networking
Kubernetes CNI(Container Network Interface)
K8S CNI는 쿠버네티스 클러스터 환경에서 네트워크 환경을 구성해주는 역할을 담당합니다. AWS 외 환경에서 K8S 클러스터를 구현할 시 Calico, Cilium 등을 통해 구현할 수 있으나, EKS는 AWS VPC CNI를 사용합니다. AWS VPC CNI는 파드에 VPC와 동일한 주소를 할당 시켜줍니다.
그럼 AWS VPC CNI를 사용하면 어느 장점이 있을까요?
첫 번째, VPC와 통합할 수 있는 점입니다. VPC Flow logs, VPC 라우팅 정책, 보안 그룹을 적용시킬 수 있습니다.
두 번째, 통신에 오버헤드가 줄어듭니다. 아래 그림에서 알 수 있듯이 기존 쿠버네티스 CNI는 오버레이(VXLAN, IP-IP 등)을 통해 다른 노드에 있는 파드와 통신할 수 있지만, 원본 패킷 위에 Outer 패킷이 존재 해야 했습니다. 하지만 AWS VPC CNI는 노드와 동일한 VPC 대역을 가지기 때문에 원본 패킷 그대로 다른 노드에 있는 Pod에 접근 가능합니다.
워커 노드에 생성 가능한 최대 파드 갯수
EC2 인스턴스는 컴퓨팅 리소스에 할당 가능한 최대 갯수가 존재합니다. t3.medium의 경우 최대 할당 가능한 네트워크 인터페이스의 수는 3개 입니다. 그리고 1개의 네트워크 인터페이스는 최대 5개의 Secoundery IP를 보유할 수 있습니다. Pod의 IP는 Secoundery IP를 할당 받으므로 t3.medium 인스턴스는 최대 15개 배치 가능하게 됩니다.
워커 노드에서 기본 네트워크 정보 확인하기
EKS의 워커노드 Network Namesapce는 Root, Per Pod 로 구분 됩니다. aws-node, kube-proxy 파드는 쿠버네티스의 호스트 네트워크 옵션을 사용해 EC2에 부착된 ENI와 동일한 Root Network Namespace를 사용하고, 파드의 IP도 노드의 IP를 사용합니다. 그 외 생성되는 파드들은 별도의 Network Namespace에 생성됩니다. 이를 구현하기 위해 워커노드에 가상의 네트워크 인터페이스를 생성하고 파드에 연결합니다.
아래 명령을 통해 Pod의 IP를 확인해보겠습니다.
kubectl get pod -A -owide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system aws-node-646z8 2/2 Running 0 50m 192.168.2.134 ip-192-168-2-134.ap-northeast-2.compute.internal <none> <none>
kube-system aws-node-bkttb 2/2 Running 0 50m 192.168.1.63 ip-192-168-1-63.ap-northeast-2.compute.internal <none> <none>
kube-system aws-node-nll7s 2/2 Running 0 50m 192.168.3.111 ip-192-168-3-111.ap-northeast-2.compute.internal <none> <none>
kube-system coredns-55474bf7b9-p2z8s 1/1 Running 0 48m 192.168.2.217 ip-192-168-2-134.ap-northeast-2.compute.internal <none> <none>
kube-system coredns-55474bf7b9-snmnt 1/1 Running 0 48m 192.168.3.92 ip-192-168-3-111.ap-northeast-2.compute.internal <none> <none>
kube-system kube-proxy-89mv7 1/1 Running 0 48m 192.168.3.111 ip-192-168-3-111.ap-northeast-2.compute.internal <none> <none>
kube-system kube-proxy-8ts9n 1/1 Running 0 48m 192.168.1.63 ip-192-168-1-63.ap-northeast-2.compute.internal <none> <none>
kube-system kube-proxy-r4ztv 1/1 Running 0 48m 192.168.2.134 ip-192-168-2-134.ap-northeast-2.compute.internal <none> <none>
aws-node, kube-proxy 파드는 노드의 IP와 동일한 것을 확인할 수 있습니다.
아래는 워커노드 192.168.3.111의 네트워킹 정보를 캡처한 사진입니다. 사진처럼 보조 프라이빗 IPv4 주소 대역 중 192.168.3.92를 coredns 파드에 할당하여 사용중인 것을 확인할 수 있습니다.
추가적인 파드를 배포해 테스트 해보도록 하겠습니다. 아래 코드를 복사/붙여넣기 하여 netshoot 컨테이너를 배포합니다.
# 테스트용 파드 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
파드의 IP 및 워커 노드의 라우팅 테이블 정보를 확인합니다.
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done
보시는 것과 같이 파드가 배포되며 워커노드는 파드를 위한 가상 네트워크 인터페이스(별도의 네임스페이스)를 생성하고 파드에 할당합니다.
노드 간 파드 통신
EKS 클러스터에 서로 다른 워커노드에 배포된 파드 간 통신은 어떻게 이루어 지는지 확인해보겠습니다.
위에서 배포한 3개의 파드들에 대한 IP 변수를 지정합니다.
# 파드 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})
Bastion에서 3개의 워커노드에 SSH를 통해 원격접속 후 아래 명령을 통해 icmp 패킷 덤프를 출력합니다.
sudo tcpdump -i any -nn icmp
파드 1 Shell에서 파드2로 ping 테스트를 해보겠습니다.
kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2
위 사진은 워커노드 1에서의 icmp 패킷 덤프를 캡쳐한 화면입니다. 사진에서 알 수 있듯 Pod의 출발지 IP, 도착지 IP에 Pod IP가 변조 없이 그대로 들어가 있습니다. 이를 통해 AWS VPC CNI를 통해 파드에 VPC의 IP대역을 할당시킬 수 있고, 별도의 오버헤드 없이 VPC 내부 통신이 가능한 것을 확인할 수 있습니다.
파드에서 외부 통신
파드에서 외부통신은 워커노드의 iptable SNAT를 통하여 eth0 IP로 변경되어 외부와 통신합니다.
Bastion에서 워커 노드의 공인 IP를 확인하고, 파드 Shell 내부에서 공인 IP를 조회하여 동일한 공인 IP를 사용하는지 확인 해봅니다.
Bastion에서 아래 명령어를 입력합니다.
# 워커 노드 EC2 : 퍼블릭IP 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s ipinfo.io/ip; echo; echo; done
파드에서 공인 IP를 조회 해 봅니다.
for i in $PODNAME1 $PODNAME2 $PODNAME3; do echo ">> Pod : $i <<"; kubectl exec -it $i -- curl -s ipinfo.io/ip; echo; echo; done
노드에 파드 생성 갯수 제한
노드에 파드 생성 갯수 제한을 알아보기 전에 파드 배포 현황을 시각화 해주는 kube-ops-view를 먼저 설치 해봅니다.
# 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"}}'
# kube-ops-view 접속 URL 확인 (1.5 배율)
kubectl get svc -n kube-system kube-ops-view -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "KUBE-OPS-VIEW URL = http://"$1":8080/#scale=1.5"}'
t3.medium은 총 3개의 ENI를 보유할 수 있고 ENI 1개당 5개의 Secoundary private IP를 보유할 수 있습니다.
아래 명령을 통해 EC2 Type 별 Max ENI 개수를 확인할 수 있습니다.
# 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
최대 배포 가능한 파드 개수 확인을 위해 파드를 배포합니다.
# 디플로이먼트 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
kubectl apply -f nginx-dp.yaml
파드의 개수를 2개에서 8개로 증가시킵니다.(위 쪽 8개, 아래는 kube-system namesapce 정보 입니다.)
kubectl scale deployment nginx-deployment --replicas=8
파드의 개수를 8개에서 30개로 증가시킵니다.
kubectl scale deployment nginx-deployment --replicas=30
워커노드의 ENI가 증가한 것을 확인할 수 있습니다.
파드의 개수를 30개에서 50개로 증가시킵니다.
kubectl scale deployment nginx-deployment --replicas=50
파드를 더이상 할당할 수 없어 Pending 상태에 빠집니다.
파드를 50개에서 2개로 줄여봅니다.
kubectl scale deployment nginx-deployment --replicas=2
EC2의 ENI가 3개에서 2개로 줄어든 것을 확인할 수 있습니다. 이를 통해 Pod의 개수에 따라 ENI의 확장/축소를 자동적으로 해주는 것을 알 수 있습니다.
서비스 & AWS LoadBalancer Controller
쿠버네티스의 파드 서비스를 외부로 노출시키기 위해 서비스를 배포해야 합니다. 그 중 EKS의 로드밸런서 타입 기본 모드는 아래 그림과 같이 NLB 인스턴스 유형으로 배포됩니다. 외부 클라이언트로가 로드밸런서를 호출하면 노드포트를 거쳐 파드로 요청을 전달하게 됩니다.
AWS LoadBalancer Controller 파드를 통해 로드밸런서를 배포하면 노드포트를 사용하지 않고 로드밸런서에서 파드로 직접 전달하게 됩니다. 따라서 기본 모드보다 Latency를 줄일 수 있습니다.
아래 명령을 통해 LoadBalancer Controller를 배포합니다.
# 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 https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/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 | jq
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy | jq
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --query 'Policy.Arn'
# AWS 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 --role-name AmazonEKSLoadBalancerControllerRole \
--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
## 설치 확인 : aws-load-balancer-controller:v2.7.1
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
아래 명령을 통해 테스트용 파드와 로드밸런서를 배포 합니다.
# 작업용 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
아래 명령을 통해 svc, targetgroup등 옵션 정보를 확인합니다.
(hakjunadmin@myeks:N/A) [root@myeks-bastion-EC2 ~]# kubectl get svc,ep,ingressclassparams,targetgroupbindings
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 170m
service/svc-nlb-ip-type LoadBalancer 10.100.88.12 k8s-default-svcnlbip-3fbddfc566-38e584ebcd432c57.elb.ap-northeast-2.amazonaws.com 80:31223/TCP 88s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.1.4:443,192.168.2.39:443 170m
endpoints/svc-nlb-ip-type 192.168.1.221:8080,192.168.3.192:8080 88s
NAME GROUP-NAME SCHEME IP-ADDRESS-TYPE AGE
ingressclassparams.elbv2.k8s.aws/alb 7m55s
NAME SERVICE-NAME SERVICE-PORT TARGET-TYPE AGE
targetgroupbinding.elbv2.k8s.aws/k8s-default-svcnlbip-4f27a734aa svc-nlb-ip-type 80 ip 83s
(hakjunadmin@myeks:N/A) [root@myeks-bastion-EC2 ~]# kubectl get targetgroupbindings -o json | jq
{
"apiVersion": "v1",
"items": [
{
"apiVersion": "elbv2.k8s.aws/v1beta1",
"kind": "TargetGroupBinding",
"metadata": {
"creationTimestamp": "2024-03-16T14:05:08Z",
"finalizers": [
"elbv2.k8s.aws/resources"
],
"generation": 1,
"labels": {
"service.k8s.aws/stack-name": "svc-nlb-ip-type",
"service.k8s.aws/stack-namespace": "default"
},
"name": "k8s-default-svcnlbip-4f27a734aa",
"namespace": "default",
"resourceVersion": "33091",
"uid": "761a1eb9-b7ec-4595-9405-02a2df4f3100"
},
"spec": {
"ipAddressType": "ipv4",
"networking": {
"ingress": [
{
"from": [
{
"securityGroup": {
"groupID": "sg-07ccacafe83e1c326"
}
}
],
"ports": [
{
"port": 8080,
"protocol": "TCP"
}
]
}
]
},
"serviceRef": {
"name": "svc-nlb-ip-type",
"port": 80
},
"targetGroupARN": "arn:aws:elasticloadbalancing:ap-northeast-2:654654143395:targetgroup/k8s-default-svcnlbip-4f27a734aa/7b3a9fa2e269bb80",
"targetType": "ip"
},
"status": {
"observedGeneration": 1
}
}
],
"kind": "List",
"metadata": {
"resourceVersion": ""
}
}
아래 명령을 통해 Target Group에 대해 자세히 살펴보겠습니다. 타겟의 IP로 192.168.3, 192.168.1.221로 확인됩니다. 즉 로드밸런서의 타겟이 노드가 아닌 포드로 직접 연결된 것을 알 수 있습니다.
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq
{
"TargetHealthDescriptions": [
{
"Target": {
"Id": "192.168.3.192",
"Port": 8080,
"AvailabilityZone": "ap-northeast-2c"
},
"HealthCheckPort": "8080",
"TargetHealth": {
"State": "healthy"
}
},
{
"Target": {
"Id": "192.168.1.221",
"Port": 8080,
"AvailabilityZone": "ap-northeast-2a"
},
"HealthCheckPort": "8080",
"TargetHealth": {
"State": "healthy"
}
}
]
}
아래 명령을 통해 접속 URL을 확인합니다.
# 웹 접속 주소 확인
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://"$1 }'
Pod Web URL = http://k8s-default-svcnlbip-3fbddfc566-38e584ebcd432c57.elb.ap-northeast-2.amazonaws.com
Ingress 서비스 배포
Ingress는 L7 LoadBalancer로 비유할 수 있습니다. HTTP/HTTPS로 외부로 서비스를 노출시킬 수 있습니다. 아래 명령을 통해 게임 파드, 서비스, 인그레스를 배포합니다.
# 게임 파드와 Service, Ingress 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ingress1.yaml
cat ingress1.yaml | yh
kubectl apply -f ingress1.yaml
아래 명령을 통해 파드 상태, ALB 생성, Ingress 생성을 확인합니다.
# 생성 확인
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
# ALB 생성 확인
aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-game2048`) == `true`]' | jq
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-game2048`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq
# Ingress 확인
kubectl describe ingress -n game-2048 ingress-2048
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}"
AWS 콘솔에서 ALB가 생성된 것도 확인 가능합니다.
아래 명령을 통해 외부 접속 URL을 확인합니다.
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Game URL = http://"$1 }'
External DNS
현재 LoadBalancer, Ingress로 배포 시 DNS주소는 난수로 나오기 때매 확인이 어렵습니다. 따라서 External DNS를 통해 사용자가 원하는 DNS를 부여할 수 있도록 합니다. K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53), Azure(DNS), GCP(Cloud DNS)에 A 레코드(TXT레코드)로 자동 생성/삭제 합니다.
먼저 Route53 정보를 확인하고 변수를 지정합니다. Public 도메인 소유를 하고 계셔야 합니다.
# 자신의 도메인 변수 지정 : 소유하고 있는 자신의 도메인을 입력하시면 됩니다
MyDomain=<자신의 도메인>
MyDomain=junkmm.site
echo "export MyDomain=junkmm.site" >> /etc/profile
# 자신의 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
ExternalDNS를 설치합니다.
# 변수 확인
echo $MyDomain, $MyDnzHostedZoneId
junkmm.site, /hostedzone/Z10261112I8TNVJK00DW5
# ExternalDNS 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
sed -i "s/0.13.4/0.14.0/g" externaldns.yaml
cat externaldns.yaml | yh
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
테스트용 파드와 LoadBalancer를 배포합니다.
# 테트리스 디플로이먼트 배포
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
배포를 확인하고 NLB에 ExternalDNS로 도메인을 연결합니다.
# 배포 확인
kubectl get deploy,svc,ep tetris
# NLB에 ExternanDNS 로 도메인 연결
kubectl annotate service tetris "external-dns.alpha.kubernetes.io/hostname=tetris.$MyDomain"
while true; do aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq ; date ; echo ; sleep 1; done
# 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 .[]
# 확인
dig +short tetris.$MyDomain @8.8.8.8
dig +short tetris.$MyDomain
# 도메인 체크
echo -e "My Domain Checker = https://www.whatsmydns.net/#A/tetris.$MyDomain"
# 웹 접속 주소 확인 및 접속
echo -e "Tetris Game URL = http://tetris.$MyDomain"
Route53에 레코드가 추가된 것을 확인할 수 있습니다.
시간을 기다리고 dig 명령으로 도메인 조회가 완료되면 도메인을 접속해서 확인해봅니다.
'클라우드' 카테고리의 다른 글
[EKS] Autoscaling (0) | 2024.04.03 |
---|---|
[EKS] Storage & NodeGroup (0) | 2024.03.23 |
[EKS] Amzaon EKS 설치 및 기본 사용 (0) | 2024.03.09 |
[k8s] Pod의 안정적인 유지 - liveness probe (0) | 2023.10.22 |
[devops] Argo project로 CI/CD Pipeline 구현하기 (0) | 2023.07.26 |