2주차 - EKS Networking

728x90

CNI

  • Container Network Inferface로 컨테이너 간 네트워크를 구성
    • 다양한 CNI가 존재( Flannel, Calico, Cilium, Weave Net, AWS VPC )

AWS VPC CNI

  • Node와 Pod의 IP 대역이 같아 직접 통신이 가능하여 네트워크 통신의 최적화(성능, 지연)
  • VPC 와 통합 : VPC Flow logs , VPC 라우팅 정책, 보안 그룹(Security group) 을 사용 가능함
  • VPC ENI 에 미리 할당된 IP(=Local-IPAM Warm IP Pool)를 파드에서 사용할 수 있음

 

Node Network 정보

  • aws-node(cni)dhk kube-proxy는 Node의 Root Net Namespace를 사용
    • aws-node와 kube-proxy의 경우 Node의 IP를 그대로 사용
  • core-dns의 경우 Per Pod Net Namespace를 사용
    • core-dns는 Node의 IP가 아닌 별도의 IP를 사용

 

EKS 내 노드 간 Pod 통신 확인

  • AWS CNI 특징으로 Node IP 대역과 Pod IP이 동일하여 직접 통신이 가능
    • 일반적인 K8S CNI의 경우 오버레이 통신

  • Test Pod 생성 
# 테스트용 파드 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

 

  • Pod 접속 후 다른 노드에 배치된 Pod로 Ping 통신 
# 파드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

 

  • 각각 워커 노드 접속 후 tcpdump를 통해 통신 확인
# 워커 노드 EC2 : TCPDUMP 확인
sudo tcpdump -i any -nn icmp

 

 

  • pod1 IP: 192.168.3.246
  • pod2 IP: 192.168.2.8
  • pod3 IP: 192.168.1.119

  • tcpdump를 통해 ping 통신 확인 결과, 노드 간 Pod 통신에서 Pod IP로 직접 통신하는 것을 확인

 

EKS Pod에서 외부 통신 확인

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

  • 워커 노드의 공인 IP 확인

  • Pod 내 공인 IP 확인

Node의 공인IP와 Pod 내 공인IP가 동일 → Pod가 외부랑 통신하는 경우, 해당하는 워커노드의 eth0에 매핑되어 있는 공인IP를 통해 외부 통신

 

  • Pod에서 Google도메인으로 ping 통신 확인 (외부 통신)

  • 워커노드에서 tcpdump를 통해 Google과 ping 통신 확인

 

 

노드에 파드 생성 갯수 제한

  • 사전준비 - kube-ops-view
    • 웹페이지를 통해 현재 워커 노드의 정보와 Pod의 상태 정보를 실시간으로 확인
# 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"}'

 

  • 노드 공간 바닦에 배치된 Pod는 kube-system 네임스페이스에 배치된 Pod
  • 노드 공간 윗쪽에 배치된 Pod는 사용자가 생성한 Pod가 순차적으로 배치

 

  • AWS Instance 종류 별(워커노드) Pod 생성 갯수
    • 최대 파드 생성 갯수 : (Number of network interfaces for the instance type × (the number of IP addressess per network interface - 1)) + 2

  • 현재 실습으로 사용하고 있는 t3.medium의 경우 ENI3개를 사용 할 수 있으며, 최대 할당 가능 IP는 15
(yjsong@myeks:default) [root@myeks-bastion-EC2 ~]# 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  |
|  12      |  3       |  t3.large    |
|  15      |  4       |  t3.xlarge   |
|  6       |  3       |  t3.medium   |
|  2       |  2       |  t3.micro    |
|  2       |  2       |  t3.nano     |
|  4       |  3       |  t3.small    |
+----------+----------+--------------+
  • 워커 노드의 스펙이 올라가면 더 많은 Pod 생성 가능 - C Type 인스턴스
