[CloudNet@] 쿠버네티스 실무 실습 스터디 2주차 (1) - 네트워크

s3ich4n·2023년 3월 17일
0
post-thumbnail

이 내용은 CloudNet@ 에서 진행하는 쿠버네티스 실무 실습 스터디에 대한 연재글입니다.

스터디에서 사용하는 교재는 24단계 실습으로 정복하는 쿠버네티스 입니다.

본 2주차에는 교재의 제 2부 전체 내용 중 상반기 부분을 살펴보고 있습니다. 전체 컨텍스트를 이해하시려면 교재를 참고하시기를 추천드립니다.

분량조절에 실패하여 2장 2부로 이어집니다.

Prerequisites

쿠버네티스를 클라우드 환경에서 사용하고자 하는 것 자체가 이미 하기 사항들에 대한 기본적인 이해와 준비사항을 요합니다.

이번 연재글에서는 개념에 대한 상세한 추가설명은 가급적 없이 작성하려 합니다. 다만 제가 공부하며 기본적으로 알아야겠다 싶은 연재글에 대해 대신 소개드립니다.

들어가며

사전 준비사항 (1)

  1. AWS Free Tier 계정(비용문제로 인해 필요합니다!)
  2. IAM User 생성 후 권한 부여
    1. 학습을 위해 자신의 작업환경 IP에서만 접근할 수 있도록 하고, 관리자 권한을 주는 식으로 해결해도 좋습니다.
  3. Route 53 퍼블릭 호스팅 영역
    1. 혹은 도메인 구매사이트에서 도메인 구매 후, Route 53 설정 지정하기

사전 준비사항 (2)

주의! 비용이 많이 발생할 수 있으니, 빠르게 실습 후 종료하시기를 권장드립니다.

  1. 마스터노드 t3.medium
  2. 워커노드 c5d.large
  3. kOps 커맨드를 수행할 인스턴스 t3.small

아래 커맨드를 통해 배포하여 주십시오.

서비스 리소스와 스토리지에 대해

앞선 장에서 언급되었듯, 쿠버네티스 환경에서의 파드는 가축(cattle)같은 개념입니다. 문제가 생겼다면 내렸다가 다시 올리지요. 각 파드별 네트워크 구성 또한 무언가가 관리하고 있을 것입니다. 스토리지도 마찬가집니다. 무언가가 스토리지를 파드와 데이터를 분리하여 별도의 추상화된 리소스로 처리합니다. 이와 같은 작업을 해주는 것을 각각 쿠버네티스 서비스쿠버네티스 스토리지 로 부릅니다.

네트워크 구성요소로는 아래 요소들이 있습니다.

  • 서비스
  • 로드밸런서
  • 인그레스

스토리지 구성요소르는 아래 요소들이 있습니다.

  • PV(Persistent Volume)
  • PVC(Persistent Volume Claim)
  • SC(Snapshot Class)
  • 스냅샷
  • 공유 스토리지

이번 장에서는 쿠버네티스 클러스터를 구성하는 쿠버네티스 서비스와 쿠버네티스 스토리지에 대해 알아보고자 합니다.

쿠버네티스 네트워크 관리

쿠버네티스 서비스는 동적으로 각 파드 간 연결을 제공합니다[^1]. 잠깐, 쿠버네티스 서비스라고요? 흔히 서비스는 웹 서비스, RDMBS같은 서비스를 말하지만, 쿠버네티스에서의 서비스는, 파드 간의 연결을 serve 하므로 서비스 라는 용어를 사용합니다.

서비스들의 특징을 살펴봅시다.

  • 서비스는 쿠버네티스 리소스의 한 종류입니다.
  • 다이나믹하게 종료되고 생성되는 파드를 자동으로 발견(Service Discovery)합니다.
    • 책의 예시를 통해 서비스 디스커버리를 확인해볼 수 있습니다.
  • 변경이 잦은 IP 주소가 아닌 지속가능한 DNS(도메인 레코드) 기반의 쿠버네티스 서비스 이름을 이용하여 통신합니다(!).
  • 외부에서 내부로 연결 시에는 NodePort와 LoadBalancer 타입의 서비스를 이용합니다.
  • 노드 내 실행중인 여러 파드로 부하를 분산하는 로드밸런싱 기능을 자체적으로 지원합니다.

AWS 서비스의 CNI

