이 글은 쿠버네티스에서 AWS VPC CNI 동작과정을 설명합니다. 몇 가지 선수지식이 필요합니다.
- 네트워크 인터페이스
- iptables
- route table
- arp 프로토콜
- AWS VPC
- 쿠버네티스
AWS VPC CNI란?
AWS에서 쿠버네티스 설치를 하면, 네트워크 설정과 동작은 AWS 네트워크 환경에 영향을 받습니다. 그래서 AWS와 쿠버네티스 사이 적절한 중계자가 필요합니다. 그 역할을 수행하는 모듈이 AWS VPC CNI입니다.
kops AWS VPC CNI
스터디에서 사용하는 kops 클러스터는 AWS VCP CNI를 지원합니다.
AWS VPC CNI설치는 kops클러스터 생성할 때 networking인자로 설정합니다.
kops create cluster \
--zones $ZONES \
--networking amazonvpc \
...
스터디에서 제공한 자동화 설치 스크립트를 확인하면, kops 설치 명령어에 AWS VPC CNI가 설정되어 있습니다.
AWS VPC CNI는 모든 node에 적용되야 하므로 daemonset으로 실행됩니다.
# AWS VCP CNI daemonset 확인
kubectl -n kube-system get ds aws-node
# AWS VCP CNI 버전 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
POD IP할당
POD IP대역
pod IP대역은 pod가 생성될 때 할당되는 IP범위입니다. pod IP대역은 놀랍게도 쿠버네티스 node IP대역과 동일합니다. 공식문서에서도 이 특징을 강조하고 있습니다!
(❇️ 보통 다른 CNI(예: calico)는 node IP대역과 pod IP대역이 다르게 설정됩니다)
AWS 네트워크 관점에서 node와 pod를 바라보면 모두 AWS VPC CIDR범위에 있습니다. 자세히 살펴보지 않았지만, podIP대역은 node가 속한 subnet CIDR 범위에 있을 것 같네요.
pod IP 생성 실습
pod를 생성하고 pod IP가 정말 node IP대역과 같은지 확인해보겠습니다. nginx 컨테이너를 실행하는 deployment를 배포합니다.
kubectl apply -f deploy.yaml
# deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "32Mi"
cpu: "10m"
ports:
- containerPort: 80
pod IP를 확인하면 node(EC2 Instance)가 사용하는 VPC대역과 동일합니다. 그리고 subnet대역도 같이 확인해보세요!
POD IP할당과정 디버깅
pod가 생성되면 daemonset pod들은 IP할당을 요청받고 IP를 할당합니다. 이 과정은 ipamd.log에서 확인할 수 있습니다.
tail -f /var/log/aws-routed-eni/ipamd.log
이 글을 쓰는 와중에 chatgpt GPT-4모델이 출시되어 pod IP생성과정을 물어봤습니다. 답변이 놀랍네요!.
(심화) 네트워크 네임스페이스 분석
AWS VPC CNI는 생성된 pod에 IP를 할당하기 위해 네트워크 네임스페이스를 생성합니다. 네트워크 네임스페이스에서 조회한 IP는 pod IP와 동일합니다.
ssh ubuntu@{workernode IP}
sudo lsns -o PID,COMMAND -t net
sudo nsenter -t {PID} -n ip -c addr
POD간 통신
네트워크 네임스페이스로 보는 pod와 node
pod가 생성되면 네트워크 네임스페이스가 만들어집니다. pod 네트워크 네임스페이스와 root 네임스페이스를 연결하기 위해 가상 인터페이스(veth)를 사용합니다.
(❇️ coredns처럼 hostIP를 사용하는 일부 pod는 root 네임스페이스를 사용합니다)
그림을 더 확대해 볼까요? root 네임스페이스에서 네트워크 인터페이스를 조회하면, pod 네트워크 인터페이스 연결된 가상 인터페이스가 보입니다.
# 인터페이스 목록 확인
ip -c -br addr show
(심화) 쿠버네티스 통신을 제어하는 route table
쿠버네티스 네트워크 통신 방향은 route table 영향을 받습니다.
pod default gateway는 root 네임스페이스에 있는 가상 네터워크 인터페이스로 빠져나갑니다.
root 네임스페이스로 흘러간 트래픽은 자연스럽게 root 네임스페이스의 route table영향을 받습니다. 트래픽은 route table에 의해 node내부로 갈지 외부로 갈지 결정됩니다.
이러한 이유로 pod가 생성되면 가상 네트워크 인터페이스와 route table이 추가가 됩니다!
같은 node의 파드간 통신 디버깅
가상 네트워크 인터페이스는 root 네임스페이스에 있으므로 node 라우팅 테이블을 따릅니다. 트래픽은 라우팅 테이블에 따라 목적지 pod에 연결된 가상 인터페이스로 흘러갑니다.
다른 node의 파드간 통신 디버깅
다른 node간 pod통신도 크게 다르지 않습니다. route table에 의해 다른 node로 트래픽이 전달됩니다. 다른 node로 트래픽이 흘러갈 때, aws route table이 관여하게 됩니다.
중간중간에 IP대역 계산시간을 줄이기 위해 chatgpt를 잘 사용하는 것도 좋은 것 같습니다.
pod 외부통신
pod가 외부와 통신하면 SNAT(출발지 IP가 node IP로 변경됨)가 됩니다. SNAT은 iptables이 수행합니다.
iptables에서 SNAT규칙은 아래 명령어로 찾을 수 있습니다.
sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'
iptables을 해석하기 어려워 chatgpt도움을 받았습니다.
Loadbalancer타입 service
동작원리
loadbalancer타입 서비스 생성/수정/삭제는 aws-load-balancer pod가 제어합니다. aws-load-balancer는 api-server와 aws API를 사용하여 aws에 NLB(default)를 생성합니다.
설치
aws-load-balancer는 kops애드온 설정으로 설치할 수 있습니다.
# kOps 클러스터 편집 : 아래 내용 추가
kops edit cluster --name ${KOPS_CLUSTER_NAME}
-----
spec:
# TLS
certManager:
enabled: true
awsLoadBalancerController:
enabled: true
-----
# 업데이트 적용
kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster
정상적으로 설치되면 kube-system 네임스페이스에 pod가 생성됩니다.
kubectl -n kube-system get po | grep aws-load
pod에 접근하는 2가지 유형
Loadbalancer타입 서비스는 결국 AWS NLB를 사용하므로, NLB설정을 그대로 따릅니다. NLB가 pod에 접근하는 유형은 2가지입니다.
- 유형1: node(인스턴스)에 직접 접근
NLB에 들어온 요청은 node로 전달합니다. node에 들어오는 service트래픽은 iptables에 의해 제어됩니다. iptables는 kube-proxy가 설정합니다.
- 유형2: pod IP로 직접 접근
유형1에서는 pod에 접근하기 위해 많은 과정을 걸칩니다. 유형2에서는 단순하게 pod에 직접접근하는 방법입니다. service를 거치지 않는다고 해서 service가 필요없는건 아닙니다. service에 설정된 endpoint를 NLB가 참조하여 pod로 접근하게 됩니다.
Loadbalancer타입 서비스와 deployment를 생성해보겠습니다. NLB유형은 IP로 설정했습니다. 유형은 annotations에서 설정할 수 있습니다.
실습
Loadbalancer타입 서비스와 deployment를 생성해보겠습니다. NLB유형은 IP로 설정했습니다. 유형은 annotations에서 설정할 수 있습니다.
kubectl apply -f deploy.yaml
# deploy.yaml
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
약 3~5분(NLB 생성시간)aws가 NLB를 생성하는 시간이 지나면 NLB타입 loadbalancer타입 서비스가 생성됩니다.
IP유형으로 NLB를 생성했기 때문에, NLB는 pod로 직접 접근합니다. pod정보는 target group으로 관리됩니다.
'연재 시리즈' 카테고리의 다른 글
pkos 스터디 3주차 2편 - 도커 레지스트리 harbor (0) | 2023.03.25 |
---|---|
pkos 스터디 3주차 1편 - gitops란? (0) | 2023.03.25 |
pkos 스터디 2주차 1편 - AWS volume 사용 원리 (0) | 2023.03.18 |
ArgoCD 13편 - kustomize 사용 방법 (0) | 2023.03.10 |
ArgoCD 12편 - helm 차트 release방법 (0) | 2023.03.10 |