(yjsong@myeks:default) [root@myeks-bastion-EC2 ~]# 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  |
|  10      |  3       |  c5d.large     |
|  10      |  3       |  c5n.large     |
|  30      |  8       |  c5.4xlarge    |
|  15      |  4       |  c5n.2xlarge   |
|  30      |  8       |  c5n.9xlarge   |
|  15      |  4       |  c5d.2xlarge   |
|  15      |  4       |  c5.xlarge     |
...
  • 파드 사용 가능 계산 : aws-node 와 kube-proxy 파드는 host-networking 사용으로 IP 2개 남음  / ((MaxENI * (IPv4addr-1[워커노드 자신 IP])) + 2)
    • 예시)t3.medium 경우 : ((3 * (6 - 1) + 2 ) = 17개 >> aws-node 와 kube-proxy 2개 제외하면 15개

 

  • 워커 노드의 상세정보를 통해서도 확인 가능
kubectl describe node | grep Allocatable: -A6
Allocatable:
  cpu:                         1930m
  ephemeral-storage:           27905944324
  hugepages-1Gi:               0
  hugepages-2Mi:               0
  memory:                      3388360Ki
  pods:                        17

 

  • 최대 Pod TEST - replicas=50
(yjsong@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl get pods | grep Pending
nginx-deployment-f7f5c78c5-2c268   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-9l9pw   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-jnbff   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-ktglf   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-l9qvk   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-ljhbq   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-m4qzx   0/1     Pending   0          111s
nginx-deployment-f7f5c78c5-ppdpc   0/1     Pending   0          111s

(yjsong@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl describe pods nginx-deployment-f7f5c78c5-2c268

...

Events:
  Type     Reason            Age                   From               Message
  ----     ------            ----                  ----               -------
  Warning  FailedScheduling  117s (x2 over 7m21s)  default-scheduler  0/3 nodes are available: 3 Too many pods. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod..

 

  • 최대 생성 Pod의 수를 넘었기 때문에 8개 Pod는 실행되지 못하고 Pending 상태
    • kube-ops-view 설치로 워커 노드 별 kube-system pod 추가 → 따라서 워커 노드 당 14개 Pod 할당 가능 

 

  • AWS Console 내 ec2 정보 확인 시 3개 eni 할당 및 15개 보조 프라이빗 주소(Secondary IP)가 할당되어 있는것을 확인
    • Pod 삭제 또는 replicas 감소 시 유동적으로 ENI 및 보조 프라이빗 주소 감소

 

Service & AWS LoadBalancer Controller

  • Service
    • Pod는 생성 및 삭제 될때 마다 내부 IP가 변경하는 특성이 있음.
    • Service는 이러한 Pod에 탑재된 애플리케이션이 외부와 상호 통신이 가능하도록 하는 역할 수행
    • Pod가 외부와 통신할 수 있도록 클러스터 내부에서 고정 IP를 갖는 Service를 이용할 수 있도록 함
    • Deployment, Statefulset 처럼 같은 애플리케이션을 구동하도록 구성된 여러 파드들에게 단일 네트워크 진입점을 부여하는 역할
      • ClusterIP
      • NodePort
      • LoadBalancer
      • ExternalName
  • LoadBalancer Controller
    • 일반적인 Service에서 Loadbalancer의 경우 로드벨런서를 거쳐 각 Node의 iptable 정책에 따라 Pod에 분산되게 설정
    • VPC CNI의 경우, 로드밸런서에 Node의 IP가 아닌, Pod의 IP를 직접 매핑하여, 부하분산
      • Pod의 IP를 직접 로드밸런서에 연결 할 수 있는 이유는 Loadbalancer Controller가 Pod의 정보를 AWS ELB에 제공하기 때문에 가능

  • AWS LoadBalancer Controller 배포 with IRSA
# 1. OIDC 확인

aws iam list-open-id-connect-providers | jq


# 2. 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

# 3. 쿠버네티스와 AWS 간 권한을 매핑하여 마춤(IRSA 세팅)

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

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

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

# 6. 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

# 7. 설치 확인 : aws-load-balancer-controller:v2.7.1
kubectl get crd
kubectl get deployment -n kube-system aws-load-balancer-controller
 
# 8. 클러스터롤, 롤 확인
kubectl describe clusterrolebindings.rbac.authorization.k8s.io aws-load-balancer-controller-rolebinding
kubectl describe clusterroles.rbac.authorization.k8s.io aws-load-balancer-controller-role

 

 

  • Service/Pod 배포 Test with NLB
# Deployment & Service 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/echo-service-nlb.yaml
kubectl apply -f echo-service-nlb.yaml

# kubectl get svc,ep,ingressclassparams,targetgroupbindings

# (옵션) 빠른 실습을 위해서 등록 취소 지연(드레이닝 간격) 수정 : 기본값 300초
vi echo-service-nlb.yaml
  annotations:
     service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: deregistration_delay.timeout_seconds=60(추가)

# 옵션 추가 후 적용     
kubectl apply -f echo-service-nlb.yaml

# AWS ELB(NLB) 정보 확인
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-default-svcnlbip`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq

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

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

 

 

  • Target group 정보 확인 시 Node의 IP가 아닌 Pod의 IP가 설정되어 있는 것을 확인

 

  • 부하분산 확인
NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})

(yjsong@myeks:default) [root@myeks-bastion-EC2 ~]# for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
     54 Hostname: deploy-echo-7f579ff9d7-pfnn8
     46 Hostname: deploy-echo-7f579ff9d7-w244z

 

  • Pod 수량 변경 Test
# 작업용 EC2 - 파드 4개 설정 
kubectl scale deployment deploy-echo --replicas=4

for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

(yjsong@myeks:default) [root@myeks-bastion-EC2 ~]# for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

     27 Hostname: deploy-echo-7f579ff9d7-w244z
     25 Hostname: deploy-echo-7f579ff9d7-rq9j9
     25 Hostname: deploy-echo-7f579ff9d7-27589
     23 Hostname: deploy-echo-7f579ff9d7-pfnn8

 

 AWS LoadBalancer Controller & Ingress

  • 클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS)

 

# 게임 파드와 Service, Ingress 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ingress1.yaml
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

# 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'}"

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

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

  • NLB와 동일하게 Pod의 IP가 Target Group에 바인딩 되어 있는 것을 확인 할 수 있음

 

728x90