CNI는 컨테이너 간의 네트워킹을 제어할 수 있는 플러그인을 만들기 위한 표준입니다[^2]. 다만 그 구현체가 클라우드 서비스 프로바이더에 따라 다를 수 있고, 벤더사 혹은 오픈소스에 따라 달라질 수 있습니다. 이 글에서는 AWS VPC CNI와 Calico에 대해 설명합니다.

실습 01-1 네트워크 기본정보 조회

아래 커맨드를 통해, 배포 후 실습환경의 기본정보를 조회할 수 있습니다.

# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
amazon-k8s-cni-init:v1.12.2
amazon-k8s-cni:v1.12.2

# 노드 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

# 파드 이름 확인
kubectl get pod -A -o name

# 파드 갯수 확인
kubectl get pod -A -o name | wc -l
kubectl ktop  # 파드 정보 출력에는 다소 시간 필요

# [master node] aws vpc cni log
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME ls /var/log/aws-routed-eni

AWS VPC CNI

기본적으로 AWS EKS에는 VPC CNI를 사용합니다. 파드의 IP를 할당해주는 것이 주요 역할입니다. 아래와 같은 특징이 있습니다.

  • 파드의 IP 네트워크 대열과 노드(워커)의 IP대역이 같아, 직접 통신이 가능합니다.
  • kOps를 통해 쿠버네티스 클러스터를 배포하면 SG for Pod을 제외하곤 VPC Flow, VPC 라우팅 정책 등 기존 AWS VPC와 통합이 쉽습니다.

k8s Calico와 AWS VPC CNI와의 차이점

Calico는 매우 유명한 서드파티 CNI 중 하나이므로, 여기서는 Calico를 소개하지는 않겠습니다. 다만 관련하여 좋은 블로그 게시글을 몇가지 소개드리겠습니다[^3]. 그리고, 이하에서는 Calico와 AWS VPC CNI의 주요 차이점을 살펴보겠습니다.

  1. AWS VPC CLI는 네트워크 통신 최적화(성능, 레이턴시)를 위해 노드와 파드의 네트워크 대역을 동일하게 설정합니다.

그림 1 - 일반적인 K8S CNI 플러그인(Calico) 와 AWS VPC CNI 간 노드와 파드 네트워크 대역 비교

  1. 파드간 통신 시 일반적인 CNI들은 오버레이(VXLAN, IP-IP, etc.) 통신을 수행합니다. 다만 AWS VPC CLI는 동일 대역으로 직접 통신합니다.

