🎄쿠버네티스 스터디 PKOS 2주차 쿠버네티스 네트워크

728x90

 

 

 목표

 

이번 2주차 스터디에서는 쿠버네티스 네트워크에 대해서 학습 할 예정이다.
또한 일반적으로 사용하는 CNI(calico, weave net, Flannel)가 아닌 AWS VPC를 사용하면서 일반적인 CNI와 차이점에 대해서 학습 할 예정이다.
추가적으로 AWS ELB를 쿠버네티스에서 어떻게 설정하고 사용하는지 학습 할 예정이다.

 

AWS VPC

 

CNI(Container Network Interface)는 Pod간 네트워크 환경을, 즉 통신 환경을 구성해 준다. 일반적으로 많이 사용되는 CNI는 calico, weave net, Flannel 등이 있다.
이번 스터디에서 사용하는 KOPS에서는 AWS에서 구성되었고, CNI또한 AWS VPC를 사용한다.
AWS VPC가 다른 CNI와 다른 점은 노드와 파드의 네트워크 대역이 같아 통신 상황이 훨씬 간결하다는 부분이다.

  • K8S Calico 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.11.4
amazon-k8s-cni:v1.11.4

# 노드 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

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

 

노드에서 기본 네트워크 정보

  • 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 과 연결되어 있다

[ Node2의 네트워크 정보 ]
현재 NIC가 2개(ens5, ens6), 논리 NIC가 2개

  • AWS 콘솔 화면 확인

노드 간 파드 통신

  • 파드간 통신 흐름 : 별도의 오버레이(Overlay) 통신 기술 없이, VPC Native 하게 파드간 직접 통신이 가능하다

 

파드간 통신 테스트 및 확인: 별도의 NAT 동작 없이 통신 가능!

# 파드 IP 변수 지정
POD1=$(kubectl get pod pod-1 -o jsonpath={.status.podIP})
POD2=$(kubectl get pod pod-2 -o jsonpath={.status.podIP})

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

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

#각 노드에서 tcpdump 모니터링
sudo tcpdump -i any -nn icmp 

 

[ TIP ] 두 번째 ENI에서 할당된 보조 IP를 사용하더라도 무조건 첫번째 ENI를 통해 통신이 되도록 설정되어 있음. 따라서 ens6의 ping은 tcpdump에서 확인 할 수 없음!!

 

파드에서 외부 통신

 

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

 

파드에서 외부 통신 테스트 및 확인

 

kubectl exec -it pod-1 -- curl -s ipinfo.io/ip : echo

#node1
curl -s ipinfo.io/ip : echo

#출력되는 공인 IP 확인
Node1의 Public IP 출력!

 

 

iptable 확인

# 파드가 외부와 통신시에는 아래 처럼 '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 172.30.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 172.30.85.242 --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 에 매칭되어, 목적지가 172.30.0.0/16 아니고 외부 빠져나갈때 SNAT 172.30.85.242 변경되어 나간다!
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

 

노드에 파드 생성 갯수 제한

 

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 타입의 정보(필터) 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
 --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
 --output table
 
 # 파드 사용 가능 계산 예시 : 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: -A6
Allocatable:
  cpu:                2
  ephemeral-storage:  59763732382
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3854320Ki
  pods:               17

 

Service

  • AWS NLB의 타겟 그룹에 Pod의 IP가 직접 바인딩 되어 통신, iptable의 Rule을 타지 않음!!
    로드밸런스 컨트롤러가 Endpint의 정보를 즉 Pod의 정보를 KOPS를 통해서 주기적으로 모니터링해서 NLB에 해당 정보를 제공!
# kOps 클러스터 편집 : 아래 내용 추가
kops edit cluster
-----
spec:
  awsLoadBalancerController:
    enabled: true
-----

# 업데이트 적용 : 적용이 안될 경우 한번 더 아래 명령 실행
kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster

 

 서비스 / 파드 배포(NLB)

apiVersion: apps/v1
kind: Deployment
metadata: 
  name: deploy-echo
spec: 
  replicas: 2
  selector: 
    matchLabels: 
      app: deploy-websrv
  template: 
    metadata: 
      labels: 
        app: deploy-websrv
    spec: 
      terminationGracePeriodSeconds: 0
      containers: 
      - name: akos-websrv
        image: k8s.gcr.io/echoserver:1.5
        ports: 
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata: 
  name: svc-nlb-ip-type
  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-healthcheck-port: "8080"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec: 
  ports: 
    - port: 80
      targetPort: 8080
      protocol: TCP
  type: LoadBalancer
  loadBalancerClass: service.k8s.aws/nlb
  selector: 
    app: deploy-websrv

 

 

 NLB 주소를 100번 호출

NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})

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

 

 파드 2개 → 1개 → 3개 설정 시 동작: Auot Discovery

kubectl scale deployment deploy-echo --replicas=1

kubectl scale deployment deploy-echo --replicas=3

 

 

 

 

ExternalDNS

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

 externalDNS설치

# 설치
kops edit cluster
--------------------------
spec:
  externalDns:
    provider: external-dns
--------------------------

# 업데이트 적용
kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster

