✍ Posted by Immersive Builder Seong
1. 시작하기 전에
VPC CNI는 Amazon EKS 클러스터 내에서 파드 네트워크를 구현하는 플러그인입니다.
이번 포스팅은 Amazon EKS 서비스보다 기반 네트워크 기술인 VPC CNI에 중점을 두었습니다.
Amazon EKS 서비스에 대해 궁금하신 분들은 아래 포스팅을 참고하시기 바랍니다.
▶ Amazon EKS 서비스 관련 포스팅 : https://okms1017.tistory.com/category/AWS/EKS
2. VPC CNI
VPC CNI 란?
Amazon EKS는 VPC CNI 플러그인을 통해 클러스터 네트워크를 구현합니다. VPC CNI는 쿠버네티스 파드가 클러스터에 배포될 때 VPC 네트워크와 동일한 대역의 IP 주소를 할당받도록 합니다. VPC CNI는 CNI Binary와 ipamd 2개의 컴포넌트로 구성됩니다. CNI Binary는 파드 간 통신을 담당하는 파드 네트워크를 구축합니다. 그리고 ipamd는 L-IPAM 데몬을 기동하여 노드의 인터페이스를 관리하고, 파드의 IP Pool을 미리 생성하여 유지하다가 파드가 배포되는 시점에 빠르게 IP 주소를 할당합니다.
* L-IPAM(long-running node-local IP Address Management)
다른 CNI 플러그인과 달리, VPC CNI의 가장 큰 차이점은 노드와 파드의 네트워크 대역이 동일하다는 것입니다. VPC CNI는 오버레이 통신을 하지 않고 호스트 네트워크와 동일한 대역으로 직접 통신을 합니다. 따라서 캡슐화로 인한 오버헤드가 발생하지 않으므로 네트워크 성능이 5~10% 정도 뛰어납니다.
기본 네트워크 정보
VPC CNI는 aws-node 명칭의 데몬셋으로 배포됩니다. aws-node-* 파드는 2개의 컨테이너로 구성되어 있습니다. 첫 번째 컨테이너는 CNI 네트워크를 구축하고, 두 번째 컨테이너는 eBPF 기반의 NetworkPolicy를 처리합니다. 각각 amazon-k8s-cni:v1.18.6-eksbuild.1와 amazon/aws-network-policy-agent:v1.1.3-eksbuild.1 이미지를 가져와 실행합니다. 그리고 aws-node와 kube-proxy 파드는 호스트 네트워크를 그대로 사용하며, 어플리케이션 파드는 호스트 네트워크와 동일한 대역을 사용하게 됩니다. coredns 파드 또한 호스트 네트워크와 동일한 대역의 IP 주소를 할당받은 것을 확인할 수 있습니다.
VPC CNI의 kube-proxy 모드는 기본적으로 iptables 모드입니다. IPVS를 지원하나 사용하지 않는 이유는 로드밸런싱이나 NAT와 같은 기능보다 파드와 VPC IP를 매핑하여 라우팅을 처리하는 동작이 우선순위가 높다고 판단해서입니다. Amazon EKS는 성능을 중요시하기 때문에 iptables 방식으로 충분하지 않나 생각합니다(개인적인 의견).
워커노드의 네트워크 인터페이스(ENI)는 Primary IP와 Secondary IP로 나뉩니다. 아래와 같이 1번 워커노드는 2개의 Primary IP와 10개의 Secondary IP를 가지고 있습니다. Primary IP는 ENI의 IP 주소를 의미하고, Secondary IP가 미리 할당된 Warm IP Pool 입니다. 즉, 새로운 파드가 배포될 때 해당 IP Pool에서 주소가 할당되는 것입니다.
노드 간 파드 통신
모든 노드에 테스트 파드를 하나씩 배포하고 내부 통신을 테스트합니다.
각 파드는 호스트와 동일한 네트워크 대역의 IP를 할당받습니다. 파드가 배포되면 노드에 eniY@ifN 인터페이스가 추가되고 라우팅 테이블에도 추가됩니다. 해당 인터페이스는 veth pair로 파드의 인터페이스와 매핑됩니다.
1번 파드에서 2번 파드로 Ping 테스트를 진행하고 tcpdump로 패킷을 캡처합니다. 1번과 2번 파드에서 확인된 패킷 모두 파드의 IP 주소를 가지고 통신합니다.
파드에서 외부 통신
파드에서 외부로 나가는 경우 일반적으로 SNAT를 통해 노드의 IP로 변경되어 통신합니다. 필요 시, 특정 네트워크 대역은 AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS 옵션을 설정하여 SNAT 없이 통신할 수도 있습니다. 대표적으로 사무실과 VPN으로 연결된 경우가 이에 해당합니다.
파드 생성 개수 제한
AWS는 인스턴스 유형과 크기에 따라 부착 가능한 ENI 수와 최대 할당 가능한 IP 수가 다릅니다. 예를 들어 t3.medium의 경우, ENI 수는 3개이고 L-IPAM이 생성하는 IP Pool은 ENI 당 6개입니다. 여기서 인터페이스 IP를 제외하면 파드에 할당 가능한 IP 수는 5개입니다. 따라서 t3.medium 인스턴스에는 최대 15개의 파드를 배치할 수 있게 됩니다.
그러나 파드가 ENI에 종속되므로 15개를 초과한 나머지 파드는 배포되지 못하는 제약사항이 있습니다. 이를 해결하기 위해 IPv4 Prefix Delegation 방식을 사용하여 파드 수 제한을 늘릴 수 있습니다. IPv4 Prefix Delegation이란 IP 대신 서브넷을 위임하는 방식입니다.
파드의 수량을 변경하면서 테스트한 자세한 내용은 아래 포스팅을 방문해주시기 바랍니다.
▶ 2-2. 파드 생성개수 제한하기 : https://okms1017.tistory.com/36
AWS LoadBalancer Controller
AWS LoadBalancer Controller는 Amazon EKS의 쿠버네티스 클러스터에서 AWS의 로드밸런서를 자동으로 생성하고 관리하는 컨트롤러입니다. 로드밸런서의 타겟그룹에 대상 파드를 등록하기 위해 EKS 클러스터 내 파드의 정보를 지속적으로 로드밸런서에 제공하는 역할을 합니다. 이 때, AWS LoadBalancer Controller는 IRSA, Pod Identity, EC2 Profile 등의 방식으로 정보를 제공합니다. 이를 통해 로드밸런서는 동적으로 타겟그룹을 변경하며 대상 파드와 직접 통신할 수 있습니다.
로드밸런서는 Instance Mode와 IP Mode 두 가지가 있습니다. Instance Mode는 NodePort를 사용하는 통신방식이고, IP Mode는 파드의 IP를 사용하는 통신방식입니다.
- Instance Mode
- IP Mode
AWS LoadBalancer Controller를 통해 NLB를 배포할 때 타겟그룹에는 노드의 IP 주소가 아닌 대상 파드의 IP 주소가 등록됩니다.
그리고 NLB로 100번 반복 접속을 시도한 결과, 부하가 고루 분산된 것을 확인할 수 있습니다.
NLB를 사용하는 경우 외부 클라이언트의 IP 주소를 바로 확인할 수 없습니다. 이 때, PPv2(Proxy Protocol v2)를 활성화하면 L7 단의 IP Header를 인식할 수 있습니다. 단, NLB 뿐만 아니라 대상 어플리케이션(Apache, Nginx, HA Proxy 등)에서도 PPv2 옵션을 활성화해주어야 합니다.
ALB를 배포하고 타겟그룹과 리소스맵을 확인합니다. NLB와 거의 마찬가지로 파드의 IP 주소로 직접 통신하며, 한 가지 다른 점은 URL 기반의 규칙이 존재한다는 점입니다.
External DNS
External DNS는 쿠버네티스 서비스/인그레스 생성 시 도메인을 설정하면 Route 53(AWS)/Cloud DNS(GCP)/DNS(Azure) 서비스의 A Record를 자동으로 생성하고 삭제합니다. External DNS 컨트롤러가 해당 서비스를 관리하기 위해서는 IRSA, Node IAM Role, Static Credentials 등의 방식으로 서비스 권한을 부여해 주어야 합니다.
External DNS를 사용하여 도메인을 연동하는 실습은 아래 포스팅을 참고하시기 바랍니다.
▶ 2-4. Ingress & External DNS : https://okms1017.tistory.com/38
Topology Aware Routing
클라우드 환경에서 쿠버네티스 클러스터 노드들이 여러 가용 영역(AZ)에 분산 배치되어 있을 때, 동일한 가용 영역(AZ)의 목적지 파드로만 트래픽을 라우팅하는 기능입니다. 주로 규모가 있는 시스템에서 가용 영역 간에 네트워크 트래픽 비용을 절감할 수 있는 장점이 있습니다. 만약 요청한 가용 영역에 파드가 존재하지 않을 경우에는 다른 가용 영역으로 트래픽을 전달합니다.
현재 클러스터 노드는 서로 다른 가용 영역(2a,2b,2c)에 분산 배치되어 있습니다. 클라이언트 파드에서 서비스로 요청을 보내면 각 노드의 어플리케이션 파드로 요청이 분산됩니다.
그 이유는 노드의 iptables chain에 약 33%의 비율로 라우팅하는 규칙이 설정되어 있기 때문입니다.
이번에는 Topology Mode를 설정하여 동작을 확인합니다. Topology Mode는 아래와 같이 어노테이션을 통해 적용할 수 있습니다.
$ kubectl annotate service svc-clusterip "service.kubernetes.io/topology-mode=auto"
이제 클라이언트 파드에서 서비스로 요청을 보낼 시 동일한 가용 영역(ap-northeast-2a)에 위치한 어플리케이션 파드(deploy-echo-859cc9b57d-wcf6t)로만 요청이 전송됩니다.
EndpointSlice 상세 정보를 조회하여 옵션 적용 여부를 확인할 수 있습니다. 힌트(hints)는 엔드포인트가 트래픽을 제공해야 하는 영역을 의미하고, 존(zone)은 엔드포인트가 위치한 가용 영역을 의미합니다.
Topology Mode를 적용한 이후의 iptables는 아래와 같이 변경됩니다. 각 노드의 규칙은 해당 노드와 동일한 가용 영역에 위치한 파드로만 라우팅하도록 합니다.
만약 동일한 가용 영역에 어플리케이션 파드가 존재하지 않을 경우 어떻게 될까요?
가용 영역(ap-northeast-2a)에 위치한 어플리케이션 파드(deploy-echo-859cc9b57d-wcf6t)를 삭제하여 테스트를 진행합니다. 결과는 다른 가용 영역(ap-northeast-2b)에 위치한 어플리케이션 파드(deploy-echo-859cc9b57d-wlckw)로 요청이 전송됩니다.
주목할 부분은 EndpointSlice 정보에서 기존 hints 항목이 사라지게 됩니다.
그리고 각 노드의 규칙은 존재하는 어플리케이션 파드로 트래픽을 라우팅하도록 고정됩니다.
Blue/Green Deployment
- v1 : v2 = 100 : 0
```
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":100
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":0
}
```
pathType: Prefix
backend:
service:
name: blue-green
port:
name: use-annotation
- v1 : v2 = 0 : 100
```
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":0
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":100
}
```
pathType: Prefix
backend:
service:
name: blue-green
port:
name: use-annotation
Canary Deployment
- v1 : v2 = 90 : 10
```
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/actions.blue-green: |
{
"type":"forward",
"forwardConfig":{
"targetGroups":[
{
"serviceName":"hello-kubernetes-v1",
"servicePort":"80",
"weight":90
},
{
"serviceName":"hello-kubernetes-v2",
"servicePort":"80",
"weight":10
}
```
pathType: Prefix
backend:
service:
name: blue-green
port:
name: use-annotation
A/B Testing
- if (HeaderName="kans-study-end") → v2, else → v1
```
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/conditions.ab-testing: >
[{"field":"http-header","httpHeaderConfig":{"httpHeaderName": "HeaderName", "values":["kans-study-end"]}}]
alb.ingress.kubernetes.io/actions.ab-testing: >
{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"hello-kubernetes-v2","servicePort":80}]}}
labels:
app: hello-kubernetes
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ab-testing
port:
name: use-annotation
- path: /
pathType: Prefix
backend:
service:
name: hello-kubernetes-v1
port:
name: http
Network Policies with VPC CNI
AWS EKS는 업스트림 Network Policy API를 지원합니다. 쿠버네티스 표준을 준수하므로 호환성을 보장합니다.
Network Policy와 관련된 내용은 아래 포스팅을 참고하시기 바랍니다.
▶ 2-5. Network Policies with VPC CNI : https://okms1017.tistory.com/39
[출처]
1) CloudNet@, KANS 실습 스터디
2) https://docs.aws.amazon.com/eks/latest/best-practices/vpc-cni.html
3) https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/cni-proposal.md
4) https://docs.aws.amazon.com/eks/latest/best-practices/cost-opt-networking.html
'K8s > Advanced Network' 카테고리의 다른 글
KANS3 스터디 완주 후기 (6) | 2024.11.04 |
---|---|
[KANS3] 8. Cilium CNI (0) | 2024.10.27 |
[KANS3] 7. Service Mesh: Istio (0) | 2024.10.20 |
[KANS3] 6. Ingress + Gateway API (0) | 2024.10.13 |
[KANS3] 5. MetalLB + IPVS (0) | 2024.10.06 |