![그림 2 - Calico CNI의 오버레이 통신 및 AWS VPC CNI의 직접 통신 비교]
(https://velog.velcdn.com/images/s3ich4n/post/1c4ca8ef-0969-4791-940f-22135d3d82ff/image.png)

  1. AWS VPC CLI를 사용하면 생성가능한 최대 파드 갯수가 정해져 있습니다.
    1. Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정합니다.
    2. IPv4 Prefix Delegation : IPv4 28bit 서브넷(prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정합니다.
    3. 이에 대한 해결방안은 CNI Custom Networking에 대한 방안이 있습니다. 상세한 내용은 아래에서 후술합니다.

실습 01-2. 컨트롤 플레인 접근 및 워커노드 접근

컨트롤 플레인에 접속 후 CNI 정보, 네트워크 정보를 확인해봅시다.

# [master node] SSH 접속
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME

# 툴 설치
sudo apt install -y tree jq net-tools

# CNI 정보 확인
ls /var/log/aws-routed-eni
cat /var/log/aws-routed-eni/plugin.log | jq
cat /var/log/aws-routed-eni/ipamd.log | jq

# 네트워크 정보 확인 : eniY는 pod network 네임스페이스와 veth pair
ip -br -c addr
ip -c addr
ip -c route
sudo iptables -t nat -S
sudo iptables -t nat -L -n -v

# 빠져나오기
exit

이어서 워커 노드에 접속 후 CNI 정보를 확인해봅시다.

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

# 워커 노드 Public IP 변수 지정
W1PIP=<워커 노드 1 Public IP>
W2PIP=<워커 노드 2 Public IP>
W1PIP=43.201.59.201
W2PIP=15.165.159.169

# 워커 노드 SSH 접속
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
exit
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP
exit

# [워커 노드1~2] SSH 접속 : 접속 후 아래 툴 설치 등 정보 각각 확인
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP

# 툴 설치
sudo apt install -y tree jq net-tools

# CNI 정보 확인
ls /var/log/aws-routed-eni
cat /var/log/aws-routed-eni/plugin.log | jq
cat /var/log/aws-routed-eni/ipamd.log | jq

# 네트워크 정보 확인
ip -br -c addr
ip -c addr
ip -c route
sudo iptables -t nat -S
sudo iptables -t nat -L -n -v

# 빠져나오기
exit

VPC CNI의 기본 네트워크 정보 확인

워커 노드의 기본 네트워크 구성을 살펴보며, 주요 특징을 이해해봅시다.

![그림 3 - 네트워크 네임스페이스 및 인스턴스 사이즈 별 특징]
(https://velog.velcdn.com/images/s3ich4n/post/c5f2d821-68b3-4bc3-b78f-efe3ecd7bb87/image.png)

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

실습 02-1. Secondary IPv4 주소를 파드가 사용하는지 확인

아래 커맨드를 입력하여, ebs-csi-node 파드의 IP 정보를 확인합니다. 아울리 워커 노드1 EC2 인스턴스의 네트워크 정보를 직접 확인해보시길 바랍니다.

# ebs-csi-node 파드 IP 정보 확인
kubectl get pod -n kube-system -l app=ebs-csi-node -owide
NAME                 READY   STATUS    RESTARTS   AGE     IP              NODE                  NOMINATED NODE   READINESS GATES
ebs-csi-node-64b66   3/3     Running   0          4h15m   172.30.50.15    i-0f63faa6cc1869699   <none>           <none>
ebs-csi-node-7h8xs   3/3     Running   0          4h15m   172.30.76.209   i-07d2a01a597c171bc   <none>           <none>
ebs-csi-node-z9xcq   3/3     Running   0          4h15m   172.30.44.223   i-0744dad15f33219cc   <none>           <none>

# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh -i ~/.ssh/id_rsa ubuntu@api.$KOPS_CLUSTER_NAME ip -c route
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP ip -c route
ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP ip -c route

실습 02-2. 테스트용 파드 생성 - nicolaka/netshoot

상기 언급한 정보를 확인하기 위해, 테스트용 파드를 생성하고 이 파드의 워커노드에 테스트용 파드 생성 및 접속 확인을 수행해봅시다.

# [터미널1~2] 워커 노드 1~2 모니터링
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP
watch -d "ip link | egrep 'ens5|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP
watch -d "ip link | egrep 'ens5|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: 2
  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})

# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

파드가 생성되면, 워커 노드에 eniY@ifN 파드가 추가되고 라우팅 테이블에도 정보가 추가됩니다.

아울러 테스트용 파드 eniY@ifN의 정보를 워커노드에서 확인해봅시다.

# 노드에서 네트워크 인터페이스 정보 확인
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

이어서 테스트용 파드를 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

VPC CNI의 노드 간 파드 통신 확인

앞서 잠시 언급하였듯, AWS VPC CNI는 별도의 오버레이 통신기술 없이 VPC의 네트워크 대역을 통해 바로 통신이 가능합니다.

그림 4 - VPC CLI의 노드 간 파드 통신

아래 도식은 파드 간의 통신 과정을 보다 자세히 풀어둔 도식입니다. 링크

그림 5 - Life of a Pod to Pod Ping Packet

실습 03-1. 파드간 통신 테스트 확인

별도의 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})

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

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

# 워커 노드 EC2 : TCPDUMP 확인 - ens6 에서 패킷 덤프 확인이 되나요?
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp
sudo tcpdump -i ens6 -nn icmp

[워커 노드1]
# routing policy database management 확인
ip rule

# routing table management 확인
ip route show table local

# 디폴트 네트워크 정보를 ens5 을 통해서 빠져나간다
ip route show table main
default via 172.30.64.1 dev ens5 proto dhcp src 172.30.85.242 metric 100

VPC CNI의 파드에서 외부통신으로의 흐름 확인

이번에는 파드에서 외부로 나가는 통신의 흐름을 살펴봅시다. iptable의 SNAT 을 통하여 노드의 eth0 IP로 변경된 후 외부와 통신을 진행합니다.

그림 6 - Life of a Pod to External Packet

  • VPC CNI 의 External source network address translation (SNAT) 설정에 따라, 외부(인터넷) 통신 시 SNAT을 사용하거나 혹은 SNAT 없이 통신을 할 수 있습니다. 참고 링크

실습 04-1. 파드에서 외부 통신 테스트 및 확인

파드의 쉘 실행 후, 외부로 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 : 퍼블릭IP 확인, TCPDUMP 확인
curl -s ipinfo.io/ip ; echo
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp

# 작업용 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 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