# 버전 확인 : v0.12.2
kubectl describe deploy -n kube-system external-dns | grep Image | cut -d "/" -f 3

# externalDns 컨트롤러 파드 확인
kubectl get pod -n kube-system -l k8s-app=external-dns
NAME                            READY   STATUS    RESTARTS   AGE
external-dns-7bf9bd8994-f2r58   1/1     Running   0          16s

 

 서비스 NLB 배포 및 externalDNS 설정

kubectl apply -f ~/pkos/2/echo-service-nlb.yaml

MyDOMAIN1=<각자 자신의 nginx 도메인 지정>
MyDOMAIN1=nginx.burst89.com
kubectl annotate service svc-nlb-ip-type "external-dns.alpha.kubernetes.io/hostname=$MyDOMAIN1."
kubectl describe svc svc-nlb-ip-type | grep Annotations: -A5

# 확인
dig +short $MyDOMAIN1

# 분산 접속 확인
curl -s $MyDOMAIN1
for i in {1..100}; do curl -s $MyDOMAIN1 | grep Hostname ; done | sort | uniq -c | sort -nr

 

 


 

도전과제

 

 과제1) 파드 간 통신 시 tcpdump 내용을 확인하고 관련 스샷을 올려주세요.

  • k get nodes -o wide // 노드 정보 / 빨간 Box가 Pod가 생성된 Node)

  • k get pods -o wide

  • kubectl exec -it pod-1 – ping -c 2 $POD2 // Pod1에서 Pod2로 Ping 통신

  • sudo tcpdump -i ens5 -nn icmp // Node1에서 ens5 인터페이스를 통해 tcpdump → 통신 확인
    • 172.30.49.61 : Pod-1 IP / 172.30.71.69 : Pod-2 IP
    • 해당 IP를 통해 별도의 NAT 동작 없이 통신하는 것을 확인 할 수 있음
    • 이유는 VPC CNI를 사용하기 때문에!!

  • sudo tcpdump -i ens5 -nn icmp → Node2에서 ens5 인터페이스를 통해 tcpdump -> 통신 확인

 

과제2) 어떤 방식을 사용하더라도 좋으니, 워커 노드 1대에 100대의 파드가 배포되게 설정하고 관련 스샷을 올려주세요.

  • 초기 kops 클러스터 구성을 c5.large로 하고 worker node의 수를 1로 설정하여 클러스터를 구성하였다.
    • 구성 후 최대 Pod수를보면 29인 것을 볼 수 있다.

  1. VPC CNI Nerwork 설정
    - kubelet의 maxPods수를 설정
    - networking에 WARM_PRENFIX_TARGET / PREFIX_DELEGATION 설정
    - kops edit cluster --state s3://버킷이름
  2. 설정 후 cluster 업데이트
    업데이트 시 Master Node 자체가 변경되어 IP도 변경된다. 따라서 도메인에 api 주소도 변경해줘야 함 → 해당 작업이 시간이 오래 걸림(시간이 오래 걸리면 알아서 이 부분도 변경되는 같다.)
  3. Worker node가 변경 확인
  4. 노드 최대 Pod 수 변경 확인
  5. PREFIX_DELEGATION 및 WARM_PREFIX_TARGET 설정 확인
  6. LimitRange 설정 변경
    기존 limits라는 LimitRange 오브젝트가 존재하며, 현재 cpu 100m으로 설정
    오브젝트에 edit하여 cpu 100m 설정 삭제
  7. Pod100개 배포 및 확인
    - kubectl apply -f ~/pkos/2/nginx-dp.yaml
    - kubectl scale deployment nginx-deployment --replicas=100




  8. 추가적으로 t3.medium 인스턴스에서도 Test 진행 클러스터 구성 전, maxpod 수 확인 : 110개(c5.large)와 동일
  9. 위 과정을 동일하게 진행 후 단일 노드(t3.medium)에 Pod 100개 배포 및 확인

과제3) 서비스(NLB) / 파드 배포 시 externalDNS 설정해서, 각자 자신의 도메인으로 NLB를 통해 애플리케이션(파드)로 접속해보고 관련 스샷을 올려주세요. 퍼블릭 도메인이 없는 멤버분들은, externalDNS 제외하고 NLB 도메인으로 접속한 결과를 올려주세요.

  • nginx.burst89.com

 

마무리

 

사실 이번 스터디 내용이 조금은 어려웠다. 아는 내용도 있었지만, Service의 내용이 많이 헷갈렸다... 핵심은 AWS VPC CNI를 사용하면, 노드의 IP와 Pod의 IP가 같은 대역을 사용한다.
또한 로드밸런서 컨트롤러를 사용하면 로드밸런스 타겟대상에 Pod의 IP가 직접 연결되며, 컨트롤러가 계속해서 모니터링을 하고 해당 내용을 로드밸런서에게 전달하여 타겟의 정보를 확인 하여 보다 효과적으로 Service를 운영할 수 있다는 내용이다!!
추가적으로 노드의 최대 Pod 수 과제는 진짜 어려웠다......ㅠㅠ 3일은 걸려서 해당 과제를 해결할 수 있었다. 점점 난이도가 어려워 지는 느낌이 든다.. 그래도 화이팅 !!!!!!!

 

728x90