실습 04-2. 다음 실습을 위해

다음 실습을 위해 테스트용으로 생성한 파드를 삭제합니다: kubectl delete deploy netshoot-pod

노드에 파드 생성 갯수 제한 확인

  • 원인

    • Secondary IPv4 addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정합니다.
    • 워커 노드의 인스턴스 타입 별 파드 생성 갯수 제한
      • 인스턴스 타입 별 ENI 최대 갯수와 할당 가능한 최대 IP 갯수에 따라서 파드 배치 갯수가 결정됩니다.
      • 단, aws-nodekube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외합니다!
  • 해결방안

    • Prefix Delegation
      • IPv4 28bit 서브넷(prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정
    • WARM & MIN IP/Prefix Targets
    • Custom Network

멤버분의 상세한 가이드!

윈도우랑 리눅스가 공식이 다르긴한데 리눅스의 경우는 -1 을 하는 이유는 ENI별로 아이피 할당이 되서이고 +2는 노드별로 kube-proxy랑 VPC CNI와 같은 호스트 네트워크에 필요한 파드때문에 추가하고 있습니다.

참고링크 1: https://aws.github.io/aws-eks-best-practices/networking/vpc-cni/
참고링크 2: https://aws.github.io/aws-eks-best-practices/windows/docs/networking/

max-pod 설정 확인

워커 노드 1대에 50대 이상의 pod를 배포해봅시다!

실습 05-1. max-pod 설정 확인

# 파드 갯수 모니터링
watch "kubectl get pod | grep -v NAME | wc -l"

# 파드 100개 배포
kubectl apply -f ~/pkos/2/nginx-dp.yaml
kubectl scale deployment nginx-deployment --replicas=0
kubectl scale deployment nginx-deployment --replicas=10
kubectl scale deployment nginx-deployment --replicas=30
kubectl scale deployment nginx-deployment --replicas=100

# Nitro 인스턴스 유형 확인
aws ec2 describe-instance-types --filters Name=hypervisor,Values=nitro --query "InstanceTypes[*].[InstanceType]" --output text | sort | egrep 't3\.|c5\.|c5d\.'

# 노드 인스턴스 타입 확인
kubectl describe nodes | grep "node.kubernetes.io/instance-type"

# 노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 노드별 최대 생성 가능한 pods 정보 확인 - 각각 마스터 노드, 워커 노드
kubectl describe node | grep Allocatable: -A6

# 파드 배포 확인
kubectl get pod
kubectl get pod | grep -v NAME | wc -l
kubectl get replicasets

# LimitRanges 기본 정책 확인
kubectl describe limitranges

# 수정 전 env 정보 확인
kubectl describe ds -n kube-system aws-node | grep ADDITIONAL_ENI_TAGS: -A22
kubectl describe daemonsets.apps -n kube-system aws-node | egrep 'ENABLE_PREFIX_DELEGATION|WARM_PREFIX_TARGET'

가능하게 된 이유는 아래와 같습니다[^4].

  • LimitRange 해제
  • Kubelet 의 max-pods args 수정
  • AWS VPC CNI(ENABLE_PREFIX_DELEGATION|WARM_PREFIX_TARGET) 설정 변경

AWS에서 제공하는 별도의 Service 확인

서비스에 대해서는 상단에 설명하였으므로, 기본적인 도식 확인 및 AWS 에서 제공하는 별도의 서비스를 중점으로 확인해봅시다.

로드밸런서 컨트롤러 + NLB IP 모드 동작

그림 7 - AWS Load Balancer Controller + NLB IP 모드 동작 with AWS VPC CNI

  1. 인스턴스 유형

    1. externalTrafficPolicy : ClusterIP ⇒ 2번 분산 및 SNAT으로 Client IP 확인이 불가능합니다 (LoadBalancer 타입 (기본 모드) 동작)
    2. externalTrafficPolicy : Local ⇒ 1번 분산 및 ClientIP 유지, 워커 노드의 iptables를 사용합니다.
    • 상세 설명

      통신 흐름

      요약 : 외부 클라이언트가 '로드밸런서' 접속 시 부하분산 되어 노드 도달 후 iptables 룰로 목적지 파드와 통신됩니다.

      • 노드는 외부에 공개되지 않고 로드밸런서만 외부에 공개되어, 외부 클라이언트는 로드밸랜서에 접속을 할 뿐 내부 노드의 정보를 알 수 없습니다.
      • 로드밸런서가 부하분산하여 파드가 존재하는 노드들에게 전달합니다: iptables 룰에서는 자신의 노드에 있는 파드만 연결합니다(externalTrafficPolicy: local).
      • DNAT 2번 동작 : 첫 번째(로드밸런서 접속 후 빠져 나갈때), 두 번째(노드의 iptables 룰에서 파드IP 전달 시)
      • 외부 클라이언트 IP 보존(유지) : AWS NLB 는 타켓인스턴스일 경우 클라이언트 IP를 유지하고, iptables 룰 경우도 externalTrafficPolicy 로 클라이언트 IP를 보존합니다.

      부하분산 최적화 : 노드에 파드가 없을 경우 '로드밸런서'에서 노드에 헬스 체크(상태 검사)가 실패하여 해당 노드로는 외부 요청 트래픽을 전달하지 않습니다.

      3번째 인스턴스(Node3)은 상태 확인 실패로 외부 요청 트래픽을 전달하지 않습니다.

  2. IP 유형 ⇒ 반드시 AWS LoadBalancer 컨트롤러 파드 및 정책 설정이 필요합니다!

    1. Proxy Protocol v2 비활성화 ⇒ NLB에서 바로 파드로 인입됩니다. 단 ClientIP가 NLB로 SNAT 되어 Client IP 확인이 불가능합니다.
    2. Proxy Protocol v2 활성화 ⇒ NLB에서 바로 파드로 인입됩니다. 그리고 ClientIP 확인이 가능합니다(→ 단 PPv2 를 애플리케이션이 인지할 수 있게 설정이 필요합니다).

AWS의 Ingress 확인

서비스를 외부망으로 노출하는 웹프록시가 인그레스입니다. AWS 쿠버네티스의 인그레스는 AWS Load Balancer Controller + Ingress (ALB) IP 모드로 동작합니다(with AWS VPC CNI).

그림 8 - AWS 쿠버네티스의 인그레스 동작 도식

실습 06-1. kOps 클러스터 편집 후 인그레스 확인

# 마스터/워커 노드에 EC2 IAM Role 에 Policy (AWSLoadBalancerControllerIAMPolicy) 추가
## IAM Policy 정책 생성 : 2주차에서 IAM Policy 를 미리 만들어두었으니 Skip
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.5/docs/install/iam_policy.json
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json

# EC2 instance profiles 에 IAM Policy 추가(attach)
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name nodes.$KOPS_CLUSTER_NAME

# IAM Policy 정책 생성 : 2주차에서 IAM Policy 를 미리 만들어두었으니 Skip
curl -s -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/AKOS/externaldns/externaldns-aws-r53-policy.json
aws iam create-policy --policy-name AllowExternalDNSUpdates --policy-document file://externaldns-aws-r53-policy.json

# EC2 instance profiles 에 IAM Policy 추가(attach)
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name nodes.$KOPS_CLUSTER_NAME

# kOps 클러스터 편집 : 아래 내용 추가
kops edit cluster
-----
spec:
  certManager:
    enabled: true
  awsLoadBalancerController:
    enabled: true
  externalDns:
    provider: external-dns
-----

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

마무리

제 2장에서는 분량을 조절해야 읽힐 듯 하여, 앞서 말씀드렸듯 AWS EKS의 네트워크에 대해서만 살펴보았습니다.

이번 장에서는 아래 내용을 반드시 기억하셨으면 좋겠습니다.

  1. AWS VPC CLI의 주요 기능 및 차이점에 대해 해제하였습니다.
    1. 기본 네트워크 정보를 확인했습니다.
    2. 노드 간 파드 통신에 대해 확인했습니다.
    3. 파드에서 외부통신으로의 흐름을 확인했습니다.
    4. AWS CLI 노드에 파드 생성갯수 제한이 왜 되는지 파악 후 이에 대한 해결책을 확인하였습니다.
    5. AWS에서 사용할 수 있는 별도의 Service와 Ingress에 대해 확인할 수 있었습니다.

이것으로 제 2장 파트 1을 마칩니다. 긴 글 읽어주셔서 감사합니다.

[^1]: 이런 개념은 Service Discovery 라고 합니다.
[^2]: 이 분의 블로그 게시글이 이해에 많은 도움이 되었습니다.
[^3]: 간략 소개는 이 게시글을, 보다 근본적인 이해를 원하신다면 이 게시글반드시 일독하시기를 권합니다.
[^4]: 해당 링크를 일독하시기를 권장드립니다!

profile
백엔드 프로그래머

0개의 댓글