---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/ ---------------------------------------- # Kubernetes 네트워킹 > **마지막 업데이트**: 2026년 9월 13일. 기능 근거는 Cilium 1.20.1, Calico Open Source 3.32, Flannel 0.28.9, AWS VPC CNI 1.23.0을 포함합니다. 설치 전 제품별 Kubernetes·플랫폼 지원 범위를 확인합니다. 이 버전들을 하나의 클러스터에서 함께 검증했다는 의미는 아닙니다. ## 개요 Kubernetes 네트워킹은 컨테이너화된 애플리케이션 간의 통신을 가능하게 하는 핵심 인프라 계층입니다. 이 섹션에서는 Kubernetes 네트워킹의 기본 개념부터 고급 CNI(Container Network Interface) 솔루션, 그리고 AWS EKS 환경에서의 네트워킹 패턴까지 다룹니다. ## Kubernetes 네트워킹 모델 현재 Kubernetes 모델의 Pod 네트워크는 **의도적인 네트워크 분리를 고려하면서**, 노드 경계를 넘어 주소 변환이나 프록시 없이 Pod끼리 직접 통신할 수 있는 기반을 제공합니다. kubelet 같은 노드 에이전트는 자기 노드의 Pod에 접근할 수 있어야 합니다. 개별 연결의 성공 여부는 정책, 라우팅, 애플리케이션 리스너에도 달려 있습니다. 일반 Pod는 자체 네트워크 네임스페이스와 클러스터 범위 주소를 가지며, 같은 Pod의 컨테이너는 그 네임스페이스와 localhost를 공유합니다. host-network Pod는 노드 네트워크를 공유하고 dual-stack·다중 네트워크 구성에서는 주소를 더 세밀하게 구분해야 합니다. Pod를 재생성하면 IP가 달라질 수 있지만 같은 Pod 내부 컨테이너를 재시작한다고 네트워크 sandbox까지 반드시 재생성되지는 않습니다. | 구성 요소 | 역할 | |---|---| | Pod 네트워크 | 워크로드 네트워크 네임스페이스의 주소와 연결 제공 | | Service·discovery | 바뀌는 엔드포인트에 안정적인 서비스 이름이나 가상 주소 제공 | | Ingress/Gateway 구현 | 설정한 외부 진입점과 애플리케이션 라우팅 제공 | | 네트워크 정책 엔진 | 선택한 구현이 지원하는 정책 강제 | 이 역할이 반드시 순서대로 지나는 패킷 경로를 뜻하지는 않습니다. Service 주소 변환, L7 프록시, 워크로드 정책에 따라 실제 요청 경로가 달라집니다. ### Pod 네트워킹 Pod 네트워킹은 통신에 필요한 주소와 라우팅을 제공합니다. 아래 그림은 일반 IPv4 Pod 예제이며 해당 정책과 네트워크 제어가 연결을 허용한다고 가정합니다. ![정책과 라우팅이 허용하는 두 노드 간 일반 IPv4 Pod 직접 경로 예제.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-readme-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-readme-1.html) 주소는 설명용 일반 Pod 주소입니다. 의도적인 격리나 host-network·다중 네트워크 구성에서는 별도의 해석이 필요합니다. #### Pod 네트워킹 구현 방식 | 방식 | 설명 | 예시 CNI | |------|------|----------| | **Overlay 네트워크** | 기존 네트워크 위에서 트래픽 캡슐화 | Flannel VXLAN, Calico VXLAN/IPIP, Cilium VXLAN/Geneve | | **Native Routing** | 해당 overlay 캡슐화 없이 하부 네트워크의 경로 사용 | AWS VPC CNI, Calico routing/BGP, Cilium native routing | | **조건부 캡슐화** | 설정한 토폴로지에 따라 직접 경로나 캡슐화 선택 | 제품별 전제가 다른 Calico/Flannel/Cilium 지원 모드 | ### Service 네트워킹 Service는 보통 Pod로 이루어진 논리적 엔드포인트 집합과 접근 방법을 정의합니다. 기본 ClusterIP는 안정적인 가상 IP를 제공하고, headless Service는 가상 IP를 생략하며, ExternalName은 DNS CNAME을 사용합니다. Pod 선택자 없이 관리하는 엔드포인트도 참조할 수 있습니다. ![ClusterIP, NodePort, LoadBalancer, ExternalName의 일반적인 진입 방식. DNS 별칭은 패킷 전달과 구분된다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-readme-2.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-readme-2.html) 일반적인 노출 방식이며 보안 보장이 아닙니다. NodePort 범위와 접근 가능한 노드 주소를 설정할 수 있고 LoadBalancer는 내부용일 수도 있습니다. ExternalName은 DNS 별칭을 반환하며 전달 프록시를 만들지 않습니다. #### Service 유형별 특징 `default`에 표시된 target port를 수신하는 `app: my-app` Pod를 준비합니다. NodePort의 기본 할당 범위는 30000–32767이며 변경할 수 있습니다. 외부 접근은 주소, 라우팅, 접근 제어에도 달려 있습니다. LoadBalancer 예제는 **AWS Load Balancer Controller**를 명시적으로 선택하고 EC2 instance 대상과 할당된 NodePort를 사용합니다. 먼저 해당 컨트롤러와 IAM·서브넷 전제를 구성합니다. EKS Auto Mode는 다른 컨트롤러·class를 사용합니다. 여기서 포트 443은 TCP 포트 선택일 뿐이며, 백엔드 8443에서 TLS를 제공하거나 로드 밸런서에 별도로 설정해야 합니다. 포트 변환은 일반 Kubernetes Service API를 설명합니다. 현재 AWS 문서의 EKS 네이티브 네트워크 정책에는 Service 포트와 컨테이너 포트 일치, 안정적인 강제를 위한 `metadata.ownerReferences`가 있는 컨트롤러 관리 Pod라는 추가 전제가 있습니다. 해당 정책 구현을 시험하기 전에 예제를 이 조건에 맞춥니다. ```yaml apiVersion: v1 kind: Service metadata: name: my-service namespace: default spec: type: ClusterIP selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 8080 --- apiVersion: v1 kind: Service metadata: name: my-nodeport-service namespace: default spec: type: NodePort selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 8080 nodePort: 30080 --- apiVersion: v1 kind: Service metadata: name: my-loadbalancer-service annotations: service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: instance namespace: default spec: type: LoadBalancer selector: app: my-app ports: - protocol: TCP port: 443 targetPort: 8443 loadBalancerClass: service.k8s.aws/nlb allocateLoadBalancerNodePorts: true ``` ### Ingress 네트워킹 Ingress 리소스에는 컨트롤러와 데이터 플레인이 필요합니다. 이 HTTP 예제는 AWS LBC, `spec.ingressClassName: alb`, IP 대상을 사용합니다. `api-v1`, `api-v2`, `web-frontend` Service가 `default`에 존재하고 포트 80 및 VPC에서 라우팅 가능한 준비된 Pod 엔드포인트를 제공해야 합니다. 필요한 HTTPS·인증서는 별도로 구성합니다. 설치·대상 전제는 [LBC 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/03-aws-lb-controller.md)를 확인합니다. Ingress는 HTTP/HTTPS 트래픽을 클러스터 내부 Service로 라우팅하는 규칙을 정의합니다. ![Ingress의 논리적 호스트·경로 라우팅과 Service 백엔드·Pod 관계.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-readme-3.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-readme-3.html) 상자는 Ingress 데이터 플레인 기능을 나타냅니다. AWS LBC는 ALB를 설정하며 애플리케이션 트래픽이 컨트롤러 조정 프로세스를 통과하지 않습니다. 대상 모드에 따라 Service 가상 IP를 실제 추가 홉으로 거치지 않고 Pod IP나 NodePort에 도달할 수 있습니다. ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip namespace: default spec: rules: - host: api.example.com http: paths: - path: /v1 pathType: Prefix backend: service: name: api-v1 port: number: 80 - path: /v2 pathType: Prefix backend: service: name: api-v2 port: number: 80 - host: web.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-frontend port: number: 80 ingressClassName: alb ``` ## CNI (Container Network Interface) CNI는 런타임이 컨테이너 네트워크를 설정하는 인터페이스를 표준화합니다. 현재 Kubernetes에서는 kubelet이 CRI로 Pod sandbox 작업을 요청하고 **컨테이너 런타임이 CNI를 관리**합니다. 과거 kubelet의 직접 CNI 관리 플래그는 Kubernetes 1.24에서 제거되었습니다. ### 런타임과 플러그인의 역할 | 주체 | 역할 | |---|---| | kubelet | 컨테이너 런타임 인터페이스로 sandbox 생성·제거 요청 | | 컨테이너 런타임 | 네트워크 설정 선택과 CNI 플러그인 체인 호출 | | CNI 플러그인 | 설정을 받아 ADD/DEL 등 지원 작업을 수행하고 결과 반환 | | IPAM 구현 | 주소 할당·반환, 위임한 플러그인이나 공급자별 에이전트로 구현 가능 | | 선택적 노드 에이전트 | 공급자별 라우팅·정책·IP pool·데이터 플레인 상태 유지 | 런타임이 CNI 인터페이스로 플러그인에 설정을 전달합니다. 모든 플러그인에 별도 장기 실행 에이전트나 IPAM 바이너리가 필수인 것은 아닙니다. veth pair가 흔하지만 다른 인터페이스 구현도 있습니다. ## CNI 비교 | 프로젝트·범위 | 네트워킹과 정책 | 구분할 기능·제약 | |---|---|---| | **Cilium 1.20.1** | eBPF 네트워킹, 해당 L7 기능에 Envoy 사용, Cilium 네트워크 정책·Hubble | Linux 워커 데이터 플레인과 AMD64/Arm64 전제. Windows CLI 제공은 Windows CNI 지원이 아님. WireGuard/IPsec과 Beta ztunnel mTLS의 범위가 다름. | | **Calico Open Source 3.32** | 라우팅·캡슐화 선택, iptables·nftables·eBPF 옵션, 순서 있는 정책 tier와 호스트·워크로드 정책 | Windows에는 Linux eBPF·WireGuard 데이터 플레인 미지원 등 별도 제약이 있음. Whisker/Goldmane 흐름 관측은 Tech Preview. 유료 기능은 제품 edition 표 확인. | | **Flannel 0.28.9** | 호스트 subnet 할당과 노드 간 전달, VXLAN·host-gw 등 백엔드 | `flanneld` 자체는 NetworkPolicy를 강제하지 않지만 차트의 선택적 `netpol.enabled`가 SIGs 정책 컨트롤러를 배포함. WireGuard 백엔드가 문서화되어 있고 IPsec은 실험 기능. Windows VXLAN은 별도 설정·제약 적용. | | **AWS VPC CNI 1.23.0 / EKS** | VPC 주소·EC2 ENI/prefix 할당, 지원되는 Linux EC2 노드에서 EKS 표준·Admin 정책 | EKS Auto Mode는 DNS 정책 기능도 가진 관리형 네트워킹 구현. Windows, Fargate, custom networking, prefix delegation, multi-NIC는 각각 조건이 다름. | | **원래 Weave Net 프로젝트** | 과거 overlay 네트워킹 구현 | 원래 `weaveworks/weave` 저장소가 archived 상태이므로 신규 클러스터의 활성·지원 기본 선택으로 설명하지 않음. | ### 정책·암호화·관측성 - Cilium은 해당 L7 구성 요소를 통해 HTTP/DNS 인식 정책과 클러스터 범위·호스트 정책을 제공합니다. deny/allow 의미는 Calico의 순서 있는 Tier API와 구분합니다. - Calico Open Source에는 계층적 정책 tier와 호스트 정책이 있습니다. 현재 제품 표의 application-layer 정책, DNS/FQDN 정책, Cluster Mesh는 Cloud/Enterprise 기능이므로 오픈소스 edition 기능으로 혼동하지 않습니다. 문서화된 전송 암호화는 WireGuard입니다. - Amazon EKS는 Auto Mode와 지원되는 EC2/VPC-CNI 설치에 `ClusterNetworkPolicy` Admin/Baseline 제어를 제공합니다. AWS가 설명하는 DNS/FQDN `ApplicationNetworkPolicy`는 **Auto Mode** 기능입니다. 이름만으로 현재 HTTP 메서드·본문 검사까지 지원한다고 해석하지 않습니다. - Flannel의 선택적 정책 컨트롤러에는 자체 전제가 있으며 네트워킹 백엔드 선택만으로 정책이 활성화되지는 않습니다. - 노드 간 암호화, 인증된 워크로드 신원, 애플리케이션 mTLS는 서로 다른 제어입니다. 네트워크 흐름 관측도 애플리케이션 추적이나 프로세스·파일 강제와 다릅니다. ### 라우팅과 성능 Calico와 Cilium은 BGP로 경로를 광고할 수 있지만 이것만으로 멀티클러스터 서비스 검색, 정책 동기화, 암호화가 제공되지는 않습니다. Flannel host-gw는 직접 경로와 적절한 L2 연결이 필요합니다. overlay에는 캡슐화·MTU 고려가 추가되지만 CNI 이름만으로 보편적인 성능 순위를 정할 수 없습니다. 이전 100/98/95/85/80/75% 처리량 그림에는 재현할 워크로드, 버전, 측정 출처가 없었습니다. 비교 가능한 하드웨어·커널·패킷/요청 크기·동시성·암호화/정책 설정·처리량·손실·꼬리 지연을 사용합니다. 별도 [Pod 벤치마크](https://www.atomai.click/kubernetes-docs/llms/ko/networking/06-pod-network-benchmark.md)의 실제 과거 환경과 측정은 해당 문서에 유지합니다. ## CNI 선택 가이드 필요한 라우팅, 정책, 운영체제, 지원 모델을 먼저 선택하고 그 조합을 시험합니다. | 요구 | 검토 경로 | |---|---| | 표준 EKS VPC 주소와 지원 네트워크 정책 | 두 번째 정책 엔진을 추가하기 전에 AWS VPC CNI/EKS 기능 검토 | | 순서 있는 정책 tier, 호스트 정책, 인프라 BGP | 해당 Calico edition·데이터 플레인과 라우팅 전제 검토 | | Cilium 정책, Hubble, 선택적 메시 기능 | Linux·커널·플랫폼 호환성과 [Cilium 메시 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/cilium-service-mesh/README.md) 확인. 해당 L7 경로에는 Envoy가 계속 포함됨. | | 제한된 기능이 필요한 작은 네트워크 | 실제 요구에 맞는 Flannel 백엔드·선택적 정책 컨트롤러 검토 | | 프로세스·시스템 호출·파일 제어 | 네트워크 정책과 별도로 Tetragon 같은 런타임 보안 구성 요소 검토 | ### EKS 관리형 Add-on 설정 다음은 **설정 payload** 예제이며 같은 워크로드에 Calico와 VPC CNI 정책 엔진을 함께 설치하라는 의미가 아닙니다. ```json { "enableNetworkPolicy": "true" } ``` 문자열 `"true"`는 공식 문서의 값 타입과 일치합니다. 기존 Kubernetes 버전에 호환되는 EKS add-on build를 선택하고 해당 build의 설정 스키마를 확인합니다. ```bash EKS_REGION=ap-northeast-2 KUBERNETES_MINOR=1.35 # Replace with the existing cluster's minor version aws eks describe-addon-versions --region "$EKS_REGION" --addon-name vpc-cni \ --kubernetes-version "$KUBERNETES_MINOR" : "${VPC_CNI_ADDON_VERSION:?Set the compatible eksbuild version selected from metadata}" aws eks describe-addon-configuration --region "$EKS_REGION" --addon-name vpc-cni \ --addon-version "$VPC_CNI_ADDON_VERSION" ``` 업스트림 1.23.0 릴리스 번호와 EKS `eksbuild` 버전은 다른 식별자입니다. 의도한 기존 add-on 설정과 변경을 합치고, 무조건 `latest`를 선택하거나 다른 값을 덮어쓰지 않습니다. 기존 타사 정책 구현에서 전환한다면 남아 있는 정책 적용 상태 제거와 검증한 노드·워크로드 전환 계획도 필요합니다. ## EKS 네트워킹 기본 사항 ### EKS 기본 네트워킹 아키텍처 | 위치·구성 요소 | 역할 | |---|---| | EKS 관리 VPC | AWS가 여러 가용 영역에서 관리형 Kubernetes 제어 평면 실행 | | 고객 클러스터 VPC | 워커 네트워킹, 선택한 서브넷, EKS 관리 cross-account ENI가 설정한 제어 평면 연결 제공 | | 선택한 고객 VPC 서브넷의 ALB/NLB | 선택한 공개·내부 애플리케이션 진입점 제공. Internet Gateway/NAT Gateway만으로 해당 라우팅이 대체되지는 않음. | | NAT Gateway·프라이빗 서비스 엔드포인트 | 워크로드 설계에 필요한 특정 아웃바운드 경로 제공 | 이전 그림은 제어 평면을 고객 VPC 안에, 로드 밸런서를 밖에 표시하여 위 소유 경계로 대체했습니다. ### 컴퓨팅 모드별 DNS와 네트워킹 | 컴퓨팅 모드 | DNS·구성 요소 위치 | |---|---| | 표준 EC2 노드 | 일반적으로 설정한 CoreDNS Deployment와 설치한 네트워킹 구성 요소를 사용하며 대체 구현은 별도 지원 설정 필요 | | 순수 EKS Auto Mode | CoreDNS, VPC CNI, kube-proxy 기능이 관리형 노드 systemd 서비스로 실행되므로 이 노드에는 CoreDNS Deployment/add-on 불필요 | | Auto Mode와 비 Auto 노드 혼합 | 다른 노드의 Auto Mode DNS 서비스를 사용할 수 없는 비 Auto 노드를 위해 CoreDNS Deployment 유지 | Auto Mode의 첫 DNS resolver는 노드 로컬입니다. 업스트림 전달과 제어 평면 통신에는 여전히 네트워크 접근이 필요할 수 있으므로 모든 DNS 관련 패킷이 노드 안에 머문다는 보장은 아닙니다. AWS는 Auto Mode의 Admin·DNS 정책을 문서화하고 있으며 표준 EC2 VPC-CNI Admin 정책에는 별도 버전·활성화 전제가 있습니다. ### VPC CNI 동작 방식 AWS VPC CNI는 선택한 IPAM 모드로 일반 Pod에 VPC에서 라우팅 가능한 주소를 제공합니다. 보조 IPv4 주소, 위임 prefix, branch ENI, multi-NIC 구성은 서로 다르며 host-network Pod는 노드 네트워크를 공유합니다. ![EC2 ENI의 보조 IPv4 주소를 Pod에 할당하는 예제와 선택적인 warm 인터페이스.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-readme-9.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-readme-9.html) secondary-IP 모드만 나타냅니다. warm ENI는 설정 가능한 할당 전략이며 모든 노드가 반드시 하나씩 예약한다는 의미가 아닙니다. prefix delegation, custom networking, branch ENI는 할당 규칙이 다릅니다. #### ENI 및 IP 제한 | 인스턴스 유형 | 최대 ENI | ENI당 IPv4 슬롯 | 과거 secondary-IP bootstrap 값 | |--------------|----------|------------|----------------| | t3.medium | 3 | 6 | 17 | | t3.large | 3 | 12 | 35 | | m5.large | 3 | 10 | 29 | | m5.xlarge | 4 | 15 | 58 | | m5.2xlarge | 4 | 15 | 58 | | c5.4xlarge | 8 | 30 | 234 | VPC CNI 1.23.0의 인스턴스 한계와 과거 max-Pods 표로 확인한 값입니다. 과거 공식은 `ENI 수 × (ENI당 IPv4 슬롯 − 1) + 2`이며 현재 모든 환경의 권장값이 아닙니다. prefix delegation, custom networking, branch ENI, 다중 네트워크 카드는 주소 용량을 바꿉니다. Kubernetes 스케줄링은 kubelet `maxPods`와 리소스에도 제한됩니다. EKS 관리형 노드 그룹은 vCPU 30개 미만에서 `maxPods` 상한 110, 그 외에는 250을 적용하며 사용 가능한 IP 수만으로 상한이 바뀌지 않습니다. ### EKS 네트워킹 고려사항 #### IP 주소 관리 **Linux VPC CNI**에서는 선택한 add-on/Helm/DaemonSet 관리 방식으로 공식 환경 변수를 구성합니다. 아래는 EKS add-on 설정 조각입니다. 이전 `amazon-vpc-cni` ConfigMap의 `enable-prefix-delegation`은 Linux IPAMD를 이렇게 설정하지 않습니다. 변경 시 의도한 다른 add-on 값도 보존합니다. ```json { "env": { "ENABLE_PREFIX_DELEGATION": "true", "WARM_PREFIX_TARGET": "1" } } ``` 대신 전체 할당 하한과 여유 IP 목표를 조정할 수 있습니다. `MINIMUM_IP_TARGET` 또는 `WARM_IP_TARGET`을 설정하면 `WARM_PREFIX_TARGET`보다 우선하므로 네 가지 독립적인 목표를 더하는 방식이 아닙니다. 실제 할당은 prefix 단위로 이루어집니다. Nitro 지원, IPv4의 연속된 `/28` 공간, 적절한 kubelet Pod 상한은 별도 전제입니다. Windows prefix 할당은 별도 경로입니다. AWS는 `amazon-vpc-cni` ConfigMap의 `enable-windows-prefix-delegation`과 warm-target 키를 문서화합니다. Linux 환경 변수 절차를 Windows에 그대로 복사하지 않습니다. ```json { "env": { "ENABLE_PREFIX_DELEGATION": "true", "MINIMUM_IP_TARGET": "5", "WARM_IP_TARGET": "2" } } ``` #### 사용자 정의 네트워킹 이 IPv4 예제에는 의도한 AZ·VPC의 실제 서브넷·보안 그룹 ID가 필요합니다. custom networking을 켜고 노드의 zone 레이블로 ENIConfig를 선택합니다. 명시적인 ENIConfig 노드 어노테이션이 있으면 레이블보다 우선합니다. 영문·한글 예제는 같은 리전 이름을 사용하며 실제 노드 zone으로 교체합니다. ENIConfig 객체만 설치한다고 custom networking이 활성화되지는 않습니다. ```json { "env": { "AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG": "true", "ENI_CONFIG_LABEL_DEF": "topology.kubernetes.io/zone" } } ``` ```yaml apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: ap-northeast-2a spec: securityGroups: - sg-0123456789abcdef0 subnet: subnet-0123456789abcdef0 --- apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: ap-northeast-2b spec: securityGroups: - sg-0123456789abcdef0 subnet: subnet-fedcba9876543210f ``` ## 네트워크 심화 개념 아래 항목들은 이 개요 곳곳에서 이름만 스치듯 언급되는 요소입니다. 각 항목의 전체 설치·설정 절차나 실측값은 링크된 심화 문서에 있으며, 이 절은 그 요소들이 서로 어떻게 다르고 어디에 맞는지를 계층별로 정리합니다. ### L2~L7과 라우터·로드밸런서의 차이 "라우터"와 "로드밸런서"는 종종 같은 자리에서 언급되지만 판단 기준이 다릅니다. 라우터는 목적지 하나에 대해 (일반적으로) 경로 하나를 고르는 장비이고, 로드밸런서는 동등한 대상 여러 개 중 하나를 분산 알고리즘으로 고르는 장비입니다. | 계층 | 장비·기능 | 판단 기준 | Kubernetes·AWS 매핑 | |---|---|---|---| | L2 (링크) | 스위치, 브리지 | 목적지 MAC 주소 | CNI가 만드는 veth pair·Linux 브리지, ENI가 노출하는 가상 NIC | | L3 (네트워크) | 라우터 또는 투과형 어플라이언스 삽입 | 라우팅은 목적지 IP, 어플라이언스 선택은 흐름 식별자 | VPC의 암묵적 라우터·TGW, IP 패킷을 캡슐화하는 GWLB | | L4 (전송) | L4 로드밸런서 | 연결·흐름 식별자, 흔히 5-tuple | NLB, kube-proxy(iptables·IPVS·nftables), 별도 eBPF Service 구현 | | L7 (애플리케이션) | L7 로드밸런서·리버스 프록시 | 요청 단위 호스트·경로·헤더, 프로토콜 인식 | ALB, Ingress/Gateway API 구현체, 서비스 메시 사이드카(Envoy) | 핵심 차이는 **분산 단위**입니다. L4 로드밸런서는 보통 TCP 연결이나 추적하는 UDP 흐름의 대상을 선택합니다. L7 프록시는 같은 연결을 공유하더라도 지원하는 애플리케이션 요청별로 대상을 선택할 수 있습니다. GWLB는 애플리케이션 요청을 해석하는 대신 캡슐화한 IP 흐름을 보안 어플라이언스에 분배합니다. 흐름 고정성은 timeout·상태 검사·failover 설정에 영향을 받으며, 대상 재선택이나 연결 중단이 절대 없다는 보장은 아닙니다. > 📎 L2/L3 개념의 프로토콜별 정의는 [네트워크 기초 Part 1](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part1.md), ALB/NLB 대상 유형과 실제 설정은 [AWS Load Balancer Controller](https://www.atomai.click/kubernetes-docs/llms/ko/networking/03-aws-lb-controller.md) 참고. ### 계정·VPC 간 연결: TGW·VPC Peering·GWLB·PrivateLink·Lattice 다섯 연결 방식은 계층과 트래픽 모델이 다릅니다. TGW RAM 공유, VPC Peering, PrivateLink, TGW Peering, VPC Lattice의 실측 지연 비교는 [Cross-Org VPC 연결](https://www.atomai.click/kubernetes-docs/llms/ko/networking/05-cross-org-vpc-connectivity.md)에 있습니다. 이 절은 그 표에 없는 GWLB를 포함해 계층 관점으로 다시 정리합니다. | 연결 방식 | 계층·모델 | 특징 | |---|---|---| | VPC Peering | L3, 양방향 IP 라우팅 | 전이(transitive)되지 않음, CIDR 중복 시 구성 불가 | | Transit Gateway (TGW) | L3, 허브-스포크 IP 라우팅 | 하나 이상의 TGW route table에서 attachment association·propagation을 구성, RAM으로 계정 간 공유 | | Gateway Load Balancer (GWLB) | L3, 투과형 어플라이언스 삽입 | GENEVE(UDP 6081)로 원본 패킷 캡슐화, VPC 엔드포인트 서비스 모델로 소비자 트래픽을 공급자의 어플라이언스 fleet에 연결 | | PrivateLink | 사설 엔드포인트 연결 | NLB 기반 endpoint service 외에 resource endpoint도 지원, 소비자·공급자 CIDR 중복 허용 | | VPC Lattice | 애플리케이션·리소스 네트워킹 | HTTP/HTTPS service는 L7 라우팅과 선택적 IAM 인가 지원, TLS passthrough·resource configuration은 기능 범위가 다름 | GWLB는 Gateway Load Balancer endpoint를 통해 방화벽·IDS/IPS 같은 검사 어플라이언스를 IP 경로에 삽입합니다. 기본 흐름 고정성은 5개 필드를 사용하며 지원되는 설정에서는 2개 또는 3개로 바꿀 수 있습니다. 왕복 라우트, 어플라이언스 상태, 캡슐화 MTU, NACL 및 실제 workload·어플라이언스의 security group을 확인합니다. GWLB 자체에는 ALB 형태의 security group이 없으며, 흐름 고정성이 장애 검증을 대신하지 않습니다. > 📎 EKS와 VPC Lattice의 전체 연동(Gateway API Controller, IAM 인가, 라우팅)은 [VPC Lattice](https://www.atomai.click/kubernetes-docs/llms/ko/networking/02-vpc-lattice.md) 참고. ### DNS resolver와 Route Table의 실제 동작 **DNS resolver:** AmazonProvidedDNS는 **Route 53 Resolver 자체**입니다. 주소에는 기본 VPC IPv4 network 주소에 2를 더한 값(`10.0.0.0/16`이면 `10.0.0.2`)과 `169.254.169.253`이 있으며, 연결된 private zone과 public 이름을 Resolver 규칙에 따라 해석합니다. CoreDNS는 보통 `cluster.local` 같은 설정된 Kubernetes cluster domain을 담당합니다. `kube-dns`는 Service 이름이며 namespace나 DNS zone이 아닙니다. 외부 조회 전달은 Corefile과 DNS Pod에서 보이는 resolver 파일에 따르므로 node 설정을 그대로 쓴다고 가정하지 말고 실제 구성을 확인합니다. Resolver endpoint를 사용하는 설계에서는 inbound endpoint가 온프레미스 조회를 받고, outbound endpoint와 연결된 규칙이 선택한 VPC 조회를 온프레미스 DNS로 보냅니다. Auto Mode의 node-local resolver도 upstream 의존성을 없애지는 않습니다. **Route Table:** VPC 라우팅은 일반적으로 최장 접두사 일치를 사용합니다. AWS는 `local` route의 target 교체와 어플라이언스 경로를 위한 지원 범위 내의 더 구체적인 subnet route를 허용하므로 `local`이 무조건 가장 구체적인 것은 아닙니다. 목적지가 같으면 정적 VPC route가 virtual private gateway에서 전파된 route보다 우선합니다. TGW를 target으로 하는 VPC route는 정적이며, TGW 내부 propagation은 별도의 TGW route table에 속합니다. 유효하지 않은 target이 `blackhole` 항목을 남기면 트래픽이 폐기되므로 목적지와 route 상태를 함께 확인합니다. 명시적으로 route table을 연결하지 않은 subnet은 VPC의 main route table을 사용합니다. > 📎 TGW/Peering 라우트 우선순위와 정적 라우트 구성 예시는 [Cross-Org VPC 연결의 운영 시 확인할 사항](https://www.atomai.click/kubernetes-docs/llms/ko/networking/05-cross-org-vpc-connectivity.md#운영-시-확인할-사항) 참고. ### 커널 데이터 플레인: iptables·IPVS·eBPF·packet filter Linux의 Service 전달과 network policy 적용은 서로 다른 메커니즘을 사용할 수 있습니다. Netfilter는 iptables·nftables에서 사용하는 패킷 경로 hook을 제공합니다. eBPF 구현은 XDP·tc·socket hook에서 Service 대상을 선택할 수 있습니다. 그렇다고 eBPF를 쓰는 cluster의 모든 패킷이 Netfilter나 connection tracking을 우회하는 것은 아닙니다. 실제 경로는 CNI·kernel·라우팅·기능 설정에 따라 달라집니다. | 구현 | 위치 | 특징 | |---|---|---| | iptables | Netfilter 후크의 순차 규칙 체인 | 규칙 수에 비례해 평가 시간 증가(O(n)), kube-proxy의 오랜 기본 모드 | | IPVS | 커널 네이티브 L4 로드밸런서, netfilter 확장 | 해시 기반 조회(O(1) 근사), Kubernetes 1.35부터 kube-proxy 모드로는 deprecated | | nftables | iptables의 후속 netfilter 프레임워크 | 1.33부터 kube-proxy의 stable 모드, 커널·CNI 호환성 확인 필요 | | eBPF (예: Cilium) | 구성한 XDP·tc·socket hook | kube-proxy의 Service 처리를 대체하는 별도 구현, Netfilter·conntrack 동작은 경로별로 다름 | 구현을 바꾸면 kernel rule과 활성 연결이 남을 수 있습니다. 배포판·CNI의 마이그레이션 절차에 따라 필요한 workload drain을 수행하고, 정리에 필요한 경우 node 재시작을 계획합니다. eBPF 기반 CNI로 kube-proxy를 대체할 때도 두 구현이 같은 Service 트래픽을 두고 충돌하지 않도록 지원되는 전환 순서를 지켜야 합니다. > 📎 IPVS deprecation 일정과 nftables stable 전환은 [Kubernetes 소개](https://www.atomai.click/kubernetes-docs/llms/ko/basics/04-kubernetes-introduction.md), Cilium의 eBPF kube-proxy 대체 구현은 [Cilium eBPF](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/02-ebpf.md), Calico eBPF 데이터 플레인과 전환 절차는 [Calico eBPF](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/06-ebpf-dataplane.md) 참고. ### 컴퓨팅 집약 네트워킹: ENI·EFA·NVLink·광 트랜시버 ENI·EFA·NVLink는 서로 다른 경로를 담당합니다. **ENI**는 하나의 AZ에 있는 EC2 instance에 연결되는 가상 network interface이지만, 일반 IP 트래픽은 라우팅·정책이 허용하면 다른 AZ와 연결된 VPC에 도달할 수 있습니다([VPC CNI](https://www.atomai.click/kubernetes-docs/llms/ko/networking/01-vpc-cni.md) 참고). **EFA**는 호환되는 MPI/NCCL 소프트웨어가 libfabric으로 사용하는 OS-bypass device를 제공합니다. **EFA device 트래픽은 라우팅할 수 없고 VPC/AZ 경계를 넘지 못합니다.** EFA-with-ENA interface의 ENA device를 지나는 일반 IP 트래픽은 여전히 라우팅할 수 있습니다. EFA-only interface에는 ENA device와 IP 주소가 없습니다. **NVLink**는 지원되는 시스템의 GPU를 연결하며, 지원되는 rack-scale NVLink domain도 포함합니다. EFA 대비 고정 배수의 성능 향상을 가정하지 말고 실제 hardware·collective 연산·배치를 측정합니다. **광 트랜시버**는 일반적인 데이터센터 네트워킹 개념입니다. 구리 DAC(Direct Attach Copper)는 짧은 구간에 사용하고, 광 모듈과 광섬유는 다른 거리·대역폭 요구를 지원합니다. QSFP·OSFP는 모듈의 form factor이며 반드시 광 매체라는 의미는 아닙니다. 이 설명만으로 특정 AWS workload의 실제 물리 배선을 알 수는 없습니다. > 📎 NVLink/IMEX 토폴로지 인식 스케줄링과 GPU 파드 배치 예시는 [AI/ML 인프라](https://www.atomai.click/kubernetes-docs/llms/ko/ai-ml/06-ai-infrastructure.md), EFA의 VPC/AZ 경계 제약과 실측은 [Cross-Org VPC 연결](https://www.atomai.click/kubernetes-docs/llms/ko/networking/05-cross-org-vpc-connectivity.md) 참고. ### 차세대 프로토콜의 Kubernetes 함의: HTTP/3·gRPC·QUIC HTTP/3(RFC 9114)와 그 전송 기반인 QUIC(RFC 9000)의 프로토콜 동작 자체는 [네트워크 기초 Part 2](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part2.md)·[Part 3](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part3.md)에서 다룹니다. 여기서는 Kubernetes 트래픽 분산에 실제로 영향을 주는 지점만 짚습니다. - **gRPC와 L4 로드밸런서:** gRPC는 HTTP/2 연결에서 요청을 다중화합니다. L4 로드밸런서는 보통 이미 맺어진 TCP 연결을 선택한 endpoint에 유지하며, 그 endpoint가 프록시라면 추가 라우팅을 수행할 수 있습니다. Pod 추가만으로 기존 연결을 재분배하지는 않습니다. RPC 단위 분산에는 호환되는 L7 프록시나 client-side 정책이 필요합니다. streaming RPC는 하나의 호출이므로 내부 메시지를 각각 분산하지 않습니다. - **Gateway API의 GRPCRoute:** Ingress에는 gRPC 전용 리소스가 없지만 Gateway API는 `GRPCRoute`로 서비스·메서드 단위 라우팅을 표준화합니다. 구현체별 지원 범위(헤더 매칭 개수, 재시도 정책 등)는 컨트롤러 문서를 확인해야 합니다. - **HTTP/3/QUIC의 클러스터 도달 범위:** 클라이언트와 엣지(예: CDN·로드밸런서) 사이의 HTTP/3 지원과, 클러스터 내부·Ingress 백엔드까지의 HTTP/3 지원은 별개입니다. 다수의 Ingress/Gateway 구현체는 여전히 백엔드 연결에 HTTP/1.1 또는 HTTP/2를 사용하며, 엔드투엔드 HTTP/3 지원 여부는 구현체와 버전마다 다르므로 일반화하지 말고 실제 사용 중인 컨트롤러의 문서를 확인해야 합니다. ## 네트워킹 하위 페이지 이 섹션에서는 다음 주제들을 상세히 다룹니다: ### [VPC CNI](https://www.atomai.click/kubernetes-docs/llms/ko/networking/01-vpc-cni.md) 일반 Pod의 VPC 주소와 모드별 IPAM·정책 전제를 다루는 EKS 네트워킹. ### [Cilium 딥다이브](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) eBPF 기반의 고성능 CNI 솔루션. L7 Network Policy, Service Mesh, 관측성(Hubble) 등 고급 기능 제공. ### [Calico 딥다이브](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) 가장 널리 사용되는 CNI 중 하나. 강력한 Network Policy, BGP 지원, 엔터프라이즈 기능. 소개, 아키텍처, 네트워킹 모드, BGP 심화, Network Policy, eBPF, 고급 주제, EKS 통합, 운영 가이드를 다룹니다. ### [VPC Lattice](https://www.atomai.click/kubernetes-docs/llms/ko/networking/02-vpc-lattice.md) AWS의 관리형 애플리케이션 네트워킹 서비스. 크로스 VPC, 크로스 계정 서비스 간 통신. ### [AWS Load Balancer Controller](https://www.atomai.click/kubernetes-docs/llms/ko/networking/03-aws-lb-controller.md) Kubernetes Service와 Ingress를 AWS ELB(ALB/NLB)와 통합. ### [Gateway API](https://www.atomai.click/kubernetes-docs/llms/ko/networking/04-gateway-api.md) 차세대 Kubernetes 인그레스 API. 표준화된 리소스 모델과 역할 기반 구성. ### [Pod 네트워크 실측 벤치마크](https://www.atomai.click/kubernetes-docs/llms/ko/networking/06-pod-network-benchmark.md) 같은 노드·같은 AZ·다른 AZ의 Pod 간 RTT·HTTP 레이턴시·처리량과 DNS `ndots:5` 쿼리 증폭을 EKS에서 직접 측정한 숫자. ## 네트워크 트러블슈팅 ### 일반적인 문제와 해결 방법 #### Pod 간 통신 실패 ```bash NAMESPACE=default POD_NAME=iperf-client # An existing diagnostic Pod with nslookup/curl SERVICE_NAME=my-service kubectl -n "$NAMESPACE" get pods -o wide kubectl -n "$NAMESPACE" exec "$POD_NAME" -- nslookup "$SERVICE_NAME" kubectl -n "$NAMESPACE" exec "$POD_NAME" -- \ curl --connect-timeout 3 --max-time 5 -v "http://$SERVICE_NAME:80/" kubectl -n kube-system logs -l k8s-app=aws-node -c aws-node --tail=100 kubectl -n kube-system logs -l k8s-app=cilium -c cilium-agent --tail=100 ``` 표시한 도구가 있는 기존 Pod에서 진단합니다. 설치된 CNI의 로그만 조회하며 Auto Mode 시스템 서비스는 해당 DaemonSet이 아닙니다. DNS 성공, TCP 도달성, 애플리케이션 HTTP 응답은 서로 다른 검사입니다. ICMP가 차단되거나 추가 권한이 필요할 수 있으므로 ping 실패만으로 TCP Service에 접근할 수 없다고 단정하지 않습니다. #### Service 접근 불가 ```bash NAMESPACE=default SERVICE_NAME=my-service kubectl -n "$NAMESPACE" get service "$SERVICE_NAME" -o yaml kubectl -n "$NAMESPACE" get endpointslices \ -l "kubernetes.io/service-name=$SERVICE_NAME" -o yaml kubectl -n kube-system logs -l k8s-app=kube-proxy --tail=100 ``` 현재 엔드포인트 진단에는 EndpointSlice를 사용합니다. Service 선택자, target port, 엔드포인트 준비 상태, 주소 계열, 적용 정책을 확인합니다. kube-proxy가 실제 Service 전달을 담당할 때만 해당 로그를 확인하고, eBPF 대체 구현이나 Auto Mode는 자체 진단을 사용합니다. #### Network Policy 디버깅 ```bash kubectl get networkpolicies.networking.k8s.io -A kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium-dbg policy get kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium-dbg endpoint list # For a Calico installation using its standard CRD datastore: kubectl get networkpolicies.crd.projectcalico.org -A kubectl get globalnetworkpolicies.crd.projectcalico.org ``` Cilium 명령은 DaemonSet 참조로 선택한 Agent 하나를 조사하므로 장애 시 해당 노드의 Agent를 지정합니다. Calico native API 설치는 다른 API group을 노출할 수 있으니 실제 제공되는 리소스를 확인합니다. Kubernetes, Calico, AWS 확장 정책은 별도 리소스이며 우선순위가 다를 수 있습니다. ### 네트워크 성능 테스트 이 제한된 TCP 실습은 게시자의 고정 Netshoot v0.16 이미지 index를 사용합니다. Linux AMD64·Arm64 이미지를 포함하고 Dockerfile에 `iperf3`가 명시되어 있습니다. TCP 5201이 허용된 테스트 환경에서 Pod를 생성합니다. 설명용 워크로드이며 측정된 CNI 비교 결과가 아닙니다. ```yaml apiVersion: v1 kind: Pod metadata: name: iperf-server namespace: default labels: app: iperf-server spec: restartPolicy: Never automountServiceAccountToken: false nodeSelector: kubernetes.io/os: linux containers: - name: netshoot image: nicolaka/netshoot:v0.16@sha256:b09d9b21381f47a79b3cbcb30da25266dc17186ea00ae65e99fdc51396f48e70 command: - iperf3 - -s workingDir: /tmp resources: requests: cpu: 100m memory: 64Mi limits: cpu: 500m memory: 256Mi securityContext: runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false capabilities: drop: - ALL seccompProfile: type: RuntimeDefault ports: - containerPort: 5201 protocol: TCP --- apiVersion: v1 kind: Pod metadata: name: iperf-client namespace: default labels: app: iperf-client spec: restartPolicy: Never automountServiceAccountToken: false nodeSelector: kubernetes.io/os: linux containers: - name: netshoot image: nicolaka/netshoot:v0.16@sha256:b09d9b21381f47a79b3cbcb30da25266dc17186ea00ae65e99fdc51396f48e70 command: - sleep - '3600' workingDir: /tmp resources: requests: cpu: 100m memory: 64Mi limits: cpu: 500m memory: 256Mi securityContext: runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false capabilities: drop: - ALL seccompProfile: type: RuntimeDefault ``` ```bash kubectl -n default wait --for=condition=Ready pod/iperf-server pod/iperf-client --timeout=120s IPERF_SERVER_IP="$(kubectl -n default get pod iperf-server -o jsonpath='{.status.podIP}')" test -n "$IPERF_SERVER_IP" kubectl -n default exec iperf-client -- iperf3 -c "$IPERF_SERVER_IP" -t 10 -b 10M ``` 클라이언트는 1시간 대기하고 명령은 10초 동안 송신 부하를 10 Mbit/s로 제한합니다. 최대 처리량이 아닌 선택한 경로를 검사합니다. 해석 전에 실제 Pod·노드·AZ 위치, 리소스 제한, 정책을 기록합니다. Windows 노드에는 해당 플랫폼의 도구를 선택합니다. 완료 후 직접 만든 테스트 리소스만 정리합니다. 이 독립 진단 Pod는 연결 검사 용도입니다. EKS 네이티브 네트워크 정책을 시험할 때는 Deployment/Job 관리 Pod와 문서화된 Service·컨테이너 포트 조건을 사용합니다. ## 모범 사례 ### 1. IP 주소 계획 - CIDR 블록을 충분히 크게 설계 - Pod 네트워크와 Service 네트워크 분리 - 향후 확장을 고려한 서브넷 설계 ### 2. Network Policy 적용 먼저 격리된 `networking-demo` 네임스페이스를 생성합니다. 예제는 표준 Kubernetes NetworkPolicy 의미에 따라 그 안의 모든 Pod의 ingress·egress를 격리하므로 필요한 DNS·애플리케이션 흐름에는 명시적 allow가 필요합니다. 지원하는 정책 엔진이 있어야 강제되며, 추가 cluster/admin 정책 API는 우선순위를 바꿀 수 있습니다. 이 매니페스트 하나가 전체 zero-trust 아키텍처는 아닙니다. - 기본 거부 정책 적용 (Zero Trust) - 필요한 트래픽만 명시적으로 허용 - 네임스페이스 간 격리 ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: networking-demo spec: podSelector: {} policyTypes: - Ingress - Egress ``` ### 3. 성능 최적화 - 적절한 CNI 선택 (워크로드에 맞는) - MTU 최적화 - 커널 파라미터 튜닝 ### 4. 보안 강화 - 지원되는 전송 암호화를 선택하고 실제 보호 트래픽 범위를 검증합니다. - 필요한 워크로드·애플리케이션 신원과 mTLS를 구성하고 DNS/IP allowlist와 구분합니다. - 정책, 인증서, 접근 제어 변경을 정기적으로 검토합니다. ### 5. 관측성 확보 - 네트워크 메트릭 수집 - 플로우 로그 활성화 - 분산 추적 구현 ## 다음 단계 1. [VPC CNI](https://www.atomai.click/kubernetes-docs/llms/ko/networking/01-vpc-cni.md) - EKS 기본 CNI 2. [Cilium 딥다이브](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) - eBPF 기반 네트워킹 3. [Calico 딥다이브](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) - 라우팅·정책·데이터 플레인 4. [VPC Lattice](https://www.atomai.click/kubernetes-docs/llms/ko/networking/02-vpc-lattice.md) - AWS 관리형 네트워킹 5. [AWS Load Balancer Controller](https://www.atomai.click/kubernetes-docs/llms/ko/networking/03-aws-lb-controller.md) - ELB 통합 6. [Gateway API](https://www.atomai.click/kubernetes-docs/llms/ko/networking/04-gateway-api.md) - 차세대 인그레스 7. [Cross-Org VPC 연결](https://www.atomai.click/kubernetes-docs/llms/ko/networking/05-cross-org-vpc-connectivity.md) - 서로 다른 AWS Organization 간 VPC 연결 (실측 기반) 8. [Pod 네트워크 실측 벤치마크](https://www.atomai.click/kubernetes-docs/llms/ko/networking/06-pod-network-benchmark.md) - 노드·AZ 경계별 실측 레이턴시와 처리량 --- ## 참고 자료 - [Kubernetes network model](https://kubernetes.io/docs/concepts/services-networking/) - [Kubernetes Services](https://kubernetes.io/docs/concepts/services-networking/service/) - [Container runtime and CNI](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) - [Kubernetes NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) - [CNI specification](https://raw.githubusercontent.com/containernetworking/cni/main/SPEC.md) - [Calico product editions](https://docs.tigera.io/calico/latest/about) - [Calico policy tiers](https://docs.tigera.io/calico/latest/network-policy/policy-tiers/tiered-policy) - [Calico Whisker flow logs](https://docs.tigera.io/calico/latest/observability/view-flow-logs) - [Calico Windows limitations](https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/limitations) - [Flannel 0.28.9 networking and policy](https://raw.githubusercontent.com/flannel-io/flannel/v0.28.9/README.md) - [Flannel backends](https://raw.githubusercontent.com/flannel-io/flannel/v0.28.9/Documentation/backends.md) - [Original Weave repository status](https://api.github.com/repos/weaveworks/weave) - [AWS VPC CNI 1.23.0](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/README.md) - [EKS network policy configuration](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy-configure.html) - [EKS standard and Admin network policies](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html) - [EKS prefix delegation and maxPods](https://docs.aws.amazon.com/eks/latest/userguide/cni-increase-ip-addresses-procedure.html) - [EKS Admin and DNS policy deployment models](https://aws.amazon.com/blogs/containers/enhance-amazon-eks-network-security-posture-with-dns-and-admin-network-policies/) - [EKS Auto Mode networking](https://docs.aws.amazon.com/eks/latest/userguide/auto-networking.html) - [EKS add-on requirements](https://docs.aws.amazon.com/eks/latest/userguide/workloads-add-ons-available-eks.html) - [EKS control plane architecture](https://docs.aws.amazon.com/eks/latest/best-practices/control-plane.html) - [Netshoot v0.16 image metadata](https://hub.docker.com/v2/repositories/nicolaka/netshoot/tags/v0.16) - [Netshoot v0.16 Dockerfile](https://raw.githubusercontent.com/nicolaka/netshoot/v0.16/Dockerfile) - [Tetragon runtime security](https://tetragon.io/docs/overview/) - [AWS LBC 3.5 NLB configuration](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/service/nlb.md) - [AWS LBC 3.5 Ingress configuration](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/ingress/annotations.md) - [Gateway Load Balancer concepts](https://docs.aws.amazon.com/vpc/latest/privatelink/gateway-load-balancers.html) - [GENEVE encapsulation (RFC 8926)](https://www.rfc-editor.org/rfc/rfc8926) - [VPC DNS resolver](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html) - [Route 53 Resolver endpoints and rules](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver.html) - [VPC route table evaluation order](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html) - [Local routes and more-specific subnet routes](https://docs.aws.amazon.com/vpc/latest/userguide/subnet-route-tables.html) - [Static and propagated route priority](https://docs.aws.amazon.com/vpc/latest/userguide/route-tables-priority.html) - [AmazonProvidedDNS addresses and behavior](https://docs.aws.amazon.com/vpc/latest/userguide/AmazonDNS-concepts.html) - [GWLB flow stickiness and failover](https://docs.aws.amazon.com/elasticloadbalancing/latest/gateway/edit-target-group-attributes.html) - [Kubernetes Service virtual IPs and kube-proxy modes](https://kubernetes.io/docs/reference/networking/virtual-ips/) - [CoreDNS Service names and forwarding configuration](https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/) - [PrivateLink resource endpoints](https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-access-resources.html) - [Netfilter/iptables project documentation](https://www.netfilter.org/documentation/index.html) - [EC2 Elastic Fabric Adapter](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html) - [QUIC transport protocol (RFC 9000)](https://www.rfc-editor.org/rfc/rfc9000) - [HTTP/3 (RFC 9114)](https://www.rfc-editor.org/rfc/rfc9114) - [gRPC over HTTP/2 and load balancing](https://grpc.io/blog/grpc-load-balancing/) - [Gateway API GRPCRoute](https://gateway-api.sigs.k8s.io/guides/user-guides/grpc-routing/) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/basics/06-network-fundamentals-part1 ---------------------------------------- # 네트워크 기초 Part 1 — 계층 모델과 링크·라우팅 계층 > **마지막 업데이트**: 2026년 9월 11일 ::: tip 4부작 시리즈입니다 **Part 1: 계층 모델과 링크·라우팅** *(현재 문서)* · [Part 2: 전송 계층과 TLS](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part2.md) · [Part 3: 애플리케이션 프로토콜](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part3.md) · [Part 4: 요청의 여정과 클라우드](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part4.md) ::: 브라우저 요청에는 여러 프로토콜이 협력합니다. 실제 순서는 캐시, 연결 재사용, IP 및 HTTP 버전에 따라 달라지므로 장애 분석에서는 HTTP뿐 아니라 관련 계층도 확인해야 합니다. 이 시리즈에서는 인터넷을 실제로 굴러가게 만드는 네트워크 프로토콜과 메커니즘 25개를 **계층 순서대로 아래에서 위로** 정리합니다. 계층을 아래에서부터 쌓는 이유는 단순합니다. 상위 계층은 하위 계층이 이미 동작한다고 가정하고 설계되어 있기 때문에, 위에서부터 읽으면 "그건 어떻게 되는 건데?"가 계속 남습니다. 각 항목은 **한 줄 정의 → 동작 방식 → 실무에서 걸리는 지점** 순으로 씁니다. --- ## 0. 계층 지도 한 장 | 계층 | 하는 일 | 이 글에서 다루는 프로토콜 | |---|---|---| | 애플리케이션 | 실제 서비스 의미론 | HTTP/3, WebSocket, WebRTC, gRPC, DNS, DoH, DHCP, MQTT, SSH, SMTP | | 보안 | 암호화·인증 (전송 계층에 얹힘) | TLS | | 전송 | 종단 간(end-to-end) 데이터 전달 | TCP, UDP, QUIC | | 인터넷 / 라우팅 | 네트워크 간 경로 결정 | IPv4, IPv6, ICMP, BGP, OSPF, NAT | | 링크 | 같은 물리 구간 내 전달 | Ethernet, Wi-Fi, VLAN, PPP, ARP | 계층 경계가 깔끔하게 지켜지지 않는 항목이 몇 개 있습니다. TLS는 전송과 애플리케이션 사이에 끼어 있고, QUIC은 UDP 위에 얹혀서 전송 계층 역할을 하며, ARP는 IP와 링크 사이를 잇습니다. NAT는 아예 프로토콜이라기보다 기능입니다. 이런 "예외"들이 실무 트러블슈팅의 대부분을 차지합니다. --- ![노트북에서 L2 스위치·공유기를 거쳐 ISP 엣지, BGP 기반 인터넷 코어, OSPF 데이터센터 라우터를 지나 서버까지 이어지는 링크·라우팅 계층 경로와 각 구간의 프로토콜·MTU를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-basics-06-network-fundamentals-part1-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-basics-06-network-fundamentals-part1-0.html) --- ## 1. 링크 계층 — 같은 구간 안에서 옮기기 링크 계층의 관심사는 딱 하나입니다. **바로 옆에 붙어 있는 장비에게 어떻게 비트를 넘기는가.** 목적지가 지구 반대편이든 옆 서버든, 이 계층은 "다음 홉(next hop)"까지만 책임집니다. ### Ethernet **정의:** 유선 로컬 네트워크에서 프레임을 실어 나르는 링크 계층 표준. **동작:** 데이터를 프레임 단위로 감싸고, 앞에 목적지·출처 MAC 주소를 붙입니다. 알려진 유니캐스트는 MAC 테이블에 따라 포트로 전달합니다. 브로드캐스트와 미학습 유니캐스트는 보통 VLAN 안에서 flooding하며 멀티캐스트 처리는 설정에 따라 다릅니다. 초기 이더넷은 충돌 감지(CSMA/CD)에 의존했지만, 현대의 스위치 기반 풀듀플렉스 환경에서는 충돌 자체가 거의 사라졌습니다. **실무 포인트:** Ethernet IP 트래픽에서 MTU 1500은 프레임 내부 IP 패킷 1500바이트를 뜻하며 Ethernet 헤더/FCS는 제외합니다. 점보 MTU는 장비/경로별로 다르고 9001은 EC2 지원 값이지 보편적인 Ethernet 크기가 아닙니다. 클라우드에서 VPN이나 오버레이 네트워크를 얹으면 캡슐화 헤더 때문에 실효 MTU가 줄어들고, MTU 탐색까지 실패하면 생길 수 있는 블랙홀은 "핑은 되는데 큰 응답만 멈춘다"는 형태로 나타납니다. 원인 파악이 유독 오래 걸리는 유형의 장애입니다. **MTU와 MSS의 관계:** MSS는 프레임 전체가 아닌 TCP 데이터 바이트 수를 제한합니다. MTU 1500에서 기본 헤더만 고려하면 IPv4는 1460(1500−20−20), IPv6는 1440(1500−40−20)입니다. 송신자는 실제 패킷에 포함하는 IP/TCP 옵션만큼 데이터 길이를 추가로 줄입니다. TCP는 핸드셰이크에서 MSS를 교환하므로, 터널 구간에서 MTU 문제가 반복될 때는 라우터에서 MSS 클램핑(TCP MSS를 강제로 낮추는 설정)으로 우회하는 방법이 널리 쓰입니다. ### Wi-Fi **정의:** 무선 구간에서 LAN 프레임을 실어 나르는 링크 계층 표준(IEEE 802.11). **동작:** 공기라는 공유 매체를 쓰기 때문에 이더넷과 근본적으로 다릅니다. 송신 중 충돌 감지에 의존하지 않고 CSMA/CA로 매체 접근을 조정합니다. 프레임을 보내기 전에 채널이 비었는지 확인하고, 일반 유니캐스트는 ACK/재시도를 사용하며 브로드캐스트/멀티캐스트 처리는 다릅니다. 즉 링크 계층에 이미 재전송이 들어 있습니다. **실무 포인트:** 링크 계층 재전송과 TCP 재전송이 겹치면서 지연 변동(jitter)이 커집니다. 실시간 통신 품질 문제가 "서버 탓"으로 보고되지만 실제로는 클라이언트의 무선 구간인 경우가 많습니다. 서버 RTT만으로 원인을 특정할 수 없으며 애플리케이션 처리 시간, 클라이언트/AP 재시도, 신호 및 큐 지표를 함께 확인합니다. ### VLAN **정의:** 공유 스위치 인프라를 논리적 L2 네트워크로 나누는 기술(IEEE 802.1Q). **동작:** 802.1Q 태그 프레임은 4바이트 VLAN 태그를 담습니다. 액세스 포트에서는 태그 없는 프레임을 스위치가 해당 VLAN에 연결할 수도 있습니다. 같은 VLAN끼리만 브로드캐스트가 도달하므로, 물리 배선을 바꾸지 않고 네트워크를 분리할 수 있습니다. VLAN 간 통신은 반드시 L3 장비(라우터 또는 L3 스위치)를 거쳐야 합니다. **실무 포인트:** VLAN은 논리적 L2 분리이며 물리적 분리나 암호화를 제공하지 않습니다. 구간 간 허용 트래픽은 라우팅과 방화벽으로 제어합니다. VPC·서브넷·보안 그룹은 서로 다른 클라우드 네트워크 역할을 하며 VLAN의 일대일 대체물이 아닙니다. > 📎 EKS의 VPC 구성은 [EKS 네트워킹 기초](https://www.atomai.click/kubernetes-docs/llms/ko/eks/03-eks-networking-part1.md) 참고. ### PPP **정의:** 두 노드를 직접 연결하는 점대점 링크에서 패킷을 실어 나르는 프로토콜. **동작:** 이더넷과 달리 주소 지정이 필요 없습니다. 링크 양 끝에 노드가 하나씩만 있으니까요. 대신 링크 수립·선택적 인증·상위 프로토콜 협상 절차(LCP/NCP)를 갖추고 있습니다. **실무 포인트:** 다이얼업 시대의 유물처럼 보이지만, PPPoE 형태로 여전히 상당수의 가정용 인터넷 회선에서 살아 있습니다. 그리고 표준 1500바이트 Ethernet payload에서는 일반적인 PPPoE 헤더 6바이트와 PPP protocol 필드 2바이트를 빼 IP에 1492바이트가 남습니다. 더 큰 하위 링크와 협상 지원이 있으면 1500을 유지할 수 있습니다. 앞서 말한 MTU 문제의 대표적 원인입니다. ### ARP **정의:** 같은 링크의 IPv4 다음 홉 주소를 MAC 주소로 확인하는 프로토콜. **동작:** IP 계층은 "10.0.1.5로 보내라"고 지시하지만, 이더넷은 MAC 주소만 이해합니다. 그래서 호스트는 브로드캐스트로 "10.0.1.5의 MAC 주소를 가진 분?"이라고 묻고, 해당 호스트가 응답합니다. 결과는 OS별 상태/시간 제한에 따라 캐시됩니다. 다른 링크의 목적지로 보낼 때는 원격 호스트가 아닌 게이트웨이의 MAC을 확인합니다. **실무 포인트:** ARP는 인증이 없습니다. 아무나 "그 IP는 제 겁니다"라고 응답할 수 있어서 ARP 스푸핑이 성립합니다. 반대로 이 성질을 이용하는 정상 기법도 있습니다. 페일오버 시 새 액티브 노드가 Gratuitous ARP로 이웃의 VIP-to-MAC 정보를 갱신하고 스위치도 프레임의 source MAC 위치를 학습하게 하는 방식이 대표적입니다. VIP 기반 HA 구성에서 전환이 느릴 때는 이 갱신이 지연되는 경우를 의심해볼 수 있습니다. > 📎 Cilium이 L2/라우팅 동작을 eBPF와 어떻게 통합하는지는 [Cilium 네트워킹](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/03-networking.md) 참고. --- ## 2. 인터넷·라우팅 계층 — 네트워크를 건너가기 링크 계층이 "옆집까지"라면, 이 계층은 "지구 반대편까지"를 담당합니다. 핵심 질문은 **어디로 보낼 것인가**입니다. ### IPv4 **정의:** 32비트 주소 체계 기반의 인터넷 계층 프로토콜. **동작:** 각 패킷에 출발지·목적지 IP를 붙이고, 라우터는 라우팅 테이블에서 가장 구체적인(longest prefix match) 경로를 찾아 다음 홉으로 넘깁니다. 배달을 보장하지 않는 최선 노력(best-effort) 방식이며, 순서 보장도 없습니다. 그 보장은 위층(TCP)의 일입니다. **실무 포인트:** IPv4 주소 공간은 약 43억 개이며 가용 주소 부족으로 NAT가 널리 사용되고, 사설 주소 대역(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)이 내부망 표준이 됐습니다. 대규모 조직에서 클라우드 마이그레이션할 때 가장 먼저 부딪히는 문제가 이 사설 대역의 중복입니다. 온프레미스와 VPC CIDR이 겹치면 단순 라우팅으로는 구분할 수 없으므로 주소 재설계·변환·프록시 같은 별도 해결책을 설계해야 합니다. IP 주소 설계는 프로젝트 시작 시점에 확정해야 하는 항목입니다. ### IPv6 **정의:** 128비트 주소 체계의 차세대 인터넷 계층 프로토콜. **동작:** 주소가 128비트로 늘어나 고갈 문제가 사라집니다. 기본 헤더는 고정 40바이트이며 헤더 체크섬이 없고 라우터가 IPv6 패킷을 단편화하지 않습니다. SLAAC를 통해 DHCP 없이도 주소를 자동 설정할 수 있고, ARP는 NDP(Neighbor Discovery Protocol)로 대체됩니다. **실무 포인트:** IPv4와 하위 호환되지 않습니다. 그래서 현실에서는 듀얼 스택으로 운영하며, 이는 방화벽 규칙과 보안 정책을 두 벌 관리해야 한다는 뜻입니다. IPv6 경로에 대한 규칙 누락은 흔한 보안 공백입니다. 글로벌 IPv6 주소만으로 워크로드가 인터넷에서 접근 가능해지는 것은 아닙니다. AWS에서도 라우팅과 보안 그룹/NACL 허용이 필요하며 egress-only internet gateway로 외부의 새 연결을 막고 아웃바운드를 제공할 수 있습니다. **전환 메커니즘:** IPv4와 공존하는 현실적 방법은 세 갈래입니다. **듀얼 스택**(두 프로토콜을 나란히 운영 — 가장 흔하지만 정책 이중화 비용), **터널링**(IPv6 패킷을 IPv4로 감싸 통과), 그리고 **NAT64/DNS64**(IPv6 전용 클라이언트가 IPv4 서버에 접근하도록 변환 — 모바일 통신망이 464XLAT 형태로 대규모로 사용). 쿠버네티스도 듀얼 스택 Service를 지원하므로, 클러스터 CIDR 설계 시 IPv6 대역을 함께 고려할 수 있습니다. ### ICMP **정의:** 네트워크 오류와 상태를 보고하는 제어 프로토콜. **동작:** 제어 정보를 전달하며 Echo payload나 원본 패킷의 일부 데이터도 포함할 수 있습니다. 목적지 도달 불가, TTL 초과, 단편화 필요 등을 알립니다. ping은 Echo Request/Reply를, traceroute는 TTL(IPv6는 Hop Limit)을 1씩 늘려가며 돌아오는 Time Exceeded를 이용합니다. **실무 포인트:** "보안상" ICMP를 전면 차단하는 정책이 흔한데, 이게 앞서 언급한 MTU 블랙홀의 직접적 원인입니다. 전통적인 IPv4 PMTUD는 ICMP Type 3 Code 4, IPv6는 ICMPv6 Packet Too Big Type 2를 사용합니다. 필요한 메시지를 막으면 블랙홀이 생길 수 있지만 PLPMTUD는 ICMP에 의존하지 않고 패킷 크기를 탐색할 수도 있습니다. IP 버전과 정책에 맞는 오류/탐색 트래픽을 허용합니다. > 📎 EKS에서 이 문제가 실제로 나타나는 형태는 [EKS 네트워킹 심화](https://www.atomai.click/kubernetes-docs/llms/ko/eks/03-eks-networking-part3.md) 참고. ### OSPF **정의:** 하나의 자율 시스템 내부에서 최적 경로를 계산하는 링크 상태 라우팅 프로토콜. **동작:** 각 라우터가 자신의 링크 상태를 영역 전체에 전파하고, 같은 영역의 라우터는 수렴 후 일관된 링크 상태 정보를 갖습니다. 그 위에서 다익스트라 알고리즘으로 최단 경로를 계산합니다. 인터페이스 코스트를 구성하며(보통 대역폭을 기준으로 산정), 규모 확장을 위해 영역(Area)으로 나눕니다. **실무 포인트:** 내부망(IGP)용입니다. 수렴이 빠르고 자동으로 최적 경로를 찾지만, 라우터마다 자신이 속한 영역의 링크 상태 정보를 유지하므로 대규모에서는 영역 설계가 성능을 좌우합니다. ### BGP **정의:** 자율 시스템(AS) 간에 경로 정보를 교환하는 경로 벡터 라우팅 프로토콜. **동작:** OSPF와 목적이 다릅니다. "가장 빠른 길"이 아니라 "정책적으로 원하는 길"을 고릅니다. 각 AS는 자신이 도달 가능한 프리픽스와 AS 경로를 이웃에게 광고하고, 수신 측은 AS_PATH 길이, Local Preference, MED 등의 속성으로 우선순위를 결정합니다. 인터넷 전체의 라우팅이 이 위에서 성립합니다. **실무 포인트:** BGP는 광고를 기본적으로 신뢰합니다. 그래서 잘못된 프리픽스 광고 하나가 대륙 단위 장애로 번지는 사고가 반복적으로 발생했습니다. RPKI origin validation은 프리픽스 origin AS 권한을 검증하지만 전체 AS 경로나 모든 경로 유출을 검증하는 것은 아닙니다. 클라우드 관점에서는 Direct Connect는 BGP를 사용하며 Site-to-Site VPN은 BGP 또는 지원되는 정적 라우팅을 사용하므로, AS 번호와 광고 프리픽스 설계, 그리고 이중화 시 경로 우선순위 조정(AS_PATH prepending 등)이 실제 설계 항목으로 들어옵니다. > 📎 Calico가 BGP를 클러스터 내부에서 어떻게 쓰는지는 [Calico BGP 심화](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md) 참고. ### NAT **정의:** IP 주소를 변환하고 NAPT/PAT에서는 전송 포트도 변환하는 네트워크 기능. **동작:** 흔한 용도는 여러 사설 호스트가 PAT/NAPT로 공인 주소 하나를 공유하는 것입니다. 사설 주소 간 변환도 가능하므로 항상 공인 인터넷 주소 공유만을 뜻하지는 않습니다. 변환 테이블에 세션별 매핑을 유지해 응답 패킷을 올바른 내부 호스트로 되돌립니다. **실무 포인트:** 계층 모델을 위반하는 대표적 존재입니다. L3 장비인데 L4 포트를 건드리고, 종단 간 연결성이라는 인터넷의 원래 전제를 깨뜨립니다. 그 결과 P2P 통신이 어려워지고, STUN/TURN 같은 우회 기법이 필요해집니다(뒤의 WebRTC 참고). 클라우드에서는 NAT Gateway의 포트 고갈과 데이터 처리 비용이 실무 이슈입니다. 아웃바운드 트래픽이 많은 워크로드라면 지원되는 AWS 서비스의 VPC 엔드포인트로 NAT 처리를 줄일 수 있지만 엔드포인트 시간/데이터 요금과 트래픽 경로를 비교해야 합니다. --- **다음:** [Part 2: 전송 계층과 TLS](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part2.md) ## 검증 참고 자료 - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/network_mtu.html - https://www.rfc-editor.org/rfc/rfc894 - https://www.rfc-editor.org/rfc/rfc6691 - https://www.rfc-editor.org/rfc/rfc4638 - https://www.rfc-editor.org/rfc/rfc5227 - https://www.rfc-editor.org/rfc/rfc792 - https://www.rfc-editor.org/rfc/rfc8899 - https://www.rfc-editor.org/rfc/rfc2328 - https://www.rfc-editor.org/rfc/rfc6811 - https://docs.kernel.org/networking/bridge.html - https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html - https://docs.aws.amazon.com/vpc/latest/userguide/egress-only-internet-gateway.html - https://docs.aws.amazon.com/vpn/latest/s2svpn/VPNRoutingTypes.html - https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-scenarios.html - https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-pricing.html ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/basics/06-network-fundamentals-part2 ---------------------------------------- # 네트워크 기초 Part 2 — 전송 계층과 TLS > **마지막 업데이트**: 2026년 9월 11일 ::: tip 4부작 시리즈입니다 [Part 1: 계층 모델과 링크·라우팅](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part1.md) · **Part 2: 전송 계층과 TLS** *(현재 문서)* · [Part 3: 애플리케이션 프로토콜](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part3.md) · [Part 4: 요청의 여정과 클라우드](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part4.md) ::: Part 1이 패킷을 목적지 호스트까지 보냈다면, 이 파트는 신뢰성 있는 스트림(TCP·QUIC)과 UDP 데이터그램을 비교하고 TLS의 통신 보호를 설명합니다. UDP 자체에는 신뢰성이나 TLS가 내장되어 있지 않습니다. 애플리케이션은 DTLS 같은 적절한 보안 프로토콜을 선택하거나 TLS 1.3을 통합한 QUIC 같은 전송 프로토콜을 사용합니다. 먼저 이 파트의 핵심을 그림 하나로 요약하면 이렇습니다. ![일반적인 새 연결에서 TCP와 TLS 1.3 전체 핸드셰이크는 첫 요청까지 약 2 RTT, QUIC은 약 1 RTT가 걸린다. 조건을 충족하는 재개에서는 핸드셰이크 완료 전에 0-RTT 조기 데이터를 전송할 수 있다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-basics-06-network-fundamentals-part2-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-basics-06-network-fundamentals-part2-0.html) --- ## 3. 전송 계층 — 종단 간 전달 여기서부터는 "네트워크"가 아니라 "프로세스"가 대화 상대입니다. 포트 번호가 등장하는 이유입니다. ### TCP **정의:** 신뢰성 있는 순차적 바이트 스트림을 제공하는 연결 지향 전송 프로토콜. **동작:** 3-way 핸드셰이크(SYN → SYN+ACK → ACK)로 연결을 수립합니다. 시퀀스 번호로 순서를 보장하고, ACK와 재전송으로 손실을 복구하며, 슬라이딩 윈도우로 흐름을 제어하고, 혼잡 제어 알고리즘으로 네트워크 부하에 적응합니다. 애플리케이션에게는 "빈틈없이 이어지는 바이트 흐름"이라는 깔끔한 추상화를 제공합니다. **실무 포인트:** 순서대로 전달하기 때문에 **HOL(Head-of-Line) 블로킹**이 발생합니다. TCP 세그먼트가 누락되면 빈 구간 뒤의 바이트를 전달할 수 없습니다. HTTP/2에서는 같은 연결의 여러 스트림 전달이 지연될 수 있지만, 이미 전달된 데이터와 독립적인 애플리케이션 작업까지 모두 멈추는 것은 아닙니다. QUIC은 이처럼 스트림 간에 공유하는 전송 계층의 순서 의존성을 제거합니다. 최적화를 사용하지 않는 일반적인 새 연결은 TCP 수립에 약 1 RTT, 이어서 TLS 1.3 전체 핸드셰이크에 1 RTT(TLS 1.2는 보통 2 RTT)가 필요합니다. 연결 재사용, 세션 재개, 조기 데이터, TCP Fast Open에 따라 달라지고 재시도는 지연을 추가할 수 있습니다. 실제 워크로드를 기준으로 커넥션 풀을 평가해야 합니다. **혼잡 제어의 계보:** 혼잡 제어는 대역폭·지연·버퍼·애플리케이션 동작과 함께 처리량에 영향을 줍니다. 고전 **Reno**는 손실 시 혼잡 윈도를 줄입니다. 리눅스에서 흔히 기본값으로 사용하는 **CUBIC**은 3차 함수 기반 윈도 증가로 고대역폭 경로의 확장성을 개선합니다. **BBR**은 병목 대역폭과 전파 RTT를 모델링해 전송을 조절하며, 손실·ECN을 사용하는 방식은 구현과 버전에 따라 다릅니다. 장거리·모바일 경로에서 항상 더 높은 처리량을 보장하지는 않습니다. `sysctl net.ipv4.tcp_congestion_control`은 설정된 기본값을 **조회**하는 명령입니다. 기본값 변경은 새 연결에 적용되며 별도의 설정과 측정이 필요합니다. **TIME_WAIT와 포트 고갈:** 정상적인 순차 종료에서는 일반적으로 능동 종료 측이 TIME_WAIT에 들어가고, 동시 종료에서는 양쪽 모두 들어갈 수 있습니다. 짧은 연결을 대량 생성하면 구현과 목적지 패턴에 따라 연결 튜플·임시 포트·NAT 매핑 한도가 부족해질 수 있습니다. TIME_WAIT 항목 하나가 모든 원격 목적지에 대해 해당 포트를 항상 독점하는 것은 아닙니다. 실제 고갈 지점을 확인하고 커널 설정 변경 전에 연결 재사용을 검토하세요. TIME_WAIT는 이전 연결의 지연 패킷으로부터 새 연결을 보호하는 역할도 합니다. ### UDP **정의:** 연결 설정 없이 데이터그램을 보내는 최소 기능 전송 프로토콜. **동작:** 헤더 8바이트에 출발지 포트, 목적지 포트, 길이, 체크섬만 있습니다. 핸드셰이크 없음, 재전송 없음, 순서 보장 없음, 혼잡 제어 없음. "IP에 포트 번호만 붙인 것"에 가깝습니다. **실무 포인트:** 실시간 애플리케이션은 오래된 데이터를 재전송하기보다 제때 전달하는 것을 우선할 수 있고, DNS도 작은 교환에 UDP를 흔히 사용합니다. 필요한 신뢰성과 적절한 혼잡 제어는 애플리케이션이 구현하거나 QUIC처럼 이를 제공하는 프로토콜을 사용해야 합니다. 주의할 점은 상태가 없어서 스푸핑과 증폭 공격에 쓰이기 쉽다는 것입니다. UDP 기반 서비스를 외부에 노출할 때는 응답 크기 제한과 요청량 제어를 함께 고려해야 합니다. ### QUIC **정의:** UDP 위에 구현된, 보안이 내장된 다중화 전송 프로토콜. **동작:** TCP+TLS가 하던 일을 UDP 위에서 처음부터 다시 설계했습니다. 핵심 특징 네 가지입니다. 1. **스트림별 독립 전달** — 각 스트림에 별도의 바이트 순서가 있어 한 스트림의 손실이 다른 스트림의 도착한 데이터 전달을 반드시 막지는 않습니다. 패킷 복구와 혼잡 제어는 여전히 연결·경로에서 공유합니다. 각 스트림의 누락 바이트, 애플리케이션 의존성, HTTP/3 QPACK 의존성으로 인한 블로킹도 남습니다. 2. **암호화 내장** — QUIC은 TLS 1.3 핸드셰이크를 통합하고 TLS 레코드 대신 자체 패킷 보호를 사용합니다. 일반적인 전체 핸드셰이크는 약 1 RTT입니다. 조건을 충족하고 서버가 수락한 재개에서는 **0-RTT 조기 데이터**를 보낼 수 있지만 핸드셰이크 완료는 그 이후입니다. Retry나 추가 핸드셰이크 교환은 지연을 늘릴 수 있습니다. 3. **연결 ID** — 경로 검증과 양 끝점의 지원을 통해 주소 변경 후 연결을 이어갈 수 있습니다. 마이그레이션 제한이나 경로 단절이 있으면 Wi-Fi에서 셀룰러로 전환할 때도 연결이 끊길 수 있으므로 무중단을 보장하지 않습니다. 4. **구현의 유연성** — 사용자 공간 구현이 흔하므로 애플리케이션·라이브러리와 함께 전송 계층 개선을 배포할 수 있습니다. 사용자 공간에서 구현해야 한다는 프로토콜 요구사항은 아닙니다. **실무 포인트:** HTTP/3은 보통 UDP 443을 사용합니다. 이 경로가 차단되면 HTTP 클라이언트가 서버 지원 여부에 따라 TCP 기반 HTTP/2 또는 HTTP/1.1을 시도할 수 있습니다. QUIC 자체가 TCP로 바뀌는 것은 아닙니다. 성능 차이를 QUIC 때문이라고 판단하기 전에 경로 도달성·협상된 프로토콜·구현과 오프로드 동작을 확인하세요. CPU 비용은 구현과 워크로드에 따라 달라집니다. 한 가지 보안 주의점: **0-RTT 데이터는 재생(replay)될 수 있습니다.** 조기 데이터 교환을 재생하면 애플리케이션이 요청을 여러 번 처리할 수 있으며, 전송 계층의 패킷 중복 제거만으로 애플리케이션 재생 방지가 보장되지는 않습니다. 애플리케이션이 재생되어도 안전하다고 명시적으로 평가한 작업만 허용하세요. GET이라는 이름이나 멱등성 주장만으로 충분하지 않습니다. 서버는 조기 데이터를 거부할 수 있고, HTTP 서버는 `425 Too Early`로 응답해 클라이언트가 핸드셰이크 후 재시도하게 할 수 있습니다. 클라이언트·CDN·원본 서버에 걸쳐 정책을 맞추어야 합니다. --- ## 4. 보안 — TLS ### TLS **정의:** 전송 구간의 기밀성, 무결성, 인증을 제공하는 프로토콜. **동작:** 핸드셰이크에서 암호 매개변수를 협상하고 키를 설정합니다. 인증서 기반 핸드셰이크는 인증서와 키 소유 증명을 통해 서버를 인증하지만, PSK 기반 핸드셰이크는 이전에 설정했거나 외부에서 제공한 키로 인증할 수도 있습니다. TLS 레코드는 애플리케이션 데이터를 보호합니다. TLS 1.3은 인증된 암호화(AEAD)로 기밀성과 무결성을 제공합니다. 일반적인 TLS 1.3 전체 핸드셰이크는 약 1 RTT이며, 재개 시에는 추가 조건하에서 조기 데이터를 선택적으로 사용할 수 있습니다. 정적 RSA 키 교환과 구형 암호 스위트는 제거됐지만 RSA 인증서 서명은 계속 지원합니다. 임시 (EC)DHE 키 교환은 PSK와 결합할 때도 전방 비밀성을 제공합니다. **PSK 단독 키 교환과 0-RTT 데이터에는 동일한 전방 비밀성 보장이 없습니다.** **실무 포인트:** 세 가지가 반복적으로 문제가 됩니다. - **인증서 만료** — 갱신을 자동화하고 만료 시점과 실제 인증서 배포 성공 여부를 별도로 모니터링하세요. - **SNI 노출** — TLS 1.3만으로는 ClientHello의 SNI가 숨겨지지 않습니다. 양 끝점에서 지원·설정한 ECH(Encrypted Client Hello, RFC 9849)는 내부 ClientHello를 보호할 수 있습니다. QUIC Initial 패킷의 키는 공개 정보로 유도할 수 있어 Initial 암호화만으로 SNI를 숨기지 못합니다. ECH도 목적지 IP나 모든 트래픽 메타데이터까지 숨기지는 않습니다. - **종료 지점 설계** — 클라이언트→로드밸런서, 로드밸런서→애플리케이션, 서비스 간 연결을 각각 명시하세요. TLS 종료가 다음 구간을 자동으로 암호화하지는 않습니다. 필요한 구간에는 TLS를 사용하고, 양쪽을 인증서로 인증해야 한다면 mTLS를 사용합니다. 서비스 메시로 자동화할 수 있지만 모든 설계에 필수인 것은 아닙니다. **인증서 체인과 OCSP 스테이플링:** 일반적인 X.509 검증은 말단 인증서에서 필요한 중간 인증서를 거쳐 설정된 신뢰 앵커까지 경로를 구성합니다. 서버는 필요한 중간 인증서를 보내야 하고, 루트는 보통 클라이언트가 이미 신뢰합니다. 중간 인증서를 빠뜨리면 클라이언트에 따라 실패할 수 있지만 모든 유효한 체인에 중간 인증서가 있는 것은 아닙니다. 폐기 확인은 발급기관과 클라이언트에 따라 다릅니다. OCSP를 지원하면 서버가 서명된 상태 응답을 첨부하는 스테이플링으로 클라이언트의 직접 조회를 줄일 수 있습니다. 보편적인 필수 방식은 아닙니다. Let’s Encrypt는 2025년 8월 OCSP 서비스를 종료하고 CRL을 사용합니다. 실제 CA와 클라이언트에 맞춰 인증서·폐기 확인을 설정하세요. > 📎 Istio가 이를 자동화하는 방식은 [Istio mTLS](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/security/01-mtls.md) 참고. **공식 참고 자료**: [TLS 1.3](https://www.rfc-editor.org/rfc/rfc8446.html), [QUIC transport](https://www.rfc-editor.org/rfc/rfc9000.html), [QUIC/TLS](https://www.rfc-editor.org/rfc/rfc9001.html), [HTTP early data](https://www.rfc-editor.org/rfc/rfc8470.html), [ECH](https://www.rfc-editor.org/rfc/rfc9849.html), [Linux TCP settings](https://docs.kernel.org/networking/ip-sysctl.html), [Let’s Encrypt OCSP retirement](https://letsencrypt.org/2025/08/06/ocsp-service-has-reached-end-of-life/). --- **다음:** [Part 3: 애플리케이션 프로토콜](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part3.md) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/basics/06-network-fundamentals-part3 ---------------------------------------- # 네트워크 기초 Part 3 — 애플리케이션 프로토콜 10종 > **마지막 업데이트**: 2026년 9월 11일 ::: tip 4부작 시리즈입니다 [Part 1: 계층 모델과 링크·라우팅](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part1.md) · [Part 2: 전송 계층과 TLS](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part2.md) · **Part 3: 애플리케이션 프로토콜** *(현재 문서)* · [Part 4: 요청의 여정과 클라우드](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part4.md) ::: 전송 프로토콜이 제공하는 스트림·데이터그램 위에서 애플리케이션 프로토콜이 실제 서비스를 만듭니다. 이 파트는 이름 해석(DNS·DoH), 부트스트랩(DHCP), 운영 접속(SSH), 메일(SMTP), HTTP/3·WebSocket·WebRTC·gRPC·MQTT를 다룹니다. --- ## 5. 애플리케이션 계층 — 실제 서비스 ### DNS **정의:** 도메인 이름을 IP 주소 등의 레코드로 변환하는 분산 디렉터리 시스템. **동작:** 계층적 위임 구조입니다. 캐시에 답이 없으면 재귀 리졸버가 루트→TLD→권한 네임서버의 위임을 따라가거나 다른 리졸버에 전달합니다. 캐시가 있으면 일부 또는 전체 조회를 생략할 수 있습니다. A/AAAA는 주소, CNAME은 별칭, MX는 메일 서버, TXT는 여러 프로토콜이 사용하는 문자열을 담습니다. **실무 포인트:** DNS는 분산 시스템이지만 특정 리졸버·공급자·설정이 공통 의존점이 될 수 있습니다. DNS 페일오버는 장애 감지, 레코드 갱신, 이미 캐시된 응답의 TTL, 애플리케이션 캐시, 기존 연결을 함께 고려해야 합니다. 지금 TTL을 줄여도 이전 응답의 캐시 수명은 단축되지 않습니다. 일부 리졸버는 정의된 장애 조건에서 만료된 응답을 제공할 수도 있습니다(RFC 8767). 각 단계를 측정하세요. 짧은 TTL만으로 전환 시간을 보장하지 못합니다. 로드밸런서와 애니캐스트를 보완적으로 사용할 수 있지만 각각의 상태 감지·수렴 시간도 필요합니다. **자주 쓰는 레코드 타입 한눈에:** | 타입 | 용도 | 실무 메모 | |---|---|---| | A / AAAA | 도메인 → IPv4 / IPv6 | 가장 기본 | | CNAME | 별칭 → 정식 이름 | apex의 SOA/NS와 공존 불가; 공급자별 ALIAS/ANAME 또는 Route 53 Alias는 지원 대상에 apex 매핑 제공 | | MX | 메일 수신 서버 | 우선순위 숫자가 낮을수록 먼저 | | TXT | 임의 문자열 | SPF/DKIM/DMARC, 도메인 소유 검증 | | NS | 위임 네임서버 | 하위 존 위임 | | SRV | 서비스 위치(호스트+포트) | 일부 프로토콜의 디스커버리 | | CAA | 인증서 발급 허용 CA 제한 | CA의 정책 준수가 필요하며 모든 잘못된 발급을 자체 차단하지는 않음 | **DNSSEC과 DoH는 다른 문제를 풉니다.** DNSSEC은 검증된 신뢰 체인으로 서명된 DNS 데이터의 출처와 무결성을 인증하며 질의를 암호화하지 않습니다. DoH는 HTTPS로 선택한 리졸버를 인증하고 클라이언트–리졸버 구간의 기밀성·무결성을 보호합니다. 악의적이거나 잘못 설정된 리졸버의 응답이 권한 데이터와 일치함을 자체 증명하지는 않습니다. 둘은 함께 사용할 수 있습니다. ![스텁 리졸버의 질의가 재귀 리졸버를 거쳐 루트, TLD, 권한 네임서버로 위임을 따라 내려가고 결과가 TTL 동안 캐시되는 DNS 재귀 해석 흐름을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-basics-06-network-fundamentals-part3-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-basics-06-network-fundamentals-part3-0.html) ### DoH **정의:** DNS 질의를 HTTPS로 감싸 전송하는 방식. **동작:** 전통적인 DNS는 일반적으로 평문 UDP와 **TCP** 53을 사용합니다. DoH는 DNS 메시지를 HTTPS로 전송해 해당 구간의 수동 도청과 변조를 방지합니다. 리졸버 주소와 트래픽 메타데이터로 DoH 사용을 식별할 가능성은 남고, 선택한 리졸버는 질의 내용을 볼 수 있습니다. **실무 포인트:** 별도로 선택한 공개 DoH 리졸버는 조직 리졸버의 필터링·로깅을 우회하거나 사설 이름 해석에 실패할 수 있습니다. DoH 자체가 정책을 무력화하지는 않습니다. 관리형 DoH 리졸버도 로깅·필터링을 적용할 수 있고 브라우저·OS 정책으로 승인된 리졸버를 선택할 수 있습니다. 암호화 해제를 항상 전제하지 말고 분할 DNS와 단말 정책을 검증하세요. ### DHCP **정의:** 호스트에게 IP 주소와 네트워크 설정을 자동으로 할당하는 프로토콜. **동작:** 일반적인 초기 **DHCPv4** 교환은 DORA, 즉 Discover→Offer→Request→Acknowledge입니다. 로컬 브로드캐스트나 DHCP 릴레이를 통해 서버에 도달합니다. 임대에는 IPv4 주소·서브넷 마스크·게이트웨이·DNS 설정이 포함될 수 있고 갱신은 더 짧은 교환으로 이루어질 수 있습니다. DHCPv6는 다른 메시지를 사용하며, IPv6 기본 라우터 정보는 보통 Router Advertisement로 받습니다. SLAAC도 IPv6 주소 설정 방법입니다. **실무 포인트:** 클라우드에서는 대부분 추상화되어 보이지 않지만, VPC의 DHCP 옵션 세트로 DNS 서버와 도메인 네임을 지정하는 지점에서 다시 만납니다. 온프레미스 DNS를 쓰는 하이브리드 구성에서 이름 해석이 안 될 때 확인해야 하는 설정입니다. ### SSH **정의:** 암호화된 원격 셸 접속과 터널링을 제공하는 프로토콜. **동작:** 서버 호스트 키로 서버를 인증하고, 키 교환으로 세션 키를 만든 뒤 사용자를 인증합니다(공개키 또는 비밀번호). 이후 모든 트래픽이 암호화됩니다. 원격 셸 외에 포트 포워딩, SFTP, 에이전트 포워딩까지 지원합니다. **실무 포인트:** 정책에 맞춰 포워딩을 제한하고 서버 호스트 키를 검증하세요. 침해된 호스트가 전달된 에이전트 소켓에 접근하면 에이전트에 서명·인증을 요청할 수 있지만, 보통 개인키 자체가 그 호스트로 복사되는 것은 아닙니다. 에이전트 포워딩이 필요 없다면 점프 호스트(`ProxyJump`)를 검토하세요. 원시 키에는 자체 만료가 없지만 OpenSSH 인증서 유효기간이나 `authorized_keys`의 만료 제한을 사용할 수 있습니다. 퇴사자의 접근을 제거하고 자격 증명을 교체·폐기해야 합니다. AWS Systems Manager Session Manager는 관리형 노드·IAM 권한·서비스 연결을 설정하면 인바운드 SSH 포트나 SSH 키 배포 없이 셸 접속을 제공할 수 있습니다. CloudTrail은 API 활동을 기록하며 셸 내용을 CloudWatch Logs/S3에 기록하려면 별도 설정이 필요합니다. **Session Manager의 SSH·포트 포워딩 세션에는 세션 내용 로깅을 사용할 수 없습니다.** IAM 기반 접속이라는 이유만으로 모든 명령이 기록되지는 않습니다. ### SMTP **정의:** 메일 서버 간에 메시지를 전달하는 프로토콜. **동작:** 클라이언트는 제출 서버에 메일을 보내고, SMTP 서버는 보통 MX 조회로 경로를 정해 메시지를 릴레이하고 수신합니다. IMAP·POP3는 사용자가 이미 사서함에 저장된 메시지에 접근·조회하는 프로토콜이며 SMTP 서버의 수신 역할을 대체하지 않습니다. **실무 포인트:** SMTP 인증과 TLS는 제출·전송을 보호하지만 표시되는 발신 도메인을 자체 증명하지는 않습니다. 다음 세 가지 도메인 인증 체계가 상호 보완적으로 사용됩니다. - **SPF** — 봉투 MAIL FROM 또는 HELO 식별자의 발신 호스트를 허용합니다. 표시되는 From 헤더와 자동으로 일치하는 것은 아닙니다. - **DKIM** — 서명 도메인의 DNS 키로 서명 대상 메시지 내용의 서명을 검증합니다. 서명 도메인은 표시되는 From 도메인과 다를 수 있습니다. - **DMARC** — 표시되는 From 도메인과 통과한 SPF **또는** DKIM 식별자의 정렬(alignment)을 요구하고 처리·리포팅 정책을 게시합니다. 필요에 맞게 SPF·DKIM·DMARC를 함께 설정하고 보고서와 전달·메일링리스트 동작을 확인하세요. DMARC는 정렬된 인증 방식 하나가 통과해도 성공할 수 있습니다. 이 통제들이 전달 성공을 보장하거나 표시 이름·유사 도메인을 이용한 사칭까지 모두 막지는 않으며 수신자도 자체 정책을 적용합니다. ### HTTP/3 **정의:** QUIC 위에서 동작하는 HTTP의 세 번째 메이저 버전. **동작:** HTTP 의미론은 버전 간에 공유하지만 HTTP/3은 QUIC 스트림과 자체 프레이밍·매핑을 사용합니다. TCP의 스트림 간 순서 의존성을 제거하지만 스트림 내부 손실, QPACK 의존성, 공유 혼잡 제어에 따른 지연은 남습니다. 일반적인 전체 핸드셰이크는 약 1 RTT이고 지원되는 마이그레이션으로 주소 변경 후 연결을 이어갈 수 있습니다. 독립적인 스트림 전달에 대응하는 QPACK이 HPACK을 대체합니다. **실무 포인트:** 클라이언트는 `Alt-Svc`, 사전 정보, 지원 프로토콜을 알리는 HTTPS DNS 레코드로 HTTP/3을 발견할 수 있습니다. `Alt-Svc`는 앞선 TCP 연결에서 배울 수 있고, HTTPS 레코드를 지원하는 클라이언트는 그 교환 전에 HTTP/3을 발견할 수 있습니다. 어느 방식도 도달성이나 일정한 지연 절감을 보장하지 않습니다. 독립 전달과 통합 핸드셰이크는 손실이나 지연이 큰 경로에서 도움이 될 수 있습니다. 실제 지연·처리량·CPU 비용은 구현·오프로드·워크로드·네트워크 조건에 따라 다릅니다. 항상 유리하거나 불리하다고 가정하지 말고 대표적인 모바일·데이터센터 트래픽을 측정하세요. **세 세대 비교로 보는 진화 방향:** | | HTTP/1.1 | HTTP/2 | HTTP/3 | |---|---|---|---| | 전송 | TCP | TCP | QUIC (UDP) | | 연결당 요청 | 순차, 또는 응답 순서를 지키는 파이프라이닝 | 다중화 | 다중화 | | HOL 블로킹 | 응답 순서와 TCP 전달 | 스트림 간 TCP 순서 의존성 | TCP 스트림 간 순서 의존성 제거; 다른 블로킹은 남음 | | 헤더 압축 | 없음 | HPACK | QPACK | | 암호화 | 선택(HTTPS) | HTTPS는 TLS 사용; 평문 HTTP/2도 존재 | QUIC에 TLS 1.3 통합 | 다중화는 순서 의존성이 발생하는 위치를 바꿉니다. HTTP/3은 블로킹 원인 하나를 줄이지만 스케줄링·흐름 제어·애플리케이션 의존성까지 모두 제거하지는 않습니다. ### WebSocket **정의:** 하나의 연결에서 양방향 메시지를 주고받는 애플리케이션 프로토콜. **동작:** HTTP/1.1 핸드셰이크는 `Upgrade`와 성공 응답 `101`을 사용합니다. HTTP/2·HTTP/3은 지원되는 경우 Extended CONNECT를 사용합니다(RFC 8441·9220). 연결 후 양쪽은 반복적인 HTTP 폴링 없이 WebSocket 메시지를 전송할 수 있습니다. **실무 포인트:** 장기 연결을 고려해 해당 유휴 타임아웃보다 짧은 하트비트, 배포 시 점진적 연결 종료, 지터를 넣은 재접속 백오프를 설계하세요. 소켓은 연결을 소유한 인스턴스에 남습니다. 공유 애플리케이션 상태나 Redis Pub/Sub 같은 메시징은 인스턴스 간 이벤트를 전달할 수 있지만 살아 있는 소켓을 이전하거나 자체적으로 영속 전달을 제공하지는 않습니다. 협상된 HTTP 버전의 핸드셰이크와 프록시 지원을 확인하세요. ### WebRTC **정의:** 브라우저·미디어 서버 등 호환되는 끝점 사이에서 실시간 미디어와 데이터를 교환하는 API와 프로토콜. **동작:** NAT는 직접 도달을 어렵게 할 수 있지만 양쪽이 NAT 뒤에 있어도 직접 연결에 성공할 수 있습니다. ICE는 호스트, STUN으로 알아낸 서버 반사(server-reflexive), TURN 릴레이 후보를 교환하고 검사합니다. 애플리케이션 시그널링이 세션 설명과 후보를 전달하며 실제 경로는 연결 검사와 정책에 따라 선택됩니다. 미디어는 SRTP를 사용하며 보통 DTLS-SRTP로 키를 설정합니다. 데이터 채널은 SCTP over DTLS를 사용합니다. **실무 포인트:** TURN 사용량은 대역폭·인프라 비용에 영향을 주며 직접 미디어 경로에서도 시그널링·STUN 등 서비스 비용은 남습니다. NAT 매핑·필터링과 방화벽 동작이 연결에 영향을 주므로 “대칭형 NAT”라는 분류만으로 중계가 항상 필수라고 단정할 수 없습니다. TURN 폴백 비용을 계획하고 실제 망을 시험하세요. 다자간 통화에서는 서버 대역폭·연산을 사용해 완전 P2P 메시보다 클라이언트 업로드를 줄이는 SFU가 흔한 선택입니다. ### gRPC **정의:** 표준 네이티브 전송에 HTTP/2를 사용하며 보통 Protocol Buffers 서비스·메시지 스키마를 사용하는 RPC 프레임워크. **동작:** Protocol Buffers 정의로 클라이언트·서버 코드를 생성할 수 있고 단일 요청·응답, 서버 스트리밍, 클라이언트 스트리밍, 양방향 스트리밍을 지원합니다. 바이너리 인코딩은 간결할 수 있지만 JSON 대비 크기·속도는 데이터·구현·압축에 따라 달라지며 프로토콜 보장이 아닙니다. **실무 포인트:** 네이티브 gRPC는 여러 서비스 API에 적합합니다. 브라우저 API가 네이티브 gRPC의 모든 요구 기능을 노출하지 않으므로 브라우저는 보통 gRPC-Web과 호환 서버 또는 변환 프록시를 사용합니다. 스트리밍 지원 범위는 구현에 따라 다릅니다. 스키마를 이해하는 도구로 내용을 확인하고 디버깅하세요. gRPC 채널은 **0개 이상의 HTTP/2 연결**을 사용할 수 있고 많은 RPC가 장기 연결을 공유할 수 있습니다. L4 분산은 연결별 백엔드를 선택하므로 연결 풀이 작으면 RPC 트래픽이 집중될 수 있으며 RPC별 분산을 보장하지 않습니다. 적절한 클라이언트 정책이나 gRPC를 이해하는 L7 프록시를 검토하세요. 서비스 메시가 그 프록시를 제공할 수도 있습니다. 이미 수립된 스트림은 선택된 백엔드에 남습니다. 스키마 변경 시 삭제한 Protocol Buffers 필드 번호·이름을 예약하고 번호를 재사용하지 마세요. > 📎 Istio에서의 gRPC 처리는 [Istio gRPC 고급](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/advanced/05-grpc.md) 참고. ### MQTT **정의:** 발행-구독 모델의 경량 메시징 프로토콜. **동작:** 클라이언트가 브로커에 연결해 토픽으로 발행·구독합니다. 고정 헤더는 최소 2바이트지만 실제 패킷에는 가변 헤더·속성·페이로드가 추가될 수 있습니다. QoS 0/1/2는 **해당 송신자–수신자 구간의 프로토콜 전달**에서 최대 1회·최소 1회·정확히 1회를 제공합니다. 발행자→브로커와 브로커→구독자 전달은 별도입니다. 설정된 Will은 특정 연결 종료 조건에서 발행될 수 있으며 MQTT 5의 Will Delay와 재접속 동작에 따라 시점이 달라집니다. **실무 포인트:** 손실·중복 허용 범위와 비용에 따라 QoS를 선택하세요. 성공하는 일반적인 QoS 2 전달은 PUBLISH·PUBREC·PUBREL·PUBCOMP를 교환하지만 애플리케이션의 데이터베이스 부수 효과나 전체 업무 흐름을 정확히 한 번 실행하게 만들지는 않습니다. QoS 1과 애플리케이션 중복 제거도 선택지입니다. 제품에 맞춰 브로커 가용성, 세션·메시지 영속 상태와 복구를 설계하세요. TLS와 적절한 디바이스 인증·인가를 사용하며 클라이언트 인증서는 발급·교체 관리가 필요한 한 가지 방법입니다. **공식 참고 자료**: [DoH](https://www.rfc-editor.org/rfc/rfc8484.html), [DNS serve-stale](https://www.rfc-editor.org/rfc/rfc8767.html), [OpenSSH](https://man.openbsd.org/ssh), [Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html), [DMARC](https://www.rfc-editor.org/rfc/rfc7489.html), [HTTP/3](https://www.rfc-editor.org/rfc/rfc9114.html), [ICE](https://www.rfc-editor.org/rfc/rfc8445.html), [gRPC performance](https://grpc.io/docs/guides/performance/), [MQTT 5.0](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html). --- **다음:** [Part 4: 요청의 여정과 클라우드](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part4.md) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/basics/06-network-fundamentals-part4 ---------------------------------------- # 네트워크 기초 Part 4 — 요청의 여정과 클라우드 매핑 > **마지막 업데이트**: 2026년 9월 11일 ::: tip 4부작 시리즈입니다 [Part 1: 계층 모델과 링크·라우팅](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part1.md) · [Part 2: 전송 계층과 TLS](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part2.md) · [Part 3: 애플리케이션 프로토콜](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part3.md) · **Part 4: 요청의 여정과 클라우드** *(현재 문서)* ::: 이 시리즈의 프로토콜·메커니즘 25개를 예시 요청으로 연결하고 AWS·쿠버네티스의 관련 역할과 비교합니다. 기능상 대응 관계이며 일대일 대체 관계는 아닙니다. ![주소 설정·DNS, 로컬 전달·라우팅, 선택적인 NAT를 거쳐 TCP+TLS의 HTTP/1.1·HTTP/2 또는 TLS가 통합된 QUIC의 HTTP/3으로 이어지는 예시 요청 경로. 캐시와 망 구성에 따라 생략되는 단계가 있다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-basics-06-network-fundamentals-part4-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-basics-06-network-fundamentals-part4-0.html) --- ## 6. 하나의 요청을 끝까지 따라가기 `https://example.com`에 새로 연결할 때의 개념적 의존 관계입니다. 실제 패킷 추적 결과는 아닙니다. DNS 질의와 핸드셰이크 패킷 자체도 링크·라우팅 계층을 사용하고, 캐시나 기존 연결이 있으면 일부 작업을 생략합니다. 1. **주소 설정** — 호스트는 DHCP, 정적 설정, IPv6 SLAAC·Router Advertisement 또는 관리형 방식으로 주소·경로·리졸버 설정을 이미 갖추고 있습니다. 2. **DNS (또는 DoH)** — 필요할 때 목적지를 조회합니다. 재귀 리졸버는 캐시·위임 조회·전달을 사용할 수 있고 애플리케이션이 루트에 직접 물을 필요는 없습니다. HTTPS 레코드로 연결 매개변수를 알 수도 있습니다. 3. **이웃 해석** — Ethernet의 IPv4에서는 선택한 다음 홉의 MAC이 캐시에 없으면 ARP로 조회합니다. IPv6는 Neighbor Discovery를 사용합니다. 다음 홉은 로컬 목적지 또는 라우터일 수 있습니다. 4. **Ethernet / Wi-Fi** — 해당 다음 홉으로 프레임을 전송합니다. 선택한 경로가 요구할 때만 기본 게이트웨이를 사용합니다. 5. **IP 라우팅** — 라우터는 포워딩 테이블로 패킷을 전달합니다. 경로는 정적·직접 연결·BGP·OSPF 또는 다른 제어 방식으로 구성될 수 있습니다. 요청마다 라우팅 프로토콜을 새로 협상하지는 않습니다. 6. **선택적인 NAT** — IPv4 인터넷 송신 경로에서 사설 주소를 공인 주소로 변환할 수 있습니다. 여러 내부·IPv6 경로는 NAT를 사용하지 않고 사설 주소 간 NAT도 존재합니다. 7. **TCP 또는 QUIC** — 전송 연결을 수립하거나 재사용합니다. HTTP/1.1·HTTP/2는 일반적으로 TCP를, HTTP/3은 UDP 위의 QUIC을 사용합니다. 8. **TLS** — 핸드셰이크 모드에 따라 상대를 인증하고 트래픽 키를 설정합니다. QUIC에서는 TLS 1.3이 7번과 통합됩니다. 재개는 새 인증서 기반 핸드셰이크와 다릅니다. 9. **HTTP** — 협상한 버전으로 요청·응답을 교환합니다. HTTP/3은 QUIC 경로가 필요하며 TCP 경로에서 동작하지 않습니다. 10. **선택적인 애플리케이션 기능** — WebSocket, 브라우저 호환 gRPC, WebRTC는 구현에 따라 추가 연결을 만들거나 기존 전송을 재사용·다중화할 수 있습니다. **ICMP**는 목적지 도달 불가나 경로에 비해 큰 패킷 등 일부 IP 계층 오류를 알릴 수 있습니다. 모든 오류를 보고하지는 않습니다. 패킷이나 ICMP 오류가 차단될 수 있고 TLS·애플리케이션 오류는 별도 방식으로 처리합니다. 필요한 ICMP 허용과 전송·애플리케이션 로그 및 측정을 함께 사용하세요. ICMP 오류가 없다고 성공이 증명되지는 않습니다. --- ## 7. 클라우드에서 이 개념들은 어디로 가는가 클라우드에도 주소·라우팅·필터링·전송 역할이 있지만 전통적인 장비와 책임 경계가 다릅니다. AWS에서는 다음과 같이 비교할 수 있습니다. | 전통적 개념 | AWS에서의 대응 | |---|---| | 분리와 필터링 | VPC·서브넷은 논리적 망 경계, 보안 그룹·NACL은 필터링이며 VLAN과 동일하지 않음 | | 라우팅 테이블 | VPC 라우트 테이블, Transit Gateway | | BGP 피어링 | Direct Connect 가상 인터페이스, 동적 라우팅 Site-to-Site VPN; 정적 VPN 라우팅도 가능 | | NAT / 사설 서비스 접근 | NAT Gateway는 주소 변환, VPC 엔드포인트는 지원 서비스의 사설 접근 경로 제공 | | DNS 서버 | Route 53, Resolver 엔드포인트 | | DHCP | VPC DHCP 옵션 세트 | | TLS 종료 / 인증서 | ALB HTTPS 리스너·NLB TLS 리스너·CloudFront; ACM은 트래픽 전달이 아닌 지원 인증서 관리 | | L7 로드밸런싱 | ALB, 별도로 관리하는 서비스 메시의 Istio·Envoy 같은 애플리케이션 프록시 | | SSH 접속 | Systems Manager Session Manager | | 내부 구간 암호화 | 애플리케이션·프록시의 TLS/mTLS; 네트워크 계층 암호화는 별도의 설계 선택 | AWS App Mesh는 기존 설계의 예이며 신규 설계 기본값으로 제시하지 않습니다. AWS가 발표한 지원 종료일은 **2026년 9월 30일**입니다. 기존 배포는 마이그레이션을 계획해야 합니다. **설계 시 먼저 결정해야 하는 항목 세 가지**를 꼽자면 이렇습니다. 1. **IP 주소 계획** — 온프레미스·Pod·Service 범위를 포함해 연결해야 하는 망의 CIDR을 계획하세요. 중복되면 주소 변환이나 재설계가 필요할 수 있습니다. 주소 변경에는 운영 비용이 있지만 언제나 가장 비싸다는 순위를 단정할 수는 없습니다. 2. **아웃바운드 경로** — 목적지별로 인터넷 또는 사설 서비스 경로를 정하세요. NAT의 시간·데이터 요금, 인터페이스 엔드포인트의 시간·데이터 요금, AZ 간 전송과 가용성 요구를 비교해야 합니다. S3·DynamoDB 게이트웨이 엔드포인트는 추가 엔드포인트 요금이 없지만 모든 인터넷 목적지를 대체하지는 않습니다. 3. **암호화 종료 지점** — 로드밸런서 이후 백엔드 연결을 포함해 구간별 암호화·인증을 명시하세요. 워크로드 요구와 적용 정책을 확인해야 하며 TLS 종료만으로 다음 구간이 보호되지는 않습니다. --- ## 8. 쿠버네티스에서는 누가 이 일을 하는가 클러스터 안에서도 같은 개념이 컴포넌트 이름만 바꿔 반복됩니다. 이 표가 이 시리즈와 이후 심화 문서들을 잇는 다리입니다. | 전통적 개념 | 쿠버네티스에서의 대응 | |---|---| | Pod IP 할당 | CNI/IPAM 통합(VPC CNI, Cilium 등); 반드시 Pod마다 DHCP를 수행하는 것은 아님 | | 로컬 전달 / 포워딩 | 호스트 인터페이스·이웃 처리·CNI 데이터패스; veth·경로·터널·eBPF 등 구현별 상이 | | DNS | 보통 CoreDNS를 사용하는 클러스터 DNS; `서비스명.네임스페이스.svc.<클러스터 도메인>`에서 `cluster.local`은 흔한 설정값 | | Service 가상 IP / L4 분산 | Linux kube-proxy의 iptables·nftables; IPVS는 1.35부터 deprecated. eBPF 구현은 kube-proxy를 대체할 수 있지만 kube-proxy 모드는 아님 | | Pod 트래픽 정책 | NetworkPolicy를 지원하는 네트워킹 컨트롤러·플러그인이 집행 | | L7 라우팅 / TLS 종료 | Ingress·Gateway API 리소스와 이를 구현하는 컨트롤러·데이터플레인 | | 서비스 간 mTLS | 애플리케이션 TLS 또는 설정된 Istio·Linkerd 등 메시; 임의의 CNI 설치만으로 활성화되지 않음 | | BGP 라우팅 / 광고 | 예: Calico BGP 경로, MetalLB의 Service 주소 BGP 광고; 역할은 서로 다름 | Pod를 백엔드로 갖는 일반적인 ClusterIP Service는 클러스터 DNS가 Service IP를 반환하고 kube-proxy 또는 대체 구현이 Service·EndpointSlice 상태로 적합한 엔드포인트를 선택합니다. 데이터패스는 같은 노드나 다른 노드의 해당 엔드포인트로 전달합니다. Headless Service는 DNS로 엔드포인트 주소를 노출하고 ExternalName Service는 CNAME을 반환합니다. mTLS는 해당 피어와 정책을 설정했을 때 적용됩니다. 이런 차이 때문에 “모든 계층이 항상 실행된다”는 패킷 흐름 가정은 성립하지 않습니다. --- ## 마무리 프로토콜·메커니즘 25개를 살펴본 뒤에는 각 선택의 절충과 보장 범위를 확인해야 합니다. TCP는 복구·순서 유지 비용으로 신뢰성 있는 순차 전달을 제공합니다. UDP는 이를 상위 계층에 맡깁니다. QUIC은 UDP 위에 신뢰성 있는 스트림과 통합 보안을 제공합니다. NAT는 공인 IPv4 주소를 절약하지만 외부에서 시작하는 연결을 복잡하게 만들며 ICE·STUN·TURN이 이를 다루는 데 도움을 줍니다. DoH는 리졸버까지의 구간을 보호하며 조직의 가시성은 리졸버·단말 정책에 따라 달라집니다. 장애 조사에서는 각 계층의 실제 보장과 관찰 증거로 원인 범위를 좁히세요. 그럴듯한 프로토콜 설명도 로그·추적·측정으로 다른 가능성과 구분하기 전까지는 가설입니다. --- ## 다음 문서 이 기초 위에서 클러스터 네트워킹으로 넘어갑니다. - [eBPF 기초](https://www.atomai.click/kubernetes-docs/llms/ko/basics/05-ebpf-fundamentals.md) — 커널에서 패킷을 처리하는 방식 - [Cilium 네트워킹](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/03-networking.md) — eBPF 기반 CNI - [Calico BGP 심화](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md) — 클러스터 내 BGP 라우팅 - [Amazon VPC CNI](https://www.atomai.click/kubernetes-docs/llms/ko/networking/01-vpc-cni.md) — VPC CNI와 IP 할당 ## 참고 프로토콜 목록 구성은 ByteByteGo의 "What Keeps the Internet Running?" 인포그래픽을 출발점으로 삼았으며, 설명과 실무 관점은 별도로 작성했습니다. 공식 참고 자료: [Kubernetes Service proxy modes](https://kubernetes.io/docs/reference/networking/virtual-ips/), [Service DNS](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/), [NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/), [NAT Gateway cost guidance](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-pricing.html), [ECR VPC endpoints](https://docs.aws.amazon.com/AmazonECR/latest/userguide/vpc-endpoints.html), [App Mesh lifecycle](https://docs.aws.amazon.com/app-mesh/latest/userguide/what-is-app-mesh.html). ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/01-vpc-cni ---------------------------------------- # Amazon VPC CNI > **검토 기준**: VPC CNI / Helm 차트 1.23.0, network policy agent 1.4.1. > **최종 검토**: 2026년 9월 11일. 실제 클러스터 버전·리전에 호환되는 EKS add-on build를 선택합니다. 업스트림, Helm, EKS `eksbuild` 버전은 서로 다른 식별자입니다. ## 목차 - [VPC CNI 개요](#vpc-cni-개요) - [네트워킹 모델](#네트워킹-모델) - [설치 및 구성](#설치-및-구성) - [IP 주소 관리](#ip-주소-관리) - [Network Policy 지원](#network-policy-지원) - [고급 기능](#고급-기능) - [트러블슈팅](#트러블슈팅) - [모범 사례](#모범-사례) ## VPC CNI 개요 Amazon VPC CNI는 표준 EKS EC2 노드에 VPC 기반 Pod 네트워킹을 제공합니다. 이 문서의 `aws-node` DaemonSet 명령도 해당 설치를 대상으로 합니다. Auto Mode는 노드 서비스로 관리형 네트워킹을 실행하고 Fargate·Windows는 관리 경로가 다릅니다. EC2/Linux 절차를 다른 컴퓨팅 모드에 적용하기 전에 [개요](https://www.atomai.click/kubernetes-docs/llms/ko/networking/README.md)를 확인합니다. Pod는 overlay 캡슐화 없이 VPC에서 라우팅 가능한 주소를 사용합니다. 실제 연결·성능은 라우팅, 보안 그룹, 네트워크 정책, ENI/IP 한계, 애플리케이션에도 달려 있습니다. EKS는 생성 시 선택한 **IPv4 또는 IPv6 Pod/Service 주소**를 지원하며 dual-stack Pod·Service는 지원하지 않습니다. dual-stack VPC나 IPv6 Pod의 IPv4 egress 보조 인터페이스는 다른 개념입니다. ### 아키텍처 | 구성 요소 | 역할 | |---|---| | 컨테이너 런타임 / CNI 바이너리 | 런타임이 Pod sandbox의 CNI를 호출하고 AWS 플러그인이 주소 요청·네트워크 네임스페이스 설정 수행 | | IPAMD | Linux EC2 노드의 주소 pool과 필요한 일반 ENI·IP 관리 | | EKS 네트워크 정책 컨트롤러 / 노드 에이전트 | 관리형 컨트롤러가 정책 엔드포인트를 해석하고, 활성화된 `aws-eks-nodeagent`가 지원 정책을 eBPF로 강제 | | VPC resource controller | 별도 전제에 따라 branch/trunk 인터페이스, Windows 주소 할당 등 관리 | 이전 그림은 kubelet이 CNI 바이너리를 직접 호출한다고 표시했습니다. 현재 Kubernetes의 CNI 관리는 컨테이너 런타임이 담당합니다. ### IP 할당 방식 | 특성 | 보조 IPv4 주소 | Prefix delegation | |---|---|---| | 할당 단위 | ENI의 개별 보조 주소 | IPv4는 16개 주소의 `/28`, IPv6는 `/80` prefix | | 용량 | 인터페이스·주소 슬롯 및 kubelet 설정에 제한 | 슬롯당 주소를 늘리지만 지원 하드웨어·여유 prefix·kubelet/리소스 한계 적용 | | 할당 절충 | 주소 단위로 세밀하게 할당 | 블록을 한 번에 할당하므로 warm target에 따라 미사용 주소 예약 | | 선택 기준 | 호환성과 측정한 수요 | Nitro 지원, 서브넷 단편화, 워크로드 변경, 기능 조합 확인 | Prefix delegation이 모든 대규모 클러스터의 필수 조건은 아니며 고갈된 서브넷에 새 주소 공간을 만들어 주지도 않습니다. ## 네트워킹 모델 ### ENI 아키텍처 일반 secondary-IPv4 모드에서는 각 ENI에 primary 주소와 CNI가 사용할 추가 주소가 있습니다. Primary ENI는 노드의 primary 주소도 전달합니다. 추가 ENI에서 더 많은 보조 Pod 주소를 공급할 수 있습니다. Custom networking은 Pod 주소를 공급하는 인터페이스·서브넷을 바꾸고, prefix·branch-ENI 모드는 할당 규칙이 다릅니다. ```text Linux EC2 노드 — 일반 secondary-IPv4 예제 ├── Primary ENI: 노드 primary IP + Pod용 secondary IP ├── 추가 ENI: 해당 ENI의 primary IP + Pod용 secondary IP └── 추가 ENI: 해당 ENI의 primary IP + Pod용 secondary IP ``` ### 인스턴스 유형별 ENI/IP 제한 | 인스턴스 유형 | 최대 ENI 수 | ENI당 IPv4 슬롯 | 과거 secondary-IP bootstrap maxPods | |---|---|---|---| | t3.medium | 3 | 6 | 17 | | t3.large | 3 | 12 | 35 | | m5.large | 3 | 10 | 29 | | m5.xlarge | 4 | 15 | 58 | | m5.2xlarge | 4 | 15 | 58 | | c5.4xlarge | 8 | 30 | 234 | | m5.8xlarge | 8 | 30 | 234 | 과거 공식은 **`ENI 수 × (ENI당 IPv4 슬롯 − 1) + 2`**입니다. `+2`는 해당 bootstrap 계산의 host-network 시스템 Pod 2개를 반영하므로 m5.large는 `3 × 9 + 2 = 29`입니다. 현재 모든 배포에 host-network Pod가 정확히 2개라는 뜻은 아닙니다. 이 값은 과거 bootstrap 값이며 현재 모든 환경의 Pod 밀도 권장값이 아닙니다. Prefix delegation, custom networking, branch 인터페이스, 다중 네트워크 카드, CPU·메모리, kubelet `maxPods`를 함께 고려합니다. EKS 관리형 노드 그룹은 vCPU 30개 미만에서 `maxPods` 110, 그 외에는 250을 상한으로 적용합니다. 실제 노드의 allocatable 용량을 확인합니다. ### Prefix Delegation Linux IPv4 prefix 모드의 EKS 관리형 add-on 설정 조각입니다. ```json { "env": { "ENABLE_PREFIX_DELEGATION": "true", "WARM_PREFIX_TARGET": "1" } } ``` 아래 관리 절차로 의도한 add-on 설정과 합칩니다. Helm 관리 설치에서는 같은 `env` 매핑을 Helm values에 둡니다. 직접 `kubectl set env`로 바꾼 값은 관리 주체가 다시 조정할 수 있습니다. IPv4 할당에는 단순히 양수 `AvailableIpAddressCount`가 아니라 적절한 연속 `/28` 블록이 필요합니다. 서브넷 예약·단편화와 Nitro 지원을 확인합니다. Prefix를 켜도 기존 kubelet Pod 상한이나 branch-ENI Pod 상한이 모두 자동으로 증가하지는 않습니다. ## 설치 및 구성 ### 관리 주체와 호환성 확인 대상 클러스터의 AWS CLI 자격 증명과 Kubernetes context를 준비하고 읽기부터 시작합니다. ```bash EKS_REGION=ap-northeast-2 CLUSTER_NAME=my-cluster KUBERNETES_MINOR="$(aws eks describe-cluster --region "$EKS_REGION" \ --name "$CLUSTER_NAME" --query cluster.version --output text)" aws eks describe-addon-versions --region "$EKS_REGION" \ --addon-name vpc-cni --kubernetes-version "$KUBERNETES_MINOR" aws eks describe-addon --region "$EKS_REGION" \ --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni kubectl -n kube-system get daemonset aws-node -o yaml ``` EKS `describe-addon`의 `ResourceNotFoundException`만으로 CNI가 없다고 판단하지 않습니다. 자체 관리 설치일 수 있습니다. 기존 DaemonSet, ServiceAccount, Helm release, 설정, IAM을 확인하고 관리 주체를 **하나** 선택합니다. Auto Mode 네트워킹에는 이 설치 절차를 적용하지 않습니다. ### 기존 EKS 관리형 Add-on 기존 설정을 내보내고 선택한 호환 build의 스키마를 확인합니다. ```bash umask 077 aws eks describe-addon --region "$EKS_REGION" \ --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni > vpc-cni-before.json jq -r '.addon.configurationValues // "{}"' vpc-cni-before.json > vpc-cni-config.json : "${VPC_CNI_ADDON_VERSION:?Select a compatible EKS add-on build from the metadata}" aws eks describe-addon-configuration --region "$EKS_REGION" \ --addon-name vpc-cni --addon-version "$VPC_CNI_ADDON_VERSION" ``` 필요한 중간 업그레이드 버전과 릴리스 변경을 검토합니다. `vpc-cni-config.json`에 의도한 기존 설정을 보존하면서 선택한 변경만 반영합니다. 일부 payload나 충돌 플래그가 모든 값을 자동 보존한다고 가정하지 않습니다. CNI IAM 권한과 설정한 IRSA/Pod Identity 역할을 확인하고 IPv6에 맞는 권한도 준비합니다. 이미 관리형인 add-on의 검토된 업데이트는 다음 형태로 실행할 수 있습니다. ```bash set -eu VPC_CNI_UPDATE_ID="$(aws eks update-addon --region "$EKS_REGION" \ --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni \ --addon-version "$VPC_CNI_ADDON_VERSION" \ --configuration-values file://vpc-cni-config.json --resolve-conflicts PRESERVE \ --query update.id --output text)" aws eks describe-update --region "$EKS_REGION" --name "$CLUSTER_NAME" \ --addon-name vpc-cni --update-id "$VPC_CNI_UPDATE_ID" ``` `PRESERVE`는 명시적인 충돌 처리 선택이며 결과 환경 변수·이미지·동작을 확인해야 합니다. 기록한 update ID의 상태가 `Successful`이 될 때까지 조회합니다. `Failed`·`Cancelled`이면 오류를 확인하고 진행을 중단합니다. 성공한 뒤 결과 add-on·DaemonSet을 확인합니다. ```bash aws eks describe-addon --region "$EKS_REGION" \ --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni kubectl -n kube-system rollout status daemonset/aws-node --timeout=10m ``` API 요청 수락이나 이전 DaemonSet의 준비 상태만으로 이번 업데이트·네트워크 검증이 끝났다고 판단하지 않습니다. 관리형 add-on이 없고 설치·소유 관계 준비를 마친 경우의 생성은 별도 작업입니다. ```bash aws eks create-addon --region "$EKS_REGION" \ --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni \ --addon-version "$VPC_CNI_ADDON_VERSION" \ --configuration-values file://vpc-cni-config.json ``` 개별 기능을 켤 때마다 `create-addon`을 반복하거나, 기존 사용자 설정에 마이그레이션 계획 없이 `OVERWRITE`를 강제하지 않습니다. ### Helm 관리 설치 Init·정책 에이전트 구성 요소도 함께 선택되도록 전체 차트를 고정합니다. 이미지 태그 두 개만 바꿔서는 차트의 나머지가 업데이트되지 않습니다. ```bash helm repo add eks https://aws.github.io/eks-charts helm repo update eks helm show values eks/aws-vpc-cni --version 1.23.0 > chart-defaults.yaml helm template aws-vpc-cni eks/aws-vpc-cni --namespace kube-system \ --version 1.23.0 -f helm-values.yaml > rendered-cni.yaml ``` 실제 IP family, CNI ServiceAccount/IAM, 선택한 기능에 맞게 `helm-values.yaml`을 준비합니다. 이 문서의 기본 Linux 예제는 IPv4입니다. 렌더링한 리소스를 검토한 뒤 적용합니다. ```bash helm upgrade --install aws-vpc-cni eks/aws-vpc-cni --namespace kube-system \ --version 1.23.0 -f helm-values.yaml --wait --timeout 10m ``` 명령은 새 설치나 Helm 관리 설치를 전제로 합니다. 기존 EKS add-on·bootstrap 관리 객체는 소유 전환 계획이 필요합니다. EKS 파티션·레지스트리 접근과 이미지 pull 전제도 환경에 맞춰야 합니다. ### 주요 설정 값 | 설정 | 의미 | 기준·기본값 구분 | |---|---|---| | `WARM_IP_TARGET` | 새 일반 Pod 할당에 대비한 여유 주소 목표 | 기본 미설정, 최대 한계가 아님 | | `MINIMUM_IP_TARGET` | 전체 할당 주소의 하한 | 기본 미설정, 사용 시 양수 warm-IP 목표와 조합 | | `WARM_ENI_TARGET` | Warm ENI 용량 목표 | 릴리스 기본 1, IP target이 우선 | | `WARM_PREFIX_TARGET` | 여유 IPv4 prefix 목표 | 릴리스 차트·매니페스트는 1, bare daemon 문서는 미설정 | | `ENABLE_PREFIX_DELEGATION` | Prefix 할당 선택 | Linux 차트 기본 `"false"` | | `AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG` | Custom networking 선택 | 기본 `"false"` | | `ENI_CONFIG_LABEL_DEF` | ENIConfig를 선택하는 노드 레이블 키 | Daemon 기본 `k8s.amazonaws.com/eniConfig`, zone 예제는 재정의 | | `ENABLE_POD_ENI` | EC2 Pod-ENI 연동 활성화 | 기본 `"false"`, 다른 SGPP 전제도 필요 | | `POD_SECURITY_GROUP_ENFORCING_MODE` | SGPP 라우팅·SNAT·보안 그룹 동작 | 기본 `strict` | | `NETWORK_POLICY_ENFORCING_MODE` | 새 Pod의 규칙을 설정하는 동안 네트워크 정책 동작 | 기본 `standard` | 두 enforcing-mode 설정은 다른 시스템을 제어합니다. EKS 설정 payload의 환경 변수 값은 문자열입니다. ### Custom Networking (ENIConfig) 의도한 VPC·AZ에 실제 Pod 서브넷·보안 그룹을 생성하고 ID를 참조합니다. ```yaml apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: ap-northeast-2a spec: subnet: subnet-0123456789abcdef0 securityGroups: - sg-0123456789abcdef0 --- apiVersion: crd.k8s.amazonaws.com/v1alpha1 kind: ENIConfig metadata: name: ap-northeast-2b spec: subnet: subnet-0abcdef0123456789 securityGroups: - sg-0123456789abcdef0 ``` Custom networking을 켜고 노드의 실제 zone 레이블을 사용합니다. ```json { "env": { "AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG": "true", "ENI_CONFIG_LABEL_DEF": "topology.kubernetes.io/zone" } } ``` 명시적인 ENIConfig 노드 어노테이션이 레이블보다 우선합니다. ENIConfig 객체만으로 custom networking이 활성화되지는 않습니다. 라우팅, DNS·보안 규칙, 주소 용량, 워크로드·노드 전환을 계획합니다. 새 CIDR·ENIConfig만 생성해도 현재 Pod가 다른 서브넷으로 이동하지는 않습니다. ## IP 주소 관리 ### Warm Pool 튜닝 측정한 Pod 수요·변경 빈도, 주소 공간, EC2 API 한계로 정합니다. 다음은 클러스터 크기만으로 정한 권장값이 아닌 서로 다른 목표 예제입니다. ```json { "env": { "WARM_IP_TARGET": "2", "MINIMUM_IP_TARGET": "4" } } ``` ```json { "env": { "WARM_IP_TARGET": "5", "MINIMUM_IP_TARGET": "10" } } ``` `MINIMUM_IP_TARGET`은 전체 할당 하한, `WARM_IP_TARGET`은 여유 주소 목표이며 ENI/prefix warm 전략보다 우선합니다. Prefix delegation에서는 실제로 prefix 단위 할당이 이루어집니다. Warm 용량은 할당 대기를 줄일 수 있지만 주소를 사용하고, 잦은 변경은 API 호출을 늘릴 수 있습니다. ### Secondary CIDR 추가 먼저 기존 연결, 연결된 네트워크와의 중복, VPC CIDR 제한, 서브넷·라우팅 전제를 검토합니다. ```bash VPC_ID=vpc-0123456789abcdef0 aws ec2 describe-vpcs --region "$EKS_REGION" --vpc-ids "$VPC_ID" \ --query 'Vpcs[0].CidrBlockAssociationSet' ``` 다음 ID·주소 공간은 예제이며 검토한 VPC 계획의 일부로만 수행합니다. ```bash aws ec2 associate-vpc-cidr-block --region "$EKS_REGION" \ --vpc-id "$VPC_ID" --cidr-block 100.64.0.0/16 aws ec2 describe-vpcs --region "$EKS_REGION" --vpc-ids "$VPC_ID" \ --query 'Vpcs[0].CidrBlockAssociationSet' ``` 새 CIDR의 상태가 associating이 아니라 **associated**인지 확인한 뒤 그 범위에 서브넷을 생성합니다. ```bash aws ec2 create-subnet --region "$EKS_REGION" --vpc-id "$VPC_ID" \ --cidr-block 100.64.0.0/19 --availability-zone ap-northeast-2a ``` 서브넷의 라우팅 테이블·보안 규칙·CNI 선택도 필요합니다. 기존 Pod는 계획한 전환 전까지 현재 네트워킹을 유지합니다. RFC 6598 `100.64.0.0/10`은 shared address space이지 전 세계에서 고유한 사설 용량이 아니므로 연결된 모든 환경과 중복을 확인합니다. ### IPv6 클러스터 구성 IP family는 생성 시 선택하고 이후 변경할 수 없습니다. 공식 `eksctl` 인터페이스는 `--ip-family` 플래그가 아닌 **설정 파일**입니다. 스키마 예제는 다음과 같습니다. ```yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: ipv6-example region: ap-northeast-2 version: '1.35' kubernetesNetworkConfig: ipFamily: IPv6 iam: withOIDC: true addons: - name: vpc-cni - name: coredns - name: kube-proxy managedNodeGroups: - name: linux-nitro amiFamily: AmazonLinux2023 instanceType: m5.large desiredCapacity: 2 privateNetworking: true ``` 예제 버전·리전을 바꾸고 지원되는 노드 이미지를 선택하며 생성 전 VPC endpoint·접근·IAM을 검토합니다. 여기서 생략한 add-on 버전은 EKS/eksctl의 지원 경로로 선택되므로, 통제된 배포에는 필요한 호환 build를 확인·고정합니다. `iam.withOIDC`와 관리형 add-on·node group은 공식 eksctl IPv6 전제를 반영합니다. ```bash eksctl create cluster --config-file ipv6-cluster.yaml ``` 이 감사에서 클러스터를 생성하지 않았습니다. 로컬 CLI help·schema 검사는 eksctl 0.229.0으로 했으며 네트워킹·IAM·리전별 build의 실제 배포 검증은 아닙니다. IPv6에는 지원되는 Linux Nitro/Fargate 경로와 prefix 할당이 필요하고 Windows는 지원되지 않습니다. IPv6 Pod에 egress-only IPv4 보조 인터페이스가 있을 수 있습니다. 정책 에이전트 문서는 주 인터페이스의 IPv6 정책이 보조 인터페이스의 IPv4 트래픽을 보호하지 않는다고 명시합니다. 이 경로를 제거해야 한다면 `ENABLE_V4_EGRESS`, 의존성, Pod 롤아웃을 검토하고 IPv6 정책만으로 차단된다고 가정하지 않습니다. ## Network Policy 지원 ### 네이티브 정책 강제 표준 네이티브 eBPF 정책은 VPC CNI 1.14에서 처음 도입되었습니다. 현재 EKS 문서의 표준·Admin 정책에는 더 새 전제가 있으며 검토한 1.23도 클러스터·플랫폼에 맞춰야 합니다. ```json { "enableNetworkPolicy": "true" } ``` `"enableNetworkPolicy": "true"`는 공식 문자열 설정입니다. 지원되는 EC2 Linux 노드에서 사용할 수 있고 Fargate·Windows에는 이 강제를 적용하지 않습니다. Auto Mode는 자체 관리형 구현을 사용합니다. EKS `ClusterNetworkPolicy` Admin/Baseline 제어와 Auto Mode DNS `ApplicationNetworkPolicy`는 표준 `NetworkPolicy`의 다른 이름이 아닌 확장입니다. Standard 모드의 새 Pod는 규칙이 해석되는 동안 처음에 트래픽을 허용합니다. 더 엄격한 시작 동작을 의도적으로 선택할 수 있습니다. ```json { "enableNetworkPolicy": "true", "env": { "NETWORK_POLICY_ENFORCING_MODE": "strict" } } ``` Strict 모드에서는 시작 전 DNS 등 필수 트래픽 정책을 올바르게 준비해야 합니다. SGPP의 별도 `POD_SECURITY_GROUP_ENFORCING_MODE`를 설정하는 기능은 아닙니다. ### NetworkPolicy 예제 전제는 `app` 네임스페이스의 컨트롤러 관리 frontend/backend 워크로드와 TCP 8080을 수신하는 backend Pod입니다. 현재 AWS는 안정적인 강제에 `metadata.ownerReferences`가 중요하며 Service·컨테이너 포트 번호(이름 있는 포트는 이름도)가 일치해야 한다고 문서화합니다. ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: app spec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080 ``` 출발 Pod 선택자는 같은 네임스페이스로 제한됩니다. 선택한 backend ingress를 격리하고 해당 frontend의 8080 접근을 허용하지만 backend egress를 거부하거나 애플리케이션 사용자를 인증하지는 않습니다. 다른 일치 정책과 Admin tier 동작도 고려합니다. 독립 진단 Pod 대신 Deployment/Job 관리 Pod로 허용·거부 흐름을 모두 확인합니다. ### 검증과 진단 ```bash kubectl -n kube-system logs -l k8s-app=aws-node -c aws-eks-nodeagent --tail=200 kubectl get networkpolicy -A kubectl get policyendpoints.networking.k8s.aws -A ``` 이는 **노드 에이전트** 로그이며 정책 컨트롤러는 EKS 관리 제어 평면에서 실행됩니다. PolicyEndpoint는 생성된 상태이므로 임의로 편집·삭제하지 않습니다. 접근 권한이 있고 정책 CLI가 설치된 Linux 노드에서는 다음을 사용합니다. ```bash sudo /opt/cni/bin/aws-eks-na-cli ebpf progs sudo /opt/cni/bin/aws-eks-na-cli ebpf maps ``` 도구는 `ebpf-sdk list-maps`가 아닌 `aws-eks-na-cli`입니다. 영향받은 노드를 확인하고 프로세스 로그, 설정한 정책 이벤트 로그, CloudWatch 전달을 구분합니다. 외부 로그 전달에는 해당 IAM·설정도 필요합니다. ## 고급 기능 ### Pod별 보안 그룹 해당 보안 그룹이 DNS·API·애플리케이션·반환 경로에 맞는 규칙을 가지고 이미 존재해야 합니다. ```yaml apiVersion: vpcresources.k8s.aws/v1beta1 kind: SecurityGroupPolicy metadata: name: my-security-group-policy namespace: app spec: podSelector: matchLabels: app: database securityGroups: groupIds: - sg-0123456789abcdef0 - sg-0abcdef0123456789 ``` Standard SGPP 동작을 선택한 **EC2** 설정 예제입니다. ```json { "env": { "ENABLE_POD_ENI": "true", "POD_SECURITY_GROUP_ENFORCING_MODE": "standard" } } ``` 완전한 SGPP 설치가 아닙니다. Trunking 지원 인스턴스, 클러스터 역할의 VPC resource-controller 권한, CNI 권한, 서브넷 용량, EKS 전제를 확인합니다. T 계열이 Nitro라고 해서 trunking을 지원하지는 않습니다. 새로 생성·재생성한 선택 Pod에 의도한 설정이 적용됩니다. 관리형 resource controller는 **추가 trunk ENI**를 붙이고 branch ENI를 연결합니다. Trunk는 노드의 primary `eth0` ENI가 아닙니다. 선택 Pod는 보안 그룹이 지정된 branch 인터페이스를 사용하며 prefix delegation이 branch Pod 상한을 늘리지 않습니다. Fargate 보안 그룹은 별도 관리 경로를 따릅니다. | 모드·기능 | 확인할 영향 | |---|---| | SGPP `strict` | Branch 보안 그룹 동작과 Pod SNAT 비활성화, NodeLocal DNSCache 및 instance 대상 LoadBalancer/NodePort의 `externalTrafficPolicy: Local` 제약 | | SGPP `standard` | 문서화된 정책·DNS 조합 지원. 기본 external-SNAT 동작에서는 VPC 밖 트래픽이 노드 primary 주소·보안 그룹 사용 | | Custom networking + SGPP | Pod 보안 그룹이 ENIConfig 보안 그룹보다 우선 | | IPv6 | EKS 문서의 버전·플랫폼 조건에 따라 지원(EC2 CNI 1.16+ 포함). 오래된 README 기능 표의 “No”가 상세 안내를 무효화하지 않음 | | Windows / Auto Mode | 이 SGPP 방식은 미지원, Auto Mode는 별도 NodeClass 네트워킹 제어 사용 | 모드 변경은 새 Pod에 적용되므로 재생성과 트래픽 경로를 검증합니다. SNAT 뒤에도 모든 패킷에 같은 보안 그룹이 적용된다고 가정하지 않습니다. ### 다중 인터페이스와 Multus VPC CNI 1.20+에는 다중 네트워크 카드를 지원하는 인스턴스용 native multi-NIC 기능이 있습니다. `ENABLE_MULTI_NIC`와 Pod NIC 설정은 Multus와 다르며 추가 인터페이스의 이점을 얻으려면 애플리케이션이 실제로 사용해야 합니다. Multus는 meta-plugin입니다. AWS 지원 구성은 VPC CNI를 **주 delegate**로 사용하며 VPC CNI를 추가 인터페이스의 플러그인으로 쓰는 것은 지원하지 않습니다. 추가 인터페이스에는 호환 플러그인, 주소 할당, 수명 주기 관리가 필요합니다. | 추가 인터페이스 전제 | 이유 | |---|---| | 식별한 전용 인터페이스 | 고정 `eth1`이 IPAMD 관리 인터페이스일 수 있음 | | 추가 ENI의 `node.k8s.amazonaws.com/no_manage=true` | VPC CNI가 Multus 인터페이스를 관리하지 않도록 함 | | AWS에 할당·라우팅된 주소와 올바른 서브넷·SG·경로 | 임의의 `192.168.1.0/24` 할당이 EC2 ENI에서 자동으로 유효해지지 않음 | | 조정된 IPAM | 공유 `host-local` 범위는 여러 노드에서 중복 주소를 할당할 수 있음 | | 인터페이스별 정책 시험 | 추가 인터페이스·IPv4 보조 경로가 모든 주 인터페이스 정책에 자동 포함되지는 않음 | NetworkAttachmentDefinition의 `spec.config`는 지원 버전, 플러그인, 실제 parent 인터페이스, IPAM을 포함한 CNI JSON입니다. 이전 범용 `ipvlan`/`eth1`/`host-local` 매니페스트는 위 전제를 누락하여 구현 요구사항으로 대체했습니다. 이 문서는 실제 배포한 Multus/IPAM 솔루션이라고 주장하지 않습니다. ### Windows Windows는 VPC resource-controller IPAM을 사용합니다. 클러스터 역할 권한, Windows 노드 역할의 인증·access entry(해당 시 `EC2_WINDOWS`), CoreDNS용 Linux/Fargate 용량을 준비합니다. Windows Fargate, Auto Mode, Hybrid Nodes, IPv6, custom networking, SGPP, 네이티브 VPC-CNI 네트워크 정책에는 문서화된 제약이 있습니다. 컨트롤러의 결과 ConfigMap에는 다음 Windows IPAM 항목이 필요합니다. 필요한 데이터를 나타내며 관리 주체가 소유한 ConfigMap 전체를 덮어쓰라는 명령은 아닙니다. ```yaml apiVersion: v1 kind: ConfigMap metadata: name: amazon-vpc-cni namespace: kube-system data: enable-windows-ipam: 'true' ``` **Helm 관리** 설치의 Windows prefix 목표는 Linux와 다른 키를 사용합니다. ```yaml enableWindowsIpam: 'true' enableWindowsPrefixDelegation: 'true' warmWindowsPrefixTarget: 1 warmWindowsIPTarget: 0 minimumWindowsIPTarget: 0 ``` 해당 build 스키마, 필드 소유, 결과 ConfigMap을 확인합니다. Helm 값이 EKS add-on 설정 속성으로 그대로 수락된다고 가정하지 않습니다. Windows 차트 플래그는 `enable-windows-ipam`, `enable-windows-prefix-delegation`으로 변환되며 여기의 warm-target 필드도 Windows용입니다. 전제와 AMI·버전을 확인한 후 node-group 명령은 다음 형태로 사용할 수 있습니다. ```bash eksctl create nodegroup --region "$EKS_REGION" --cluster "$CLUSTER_NAME" \ --name windows-example --managed --node-type m5.large --nodes 2 \ --node-ami-family WindowsServer2022FullContainer ``` Windows secondary-IP 모드는 일반적으로 ENI 1개와 그 주소 슬롯 상한을 사용하며 Linux의 다중 ENI 공식과 다릅니다. Prefix delegation과 실제 kubelet 상한도 별도로 계산합니다. ## 트러블슈팅 ### IP 할당과 스케줄링 Pod 이벤트로 스케줄링 실패와 sandbox/CNI 주소 할당 실패를 구분합니다. ```bash kubectl -n kube-system logs -l k8s-app=aws-node -c aws-node --tail=300 kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, allocatablePods: .status.allocatable.pods}' SUBNET_ID=subnet-0123456789abcdef0 aws ec2 describe-subnets --region "$EKS_REGION" --subnet-ids "$SUBNET_ID" \ --query 'Subnets[].{SubnetId:SubnetId,AvailableIPs:AvailableIpAddressCount}' ``` `allocatablePods`는 kubelet 스케줄링 용량이지 현재 IP 사용률이 아닙니다. 서브넷의 여유 주소 수만으로 연속 `/28` 존재를 알 수 없습니다. 대응을 정하기 전에 IPAMD 로그, 모드, warm target, ENI 한계, API 오류, 영향받은 노드를 확인합니다. ### ENI 수 ```bash INSTANCE_ID=i-0123456789abcdef0 aws ec2 describe-instances --region "$EKS_REGION" --instance-ids "$INSTANCE_ID" \ --query 'Reservations[].Instances[].{InstanceId:InstanceId,AttachedENIs:length(NetworkInterfaces)}' aws ec2 describe-instance-types --region "$EKS_REGION" --instance-types m5.large \ --query 'InstanceTypes[].NetworkInfo.{MaxENI:MaximumNetworkInterfaces,IPv4PerENI:Ipv4AddressesPerInterface}' ``` 이전 쿼리는 인터페이스 자체가 아닌 중첩된 목록 수를 세었습니다. 수정 쿼리는 인스턴스별 개수를 반환합니다. 다중 카드, unmanaged/trunk 인터페이스, 인스턴스별 한계는 추가 해석이 필요합니다. ### Introspection과 메트릭 해당 노드의 실제 `aws-node` Pod를 선택하고 forwarding 세션을 유지합니다. ```bash kubectl -n kube-system get pods -l k8s-app=aws-node -o wide AWS_NODE_POD=aws-node-example kubectl -n kube-system port-forward "pod/$AWS_NODE_POD" 61678:61678 61679:61679 ``` 다른 로컬 터미널에서 조회합니다. ```bash curl --fail http://127.0.0.1:61679/v1/enis curl --fail http://127.0.0.1:61678/metrics ``` IPAMD introspection 기본값은 loopback **61679**, Prometheus 메트릭은 **61678**입니다. `/v1/enis`는 메트릭 엔드포인트가 아닙니다. Kubernetes forwarding 경로의 로컬 curl을 사용하므로 CNI 이미지 안의 curl에 의존하지 않습니다. ### 변경 전 오류 분류 | 관측 | 조치 전 확인 | |---|---| | `InsufficientFreeAddressesInSubnet` | 실제 여유 주소, warm 할당, 선택 서브넷, 계획한 용량 확장 | | `InsufficientCidrBlocks` | 연속 prefix·단편화·서브넷 예약 | | ENI/SG 한계 오류 | 해당 quota, 인스턴스·인터페이스 유형, 사용 중 객체. 무관한 SG를 제거하지 않음 | | ENI 생성 실패 | 상세 AWS 오류, CNI 자격 증명 역할, 권한·조건, quota, API 연결 | | Pod IP 대기 | IPAMD 상태, 컨트롤러·API 지연, 스로틀링, sandbox 이벤트, 주소 준비 | IPAMD 재시작, 인스턴스 확대, 노드 역할 권한 추가는 범용 해결책이 아닙니다. 근거를 먼저 수집하고 실제 실패 작업의 관리 주체에 검토한 변경을 적용합니다. ## 모범 사례 예상 Pod, warm pool, 증가량, 장애·교체 중복을 고려해 서브넷을 계획합니다. `/19`나 RFC 6598 범위는 설계 예제이지 필수 조건이 아닙니다. 새 CIDR 연결, 필요한 서브넷·경로, CNI·워크로드 전환을 함께 계획합니다. Warm 전략을 하나 선택합니다. 여유 IP와 전체 IP 목표를 사용하는 IPv4 prefix 예제입니다. ```json { "env": { "ENABLE_PREFIX_DELEGATION": "true", "WARM_IP_TARGET": "5", "MINIMUM_IP_TARGET": "10" } } ``` IP target이 설정된 상태의 추가 `WARM_PREFIX_TARGET`을 독립적으로 유효한 목표라고 해석하지 않습니다. 클러스터 크기만으로 안전성을 추정하지 말고 할당 실패·실제 제약을 감시합니다. ### 메트릭과 알림 릴리스 IPAMD 코드는 `awscni_total_ip_addresses`, `awscni_assigned_ip_addresses`, counter `awscni_no_available_ip_addresses`를 제공합니다. Total/assigned gauge는 전체 VPC 서브넷이 아닌 **IPAMD 할당 pool**을 나타냅니다. 작은 warm target에서는 높은 assigned/total 비율이 정상일 수 있으며 cooldown, branch 인터페이스, IP family, kubelet 용량을 함께 봐야 합니다. Prometheus Operator CRD와 선택자가 먼저 구성되어 있어야 합니다. Helm 관리 CNI에서는 다음 **IPv4 클러스터 예제**로 차트의 PodMonitor를 생성할 수 있습니다. ```yaml podMonitor: create: true labels: release: prometheus interval: 30s relabelings: - sourceLabels: - __meta_kubernetes_pod_node_name targetLabel: node - targetLabel: cluster replacement: example-cluster - targetLabel: ip_family replacement: ipv4 - targetLabel: job replacement: aws-vpc-cni ``` 클러스터 레이블을 교체하고 `release` 선택자를 맞추며 IP family를 사실대로 지정합니다. 레이블이 실제 family를 탐지하지는 않습니다. 차트는 Agent의 이름 있는 `metrics` 포트와 활성화한 정책 에이전트의 `agentmetrics`를 수집합니다. EKS 관리형 add-on에는 두 번째 CNI Helm release를 설치하지 말고 독립된 scraper/PodMonitor를 구성합니다. ```yaml apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: vpc-cni-signals namespace: monitoring labels: release: prometheus spec: groups: - name: vpc-cni rules: - record: vpc_cni:allocated_ipv4_pool_utilization:ratio expr: (awscni_assigned_ip_addresses{job="aws-vpc-cni",ip_family="ipv4"} / awscni_total_ip_addresses{job="aws-vpc-cni",ip_family="ipv4"}) and (awscni_total_ip_addresses{job="aws-vpc-cni",ip_family="ipv4"} > 0) - alert: CniHighAllocatedIPv4PoolUtilization expr: vpc_cni:allocated_ipv4_pool_utilization:ratio > 0.9 for: 5m labels: severity: info annotations: summary: Most currently allocated IPAMD IPv4 addresses are assigned description: This is allocated-pool utilization, not subnet exhaustion. Check warm targets, assignment failures and available subnet space. - alert: CniIPAssignmentFailures expr: increase(awscni_no_available_ip_addresses{job="aws-vpc-cni"}[5m]) > 0 for: 1m labels: severity: warning annotations: summary: IPAMD could not assign an available IP address - alert: CniMetricsScrapeFailed expr: up{job="aws-vpc-cni"} == 0 for: 5m labels: severity: warning annotations: summary: A known CNI metrics endpoint cannot be scraped ``` Pool 비율은 IPv4와 관측된 양수 분모로 한정합니다. 서브넷 고갈 증거가 아닌 정보성 튜닝 신호입니다. 할당 실패 counter는 실제 실패를 나타냅니다. 수집 실패와 대상 삭제는 다르므로 예상 노드·구성 요소 목록도 비교합니다. 데이터 없음은 정상 0이 아닙니다. 실제 관측 환경에 맞게 임계값, 레이블, 알림 전달을 조정합니다. ## 참고 자료 - [VPC CNI 1.23.0 documentation](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/README.md) - [VPC CNI Helm values](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/charts/aws-vpc-cni/values.yaml) - [Chart version metadata](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/charts/aws-vpc-cni/Chart.yaml) - [Released CNI manifest](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/config/master/aws-k8s-cni.yaml) - [IPAMD implementation](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/pkg/ipamd/ipamd.go) - [IPAMD introspection server](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/pkg/ipamd/introspect.go) - [IPAM datastore](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/pkg/ipamd/datastore/data_store.go) - [IPAMD metric definitions](https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/v1.23.0/utils/prometheusmetrics/prometheusmetrics.go) - [Network policy agent 1.4.1](https://github.com/aws/aws-network-policy-agent/blob/v1.4.1/README.md) - [AWS Helm chart index](https://aws.github.io/eks-charts/index.yaml) - [EKS security groups for Pods](https://docs.aws.amazon.com/eks/latest/userguide/security-groups-for-pods.html) - [SGPP operating considerations](https://docs.aws.amazon.com/eks/latest/best-practices/sgpp.html) - [EKS Multus support boundaries](https://docs.aws.amazon.com/eks/latest/userguide/pod-multus.html) - [EKS Windows networking](https://docs.aws.amazon.com/eks/latest/userguide/windows-support.html) - [EKS IPv6 support](https://docs.aws.amazon.com/eks/latest/userguide/cni-ipv6.html) - [eksctl IPv6 configuration](https://docs.aws.amazon.com/eks/latest/eksctl/vpc-ip-family.html) - [CNI IAM configuration](https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html) - [EKS VPC CNI management](https://docs.aws.amazon.com/eks/latest/userguide/managing-vpc-cni.html) - [EKS network policy conditions](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html) - [Enable EKS network policy](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy-configure.html) - [Prefix allocation and Pod-capacity limits](https://docs.aws.amazon.com/eks/latest/userguide/cni-increase-ip-addresses-procedure.html) ## 퀴즈 [VPC CNI 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/01-vpc-cni-quiz)로 이해를 확인합니다. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/ ---------------------------------------- # Cilium 딥다이브: 클라우드 네이티브 네트워킹의 미래 ## 개요와 검토 기준 Cilium 네트워킹, 정책, 관측성을 다룹니다. 예제 검토 기준은 **Cilium/Helm 차트 1.20.1**, Cilium CLI **0.20.0**, Hubble CLI **1.19.4**입니다. Cilium 1.20 호환성 문서의 Kubernetes 테스트 범위는 **1.33–1.36**이며 업스트림 1.37 출시만으로 자동 확대되지 않습니다. 호스트는 AMD64/AArch64 Linux, 커널 **5.10 이상** 또는 문서화된 배포판 동등 조건(예: RHEL 8.10의 backport된 4.18)을 충족해야 합니다. 개별 기능에는 추가 조건이 있습니다. > **마지막 업데이트**: 2026년 9월 12일 ### 릴리스 이력 아래 날짜는 GitHub 공개 시각의 UTC 날짜로 해당 릴리스 기록이며 현재 설치 버전 지정이 아닙니다. 릴리스 라인마다 백포트 내용이 다릅니다. | 날짜 | 릴리스 | 확인한 주요 내용 | | --- | --- | --- | | 2026-07-14 | [1.20.0-rc.0](https://github.com/cilium/cilium/releases/tag/v1.20.0-rc.0) | 1.20 첫 release candidate | | 2026-07-16 | [1.19.6](https://github.com/cilium/cilium/releases/tag/v1.19.6), [1.18.12](https://github.com/cilium/cilium/releases/tag/v1.18.12), [1.17.18](https://github.com/cilium/cilium/releases/tag/v1.17.18) | Gateway access-log 설정은 1.19.6/1.18.12에, 여기서 다루는 restart-policy·ClusterMesh affinity 수정은 1.19.6에 명시되어 있으며 세 릴리스 모두의 변경이 아님 | | 2026-07-21 | [1.20.0-rc.1](https://github.com/cilium/cilium/releases/tag/v1.20.0-rc.1) | 1.20 두 번째 release candidate | | 2026-07-29 | [1.20.0](https://github.com/cilium/cilium/releases/tag/v1.20.0) | GA 릴리스, 주요 변경은 아래 설명 | | 2026-08-03 | [1.21.0-pre.0](https://github.com/cilium/cilium/releases/tag/v1.21.0-pre.0) | 다음 사이클 prerelease, 이 가이드의 배포 기준이 아님 | | 2026-08-18 | [1.20.1](https://github.com/cilium/cilium/releases/tag/v1.20.1) | ClusterMesh 문서와 restart/CIDR-policy 처리 등 버그 수정 | | 2026-08-18 | [1.19.7](https://github.com/cilium/cilium/releases/tag/v1.19.7) | ENI 인터페이스 타이밍, Service/LB 등 수정 | | 2026-08-18 | [1.18.13](https://github.com/cilium/cilium/releases/tag/v1.18.13) | VRRP/IGMP host-firewall 지원 및 관련 수정 | 1.20.0 발표는 **2,660개 이상의 새 커밋**과 **1,100명 이상 기여자의 커뮤니티**를 소개합니다. 후자는 해당 릴리스 작성자 수가 아닌 커뮤니티 규모입니다. 주요 변경은 다음과 같습니다. - Gateway API **1.6.1**, TCPRoute/UDPRoute, BackendTLSPolicy, ListenerSets, ExternalAuth, CORS 지원이며 각각의 설정과 API 성숙도를 확인해야 합니다. - Datapath plugin과 명시적으로 선택하는 `bpf.datapathMode=auto`, 발표된 기본값은 여전히 veth입니다. Dual-stack 클러스터에는 IPv6 egress gateway 주소를 지정할 수 있습니다. - **Beta** IPv6 ENI IPAM 및 클러스터를 재구축하지 않는 cluster-pool→multi-pool 전환입니다. In-place가 중단 없음을 보장하지는 않습니다. - 트래픽 분배 힌트, 가중치 Maglev backend, stable MCS 통합, Kubernetes ClusterNetworkPolicy, **beta** ztunnel 워크로드 identity 지원입니다. - 발표된 `cilium-cni` 바이너리 크기 감소는 약 **77MB→16MB**입니다. ADS/Delta xDS 개선은 1.20 발표 내용이며 1.18.13 패치 내용으로 귀속하면 안 됩니다. 이 감사에서 다시 측정한 결과가 아닌 업스트림 릴리스의 설명입니다. 레거시 Mutual Authentication, Envoy Go 확장, Kafka 인지 정책, 이전 CiliumNodeConfig API, libnetwork, 사용자 정의 CNI 설정의 제거/변경은 [1.20 업그레이드 안내](https://docs.cilium.io/en/v1.20/operations/upgrade/#upgrade-notes)를 확인하세요. ### NetworkPolicy 보안 권고 [GHSA-fm8w-2m5w-9j7r / CVE-2026-56743](https://github.com/cilium/cilium/security/advisories/GHSA-fm8w-2m5w-9j7r)의 프로젝트 권고문 공개일은 **2026년 7월 6일**입니다. 프로젝트 API와 GitHub 통합 권고 API의 날짜는 다르며 통합 기록은 9월 3일입니다. 이 릴리스 연혁에는 프로젝트 공개일을 사용하며 통합 기록 날짜를 새로운 수정 릴리스 날짜로 취급하지 않습니다. 권고문이 설명하는 custom cluster-name 조건에서 **1.19.0–1.19.4**가 영향을 받으며, 표준 Kubernetes NetworkPolicy의 `ipBlock`만 있는 peer 규칙이 선택한 Pod와 같은 네임스페이스의 워크로드 ingress를 의도하지 않게 허용할 수 있습니다. 해당 문제는 **1.19.5**에서 수정되었고 실제 배포에는 적절한 최신 패치 버전을 선택해야 합니다. 권고문은 CiliumNetworkPolicy/ClusterwideNetworkPolicy와 1.19.0 이전 릴리스는 이 특정 문제의 영향을 받지 않는다고 설명합니다. ## 소개 Cilium은 지원되는 Linux Kubernetes 환경의 네트워킹, 보안, 관측성을 제공합니다. 라우팅, IPAM, 암호화, Service 처리는 별도 선택이며 eBPF 선택만으로 모든 기능이나 성능이 보장되지는 않습니다. 이전 Docker libnetwork 통합은 1.20에서 제거되었으므로 Docker/Mesos를 현재 동일한 설치 대상으로 나열하면 안 됩니다. ### eBPF와 주요 기능 커널은 eBPF 프로그램을 로드하기 전에 검증하고 지원 hook에서 실행하도록 JIT 컴파일할 수 있습니다. 별도 커널 모듈 없이 패킷 처리와 관측성을 구현할 수 있지만 verifier가 애플리케이션이나 정책의 정확성을 증명하지는 않습니다. 실제 처리량, 지연, 메모리는 프로그램·플랫폼·워크로드에 따라 달라집니다. Cilium은 L3/L4 정책, Envoy/DNS proxy 통합의 L7 정책, 선택적 WireGuard/IPsec, Service 부하 분산, Hubble flow 가시성, ClusterMesh, BGP 광고를 제공합니다. XDP 가속은 장치·설정에 따른 선택 기능입니다. L7은 노드별 Envoy를 사용할 수 있고 ztunnel 워크로드 identity에는 별도 beta 설정이 필요합니다. 에이전트 설치만으로 모든 mesh·암호화·멀티클러스터 동작이 활성화되지는 않습니다. ### 다른 네트워킹 프로젝트와 비교 | 프로젝트 | 연결 / IPAM | 정책과 관련 기능 | | --- | --- | --- | | Cilium | Native/overlay 라우팅, ENI 등 IPAM 모드 | eBPF, Cilium/Kubernetes 정책, L7 통합, Hubble, 선택적 암호화 | | Calico | Calico 또는 외부 IPAM과 native/IPIP/VXLAN 구성 | Linux Iptables/Nftables/BPF, 지원 Windows HNS, OSS WireGuard·staged policy·별도 L7 통합 | | Flannel | VXLAN/host-gw/WireGuard 등 선택한 backend의 Pod 연결 | 라우팅 데몬 자체는 NetworkPolicy를 강제하지 않지만 차트의 netpol.enabled로 SIGs 정책 컨트롤러를 함께 배포하거나 다른 정책 구현과 결합 가능 | | AWS VPC CNI | VPC ENI 주소 할당/네트워킹 | 지원 EC2 Linux의 네이티브 정책과 별도 SG-for-Pods, EKS Auto Mode는 다른 관리형 구현 | 라우팅 모드와 패킷 처리 구현은 같은 분류가 아닙니다. Calico는 iptables/IPVS로 제한되지 않고 Flannel에는 암호화 backend가 있으며 AWS 정책과 Security Group도 동일하지 않습니다. 서비스 메시는 선택 계층이고 클러스터 간 VPC 연결이 Transit Gateway로만 가능한 것도 아닙니다. 보편적인 성능 등급 대신 실제 워크로드와 지원 매트릭스를 비교하세요. ## 아키텍처 **Kubernetes API 서버**가 Kubernetes/Cilium 리소스를 저장합니다. Cilium Agent는 관련 상태를 감시해 노드 데이터플레인을 설정하고 Operator는 선택한 IPAM, identity/controller 작업 등 클러스터 수준 책임을 담당합니다. 기본 구조에 별도의 필수 클러스터 전체 “Cilium API Server” Deployment는 없습니다. Agent 로컬 API와 선택적 ClusterMesh API server는 서로 다른 목적입니다. | 컴포넌트 | 역할 | | --- | --- | | Cilium Agent | 노드 endpoint, 정책, routing/Service 상태, eBPF 관리 | | Cilium Operator | 클러스터 수준 조정과 모드별 할당/controller 작업 | | Envoy | 활성화한 L7 정책, Ingress/Gateway 등 userspace 프록시 | | Hubble server | Agent와 통합된 노드 로컬 flow API | | Hubble Relay / UI | Flow stream 집계 / 서비스 맵과 flow 표시 | | Prometheus metrics endpoint | 별도 통계 수집, Relay/UI는 metrics scrape 파이프라인이 아님 | | cilium / cilium-dbg / hubble | 클러스터 관리 CLI / Agent 진단 / Flow 클라이언트 | ### 네트워킹과 패킷 경로 Native routing에는 도달 가능한 underlay가 필요하고 tunneling은 VXLAN 또는 Geneve를 사용합니다. AWS ENI와 Azure IPAM은 플랫폼 전제가 있는 할당/통합 선택입니다. Cilium BGP Control Plane은 라우터에 경로를 광고하며 **datapath를 설정하거나 클러스터 내부 라우팅을 제공하지 않습니다**. 모든 패킷이 XDP→TC→Pod로 이동하는 것은 아닙니다. Socket load balancing은 패킷 생성 전에 작동할 수 있고 TC/netkit hook은 datapath에 따라 다르며 선택적 XDP는 일부 트래픽을 가속하고 L7은 Envoy를 거칠 수 있습니다. 반환 경로도 NAT, conntrack, DSR 선택에 따라 달라집니다. 해당 구성의 네트워킹/eBPF 장을 참고하세요. ## Amazon EKS와의 통합 설치 전에 실제 네트워킹과 컴퓨팅 구성을 선택하세요. 기존 예제의 `cilium` / `v1.17.0-eksbuild.1` 애드온 이름·버전은 검증된 AWS 배포판이 아니므로 설치 명령에서 제거했습니다. Vendor 애드온을 고려한다면 리전의 실제 카탈로그, 게시자, 라이선스, 지원 컴퓨팅 유형을 확인해야 합니다. | EKS 구성 | 확인할 사항 | | --- | --- | | 일반 EC2, Cilium ENI로 VPC CNI 교체 | 업스트림/파트너 관리 CNI이며 AWS가 지원하는 EC2 CNI는 VPC CNI, CNI 소유권·IAM·주소·경로·bootstrap·노드 전환 계획 필요 | | 일반 EC2, AWS VPC CNI chaining | VPC CNI가 인터페이스/IPAM을 소유하고 이후 Cilium이 연결, 기존 Pod 재생성과 L7/IPsec 제약 확인 | | Hybrid Nodes | AWS 전용 CNI 가이드와 AWS 유지 Cilium 빌드 매트릭스 사용, 업스트림 1.20.1이 자동으로 AWS 지원 빌드가 아님 | | Auto Mode | 대체 CNI/정책 플러그인 미지원, 관리형 NodeClass/네트워킹 사용 | | Fargate | 대체 CNI/DaemonSet 설치 미지원 | | Windows | Cilium Agent 요구사항은 Linux, 이 레시피를 Windows 워커에 적용하지 않음 | AWS 일반 대체 CNI 문서와 Hybrid 전용 가이드의 Calico 지원 표현은 다르며 예제 저장소 이동만으로 지원 종료가 입증되지는 않습니다. Hybrid는 정확한 배포판·기능·지원 주체를 확인하세요. Auto Mode의 node-local CoreDNS/시스템 네트워킹도 이 문서의 일반 EC2 구성과 다릅니다. Non-Auto 노드가 섞이면 기존 DNS Deployment가 여전히 필요합니다. ### 준비된 EC2 클러스터의 Cilium ENI 다음은 **준비된 IPv4 EC2 클러스터용 Cilium Helm values**이며 전체 클러스터 생성이나 in-place 전환 레시피가 아닙니다. 사용 전에 다음을 준비하세요. 1. 지원 EKS/Kubernetes 버전과 Linux AMI, 하나의 CNI 소유자를 선택합니다. 기존 워크로드 클러스터에서 `aws-node`를 삭제하는 단축 방법을 사용하지 않습니다. 2. Cilium이 노드를 관리하기 전까지 워크로드가 기다리도록 taint/스케줄링을 준비합니다. 업스트림 EKS 가이드는 `node.cilium.io/agent-not-ready=true:NoExecute`를 사용하므로 실제 노드 수명주기의 eviction/bootstrap 영향을 검토해야 합니다. 3. 서브넷 용량, ENI quota/Security Group, 노드 metadata 접근, operator의 EC2 권한을 준비합니다. 아래 ARN은 **cilium-operator ServiceAccount**를 올바르게 신뢰하는 역할의 자리표시자이며 values 파일이 역할을 생성하지는 않습니다. 4. 이 `kubeProxyReplacement: false` 예제는 작동하는 kube-proxy와 DNS를 유지합니다. 교체 모드는 별도 API 직접 접근/bootstrap DNS 조건을 따르세요. max-Pods는 보편적인 110이 아닌 실제 인스턴스/IPAM 용량으로 결정합니다. `cilium-eni-values.yaml`로 저장하고 역할·인터페이스를 검토한 값으로 변경하세요. ```yaml eni: enabled: true ipam: mode: eni routingMode: native kubeProxyReplacement: false ipv4: enabled: true ipv6: enabled: false egressMasqueradeInterfaces: eth0 serviceAccounts: operator: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/CiliumOperatorENI ``` ```bash helm repo add cilium https://helm.cilium.io/ helm repo update cilium helm template cilium cilium/cilium --version 1.20.1 \ --namespace kube-system -f cilium-eni-values.yaml > cilium-eni-rendered.yaml # 클러스터 준비와 렌더링 결과 검토 후: helm install cilium cilium/cilium --version 1.20.1 \ --namespace kube-system -f cilium-eni-values.yaml ``` 설치 소유 경로에서 설정을 관리하세요. 몇 개 키만 있는 `cilium-config`로 교체하면 다른 필수 설정을 잃을 수 있습니다. 이전 `tunnel=disabled` 대신 `routingMode: native`를 사용합니다. ENI 할당/권한과 SNAT 동작은 실제 환경에서 검증해야 하며 렌더링 성공만으로 EC2 네트워킹이 입증되지는 않습니다. **IPv6 조건:** 1.20.1 ENI IPAM 참조는 IPv6를 beta로 설명하지만 같은 버전 EKS 전제 페이지에는 여전히 IPv4-only ENI 제한이 있습니다. 이 가이드는 IPv4 예제를 유지하고 문서 불일치를 기록하며 어느 한 문장만으로 프로덕션 EKS IPv6 호환성이 증명되었다고 판단하지 않습니다. 별도 IPv6 설계에는 현재 ENI/dual-stack 서브넷 조건을 확인하세요. ### 대안: VPC CNI Chaining 업스트림 chaining 가이드는 VPC CNI 1.11.2 이상과 다음 구성을 문서화합니다. ```yaml cni: chainingMode: aws-cni exclusive: false enableIPv4Masquerade: false routingMode: native kubeProxyReplacement: false ``` ENI 교체 values 위에 추가하는 설정이 아닌 **별도 구성**으로 사용하세요. VPC CNI가 할당자로 남습니다. 과거 업스트림 DaemonSet을 적용하는 대신 실제 관리 애드온을 소유 경로에서 업데이트합니다. 같은 endpoint의 경쟁 정책 엔진을 피하세요. CNI chain이 바뀌어도 기존 Pod가 자동으로 Cilium에 연결되지는 않습니다. 계획한 rollout으로 재생성하고 endpoint 관리를 확인해야 합니다. Chaining에는 L7 정책/IPsec 제약이 있으므로 아래 모든 예제가 해당 구성에서도 작동한다고 가정하지 마세요. ### ClusterMesh 고유한 cluster identity, 호환 버전, 접근 가능하고 중복되지 않는 Pod 네트워크, 인증된 API 연결, 적절한 노출 방식이 필요합니다. LoadBalancer Service는 클라우드 리소스를 만들 수 있어 의도적인 네트워크/보안 설계가 필요합니다. 공개 endpoint 두 개만 만든다고 안전한 클러스터 연결이 완성되지는 않습니다. 선택한 토폴로지의 [ClusterMesh 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/cilium-service-mesh/01-architecture.md)와 고급 장을 따르세요. ## 설치 및 구성 ### 클라이언트 도구 워크스테이션 OS/아키텍처에 맞는 공식 Cilium CLI 0.20.0, Hubble CLI 1.19.4 자산을 사용하고 압축 해제 전에 제공된 체크섬을 검증하세요. Linux ARM64/AMD64는 다르며 macOS는 해당 Darwin 자산을 사용합니다. CLI 버전은 Cilium Agent/chart 버전과 별개입니다. [검증된 CLI 설치 안내](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/cilium-service-mesh/README.md)를 참고하세요. ```bash cilium version --client hubble version ``` ### 일반 Cluster-Pool 예제 다음은 kube-proxy와 DNS가 작동하는 일반 Linux 클러스터의 **별도 대안**입니다. 예시 Pod 범위 `10.244.0.0/16`이 클러스터와 맞고 Service, 노드, VPC, 연결 네트워크와 중복되지 않아야 합니다. ENI 모드에 이 pool 설정을 사용하지 마세요. ```yaml routingMode: tunnel tunnelProtocol: vxlan kubeProxyReplacement: false ipv4: enabled: true ipv6: enabled: false ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 hubble: enabled: true relay: enabled: true ui: enabled: true metrics: enabled: - dns - drop - tcp - flow - icmp - httpV2 ``` `cilium-values.yaml`로 저장하고 고정 버전 차트를 렌더링한 후 준비된 클러스터에만 설치합니다. ```bash helm template cilium cilium/cilium --version 1.20.1 \ --namespace kube-system -f cilium-values.yaml > cilium-rendered.yaml helm install cilium cilium/cilium --version 1.20.1 \ --namespace kube-system -f cilium-values.yaml cilium status --wait ``` 기존 release는 업그레이드/GitOps 경로에서 소유 values를 보존하며 버전별 절차를 따릅니다. `cilium install`을 반복하는 것은 개별 설정 변경의 일반적인 방법이 아닙니다. | 선택 | 현재 설정과 전제 | | --- | --- | | VXLAN/Geneve | `routingMode: tunnel`과 `tunnelProtocol`, 해당 캡슐화 허용과 경로 MTU 설정 | | Native routing | `routingMode: native`, underlay의 Pod 주소 라우팅 필요, `autoDirectNodeRoutes`는 임의의 다중 서브넷이 아닌 적합한 직접 연결 전제 | | kube-proxy 교체 | 이전 `strict` 대신 `kubeProxyReplacement: true`/`false`, 교체 시 도달 가능한 `k8sServiceHost`/`k8sServicePort`와 bootstrap 계획 필요 | | WireGuard | 커널/플랫폼과 peer 경로 확인 후 지원 모드 활성화, 모든 트래픽 자동 암호화가 아님 | | IPsec | 문서화된 key Secret, 키 배포/교체, 호환 모드 필요, Helm enable 플래그만으로 불충분 | | XDP/DSR/BBR | 장치/커널/토폴로지별 별도 선택, 보편적인 설치 프리셋이 아님 | ## 네트워크 정책 Kubernetes `networking.k8s.io/v1` NetworkPolicy와 Cilium `cilium.io/v2` 정책은 별도 API입니다. 여러 allow 정책이 합쳐질 수 있습니다. 다음은 L4 허용이 L7 제한을 우회하지 않도록 **각기 다른 준비된 테스트 네임스페이스**를 사용합니다. 실제 endpoint를 선택하는 모든 정책을 검토하세요. ### L4 예제 `cilium-l4-demo`에서 backend Pod를 선택하고 같은 네임스페이스 frontend Pod의 TCP 8080 ingress를 허용합니다. ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: cilium-l4-demo spec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - port: 8080 protocol: TCP ``` ### HTTP 예제 별도 `cilium-l7-demo`에서 backend Pod를 선택하고 해당 네임스페이스 frontend의 TCP 8080 평문 HTTP를 지정한 method/path로 제한합니다. 선택한 CNI 모드에서 L7 proxy를 지원해야 하며 암호화된 HTTP가 지원 termination 설정 없이 자동 검사되지는 않습니다. ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: allow-product-read namespace: cilium-l7-demo spec: endpointSelector: matchLabels: k8s:app: backend ingress: - fromEndpoints: - matchLabels: k8s:app: frontend k8s:io.kubernetes.pod.namespace: cilium-l7-demo toPorts: - ports: - port: '8080' protocol: TCP rules: http: - method: GET path: ^/api/v1/products$ ``` 같은 peer/port에 제한 없는 L4 allow를 추가하지 마세요. Cilium 문서는 이 경우 더 좁은 L7 제한이 효력을 잃는다고 설명합니다. L7 거부는 패킷 drop 대신 HTTP 403을 반환할 수 있습니다. 실제 endpoint identity로 허용 GET과 거부 method/path를 테스트하세요. ### DNS/FQDN 예제 `cilium-dns-demo`에서 일반 CoreDNS Pod로의 DNS 질의와 `api.example.com`에서 학습한 주소의 TCP 443을 허용합니다. 도메인은 예시이므로 승인한 대상으로 바꾸세요. 광범위한 `*.amazonaws.com`은 계정/리소스 경계가 아니므로 제외했습니다. ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: allow-api-domain namespace: cilium-dns-demo spec: endpointSelector: matchLabels: k8s:app: web egress: - toEndpoints: - matchLabels: k8s:k8s-app: kube-dns k8s:io.kubernetes.pod.namespace: kube-system toPorts: - ports: - port: '53' protocol: ANY rules: dns: - matchPattern: '*' - toFQDNs: - matchName: api.example.com toPorts: - ports: - port: '443' protocol: TCP ``` DNS wildcard는 선택한 resolver로 질의를 허용할 뿐 반환된 모든 주소로의 연결을 허용하지는 않습니다. 임의 DNS 이름을 전달할 수 있으므로 필요하면 search suffix까지 고려해 질의 이름을 제한하세요. NodeLocal DNS나 다른 resolver에는 알맞은 목적지 선택이 필요합니다. FQDN 정책은 DNS로 얻은 IP 인가이며 TLS hostname 검증이나 HTTP URL 인가가 아닙니다. 공유 IP와 애플리케이션 TLS/인증도 고려해야 합니다. ## Hubble 관측성 위 cluster-pool values는 Relay/UI와 메트릭을 활성화합니다. 기존 설치는 소유 경로에서 의도한 Hubble values를 적용하세요. `cilium hubble enable --ui`는 지원 편의 명령이지만 `cilium hubble enable --metrics=...`는 CLI 0.20.0의 지원 플래그가 아닙니다. Helm의 `hubble.metrics.enabled`를 설정하고 이전 `http`와 `httpV2` handler를 동시에 켜지 마세요. 한 터미널에서 Relay port-forward를 유지합니다. ```bash cilium hubble port-forward --port-forward 4245 ``` Hubble CLI가 있는 다른 터미널에서 실행합니다. ```bash hubble observe --server 127.0.0.1:4245 --namespace cilium-l7-demo hubble observe --server 127.0.0.1:4245 --protocol http hubble observe --server 127.0.0.1:4245 --from-label k8s:app=frontend --to-label k8s:app=backend hubble observe --server 127.0.0.1:4245 --verdict DROPPED hubble observe --server 127.0.0.1:4245 --http-status 403 ``` 로컬 예제는 Relay 서버 기본 구성을 전제로 하며 TLS를 켠 Relay에는 맞는 client trust/인증이 필요합니다. HTTP event는 해당 트래픽이 설정한 L7 proxy를 거쳐야 나옵니다. `DROPPED`는 datapath verdict이며 모든 애플리케이션 실패를 뜻하지 않습니다. UI port-forward는 `cilium hubble ui`를 사용하고 Prometheus target discovery는 별도로 설정합니다. Hubble flow streaming 자체가 분산 애플리케이션 tracing은 아닙니다. ## 테스트와 운영 Connectivity/performance 명령은 테스트 워크로드를 만들며 정책을 바꾸거나 상당한 트래픽을 생성할 수 있습니다. 검토한 테스트 네임스페이스/환경과 권한을 사용해야 합니다. 이 감사에서는 실제 클러스터에 실행하지 않았습니다. ```bash cilium connectivity test --help cilium connectivity perf --help ``` 성능 runner는 `cilium connectivity perf`입니다. `connectivity test --test=performance`는 테스트 이름 filter일 뿐 성능 runner가 아닙니다. 처리량/지연 비교 전에 소프트웨어 버전, 토폴로지, 트래픽, 원본 결과를 기록하세요. 조회 시 관리 CLI와 **에이전트 내부 `cilium-dbg`**를 구분합니다. ```bash cilium status --verbose kubectl get cnp,ccnp -A kubectl get pods -n kube-system -l k8s-app=cilium -o wide # 문제가 있는 노드의 Agent Pod를 선택합니다. CILIUM_POD=replace-with-actual-cilium-pod kubectl exec -n kube-system "$CILIUM_POD" -c cilium-agent -- cilium-dbg endpoint list kubectl exec -n kube-system "$CILIUM_POD" -c cilium-agent -- cilium-dbg map list kubectl exec -n kube-system "$CILIUM_POD" -c cilium-agent -- cilium-dbg metrics list kubectl logs -n kube-system "$CILIUM_POD" -c cilium-agent --since=15m --tail=200 --timestamps ``` 관리 CLI의 `cilium endpoint list`, `cilium bpf maps list`, `cilium metrics list`는 같은 명령이 아닙니다. `cilium sysdump`로 진단 자료를 모을 수 있지만 인프라/로그 자료를 보호해야 합니다. Agent Ready나 scrape 성공은 애플리케이션과 거부 정책 테스트를 대신하지 않습니다. ### 운영 우선순위 - Map preallocation, XDP, DSR, BBR, 고정 device 패턴을 켜기 전에 측정하세요. 자원을 사용하거나 패킷 경로를 바꾸므로 기능별 검증이 필요합니다. - 선택한 범위에서 DNS, API, identity, 애플리케이션 의존성을 명시적으로 허용한 뒤 default-deny를 도입합니다. 기존 연결뿐 아니라 신규 Pod와 업그레이드 전환도 확인하세요. - 암호화, 인증서/키 교체, 정책 적용, 관측성을 별도 인수 검사로 다룹니다. 사용할 수 있는 관리/복구 경로를 보존하세요. - 현재 플랫폼 매트릭스와 지원 업그레이드 경로를 확인합니다. 과거 릴리스 발표로 현재 배포 호환성이 증명되지는 않습니다. ## 딥다이브 목차 **[Cilium 소개 및 기본 개념](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/01-introduction.md)** - Cilium 개요 및 역사 - 컨테이너 네트워킹 기초 - CNI(Container Network Interface) 이해하기 - Cilium의 차별화 포인트 **[eBPF 기술 심층 분석](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/02-ebpf.md)** - eBPF 기술 소개 및 역사 - 커널 내 eBPF 작동 방식 - eBPF 프로그램 유형 및 맵 - Cilium에서의 eBPF 활용 **[네트워킹 모델 및 VXLAN](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/03-networking.md)** - 컨테이너 네트워킹 모델 비교 - VXLAN 기술 심층 분석 - Cilium의 오버레이 네트워킹 - 성능 최적화 기법 - 라우팅 메커니즘 (Encapsulation vs Native-Routing) - 클라우드 제공업체별 네트워킹 (AWS ENI, Google Cloud) **[IPAM 및 네트워크 정책](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/04-ipam-policy.md)** - IP 주소 관리(IPAM) 전략 - Kubernetes와 Cilium IPAM 통합 - 네트워크 정책 설계 및 구현 - 멀티 클러스터 시나리오 - IPAM 모드 심층 분석 (Cluster Scope, Kubernetes Host Scope, Multi-Pool) - 클라우드 제공업체별 IPAM (Azure IPAM, AWS ENI, GKE) - CRD 기반 IPAM **[L2-L7 네트워킹 및 로드 밸런싱](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/05-l2-l7-networking.md)** - OSI 모델 계층 이해 (L2, L3, L4, L7) - Cilium의 계층별 기능 - 서비스 메시 통합 - 로드 밸런싱 아키텍처 - 마스커레이딩 구성 및 구현 모드 - IPv4 프래그먼트 처리 **[보안 및 가시성](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/06-security-visibility.md)** - Cilium의 보안 기능 - 네트워크 가시성 및 모니터링 - Hubble 아키텍처 및 활용 - 실시간 위협 탐지 **[고급 주제 및 실제 사례](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/07-advanced-topics.md)** - 성능 튜닝 및 문제 해결 - 대규모 배포 전략 - 실제 사용 사례 연구 - 미래 로드맵 및 발전 방향 ## 추가 자료 - [네트워킹 개념 심층 분석](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/networking-concepts.md) - [용어 및 약어](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/glossary.md) ## 참고 자료 - [Cilium 1.20 Kubernetes 호환성](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/kubernetes/compatibility.rst) - [Cilium 시스템 요구사항](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/operations/system_requirements.rst) - [EKS 전제조건](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/installation/requirements-eks.rst) - [Cilium ENI IPAM](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/eni.rst) - [AWS 대체 CNI 지원](https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html) - [AWS Hybrid Nodes CNI](https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html) - [Cilium L7 정책 의미](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/policy/layer7.rst) - [Hubble 프로젝트](https://github.com/cilium/hubble) - [Flannel 네트워킹/정책 통합](https://github.com/flannel-io/flannel) - [Calico 비교 용어](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/glossary.md) ## 퀴즈 이 섹션에서 배운 내용을 테스트하려면 [Cilium 딥다이브 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/01-introduction-quiz)를 풀어보세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/01-introduction ---------------------------------------- # Part 1: 소개 > **검토 기준**: Cilium 1.20.1 / Cilium CLI 0.20.0 / Hubble CLI 1.19.4. **마지막 업데이트**: 2026년 9월 12일 ## 실습 환경 설정 격리된 준비 환경을 사용하세요. Cilium 1.20의 Kubernetes 테스트 범위는 **1.33–1.36**입니다. 노드는 AMD64/AArch64 Linux와 커널 **5.10 이상**, 또는 문서화된 동등 조건(예: RHEL 8.10의 backport된 4.18)을 충족해야 합니다. Kind/minikube는 노드/VM 커널을 사용하므로 워크스테이션 OS 이름만으로 호환성이 확인되지는 않습니다. 기능별 추가 조건도 확인하세요. Kubectl은 API 서버와 한 마이너 버전 이내여야 합니다. [메인 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)에 따라 OS/아키텍처에 맞는 Cilium/Hubble CLI 자산과 체크섬을 검증하세요. Helm으로 설정을 렌더링·검토할 수 있으며 이 감사는 Helm 3.21.3을 사용했습니다. 검증하지 않은 `latest` AMD64 다운로드나 장마다 같은 release 재설치를 반복하지 마세요. ### 선택한 구성으로 한 번 설치 EKS ENI, VPC CNI chaining, 일반 cluster-pool은 전제조건이 다릅니다. [메인 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)를 참고하세요. 다음은 kube-proxy/DNS가 작동하고 하나의 CNI 소유자를 준비한 **일반 IPv4 cluster-pool 대안**입니다. EKS 마이그레이션 레시피가 아닙니다. Pod CIDR이 Service, 노드, 연결 네트워크와 겹치지 않는지 확인하세요. `cilium-lab-values.yaml`로 저장합니다. ```yaml routingMode: tunnel tunnelProtocol: vxlan kubeProxyReplacement: false ipv4: enabled: true ipv6: enabled: false ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 hubble: enabled: true relay: enabled: true ui: enabled: true metrics: enabled: - dns - drop - tcp - flow - icmp - httpV2 ``` ```bash CILIUM_LAB_CONTEXT=replace-with-nonproduction-context kubectl --context "$CILIUM_LAB_CONTEXT" get nodes -o wide cilium version --client # 클러스터/CNI 소유권과 values를 준비한 새 설치에만 적용합니다. cilium install --context "$CILIUM_LAB_CONTEXT" --version 1.20.1 \ --values cilium-lab-values.yaml cilium status --context "$CILIUM_LAB_CONTEXT" --wait ``` 이미 Cilium이 있다면 버전/설정을 확인하고 소유자의 업그레이드 절차를 따르세요. 설치/status 명령이나 이후 connectivity test가 프로덕션 호환성을 증명하지는 않습니다. 이 감사에서 클러스터를 프로비저닝하지 않았습니다. ## Cilium이란? Cilium은 Linux eBPF 데이터플레인과 Kubernetes 통합으로 네트워킹, 보안, 관측성을 제공합니다. Endpoint 정책, 모드별 IPAM/라우팅, Service 처리, Hubble flow 가시성이 포함됩니다. Docker libnetwork 통합은 1.20에서 제거되었으므로 Kubernetes, Docker, Mesos를 현재 동일한 설치 대상으로 제시하면 안 됩니다. ### 핵심 컴포넌트와 기능 | 컴포넌트 / 기능 | 역할과 조건 | | --- | --- | | Cilium Agent | 노드 endpoint/dataplane 관리, 모든 호스트 네트워킹 기능을 소유하는 것은 아님 | | Cilium Operator | 클러스터 할당/identity/controller 작업, 여러 replica와 해당 작업의 leader election 지원, 검토한 차트 기본 replica는 2 | | eBPF | 검증과 기능 요건을 따르는 커널 hook의 프로그램/map, 실제 성능은 측정 필요 | | L3/L4와 L7 정책 | L7은 지원 Envoy/DNS proxy 경로 필요, Kafka 인지 L7은 1.20에서 제거되었지만 Kafka 연결의 L4 제어는 가능 | | kube-proxy 교체 | 선택적인 Service 처리, DSR·Maglev·XDP는 별도 설정/토폴로지 조건 | | 암호화 | `encryption.type`에서 `ipsec`, `wireguard`, beta `ztunnel`을 선택하며, ztunnel 워크로드 mTLS는 별도의 enrollment·bootstrap·트래픽·정책 전제가 필요 | | Hubble | 네트워크/proxy flow와 서비스 맵 관측, 자동 end-to-end 애플리케이션 tracing이 아님 | | ClusterMesh / BGP | ClusterMesh는 identity·신뢰·네트워크 도달성 필요, BGP는 광고 기능이며 내부 라우팅을 설정하지 않음 | 컴포넌트 관계는 kubelet이 **CRI**로 Pod sandbox 작업을 요청하고, 컨테이너 런타임이 **CNI 플러그인**을 호출하며, Cilium이 endpoint 설정을 조정하고 agent가 dataplane을 설정하는 구조입니다. CNI는 패킷마다 통과하는 전달 계층이 아닙니다. Envoy는 설정한 L7 proxy 트래픽을 처리하고 Hubble flow event와 Prometheus scrape는 별도 경로입니다. ### 보안 Identity Security identity는 해당 할당 범위에서 endpoint의 **보안 관련 레이블 집합**에 할당한 숫자 식별자입니다. 레이블은 필터링/설정되며 namespace에서 파생한 레이블을 포함할 수 있습니다. 같은 `app` 값만으로 네임스페이스나 클러스터가 다른 Pod가 같은 identity를 공유한다고 보장할 수 없습니다. 숫자 ID는 영구적 전역 hash나 Pod IP가 아니며 다른 endpoint 유형도 identity를 사용합니다. ## 컨테이너 네트워킹 기초 Host networking은 호스트 network namespace를 공유합니다. Bridge는 호스트의 인터페이스를 연결하고 overlay는 underlay 위에 캡슐화합니다. Native routing은 underlay가 Pod 주소에 도달할 수 있어야 합니다. 이 개념은 함께 사용될 수 있으며 eBPF와 Netfilter 구현 선택과 혼동하면 안 됩니다. 운영에서는 주소 용량, 라우팅/MTU, Service 동작, tenant 정책, 관측성, 장애 복구를 검토합니다. 네트워크 모델 하나만으로 성능이나 보안이 결정되지는 않습니다. ## CNI 이해하기 CNI는 컨테이너 네트워크 설정을 위한 CNCF 규격/라이브러리/플러그인 체계입니다. JSON으로 설정과 결과를 교환하고 IPAM 플러그인에 주소 할당을 위임할 수 있습니다. CNI의 설정/제거 계약은 kubelet과 컨테이너 런타임 사이의 CRI와 다릅니다. Kubernetes 1.24부터 kubelet은 제거된 `--network-plugin`/`--cni-bin-dir` 설정 플래그를 소유하지 않습니다. | 프로젝트 | 구분할 사항 | | --- | --- | | Cilium | Linux eBPF, 여러 routing/IPAM 모드, proxy를 사용하는 L7 정책과 Hubble | | Calico | Linux Iptables/Nftables/BPF와 지원 Windows HNS, OSS WireGuard·staged policy·별도 L7 통합 | | Flannel | VXLAN/host-gw/WireGuard 등 연결 backend, 선택적 차트 컨트롤러나 다른 구현으로 정책 추가 | | AWS VPC CNI | VPC 주소 할당/네트워킹, 지원 EC2 Linux의 네이티브 정책과 별도 SG-for-Pods | | Weave Net | 원래 weaveworks/weave 저장소는 archived 상태, 과거 선택지로 구분하고 사용할 유지 배포판은 별도 확인 | 지원 경계는 [현재 비교](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)를 참고하세요. 상한 없는 Kubernetes 호환성이나 “매우 높음/높음/중간” 성능 등급을 배포 근거로 사용하지 마세요. 서비스 메시는 일반 Pod 네트워킹의 필수 요건이 아닌 선택적 통합입니다. ## 실습: 제한된 L4 정책 선택한 context와 전용 네임스페이스를 사용합니다. 예제는 정책을 정의하며 애플리케이션 서버나 클라이언트 이미지를 **배포하지 않습니다**. 승인된 이미지/도구로 controller가 관리하는 테스트 워크로드를 준비하세요. - `app=backend` 레이블과 TCP 8080 listener가 있는 backend Pod. - 적절한 테스트 클라이언트가 있는 `app=frontend` Pod와 다른 레이블의 client Pod. - 일반 관리 Pod 인터페이스, 기본 레이블 처리, 확인한 DNS/Service 구성, 의도한 격리를 무효화할 다른 matching allow 정책이 없는 환경. 네임스페이스 정의를 `cilium-intro-namespace.yaml`로 저장·적용한 뒤 테스트 워크로드를 준비하세요: ```yaml apiVersion: v1 kind: Namespace metadata: name: cilium-intro-demo ``` ```bash kubectl --context "$CILIUM_LAB_CONTEXT" apply -f cilium-intro-namespace.yaml ``` 다음을 `cilium-intro-policy.yaml`로 저장합니다. ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: allow-frontend-backend namespace: cilium-intro-demo spec: endpointSelector: matchLabels: k8s:app: backend ingress: - fromEndpoints: - matchLabels: k8s:app: frontend k8s:io.kubernetes.pod.namespace: cilium-intro-demo toPorts: - ports: - port: '8080' protocol: TCP ``` ```bash kubectl --context "$CILIUM_LAB_CONTEXT" get pods -n cilium-intro-demo --show-labels kubectl --context "$CILIUM_LAB_CONTEXT" apply -f cilium-intro-policy.yaml kubectl --context "$CILIUM_LAB_CONTEXT" get cnp -n cilium-intro-demo ``` 위 전제와 추가 matching allow가 없을 때 **일반 Pod 간 ingress**의 기대 결과는 다음과 같습니다. | 출발지 / 목적지 | 기대 결과 | | --- | --- | | 같은 namespace frontend → backend TCP 8080 | 허용 | | 다른 client 레이블 → backend TCP 8080 | 거부 | | Frontend → backend의 다른 port/protocol | 이 정책에서는 허용하지 않음 | | 다른 namespace의 같은 app 레이블 | 이 정책에서는 허용하지 않음 | 정책 반영 후 성공/실패 연결을 모두 검증하세요. 다른 allow/deny 정책, host 트래픽과 probe는 결과를 바꿀 수 있습니다. 이 ingress 규칙은 frontend egress나 HTTP method/path를 제한하지 않습니다. API 접수 성공을 적용 성공으로 가정하지 말고 실제 트래픽과 endpoint 상태를 확인하세요. `cilium connectivity test`는 워크로드와 정책을 만드는 추가 테스트 runner입니다. 필요한 권한을 갖춘 검토 환경에서만 실행하며 읽기 전용 status 명령이 아닙니다. 별도 성능 runner는 `cilium connectivity perf`이며 측정 시 실제 버전·토폴로지·원본 결과를 보존하세요. ## 참고 자료와 다음 단계 - [Cilium 요구사항과 설정](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) - [CNI 프로젝트](https://github.com/containernetworking/cni) - [Kubernetes network plugin과 runtime 소유권](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) - [Kubernetes client version skew](https://kubernetes.io/releases/version-skew-policy/) - [Cilium identity와 용어](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/gettingstarted/terminology.rst) - [원래 Weave 저장소 metadata](https://api.github.com/repos/weaveworks/weave) [eBPF](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/02-ebpf.md)로 진행하거나 [소개 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/01-introduction-quiz)로 이해를 확인하세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/02-ebpf ---------------------------------------- # eBPF 기술 심층 분석 > **검토 기준**: Cilium 1.20.1, Linux 5.10+ 또는 문서화된 동등 백포트(예: RHEL 8.10의 4.18 커널), 테스트된 Kubernetes 1.33–1.36. 개별 BPF 기능에는 별도 조건이 있습니다. > **최종 검토**: 2026년 9월 12일 ## 실습 환경 설정 유지보수되는 배포판, 아래 tracepoint, 추적 프로그램을 로드할 권한이 있는 일회용 Linux 개발 VM을 사용합니다. Cilium 설치와 별개 실습이므로 실험용 프로그램을 클러스터 노드에 로드하지 않습니다. 검증기 설명의 소스 기준은 Linux 6.12이며 모든 6.12 배포판에서 모든 기능이 활성화되어 있다는 뜻은 아닙니다. BPF 백엔드가 있는 Clang, 대상 아키텍처의 UAPI 헤더, libbpf 1.x 개발 헤더·라이브러리, libelf, zlib, C 컴파일러와 bpftool이 필요합니다. BCC와 bpftrace는 선택 가능한 다른 도구입니다. Debian/Ubuntu에서는 보통 `clang`, `libbpf-dev`, `libelf-dev`, `zlib1g-dev`, `build-essential`, `pkg-config`를 사용하지만 bpftool 패키징은 배포판·커널에 따라 다릅니다. 모든 Debian 시스템에 `linux-tools-generic`이 맞는다고 가정하지 않습니다. Cilium은 AMD64/AArch64 호스트를 문서화하며 컨테이너 이미지 밖에서 Cilium을 네이티브 실행할 때는 Clang/LLVM 18.1+가 추가로 필요합니다. 이는 작은 추적 실습의 요구사항과 별개입니다. ```bash uname -r clang --version clang --print-targets pkg-config --modversion libbpf bpftool version test -r /sys/kernel/tracing/events/syscalls/sys_enter_execve/format test -r /sys/kernel/tracing/events/sched/sched_process_exec/format # 준비된 실습 VM에서만 BPF 기능을 능동적으로 탐색합니다. sudo bpftool feature probe kernel ``` Tracefs가 마운트되어 있고 접근 가능해야 합니다. 일부 시스템에서는 `/sys/kernel/debug/tracing`에 노출됩니다. 커널 설정, capability, lockdown/LSM 정책, 컨테이너 제약 때문에 컨테이너 root도 로드·연결하지 못할 수 있습니다. `CAP_BPF` 하나가 모든 추적 권한을 뜻하지 않으며 필요한 권한은 커널, 프로그램 타입, BPF token 위임에 따라 다릅니다. **검증 범위:** 예제는 libbpf 1.7 헤더를 사용한 호스트 C 문법 검사, 사용자 공간 링크, 결정적인 헬퍼 시뮬레이션을 통과했습니다. Clang BPF 대상 컴파일, 실행 커널 검증기와 실제 tracepoint 연결은 준비된 VM에서 추가 검증해야 합니다. 운영 환경 검증이나 무손실 추적을 보장하는 예제가 아닙니다. ## eBPF 기술 소개 및 역사적 배경 eBPF는 허용된 프로그램을 Linux의 지원 훅에서 실행하여 커널 동작을 관찰하거나 제어합니다. 검증기는 메모리 접근과 실행을 제한하지만 커널, 검증기, JIT, 헬퍼의 버그 가능성은 남습니다. 검증 통과가 호스트 장애 불가능이나 의도한 정책의 정확성을 보장하지는 않습니다. ### BPF에서 eBPF로: 발전 역사 McCanne과 Jacobson의 *The BSD Packet Filter: A New Architecture for User-level Packet Capture*에는 1992년 12월 19일 사전 원고 날짜와 1993년 1월 25–29일 Winter USENIX 발표가 함께 명시되어 있습니다. 연도를 인용할 때 이 차이를 보존합니다. Classic BPF는 32비트 A/X 레지스터와 scratch 메모리로 필터링하여 불필요한 사용자 공간 패킷 복사를 줄였습니다. 제한된 명령어 집합이 현대 CPU에서 실행할 수 없다는 뜻은 아닙니다. 확장 BPF는 64비트 명령어 집합, R0–R10의 레지스터 11개(R10은 읽기 전용 프레임 포인터), 일반적으로 512바이트로 제한되는 스택, 맵과 다양한 프로그램 타입을 추가했습니다. 범용 레지스터가 역사적으로 10개에서 11개로 늘어났다는 뜻은 아닙니다. 함수·tail call 조합에는 추가 스택 제약이 있습니다. ### eBPF의 기술적 진화: 커널 버전별 주요 기능 버전이 고정된 업스트림 소스에서 확인한 주요 변화입니다. 배포판 지원표는 아니며 백포트, 빌드 옵션, 아키텍처와 헬퍼 지원은 다를 수 있습니다. | Kernel | 주요 변화 | |---|---| | [3.15](https://github.com/torvalds/linux/blob/v3.15/include/linux/filter.h) | 확장 명령어 집합과 내부 classic BPF 변환 | | [3.16](https://github.com/torvalds/linux/blob/v3.16/arch/x86/net/bpf_jit_comp.c) | x86 확장 BPF JIT | | [3.18](https://github.com/torvalds/linux/blob/v3.18/include/uapi/linux/bpf.h) | BPF 시스템 호출·검증 기반; 아직 사용 가능한 HASH/ARRAY 타입 없음 | | [3.19](https://github.com/torvalds/linux/blob/v3.19/include/uapi/linux/bpf.h) | HASH/ARRAY 맵과 socket-filter 프로그램 타입 | | [4.1](https://github.com/torvalds/linux/blob/v4.1/include/uapi/linux/bpf.h) | KPROBE와 TC SCHED_CLS/SCHED_ACT | | [4.2](https://github.com/torvalds/linux/blob/v4.2/include/uapi/linux/bpf.h) | PROG_ARRAY와 tail call | | [4.8](https://github.com/torvalds/linux/blob/v4.8/include/uapi/linux/bpf.h) | XDP 프로그램 타입 | | [4.10](https://github.com/torvalds/linux/blob/v4.10/include/uapi/linux/bpf.h) | LRU 해시 맵 | | [4.16](https://github.com/torvalds/linux/blob/v4.16/include/uapi/linux/bpf.h) | BPF-to-BPF 함수 호출 | | [4.17](https://github.com/torvalds/linux/blob/v4.17/include/uapi/linux/bpf.h) | Raw tracepoint | | [4.18](https://github.com/torvalds/linux/blob/v4.18/include/uapi/linux/bpf.h) | BTF 로드 API | | [5.2](https://github.com/torvalds/linux/blob/v5.2/include/uapi/linux/bpf.h) | 전역 데이터에 사용하는 맵 값 직접 접근 | | [5.7](https://github.com/torvalds/linux/blob/v5.7/include/uapi/linux/bpf.h) | BPF link API와 BPF LSM | | [5.8](https://github.com/torvalds/linux/blob/v5.8/include/uapi/linux/bpf.h) | BPF 링 버퍼 | | [5.10](https://github.com/torvalds/linux/blob/v5.10/include/uapi/linux/bpf.h) | 지원되는 연결 타입의 sleepable 프로그램 | | [5.15](https://github.com/torvalds/linux/blob/v5.15/include/uapi/linux/bpf.h) | BPF 타이머 헬퍼 | | [5.19](https://github.com/torvalds/linux/blob/v5.19/include/uapi/linux/bpf.h) | 동적 포인터 헬퍼 | | [6.2](https://github.com/torvalds/linux/blob/v6.2/kernel/bpf/helpers.c) | 타입이 있는 객체 할당 kfunc; 임의 malloc과는 다름 | Bounded loop는 Linux 5.3에 도입되었습니다. [업스트림 검증기 변경](https://github.com/torvalds/linux/commit/2589726d12a1b12eaaa93c7f1ea64287e383c7a5)은 루프 분석과 상태 가지치기를 설명합니다. 설계 FAQ에 남아 있는 “루프 미구현” 문단을 현재 기능 안내로 사용하면 안 됩니다. 제한된 루프도 검증 복잡도 한도를 초과할 수 있습니다. ### 생태계 성장과 활용 분야 Cilium 공개 저장소는 2015년 12월 생성되었으므로 프로젝트가 2017년에 처음 시작되었다는 설명은 부정확합니다. 저장소 생성일이 정확한 제품 출시일이나 “최초의 주요 프로젝트”라는 순위의 근거는 아닙니다. | 분야 | 예와 경계 | |---|---| | 네트워킹 | Cilium/Calico 데이터플레인, Katran 로드밸런싱, XDP 필터링 | | 런타임 보안 | Falco, Tracee, Tetragon의 커널 이벤트 활용; 차단 기능은 제품·훅에 따라 다름 | | 추적 | Python/Lua 프런트엔드를 포함한 BCC, bpftrace, 스토리지·블록 I/O 추적 | | 네트워크 관측 | Hubble의 플로우·프록시 이벤트; 플로우 그래프가 분산 애플리케이션 span 추적은 아님 | | 서비스 메시 | Cilium이 커널 전달과 사용자 공간 프록시를 조합하여 지원되는 L7 기능 제공 | | 커뮤니티 | eBPF Foundation이 생태계를 지원하며 투자·성숙도가 호환성 기준은 아님 | `seccomp-bpf`는 시스템 호출 판단에 classic BPF 필터 인터페이스를 사용합니다. Linux가 내부에서 classic 필터를 변환할 수 있지만 일반 eBPF 프로그램·맵·헬퍼 API와 같지는 않습니다. ### eBPF와 전통적 커널 모듈 비교 | 특성 | eBPF | 커널 모듈 | |---|---|---| | 안전성 | 검증기로 실행 제한; 구현 버그와 운영 위험은 남음 | 더 넓은 네이티브 커널 접근; 버그로 호스트 장애 가능 | | 배포 | 지원 프로그램은 재부팅 없이 로드·연결 가능 | 의존성·사용 상태가 허용하면 많은 모듈도 재부팅 없이 로드·해제 가능 | | 호환성 | 명령어·헬퍼 ABI와 기능 조건; CO-RE는 지원되는 타입 접근 재배치 | 커널·모듈 ABI, 설정, 배포판 지원에 의존 | | 성능 | 흔히 JIT 사용; 훅·프로그램·워크로드에 따른 오버헤드 | 네이티브 실행도 워크로드에 따른 비용 발생 | | 개발 | 제한된 context, 헬퍼·kfunc, 검증기 한도 | 커널 API와 일반적인 커널 개발 제약 | | 권한 | 로드·연결을 위한 적절한 권한 또는 위임 필요 | 특권 로드; 서명·lockdown 제약 가능 | 두 방식 모두 운영 검증이 필요합니다. 모듈이 벤더 구현으로만 제한되지 않으며 eBPF라는 이유만으로 운영 배포가 안전해지지 않습니다. ## 커널 내부 eBPF 아키텍처 심층 분석 ### eBPF 아키텍처 구성 요소 상세 설명 사용자 공간에서 Clang은 C를 BPF ELF로 컴파일하고 Rust는 자체 컴파일러·도구 생태계를 사용합니다. libbpf는 ELF section, 맵, 재배치, 로드와 지원되는 연결 API를 처리합니다. BCC는 상위 API, bpftrace는 추적 언어를 제공합니다. CO-RE는 BTF와 재배치로 지원되는 타입·필드 접근을 조정합니다. 없는 헬퍼, 프로그램 타입, 커널 설정을 제공하거나 임의의 아키텍처·커널 간 호환성을 보장하지 않습니다. BTF를 쓴다고 커널 내부 구조, tracepoint 형식, kfunc가 안정적인 ABI가 되지는 않습니다. 커널 검증기는 프로그램 타입, context, 헬퍼와 권한을 검사합니다. JIT는 허용된 BPF를 네이티브 명령어로 변환할 수 있으며 인터프리터는 지원 환경의 다른 실행 방식입니다. JIT 출력이 반드시 별도 VM 단계를 다시 통과하는 구조는 아닙니다. 연결 단계가 로드된 프로그램과 훅을 연결하며 로드만으로 tracepoint를 구독하지 않습니다. ### eBPF 프로그램 생명주기 상세 분석 1. **개발:** 훅/context와 맵·라이선스 메타데이터를 정의합니다. 모든 프로그램에 GPL 호환성이 필요한 것은 아니지만 GPL 전용 헬퍼와 일부 타입·kfunc에는 제한이 있습니다. 이 추적 예제는 헬퍼에 맞춰 GPL 메타데이터를 사용합니다. 2. **컴파일:** 대상 도구·헤더로 BPF ELF와 필요한 debug/BTF 정보를 만듭니다. 3. **열기·로드:** ELF를 읽고 맵을 생성하거나 명시적으로 재사용하며 재배치와 BPF 로드 API를 수행합니다. 검증과 선택적 JIT는 로드 중 발생합니다. 4. **연결:** 적절한 API를 사용합니다. libbpf는 아래 `SEC("tracepoint/...")`에서 훅을 추론할 수 있으며 link/연결의 수명을 유지해야 합니다. 5. **실행·관찰:** 이벤트가 프로그램을 호출하고 사용자 공간이 맵·버퍼를 읽습니다. 샘플링·용량 제한 때문에 관측이 누락될 수 있습니다. 6. **갱신·해제:** 필요한 경우에만 호환 맵·link·pin을 의도적으로 유지합니다. 이 실습에서는 link와 object를 닫아 자원을 해제합니다. Linux 6.12에서 BPF 권한이 있는 로드 경로의 프로그램 길이는 최대 1,000,000개 명령어, 비특권 경로는 4,096개로 제한됩니다. 검증기에는 별도로 1,000,000개 명령어의 **분석 복잡도** 한도가 있습니다. 더 작은 프로그램도 검증에 실패할 수 있습니다. 비특권 BPF는 비활성화된 경우가 많으며 token/capability와 프로그램 타입 검사도 적용됩니다. ### eBPF 프로그램 유형과 특성 | 훅 / 프로그램 타입 | 용도와 반환값의 경계 | |---|---| | XDP / `BPF_PROG_TYPE_XDP` | Native driver XDP는 skb 할당 전에 실행하며 generic/offload 모드는 다름. `XDP_DROP`, `PASS`, `TX`, `REDIRECT`는 동작이지 처리량 보장이 아님 | | TC / `SCHED_CLS`, `SCHED_ACT` | Ingress/egress 패킷 분류·동작. Classifier의 `TC_ACT_*` 의미에는 적절한 direct-action 설정 필요 | | Socket filter / `SOCKET_FILTER` | 소켓 패킷 전달: 0은 폐기, 양수 캡처 길이는 절단 가능. 생성·connect 정책은 다른 훅 사용 | | kprobe/uprobe / `KPROBE` | 커널·사용자 공간 probe. 별도 `BPF_PROG_TYPE_UPROBE` 없음. 인라이닝·금지 목록·심볼 존재가 연결 제한 | | Tracepoint / `TRACEPOINT` | 정적 이벤트 context. 대상 format 확인 필요; 안정적인 커널 ABI 보장 아님 | | Perf event / `PERF_EVENT` | 성능 샘플링; 반환 동작은 perf-event 통합에 따름 | | cgroup / `CGROUP_SKB`, `CGROUP_SOCK`, `CGROUP_SOCK_ADDR` 등 | 네트워크·소켓 제어; context와 허용·거부 규칙이 다름 | | LSM / `LSM` | MAC 방식은 보통 이전 오류를 유지하고 0/오류 반환; cgroup-LSM 허용 의미는 다름 | | Socket operations / `SOCK_OPS` | TCP 콜백; 동작·reply 필드·헬퍼 지원 확인 필요 | | fentry/fexit / `TRACING` | 지원 대상의 BTF 기반 함수 추적; 대상·연결 제약은 남음 | 필요한 가시성·제어로 훅을 선택합니다. XDP에는 후단 스택 context 일부가 없고 TC는 skb 기반 트래픽을 처리합니다. Tracepoint/probe 관측이 자동으로 정책을 집행하는 것은 아닙니다. 타입 간 context 구조나 반환 코드를 그대로 복사하지 않습니다. ### eBPF 맵: 데이터 공유와 상태 저장의 핵심 맵은 FD, 로드된 프로그램, 명시적인 bpffs pin 등의 참조가 남아 있는 동안 존재합니다. Pin은 디스크 영속 저장이 아니며 재부팅 후 내용도 보존하지 않습니다. 재로드 시 이전 맵을 자동 재사용하지 않습니다. | 타입 | 용도와 제약 | |---|---| | `HASH` | 용량 제한 키·값 테이블. 가득 차면 삽입 실패 가능. 평균 상수 시간 조회가 지연 보장은 아님 | | `ARRAY` | 유효 인덱스의 값은 미리 할당되고 0으로 초기화됨. 0이 없는 해시 엔트리를 뜻하지 않음 | | `LRU_HASH` | LRU 방식 축출 캐시; 무손실 누적 카운터가 아님 | | `RINGBUF` | CPU 간 다중 생산자·단일 소비자; key/value 크기 0, 2의 거듭제곱 바이트 용량; 예약 실패 시 블로킹하지 않음 | | `PERF_EVENT_ARRAY` | CPU별 perf 채널; 사용자 공간 설정·소비와 유실 레코드 집계 필요 | | `PROG_ARRAY` | Tail call 프로그램 참조; 대상 호환성과 호출 한도 적용 | | `PERCPU_HASH` / `PERCPU_ARRAY` | CPU 간 경합 감소; 모든 race 제거는 아님. 사용자 공간은 모든 possible CPU 슬롯·패딩 고려 | | `SOCKMAP` / `SOCKHASH` | 지원되는 리다이렉션·프로그램의 소켓 참조; 임의 소켓 동작 훅이 아님 | libbpf 1.x에서 `struct bpf_map_def SEC("maps")`가 제거되었습니다. 다음 BTF 선언은 실제 타입으로 여덟 맵 범주를 보여줍니다. 필요한 맵을 적절한 프로그램과 조합해야 하며 선언만으로 이벤트 파이프라인이 완성되지 않습니다. **`map_types.bpf.c`** ```c #include #include /* Definitions only; combine the needed maps with a suitable program. */ struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 1024); __type(key, __u32); __type(value, __u64); } hash_counts SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u64); } total SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, 1024); __type(key, __u32); __type(value, __u64); } cache SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } events SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __type(key, __u32); __type(value, __u32); /* libbpf determines max_entries from the number of possible CPUs. */ } perf_events SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PROG_ARRAY); __uint(max_entries, 10); __type(key, __u32); __type(value, __u32); } jump_table SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, 1); __type(key, __u32); __type(value, __u64); } cpu_counts SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_SOCKMAP); __uint(max_entries, 1024); __type(key, __u32); __type(value, __u32); } sockets SEC(".maps"); ``` 공유 카운터는 원자적 증가가 필요합니다. 새 해시 키에는 `BPF_NOEXIST` 삽입 후 실제로 만들어진 엔트리를 조회·증가시켜야 합니다. `BPF_ANY` 초기화는 다른 CPU의 카운트를 덮어쓸 수 있습니다. 배열의 유효 인덱스에는 엔트리가 이미 존재합니다. ## Cilium에서의 eBPF 활용: 컨테이너 네트워킹의 혁신 ### Cilium 아키텍처와 eBPF의 역할 ![Cilium 논리 역할: Kubernetes 상태와 Operator, 노드별 에이전트, 커널 프로그램·맵과 Hubble 플로우 관측.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-02-ebpf-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-02-ebpf-1.html) 그림은 논리적 역할이며 필수 위치나 단일 컴파일 파이프라인이 아닙니다. CLI는 클러스터 밖에서 실행할 수 있고 에이전트는 적격 관리 노드에서 실행합니다. 프로그램 빌드·로드는 버전·기능에 따라 다릅니다. Hubble Relay는 플로우를 집계하며 Prometheus 메트릭은 별도 엔드포인트입니다. 에이전트는 endpoint, identity, 정책, 데이터플레인 상태를 조정합니다. Operator는 설정된 identity/IPAM 생명주기 같은 클러스터 작업을 담당하며 패킷 전달 경로가 아닙니다. Hubble은 BPF 플로우 정보와 사용자 공간 프록시 이벤트를 조합합니다. ### Cilium eBPF 데이터플레인 상세 분석 다음은 협력하는 기능이며 모든 패킷의 고정 처리 순서가 아닙니다. 1. **진입:** 소켓 훅은 패킷 생성 전 Service 백엔드를 결정할 수 있고 TC는 패킷 경로를 처리합니다. 선택적 XDP 가속은 지원되는 외부 트래픽에 적용합니다. 2. **Identity·정책:** IP/identity와 endpoint 정책으로 L3/L4 접근을 제어합니다. 지원되는 HTTP/gRPC 정책은 Envoy, DNS는 DNS 프록시를 사용하며 L7 파싱·집행 전체가 BPF는 아닙니다. 3. **상태·변환:** conntrack, service/backend, reverse-NAT, affinity 맵은 역할이 다르며 모든 패킷에서 백엔드를 다시 선택하지 않습니다. 4. **전달:** native routing 또는 설정된 overlay를 사용합니다. DSR dispatch·반환 경로에는 모드에 맞는 네트워크 조건이 필요합니다. 5. **관찰:** 데이터플레인 카운터·이벤트와 프록시 이벤트에는 설정·수집 유실의 한계가 있습니다. 백엔드 readiness는 제어플레인 상태와 해당 health 메커니즘에서 얻으며 모든 애플리케이션을 BPF가 probe한다는 뜻은 아닙니다. Maglev, affinity, DSR, 가속은 선택 기능이지 항상 적용되는 기본값이 아닙니다. ### Cilium 주요 eBPF 프로그램 상세 설명 | Cilium 1.20.1 소스 | 역할 | |---|---| | `bpf/bpf_lxc.c` | Endpoint 패킷 경로, 정책, conntrack과 전달 | | `bpf/bpf_overlay.c` | Overlay 패킷 경로 | | `bpf/bpf_host.c` | 호스트·장치 경로와 지원되는 host firewall 처리 | | `bpf/bpf_xdp.c` | 설정된 로드밸런서 가속 등을 포함한 XDP 경로 | | `bpf/bpf_sock.c` | connect/sendmsg/recvmsg 서비스 변환 등의 socket-address 훅 | | `bpf/lib/lb.h` | 공통 로드밸런싱 헬퍼 | | `bpf/lib/policy.h` | 공통 정책 헬퍼 | 이 릴리스에는 최상위 `bpf_lb.c`, `bpf_network.c` 파일이 없습니다. 함수명·기능 조건은 변하므로 개념적인 이름을 소스 파일로 가정하지 말고 정확한 버전을 확인합니다. ### Cilium의 eBPF 맵 활용 다음 예는 안정적인 맵 레이아웃 API가 아닙니다. | 이름 / 계열 | 키와 역할 | |---|---| | `cilium_lxc` | 주소·주소 계열 → endpoint 전달 메타데이터. 단순 endpoint ID가 아님 | | `cilium_ipcache_v2` | Prefix, 주소 계열, 클러스터 context → identity·터널 메타데이터 | | `cilium_policy_v3_` | Identity, 방향, 프로토콜, 목적지 포트와 prefix → 정책 엔트리 | | `cilium_ct4_global`, `cilium_ct6_global`, `cilium_ct_any4_global` 등 | 연결 tuple 상태. 실제 맵은 프로토콜·주소 계열·설정에 따름 | | `cilium_lb4_services_v2` / `cilium_lb6_services_v2` | 주소·포트, 프로토콜, scope, backend slot → 서비스 메타데이터·백엔드 참조. 백엔드 레코드는 별도 맵 | | `cilium_metrics` | 사유, 방향, 소스 위치 키 → 패킷·바이트 카운터 | `cilium-dbg map get`은 사용자 공간 캐시이며 항상 최신 커널 덤프가 아닙니다. 버전에 맞는 `cilium-dbg bpf ...` 디코더나 bpftool의 지원 커널 뷰를 사용합니다. 문제 해결 편의상 Cilium 맵에 원시 바이트를 쓰지 않습니다. ### eBPF 기반 기능과 경계 - **정책:** Kubernetes NetworkPolicy는 L3/L4 의미를 제공하고 Cilium 리소스가 지원 기능을 확장합니다. 무제한 L4 허용이 겹치는 L7 제한을 우회할 수 있으므로 합쳐진 정책을 확인합니다. - **암호화:** WireGuard/IPsec 모드에서는 BPF가 해당 커널 시설로 트래픽을 유도하며 암호 연산 전체를 BPF 명령어로 수행하지 않습니다. 모드에 맞는 키·포트·MTU·범위를 설정합니다. IPsec과 WireGuard 키 운영은 다릅니다. - **서비스 메시:** 사용자 공간 프록시가 지원 L7 처리를 제공합니다. Kafka L7 정책은 제거되었습니다. Beta workload mTLS/ztunnel에는 별도 조건이 있으며 노드 암호화로 자동 활성화되지 않습니다. - **대역폭:** EDT/bandwidth-manager와 혼잡 제어가 종단 간 QoS나 처리량을 보장하지 않습니다. - **다중 클러스터:** Cluster Mesh에는 연결, identity, 주소와 호환 설정이 필요하며 모든 정책 객체를 자동 동기화하거나 라우팅을 자동 해결하지 않습니다. ## 실습: eBPF 프로그램 개발 및 디버깅 ### 1. 기본 eBPF 프로그램 개발 새 실습 디렉터리에 표시된 파일명으로 저장합니다. 이 프로그램은 이후 실패하는 경우까지 포함해 `execve` **시도**를 기록합니다. `execveat`은 별도 syscall 진입 tracepoint입니다. Debug 출력은 공유되고 잡음이 많아 운영 이벤트 전송 방식이 아닙니다. `SEC()`가 타입·훅을 지정하며 GPL 메타데이터는 사용한 헬퍼에 맞춘 것입니다. **`hello.bpf.c`** ```c #include #include SEC("tracepoint/syscalls/sys_enter_execve") int hello_execve(void *ctx) { (void)ctx; char message[] = "execve attempt\n"; bpf_trace_printk(message, sizeof(message)); return 0; } char LICENSE[] SEC("license") = "GPL"; ``` ### 2. 맵을 활용한 고급 eBPF 프로그램 참조 커널에서 `sched_process_exec`는 실행 전환 성공 후 발생합니다. 종료 문자를 포함해 최대 16바이트인 짧은 task 이름 `comm`별로 집계하며 고유 실행 파일 경로·프로세스 ID가 아닙니다. 이름은 충돌·변경될 수 있습니다. 호스트 관측이며 자동으로 특정 Pod에 한정되지 않습니다. 맵에는 최대 1,024개 이름을 보관합니다. `lost_events[0]`은 이름 조회 실패, `[1]`은 용량 부족 등 사용 가능한 카운터 엔트리를 얻지 못한 이벤트 수입니다. 모든 관측 실패를 포함하지 않으며 64비트 카운터도 overflow할 수 있습니다. 실습은 집계 중 엔트리를 삭제하지 않습니다. **`exec_shared.h`** ```c #ifndef EXEC_SHARED_H #define EXEC_SHARED_H #define COMM_BYTES 16 #define MAX_COMMANDS 1024 struct comm_key { char comm[COMM_BYTES]; }; #endif ``` **`exec_count.bpf.c`** ```c #include #include #include "exec_shared.h" struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, MAX_COMMANDS); __type(key, struct comm_key); __type(value, __u64); } exec_counts SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 2); __type(key, __u32); __type(value, __u64); } lost_events SEC(".maps"); static __always_inline void record_loss(__u32 reason) { __u64 *lost = bpf_map_lookup_elem(&lost_events, &reason); if (lost) __sync_fetch_and_add(lost, 1); } SEC("tracepoint/sched/sched_process_exec") int count_exec(void *ctx) { (void)ctx; struct comm_key key = {}; __u64 zero = 0; if (bpf_get_current_comm(key.comm, sizeof(key.comm)) != 0) { record_loss(0); return 0; } __u64 *count = bpf_map_lookup_elem(&exec_counts, &key); if (!count) { /* A competing CPU may insert first; never overwrite its count. */ bpf_map_update_elem(&exec_counts, &key, &zero, BPF_NOEXIST); count = bpf_map_lookup_elem(&exec_counts, &key); } if (count) __sync_fetch_and_add(count, 1); else record_loss(1); return 0; } char LICENSE[] SEC("license") = "GPL"; ``` #### 사용자 공간 애플리케이션과 연결 수명 이 로더는 두 예제 object 중 하나를 받아 프로그램이 정확히 하나인지 확인하고 연결한 뒤 Ctrl-C/SIGTERM까지 link를 유지합니다. Pin을 가정하지 않고 같은 object에서 카운터 맵 FD를 얻습니다. NULL부터 제한된 횟수로 순회하는 비원자적 실시간 표본입니다. **`run_bpf.c`** ```c #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "exec_shared.h" static volatile sig_atomic_t stopping; static void stop(int signal_number) { (void)signal_number; stopping = 1; } static int dump_counts(int map_fd, int lost_fd) { struct comm_key current, next; const struct comm_key *previous = NULL; unsigned int seen = 0; while (seen < MAX_COMMANDS) { if (bpf_map_get_next_key(map_fd, previous, &next) != 0) { if (errno == ENOENT) break; perror("get next key"); return -1; } __u64 value; if (bpf_map_lookup_elem(map_fd, &next, &value) == 0) printf("%.*s: %" PRIu64 "\n", COMM_BYTES, next.comm, (uint64_t)value); else if (errno != ENOENT) { perror("lookup count"); return -1; } current = next; previous = ¤t; seen++; } for (__u32 reason = 0; reason < 2; reason++) { __u64 value; if (bpf_map_lookup_elem(lost_fd, &reason, &value) != 0) { perror("lookup loss"); return -1; } printf("lost[%u]: %" PRIu64 "\n", reason, (uint64_t)value); } if (fflush(stdout) != 0) { perror("flush output"); return -1; } return 0; } int main(int argc, char **argv) { struct bpf_object *object = NULL; struct bpf_link *link = NULL; int result = 1; if (argc != 2) { fprintf(stderr, "usage: %s OBJECT.bpf.o\n", argv[0]); return 2; } struct sigaction action = {.sa_handler = stop}; sigemptyset(&action.sa_mask); if (sigaction(SIGINT, &action, NULL) || sigaction(SIGTERM, &action, NULL)) { perror("sigaction"); return 1; } object = bpf_object__open_file(argv[1], NULL); if (!object) { perror("open BPF object"); return 1; } struct bpf_program *program = bpf_object__next_program(object, NULL); if (!program || bpf_object__next_program(object, program)) { fprintf(stderr, "expected exactly one program\n"); goto cleanup; } if (bpf_object__load(object) != 0) { fprintf(stderr, "load failed; inspect libbpf/verifier diagnostics\n"); goto cleanup; } int counts = bpf_object__find_map_fd_by_name(object, "exec_counts"); int losses = bpf_object__find_map_fd_by_name(object, "lost_events"); if (counts >= 0 && losses < 0) { fprintf(stderr, "counter object is missing lost_events\n"); goto cleanup; } link = bpf_program__attach(program); if (!link) { perror("attach tracepoint"); goto cleanup; } fprintf(stderr, "Attached; Ctrl-C detaches. Counts are live samples.\n"); result = 0; while (!stopping) { if (counts >= 0 && dump_counts(counts, losses) != 0) { result = 1; break; } sleep(2); } cleanup: bpf_link__destroy(link); bpf_object__close(object); return result; } ``` #### 컴파일 및 실행 Debian/Ubuntu multiarch에서는 GCC의 multiarch 경로가 UAPI `asm/` 헤더를 제공합니다. 다른 배포판은 경로를 조정합니다. `-g`가 `.maps`용 BTF를 생성합니다. 준비된 VM의 터미널 A에서 컴파일하고 로더를 시작합니다. ```bash MULTIARCH=$(gcc -print-multiarch) test -n "$MULTIARCH" clang -O2 -g -target bpf -I"/usr/include/$MULTIARCH" \ -c hello.bpf.c -o hello.bpf.o clang -O2 -g -target bpf -I"/usr/include/$MULTIARCH" \ -c exec_count.bpf.c -o exec_count.bpf.o cc -O2 -Wall -Wextra run_bpf.c -o run_bpf \ $(pkg-config --cflags --libs libbpf) sudo ./run_bpf hello.bpf.o ``` 터미널 B에서 `sudo cat /sys/kernel/tracing/trace_pipe`를 읽고 C에서 `/usr/bin/true` 같은 외부 실행 파일을 호출합니다. Hello 로더를 Ctrl-C로 종료한 뒤 `sudo ./run_bpf exec_count.bpf.o`를 실행합니다. 다른 터미널에서 명령을 호출하며 이름별 값 변화를 확인합니다. 관측 도구와 다른 호스트 활동도 이벤트를 만들므로 고정 총합·PID를 약속하지 않습니다. 실패한 `execve`는 hello 출력에 나타날 수 있지만 `sched_process_exec`를 만들지 않아야 합니다. `bpftool prog load OBJECT PIN`만으로는 이 tracepoint에 연결하지 않습니다. 예제는 명시적으로 link를 소유합니다. Pin·재사용은 별도 수명 결정이며 `pinmaps`와 `map ... pinned ...`는 서로 바꿔 쓸 수 없습니다. 로더 종료로 pin하지 않은 자원을 해제합니다. ### 3. Cilium eBPF 프로그램 탐색 및 디버깅 이미 준비된 클러스터와 올바른 kubeconfig context를 사용합니다. 대상 Pod의 노드에 있는 에이전트를 선택하며 endpoint ID는 노드별 값입니다. 아래 자리표시자를 실제 값으로 바꿉니다. ```bash kubectl config current-context kubectl -n kube-system get pods -l k8s-app=cilium -o wide export CILIUM_POD=cilium-REPLACE-WITH-ACTUAL-POD export ENDPOINT_ID=REPLACE-WITH-NODE-LOCAL-ID kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg status --verbose kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg endpoint list kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg endpoint get "$ENDPOINT_ID" kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg map list kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg service list kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg bpf lb list --frontends kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg bpf lb list --backends ``` `kubectl get networkpolicy,ciliumnetworkpolicy -n YOUR_NAMESPACE`와 해당 cluster-wide 정책을 별도로 확인합니다. Endpoint에 실현된 상태와 실제 플로우를 비교합니다. 제거된 `policy trace`와 폐기 예정인 `policy get`은 이를 대신하지 못합니다. Monitor는 한 번에 하나씩 실행하고 Ctrl-C로 종료합니다. ```bash kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg monitor --related-to "$ENDPOINT_ID" --type drop ``` 발행된 정책 판단은 `--type policy-verdict`, 제공되는 프록시 이벤트는 `--type l7`로 확인합니다. 가시성은 설정에 따르며 HTTP 거부는 네트워크 DROPPED 대신 HTTP 403일 수 있습니다. Hubble Relay가 활성화되어 있다면 `cilium hubble port-forward`를 유지하고 다음을 실행합니다. ```bash hubble status hubble observe --namespace default --last 20 hubble observe --protocol http --last 20 hubble observe --namespace default --last 20 --output json ``` JSON을 `jq`로 전달해도 서비스 의존성 그래프가 생성되지 않습니다. 활성화된 Hubble UI에서 서비스 맵을 제공합니다(`cilium hubble ui`). HTTP 가시성에는 지원되는 프록시/L7 경로가 필요하며 암호화된 애플리케이션 내용을 자동 해독하지 않습니다. ### 4. 성능 분석 및 최적화 Profiling 권한·지원이 있는 통제된 노드에서 실제 프로그램 ID를 확인합니다. 다음은 로컬 커널 상태 대상 명령이며 이번 감사에서 실행하지 않았습니다. ```bash sudo bpftool prog show export PROG_ID=REPLACE-WITH-ACTUAL-ID sudo bpftool prog show id "$PROG_ID" sudo bpftool prog dump xlated id "$PROG_ID" sudo bpftool prog profile id "$PROG_ID" duration 10 cycles instructions ``` Profiling에는 metric 이름과 적절한 커널·PMU 지원이 필요합니다. `bpftool -p map dump ...`는 내용을 보기 좋게 출력하며 조회 지연을 측정하지 않습니다. `perf`/bpftrace를 사용할 때 대상 심볼, probe와 인자를 확인합니다. Kretprobe는 명시적인 연계 없이 진입 `arg0`를 신뢰할 수 있게 제공하지 않습니다. 프로토콜, 패킷 크기, 동시성, 정책, 암호화, 프록시와 라우팅을 기록하고 전체 워크로드를 측정합니다. XDP/native routing 변경 전 설치 Helm 값과 `cilium-dbg status --verbose`를 확인합니다. 빠른 훅이나 합성 결과가 애플리케이션 지연 감소를 증명하지 않습니다. ### 5. 문제 해결 팁 | 증상 | 확인 사항 | |---|---| | C 빌드 실패 | UAPI/libbpf 헤더, BPF compiler target, `__u32`/`__u64`, BTF용 `-g` | | 검증기 거부 | 로더 stderr·검증기 로그, 경계·스택 초기화, 헬퍼, 라이선스, 복잡도 | | 로드했지만 이벤트 없음 | 연결/link 수명, 정확한 tracepoint, 이벤트 유발과 권한 | | 맵 데이터 누락 | 같은 맵인지, 삽입 오류·용량, 키 의미, 참조·pin 수명 | | 예상과 다른 Cilium 플로우 | 노드·endpoint, 합쳐진 의도·실현 정책, 라우트·백엔드, L7 프록시 | | Hubble 누락 | Relay, 필터, 설정된 가시성, 유실 이벤트 보고 | `trace_pipe`는 추적 출력이며 검증기 진단 로그가 아닙니다. 격리 VM의 의도적인 로드 시험에서는 bpftool `-d`로 로더·검증기 진단을 보지만 로드로 연결·동작까지 증명하지 못합니다. 테스트를 통과시키기 위해 정책을 끄거나 운영 권한을 확대하거나 맵을 변경하지 않습니다. ## 참고 자료 - [Original BPF paper](https://www.tcpdump.org/papers/bpf-usenix93.pdf), [Linux BPF design Q&A](https://docs.kernel.org/bpf/bpf_design_QA.html), [verifier](https://docs.kernel.org/bpf/verifier.html), [ring buffer](https://docs.kernel.org/bpf/ringbuf.html), [licensing](https://docs.kernel.org/bpf/bpf_licensing.html), [seccomp](https://docs.kernel.org/userspace-api/seccomp_filter.html) - [Linux 6.12 BPF loading](https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/syscall.c), [exec event placement](https://github.com/torvalds/linux/blob/v6.12/fs/exec.c), [libbpf 1.7](https://github.com/libbpf/libbpf/tree/v1.7.0), [bpftool 7.7](https://github.com/libbpf/bpftool/releases/tag/v7.7.0) - [Cilium 1.20.1 BPF source](https://github.com/cilium/cilium/tree/v1.20.1/bpf), [maps](https://github.com/cilium/cilium/tree/v1.20.1/pkg/maps), [load-balancer maps](https://github.com/cilium/cilium/tree/v1.20.1/pkg/loadbalancer/maps), [command reference](https://github.com/cilium/cilium/tree/v1.20.1/Documentation/cmdref) - [Cilium system requirements](https://docs.cilium.io/en/v1.20/operations/system_requirements/), [Kubernetes compatibility](https://docs.cilium.io/en/v1.20/network/kubernetes/compatibility/), [kube-proxy replacement](https://docs.cilium.io/en/v1.20/network/kubernetes/kubeproxy-free/), [encryption](https://docs.cilium.io/en/v1.20/security/network/encryption/) ## 퀴즈 [이해도 확인하기](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/02-ebpf-quiz). ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/03-networking ---------------------------------------- # 네트워킹 모델 및 VXLAN > **검토 기준**: Cilium 1.20.1, 테스트된 Kubernetes 1.33–1.36, Linux 5.10+ 또는 RHEL 8.10의 4.18 같은 문서화된 동등 백포트. > **최종 검토**: 2026년 9월 12일 > 📎 Ethernet, ARP 등 링크 계층 기초는 [네트워크 기초 Part 1](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part1.md)을 참고하세요. ## 실습 환경 설정 [설치 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)에 따라 일회용 클러스터와 아키텍처에 맞는 Cilium CLI를 준비합니다. kubectl은 API 서버와 한 minor 버전 이내로 맞춥니다. “v1.31 이상”만으로 호환성을 판단할 수 없습니다. 아래 일반 모드 예제에는 스케줄링 가능한 Linux 노드 두 개 이상, 경쟁하는 Pod CNI가 없는 환경, 작동하는 kube-proxy, 겹치지 않는 Pod CIDR이 필요합니다. Native 예제는 노드가 같은 L2 구간에 있어야 합니다. EKS ENI, GKE Dataplane V2, AKS 관리형 Cilium이나 기존 CNI 전환 절차가 아닙니다. ### 네트워크 분석 도구 분석 호스트의 지원 패키지 소스로 tcpdump/Wireshark를 설치합니다. 노드 패킷 캡처는 kubectl 노트북이 아니라 해당 노드·네트워크 네임스페이스에서 실행해야 합니다. 에이전트 monitor는 발행된 BPF 이벤트를 보여주며 전체 패킷 캡처가 아닙니다. ```bash kubectl config current-context kubectl -n kube-system get pods -l k8s-app=cilium -o wide export CILIUM_POD=cilium-REPLACE-WITH-AGENT-ON-TARGET-NODE kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- \ cilium-dbg monitor --type trace -v ``` 아래 명시적 VXLAN 프로필에서는 해당 워커에서 제한된 표본을 캡처합니다. ```bash sudo tcpdump -nn -i any -c 50 'udp port 8472' ``` 터널 포트를 변경했다면 실제 값을 사용합니다. 서로 다른 노드의 Pod 사이에 트래픽을 만듭니다. 같은 노드의 통신은 overlay를 지나지 않을 수 있습니다. 빈 캡처는 네트워크 장애가 아니라 노드·인터페이스·포트·경로 선택 문제일 수도 있습니다. ## 컨테이너 네트워킹 모델 비교 호스트 네임스페이스, 브리지, 노드 간 전송은 다른 측면을 설명하며 함께 존재할 수 있습니다. 보편적인 성능·보안 순위가 아닙니다. | 모델 | 메커니즘 | 주요 고려사항 | |---|---|---| | Host network | Pod가 노드 네트워크 네임스페이스 공유 | 포트 충돌과 네트워크 네임스페이스 격리 감소; 애플리케이션 최고 성능을 자동 보장하지 않음 | | Bridge | 가상 L2 브리지로 인터페이스 연결 | 노드 간에는 라우팅·전송이 추가로 필요; Cilium의 모든 endpoint에 Linux bridge가 필요한 것은 아님 | | Overlay | IP underlay 위로 캡슐화 전송 | 헤더·처리 비용이 있지만 underlay에 모든 Pod prefix 경로가 필요하지 않음 | | Native/underlay routing | 네트워크가 workload 주소를 라우팅 | 전달·반환 경로와 주소 계획 필요; 자체적으로 정책·암호화를 제공하거나 제거하지 않음 | ### Cilium 네트워킹 모드 Cilium의 `routingMode`는 `tunnel` 또는 `native`입니다. VXLAN/Geneve는 터널 프로토콜 선택입니다. 클라우드 IPAM 통합은 별도 설정 축으로 보통 native 데이터플레인과 조합하며 세 번째 `routingMode` 값이 아닙니다. BGP는 경로 광고 메커니즘이지 별도의 패킷 전달 모드가 아닙니다. ## VXLAN 기술 심층 분석 VXLAN은 내부 Ethernet 프레임을 IP 네트워크의 UDP에 담습니다. VTEP이 캡슐화·해제를 담당하고 24비트 VNI는 이론적으로 2^24개 식별자 공간을 제공합니다. Kubernetes에서 1,600만 tenant를 지원한다는 약속은 아닙니다. 표준 VXLAN 목적지 포트는 UDP 4789입니다. **Cilium VXLAN 기본값은 UDP 8472**, Geneve 기본값은 UDP 6081이며 변경할 수 있습니다. Cilium은 캡슐화 메타데이터로 보안 identity를 전달할 수 있으므로 일반 VXLAN segment 수를 Cilium tenant·정책 경계와 동일시하지 않습니다. ### VXLAN 패킷 구조 ```text 외부 Ethernet 외부 IP (IPv4 또는 IPv6) 외부 UDP (Cilium VXLAN 기본 목적지 8472; 표준 4789) VXLAN 헤더 (VNI 포함 8바이트) 내부 Ethernet 내부 IP 패킷과 전송 계층·애플리케이션 데이터 ``` IP가 UDP를 운반하며 외부 IP 헤더 자체가 외부 UDP 헤더 안에 담기는 것은 아닙니다. VXLAN 분할이 암호화·무결성·자동 NetworkPolicy 격리를 제공하지 않습니다. Underlay 경로를 적절히 제한하고 정책·암호화는 별도로 구성합니다. ### MTU 계산 추가 캡슐화·옵션이 없는 일반 VXLAN에서 내부 IP에 사용할 수 있는 크기의 감소량은 다음과 같습니다. | Underlay IP 계열 | 외부 IP + UDP + VXLAN + 내부 Ethernet | Underlay IP MTU 1,500바이트의 내부 IP 한도 | |---|---|---| | IPv4 | 20 + 8 + 8 + 14 = 50바이트 | 1,450바이트 | | IPv6 | 40 + 8 + 8 + 14 = 70바이트 | 1,430바이트 | 외부 Ethernet 헤더는 이 underlay IP MTU 밖에 있습니다. 암호화, Geneve 옵션, 다른 경로는 계산을 바꿀 수 있습니다. 유효 route MTU와 Pod veth의 device MTU가 반드시 같지는 않습니다. Cilium 1.20.1의 Helm **`MTU`는 기반 네트워크 MTU를 덮어쓰며**, 그 뒤 Cilium이 경로 오버헤드를 계산합니다. `MTU: 0`은 자동 탐지입니다. `MTU: 1450`을 “최종 Pod payload MTU”로 생각하고 넣으면 터널 오버헤드를 다시 뺄 수 있습니다. 로컬 인터페이스 탐지로 전체 경로의 최소 MTU까지 증명하지는 못합니다. ### VXLAN과 다른 캡슐화 비교 | 기술 | 운반 방식·식별자 | 프로토콜·포트 | 경계 | |---|---|---|---| | VXLAN | Ethernet in UDP; 24비트 VNI | 표준 UDP 4789; Cilium 기본 8472 | 고정 기본 헤더; 암호화 아님 | | Geneve | 확장 옵션을 가진 네트워크 가상화; 24비트 VNI | UDP 6081 | 옵션 길이에 따라 오버헤드 변화 | | GRE | 일반 캡슐화; 기본 GRE에 VXLAN식 VNI 없음 | IP 프로토콜 47, TCP/UDP 포트 47이 아님 | 선택 확장 고려 필요; “무제한 네트워크”는 정의된 용량이 아님 | | NVGRE | Ethernet over GRE; GRE key 내부 24비트 VSID | IP 프로토콜 47 | 식별자·flow 의미가 다르며 구현별 지원 확인 필요 | ## Cilium의 오버레이 네트워킹 플랫폼·프로필이 기본값을 바꾸지 않으면 Cilium은 VXLAN tunnel routing을 사용합니다. 노드 간 Pod 전송에는 도달 가능한 노드 주소, 허용된 터널 UDP 트래픽과 적절한 MTU가 필요합니다. Overlay가 겹치는 Pod 주소를 해결하거나 연결되지 않은 노드를 연결해 주지 않습니다. ![노드 간 overlay 흐름: endpoint 처리, 소스 VTEP 캡슐화, underlay 전송, 대상 캡슐 해제와 endpoint 전달.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-03-networking-2.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-03-networking-2.html) 그림은 개념적 흐름입니다. 표시된 주소는 노드별 IPAM 계획이 아니며 실제 Pod 블록은 일관되고 겹치지 않게 할당해야 합니다. 정책은 설정된 지점에서 적용되며 출발·도착 훅은 다를 수 있습니다. 1. 제어플레인·데이터플레인 상태에서 원격 endpoint·노드를 식별합니다. 2. 소스가 해당 Pod 패킷을 캡슐화하고 underlay 노드 주소로 전송합니다. 3. 대상이 캡슐화를 해제하고 내부 패킷을 처리·전달합니다. 4. 데이터플레인 이벤트, 경로 상태, 캡처를 함께 확인합니다. 이벤트 하나가 없다는 사실만으로 원인을 단정하지 않습니다. ## 라우팅 메커니즘 ### 캡슐화 Underlay에는 모든 Pod prefix 대신 노드·터널 경로가 필요합니다. 헤더와 처리 비용이 추가되며 더 큰 프레임으로 상대적인 오버헤드를 줄이려면 전체 경로가 해당 MTU를 지원해야 합니다. ### 네이티브 라우팅 노드와 underlay가 반환 트래픽까지 포함해 Pod 주소를 라우팅해야 합니다. 경로는 클라우드 네트워크, 라우터, 정적 설정 또는 별도 라우팅 구성 요소에서 얻을 수 있습니다. Native 모드 활성화가 BGP 시작이나 모든 Pod CIDR 광고를 자동 수행하지 않습니다. `autoDirectNodeRoutes: true`는 같은 L2 네트워크를 공유하는 노드의 직접 PodCIDR 경로를 설치합니다. 여러 L2 구간에서는 `directRoutingSkipUnreachable`로 직접 도달하지 못하는 경로를 건너뛰고 별도로 작동하는 라우팅 경로를 사용할 수 있습니다. Overlay 터널로 fallback하는 기능이 아닙니다. **Tunnel routing과 `autoDirectNodeRoutes: true`를 조합하면 안 됩니다. Cilium 1.20.1은 시작 시 이 조합을 명시적으로 거부합니다.** 기존 “하이브리드 모드” 예제는 잘못되었습니다. Native routing에서도 설정한 Geneve DSR 같은 기능별 캡슐화는 가능하지만 별도 서비스 기능입니다. Cilium BGP Control Plane은 설정된 Pod·Service prefix를 peer에 광고합니다. **로컬 데이터플레인을 프로그래밍하지 않으므로** 누락된 클러스터 내부 경로를 자동 설치하는 구성 요소로 생각하면 안 됩니다. ## 성능 최적화 기법 모드를 비교할 때 프로토콜, payload 크기, 동시성, 노드 배치, 정책, 암호화와 프록시를 동일하게 맞추고 측정합니다. 캡슐화 헤더 하나를 없앤다고 애플리케이션 지연 감소가 보장되지 않습니다. - **데이터 경로:** socket load balancing, 지원되는 XDP 가속과 DSR은 특정 경로의 기능입니다. VXLAN/native 모드만으로 자동 활성화되지 않습니다. - **연결 추적:** Cilium BPF conntrack과 Linux netfilter conntrack은 다른 상태 메커니즘입니다. Netfilter 경로 우회가 기존 연결에서 Cilium 상태도 모두 사용하지 않는다는 뜻은 아닙니다. - **맵:** 실제 용량·메모리 압력에 맞춰 크기를 정합니다. LRU 축출은 캐시에 유용하지만 모든 맵의 보편적 최적화는 아닙니다. - **호스트 조정:** CPU/NUMA 배치, IRQ 분배·병합, queue 설정은 워크로드에 따라 도움이 되거나 악화시킬 수 있습니다. Huge page는 일반적인 Cilium 가속 스위치가 아니며 실제 사용 주체·환경의 근거가 필요합니다. ## 클라우드 제공업체별 네트워킹 | 환경 | 구분할 점 | |---|---| | AWS ENI IPAM | Cilium이 VPC에서 라우팅 가능한 ENI 주소를 할당하며 operator IAM·API·subnet·인스턴스 용량 조건 필요. ENI 보안 그룹과 Cilium 정책은 보완 관계이며 AWS VPC CNI의 Pod별 branch-ENI 기능과 자동으로 같아지지 않음 | | EKS 플랫폼 | 일반 EC2 노드의 대체 CNI에는 별도 지원 책임이 있음. Fargate와 EKS Auto Mode에서는 이 일반 실습 프로필로 CNI를 교체할 수 없음. Hybrid Nodes는 별도 지원 설치 경로 사용 | | Google Cloud | 자체 관리 upstream Cilium은 Kubernetes host-scope IPAM과 라우팅 가능한 alias range 사용 가능. 관리형 GKE Dataplane V2는 Google 관리 Cilium/`anetd`를 사용하므로 upstream 데이터플레인을 덧씌우거나 기능 노출이 같다고 가정하지 않음 | | Azure | Azure CNI Powered by Cilium은 AKS 관리형이며 delegated IPAM 사용. Upstream Azure IPAM은 자체 관리 Azure VM/VMSS 클러스터 대상. AKS BYOCNI는 별도로 선택하는 배포 모델 | 모든 Cilium 정책이 클라우드 방화벽·보안 그룹 설정을 자동 생성하지 않습니다. 플랫폼 가이드와 지원 모델을 먼저 선택합니다. ## 실습: Cilium 네트워킹 모드 구성 및 성능 테스트 ### 준비된 새 클러스터에서 한 모드 선택 공통 파일을 저장하되 Pod CIDR이 노드·Service·VPC·연결 네트워크와 겹치면 먼저 변경합니다. 선택 범위를 클러스터·kube-proxy 설정 및 native 프로필의 `ipv4NativeRoutingCIDR`과 일치시켜야 하며 파일 하나만 변경해서는 안 됩니다. `kubeProxyReplacement: false`는 작동하는 kube-proxy를 전제로 합니다. kubectl로 적용할 ConfigMap이 아니라 Helm values입니다. **`lab-common.yaml`** ```yaml kubeProxyReplacement: false ipv4: enabled: true ipv6: enabled: false ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 MTU: 0 hubble: enabled: true relay: enabled: true ui: enabled: true ``` 아래 모드 파일 중 하나만 선택합니다. 기존 클러스터에서 CNI를 반복 재설치하지 말고 모드 비교에는 별도 일회용 클러스터를 사용합니다. **`mode-vxlan.yaml`** ```yaml routingMode: tunnel tunnelProtocol: vxlan tunnelPort: 8472 autoDirectNodeRoutes: false ``` **`mode-geneve.yaml`** ```yaml routingMode: tunnel tunnelProtocol: geneve tunnelPort: 6081 autoDirectNodeRoutes: false ``` **`mode-native.yaml`** ```yaml routingMode: native ipv4NativeRoutingCIDR: 10.244.0.0/16 autoDirectNodeRoutes: true ``` VXLAN 선택 예제입니다. ```bash kubectl config current-context cilium install --version 1.20.1 --values lab-common.yaml --values mode-vxlan.yaml cilium status --wait ``` 네트워크 조건이 맞을 때만 대신 `mode-geneve.yaml` 또는 `mode-native.yaml`을 선택합니다. 예전 `tunnel: vxlan`, `ipv4-range`, `ipv4-service-range` ConfigMap 예제를 적용하지 말고 지원 Helm 필드로 IPAM을 구성합니다. ### 네트워크 성능 테스트 무관한 manifest가 `netperf-client`, `netperf-server`를 만든다고 가정하지 말고 CLI가 유지보수하는 성능 workload를 사용합니다. 준비된 일회용 클러스터에서 실행합니다. ```bash cilium connectivity perf --test-namespace cilium-net-perf \ --namespace-labels docs-audit-lab=cilium-networking-03 \ --duration 10s --samples 2 --crr --udp \ --host-net=false --pod-net=true --same-node=true --other-node=true \ --report-dir ./cilium-net-perf-results ``` Duration은 전체 실행 10초가 아니라 각 test case·sample의 시간입니다. 테스트 workload와 네트워크 부하를 만듭니다. CLI 0.20.0은 namespace에 순번을 붙입니다(기본 단일 suite는 `cilium-net-perf-1`). 결과와 버전·배치·설정을 함께 저장하며 처리량·지연 수치를 보장하지 않습니다. TCP request/response, 연결 생성률, stream은 다른 질문에 답합니다. 별도로 준비한 iperf3를 사용한다면 UDP 테스트에도 TCP 제어 연결과 UDP 데이터 경로가 필요합니다. TCP 5201만 노출한 Service로는 부족합니다. 제공한 UDP 전송률은 실제 달성 처리량이 아닙니다. 현재 공식 values, API schema, CLI 소스와 대조한 예제입니다. 호스트 재시작 이후 이번 감사에서 Helm template 렌더링, 클러스터 배포, 네트워크 벤치마크는 실행하지 않았습니다. 결과에 의존하기 전에 전체 플랫폼·실습 환경을 검증해야 합니다. ## 참고 자료 - [Cilium 1.20.1 routing](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/routing.rst), [Helm values](https://github.com/cilium/cilium/blob/v1.20.1/install/kubernetes/cilium/values.yaml), [startup validation](https://github.com/cilium/cilium/blob/v1.20.1/daemon/cmd/daemon_main.go), [MTU calculation](https://github.com/cilium/cilium/blob/v1.20.1/pkg/mtu/mtu.go), [MTU option](https://github.com/cilium/cilium/blob/v1.20.1/pkg/mtu/cell.go) - [VXLAN RFC 7348](https://www.rfc-editor.org/rfc/rfc7348.txt), [Geneve RFC 8926](https://www.rfc-editor.org/rfc/rfc8926.txt), [GRE RFC 2784](https://www.rfc-editor.org/rfc/rfc2784.txt), [NVGRE RFC 7637](https://www.rfc-editor.org/rfc/rfc7637.txt) - [BGP Control Plane](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/bgp-control-plane/bgp-control-plane.rst), [AWS ENI](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/eni.rst), [EKS alternate CNI](https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html), [GKE Dataplane V2](https://docs.cloud.google.com/kubernetes-engine/docs/concepts/dataplane-v2), [Azure IPAM](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/azure.rst) - [CLI 0.20.0 connectivity/perf options](https://github.com/cilium/cilium-cli/blob/v0.20.0/vendor/github.com/cilium/cilium/cilium-cli/cli/connectivity.go), [iperf3 invocation](https://software.es.net/iperf/invoking.html), [kubectl version skew](https://kubernetes.io/releases/version-skew-policy/) [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) ## 퀴즈 [네트워킹 검증 실습과 기대 결과](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/03-networking-quiz)를 확인하세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/04-ipam-policy ---------------------------------------- # IPAM 및 네트워크 정책 > **검토 기준**: Cilium 1.20.1, 테스트된 Kubernetes 1.33–1.36. 리소스 API 버전과 플랫폼 조건은 별도로 확인합니다. > **최종 검토**: 2026년 9월 12일 ## 실습 환경 설정 [설치 프로필](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)과 [네트워킹 조건](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/03-networking.md)에 따라 일회용 Linux 클러스터를 준비합니다. kubectl은 API 서버와 지원되는 버전 차이로 맞춥니다. 아래 IPAM 프로필은 설치·문서화된 마이그레이션의 선택지이며 실행 중인 클러스터에 ConfigMap을 순서대로 바꾸는 절차가 아닙니다. 정책 실습은 아래 별도 namespace와 일치하는 workload를 사용합니다. 이전 Cilium 1.14 Star Wars manifest는 정책의 label과 맞지 않았습니다. 기존 Kubernetes/Cilium cluster-wide 정책과 admission 설정부터 확인하며 새 namespace가 이를 무효화하지는 않습니다. ```bash cilium status --wait kubectl -n kube-system get configmap cilium-config -o yaml kubectl get ciliumnodeconfigs --all-namespaces -o yaml helm -n kube-system get values cilium --all ``` Namespace·release 이름이 다르면 실제 값을 사용합니다. 의도한 Helm values·ConfigMap, 노드 override, operator 설정, 에이전트 실현 상태는 다른 질문에 답합니다. `ipam`을 포함한 grep 결과만으로 전체 유효 설정을 확인할 수 없습니다. ## IP 주소 관리 전략 Cilium Pod IPAM은 workload 주소를 할당합니다. Service **ClusterIP**는 Kubernetes가 할당하며 Cilium **LoadBalancer IPAM**은 LoadBalancer 주소를 위한 별도 기능입니다. `CiliumPodIPPool`이 Service ClusterIP를 할당하지 않습니다. ### 할당 주체와 기준 데이터 | 설정 | 노드 용량·Pod IP 할당 주체 | 확인할 상태 | |---|---|---| | `cluster-pool`(일반 기본값) | Cilium Operator가 노드 CIDR, 각 에이전트가 로컬 주소 할당 | `CiliumNode.spec.ipam.podCIDRs`, operator 상태 | | `kubernetes` / host scope | Kubernetes가 노드 PodCIDR 제공, 에이전트가 범위 내 할당 | Kubernetes `Node.spec.podCIDRs` / `spec.podCIDR`, 지원 provider annotation | | `multi-pool` | 에이전트 수요에 따라 Operator가 이름 있는 풀의 블록 할당, 에이전트가 Pod IP 할당 | `CiliumPodIPPool`, `CiliumNode.spec.ipam.pools.requested` / `allocated` | | `crd` | 외부 할당기가 주소를 공급하고 에이전트가 사용·반납 | `CiliumNode.spec.ipam.pool` / `status.ipam.used` 및 해당 IPv6 필드 | | `eni` | Operator가 AWS ENI·IP·prefix 관리, 에이전트가 인터페이스 상태를 multi-pool allocator로 변환 | `CiliumNode.status.eni.enis`, 모드별 pool·수요 상태 | | `azure` | 자체 관리 Azure VM/VMSS용 upstream operator·agent 통합 | Azure/CiliumNode 할당 상태 | | `delegated-plugin` | Cilium CNI가 관리형 AKS의 Azure IPAM 같은 외부 플러그인 호출 | Provider·plugin 상태. Upstream Azure IPAM 설정으로 대체하지 않음 | | GKE 통합 | Upstream GKE 통합은 host-scope `kubernetes` IPAM 사용, 관리형 Dataplane V2는 별도 관리 주체 | Provider 설정·Node CIDR. 별도 `gke` IPAM 값이 아님 | Cluster-pool과 Kubernetes host scope 모두 개별 Pod 주소를 로컬에서 할당합니다. 차이는 **노드 prefix**를 누가 할당하는지입니다. 조정이 없어지거나 사용자가 지정한 풀이 VPC·노드·Service·다른 클러스터와 겹치지 않는다고 보장하지 않습니다. 릴리스된 CRD·타입은 일반 CRD-backed 할당에 `pool`, `used`를 사용합니다. 일부 설명에 남아 있는 `available`/`inuse` 대신 설치된 스키마와 모드별 필드를 기준으로 합니다. 1.20.1의 ENI는 인터페이스·multi-pool 경로를 사용하므로 일반 CRD-backed 필드가 모든 ENI 상태의 기준은 아닙니다. ### Kubernetes/CNI 통합 Kubelet이 컨테이너 런타임에 Pod sandbox 작업을 요청하고 CNI 지원 런타임이 Cilium CNI 플러그인을 호출합니다. 선택한 backend로 주소를 할당한 뒤 plugin·agent가 endpoint 네트워크를 구성합니다. Host-scope 모드에서는 적절히 설정된 node-CIDR allocator 등으로 Kubernetes가 필요한 주소 계열의 CIDR을 제공해야 합니다. ## IPAM 구성 해당 설치 프로필과 합칠 **Helm values 조각**입니다. Pod·Service·노드·외부 네트워크 범위를 먼저 확인합니다. ### 클러스터 풀 **`cluster-pool-values.yaml`** ```yaml ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 ipv4: enabled: true ipv6: enabled: false ``` Operator는 노드 블록을 조정하며 Pod마다 중앙에서 주소를 요청하는 구조가 아닙니다. `clusterPoolIPv4PodCIDRList`의 여러 CIDR은 공통 할당 공간을 확장합니다. Workload별 이름 있는 풀 선택과는 다릅니다. 실행 중인 클러스터를 확장할 때 기존 목록 항목을 교체하지 않습니다. 문서화된 절차로 겹치지 않는 CIDR을 추가하며 노드 mask size를 일반적인 가변 설정으로 취급하지 않습니다. 예약 주소·노드 기능 때문에 블록 주소 수가 사용 가능한 Pod 수와 같지는 않습니다. 이미 Kubernetes/Cilium dual stack을 준비한 클러스터의 예입니다. **`dual-stack-values.yaml`** ```yaml ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 clusterPoolIPv6PodCIDRList: - fd00:10:244::/104 clusterPoolIPv6MaskSize: 120 ipv4: enabled: true ipv6: enabled: true ``` 이 두 Cilium 주소 계열 flag만으로 Kubernetes Service CIDR, underlay IPv6 연결이나 클라우드 지원이 구성되지 않습니다. ### Multi-Pool과 CiliumPodIPPool 문서화된 모드는 `multi-pool`이며 리소스 API는 여전히 `cilium.io/v2alpha1`입니다. API 접미사만으로 기능 성숙도를 추론하지 않습니다. 이 모드의 새 클러스터에는 일반 할당용 default pool을 준비합니다. **`multi-pool-values.yaml`** ```yaml ipam: mode: multi-pool operator: autoCreateCiliumPodIPPools: default: ipv4: cidrs: - 10.244.0.0/16 maskSize: 24 ``` 추가 풀은 현재 필드인 `cidrs`, `maskSize`를 사용합니다. **`blue-pool.yaml`** ```yaml apiVersion: cilium.io/v2alpha1 kind: CiliumPodIPPool metadata: name: blue-pool spec: ipv4: cidrs: - 10.245.0.0/16 maskSize: 24 namespaceSelector: matchLabels: ipam-pool: blue podSelector: matchLabels: role: blue ``` 풀 리소스는 cluster 범위입니다. `podSelector`와 `namespaceSelector`는 별도 필드이며 둘 다 설정하면 모두 일치해야 합니다. 이전 `ipv4.cidr`, `blockSize`, 일반 `selector` 예제는 잘못되었습니다. 선택기 예제입니다. **`blue-namespace.yaml`** ```yaml apiVersion: v1 kind: Namespace metadata: name: ipam-selection-demo labels: ipam-pool: blue annotations: ipam.cilium.io/require-pool-match: 'true' ``` **`blue-pod.yaml`** ```yaml apiVersion: v1 kind: Pod metadata: name: blue-client namespace: ipam-selection-demo labels: role: blue spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause ``` 준비된 multi-pool 설치에서만 적용합니다. Namespace의 `require-pool-match`는 비기본 selector match가 필요할 때 자동 default pool fallback을 방지합니다. 풀 선택은 명시적인 Pod·namespace의 `ipam.cilium.io/ip-pool` 또는 주소 계열별 pool annotation, 자동 selector, default pool 순서입니다. 자동 선택은 주소 계열별로 정확히 한 풀과 일치해야 하며 겹치는 selector는 할당 실패를 일으킵니다. Pool annotation은 **새 할당**에 적용되고 기존 Pod IP를 바꾸지 않습니다. 노드별 기본 풀은 `CiliumNodeConfig`로도 구성할 수 있습니다. 풀 선택이 네트워크 인가를 대신하지 않습니다. Workload label·annotation 변경 권한을 통제하고 트래픽 정책을 별도로 적용합니다. 풀 CIDR은 겹치면 안 되며 사용 중인 범위·풀을 임의로 삭제하지 않습니다. `maskSize`, `allowFirstIP`, `allowLastIP`는 불변이고 첫·마지막 주소 예약에는 문서화된 작은 prefix 예외가 있습니다. 현재 **cluster-pool→multi-pool** 온라인 마이그레이션 절차가 문서화되어 있습니다. 임의의 실행 중 IPAM 전환이나 계획 없는 역방향 전환을 뜻하지 않습니다. 조건과 workload·용량을 검증해야 하며 이 장에서는 마이그레이션을 실행하지 않습니다. ### AWS ENI 라우팅, operator IAM, subnet·인스턴스 용량과 노드 준비는 전체 EKS/ENI 설치 프로필을 따릅니다. 다음은 선택적 IPv4 prefix delegation을 포함한 현재 키의 예입니다. **`eni-values-fragment.yaml`** ```yaml ipam: mode: eni eni: enabled: true eniTags: team: platform awsEnablePrefixDelegation: true routingMode: native endpointRoutes: enabled: true ipv4: enabled: true ipv6: enabled: false ``` `eni.awsEnablePrefixDelegation`에는 해당 prefix를 지원하는 인스턴스·subnet 구성이 필요합니다. IPv4 `/28`은 주소 16개이며 모든 구성에서 Pod 16개를 추가 스케줄링할 수 있다는 뜻은 아닙니다. 기본값은 비활성입니다. 존재하지 않는 `eni-prefix-delegation-enabled` 키를 추가하지 않습니다. EC2 API는 Operator가 호출합니다. 사전 할당은 Pod별 지연을 줄이지만 quota·API·subnet 고갈을 없애지 못합니다. `eni.eniTags`는 관리 인터페이스의 태그입니다. 검증된 의도적인 `eni.ec2APIEndpoint` override가 필요하지 않으면 SDK가 적절한 EC2 endpoint를 결정하게 합니다. 이 예제는 IPv4입니다. ENI 참조는 다른 prefix·할당 동작을 가진 IPv6를 beta로 문서화하며 플랫폼 검증은 `ipv6.enabled` 설정과 별개입니다. AWS VPC CNI chaining에서는 주소 관리를 AWS VPC CNI가 유지합니다. 일반 EC2, Hybrid Nodes, Fargate, Auto Mode는 설치·지원 경계가 다르고 Fargate·Auto Mode에는 이 대체 프로필을 사용할 수 없습니다. ## 노드별 할당 상태 조회 ### CiliumNode 예제 **읽기 전용 객체 형태 예시**이며 Operator가 소유한 상태 위에 적용할 manifest가 아닙니다. **`ciliumnode-example.yaml`** ```yaml apiVersion: cilium.io/v2 kind: CiliumNode metadata: name: hybrid-node-001 spec: addresses: - ip: 10.85.0.1 type: CiliumInternalIP - ip: 10.80.1.10 type: InternalIP ipam: podCIDRs: - 10.85.0.0/25 ``` 첫 주소가 underlay 노드의 `InternalIP`가 아니라 `CiliumInternalIP`일 수 있습니다. InternalIP·주소 계열·할당 항목이 여러 개일 수도 있습니다. 타입이 InternalIP여도 모든 라우터에서 유효한 next hop이 자동으로 되는 것은 아닙니다. ### 모드별 인벤토리 다음 쿼리를 `ciliumnode-inventory.jq`로 저장합니다. **`ciliumnode-inventory.jq`** ```text .items[] | { name: .metadata.name, internalNodeIPs: ([.spec.addresses[]? | select(.type == "InternalIP") | .ip] | unique), clusterPoolPodCIDRs: (.spec.ipam.podCIDRs // []), multiPoolAllocations: (.spec.ipam.pools.allocated // []), eniInterfaceIDs: ((.status.eni.enis // {}) | keys), operatorStatus: (.status.ipam["operator-status"] // {}) } ``` ```bash kubectl get ciliumnodes -o json | jq -f ciliumnode-inventory.jq ``` 다른 IPAM 모드에서는 없거나 빈 필드가 정상일 수 있습니다. Kubernetes host scope라면 Kubernetes Node를 확인합니다. **`kubernetes-node-inventory.jq`** ```text .items[] | { name: .metadata.name, internalNodeIPs: ([.status.addresses[]? | select(.type == "InternalIP") | .address] | unique), podCIDRs: (.spec.podCIDRs // []), legacyPodCIDR: (.spec.podCIDR // null) } ``` ```bash kubectl get nodes -o json | jq -f kubernetes-node-inventory.jq ``` `[0]`만 고르지 않고 관련 주소·CIDR을 유지하는 인벤토리입니다. `ip route add` 명령을 생성하지 않습니다. 네트워크 라우팅 절차에 따라 인터페이스·next hop과 전달·반환 경로를 검증해야 하며 모든 CIDR을 첫 주소로 설치한다고 가정하지 않습니다. [EKS Hybrid Nodes 네트워크 계획](https://www.atomai.click/kubernetes-docs/llms/ko/eks-hybrid-nodes/02-network-configuration.md)에 활용할 수 있지만 해당 환경의 라우팅·도달 가능성 확인을 대신하지 않습니다. ## 네트워크 정책 설계 및 구현 ### 리소스와 규칙 의미 - Namespace 범위 `CiliumNetworkPolicy`는 해당 namespace의 endpoint를 선택하고 `CiliumClusterwideNetworkPolicy`는 cluster 범위를 제공합니다. Host firewall의 `nodeSelector`는 후자에서만 지원하며 host firewall 구성이 필요합니다. - `endpointSelector`는 정책 대상, ingress·egress는 대상 기준의 트래픽 방향입니다. Cilium 규칙의 `spec.labels`는 선택적 식별·메타데이터이며 다른 정책을 상속하는 참조가 아닙니다. Kubernetes `metadata.labels`는 리소스 자체의 label입니다. - 적용되는 정책의 허용은 합쳐지며 명시적 deny에는 문서화된 우선순위가 있습니다. 별도의 무제한 L4 허용이 겹치는 L7 제한을 우회할 수 있습니다. - Default deny는 방향별입니다. 필요한 DNS·애플리케이션 경로를 의도적으로 유지하고 실현 정책·실제 플로우를 확인합니다. Default deny 비활성화가 보편적인 L7 dry run은 아닙니다. ### Label이 일치하는 정책 실습 일회용 클러스터의 같은 셸에서 실행합니다. ```bash set -euo pipefail kubectl create namespace cilium-ipam-policy-demo kubectl label namespace cilium-ipam-policy-demo docs-audit-lab=cilium-ipam-policy-04 ``` Namespace가 이미 있으면 중단하고 모든 파일·명령에 새 이름을 일관되게 사용합니다. 정책과 일치하는 label 및 공식 CLI 테스트 이미지를 사용하는 workload입니다. **`policy-app.yaml`** ```yaml apiVersion: v1 kind: Pod metadata: name: frontend namespace: cilium-ipam-policy-demo labels: app: frontend spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause --- apiVersion: v1 kind: Pod metadata: name: outsider namespace: cilium-ipam-policy-demo labels: app: outsider spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause --- apiVersion: apps/v1 kind: Deployment metadata: name: backend namespace: cilium-ipam-policy-demo spec: replicas: 1 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: automountServiceAccountToken: false affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: frontend topologyKey: kubernetes.io/hostname containers: - name: http image: quay.io/cilium/json-mock:v1.4.1@sha256:6a66df90808a39c02e7a9d58af7bf0e54d8f8b7d4bc528f48c891969a7049195 ports: - containerPort: 8080 name: http readinessProbe: httpGet: path: / port: http --- apiVersion: v1 kind: Service metadata: name: backend namespace: cilium-ipam-policy-demo spec: selector: app: backend ports: - name: http port: 8080 targetPort: http protocol: TCP --- apiVersion: v1 kind: Pod metadata: name: client namespace: cilium-ipam-policy-demo labels: app: client spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause ``` ```bash kubectl apply -f policy-app.yaml kubectl -n cilium-ipam-policy-demo wait --for=condition=Ready \ pod/frontend pod/outsider pod/client --timeout=120s kubectl -n cilium-ipam-policy-demo rollout status deployment/backend --timeout=120s kubectl -n cilium-ipam-policy-demo get pods -o wide --show-labels BACKEND_IP=$(kubectl -n cilium-ipam-policy-demo get service backend -o jsonpath='{.spec.clusterIP}') test -n "$BACKEND_IP" kubectl -n cilium-ipam-policy-demo exec frontend -- \ curl --fail --silent --show-error --max-time 5 "http://$BACKEND_IP:8080/" ``` Outsider의 기본 연결도 먼저 확인합니다. Backend anti-affinity에는 다른 적격 노드가 필요합니다. 아래 database 규칙은 추가 애플리케이션 의존성을 나타내며 실습이 데이터베이스 서버를 배포·검증하지는 않습니다. ### L3/L4 정책 **`backend-l4.yaml`** ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: backend-access namespace: cilium-ipam-policy-demo spec: endpointSelector: matchLabels: app: backend ingress: - fromEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-ipam-policy-demo k8s:app: frontend toPorts: - ports: - port: '8080' protocol: TCP egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-ipam-policy-demo k8s:app: database toPorts: - ports: - port: '3306' protocol: TCP - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s:k8s-app: kube-dns toPorts: - ports: - port: '53' protocol: UDP - port: '53' protocol: TCP ``` `backend-l4.yaml`을 적용하고 backend 에이전트에 정책이 실현된 뒤 새 요청을 확인합니다. Frontend 접근은 유지되어야 하고 outsider 거부에는 curl 비정상 종료뿐 아니라 플로우 근거가 필요합니다. ### L7 HTTP 정책 두 번째로 겹치는 허용 정책이 아니라 **같은 `backend-access` 리소스의 대체 정의**입니다. 적용하면 이 실습의 L4 버전을 교체하며 다른 적용 정책도 확인해야 합니다. **`backend-http.yaml`** ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: backend-access namespace: cilium-ipam-policy-demo spec: endpointSelector: matchLabels: app: backend ingress: - fromEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-ipam-policy-demo k8s:app: frontend toPorts: - ports: - port: '8080' protocol: TCP rules: http: - method: ^GET$ path: ^/$ - method: ^POST$ path: ^/$ headerMatches: - name: content-type value: application/json egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-ipam-policy-demo k8s:app: database toPorts: - ports: - port: '3306' protocol: TCP - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s:k8s-app: kube-dns toPorts: - ports: - port: '53' protocol: UDP - port: '53' protocol: TCP ``` ```bash kubectl apply -f backend-http.yaml kubectl -n cilium-ipam-policy-demo get cnp backend-access -o yaml ``` 실현 후 GET `/`와 정확한 `content-type: application/json`을 가진 POST `/`를 허용합니다. 다른 경로·메서드는 프록시에서 거부되어야 합니다. 허용한 동작을 애플리케이션도 구현해야 하며 정책 허용이 애플리케이션 성공을 보장하지 않습니다. Demo server에서 확인된 readiness 경로는 GET `/`입니다. HTTP method·path 필드는 정규식입니다. 예제처럼 범위를 고정하고 수정할 때 메타문자를 escape합니다. HTTP/gRPC 규칙에는 Envoy와 보이는 애플리케이션 트래픽이 필요하며 필요한 TLS 종료·가로채기를 구성해야 합니다. gRPC 서비스·메서드는 HTTP/2 path, metadata는 header로 표현되며 별도 `rules.grpc`나 임의 protobuf payload 필터가 아닙니다. ### Kafka 정책의 경계 현재 Cilium에는 제거된 `rules.kafka` API가 없습니다. 이전 topic·API-key·client-ID YAML을 적용하지 않습니다. Broker의 **실제 listener 포트**에 맞는 L4 연결 규칙을 사용하고 topic 접근 같은 인증·인가는 broker에서 설정합니다. NetworkPolicy가 broker 인가를 대신하거나 암호화를 활성화하지 않습니다. ### DNS/FQDN 정책 이 예제는 `kube-system`의 CoreDNS/kube-dns Pod가 TCP/UDP 53 resolver인 환경을 가정합니다. 먼저 Pod의 실제 resolver를 확인합니다. NodeLocal DNS, OpenShift, 관리형 DNS 경로는 맞는 별도 설정이 필요합니다. **`dns-egress.yaml`** ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: dns-egress namespace: cilium-ipam-policy-demo spec: endpointSelector: matchLabels: app: client egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s:k8s-app: kube-dns toPorts: - ports: - port: '53' protocol: UDP - port: '53' protocol: TCP rules: dns: - matchPattern: '*' - toFQDNs: - matchName: api.example.com - matchPattern: '*.googleapis.com' toPorts: - ports: - port: '443' protocol: TCP ``` DNS L7 규칙이 해당 resolver 트래픽을 Cilium DNS 프록시로 보내 이름·IP 응답을 학습하게 합니다. 53번 포트 허용만으로는 관찰되지 않습니다. `matchPattern: "*"`는 선택한 resolver로의 DNS 질의를 허용하고 HTTPS egress는 `toFQDNs` 이름에서 학습한 IP로 별도 제한합니다. 외부 테스트 성공을 주장하기 전에 `api.example.com`을 실제 조회 가능한 통제된 이름으로 바꿉니다. `*.googleapis.com`은 suffix 아래 한 label만 일치하며 apex·여러 단계는 일치하지 않습니다. 이 릴리스의 `**.googleapis.com`은 한 단계 이상 하위 도메인에 일치하지만 apex는 제외합니다. DNS TTL·캐시와 새 조회가 중요합니다. 이름·IP 허용이 HTTP hostname·URL·애플리케이션 사용자 인가 검사는 아닙니다. 기본 DNS 프록시는 agent 내부에서 실행하며 별도 standalone DNS proxy는 alpha로 문서화됩니다. 모든 DNS 정책에 Envoy가 필요한 것은 아닙니다. OpenShift 공식 예제는 이 규칙을 그대로 복사하지 않고 `openshift-dns` resolver 설정과 5353 포트를 사용합니다. ### CIDR·Service·Entity | 규칙 | 경계 | |---|---| | `toCIDR` / `toCIDRSet` | 주로 외부 peer의 IP prefix 선택. 기본적으로 Cilium 관리 Pod·노드 selector를 대신하지 않으며 `pods`/`nodes` 선택적 CIDR 매칭은 beta이고 identity를 소비 | | `toServices` | Service selector 또는 selectorless EndpointSlice 주소를 정책 selector로 변환. Service·경로를 만들지 않으며 selectorless 경우 CIDR 모드 한계 적용 | | `world` | 넓은 외부 identity 범주. “공용 인터넷만” 또는 특정 원격 클러스터 선택자가 아님 | | `cluster` / `cluster-mesh` | `cluster`는 로컬 endpoint와 문서화된 예약 entity·원격 노드, `cluster-mesh`는 연결된 클러스터 endpoint도 포함 | | `all` | Cluster·mesh·외부 peer를 포함하는 넓은 조합이며 최소 권한의 지름길이 아님 | API 서버에는 문서화된 `kube-apiserver` entity 동작을 사용하며 `toServices: default/kubernetes`가 일반 workload selector처럼 작동한다고 가정하지 않습니다. ## 멀티 클러스터 시나리오 Cluster Mesh는 상태를 공유하지만 Kubernetes 클러스터·네트워크 네임스페이스는 분리됩니다. 원격 노드가 로컬 Kubernetes Node 객체가 되거나 정책 리소스가 자동 배포되지 않습니다. ```text 상태: cluster A의 Cluster Mesh 제어플레인 <-- mTLS --> cluster B 제어플레인 데이터: Pod A --> 노드 A datapath --> 도달 가능한 네트워크 --> 노드 B datapath --> Pod B ``` Cluster Mesh API server는 상태를 동기화하며 Pod 패킷이 이 서버를 경유할 필요는 없습니다. 제어플레인 mTLS만으로 Pod 간 트래픽이 암호화되지 않습니다. ### 설정 조건과 부분 절차 서로 겹치지 않는 Pod CIDR, 도달 가능한 노드 InternalIP, 허용된 네트워크 경로, 같은 datapath 모드, 문서화된 한 minor 이내 Cilium 버전 차이를 준비합니다. Native routing은 원격 Pod 범위에도 도달하고 native-routing CIDR이 이를 포함해야 합니다. 설치 시 고유 Cilium 이름·ID(예: `cluster-a`/1, `cluster-b`/2)를 지정하고 peer 인증서 신뢰를 구성합니다. 다음은 조건과 사설 NodePort 제어플레인 경로를 준비한 **이후의 부분 절차**입니다. Kubeconfig context 이름과 Cilium cluster 이름은 같을 필요가 없습니다. ```bash export CTX_A=prepared-context-a export CTX_B=prepared-context-b cilium clustermesh enable --context "$CTX_A" --service-type NodePort cilium clustermesh enable --context "$CTX_B" --service-type NodePort cilium clustermesh connect --context "$CTX_A" --destination-context "$CTX_B" cilium clustermesh status --context "$CTX_A" --wait cilium clustermesh status --context "$CTX_B" --wait ``` VPC peering/VPN, route, firewall, private endpoint, 인증서 신뢰를 자동 준비하지 않습니다. 전체 플랫폼 절차를 따르고 실행 중인 클러스터의 이름·ID를 임의로 바꾸지 않습니다. ### Global Service와 클러스터 간 정책 각 준비된 클러스터에 같은 namespace와 실제 backend workload를 만들고 **동일한 Service 이름·namespace** 및 호환 포트를 사용합니다. **`global-service.yaml`** ```yaml apiVersion: v1 kind: Service metadata: name: global-service namespace: mesh-demo annotations: service.cilium.io/global: 'true' spec: type: ClusterIP selector: app: global-app ports: - name: http port: 80 targetPort: 8080 protocol: TCP ``` 현재 annotation은 `service.cilium.io/global`입니다. Global Service는 기본적으로 로컬 backend를 공유하며 `service.cilium.io/shared: "false"`는 peer에 공유를 중지하지만 로컬 client의 원격 backend 사용까지 반드시 막지는 않습니다. 로컬 ClusterIP가 같을 필요는 없습니다. 이 annotation만으로 자동 장애 조치를 보장하지 않습니다. 기본값(`clustermesh.cacheTTL: 0s`)은 연결이 끊긴 클러스터 상태도 유지합니다. 양수 TTL은 제어 연결 단절 후 오래된 원격 정보를 회수할 수 있지만 애플리케이션 health probe나 무중단 보장이 아닙니다. `cluster-a`에 해당 source workload가 있을 때 목적지 클러스터의 `mesh-demo`에 다음 ingress 정책을 적용합니다. **`cross-cluster-policy.yaml`** ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: allow-cluster-a-frontend namespace: mesh-demo spec: endpointSelector: matchLabels: app: global-app ingress: - fromEndpoints: - matchLabels: k8s:app: frontend k8s:io.kubernetes.pod.namespace: frontend-ns k8s:io.cilium.k8s.policy.cluster: cluster-a toPorts: - ports: - port: '8080' protocol: TCP ``` Cluster label에는 kubeconfig context가 아니라 설정된 **Cilium cluster 이름**을 사용합니다. 현재 Cilium endpoint selector는 peer를 명시하지 않으면 로컬 클러스터가 기본 대상입니다. 각 클러스터에 필요한 정책을 독립적으로 배포하고 양방향 트래픽을 검증합니다. ## 검증과 정리 릴리스별 schema, 설정·소스 계약과 제한된 로컬 fixture로 검토한 예제입니다. 이번 감사에서 실제 IP 할당, 커널 정책 집행, 클라우드 프로비저닝, 동작하는 database 또는 클러스터 간 트래픽을 확인했다고 주장하지 않습니다. 의도·실현 상태와 성공 기준 요청을 확인한 뒤 기대한 거부를 해당 플로우와 대조합니다. 소유를 확인하고 이번 namespace 정책 workload만 정리합니다. 별도 multi-pool 실습은 workload를 해제하고 사용 중인 할당이 없는지 확인한 후 풀 삭제를 검토해야 합니다. 활성 CiliumNode·pool 상태를 지름길로 삭제하지 않습니다. ## 참고 자료 - [IPAM modes/migration](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/index.rst), [cluster pool](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/cluster-pool.rst), [host scope](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/kubernetes.rst), [multi-pool](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/multi-pool.rst), [migration procedure](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/cluster-pool-to-multi-pool.rst) - [PodIPPool schema](https://github.com/cilium/cilium/blob/v1.20.1/pkg/k8s/apis/cilium.io/client/crds/v2alpha1/ciliumpodippools.yaml), [CiliumNode schema](https://github.com/cilium/cilium/blob/v1.20.1/pkg/k8s/apis/cilium.io/client/crds/v2/ciliumnodes.yaml), [ENI IPAM](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/ipam/eni.rst), [Helm values](https://github.com/cilium/cilium/blob/v1.20.1/install/kubernetes/cilium/values.yaml), [EKS CNI boundaries](https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html) - [Policy rule API](https://github.com/cilium/cilium/blob/v1.20.1/pkg/policy/api/rule.go), [L3 rules](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/policy/layer3.rst), [L7 rules](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/policy/layer7.rst), [DNS policies](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/dns.rst), [wildcard implementation](https://github.com/cilium/cilium/blob/v1.20.1/pkg/fqdn/matchpattern/matchpattern.go) - [Cluster Mesh setup](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/clustermesh/setup.rst), [architecture](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/clustermesh/intro.rst), [global services](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/clustermesh/global-services.rst), [cross-cluster policy](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/clustermesh/policy.rst) [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) ## 퀴즈 [IPAM·정책 이해도 확인](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/04-ipam-policy-quiz). ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/05-l2-l7-networking ---------------------------------------- # L2–L7 네트워킹 및 로드 밸런싱 > **검토 기준**: Cilium 1.20.1, CLI 0.20.0. Istio 예제는 1.31 API를 사용합니다. > **최종 검토**: 2026년 9월 12일 ## 실습 환경 설정 [설치](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)·[네트워킹](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/03-networking.md) 가이드로 준비한 일회용 클러스터에서 플랫폼·커널 지원과 kubectl 버전 차이를 확인합니다. HTTP 실습에는 스케줄링 가능한 Linux 노드 두 개가 필요합니다. DSR/Maglev 실험에는 준비된 kube-proxy-free 클러스터, 실제 API 서버 연결과 지원되는 네트워크 경로가 추가로 필요합니다. 실험에 맞는 전체 설치 값을 선택합니다. `cilium install --config ...` 반복은 실행 중 기능 전환 절차가 아니며 임의 설치 후 kube-proxy를 삭제하는 것도 안전한 지름길이 아닙니다. ## OSI 계층 이해 OSI는 개념적 모델이며 Cilium 프로세스 일곱 개나 고정된 정책 훅 순서를 뜻하지 않습니다. | 계층 | 역할·예 | Cilium과의 관계 | |---|---|---| | L1 물리 | Bit, 매체, transceiver, repeater | 기반 하드웨어·네트워크 조건 | | L2 데이터 링크 | Ethernet frame, MAC 주소, bridge·switch | 패킷 처리와 명시적으로 구성한 L2 Service 광고 | | L3 네트워크 | IP packet, routing, ICMP | 라우팅, identity·CIDR 정책, 지원 fragment 처리 | | L4 전송 | TCP segment·신뢰할 수 있는 stream, 전달·순서 보장이 없는 UDP datagram | Port·protocol 정책, 연결 상태, Service 변환 | | L5 세션 | Session·dialog 조직 | 실제 애플리케이션·프로토콜 안에 구현되는 개념적 기능 | | L6 표현 | 표현·인코딩·암호 변환 | TLS를 개념적으로 배치하기도 하지만 보편적인 별도 Linux 계층은 아님 | | L7 응용 | HTTP, DNS, gRPC 등 | 지원 proxy 정책. 프로토콜이 존재한다고 Cilium 정책 parser가 있는 것은 아님 | ### 실제 계층별 기능 - **L2:** L2 Announcements는 적격 Service IP에 ARP/NDP로 응답하는 설정형 beta 기능입니다. Kube-proxy 대체와 적절한 장치·로컬 네트워크 연결이 필요하며 선출된 노드가 해당 Service 트래픽을 받습니다. 임의 MAC/VLAN ACL이나 일반 L2 bridge·모든 패킷 캡처 보장이 아닙니다. `externalTrafficPolicy: Local`과의 비호환성이 문서화되어 있습니다. - **L3:** IP·identity 정책과 라우팅에는 모드별 조건이 있습니다. Multicast는 VXLAN이 필요한 별도 beta 기능이며 문서상 최소 커널은 AMD64 5.10, AArch64 6.0입니다. 모든 라우팅 모드에서 된다고 가정하지 않습니다. - **L4:** TCP/UDP port 정책, conntrack, socket·packet Service LB, 지원 affinity는 다른 훅에서 작동합니다. Socket 선택은 패킷 생성 전일 수 있습니다. - **L7:** 현재 내장 정책 그룹은 HTTP와 DNS입니다. gRPC는 지원 HTTP/2 경로를 사용하고 Kafka L7 규칙은 제거되었습니다. TLS/SNI 기능에는 문서화된 proxy 설정이 필요하며 암호화된 내용을 자동 검사하지 않습니다. HTTP/gRPC 정책은 Envoy, DNS는 Cilium DNS proxy가 담당합니다. Envoy는 values·upgrade compatibility에 따라 agent 관리 프로세스 또는 `cilium-envoy` DaemonSet일 수 있습니다. L7을 켠 새 1.20 chart 기본값은 DaemonSet을 사용하며 아래 프로필은 이를 명시합니다. 정책 추가가 모든 설치 설정을 덮어쓰지는 않습니다. ## HTTP 정책 실습 새 namespace와 일치하는 workload를 만듭니다. 이미지·digest와 확인된 server readiness 경로는 공식 CLI 테스트 배포 정의에 근거합니다. ```bash set -euo pipefail kubectl create namespace cilium-l2l7-demo kubectl label namespace cilium-l2l7-demo docs-audit-lab=cilium-l2l7-05 ``` 이미 존재하면 중단하고 모든 곳에 새 이름을 일관되게 사용합니다. 다른 실행의 리소스를 재사용하지 않습니다. **`l7-app.yaml`** ```yaml apiVersion: v1 kind: Pod metadata: name: client namespace: cilium-l2l7-demo labels: app: client spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause --- apiVersion: v1 kind: Pod metadata: name: outsider namespace: cilium-l2l7-demo labels: app: outsider spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause --- apiVersion: apps/v1 kind: Deployment metadata: name: app1 namespace: cilium-l2l7-demo spec: replicas: 1 selector: matchLabels: app: app1 template: metadata: labels: app: app1 spec: automountServiceAccountToken: false affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: client topologyKey: kubernetes.io/hostname containers: - name: http image: quay.io/cilium/json-mock:v1.4.1@sha256:6a66df90808a39c02e7a9d58af7bf0e54d8f8b7d4bc528f48c891969a7049195 ports: - containerPort: 8080 name: http readinessProbe: httpGet: path: / port: http --- apiVersion: v1 kind: Service metadata: name: app1-service namespace: cilium-l2l7-demo spec: selector: app: app1 ports: - name: http port: 80 targetPort: http protocol: TCP ``` ```bash kubectl apply -f l7-app.yaml kubectl -n cilium-l2l7-demo wait --for=condition=Ready pod/client pod/outsider --timeout=120s kubectl -n cilium-l2l7-demo rollout status deployment/app1 --timeout=120s kubectl -n cilium-l2l7-demo get pods,services -o wide kubectl -n cilium-l2l7-demo exec client -- \ curl --fail --silent --show-error --max-time 5 http://app1-service/ ``` Outsider의 기준 연결도 확인합니다. Service는 80을 노출하지만 backend는 **8080**을 수신하므로 Pod ingress 정책은 backend port를 사용합니다. 이전의 없거나 맞지 않는 앱 설정과 정책 label·entrypoint가 다른 client Pod를 교정한 예제입니다. **`app1-http.yaml`** ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: app1-http namespace: cilium-l2l7-demo spec: endpointSelector: matchLabels: app: app1 ingress: - fromEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-l2l7-demo k8s:app: client toPorts: - ports: - port: '8080' protocol: TCP rules: http: - method: ^GET$ path: ^/$ - method: ^POST$ path: ^/api/v1$ headerMatches: - name: x-demo-tenant value: team-a ``` ```bash kubectl apply -f app1-http.yaml kubectl -n cilium-l2l7-demo get cnp app1-http -o yaml ``` 정책 실현 후 지정 client의 GET `/`를 허용합니다. POST `/api/v1`은 이 규칙상 정확한 `x-demo-tenant: team-a`가 있어야 하며 backend도 해당 API를 구현해야 합니다. 다른 method·path·peer 거부는 다른 적용 정책의 허용이 없는 범위에서 성립합니다. 애플리케이션 응답과 실현 정책·플로우를 함께 확인합니다. 이 header는 **실습용 필터이지 인증이 아닙니다**. 이전 32문자 “토큰” 조건은 신원·서명·발급자·만료·인가를 검증하지 않습니다. 릴리스 구현에서 값이 있는 `headers` 문자열은 리터럴 비교입니다. `X-Auth-Token: ^[a-zA-Z0-9]{32}$`는 정규식 토큰 검증기가 아닙니다. 정확한 값·존재 조건에는 명시적인 `headerMatches`를 사용하고 사용자 인증은 애플리케이션·적절한 인증 계층에서 처리합니다. Method·path는 정규식을 지원합니다. 내장 Cilium HTTP 정책에는 임의 요청 본문 조건이 없습니다. Header 존재, 정확한 값, URL 필터, 애플리케이션 인가는 다른 제어입니다. ## 서비스 메시 통합 Cilium은 네트워킹·지원 네트워크 정책을, Istio는 구성한 proxy·mesh 동작을 담당합니다. 통합으로 Istio sidecar를 자동 우회하거나 mTLS 비용을 제거하거나 모든 trace를 합치거나 요청 성능 향상을 보장하지 않습니다. ```text 설정: istiod --> Istio Envoy proxy 요청: app --> source sidecar --> Cilium/network --> destination sidecar --> app ``` ### Istio 트래픽 경로 보존 현재 Cilium 통합 가이드는 kube-proxy 공존과 신중히 구성한 완전 대체 방식을 제공합니다. 공존 설정 조각은 다음과 같습니다. **`istio-cilium-values.yaml`** ```yaml kubeProxyReplacement: false socketLB: hostNamespaceOnly: true cni: exclusive: false ``` 의도적으로 준비한 대체 구성의 `kubeProxyReplacement: true`에는 실제 API endpoint와 대체 조건이 추가로 필요합니다. Pod socket 변환이 Istio 경로를 우회하지 않도록 `socketLB.hostNamespaceOnly: true`, 노드 CNI 설정을 공유할 때 `cni.exclusive: false`를 유지합니다. Istio sidecar redirection은 init container 또는 Istio CNI node agent를 사용할 수 있고 ambient는 해당 node·CNI 경로를 사용합니다. [유지보수되는 Istio 설치 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/01-installation.md)에서 한 모드를 선택합니다. Kubernetes API 서버가 Istio admission webhook에 도달해야 합니다. 관리형 제어플레인·overlay 환경에는 문서화된 routing·host-network 대책이 필요할 수 있지만 모든 overlay에 `istiod hostNetwork: true`를 처방하지 않습니다. ### mTLS 유지와 L7 책임 분리 Istio가 암호화한 workload 트래픽에 평문 Cilium HTTP 검사를 적용하지 않습니다. 아래 예제는 Istio mTLS·L7 routing을 유지하고 Cilium에는 **L3/L4 정책만** 사용합니다. 이전 이중 L7 예제를 통과시키려고 mTLS를 끄면 보안 설계가 바뀝니다. 이미 준비된 `istio-cilium-demo` namespace에 `productpage`, `reviews`, 9080 reviews Service와 `version: v1`/`v2` Pod가 있는 **sidecar 모드** 구성입니다. 완전한 Bookinfo 배포가 아닙니다. **`istio-reviews.yaml`** ```yaml apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews-route namespace: istio-cilium-demo spec: hosts: - reviews.istio-cilium-demo.svc.cluster.local http: - match: - headers: end-user: exact: jason route: - destination: host: reviews.istio-cilium-demo.svc.cluster.local subset: v2 port: number: 9080 - route: - destination: host: reviews.istio-cilium-demo.svc.cluster.local subset: v1 port: number: 9080 --- apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: reviews-subsets namespace: istio-cilium-demo spec: host: reviews.istio-cilium-demo.svc.cluster.local subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 --- apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: default namespace: istio-cilium-demo spec: mtls: mode: STRICT --- apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: reviews-l4 namespace: istio-cilium-demo spec: endpointSelector: matchLabels: app: reviews ingress: - fromEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: istio-cilium-demo k8s:app: productpage toPorts: - ports: - port: '9080' protocol: TCP ``` DestinationRule이 VirtualService의 subset을 정의합니다. `end-user: jason`은 실습 route 선택이며 인증된 identity가 아닙니다. 실제 주입, endpoint label, subset readiness와 mesh telemetry를 확인합니다. 9080 Cilium 규칙을 ambient 예제로 재사용하지 않습니다. Ambient HBONE은 15008의 암호화된 터널이며 관측 트래픽·identity 경계가 달라집니다. 해당 topology의 Istio 정책과 플랫폼별 Cilium 네트워크 제어를 적용합니다. ## 로드 밸런싱 아키텍처 Cilium service map, backend map, reverse-NAT 상태와 conntrack은 서로 다른 전달 단계를 담당합니다. L7 Envoy LB는 다른 구성 요소이므로 BPF Service datapath와 알고리즘 목록을 합치지 않습니다. ### BPF 전달 모드와 알고리즘 | 설정·메커니즘 | 의미와 경계 | |---|---| | `loadBalancer.mode: snat` | 기본 전달 모드. 해당 외부 Service 경로에 직접 반환 대신 소스 변환·역방향 상태 사용 | | `dsr` | 원격 backend 응답이 진입 LB 노드를 우회할 수 있음. 반환 경로가 허용되어야 하며 앞단 proxy가 바꾼 client IP를 복구하지 못함 | | `hybrid` | TCP는 DSR, UDP는 SNAT. 잘못된 tunnel/auto-direct-routing 조합과 다른 유효한 LB 기능 | | Annotation 기반 전달 | 선택적 Service별 동작. Forwarding annotation은 생성 시 선택하며 변경하면 연결이 끊길 수 있음 | | `loadBalancer.algorithm: random` | 기본 BPF backend 선택 알고리즘 | | `maglev` | 지원되는 외부 N–S 경로와 XDP의 일관된 선택. 일반 socket-LB E–W 연결에는 적용되지 않음 | | `sessionAffinity: ClientIP` | 별도 Kubernetes Service affinity. 외부 source IP 또는 해당 내부 socket-LB 경로의 client network-namespace cookie 사용 | Maglev가 backend 제거 후 session 생존을 보장하지 않습니다. 노드의 backend 상태·table size·seed가 일치해야 합니다. 기본 크기는 16381이며 아래 65521은 허용 값이지 보편적 권장값은 아닙니다. 큰 table은 메모리를 더 사용하고 affinity 만료·연결 상태는 hashing과 별개입니다. DSR dispatch는 native-routing IP option, 문서화된 native/Geneve-overlay의 Geneve 또는 native 전용 IPIP/IP6IP6 경로를 사용할 수 있습니다. VXLAN overlay를 Geneve DSR로 그대로 바꿔 생각하면 안 됩니다. IPIP에는 별도 port·변환 제약이 있으므로 선택 전 릴리스 가이드를 확인합니다. XDP 가속에는 지원 장치·driver가 필요합니다. `native`는 선택 장치의 지원을 전제로 하고 `best-effort`는 지원 장치에서만 켭니다. 예전 `enable-xdp-acceleration` 키만 쓰면 되는 기능이 아니며 초기 XDP 전달은 후단 tcpdump 지점에 보이지 않을 수 있습니다. ### Cilium과 kube-proxy | 항목 | 올바른 비교 | |---|---| | Linux Service 구현 | Cilium은 BPF hook·map, kube-proxy는 iptables·nftables 및 Kubernetes 1.35부터 폐기 예정인 IPVS | | 플랫폼 | Cilium의 Linux·kernel 조건 적용. Windows kernelspace kube-proxy는 별도 구현 | | 연결 상태 | Cilium BPF conntrack·NAT와 Linux netfilter conntrack은 다름. “선택적 대 항상”은 지나친 단순화 | | L7 | Cilium은 지원 proxy를 통합하며 kube-proxy Service 전달은 HTTP 정책 엔진이 아님 | | 성능 | 동일 workload·설정으로 측정. 제품 이름으로 고정 순위가 결정되지 않음 | Linux IPVS 하위 시스템에 direct-routing 기능이 있다고 kube-proxy에도 동일한 DSR 설정이 있다고 추론하지 않습니다. ## 준비된 DSR/Maglev 실습 새로 준비한 kube-proxy-free **IPv4 Geneve-overlay** 테스트 클러스터용 프로필입니다. 기존 CNI 전환, kube-proxy 제거, 클라우드 anti-spoofing·routing 설정을 수행하지 않습니다. 겹치지 않는 Pod CIDR과 외부 반환 경로를 검증합니다. **`lb-values.yaml`** ```yaml kubeProxyReplacement: true routingMode: tunnel tunnelProtocol: geneve ipv4: enabled: true ipv6: enabled: false ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 loadBalancer: mode: dsr dsrDispatch: geneve algorithm: maglev acceleration: disabled maglev: tableSize: 65521 bpf: masquerade: true enableIPv4Masquerade: true enableIPv6Masquerade: false l7Proxy: true envoy: enabled: true hubble: enabled: true relay: enabled: true ``` 실제 API endpoint와 클러스터별로 보존한 Maglev seed를 지정합니다. Seed는 무작위 12바이트의 base64 인코딩입니다. 한 번 생성해 cluster values와 함께 보존·재사용하며 업그레이드마다 다시 만들지 않습니다. ```bash : "${API_SERVER_HOST:?Set the reachable real API server host, not its ClusterIP}" : "${API_SERVER_PORT:?Set the actual API server port}" : "${MAGLEV_SEED:?Set the persisted base64 encoding of 12 random bytes}" helm repo add cilium https://helm.cilium.io/ helm repo update cilium helm install cilium cilium/cilium --version 1.20.1 --namespace kube-system \ --values lb-values.yaml \ --set-string k8sServiceHost="$API_SERVER_HOST" \ --set k8sServicePort="$API_SERVER_PORT" \ --set-string maglev.hashSeed="$MAGLEV_SEED" cilium status --wait ``` 선택한 클러스터에서 이 가이드가 만든 namespace를 사용합니다. 다른 일회용 클러스터라면 먼저 해당 클러스터에서 namespace 생성·label 단계를 반복합니다. 앞선 client 전용 HTTP 정책이 실험을 막지 않도록 HTTP 앱과 외부 LB backend의 label을 구분했습니다. **`lb-echo.yaml`** ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: lb-echo namespace: cilium-l2l7-demo spec: replicas: 1 selector: matchLabels: app: lb-echo template: metadata: labels: app: lb-echo spec: automountServiceAccountToken: false containers: - name: http image: quay.io/cilium/json-mock:v1.4.1@sha256:6a66df90808a39c02e7a9d58af7bf0e54d8f8b7d4bc528f48c891969a7049195 ports: - containerPort: 8080 name: http readinessProbe: httpGet: path: / port: http --- apiVersion: v1 kind: Service metadata: name: lb-echo namespace: cilium-l2l7-demo spec: type: NodePort selector: app: lb-echo ports: - name: http port: 80 targetPort: http protocol: TCP ``` ```bash kubectl apply -f lb-echo.yaml kubectl -n cilium-l2l7-demo rollout status deployment/lb-echo --timeout=120s kubectl -n cilium-l2l7-demo get pods -l app=lb-echo -o wide kubectl -n cilium-l2l7-demo get service lb-echo -o wide NODEPORT=$(kubectl -n cilium-l2l7-demo get service lb-echo -o jsonpath='{.spec.ports[0].nodePort}') ``` **Backend와 다른 진입 노드** 및 클러스터 socket LB의 영향을 받지 않는 외부 client를 사용합니다. 실제 값으로 `http://ENTRY_NODE_IP:NODEPORT/`를 요청합니다. Pod→ClusterIP curl로 외부 DSR·Maglev 동작을 증명하지 못합니다. HTTP 성공만 보지 말고 요청·응답 경로와 backend·연결 상태를 검증합니다. ## 마스커레이딩 Pod egress masquerading은 설정된 외부 경로에서 필요한 경우 source 주소를 변환합니다. 암호화·방화벽이 아니며 Service DNAT·DSR과 구분합니다. 다음 조각은 **실제로 해당 Pod source·반환 경로를 지원하는 네트워크에서만** 예시 목적지 `10.0.0.0/8`을 source masquerading에서 제외합니다. **`masquerade-values.yaml`** ```yaml bpf: masquerade: true enableIPv4Masquerade: true enableIPv6Masquerade: false ipv4NativeRoutingCIDR: 10.0.0.0/8 ``` `ipv4NativeRoutingCIDR`은 라우팅 가능하다고 가정한 범위와 masquerade 제외를 지정합니다. Route를 설치하거나 전체 datapath를 native 모드로 바꾸지 않습니다. 반환 경로 없이 넓게 제외하면 연결이 깨질 수 있습니다. - 이 릴리스의 BPF masquerading은 BPF NodePort에 의존하고 프로그램이 붙은 장치에서만 적용됩니다. 실제 장치를 확인하고 필요하면 문서화된 `devices` 설정을 사용합니다. - Iptables 구현에는 `egressMasqueradeInterfaces` 동작이 있습니다. 이를 예전 일반 `masquerade-interfaces`·`masquerade-all` 예제와 함께 모든 BPF 경로의 공통 제어로 생각하지 않습니다. - IPv6 BPF masquerading은 beta입니다. 어느 구현도 Cilium 플랫폼·커널 조건을 없애지 않으며 둘 다 결국 커널에서 처리됩니다. - 노드 주소 예외, ip-masq-agent 제외와 후단 cloud/NAT gateway가 관측 source에 영향을 줍니다. 통제된 관측 서버와 노드 상태·캡처를 함께 사용합니다. 임의 공용 사이트 접속 성공은 특정 NAT 구현의 증거가 아닙니다. 관련 에이전트에서 확인합니다. ```bash kubectl -n kube-system get pods -l k8s-app=cilium -o wide export CILIUM_POD=cilium-REPLACE-WITH-AGENT-ON-TARGET-NODE kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg status --verbose kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg bpf nat list ``` ## Fragment 처리와 MTU 릴리스된 fragment tracker는 제한된 LRU map에 datagram 식별자와 L4 source·destination port를 저장합니다. L4 header가 없는 후속 fragment의 port 문맥을 복원할 수 있지만 **BPF payload 재조립 엔진**이나 fragment 공격 방지 보장이 아닙니다. 문서화된 기능은 해당 flag로 기본 활성화되는 IPv4·IPv6 tracking을 포함하며 beta로 표시됩니다. IPv4 flag `enable-ipv4-fragment-tracking`은 여전히 유효합니다. `bpf-fragments-map-max`는 추적 datagram map 용량이며 이전 `fragment-tracking-timeout`, `max-fragments-per-flow`는 릴리스 설정 계약이 아닙니다. Chart의 추가 설정 map을 사용하는 명시적 예입니다. **`fragment-values.yaml`** ```yaml extraConfig: enable-ipv4-fragment-tracking: 'true' bpf-fragments-map-max: '8192' ``` 8192는 용량 예시이며 flow별 fragment 수 한도가 아닙니다. 용량 진단에는 `cilium_ipv4_frag_datagrams` / `cilium_ipv6_frag_datagrams`와 pressure를 확인합니다. Pressure가 재조립 성공·공격 차단 카운터는 아닙니다. 올바른 packet 크기와 작동하는 PMTUD 경로를 우선합니다. PMTUD는 관련 오류 신호와 네트워크 동작에 의존하며 어디서나 자동 최적 크기를 보장하지 않습니다. Cilium `MTU`는 **기반 네트워크 override**입니다. [네트워킹 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/03-networking.md)처럼 일반 VXLAN 오버헤드는 IPv4 underlay 50바이트, IPv6 70바이트이며 1500 경로의 기반 값을 무조건 1450으로 설정하면 두 번 뺄 수 있습니다. ## 관측과 문제 해결 올바른 노드, 실제 Envoy 배포 방식, 의도한 정책, 실현 endpoint 상태와 새 트래픽을 확인합니다. Agent 로컬 작업에는 `cilium-dbg`, cluster 작업에는 독립 CLI를 사용합니다. 제거된 `policy trace`나 강제 endpoint 재생성부터 시작하지 않습니다. 활성 Hubble Relay의 port-forward를 별도 터미널에서 유지하고 관련 플로우를 관찰합니다. ```bash cilium hubble port-forward ``` ```bash hubble observe --namespace cilium-l2l7-demo --protocol http --last 20 hubble observe --namespace cilium-l2l7-demo --verdict DROPPED --last 20 ``` HTTP 정책 거부는 패킷 DROPPED 대신 HTTP 403일 수 있습니다. Timeout은 readiness, DNS, routing, TLS, 관측 문제일 수도 있으므로 모든 오류를 정책 성공으로 해석하지 말고 계층별 근거를 대조합니다. ## 검증 한계와 참고 자료 릴리스 소스·schema와 제한된 로컬 fixture로 확인한 예제이며 운영 검증 플랫폼·실제 클러스터 벤치마크가 아닙니다. 이미지 실행, webhook 연결, mTLS 트래픽, DSR 반환, NAT·fragment 동작은 준비한 환경에서 검증해야 합니다. 이번 실행의 label을 가진 애플리케이션 리소스만 정리하고 실습 종료 목적으로 클러스터 CNI를 제거하지 않습니다. - [Cilium kube-proxy replacement/DSR/Maglev](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/kubernetes/kubeproxy-free.rst), [masquerading](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/masquerading.rst), [fragment handling](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/concepts/fragmentation.rst), [fragment map implementation](https://github.com/cilium/cilium/blob/v1.20.1/pkg/maps/fragmap/fragmap.go) - [L2 Announcements](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/l2-announcements.rst), [multicast](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/multicast.rst), [HTTP rule translator](https://github.com/cilium/cilium/blob/v1.20.1/pkg/envoy/policy/envoy_l7_rules_translator.go), [Envoy chart defaults](https://github.com/cilium/cilium/blob/v1.20.1/install/kubernetes/cilium/templates/_helpers.tpl) - [Cilium/Istio integration](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/servicemesh/istio.rst), [Istio CNI/init-container modes](https://istio.io/latest/docs/setup/additional-setup/cni/), [webhook requirements](https://istio.io/latest/docs/ops/configuration/mesh/webhook/), [Istio 1.31 schemas](https://github.com/istio/istio/blob/1.31.0/manifests/charts/base/files/crd-all.gen.yaml) - [Kubernetes Service proxy modes/affinity](https://kubernetes.io/docs/reference/networking/virtual-ips/), [Cilium 1.20.1 values](https://github.com/cilium/cilium/blob/v1.20.1/install/kubernetes/cilium/values.yaml) [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) ## 퀴즈 [L2–L7·로드 밸런싱 문제 확인](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/05-l2-l7-networking-quiz). ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/06-security-visibility ---------------------------------------- # 보안 및 가시성 > **검토 기준**: Cilium 1.20.1, Cilium CLI 0.20.0, Hubble CLI 1.19.4. > **최종 검토**: 2026년 9월 12일. Cilium 1.20의 Kubernetes 호환 범위는 1.33–1.36이며, kubectl은 API 서버의 지원 버전 차이 범위에 맞춥니다. ## 실습 환경 설정 스케줄링 가능한 Linux 노드가 최소 2개이고 DNS와 정책 적용이 정상인 기존 Cilium 1.20.1 테스트 클러스터를 사용합니다. EKS 제약과 검증된 CLI 다운로드를 포함한 [설치 및 플랫폼 전제 조건](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)을 먼저 확인합니다. 이 예제는 CNI를 설치하거나 교체하지 않습니다. Helm, kubectl, Cilium/Hubble CLI와 jq가 필요합니다. 실습은 `kube-system`의 `k8s-app=kube-dns` 레이블을 가진 CoreDNS Pod를 가정합니다. 실제 DNS 경로를 확인해야 하며, NodeLocal DNS나 다른 배포판은 목적지와 포트가 다를 수 있습니다. 기존 정책, 프록시 구성과 플랫폼 제약도 결과에 영향을 줍니다. ### Hubble 설치 및 설정 `hubble-values.yaml`로 저장합니다. 로컬 서버, Relay, UI와 선택한 메트릭 플러그인을 활성화하는 설정 조각입니다. 기존 값을 먼저 검토한 뒤 **같은 차트 버전의 기존 릴리스**에 적용합니다. 버전을 올리는 경우에는 이전 값을 그대로 재사용하지 말고 업그레이드 절차를 따릅니다. ```yaml # hubble-values.yaml hubble: enabled: true relay: enabled: true ui: enabled: true metrics: enabled: - dns - drop - tcp - flow - httpV2 serviceMonitor: enabled: false ``` ```bash helm upgrade cilium cilium/cilium --namespace kube-system \ --version 1.20.1 --reuse-values --values hubble-values.yaml --wait cilium status --wait # Terminal 1: keep this process running; stop it with Ctrl-C. cilium hubble port-forward ``` 다른 터미널에서 API 연결을 확인합니다. 포트 포워딩은 로컬 연결이며 Relay나 UI를 공인 LoadBalancer로 노출하지 않습니다. ```bash # Terminal 2 hubble status hubble observe --last 20 # Optional UI; keep its local forwarding process running while using it. cilium hubble ui ``` ## Cilium의 보안 기능 Cilium은 네트워크 정책, 엔드포인트 ID와 선택적 암호화를 제공합니다. Hubble은 그 결과 발생하는 네트워크 이벤트를 관측합니다. 보안 요구 사항을 검토할 때 각 기능의 범위를 구분해야 합니다. ### Cilium 보안 아키텍처 | 책임 | 구성 요소와 범위 | | --- | --- | | 네트워크 마이크로세그멘테이션 | Cilium L3/L4 정책이 ID, 주소, 포트와 방향을 선택합니다. 네임스페이스 범위의 CiliumNetworkPolicy는 해당 네임스페이스의 엔드포인트를 선택합니다. | | HTTP 정책 | Cilium의 Envoy 연동이 HTTP를 볼 수 있는 경로에서 메서드, 경로와 헤더를 검사합니다. DNS 정책은 DNS 프록시를 사용합니다. | | DNS/FQDN 제어 | DNS 규칙은 질의를 제어하고, `toFQDNs`는 관측한 DNS 응답에서 학습한 목적지 IP를 허용합니다. 악성 도메인 평판 피드가 자동으로 제공되는 것은 아닙니다. | | 노드 전송 암호화 | IPsec 또는 WireGuard가 지원되는 노드 간 트래픽을 보호합니다. 적용 범위는 모드와 구성에 따라 달라집니다. | | 네트워크 조사 | Hubble이 흐름 메타데이터와 정책 판정을 기록하고 Relay, CLI, UI로 제공합니다. | | 프로세스와 시스템 호출 보안 | **Tetragon**은 런타임 이벤트와 구성된 정책 집행을 담당하는 별도 프로젝트입니다. Hubble 활성화가 Tetragon 설치를 뜻하지 않습니다. | | 위협 탐지와 대응 | 외부 알림 규칙, SIEM/WAF, 대응 컨트롤러를 탐지 목적과 동작에 맞게 별도로 구성합니다. | ### 네트워크 및 애플리케이션 보안 최소 권한 정책으로 횡적 이동을 제한하고 명시적 egress 정책으로 의존성을 제한합니다. 보안 ID는 보안 관련 레이블 집합에서 도출되므로 Pod마다 반드시 고유한 것은 아니며, 최종 사용자를 인증하지도 않습니다. 현재 Cilium 정책은 HTTP와 DNS L7 규칙을 지원합니다. gRPC는 HTTP/2 트래픽이 보이는 경로에서 HTTP 메서드·경로·헤더 규칙을 사용할 수 있습니다. Kafka 토픽 정책은 더 이상 지원되지 않습니다. HTTP `headers`의 값은 리터럴 일치 조건이며 정규식 기반 인증이 아닙니다. `Authorization` 헤더의 존재나 형태만으로 JWT 서명, 발급자 또는 인가 클레임을 검증할 수 없습니다. 애플리케이션 인증 계층이나 구성된 게이트웨이를 사용합니다. 8443 포트에 HTTP 규칙을 지정해도 HTTPS가 복호화되지는 않습니다. 암호화된 애플리케이션 데이터를 HTTP 정책으로 평가하려면 TLS 종료 또는 별도로 지원되는 검사 구성이 필요합니다. L7 검사를 위해 서비스 메시 암호화를 우회하지 않습니다. ### ID, 인증과 암호화 SPIRE 기반 Cilium **상호 인증은 Beta**이며 보안 ID에 대한 별도 경로의 핸드셰이크를 수행합니다. 이 핸드셰이크 자체는 애플리케이션 트래픽을 암호화하지 않습니다. 문서화된 제약에는 ClusterMesh 미지원과 임의의 외부 mTLS 시스템과의 비호환이 포함됩니다. 별도의 **ztunnel 워크로드 mTLS도 Beta**이며 등록과 인증서 전제 조건이 따로 있습니다. ID 선택자만으로 어느 기능도 자동 활성화되지 않습니다. ### 암호화 구성 다음은 **둘 중 하나를 선택하는 Helm 설정 조각**입니다. 함께 켜는 설정이 아닙니다. 설치나 변경을 계획할 때 하나를 선택하고 커널, 라우팅, 플랫폼 전제 조건을 검증합니다. ```yaml # wireguard-values.yaml encryption: enabled: true type: wireguard nodeEncryption: false ``` ```yaml # ipsec-values.yaml encryption: enabled: true type: ipsec nodeEncryption: false ipsec: secretName: cilium-ipsec-keys ``` WireGuard에는 커널 지원과 노드 간 UDP 51871 경로가 필요합니다. IPsec 활성화 전에는 Cilium 네임스페이스에 올바른 형식으로 안전하게 관리되는 `cilium-ipsec-keys` Secret이 있어야 합니다. 공식 키 생성·교체 절차를 따릅니다. ConfigMap에 키 파일 이름만 지정해도 키와 볼륨이 생성되는 것은 아닙니다. 기본적으로 이 모드는 노드를 가로지르는 지원 대상 Cilium 관리 Pod 트래픽을 보호하며, 같은 노드의 트래픽은 이러한 노드 터널로 암호화되지 않습니다. 임의의 외부 목적지 트래픽도 자동 보호되지 않습니다. WireGuard의 노드 간 암호화 확장은 별도 Beta 옵션입니다. 기본적으로 컨트롤 플레인 노드는 이 확장에서 제외되지만, 해당 노드의 Cilium 관리 Pod 간 트래픽은 다른 노드를 통과할 때 암호화될 수 있습니다. 실제 패킷 경로를 검증하고 필요한 곳에는 애플리케이션 TLS를 사용합니다. 호스트 방화벽 호환성도 암호화 모드에 따라 다릅니다. ## Hubble을 통한 네트워크 가시성 Hubble은 데이터 경로, 프록시, 에이전트 이벤트를 받아 Kubernetes 메타데이터를 추가합니다. 모든 eBPF 맵을 주기적으로 읽기만 하는 도구가 아닙니다. ```text 커널/데이터 경로 이벤트 + 프록시/에이전트 이벤트 | v 각 Cilium 에이전트의 Hubble 서버 | | | 유한 흐름 버퍼 메트릭 엔드포인트 선택적 파일 내보내기 | TCP 9965 | Relay 질의 ^ 로그 수집기/저장소 ^ | 스크레이프 | Prometheus <--- Grafana 질의 CLI / UI ``` 서버의 메모리 이력은 유한합니다. Relay는 여러 서버를 질의하며 영구 데이터베이스가 아닙니다. UI는 흐름과 서비스 의존성 맵을 제공하고 CLI는 명시적 필터를 지원합니다. 조회 결과가 없으면 트래픽 부재뿐 아니라 잘못된 필터, 피어 장애, L7 가시성 부재, 버퍼 덮어쓰기나 이벤트 손실도 확인합니다. ### Hubble CLI 사용 예제 ```bash hubble observe --namespace cilium-security-demo --last 100 hubble observe --from-pod cilium-security-demo/frontend \ --to-service cilium-security-demo/backend --last 100 hubble observe --namespace cilium-security-demo --protocol http \ --http-status '4+' --http-status '5+' --last 100 hubble observe --namespace cilium-deny-demo --verdict DROPPED \ --drop-reason-desc POLICY_DENIED --last 100 hubble observe --pod cilium-security-demo/frontend --follow ``` `--pod namespace/name`은 양쪽 엔드포인트를 선택합니다. 방향을 지정하려면 `--from-pod`/`--to-pod`를 사용합니다. Pod 이름은 레이블 선택자가 아니며 레이블에는 `--from-label`/`--to-label`을 사용합니다. `--namespace`와 `--from-pod`/`--to-pod`를 함께 지정하면 CLI가 거부합니다. `DROPPED`는 판정이고 `POLICY_DENIED`는 `--drop-reason-desc`로 선택하는 드롭 사유입니다. HTTP 상태 접두사는 `4..`/`5..`가 아니라 `4+`/`5+`입니다. HTTP 필터에는 프록시가 생성한 L7 이벤트가 필요하며, TCP 연결이 차단되면 HTTP 상태가 없을 수도 있습니다. ## 네트워크 가시성 및 모니터링 ### Hubble 메트릭 플러그인마다 관측 대상이 다릅니다. | 플러그인 | 메트릭 예 | 의미와 제약 | | --- | --- | --- | | `flow` | `hubble_flows_processed_total` | 프로토콜·유형·판정별 처리된 흐름 이벤트이며 모든 경로의 고유 요청이나 패킷 수가 아닙니다. | | `drop` | `hubble_drop_total{reason="POLICY_DENIED"}` | 관측된 드롭이며 사유 레이블은 enum 이름입니다. | | `tcp` | `hubble_tcp_flags_total` | 관측된 TCP 플래그이며 일반적인 RTT·재전송·동시 연결 수 메트릭이 아닙니다. | | `dns` | `hubble_dns_queries_total`, `hubble_dns_responses_total` | DNS 질의·응답·응답 코드이며 일반적인 DNS 지연 히스토그램이 아닙니다. | | `httpV2` | `hubble_http_requests_total`, `hubble_http_request_duration_seconds` | HTTP 응답 흐름으로부터 요청 수·상태와 초 단위 시간을 기록합니다. HTTP 가시성이 필요합니다. | `http`와 `httpV2`를 동시에 활성화하지 않습니다. 출발지·목적지 레이블의 카디널리티를 관리하고 요청 헤더나 민감한 ID는 목적 없이 추가하지 않습니다. 이벤트 부재를 트래픽 부재의 증거로 해석하기 전에 `hubble_lost_events_total`과 피어 상태를 확인합니다. ### Prometheus 통합 차트는 Cilium 네임스페이스에 기본 포트 **9965**의 헤드리스 `hubble-metrics` Service를 생성합니다. Service의 `k8s-app=hubble` 레이블은 탐색에 사용되며, Service 자체는 `k8s-app=cilium` 에이전트 Pod를 선택합니다. Prometheus는 단일 정적 DNS 주소에 의존하지 말고 개별 엔드포인트를 탐색해야 합니다. Prometheus Operator와 ServiceMonitor CRD가 이미 있다면 다음 조각을 릴리스 값에 병합합니다. `release: monitoring`은 예시이며 Prometheus의 `serviceMonitorSelector`와 일치해야 합니다. 네임스페이스 선택자에도 ServiceMonitor의 네임스페이스가 포함되어야 합니다. 선택되지 않은 리소스는 스크레이프되지 않습니다. ```yaml # hubble-servicemonitor-values.yaml hubble: metrics: serviceMonitor: enabled: true labels: release: monitoring ``` 차트의 ServiceMonitor는 `hubble-metrics`라는 포트 이름과 Cilium 네임스페이스의 엔드포인트를 사용합니다. Operator가 없다면 실제 Prometheus 설정에 동등한 Kubernetes 서비스 탐색을 구성합니다. 연결되지 않은 ConfigMap 생성만으로 Prometheus가 설정되지는 않습니다. `*.hubble-metrics.cilium.io`는 메트릭 TLS ID 구성에 쓰이며 9091 포트의 공인 스크레이프 대상이 아닙니다. Grafana 대시보드는 Prometheus 메트릭을, Hubble UI 서비스 맵은 Relay를 질의합니다. 활성화한 플러그인과 레이블에 맞는 대시보드를 가져옵니다. HTTP 페이로드를 관측할 수 없는 트래픽은 HTTP 대시보드에 나타나지 않습니다. ### 흐름 내보내기와 보존 노드 로컬 순환 파일이 필요하면 `hubble-export-values.yaml`을 선택적으로 병합합니다. 필드 마스크는 네트워크 메타데이터만 유지하며 전체 HTTP 헤더를 내보내지 않습니다. ```yaml # hubble-export-values.yaml hubble: export: static: enabled: true filePath: /var/run/cilium/hubble/events.log fileMaxSizeMb: 10 fileMaxBackups: 5 fieldMask: - time - source.namespace - source.pod_name - destination.namespace - destination.pod_name - l4 - IP - node_name - is_reply - verdict - drop_reason_desc ``` 정적 exporter는 노드마다 파일을 쓰고 설정에 따라 순환시킵니다. 노드 손실 뒤에도 이벤트가 필요하면 별도 수집기, 접근 제어와 저장소 보존 정책을 구성합니다. 정적 설정 변경에는 에이전트 롤아웃이 필요하며 동적 exporter의 갱신 방식은 다릅니다. 필터와 필드 마스크는 이벤트나 필드를 의도적으로 생략할 수 있고 유한 버퍼에서 관측 데이터가 손실될 수도 있습니다. ## 실시간 위협 탐지 Hubble은 조사 근거를 제공하지만 완전한 IDS, WAF 또는 자동 격리 시스템을 활성화하는 스위치는 제공하지 않습니다. `enable-threat-detection`, `enable-anomaly-detection`, `alert-to-slack`은 지원되는 Cilium 설정이 아닙니다. 여러 목적지에 대한 반복 거부는 스캔 의심의 근거가 될 수 있고 트래픽 급증도 조사할 가치가 있습니다. 그 자체가 공격의 증거는 아닙니다. 애플리케이션 인증 로그, 워크로드 변경, API 감사 이벤트, 배포되어 있다면 Tetragon 런타임 이벤트와 연관 분석합니다. SQL 삽입, XSS, 명령 삽입은 적절한 애플리케이션/WAF/탐지 규칙이 필요하며 일반 HTTP 흐름 기록만으로 분류되지 않습니다. 알림을 위해서는 정상 트래픽, 데이터 부재와 이벤트 손실을 포함해 외부 Prometheus/SIEM 규칙을 정의하고 시험합니다. 속도 제한, 방화벽 격리와 대응 자동화는 별도 제어입니다. 대응 범위를 제한하고 복구 경로를 마련하며 드롭이 관측된 모든 Pod를 자동 격리하지 않습니다. ## 실습: Hubble 설치 및 활용 넓은 허용 규칙이 기본 거부 실습을 무효화하지 않도록 **서로 다른 새 네임스페이스 2개**를 사용합니다. 데이터베이스나 외부 API는 생성하지 않습니다. 명령은 테스트 클러스터에서 실행할 예시이며, 문서 감사에서는 스키마와 로컬 fixture를 검증했고 실제 배포는 하지 않았습니다. ### 1. 워크로드 생성과 기준 상태 확인 `visibility-app.yaml`로 저장합니다. 클라이언트와 서버 이미지는 Cilium CLI의 버전별 테스트 기본값을 사용합니다. 백엔드 안티어피니티 때문에 두 번째 스케줄 가능 노드가 필요하며 frontend에서 backend로의 경로가 노드를 가로지릅니다. ```yaml # visibility-app.yaml apiVersion: v1 kind: Pod metadata: name: frontend labels: app: frontend spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause --- apiVersion: v1 kind: Pod metadata: name: outsider labels: app: outsider spec: automountServiceAccountToken: false containers: - name: client image: quay.io/cilium/alpine-curl:v1.10.0@sha256:913e8c9f3d960dde03882defa0edd3a919d529c2eb167caa7f54194528bde364 command: - /usr/bin/pause --- apiVersion: apps/v1 kind: Deployment metadata: name: backend spec: replicas: 1 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: automountServiceAccountToken: false affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: frontend topologyKey: kubernetes.io/hostname containers: - name: http image: quay.io/cilium/json-mock:v1.4.1@sha256:6a66df90808a39c02e7a9d58af7bf0e54d8f8b7d4bc528f48c891969a7049195 ports: - containerPort: 8080 name: http readinessProbe: httpGet: path: / port: http --- apiVersion: v1 kind: Service metadata: name: backend spec: selector: app: backend ports: - name: http port: 8080 targetPort: http protocol: TCP ``` ```bash set -eu for ns in cilium-security-demo cilium-deny-demo; do kubectl create namespace "$ns" kubectl label namespace "$ns" audit-lab=security-visibility kubectl --namespace "$ns" apply -f visibility-app.yaml kubectl --namespace "$ns" wait --for=condition=Ready pod/frontend pod/outsider --timeout=120s kubectl --namespace "$ns" rollout status deployment/backend --timeout=120s for client in frontend outsider; do kubectl --namespace "$ns" exec "$client" -- \ curl --fail --silent --show-error --max-time 5 http://backend:8080/ done done ``` 정책 적용 전에 두 네임스페이스의 두 클라이언트 모두 백엔드에 접근할 수 있어야 합니다. 그렇지 않으면 준비 상태, 스케줄링, DNS와 네트워크부터 해결합니다. 임의의 curl 오류를 정책 차단의 증거로 해석하지 않습니다. ### 2. HTTP 정책 적용과 관찰 `backend-http.yaml`로 저장합니다. `frontend` ID가 백엔드 TCP 8080에 `GET /`를 보내도록 허용합니다. L4 허용 규칙이 겹치면 L7 제한을 우회할 수 있으므로 넓은 ingress 허용 규칙을 함께 두지 않습니다. ```yaml # backend-http.yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: backend-http namespace: cilium-security-demo spec: endpointSelector: matchLabels: app: backend ingress: - fromEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-security-demo k8s:app: frontend toPorts: - ports: - port: '8080' protocol: TCP rules: http: - method: GET path: / ``` ```bash kubectl apply -f backend-http.yaml # After the endpoint has realized the policy: kubectl -n cilium-security-demo exec frontend -- \ curl --fail --silent --show-error --max-time 5 http://backend:8080/ # Display the HTTP response code; do not use --fail here. kubectl -n cilium-security-demo exec frontend -- \ curl --silent --show-error --max-time 5 --output /dev/null \ --write-out '%{http_code}\n' --request POST http://backend:8080/ # A separate client is not in the allowed identity selector. kubectl -n cilium-security-demo exec outsider -- \ curl --silent --show-error --max-time 5 http://backend:8080/ hubble observe --namespace cilium-security-demo --verdict DROPPED --last 100 ``` 정책이 실제 적용된 뒤에는 frontend의 `GET /`가 성공하고 `POST /`는 프록시의 HTTP 403을 받아야 합니다. outsider의 새 연결은 L3/L4에서 거부되어야 합니다. 요청 시각, 엔드포인트와 Hubble 이벤트를 대조합니다. DNS 오류, 컨테이너 부재나 무관한 HTTP 오류는 거부 시험의 성공이 아닙니다. Hubble UI에서 생성된 의존성 연결과 드롭을 확인합니다. 데이터베이스와 외부 API가 필요한 실제 백엔드에는 다음 **선택적 의존성 정책**을 참고할 수 있습니다. 실습에서는 적용하지 않으며 `database`와 `api.example.com`은 실제 의존성으로 바꿔야 합니다. DNS 엔드포인트부터 확인합니다. ```yaml # backend-dependencies.yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: backend-dependencies namespace: cilium-security-demo spec: endpointSelector: matchLabels: app: backend egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s:k8s-app: kube-dns toPorts: - ports: - port: '53' protocol: UDP - port: '53' protocol: TCP rules: dns: - matchPattern: '*' - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-security-demo k8s:app: database toPorts: - ports: - port: '3306' protocol: TCP - toFQDNs: - matchName: api.example.com toPorts: - ports: - port: '443' protocol: TCP ``` TCP/UDP DNS를 허용하고 DNS 프록시 규칙으로 응답을 관측해 `toFQDNs`에 사용합니다. DNS 질의 허용과 이후 해석된 IP로의 연결 허용은 별개입니다. 이 정책의 DNS `*`는 모든 질의 이름을 허용하며 도메인 차단 목록이 아닙니다. ### 3. 기본 거부를 별도로 검증 `deny-except-dns.yaml`로 저장합니다. 명시적 `policyTypes`가 양방향 격리를 활성화하고 `ingress: []`에는 **ingress 허용 규칙이 없습니다**. 유일한 egress 예외는 선택한 DNS Pod로의 DNS 트래픽입니다. ```yaml # deny-except-dns.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-except-dns namespace: cilium-deny-demo spec: podSelector: {} policyTypes: - Ingress - Egress ingress: [] egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 - protocol: TCP port: 53 ``` ```bash kubectl apply -f deny-except-dns.yaml kubectl -n cilium-deny-demo exec frontend -- \ curl --silent --show-error --max-time 5 http://backend:8080/ hubble observe --namespace cilium-deny-demo --verdict DROPPED \ --drop-reason-desc POLICY_DENIED --last 100 ``` 빈 ingress 목록을 `ingress: [{}]`로 바꾸지 않습니다. 후자는 모든 ingress를 허용합니다. 표준 NetworkPolicy의 허용은 합산되므로 다른 정책이 트래픽을 열 수 있습니다. Cilium 거부 규칙이나 클러스터 정책은 추가 제한을 줄 수 있습니다. 이 네임스페이스 실습은 hostNetwork 트래픽이나 모든 호스트 출발 경로의 격리를 보장하지 않습니다. ### 4. JSON 확인과 로컬 요약 다음을 `flow-summary.jq`로 저장합니다. `--output jsonpb`는 `.flow`가 포함된 protobuf 응답 구조를 사용하므로 CLI의 레거시 `json` 호환 설정에 의존하지 않습니다. ```text [.[] | select(.flow != null) | .flow] as $flows | { flow_records: ($flows | length), other_records: (length - ($flows | length)), policy_denied_records: ( [$flows[] | select(.verdict == "DROPPED" and .drop_reason_desc == "POLICY_DENIED")] | length ), dropped_by_reason: ( [$flows[] | select(.verdict == "DROPPED")] | group_by(.drop_reason_desc // "UNKNOWN") | map({reason: (.[0].drop_reason_desc // "UNKNOWN"), records: length}) ) } ``` ```bash set -eu hubble observe --namespace cilium-deny-demo --last 100 --output jsonpb > flows.jsonl jq --slurp --from-file flow-summary.jq flows.jsonl ``` 결과는 **이 유한 표본의 흐름 레코드 수**이며 고유 공격, 연결 또는 전체 클러스터 패킷 수가 아닙니다. 흐름 이외 레코드 수도 따로 세므로 손실·상태 정보를 확인합니다. 실시간 로컬 필터는 다음과 같습니다. ```bash hubble observe --namespace cilium-deny-demo --follow --output jsonpb | jq --unbuffered -c 'select(.flow.verdict == "DROPPED" and .flow.drop_reason_desc == "POLICY_DENIED")' ``` 이 파이프라인은 로컬에 출력합니다. 알림에는 별도로 구성한 연동, 자격 증명, 재시도·중복 제거 정책과 스트림 장애 처리가 필요합니다. ### 5. 테스트 네임스페이스 정리 네임스페이스 이름을 확인한 뒤 이 실습의 워크로드와 정책만 제거합니다. 소유 레이블 조회 실패나 불일치 시 검사가 중단됩니다. ```bash set -eu for ns in cilium-security-demo cilium-deny-demo; do LAB_OWNER=$(kubectl get namespace "$ns" -o jsonpath='{.metadata.labels.audit-lab}') test "$LAB_OWNER" = security-visibility kubectl delete namespace "$ns" done ``` ## 공식 근거 - [Cilium policy enforcement](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/policy/intro.rst) - [Kubernetes NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) - [HTTP policy](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/policy/layer7.rst) - [DNS policy](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/dns.rst) - [Hubble setup](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/observability/hubble/setup.rst) - [Metrics](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/observability/metrics.rst) - [Flow exporter](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/observability/hubble/configuration/export.rst) - [WireGuard](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/network/encryption-wireguard.rst) - [IPsec](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/network/encryption-ipsec.rst) - [Mutual authentication](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/servicemesh/mutual-authentication/mutual-authentication.rst) - [ztunnel](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/network/encryption-ztunnel.rst) - [Tetragon](https://raw.githubusercontent.com/cilium/tetragon/main/README.md) - [Hubble CLI filters](https://raw.githubusercontent.com/cilium/hubble/v1.19.4/vendor/github.com/cilium/cilium/hubble/cmd/observe/flows.go) [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) ## 퀴즈 [주제 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/06-security-visibility-quiz)에서 정책, 암호화와 관측성의 범위를 확인합니다. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/07-advanced-topics ---------------------------------------- # 고급 주제 및 실제 사례 > **검토 기준**: Cilium 1.20.1, Cilium CLI 0.20.0, Hubble CLI 1.19.4. > **최종 검토**: 2026년 9월 12일. Cilium 1.20의 Kubernetes 호환 범위는 1.33–1.36이며 아래 과거 측정은 원래 환경을 유지합니다. ## 실습 환경 설정 [설치 전제 조건](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)을 따르고 스케줄링 가능한 Linux 노드가 최소 2개인 폐기 가능한 테스트 클러스터를 사용합니다. 결과에는 OS, 커널, Cilium 설정, 토폴로지, MTU, 정책, 암호화와 테스트 워크로드를 기록합니다. kubectl은 API 서버의 지원 버전 차이 범위에 맞춥니다. 아래 명령에는 Helm, jq, Cilium/Hubble CLI가 필요하며 `kubectl top`에는 메트릭 API도 필요합니다. ### 성능 테스트 환경 설정 변경 전에 기존 배포를 기록합니다. ```bash cilium version cilium status --verbose kubectl version kubectl get nodes -o wide kubectl -n kube-system get pods -l k8s-app=cilium -o wide helm get values cilium --namespace kube-system -o yaml > cilium-current-values.yaml ``` 현재 CLI가 서로 맞는 테스트 워크로드를 생성할 수 있습니다. 네트워크 부하 발생과 테스트 리소스 생성이 허용되는 환경에서만 실행합니다. ```bash cilium connectivity perf --test-namespace cilium-advanced-perf \ --namespace-labels docs-audit-lab=cilium-advanced-07 \ --duration 10s --samples 2 --crr --udp \ --host-net=false --pod-net=true --same-node=true --other-node=true \ --report-dir ./cilium-advanced-perf-results ``` 기본 동시성 1에서는 인자에 접미사가 없어도 **`cilium-advanced-perf-1`** 네임스페이스를 사용합니다. 테스트 전에 해당 이름이 사용 중이지 않은지 확인합니다. `--duration`은 전체 실행이 아니라 시나리오·표본마다 적용되며, 스케줄링과 준비 및 여러 경우의 조합에 시간이 추가됩니다. 읽기 전용 진단이 아니라 실제 부하 테스트입니다. 동일 노드와 다른 노드, TCP 요청·응답과 UDP를 구분하고 실제 CPU·메모리·패킷 손실을 비교합니다. 계획한 변경 하나씩 반복합니다. 성공한 실행이 애플리케이션 SLO, 프로덕션 용량 한계나 모든 정책 경로를 증명하지는 않습니다. 문서 감사에서는 클러스터 생성이나 부하 실행 없이 명령 계약을 검증했습니다. ## 성능 튜닝 및 문제 해결 ### 성능 튜닝 아키텍처 ![Cilium 성능 조사 영역인 커널 동작, eBPF 맵, 리소스 할당과 선택한 네트워킹 경로를 구분한 그림.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-07-advanced-topics-0.png) [인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-07-advanced-topics-0.html) 그림은 조사 영역을 분류하며 모든 설정을 늘리거나 보안 제어를 끄라고 권장하지 않습니다. ### 성능 튜닝 영역 | 영역 | 측정 대상과 설정의 의미 | | --- | --- | | 소켓 대기열 | `net.core.somaxconn`은 소켓 listen backlog 한계이며 `net.ipv4.tcp_max_syn_backlog`는 리스너별 SYN_RECV 대기 요청과 관련됩니다. 서버의 accept 동작과 해당 네트워크 네임스페이스를 확인합니다. | | 이웃 항목 | `net.ipv4.neigh.default.gc_thresh1`, `gc_thresh2`, `gc_thresh3`는 서로 다른 가비지 컬렉션 임계값입니다. 단일 `gc_thresh` 설정은 없습니다. | | 연결 추적 | Netfilter의 `nf_conntrack_max`와 Cilium BPF CT 맵은 별개입니다. 전자를 늘려도 후자의 크기가 바뀌지 않습니다. | | BPF 맵 | 맵 압력, 삽입 실패, 항목 변동과 메모리를 관측합니다. CT, NAT, LB, 엔드포인트별 정책 맵은 범위와 크기 계산 방식이 다릅니다. | | CPU와 메모리 | 에이전트, operator, Envoy, Hubble을 따로 측정합니다. requests는 스케줄링에 영향을 주고 CPU limits는 제한, memory limits는 OOM 종료를 일으킬 수 있습니다. 모든 한계를 올리는 것은 진단이 아닙니다. | | 네트워크 경로 | native/tunnel 라우팅, MTU, masquerading, 암호화와 서비스 전달을 식별합니다. XDP 가속은 지원 경로·드라이버에 적용되며 kube-proxy 대체의 필수 조건이 아닙니다. | 성능 비교에서 보안·라우팅 요구 사항을 유지합니다. 필수 암호화나 정책을 제거해 빨라진 결과는 동등한 구성의 비교가 아닙니다. ### 맵 크기 조정 다음은 압력을 측정한 뒤 검토할 **변경 후보**입니다. 완전한 버전별 릴리스 값에 병합하며, 작은 조각으로 전체 Cilium ConfigMap을 교체하지 않습니다. ```yaml # map-sizing-values.yaml bpf: mapDynamicSizeRatio: 0.005 ``` `0.005`는 노드 메모리의 명목상 **0.5%**를 동적 크기 계산에 사용한다는 뜻입니다. 5%도 아니고 모든 Cilium 메모리의 하드 한계도 아닙니다. 예를 들어 32 GiB × 0.005는 맵 한계, 반올림과 다른 할당을 반영하기 전 163.84 MiB입니다. CT, NAT, 이웃과 소켓 역방향 NAT 등 큰 맵에 적용되며 다른 맵과 사용자 공간 메모리는 별도입니다. 명시적 `bpf.ctTcpMax`, `bpf.ctAnyMax`, `bpf.natMax` 값은 해당 맵의 동적 크기 계산을 대체합니다. NAT와 CT 용량의 관계를 유지하고 시작 시 결정된 실제 크기를 확인합니다. 맵 증가에는 메모리가 더 필요하며 맵 교체는 기존 연결을 방해할 수 있습니다. 분산 LRU(`bpf.distributedLRU.enabled`)는 CPU별 풀로 경합을 줄이는 대신 메모리·퇴출 동작이 달라집니다. 동적 크기 계산이 필요하며 활성화 시 맵을 다시 생성합니다. 공식 고성능 프로필은 모든 환경에 실시간으로 켜는 스위치가 아닙니다. 준비한 새 노드나 문서화된 마이그레이션 절차로 데이터 경로 변경을 도입합니다. 이전의 `proxy-max-memory-percentage`, `proxy-max-threads`, `enable-xdp`, `tunnel: disabled`, `kube-proxy-replacement: strict`는 현재 구성 예제로 사용할 수 없습니다. `envoy.resources`, `routingMode`, 불리언 `kubeProxyReplacement`, `loadBalancer.acceleration` 등 지원되는 차트 설정을 전제 조건과 함께 사용합니다. ### Hubble 비용과 이벤트 손실 큰 이벤트 큐는 순간적인 유입을 흡수할 수 있지만 메모리를 사용하며 지속적인 처리 능력 부족을 해결하거나 CPU 사용량을 줄이지는 않습니다. ```yaml # hubble-queue-values.yaml hubble: eventQueueSize: 32768 ``` 반복 추적 이벤트가 처리량의 대부분이라면 더 긴 집계 간격을 별도 변경으로 평가합니다. ```yaml # hubble-aggregation-values.yaml bpf: monitorAggregation: medium monitorInterval: 10s ``` 실제 차트 필드는 `bpf.events.monitorInterval`이 아니라 **`bpf.monitorInterval`**입니다. 배포 전에 렌더링된 `monitor-aggregation-interval`을 확인합니다. 집계 증가, 이벤트 속도 제한과 이벤트 유형 비활성화는 monitor, Hubble 메트릭과 내보내기의 관측 데이터를 줄입니다. Hubble 이벤트 손실 자체가 애플리케이션 패킷 드롭을 의미하지는 않으므로 관측 손실과 데이터 경로 드롭을 모두 확인합니다. ### 고급 데이터 경로 전제 조건 | 기능 | 확인할 전제 조건과 제약 | | --- | --- | | netkit | 이 검토 버전에서 Beta이며 Linux 6.8 이상과 BPF 호스트 라우팅이 필요합니다. 에이전트 재시작만으로 기존 veth Pod의 장치를 바꿀 수 없습니다. | | BIG TCP | 주소군별 커널·NIC 요구 사항이 있습니다. 통합 튜닝 프로필에는 Linux 6.8 이상과 지원 NIC가 필요하며 단순한 MTU 증가가 아닙니다. | | BPF 호스트 라우팅 | 호환되는 kube-proxy 대체와 BPF masquerading이 필요합니다. 호스트 netfilter 훅을 우회하므로 Istio 등 해당 훅에 의존하는 연동을 확인합니다. | | XDP 서비스 가속 | native XDP 지원 장치와 지원되는 외부 서비스 전달 경로가 필요합니다. 드라이버·플랫폼 지침을 따르고 실행 상태를 확인합니다. | | Bandwidth Manager | Pod별 egress는 EDT, ingress는 eBPF 토큰 버킷을 사용합니다. 대역폭 annotation의 `10M`은 10 MB/s가 아니라 10 Mbit/s입니다. | | Pod용 BBR | Bandwidth Manager, Linux 5.18 이상과 BPF 호스트 라우팅이 필요하며 새 Pod부터 적용됩니다. 호스트 전용 BBR은 별도 옵션입니다. | 대역폭 집행에는 egress L7 Cilium 정책과 kind 같은 중첩 네트워크 네임스페이스 관련 제약이 있습니다. 일반 Cilium 설치 요구 사항과 구분합니다. 하나의 튜닝 프로필이 모든 메시·클라우드·커널 조합의 동작을 증명하지는 않습니다. ### 대상 노드를 지정한 문제 해결 명령 클러스터 작업에는 독립 `cilium` CLI를, 로컬 엔드포인트·정책·맵에는 **관련 에이전트 내부의 `cilium-dbg`**를 사용합니다. 임의의 첫 에이전트 대신 조사할 노드를 선택합니다. ```bash set -euo pipefail : "${NODE_NAME:?Set NODE_NAME to the node being investigated}" CILIUM_POD=$(kubectl -n kube-system get pods -l k8s-app=cilium \ --field-selector "spec.nodeName=$NODE_NAME,status.phase=Running" -o json | jq -er 'if (.items | length) == 1 then .items[0].metadata.name else error("expected exactly one running Cilium Pod on the selected node") end') kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg status --verbose kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg endpoint list kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg policy get kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg policy selectors kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg map list kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- cilium-dbg bpf metrics list kubectl -n kube-system logs "$CILIUM_POD" -c cilium-agent --since=10m --tail=200 ``` 엔드포인트 ID는 해당 에이전트에 로컬입니다. 그 에이전트의 목록에서 ID를 선택합니다. ```bash : "${CILIUM_POD:?Select the owning Cilium Pod first}" : "${ENDPOINT_ID:?Read the endpoint ID from the selected agent endpoint list}" kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- \ cilium-dbg endpoint get "$ENDPOINT_ID" # Stream local BPF drop events; stop with Ctrl-C. kubectl -n kube-system exec "$CILIUM_POD" -c cilium-agent -- \ cilium-dbg monitor --type drop ``` `cilium-dbg map list`는 에이전트 맵 관리자가 아는 열린 맵을 나열하며 모든 커널 BPF 맵의 목록이 아닙니다. `cilium-dbg monitor`는 발생한 BPF 이벤트와 선택적 캡처 추적을 표시하며 모든 패킷의 무손실 tcpdump가 아닙니다. 큰 CT 맵 전체 출력은 비용이 들 수 있으므로 `cilium-dbg bpf ct list global` 전에 메트릭과 영향받은 노드를 확인합니다. Hubble에는 [보안 및 가시성](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/06-security-visibility.md)의 로컬 Relay 연결부터 준비합니다. ```bash hubble status hubble observe --protocol tcp --verdict DROPPED --since 1h hubble observe --protocol dns --from-label k8s:app=frontend --last 100 hubble observe --http-status '5+' --from-namespace production --last 100 ``` `--type`은 DNS 레코드 A가 아니라 이벤트 유형을 선택합니다. 질의 유형을 구분하려면 DNS 흐름 필드를 확인합니다. HTTP 상태 필터는 `5xx`가 아니라 `5+`입니다. `--since 1h`도 보존 이력과 이벤트 가용성에 제한되며 이미 덮어쓴 한 시간의 이벤트를 복구하지 못합니다. ### 일반적인 문제 해결 시나리오 | 증상 | 수집할 근거 | 다음 판단 | | --- | --- | --- | | CT/NAT 압력 | 맵 압력, 삽입·드롭 사유, 연결 변동, 실제 구성 크기 | 크기 변경 전에 연결 변동·시간 제한과 메모리 여유를 조사합니다. | | OOM 또는 CPU 포화 | 종료 사유, CPU·메모리 이력, throttling, 프록시·Hubble 부하 | 해당 구성 요소를 식별하고 자원이나 워크로드를 조정합니다. | | 예상 밖 정책 결과 | 네임스페이스·레이블, 엔드포인트 정책 revision, 프록시 오류, Hubble 판정 | 방향, 허용 합산과 거부 우선 적용을 확인합니다. 일반 허용은 우선순위 순서의 방화벽 목록이 아닙니다. | | 노드 간 실패 | DNS, 노드·Pod 라우팅, MTU, 터널·암호화 포트, 플랫폼 방화벽 | 양방향 경로를 검증합니다. BGP 세션만으로 실제 데이터 경로가 검증되지는 않습니다. | | 업그레이드 회귀 | 이전·현재 값, 버전 노트, 전체 구성 요소 버전, 프록시 재연결 | 준비한 지원 롤백 경로를 사용하고 기능 호환성을 조사합니다. | ## 대규모 배포 전략 용량 계획에는 노드·Pod 밀도, Service와 백엔드, ID, 정책 확장, API watch 트래픽, IPAM 할당과 흐름량을 포함합니다. 정책 객체 수만으로 엔드포인트별 맵 비용을 알 수는 없습니다. ### 대규모 배포 아키텍처 ```text 관리 / GitOps / 공유 모니터링 | 설정과 수집된 텔레메트리 +-------------------------+ v v 워크로드 클러스터 A 워크로드 클러스터 B - Cilium Operator - Cilium Operator - 노드별 에이전트 - 노드별 에이전트 - 로컬 Hubble 서버 - 로컬 Hubble 서버 - Relay/내보내기 구성 - Relay/내보내기 구성 | | +--- 선택적 ClusterMesh 메타데이터/데이터 경로 ``` 중앙 관리 클러스터가 각 관리 대상 클러스터의 operator를 대체하지 않습니다. 클러스터마다 operator와 에이전트를 산정하며 차트의 operator 복제본과 안티어피니티를 만족할 노드가 필요합니다. 구성된 수집기로 메트릭·로그를 모읍니다. ClusterMesh에는 주소, ID, 신뢰와 연결성 설계가 필요하며 모든 정책 리소스를 자동 복제하지는 않습니다. `ciliumEndpointSlice.enabled`는 Kubernetes EndpointSlice와 다른 선택적 Cilium 기능입니다. 이전 ConfigMap의 `enable-endpoint-slice` 플래그가 아닙니다. 활성화 전에 버전·기능 호환성을 평가합니다. 현재 Egress Gateway는 CiliumEndpointSlice나 ClusterMesh와 함께 사용할 수 없습니다. Egress Gateway는 선택된 트래픽을 예측 가능한 게이트웨이 주소로 **SNAT**하며 원래 Pod 출발지 주소를 보존하지 않습니다. AWS 같은 플랫폼별 조건을 포함해 게이트웨이 인터페이스·IP와 라우팅이 준비되어 있어야 합니다. BPF masquerading, kube-proxy 대체와 CRD ID 할당이 필요합니다. 새 Pod는 egress 정책 적용 전 잠시 트래픽을 보낼 수 있으므로 즉시 적용되는 fail-closed 출발지 IP 보장으로 취급하지 않습니다. ### 롤아웃과 복구 원하는 설정, 정책, 주소 풀 정의와 필요한 신뢰·키 자료에 적절한 버전 관리와 백업 제어를 적용합니다. 복구를 연습하며 ConfigMap 백업만으로 IPAM이나 암호화 복구 계획이 완성되지는 않습니다. 마이너 업그레이드는 현재 마이너의 최신 패치, 필수 preflight를 거쳐 **한 마이너씩** 수행합니다. 업그레이드 지침에 따라 최초 `upgradeCompatibility`를 유지하고 이름 변경·제거된 값을 옮깁니다. 마이너 버전 간에 `--reuse-values`를 사용하지 않습니다. 에이전트, operator와 다른 Cilium 구성 요소는 같은 버전으로 수렴해야 합니다. 사용자 공간 프록시를 통과하는 트래픽은 업그레이드 중 재연결될 수 있고 버퍼의 관측 이벤트도 손실될 수 있습니다. 새 기능·리소스는 롤백 전에 제거나 이전이 필요할 수 있습니다. 일반 애플리케이션의 블루/그린 배포나 Helm rollback은 가역적 무중단 CNI 마이그레이션을 보장하지 않습니다. ## 실제 사용 사례 연구 ### 문서화된 과거 확장성 실험 공식 확장성 보고서는 Google Cloud의 **워커 1,000개**, 컨트롤러 3개와 커널 **5.4.0-1009-gcp**를 설명합니다. 설정 부분에 Cilium 버전이 명시되지 않았으므로 이를 Cilium 1.20.1 벤치마크로 다시 표시하면 안 됩니다. 해당 워크로드의 자원 소비와 수렴을 다루는 보고서이며 현재 지원 행렬이나 용량 보장이 아닙니다. 상태 점검 변경과 높은 롤아웃 동시성은 그 실험의 선택입니다. 인용할 때 실제 테스트 조건을 유지하고 운영 구성은 따로 검증합니다. ### 설계 시나리오 1: 대규모 전자 상거래 많은 서비스와 요청량에 대해 eBPF 서비스 전달, ID/L7 정책, Hubble과 선택적 ClusterMesh를 평가합니다. 같은 토폴로지와 보호 조건에서 p95/p99 지연, 처리량, 오류, 요청당 CPU와 정책 수렴을 측정합니다. 여기에는 보편적 개선율을 뒷받침하는 실명 구현 사례나 재현 가능한 측정이 없습니다. ### 설계 시나리오 2: 금융 서비스 최소 권한 정책, 범위에 맞는 전송·애플리케이션 암호화, 통제된 흐름 내보내기를 애플리케이션/API 감사 기록과 결합합니다. 키 교체, 이벤트 손실, 보존과 클러스터 간 신뢰를 시험합니다. Hubble 흐름만으로 완전한 규제 감사 근거가 되거나 감사 기간 단축이 보장되지는 않습니다. ### 설계 시나리오 3: 통신과 엣지 현실적인 트래픽에서 NIC·드라이버, CPU 스케줄링, 서비스 전달 경로, 패킷 크기, 손실과 지연을 평가합니다. XDP는 조건에 맞는 전달 경로에 도움이 될 수 있습니다. 그 자체가 5G 사용자 평면 기능을 구현하거나 임의 하드웨어의 고정 초당 패킷 수를 증명하지는 않습니다. 원격 사이트에는 underlay와 명시적 장애·복구 시험이 필요합니다. ## 미래 로드맵 및 발전 방향 커뮤니티 로드맵은 **일정을 약속하지 않는다**고 명시합니다. 희망하는 연동 목록을 출시 약속으로 취급하지 말고 특정 기능의 릴리스 노트, 승인된 설계와 이슈를 추적합니다. | 영역 | 조사할 질문 | | --- | --- | | eBPF와 커널 | 제안한 경로에는 어떤 커널 기능, 백포트, NIC와 아키텍처가 필요한가요? CO-RE가 없는 커널 기능을 제공하지는 않습니다. | | 네트워킹과 IPv6 | 선택한 릴리스에서 어떤 IPAM·라우팅·정책·외부 연동 조합을 지원하나요? | | 보안과 관측성 | Cilium 네트워크 정책, Beta 워크로드 인증·암호화, Tetragon 런타임 집행, 외부 탐지·저장 시스템 중 어디에 해당하나요? | | 클라우드·메시·서버리스 | 관리형 플랫폼이 해당 CNI·호스트 훅을 허용하나요? 메시의 트래픽 가로채기와 인증이 유지되나요? | | 엣지·IoT·5G·AI/ML | 추가 장치·전송·런타임·가속기 연동이 무엇인가요? Kubernetes CNI만으로 모든 애플리케이션별 기능이 성립하지는 않습니다. | 프로젝트 이슈, 설계 제안, 문서와 커뮤니티 논의로 참여합니다. 상용 지원과 관리형 배포판은 별도의 기능·지원 계약을 가집니다. ## 현재 BGP 구성 이전의 `CiliumBGPPeeringPolicy` 예제는 이 검토 버전에서 사용할 수 없습니다. 현재 구성은 클러스터·노드 선택, 피어 설정과 광고할 접두사를 **`cilium.io/v2`** 리소스 3개로 구분합니다. 이는 격리된 라우팅 실습용 구성 모델이며 완전한 클라우드 라우터 설정이 아닙니다. `PodCIDR` 광고를 위한 cluster-pool 또는 Kubernetes host-scope IPAM, 접근 가능한 피어 라우터와 그 설정, 의도적으로 `cilium-bgp=lab` 레이블을 지정한 노드를 가정합니다. `192.0.2.1`은 문서용 주소이므로 `/32` 없는 실제 피어 IP로 바꿉니다. Multi-pool IPAM은 다른 광고 유형과 풀 선택을 사용합니다. ```yaml # bgp-values.yaml bgpControlPlane: enabled: true ``` ```yaml # bgp-lab.yaml apiVersion: cilium.io/v2 kind: CiliumBGPClusterConfig metadata: name: lab-bgp spec: nodeSelector: matchLabels: cilium-bgp: lab bgpInstances: - name: asn-64512 localASN: 64512 peers: - name: router-64513 peerASN: 64513 peerAddress: 192.0.2.1 peerConfigRef: name: lab-peer --- apiVersion: cilium.io/v2 kind: CiliumBGPPeerConfig metadata: name: lab-peer spec: timers: connectRetryTimeSeconds: 120 holdTimeSeconds: 90 keepAliveTimeSeconds: 30 gracefulRestart: enabled: true restartTimeSeconds: 120 families: - afi: ipv4 safi: unicast advertisements: matchLabels: advertise: lab --- apiVersion: cilium.io/v2 kind: CiliumBGPAdvertisement metadata: name: lab-pod-cidrs labels: advertise: lab spec: advertisements: - advertisementType: PodCIDR ``` 피어의 광고 선택자는 `advertise: lab`과 일치하며 `peerConfigRef`는 `lab-peer`로 연결됩니다. Graceful Restart에는 호환되는 피어 동작과 적절한 타이머가 필요하며 실패한 데이터 경로나 애플리케이션 가용성을 보장하지 못합니다. BGP는 연결성을 광고하지만 **로컬 데이터 경로 라우트를 설치하지 않으며** DNS 레코드도 생성하지 않습니다. 라우팅 실습에서 적절한 구성을 준비·적용한 뒤 Cilium 상태와 외부 라우터가 받은 경로를 모두 확인합니다. ```bash kubectl get ciliumbgpclusterconfigs,ciliumbgppeerconfigs,ciliumbgpadvertisements cilium bgp peers cilium bgp routes advertised ipv4 unicast ``` 실제 왕복 트래픽은 별도로 시험합니다. 오래전부터 존재한 HTTP 정책이나 임의 개선율을 과거 릴리스의 새 기능으로 설명하지 않습니다. Cilium 1.18 등 이전 마이너에서 옮길 때는 정확한 릴리스 노트를 확인합니다. ## 다음 단계 [IPAM과 정책](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/04-ipam-policy.md), [L2–L7 네트워킹](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/05-l2-l7-networking.md), [보안·가시성](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/06-security-visibility.md)에서 개별 경로를 검증합니다. 결과마다 벤치마크 조건과 한계를 기록하고 단계적 롤아웃과 복구를 연습합니다. 성능 테스트가 만든 정확한 네임스페이스를 확인한 뒤 리소스를 제거합니다. ```bash set -eu PERF_NS=cilium-advanced-perf-1 LAB_OWNER=$(kubectl get namespace "$PERF_NS" -o jsonpath='{.metadata.labels.docs-audit-lab}') test "$LAB_OWNER" = cilium-advanced-07 kubectl delete namespace "$PERF_NS" ``` ## 공식 근거 - [Tuning guide](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/operations/performance/tuning.rst) - [Chart values](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/install/kubernetes/cilium/values.yaml) - [Chart ConfigMap template](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/install/kubernetes/cilium/templates/cilium-configmap.yaml) - [Map sizing](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/ebpf/maps.rst) - [Map sizing implementation](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/pkg/option/config.go) - [Upgrade guide](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/operations/upgrade.rst) - [Upgrade limitations](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/operations/upgrade-warning.rst) - [BGP configuration](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/bgp-control-plane/bgp-control-plane-configuration.rst) - [BGP operation](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/bgp-control-plane/bgp-control-plane-operation.rst) - [Bandwidth Manager](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/kubernetes/bandwidth-manager.rst) - [Egress Gateway](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/egress-gateway/egress-gateway.rst) - [Historical scalability report](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/operations/performance/scalability/report.rst) - [Community roadmap](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/community/roadmap.rst) - [Linux IP sysctls](https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html) [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md) ## 퀴즈 [주제 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/07-advanced-topics-quiz)에서 운영 범위와 진단 명령을 확인합니다. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/networking-concepts ---------------------------------------- # 네트워킹 개념 심층 분석 > **검토 기준**: Cilium 1.20.1. > **최종 검토**: 2026년 9월 12일. 이 문서는 Cilium을 이해하는 데 필요한 핵심 네트워킹 개념에 대한 심층적인 설명을 제공합니다. 컨테이너 네트워킹, 오버레이, NAT, 라우팅, DNS, 로드 밸런싱과 정책을 다룹니다. 예제는 준비된 테스트 환경의 개념·부분 Helm/API 구성이며 완전한 설치나 마이그레이션 절차가 아닙니다. 관리형 플랫폼마다 허용하는 CNI 기능이 다르므로 [Cilium 개요](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/README.md)의 플랫폼·버전 전제 조건을 확인합니다. ## 학습 목표 이 문서를 통해 다음을 이해할 수 있습니다: - OSI 모델과 TCP/IP 스택의 기본 구조와 각 계층의 역할 - 컨테이너 네트워킹의 기본 원리와 구현 방식 - 오버레이 네트워크와 언더레이 네트워크의 차이점 - NAT, 라우팅, DNS 등 핵심 네트워킹 개념이 Cilium에서 어떻게 활용되는지 ## 목차 1. [OSI 모델 및 TCP/IP 스택](#osi-모델-및-tcp-ip-스택) 2. [컨테이너 네트워킹 기초](#컨테이너-네트워킹-기초) 3. [오버레이 네트워크](#오버레이-네트워크) 4. [네트워크 주소 변환(NAT)](#네트워크-주소-변환-nat) 5. [라우팅 프로토콜](#라우팅-프로토콜) 6. [DNS 및 서비스 디스커버리](#dns-및-서비스-디스커버리) 7. [로드 밸런싱 개념](#로드-밸런싱-개념) 8. [네트워크 보안 기초](#네트워크-보안-기초) ## OSI 모델 및 TCP/IP 스택 > **핵심 개념**: OSI 모델은 네트워크 통신을 7개의 추상 계층으로 분류하여 복잡한 네트워킹 프로세스를 이해하기 쉽게 분해합니다. OSI(Open Systems Interconnection) 모델은 네트워크 통신을 7개의 추상 계층으로 분류한 개념적 프레임워크입니다. 각 계층은 특정 네트워킹 기능을 담당하며, 이를 통해 복잡한 네트워킹 프로세스를 이해하기 쉽게 분해할 수 있습니다. ### OSI 모델과 TCP/IP 모델 비교 ![OSI 7계층 모델의 각 계층이 TCP/IP 4계층 모델로 어떻게 그룹핑되어 매핑되는지, 그리고 각 OSI 계층에서 사용하는 대표 프로토콜을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-0.html) 계층 매핑은 학습용 근사이며 프로토콜 구현 명세가 아닙니다. 그림의 SSL은 과거 명칭이며 현재 시스템에는 지원되는 TLS 버전을 사용합니다. ### OSI 7계층 모델 1. **물리 계층(Physical Layer)** - 비트 스트림을 전기 신호, 광 신호 또는 무선 신호로 변환 - 케이블, 트랜시버와 물리 신호 등을 포함하며 스위치는 상위 계층 기능도 구현 - 데이터 단위: 비트(Bit) 2. **데이터 링크 계층(Data Link Layer)** - 물리적 네트워크 상의 노드 간 데이터 전송 담당 - MAC(Media Access Control) 주소를 사용한 장치 식별 - 오류 감지와 링크 프로토콜이 제공하는 복구 기능; Ethernet의 오류 감지 자체가 손상된 프레임을 수정하지는 않음 - 데이터 단위: 프레임(Frame) - 이더넷, Wi-Fi 프로토콜이 이 계층에서 작동 3. **네트워크 계층(Network Layer)** - 서로 다른 네트워크 간의 패킷 라우팅 담당 - 논리적 주소 지정(IP 주소) - 경로 결정 및 패킷 전달 - 데이터 단위: 패킷(Packet) - IP(Internet Protocol)가 이 계층의 핵심 프로토콜 4. **전송 계층(Transport Layer)** - 종단 간(end-to-end) 통신 제어 - 데이터 분할 및 재조립 - TCP는 흐름 제어와 재전송을 제공하지만 UDP 자체는 이를 보장하지 않음 - 데이터 단위: TCP 세그먼트 또는 UDP 데이터그램 - TCP(Transmission Control Protocol)와 UDP(User Datagram Protocol)가 이 계층의 주요 프로토콜 5. **세션 계층(Session Layer)** - 통신 세션 설정, 유지 및 종료 - 동기화 및 대화 제어 - 체크포인트 설정 및 복구 - 세션 관리를 이 계층에서 설명할 수 있지만 실제 RPC 구현이 하나의 OSI 계층에만 대응하는 것은 아님 6. **표현 계층(Presentation Layer)** - 데이터 형식 변환 및 암호화 - 문자 인코딩, 데이터 압축, 암호화/복호화 - 인코딩·압축이 이 책임을 설명하며 TLS는 Internet 프로토콜이지 문자 그대로 OSI 표현 계층을 구현한 것은 아님 7. **응용 계층(Application Layer)** - 애플리케이션이 사용하는 네트워크 서비스를 제공하며 그래픽 사용자 인터페이스를 뜻하지는 않음 - 이메일, 파일 전송, 웹 브라우징 등의 서비스 - HTTP, FTP, SMTP, DNS가 이 계층의 예 ### Cilium과 OSI 모델의 관계 Cilium은 여러 OSI 계층에서 작동합니다: | OSI 계층 | Cilium 기능 | 예시 | |---------|------------|------| | L2 (데이터 링크) | 링크 연결성과 선택적 L2 Announcements | 구성한 Service VIP에 대한 ARP/NDP 응답 | | L3 (네트워크) | IP 라우팅, CIDR 기반 정책 | 포드 간 IP 라우팅 | | L4 (전송) | 포트 기반 필터링, 연결 추적 | 서비스 포트 접근 제어 | | L7 (응용) | 지원 HTTP/gRPC 및 DNS 프록시 규칙 | HTTP 경로 정책 또는 DNS 질의 정책 | 이 검토 버전의 L2 Announcements는 Beta이며 컨트롤러·장치 구성이 필요합니다. 일반적인 MAC 주소 보안 정책 인터페이스와 구분합니다. ### TCP/IP 스택 TCP/IP 스택은 인터넷의 기반이 되는 프로토콜 집합으로, 흔히 4계층으로 설명해 OSI와 비교하는 별도의 아키텍처이며 OSI 7계층을 직접 구현한 것은 아닙니다. 1. **네트워크 인터페이스 계층(Network Interface Layer)** - OSI 모델의 물리 계층과 데이터 링크 계층에 해당 - 물리적 네트워크 매체와의 인터페이스 담당 - 이더넷, Wi-Fi 등의 프로토콜 포함 2. **인터넷 계층(Internet Layer)** - OSI 모델의 네트워크 계층에 해당 - IP(Internet Protocol)를 사용한 패킷 라우팅 - ICMP(Internet Control Message Protocol)를 포함; ARP는 링크 경계에서 IPv4 다음 홉 주소를 해석하며 IPv6는 Neighbor Discovery를 사용 3. **전송 계층(Transport Layer)** - OSI 모델의 전송 계층과 동일 - TCP와 UDP 프로토콜 포함 - 연결 지향(TCP) 및 비연결 지향(UDP) 통신 제공 4. **응용 계층(Application Layer)** - OSI 모델의 세션, 표현, 응용 계층을 통합 - HTTP, SMTP, FTP, DNS 등의 프로토콜 포함 - 사용자 애플리케이션과 네트워크 간의 인터페이스 제공 ### Cilium과 계층별 기능 Cilium은 다양한 네트워크 계층에서 기능을 제공합니다: - **L2(데이터 링크 계층)**: 링크 연결성과 선택적 L2 서비스 광고; 일반적인 MAC 주소 NetworkPolicy API나 보편적 ARP 스푸핑 방지를 의미하지 않음 - **L3(네트워크 계층)**: IP 주소 기반 라우팅 및 필터링, IPAM - **L4(전송 계층)**: 포트 기반 필터링, 로드 밸런싱, 연결 추적 - **L7(응용 계층)**: 구성된 HTTP/gRPC 프록시 기능과 DNS 정책; 이전 Kafka L7 정책 API는 제거됨 ## 컨테이너 네트워킹 기초 컨테이너 네트워킹은 컨테이너화된 애플리케이션이 서로 통신하고 외부 세계와 통신할 수 있게 해주는 메커니즘입니다. Kubernetes와 같은 컨테이너 오케스트레이션 플랫폼에서는 다양한 네트워킹 모델과 솔루션이 사용됩니다. ### 컨테이너 네트워크 인터페이스(CNI) CNI(Container Network Interface)는 컨테이너 런타임과 네트워크 플러그인 간의 표준 인터페이스를 정의합니다. 현재 Kubernetes에서는 CRI 컨테이너 런타임이 CNI 플러그인을 호출합니다. 다양한 네트워크 구현을 연결하는 인터페이스이며 모든 플러그인이 Kubernetes NetworkPolicy를 구현해야 한다는 명세는 아닙니다. #### CNI의 주요 구성 요소: 1. **플러그인**: 네트워크 인터페이스 생성 및 구성을 담당하는 실행 파일 2. **구성 파일**: 플러그인의 동작을 정의하는 JSON 형식의 파일 3. **IPAM(IP Address Management)**: IP 주소 할당 및 관리를 담당하는 모듈 #### CNI 플러그인의 주요 책임: - 컨테이너 네트워크 네임스페이스에 인터페이스 추가/제거 - IP 주소 할당 및 해제 - 라우팅 테이블 구성 - 네트워크 구현이 별도 정책 컨트롤러·데이터 경로 집행을 제공할 수 있으나 정책은 필수 CNI 실행 연산이 아님 ### 컨테이너 네트워킹 모델 컨테이너 네트워킹에는 여러 모델이 있으며, 각각 다른 사용 사례와 요구 사항에 적합합니다. #### 1. 브리지 네트워킹 - 호스트에 가상 브리지를 생성하여 컨테이너를 연결 - 각 컨테이너는 가상 이더넷(veth) 쌍을 통해 브리지에 연결 - 동일한 호스트의 컨테이너 간 통신이 효율적 - 독립 Linux Docker의 기본 브리지 예이며 Kubernetes나 Cilium의 네트워크 모델 자체는 아님 ![veth 페어로 연결된 두 컨테이너가 docker0 가상 브리지를 통해 호스트의 물리 네트워크로 나가는 기본 브리지 네트워킹 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-1.html) 예시 주소를 사용한 Linux Docker 브리지 그림입니다. 호스트 라우팅·NAT 경로를 단순화했으며 Cilium의 기본 브리지 토폴로지를 뜻하지 않습니다. #### 2. 호스트 네트워킹 - 컨테이너가 호스트의 네트워크 네임스페이스를 직접 사용 - 별도의 네트워크 격리 없음 - 별도의 컨테이너 네트워크 네임스페이스를 사용하지 않으며 실제 성능은 워크로드와 경로에 따라 달라짐 - 포트 충돌 가능성 있음 ![두 컨테이너가 별도의 네트워크 네임스페이스 없이 호스트의 네트워크 스택(eth0, 192.168.1.10)을 그대로 공유하는 호스트 네트워킹 모드 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-2.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-2.html) 그림의 ‘격리 없음’은 네트워크 네임스페이스 공유를 뜻하며 모든 프로세스·파일 시스템 등 다른 컨테이너 격리가 제거된다는 뜻은 아닙니다. #### 3. 오버레이 네트워킹 - 여러 호스트에 걸쳐 있는 컨테이너 간의 통신 지원 - VXLAN, GENEVE 등의 캡슐화 프로토콜 사용 - 대규모 클러스터에 적합 - Cilium, Calico, Flannel 등이 이 모델 지원 ![서로 다른 호스트에 있는 두 컨테이너가 오버레이 네트워크(10.0.0.0/24)를 거쳐 각 호스트의 eth0에서 VXLAN 캡슐화·디캡슐화되어 물리 네트워크 위에서 통신하는 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-3.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-3.html) 일반 VXLAN 그림은 표준 UDP 4789와 공유 L2 서브넷을 사용합니다. Cilium 기본 VXLAN 포트는 8472이며 Pod CIDR 할당은 그림 복사가 아니라 선택한 IPAM 모드를 따라야 합니다. #### 4. 언더레이 네트워킹(직접 라우팅) - 물리적 네트워크 인프라를 직접 활용 - 캡슐화 오버헤드 없음 - 네트워크 인프라에 대한 제어가 필요 - BGP와 같은 라우팅 프로토콜과 통합 가능 ![캡슐화 없이 각 호스트의 라우팅 테이블 항목을 이용해 컨테이너 IP 대역을 직접 상대 호스트로 전달하는 라우팅 기반 네트워킹 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-4.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-4.html) 호스트 라우트를 설명하는 그림입니다. 실제 배포에는 접근 가능한 다음 홉과 Pod 주소의 올바른 왕복 라우트가 필요합니다. ### Kubernetes 네트워킹 모델 Kubernetes 모델은 **의도적 네트워크 분할을 제외하고** 직접 Pod 연결을 제공합니다. 1. Pod 네트워크에서 Pod끼리는 필수 프록시나 NAT 없이 통신할 수 있습니다. 2. 노드 에이전트는 **해당 노드의 Pod**에 접근할 수 있어야 합니다. 모든 호스트가 모든 Pod에 접근해야 한다는 보편적 요구가 아닙니다. 3. 외부 연결은 클러스터 라우팅·보안 정책을 따르며 무제한 인터넷 접근은 요구되지 않습니다. NetworkPolicy 집행에는 지원 네트워크 구현이 필요합니다. 설치한 플러그인이 집행하지 않아도 API는 존재할 수 있습니다. #### Kubernetes 네트워크 구성 요소: 1. **포드 네트워크**: 클러스터 내 모든 포드를 연결하는 네트워크 2. **서비스 네트워크**: 포드 집합에 대한 안정적인 엔드포인트 제공 3. **클러스터 DNS**: 서비스 디스커버리를 위한 DNS 서비스 4. **인그레스/이그레스**: 클러스터 외부와의 통신 관리 ### Cilium의 컨테이너 네트워킹 접근 방식 Cilium은 eBPF를 활용하여 고성능, 확장 가능한 컨테이너 네트워킹 솔루션을 제공합니다: 1. **eBPF 기반 데이터 경로**: 커널 내에서 직접 패킷 처리 2. **다양한 네트워킹 모드 지원**: 오버레이(VXLAN, Geneve)와 native 라우팅; 일반 Helm 기본값은 VXLAN 터널 모드이며 플랫폼에서 재정의할 수 있음 3. **고급 로드 밸런싱**: kube-proxy 대체 기능 4. **네트워크 정책**: L3-L7 수준의 세분화된 정책 5. **통합 IPAM**: 다양한 IP 주소 할당 전략 지원 플랫폼 재정의가 없는 일반 Helm 설치의 기본 IPAM은 cluster-pool입니다. Operator가 노드 CIDR을, 에이전트가 노드 풀의 Pod IP를 할당합니다. `ipam.mode: kubernetes`는 Node의 `spec.podCIDR`/`spec.podCIDRs`를 사용합니다. ENI 모드는 EC2 인터페이스와 VPC 주소를 사용하며 모든 EKS 컴퓨팅 모드의 보편적인 권장은 아닙니다. [IPAM과 정책](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/04-ipam-policy.md)을 참고합니다. ## 오버레이 네트워크 오버레이 네트워크는 기존 네트워크 인프라 위에 가상 네트워크 계층을 구축하는 기술입니다. 이 기술은 물리적 네트워크 토폴로지와 독립적으로 가상 네트워크 토폴로지를 생성할 수 있게 해줍니다. 컨테이너 환경에서는 여러 호스트에 걸쳐 있는 컨테이너 간의 통신을 가능하게 하는 데 널리 사용됩니다. ### 오버레이 네트워크의 작동 원리 오버레이 네트워크는 캡슐화(Encapsulation) 기술을 사용하여 작동합니다. 원본 패킷은 다른 패킷 내에 캡슐화되어 물리적 네트워크를 통해 전송됩니다. 1. **패킷 캡슐화**: 원본 패킷(내부 패킷)이 새로운 헤더와 때로는 새로운 트레일러로 감싸집니다. 2. **터널링**: 캡슐화된 패킷은 물리적 네트워크를 통해 목적지 호스트로 전송됩니다. 3. **패킷 디캡슐화**: 목적지 호스트에서 외부 헤더가 제거되고 원본 패킷이 추출됩니다. 4. **패킷 전달**: 원본 패킷은 목적지 컨테이너로 전달됩니다. ### 주요 오버레이 네트워크 프로토콜 #### VXLAN(Virtual Extensible LAN) VXLAN은 컨테이너 네트워킹에서 가장 널리 사용되는 오버레이 프로토콜 중 하나입니다. - **VXLAN 터널 엔드포인트(VTEP)**: 패킷의 캡슐화 및 디캡슐화를 담당 - **VXLAN 네트워크 식별자(VNI)**: 16,777,216개 값이 가능한 24비트 필드이며 Cilium의 지원 테넌트·엔드포인트 용량이 아님 - **UDP 캡슐화**: 표준 VXLAN은 UDP 4789를 사용하며 Cilium 기본 VXLAN 터널 포트는 UDP 8472 - **MAC-in-UDP 캡슐화**: 원본 L2 프레임을 UDP 패킷으로 캡슐화 VXLAN 패킷 구조: ![원본 이더넷/IP/TCP 패킷이 VXLAN 헤더와 외부 UDP·IP·이더넷 헤더로 감싸여 캡슐화되는 VXLAN 패킷 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-5.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-5.html) 일반 VXLAN 전송 형식입니다. UDP 4789와 24비트 VNI는 표준 설명이며 Cilium은 기본 UDP 8472와 ID용 오버레이 메타데이터를 사용합니다. 필드 폭이 클러스터 용량 보장은 아닙니다. #### GENEVE(Generic Network Virtualization Encapsulation) GENEVE는 VXLAN의 제한을 극복하기 위해 설계된 보다 유연한 오버레이 프로토콜입니다. - **확장 가능한 옵션 헤더**: 다양한 메타데이터 지원 - **프로토콜 독립적**: 다양한 가상화 기술과 함께 사용 가능 - **UDP 캡슐화**: UDP 포트 6081을 통해 전송 - **유연한 터널링**: 다양한 네트워크 가상화 요구 사항 지원 #### IPsec IPsec은 IP 패킷 수준에서 보안 서비스를 제공하는 프로토콜 스위트입니다. - **인증 및 암호화**: IPsec은 무결성·인증과 적절한 모드의 기밀성을 위한 수단을 제공 - **전송 모드 및 터널 모드**: 다양한 배포 시나리오 지원 - **보안 연결(SA)**: 통신 당사자 간의 보안 매개변수 정의 - **인터넷 키 교환(IKE)**: 일반적인 IPsec 협상 방식이며 Cilium IPsec 설정은 관리자가 제공하는 키 Secret과 문서화된 교체 절차를 사용 ### 오버레이 네트워크의 장단점 #### 장점: - **유연성**: 물리적 네트워크 토폴로지와 독립적으로 가상 네트워크 구성 가능 - **확장성**: 대규모 네트워크 세그먼트 및 다수의 엔드포인트 지원 - **격리**: 올바르게 구성한 논리 세그먼트가 트래픽을 구분할 수 있지만 캡슐화 자체가 인증·암호화·완전한 정책 경계는 아님 - **호환성**: 기존 네트워크 인프라와 함께 작동 가능 #### 단점: - **오버헤드**: 캡슐화로 인한 패킷 크기 증가 및 처리 오버헤드 - **MTU 고려 사항**: 캡슐화로 인한 최대 전송 단위(MTU) 감소 - **복잡성**: 문제 해결 및 디버깅이 더 복잡해질 수 있음 - **지연 시간**: 캡슐화는 처리 작업을 추가하며 선택한 구현·오프로딩의 실제 영향을 측정해야 함 ### Cilium에서의 오버레이 네트워크 Cilium은 VXLAN 및 Geneve와 같은 오버레이 프로토콜을 지원하며, eBPF를 활용하여 효율적인 패킷 처리를 제공합니다. - **eBPF 기반 VXLAN 처리**: 커널 내에서 직접 패킷 캡슐화 및 디캡슐화 - **효율적인 라우팅**: 최적화된 경로를 통한 패킷 전달 - **암호화 옵션**: IPsec 또는 WireGuard를 통한 암호화된 오버레이 - **모드 선택**: 지원되는 라우팅 모드를 선택; 터널 모드와 자동 직접 노드 라우트를 함께 켜면 거부되며 fallback 수단이 아님 #### Cilium VXLAN 구성 예제: **새로 준비한 IPv4 테스트 설치**의 Helm 값입니다. 겹치지 않는 Pod CIDR을 선택하며 기존 IPAM의 실시간 이전이나 ConfigMap 교체 예제가 아닙니다. ```yaml # vxlan-values.yaml routingMode: tunnel tunnelProtocol: vxlan tunnelPort: 8472 autoDirectNodeRoutes: false ipv4: enabled: true ipv6: enabled: false ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: - 10.244.0.0/16 clusterPoolIPv4MaskSize: 24 ``` ## 네트워크 주소 변환(NAT) 네트워크 주소 변환(Network Address Translation, NAT)은 IP 패킷의 소스 또는 목적지 주소를 수정하는 프로세스입니다. NAT는 주로 사설 네트워크의 장치가 공용 인터넷과 통신할 수 있도록 하거나, 네트워크 주소 공간이 겹치는 두 네트워크 간의 통신을 가능하게 하는 데 사용됩니다. ### NAT의 주요 유형 #### 1. 소스 NAT(SNAT) 소스 NAT는 패킷의 소스 IP 주소를 수정합니다. 일반적으로 사설 네트워크의 장치가 인터넷에 액세스할 때 사용됩니다. - **작동 방식**: 출발지 주소와 경우에 따라 포트를 변경; 사설→공인 변환은 흔한 사례 중 하나 - **사용 사례**: 인터넷 액세스, 아웃바운드 연결 - **추적**: NAT 테이블에 연결 상태 저장 ![내부 네트워크의 클라이언트(10.0.0.2:1234)가 NAT 라우터를 거치면서 출발지 주소가 공용 IP 198.51.100.1:5678로 변환되어 인터넷의 서버(203.0.113.5)와 통신하는 SNAT 동작을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-6.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-6.html) 문서용 주소로 사설→공인 SNAT 사례 하나를 설명합니다. SNAT는 출발지 변환이며 변환된 주소가 항상 공인 주소일 필요는 없습니다. #### 2. 목적지 NAT(DNAT) 목적지 NAT는 패킷의 목적지 IP 주소를 수정합니다. 일반적으로 공용 인터넷에서 사설 네트워크의 서비스에 액세스할 때 사용됩니다. - **작동 방식**: 목적지 주소·포트를 변경; 공인→사설 전달은 사례 중 하나 - **사용 사례**: 포트 포워딩, 로드 밸런싱, 인바운드 연결 - **구성**: 특정 포트 또는 포트 범위에 대한 매핑 정의 ![인터넷의 클라이언트가 NAT 라우터를 거치면서 목적지 주소가 공용 IP에서 사설 IP로 변환되어 내부 네트워크의 서버로 전달되는 DNAT 동작을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-7.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-7.html) 문서용 주소의 공인→사설 DNAT 사례입니다. DNAT는 목적지 변환이며 다른 주소 영역 사이에서도 사용됩니다. #### 3. 포트 주소 변환(PAT) PAT는 IP 주소와 포트 번호를 모두 수정합니다. 이를 통해 여러 내부 호스트가 단일 공용 IP 주소를 공유할 수 있습니다. - **작동 방식**: 내부 호스트의 IP:포트 조합을 단일 공용 IP의 다른 포트로 변환 - **사용 사례**: IP 주소 보존, 다수의 내부 호스트 지원 - **제한 사항**: 포트와 상태 자원은 유한하며 동시 흐름 수는 프로토콜·목적지 튜플·매핑 재사용에 따라 달라져 보편적인 65,000 연결 한계가 아님 ![서로 다른 내부 호스트가 포트 번호로 구분되어 하나의 공용 IP(198.51.100.1)를 공유해 인터넷의 서버와 통신하는 PAT 동작을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-8.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-8.html) 같은 원격 서버에 서로 다른 변환 포트를 사용하는 예입니다. 약 65,000개 포트 공간이 모든 NAT 연결의 보편적 한계는 아니며 프로토콜, 목적지 튜플, 매핑 방식과 상태 용량을 함께 봐야 합니다. #### 4. Twice NAT와 양방향 NAT **Twice NAT**는 트래픽이 주소 영역을 지날 때 출발지와 목적지 주소를 모두 바꾸며 중복 주소 영역을 조정하는 데 사용할 수 있습니다. 주소 매핑, DNS·애플리케이션 가정과 반환 경로를 함께 설계해야 합니다. RFC 2663의 **양방향 NAT(Bi-directional NAT)**는 양쪽 영역에서 세션을 시작할 수 있다는 뜻입니다. 각 패킷의 두 주소를 모두 바꾼다는 정의가 아닙니다. ### NAT의 장단점 #### 장점: - **IP 주소 보존**: 제한된 수의 공용 IP 주소로 다수의 내부 호스트 지원 - **주소 은닉**: 내부 주소를 숨길 수 있지만 NAT는 방화벽 정책이나 인증의 대체 수단이 아님 - **주소 영역 조정**: 적절한 변환으로 중복 주소 영역을 연결할 수 있으나 그 자체가 격리를 제공하지는 않음 - **유연한 네트워크 설계**: 내부 네트워크 재구성 없이 ISP 변경 가능 #### 단점: - **연결 추적 오버헤드**: 상태 테이블 유지 관리에 리소스 필요 - **특정 프로토콜 문제**: 일부 프로토콜은 NAT와 호환되지 않을 수 있음 - **엔드-투-엔드 연결성 손실**: 직접 피어-투-피어 통신 어려움 - **복잡한 문제 해결**: NAT 관련 문제 디버깅이 복잡할 수 있음 ### Kubernetes 및 Cilium에서의 NAT #### Kubernetes에서의 NAT Kubernetes는 다양한 시나리오에서 NAT를 사용합니다: 1. **클러스터 외부 통신**: 주소 연결성, masquerading 예외와 선택한 데이터 경로에 따라 SNAT를 사용할 수 있음 2. **서비스 구현**: 패킷 구현은 Service 목적지를 변환할 수 있으며 소켓 수준 로드 밸런싱은 해당 패킷 생성 전에 백엔드를 선택할 수 있음 3. **NodePort 서비스**: 노드 IP:포트에서 선택한 백엔드로 전달; 반환 동작은 SNAT/DSR과 트래픽 정책에 따라 다름 4. **LoadBalancer 서비스**: 제공자·컨트롤러 구현이 다르며 공인 주소에서 Pod로의 단일 DNAT 단계인 것은 아님 #### Cilium에서의 NAT Cilium은 eBPF를 활용하여 효율적인 NAT 구현을 제공합니다: 1. **eBPF 기반 NAT**: 커널 내에서 직접 NAT 수행 2. **고성능 연결 추적**: 최적화된 BPF 맵을 사용한 연결 상태 추적 3. **NAT 제어**: 지원 masquerading 예외, 서비스 전달과 Egress Gateway는 구성과 전제 조건이 서로 다름 4. **마스커레이딩**: 구성된 경로·장치의 조건부 출발지 변환이며 제외 CIDR과 지원 모드에 따라 결과가 달라짐 Egress Gateway는 일치하는 송신 트래픽을 선택 노드로 보내 구성된 게이트웨이 주소로 SNAT하는 별도 기능입니다. 원래 출발지 IP를 바꾸며 인터페이스·주소·반환 경로를 준비해야 합니다. 새 Pod가 정책 수렴 전에 트래픽을 보낼 수 있으므로 즉시 적용되는 fail-closed 출발지 IP 보장은 아닙니다. #### Cilium NAT 구성 예제: 접근 가능한 API 엔드포인트와 kube-proxy 대체·BPF masquerading을 준비한 환경의 Helm 조각입니다. 먼저 실제 부착 장치와 라우트를 확인합니다. CIDR의 SNAT 제외가 반환 라우트를 만들지는 않으며 NAT 크기는 예시일 뿐 보편적 권장 용량이 아닙니다. ```yaml # masquerade-values.yaml enableIPv4Masquerade: true kubeProxyReplacement: true bpf: masquerade: true natMax: 262144 ipMasqAgent: enabled: true config: nonMasqueradeCIDRs: - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 masqLinkLocal: false ``` ## 라우팅 프로토콜 라우팅 프로토콜은 네트워크에서 패킷이 소스에서 목적지로 이동하는 최적의 경로를 결정하는 규칙과 절차를 정의합니다. 이러한 프로토콜은 네트워크 토폴로지 변화에 적응하고, 트래픽을 효율적으로 전달하며, 네트워크 장애를 우회하는 데 중요한 역할을 합니다. ### 라우팅 프로토콜의 분류 #### 1. 내부 게이트웨이 프로토콜(IGP) 내부 게이트웨이 프로토콜은 단일 자율 시스템(AS) 내에서 라우팅 정보를 교환하는 데 사용됩니다. ##### 거리 벡터 프로토콜 - **RIP(Routing Information Protocol)** - 홉 카운트를 메트릭으로 사용 - 유효 메트릭은 15홉까지이며 16은 연결 불가를 의미 - 간단한 구현, 작은 네트워크에 적합 - 약 30초의 주기적 갱신에 타이머 무작위화와 변경 시 triggered update를 사용 - **EIGRP(Enhanced Interior Gateway Routing Protocol)** - 구성 가능한 복합 메트릭; 기본 계수는 처리량·대역폭과 지연을 사용하며 부하·신뢰성은 기본 계산에 포함되지 않음 - 부분 업데이트만 전송 - 빠른 수렴 - Cisco에서 시작되어 Informational RFC 7868에 문서화되었으며 이 공개가 IETF Standards Track 지정을 뜻하지는 않음 ##### 링크 상태 프로토콜 - **OSPF(Open Shortest Path First)** - 다익스트라 알고리즘을 사용한 최단 경로 계산 - 영역 기반 계층 구조 - 빠른 수렴 - 대규모 네트워크 지원 - 링크 상태 광고(LSA)를 통한 토폴로지 정보 교환 - **IS-IS(Intermediate System to Intermediate System)** - OSPF와 유사한 링크 상태 프로토콜 - 대규모 서비스 제공업체 네트워크에서 널리 사용 - 다중 네트워크 계층 지원 - 효율적인 라우팅 업데이트 #### 2. 외부 게이트웨이 프로토콜(EGP) 외부 게이트웨이 프로토콜은 서로 다른 자율 시스템 간에 라우팅 정보를 교환하는 데 사용됩니다. - **BGP(Border Gateway Protocol)** - 인터넷의 핵심 라우팅 프로토콜 - 경로 벡터 프로토콜 - 정책 기반 라우팅 결정 - TCP를 통한 안정적인 세션 - 경로 속성(AS 경로, 로컬 선호도 등)을 통한 경로 선택 - iBGP(내부 BGP) 및 eBGP(외부 BGP) 변형 ### 컨테이너 네트워킹에서의 라우팅 프로토콜 컨테이너 환경에서는 전통적인 라우팅 프로토콜과 함께 컨테이너 특화 라우팅 메커니즘이 사용됩니다. #### 1. BGP를 활용한 컨테이너 네트워킹 BGP는 컨테이너 네트워킹에서 다음과 같은 이유로 인기를 얻고 있습니다: - **연결성 광고**: Pod·Service 접두사를 라우터에 광고하며 실제 트래픽 경로는 로컬 전달 구현이 결정 - **확장성**: 대규모 클러스터 및 멀티 클러스터 환경 지원 - **기존 네트워크 통합**: 데이터 센터 네트워크 인프라와의 통합 - **가용성**: 다중 경로·수렴은 라우터 정책, 타이머와 작동하는 데이터 경로에 의존하며 세션만으로 빠른 장애 조치가 보장되지 않음 #### 2. 컨테이너 네트워크 라우팅 메커니즘 - **호스트 기반 라우팅**: 호스트가 Pod 라우트를 유지하며 별도로 구성한 라우트 광고 수단에 참여할 수 있음 - **중앙 집중식 라우팅**: 컨트롤러가 라우팅 결정을 중앙에서 관리 - **분산 라우팅**: 노드 간 직접 라우팅 정보 교환 - **정책 기반 라우팅**: 트래픽 특성에 따른 라우팅 결정 ### Cilium에서의 라우팅 Cilium은 eBPF로 라우팅을 구현하고 여러 데이터 경로 모드를 지원합니다. **호스트 라우팅은 별도의 축**입니다. BPF 호스트 라우팅은 노드 내부 전달을 최적화하며 호스트 스택·netfilter 일부를 우회할 수 있습니다. 호환되는 kube-proxy 대체·BPF masquerading이 필요하고 연동 제약도 있습니다. 노드 간 tunnel 대신 native 라우팅을 선택한다는 뜻은 아닙니다. #### 1. 직접 라우팅(Native Routing) 직접 라우팅 모드에서 Cilium은 오버레이 캡슐화 없이 포드 IP를 직접 라우팅합니다. - **작동 방식**: Pod 트래픽이 오버레이 캡슐화 없이 underlay 라우트를 사용하며 native 모드만으로 BGP가 활성화되지는 않음 - **장점**: 오버레이 캡슐화 비용을 피하며 실제 성능은 측정이 필요 - **요구 사항**: 노드 IP 연결성뿐 아니라 해당 Pod 주소와 반환 트래픽의 유효한 라우트 - **사용 사례**: 성능이 중요한 워크로드, 단일 서브넷 클러스터 `routingMode: native` 선택만으로 Pod 라우트가 BGP로 자동 광고되지는 않습니다. Underlay·반환 라우트를 준비하거나 적절한 라우트 배포 수단을 구성합니다. 앞의 호스트 라우트 그림이 전달 원리를 설명합니다. #### 2. BGP 라우팅 Cilium은 BGP 라우팅을 지원하여 포드 IP를 물리적 네트워크 인프라와 통합할 수 있습니다. - **작동 방식**: Cilium은 BGP 피어링을 통해 포드 CIDR을 광고 - **장점**: 기존 네트워크 인프라와의 통합, 고가용성 - **구성 요소**: BGP 피어링, 경로 필터링, 커뮤니티 속성 - **사용 사례**: 데이터 센터 네트워크와의 통합, 멀티 클러스터 환경 #### 3. 오버레이 라우팅 Cilium은 VXLAN 또는 Geneve와 같은 오버레이 프로토콜을 사용하여 노드 간 포드 트래픽을 라우팅할 수 있습니다. - **작동 방식**: 포드 패킷을 캡슐화하여 노드 간 전송 - **장점**: 네트워크 인프라 요구 사항 최소화, 유연한 배포 - **사용 사례**: 클라우드 환경, 복잡한 네트워크 토폴로지 #### 4. 하이브리드 라우팅 Cilium이 접근 가능하면 native 라우트를 사용하고 그렇지 않으면 자동으로 오버레이로 전환한다고 가정하지 않습니다. 현재 터널 모드와 `autoDirectNodeRoutes: true`를 함께 사용하면 에이전트가 구성을 거부합니다. 지원되는 데이터 경로와 underlay를 준비합니다. 유효한 로드 밸런서 모드 `hybrid`는 다른 기능입니다. TCP에는 DSR, UDP에는 SNAT를 사용하며 오버레이/native 라우팅 fallback이 아닙니다. ### Cilium 라우팅 구성 예제 #### 직접 라우팅 구성: 의도한 Pod CIDR과 자동 직접 라우트용 공유 L2 네트워크의 노드를 가정한 native 라우팅 조각입니다. 다른 토폴로지에는 별도 라우팅 수단이 필요하며 터널 모드와 자동 fallback 용도로 함께 사용하지 않습니다. ```yaml # native-values.yaml routingMode: native autoDirectNodeRoutes: true ipv4NativeRoutingCIDR: 10.244.0.0/16 ``` #### BGP 라우팅 구성: 아래 기능 플래그는 전제 조건 중 하나일 뿐입니다. [고급 주제](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/07-advanced-topics.md)의 현재 `CiliumBGPClusterConfig`, `CiliumBGPPeerConfig`, `CiliumBGPAdvertisement`와 외부 라우터를 구성합니다. BGP 광고와 데이터 경로 라우팅 모드는 별도 선택입니다. ```yaml # bgp-values.yaml bgpControlPlane: enabled: true ``` #### 오버레이 라우팅 구성: 앞의 완전한 VXLAN Helm 값 예제를 사용합니다. 실제 모드와 포트는 에이전트 상태에서 확인하며 작은 ConfigMap으로 전체 설치 설정을 덮어쓰지 않습니다. ## DNS 및 서비스 디스커버리 DNS(Domain Name System)와 서비스 디스커버리는 현대적인 네트워크 애플리케이션, 특히 동적 컨테이너 환경에서 핵심적인 역할을 합니다. 이러한 메커니즘은 서비스 위치를 추상화하고, 애플리케이션이 네트워크 토폴로지 변화에 적응할 수 있게 해줍니다. ### DNS(Domain Name System) DNS는 사람이 읽을 수 있는 도메인 이름을 IP 주소로 변환하는 분산 시스템입니다. #### DNS 작동 원리 1. **계층적 네임스페이스**: 도메인 이름은 점으로 구분된 계층 구조로 구성됨(예: www.example.com) 2. **분산 데이터베이스**: 전 세계에 분산된 DNS 서버 네트워크 3. **반복적 및 재귀적 쿼리**: 클라이언트 요청을 처리하는 두 가지 주요 방법 4. **캐싱**: 성능 향상을 위한 임시 결과 저장 #### DNS 레코드 유형 - **A 레코드**: 도메인 이름을 IPv4 주소에 매핑 - **AAAA 레코드**: 도메인 이름을 IPv6 주소에 매핑 - **CNAME 레코드**: 도메인 이름의 별칭(canonical name) - **MX 레코드**: 메일 서버 지정 - **SRV 레코드**: 특정 서비스를 제공하는 서버 지정 - **TXT 레코드**: 텍스트 정보 저장(주로 검증 및 정책에 사용) - **PTR 레코드**: IP 주소를 도메인 이름으로 역방향 매핑(역방향 DNS) #### DNS 해석 과정 일반적인 캐시 미적중 질의는 애플리케이션의 stub resolver와 재귀 resolver를 구분합니다. | 단계 | 질의·응답 | | --- | --- | | 1 | Stub이 구성된 재귀 resolver에 `www.example.com`을 질의합니다. | | 2 | Resolver가 루트 서버에 질의하고 `.com` 서버 참조를 받습니다. | | 3 | Resolver가 `.com` 서버에 질의하고 `example.com` 권한 서버 참조를 받습니다. | | 4 | Resolver가 권한 서버에 질의해 해당 응답을 얻습니다. | | 5 | Resolver가 TTL에 따라 캐시하고 stub에 응답합니다. | 일반적으로 권한 서버들이 이 질의를 서로 전달하는 과정이 아닙니다. 캐시, 별칭과 구성된 forwarder에 따라 실제 교환은 달라질 수 있습니다. ### 컨테이너 환경에서의 서비스 디스커버리 서비스 디스커버리는 네트워크에서 사용 가능한 서비스를 자동으로 감지하고 위치를 파악하는 프로세스입니다. 컨테이너 환경에서는 동적으로 생성되고 제거되는 서비스를 효과적으로 관리하기 위해 특히 중요합니다. #### 서비스 디스커버리 접근 방식 1. **DNS 기반 서비스 디스커버리** - 서비스 등록 시 DNS 레코드 생성 - 클라이언트는 표준 DNS 조회를 통해 서비스 발견 - 간단하고 널리 지원됨 - 예: Kubernetes DNS, CoreDNS 2. **키-값 저장소 기반 서비스 디스커버리** - 중앙 집중식 키-값 저장소에 서비스 정보 저장 - 클라이언트는 저장소를 쿼리하여 서비스 발견 - 풍부한 메타데이터 지원 - 예: etcd, Consul, ZooKeeper 3. **API 기반 서비스 디스커버리** - 전용 API를 통해 서비스 정보 제공 - 클라이언트는 API를 호출하여 서비스 발견 - 복잡한 쿼리 및 필터링 지원 - 예: Kubernetes API 서버 4. **메시 기반 서비스 디스커버리** - 서비스 메시 인프라가 서비스 디스커버리 처리 - 클라이언트 측 로드 밸런싱 및 라우팅 지원 - 고급 트래픽 관리 기능 - 예: Istio, Linkerd ### Kubernetes에서의 DNS 및 서비스 디스커버리 Kubernetes는 클러스터 내 서비스 디스커버리를 위한 내장 메커니즘을 제공합니다. #### Kubernetes 서비스 Kubernetes 서비스는 포드 집합에 대한 안정적인 엔드포인트를 제공합니다: - **ClusterIP**: 보통 클러스터 안에서 사용하는 Service 가상 IP이며 외부 라우팅 가능 여부는 명시적 네트워크 설계에 따름; 본질적인 보안 경계는 아님 - **NodePort**: 적격 노드 주소의 노드 포트이며 트래픽 정책·라우팅·방화벽 조건을 따름 - **LoadBalancer**: 제공자·컨트롤러 구현을 요청하며 공인 또는 내부용일 수 있음 - **ExternalName**: 외부 서비스에 대한 DNS 별칭 #### Kubernetes DNS Kubernetes는 클러스터 내 DNS 서비스(일반적으로 CoreDNS)를 실행하여 서비스 디스커버리를 지원합니다: - **서비스 DNS**: `..svc.`; `cluster.local`은 흔한 구성값이지 보편적인 상수가 아님 - **포드 DNS**: 이전 주소 기반 `pod.` 형식은 구현 의존적·레거시 방식; 안정적인 Pod 이름에는 보통 hostname/subdomain과 대응하는 headless Service를 사용 - **헤드리스 서비스**: VIP 대신 엔드포인트 주소를 반환할 수 있으며 readiness와 `publishNotReadyAddresses`가 레코드 게시에 영향을 줌 DNS 조회와 Service 전달은 별개입니다. | 단계 | 담당 동작 | | --- | --- | | DNS 조회 | CoreDNS는 Kubernetes 객체 상태를 바탕으로 일반 Service 이름을 ClusterIP로 해석합니다. 해당 연결의 애플리케이션 백엔드를 선택하지 않습니다. | | 연결 | 클라이언트가 응답받은 Service 주소로 트래픽을 보냅니다. | | 전달 | Cilium 데이터 경로 같은 Service 구현이 Service/EndpointSlice에서 얻은 상태로 적격 백엔드를 선택합니다. | | Headless Service | DNS가 Service VIP 대신 엔드포인트 주소를 반환하며 클라이언트가 사용할 주소를 선택합니다. | 객체 watch와 데이터 경로 갱신은 비동기이며 DNS 응답은 백엔드 상태 점검이 아닙니다. #### Kubernetes 서비스 디스커버리 메커니즘 1. **환경 변수**: Pod 생성 시 존재한 Service를 반영할 수 있으며 실시간 검색 피드가 아니고 비활성화할 수 있음 2. **DNS**: 클러스터 DNS를 통한 서비스 이름 확인 3. **API 서버**: Kubernetes API를 직접 쿼리하여 서비스 정보 검색 4. **EndpointSlice 객체**: Service 구현에 백엔드 주소·포트·준비 상태 정보를 제공 ### Cilium에서의 DNS 및 서비스 디스커버리 Cilium은 Kubernetes의 서비스 디스커버리 메커니즘과 통합되며, 추가적인 기능을 제공합니다. #### Cilium의 DNS 기반 정책 Cilium은 DNS 이름을 기반으로 네트워크 정책을 정의할 수 있습니다: - **DNS 이름 기반 필터링**: 특정 도메인 이름에 대한 액세스 제어 - **와일드카드 지원**: `*.example.com`은 한 단계 하위 이름, 이 버전의 `**.example.com`은 여러 단계에 일치하며 apex에는 별도 명시적 일치가 필요 - **FQDN 정책**: 완전한 도메인 이름(FQDN)의 DNS 응답에서 학습한 IP를 사용하는 정책 네임스페이스, 레이블이 맞는 워크로드와 검증한 DNS 경로가 필요한 정책 전용 예입니다. DNS 관측과 TCP 443 목적지 허용은 별개입니다. DNS `*`는 모든 질의 이름을 허용하며 toFQDNs는 호스트 이름 인증이 아닙니다. ```yaml # dns-policy.yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: dns-policy namespace: cilium-fqdn-demo spec: endpointSelector: matchLabels: app: myapp egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s:k8s-app: kube-dns toPorts: - ports: - port: '53' protocol: UDP - port: '53' protocol: TCP rules: dns: - matchPattern: '*' - toFQDNs: - matchName: api.example.com - matchPattern: '*.api.example.com' toPorts: - ports: - port: '443' protocol: TCP ``` #### Cilium의 서비스 디스커버리 향상 Cilium은 Kubernetes 서비스 디스커버리를 향상시키는 여러 기능을 제공합니다: 1. **eBPF 기반 서비스 구현**: - kube-proxy 대체 - 커널 내 직접 서비스 로드 밸런싱 - 향상된 성능 및 기능 2. **글로벌 서비스**: - 여러 클러스터에 걸친 서비스 디스커버리 - 클러스터 간 로드 밸런싱 - 일치하는 Service 이름·네임스페이스와 명시적 공유·ClusterMesh 구성 3. **서비스 어피니티**: - 세션 어피니티 지원 - ClientIP 어피니티는 로드 밸런싱 알고리즘과 별개이며 소켓 경로는 네트워크 네임스페이스 쿠키를 사용할 수 있음 - 상태 유지 연결 지원 4. **헬스 체크 통합**: - 백엔드 상태는 Kubernetes readiness/EndpointSlice와 구성된 프록시 점검을 반영 - 변경은 비동기로 전파됨 - 모든 Cilium Service가 애플리케이션 능동 점검이나 즉시 장애 조치를 수행한다고 가정하지 않음 #### Cilium 서비스 구성 예제: 세션 어피니티는 Service에 설정하고 글로벌 Service에는 annotation과 작동하는 ClusterMesh가 필요합니다. 피어 Service는 이름과 네임스페이스가 같아야 합니다. 이 예는 애플리케이션, ClusterMesh나 외부 로드 밸런서를 생성하지 않습니다. ```yaml # global-service.yaml apiVersion: v1 kind: Service metadata: name: api namespace: cilium-service-demo annotations: service.cilium.io/global: 'true' spec: type: ClusterIP selector: app: api ports: - name: http port: 80 targetPort: 8080 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 ``` ## 로드 밸런싱 개념 로드 밸런싱은 네트워크 트래픽을 여러 서버나 백엔드 서비스에 분산하여 리소스 활용을 최적화하고, 적절한 용량과 백엔드 상태 처리를 함께 사용해 처리량·지연·가용성 목표를 지원하는 기술입니다. 컨테이너 환경에서는 동적으로 변화하는 백엔드 인스턴스 간에 트래픽을 효과적으로 분산하는 것이 특히 중요합니다. ### 로드 밸런싱 유형 #### 1. L4(전송 계층) 로드 밸런싱 L4 로드 밸런싱은 IP 주소와 포트 번호와 같은 전송 계층 정보를 기반으로 트래픽을 분산합니다. - **작동 방식**: TCP/UDP 헤더 정보를 기반으로 라우팅 결정 - **장점**: 빠른 처리, 낮은 오버헤드, 암호화된 트래픽 처리 가능 - **단점**: 애플리케이션 계층 정보에 기반한 고급 라우팅 불가 - **사용 사례**: TCP/UDP 기반 서비스, 고성능 요구 사항 ![클라이언트의 요청이 L4 로드 밸런서에서 TCP/UDP 헤더만 분석되어 두 백엔드 서버 중 하나로 분배되는 전송 계층(L4) 로드 밸런싱 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-12.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-12.html) 분기는 가능한 백엔드 선택이며 하나의 연결을 두 서버에 방송한다는 뜻이 아닙니다. L4 전달은 암호화된 HTTP 내용을 검사하지 않고 TLS를 운반할 수 있습니다. #### 2. L7(애플리케이션 계층) 로드 밸런싱 L7 로드 밸런싱은 HTTP 헤더, URL, 쿠키 등과 같은 애플리케이션 계층 정보를 기반으로 트래픽을 분산합니다. - **작동 방식**: HTTP/HTTPS 요청 내용을 검사하여 라우팅 결정 - **장점**: 콘텐츠 기반 라우팅, 고급 트래픽 관리, 보안 기능 - **단점**: 프록시 처리 비용; HTTPS의 HTTP 내용 검사에는 적절한 TLS 종료가 필요 - **사용 사례**: 웹 애플리케이션, 마이크로서비스, API 게이트웨이 ![클라이언트의 HTTP 요청이 L7 로드 밸런서에서 URL 경로와 헤더까지 검사되어 API 서버와 웹 서버 중 알맞은 백엔드로 분배되는 애플리케이션 계층(L7) 로드 밸런싱 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-13.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-13.html) 선택한 경로는 요청 속성에 따라 달라집니다. HTTPS의 HTTP 내용 기반 라우팅에는 적절한 TLS 종료·검사 경로가 필요합니다. ### 로드 밸런싱 알고리즘 로드 밸런싱 알고리즘은 트래픽을 백엔드 서버에 분산하는 방법을 결정합니다. #### 1. 라운드 로빈(Round Robin) - **작동 방식**: 순차적으로 각 백엔드 서버에 요청 분배 - **장점**: 간단한 순차 선택; 같은 요청 수가 같은 백엔드 작업량을 뜻하지는 않음 - **단점**: 서버 용량 차이나 현재 부하를 고려하지 않음 - **변형**: 가중치 라운드 로빈(서버 용량에 따른 가중치 적용) #### 2. 최소 연결(Least Connections) - **작동 방식**: 활성 연결이 가장 적은 서버로 새 요청 전달 - **장점**: 서버 부하 고려, 긴 연결 처리에 효과적 - **단점**: 연결 수가 항상 부하를 정확히 반영하지는 않음 - **변형**: 가중치 최소 연결(서버 용량에 따른 가중치 적용) #### 3. IP 해시(IP Hash) - **작동 방식**: 클라이언트 IP 주소를 해싱하여 일관된 백엔드 서버 선택 - **장점**: 입력·백엔드 구성이 유지되는 동안 선택을 안정화할 수 있으나 영구 세션 저장소는 아님 - **단점**: 불균등한 분배 가능성, 특정 서버에 과부하 발생 가능 - **변형**: 소스-목적지 IP 해시(소스 및 목적지 IP 모두 고려) #### 4. 최소 응답 시간(Least Response Time) - **작동 방식**: 응답 시간이 가장 짧은 서버로 요청 전달 - **장점**: 성능 및 가용성 고려, 지연 시간에 민감한 애플리케이션에 적합 - **단점**: 응답 시간 측정 오버헤드, 네트워크 변동성에 영향 받음 - **변형**: 가중치 응답 시간(서버 용량과 응답 시간 모두 고려) #### 5. 임의 선택(Random) - **작동 방식**: 무작위로 백엔드 서버 선택 - **장점**: 간단한 구현, 특별한 상태 추적 불필요 - **단점**: 불균등한 분배 가능성 - **변형**: 가중치 임의 선택(서버 용량에 따른 확률 조정) ### 로드 밸런서 배포 모델 #### 1. 하드웨어 로드 밸런서 - **특징**: 전용 물리적 장비 - **장점**: 고성능, 안정성, 전용 하드웨어 가속 - **단점**: 비용, 확장성 제한, 유연성 부족 - **예**: 애플리케이션 전달 컨트롤러 어플라이언스; 일부 제품군은 가상·소프트웨어 형태도 제공 #### 2. 소프트웨어 로드 밸런서 - **특징**: 범용 서버에서 실행되는 소프트웨어 - **장점**: 유연성, 비용 효율성, 프로그래밍 가능 - **단점**: 용량은 구현·하드웨어·워크로드에 따라 달라지며 소프트웨어가 모든 어플라이언스보다 본질적으로 느린 것은 아님 - **예**: NGINX, HAProxy, Envoy #### 3. 클라우드 로드 밸런서 - **특징**: 클라우드 제공업체가 관리하는 서비스 - **장점**: 관리 오버헤드 감소, 자동 확장, 고가용성 - **단점**: 제공업체 종속성, 제한된 커스터마이징 - **예**: AWS ELB/ALB/NLB, Google Cloud Load Balancing, Azure Load Balancer #### 4. 컨테이너 네이티브 로드 밸런서 - **특징**: 컨테이너 환경에 최적화된 로드 밸런싱 - **장점**: 컨테이너 오케스트레이션과의 통합, 동적 서비스 디스커버리 - **단점**: 컨테이너 환경에 특화됨 - **예**: Kubernetes 서비스, Istio, Cilium ### Kubernetes에서의 로드 밸런싱 Kubernetes는 여러 수준의 로드 밸런싱을 제공합니다: #### 1. 서비스 로드 밸런싱 - **ClusterIP**: 클러스터 내부 로드 밸런싱 - **NodePort**: 노드 포트를 통한 외부 액세스 - **LoadBalancer**: 외부 로드 밸런서 프로비저닝 - **ExternalName**: 외부 서비스에 대한 DNS 별칭 #### 2. Ingress 컨트롤러 - L7 로드 밸런싱 및 라우팅 제공 - URL 기반 라우팅과 TLS 종료; 인증 기능은 컨트롤러와 구성에 따라 다름 - Traefik, HAProxy, Istio 기반 등의 구현이 있음. 커뮤니티 `ingress-nginx`는 2026년 3월에 유지 관리를 종료했으며 남아 있는 배포 파일이 현재 유지 관리되는 설치 권장은 아님 #### 3. 서비스 메시 - 마이크로서비스 간 고급 트래픽 관리 - 세분화된 라우팅, 트래픽 분할, 장애 주입 - 예: Istio, Linkerd, Consul 서비스 메시; 트래픽 관리·보안 기능 범위는 구현마다 다름 ### Cilium에서의 로드 밸런싱 Cilium은 eBPF를 활용하여 효율적인 로드 밸런싱을 구현합니다: #### 1. eBPF 기반 로드 밸런싱 - **kube-proxy 대체**: 커널 내에서 직접 서비스 로드 밸런싱 - **성능**: 지원 BPF 경로가 일반 스택 일부를 피할 수 있으며 실제 워크로드에서 효과를 측정 - **확장성**: 대규모 서비스 및 엔드포인트 지원 - **연결 추적 최적화**: 효율적인 상태 관리 ![Pod A가 서비스 IP로 보낸 패킷이 커널의 eBPF 프로그램에서 패킷 인터셉트·서비스 맵 조회·백엔드 선택·패킷 전달의 4단계를 거쳐 kube-proxy 없이 곧바로 Pod B로 전달되는 Cilium eBPF 기반 로드 밸런싱 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-cilium-networking-concepts-14.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-cilium-networking-concepts-14.html) 패킷 경로의 Service 변환 예입니다. 소켓 수준 로드 밸런싱은 Service IP 패킷이 만들어지기 전에 백엔드를 선택할 수도 있습니다. 지연 개선은 실제 경로에서 측정해야 합니다. #### 2. 로드 밸런싱 알고리즘 BPF Service 알고리즘은 기본값 **random**과 **Maglev**입니다. Maglev는 흐름 정보를 해싱하며 단순한 출발지 IP 어피니티가 아닙니다. 일반 소켓 수준 east-west 선택 경로와 Maglev가 적용되는 외부 패킷 경로도 구분합니다. `ClientIP` 세션 어피니티는 Service에 별도로 설정합니다. 어피니티 시간 제한은 ‘Maglev 타임아웃’이 아니며 Maglev가 주기적으로 세션을 재분배하는 타이머는 없습니다. 구성원·시드·테이블 변경으로 선택이 바뀔 수 있고 일관된 해싱이라고 제거된 백엔드가 연결을 계속 처리할 수는 없습니다. #### 3. L7 로드 밸런싱 Cilium은 L7(애플리케이션 계층) 로드 밸런싱도 지원합니다: - **HTTP 헤더 기반 라우팅**: 특정 헤더 값에 따른 라우팅 - **URL 경로 기반 라우팅**: URL 패턴에 따른 트래픽 분배 - **gRPC 라우팅**: gRPC 메서드 및 메타데이터 기반 라우팅 - **Kafka**: 현재 Cilium에는 이전 Kafka 토픽 L7 정책·라우팅 기능이 없으므로 브로커에 맞는 제어를 사용 #### 4. 글로벌 서비스 로드 밸런싱 Cilium은 여러 클러스터에 걸친 로드 밸런싱을 지원합니다: - **클러스터 간 로드 밸런싱**: 여러 클러스터의 백엔드 간 트래픽 분산 - **지역 선호**: 구성된 local/remote 어피니티이며 네트워크 지연을 자동 측정하는 기능은 아님 - **장애 처리**: 엔드포인트 상태와 원격 캐시 동작에 의존; 기본 cache TTL 0은 오래된 원격 상태를 유지할 수 있으므로 애플리케이션 장애 조치를 시험해야 함 #### Cilium 로드 밸런싱 구성 예제: 준비된 설치용 Helm 값입니다. 해시 시드는 유효한 **12바이트 base64 학습용 값**입니다. 배포에는 참여 노드가 공유할 임의 시드를 생성·보존하고 시드·테이블 변경의 연결 영향을 검토합니다. [준비된 로드 밸런싱 프로필](https://www.atomai.click/kubernetes-docs/llms/ko/networking/cilium/05-l2-l7-networking.md)을 참고합니다. ```yaml # load-balancing-values.yaml kubeProxyReplacement: true loadBalancer: algorithm: maglev maglev: tableSize: 16381 hashSeed: AAECAwQFBgcICQoL ``` ## 네트워크 보안 기초 네트워크 보안은 네트워크 인프라, 애플리케이션 및 데이터를 무단 액세스, 오용, 장애 또는 수정으로부터 보호하는 관행입니다. 컨테이너 환경에서는 동적이고 분산된 특성으로 인해 네트워크 보안이 더욱 중요합니다. ### 네트워크 보안의 핵심 개념 #### 1. 심층 방어(Defense in Depth) 심층 방어는 개별 실패의 영향을 줄이기 위해 제어를 조합하는 접근입니다. 공유 의존성이나 공통 구성 오류는 여러 계층에 동시에 영향을 줄 수 있습니다. - **다중 보안 계층**: 네트워크, 호스트, 애플리케이션, 데이터 수준의 보호 - **중복 제어**: 다양한 보안 메커니즘의 조합 - **실패 격리**: 경계를 설계·시험해야 하며 실패의 독립성이 자동으로 보장되지는 않음 - **위협 탐지 및 대응**: 각 계층에서의 모니터링 및 대응 #### 2. 최소 권한 원칙 최소 권한 원칙은 사용자, 프로세스 또는 애플리케이션에 작업 수행에 필요한 최소한의 권한만 부여하는 보안 관행입니다. - **세분화된 액세스 제어**: 필요한 리소스에만 액세스 제한 - **권한 분리**: 다양한 기능에 대한 권한 분리 - **기본 거부**: 명시적으로 허용되지 않은 모든 액세스 거부 - **정기적인 검토**: 권한의 정기적인 감사 및 조정 #### 3. 네트워크 세분화 네트워크 세분화는 네트워크를 더 작은 세그먼트 또는 영역으로 분할하여 보안을 강화하고 위협의 측면 이동을 제한하는 기술입니다. - **보안 영역**: 유사한 보안 요구 사항을 가진 시스템 그룹화 - **마이크로세분화**: 워크로드 수준의 세분화된 제어 - **경계 보호**: 영역 간 트래픽 제어 및 모니터링 - **위협 격리**: 침해의 영향 범위 제한 #### 4. 암호화 암호화는 권한이 없는 당사자가 읽을 수 없도록 데이터를 변환하는 프로세스입니다. - **전송 중 암호화**: 네트워크를 통해 이동하는 데이터 보호(예: 지원되는 TLS) - **저장 중 암호화**: 디스크 또는 데이터베이스에 저장된 데이터 보호 - **엔드-투-엔드 암호화**: 전체 통신 경로에 걸쳐 데이터 보호 - **키 관리**: 암호화 키의 안전한 생성, 저장 및 교체 ### 컨테이너 네트워킹 보안 위협 컨테이너 환경은 고유한 보안 과제를 제시합니다: #### 1. 네트워크 기반 공격 - **DDoS(분산 서비스 거부) 공격**: 서비스 가용성을 방해하기 위한 대량의 트래픽 - **포트 스캐닝**: 열린 포트 및 취약점 탐색 - **ARP 스푸핑**: 네트워크 트래픽을 가로채기 위한 주소 확인 프로토콜 조작 - **DNS 포이즈닝**: DNS 조회를 악의적인 대상으로 리디렉션 #### 2. 애플리케이션 계층 공격 - **SQL 인젝션**: 악의적인 SQL 코드 삽입 - **XSS(크로스 사이트 스크립팅)**: 클라이언트 측 스크립트 삽입 - **CSRF(크로스 사이트 요청 위조)**: 인증된 사용자를 통한 악의적인 작업 수행 - **명령 인젝션**: 시스템 명령 실행을 위한 악의적인 입력 #### 3. 컨테이너 특화 위협 - **이미지 취약점**: 취약한 구성 요소가 포함된 컨테이너 이미지 - **권한 에스컬레이션**: 경계 내부 또는 외부로의 권한 상승이며 항상 컨테이너 탈출과 같은 사건은 아님 - **측면 이동**: 한 컨테이너에서 다른 컨테이너로의 무단 액세스 - **볼륨 마운트 악용**: 민감한 호스트 경로에 대한 액세스 ### 네트워크 보안 제어 #### 1. 방화벽 방화벽은 정의된 보안 규칙에 따라 네트워크 트래픽을 필터링하는 네트워크 보안 시스템입니다. - **패킷 필터링**: IP 주소, 포트, 프로토콜 기반 필터링 - **상태 검사**: 연결 상태를 추적하여 컨텍스트 기반 결정 - **애플리케이션 계층 필터링**: 애플리케이션 프로토콜 이해 및 검사 - **차세대 방화벽(NGFW)**: 고급 위협 탐지 및 방지 기능 #### 2. 침입 탐지 및 방지 시스템(IDS/IPS) IDS/IPS는 네트워크 트래픽을 모니터링하고 악의적인 활동을 탐지하거나 차단하는 시스템입니다. - **시그니처 기반 탐지**: 알려진 공격 패턴 매칭 - **이상 탐지**: 정상 동작에서 벗어난 활동 식별 - **행동 모니터링**: 의심스러운 활동 패턴 분석 - **자동 대응**: 탐지된 위협에 대한 실시간 대응 #### 3. 네트워크 정책 네트워크 정책은 네트워크 내에서 허용되는 통신을 정의하는 규칙 집합입니다. - **인그레스 제어**: 들어오는 트래픽 제한 - **이그레스 제어**: 나가는 트래픽 제한 - **세분화된 정책**: 워크로드 수준의 통신 제어 - **레이블 기반 정책**: 동적 환경에서의 유연한 정책 적용 #### 4. 암호화 프로토콜 암호화 프로토콜은 네트워크를 통한 안전한 통신을 제공합니다. - **TLS**: 웹 트래픽과 API 통신 보호; SSL 프로토콜은 폐기됨 - **IPsec**: 네트워크 계층 암호화 - **WireGuard**: 현대적이고 효율적인 VPN 프로토콜 - **mTLS(상호 TLS)**: 클라이언트와 서버 모두의 인증 ### Kubernetes에서의 네트워크 보안 Kubernetes는 컨테이너화된 애플리케이션의 네트워크 보안을 위한 여러 메커니즘을 제공합니다: #### 1. 네트워크 정책 Kubernetes NetworkPolicy는 선택한 Pod의 L3/L4 허용을 지정합니다. 집행할 네트워크 구현이 필요하며 적용되는 정책의 허용은 합산됩니다. 기존 연결·hostNetwork 경로의 의미도 따로 확인합니다. - **포드 선택기**: 레이블을 기반으로 정책이 적용되는 포드 선택 - **인그레스 규칙**: 들어오는 트래픽 제어 - **이그레스 규칙**: 나가는 트래픽 제어 - **CIDR 기반 규칙**: IP 범위 기반 필터링 별도 네임스페이스의 L4 예제로, 레이블이 맞는 frontend/API/database와 해당 CoreDNS 레이블을 가정합니다. DNS egress를 포함합니다. 넓은 L4 허용은 겹치는 L7 제한을 우회할 수 있으므로 뒤의 L7 예제와 분리합니다. ```yaml # api-l4-policy.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: api-allow namespace: cilium-policy-l4-demo spec: podSelector: matchLabels: app: api policyTypes: - Ingress - Egress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080 egress: - to: - podSelector: matchLabels: app: database ports: - protocol: TCP port: 5432 - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 - protocol: TCP port: 53 ``` #### 2. 서비스 메시 보안 서비스 메시는 마이크로서비스 간의 통신을 관리하고 보호하는 인프라 계층입니다. - **mTLS**: 서비스 간 암호화된 통신 - **인증 및 권한 부여**: 서비스 ID 확인 및 액세스 제어 - **트래픽 정책**: 세분화된 라우팅 및 액세스 제어 - **관찰 가능성**: 서비스 간 통신에 대한 가시성 #### 3. 보안 컨텍스트 보안 컨텍스트는 포드 및 컨테이너의 권한 및 액세스 제어 설정을 정의합니다. - **권한 제한**: 루트가 아닌 사용자로 실행 - **기능 제한**: 필요한 Linux 기능만 허용 - **읽기 전용 루트 파일 시스템**: 컨테이너 루트 파일 시스템 쓰기를 제한하지만 마운트 볼륨은 쓰기 가능할 수 있음 - **seccomp 및 AppArmor**: 시스템 호출 및 애플리케이션 동작 제한 ### Cilium의 네트워크 보안 기능 Cilium은 eBPF를 활용하여 강력한 네트워크 보안 기능을 제공합니다: #### 1. 신원 기반 보안 Cilium은 보안 관련 레이블에서 얻은 워크로드 ID 정책과 구성된 범위의 명시적 CIDR/IP 제어를 지원합니다. - **레이블 기반 정책**: 동적 환경에서의 일관된 보안 - **서비스 계정 기반 정책**: Kubernetes 서비스 계정을 기반으로 한 액세스 제어 - **DNS 기반 정책**: FQDN을 기반으로 한 이그레스 제어 - **API 인식 보안**: HTTP 메서드 및 경로 기반 필터링 `toCIDR`는 목적지 범위를 선택하지만 기본적으로 클러스터 내부 관리 Pod·노드는 CIDR 선택자에 일치하지 않습니다. 이 버전에는 해당 경우를 위한 명시적 Beta 선택 기능이 있습니다. `world` 엔티티는 알려진 모든 클러스터·ClusterMesh ID가 아니라 외부 엔드포인트를 대상으로 합니다. `world`를 모든 클러스터 허용의 동의어로 쓰지 말고 적절한 ID·엔티티 범위를 선택합니다. #### 2. 투명한 암호화 Cilium은 애플리케이션 변경 없이 지원 경로를 암호화할 수 있습니다. 노드 터널이 동일 노드 트래픽이나 모든 외부 목적지를 보호하지는 않습니다. 별도 SPIRE 상호 인증 핸드셰이크 자체는 애플리케이션 트래픽을 암호화하지 않으며 Beta ztunnel 워크로드 mTLS에는 별도의 전제 조건이 있습니다. - **IPsec**: 노드 간 트래픽에 대한 네트워크 계층 암호화 - **WireGuard**: 현대적이고 효율적인 암호화 프로토콜 - **투명한 통합**: 애플리케이션 변경 없이 암호화 적용 - **키 교체**: 선택 모드의 키 수명을 따르며 Cilium IPsec에는 제공한 키 자료와 문서화된 Secret 교체 절차가 필요 #### 3. 위협 탐지 및 가시성 Cilium/Hubble은 조사에 사용할 네트워크 관측 데이터를 제공합니다. 완전한 IDS/WAF, 런타임 집행이나 알림·대응에는 적절한 별도 구성 또는 연동이 필요합니다. - **Hubble**: 네트워크 흐름 모니터링 및 분석 - **흐름 로그**: 포드 간 통신에 대한 상세한 로그 - **이상 탐지**: 외부 탐지 규칙으로 관측 패턴을 분석할 수 있으며 Hubble이 모든 공격을 자동 분류하지는 않음 - **보안 이벤트 알림**: 알림·SIEM 연동을 구성하고 이벤트 손실, 잡음과 불완전한 관측을 고려 #### 4. L3-L7 정책 시행 Cilium은 네트워크 계층부터 애플리케이션 계층까지 포괄적인 정책 시행을 제공합니다. - **L3/L4 정책**: IP 및 포트 기반 필터링 - **L7 HTTP 필터링**: URL, 메서드, 헤더 기반 제어 - **L7 gRPC 필터링**: gRPC 메서드 및 메타데이터 기반 제어 - **DNS 정책**: 질의 필터링과 FQDN 규칙용 DNS 관측; 현재 Kafka 토픽 L7 정책은 없음 #### Cilium 네트워크 보안 구성 예제: L4 예제와 다른 네임스페이스를 사용하는 L7 대안 정책입니다. 관측 가능한 평문 HTTP 또는 적절한 TLS 검사 경로, 실제 레이블 의존성과 DNS 연결이 필요합니다. `.example` 외부 이름은 자리표시자이며 작동하는 외부 서비스를 제공하지 않습니다. ```yaml # api-l7-policy.yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: secure-api namespace: cilium-policy-l7-demo spec: endpointSelector: matchLabels: app: api ingress: - fromEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-policy-l7-demo k8s:app: frontend toPorts: - ports: - port: '8080' protocol: TCP rules: http: - method: GET path: /api/v1/products egress: - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: kube-system k8s:k8s-app: kube-dns toPorts: - ports: - port: '53' protocol: UDP - port: '53' protocol: TCP rules: dns: - matchPattern: '*' - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: cilium-policy-l7-demo k8s:app: database toPorts: - ports: - port: '5432' protocol: TCP - toFQDNs: - matchName: api.external-service.example toPorts: - ports: - port: '443' protocol: TCP ``` ### 네트워크 보안 모범 사례 #### 1. 기본 거부 정책 - 명시적으로 허용된 트래픽만 허용하는 기본 거부 정책 구현 - 필요한 통신 경로만 열어두기 - 정기적인 정책 검토 및 불필요한 규칙 제거 - 정책 변경에 대한 감사 추적 유지 #### 2. 심층 방어 접근 방식 - 여러 보안 계층 구현 - 네트워크, 호스트, 애플리케이션 수준의 보호 조합 - 다양한 보안 메커니즘의 중복 제어 - 단일 실패 지점 제거 #### 3. 최소 권한 네트워킹 - 필요한 최소한의 네트워크 액세스만 허용 - 서비스별 세분화된 정책 정의 - 불필요한 포트 및 프로토콜 차단 - 정기적인 액세스 검토 및 조정 #### 4. 지속적인 모니터링 및 감사 - 네트워크 트래픽 및 정책 위반 모니터링 - 이상 징후 및 잠재적 위협 탐지 - 보안 이벤트에 대한 알림 및 대응 - 정기적인 보안 감사 및 취약점 평가 ## 공식 근거 - [Cilium routing](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/concepts/routing.rst) - [Cilium chart values](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/install/kubernetes/cilium/values.yaml) - [Kube-proxy replacement](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/kubernetes/kubeproxy-free.rst) - [Masquerading](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/concepts/masquerading.rst) - [BGP Control Plane](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/bgp-control-plane/bgp-control-plane.rst) - [Global Services](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/clustermesh/global-services.rst) - [Policy language](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/policy/layer3.rst) - [DNS policy](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/dns.rst) - [IPsec](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/network/encryption-ipsec.rst) - [WireGuard](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/network/encryption-wireguard.rst) - [Kubernetes network model](https://kubernetes.io/docs/concepts/services-networking/) - [Services](https://kubernetes.io/docs/concepts/services-networking/service/) - [DNS for Services and Pods](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/) - [NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) - [CNI specification](https://raw.githubusercontent.com/containernetworking/cni/main/SPEC.md) - [Docker bridge networking](https://docs.docker.com/engine/network/drivers/bridge/) - [Docker host networking](https://docs.docker.com/engine/network/drivers/host/) - [Ingress NGINX retirement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/) - [Internet architecture / RFC 1122](https://www.rfc-editor.org/rfc/rfc1122.txt) - [DNS / RFC 1034](https://www.rfc-editor.org/rfc/rfc1034.txt) - [NAT terminology / RFC 2663](https://www.rfc-editor.org/rfc/rfc2663.txt) - [NAT mapping behavior / RFC 4787](https://www.rfc-editor.org/rfc/rfc4787.txt) - [RIP v2 / RFC 2453](https://www.rfc-editor.org/rfc/rfc2453.txt) - [EIGRP / RFC 7868](https://www.rfc-editor.org/rfc/rfc7868.txt) ## 퀴즈 이 장에서 배운 내용을 테스트하려면 [주제 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/networking-concepts-quiz)를 풀어보세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/cilium/glossary ---------------------------------------- # 용어 및 약어 > **검토 기준**: Cilium 1.20.1. > **최종 검토**: 2026년 9월 12일. Cilium, eBPF, Kubernetes와 네트워킹 용어를 알파벳순으로 정리합니다. 반복된 정의는 하나로 합쳤습니다. ## A **API(Application Programming Interface)** ⚪ - 애플리케이션 간의 통신을 가능하게 하는 인터페이스 정의 집합 **ARP(Address Resolution Protocol)** 🟣 - 로컬 링크에서 IPv4 주소를 링크 계층 주소(보통 Ethernet MAC 주소)로 해석합니다. - 원격 목적지에는 다음 홉의 주소를 해석합니다. IPv6는 ARP 대신 Neighbor Discovery를 사용합니다. **AWS ENI(Elastic Network Interface)** 🟣 - Amazon Web Services에서 제공하는 가상 네트워크 인터페이스 - Cilium의 AWS ENI IPAM 모드에서 사용됨 ## B **BGP(Border Gateway Protocol)** 🟣 - 피어 간 연결성을 광고하는 도메인 간 라우팅 프로토콜입니다. - Cilium BGP Control Plane은 선택한 접두사를 광고하며, native 라우팅 모드 자체이거나 로컬 데이터 경로 라우트를 설치하는 기능은 아닙니다. **BPF(Berkeley Packet Filter)** 🟠 - 패킷 필터링을 위한 기술, eBPF의 전신 - 원래 네트워크 패킷 캡처를 위해 개발됨 **BPF 맵(BPF Maps)** 🟠 - BPF 프로그램과 사용자 공간이 상태·이벤트를 공유하는 커널 관리 자료 구조입니다. - 여러 유형이 키와 값을 사용하지만 ring buffer·queue·stack의 연산은 다릅니다. BPF ring buffer는 map lookup/update/delete를 지원하지 않습니다. ## C **CGroup(Control Group)** 🟢 - Linux control group은 프로세스를 묶고 CPU·메모리 등의 자원을 집계·제어합니다. - 컨테이너 런타임이 사용하지만 cgroup 자체가 프로세스·네트워크 네임스페이스 격리를 제공하지는 않습니다. **CIDR(Classless Inter-Domain Routing)** 🟣 - IP 주소 할당 및 라우팅 집계 방법 - 예: 192.168.1.0/24는 192.168.1.0부터 192.168.1.255까지의 IP 주소 범위를 나타냄 **Cilium** 🔵 - eBPF를 기반으로 하는 오픈 소스 네트워킹, 보안 및 관찰성 솔루션 - Kubernetes CNI 구현체로 사용됨 **Cilium Agent** - Cilium - 엔드포인트, BPF 프로그램과 정책·데이터 경로 상태를 관리하는 노드 로컬 구성 요소입니다. Cilium이 관리하는 적격 노드에서 실행됩니다. **Cilium Operator** - Cilium - CRD 등록, 모드별 IPAM/LB IPAM, 가비지 컬렉션과 활성화한 Ingress/Gateway 컨트롤러 등을 담당하는 클러스터 수준 컨트롤러입니다. - 복제본 수는 구성할 수 있습니다. 선택적 ID 관리와 ClusterMesh 동기화는 활성 기능에 따라 다르며 노드의 패킷 전달 구성 요소는 아닙니다. **ClusterMesh** - Cilium - 서비스 검색, 로드 밸런싱과 원격 ID 정책을 위한 Cilium 멀티 클러스터 네트워크 메타데이터·연결 기능입니다. - 호환 주소, 신뢰와 연결 경로가 필요하며 공유 스토리지나 모든 정책 리소스의 자동 복제를 제공하지 않습니다. **CNI(Container Network Interface)** 🟢 - Container Network Interface: 컨테이너 네트워크 연결을 구성하는 명세와 플러그인입니다. - 현재 Kubernetes에서는 CRI 컨테이너 런타임이 CNI 플러그인을 로드·호출합니다. kubelet의 이전 직접 CNI 관리 플래그는 Kubernetes 1.24에서 제거되었습니다. **CoreDNS** 🟢 - Kubernetes 클러스터에서 일반적으로 사용되는 DNS 서버 - 서비스 디스커버리에 중요한 역할을 함 **CRD(Custom Resource Definition)** 🟢 - Kubernetes API를 확장하여 사용자 정의 리소스를 정의하는 방법 - Cilium은 CRD를 사용하여 네트워크 정책 등을 정의함 ## D **DaemonSet** - 스케줄링 제약에 따라 선택된 적격 노드에 데몬 Pod를 실행하는 Kubernetes 컨트롤러입니다. 모든 노드를 반드시 포함하지는 않습니다. **DNAT(Destination Network Address Translation)** 🟣 - 패킷의 목적지 IP 주소를 수정하는 NAT 유형 - 로드 밸런싱 및 포트 포워딩에 사용됨 **DNS(Domain Name System)** 🟣 - A/AAAA 주소, CNAME 별칭, SRV 서비스 정보 등의 레코드를 제공하는 분산 이름 시스템입니다. - Cilium DNS 정책과 학습한 IP를 사용하는 FQDN 정책은 관련되지만 서로 다른 제어입니다. ## E **eBPF(extended Berkeley Packet Filter)** 🟠 - Extended Berkeley Packet Filter: Cilium이 사용하는 프로그래밍 가능한 커널 훅과 관련 기반 기능입니다. - 검증기가 프로그램 수락 전에 속성을 검사하지만 커널·검증기 구현의 취약점 부재를 보장하지는 않습니다. **Endpoint** 🔵 - 로컬 데이터 경로·정책 상태를 가진 Cilium 관리 네트워크 엔드포인트이며 보통 Pod에 해당합니다. - 엔드포인트 ID는 에이전트에 로컬이며 여러 엔드포인트가 공유할 수 있는 보안 ID와 다릅니다. **Envoy** 🔵 - Cilium이 구성된 HTTP/gRPC 정책, L7 가시성, 프록시 기반 서비스 라우팅에 사용하는 오픈 소스 프록시입니다. - DNS 정책은 Cilium DNS 프록시를 사용합니다. Kafka L7 정책은 제거되었으며 모든 L7 규칙이 Envoy 인스턴스를 자동 배포하는 것은 아닙니다. ## F **FQDN(Fully Qualified Domain Name)** - Fully Qualified Domain Name: DNS 트리에서 전체 위치를 나타내는 절대 이름이며 `www.example.com.`처럼 마지막 루트 점을 쓰기도 합니다. - Cilium `toFQDNs`는 학습한 목적지 IP를 허용합니다. 자체적으로 HTTPS 서버를 인증하거나 공유 IP의 모든 HTTP Host 값을 제한하지는 않습니다. ## G **GENEVE(Generic Network Virtualization Encapsulation)** - 네트워크 가상화를 위한 캡슐화 프로토콜 **gRPC(gRPC Remote Procedure Call)** - Google에서 개발한 고성능 RPC(원격 프로시저 호출) 프레임워크 ## H **Hubble** 🔵 - 흐름 이벤트, 지원 프로토콜 메타데이터, 메트릭과 질의 인터페이스를 제공하는 Cilium 네트워크 관측 계층입니다. - 이력은 유한하며 관측 데이터가 손실·필터링될 수 있습니다. 알림·영구 저장·자동 대응에는 구성된 연동이 필요합니다. ## I **Identity** - Cilium - 보안 관련 레이블에서 도출한 숫자 보안 식별자입니다. 해당 할당 범위 안에서 여러 엔드포인트가 공유할 수 있습니다. - CRD 할당 모드에서 CiliumIdentity의 `security-labels`가 원본 필드입니다. 예약 ID와 노드 로컬 ID가 모두 이러한 클러스터 범위 객체로 표현되는 것은 아닙니다. **IPAM(IP Address Management)** 🟣 - IP Address Management: 주소 할당, 추적과 회수입니다. - Cilium의 cluster-pool, multi-pool, Kubernetes host-scope, 클라우드별 모드는 할당 주체와 데이터 원본이 다릅니다. 플랫폼 이름이 반드시 별도 `ipam.mode` 값인 것은 아닙니다. **IPsec** 🟣 - Internet Protocol Security: IP 계층 인증·무결성과 적절한 구성의 기밀성을 제공하는 프로토콜 모음입니다. - Cilium은 지원되는 노드 간 트래픽 암호화에 IPsec을 사용하며 키 관리와 경로별 제약을 확인해야 합니다. **Istio** - 서비스 메시를 구현하는 오픈 소스 플랫폼 ## K **Kafka** - 분산 이벤트 스트리밍 플랫폼입니다. 워크로드로 사용할 수 있지만 현재 Cilium에는 이전 Kafka 토픽 L7 정책 API가 없습니다. **kube-proxy** 🟢 - 지원되는 노드 네트워크 방식으로 Service 가상 IP·포트 전달을 구현하는 Kubernetes 구성 요소입니다. - Cilium은 eBPF로 이 기능을 대체할 수 있습니다. XDP 가속은 선택 사항이며 플랫폼이 선택한 구성을 지원해야 합니다. **Kubernetes** - 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하는 오픈 소스 플랫폼 ## L **L2(Layer 2)** - OSI 모델의 데이터 링크 계층 **L3(Layer 3)** - OSI 모델의 네트워크 계층 **L4(Layer 4)** - OSI 모델의 전송 계층 **L7(Layer 7)** - OSI 모델의 애플리케이션 계층 **LoadBalancer** - 트래픽 분산 기능입니다. Kubernetes `type: LoadBalancer`는 컨트롤러·제공자에 구현을 요청하며 해당 구현 없이 외부 로드 밸런서가 보장되지는 않습니다. ## M **MAC(Media Access Control) 주소** - Media Access Control 주소: 인터페이스와 연결된 링크 계층 주소입니다. - 로컬에서 할당하거나 변경할 수 있으므로 고유성과 진위를 무조건 가정하지 않습니다. **mTLS(mutual TLS)** - Mutual TLS: 양쪽 피어가 보통 상대 인증서를 검증해 서로 인증하는 TLS 사용 방식입니다. - 피어 인증과 애플리케이션 인가는 별개입니다. Cilium의 별도 경로 상호 인증과 Beta ztunnel 워크로드 mTLS는 트래픽 보호 방식이 다른 기능입니다. **MTU(Maximum Transmission Unit)** - Maximum Transmission Unit: 링크·인터페이스에서 단편화 없이 운반하는 최대 네트워크 계층 패킷 크기이며 IP 헤더를 포함하고 링크 계층 헤더는 제외합니다. - 경로 MTU는 경로의 제약을 받으며 터널·암호화 오버헤드가 내부 패킷 크기에 영향을 줍니다. TCP MSS나 애플리케이션 페이로드 크기와 다릅니다. ## N **NAT(Network Address Translation)** - IP 패킷의 IP 주소 정보를 수정하는 프로세스 **NodePort** - 할당한 노드 포트를 적격 노드 주소에 사용하는 Kubernetes Service 노출 방식입니다. - 주소 선택, 트래픽 정책, 방화벽과 플랫폼 라우팅이 연결성을 결정하며 NodePort 선언만으로 공인 접근이 보장되지는 않습니다. ## O **OSI(Open Systems Interconnection) 모델** - 네트워크 통신을 7개의 추상 계층으로 분류한 개념적 모델 **Overlay Network** - 기존 네트워크 위에 구축된 가상 네트워크 ## P **Pod** - Kubernetes에서 가장 작은 배포 가능한 컴퓨팅 단위 **Proxy** - 피어 간 통신을 중개하는 구성 요소이며 반드시 별도 물리 서버일 필요는 없습니다. ## R **RBAC(Role-Based Access Control)** - 역할에 따라 시스템 리소스에 대한 액세스를 제어하는 방법 ## S **Service** - 논리적 백엔드 집합에 접근하는 Kubernetes 추상화이며 보통 레이블로 선택한 Pod를 대상으로 합니다. - 일반 ClusterIP Service에는 가상 IP가 있지만 headless Service에는 없습니다. ExternalName은 DNS 별칭이며 선택자 없는 Service는 수동 관리 EndpointSlice를 사용할 수 있습니다. **SNAT(Source Network Address Translation)** - 패킷의 소스 IP 주소를 수정하는 NAT 유형 **Socket** - 네트워크 또는 로컬 프로세스 간 통신에 사용하는 운영체제 통신 엔드포인트입니다. ## T **TCP(Transmission Control Protocol)** - 연결 지향적이고 신뢰할 수 있는 바이트 스트림을 제공하는 전송 프로토콜 **TLS(Transport Layer Security)** - 네트워크를 통한 통신을 보호하는 암호화 프로토콜 ## U **UDP(User Datagram Protocol)** - 비연결형 전송 프로토콜 ## V **VETH(Virtual Ethernet)** - 가상 이더넷 장치로, 일반적으로 쌍으로 생성됨 **VNI(VXLAN Network Identifier)** - VXLAN Network Identifier: VXLAN 헤더의 24비트 필드입니다. - Cilium은 오버레이 메타데이터로 ID 정보를 전달할 수 있으며 필드 폭이 수백만 개의 독립 테넌트 네트워크 구성을 보장하지는 않습니다. **VTEP(VXLAN Tunnel Endpoint)** - VXLAN 패킷의 캡슐화 및 디캡슐화를 담당하는 엔드포인트 **VXLAN(Virtual Extensible LAN)** 🟣 - 레이어 2 네트워크를 레이어 3 네트워크 위에 오버레이하는 네트워크 가상화 기술 - Cilium의 오버레이 네트워킹 모드 중 하나 ## W **WireGuard** 🟣 - Cilium이 지원되는 노드 간 트래픽에 사용하는 VPN 터널 프로토콜입니다. - 동일 노드 Pod 트래픽은 노드 터널로 암호화되지 않으며 외부 트래픽과 선택적 노드 암호화에는 별도 제약이 있습니다. IPsec 대비 성능은 동등한 조건의 측정이 필요합니다. ## X **XDP(eXpress Data Path)** 🟠 - eXpress Data Path: 패킷 처리 훅이며 native XDP는 지원 네트워크 드라이버의 수신 경로에서 실행됩니다. - PASS는 네트워크 스택으로 계속 전달하며 다른 동작으로 드롭·전송·리다이렉트할 수 있습니다. Cilium XDP 가속은 선택 사항이며 보편적 처리량·DDoS 방어 보장이 아닙니다. ## 공식 근거 - [Cilium identities](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/internals/security-identities.rst) - [CiliumIdentity schema](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/pkg/k8s/apis/cilium.io/client/crds/v2/ciliumidentities.yaml) - [Cilium Operator](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/internals/cilium_operator.rst) - [Identity management modes](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/kubernetes/identity-management-mode.rst) - [WireGuard](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/security/network/encryption-wireguard.rst) - [BGP](https://raw.githubusercontent.com/cilium/cilium/v1.20.1/Documentation/network/bgp-control-plane/bgp-control-plane.rst) - [Kubernetes CNI/CRI](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) - [Kubernetes Services](https://kubernetes.io/docs/concepts/services-networking/service/) - [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) - [BPF ring buffer](https://docs.kernel.org/bpf/ringbuf.html) - [ARP / RFC 826](https://www.rfc-editor.org/rfc/rfc826.txt) - [IPv6 Neighbor Discovery / RFC 4861](https://www.rfc-editor.org/rfc/rfc4861.txt) - [VXLAN / RFC 7348](https://www.rfc-editor.org/rfc/rfc7348.txt) - [MAC addressing / RFC 7042](https://www.rfc-editor.org/rfc/rfc7042.txt) ## 퀴즈 [주제 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/cilium/glossary-quiz) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/ ---------------------------------------- # Calico 딥다이브: Kubernetes 네트워킹과 정책 > **검토 기준**: Calico Open Source 3.32.2 · **마지막 업데이트**: 2026년 9월 12일 > Calico 3.32의 공식 시험 대상은 Kubernetes 1.34–1.36입니다. `3.29+ / Kubernetes 1.28+` 전체의 호환성을 보장하는 범위가 아닙니다. ## 개요 Calico는 Kubernetes 네트워킹과 네트워크 정책을 제공하며, 배포 방식과 제품 에디션에 따라 호스트·VM 기능도 제공합니다. 이 시리즈는 아키텍처, 캡슐화와 라우팅, BGP, 정책, eBPF, EKS 통합과 운영을 다룹니다. 출처 없는 성숙도·리소스 순위 대신 [현재 요구사항](https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements)을 기준으로 구성을 선택하세요. ### 2026년 7월: Calico for VMs on Kubernetes Tigera의 [공식 발표](https://www.tigera.io/news/tigera-launches-ebpf-powered-calico-for-vms-on-kubernetes-vm-migration-that-doesnt-require-rebuilding-the-network/) 날짜는 **2026년 7월 23일**입니다. VMware 마이그레이션을 위한 VM·컨테이너 네트워킹, IP 유지, L2 브리지 확장, 정책과 관측성을 설명합니다. 모든 광고 기능이 Calico Open Source에 포함된다는 뜻은 아닙니다. 정확한 에디션·토폴로지·기능 상태를 확인하세요. [Enterprise 3.23 릴리스 노트](https://docs.tigera.io/calico-enterprise/latest/release-notes/)는 KubeVirt live migration을 여전히 tech preview로 표시합니다. 제품 출시 발표와 개별 기능의 지원 상태를 구분해야 합니다. ## 호환성과 기능 범위 - Calico 3.32.2는 2026년 8월 30일에 공개되었습니다. 공식 Kubernetes 시험 대상은 1.34·1.35·1.36이며, Kubernetes 1.37이 출시되었다고 호환성이 입증된 것은 아닙니다. - 일반 Linux 요구사항은 필요한 모듈을 포함한 커널 5.10 이상입니다. 지원 아키텍처·벤더 백포트·개별 기능의 더 높은 요구사항은 eBPF 가이드에서 확인합니다. - Linux 데이터플레인에는 iptables·nftables·eBPF가 있습니다. 기본값은 설치 방식과 플랫폼에 따라 다르며 현재 자체 관리 kubeadm의 operator 설치는 eBPF가 기본일 수 있습니다. 모든 기능이 동일하다고 보장하지 않습니다. - [Calico for Windows](https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/limitations)는 명시된 IPv4 VXLAN·BGP 구성을 지원하지만 Linux eBPF·IPIP·IPv6/dual stack·WireGuard와 모든 Linux 정책 기능을 지원하지는 않습니다. - Open Source에도 정책 tier, Goldmane 플로우 집계와 Whisker UI가 있습니다. DNS/FQDN 정책·애플리케이션 계층 정책 등은 [제품 비교](https://docs.tigera.io/calico/latest/about/calico-product-editions)의 에디션 구분을 확인해야 합니다. ## Calico와 Cilium | 요구사항 | Calico | Cilium | |---|---|---| | Linux 데이터플레인 | 구성에 따라 iptables / nftables / eBPF | eBPF, 해당 L7 기능에는 Envoy 사용 | | Kubernetes NetworkPolicy | 지원, Calico 정책과 tier 확장 | 지원, Cilium 정책 확장 | | L7 / DNS 정책 | Enterprise/Cloud 라이선스와 기능 상태 확인 | HTTP·DNS 정책 제공, 프로토콜별 제약 확인 | | BGP | 해당 네트워킹 모드에서 BIRD 기반 라우팅 | BGP 컨트롤 플레인 광고, 필요한 경로·토폴로지 확인 | | 관측성 | Open Source Goldmane/Whisker와 메트릭, 추가 유료 기능 | Hubble과 메트릭 | | Windows | 상당한 제약이 있는 명시적 구성 지원 | Cilium 1.20 에이전트는 Linux 필요, Windows beta 데이터플레인 아님 | | kube-proxy 대체 | eBPF 데이터플레인에서 가능 | 구성 시 가능 | | 멀티클러스터 / 메시 | 별도 기능·통합이며 에디션에 따라 다름 | Cluster Mesh와 선택적 메시 기능, 설치만으로 모두 활성화되지 않음 | 둘 다 운영 환경의 선택지가 될 수 있습니다. 리소스 사용량과 운영 복잡도는 정책·트래픽·플랫폼·설정에 따라 달라집니다. 대상 환경에서 필요한 기능을 검증하세요. 서로 다른 환경에서 동작한다는 이유로 한 클러스터에 기본 CNI 두 개를 설치하면 안 됩니다. Cilium의 [버전별 요구사항](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/operations/system_requirements.rst)과 이 사이트의 [Cilium 서비스 메시 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/cilium-service-mesh/README.md)에서 플랫폼·메시 범위를 확인할 수 있습니다. ## 아키텍처 컴포넌트는 네트워킹 모드에 따라 달라집니다. 아래 EKS policy-only 예제는 Kubernetes 데이터스토어를 사용하고 BIRD/confd는 사용하지 않습니다. “컨트롤 플레인”은 논리적 역할이며 EKS 관리형 컨트롤 플레인 머신에 배포한다는 뜻이 아닙니다. Typha는 노드별 프로세스가 아니라 별도 Deployment입니다. | 컴포넌트 | 역할과 범위 | |---|---| | Felix | 워크로드 노드에서 정책과 해당 라우트 프로그래밍 | | BIRD / confd | 해당 백엔드를 켰을 때 BGP와 설정 생성, policy-only에는 없음 | | Typha | 선택적 데이터스토어 업데이트 캐시·분배, operator가 규모에 맞춰 복제 수 관리, 항상 3개가 아님 | | kube-controllers | Kubernetes 리소스 조정·동기화·정리 | | Calico CNI / IPAM | Calico가 네트워킹을 맡을 때 인터페이스·Pod 주소 관리, EKS 예제에서는 Amazon VPC CNI/IPAM이 담당 | | Calico API server | 기본 모델에서 내부 CRD 위에 `projectcalico.org/v3` 집계 API 제공, native v3 CRD는 별도의 tech preview | [아키텍처 레퍼런스](https://docs.tigera.io/calico/latest/reference/architecture/overview)와 실제 렌더링된 워크로드로 활성 컴포넌트를 확인하세요. 이 가이드는 Kubernetes API 데이터스토어를 사용하며 etcd 기반 설계에는 별도의 설치·기능 제약이 있습니다. ## 네트워킹 모드와 MTU | 모드 | 캡슐화와 라우팅 | MTU 1500인 IPv4 언더레이의 Pod MTU 예 | |---|---|---| | IPIP | IPv4-in-IPv4, 일반적으로 BGP로 라우트 배포 | 1480 | | VXLAN | 기본 UDP 4789, VXLAN Pod 라우팅에는 BGP가 필수가 아님 | 1450 | | 비캡슐화 | 언더레이에서 Pod 주소 라우팅 필요, BGP는 경로 배포 방법 중 하나 | 1500 | | CrossSubnet | 노드 서브넷을 넘을 때만 캡슐화하는 IPIP 또는 VXLAN 설정 | 터널이 필요한 경로의 오버헤드를 여전히 확보해야 함 | 이 MTU는 예시이며 고정 상수가 아닙니다. IPv6 VXLAN 오버헤드·점보 언더레이·WireGuard·클라우드 경로 한도에 따라 달라집니다. IPIP는 IPv4 전용이며 IPIP가 부적합한 환경에서도 IPv4 VXLAN을 사용할 수 있습니다. [MTU 설정](https://docs.tigera.io/calico/latest/networking/configuring/mtu)과 [오버레이 요구사항](https://docs.tigera.io/calico/latest/networking/configuring/vxlan-ipip)을 확인하세요. BGP가 가능하다고 언더레이 모든 홉의 Pod CIDR 라우팅이 보장되지는 않으며, 라우팅된 비캡슐화 패브릭에 동일 L2 인접성이 항상 필요한 것도 아닙니다. 언더레이·포트·주소 패밀리·플랫폼을 먼저 계획해야 합니다. ## EKS: Amazon VPC CNI를 유지하고 Calico 정책 추가 기존의 지원되는 Amazon VPC CNI를 사용하는 Linux EC2 노드용 예제이며 Pod 네트워킹을 교체하지 않습니다. Auto Mode나 Fargate 설치 절차는 아닙니다. [공식 EKS 가이드](https://docs.tigera.io/calico/latest/getting-started/kubernetes/managed-public-cloud/eks)의 조건은 다음과 같습니다. 1. Calico를 정책 엔진으로 선택하기 전에 Amazon VPC CNI의 기본 네트워크 정책 강제를 비활성화해야 합니다. 둘을 함께 실행하면 충돌합니다. 보호 중인 기존 클러스터에서는 무방비 전환 구간이 생기지 않도록 정책 인계를 계획하고 검증합니다. 2. VPC CNI에 `ANNOTATE_POD_IP=true`를 설정하고 `aws-node` ServiceAccount에 Pod `patch` 권한을 부여합니다. 조정 루프가 되돌리지 않도록 설치된 애드온·설정 소유자를 통해 관리합니다. 아래 추가 RBAC 예제를 적용하기 전에 실제 ServiceAccount 이름을 확인하세요. 3. `ENABLE_V4_EGRESS=true`인 IPv6 Pod의 강제를 보장하면 안 됩니다. Calico EKS 가이드는 이 조합을 명시적으로 제외합니다. 4. 아래 설치 방식 중 **하나만** 선택합니다. 새 설치용이며 기존 operator의 소유권을 가져오거나 활성 CNI를 마이그레이션하는 명령이 아닙니다. ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: calico-vpc-cni-pod-ip-patch rules: - apiGroups: [""] resources: ["pods"] verbs: ["patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: calico-vpc-cni-pod-ip-patch roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: calico-vpc-cni-pod-ip-patch subjects: - kind: ServiceAccount name: aws-node namespace: kube-system ``` ### 방식 A: 버전을 고정한 operator 매니페스트 ```bash set -euo pipefail CALICO_VERSION=v3.32.2 kubectl create -f "https://raw.githubusercontent.com/projectcalico/calico/$CALICO_VERSION/manifests/v1_crd_projectcalico_org.yaml" kubectl create -f "https://raw.githubusercontent.com/projectcalico/calico/$CALICO_VERSION/manifests/tigera-operator.yaml" kubectl -n tigera-operator rollout status deployment/tigera-operator --timeout=300s kubectl apply -f - <<'YAML' apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: kubernetesProvider: EKS cni: type: AmazonVPC calicoNetwork: bgp: Disabled linuxDataplane: Iptables --- apiVersion: operator.tigera.io/v1 kind: APIServer metadata: name: default spec: {} YAML ``` ### 방식 B: 버전을 고정한 Helm 설치 동일한 VPC CNI 사전 조건을 먼저 충족합니다. Calico 3.32는 CRD 설치와 operator 차트를 분리하므로 새 클러스터에서 작은 operator 차트만 설치해서는 충분하지 않습니다. 아래 값을 `calico-eks-values.yaml`로 저장합니다. ```yaml installation: kubernetesProvider: EKS cni: type: AmazonVPC calicoNetwork: bgp: Disabled linuxDataplane: Iptables apiServer: enabled: true ``` ```bash set -euo pipefail helm repo add projectcalico https://docs.tigera.io/calico/charts helm repo update projectcalico helm template calico-crds projectcalico/crd.projectcalico.org.v1 --version v3.32.2 | kubectl apply --server-side -f - helm install calico projectcalico/tigera-operator --version v3.32.2 --namespace tigera-operator --create-namespace -f calico-eks-values.yaml ``` 이 버전의 차트는 Goldmane와 Whisker도 기본으로 활성화합니다. 렌더링한 매니페스트에서 해당 컴포넌트와 접근 통제를 검토하세요. native `projectcalico.org/v3` CRD는 별도의 tech preview이며 여기서는 기존 내부 CRD와 집계 API 서버를 사용합니다. ### 설치를 확인한 뒤 정책 동작 검증 ```bash kubectl get tigerastatus kubectl -n calico-system get pods -o wide kubectl -n calico-system rollout status daemonset/calico-node --timeout=300s kubectl wait --for=condition=Available apiservice/v3.projectcalico.org --timeout=300s kubectl get felixconfigurations.projectcalico.org ``` Degraded·Progressing 상태를 확인하고 임시 워크로드로 허용·거부 흐름을 모두 시험한 뒤 정책 강제를 신뢰하세요. Ready DaemonSet만으로 정책이 입증되지는 않습니다. AmazonVPC policy-only에서는 AWS가 Pod IPAM과 네트워킹을 제공하므로 Calico IPPool이 없거나 BIRD 세션이 없는 것이 반드시 장애는 아닙니다. ### Calico 전체 네트워킹과 다른 설치 방법 EKS에서 Calico가 전체 네트워킹을 맡는 구성은 별도의 새 클러스터 설계입니다. 공식 절차는 워크로드 노드가 없는 상태에서 시작해 CNI를 설정한 뒤 노드를 추가합니다. 실행 중인 VPC CNI 클러스터에 `cni.type: Calico` 조각을 덮어 적용하지 마세요. [EKS 통합](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md)과 공식 EKS 절차를 참고하세요. CNI가 아직 없는 자체 관리 클러스터에는 [온프레미스 가이드](https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises)를 사용합니다. 직접 매니페스트도 대안이지만 네임스페이스·Typha 구성·수명주기가 operator 설치와 다릅니다. Helm·operator·`calico.yaml`을 겹치지 말고 소유자를 하나 선택하세요. ## 적용 범위를 명시한 정책 예제 전용 `calico-demo` 네임스페이스를 사용합니다. 아래 ingress·egress 예제는 해당 네임스페이스만 선택하며 클러스터 전체의 제로 트러스트 전환 절차가 아닙니다. 기존 Calico tier와 앞선 정책이 결과에 영향을 줄 수 있습니다. ```yaml apiVersion: v1 kind: Namespace metadata: name: calico-demo --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: calico-demo spec: podSelector: matchLabels: app: backend policyTypes: [Ingress] ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080 ``` peer의 `podSelector`는 **같은 네임스페이스**의 frontend Pod를 뜻합니다. 사용자 인증·같은 네임스페이스 전체 통신 허용·egress 정책 설정을 의미하지 않습니다. 다음 별도 예제는 데모 네임스페이스의 egress를 선택된 CoreDNS Pod의 UDP/TCP 53으로 제한하고 나머지를 거부합니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: calico-demo-dns-only spec: namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: all() order: 100 types: [Egress] egress: - action: Allow protocol: UDP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Deny ``` 실제 DNS 엔드포인트와 라벨을 먼저 확인합니다. 이 selector 예제는 일반 CoreDNS Pod용이며 NodeLocal DNSCache나 Auto Mode 시스템 리졸버용 정책이 아닙니다. 53번 포트만으로 승인된 DNS 서버를 식별할 수는 없습니다. 애플리케이션 egress도 필요하면 마지막 Deny를 켜기 전에 명시적인 허용을 설계하고 시험하세요. 뒤의 별도 Allow는 이미 매칭된 앞선 Calico Deny를 뒤집지 못합니다. ### FQDN 정책은 에디션별 기능 Calico Enterprise/Cloud DNS 정책의 `destination.domains` 필드는 **Open Source 3.32.2 NetworkPolicy 스키마에 없습니다**. 이 Open Source 설치에 적용하면 안 됩니다. 해당 제품을 사용하는 배포에서는 [도메인 기반 정책 가이드](https://docs.tigera.io/calico-enterprise/latest/network-policy/domain-based-policy)에 따라 신뢰하는 DNS 서버와 DNS 허용 경로를 구성하세요. `*.amazonaws.com`은 광범위한 허용이며 특정 AWS 계정이나 서비스의 인증이 아닙니다. DNS-IP 기반 허용은 HTTP Host나 TLS 신원 검증과도 다릅니다. ## 모니터링과 상태 확인 ```yaml apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: prometheusMetricsEnabled: true prometheusMetricsPort: 9091 ``` Felix 메트릭은 기본 비활성입니다. 리스너를 켠다고 Prometheus scrape job이 만들어지거나 공개 노출이 안전해지는 것은 아닙니다. [메트릭 가이드](https://docs.tigera.io/calico/latest/operations/monitor/monitor-component-metrics)에 따라 사설 디스커버리와 접근 통제를 구성하세요. `flowLogsFileEnabled`는 Open Source FelixConfiguration 필드가 아닙니다. Enterprise 파일 로그 설정을 복사하지 말고 지원되는 [Goldmane/Whisker 플로우 로그 경로](https://docs.tigera.io/calico/latest/observability/view-flow-logs)를 사용합니다. | 메트릭 | 의미 | |---|---| | `felix_active_local_endpoints` | 활성 로컬 workload·host 엔드포인트 | | `felix_active_local_policies` | 이 노드의 엔드포인트에 활성인 정책 | | `felix_iptables_rules` | 활성 iptables 규칙, 데이터플레인에 따라 적용 | | `felix_int_dataplane_failures` | 재시도할 데이터플레인 업데이트 실패 | | `felix_cluster_num_hosts` | Felix가 보는 클러스터 전체 호스트 수, 모든 Felix 값 합산 금지 | | `typha_connections_accepted` | 누적 수락 연결 수, 현재 연결 수가 아님 | | `typha_connections_active` | 현재 열린 클라이언트 연결 수 | [Felix](https://docs.tigera.io/calico/latest/reference/felix/prometheus)와 [Typha](https://docs.tigera.io/calico/latest/reference/typha/prometheus) 레퍼런스를 참고하세요. 컴포넌트 상태·설정 메트릭이며 보편적인 거부 패킷 카운터가 아닙니다. Felix 상태 서버의 기본값은 localhost:9099이고 Typha는 활성화 시 일반적으로 9098을 사용합니다. 실제 배포의 probe부터 확인하세요. 노트북에서 `curl localhost`를 실행해도 노드 상태 서버를 검사하는 것은 아닙니다. ## 트러블슈팅 ```bash kubectl -n calico-system get pods -o wide kubectl -n calico-system logs -l k8s-app=calico-node -c calico-node --tail=100 kubectl get installations.operator.tigera.io default -o yaml kubectl get networkpolicies.networking.k8s.io -A kubectl get networkpolicies.projectcalico.org -A kubectl get globalnetworkpolicies.projectcalico.org kubectl get ippools.projectcalico.org -o wide ``` Operator는 일반적으로 `calico-system`, 직접 매니페스트는 `kube-system`을 사용할 수 있습니다. Kubernetes와 Calico NetworkPolicy를 구분하도록 전체 API 리소스 이름을 사용하세요. `kubectl get nodes ...status.conditions`는 Calico 라우팅 상태 명령이 아닙니다. BIRD 상태 명령은 BGP가 켜진 경우에만 적용되고, `calicoctl node status`는 임의의 관리자 노트북이 아니라 적절한 Calico 노드 환경이 필요합니다. | 증상 | 설정 변경 전에 확인할 사항 | |---|---| | Pod IP 없음 | 먼저 IPAM 소유자 확인, policy-only EKS는 VPC CNI 로그·용량, 그 외는 Calico IPAM | | 노드 간 실패 | 경로·언더레이/방화벽 허용·MTU·선택한 캡슐화, 무작정 터널을 켜면 장애 악화 가능 | | 정책 불일치 | 엔드포인트 라벨·네임스페이스·방향·tier/order·기존 정책·실제 데이터플레인 | | 높은 CPU | 트래픽/규칙 규모와 메트릭·프로파일 근거, eBPF 전환은 즉석 만능 해결책이 아닌 계획된 변경 | 필요할 때만 일치하는 버전의 [calicoctl](https://docs.tigera.io/calico/latest/reference/calicoctl/)을 사용하고 실제 운영체제·CPU 아키텍처와 릴리스 파일을 검증하세요. BGP나 Calico IPAM 상태가 없다는 이유만으로 policy-only 장애를 단정하면 안 됩니다. ## 딥다이브 목차 | 파트 | 주제 | |---|---| | [1](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/01-introduction.md) | 소개·프로젝트 이력·실습 준비 | | [2](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/02-architecture.md) | 컴포넌트·데이터스토어·패킷 흐름 | | [3](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md) | 캡슐화·직접 라우팅·MTU | | [4](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md) | BGP·라우트 리플렉터·외부 연동 | | [5](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md) | NetworkPolicy·tier·정책 설계 | | [6](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/06-ebpf-dataplane.md) | eBPF 설정·제약·트러블슈팅 | | [7](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md) | 고급 네트워킹·보안 주제 | | [8](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md) | EKS·VPC CNI 통합 | | [9](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/09-operations.md) | 운영·진단 | | [용어집](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/glossary.md) | 용어 정리 | [Calico 소개 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/01-introduction-quiz) · [공식 문서](https://docs.tigera.io/calico/latest/about/) · [3.32.2 릴리스](https://github.com/projectcalico/calico/releases/tag/v3.32.2) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/01-introduction ---------------------------------------- # Part 1: Calico 소개 및 기본 개념 > **검토 기준**: Calico Open Source 3.32.2, kind 0.33.0, Kubernetes 1.36.4 > **마지막 업데이트**: 2026년 9월 12일. Calico 3.32의 공식 Kubernetes 시험 대상은 1.34–1.36입니다. ## 실습 환경 iptables·VXLAN·Calico IPAM을 명시적으로 선택한 폐기 가능한 로컬 실습입니다. 기존 CNI를 교체하거나 EKS를 구성하는 절차가 아닙니다. 감사에서는 공개 파일과 설정을 확인했지만 클러스터 생성이나 실제 트래픽 시험은 하지 않았습니다. | 도구·환경 | 요구사항 | |---|---| | kind | 0.33.0, 고정되지 않은 기본값 대신 아래 1.36.4 이미지 사용 | | Docker | kind가 지원하는 정상 런타임과 노드 3개를 수용할 용량 | | 노드 OS | [Calico 요구사항](https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements)을 충족하는 Linux 커널·모듈, macOS에서는 컨테이너 VM의 커널 | | kubectl | API 서버 1.36과 마이너 차이 1 이내, 동일한 1.36 클라이언트 사용이 간편 | | calicoctl | 선택적인 동일 버전 3.32.2 클라이언트, CLI 호스트의 실제 OS·아키텍처 선택 | | curl / Python 3 | 아래 선택적 클라이언트 다운로드·SHA-256 검증 | | Helm | [개요](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md)의 대체 설치 방법에 사용, 이 실습에는 불필요 | [Kubernetes 버전 차이 정책](https://kubernetes.io/releases/version-skew-policy/)은 임의의 `kubectl 1.28+`가 이후 모든 서버와 호환됨을 뜻하지 않습니다. 실습 생성 전에 Pod·Service CIDR과 컨테이너 네트워크·호스트 LAN·VPN의 충돌을 확인하세요. ### 선택 사항: 일치하는 calicoctl 플랫폼 하나를 선택하고 해당 릴리스 파일의 공개 digest를 검증한 뒤 실습 디렉터리에 둡니다. 전역 설치나 홈 디렉터리 설정 변경은 필요하지 않습니다. ```bash set -euo pipefail CALICO_VERSION=v3.32.2 case "$(uname -s)" in Linux) CALICO_OS=linux ;; Darwin) CALICO_OS=darwin ;; *) echo "Select a supported calicoctl OS" >&2; exit 1 ;; esac case "$(uname -m)" in x86_64|amd64) CALICO_ARCH=amd64 ;; aarch64|arm64) CALICO_ARCH=arm64 ;; *) echo "Select a supported calicoctl architecture" >&2; exit 1 ;; esac CALICO_ASSET="calicoctl-$CALICO_OS-$CALICO_ARCH" curl --fail --location --retry 3 \ "https://api.github.com/repos/projectcalico/calico/releases/tags/$CALICO_VERSION" \ --output calico-release.json curl --fail --location --retry 3 \ "https://github.com/projectcalico/calico/releases/download/$CALICO_VERSION/$CALICO_ASSET" \ --output calicoctl python3 - "$CALICO_ASSET" <<'PY' import hashlib import json import pathlib import sys release = json.loads(pathlib.Path("calico-release.json").read_text()) if release["tag_name"] != "v3.32.2": raise SystemExit("Unexpected release") asset = next(a for a in release["assets"] if a["name"] == sys.argv[1]) expected = asset.get("digest") or "" actual = "sha256:" + hashlib.sha256(pathlib.Path("calicoctl").read_bytes()).hexdigest() if not expected.startswith("sha256:") or actual != expected: raise SystemExit("Digest mismatch or missing published digest") print("Verified", asset["name"], actual) PY chmod +x calicoctl ./calicoctl --help ``` 실습 데이터스토어 설정 후 `./calicoctl version`으로 클라이언트·클러스터 정보를 확인합니다. 문서화된 `version` 명령에는 `--client` 옵션이 없습니다. 집계 API 서버가 준비되면 `kubectl`로도 Calico 리소스를 관리할 수 있으며 모든 작업에 calicoctl이 필수인 것은 아닙니다. ### 별도의 kind 클러스터 생성 사용하지 않는 클러스터 이름과 새로운 로컬 kubeconfig를 사용합니다. [kind 0.33.0 릴리스](https://github.com/kubernetes-sigs/kind/releases/tag/v0.33.0)에 Calico 시험 대상 마이너 범위의 1.36.4 이미지가 공개되어 있습니다. 감사에서는 레지스트리 digest·amd64/arm64 매니페스트를 확인했고 노드 이미지 레이어는 내려받지 않았습니다. ```bash set -euo pipefail CALICO_LAB_KUBECONFIG="$PWD/calico-lab.kubeconfig" test ! -e "$CALICO_LAB_KUBECONFIG" cat > kind-calico.yaml <<'YAML' kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: disableDefaultCNI: true kubeProxyMode: iptables podSubnet: 10.244.0.0/16 nodes: - role: control-plane - role: worker - role: worker YAML kind create cluster --name calico-lab --config kind-calico.yaml \ --kubeconfig "$CALICO_LAB_KUBECONFIG" \ --image kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed export KUBECONFIG="$CALICO_LAB_KUBECONFIG" export DATASTORE_TYPE=kubernetes kubectl config current-context kubectl cluster-info ``` CNI 설치 전에는 노드와 일반 Pod가 준비되지 않을 수 있습니다. 이를 없애려고 다른 CNI를 설치하지 마세요. Pod CIDR이 충돌하면 클러스터 생성 전에 kind와 Installation 양쪽 값을 바꿉니다. ```bash CALICO_VERSION=v3.32.2 kubectl create -f "https://raw.githubusercontent.com/projectcalico/calico/$CALICO_VERSION/manifests/v1_crd_projectcalico_org.yaml" kubectl create -f "https://raw.githubusercontent.com/projectcalico/calico/$CALICO_VERSION/manifests/tigera-operator.yaml" kubectl -n tigera-operator rollout status deployment/tigera-operator --timeout=300s kubectl apply -f - <<'YAML' apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: kubernetesProvider: Kind cni: type: Calico calicoNetwork: linuxDataplane: Iptables bgp: Disabled ipPools: - cidr: 10.244.0.0/16 blockSize: 26 encapsulation: VXLAN natOutgoing: Enabled nodeSelector: all() --- apiVersion: operator.tigera.io/v1 kind: APIServer metadata: name: default spec: {} YAML kubectl get tigerastatus kubectl -n calico-system get pods -o wide ``` operator가 관리하는 워크로드가 생성되기를 기다린 뒤 rollout과 상태를 확인합니다. 빈 라벨 선택 결과나 컨트롤러 하나의 Available만으로 전체 노드 네트워킹이 동작한다고 판단하면 안 됩니다. ```bash kubectl -n calico-system rollout status daemonset/calico-node --timeout=300s kubectl -n calico-system rollout status deployment/calico-kube-controllers --timeout=300s kubectl wait --for=condition=Available apiservice/v3.projectcalico.org --timeout=300s kubectl wait --for=condition=Ready nodes --all --timeout=300s kubectl get ippools.projectcalico.org -o wide kubectl get installations.operator.tigera.io default -o yaml # Optional, if the matching local client was downloaded: ./calicoctl version ./calicoctl get nodes ``` 여기서는 BGP를 끄므로 BIRD 세션과 `calicoctl node status`는 준비 상태 기준이 아닙니다. 해당 명령에는 노트북 kubeconfig만이 아니라 적절한 노드 환경도 필요합니다. CSI·Typha 복제 수는 고정이 아니므로 실제 컴포넌트 수를 확인하세요. 임시 워크로드로 Pod·Service·DNS 연결성과 정책의 허용·거부 흐름을 모두 점검합니다. ## Calico가 제공하는 기능 Calico는 Kubernetes 네트워킹·IPAM·정책 강제를 결합합니다. policy-only 통합에서는 다른 CNI가 네트워킹과 IPAM을 유지합니다. 기능은 운영체제·데이터플레인·에디션에 따라 다르며 플랫폼 목록이 동일 동작을 보장하지는 않습니다. ![Calico의 네트워킹·정책·관측성·IPAM 기능 개념도.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-01-introduction-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-01-introduction-0.html) 기능 범위의 개념도이며 성능 보장이 아닙니다. EKS policy-only에서는 VPC CNI가 네트워킹과 IPAM을 담당합니다. ## 프로젝트 이력과 거버넌스 Project Calico는 2014년 Metaswitch에서 시작했으며 2016년에 설립된 Tigera가 주요 유지관리자입니다. 아래 릴리스 기록은 기존의 3.0·3.29 연도를 바로잡고 초기 eBPF preview와 이후 기능 제공을 구분합니다. | 날짜 | 공식 릴리스 기록 | |---|---| | 2017년 12월 21일 | [Calico 3.0.0](https://github.com/projectcalico/calico/releases/tag/v3.0.0), 설치 권장이 아닌 과거 릴리스 | | 2020년 2월 25일 | [eBPF 소개](https://www.tigera.io/blog/introducing-the-calico-ebpf-dataplane/), GA가 아닌 **3.13용 tech preview** | | 2024년 10월 29일 | [Calico 3.29.0](https://github.com/projectcalico/calico/releases/tag/v3.29.0) | | 2026년 8월 30일 | [Calico 3.32.2](https://github.com/projectcalico/calico/releases/tag/v3.32.2), 이번 검토 기준 | 기존의 “eBPF 완전 동등성”과 “Windows eBPF” 주장은 잘못된 내용입니다. 현재 [Windows 제약](https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/limitations)도 Linux eBPF·IPIP·IPv6/dual stack·WireGuard를 제외합니다. Calico는 Apache-2.0 라이선스이며 Tigera와 커뮤니티가 유지관리합니다. CNCF Landscape 등재는 CNCF 소유·인큐베이션·졸업을 뜻하지 않습니다. Enterprise는 상용 자체 관리 제품, Cloud는 SaaS이며 Open Source가 소규모나 비운영 클러스터에만 한정되지는 않습니다. ![Calico 생태계와 상용 제품의 관계.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-01-introduction-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-01-introduction-1.html) CNCF 상자는 Landscape·생태계 참여만 뜻합니다. Tigera는 상용 제품뿐 아니라 오픈소스도 유지관리하며 그림의 그룹 구성이 CNCF의 관리 권한을 뜻하지는 않습니다. ## 핵심 기능 ### 1. 네트워킹과 데이터플레인 캡슐화와 구현은 별개의 선택입니다. Calico는 IPIP·VXLAN·라우팅된 언더레이를 사용할 수 있습니다. CrossSubnet은 IPIP·VXLAN의 조건부 설정이며 WAN 연결 서비스가 아닙니다. Linux 데이터플레인에는 iptables·nftables·eBPF가 있습니다. eBPF는 **커널 안에서** 실행되며 기존 패킷 처리 경로 일부를 우회할 수 있지만 커널 자체를 우회하지는 않습니다. 비캡슐화는 언더레이에 필요한 Pod 경로가 있을 때 터널 헤더를 없애며 모든 부하의 최저 지연을 보장하지 않습니다. ### 2. Kubernetes와 Calico 정책 Kubernetes NetworkPolicy는 네임스페이스 범위이며 허용을 합산합니다. Calico는 명시적 action·정렬된 정책·tier를 추가하며 Open Source도 tier를 제공합니다. GlobalNetworkPolicy는 클러스터 범위 리소스지만 한 네임스페이스만 선택할 수 있습니다. HostEndpoint는 보호할 호스트 엔드포인트이며 NetworkPolicy 아래의 세 번째 정책 종류나 고정 계층이 아닙니다. 다음은 전용 네임스페이스의 **서로 독립적인 예제**입니다. 기존 tier·우선 정책을 고려해야 하며 완전한 보안 기준선은 아닙니다. ```yaml apiVersion: v1 kind: Namespace metadata: name: calico-demo --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress namespace: calico-demo spec: podSelector: {} policyTypes: [Ingress] ingress: [] ``` ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: calico-demo-trusted-ingress spec: namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: app == 'backend' order: 100 types: [Ingress] ingress: - action: Allow protocol: TCP source: namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: trusted == 'true' destination: ports: [8080] - action: Deny ``` Calico 예제는 데모 네임스페이스의 매칭 엔드포인트에서 선택된 backend의 TCP 8080을 허용하고 다른 ingress를 거부합니다. `trusted`는 암호학적 신원이 아니므로 라벨 변경 권한을 통제하세요. 두 예제 모두 egress·DNS를 설정하지 않습니다. CIDR·포트 규칙을 지원하지만 큰 사설 CIDR은 신원 경계가 아닙니다. DNS/FQDN·애플리케이션 계층 정책은 해당 Enterprise/Cloud 기능이 필요합니다. [에디션 표](https://docs.tigera.io/calico/latest/about/calico-product-editions)를 확인하세요. ### 3. IP 주소 관리 Calico가 IPAM을 맡으면 pool·block으로 주소를 할당합니다. IPv4 /26 block은 주소 64개이며 모든 플랫폼에서 Pod 주소 64개를 보장하지는 않습니다. Windows 예약 주소와 IPv6의 다른 기본값을 고려해야 합니다. VPC CNI policy-only에서는 AWS가 IPAM을 맡습니다. 아래는 [IPPool API](https://docs.tigera.io/calico/latest/reference/resources/ippool) 예입니다. **operator 관리 pool과 중첩되게 추가 생성하지 마세요.** kind 실습에는 이미 pool이 있으며 캡슐화·IPAM 변경은 별도로 계획할 실습입니다. ```yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: example-ipv4-pool spec: cidr: 10.244.0.0/16 blockSize: 26 ipipMode: Never vxlanMode: Always natOutgoing: true nodeSelector: all() ``` 중첩되지 않는 여러 pool과 node selector로 할당을 구분할 수 있습니다. `natOutgoing`은 일반적으로 Calico pool 밖으로 나가는 트래픽에 적용되며 방화벽·암호화 설정이 아닙니다. 직접 라우팅이나 CrossSubnet만으로 언더레이 설계 없이 사이트를 연결할 수는 없습니다. ### 4. BGP 라우팅 BGP는 경로를 배포하며 애플리케이션 패킷이 BIRD 프로세스를 통과하거나 BGP로 암호화되는 것은 아닙니다. 직접 라우팅을 지원하거나 IPIP와 함께 사용할 수 있으며 full mesh·라우트 리플렉터·외부 peer는 토폴로지별 선택입니다. 다음은 BGP를 끈 kind 예제가 아닌 **별도 라우팅 실습용**입니다. 문서용 주소·ASN·노드 라벨을 설계한 토폴로지와 대응 라우터 설정으로 바꾸세요. 대체 경로 배포가 동작하기 전에 노드 mesh를 끄면 안 됩니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info nodeToNodeMeshEnabled: true asNumber: 64512 --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: example-rack-tor spec: peerIP: 192.0.2.1 asNumber: 64513 nodeSelector: rack == 'rack-1' ``` BGPPeer는 세션 인증에 `password.secretKeyRef`를 지원합니다. Secret은 Calico 노드 컴포넌트의 네임스페이스에 두고 라우터의 자격 증명도 맞춰야 합니다. 워크로드 트래픽 암호화는 아닙니다. Service CIDR 광고·mesh 제거에는 추가 시험이 필요합니다. [BGP 심층 분석](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md)을 참고하세요. ### 5. 플랫폼과 확장 범위 | 환경 | 범위 | |---|---| | EKS | VPC CNI + Calico 정책은 통합 중 하나, 전체 Calico CNI는 별도의 새 클러스터 설계 | | AKS | 제공자의 지원 CNI·정책 조합과 현재 설치 절차 확인 | | GKE | Dataplane V2는 **Cilium**, Calico는 해당 legacy 구성에 적용하며 V2 위에 설치하지 않음 | | 자체 관리 Kubernetes | 배포판·커널·CNI 소유권·경로·권한 확인 | | Windows | 명시된 IPv4 구성, Linux eBPF·IPIP·IPv6/dual stack·WireGuard 동등성 없음 | | 호스트 / VM | 별도 설치·기능 조건, KubeVirt·Enterprise 상태는 기본 호스트 보호와 다름 | [GKE 문서](https://cloud.google.com/kubernetes-engine/docs/concepts/dataplane-v2)는 V2의 Cilium과 legacy Calico 경로를 명시적으로 구분합니다. Typha는 별도 Pod 집합에서 업데이트를 캐시·분배해 Felix의 직접 데이터스토어 watch를 줄입니다. 복제 3개는 예일 뿐 보편적인 최소값이 아닙니다. 용량은 정책·엔드포인트·Service 변경률·하드웨어·데이터스토어·데이터플레인에 좌우됩니다. 이 소개 문서에는 “5,000노드 / 100,000 Pod / 수백만 규칙”이라는 고정 한도의 재현 가능한 근거가 없습니다. ## Calico·kube-proxy·성능 kube-proxy는 Service 전달을 구현하며 CNI 네트워킹이나 NetworkPolicy 엔진이 아닙니다. 이 실습처럼 Calico 표준 데이터플레인은 kube-proxy와 함께 동작할 수 있고 eBPF는 구성 시 Service 처리를 대체할 수 있습니다. | 관심사 | 비교 대상 | |---|---| | Pod 네트워킹·IPAM | 같은 토폴로지의 CNI·IPAM 구현 | | Service 전달 | 선택한 kube-proxy 백엔드 또는 eBPF 대체 구현 | | 정책 | 동등한 규칙과 강제 범위 | | 규모 | Service·엔드포인트·selector·변경률·연결 재사용 | | CPU·메모리·지연 | 하드웨어·커널·버전·부하·준비 구간·반복·오류 | kube-proxy는 iptables 전용이 아니며 현재 Kubernetes에는 nftables와 버전별 legacy 백엔드도 있습니다. IP set 조회가 Calico 전체 패킷 경로를 O(1)로 만들지는 않습니다. iptables Service NAT의 첫 선택과 이후 패킷의 conntrack 빠른 경로도 다릅니다. 기존의 출처 없는 1,000노드/50,000 Pod 규칙 수·지연·메모리 예제는 재현 가능한 벤치마크가 아니므로 용량 산정에 사용할 수 없습니다. 전통적인 VM 네트워크도 자동화·분산 구성이 가능합니다. Calico의 선언적 정책이 무제한 IP 용량이나 초 단위 수렴 보장을 뜻하지는 않습니다. ## 배포 시나리오 - **온프레미스**: Pod 경로·BGP peer/필터·반환 경로·호스트 보호를 조정합니다. 캡슐화만 끈다고 언더레이 경로가 생기지는 않습니다. - **EKS**: AWS 네트워킹을 유지하려면 `cni.type: AmazonVPC`를 선택하고 정책 엔진 소유권·Pod IP annotation 등 [검토된 개요](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md)를 따릅니다. EKS Installation을 이 Kind 실습에 적용하거나 정책 엔진 둘을 실행하지 마세요. - **하이브리드·멀티클러스터**: 연결성·디스커버리·정책 관리는 별개입니다. CrossSubnet IPPool은 VPN·공유 신원·클러스터 간 디스커버리를 만들지 않습니다. 해당 cluster mesh·멀티클러스터 제품 기능과 언더레이를 별도로 평가해야 하며 “Calico Federation”이 보편적인 기본 연결은 아닙니다. - **규제 대상 워크로드**: Enterprise/Cloud가 보고서·로그·보안 기능을 추가할 수 있지만 설치만으로 규정 준수가 성립하지 않습니다. API 감사 로그는 API 변경, 플로우 로그는 네트워크 관측을 기록하며 모든 정책 결정이 자동으로 기록되지는 않습니다. WireGuard는 지원되는 Open Source Linux 구성에도 있습니다. ## 커뮤니티와 소스 개발 현재 Slack·모임 링크는 [커뮤니티 페이지](https://www.tigera.io/project-calico/community/), 재현 가능한 보고는 [이슈 트래커](https://github.com/projectcalico/calico/issues), 기여 절차는 [기여 가이드](https://github.com/projectcalico/calico/blob/v3.32.2/CONTRIBUTING.md)를 사용합니다. 날짜 없는 격주 일정이나 오래된 포럼 주소가 현재도 유효하다고 가정하지 마세요. 소스 학습용 [개발자 가이드](https://github.com/projectcalico/calico/blob/v3.32.2/DEVELOPER_GUIDE.md)는 Linux·Docker·git·make 환경과 컴포넌트별 시험을 설명합니다. 루트의 `make dev-environment` target은 없습니다. 아래 선택적 소스 작업은 네트워킹 실습과 별개이며 감사에서 실행하지 않았습니다. ```bash git clone --depth 1 --branch v3.32.2 https://github.com/projectcalico/calico.git calico-source-study cd calico-source-study # Read prerequisites and the selected component's Makefile before running tests. cat DEVELOPER_GUIDE.md make -C calicoctl test ``` Open Source는 실습뿐 아니라 운영 환경에도 커뮤니티 지원 네트워킹·정책을 제공합니다. Enterprise는 상용 기능·지원을 추가하고 Cloud는 SaaS 관리를 제공합니다. 단순한 “소규모 대규모” 구분 대신 [기능 표](https://docs.tigera.io/calico/latest/about/calico-product-editions)로 선택하세요. ## 폐기 가능한 실습 정리 결과 보관 후 이번 실습에서 만든 `calico-lab`만 `kind delete cluster --name calico-lab`으로 삭제합니다. 관련 없는 클러스터와 kubeconfig는 유지하세요. EKS 삭제 절차가 아닙니다. [다음: Calico 아키텍처](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/02-architecture.md) · [Calico 개요](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) · [소개 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/01-introduction-quiz) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/02-architecture ---------------------------------------- # Part 2: 아키텍처 > **검토 기준**: Calico Open Source 3.32.2 / operator 1.42.6, Calico 3.32의 Kubernetes 시험 대상은 1.34–1.36입니다. > **마지막 업데이트**: 2026년 9월 12일. 예제는 설정 참고이며 실제 클러스터 검증 결과가 아닙니다. ## 개요 Calico의 아키텍처는 확장성, 성능, 유연성을 중심으로 설계되었습니다. 이 장에서는 각 컴포넌트의 역할, 내부 동작 방식, 그리고 컴포넌트 간 상호작용을 심층적으로 분석합니다. ## 전체 아키텍처 다이어그램 ![쿠버네티스 API 서버가 Typha를 거쳐 각 노드의 Felix·BIRD로 정책과 라우팅 정보를 전달하고, BIRD가 ToR 스위치·Spine을 통해 외부 네트워크와 BGP로 연결되는 Calico 전체 아키텍처를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-0.html) 제어 상태의 단순화된 그림입니다. BIRD 쪽 화살표에는 설정을 렌더링하는 confd가 생략되어 있으며 Typha가 BIRD의 직접 설정 API는 아닙니다. BIRD/confd·Typha의 사용은 설치 모드에 따라 다르고 모든 컨트롤 플레인 컴포넌트가 표시된 것은 아닙니다. ## Felix 심층 분석 Felix는 선택된 워크로드 노드의 Calico 노드 에이전트 안에서 해당 라우트·인터페이스 설정·커널 정책을 관리합니다. Linux 전체 네트워킹 경로에서는 컨테이너 런타임이 CNI 체인을 호출하고 CNI/IPAM 플러그인이 인터페이스 생성과 주소 할당을 수행합니다. Felix는 엔드포인트 변경을 비동기로 관찰하며 직접 CNI ADD 호출을 처리하는 주체가 아닙니다. 정확한 컴포넌트는 operator·플랫폼·네트워킹 모드에 따라 다릅니다. ### Felix의 주요 책임 Linux Pod 인터페이스·주소는 CNI/IPAM이 생성합니다. Felix는 엔드포인트 상태와 커널 정책을 조정합니다. HTTP 상태 서버와 데이터스토어 상태 보고는 별개의 기능입니다. ### Felix 내부 워크플로우 Pod 생성 시 런타임이 CNI/IPAM 체인을 호출해 네트워크를 설정하고 엔드포인트 상태를 기록합니다. Felix는 관련 변경을 관찰해 정책·라우트를 반영하며 BGP 모드에는 별도의 confd/BIRD 경로가 있습니다. Pod Running은 라우팅·정책 수렴의 증거가 아닙니다. ### Felix 설정 상세 ```yaml apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: logSeverityScreen: Info healthEnabled: true healthPort: 9099 prometheusMetricsEnabled: true prometheusMetricsPort: 9091 reportingInterval: 30s reportingTTL: 90s ``` Calico 3.32.2가 받아들이는 필드만 사용한 최소 예제입니다. 설정 소유자를 통해 변경해야 하며 데이터플레인 전환이나 성능 튜닝 절차는 아닙니다. Felix 상태 서버의 기본 호스트는 localhost입니다. 메트릭 활성화가 Prometheus scrape 설정이나 공개 노출의 적절성을 보장하지는 않습니다. | 설정 대상 | 올바른 소유자·해석 | |---|---| | Linux 데이터플레인 | 지원되는 구성에서 operator의 `Installation.spec.calicoNetwork.linuxDataplane`으로 `Iptables`·`Nftables`·`BPF` 선택 | | `bpfEnabled` | Felix의 하위 설정, 이것만 바꾸지 말고 operator 전환·kube-proxy·API 연결성을 함께 조정 | | `iptablesBackend: NFT` | iptables-nft 도구 백엔드이며 Calico의 native nftables 데이터플레인과 다름 | | 연결 시점 로드밸런싱 | 현재 필드는 `bpfConnectTimeLoadBalancing: TCP`·`Enabled`·`Disabled`, 기존 boolean `bpfConnectTimeLoadBalancingEnabled`는 여전히 허용되지만 deprecated | | 노드 주소 감지 | operator의 `calicoNetwork.nodeAddressAutodetectionV4`·`V6` 또는 매니페스트 설치의 노드 시작 환경 변수, Felix의 `ipAutoDetectionMethod`·`ipv6AutoDetectionMethod` 필드가 아님 | | 플로우 가시성 | 지원되는 Goldmane/Whisker 설정 사용, 기존 예제의 Enterprise 파일 로그 필드는 Open Source에서 허용되지 않음 | | MTU·터널 모드 | 언더레이·캡슐화·암호화로 산정하고 Installation/IPPool과 조정, 1440/1410/1420을 임의 적용하거나 모든 터널을 켜지 않음 | | 호스트 failsafe 포트 | 기본 목록을 대체하기 전에 실제 API·BGP·etcd·관리 경로 확인, 기존의 짧은 목록은 필요한 예외를 없앨 수 있음 | | 기간 필드 | `reportingInterval`·`reportingTTL`·`iptablesPostWriteCheckInterval`·`iptablesLockProbeInterval` 등 현재 이름 사용, 기계적으로 `Secs`·`Millis`를 붙이지 않음 | 공개 스키마에는 기존 `iptablesLockFilePath`, `iptablesLockTimeoutSecs`, `iptablesLockProbeIntervalMillis`와 `flowLogsFileEnabled`, `flowLogsFileDirectory`, `flowLogsFileMaxFiles`, `flowLogsFileMaxFileSizeMb`, `flowLogsEnableHostEndpoint`가 없습니다. [Felix 레퍼런스](https://docs.tigera.io/calico/latest/reference/resources/felixconfig)와 [operator API](https://docs.tigera.io/calico/latest/reference/installation/api)를 확인하세요. 주소·데이터플레인 변경에는 별도의 rollout 검증이 필요합니다. ### Felix iptables 규칙 구조 Felix가 생성하는 iptables 규칙 체인 구조: 아래는 [릴리스 규칙 정의](https://github.com/projectcalico/calico/blob/v3.32.2/felix/rules/rule_defs.go)의 일부 접두사이며 전체 체인 그래프가 아닙니다. iptables 데이터플레인에 해당하며 실제 모드·설정의 규칙을 확인해야 합니다. | 체인·접두사 | 역할 | |---|---| | `cali-FORWARD` | Calico 포워딩 hook | | `cali-from-wl-dispatch` | 워크로드 인터페이스에서 오는 트래픽 분기 | | `cali-to-wl-dispatch` | 워크로드 인터페이스로 가는 트래픽 분기 | | `cali-fw-…` / `cali-tw-…` | 워크로드별 방향 체인 | | `cali-pi-…` / `cali-po-…` | 인바운드·아웃바운드 정책 체인 | ## BIRD 심층 분석 BIRD는 Calico BGP 백엔드가 활성일 때 라우트를 교환합니다. policy-only나 BGP 비활성 VXLAN 설치에 BIRD/confd가 항상 필요한 것은 아닙니다. 아래 토폴로지는 적절하게 설계한 BGP 클러스터용이며 소개 장의 BGP 비활성 kind 실습에 그대로 추가하는 설정이 아닙니다. ### BIRD의 역할 ![Felix가 커널 라우팅 테이블에 추가한 라우트 정보를 BIRD가 받아 BGP 세션을 관리하고, 라우트 교환 기능으로 Pod CIDR를 BGP UPDATE로 다른 노드와 외부 라우터에 광고하며, 대규모 클러스터용 Route Reflector와 export filter 기반 라우트 필터링 기능도 BIRD 안에 있음을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-4.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-4.html) 라우트 정보 경로의 일부입니다. BIRD의 커널 프로토콜도 학습 경로를 설치할 수 있으며 confd/IPAM 데이터도 생성 설정에 관여합니다. BGP 라우트 필터는 라우팅 정책이며 Kubernetes NetworkPolicy 강제가 아닙니다. ### BGP 클러스터 토폴로지 #### Full Mesh (소규모 클러스터) ![50노드 미만의 소규모 클러스터에서 노드 4대가 모두 서로 iBGP로 직접 연결되는 Full Mesh BGP 토폴로지를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-5.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-5.html) 4노드 full mesh의 세션 6개는 N(N−1)/2로 계산합니다. 50노드는 프로토콜 한계가 아니며 라우트 수·수렴·장애 설계와 제어 부하를 기준으로 토폴로지를 선택해야 합니다. #### Route Reflector (대규모 클러스터) RR 수와 피어링은 장애 설계에 따라 결정합니다. 클라이언트가 RR 하나에만 연결된 그림은 두 RR에 연결된 구성의 이중화를 제공하지 않습니다. 아래 selector와 전환 절차에서는 양쪽 RR 및 RR 간 세션을 검증합니다. ### Route Reflector 전환 순서 [공식 BGP 전환 절차](https://docs.tigera.io/calico/latest/networking/configuring/bgp)를 따릅니다. RR cluster ID를 지정하면 해당 노드가 기존 node mesh에서 즉시 빠져 워크로드에 영향을 줄 수 있습니다. 애플리케이션 워크로드가 없는 전용 노드를 준비하거나 명시적인 유지보수 전환을 계획하세요. 기존 Calico Node를 다른 필드가 빠진 부분 객체로 대체하면 안 됩니다. Kubernetes API 데이터스토어에서는 문서화된 노드 annotation을 사용해 다른 Node 필드를 보존합니다. 이름을 준비한 노드로 바꿉니다. ```bash # Existing, prepared RR nodes with no application workloads. kubectl get nodes rr-1 rr-2 -o yaml > rr-nodes-before.yaml kubectl get bgpconfiguration.projectcalico.org default -o yaml > bgp-before.yaml kubectl annotate node rr-1 projectcalico.org/RouteReflectorClusterID=244.0.0.1 --overwrite kubectl annotate node rr-2 projectcalico.org/RouteReflectorClusterID=244.0.0.2 --overwrite kubectl label nodes rr-1 rr-2 route-reflector=true --overwrite kubectl apply -f - <<'YAML' apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: nodes-to-route-reflectors spec: nodeSelector: all() peerSelector: route-reflector == 'true' YAML ``` `all()`에서 RR selector로의 피어링은 클라이언트와 RR 간 피어링을 포함합니다. 양쪽 RR과 클라이언트의 세션·라우트·실제 도달성을 확인한 뒤 기존 mesh를 끕니다. Established 세션만으로 필요한 경로가 수락되었다고 보장할 수는 없습니다. ```bash # Only after replacement sessions, routes and test traffic have been verified. kubectl patch bgpconfiguration.projectcalico.org default --type merge \ -p '{"spec":{"nodeToNodeMeshEnabled":false}}' ``` 블록을 한 번에 모두 적용하거나 무중단을 보장하는 절차가 아닙니다. 이전 설정과 검증한 복구 경로를 보관하세요. 외부 패브릭의 주소·ASN·AS 재사용에는 경로 정책과 AS-loop 처리가 필요합니다. ### 외부 네트워크 연동 ![각 워커 노드(AS 64512)가 ToR 스위치(AS 64513)와 Spine 스위치(AS 64514)를 거쳐 데이터센터의 Core 라우터(AS 64515)까지 eBGP로 계층적으로 연결되는 외부 네트워크 연동 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-7.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-7.html) 주소·ASN은 구조 설명용이며 완성된 패브릭 설정이 아닙니다. 여러 랙에서 같은 ASN을 재사용하면 경로 수락과 AS-loop 처리를 명시적으로 설계해야 합니다. BGP 선은 제어 세션이며 패킷의 userspace 경유를 뜻하지 않습니다. ## confd 심층 분석 confd는 BIRD 설정 파일을 동적으로 생성하는 템플릿 엔진입니다. ### confd 동작 방식 ![confd가 데이터스토어의 BGP 설정을 감시해 템플릿과 병합한 bird.cfg를 생성하고 BIRD 프로세스를 리로드하며, Watch Loop로 이후 변경도 반영하는 동작 방식을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-8.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-8.html) Watch·템플릿 생성·검사·reload의 개념 흐름입니다. 실제 3.32.2 confd 정의의 reload 동작은 `sv hup bird || true`이며 생성 파일은 API 설정에서 다시 만들어집니다. ### BIRD 상태와 생성 설정 IPv4 BIRD가 실행 중인 노드를 선택합니다. [릴리스 시작 스크립트](https://github.com/projectcalico/calico/blob/v3.32.2/node/filesystem/etc/service/available/bird/run)는 아래 제어 소켓을 사용합니다. 매니페스트 설치라면 네임스페이스가 다를 수 있습니다. ```bash CALICO_NODE=worker-node-name CALICO_POD=$(kubectl -n calico-system get pods -l k8s-app=calico-node \ --field-selector "spec.nodeName=$CALICO_NODE" -o jsonpath='{.items[0].metadata.name}') : "${CALICO_POD:?No Calico Pod on the selected node}" kubectl -n calico-system exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show protocols kubectl -n calico-system exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show route ``` 자세한 조회에는 실제 출력의 프로토콜 이름과 prefix를 사용합니다. 다음은 [릴리스 템플릿](https://github.com/projectcalico/calico/blob/v3.32.2/confd/etc/calico/confd/templates/bird.cfg.template)의 커널 동기화 부분입니다. 필터 정의와 주변 설정이 생략되어 있으므로 완전한 `bird.cfg`가 아닙니다. ```text protocol kernel { learn; persist; scan time 2; import all; export filter calico_kernel_programming; graceful restart; merge paths on; } ``` [confd 정의](https://github.com/projectcalico/calico/blob/v3.32.2/confd/etc/calico/confd/conf.d/bird.toml)는 `/etc/calico/confd/config/bird.cfg`를 생성하고 `bird -p -c {{.src}}`로 후보 설정을 검사한 뒤 `sv hup bird || true`를 reload 동작으로 사용합니다. BIRD도 선택된 학습 경로를 커널에 설치할 수 있으며 모든 경로를 Felix에서 받기만 하는 것은 아닙니다. Reload·graceful restart 후에도 상태와 트래픽 확인이 필요합니다. 생성 파일 직접 수정 대신 BGP API 설정을 원본으로 관리하세요. ## Typha 심층 분석 Typha는 데이터스토어 업데이트를 Felix 클라이언트에 분배하는 프록시입니다. Operator는 50노드 미만에서도 Typha를 배포·확장할 수 있으므로 고정된 50노드 필수 기준으로 해석하면 안 됩니다. ### Typha의 필요성 각 Felix의 직접 데이터스토어 watch는 변경량에 따른 부하를 늘릴 수 있습니다. 50노드는 필수 기준이 아니며 실제 설치의 operator 계산과 데이터스토어 부하를 확인해야 합니다. ![Typha 세 대가 API 서버에 대한 Watch 연결을 대신 맺고 각 Felix 그룹으로 변경 사항을 팬아웃해 API 서버 커넥션 수를 줄이는 해결책 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-10.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-10.html) 복제 3개와 클라이언트 그룹은 예시입니다. 선은 논리적 데이터 흐름이지 정확히 API watch 세 개만 열린다는 뜻은 아닙니다. 현재 목표 복제 수는 아래의 버전별 operator 계산을 따릅니다. ### Operator 1.42.6의 Typha 스케일링 Operator가 Typha를 배포·확장합니다. 고정된 “50노드 이상에서만 필요” 규칙은 아닙니다. 이 버전의 [autoscaler](https://github.com/tigera/operator/blob/v1.42.6/pkg/controller/installation/typha_autoscaler.go)는 unschedulable로 표시되지 않은 노드를 세고 AKS virtual node를 제외한 뒤, 목표 복제 수를 배치할 Linux 노드가 충분한지 따로 확인합니다. Taint 등 다른 배치 제약도 고려해야 합니다. 축약 주석이 아닌 실제 [계산 함수](https://github.com/tigera/operator/blob/v1.42.6/pkg/common/autoscale.go)의 결과는 다음과 같습니다. - 집계 노드 1–2개: 복제 1개. - 집계 노드 3–4개: 복제 2개. - 집계 노드 5개 이상: `max(3, floor(N / 200) + 2)`. | 집계 노드 수 | 이 버전의 목표 복제 수 | |---|---| | 50 | 3 | | 200 | 3 | | 500 | 4 | | 1,000 | 7 | | 2,000 | 12 | 버전별 목표값이며 복제본당 용량 보장이나 모든 설치의 권장값은 아닙니다. 비클러스터 호스트 모드는 별도의 대상 HostEndpoint 수를 사용합니다. 기존 표와 `max(3, ceil(N / 200))` 공식은 이 operator를 설명하지 못했습니다. ### Operator가 관리하는 Typha 설정 Operator의 Deployment·ServiceAccount/RBAC·Service·중단 예산·TLS 설정을 함께 유지하세요. 기존 수동 Deployment는 필요한 의존성을 빠뜨렸고 operator 관리 설정을 덮어쓸 수 있었습니다. Felix–Typha TLS에는 신뢰 CA, Typha 서버 인증서·키, 예상 Felix 클라이언트 신원이 필요합니다. 기본 5473 포트는 동기화용이며 사용자 트래픽 프록시가 아닙니다. ```bash # Change the operator's supported setting through its API. kubectl patch installation.operator.tigera.io default --type merge \ -p '{"spec":{"typhaMetricsPort":9093}}' kubectl -n calico-system get deployment calico-typha -o yaml kubectl -n calico-system get service calico-typha -o yaml kubectl -n calico-system get pdb ``` Typha 상태 엔드포인트 기본값은 localhost:9098입니다. 이 operator는 설정된 Felix 상태 포트보다 1 작은 포트를 계산하고 probe를 맞춥니다. Pod 네트워크 Deployment에서 probe가 Pod IP로 접속하면 localhost에만 바인딩된 리스너에 닿지 않습니다. 네트워크·바인딩 설정 없이 probe만 복사하면 안 됩니다. Operator 소스는 기존 단독 예제에 없던 TLS mount와 클라이언트 신원 설정도 제공합니다. ## kube-controllers 심층 분석 kube-controllers는 선택된 조정 작업을 실행합니다. 활성 컨트롤러는 데이터스토어·에디션·설치 구성에 따라 달라집니다. Kubernetes 정책·네임스페이스·ServiceAccount를 etcd에 투영하는 경로와 Kubernetes API 데이터스토어의 처리를 구분해야 합니다. ### 포함된 컨트롤러 다음은 가능한 역할 목록입니다. 기본 Open Source operator 배포의 활성 목록인 node·loadbalancer와 etcd 투영 경로의 다른 컨트롤러를 구분하세요. ### 컨트롤러별 역할 | 컨트롤러 | 역할 | Watch 대상 | | -------------------- | ------------------------------------ | --------------------- | | **Policy** | K8s NetworkPolicy → Calico Policy 변환 | NetworkPolicy | | **Namespace** | 네임스페이스 라벨 기반 프로필 관리 | Namespace | | **ServiceAccount** | SA 라벨을 프로필에 반영 | ServiceAccount | | **WorkloadEndpoint** | 해당 데이터스토어 경로에서 Pod 라벨 등 엔드포인트 메타데이터 갱신 | Pod, WorkloadEndpoint | | **Node** | 노드 정보 동기화, 제거된 노드 정리 | Node | ### kube-controllers 설정 Operator 설치에서는 실제 [KubeControllersConfiguration API](https://docs.tigera.io/calico/latest/reference/resources/kubecontrollersconfig)를 사용합니다. 임의의 JSON ConfigMap을 만들거나 operator의 Deployment를 수동 예제로 대체하는 방식이 아닙니다. ```bash kubectl get kubecontrollersconfiguration.projectcalico.org default -o yaml kubectl patch kubecontrollersconfiguration.projectcalico.org default --type merge \ -p '{"spec":{"logSeverityScreen":"Info","healthChecks":"Enabled","prometheusMetricsPort":9094}}' ``` 이 merge patch는 기존 `controllers` 설정을 보존합니다. GitOps가 관리한다면 원하는 상태의 원본에 같은 변경을 반영하세요. 빈 controller 객체를 넣은 대체 매니페스트는 기존 조정·주소 할당 설정을 바꿀 수 있습니다. Operator 1.42.6의 기본 Open Source 배포는 `ENABLED_CONTROLLERS=node,loadbalancer`를 선택합니다. 위 표는 가능한 컨트롤러 역할이며 모든 데이터스토어에서 다섯 개가 항상 실행됨을 뜻하지 않습니다. [렌더러](https://github.com/tigera/operator/blob/v1.42.6/pkg/render/kubecontrollers/kube-controllers.go)는 복제 1개와 `Recreate` 전략을 지정합니다. 기존의 leader election 설명은 이 구성의 근거가 없었습니다. 설치 소유자를 유지하고 Deployment를 임의 교체·확장하지 마세요. ## 데이터스토어 옵션 이 operator 예제는 Kubernetes API 데이터스토어를 사용합니다. Calico 상태에는 Calico CRD와 기본 Kubernetes 객체가 관여하며 모든 논리적 리소스가 별도 CRD인 것은 아닙니다. 일반적인 집계 API 서버는 내부 표현 위에 `projectcalico.org/v3`를 제공합니다. Native v3 CRD는 Calico 3.32의 별도 tech preview이며 전용 마이그레이션 절차가 있습니다. Typha는 읽기·watch 업데이트를 분배하며 Felix의 일반적인 쓰기 프록시가 아닙니다. 상태나 리소스를 갱신하는 컴포넌트는 각자의 데이터스토어 접근을 사용합니다. Kubernetes API 상태는 그 저장소에 보존되지만 이 모드에 별도 Calico etcd 클러스터는 필요하지 않습니다. 직접 etcdv3 접근은 지원·기능 조건을 확인할 별도 설치 선택입니다. 더 빠르거나 무제한이거나 5,000노드 이상에서 필수라고 추론하면 안 됩니다. eBPF 데이터플레인은 Kubernetes 데이터스토어를 요구합니다. 직접 etcd에는 자체 TLS 신뢰·자격 증명·가용성·일관된 백업/복원 설계도 필요합니다. | 관심사 | Kubernetes API 데이터스토어 | 직접 etcdv3 | |---|---|---| | 접근 통제 | Kubernetes 인증·RBAC와 해당 Calico API 경로 | etcd 인증·TLS·접근 통제 | | 운영 | 클러스터 API 재사용, 제공자별 백업 절차 | 선택한 etcd 배포를 직접 운영·백업 | | 호스트·VM | 설치 방식과 에디션별 확인 | 설치 방식과 에디션별 확인 | | 선택 | 이 operator 가이드의 경로 | 노드 수의 지름길이 아닌 별도 검증 설계 | 관리형 Kubernetes의 “Kubernetes 백업”이 사용자의 직접 컨트롤 플레인 etcd snapshot 접근을 뜻하지는 않습니다. 플랫폼이 지원하는 리소스 백업 절차를 사용합니다. `CalicoAPIConfig`는 calicoctl 클라이언트의 연결 설정 파일이며 `kubectl apply`할 Kubernetes 리소스가 아닙니다. 별도 파일을 사용할 때는 `calicoctl get nodes --config ./calicoctl-config.yaml`처럼 명시하고, 실제 kubeconfig 또는 etcd TLS 파일 경로를 준비하세요. ### 클라이언트 설정 파일 예 경로와 인증서를 실제 환경에 맞춘 뒤 명시적인 `--config` 파일로 사용합니다. 이 객체들은 클러스터에 적용하지 않습니다. ```yaml # calicoctl 설정 apiVersion: projectcalico.org/v3 kind: CalicoAPIConfig metadata: spec: datastoreType: "kubernetes" kubeconfig: "/path/to/.kube/config" ``` ```yaml # calicoctl 설정 apiVersion: projectcalico.org/v3 kind: CalicoAPIConfig metadata: spec: datastoreType: "etcdv3" etcdEndpoints: "https://etcd1:2379,https://etcd2:2379,https://etcd3:2379" etcdKeyFile: "/path/to/etcd-key.pem" etcdCertFile: "/path/to/etcd-cert.pem" etcdCACertFile: "/path/to/etcd-ca.pem" ``` ## 컴포넌트 상호작용 시퀀스 ### Pod 생성 시 전체 흐름 Kubelet은 컨테이너 런타임에 sandbox 생성을 요청하고 런타임이 CNI/IPAM을 호출합니다. 엔드포인트·정책 데이터는 선택한 데이터스토어/watch 경로로 Felix에 전달됩니다. BGP 모드에서는 confd/BIRD가 별도로 라우팅 설정을 처리합니다. 비동기 수렴이므로 실제 연결성과 정책 강제를 확인해야 합니다. ### 패킷 흐름 (Pod-to-Pod, 다른 노드) ![다른 노드의 Pod로 향하는 패킷이 Felix/iptables의 Egress Policy 검사를 거친 뒤 IPIP/VXLAN 캡슐화 또는 BGP 기반 직접 라우팅 중 한 경로로 전달되어 목적지 노드에서 Ingress Policy 검사를 받고 Pod B로 전달되는 흐름을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-02-architecture-13.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-02-architecture-13.html) 캡슐화와 직접 전달은 대안 경로입니다. “Felix/iptables”는 Felix가 프로그래밍한 커널 규칙이며 데몬이 패킷을 중계한다는 뜻은 아닙니다. BIRD는 BGP 모드의 제어 정보를 제공할 뿐 애플리케이션 패킷을 운반하지 않습니다. *** ## 요약 이 장에서 학습한 내용: 1. **Felix**: 각 노드의 핵심 에이전트, iptables/eBPF 규칙 및 라우팅 관리 2. **BIRD**: BGP 라우팅 데몬, 노드 간 및 외부 네트워크 라우트 교환 3. **confd**: BIRD 설정 동적 생성, 데이터스토어 변경 감지 4. **Typha**: 대규모 클러스터를 위한 팬아웃 프록시, API 서버 부하 감소 5. **kube-controllers**: Kubernetes ↔ Calico 리소스 동기화 6. **데이터스토어**: Kubernetes API (권장) 또는 etcd 선택 다음 장에서는 [네트워킹 모드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)를 심층적으로 분석합니다. *** [← 이전: 소개 및 기본 개념](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/01-introduction.md) | [메인 페이지](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) | [다음: 네트워킹 모드 →](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md) ## 퀴즈 이 장에서 배운 내용을 테스트하려면 [Part 2 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/02-architecture-quiz)를 풀어보세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/03-networking-modes ---------------------------------------- # Part 3: 네트워킹 모드 > **검토 기준**: Calico Open Source 3.32.2 / operator 1.42.6, Calico 3.32의 Kubernetes 시험 범위는 1.34–1.36입니다. > **마지막 업데이트**: 2026년 9월 12일. 아래 기존 벤치마크 수치는 검증되지 않은 보고값으로 보존하며 새 측정값이 아닙니다. ## 범위와 모드 선택 이 장은 Calico가 담당하는 Linux Pod 네트워킹·IPAM을 다룹니다. EKS policy-only에서는 Amazon VPC CNI가 Pod 네트워킹을 유지하며 Calico IPPool 생성만으로 오버레이로 바뀌지 않습니다. 예제는 대안 설계이며 함께 적용하거나 소개 실습의 기존 pool 위에 겹쳐 생성하는 매니페스트가 아닙니다. 감사에서는 클러스터 마이그레이션이나 네트워크 벤치마크를 실행하지 않았습니다. | 선택 | 의미 | 주요 조건 | |---|---|---| | IPIP | IPv4-in-IPv4, IP protocol 4 | Calico IPIP는 IPv4 전용, 언더레이의 프로토콜 허용 필요 | | VXLAN | 내부 Ethernet을 UDP로 운반, Calico 기본 포트 4789 | 외부 IPv4·IPv6 오버헤드가 다르며 포트·VNI는 설정 가능 | | Direct / 비캡슐화 | Pod 네트워크 오버레이 없이 Pod IP 패킷 라우팅 | 언더레이와 반환 경로가 Pod 주소를 라우팅해야 함 | | CrossSubnet | IPIP 또는 VXLAN의 설정 | 관련 노드 주소가 서로 다른 설정 서브넷일 때만 노드 간 트래픽 캡슐화 | `Always`는 해당 pool 주소로 가는 대상 노드 간 트래픽에 적용하며 같은 노드 통신에는 물리 터널이 필요하지 않습니다. `Never`는 해당 캡슐화를 끄는 값이지 모든 네트워킹을 끄는 값이 아닙니다. CrossSubnet은 AZ·리전·WAN 링크 감지기가 아니며 한 AZ의 다른 서브넷 사이도 캡슐화될 수 있습니다. Calico가 사용하는 노드 주소·서브넷 마스크를 확인해야 합니다. 기본값은 설치 방식·제공자·데이터플레인에 따라 다릅니다. “모든 클라우드에서 IPIP 기본”이나 “Direct 항상 최고 성능”으로 일반화할 수 없습니다. [오버레이 가이드](https://docs.tigera.io/calico/latest/networking/configuring/vxlan-ipip)에서 지원 경로를 확인하세요. ### 라우팅과 캡슐화는 별개의 선택 기본적으로 VXLAN pool 경로는 Felix가, IPIP·비캡슐화 pool의 클러스터 경로는 confd/BIRD가 프로그래밍합니다. Calico 3.32에서는 후자의 경로에도 `Installation.spec.calicoNetwork.clusterRoutingMode: Felix`를 선택할 수 있습니다. 하위 설정은 Felix의 `programClusterRoutes: Enabled`와 BGP의 `programClusterRoutes: Disabled`이며 operator 설치에는 operator 설정을 사용합니다. 외부 BGP 광고에는 여전히 BGP가 필요합니다. 정적 경로나 적합한 라우팅 패브릭도 언더레이 연결성을 제공하므로 모든 비캡슐화 설계에 BGP나 동일 L2 인접성이 필수인 것은 아닙니다. ## 패킷 구조와 오버헤드 아래는 **언더레이 IP MTU** 기준입니다. 외부 Ethernet 헤더는 그 IP MTU에 포함되지 않습니다. IPv4 옵션과 추가 내부 VLAN 태그가 없다고 가정하며 TCP 옵션·다른 캡슐화는 페이로드를 더 줄일 수 있습니다. ```text Direct: outer Ethernet | Pod IP | TCP or UDP | payload IPIP: outer Ethernet | outer IPv4 | Pod IPv4 | TCP or UDP | payload VXLAN: outer Ethernet | outer IP | UDP | VXLAN | inner Ethernet | Pod IP | TCP or UDP | payload ``` | 전송 방식 | Pod IP 패킷에 추가되는 오버헤드 | 언더레이 IP MTU 1500의 Pod IP MTU | |---|---|---| | 다른 터널 없는 Direct | 0 | 1500 | | 외부 IPv4 IPIP | 20 | 1480 | | 외부 IPv4 VXLAN | 20 + 8 + 8 + 14 = 50 | 1450 | | 외부 IPv6 VXLAN | 40 + 8 + 8 + 14 = 70 | 1430 | | 외부 IPv4 WireGuard | 60 | 1440 | | 외부 IPv6 WireGuard | 80 | 1420 | VXLAN MTU 오버헤드의 14바이트는 외부가 아닌 **내부 Ethernet 헤더**입니다. 기본 TCP 헤더는 최소 20바이트, UDP 헤더는 8바이트입니다. 이 가정에서 IPv4 IP 패킷 1500바이트에는 최대 TCP 페이로드 1460바이트 또는 UDP 페이로드 1472바이트가 들어갑니다. 기존의 공통 “TCP/UDP = 20바이트” 표기는 잘못되었습니다. IPIP의 4는 TCP·UDP 포트가 아닌 IP 프로토콜 번호입니다. 일반적인 Calico VXLAN VNI는 4096, 기본 UDP 포트는 4789이며 둘 다 설정 가능합니다. 다른 현재 VXLAN 구현도 8472를 사용할 수 있으므로 오래된 소프트웨어에만 한정된 값은 아닙니다. [IP-in-IP](https://www.rfc-editor.org/rfc/rfc2003)와 [VXLAN](https://www.rfc-editor.org/rfc/rfc7348)을 참고하세요. ### 패킷 경로 개념도 ![출발지 tunl0의 IPv4 캡슐화와 목적지 tunl0의 디캡슐화를 거치는 Pod 경로.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-03-networking-modes-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-03-networking-modes-1.html) 그림의 Always는 대상 노드 간 트래픽을 뜻하며 같은 노드 트래픽까지 터널을 통과한다는 뜻이 아닙니다. 1480은 옵션 없는 IPv4 헤더와 1500바이트 언더레이의 예입니다. tunl0는 캡슐화 장치이며 암호화·보안 경계가 아닙니다. ![출발지 커널의 IPIP 캡슐화, 네트워크 전달, 목적지 커널의 디캡슐화 순서.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-03-networking-modes-2.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-03-networking-modes-2.html) “Felix” 열은 Felix가 프로그래밍한 커널 라우팅·정책을 나타내며 패킷이 Felix 데몬을 경유하지는 않습니다. IPv4 노드 간 경로의 개념도입니다. ![Calico VTEP 두 개가 내부 프레임을 UDP로 캡슐화·디캡슐화하는 경로.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-03-networking-modes-3.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-03-networking-modes-3.html) 4789와 VNI 4096은 그림의 기본값 예입니다. 50바이트 오버헤드·1450 MTU는 외부 IPv4, 언더레이 1500과 명시한 헤더 가정에 한정됩니다. ### CrossSubnet 예 노드 주소가 10.0.1.10/24와 10.0.1.11/24이면 같은 서브넷 경로를 비캡슐화할 수 있습니다. 10.0.2.20/24의 peer에는 CrossSubnet 설계상 캡슐화가 필요합니다. 클라우드 서브넷 이름이 맞아 보여도 잘못된 노드 마스크는 결과를 바꿀 수 있습니다. CrossSubnet은 VPC·리전 간 연결을 만들거나 암호화를 제공하지 않습니다. ![설정된 노드 서브넷이 같으면 비캡슐화, 다르면 IPIP 또는 VXLAN을 쓰는 CrossSubnet 경로.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-03-networking-modes-5.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-03-networking-modes-5.html) 서브넷 판단은 노드 주소·마스크 기준이며 AZ 이름 기준이 아닙니다. 그림의 EKS 문구는 Calico가 네트워킹을 맡는 별도 구성에만 해당하고 기본 VPC CNI를 의미하지 않습니다. ### 노드 진단 일반 애플리케이션 Pod가 아니라 승인된 **Linux 노드 네트워크 네임스페이스**에서 읽기 전용 명령을 실행합니다. 해당 모드가 켜져 있어야 인터페이스가 존재하며 출력값은 실제 설치에 따라 달라집니다. ```bash ip link show tunl0 ip link show vxlan.calico bridge fdb show dev vxlan.calico ip route show ``` 일반적인 로컬 Pod 경로는 `10.244.1.5/32 dev cali…` 같은 host route입니다. /24나 /26 전체를 Pod 하나의 veth로 보내는 예시로 이해하면 안 됩니다. 집계 block에는 blackhole 경로와 더 구체적인 Pod 경로가 함께 있을 수 있습니다. 원격 block은 터널이나 다음 노드·라우터를 사용하며 프로토콜 표시는 BIRD·Felix 중 경로 프로그래밍 주체에 따라 달라집니다. ## 소유자를 통해 pool 설정 `kubectl …projectcalico.org` 예제는 소개 장의 Calico 집계 API 또는 적절한 native-v3 구성을 전제로 합니다. 없다면 일치하는 calicoctl로 논리적 Calico 리소스를 관리합니다. Operator 명령은 operator 설치에만 적용합니다. Pool 범위가 Service·노드/언더레이 범위와 충돌하지 않는지도 확인하세요. 설정 소유자를 하나로 유지합니다. `Installation.spec.calicoNetwork.ipPools`에 있는 pool은 operator가 조정하므로 경쟁하는 IPPool 객체를 적용하지 말고 원본 목록을 소유자를 통해 수정합니다. 독립 pool에는 Calico IPPool API를 사용합니다. 두 경우 모두 실제 클러스터 Pod CIDR·비중첩·IPAM 종류·기존 할당을 먼저 확인하세요. ```bash kubectl get installation.operator.tigera.io default -o yaml kubectl get ippools.projectcalico.org -o yaml calicoctl ipam show --show-blocks ``` operator 관리 pool에서는 아래를 기존 `ipPools` 목록의 **항목 조각**으로 사용합니다. 다른 항목과 Installation 필드를 보존하세요. 소개 실습에서 이미 할당한 /16 pool 위에 추가 생성하면 안 됩니다. ```yaml - name: mode-demo-pool cidr: 10.244.0.0/16 blockSize: 26 encapsulation: VXLAN natOutgoing: Enabled nodeSelector: all() ``` 독립적으로 새 pool을 설계하는 경우의 동등한 IPv4 리소스는 아래와 같습니다. Operator 항목과 함께 만드는 중첩 pool이 아니라 대안입니다. CIDR은 실제 클러스터 범위 안에서 기존 pool과 겹치지 않게 선택해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: mode-demo-pool spec: cidr: 10.244.0.0/16 blockSize: 26 ipipMode: Never vxlanMode: Always natOutgoing: true nodeSelector: all() ``` 같은 CIDR의 리소스를 여러 개 만들지 말고 한 행을 선택합니다. | IPv4 설계 | IPPool `ipipMode` | IPPool `vxlanMode` | Operator `encapsulation` | |---|---|---|---| | IPIP Always | Always | Never | IPIP | | IPIP CrossSubnet | CrossSubnet | Never | IPIPCrossSubnet | | VXLAN Always | Never | Always | VXLAN | | VXLAN CrossSubnet | Never | CrossSubnet | VXLANCrossSubnet | | Direct | Never | Never | None | 한 pool에서 IPIP와 VXLAN을 동시에 켤 수는 없습니다. `encapsulation`은 operator pool의 필드이며 독립 IPPool의 필드명이 아닙니다. 일반적인 집계 API 설치에서는 중첩 pool 생성이 거부됩니다. Native v3 CRD(tech preview)는 중첩 검증이 비동기이므로 생성된 pool에 Disabled 조건이 생길 수 있습니다. 생성 성공만으로 할당 가능함을 입증할 수는 없습니다. Calico 3.32의 Installation 스키마는 최대 25개 항목의 pool 목록을 허용하며 컨트롤러 검증·플랫폼 제약이 적용됩니다. 예전의 IPv4 pool 한 개 제한 예제를 현재 모든 설치의 제한으로 일반화하면 안 됩니다. ### 외부 BGP와 직접 라우팅 외부 BGP 설계라면 오버레이 제거 전에 실제 peer와 반환 경로를 구성합니다. Peer 선언만으로 물리 라우터가 설정되거나 경로 수락이 입증되지는 않습니다. 아래는 BGP 비활성 VXLAN 실습에 추가하는 설정이 아닌 별도 토폴로지 예입니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: example-rack-tor spec: peerIP: 192.0.2.1 asNumber: 65001 nodeSelector: rack == 'rack1' ``` 문서용 주소·ASN을 바꾸고 대상 노드 라벨과 랙별 라우트 필터·AS-loop 처리를 검증합니다. `natOutgoing: false`는 반환 라우팅과 필요한 외부 NAT가 설계된 경우에만 적절합니다. BGP만으로 사설 Pod 주소가 인터넷에서 라우팅되지는 않습니다. Mesh·RR 변경은 [BGP 전환 설명](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/02-architecture.md)과 [BGP 심층 분석](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md)을 참고하세요. ![이 예제에서는 BGP로 경로를 제공하는 언더레이를 비캡슐화 Pod 패킷이 통과합니다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-03-networking-modes-4.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-03-networking-modes-4.html) BGP를 사용한 설계 예이며 모든 Direct 설계에 BGP가 필수라는 뜻이 아닙니다. 1500은 해당 경로와 다른 터널이 없다는 가정입니다. eBPF Service 전달이나 암호화로 워크로드 MTU가 더 작아질 수 있습니다. ## NAT와 pool 선택 `natOutgoing: true`의 일반적인 Calico 동작은 해당 pool의 출발지에서 **모든 Calico IPPool 밖**의 목적지로 갈 때 SNAT하는 것입니다. 단순한 “클러스터 밖으로 나감” 조건이 아닙니다. 비활성 pool도 NAT를 하지 않을 목적지 범위를 나타낼 수 있으므로 삭제하면 NAT 동작이 바뀔 수 있습니다. Felix의 추가 설정으로 호스트 IP도 제외할 수 있습니다. NAT가 NetworkPolicy 허용을 부여하지는 않습니다. [아웃바운드 NAT](https://docs.tigera.io/calico/latest/networking/configuring/workloads-outside-cluster)를 참고하세요. ### 토폴로지 기반 자동 할당 클러스터 /16 범위 안에서 겹치지 않는 /18 pool 두 개를 계획하는 별도 예입니다. 할당 중인 상위 /16 pool과 공존하면 안 됩니다. 예제에 맞추려고 사용 중인 상위 pool을 삭제하지 마세요. ```yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: zone-a-pool spec: cidr: 10.244.0.0/18 ipipMode: Never vxlanMode: CrossSubnet natOutgoing: true nodeSelector: topology.kubernetes.io/zone == 'ap-northeast-2a' --- apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: zone-b-pool spec: cidr: 10.244.64.0/18 ipipMode: Never vxlanMode: CrossSubnet natOutgoing: true nodeSelector: topology.kubernetes.io/zone == 'ap-northeast-2b' ``` 자동 할당에서는 모든 대상 노드가 사용 가능한 pool과 매칭되는지 확인합니다. Selector는 Pod를 스케줄링하지 않습니다. 위 zone 라벨은 할당 pool을 선택하고 CrossSubnet의 캡슐화 판단은 여전히 노드 주소·마스크를 사용합니다. ### 네임스페이스·Pod의 명시적 pool 요청 Pool을 먼저 생성·확인한 뒤 요청해야 합니다. 아래는 네임스페이스의 기존 소유자를 통해 추가할 annotation 조각이며 전체 네임스페이스 대체 객체가 아닙니다. Pod annotation이 네임스페이스 annotation보다 우선하고 CNI pool 설정보다도 우선합니다. ```yaml metadata: annotations: cni.projectcalico.org/ipv4pools: '["production-pool"]' ``` `production-pool`은 충분한 주소를 가진 기존 활성 pool이어야 합니다. `assignmentMode: Manual`은 자동 선택에서 제외하면서 명시적 요청을 허용하는 데 사용할 수 있습니다. **Pool selector나 annotation은 보안 경계가 아닙니다.** [릴리스 IPAM 구현](https://github.com/projectcalico/calico/blob/v3.32.2/libcalico-go/lib/ipam/ipam.go)은 활성 pool을 명시적으로 요청하면 호환성을 위해 노드·네임스페이스 pool selector를 무시합니다. 주소 대역을 신뢰 기준으로 삼는다면 pool 요청 권한을 통제해야 합니다. 기존 Pod의 주소는 유지되며 annotation 변경만으로 다시 할당되지 않습니다. ## 클라우드·플랫폼 범위 | 환경 | 기준 | |---|---| | 자체 관리 AWS EC2 | 선택한 모드의 IP protocol 4·VXLAN UDP 허용, 라우트, source/destination check와 반환 경로 확인 | | Amazon VPC CNI를 쓰는 EKS | 기본 Pod 네트워킹은 Calico VXLAN이 아닌 VPC CNI, policy-only Calico가 이 pool들을 소유하지 않음 | | 전체 Calico 네트워킹 EKS | Calico CNI·IPAM의 별도 계획된 설치, [공식 EKS 절차](https://docs.tigera.io/calico/latest/getting-started/kubernetes/managed-public-cloud/eks) 사용 | | Calico가 네트워킹을 맡는 Azure | Calico 오버레이 가이드는 IPIP가 지원되지 않는 환경에 VXLAN을 지원, UDR 설정이 미지원 IPIP 캡슐화를 해결하지는 않음 | | AKS | 특정 Azure CNI·정책 통합의 지원 절차 사용, 일반 Calico 오버레이로 가정하지 않음 | | GCE / GKE | 자체 관리 GCE 라우팅과 관리형 GKE를 구분, [GKE Dataplane V2는 Cilium](https://cloud.google.com/kubernetes-engine/docs/concepts/dataplane-v2) | | 온프레미스 | 언더레이 연결성에 따라 Direct·정적/BGP 라우팅·오버레이 선택, 항상 가장 빠른 한 가지 모드는 없음 | | OpenStack Neutron 통합 | 인용한 Calico 오버레이 가이드가 제외하는 통합, 플랫폼 절차 없이 Kubernetes 오버레이 예제를 복사하지 않음 | 이 장은 EKS Auto Mode·Fargate용 커스텀 CNI 설치 절차를 제공하지 않습니다. VXLAN 예제에서 BGP를 끈다는 것은 그 구성에 필요하지 않다는 뜻이지 AWS에 BGP를 사용하는 서비스가 없다는 뜻은 아닙니다. Windows에는 Calico IPIP·VXLAN CrossSubnet 미지원 등 별도의 제약도 있습니다. ## MTU 설정과 확인 암호화·Service 경로를 포함해 워크로드가 사용할 수 있는 경로 중 가장 작은 유효 MTU를 사용합니다. [Calico MTU 가이드](https://docs.tigera.io/calico/latest/networking/configuring/mtu)는 자동 감지와 operator·매니페스트 관리 방식을 설명합니다. `mtuIfacePattern`은 감지에 사용할 인터페이스 선택식이며 활성화 스위치나 종단 간 경로 MTU의 증거가 아닙니다. **IPIP와 WireGuard 오버헤드를 무조건 합산하지 마세요.** 일반적인 Calico 혼합 배포에서는 활성 peer 간 WireGuard를 사용하고 다른 경로에 IPIP·VXLAN을 사용합니다. 적용되는 MTU 중 최솟값을 선택합니다. 실제 경로 MTU가 1500일 때 IPv4 WireGuard와 IPIP의 조합은 `min(1440, 1480) = 1440`이며 `1500 − 60 − 20 = 1420`이 아닙니다. 외부 IPv6 WireGuard는 별도로 80바이트 오버헤드입니다. AKS에는 문서화된 WireGuard 예외가 있습니다. 인터페이스가 1500이어도 하부 경로는 1400일 수 있어 IPv4 WireGuard는 1340, IPv6는 1320이 됩니다. eBPF NodePort 경로도 VXLAN을 사용하므로 비캡슐화 Pod pool만으로 워크로드 MTU 1500을 보장할 수는 없습니다. Operator 설치에서 **해당 IPv4 VXLAN 경로에 1450이 적절함을 확인한 뒤** 기존 원하는 상태에 병합합니다. ```bash kubectl patch installation.operator.tigera.io default --type merge -p '{"spec":{"calicoNetwork":{"mtu":1450}}}' ``` 매니페스트 관리 설치의 문서화된 설정은 `calico-config.data.veth_mtu`입니다. 해당 ConfigMap 변경 후 절차에 따라 Calico 노드 DaemonSet을 롤링합니다. Operator 관리 배포에 매니페스트 절차를 섞지 마세요. **변경된 워크로드 MTU는 새 워크로드에 적용됩니다.** calico-node 재시작만으로 애플리케이션 Pod가 재생성되거나 MTU 변경이 입증되지는 않습니다. | 언더레이 IP MTU 예 | IPv4 IPIP | IPv4 VXLAN | IPv6 VXLAN | IPv4 WireGuard | IPv6 WireGuard | |---|---|---|---|---|---| | 9000 | 8980 | 8950 | 8930 | 8940 | 8920 | | 실제 AWS 경로가 지원하는 9001 | 8981 | 8951 | 8931 | 8941 | 8921 | 전체 경로가 점보를 지원해야 하며 인터페이스 설정만으로는 부족합니다. 워크로드 경로는 노드뿐 아니라 진단 워크로드에서 확인하세요. 다음 제한된 시험은 iputils와 필요한 권한이 있는 승인된 Linux 진단 Pod를 가정합니다. 실제 Pod 이름·주소를 사용하세요. 아래는 **IPv4 ICMP** 페이로드 크기이며 IPv4 20바이트와 ICMP 8바이트를 더합니다. IPv6 계산은 다르고 성공한 probe가 모든 ECMP 경로의 안전성을 증명하지는 않습니다. ```bash CHECK_NS=calico-demo CHECK_POD=diagnostic-client CHECK_TARGET=diagnostic-server DEST_IPV4=$(kubectl -n "$CHECK_NS" get pod "$CHECK_TARGET" -o jsonpath='{.status.podIP}') case "$DEST_IPV4" in ""|*:*) echo "Select a ready target Pod with an IPv4 address" >&2; exit 1 ;; esac kubectl -n "$CHECK_NS" exec "$CHECK_POD" -- ip link show eth0 kubectl -n "$CHECK_NS" exec "$CHECK_POD" -- ping -4 -c 3 -W 2 -M do -s 1472 "$DEST_IPV4" kubectl -n "$CHECK_NS" exec "$CHECK_POD" -- ping -4 -c 3 -W 2 -M do -s 1452 "$DEST_IPV4" kubectl -n "$CHECK_NS" exec "$CHECK_POD" -- ping -4 -c 3 -W 2 -M do -s 1422 "$DEST_IPV4" ``` 세 페이로드는 IP 패킷 크기 1500·1480·1450을 시험합니다. 실패 원인은 MTU 외에 정책·ICMP 필터링일 수도 있습니다. 패킷 캡처에는 적절한 권한으로 IPv4 fragmentation-needed와 IPv6 Packet Too Big을 확인해야 하며 기존 IPv4 전용 필터는 IPv6를 포함하지 않았습니다. ## 모드 변경과 주소 이전 구분 캡슐화 변경과 Pod CIDR·block size 변경은 다릅니다. Calico는 캡슐화 설정 변경을 지원하지만 진행 중인 연결이 중단될 수 있습니다. 유지보수 변경 전에 언더레이 허용·경로·실제 MTU·데이터플레인 지원·복구를 검증하세요. 일반적인 마이그레이션 단계로 모든 노드나 네임스페이스 전체 Deployment를 재시작하지 마세요. operator 관리 pool은 다른 pool·설정을 보존하며 기존 Installation 목록의 `encapsulation`을 변경합니다. **독립 관리 IPv4 IPPool에 한해** 아래 모드 변경 예제는 CIDR·할당 설정을 보존하고 두 캡슐화 필드를 함께 바꿉니다. ```bash POOL_NAME=mode-demo-pool kubectl get ippool.projectcalico.org "$POOL_NAME" -o yaml > pool-before.yaml kubectl patch ippool.projectcalico.org "$POOL_NAME" --type merge -p '{"spec":{"ipipMode":"Never","vxlanMode":"Always"}}' ``` 무중단 보장은 아닙니다. 계획한 Direct→IPIP CrossSubnet 전환이 적절하다면 필드 조합은 `ipipMode: CrossSubnet` / `vxlanMode: Never`이며 pool CIDR을 대체할 필요는 없습니다. 검증한 MTU·주소 계획에 필요한 경우에만 선택한 애플리케이션을 자체 rollout·readiness 전략으로 재생성합니다. [PodDisruptionBudget](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/)은 Deployment 컨트롤러의 롤링 업데이트를 제한하지 않습니다. ### 별도의 IPPool·CIDR 이전 Calico가 IPAM을 맡고 오케스트레이터·네트워크 설계가 지원하는 경우에만 [pool 이전 절차](https://docs.tigera.io/calico/latest/networking/ipam/migrate-pools)를 사용합니다. 1. 기존 pool·Kubernetes/kube-proxy cluster CIDR·명시적 pool 요청·모든 할당을 조사합니다. Cluster CIDR 밖의 새 pool은 NAT를 바꾸거나 트래픽을 끊을 수 있습니다. 기존 예제의 10.245/16이 소개 실습의 10.244/16 클러스터와 자동 호환되지는 않습니다. 2. 소유자를 통해 검증한 비중첩 pool을 추가하고 기존 pool을 철수하기 전에 새 할당을 시험합니다. 기존 워크로드를 위해 기존 pool은 유지합니다. 3. 소유자에 맞는 방법으로 기존 pool의 새 할당을 막습니다. 독립 pool의 `spec.disabled: true`는 IPAM에서 제외합니다. Operator의 `nodeSelector: "!all()"`은 **자동 선택**을 막지만 명시적 기존 pool 요청은 selector를 우회하므로 그 요청도 제거해야 합니다. 4. 선택한 워크로드를 제한된 단위로 옮기며 주소·MTU·라우트·정책·애플리케이션 준비 상태를 확인합니다. Pod 재생성은 중단과 IP 변경을 일으킬 수 있으며 새 pool이 원활한 롤백을 보장하지 않습니다. 5. 터널·LoadBalancer 용도 등을 포함한 남은 할당·의존성을 모두 확인한 뒤에만 기존 pool을 폐기합니다. Pod 목록만으로는 부족합니다. 소유자에서 제거할 때 NAT·라우팅 영향도 고려해야 합니다. 읽기 전용 확인 명령: ```bash kubectl get ippools.projectcalico.org -o yaml calicoctl ipam show --show-blocks calicoctl ipam show --show-borrowed kubectl get pods --all-namespaces -o wide ``` Block size는 별도의 이전 대상입니다. 튜토리얼 매니페스트를 대체 적용해 기존 pool의 변경 불가능한 할당 구조를 바꾸면 안 됩니다. 기존의 “즉시 적용을 위한 calico-node 재시작” 예제는 워크로드 MTU나 애플리케이션 복구를 입증하지 못했습니다. ## 이전 벤치마크 기록 — 출처·조건 미검증 기존 영문·한글 페이지에는 서로 다른 수치가 있었고 원시 결과·전체 소프트웨어 버전·배치·재현 가능한 하네스가 제공되지 않았습니다. 두 기록을 아래에 보존하지만 하나의 실험이나 검증된 성능 보장으로 볼 수 없습니다. 이번 감사에서 다시 실행하지 않았습니다. ### 기록 A: 기존 영문 페이지 보고된 환경은 **AWS c5.xlarge 3대**, 표기상 10 Gbps 네트워크, iperf3 TCP **단일 스트림 60초**입니다. Placement group·Calico/커널 버전·지연 수집 방법은 제공되지 않았습니다. | 보고된 지표 | Direct | IPIP | VXLAN | |---|---|---|---| | 처리량, Gbps | 9.41 | 9.12 | 8.89 | | p99 지연, µs | 45 | 52 | 61 | | CPU, % per Gbps | 2.1 | 2.8 | 3.4 | AWS는 클러스터 placement group 밖의 일반적인 단일 플로우 한도 5 Gbps와 별도 예외 조건을 문서화합니다. 이 기록의 9 Gbps 이상 값을 새 배포 예측에 사용하려면 빠진 배치·경로 조건을 확인해야 합니다. “최대 10 Gbps”가 지속 베이스라인 대역폭을 뜻하지도 않습니다. [EC2 대역폭](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html)을 참고하세요. ### 기록 B: 기존 한글 페이지 | 보고된 지표 | Direct | IPIP | VXLAN | 표기된 방법 | |---|---|---|---|---| | 처리량, Gbps | 9.8 | 9.2 | 8.5 | iperf3, MTU 1500 | | 지연, µs (통계량 미지정) | 35 | 42 | 55 | netperf TCP_RR | | CPU 사용률 | 낮음 | 중간 | 중간-높음 | 10 Gbps 전송 시 | | PPS, 백만/초 | 1.8 | 1.5 | 1.2 | 64바이트 패킷 | 하드웨어·표본 수·“64바이트”의 정확한 의미는 제공되지 않았습니다. netperf TCP_RR의 기본 출력은 **초당 트랜잭션 수**입니다. 근거를 명시한 역수로 평균 요청·응답 주기를 추정할 수 있지만 p99나 순수한 단방향 네트워크 지연은 아닙니다. 보고된 마이크로초 값의 원시 출력·환산 과정이 없습니다. 헤더 크기만으로 빠른 모드를 결정할 수는 없습니다. NIC offload·커널/데이터플레인·패킷 크기·CPU·경로·연결 재사용·부하가 결과를 바꿀 수 있습니다. 이 수치는 검증되지 않은 기록으로 보존하고 모드 순위의 근거로 쓰기보다 대상 환경을 측정해야 합니다. ### 새 실험용 제한된 클라이언트 시험 동일 버전의 iperf3·netperf, 실행 중인 서버 리스너, 필요한 정책 허용을 가진 전용 테스트 Pod를 준비합니다. 아래는 클라이언트 시험일 뿐 어느 기록의 완전한 재현 절차도 아닙니다. 버전·노드/AZ 배치·MTU·요청/응답 크기·원시 출력·반복 실행을 기록하세요. 이 예제에는 서버의 IPv4 주소를 사용합니다. ```bash set -euo pipefail BENCH_NS=calico-demo CLIENT_POD=benchmark-client SERVER_POD=benchmark-server SERVER_IP=$(kubectl -n "$BENCH_NS" get pod "$SERVER_POD" -o jsonpath='{.status.podIP}') : "${SERVER_IP:?Server Pod has no address}" case "$SERVER_IP" in *:*) echo "This example requires an IPv4 server Pod" >&2; exit 1 ;; esac kubectl -n "$BENCH_NS" get pods "$CLIENT_POD" "$SERVER_POD" -o wide kubectl -n "$BENCH_NS" exec "$CLIENT_POD" -- iperf3 -c "$SERVER_IP" -t 30 -P 4 -J > iperf3-result.json kubectl -n "$BENCH_NS" exec "$CLIENT_POD" -- netperf -H "$SERVER_IP" -t TCP_RR -l 60 > netperf-result.txt ``` iperf3 예제는 4개 스트림이므로 단일 스트림 기록 A의 방법과 다릅니다. [netperf 매뉴얼](https://github.com/HewlettPackard/netperf/blob/master/doc/netperf.txt)에서 출력 단위와 선택적 지연 출력을 확인하세요. 부하를 격리하고 이후에는 소유한 테스트 서버·리소스만 정리합니다. 출처 없는 그래프를 재현하려고 운영 네트워크 모드를 바꾸지 마세요. [Calico 개요](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) · [아키텍처](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/02-architecture.md) · [다음: BGP 심층 분석](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md) · [네트워킹 모드 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/03-networking-modes-quiz) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/04-bgp-deep-dive ---------------------------------------- # Part 4: BGP 심화 > **검토 기준**: Calico 3.32.2; Calico 3.32의 Kubernetes 테스트 범위는 1.34–1.36입니다. **마지막 업데이트**: 2026년 9월 12일. > > 설정 예시는 BGP가 활성화되고 표준 Calico API 서버(`projectcalico.org/v3`)가 설치된 Linux 클러스터를 전제로 합니다. 각 예시는 서로 다른 토폴로지 대안이며 순서대로 모두 적용하는 매니페스트가 아닙니다. 기존 operator/GitOps 소유권을 유지하고 의도한 필드만 기존 설정에 병합하세요. API 설치 전제는 [설치 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/01-introduction.md), BGP 없는 라우팅 대안은 [네트워킹 모드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)를 참고하세요. 주소·ASN·CIDR은 관리 권한이 있는 실제 네트워크에 맞춰야 합니다. 이번 검토에서는 실제 fabric이나 클러스터 장애 전환을 실행하지 않았습니다. > 📎 BGP 자체가 생소하다면 [네트워크 기초 Part 1](https://www.atomai.click/kubernetes-docs/llms/ko/basics/06-network-fundamentals-part1.md)의 BGP 절을 먼저 읽어보세요. ## 개요 BGP(Border Gateway Protocol)는 경로 도달성 정보를 교환하는 제어 평면 프로토콜입니다. Calico는 BGP로 워크로드 경로를 배포하고 기존 routed fabric에 통합할 수 있습니다. BGP는 비캡슐화 라우팅이나 IP-in-IP와 함께 사용할 수 있으며 성능 우위를 자체 보장하지 않습니다. Calico 3.32에는 BGP 없이 Felix가 클러스터 경로를 관리하는 방식도 있지만 외부 BGP 광고에는 BGP speaker가 필요합니다. Cilium도 BGP 제어 평면을 제공하므로 BGP를 Calico만의 기능으로 구분하지 않습니다. 이 문서에서는 BGP의 기본 개념부터 Calico에서의 고급 BGP 구성까지 심층적으로 다룹니다. ## BGP 기본 개념 ### BGP란? BGP(Border Gateway Protocol)는 인터넷의 핵심 라우팅 프로토콜로, 자율 시스템(Autonomous System, AS) 간에 라우팅 정보를 교환합니다. 현재 BGP-4가 표준이며, RFC 4271에 정의되어 있습니다. ### AS 번호 (Autonomous System Number) AS 번호는 BGP에서 네트워크를 식별하는 고유 번호입니다. | 구분 | ASN | | --- | --- | | 16비트 프라이빗 | 64512–65534 | | 32비트 프라이빗 | 4200000000–4294967294 | | 문서·예시용 | 64496–64511, 65536–65551 | | AS_TRANS | 23456 | 그 밖의 값에도 예약·미할당 범위가 있으므로 [IANA 레지스트리](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml)를 확인하세요. Calico의 기본 ASN은 64512입니다. 프라이빗 ASN은 IP 주소처럼 “라우팅 불가능”한 값이 아닙니다. 해당 ASN이 포함된 AS_PATH를 글로벌 인터넷에 유출하지 않도록 경계에서 처리해야 합니다. **Calico에서의 AS 번호 사용 권장사항:** ```yaml # 권장: 프라이빗 AS 범위 사용 apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: asNumber: 64512 # 프라이빗 AS 범위 (64512-65534) ``` ### iBGP vs eBGP | 특성 | iBGP | eBGP | | --- | --- | --- | | AS 관계 | 동일 AS | 서로 다른 AS | | AS_PATH | 일반적으로 유지 | 일반적으로 로컬 AS 추가 | | 경로 전파 | iBGP로 배운 경로는 다른 iBGP 피어에 보통 재광고하지 않음. RR은 예외 | export 정책과 루프 방지 규칙에 따름 | | Next hop | 대체로 유지하며 도달 가능해야 함 | 대체로 변경하며 `nextHopMode`와 토폴로지에 영향받음 | | TTL·Administrative Distance | 구현·설정에 따라 다름 | 구현·설정에 따라 다름 | 로컬 생성 경로나 eBGP로 배운 경로는 iBGP 피어에 광고할 수 있습니다. Cisco의 Weight나 Administrative Distance 20/200을 Calico BIRD의 속성·기본값으로 적용하지 마세요. Calico가 생성하는 외부 피어 설정에는 BIRD multihop이 포함되므로 “eBGP TTL은 항상 1”이라고 판단할 수 없습니다. ### BGP 경로 선택 알고리즘 Calico 3.32.2는 BIRD fork `v0.3.3-211-g9111ec3c`를 사용합니다. 비교 가능한 유효 BGP 경로에서는 높은 LOCAL_PREF, 짧은 AS_PATH(비교 활성화 시), 낮은 ORIGIN, 적용 가능한 인접 AS 정책에 따른 낮은 MED, eBGP 우선, 낮은 IGP metric 순으로 비교합니다. 남은 동률에는 Router/ORIGINATOR_ID, CLUSTER_LIST 길이, 피어 IP를 사용하며 older-route 옵션이 동률 처리를 바꿀 수 있습니다. 경로 억제·next-hop 도달성·stale 상태·BIRD route preference도 판단에 관여합니다. 이는 Cisco Weight부터 시작하는 보편적인 11단계 알고리즘이 아닙니다. Calico 3.32는 자체 route priority를 LOCAL_PREF 및 커널 metric에 반영하므로 로컬 광고 경로의 LOCAL_PREF를 모두 upstream 기본값 100으로 가정하지 마세요. ## Calico BGP 아키텍처 ### Full-Mesh 토폴로지 BGP와 기본 node mesh가 활성화되면 참여하는 일반 노드끼리 full-mesh를 구성합니다. Route Reflector로 지정된 노드는 자동 mesh에서 제외됩니다. BGP 비활성화 설치에는 이 설명이 적용되지 않습니다. ![노드 다섯 개의 모든 쌍을 연결하는 열 개의 full-mesh 세션.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-04-bgp-deep-dive-3.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-04-bgp-deep-dive-3.html) > 화살표는 단방향 트래픽이 아닌 양방향 세션을 열거합니다. 해당 주소 패밀리의 노드 쌍당 세션 하나를 계산합니다. **Full-Mesh BGP 세션 계산:** 세션 수 = N × (N-1) / 2 | 전체 노드 수 | 전체 mesh 세션 수 | 노드별 피어 수 | | --- | --- | --- | | 5 | 10 | 4 | | 10 | 45 | 9 | | 50 | 1,225 | 49 | | 100 | 4,950 | 99 | | 200 | 19,900 | 199 | 주소 패밀리별 노드 쌍당 세션 하나를 가정한 계산입니다. 실제 CPU·메모리와 수렴 시간은 경로 수, 변경 빈도, 정책, 하드웨어에 좌우됩니다. 50개 이상은 불가하다는 고정 제한이나 노드당 메모리 수치를 이 계산에서 도출할 수 없습니다. ### Route Reflector 토폴로지 Route Reflector(RR)는 iBGP의 full-mesh 요구사항을 해결합니다. ![클라이언트 여섯 개가 서로 피어링한 RR 두 대에 각각 연결되는 구조.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-04-bgp-deep-dive-4.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-04-bgp-deep-dive-4.html) > 그림은 클라이언트 6개와 RR 2개, 총 8노드의 13세션입니다. 그림의 2N+1은 N을 클라이언트 수로, full-mesh 공식은 전체 노드 수로 사용하므로 구분하세요. 자동 mesh는 명시적 대체 토폴로지 검증 후에 끕니다. **Route Reflector 동작 원리:** 1. **클라이언트로부터 경로 수신**: RR은 클라이언트 노드의 경로를 수집 2. **경로 반사 (Reflection)**: 수집된 경로를 다른 클라이언트들에게 전파 3. **루프 방지**: Cluster ID와 Originator ID로 라우팅 루프 방지 **세션 수 계산과 설계:** 전체 노드를 `T`, RR 수를 `R`, 클라이언트 수를 `C=T−R`로 정의합니다. 모든 클라이언트가 모든 RR과 피어링하고 RR끼리 mesh를 맺으면: ```text RR 세션 수 = C×R + R×(R−1)/2 T=100, R=2: 98×2+1 = 197 (동일한 전체 100노드 full-mesh: 4,950) T=500, R=2: 498×2+1 = 997 (동일한 전체 500노드 full-mesh: 124,750) ``` 100개가 클라이언트 수이고 RR 두 대가 추가되는 경우에만 201세션이며, 그때 전체 노드는 102개입니다. RR 수·계층은 고정 노드 수 표보다 경로 변경량, 장애 영역, 장애 후 용량 및 수렴 목표로 정하세요. ### Route Reflector 구성 전환에는 준비된 워크로드 없는 RR 노드를 사용합니다. Cluster ID를 설정하면 해당 노드는 즉시 자동 mesh에서 제외되므로 사용 중인 노드를 그대로 바꾸면 연결이 끊길 수 있습니다. 아래 Kubernetes datastore 방식은 기존 노드 IP와 다른 필드를 보존합니다. #### 1. 준비된 RR 노드에 레이블·annotation 설정 ```bash kubectl label node rr-node-1 rr-node-2 route-reflector=true kubectl annotate node rr-node-1 rr-node-2 projectcalico.org/RouteReflectorClusterID=244.0.0.1 ``` 공유 ID는 동일 클라이언트를 담당하는 중복 RR 그룹을 식별합니다. Kubernetes 클러스터 ID가 아니며 다른 RR 그룹·계층에는 별도 ID 설계가 필요합니다. #### 2. 명시적 피어링 생성 ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: peer-to-rr spec: nodeSelector: "!has(route-reflector)" peerSelector: "has(route-reflector)" --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rr-mesh spec: nodeSelector: "has(route-reflector)" peerSelector: "has(route-reflector)" ``` `peerSelector`는 Calico 노드를 선택하며 `reversePeering: Manual`을 지정하지 않으면 역방향 피어링도 자동 생성합니다. 임의의 외부 라우터를 검색하는 기능이 아닙니다. #### 3. 기존 경로를 제거하기 전에 검증 두 RR과 클라이언트에서 Established 세션, 예상 워크로드 경로의 송수신, next-hop 도달성, 대표 노드 간 트래픽을 확인합니다. RR 하나가 중단되는 시나리오에서도 전달 경로와 용량이 유지되는지 검증하세요. 전환 중에는 일반 클라이언트 간 mesh를 유지할 수 있습니다. #### 4. 검증 후 자동 mesh 비활성화 ASN·커뮤니티 등 다른 설정을 유지하며 관리 중인 `BGPConfiguration/default` 매니페스트를 갱신하세요. 이미 존재하는 리소스에 대한 동등한 merge patch는 다음과 같습니다. ```bash kubectl patch bgpconfiguration.projectcalico.org default --type=merge -p '{"spec":{"nodeToNodeMeshEnabled":false}}' ``` `default`가 없다면 동일한 사전 검증 후 기존 설정 소유자를 통해 생성합니다. 변경 후 경로와 트래픽을 다시 확인하고 원래 토폴로지로 돌아갈 계획을 유지하세요. ## BGPPeer 리소스 상세 ### Global BGP Peer `node`와 `nodeSelector`를 생략한 피어는 모든 노드에 적용됩니다. 아래는 옵션을 설명하기 위해 특정 노드로 제한한 예시입니다. 먼저 보안 절의 `tor-policy` BGPFilter와 참조하는 Secret을 만들고 직접 연결된 상대 피어에도 동일한 GTSM·인증 설정을 적용해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: advanced-peer spec: node: specific-node-name peerIP: 192.168.1.1 asNumber: 65100 password: secretKeyRef: name: bgp-secrets key: datacenter-password keepaliveTime: 30s maxRestartTime: 120s sourceAddress: UseNodeIP nextHopMode: Auto ttlSecurity: 1 filters: - tor-policy ``` | 필드 | Calico 3.32.2에서의 의미 | | --- | --- | | `keepaliveTime` | 기간 문자열. 소문자 `a`를 사용하며 릴리스 CRD·renderer에서 확인한 필드입니다. | | `maxRestartTime` | 피어에 알리는 Graceful Restart 시간. 연결 재시도 간격이 아닙니다. | | `sourceAddress` | `UseNodeIP` 또는 `None`. 임의의 IP 문자열은 허용하지 않습니다. | | `filters` | 기존 `BGPFilter` 이름 목록이며 규칙 객체를 직접 넣지 않습니다. | | `ttlSecurity` | GTSM 경로의 edge 수. `1`은 직접 연결된 피어입니다. | | `numAllowedLocalASNumbers` | 수신 AS_PATH에 허용할 로컬 ASN의 출현 수. 루프 방지를 완화하며 multihop 설정이 아닙니다. 명시적 설계가 없으면 생략합니다. | 현재 `BGPPeer` API에는 `holdTime`, `keepAliveTime`, `restartTime` 필드가 없습니다. `nextHopMode`는 `Auto`, `Self`, `Keep`을 지원하며 이전 `keepOriginalNextHop`은 deprecated이지만 제거되지는 않았습니다. ### Node-specific BGP Peer 특정 노드에만 적용되는 BGP 피어 설정입니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rack1-tor-peer spec: # 특정 노드에만 적용 nodeSelector: "rack == 'rack1'" # 피어의 IP 주소 peerIP: 192.168.10.1 # 피어의 AS 번호 asNumber: 64520 # keepAlive 시간 keepaliveTime: 10s # MD5 인증 password: secretKeyRef: name: bgp-secrets key: rack1-password --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rack2-tor-peer spec: nodeSelector: "rack == 'rack2'" peerIP: 192.168.20.1 asNumber: 64521 keepaliveTime: 10s password: secretKeyRef: name: bgp-secrets key: rack2-password ``` ### peerSelector를 사용한 동적 피어링 대상은 Calico Node 레이블입니다. `bgp-peer=external`이라는 레이블 이름도 실제 외부 라우터를 자동 검색하지 않습니다. 외부 라우터는 `peerIP`와 `asNumber`로 정의하세요. ```yaml # 특정 레이블을 가진 노드들과 동적으로 피어링 apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: dynamic-peer spec: # 소스 노드 선택 (어떤 노드에서 피어링할지) nodeSelector: "zone == 'zone-a'" # 대상 노드 선택 (누구와 피어링할지) peerSelector: "bgp-peer == 'external'" # AS 번호는 대상 노드의 spec.bgp.asNumber 사용 ``` ## BGPConfiguration 리소스 상세 ### 기본 설정 ```yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: # 로컬 AS 번호 (전체 클러스터) asNumber: 64512 # Node-to-Node mesh 활성화 여부 nodeToNodeMeshEnabled: true # 로그 수준 logSeverityScreen: Info ``` ### Service IP 광고 설정 광고는 주소 할당, 클라우드 로드 밸런서 생성, 왕복 경로 확보와 별개입니다. 아래 CIDR은 예시이며 필요한 범위만 기존 설정에 병합하세요. 배열을 교체하여 기존 광고 범위를 잃지 않도록 확인해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: asNumber: 64512 # Service External IP 광고 serviceExternalIPs: - cidr: 203.0.113.0/24 # Service LoadBalancer IP 광고 serviceLoadBalancerIPs: - cidr: 198.51.100.0/24 # Service ClusterIP 광고 (선택적, 일반적으로 비권장) serviceClusterIPs: - cidr: 10.96.0.0/12 ``` ### Service 주소 할당과 광고의 구분 기본 집계 동작에서 Cluster 모드 Service는 설정된 집계 경로를 사용하며 Local 모드 Service는 준비된 로컬 endpoint가 있는 노드의 host route(`/32` 또는 `/128`)를 사용합니다. 명시적 host-prefix 범위나 Calico 3.32의 `serviceLoadBalancerAggregation` 설정에 따라 광고 경로가 달라질 수 있으므로 Service 유형만으로 판단하지 말고 실제 RIB/export를 확인하세요. 정상 endpoint, Service 데이터 평면, upstream ECMP, 반환 경로도 함께 검증합니다. Pod IPAM 블록 광고와는 별개입니다. Kubernetes가 ClusterIP를 할당합니다. `spec.externalIPs`는 운영자가 소유하고 라우팅하는 주소를 지정하는 기능이며 Kubernetes 1.36부터 deprecated입니다. 제거되었다는 뜻은 아닙니다. LoadBalancer 주소는 호환되는 컨트롤러가 할당해야 하며 클라우드 LB의 hostname을 BGP IP 접두사로 광고할 수 없습니다. ### Calico 자체 LoadBalancer IPAM Calico 3.32의 `calico-kube-controllers`에는 LoadBalancer 컨트롤러가 있습니다. `allowedUses: [LoadBalancer]`인 IPPool이 필요하며 일반 Pod 풀에서 자동으로 주소를 가져오는 것은 아닙니다. 해당 컨트롤러가 활성화되어 있는지 확인하세요. 아래 독립적인 bare-metal 예시는 `calico-demo` namespace와 지정 포트에서 준비된 `app=my-app` endpoint를 전제로 합니다. 문서용 CIDR을 소유한 실제 라우팅 가능 범위로 바꾸고 기존 BGPConfiguration 필드를 유지하며 병합하세요. ```yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: service-lb-pool spec: cidr: 198.51.100.0/24 allowedUses: - LoadBalancer assignmentMode: Automatic --- apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: serviceLoadBalancerIPs: - cidr: 198.51.100.0/24 --- apiVersion: v1 kind: Service metadata: name: my-lb-service namespace: calico-demo annotations: projectcalico.org/loadBalancerIPs: '["198.51.100.50"]' spec: type: LoadBalancer loadBalancerClass: calico externalTrafficPolicy: Local selector: app: my-app ports: - port: 443 targetPort: 8443 ``` 명시한 `projectcalico.org/loadBalancerIPs` 주소가 적합한 풀에 속하고 사용 가능해야 합니다. 요청 주소를 할당할 수 없어도 다른 주소로 자동 fallback하지 않습니다. 주소 할당과 BGP 광고는 별도 단계입니다. `assignIPs: RequestedServicesOnly`로 변경하면 기존 annotation 없는 Service의 주소가 해제될 수 있으므로 기존 컨트롤러·풀 소유권과 할당 상태를 먼저 확인하세요. MetalLB를 할당자로 선택할 수도 있으며 현재 IP 요청 annotation은 `metallb.io/loadBalancerIPs`입니다. 같은 VIP에 대한 할당자·BGP speaker 소유권이 경쟁하지 않도록 설계하세요. AWS가 관리하는 로드 밸런서 IP를 로컬 소유 풀처럼 광고하지 않습니다. ### 특정 Service 광고 제한 `projectcalico.org/bgp-advertise`라는 Calico Service 광고 제외 annotation은 문서화된 기능이 아닙니다. `BGPConfiguration`의 범위와 피어별 BGPFilter를 사용합니다. 지원되는 `node.kubernetes.io/exclude-from-external-load-balancers=true` 레이블은 노드 전체 제외이며 Service별 opt-out이 아닙니다. 특정 `/32`만 거부해도 이를 포함하는 Service 집계 경로가 남으면 그 주소에 도달할 수 있습니다. 내부 전용 Service에는 광고 범위가 겹치지 않는지 확인하고 접근 정책도 별도로 적용하세요. 경로 필터는 권한 검사의 경계가 아닙니다. ### 접두사 광고 및 커뮤니티 태깅 현재 renderer의 `prefixAdvertisements`는 Pod 경로를 포함해 CIDR에 일치하는 기존 경로에 커뮤니티를 추가합니다. 지정한 접두사를 새로 생성하거나 모든 Pod 블록을 하나로 집계하지 않습니다. 이름만 정의한 커뮤니티는 적용되지 않으며, `internal-only`·`high-priority`라는 이름이나 임의의 숫자 자체가 차단·우선순위 정책을 만들지는 않습니다. 해당 태그를 처리하는 라우터 정책을 별도로 구성해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: asNumber: 64512 # BGP 커뮤니티 정의 communities: - name: internal-only value: "64512:100" - name: advertise-to-upstream value: "64512:200" - name: low-priority value: "64512:50" - name: high-priority value: "64512:500" # 접두사별 광고 설정 prefixAdvertisements: # Pod CIDR - 내부용 정책으로 해석할 태그 (수신 라우터 정책 필요) - cidr: 10.244.0.0/16 communities: - internal-only # 기존 LoadBalancer 경로에 태그 추가 - cidr: 198.51.100.0/24 communities: - advertise-to-upstream - high-priority # 기존 External IP 경로에 태그 추가 (낮은 우선순위는 별도 정책) - cidr: 203.0.113.0/24 communities: - advertise-to-upstream - low-priority ``` ### BGP 리스너 설정 ```yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: asNumber: 64512 # BGP 리스닝 포트 (기본값: 179) listenPort: 179 # 바인드 모드 # - None: 특정 주소로 제한하지 않고 listen # - NodeIP: 노드 IP에 바인드 (변경 후 calico-node 재시작 필요) bindMode: NodeIP # 자동 node mesh 피어링의 MD5 인증 (IPv4 활성화 스위치가 아님) nodeMeshPassword: secretKeyRef: name: bgp-secrets key: mesh-password ``` ## 물리 네트워크 통합 ### ToR (Top of Rack) 스위치 연동 라우터의 ASN, 노드 피어, 주소 패밀리, 인증, import/export 정책, next-hop 도달성을 함께 설계합니다. 노드가 기존 underlay 기본 경로를 사용하는지 BGP로 기본 경로를 받는지 먼저 정하세요. `network`는 일치하는 기존 경로를 생성·광고하기 위한 설정이지 수신 허용 명령이 아닙니다. 광범위한 `redistribute connected`는 관계없는 네트워크를 유출할 수 있습니다. | 플랫폼 | 적용 시 확인할 점 | | --- | --- | | Cisco IOS XE / NX-OS | 정확한 플랫폼·릴리스 구문을 사용합니다. IOS XE 동적 피어는 peer group과 `bgp listen range`를 사용하며 IOS와 NX-OS 명령 계층을 혼합하지 않습니다. 참조하는 route map·prefix list를 모두 정의해야 합니다. | | Arista EOS | 배포한 릴리스의 peer group, 주소 패밀리, 비밀 설정, import/export 정책을 사용합니다. 이전의 검증되지 않은 EOS 명령 블록을 실행 가능한 절차로 사용하지 않습니다. | | Junos | 단순 prefix-list는 정확한 접두사 매칭입니다. more-specific 경로가 필요하면 route-filter의 매칭 형식을 명시합니다. | 다음은 ToR의 해당 노드 방향 BGP 그룹에 import 정책으로 연결할 **Junos 정책 조각**입니다. 계획된 Pod `/26`–`/32`, LoadBalancer `/32` 경로를 허용하고 나머지를 거부합니다. ```text policy-options { policy-statement K8S-IMPORT { term approved { from { route-filter 10.244.0.0/16 prefix-length-range /26-/32; route-filter 198.51.100.0/24 prefix-length-range /32-/32; } then accept; } term reject-rest { then reject; } } } ``` Pod 최소 길이는 IPAM 블록이 `/26`이라는 가정이며 실제 풀·경로 목록에 맞게 조정해야 합니다. borrowed 주소나 일부 이동 경로에는 `/32`가 필요하므로 `le 26`을 보편적 필터로 사용하지 마세요. 이 조각만으로 피어나 기본 경로를 만들지는 않습니다. 실제 라우터 실행·장애 전환은 검증하지 않았으므로 해당 릴리스에서 export 정책, 경로 수 제한, next-hop 처리를 완성하고 검증해야 합니다. ### Spine-Leaf 아키텍처 통합 ![노드가 로컬 leaf와, leaf가 spine 계층과 피어링하는 구조.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-04-bgp-deep-dive-5.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-04-bgp-deep-dive-5.html) > 묶인 상자는 여러 세션을 요약합니다. 공통 노드 ASN에는 명시적인 AS-loop·override 설계가 필요하며 spine 중복만으로 leaf나 노드 uplink까지 중복되지 않습니다. 실제 Secret·주소·ASN·반환 경로도 별도로 준비해야 합니다. #### Calico 설정 (Spine-Leaf 통합) 노드 레이블, next-hop·반환 경로, 양방향 정책과 각 Secret 키를 먼저 준비하세요. 여러 랙의 노드에 같은 ASN을 재사용하면 수신 AS_PATH에 자신의 ASN이 포함되어 경로가 거부될 수 있습니다. 고유 ASN이나 검증한 fabric AS-override·루프 정책을 설계해야 하며 `numAllowedLocalASNumbers`를 무조건 올려 우회하지 않습니다. ```yaml # 랙별 BGP 피어 설정 apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rack1-leaf-peer spec: nodeSelector: "rack == 'rack1'" peerIP: 10.0.10.1 asNumber: 64520 keepaliveTime: 10s password: secretKeyRef: name: bgp-secrets key: rack1-leaf-password --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rack2-leaf-peer spec: nodeSelector: "rack == 'rack2'" peerIP: 10.0.20.1 asNumber: 64521 keepaliveTime: 10s password: secretKeyRef: name: bgp-secrets key: rack2-leaf-password --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rack3-leaf-peer spec: nodeSelector: "rack == 'rack3'" peerIP: 10.0.30.1 asNumber: 64522 keepaliveTime: 10s password: secretKeyRef: name: bgp-secrets key: rack3-leaf-password ``` ## BGP 보안 ### MD5 인증 Calico는 BGP에 TCP MD5 signature를 지원합니다. 공유 비밀을 가진 피어의 트래픽을 인증하지만 암호화나 인증된 피어가 보낸 경로의 정당성을 보장하지 않습니다. `calico-node`가 실행되는 namespace에 비밀 관리 절차로 `bgp-secrets`를 준비하세요. 여기의 operator 설치는 `calico-system`이며 manifest 설치는 `kube-system`일 수 있습니다. 아래 예시는 `datacenter-password` 키가 필요합니다. 다른 예시가 참조하는 `mesh-password` 및 랙·leaf별 키도 별도로 준비하고 상대 라우터에 일치하는 비밀을 설정해야 합니다. Calico 서비스 계정의 Secret 읽기 권한도 확인하세요. ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: secure-peer spec: peerIP: 192.168.1.1 asNumber: 65100 password: secretKeyRef: name: bgp-secrets key: datacenter-password ``` ### 접두사 필터링 규칙은 순서대로 평가하고 첫 일치 시 즉시 동작합니다. 어떤 규칙에도 일치하지 않으면 기본 **Accept**이므로 허용 목록에는 무조건 거부하는 마지막 규칙이 필요합니다. `Equal 0.0.0.0/0`은 기본 경로만, `In 0.0.0.0/0`은 모든 IPv4 경로를 매칭합니다. `NotIn 0.0.0.0/0`에는 어떤 IPv4 경로도 일치하지 않습니다. 다음 외부 피어 예시는 기본 경로와 계획된 underlay `10.0.0.0/16`만 수신합니다. 송신에는 실제 Pod `/26`–`/32`, LoadBalancer `/32` 경로만 허용합니다. 실제 경로 목록에 맞게 CIDR·길이를 조정하고 RR/클라이언트 세션에 이 외부 정책을 무차별 적용하지 마세요. ```yaml apiVersion: projectcalico.org/v3 kind: BGPFilter metadata: name: tor-policy spec: importV4: - action: Accept matchOperator: Equal cidr: 0.0.0.0/0 - action: Accept matchOperator: In cidr: 10.0.0.0/16 - action: Reject exportV4: - action: Accept matchOperator: In cidr: 10.244.0.0/16 prefixLength: min: 26 max: 32 operations: - addCommunity: value: "64512:100" - action: Accept matchOperator: In cidr: 198.51.100.0/24 prefixLength: min: 32 max: 32 - action: Reject --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: filtered-peer spec: peerIP: 192.168.1.1 asNumber: 65100 filters: - tor-policy ``` `prefixLength`는 범위 문자열이 아니라 `min`·`max` 객체입니다. Calico 3.32에는 허용 경로의 `addCommunity` 같은 operation도 있습니다. 명시적 export Accept는 기본 Calico export·집계·`prefixAdvertisements` 처리 전에 반환하므로 RIB에 있는 more-specific 경로도 광고할 수 있습니다. 따라서 예시는 규칙 안에서 Pod 태그를 추가합니다. fabric 적용 전에 `show route export`로 실제 광고를 확인하세요. BGPFilter가 없는 경로를 생성하지는 않습니다. ### GTSM (TTL Security) GTSM은 경로 길이에 비해 TTL이 작은 패킷을 거부하여 off-path 스푸핑 노출을 줄입니다. 피어 인증이나 동일 링크 공격 방어를 대체하지 않습니다. 양쪽 피어를 일치하게 설정해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: gtsm-enabled-peer spec: peerIP: 192.168.1.1 asNumber: 65100 ttlSecurity: 1 ``` 해당 BIRD 구현은 GTSM 송신 TTL 255, 최소 수신 TTL `256−hops`를 사용합니다. 따라서 `ttlSecurity: 1`은 254가 아닌 255를 요구하고, 두 edge는 최소 254입니다. 활성화 전에 실제 경로 길이를 확인하세요. 이 값은 AS_PATH에 허용할 로컬 ASN 출현 수와 무관합니다. ## 성능 튜닝 ### BGP 타이머 설정 ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: tuned-peer spec: peerIP: 192.168.1.1 asNumber: 65100 keepaliveTime: 20s maxRestartTime: 120s ``` 해당 BIRD fork의 기본 제안 Hold Time은 240초이며 상대가 제안한 값과 작은 쪽으로 협상합니다. keepalive 간격을 지정하지 않으면 협상된 Hold Time의 1/3을 사용합니다. 명시적 `keepaliveTime`은 전송 간격만 바꾸며 Hold Time을 자동으로 그 3배로 바꾸지 않습니다. 실제 협상 타이머를 확인하고 그 안에 맞는 간격을 선택하세요. `BGPPeer`에는 `holdTime` 필드가 없습니다. 기존 60/180·10/30·3/9 표는 검증된 Calico 기본값이나 장애 감지 보장이 아닙니다. BIRD 자체의 BFD 지원이 Calico의 BFD CRD·설정 필드 지원을 뜻하지 않으므로 임의의 필드를 추가하지 말고 별도 통합의 지원·실행 조건을 확인해야 합니다. ### Graceful Restart Calico BIRD 템플릿은 Graceful Restart를 활성화합니다. 피어와 기능을 협상하고 실제 전달 경로가 계속 동작해야 효과가 있으며 stale 경로 유지로 blackhole이 생길 수도 있습니다. 무중단 업데이트를 보장하지 않습니다. 명시적 피어에는 `BGPPeer.maxRestartTime`으로 광고할 재시작 시간을 지정합니다. 다음 필드는 모든 명시적 피어가 아닌 **자동 node mesh** 세션에 적용합니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: nodeMeshMaxRestartTime: 120s ``` 정수가 아닌 기간 문자열이며 활성화 스위치도 아닙니다. 기존 설정 소유자를 통해 변경하고 실제 피어 capability와 복구 동작을 검증하세요. ### 경로 집계 Calico는 일반적으로 로컬 IPAM 주소를 할당 블록으로 집계하며 현재 BIRD 집계 템플릿은 우선순위가 높은 more-specific 경로도 허용합니다. borrowed 주소·이동 경로에는 host route가 필요할 수 있습니다. `prefixAdvertisements`는 기존 경로에 태그를 붙일 뿐 `/26`들을 새 `/16` 경로로 만들지 않습니다. 큰 블록은 블록 경로 수와 주소 활용도·할당 세분성의 교환 관계입니다. 기존 IPPool의 `blockSize`는 변경할 수 없으므로 새 풀이 필요하면 [네트워킹 모드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)의 마이그레이션 절차를 따르세요. 기존 기본 풀에 새 blockSize를 덮어쓰거나 포함한 모든 목적지에 도달하지 못하는 라우터에서 집계 경로를 광고하면 안 됩니다. ## BGP 디버깅 ### 올바른 노드의 BIRD 조회 실제 노드 이름과 설치 namespace를 선택하세요. 다음 읽기 전용 명령은 운영자 셸에서 IPv4 BIRD control socket에 접속합니다. IPv6에는 `birdcl6`와 `/var/run/calico/bird6.ctl`을 사용합니다. BGP 비활성화 설치에는 데몬이 없을 수 있습니다. ```bash CALICO_NAMESPACE=calico-system CALICO_NODE=worker-1 CALICO_POD="$(kubectl -n "$CALICO_NAMESPACE" get pods -l k8s-app=calico-node \ --field-selector "spec.nodeName=$CALICO_NODE" -o jsonpath='{.items[0].metadata.name}')" test -n "$CALICO_POD" kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show protocols all kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show route ``` ```bash CALICO_BGP_PROTOCOL=Global_192_168_1_1 kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show protocols all "$CALICO_BGP_PROTOCOL" kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show route export "$CALICO_BGP_PROTOCOL" kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show route protocol "$CALICO_BGP_PROTOCOL" kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl 'show route where net ~ [10.244.0.0/16+]' ``` ```bash kubectl get bgpconfiguration.projectcalico.org default -o yaml kubectl get bgppeers.projectcalico.org -o wide kubectl get bgpfilters.projectcalico.org -o yaml kubectl -n "$CALICO_NAMESPACE" logs "$CALICO_POD" -c calico-node --tail=200 ``` `CALICO_BGP_PROTOCOL`을 첫 `show protocols` 결과의 실제 이름으로 바꾸세요. `Mesh_…`, `Global_…`, `Node_…` 등이 사용되며 모두 `bgp*`로 시작하지 않습니다. 경로 표현식은 로컬 셸이 확장하지 않도록 따옴표로 감싸고 `show protocols all`에는 BGP 이외 프로토콜도 나온다는 점을 구분하세요. 컨테이너 로그는 시작·confd 오류 확인에 도움이 되지만 특정 stdout 문자열이 없다는 사실이 BIRD 정상 동작을 증명하지 않습니다. 설치의 BIRD 로그 위치와 실제 세션을 확인하세요. `calicoctl node status`는 노드 환경이 필요한 로컬 진단이며 워크스테이션 kubeconfig만으로 원격 노드 상태를 보여주는 명령이 아닙니다. `ip route`도 대상 노드·네트워크 namespace에서 확인해야 합니다. | 증상 | 확인 항목 | | --- | --- | | Active 상태 유지 | 피어 IP·ASN, TCP listener·방화벽, 소스 주소, MD5·GTSM 일치, 전달망 도달성 | | Established지만 필요한 경로 없음 | import/export 필터, RR 역할, endpoint·IPAM 상태, next hop, AS-loop 거부 | | flapping·reset | 전달망 손실, MTU, 인증, 협상 타이머, 컨트롤러 설정 변경 | | 경로는 있지만 통신 실패 | 실제 커널/FIB, 반환 경로, Service 전달, 접근 정책, covering aggregate | BGP Established만으로 워크로드 연결을 검증할 수는 없습니다. ## 멀티 데이터센터 BGP 설계 ### AS-per-Rack 설계 패턴 각 랙에 별도의 AS 번호를 할당하여 확장성을 높입니다. ![랙별 AS와 데이터센터 간 WAN 경계의 개념적 배치.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-04-bgp-deep-dive-6.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-04-bgp-deep-dive-6.html) > 그림의 64510·64511·64500은 문서용 ASN이며 64000은 프라이빗 범위가 아닙니다. 배치 시 소유 ASN 또는 아래 설정의 프라이빗 ASN 계획을 사용하세요. 도식만으로 모든 WAN 세션·반환 경로가 구성되지는 않습니다. #### AS-per-Rack Calico 설정 다음은 Kubernetes datastore의 기존 노드에 대한 설정 조각입니다. 바뀔 ASN과 상대 라우터 설정을 함께 준비하고 AS 변경으로 인한 세션 재설정을 계획하세요. 임의 IP가 담긴 부분 Node 객체로 기존 노드 전체를 대체하지 않습니다. ```bash kubectl label node node-1 node-2 rack=dc1-rack1 datacenter=dc1 kubectl label node node-3 rack=dc1-rack2 datacenter=dc1 kubectl label node node-5 rack=dc2-rack1 datacenter=dc2 kubectl annotate node node-1 node-2 projectcalico.org/ASNumber=64512 kubectl annotate node node-3 projectcalico.org/ASNumber=64513 kubectl annotate node node-5 projectcalico.org/ASNumber=64610 ``` ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: dc1-rack1-ibgp spec: nodeSelector: "rack == 'dc1-rack1'" peerSelector: "rack == 'dc1-rack1'" --- apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: dc1-rack1-tor spec: nodeSelector: "rack == 'dc1-rack1'" peerIP: 10.1.10.1 asNumber: 65001 ``` 다른 랙·DC의 피어와 반환 경로는 각각 별도로 완성해야 합니다. 기존 자동 mesh를 제거하기 전에 대체 fabric 세션·경로·대표 트래픽을 먼저 검증하세요. 위 조각만으로 모든 랙의 연결이 완성되지는 않습니다. ### eBGP Between Racks 패턴 원격 랙의 ToR에 eBGP 피어링하는 대안입니다. 기존 node mesh나 로컬 ToR 설계와 자동으로 호환되는 것은 아닙니다. 해당 Calico renderer는 외부 피어에 multihop을 생성하지만 실제 왕복 경로와 상대 설정은 별도 전제입니다. `numAllowedLocalASNumbers`를 TTL 설정으로 사용하면 루프 방지만 약화됩니다. ```yaml # Rack1 노드가 Rack2의 ToR과 피어링 apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: rack1-to-rack2 spec: nodeSelector: "rack == 'rack1'" peerIP: 10.0.20.1 # Rack2 ToR IP asNumber: 64521 # Rack2 AS sourceAddress: UseNodeIP # 상대 라우터의 multihop·반환 경로도 별도 구성 ``` ## 모범 사례 요약 ### BGP 설계 체크리스트 * [ ] **ASN·CIDR**: 소유 범위와 프라이빗 ASN, 경계 AS_PATH 처리 및 반환 경로 문서화 * [ ] **토폴로지**: 경로 수·변경 빈도·수렴 목표를 측정해 mesh/RR/계층 선택 * [ ] **중복성**: RR을 장애 영역에 분산하고 한쪽 장애 후 용량·전달망 검증 * [ ] **보안**: MD5·GTSM 전제 확인, 명시적 import/export 허용 목록 적용 * [ ] **타이머**: 실제 협상된 Hold·Keepalive 확인 * [ ] **Graceful Restart**: capability·전달 경로·stale 경로 위험 검증 * [ ] **모니터링**: 세션 상태뿐 아니라 광고·수신 경로와 실제 트래픽 확인 * [ ] **전환**: 대체 경로 검증 후 기존 mesh 제거, 원복 계획 유지 *** ## 참고 자료 * [Calico BGP 공식 문서](https://docs.tigera.io/calico/latest/networking/configuring/bgp) * [BGP Route Reflector 설정](https://docs.tigera.io/calico/latest/networking/configuring/bgp) * [BIRD Routing Daemon](https://bird.network.cz/) * [RFC 4271 - BGP-4](https://www.rfc-editor.org/rfc/rfc4271) * [RFC 4456 - BGP Route Reflection](https://www.rfc-editor.org/rfc/rfc4456) * [Calico BGPPeer API](https://docs.tigera.io/calico/latest/reference/resources/bgppeer) * [Calico BGPConfiguration API](https://docs.tigera.io/calico/latest/reference/resources/bgpconfig) * [Calico BGPFilter API](https://docs.tigera.io/calico/latest/reference/resources/bgpfilter) * [Service IP advertisement](https://docs.tigera.io/calico/latest/networking/configuring/advertise-service-ips) * [Calico LoadBalancer IPAM](https://docs.tigera.io/calico/latest/networking/ipam/service-loadbalancer) * [Calico 3.32.2 BIRD configuration processing](https://github.com/projectcalico/calico/blob/v3.32.2/confd/pkg/backends/calico/bgp_processor.go) * [Calico 3.32.2 BIRD template](https://github.com/projectcalico/calico/blob/v3.32.2/confd/etc/calico/confd/templates/bird.cfg.template) * [Pinned BIRD best-path implementation](https://github.com/projectcalico/bird/blob/9111ec3c3ff3e769727a5940d3d829a0be8b5201/proto/bgp/attrs.c) * [Pinned BIRD timers and GTSM](https://github.com/projectcalico/bird/blob/9111ec3c3ff3e769727a5940d3d829a0be8b5201/proto/bgp/bgp.c) * [Cisco IOS XE dynamic neighbors](https://www.cisco.com/c/en/us/td/docs/routers/ios/config/17-x/ip-routing/b-ip-routing/m_irg-bgp-dynamic-neighbors.html) * [Junos route-filter match types](https://www.juniper.net/documentation/en_US/junos/topics/usage-guidelines/policy-configuring-route-lists-for-use-in-routing-policy-match-conditions.html) * [Kubernetes Service API and externalIPs deprecation](https://kubernetes.io/docs/concepts/services-networking/service/) * [Calico 3.32.2 Service route generation](https://github.com/projectcalico/calico/blob/v3.32.2/confd/pkg/backends/calico/routes.go) [이전: Part 3 - 네트워킹 모드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md) | [다음: Part 5 - Network Policy 심화](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md) | [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/05-network-policy ---------------------------------------- # Part 5: Network Policy > **검토 기준**: Calico 3.32.2; Calico 3.32의 Kubernetes 테스트 범위는 1.34–1.36입니다. **마지막 업데이트**: 2026년 9월 12일. > > 표준 Calico API 서버(`projectcalico.org/v3`), Kubernetes datastore와 호환되는 정책 적용 데이터 평면을 전제로 합니다. 전용 `calico-demo` namespace에서 워크로드 레이블·준비된 endpoint·기본 연결을 확인한 뒤 정책을 적용하세요. 각 절은 독립적인 패턴이며 한 번에 적용하는 매니페스트 묶음이 아닙니다. 기존 상위 정책, DNS 구현, Service NAT, 호스트 정책, 애플리케이션 동작도 결과에 영향을 줍니다. 이번 검토에서는 운영 클러스터·admission 서버·실제 패킷 전달을 실행하지 않았습니다. ## 개요 Network Policy는 워크로드와 다른 endpoint 사이의 허용 연결을 제어합니다. Calico는 표준 Kubernetes API에 정책 순서, 명시적 action, 전역 범위, HostEndpoint 제어를 추가합니다. 기능은 제품과 적용 경로에 따라 다릅니다. DNS 도메인 정책은 상용 확장이며 Open Source HTTP 정책에는 문서화된 Istio/Dikastes 통합이 필요합니다. ## Kubernetes NetworkPolicy 기본 ### 표준 NetworkPolicy 구조 Kubernetes NetworkPolicy는 namespace 범위에서 Pod를 선택합니다. ingress와 egress 격리는 독립적이며 각 방향의 허용 범위는 일치하는 Kubernetes 정책들의 합집합입니다. 양쪽이 격리되면 새 연결에 송신 egress와 수신 ingress 허용이 모두 필요합니다. 허용된 연결의 응답에는 별도의 반대 방향 허용 규칙이 필요하지 않습니다. `from`/`to` 목록의 항목끼리는 **OR**, 한 항목 안의 `namespaceSelector`와 `podSelector`는 **AND**입니다. namespace selector 없는 pod selector는 해당 정책의 namespace를 선택합니다. 일치하는 Kubernetes 정책이 없다는 것은 그 API가 해당 방향을 격리하지 않는다는 뜻이며 호스트 방화벽·Calico 정책 등 다른 제어를 무시하지 않습니다. 정책 변경이 기존 연결에 미치는 영향은 구현에 따라 다르므로 새 연결로 확인하세요. Service/LB NAT 전후에 `ipBlock`이 보게 되는 주소도 구현에 영향을 받습니다. ```yaml # 기본 Kubernetes NetworkPolicy 예시 apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: calico-demo spec: # 정책이 적용될 Pod 선택 podSelector: matchLabels: app: backend tier: api # 정책 유형 (Ingress, Egress, 또는 둘 다) policyTypes: - Ingress - Egress # Ingress 규칙 (들어오는 트래픽) ingress: # 규칙 1: frontend에서 8080 포트로 접근 허용 - from: - podSelector: matchLabels: app: frontend namespaceSelector: matchLabels: kubernetes.io/metadata.name: calico-demo ports: - protocol: TCP port: 8080 # 규칙 2: monitoring 네임스페이스에서 메트릭 수집 허용 - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: monitoring ports: - protocol: TCP port: 9090 # Egress 규칙 (나가는 트래픽) egress: # DNS 허용 - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 - protocol: TCP port: 53 # 데이터베이스 접근 허용 - to: - podSelector: matchLabels: app: database ports: - protocol: TCP port: 5432 ``` ### Kubernetes NetworkPolicy의 한계 | 기능 | Kubernetes NetworkPolicy | Calico 확장·전제 | | --- | --- | --- | | 범위 | namespace 안의 Pod | GlobalNetworkPolicy로 여러 namespace의 워크로드·HostEndpoint 선택 | | 순서·action | 허용 규칙의 합집합, 사용자 지정 정책 순서 없음 | Tier/order와 Allow·Deny·Log·Pass | | 포트 | TCP/UDP/SCTP, named port, `endPort` 숫자 범위(1.25부터 stable, 플러그인 지원 필요) | Calico 포트 범위 표현과 추가 IP 프로토콜·ICMP 매칭 | | HTTP 메서드·경로 | 이 API에 없음 | Open Source의 `http` 규칙에는 Istio/Dikastes 적용 경로 필요 | | DNS 도메인 | 이 API에 없음 | 예: Calico Enterprise 도메인 정책. Open Source 3.32 CRD에는 `domains` 없음 | | 호스트 인터페이스 | 일반 노드 방화벽이 아님 | HostEndpoint의 local·forwarded·failsafe 동작을 별도 고려 | 표준 NetworkPolicy 객체와 새 Kubernetes 클러스터 정책 API는 구분해야 합니다. 모든 Kubernetes 네트워크 보안 API가 namespace 전용이라는 뜻은 아닙니다. 스키마가 필드를 허용해도 실제 데이터 평면의 프로토콜·로깅 지원을 확인해야 합니다. ## Calico NetworkPolicy ### 기본 구조 Calico NetworkPolicy는 Kubernetes NetworkPolicy를 확장합니다. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: advanced-backend-policy namespace: calico-demo spec: # Calico 셀렉터 문법 (표현식 기반) selector: app == 'backend' && tier == 'api' # 정책 순서 (낮을수록 먼저 평가, 기본값: 무한대) order: 100 # Ingress 규칙 ingress: - action: Allow protocol: TCP source: selector: app == 'frontend' destination: ports: - 8080 # Egress 규칙 egress: - action: Allow protocol: TCP destination: selector: app == 'database' ports: - 5432 ``` ### 셀렉터 문법 Calico는 강력한 표현식 기반 셀렉터를 제공합니다. | 표현식 | 의미 | | --- | --- | | `app == 'frontend'` | 정확한 값 일치 | | `app == 'backend' && env == 'production'` | 두 조건 모두 일치 | | `app == 'frontend' \|\| app == 'backend'` | 어느 조건이든 일치 | | `app != 'legacy'` | 다른 값 또는 레이블 없음 | | `has(app)` / `!has(internal)` | 레이블 존재 / 부재 | | `app in {'frontend', 'backend'}` | 집합에 속하는 값 | | `env not in {'dev', 'staging'}` | 집합 밖의 값 또는 레이블 없음 | | `all()` / `!all()` | 범위 안의 모든 리소스 / 아무 리소스도 선택하지 않음 | 한 `spec`에 `selector` 키를 여러 번 쓰지 말고 표현식 하나를 선택하거나 `&&`·`||`로 결합하세요. YAML 문자열이 `!`로 시작하면 따옴표로 감쌉니다. `selector: !has(x)`는 알려진 리소스 중 레이블이 없는 대상을 선택합니다. `notSelector: has(x)`는 패킷 매칭을 부정하므로 해당 selector에 속하지 않는 외부 주소도 매칭할 수 있습니다. 규칙의 `selector: all()`도 모든 패킷을 뜻하지 않으며, 모든 패킷을 매칭하려면 해당 endpoint selector 조건을 생략합니다. ### Action 유형 Calico 정책은 네 가지 action을 사용합니다. 일반 정책에서 Allow/Deny는 해당 endpoint 방향의 정책 평가를 끝내고 Log는 다음 규칙으로 진행합니다. Pass는 같은 Tier의 **나머지 정책까지 건너뛰므로** 뒤에 배치한 보안 규칙도 실행되지 않습니다. 적용 가능한 다음 Tier로 이동하고 마지막 Tier에서 Pass하면 Profile 평가로 넘어갑니다. Host pre-DNAT/untracked 경로에는 별도 기본 동작이 있습니다. ```yaml # Allow - 트래픽 허용 - action: Allow protocol: TCP destination: ports: - 8080 # Deny - 트래픽 명시적 거부 - action: Deny source: selector: "has(untrusted)" # Log - 트래픽 로깅 (처리는 다음 규칙으로) - action: Log protocol: TCP destination: ports: - 22 # Pass - 다음 Tier로 전달 (Tier 사용 시) - action: Pass ``` ### 프로토콜 및 포트 지정 SCTP 예시는 호환되는 classic 데이터 평면이 필요합니다. Calico 3.32 eBPF는 SCTP 정책·Service를 지원하지 않습니다. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: protocol-examples namespace: calico-demo spec: selector: app == 'backend' ingress: # TCP 특정 포트 - action: Allow protocol: TCP destination: ports: - 80 - 443 - "8080:8090" # 포트 범위 # UDP - action: Allow protocol: UDP destination: ports: - 53 # ICMP (IPv4) - action: Allow protocol: ICMP icmp: type: 8 # Echo Request code: 0 # ICMPv6 - action: Allow protocol: ICMPv6 icmp: type: 128 # Echo Request code: 0 # SCTP - action: Allow protocol: SCTP destination: ports: - 36412 # S1AP # TCP 전체 포트 (다른 프로토콜까지 허용하는 것은 아님) - action: Allow protocol: TCP destination: ports: - "1:65535" # 모든 포트 ``` ### Source/Destination 지정 ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: source-dest-examples namespace: calico-demo spec: selector: app == 'backend' ingress: # Pod 셀렉터 - action: Allow protocol: TCP source: selector: app == 'frontend' destination: ports: - 8080 # 네임스페이스 셀렉터 - action: Allow protocol: TCP source: namespaceSelector: env == 'production' destination: ports: - 8080 # 네임스페이스 + Pod 셀렉터 조합 - action: Allow protocol: TCP source: selector: app == 'api-gateway' namespaceSelector: kubernetes.io/metadata.name == 'ingress' destination: ports: - 8080 # CIDR 블록 - action: Allow protocol: TCP source: nets: - 10.0.0.0/8 - 172.16.0.0/12 notNets: - 10.0.100.0/24 # 제외할 서브넷 destination: ports: - 8080 # 서비스 계정 기반 - action: Allow source: serviceAccounts: names: - frontend-sa - api-gateway-sa selector: role == 'frontend' ``` ## GlobalNetworkPolicy GlobalNetworkPolicy는 namespace에 속하지 않으며 여러 namespace의 워크로드나 HostEndpoint를 선택할 수 있습니다. `selector: all()`만으로 “애플리케이션 Pod만 전체 선택”하는 것은 아닙니다. 아래 예시는 demo namespace의 워크로드로 범위를 제한하여 시스템·호스트 트래픽을 분리합니다. ### 기본 거부와 명시적 예외 빈 정책은 양쪽 방향을 선택합니다. order를 생략하면 명시한 정책들보다 나중에 평가하며 10,000이 특별한 “최하위 우선순위” 값은 아닙니다. 빈 규칙은 앞선 최종 Allow를 덮어쓰지 않습니다. 애플리케이션 정책이 우회하면 안 되는 제한은 별도 관리하는 앞쪽 Tier에 배치하세요. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.demo-default-deny spec: tier: default namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' types: [Ingress, Egress] ingress: [] egress: [] --- apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.demo-essential-egress spec: tier: default order: 100 namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: needs-platform == 'true' types: [Egress] egress: - action: Allow protocol: UDP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow destination: services: name: kubernetes namespace: default ``` 플랫폼 egress는 `needs-platform=true` 레이블을 가진 워크로드에만 허용합니다. Service 매칭은 **Kubernetes datastore**에서 실제 `kubernetes/default` endpoint와 포트를 사용하며 etcd datastore에서는 무시됩니다. 해당 Service 매칭에 destination ports·CIDR·selector를 함께 지정하지 마세요. API 네트워크 연결과 API 인증·RBAC은 별개입니다. 실제 DNS 배포를 확인하세요. namespace와 Pod selector를 함께 사용하면 다른 namespace의 임의 `kube-dns` 레이블 Pod를 resolver로 신뢰하지 않습니다. 노드 로컬 DNS에는 실제 경로에 맞는 매칭이 필요합니다. 시스템 의존성을 파악하기 전에 `kube-system`에 빈 정책을 적용하면 클러스터가 중단될 수 있으므로 별도 테스트 namespace에서 패턴을 확인하세요. ### 앞쪽 Tier의 egress 제한 다음 독립 예시는 demo 워크로드의 IPv4 메타데이터 주소를 차단하고 Tier 끝에서 다른 트래픽을 위임합니다. 전체 SSRF 방어, privileged/hostNetwork 프로세스 보호, 모든 플랫폼 메타데이터 주소의 차단을 보장하는 예시는 아닙니다. ```yaml apiVersion: projectcalico.org/v3 kind: Tier metadata: name: egress-guardrail spec: order: 50 defaultAction: Pass --- apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: egress-guardrail.block-imds-v4 spec: tier: egress-guardrail order: 10 namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' types: [Egress] egress: - action: Deny destination: nets: [169.254.169.254/32] ``` 같은 Tier의 뒤쪽 제한보다 먼저 무조건 Pass를 넣지 마세요. 플랫폼에 필요한 identity·DNS 경로를 유지하고 실제 workload-to-host 적용 동작을 확인해야 합니다. ## NetworkSet / GlobalNetworkSet NetworkSet은 IP/CIDR 집합에 레이블을 붙여 정책에서 재사용합니다. 객체 이름이 아닌 레이블 selector로 참조하며, 같은 레이블의 endpoint도 매칭될 수 있으므로 레이블 관리 권한과 namespace/global 범위를 함께 확인하세요. 예시 주소는 실제 국가별 주소 목록이나 위협 정보가 아닙니다. namespaced 정책에서 GlobalNetworkSet을 선택하려면 entity의 `namespaceSelector: global()`과 별도 레이블 selector를 사용합니다. namespaced NetworkSet은 선택한 namespace 안에서 매칭됩니다. 신뢰·차단 범위가 겹치면 먼저 Deny를 평가해야 하며 앞선 Allow는 최종 결정입니다. ### NetworkSet (네임스페이스 범위) ```yaml # 외부 데이터베이스 IP 집합 apiVersion: projectcalico.org/v3 kind: NetworkSet metadata: name: external-databases namespace: calico-demo labels: service-type: database environment: production spec: nets: - 10.100.10.10/32 # Primary PostgreSQL - 10.100.10.11/32 # Secondary PostgreSQL - 10.100.20.10/32 # MongoDB Primary - 10.100.20.11/32 # MongoDB Secondary - 10.100.30.0/24 # Redis Cluster --- # NetworkSet 참조 정책 apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: allow-database-access namespace: calico-demo spec: selector: app == 'backend' egress: - action: Allow protocol: TCP destination: # NetworkSet 참조 (레이블 셀렉터 사용) selector: service-type == 'database' namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' ports: - 5432 # PostgreSQL - 27017 # MongoDB - 6379 # Redis ``` ### GlobalNetworkSet (클러스터 전역) ```yaml # 신뢰할 수 있는 파트너 IP apiVersion: projectcalico.org/v3 kind: GlobalNetworkSet metadata: name: trusted-partners labels: partner: trusted access-level: external spec: nets: - 203.0.113.0/24 # Partner A 네트워크 - 198.51.100.0/24 # Partner B 네트워크 - 192.0.2.10/32 # Partner C 단일 IP --- # 차단해야 할 악성 IP apiVersion: projectcalico.org/v3 kind: GlobalNetworkSet metadata: name: blocked-ips labels: threat: malicious spec: nets: - 192.0.2.128/25 # 문서용 차단 예시, 실제 위협 정보 아님 - 192.0.2.100/32 # 문서용 단일 주소 --- # GlobalNetworkSet 참조 정책 apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: block-malicious-traffic spec: namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: all() order: 10 # 가장 먼저 평가 ingress: # 악성 IP 차단 - action: Deny source: namespaceSelector: global() selector: "threat == 'malicious'" # 신뢰할 수 있는 파트너 허용 - action: Allow protocol: TCP source: namespaceSelector: global() selector: "partner == 'trusted'" destination: ports: - 443 ``` ## Tier 기반 정책 Tier는 Calico Open Source 3.32에서도 지원합니다. namespaced·global Calico 정책을 묶는 기능이며 Kubernetes NetworkPolicy에서 GlobalNetworkPolicy로 기능이 순차 확장되는 계층은 아닙니다. ### 평가 순서와 기본 동작 일반 endpoint 정책에서는 Tier의 `order`, Tier 안의 정책 `order`가 낮은 순서로 평가합니다. 정책 order를 생략하면 명시한 정책들보다 뒤에서 평가합니다. 대상 endpoint뿐 아니라 **트래픽 방향**도 구분하세요. | 상황 | 결과 | | --- | --- | | 해당 endpoint·방향을 선택하는 정책이 Tier에 없음 | Tier 건너뛰기 | | 규칙이 Allow/Deny | 해당 endpoint·방향의 정책 판단 종료 | | 규칙이 Log | 다음 규칙 계속 평가 | | 규칙이 Pass | 같은 Tier의 나머지 정책까지 건너뛰고 다음 적용 가능한 Tier로 이동 | | 적용되는 Tier에서 최종 action에 일치하지 않음 | Tier의 `defaultAction` 적용. 기본값은 Deny | | 마지막 적용 Tier에서 Pass | endpoint Profile 평가. 허용하는 Profile이 없으면 거부 | “규칙이 안 맞으면 언제나 다음 Tier”가 아닙니다. 한 endpoint의 Allow도 반대쪽 endpoint 정책까지 우회하지는 않습니다. Host pre-DNAT·untracked 경로의 fall-through는 뒤에서 별도로 다룹니다. 기본 `default` Tier의 고정 order는 **1,000,000**이며 1,000이나 무한대가 아닙니다. Kubernetes NetworkPolicy와 Tier를 지정하지 않은 Calico 정책이 들어갑니다. 현재 Kubernetes 클러스터 정책 통합의 `kube-admin`·`kube-baseline` Tier는 각각 1,000·10,000,000과 Pass 기본 동작을 사용하므로 `default`가 모든 구성에서 무조건 마지막인 것도 아닙니다. ### 보안·플랫폼·애플리케이션 판단 분리 다음 예시는 security/platform Tier 끝의 Pass로 위임합니다. 그래야 각 Tier의 적용 가능한 규칙을 모두 검사한 뒤 다음 Tier로 넘어갑니다. Tier 생성·순서 변경 권한은 중앙에서 통제하세요. ```yaml apiVersion: projectcalico.org/v3 kind: Tier metadata: name: security spec: order: 100 defaultAction: Pass --- apiVersion: projectcalico.org/v3 kind: Tier metadata: name: platform spec: order: 200 defaultAction: Pass --- apiVersion: projectcalico.org/v3 kind: Tier metadata: name: application spec: order: 500 defaultAction: Deny ``` 아래 문서용 위협 주소 차단 뒤에는 별도의 제한 데이터 규칙이 있습니다. 첫 보안 정책 끝에 Pass를 넣으면 두 번째 정책을 건너뛰므로 **Tier 끝에서 위임**합니다. 데이터 범위 레이블은 분할 예시이며 완전한 PCI DSS 준수 구현이 아닙니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkSet metadata: name: demo-threats labels: network-group: demo-threat spec: nets: - 192.0.2.100/32 --- apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: security.block-threats spec: tier: security order: 10 namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' types: [Ingress, Egress] ingress: - action: Deny source: namespaceSelector: global() selector: network-group == 'demo-threat' egress: - action: Deny destination: namespaceSelector: global() selector: network-group == 'demo-threat' --- apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: security.restricted-data spec: tier: security order: 20 namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: data-scope == 'restricted' types: [Ingress] ingress: - action: Deny source: notSelector: data-scope == 'restricted' --- apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: platform.dns spec: tier: platform order: 10 namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' types: [Egress] egress: - action: Allow protocol: UDP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] --- apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: application.frontend namespace: calico-demo spec: tier: application order: 10 selector: app == 'frontend' types: [Ingress, Egress] ingress: - action: Allow protocol: TCP source: selector: app == 'gateway' destination: ports: [8080] egress: - action: Allow protocol: TCP destination: selector: app == 'backend' ports: [8080] ``` GlobalNetworkSet은 `namespaceSelector: global()`과 별도의 레이블 selector로 선택합니다. `global(label == 'value')`는 올바른 구문이 아닙니다. 플랫폼 DNS Allow는 선택된 워크로드 egress의 의도적인 최종 허용이므로 이후 application Tier에서 다시 제한하지 못합니다. 애플리케이션 정책은 선택된 frontend에만 적용하며 namespace의 모든 워크로드를 보호하지는 않습니다. DNS 예시는 namespace·레이블을 확인한 일반 CoreDNS Pod를 전제로 합니다. NodeLocal DNSCache나 EKS Auto Mode의 노드 로컬 DNS에는 실제 resolver 경로에 맞는 규칙이 필요합니다. Pod selector를 그대로 복사하지 말고 필요한 resolver 접근과 UDP·TCP 질의를 모두 검증하세요. ### Tier RBAC 통합 Calico Tier RBAC은 `tier.networkpolicies`·`tier.globalnetworkpolicies`라는 pseudo-resource와 해당 Tier의 `get` 권한을 사용합니다. Calico authorizer가 `application.*` 같은 합성 이름을 명시적으로 검사합니다. 일반 `networkpolicies`에 적용되는 Kubernetes `resourceNames` 와일드카드가 아닙니다. 다음 완전한 binding 예시는 서비스 계정 하나에 calico-demo namespace·application Tier의 정책 편집을 허용합니다. Tier 생성·재정렬이나 전역 정책 관리 권한은 부여하지 않습니다. ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: policy-editor namespace: calico-demo --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: demo-get-application-tier rules: - apiGroups: ["projectcalico.org"] resources: ["tiers"] resourceNames: ["application"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: demo-get-application-tier subjects: - kind: ServiceAccount name: policy-editor namespace: calico-demo roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: demo-get-application-tier --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: demo-edit-application-policies namespace: calico-demo rules: - apiGroups: ["projectcalico.org"] resources: ["tier.networkpolicies"] resourceNames: ["application.*"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: demo-edit-application-policies namespace: calico-demo subjects: - kind: ServiceAccount name: policy-editor namespace: calico-demo roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: demo-edit-application-policies ``` 전역 정책 편집은 의도한 범위의 ClusterRole/ClusterRoleBinding에서 `tier.globalnetworkpolicies`로 별도 부여합니다. Tier를 조회할 `get`과 Tier 순서를 수정할 권한은 구분하세요. 예시는 표준 Calico aggregated API 서버를 전제로 합니다. native v3 CRD의 Tier 권한은 admission webhook이 create/update/delete에 적용하며 GET/LIST/WATCH는 제한하지 못하므로 같은 읽기 격리를 보장하지 않습니다. 일반 `kubectl auth can-i`만으로 Calico의 복합 Tier 권한을 검증할 수 없습니다. 더 넓은 binding이 없는 테스트 주체로 허용·거부되어야 하는 실제 요청을 격리 환경에서 확인하세요. Kubernetes RBAC은 합집합이므로 기존 광범위한 권한이 제한을 무력화할 수 있습니다. ## FQDN 기반 Egress 정책 Open Source 3.32 CRD에는 `destination.domains`와 Felix `dnsTrustedServers` 필드가 없습니다. `policySyncPathPrefix`는 애플리케이션 계층 통합에서 사용하는 policy-sync 경로이며 이를 설정해도 Open Source에 DNS 도메인 정책이 추가되지는 않습니다. Calico Enterprise 3.23은 **egress Allow** 규칙의 도메인 매칭을 문서화합니다. 신뢰한 DNS의 A/AAAA/CNAME 응답에서 학습한 목적지 IP에 정책을 적용합니다. HTTPS 호스트 인증이 아니므로 공유 목적지 IP와 애플리케이션 인증도 별도로 고려해야 합니다. 클러스터 내부 서비스에는 워크로드·Service selector를 사용하세요. 다음 상용 예시는 해당 기능, 신뢰할 resolver, 앞선 최종 Allow가 없는 평가 경로를 전제로 합니다. 규칙마다 `destination` map은 하나만 사용합니다. YAML 키를 중복하면 도메인 제한이 사라질 수 있습니다. ```yaml # Calico Enterprise 3.23 example; NOT an Open Source 3.32 resource. apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: allow-approved-domains namespace: calico-demo spec: selector: app == 'external-api-client' types: [Egress] egress: - action: Allow protocol: UDP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: domains: - api.github.com - "*.example.com" ports: [443] ``` 문서용 도메인은 실제 승인한 도메인으로 바꾸세요. `*.example.com`은 `api.example.com`과 `deep.api.example.com`을 모두 매칭하지만 최상위 `example.com`은 매칭하지 않습니다. 와일드카드는 구성요소 전체를 차지해야 하고 하나만 지원합니다. Inline DNS 정책 모드는 앞쪽 와일드카드를 지원하며 중간 위치 패턴은 해당 패턴을 지원하는 별도 모드가 필요합니다. `*.amazonaws.com` 같은 넓은 접미사는 AWS 계정·IAM 경계가 아닙니다. 실제 resolver의 UDP·TCP 접근과 trusted DNS IP 구성을 함께 확인하세요. 노드 로컬 resolver에는 배포별 설정이 필요합니다. 상용 가이드는 egress-gateway Pod의 egress hook에서 도메인 정책을 지원하지 않는다고 명시합니다. 노드 단위 DNS 캐시 때문에 매칭이 없거나 간헐적일 수 있기 때문입니다. Open Source에서는 자체 애플리케이션 권한 검사를 갖춘 egress proxy/gateway나 관리되는 IP/CIDR NetworkSet을 필요에 맞게 사용합니다. 한 번 조회한 DNS IP를 지속적인 도메인 정책의 대체물로 취급하지 마세요. ## L7 (HTTP) 정책 Calico Open Source 3.32도 문서화된 **Istio + Dikastes** 통합으로 HTTP 정책을 지원합니다. 임의의 Envoy를 설치하거나 일반 CNI 정책에 `http` 필드만 추가해도 L7 적용이 활성화되는 것은 아닙니다. Felix Policy Sync API, Calico CSI socket mount, Dikastes injection, 해당 트래픽 경로의 Envoy external authorization이 필요합니다. 현재 통합 가이드는 Kubernetes native-sidecar 기반 Istio injection과 Istio 1.28.1을 권장하지만 모든 새 Istio 버전과의 호환성을 보장하지는 않습니다. 운영 조합은 유지 중인 [Istio 설치 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/01-installation.md), 양쪽 지원 기간, 실제 통합을 함께 확인하세요. 여기서는 통합 배포를 실행하지 않았습니다. 아래는 통합을 이미 구성한 워크로드의 **ingress Allow** 예시입니다. HTTPS 메서드·경로를 검사하려면 적용 proxy가 TLS 종료 후 HTTP 요청을 볼 수 있어야 합니다. Pod 레이블은 워크로드 식별자이지 최종 사용자 인증이 아닙니다. 신뢰하는 워크로드 identity/mTLS 경로와 DNS·Istio 제어 평면에 필요한 연결을 구성하고, 허용·거부 요청 및 proxy·인가 서비스 장애 동작을 확인해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: l7-api-policy namespace: calico-demo spec: selector: app == 'api-server' ingress: # GET 요청만 허용 (읽기 전용) - action: Allow protocol: TCP source: selector: role == 'reader' destination: ports: - 8080 http: methods: - GET - HEAD paths: - prefix: "/api/v1/read" # admin 레이블 워크로드에 열거한 메서드 허용 - action: Allow protocol: TCP source: selector: role == 'admin' destination: ports: - 8080 http: methods: - GET - POST - PUT - DELETE - PATCH paths: - prefix: "/api/" # /health 엔드포인트는 모두 허용 - action: Allow protocol: TCP destination: ports: - 8080 http: methods: - GET paths: - exact: "/health" - exact: "/ready" ``` ## HostEndpoint 보호 HostEndpoint는 Calico가 관리하는 노드 인터페이스를 나타냅니다. 생성 즉시 호스트 연결에 영향을 줄 수 있습니다. `defaultEndpointToHostAction`은 워크로드에서 로컬 호스트로 가는 동작을 제어하며 HostEndpoint를 만들지 않습니다. `Installation.calicoNetwork.hostPorts`도 hostPort 지원 설정이지 자동 호스트 보호 설정이 아닙니다. ### 수동 HostEndpoint와 정책 다음은 **완전한 호스트 방화벽이 아닌 필드 예시**입니다. 자체 관리 테스트 worker `demo-worker`의 주소 `10.0.1.10`, bastion `10.0.0.100`, 제어 평면 소스 `10.0.1.5`, 인터페이스 `eth0`을 가정합니다. 확인한 실제 값으로 바꾸고 HostEndpoint 생성 전에 관리·DNS·DHCP·BGP·API·상태 검사·egress 규칙을 모두 준비하세요. EKS가 관리하는 제어 평면 노드의 구성을 의미하지 않습니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.demo-worker-ingress spec: order: 100 selector: host-demo == 'true' && !has(projectcalico.org/namespace) types: [Ingress] ingress: - action: Allow protocol: TCP source: nets: [10.0.0.100/32] destination: ports: [22] - action: Allow protocol: TCP source: nets: [10.0.1.5/32] destination: ports: [10250] --- apiVersion: projectcalico.org/v3 kind: HostEndpoint metadata: name: demo-worker-eth0 labels: host-demo: "true" spec: node: demo-worker interfaceName: eth0 expectedIPs: [10.0.1.10] ``` kubelet 규칙은 worker의 인증된 10250 endpoint를 대상으로 합니다. 이전의 인증 없는 10255 read-only 포트를 기본 요구 사항처럼 열지 마세요. 예시는 ingress만 정의하므로 수동 HostEndpoint에 egress 정책·Profile이 없으면 호스트에서 시작하는 트래픽이 거부될 수 있습니다. 실제 연결 baseline을 먼저 완성해야 합니다. Calico 기본 failsafe에는 inbound TCP 22 등 연결 유지 포트가 포함됩니다. 따라서 위 SSH 규칙만으로 bastion 전용 접근을 보장하지 않습니다. failsafe 변경 전 실제 목록과 검증한 복구 경로를 확인하고 무조건 빈 목록으로 바꾸지 마세요. ### 자동 HostEndpoint 자동 생성은 node controller의 `KubeControllersConfiguration.spec.controllers.node.hostEndpoint.autoCreate`가 제어합니다. 다른 controller 설정을 보존하도록 merge patch를 사용합니다. ```bash kubectl get kubecontrollersconfiguration.projectcalico.org default -o yaml # Apply only after reviewing existing host endpoints and global policies. kubectl patch kubecontrollersconfiguration.projectcalico.org default --type=merge \ -p '{"spec":{"controllers":{"node":{"hostEndpoint":{"autoCreate":"Enabled"}}}}}' ``` 적합한 모든 노드에 영향을 줄 수 있습니다. 자동 endpoint에는 일반적으로 default-allow Profile이 있지만 일치하는 정책 Deny를 덮어쓰지 않습니다. custom template과 `createDefaultHostEndpoint`로 생성 범위를 조정할 수 있으나 기존 배포에서 변경하기 전 endpoint·정책을 확인해야 합니다. ### 로컬·전달 트래픽 구분 일반 host 정책의 `applyOnForward` 기본값은 false입니다. true이면 전달 트래픽에도 적용하지만 관련 워크로드 정책 역시 통과해야 합니다. 해당 endpoint·방향을 선택하는 forward 정책이 없으면 전달 트래픽은 기본 허용하고, 선택하는 정책이 있는데 허용 규칙이 없으면 거부합니다. 호스트에서 종료되는 트래픽에는 별도의 기본 거부 동작과 Profile·failsafe가 적용됩니다. ## DoNotTrack / PreDNAT 정책 다음은 Linux 호스트 정책 패턴입니다. HostEndpoint에 적용하며 일반 Pod를 선택해 conntrack을 끄는 설정이 아닙니다. 사용한 데이터 평면의 지원을 확인하세요. `doNotTrack`과 `preDNAT`을 동시에 true로 설정할 수 없고 둘 중 하나라도 true이면 `applyOnForward: true`가 필요합니다. ### DoNotTrack untracked Allow는 매칭 트래픽의 연결 추적을 생략합니다. 항상 성능이 향상되는 것은 아니며 conntrack이 필요한 Service/NAT 경로와 충돌할 수 있습니다. 요청·응답 규칙을 모두 정의해야 합니다. 아래는 테스트 호스트의 DNS 프로세스가 신뢰한 클라이언트 서브넷에 직접 서비스하는 가정입니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.demo-untracked-dns spec: selector: host-demo == 'true' && !has(projectcalico.org/namespace) order: 10 types: [Ingress, Egress] doNotTrack: true applyOnForward: true ingress: - action: Allow protocol: UDP source: nets: [10.0.0.0/24] destination: ports: [53] - action: Allow protocol: TCP source: nets: [10.0.0.0/24] destination: ports: [53] egress: - action: Allow protocol: UDP source: ports: [53] destination: nets: [10.0.0.0/24] - action: Allow protocol: TCP source: ports: [53] destination: nets: [10.0.0.0/24] ``` 일반 endpoint 정책과 달리 untracked 단계에서 매칭하지 않아도 Tier 끝에서 기본 drop하지 않으며 이후 tracked 정책이 적용될 수 있습니다. 이 예시는 호스트 전체의 암묵적 deny-all 방화벽이 아닙니다. ### PreDNAT pre-DNAT 정책은 DNAT 전 원래 목적지 IP·포트를 봅니다. ingress만 정의할 수 있고 허용된 응답에는 일반 conntrack을 사용합니다. 다음은 선택한 호스트 경로의 TCP NodePort 30080 하나를 보호하는 예시입니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.demo-nodeport spec: selector: host-demo == 'true' && !has(projectcalico.org/namespace) order: 20 types: [Ingress] preDNAT: true applyOnForward: true ingress: - action: Allow protocol: TCP source: nets: [10.0.0.0/24] destination: ports: [30080] - action: Deny protocol: TCP destination: ports: [30080] ``` pre-DNAT 단계 자체에는 기본 drop이 없습니다. 미일치 트래픽은 뒤의 host/workload 정책으로 진행합니다. 두 번째 명시적 규칙은 해당 NodePort의 비신뢰 소스를 거부하며 다른 포트는 예시 범위 밖입니다. 해당 호스트 NodePort를 거치지 않고 Pod로 직접 가는 경로도 이 규칙의 대상이 아닙니다. ## 정책 디버깅 실제 레이블·namespace·Service endpoint·모든 적용 Tier·양쪽 방향을 확인합니다. 표준 Calico API 서버는 Tier selector 없는 목록을 `default` Tier로 제한할 수 있습니다. `-A`는 모든 namespace라는 뜻이며 모든 Tier까지 자동 조회한다는 뜻이 아닙니다. ```bash kubectl get networkpolicies.networking.k8s.io -n calico-demo -o yaml kubectl get tiers.projectcalico.org -o yaml for CALICO_TIER in $(kubectl get tiers.projectcalico.org -o jsonpath='{.items[*].metadata.name}'); do kubectl get networkpolicies.projectcalico.org -n calico-demo \ -l "projectcalico.org/tier=$CALICO_TIER" -o yaml kubectl get globalnetworkpolicies.projectcalico.org \ -l "projectcalico.org/tier=$CALICO_TIER" -o yaml done calicoctl get workloadendpoint -n calico-demo \ --selector="app == 'frontend'" -o yaml ``` ```bash CALICO_NAMESPACE=calico-system CALICO_NODE=demo-worker CALICO_POD="$(kubectl -n "$CALICO_NAMESPACE" get pods -l k8s-app=calico-node \ --field-selector "spec.nodeName=$CALICO_NODE" -o jsonpath='{.items[0].metadata.name}')" test -n "$CALICO_POD" kubectl -n "$CALICO_NAMESPACE" logs "$CALICO_POD" -c calico-node --tail=200 kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -felix-ready ``` ```bash # Assumes named, ready test Pods and an nc binary in the client image. TARGET_POD=backend-test TARGET_IP="$(kubectl -n calico-demo get pod "$TARGET_POD" -o jsonpath='{.status.podIP}')" test -n "$TARGET_IP" kubectl -n calico-demo exec frontend-client -- nc -z -w 3 "$TARGET_IP" 8080 ``` `calico-node -felix-ready`는 준비 상태 검사이며 정책 trace나 패킷별 평가 시간 측정 명령이 아닙니다. 릴리스된 Open Source `calicoctl`에는 이전 예시의 `policy-trace` 명령이 없습니다. endpoint 조회나 정책 텍스트 검색만으로 전체 유효 정책을 계산하지도 않습니다. 허용 클라이언트, 비신뢰 클라이언트, 잘못된 포트, 다른 namespace, resolver 접근을 **새 연결**로 확인하세요. 서버 목적지 포트와 클라이언트의 임시 소스 포트를 구분합니다. Pod IP와 Service 주소를 별도로 검사하면 정책과 endpoint/NAT/전달 문제를 구분할 수 있으므로 kube-proxy나 대체 구현도 관련이 있습니다. iptables 데이터 평면은 대상 노드·네트워크 namespace의 실제 `cali-` 체인을 확인합니다. 예시 hash는 실제 체인 이름이 아닙니다. iptables Log action은 호스트 커널 로그로 출력하며 Felix stdout은 주로 컴포넌트·컨트롤러 진단입니다. stdout 메시지 부재나 0 카운터만으로 모든 경로에서 정책이 사용되지 않았다고 판단하지 마세요. eBPF/nftables에는 해당 backend 진단이 필요하며 `tc filter show`만으로 전체 정책 verdict를 설명할 수 없습니다. ### 적용 전 staged policy Open Source 3.32에는 `StagedNetworkPolicy`, `StagedGlobalNetworkPolicy`, `StagedKubernetesNetworkPolicy`가 있습니다. staged 리소스는 패킷 결정을 강제하지 않습니다. flow-log/Whisker 경로를 구성했다면 `policies.pending`에서 관찰한 영향을 확인할 수 있습니다. ```yaml apiVersion: projectcalico.org/v3 kind: StagedNetworkPolicy metadata: name: default.preview-backend-egress namespace: calico-demo spec: tier: default order: 100 selector: app == 'frontend' types: [Egress] egress: - action: Allow protocol: TCP destination: selector: app == 'backend' ports: [8080] ``` 이 preview는 backend 연결만 포함합니다. 실제 enforced 정책을 만들기 전에 DNS나 다른 필수 연결이 거부되는지 확인하세요. 관찰된 트래픽이 없다는 사실만으로 의존성이 불필요하다고 단정하지 않습니다. `action: Log`만 사용해도 평가가 계속되어 마지막에 거부될 수 있으므로 보편적인 audit-only 모드는 아닙니다. ## 일반적인 정책 패턴 ### Frontend → Backend → Database 표시한 레이블과 listener를 가진 준비된 워크로드를 전제로 합니다. 서버 포트는 **destination** port이며 `source.ports: [8080]`을 사용하면 임시 소스 포트를 쓰는 클라이언트가 보통 거부됩니다. 숫자 포트 규칙에는 TCP를 지정합니다. gateway는 demo 애플리케이션이며 특정 ingress controller의 실제 레이블을 가정하지 않습니다. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.frontend namespace: calico-demo spec: order: 100 selector: app == 'frontend' types: [Ingress, Egress] ingress: - action: Allow protocol: TCP source: selector: app == 'gateway' destination: ports: [8080] egress: - action: Allow protocol: TCP destination: selector: app == 'backend' ports: [8080] --- apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.backend namespace: calico-demo spec: order: 100 selector: app == 'backend' types: [Ingress, Egress] ingress: - action: Allow protocol: TCP source: selector: app == 'frontend' destination: ports: [8080] egress: - action: Allow protocol: TCP destination: selector: app == 'database' ports: [5432] --- apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.database namespace: calico-demo spec: order: 100 selector: app == 'database' types: [Ingress, Egress] ingress: - action: Allow protocol: TCP source: selector: app == 'backend' destination: ports: [5432] egress: [] ``` DNS가 필요한 클라이언트에는 앞의 필수 egress 패턴과 해당 레이블로 resolver 접근을 별도 허용하세요. 단순화한 데이터베이스는 새 egress 연결을 시작하지 않으며 상태 추적된 응답은 가능합니다. 백업·복제·외부 의존성은 별도 규칙이 필요합니다. ### 테넌트 격리 Calico selector는 `$(namespace.tenant)`, `${namespace.labels.tenant}`, `${namespace.name}`를 동적으로 치환하지 않습니다. 테넌트 값을 명시한 정책을 각각 생성하거나 같은 namespace 격리에는 namespaced 정책을 사용하세요. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.team-a-isolation spec: order: 500 namespaceSelector: tenant == 'team-a' types: [Ingress, Egress] ingress: - action: Allow source: namespaceSelector: tenant == 'team-a' egress: - action: Allow destination: namespaceSelector: tenant == 'team-a' - action: Allow protocol: UDP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] ``` 같은 레이블의 여러 namespace를 포함하여 team-a 내부 트래픽을 모두 허용하는 예시입니다. namespace 레이블·정책 편집 권한을 통제해야 하며 앞선 Allow가 격리 의도를 무력화할 수 있습니다. 다른 테넌트와 레이블 없는 namespace도 음성 테스트에 포함하세요. ### 같은 namespace와 공유 서비스 namespaced 정책의 entity selector에서 namespace selector를 생략하면 `calico-demo` 안으로 범위가 제한됩니다. 이 예시는 제한적인 마이크로서비스 패턴의 대안이며 위에 추가로 겹쳐 적용하는 정책이 아닙니다. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.namespace-and-shared namespace: calico-demo spec: order: 200 selector: all() types: [Ingress, Egress] ingress: - action: Allow source: selector: all() egress: - action: Allow destination: selector: all() - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'logging' selector: app == 'log-receiver' ports: [24224] - action: Allow protocol: TCP destination: namespaceSelector: kubernetes.io/metadata.name == 'auth' selector: app == 'identity-provider' ports: [8080] ``` 필요한 resolver 규칙은 별도 추가하고 목적지 쪽 정책도 클라이언트를 허용하는지 확인하세요. 로깅 포트·인증 서비스 레이블은 demo 가정이므로 실제 수신 프로토콜·포트·애플리케이션 인증을 검증해야 합니다. ### Open Source egress 제어 주소 계약이 있는 외부 서비스는 NetworkSet에서 승인한 주소를 관리할 수 있습니다. 아래 IP는 문서용이며 실제 API 주소나 영구적인 DNS 조회 결과가 아닙니다. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkSet metadata: name: approved-api-ips namespace: calico-demo labels: destination-group: approved-api spec: nets: [203.0.113.10/32] --- apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: default.approved-api-egress namespace: calico-demo spec: order: 100 selector: app == 'external-api-client' types: [Egress] egress: - action: Allow protocol: TCP destination: selector: destination-group == 'approved-api' ports: [443] ``` 이름을 조회하는 애플리케이션에는 앞의 DNS 규칙을 별도로 적용합니다. 동적 외부 서비스에는 적절한 권한 검사를 갖춘 proxy나 별도 문서화한 상용 도메인 정책을 사용하세요. RFC1918 전체 허용은 “이 클러스터만” 허용한다는 뜻이 아니며 다른 사설 네트워크까지 열 수 있습니다. ### 보안 설계의 일부인 기본 거부 범위를 제한한 default-deny와 필요한 연결, 레이블·RBAC 소유권, 애플리케이션 인증을 함께 구성하세요. 네트워크 분할만으로 전체 zero-trust나 규정 준수를 구현한 것은 아닙니다. staged policy와 음성 테스트를 거쳐 적용해야 합니다. ## 정책 성능 최적화 정책 수만으로 처리 용량을 판단할 수 없습니다. endpoint 수, selector 변경, 규칙 구조, 활성 연결, 업데이트 빈도, 데이터 평면이 비용에 영향을 줍니다. “1,000개 이상은 매우 느림” 같은 기존 표에는 측정 환경이나 원자료가 없었습니다. 동등 비교, 집합 membership, 레이블 존재 selector는 모두 최적화 대상이 될 수 있습니다. 객체 수를 줄이려고 허용 범위를 넓히는 정책 통합을 하지 마세요. 관리되는 NetworkSet을 재사용하고 로그량을 제한하며 대표 변경 부하에서 수렴 시간을 측정합니다. readiness 검사, “Policy sync” 검색, `iptables -L` 출력 줄 수는 정책 평가 지연을 측정하지 않습니다. 문서화된 Felix metrics endpoint를 활성화·수집하고 TYPE/HELP·단위를 확인하여 제어한 워크로드의 정책 반영 시간과 연결하세요. ```bash # After enabling the documented Felix metrics endpoint through its config owner: kubectl -n "$CALICO_NAMESPACE" port-forward "pod/$CALICO_POD" 9091:9091 ``` ```bash # In another terminal while the localhost port-forward remains active: curl --fail --silent --show-error http://127.0.0.1:9091/metrics ``` Felix metrics는 기본 비활성화이므로 설정 소유자를 통해 `prometheusMetricsEnabled`를 먼저 활성화해야 합니다. 대상 노드에서 endpoint에 접근 가능해야 합니다. 위 명령은 메트릭 탐색이며 발표한 성능 측정 결과가 아닙니다. 정책 변경 중 허용·거부 경로를 검사하고 Calico/Kubernetes/kernel 버전, 데이터 평면, 토폴로지, 부하, 측정값을 함께 보존하세요. ## 운영 원칙 1. 격리 namespace에서 필수 연결을 파악하고 staged policy를 거쳐 적용합니다. 2. Pod·namespace 레이블과 정책 편집 RBAC을 권한 경계의 일부로 관리합니다. 3. Tier 추가·재정렬 시 최종 Allow와 Pass의 영향을 재검토합니다. 4. 호스트 관리·failsafe·Service/DNS 전제를 애플리케이션 규칙과 구분합니다. 5. 네트워크 분할과 워크로드·최종 사용자 인증을 함께 사용하며 IP 규칙만으로 모든 SSRF·규정 준수 문제를 해결했다고 주장하지 않습니다. *** ## 참고 자료 * [Calico NetworkPolicy API](https://docs.tigera.io/calico/latest/reference/resources/networkpolicy) * [GlobalNetworkPolicy API](https://docs.tigera.io/calico/latest/reference/resources/globalnetworkpolicy) * [Tier evaluation](https://docs.tigera.io/calico/latest/reference/resources/tier) * [Tier RBAC](https://docs.tigera.io/calico/latest/network-policy/policy-tiers/rbac-tiered-policies) * [Kubernetes NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) * [Kubernetes RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) * [Open Source Istio/Dikastes application policy](https://docs.tigera.io/calico/latest/network-policy/istio/app-layer-policy) * [Enterprise domain policy](https://docs.tigera.io/calico-enterprise/latest/network-policy/domain-based-policy) * [Staged policies](https://docs.tigera.io/calico/latest/network-policy/staged-network-policies) * [Host failsafes](https://docs.tigera.io/calico/latest/reference/host-endpoints/failsafe) * [Pre-DNAT](https://docs.tigera.io/calico/latest/reference/host-endpoints/pre-dnat) * [Forwarded host traffic](https://docs.tigera.io/calico/latest/reference/host-endpoints/forwarded) * [KubeControllersConfiguration](https://docs.tigera.io/calico/latest/reference/resources/kubecontrollersconfig) * [Policy logging](https://docs.tigera.io/calico/latest/network-policy/policy-rules/log-rules) * [Component metrics](https://docs.tigera.io/calico/latest/operations/monitor/monitor-component-metrics) [이전: Part 4 - BGP 아키텍처 심화](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md) | [다음: Part 6 - eBPF 데이터플레인](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/06-ebpf-dataplane.md) | [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) * [Calico eBPF protocol support](https://docs.tigera.io/calico/latest/operations/ebpf/install) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/06-ebpf-dataplane ---------------------------------------- # Part 6: eBPF 데이터플레인 > **검토 기준**: Calico 3.32.2; Calico 3.32의 Kubernetes 테스트 범위는 1.34–1.36입니다. **마지막 업데이트**: 2026년 9월 12일. > > 호환되는 기존 Linux Calico 클러스터와 표준 Calico API 서버를 전제로 합니다. 설치 소유자의 절차를 선택하며 각 설정 조각을 모든 클러스터에 순서대로 적용하지 않습니다. 이번 검토에서는 BPF 프로그램 로드·클러스터 전환·기존 벤치마크 재현을 실행하지 않았습니다. ## 개요 Calico eBPF 데이터 평면은 BPF 프로그램·맵으로 워크로드 네트워크, 정책, Kubernetes Service를 처리합니다. 적합한 경로에서 오버헤드를 줄일 수 있지만 성능은 부하와 설정에 따라 다릅니다. Calico는 기존 Linux 데이터 평면과 Windows HNS도 제공하므로 eBPF가 모든 플랫폼에 맞는 업그레이드는 아닙니다. 이 문서에서는 eBPF의 기본 개념부터 Calico에서의 활용, 마이그레이션 방법, 그리고 성능 최적화까지 심층적으로 다룹니다. ## eBPF 기본 개념 ### eBPF란? eBPF(extended Berkeley Packet Filter)는 Linux 커널 내에서 샌드박스된 프로그램을 실행할 수 있게 해주는 기술입니다. 커널을 수정하거나 모듈을 로드하지 않고도 커널의 동작을 확장할 수 있습니다. ![일반적인 BPF 로드와 hook 구조.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-06-ebpf-dataplane-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-06-ebpf-dataplane-1.html) > VM은 추상적인 명령어 모델입니다. JIT를 사용하면 hook에서 네이티브 코드를 실행하며 JIT 뒤에 별도 guest VM이 추가되는 것은 아닙니다. Calico hook 전체의 정확한 목록도 아닙니다. ### eBPF 핵심 구성 요소 | 구성 요소 | 역할 | 설명 | | -------------------- | -------- | ------------------ | | **Verifier** | 안전성 검증 | 무한 루프, 메모리 위반 방지 | | **JIT Compiler** | 성능 최적화 | 바이트코드를 네이티브 코드로 변환 | | **BPF Maps** | 데이터 저장 | 커널-사용자 공간 데이터 공유 | | **Helper Functions** | 커널 기능 접근 | 안전한 커널 API 호출 | | **Hook Points** | 실행 지점 | XDP, TC, Socket 등 | ### 실행 지점과 커널 경로 iptables와 eBPF 패킷 처리는 모두 커널에서 수행합니다. iptables 규칙 하나를 검사할 때마다 사용자 공간으로 전환하는 것은 아닙니다. iptables 모드의 kube-proxy는 Service 규칙을 만드는 제어 평면 프로세스이며 패킷이 그 프로세스를 통과하지 않습니다. | 구성 | 역할·범위 | | --- | --- | | TC packet hook | 정책·라우팅·연결 상태·패킷 기반 Service 처리. 인터페이스 ingress/egress와 워크로드 방향은 구분 | | Cgroup socket-address hook | connect-time 목적지 변환. 현재 loader는 connect 및 UDP 활성화 시 sendmsg/recvmsg hook 연결 | | XDP | 지원·설정된 조기 패킷 처리. classic 데이터 평면 가속과 전체 eBPF 데이터 평면 내부 동작을 구분 | | 프로그램·IP-set·counter map | 컴파일한 프로그램과 상태 지원. 모든 정책이 tuple→action 맵 하나에 담기는 것은 아님 | 서로 다른 실행 문맥이며 XDP → TC → sockops → sk_msg → TC를 모든 패킷이 통과하는 파이프라인이 아닙니다. Calico CTLB가 sk_msg로 HTTP 메서드를 검사하지는 않으며 L7에는 별도의 [Istio/Dikastes 통합](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md)이 필요합니다. 해시 조회, longest-prefix match, 정책 명령어, tail call, conntrack의 비용은 서로 다릅니다. “모든 eBPF 정책은 O(1)”이나 “규칙·연결 수와 무관하게 메모리가 일정”하다는 결론은 틀립니다. iptables도 IP set을 활용할 수 있고 NAT 규칙 선택은 보통 연결 첫 패킷에서 수행하며 이후 변환은 conntrack 상태를 사용합니다. ## Calico eBPF와 iptables의 비교 지연·처리량·CPU 차이는 데이터 경로와 부하에서 측정해야 합니다. 단일 eBPF 프로그램이 모든 계층을 한 번에 처리한다고 가정하지 마세요. 아래의 기존 수치는 별도 검증되지 않은 보고 기록이며 현재 성능 보장이 아닙니다. ### 기존 성능 보고 기록 이전 영문·한글 문서는 **서로 다른 검증되지 않은 기록**을 담고 있었습니다. 원래 숫자를 아래에 보존하지만 이번 감사에서 측정한 결과나 Calico 3.32.2의 성능 보장은 아닙니다. 두 기록 모두 원자료, 측정일, 정확한 Calico/Kubernetes/kernel 버전, 토폴로지, NIC/CPU, 연결 상태와 전체 측정 방법을 제시하지 않습니다. #### 기록 A: 이전 영문 문서 | 보고된 지연 | iptables | eBPF | 기존 반올림 감소율 | | --- | --- | --- | --- | | 같은 노드 Pod | 45 μs | 25 μs | 44% | | 다른 노드 Pod | 120 μs | 80 μs | 33% | | ClusterIP | 150 μs | 60 μs | 60% | | NodePort | 180 μs | 70 μs | 61% | | 보고된 처리량 | iptables | eBPF | 기존 반올림 증가율 | | --- | --- | --- | --- | | TCP 단일 스트림 | 15 Gbps | 23 Gbps | 53% | | TCP 다중 스트림 | 35 Gbps | 48 Gbps | 37% | | UDP 단일 스트림 | 8 Gbps | 18 Gbps | 125% | | 64바이트 패킷 | 2M pps | 5M pps | 150% | | 보고된 규칙 수 | iptables 연결/초 | eBPF 연결/초 | | --- | --- | --- | | 1,000 | 50,000 | 120,000 | | 5,000 | 35,000 | 115,000 | | 10,000 | 20,000 | 110,000 | 지연의 percentile은 지정되어 있지 않습니다. 연결/초는 직접적인 CPU 사용량 측정이 아니며 세 점만으로 모든 규모에서 정책 비용이 일정하다고 증명할 수 없습니다. #### 기록 B: 이전 한글 문서 | 보고된 지표 | iptables | eBPF | 기존 반올림 변화율 | | --- | --- | --- | --- | | 처리량 | 1.2M pps | 2.0M pps | +67% | | 지연 | 120 μs | 75 μs | −38% | | Service 1,000개에서 CPU | 70% | 30% | −57% | | 연결 설정 | 절대값 없음 | 절대값 없음 | 기존 주장 −50% | 기록 B와 A를 같은 실험으로 합치지 마세요. 메모리·복잡도 설명은 측정 데이터가 아니었으며 연결 설정 감소율에는 기준 시간이 없습니다. 고정된 “20–40% 향상”을 기대값으로 사용하지 않습니다. #### 재현 가능한 비교 같은 client/server 테스트 이미지, 노드, 트래픽 경로, CPU/NIC 할당, MTU, 부하로 비교하세요. 데이터 평면·커널·소프트웨어 버전, 시간, 표본 수, warm-up, 동시성, conntrack 상태, 로깅 설정을 기록합니다. 직접 Pod IP와 애플리케이션 Service는 별도로 검사합니다. ```bash # Requires ready test Pods with netperf/netserver and an appropriate test policy. CLIENT_POD=netperf-client SERVER_POD=netperf-server SERVER_IP="$(kubectl -n calico-demo get pod "$SERVER_POD" -o jsonpath='{.status.podIP}')" test -n "$SERVER_IP" kubectl -n calico-demo exec "$CLIENT_POD" -- \ netperf -H "$SERVER_IP" -t TCP_RR -l 30 kubectl -n calico-demo exec "$CLIENT_POD" -- \ netperf -H "$SERVER_IP" -t TCP_STREAM -l 30 ``` `TCP_RR` 기본 출력은 지연 percentile이 아닌 **초당 트랜잭션 수**입니다. 조건에 맞는 역수는 평균 트랜잭션 시간으로 해석할 수 있지만 p99 네트워크 지연이 아닙니다. netperf의 제어·데이터 연결을 격리 테스트 환경에서 허용해야 합니다. 워크스테이션에 설치해도 client Pod에 netperf가 설치되지는 않습니다. 이번 감사에서는 Calico 클러스터에서 이 절차를 실행하지 않았습니다. ## BPF Map 구조 다음은 **Calico 3.32.2의 IPv4 버전별 내부 형식**입니다. 안정적인 공개 ABI나 커널 map 쓰기 절차가 아닙니다. | Map | 유형 | key / value 바이트 | 용도 | | --- | --- | --- | --- | | Route | LPM trie | 8 / 8 | 목적지 접두사, flags, next-hop/interface union | | NAT frontend | LPM trie | 16 / 20 | Service·소스 접두사 매칭, backend group/count, affinity, flags | | NAT backend | Hash | 8 / 8 | backend group/ordinal → 주소·포트 | | Conntrack v4 형식 | LRU hash | 16 / 88 | 프로토콜·주소 쌍·포트·상태·NAT 정보 | | Affinity | LRU hash | 버전별 상이 | 클라이언트의 backend 선택 캐시 | IPv4 route key의 앞 4바이트는 little-endian 접두사 길이이고 뒤에는 IPv4 주소가 옵니다. value는 flags와 4바이트 next-hop/interface-index union이며 MAC 필드는 없습니다. conntrack은 32비트 프로토콜 필드 뒤에 주소·포트를 저장합니다. IPv6 형식은 다르므로 예전의 단순화된 struct를 실제 ABI로 사용하지 마세요. 정책은 BPF 명령어로 컴파일되고 map이 이를 지원합니다. rule-counter map이 정책 자체는 아닙니다. 해당 릴리스의 Calico 진단 도구로 type·용량·버전을 확인한 뒤 원시 바이트를 해석하세요. ## Direct Server Return (DSR) ![Kubernetes ingress 노드의 Service 전달과 DSR 반환 경로 비교.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-06-ebpf-dataplane-6.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-06-ebpf-dataplane-6.html) > frontend는 NodePort나 다른 Service 주소일 수 있습니다. 반환 소스 변환은 Calico가 처리하며 외부 클라우드 로드 밸런서는 그림에 없으므로 해당 반환 경로 제약도 별도로 적용됩니다. 그림의 “LB 노드”는 Kubernetes Service를 전달하는 노드입니다. DSR 응답이 그 노드를 우회할 수 있지만 외부 클라우드 로드 밸런서까지 자동으로 우회하지는 않습니다. 반환 소스 변환은 Calico가 처리하며 애플리케이션 Pod에 VIP 바인딩을 요구하는 것은 아닙니다. | `bpfExternalServiceMode` | 원격 backend 경로 | | --- | --- | | `Tunnel` (기본) | 요청·응답이 ingress 노드/터널 경로 사용 | | `DSR` | 요청을 원격 노드로 터널링하고 응답은 클라이언트 방향으로 직접 반환 | 이 필드에 `Disabled`나 `IPIP` 값은 없습니다. Calico의 해당 Service 전달은 VXLAN을 사용하므로 양쪽 모드 모두 MTU·underlay를 고려해야 합니다. DSR에는 원래 frontend/ingress 노드 주소를 소스로 보내는 트래픽을 fabric이 허용해야 합니다. Calico의 AWS 가이드는 노드가 같은 서브넷에 있고 source/destination check가 비활성화되어야 한다고 명시하므로 임의의 서브넷 간 배치로 일반화하지 마세요. 현재 Calico 문제 해결 가이드는 원래 target을 통한 반환이 필요한 AWS/GCP 외부 로드 밸런서 경로를 제외합니다. 같은 서브넷·source-check 조건만으로 그 트래픽에 DSR을 활성화하지 마세요. 이미 정상 동작하는 호환 eBPF 경로에서 설정할 필드는 다음과 같습니다. ```yaml apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: bpfExternalServiceMode: DSR ``` 설정 소유자를 통해 병합하고 반환 경로·소스 검사·기존 연결을 검증하세요. 모드 변경은 연결을 끊을 수 있습니다. DSR과 CTLB는 서로 다른 최적화입니다. ## Connect-Time Load Balancing ![Connect-time Service 변환과 패킷 기반 Service 변환 비교.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-06-ebpf-dataplane-7.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-06-ebpf-dataplane-7.html) > kube-proxy는 커널 규칙을 구성하며 패킷을 직접 운반하지 않습니다. iptables의 기존 연결은 최초 규칙 선택 이후 conntrack을 사용합니다. CTLB는 해당 Service DNAT 경로를 줄일 뿐 모든 패킷 처리를 제거하지 않습니다. 그림은 개념도입니다. kube-proxy는 커널 상태를 구성하고 이후 패킷은 Service 규칙 전체에서 backend를 다시 고르지 않고 conntrack을 사용합니다. CTLB는 지원되는 소켓의 Service 목적지를 패킷 처리 전에 바꾸며 모든 라우팅·정책·conntrack·다른 NAT까지 제거하지는 않습니다. 현재 필드는 `bpfConnectTimeLoadBalancing: TCP`(기본), `Enabled`, `Disabled`입니다. 이전 boolean `bpfConnectTimeLoadBalancingEnabled`는 deprecated이지만 아직 허용됩니다. 양쪽을 무조건 함께 설정하지 말고 기존 override를 소유자와 확인·정리하세요. ```yaml apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: bpfConnectTimeLoadBalancing: TCP bpfHostNetworkedNATWithoutCTLB: Enabled ``` `Enabled`는 UDP 소켓 처리도 포함할 수 있고 `TCP`는 TCP만 처리합니다. `bpfHostNetworkedNATWithoutCTLB`는 보완적인 host-network NAT 경로이며 ClusterIP 자체의 지원 스위치가 아닙니다. 원래 Service 주소를 봐야 하는 서비스 메시에서는 CTLB를 꺼야 할 수 있으므로 검증한 통합 절차를 따르세요. ## XDP 가속 Native driver XDP는 skb 할당 전에 처리할 수 있지만 Generic XDP는 skb가 있는 더 뒤의 경로에서 동작합니다. 하드웨어 offload는 NIC·driver·프로그램에 따라 지원이 다르며 모드 이름만으로 성능 순위를 보장하지 않습니다. Felix의 `xdpEnabled`는 classic iptables 데이터 평면에서 적합한 untracked ingress Deny를 가속하는 **boolean**입니다. `genericXDPEnabled`의 기본값은 false이므로 자동 Generic fallback을 보장하지 않습니다. 전체 eBPF 데이터 평면 내부 XDP 프로그램과는 별도 설정입니다. ```yaml # 별도의 classic iptables 데이터 평면 가속 예시 apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: xdpEnabled: true genericXDPEnabled: false ``` 해당 인터페이스의 driver와 실제 attachment를 확인하세요. `Enabled`, `Offload`, `BestEffort`를 xdpEnabled enum 값으로 사용하지 않습니다. ## eBPF 모드 요구사항 과거 최소 버전이 아닌 선택한 릴리스의 현재 조건을 확인하세요. | 항목 | Calico 3.32 eBPF 범위 | | --- | --- | | 기본 Linux 커널 | 5.10 이상. 문서화된 RHEL 예외는 RHEL 8.4의 4.18.0-305 이상 | | 아키텍처 | x86-64 또는 little-endian arm64 | | Datastore | Kubernetes. 이 모드는 etcd datastore를 지원하지 않음 | | 추가 기능 | eBPF Log 규칙은 커널 5.16, 문서화된 QoS 대역폭 제어는 6.6/TCX 필요 | | Underlay | 설정된 VXLAN 노드 간 통신 허용. Pod 풀이 비캡슐화여도 NodePort 전달에 사용 가능 | | 실행 환경 | 필요한 BPF/cgroup 기능·권한·쓰기 가능한 mount. 불변 OS에는 적합한 `CgroupV2Path` 필요 | BTF는 CO-RE와 도구의 타입 정보이며 verifier 자체나 모든 커널 호환성 보장이 아닙니다. 릴리스 loader에는 지원되는 경로에서 CO-RE/non-CO-RE 객체를 선택하는 코드가 있으므로 `/sys/kernel/btf/vmlinux`만으로 준비 상태를 판정하지 마세요. 실제 요구 사항·노드 설정·로드 진단을 확인합니다. bpffs에 pin한 객체는 생성 프로세스 이후에도 유지될 수 있지만 재부팅을 넘어 보존하는 영구 디스크 데이터는 아닙니다. ### 플랫폼 경계 현재 Calico 가이드는 자체 관리/kubeadm, kOps, OpenShift, EKS, MKE와 조건이 있는 AKS/RKE 경로를 설명합니다. GKE, eBPF와 표준 데이터 평면/Windows를 계속 혼용하는 클러스터, SCTP 정책·Service는 지원하지 않습니다. IPv6와 IPv6-only 경로가 문서화되어 있으므로 “IPv6 미지원은 dual-stack으로 해결”하는 설명은 맞지 않습니다. AKS Azure CNI는 관리되는 kube-proxy를 끌 수 없으며 Calico networking을 사용하는 AKS 경로는 별도로 테스트 중이라고 명시합니다. OS 이름·Ubuntu 이미지·커널 버전만으로 플랫폼 지원을 보장할 수 없습니다. EKS 노드 모드, CNI 조합, OS variant는 해당 Calico/EKS 절차를 따라야 하며 필요한 privileged 노드 컴포넌트를 실행할 수 없는 환경이나 관리형 네트워킹 모드까지 지원된다고 확대하지 마세요. Windows는 Linux iptables가 아닌 HNS 데이터 평면을 사용합니다. Windows/표준 노드와 eBPF canary 노드를 지속적으로 혼합하지 마세요. 별도 대표 테스트 클러스터에서 검증한 뒤 문서화된 전체 전환을 따릅니다. ### 읽기 전용 현황 확인 ```bash kubectl get nodes -o wide kubectl -n kube-system get daemonset kube-proxy -o yaml kubectl get installation.operator.tigera.io default -o yaml kubectl get felixconfiguration.projectcalico.org default -o yaml ``` 실제 설치 namespace에서 Calico/operator 이미지 버전을 확인하세요. 각 노드의 호스트 mount namespace에서 `uname -r`, BTF, bpffs, cgroup을 확인합니다. debug 컨테이너의 파일시스템이 자동으로 호스트와 같은 것은 아닙니다. 모드 변경을 위해 추측한 Helm release 이름으로 업그레이드하거나 기존 values를 버리지 마세요. ## iptables → eBPF 마이그레이션 ### Operator 자동 bootstrap의 조건 Tigera Operator로 설치한 자체 관리 kubeadm 기반 클러스터이고, `kube-system`의 kube-proxy를 **Helm·Argo CD 등 다른 reconciler가 관리하지 않으며**, operator가 Kubernetes Service/endpoint를 읽을 수 있어야 합니다. ```bash kubectl get installation.operator.tigera.io default -o yaml # Only when every automatic-bootstrap prerequisite above is met: kubectl patch installation.operator.tigera.io default --type=merge \ -p '{"spec":{"calicoNetwork":{"linuxDataplane":"BPF","bpfNetworkBootstrap":"Enabled","kubeProxyManagement":"Enabled"}}}' ``` operator가 직접 API 연결과 kube-proxy 전환을 관리합니다. rolling update 동안 일시적으로 노드 모드가 달라지며 공식 가이드는 NodePort 트래픽 중단 가능성을 명시합니다. 무중단을 보장하는 전환으로 설명하지 마세요. ### 수동 준비와 소유권 다른 지원 설치에서는 바꾸려는 Service 구현에 의존하지 않는 안정적인 **직접 API 서버 연결**을 먼저 준비합니다. 실제 API 로드 밸런서 hostname/주소와 포트를 사용하세요. EKS는 클러스터 API endpoint hostname과 보통 443을 사용하며 아래는 자체 관리 API 주소의 placeholder입니다. ```yaml apiVersion: v1 kind: ConfigMap metadata: name: kubernetes-services-endpoint namespace: tigera-operator data: KUBERNETES_SERVICE_HOST: api.internal.example.com KUBERNETES_SERVICE_PORT: "6443" ``` operator 설치는 `tigera-operator`, 독립 manifest 설치는 `kube-system`에 둡니다. 이름은 복수형 **`kubernetes-services-endpoint`**입니다. Calico가 변경을 받아 주소를 해석·연결하는지 확인한 뒤 Service 데이터 평면을 바꾸세요. DNS bootstrap·보안 규칙·도달성은 플랫폼별 전제입니다. kube-proxy가 IPVS라면 공식 절차에 따라 iptables로 전환하고 계획된 노드 재시작을 먼저 수행해야 합니다. 별도의 통제된 변경으로 다루세요. kube-proxy를 실제 소유자와 조정합니다. AKS Azure CNI처럼 계속 실행해야 하는 경우 다음 필드를 기존 Felix 설정에 병합합니다. ```yaml apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: bpfKubeProxyIptablesCleanupEnabled: false bpfKubeProxyHealthzPort: 0 ``` cleanup 필드는 Service 처리 활성화 스위치가 아닙니다. kube-proxy가 실행 중인데 cleanup을 켜면 iptables 규칙을 서로 생성·삭제하고, 양쪽 health server가 10256을 사용하면 충돌합니다. 다른 Felix 설정은 보존하세요. 데이터 평면은 소유권에 맞는 **한 가지** 경로로 전환합니다. ```bash # Operator installation: kubectl patch installation.operator.tigera.io default --type=merge \ -p '{"spec":{"calicoNetwork":{"linuxDataplane":"BPF"}}}' ``` ```bash # Alternative: standalone manifest installation, without operator ownership: kubectl patch felixconfiguration.projectcalico.org default --type=merge \ -p '{"spec":{"bpfEnabled":true}}' ``` kube-proxy를 수동으로 끄는 설치는 계획된 전환 시간에 해당 플랫폼 절차를 따릅니다. 먼저 원하는 기존 설정을 보존하세요. 임시 nodeSelector를 사용한다면 기존에 없는 키를 선택하고 어느 노드에도 일치하지 않는지 확인한 뒤 복구 시 추가한 키만 제거합니다. 원래 selector map 전체를 null로 덮어쓰지 마세요. DaemonSet 삭제나 존재하지 않는 kube-proxy Deployment를 0으로 scale하는 것은 일반적인 전환 절차가 아닙니다. ### 로드된 프로그램뿐 아니라 트래픽 검증 대상 노드의 rollout과 실제 BPF 프로그램·맵을 확인합니다. 새 Pod 간 연결, DNS, ClusterIP, NodePort/외부 연결, 정책 거부, 필요한 host-network 경로를 노드 간에 검증하세요. 프로그램 목록이나 iptables 출력 줄 수만으로 정상 연결을 증명하지 못합니다. Kubernetes API는 보통 `http://kubernetes.default.svc`가 아닌 HTTPS를 사용합니다. 올바른 TLS 신뢰와 적절한 identity로 검사하고 인증 실패와 네트워크 실패를 구분하세요. 인증 없는 API 요청보다 통제한 애플리케이션 Service를 연결 검사 대상으로 사용하는 편이 명확합니다. ### 롤백 동일한 소유자로 모드 변경을 되돌립니다. ```bash # Operator installation: use its owner/GitOps source for the same change. kubectl patch installation.operator.tigera.io default --type=merge \ -p '{"spec":{"calicoNetwork":{"linuxDataplane":"Iptables"}}}' ``` ```bash # Alternative for standalone manifest installations: kubectl patch felixconfiguration.projectcalico.org default --type=merge \ -p '{"spec":{"bpfEnabled":false}}' ``` 자동 bootstrap은 operator가 kube-proxy를 복원합니다. 수동으로 비활성화했다면 원래 selector와 다른 설정을 유지하며 임시 변경만 소유자를 통해 복원합니다. Service 규칙과 트래픽을 다시 확인하세요. eBPF 비활성화나 외부 Service 모드 변경은 기존 연결을 끊을 수 있으며 노드 재시작이 모든 애플리케이션 상태를 정리한다고 보장하지 않습니다. ## eBPF 디버깅 Calico node 이미지에는 **`calico-node -bpf`**로 실행하는 진단 도구가 포함됩니다. 별도 `calico-bpf` 소스 entry point도 있지만 node 이미지에 독립 바이너리가 설치되었다고 가정하지 마세요. 내장 도구에는 `help`를 사용합니다. wrapper가 BPF 하위 명령보다 먼저 `--help`를 처리할 수 있습니다. ```bash CALICO_NAMESPACE=calico-system CALICO_NODE=demo-worker CALICO_POD="$(kubectl -n "$CALICO_NAMESPACE" get pods -l k8s-app=calico-node \ --field-selector "spec.nodeName=$CALICO_NODE" -o jsonpath='{.items[0].metadata.name}')" test -n "$CALICO_POD" kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf help kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf routes dump kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf conntrack dump kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf nat dump kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf counters dump ``` ```bash # Choose an interface actually attached on this node. BPF_INTERFACE=eth0 kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf policy dump "$BPF_INTERFACE" all # IPv6, when enabled: put the debug-tool flag after its subcommand. kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ calico-node -bpf routes dump --ipv6 ``` ```bash kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- bpftool prog show kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- bpftool map show kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- bpftool net show kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ tc filter show dev "$BPF_INTERFACE" ingress kubectl -n "$CALICO_NAMESPACE" exec "$CALICO_POD" -c calico-node -- \ tc filter show dev "$BPF_INTERFACE" egress ``` `policy dump`에는 인터페이스와 hook(`ingress`, `egress`, `xdp`, `all`)을 모두 지정해야 합니다. 정책 debug 정보가 있어야 하며 `bpfPolicyDebugEnabled` 기본값은 true입니다. 워크로드 ingress는 host-side veth의 TC/TCX **egress** hook에, host ingress는 호스트 인터페이스 ingress에 적용되므로 hook 단어만으로 워크로드 방향을 판단하지 마세요. `nat dump`는 인수 없이 또는 IP/port/protocol 세 개로 실행합니다. 검토한 CLI에는 `nat frontend list`가 없습니다. bpftool의 ID별 명령은 실제 program/map ID를 확인한 뒤 사용하세요. raw key는 버전별 전체 map 형식에 맞아야 하며 IPv4 주소 4바이트만으로 route map을 조회할 수는 없습니다. bpftool의 실제 `max_entries`와 크기 정보를 사용하세요. `/proc/sys/kernel/bpf_map_max_entries`는 일반적인 Linux map 용량 제어 항목이 아닙니다. `run_cnt`·`run_time_ns`는 런타임 통계 활성화가 필요하고 전체 요청 지연을 의미하지 않습니다. TCX 연결은 기존 `tc filter show` 외에 bpftool link/attachment 조회도 필요할 수 있습니다. ### 로그와 패킷 캡처 `bpfLogLevel`은 **Off·Info·Debug**를 허용하며 Warn/Warning은 잘못된 값입니다. BPF 프로그램 로그는 trace pipe로, Felix 컴포넌트 로그는 컨테이너 stdout으로 출력됩니다. 알맞은 노드와 trace 도구를 사용하고 Pod rollout 성공만으로 패킷이 허용된다고 판단하지 마세요. Calico가 workload peer로 직접 redirect하면 host-side veth의 hook을 거치지 않아 해당 위치의 캡처에서 트래픽이 보이지 않을 수 있습니다. 실제 경로에서 캡처하고 정책·route·conntrack·Service backend를 함께 확인하세요. ## Kubernetes Service 대체와 제한 ![CTLB를 사용하지 않는 패킷 기반 Service 전달의 개념적 경로.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-06-ebpf-dataplane-8.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-06-ebpf-dataplane-8.html) > 초기 backend 선택과 이후 conntrack 변환을 구분하세요. NAT frontend는 단순 해시가 아닌 LPM trie이며 backend 선택 방식도 설정에 따라 달라집니다. 그림은 모든 Service 경로의 보편적인 알고리즘이 아닙니다. Calico eBPF는 Service 전달을 구현하며 외부 클라우드 로드 밸런서를 생성하지 않습니다. AWS/클라우드 controller의 책임과 구분하세요. 현재 구현에는 IPv4/IPv6 NAT map, local-traffic flag, affinity 처리가 있습니다. 이전의 “IPv6/Local 미지원” 표를 그대로 적용하거나 모든 kube-proxy 옵션이 동일하다고 가정하지 않습니다. 릴리스된 WireGuard 기능 테스트에는 BPF 모드와 IPv4/IPv6 설정이 포함되므로 WireGuard가 eBPF와 **무조건 호환되지 않는 것은 아닙니다**. 실제 CNI·트래픽 종류·커널·MTU·암호화 경로를 검증하세요. 일반 암호화 가이드에는 오래된 제약·설치 예시도 있어 현재 OS에 그대로 적용하면 안 됩니다. hostNetwork 워크로드는 CTLB/host-NAT와 HostEndpoint 정책을 각각 확인합니다. 현재 eBPF 가이드에서는 SCTP와 eBPF/표준/Windows를 지속 혼합하는 클러스터를 제외합니다. Windows는 지원되는 HNS 구성이 필요합니다. 현재 Calico 문제 해결 가이드는 AWS/GCP 외부 로드 밸런서가 원래 target을 통한 응답 경로를 요구하는 경우 DSR이 정상 동작하지 않는다고 명시합니다. 같은 서브넷·source-check 조건만으로 클라우드 LB 호환성을 보장하지 말고 지원된 모드와 전체 경로를 확인하세요. ## 설정과 관측성 측정 근거가 없으면 릴리스 기본값을 우선 사용하세요. 아래는 이미 활성화된 eBPF 설치에서 현재 필드·값을 설명하는 조각이며 설정 소유자를 통해 병합합니다. ```yaml apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: bpfLogLevel: "Off" bpfExternalServiceMode: Tunnel bpfConnectTimeLoadBalancing: TCP bpfHostNetworkedNATWithoutCTLB: Enabled ``` `bpfDataIfacePattern`을 임의의 `eth*`나 좁은 표현식으로 덮어쓰지 마세요. 정규식으로 실제 underlay/Service 인터페이스를 포함하고 workload·Calico 전용 장치는 제외해야 합니다. 인터페이스 이름만으로 XDP offload 지원을 증명하지는 못합니다. `bpfKubeProxyEndpointSlicesEnabled`는 현재 Felix 필드가 아닙니다. 이전 CTLB boolean은 deprecated이지만 제거되지는 않았습니다. conntrack timeout을 조정한다면 현재 키는 `tcpEstablished`, `tcpFinsSeen`, `tcpResetSeen`, `udpTimeout`, `genericTimeout`, `icmpTimeout` 등이며 `tcpClosing`, `udp`, `icmp`가 아닙니다. 모든 배포에 일괄적으로 백만 엔트리를 지정하기보다 실제 점유율·메모리를 측정하세요. 릴리스 endpoint manager가 등록하는 실제 gauge는 다음과 같습니다. | 메트릭 | 의미 | | --- | --- | | `felix_bpf_dataplane_endpoints` | 관리하는 BPF endpoint 수 | | `felix_bpf_dirty_dataplane_endpoints` | 실패 후 아직 dirty인 endpoint 수 | | `felix_bpf_happy_dataplane_endpoints` | 정상 프로그래밍된 endpoint 수 | 설정 소유자를 통해 Felix metrics endpoint를 활성화하고 실제 scrape의 HELP/TYPE을 확인하세요. 이전 `calico_bpf_*` 목록은 실제 export 메트릭으로 검증되지 않았습니다. endpoint gauge가 map 점유율·패킷 거부 수·애플리케이션 지연을 대신 측정하지는 않습니다. 현재 플랫폼 호환성, 필요한 Service·정책·암호화 기능, 실제 부하 측정으로 데이터 평면을 선택하세요. 전환과 롤백을 모두 연습해야 하며 모든 클러스터에 eBPF나 iptables 중 하나가 항상 옳은 것은 아닙니다. *** ## 참고 자료 * [Calico 3.32 eBPF installation requirements](https://docs.tigera.io/calico/latest/operations/ebpf/install) * [Calico eBPF migration and rollback](https://docs.tigera.io/calico/latest/operations/ebpf/enabling-ebpf) * [Calico eBPF troubleshooting and CLI](https://docs.tigera.io/calico/latest/operations/ebpf/troubleshoot-ebpf) * [Felix configuration](https://docs.tigera.io/calico/latest/reference/resources/felixconfig) * [Operator installation API](https://docs.tigera.io/calico/latest/reference/installation/api) * [Kernel BTF](https://docs.kernel.org/bpf/btf.html) * [libbpf and CO-RE](https://docs.kernel.org/bpf/libbpf/libbpf_overview.html) * [Calico 3.32.2 route map layout](https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/felix/bpf/routes/map.go) * [Calico 3.32.2 NAT maps](https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/felix/bpf/nat/maps.go) * [Calico 3.32.2 conntrack v4 layout](https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/felix/bpf/conntrack/v4/map.go) * [Calico 3.32.2 connect-time loader](https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/felix/bpf/nat/connecttime.go) * [Calico 3.32.2 WireGuard functional tests](https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/felix/fv/wireguard_test.go) * [bpftool program reference](https://raw.githubusercontent.com/libbpf/bpftool/main/docs/bpftool-prog.rst) * [bpftool map reference](https://raw.githubusercontent.com/libbpf/bpftool/main/docs/bpftool-map.rst) [이전: Part 5 - Network Policy 심화](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md) | [다음: Part 7 - Calico 고급 주제](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md) | [메인 페이지로 돌아가기](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/README.md) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/07-advanced-topics ---------------------------------------- # Part 7: Calico 고급 주제 > **지원 버전**: Calico 3.32.2 / Kubernetes 1.34–1.36 (공식 테스트 범위) > **마지막 업데이트**: 2026년 9월 12일 ## 개요 이 문서에서는 Calico의 고급 기능과 대규모 프로덕션 환경에서의 활용 방법을 다룹니다. IPAM 심화, WireGuard 암호화, Egress Gateway, 멀티 클러스터 페더레이션, Windows 지원, 그리고 대규모 클러스터 설계에 대해 상세히 알아봅니다. ## IPAM 심화 이 절은 **Calico IPAM**을 설명합니다. Host-local과 클라우드 제공자 IPAM은 다른 할당자이며 IPPool 객체를 만드는 것만으로 다른 CNI가 Calico IPAM으로 바뀌지 않습니다. ### 블록·어피니티·할당 제한 Calico는 노드와 연결된 블록에서 주소를 할당합니다. IPv4 `/26`과 IPv6 `/122`는 각각 주소 64개를 포함하지만 모든 플랫폼에서 Pod 64개가 사용 가능하다는 뜻은 아닙니다. Windows는 Calico 소유 블록당 주소 네 개를 예약합니다. ![데이터스토어가 IPPool 10.244.0.0/16에서 /26 블록을 각 노드에 할당하고, 노드는 자신에게 친화성이 있는 블록(소진 시 추가 블록으로 확장) 안에서 Pod IP를 개별 배정하는 Block 기반 IPAM 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-07-advanced-topics-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-07-advanced-topics-0.html) > 그림은 할당 모델이며 모든 데이터스토어 쓰기를 없애는 노드 로컬 캐시를 의미하지 않습니다. 마지막 Pod가 없어져도 터널·VM 할당과 조정/lifecycle 상태 때문에 블록 어피니티가 즉시 해제되는 것은 아닙니다. 일반적인 자동 할당은 기존 어피니티 블록, 새 적합 블록, 허용된 차용을 사용합니다. `strictAffinity`, `autoAllocateBlocks`, 전역/요청별 블록 제한, 풀 선택, 플랫폼 제약 때문에 다른 풀에 여유 IP가 있어도 실패할 수 있습니다. ![Pod 생성 시 Calico IPAM이 노드 친화 블록의 여유 IP를 먼저 쓰고, 없으면 IPPool에서 새 블록을 할당하고, 그것도 없으면 다른 노드 블록에서 IP를 차용하며, 어디에도 여유 IP가 없을 때만 할당이 실패하는 IP 할당 알고리즘의 결정 흐름을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-07-advanced-topics-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-07-advanced-topics-1.html) > 단순화한 흐름은 적합한 풀·자동 블록 할당·허용된 차용·제한에 걸리지 않는 상황을 가정합니다. Windows는 차용을 지원하지 않으며 전체 주소가 고갈되어야만 실패한다는 보장이 아닙니다. 전역 설정은 `IPAMConfiguration/default`를 확인하세요. 웹 참고 문서에는 기본 제한 20이 적혀 있지만 **3.32.2 릴리스 구현과 공개 CRD는 설정이 없을 때 `maxBlocksPerHost: 0`을 생성**합니다. 0은 전역 블록 제한이 없다는 뜻이며 요청별·플랫폼 제한은 남습니다. 기존 클러스터의 설정값도 유지됩니다. 검토한 IPAM 설정 경로에서는 양수 전역 제한에 `strictAffinity: true`가 필요합니다. ### 풀 생성 전에 blockSize 선택 기본값은 IPv4 `/26`, IPv6 `/122`이며 범위는 IPv4 `/20`–`/32`, IPv6 `/116`–`/128`입니다. GPU 대역폭이나 “200노드 이상이면 /28” 같은 공식이 아니라 예상 주소 수, 노드 수, 경로 집계, 할당 오버헤드로 선택하세요. ```yaml # Fresh-pool example; do not apply over an existing pool or overlapping pools. apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: demo-ipv4-pool spec: cidr: 10.244.0.0/16 blockSize: 26 ipipMode: Never vxlanMode: Always natOutgoing: true nodeSelector: all() ``` 기존 풀의 `blockSize`와 CIDR은 변경할 수 없습니다. 새 풀과 마이그레이션은 실제 라우팅, Service/노드 CIDR 경계, 워크로드 할당 계획을 보존해야 합니다. [네트워킹 모드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)를 참고하고 위 집계 풀과 아래 하위 풀을 겹치게 생성하지 마세요. ### Host-Local IPAM Host-local은 노드 로컬 할당 상태와 Kubernetes가 제공하는 노드별 PodCIDR 구성을 사용합니다. operator의 선택 필드는 **`spec.cni.ipam.type: HostLocal`**입니다. ```yaml # Installation fragment: preserve other settings through the configuration owner. apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: cni: type: Calico ipam: type: HostLocal ``` `calicoNetwork.hostLocalIPAMEnabled`라는 스위치는 없습니다. Kubernetes controller/네트워크 설정이 유효하고 서로 다른 노드 PodCIDR을 제공해야 합니다. 기존 Node를 수동 patch하는 것은 IPAM 마이그레이션 절차가 아닙니다. 두 할당자의 IP 해제 속도·확장성에 보편적인 순위를 매기지 마세요. ### 여러 풀과 명시적 요청 목적을 정한 서로 겹치지 않는 풀을 사용합니다. 세 번째 풀은 manual-only로 설정하여 일반 워크로드가 자동으로 비-SNAT 범위를 소비하지 않게 합니다. ```yaml # Alternative to demo-ipv4-pool; these sub-pools must not overlap another pool. apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: production-pool spec: cidr: 10.244.0.0/18 blockSize: 26 vxlanMode: Always natOutgoing: true nodeSelector: node-type == 'production' --- apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: development-pool spec: cidr: 10.244.64.0/18 blockSize: 28 vxlanMode: Always natOutgoing: true nodeSelector: node-type == 'development' --- apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: routed-workloads-pool spec: cidr: 10.244.128.0/18 blockSize: 26 ipipMode: Never vxlanMode: Never natOutgoing: false assignmentMode: Manual allowedUses: [Workload] ``` `natOutgoing: false`에는 외부 반환 경로가 필요하고 이후의 upstream NAT가 적용될 수도 있습니다. Egress Gateway나 namespace별 고정 SNAT를 생성하지는 않습니다. LoadBalancer 주소 할당은 [BGP 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md)의 별도 `allowedUses: [LoadBalancer]` 절차를 사용하세요. 아래 Pod에는 준비한 namespace·노드 레이블과 placeholder를 대체할 검토된 워크로드 이미지가 필요합니다. ```yaml apiVersion: v1 kind: Pod metadata: name: production-app namespace: calico-demo annotations: cni.projectcalico.org/ipv4pools: '["production-pool"]' spec: nodeSelector: node-type: production containers: - name: app image: registry.example.com/team/app:approved ``` annotation은 IPPool을 요청하고 Pod의 `nodeSelector`는 스케줄링을 제어합니다. 검토한 Calico IPAM 코드에서 명시적 풀 요청은 호환성을 위해 풀의 node/namespace selector를 건너뛰므로 **권한 경계가 아닙니다**. 비활성화되었거나 없는 풀은 실패합니다. namespace annotation으로 기본 풀을 지정할 수 있지만 네트워크 정책을 대체하지 않습니다. ### IPv6와 Dual Stack Kubernetes, CNI/IPAM, 노드 주소, underlay가 선택한 IP family를 이미 지원해야 합니다. 풀이나 Felix flag만 추가해 클러스터 IP-family 구성을 전환하지 못합니다. ```yaml # A separate fresh dual-stack example. apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: dual-ipv4-pool spec: cidr: 10.244.0.0/16 blockSize: 26 vxlanMode: Always natOutgoing: true --- apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: dual-ipv6-pool spec: cidr: fd00:10:244::/48 blockSize: 122 ipipMode: Never vxlanMode: Always natOutgoing: false ``` 호환되는 Linux 데이터 평면은 IPv6 VXLAN을 지원하지만 IPv6 IP-in-IP는 지원하지 않습니다. 위 ULA 주소는 IPv6라는 이유로 인터넷에서 라우팅되지 않습니다. `natOutgoing: false`에는 적절한 반환 경로나 별도 egress 설계가 필요합니다. 노드 주소 자동 감지는 operator 설정 또는 해당 설치 환경에 속하며 임의의 Felix 필드가 아닙니다. ```yaml # Operator configuration fragment, not a replacement for the existing Installation. apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: calicoNetwork: nodeAddressAutodetectionV4: kubernetes: NodeInternalIP nodeAddressAutodetectionV6: kubernetes: NodeInternalIP ``` Felix의 `ipv6Support`는 `Enabled`가 아닌 boolean입니다. Felix 처리를 제어할 뿐 전체 dual-stack 전제를 대신하지 않습니다. ### 주소 해제 전 고갈 원인 확인 ```bash kubectl get ipamconfigurations.projectcalico.org default -o yaml calicoctl ipam show calicoctl ipam show --show-blocks calicoctl ipam check --show-problem-ips -o ipam-report.json ``` 풀 자격, 예약, 어피니티, 노드별 제한, 실제 워크로드·터널·VM 소유 상태를 확인하세요. 예시가 orphaned라고 부른다는 이유만으로 주소를 해제하지 않습니다. 검토한 CLI에는 `calicoctl ipam release --block` 옵션이 없습니다. release 도구는 `--from-report`와 여러 보고서의 교집합을 지원합니다. 하나 이상은 새 보고서여야 하며 보고서 기반 처리는 allocation sequence 정보도 사용합니다. 보고된 할당을 확인한 뒤 해당 버전의 복구 절차를 따르세요. `--force`, IPAMBlock/BlockAffinity 직접 삭제, 임의 단일 IP 해제를 일반적인 고갈 해결책으로 사용하지 않습니다. ## 노드와 연결된 CIDR 블록 조회 `BlockAffinity`는 Calico IPAM이 관리하며 상태·노드·CIDR·삭제 여부·어피니티 유형을 표시합니다. `Node.spec.podCIDR`과 동일하지 않으며 차용·이동 주소가 있을 때 모든 호스트 경로를 표현하지도 않습니다. ```bash kubectl get blockaffinities.projectcalico.org \ -o custom-columns='NAME:.metadata.name,CIDR:.spec.cidr,NODE:.spec.node,STATE:.spec.state,DELETED:.spec.deleted,TYPE:.spec.type' kubectl get ippools.projectcalico.org \ -o custom-columns='NAME:.metadata.name,CIDR:.spec.cidr,BLOCK_SIZE:.spec.blockSize' # Review active host affinities; exclude deletion states and virtual affinities. kubectl get blockaffinities.projectcalico.org -o json | jq -r \ '.items[] | select(.spec.state == "confirmed" and .spec.deleted != true and ((.spec.type // "") == "" or .spec.type == "host")) | [.spec.cidr, .spec.node] | @tsv' ``` 이 출력은 현황이며 즉시 실행할 `ip route add` 목록이 아닙니다. 실제 node next hop, 할당 상태, 풀 export/encapsulation 규칙, more-specific 경로가 필요합니다. placeholder 노드 IP나 모든 affinity 레코드만으로 유효한 정적 라우팅 계획을 만들 수 없습니다. **EKS Hybrid Nodes**의 [전용 CNI 가이드](https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html)는 AWS 유지 Cilium 빌드를 문서화하고 Calico 예시를 Hybrid Examples 저장소로 안내합니다. 한편 AWS [일반 대체 CNI 문서](https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html)는 여전히 Hybrid Nodes에서 Cilium/Calico 핵심 기능 지원을 설명합니다. 두 문서에서 일관된 Calico 버전별 지원 매트릭스를 확인할 수 없으므로 예시 이동만으로 지원 종료를 추론하지 마세요. 계획한 배포판·기능·지원 주체를 확인해야 하며, 이 절은 Hybrid 설치 레시피가 아닌 Calico IPAM 현황 확인입니다. ## WireGuard 암호화 WireGuard는 **기능을 지원하고 설정한 노드 사이**의 지원 트래픽을 보호합니다. 애플리케이션 간 TLS가 아니며 같은 노드의 Pod 트래픽은 이 노드 간 터널을 지나지 않습니다. WireGuard를 지원하지 않는 노드와의 트래픽은 암호화되지 않을 수 있으므로 실제 CNI, IP family, 워크로드/호스트 경로를 검증해야 합니다. ![Pod A의 평문 트래픽이 Node 1의 WireGuard 인터페이스(wireguard.cali)에서 암호화되어 eth0을 통해 UDP 51820 WireGuard 터널로 Node 2에 전달되고, Node 2의 WireGuard 인터페이스에서 복호화되어 Pod B에 평문으로 도달하는 노드 간 암호화 경로를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-07-advanced-topics-2.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-07-advanced-topics-2.html) > 그림의 “평문”은 로컬 구간에 WireGuard 터널 보호가 적용되지 않는다는 의미이며 애플리케이션은 별도로 TLS를 사용할 수 있습니다. 예시는 IPv4 기본 포트 51820이며 IPv6에는 별도 인터페이스·포트 설정이 있습니다. ### 필요한 IP Family만 활성화 ```yaml # Example for an already compatible dual-stack Linux deployment. apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: name: default spec: wireguardEnabled: true wireguardEnabledV6: true ``` IPv4에는 `wireguardEnabled`, 실제 활성화된 IPv6 경로에는 `wireguardEnabledV6`를 사용합니다. 필드가 있다는 이유만으로 IPv6를 켜지 마세요. 설정 소유자를 통해 다른 Felix 값을 보존하고 양쪽 peer의 커널 지원을 확인합니다. operator IPPool encapsulation에 `WireguardCrossSubnet`이라는 값은 없습니다. 실제 underlay/캡슐화 경로에 별도 값이 필요한 경우가 아니면 MTU 자동 감지를 유지하세요. IPv4 underlay 1500과 WireGuard 오버헤드 60이면 1440이지만 IPv6·플랫폼 경로는 다릅니다. [MTU 설명](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)에서 서로 대체하는 캡슐화 경로의 오버헤드를 무조건 합산하면 안 되는 이유를 확인하세요. `wireguardHostEncryptionEnabled`는 지원되는 **노드 간 host-originated/hostNetwork 트래픽**을 다루며 로컬 host-to-Pod 구간 암호화 스위치가 아닙니다. 플랫폼의 지원 트래픽 범위를 확인해야 합니다. ### 키·Keepalive·상태 확인 Calico는 노드 키를 관리하고 peer에게 공개 키 정보를 제공합니다. WireGuard handshake에서 세션 키를 새로 유도하는 동작과 관리자가 정한 노드 identity 키 교체 정책은 별개입니다. Persistent keepalive는 유휴 중 NAT/방화벽 상태를 유지합니다. 알려진 25 **초** 예시는 키 로테이션 간격이 아닙니다. 검토한 Open Source Felix 스키마에는 `wireguardPersistentKeepAlive`와 `wireguardPersistentKeepalive` 필드가 모두 없습니다. ```bash # On the intended node with wireguard-tools available: WIREGUARD_INTERFACE=wireguard.cali wg show "$WIREGUARD_INTERFACE" public-key wg show "$WIREGUARD_INTERFACE" latest-handshakes wg show "$WIREGUARD_INTERFACE" endpoints wg show "$WIREGUARD_INTERFACE" transfer ``` ```bash # Kubernetes datastore: public identity information only. kubectl get nodes -o json | jq -r \ '.items[] | [.metadata.name, (.metadata.annotations["projectcalico.org/WireguardPublicKey"] // "-"), (.metadata.annotations["projectcalico.org/WireguardPublicKeyV6"] // "-")] | @tsv' ``` IP family에 맞는 실제 인터페이스를 선택하세요. 공개 키·handshake·바이트 카운터는 진단 자료이지만 모든 애플리케이션 흐름의 암호화를 증명하지 않습니다. 의도한 트래픽 경로와 암호화된 전송을 확인하세요. `calicoctl node status`는 WireGuard 상태 표가 아니며 이 확인을 위해 개인 키를 출력할 필요도 없습니다. ### 보존한 성능 보고 기록 이전 두 언어 문서는 서로 다른 검증되지 않은 수치를 제시했습니다. 측정일, 하드웨어, 가속 설정, 소프트웨어 버전, 원자료가 없으므로 현재 성능 보장이 아닌 기존 보고값으로 보존합니다. **기록 A — 이전 영문 문서:** | 지표 | WireGuard | IPsec (AES-GCM) | | --- | --- | --- | | 기준 대비 처리량 변화 | −5 to −10% | −15 to −25% | | 추가 지연 | 0.1–0.3 ms | 0.5–1.0 ms | | CPU 사용량 변화 | +10–15% | +30–50% | **기록 B — 이전 한글 문서:** | 지표 | WireGuard | IPsec (AES-GCM) | | --- | --- | --- | | 기준 대비 처리량 비율 | 95–98% | 85–90% | | 기준 대비 지연 변화 | +5–10% | +15–25% | | 정성적인 CPU 설명 | 중간 | 높음 | 기록 B는 비암호화 상태를 100% 기준으로 두고 비암호화 CPU를 낮음으로 표현했습니다. CPU 변화율이 percentage point인지 상대 변화인지도 지정하지 않았습니다. 두 기록을 같은 실험으로 합치지 마세요. ### WireGuard와 IPsec의 선택 조건 WireGuard는 제한된 암호 설계를 사용하고 IPsec은 여러 구현·알고리즘·키 관리 방식을 갖춘 프레임워크입니다. CPU, 패킷 오버헤드, 하드웨어 가속, roaming, 설정 복잡도는 구현과 실제 경로에 따라 다릅니다. 버전 없는 코드 줄 수는 보안 지표가 아니며 검토한 Open Source Felix 스키마에는 `ipsecEnabled` 필드가 없습니다. FIPS 요구 사항은 채택할 제품의 현행 인증 기록·버전·운영 조건과 대조하세요. ## Egress Gateway Calico Enterprise Egress Gateway는 선택한 클라이언트 트래픽을 SNAT하는 **transit Pod**입니다. 별도의 제품·플랫폼 요구 사항이 있으며 이 장 상단의 Open Source 검토 버전을 Enterprise 호환성 표로 사용하지 마세요. 경로는 클라이언트 egress 정책 → gateway Pod로 터널링 → gateway SNAT → gateway egress 정책 → 외부 네트워크입니다. 일반 NetworkPolicy Allow는 패킷을 우회시키거나 SNAT하지 않습니다. `BGPConfiguration.serviceExternalIPs`도 Service 경로 광고이지 워크로드의 egress identity 할당이 아닙니다. ### 현재 상용 설정 구조 문서화된 on-premises Calico CNI 경로에는 지원되는 Enterprise 설치, 준비된 namespace·Pod-security 권한, 라우팅되는 egress 주소, UDP 4790 연결이 필요합니다. GKE·Windows는 제외하며 AWS·Azure에는 별도 절차가 있습니다. 클라우드 CNI 구성에 아래 풀을 그대로 적용하지 마세요. 기존 default Felix 설정 소유자를 통해 `egressIPSupport`를 모든 노드에 일관되게 `EnabledPerNamespace` 또는 권한이 있는 `EnabledPerNamespaceOrPerPod`로 설정합니다. 리소스는 **`operator.tigera.io/v1` EgressGateway**이며 이미지·설정은 operator가 관리합니다. 임의의 `calico/egress-gateway` Deployment를 만들지 않습니다. ```yaml # Calico Enterprise example, not an Open Source gateway installation. apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: egress-demo-pool spec: cidr: 203.0.113.0/28 blockSize: 32 nodeSelector: "!all()" natOutgoing: false --- apiVersion: operator.tigera.io/v1 kind: EgressGateway metadata: name: approved-egress namespace: calico-egress spec: replicas: 2 ipPools: - cidr: 203.0.113.0/28 template: metadata: labels: egress-code: approved spec: nodeSelector: kubernetes.io/os: linux --- apiVersion: v1 kind: Namespace metadata: name: calico-demo annotations: egress.projectcalico.org/selector: egress-code == 'approved' egress.projectcalico.org/namespaceSelector: projectcalico.org/name == 'calico-egress' ``` 문서용 CIDR을 소유한 실제 주소로 바꾸고 해당 네트워크의 캡슐화·라우팅을 구성하세요. `/32` 블록은 gateway마다 큰 블록을 예약하는 낭비를 줄입니다. `!all()`은 일반 자동 할당을 막지만 명시적 풀 요청은 사용할 수 있으므로 annotation 권한도 통제해야 합니다. 복제본 두 개에는 사용 가능한 IP 두 개와 적절한 노드·장애 영역 배치가 필요합니다. 복제본 수만으로 가용성을 보장하지 않습니다. Gateway 선택은 기본적으로 클라이언트 namespace 안에서 이루어지므로 다른 namespace에는 namespace selector가 필요합니다. `natOutgoing: false`는 해당 Calico NAT 단계에서 gateway Pod IP를 유지하지만 upstream NAT가 변경할 수 있습니다. Gateway 풀의 NAT를 켜면 gateway 노드 IP가 보일 수도 있습니다. 외부 수신자가 관찰한 주소와 의도한 허용 목록을 확인하세요. Gateway 교체·업그레이드는 기존 연결을 끊을 수 있습니다. ### 정책과 Identity 경계 클라이언트 egress 정책은 **원래 외부 목적지**를 봅니다. Gateway Pod IP로의 연결만 허용해도 원래 외부 흐름이 라우팅·허가되는 것은 아닙니다. Gateway egress에서는 원래 클라이언트 identity와 source port 정보가 이미 변환되었습니다. 목적지 CIDR·port 정책은 유용하지만 해당 hook의 도메인 기반 정책은 지원하지 않습니다. 고급 `EgressGatewayPolicy`에는 목적지·gateway 선택과 `maxNextHops` 필드가 있습니다. 이전 예시의 `projectcalico.org/v3 EgressGateway`에 `maxGatewaysPerClient`를 넣는 방식이 아닙니다. Open Source에서는 필요에 맞는 애플리케이션 proxy나 underlay/클라우드 NAT를 별도로 구성하고 허용 트래픽을 제어합니다. Bootstrap·listener·upstream 설정 없는 Envoy Pod는 작동하는 egress proxy가 아닙니다. 고정 소스 주소는 외부 허용 목록을 돕지만 자체적으로 PCI DSS/HIPAA 준수나 애플리케이션 권한 검사를 완성하지 않습니다. ## 멀티 클러스터 연결과 Federation 라우팅, endpoint identity, Service 검색을 구분하세요. BGP는 경로를 교환하지만 Kubernetes 정책·DNS 레코드를 배포하지 않습니다. Typha는 자체 배포 안의 상태를 배포하며 이전 그림처럼 대표 인스턴스가 공유 Federation Controller에 보고하는 구조가 아닙니다. ### Open Source 라우팅 연결 겹치지 않는 주소, 양방향 라우팅, 도달 가능한 next hop, 각 클러스터 정책이 필요합니다. 로컬 Calico 풀 밖의 원격 Pod CIDR로 보내면 `natOutgoing`이 SNAT하여 수신자가 보는 소스를 바꿀 수 있습니다. 적절한 NAT 제외와 라우팅을 계획하고 Pod identity가 그대로 유지된다고 가정하지 마세요. ```yaml # Receiving cluster only, after routing/source preservation is verified. apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: default.remote-client-access spec: order: 100 namespaceSelector: kubernetes.io/metadata.name == 'calico-demo' selector: app == 'shared-service' types: [Ingress] ingress: - action: Allow protocol: TCP source: nets: [10.245.0.0/16] destination: ports: [8080] ``` 이 정책은 로컬 수신 endpoint에만 적용합니다. `GlobalNetworkPolicy`는 해당 클러스터/데이터스토어 전역 범위이지 원격 클러스터에 자동 적용되는 정책이 아닙니다. 송신 egress·수신 ingress·실제 소스 주소를 확인하세요. 정책 배포에는 명시적인 관리 절차가 필요하며 BGP·Typha가 대신하지 않습니다. ### Enterprise Federation 현재 Enterprise 가이드는 다음 기능을 구분합니다. | 기능 | 동작 | | --- | --- | | Federated endpoint identity | 원격 workload/host endpoint 정보를 로컬 정책 계산의 입력으로 사용 | | Federated Services Controller | 원격 Kubernetes API에서 Service/endpoint 정보를 읽고 선택한 로컬 federated Service 유지 | | Multi-cluster networking | 지원되는 overlay 또는 별도로 구성한 Pod IP 라우팅 사용 | Federated endpoint identity는 **네트워크 정책을 복제하지 않습니다**. 원격 정책이 로컬에 자동 적용되는 것이 아니며 각 클러스터의 정책은 로컬에서 적용됩니다. Pod IP 도달성과 소스 보존은 identity 및 실제 사용 가능한 원격 Service endpoint의 전제입니다. 상용 federated Service의 annotation은 Pod가 아닌 **backing Service의 레이블**을 선택합니다. ```yaml # Commercial controller integration; backing Services already exist. apiVersion: v1 kind: Service metadata: name: catalog-federated namespace: calico-demo annotations: federation.tigera.io/serviceSelector: app == 'catalog' spec: type: ClusterIP ports: - name: http protocol: TCP port: 8080 ``` Backing Service는 선택한 클러스터에서 같은 namespace 이름에 있고 포트 **이름·프로토콜**이 일치해야 합니다. Federated Service에는 `spec.selector`를 생략하며 `targetPort`로 backing port를 선택하지 않습니다. Endpoint 레코드를 수동 관리하지 마세요. 원격 API 자격 증명, controller 설치, Kubernetes 버전·EndpointSlice 호환성, 네트워크 도달성은 별도 전제입니다. 위 예시가 이를 구성하거나 장애 전환을 검증한 것은 아닙니다. 현재 제품 절차와 실제 경로를 확인하고 문서의 2018년 Endpoints 출력 예시를 현재 배포 매니페스트로 복사하지 마세요. ## Windows 컨테이너 지원 Calico는 **HNS**로 Windows를 지원하지만 기능·플랫폼 제약이 있습니다. 제어 컴포넌트와 Typha에는 Linux 노드가 필요하며 혼합 클러스터가 Calico eBPF와 Windows를 함께 사용하는 방법은 아닙니다. ### 버전·플랫폼 교집합 Calico 3.32 테스트 범위의 Kubernetes 1.36 예시에는 양쪽이 명시한 Windows Server 2022를 사용할 수 있습니다. Kubernetes 1.36은 Server 2025도 명시하지만 Calico 요구 사항에는 이전 Server 1809와 Server 2022 항목이 남아 있습니다. 오래된 OS나 새 Kubernetes 지원 OS가 선택한 Calico·제공자 조합에서 모두 검증되었다고 가정하지 마세요. 호스트·컨테이너 base image의 OS/build와 유지보수 중인 호환 runtime/kubelet/kube-proxy 버전을 맞춰야 합니다. Kubernetes Windows Pod는 Hyper-V 컨테이너 격리가 아닌 process isolation을 사용합니다. 현재 Calico 문서의 설치 방식은 operator가 관리하는 HostProcess 컨테이너입니다. 이전 3.29 ZIP/manual-service 예시나 오래된 runtime/kubelet 버전을 현재 설치 절차로 사용하지 마세요. | 항목 | 현재 Calico Windows 제약 | | --- | --- | | 네트워크 | CrossSubnet 없는 IPv4 VXLAN 또는 지원되는 비캡슐화 BGP. IPIP 미지원 | | VXLAN | UDP 4789. 문서 범위에서 Windows CrossSubnet·사용자 지정 VXLAN MTU 미지원 | | IPAM | 차용 불가. Calico 소유 블록당 주소 4개 예약으로 `/26`은 Pod 주소 60개. Windows kube-proxy의 단일 블록 제약 고려 | | 라우팅 | 지원되는 BGP 피어링은 가능하지만 Windows는 RR이나 Service IP 광고 역할을 하지 않음 | | 여기서 미지원 | IPv6/dual stack, eBPF, WireGuard, HostEndpoint 정책, Istio ALP | | 관리형 플랫폼 | EKS Windows는 VPC CNI, AKS는 Azure CNI. GKE와 자체 관리 GCE는 구분 | ### Operator 설정 호환 Windows 노드를 먼저 준비하고 제어 컴포넌트/Typha HA에 필요한 Linux 용량을 확보하세요. Windows 가이드는 해당 HA 구성에 Linux worker 3개를 요구합니다. operator namespace의 `kubernetes-services-endpoint`에 안정적인 직접 API 주소를 준비하고 실제 Service CIDR을 사용합니다. 다음은 **자체 관리 Calico CNI VXLAN 대안**의 설정 구조입니다. 기존 설치의 풀 목록을 예시로 덮어쓰거나 EKS/Azure CNI 구성에 그대로 적용하지 마세요. ```yaml # Self-managed Calico-CNI IPv4 VXLAN target configuration; preserve existing pools/settings. apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: serviceCIDRs: - 10.96.0.0/12 cni: type: Calico calicoNetwork: linuxDataplane: Iptables windowsDataplane: HNS bgp: Disabled ipPools: - cidr: 10.244.0.0/16 blockSize: 26 encapsulation: VXLAN natOutgoing: Enabled ``` 올바른 필드는 `spec.calicoNetwork.windowsDataplane`이며 root의 `spec.windowsDataplane`이나 `windowsIPAM`이 아닙니다. 이 VXLAN 구성은 BGP를 비활성화하고 `VXLANCrossSubnet` 대신 `VXLAN`을 사용합니다. 비캡슐화 BGP는 별도 설정 대안이며 Linux IPIP 풀을 Windows peer와 혼용하지 마세요. ```bash kubectl get ipamconfigurations.projectcalico.org default -o yaml # Required for the documented mixed Windows/Calico-IPAM installation: kubectl patch ipamconfigurations.projectcalico.org default --type=merge \ -p '{"spec":{"strictAffinity":true}}' ``` 문서화된 Calico-IPAM Windows 구성에는 strict affinity가 필요합니다. Pod 네트워킹 전에 블록 크기를 계획해야 하며 나중에 기존 blockSize를 변경할 수 없습니다. Windows kube-proxy의 버전·소유권도 확인하세요. Legacy manual 설치를 HostProcess로 옮기면 이전 서비스가 제거되고 파일이 교체될 수 있으므로 기존 설정을 먼저 보존해야 합니다. ```bash kubectl get nodes -l kubernetes.io/os=windows -o wide kubectl get pods -n calico-system -l k8s-app=calico-node-windows -o wide kubectl logs -n calico-system -l k8s-app=calico-node-windows -c felix --tail=100 ``` 위 내용은 설정 검토이며 실제 Windows 노드 구축·장애 전환을 검증한 것이 아닙니다. 양쪽 OS에서 Pod/Service 트래픽과 정책을 검사하세요. Windows NAT 변경은 새로 네트워킹한 Pod에만 적용될 수 있고 일부 HNS 정책 갱신은 연결을 재설정할 수 있습니다. ### HNS와 패킷 경로 ![Windows 노드에서 컨테이너와 Calico Node 서비스의 트래픽이 모두 HNS로 모여 VFP를 거쳐 물리 NIC로 나가는 Calico Windows의 HNS 통합 경로를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-07-advanced-topics-5.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-07-advanced-topics-5.html) > HNS/HCS는 네트워크·endpoint를 관리하고 virtual switch/VFP가 데이터 경로를 적용합니다. 패킷이 Calico 사용자 공간 서비스를 forwarding proxy처럼 통과하지는 않습니다. 그림의 “Windows Service”는 논리적 agent이며 현재 operator 설치는 HostProcess 컨테이너에서 실행합니다. ## Calico 제품 구분 관측성·고급 정책을 모두 Enterprise 전용으로 분류하지 말고 현재 제품별 조건을 확인하세요. | 기능 | Open Source 3.32 | 상용 제품 구분 | | --- | --- | --- | | 네트워킹·정책 | Calico 네트워킹, 전역/namespaced 정책, 지원되는 여러 데이터 평면 | 추가 제품·플랫폼 통합 | | Tier·RBAC | Calico Tier 권한 제어 포함 지원 | 제품 관리 workflow와 추가 제어 | | HTTP 정책 | 설정된 Istio/Dikastes 통합으로 지원 | 해당 제품의 적용 경로 확인 | | Flow/UI | Goldmane/Whisker와 staged-policy workflow 제공 | 추가 분석·보고·관리 기능 | | DNS 도메인 정책 | 검토한 OSS CRD에는 `domains` 없음 | 문서화된 상용 DNS 정책 | | Egress·Federation | 별도 라우팅/proxy 설계 가능. 임의의 OSS gateway/federation CR은 아님 | 지원되는 gateway·원격 identity·federated Service | | 지원 | 커뮤니티·프로젝트 지원 | 계약한 지원 상품에 따름 | Calico Cloud는 관리형 SaaS, Calico Enterprise는 자체 관리 제품입니다. Cloud 문서에는 단일 클러스터의 관측성·정책 관리를 위한 Free Tier도 있습니다. 현재 기능·보존 기간·지원 조건을 확인하고 근거 없는 표에서 보편적인 24/7 SLA, 노드별 가격, 동일한 데이터 평면 기능이나 SaaS 내부 데이터 흐름을 추론하지 마세요. ## 대규모 클러스터 설계 endpoint 수, 정책 복잡도, 변경 빈도, 클라이언트 연결, CPU/RSS, 수렴 목표를 측정하세요. 노드 수 표만으로 운영 용량을 보장할 수 없습니다. ### Operator의 Typha 자동 조정 검토한 Tigera Operator **1.42.6**은 집계한 노드 수에 다음 계산을 사용합니다. ```text N <= 2: 복제본 1개 N <= 4: 복제본 2개 그 외: max(3, floor(N / 200) + 2) 100노드 -> 3 500노드 -> 4 1,000노드 -> 7 2,000노드 -> 12 5,000노드 -> 27 ``` 구현의 자동 목표값이지 “Typha 하나당 200노드”를 검증한 벤치마크가 아닙니다. 명시적으로 unschedulable인 노드와 해당 AKS virtual-node 조건을 집계에서 제외하며, 실제 Linux 배치·용량도 목표값을 수용해야 합니다. Typha는 datastore 업데이트를 Felix에 배포합니다. 쓰기를 집계하거나 클러스터 간 federation controller가 되지는 않습니다. operator의 서비스 계정·RBAC·TLS mount·배치·lifecycle을 유지하세요. ### 지원되는 Override 현재 `typhaDeployment` override에는 `spec.replicas`나 임의의 컨테이너 `env`가 없습니다. 복제본 수를 바꾸기 위해 소유된 Deployment를 불완전한 수동 예시로 덮어쓰지 마세요. 설정 소유자를 통해 허용된 필드를 사용합니다. ```yaml # Override shape only: these illustrative requests are not a capacity recommendation. apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: typhaDeployment: spec: template: spec: containers: - name: calico-typha resources: requests: cpu: 500m memory: 512Mi ``` 실제 requests는 관찰한 사용량과 장애 영역 용량으로 정해야 합니다. 위 값은 필드 구조만 설명합니다. limits·anti-affinity·topology 조건도 함께 확인하세요. 불가능한 배치 조건은 Pod를 Pending 상태로 남길 수 있습니다. ```bash kubectl get installation.operator.tigera.io default -o yaml kubectl -n calico-system get deployment calico-typha -o yaml # Requires a working resource-metrics API: kubectl -n calico-system top pods -l k8s-app=calico-typha ``` ### Route Reflector ![Tier 1의 세 Route Reflector가 서로 iBGP 풀메시로 피어링하고, 각각 자신이 담당하는 랙 RR과 워커 노드 그룹으로 라우트를 반영하는 1000+ 노드용 Route Reflector 계층 토폴로지를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-07-advanced-topics-6.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-07-advanced-topics-6.html) > 그림은 계층의 예시이며 완성된 장애 대응 구성이 아닙니다. 각 랙에는 RR/uplink 하나만 표시되어 있고 숫자도 용량 보장이 아닙니다. Cluster ID와 reflection 관계를 실제 계층에 맞게 설계해야 합니다. 현재 [BGP 전환 절차](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md)를 따르세요. 준비한 RR 노드, 기존 필드를 보존하는 annotation, 명시적 세션·경로·실제 트래픽 검증 후 기존 mesh를 제거합니다. 그림만으로 중복성·next hop·정책이 구성되지는 않습니다. ### Felix 설정별 실제 영향 | 설정 영역 | 의미 | | --- | --- | | Route/iptables refresh | 로컬 데이터 평면 재확인. 일반적인 Kubernetes API polling 주기가 아님 | | `iptablesBackend: NFT` | iptables-nft frontend 선택. Calico native `Nftables` 데이터 평면과 다름 | | Logging/flow logs | 지원되는 로그 경로·비용 확인. 상용 전용 file aggregation 필드를 OSS에 추가하지 않음 | | Health timeout | 장애·readiness 판단 시간. 늘려도 프로그래밍이 빨라지지 않음 | | Mark·route-table range·failsafe | 공유 호스트 네트워크와 제어 연결에 영향. 일반적인 CPU/메모리 최적화 옵션이 아님 | | eBPF/DSR | 플랫폼 조건이 필요한 별도 데이터 평면·경로 변경이며 단순 용량 preset이 아님 | 검토한 FelixConfiguration API에는 `datastoreType`, `typhaAddr`, `typhaK8sServiceName` 필드가 없습니다. 이전 예시의 일부 `...Secs`/`...Millis` 이름도 현재 API 필드가 아니었습니다. 실제 리소스·현재 참고 문서를 확인하고 소유자를 통해 변경하세요. ### Datastore 선택 Kubernetes datastore는 별도 Calico etcd 운영을 피하며 현재 eBPF 데이터 평면에 필요합니다. 직접 etcd는 지원되는 비-Kubernetes 환경이나 별도 설계에 적합할 수 있지만 “5,000노드 이상이면 필수” 또는 “항상 더 빠름”이라는 결론은 근거가 없습니다. `etcd-config`라는 ConfigMap만 만들어도 etcd 프로세스가 설정을 읽는 것은 아닙니다. 관리형 클라우드 제어 평면 datastore도 이 방식으로 조정하지 못합니다. 직접 etcd에는 자체 토폴로지·TLS/인증·백업·복구·용량 계획이 필요합니다. etcd 가이드는 heartbeat/election 값을 네트워크·디스크 지연과 연결합니다. 실제 측정 없이 quota/snapshot/timeout preset을 이식하지 마세요. 제어 평면 설정과 Calico 로컬 데이터 평면 refresh를 구분해야 합니다. ## 확장 변경 전 검증 1. 현재 할당·경로·정책·클라이언트 연결의 기준을 확인합니다. 2. 소유자를 통해 의도한 필드만 변경하고 나머지 설정은 보존합니다. 3. 자원 사용·조정 지연·readiness·실제 허용/거부 경로를 관찰합니다. 4. 계획한 컴포넌트·노드·장애 영역 손실과 원복을 검증합니다. 이전 CPU/메모리/노드 수 범위는 측정 결과가 아닌 검증되지 않은 계획값이었습니다. 이번 검토에서는 대규모 클러스터·Windows·Gateway·datastore를 배포하지 않았습니다. ## 다음 단계 - [Part 8: Amazon EKS 환경에서의 Calico](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md)에서 EKS 통합을 학습합니다 - [Part 9: 운영 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/09-operations.md)에서 운영 방법을 익힙니다 - [용어집](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/glossary.md)에서 용어를 확인합니다 ## 참고 자료 - [Calico IPPool API](https://docs.tigera.io/calico/latest/reference/resources/ippool) - [IPAMConfiguration API](https://docs.tigera.io/calico/latest/reference/resources/ipamconfig) - [BlockAffinity API](https://docs.tigera.io/calico/latest/reference/resources/blockaffinity) - [Released IPAM defaults and allocation logic](https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/libcalico-go/lib/ipam/ipam.go) - [Current AWS Hybrid Nodes CNI support](https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html) - [WireGuard protocol](https://www.wireguard.com/protocol/) - [WireGuard keepalive semantics](https://www.wireguard.com/quickstart/) - [Calico encryption](https://docs.tigera.io/calico/latest/network-policy/encrypt-cluster-pod-traffic) - [Enterprise Egress Gateway on premises](https://docs.tigera.io/calico-enterprise/latest/networking/egress/egress-gateway-on-prem) - [Enterprise Egress Gateway on AWS](https://docs.tigera.io/calico-enterprise/latest/networking/egress/egress-gateway-aws) - [Enterprise federation scope](https://docs.tigera.io/calico-enterprise/latest/multicluster/federation/overview) - [Federated Services Controller](https://docs.tigera.io/calico-enterprise/latest/multicluster/federation/services-controller) - [Calico Windows requirements](https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/requirements) - [Calico Windows operator workflow](https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/operator) - [Calico Windows limitations](https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/limitations) - [Windows networking architecture](https://learn.microsoft.com/en-us/virtualization/windowscontainers/container-networking/architecture) - [Kubernetes 1.36 Windows documentation source](https://raw.githubusercontent.com/kubernetes/website/release-1.36/content/en/docs/concepts/windows/intro.md) - [Current Calico product overview](https://docs.tigera.io/calico-cloud/about) - [Operator 1.42.6 scaling function](https://raw.githubusercontent.com/tigera/operator/v1.42.6/pkg/common/autoscale.go) - [Operator 1.42.6 Typha autoscaler](https://raw.githubusercontent.com/tigera/operator/v1.42.6/pkg/controller/installation/typha_autoscaler.go) - [Operator API](https://docs.tigera.io/calico/latest/reference/installation/api) - [Felix API](https://docs.tigera.io/calico/latest/reference/resources/felixconfig) - [Component metrics](https://docs.tigera.io/calico/latest/operations/monitor/monitor-component-metrics) - [etcd tuning](https://etcd.io/docs/v3.6/tuning/) - [etcd configuration](https://etcd.io/docs/v3.6/op-guide/configuration/) ## 퀴즈 [고급 주제 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/07-advanced-topics-quiz) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/08-eks-integration ---------------------------------------- # Part 8: EKS 통합 > **검토 기준**: Calico 3.32.2 / Tigera Operator 1.42.6 / Calico의 Kubernetes 공식 테스트 범위 1.34–1.36. **마지막 업데이트**: 2026년 9월 12일 ## 개요 이 문서는 **Amazon VPC CNI를 사용하는 일반 Linux EC2 워커 노드**에 Calico 정책을 적용하는 구성을 다룹니다. VPC CNI가 Pod IP 할당과 VPC 네트워킹을 담당하고, Calico는 노드 데이터플레인에 정책을 설정합니다. 아래 설치 예제는 Iptables 데이터플레인과 kube-proxy를 유지합니다. Calico CNI와 eBPF는 추가 전제가 필요한 별도 배포 선택입니다. 검토일 기준 EKS의 표준 지원 버전은 1.34–1.36, 연장 지원 버전은 1.31–1.33입니다. Calico 3.32가 공개한 Kubernetes 테스트 범위는 1.34–1.36입니다. EKS 제공 여부, 업스트림 Kubernetes 릴리스, Calico 호환성은 각각 확인해야 합니다. 업스트림 1.37 출시만으로 이 호환 범위가 늘어나지는 않습니다. 설치 전 대상 리전의 EKS 및 애드온 버전을 확인하세요. ## VPC CNI + Calico 아키텍처 ![VPC CNI가 Pod 인터페이스와 VPC IP 할당을 관리하고 Felix가 노드 정책 데이터플레인을 설정한다. 화살표는 컴포넌트의 제어·설정 관계이며 프로세스를 통과하는 패킷 경로가 아니다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-08-eks-integration-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-08-eks-integration-0.html) 그림은 컴포넌트의 책임을 요약합니다. 패킷이 pause 컨테이너나 Felix 프로세스를 전달 프록시처럼 통과하지는 않습니다. Felix가 규칙을 설정하면 Linux 커널이 이를 평가합니다. `iptables / eBPF` 표기는 데이터플레인 대안을 뜻하며, 이 문서에서는 Iptables를 설치합니다. Typha와 kube-controllers는 고객 워커 자원에서 실행되고 AWS 관리 EKS 컨트롤 플레인 내부에 배포되지 않습니다. | 컴포넌트 | 이 구성에서의 책임 | | --- | --- | | `aws-node` / IPAMD 및 VPC CNI 플러그인 | ENI/IP 할당 관리, Pod 연결 설정 | | `calico-node`의 Felix | 로컬 엔드포인트의 정책 규칙 설정 | | Typha | 데이터스토어 변경을 Felix에 배포, operator가 규모 조절 | | kube-controllers | Calico 데이터와 Kubernetes 리소스 조정 | | kube-proxy | 기본 구성의 Kubernetes Service 전달 처리 | 노드 간 트래픽은 커널에서 적용 대상 정책을 평가한 후 VPC 경로를 이용합니다. 같은 노드의 트래픽은 호스트 내부에 머물 수 있습니다. 정책이 패킷을 드롭하면 버려지는 것이며 발신자에게 패킷이 되돌아가는 것은 아닙니다. 적용 대상 출발지 egress와 목적지 ingress 제어가 모두 연결을 허용해야 합니다. ## 정책 엔진과 설치 방법 선택 | 선택 | 설치 대상 | 수명주기와 적용 범위 | | --- | --- | --- | | Amazon VPC CNI 네트워크 정책 | AWS의 정책 구현 | 호환되는 `vpc-cni` 애드온 설정으로 활성화하며 Calico를 설치하지 않음 | | Tigera Operator 매니페스트 | Operator와 Calico 사용자 정의 리소스 | 릴리스 고정, CRD 관리, Installation 조정 | | Tigera Operator Helm 차트 | 같은 operator와 Helm으로 관리하는 설정 | values를 렌더링·검토하고 기존 release를 업그레이드 | | 직접 Calico 매니페스트 | Operator 없이 Calico 컴포넌트 | 플랫폼별 수정 사항과 업그레이드 절차를 직접 관리 | EKS 애드온은 새 버전 출시나 클러스터 마이너 버전 변경 시 자동 업그레이드되지 않습니다. AWS·Marketplace·커뮤니티 애드온의 지원 주체도 다릅니다. `calico`라는 애드온 이름이 존재한다고 가정하거나 Marketplace 제품을 이 OSS 설치와 동일하게 취급하지 마세요. 실제 카탈로그, 게시자, 버전, 라이선스, 컴퓨팅 호환성을 확인해야 합니다. **같은 엔드포인트에는 하나의 네트워크 정책 엔진을 사용하세요.** Calico EKS 가이드는 AWS VPC CNI 네트워크 정책을 비활성화하도록 요구합니다. 마이그레이션에는 정책, 노드 상태, 가용성을 고려한 전환 계획이 필요합니다. 두 엔진을 함께 실행하며 플래그만 바꾸는 것은 전환 절차가 아닙니다. AWS는 정책 에이전트를 제거해도 규칙이 남을 수 있다고 경고하며 타사 엔진에서 전환할 때 영향받은 노드 교체를 권고합니다. [AWS 정책 고려사항](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html)과 [비활성화 절차](https://docs.aws.amazon.com/eks/latest/userguide/network-policy-disable.html)를 함께 확인하세요. ## 기존 VPC CNI 설치 준비 다음 명령은 기존 관리형 `vpc-cni` 애드온을 조회합니다. 실제 리전과 클러스터 이름을 사용하세요. 자체 관리 VPC CNI는 해당 매니페스트 또는 Helm 소유 경로에서 변경해야 합니다. ```bash EKS_CLUSTER=my-cluster EKS_REGION=ap-northeast-2 EKS_VERSION=$(aws eks describe-cluster --name "$EKS_CLUSTER" \ --region "$EKS_REGION" --query cluster.version --output text) aws eks describe-addon-versions --addon-name vpc-cni \ --kubernetes-version "$EKS_VERSION" --region "$EKS_REGION" \ --query 'addons[0].addonVersions[].{version:addonVersion,compatibility:compatibilities,compute:computeTypes}' aws eks describe-addon --cluster-name "$EKS_CLUSTER" \ --addon-name vpc-cni --region "$EKS_REGION" > vpc-cni-current.json VPC_CNI_VERSION=$(jq -r '.addon.addonVersion' vpc-cni-current.json) aws eks describe-addon-configuration --addon-name vpc-cni \ --addon-version "$VPC_CNI_VERSION" --region "$EKS_REGION" \ --query configurationSchema --output text > vpc-cni-schema.json ``` Calico에는 VPC CNI가 `vpc.amazonaws.com/pod-ips`를 신속하게 게시하도록 `ANNOTATE_POD_IP=true`가 필요합니다. `aws-node` ServiceAccount에는 Pod patch 권한이 있어야 합니다. 현재 VPC CNI 문서는 EKS 애드온이 이 권한을 자동 갱신한다고 설명합니다. 기존 ClusterRole을 덮어쓰지 말고 실제 권한을 확인하세요. ```bash kubectl auth can-i patch pods --all-namespaces \ --as=system:serviceaccount:kube-system:aws-node ``` 이 검사는 해당 ServiceAccount를 impersonate할 권한이 필요합니다. 권한이 없다면 별도 바인딩으로 기존 규칙을 교체하지 않고 필요한 권한만 추가할 수 있습니다. 표준 설치가 아니라면 실제 ServiceAccount 이름으로 변경하세요. ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: calico-vpc-pod-annotations rules: - apiGroups: [""] resources: ["pods"] verbs: ["patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: calico-vpc-pod-annotations roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: calico-vpc-pod-annotations subjects: - kind: ServiceAccount name: aws-node namespace: kube-system ``` 검토된 **새 구성 또는 이미 policy-only인 구성**에서는 기존 애드온 설정을 보존하면서 다음 변경 후보를 준비합니다. ```bash jq '(.addon.configurationValues // "") as $current | (if $current == "" then {} else ($current | fromjson) end) | .enableNetworkPolicy = "false" | .env.ANNOTATE_POD_IP = "true" | del(.env.NETWORK_POLICY_ENFORCING_MODE)' \ vpc-cni-current.json > vpc-cni-calico.json ``` 이 명령은 JSON 형식의 설정을 전제로 합니다. 기존 값이 YAML이라면 필드를 잃지 않도록 파싱·변환한 뒤 후보를 준비하세요. 파싱 오류가 나면 진행하지 마세요. 조회한 EKS 빌드 스키마로 후보 설정을 검증하고 diff를 검토하세요. `NETWORK_POLICY_ENFORCING_MODE`는 AWS 정책 에이전트용 설정입니다. 에이전트가 없는데 이 변수가 남아 있으면 Pod 생성에 실패할 수 있습니다. AWS 정책이 현재 활성 상태라면 후보를 적용하기 전에 전환 계획부터 수행해야 합니다. 승인된 설정을 애드온 소유 경로로 적용합니다. ```bash aws eks update-addon --cluster-name "$EKS_CLUSTER" \ --addon-name vpc-cni --region "$EKS_REGION" \ --configuration-values file://vpc-cni-calico.json \ --resolve-conflicts PRESERVE ``` 반환된 업데이트 상태와 실제 DaemonSet을 확인하세요. 보존된 충돌 때문에 원하는 필드가 적용되지 않을 수 있습니다. JSON 파싱 성공이나 업데이트 요청 접수만으로 정책 적용이 검증되지는 않습니다. ## Operator로 Calico 설치 새 설치에는 아래 매니페스트 또는 Helm 경로 중 **하나**를 선택합니다. 기존 release 위에 두 번째 operator를 설치하지 마세요. 예제는 일반 EC2 Linux 워커, 접근 가능한 Kubernetes API/DNS, 호환 VPC CNI, 경쟁 정책 엔진이 없는 환경을 전제로 합니다. ### Operator 매니페스트 경로 Calico 3.32는 Calico CRD와 operator 매니페스트가 분리되어 있습니다. ```bash kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/v1_crd_projectcalico_org.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/tigera-operator.yaml ``` 다음을 `calico-eks-installation.yaml`로 저장합니다. ```yaml apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: kubernetesProvider: EKS cni: type: AmazonVPC calicoNetwork: bgp: Disabled linuxDataplane: Iptables nodeUpdateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 --- apiVersion: operator.tigera.io/v1 kind: APIServer metadata: name: default spec: {} --- apiVersion: operator.tigera.io/v1 kind: Goldmane metadata: name: default spec: {} --- apiVersion: operator.tigera.io/v1 kind: Whisker metadata: name: default spec: {} ``` ```bash kubectl apply -f calico-eks-installation.yaml kubectl get tigerastatus kubectl get pods -n calico-system ``` API 서버, Goldmane flow aggregator, Whisker UI는 OSS에서도 제공됩니다. 환경에 맞게 접근 제어와 자원 용량을 설정하세요. `cni.type: AmazonVPC`가 IPAM/네트워킹을 VPC CNI에 위임하며 `bgp: Disabled`만으로 CNI가 선택되지는 않습니다. Typha 복제본은 operator가 관리하도록 두세요. `typhaDeployment.spec.replicas`는 지원되는 Installation override가 아닙니다. [확장 세부사항](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md)을 참고하세요. ### Helm 경로 다음을 `calico-eks-values.yaml`로 저장합니다. `installation`은 Installation API에 대응합니다. 최상위 `nodeSelector`는 operator Pod를 제어하며 모든 Calico 컴포넌트를 선택하는 설정이 아닙니다. 지원되지 않는 values가 Helm에서 조용히 무시될 수 있으므로 렌더링 결과를 확인하세요. ```yaml installation: enabled: true kubernetesProvider: EKS cni: type: AmazonVPC calicoNetwork: bgp: Disabled linuxDataplane: Iptables nodeUpdateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 apiServer: enabled: true goldmane: enabled: true whisker: enabled: true manageCRDs: true ``` ```bash helm repo add projectcalico https://docs.tigera.io/calico/charts helm repo update projectcalico helm template calico projectcalico/tigera-operator \ --version v3.32.2 --namespace tigera-operator \ -f calico-eks-values.yaml > calico-rendered.yaml # 준비된 클러스터에 맞게 렌더링 결과를 검토한 후: helm install calico projectcalico/tigera-operator \ --version v3.32.2 --namespace tigera-operator --create-namespace \ -f calico-eks-values.yaml ``` `manageCRDs: true`에서는 operator가 시작 후 필요한 CRD를 관리합니다. 업그레이드와 동시에 새 필드를 사용하려면 [Calico 업그레이드 절차](https://docs.tigera.io/calico/latest/operations/upgrading/kubernetes-upgrade)에 따라 일치하는 CRD를 기존 소유 경로에서 먼저 적용하세요. Helm rollback만으로 CRD나 저장 데이터 마이그레이션이 되돌아간다고 보장할 수 없습니다. ## 대안: AWS 네이티브 네트워크 정책 AWS VPC CNI의 표준 NetworkPolicy 지원은 **VPC CNI 1.14**에서 시작되었으며 EKS 1.14를 뜻하지 않습니다. 현재 AWS 가이드는 **표준·관리자 정책을 함께 사용하려면 VPC CNI 1.21 이상**, 호환 EKS/platform 버전, Linux 커널 5.10 이상을 요구합니다. 과거 출시 시점 예제 대신 현재 호환성 안내를 따르세요. 현재 AWS는 네임스페이스 범위 `networking.k8s.io/v1` NetworkPolicy와 함께 Admin/Baseline tier가 있는 `networking.k8s.aws/v1alpha1` **ClusterNetworkPolicy**를 문서화합니다. Calico GlobalNetworkPolicy 및 사용자 정의 Tier와는 별도 API입니다. 네이티브 정책을 영구적으로 네임스페이스 규칙만 지원하는 기능으로 설명하면 안 됩니다. | 기능 | AWS 네이티브 구현 | 이 문서의 Calico OSS | | --- | --- | --- | | Kubernetes NetworkPolicy | 조건을 충족하는 EC2 Linux 노드에서 지원 | 관리 대상 엔드포인트에서 지원 | | 클러스터 정책 | 자체 규칙과 전제가 있는 AWS ClusterNetworkPolicy | Calico GlobalNetworkPolicy와 Tier | | 정책 관측성 | 에이전트 메트릭/이벤트 로그, CloudWatch 전송은 별도 설정 | Felix/Typha 메트릭, Goldmane·Whisker flow 관측성 | | 애플리케이션 계층 정책 | L3/L4 정책에서 지원을 추론하지 않음 | 별도 Dikastes/Istio 통합이 필요하며 이 설치로 활성화되지 않음 | | Calico의 DNS/FQDN 정책 | Calico API 구현이 아님 | 문서화된 도메인 기반 정책 기능은 상용 Calico 에디션 필요 | 네이티브 대안은 호환 VPC CNI 애드온/Helm 설정으로 `enableNetworkPolicy`를 활성화합니다. `aws-node`에 존재하지 않는 `ENABLE_NETWORK_POLICY` 환경변수를 설정하는 것으로는 활성화되지 않습니다. 네이티브 `standard` 시작 모드는 정책 구성 전 트래픽을 허용하며, `strict`는 deny로 시작하고 DNS를 포함한 필수 경로 정책이 필요합니다. 이 AWS 설정이 Calico의 시작 동작을 설정하지는 않습니다. 네이티브 적용에는 EC2 Linux 한정, Pod 기본 인터페이스 한정, IP family 제한, controller 소유 Pod에서의 안정적 적용 등 문서화된 제약이 있습니다. 포트/프로토콜 개수 및 Service 포트 조건도 확인하세요. AWS가 관리하는 PolicyEndpoint는 컨트롤러가 소유하도록 유지합니다. 전체 설정과 전환 세부사항은 [VPC CNI 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/01-vpc-cni.md)를 참고하세요. ## 노드 유형과 네트워킹 구성 | 구성 | 적용 범위 | | --- | --- | | 일반 관리형 또는 자체 관리 EC2 Linux 노드 + VPC CNI | 위 policy-only 설치 적용, 노드 관리 방식만으로 Calico 기능이 결정되지는 않음 | | EC2 노드 + 전체 Calico CNI | Tigera가 문서화한 별도 설계, Pod 주소·컨트롤 플레인 접근·CNI 소유권·지원 경계 변경 | | EKS Fargate | 해당 Pod에서 Calico 노드 에이전트 및 VPC CNI 네이티브 네트워크 정책 사용 불가, Security Groups for Pods는 별도 지원 제어 | | EKS Auto Mode | AWS 내장 네트워킹/정책 사용, 대체 CNI와 정책 플러그인 미지원 | | EKS Hybrid Nodes | VPC CNI 사용 불가, 전용 CNI 가이드에는 AWS 유지 Cilium 1.17/1.18 빌드가 나열되며 아래 지원 조건 참고 | | Windows 노드 | 별도 Windows/VPC CNI 및 Calico HNS 절차 필요, Calico eBPF 데이터플레인 미지원, [Windows 제약](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md) 참고 | Hybrid 전용 CNI 가이드는 AWS 유지 Cilium 빌드를 나열하고 Calico 예시를 별도 저장소로 안내하지만 [일반 대체 CNI 문서](https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html)는 여전히 Hybrid Nodes의 Cilium/Calico 핵심 기능 지원을 설명합니다. 예시 이동만으로 Calico 지원 종료를 추론하거나 임의의 업스트림 버전을 AWS 지원으로 취급하지 마세요. 배포판·기능별 지원 범위를 확인해야 합니다. EC2 엔드포인트에 적용한 Calico 정책이 Fargate 상대와의 트래픽을 제한할 수는 있습니다. 이것이 Fargate 내부에서 정책을 적용한다는 뜻은 아닙니다. 혼합 컴퓨팅 클러스터에서는 모든 워커를 “Calico 전체 지원”으로 표시하는 대신 스케줄링과 정책 적용 경계를 정의해야 합니다. VPC CNI의 `enableNetworkPolicy`를 false로 바꿔도 전체 Calico CNI가 활성화되지 않습니다. Tigera의 새 클러스터 절차는 워커 추가 전에 경쟁 CNI를 제거합니다. 기존 프로덕션 클러스터에서 `aws-node`를 삭제하는 것을 간단한 전환 방법으로 사용하지 마세요. 문서화된 overlay 구성은 admission webhook 같은 API 서버→Pod 경로도 별도 고려해야 합니다. 신뢰할 수 있는 컴포넌트의 `hostNetwork` 사용은 문서화된 우회 방법 중 하나입니다. Pod CIDR, 반환 경로, MTU, 필요한 경우 노드 IAM과 source/destination check, AWS/Tigera 지원 경계를 검토한 후 선택하세요. Auto Mode의 관리형 네트워킹은 VPC CNI 환경변수나 ENIConfig로 설정되지 않습니다. NodeClass를 사용합니다. Auto Mode는 CoreDNS를 노드 시스템 서비스로 실행하므로 순수 Auto Mode 클러스터에는 기존 CoreDNS Deployment가 필요하지 않지만, non-Auto 노드가 섞인 클러스터에서는 유지해야 합니다. 최초 DNS 질의가 로컬이어도 업스트림 전달은 노드 밖으로 나갈 수 있습니다. ## IAM, IRSA와 Pod Identity 기본 Calico policy-only 설치는 Kubernetes RBAC를 사용하며 `calico-node`에 광범위한 EC2 조회 또는 CloudWatch IAM 역할이 필요하지 않습니다. VPC CNI에는 해당 컴포넌트가 요구하는 AWS 권한이 필요합니다. 별도 로그 exporter나 상용 클라우드 통합이 AWS 권한을 요구하면 **실제로 호출하는 컴포넌트**의 ServiceAccount에 부여하세요. IRSA는 클러스터 OIDC provider와 적절히 제한된 trust policy를 사용하여 ServiceAccount의 AWS 자격증명을 발급합니다. EKS Pod Identity도 컴포넌트·SDK·컴퓨팅 유형이 지원할 때 선택할 수 있습니다. IAM 정책만 만들거나 존재하지 않는 `Installation.spec.nodeMetadata`를 설정한다고 워크로드에 자격증명이 연결되지는 않습니다. 컴포넌트 소유 설정을 따르고 operator가 관리하는 ServiceAccount와 소유권이 충돌하지 않도록 하세요. [VPC CNI IAM 설정](https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html)을 참고하세요. ## Security Group과 Calico 정책 ![Security Group, Calico 정책, 애플리케이션 인증은 서로 다른 접근 제어 계층을 제공한다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-08-eks-integration-3.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-08-eks-integration-3.html) 이 그림은 계층의 개념도이며 고정 평가 순서를 뜻하지 않습니다. Calico는 tier/order와 규칙 의미에 따라 평가하므로 NetworkPolicy가 항상 GlobalNetworkPolicy보다 먼저 실행되지는 않습니다. CloudTrail은 AWS API 활동을 기록하며 패킷 허용·거부 로그가 아닙니다. VPC Flow Logs와 정책 로깅 도구는 서로 다른 트래픽 근거를 제공합니다. 애플리케이션 mTLS/인가도 별도로 배포해야 합니다. Security Group은 ENI에 연결됩니다. **Security Groups for Pods**는 SecurityGroupPolicy로 워크로드를 선택할 수 있으므로 “Security Group은 인스턴스만 선택한다”는 설명은 틀립니다. SG-for-Pods와 Calico 정책을 조합하려면 AWS는 VPC CNI 1.11 이상과 `POD_SECURITY_GROUP_ENFORCING_MODE=standard`를 요구합니다. strict 모드에서는 해당 Pod 트래픽에 Calico 정책이 적용되지 않습니다. branch ENI/인스턴스 지원을 확인하고 모드 변경 후 해당 Pod를 재생성해야 합니다. Windows와 Auto Mode에서는 SG-for-Pods를 지원하지 않습니다. standard 모드에서 VPC CNI의 일반적인 외부 SNAT가 활성화되어 있으면(`AWS_VPC_K8S_CNI_EXTERNALSNAT=false`) VPC 외부 트래픽은 노드 기본 ENI IP와 Security Group을 사용합니다. Pod Security Group egress 규칙이 모든 경로에 적용된다고 가정하지 마세요. [AWS의 정확한 조건](https://docs.aws.amazon.com/eks/latest/userguide/security-groups-for-pods.html)을 확인하세요. ### 네임스페이스 범위 애플리케이션 정책 다음 예제는 준비된 `calico-eks-demo` 네임스페이스의 `app=frontend` Pod만 선택합니다. 같은 네임스페이스의 **클러스터 내부 gateway Pod**가 TCP 8080으로 접근하도록 허용하고 frontend가 같은 네임스페이스의 backend Pod TCP 8080에 연결하도록 허용합니다. DNS는 `k8s-app=kube-dns`인 일반 CoreDNS Pod를 전제로 하므로 NodeLocal DNS나 다른 resolver에는 목적지 조정이 필요합니다. 다른 정책/tier가 결과를 바꿀 수 있으므로 전체 유효 정책 집합을 확인하세요. ```yaml apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: frontend-policy namespace: calico-eks-demo spec: selector: app == 'frontend' types: [Ingress, Egress] ingress: - action: Allow protocol: TCP source: selector: app == 'gateway' destination: ports: [8080] egress: - action: Allow protocol: TCP destination: selector: app == 'backend' ports: [8080] - action: Allow protocol: UDP destination: namespaceSelector: projectcalico.org/name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] - action: Allow protocol: TCP destination: namespaceSelector: projectcalico.org/name == 'kube-system' selector: k8s-app == 'kube-dns' ports: [53] ``` 목적지 selector와 포트는 **동일한 `destination` 매핑**에 두어야 합니다. YAML 키가 중복되면 selector가 조용히 사라져 의도하지 않은 엔드포인트 TCP 8080까지 허용될 수 있습니다. ALB/NLB는 `app=load-balancer` 레이블이 있는 Kubernetes Pod가 아닙니다. [로드 밸런서 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/03-aws-lb-controller.md)에 따라 target mode, 상태 검사, 실제 관측 출발지 주소를 별도로 고려하세요. `all()`을 선택하는 빈 클러스터 전체 GlobalNetworkPolicy는 DNS, API, 모니터링, 애플리케이션 트래픽을 끊을 수 있습니다. 선택한 테스트 네임스페이스에서 의존성 허용을 명시한 default-deny 동작부터 구성하세요. [네트워크 정책](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md)을 참고하세요. ## 업그레이드와 복구 1. EKS 컨트롤 플레인, 노드 OS/kubelet, VPC CNI, kube-proxy, Calico/operator/CRD, calicoctl 버전을 조사하고 소유 설정과 정책을 내보냅니다. 2. **전환 전후 양쪽 버전**과 호환되는 Calico 버전을 선택합니다. “항상 Calico 먼저/나중”이라는 공통 순서는 없습니다. 설치 방식별 절차와 CRD 마이그레이션 안내를 따릅니다. 3. EKS upgrade insights, 제거 API, 모든 애드온 호환성을 확인합니다. EKS 컨트롤 플레인을 한 번에 한 마이너 버전씩 업그레이드한 뒤 노드와 해당 애드온을 호환 버전으로 맞춥니다. 4. 롤링 전환 중 새 Pod와 거부되어야 할 연결을 포함하여 정책·Service 동작을 확인합니다. 설정 소유권과 복구 전제를 명확히 유지합니다. ```bash aws eks describe-cluster --name "$EKS_CLUSTER" --region "$EKS_REGION" \ --query 'cluster.{version:version,platform:platformVersion,status:status}' kubectl get nodes -o wide kubectl get daemonset calico-node -n calico-system -o wide kubectl get tigerastatus helm get values calico -n tigera-operator -o yaml ``` 현재 EKS는 **인플레이스 업그레이드 완료 후 7일 이내, 직전 마이너 버전으로 조건부 롤백**을 지원합니다. 문서화된 자격과 준비 상태 요건을 만족해야 하며 일반적인 임의 다운그레이드 기능은 아닙니다. 일반 관리형/자체 관리/Hybrid 노드와 비호환 애드온은 컨트롤 플레인보다 먼저 준비해야 합니다. Auto Mode는 자체 노드 롤백을 처리하고 Fargate는 별도 워크로드 처리가 필요합니다. Calico와 EKS 애드온은 자동 복구되지 않으며 etcd 데이터를 보존한다고 비호환 리소스가 안전해지지는 않습니다. 해결하지 않은 호환성 문제를 `--force`로 숨기지 말고 [현재 롤백 절차](https://docs.aws.amazon.com/eks/latest/userguide/rollback-cluster.html)를 따르세요. 자격이 없는 경우 다른 지원 클러스터로의 이전을 계획합니다. 이전 operator 매니페스트 적용이나 `helm rollback`만으로 안전한 Calico 다운그레이드가 입증되지는 않습니다. 복구 방법을 선택하기 전에 해당 릴리스 지원과 스키마/데이터 변경을 확인하세요. `calicoctl node status`는 노드 로컬 BGP 상태를 보여주며 policy-only EKS의 인수 검사가 아닙니다. ## 비용과 성능 | 요소 | 평가할 사항 | | --- | --- | | 워커 자원 | 실제 정책/엔드포인트 변경량으로 Felix·Typha·컨트롤러·flow 집계 자원 측정, CPU request는 별도 AWS 요금 항목이 아님 | | VPC IP 용량 | Prefix delegation은 주소 할당과 밀도를 바꾸며 ENI 연결 요금이 자동 할인되는 기능이 아님 | | 로그/메트릭 | 보관·수집·조회·exporter 전달 비용을 구분하며 메트릭을 flow log로 취급하지 않음 | | Cross-AZ 트래픽 | 실제 출발지/목적지 경로와 서비스 요금 확인, locality와 가용성 함께 평가 | | EKS 수명주기 | 연장 지원에 추가 클러스터 요금이 발생할 수 있으므로 현재 지원 일정 확인 | 기존 컴포넌트별 달러 추정에는 리전, 인스턴스 요금, 비용 배분 전제가 없었습니다. 사용할 수 있는 비용 모델이 아니므로 임의 자원 제한으로 고정 월 절감액을 주장하지 말고 관측한 자원 수요와 해당 AWS 요금을 사용하세요. ### Prefix Delegation 일반 VPC CNI 노드에서는 애드온의 **`env`** 또는 해당 DaemonSet/Helm 소유 설정에 `ENABLE_PREFIX_DELEGATION`, `WARM_PREFIX_TARGET`, `MINIMUM_IP_TARGET`, `WARM_IP_TARGET`을 구성합니다. ConfigMap에 소문자 `enable-prefix-delegation`을 넣는 것으로 IPAMD가 설정되지는 않습니다. 하나의 문서화된 할당 전략으로 시작하세요. `WARM_IP_TARGET`과 `MINIMUM_IP_TARGET`은 `WARM_PREFIX_TARGET`보다 우선하며 모두 설정한다고 효과가 누적되지 않습니다. IPv4 prefix에는 연속된 `/28` 서브넷 공간과 적합한 인스턴스가 필요합니다. 서브넷 단편화, 예약, max-Pods/kubelet 설정, 전환 계획을 확인하세요. [AWS prefix 절차](https://docs.aws.amazon.com/eks/latest/userguide/cni-increase-ip-addresses-procedure.html)를 참고하세요. ### EKS의 Calico eBPF Calico는 eBPF 데이터플레인에서 EKS와 호환 VPC CNI 네트워킹을 문서화하지만 Service 처리가 변경되므로 별도 전환이 필요합니다. [Part 6](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/06-ebpf-dataplane.md)의 커널/플랫폼 확인, API 서버 FQDN 직접 접근과 bootstrap DNS, kube-proxy 소유권, 상태 검사 포트, 복구 상태를 검토하세요. VPC CNI가 kube-proxy의 상시 실행을 보편적으로 요구하는 것은 **아닙니다**. kube-proxy를 함께 유지해야 한다면 문서화된 충돌을 피하도록 `bpfKubeProxyIptablesCleanupEnabled: false`와 `bpfKubeProxyHealthzPort: 0`이 모두 필요합니다. 일반적인 DaemonSet selector patch는 소유 컨트롤러가 되돌릴 수 있고 기존의 다른 selector를 덮어써서는 안 됩니다. DSR은 기본 EKS 최적화가 아닙니다. AWS 서브넷/출발지 주소 검사와 외부 로드 밸런서 제약을 별도로 검증해야 합니다. 이 문서의 기본 구성은 Iptables와 kube-proxy를 유지합니다. 일반 sysctl 프리셋, 임의의 최소 메모리, conntrack 수명 단축만으로 성능 향상이 입증되지는 않습니다. 실제 워크로드를 측정하면서 반환 경로, 기존 연결, 장애 복구를 보존하세요. ## eksctl 클러스터 계획 예제 다음은 **일반 관리형 Linux 노드의 계획 예제**이며 프로덕션 검증 레시피나 Auto Mode 전환 절차가 아닙니다. `describe-addon-versions`로 지원 빌드를 확인하고 프로비저닝 전 승인한 애드온 빌드를 설정에 고정하세요. 애드온 버전을 생략하면 호환 기본값을 선택하며 향후 모든 릴리스를 승인한다는 뜻은 아닙니다. Private API에는 VPC로 들어가는 관리 경로가 필요합니다. NAT, 로그, 워커 용량, 주소 범위는 환경별 설계가 필요합니다. ```yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: calico-eks-demo region: ap-northeast-2 version: "1.36" iam: withOIDC: true vpc: cidr: 10.0.0.0/16 clusterEndpoints: publicAccess: false privateAccess: true managedNodeGroups: - name: linux-workers instanceType: m5.large amiFamily: AmazonLinux2023 desiredCapacity: 3 minSize: 3 maxSize: 6 privateNetworking: true volumeType: gp3 volumeSize: 100 addons: - name: vpc-cni attachPolicyARNs: - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy configurationValues: | enableNetworkPolicy: "false" env: ANNOTATE_POD_IP: "true" - name: coredns - name: kube-proxy cloudWatch: clusterLogging: enableTypes: [api, audit, authenticator, controllerManager, scheduler] ``` IPv4 CNI 정책은 Calico가 아닌 VPC CNI identity에 속합니다. 다른 IP family와 identity 방식에서는 IAM 구성을 다시 검토하세요. 승인된 클러스터를 준비한 후 위의 annotation/RBAC 확인과 **하나의** Calico 설치 경로를 수행합니다. ## 결과 검증 ```bash kubectl get tigerastatus kubectl rollout status daemonset/calico-node -n calico-system --timeout=300s kubectl get pods -n calico-system -o wide kubectl get pods -n calico-eks-demo -o json \ | jq '.items[] | {name: .metadata.name, ip: .status.podIP, annotatedIPs: .metadata.annotations["vpc.amazonaws.com/pod-ips"]}' kubectl get networkpolicies.projectcalico.org -n calico-eks-demo ``` Controller가 관리하는 테스트 워크로드로 같은 노드, 노드/AZ 간, Pod 재생성 후, 업데이트 중 허용·거부 연결을 모두 확인하세요. DNS, 애플리케이션에 필요한 API/identity 엔드포인트, Service 트래픽, 로드 밸런서 상태 검사를 포함합니다. Pod Running이나 노드 Ready만으로 원하는 차단 또는 시작 시 정책 공백 부재가 검증되지는 않습니다. IPv6는 따로 검증해야 합니다. 현재 Calico EKS 가이드는 `ENABLE_V4_EGRESS=true`인 IPv6 Pod에 대한 정책 적용을 지원하지 않는다고 명시합니다. 이 문서의 예제는 릴리스 스키마와 렌더링한 차트를 검증한 것이며, 검증을 위해 EKS 클러스터·IAM 리소스·프로덕션 트래픽을 생성하지 않았습니다. ## 참고 자료 - [Calico on EKS](https://docs.tigera.io/calico/latest/getting-started/kubernetes/managed-public-cloud/eks) - [Calico 요구사항](https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements) - [Calico Helm 설치](https://docs.tigera.io/calico/latest/getting-started/kubernetes/helm) - [Calico 업그레이드](https://docs.tigera.io/calico/latest/operations/upgrading/kubernetes-upgrade) - [VPC CNI 1.23 설정 참조](https://github.com/aws/amazon-vpc-cni-k8s/blob/v1.23.0/README.md) - [EKS 네이티브 네트워크 정책](https://docs.aws.amazon.com/eks/latest/userguide/cni-network-policy.html) - [EKS 애드온 업데이트](https://docs.aws.amazon.com/eks/latest/userguide/updating-an-add-on.html) - [EKS 버전 수명주기](https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html) - [EKS Auto Mode 네트워킹](https://docs.aws.amazon.com/eks/latest/userguide/auto-networking.html) - [EKS Hybrid Nodes CNI](https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html) ## 다음 단계와 퀴즈 [운영 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/09-operations.md)로 진행하거나 [고급 주제](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md)·[용어집](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/glossary.md)을 복습하고 [EKS 통합 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/08-eks-integration-quiz)를 풀어보세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/09-operations ---------------------------------------- # Part 9: Calico 운영 가이드 > **검토 기준**: Calico 3.32.2 / Operator 1.42.6 / Calico의 Kubernetes 테스트 범위 1.34–1.36. > **마지막 업데이트**: 2026년 9월 12일 ## 개요 이 문서에서는 Calico의 설치, 모니터링, 문제 해결, 업그레이드 및 백업/복구에 대한 운영 가이드를 제공합니다. 프로덕션 환경에서 Calico를 안정적으로 운영하기 위한 모범 사례를 다룹니다. ## 설치 가이드 플랫폼에 맞는 구성과 하나의 설치 소유 경로를 사용하세요. 다음 예제는 **전체 Calico CNI를 사용하는 새 자체 관리 Linux 클러스터**의 Iptables/VXLAN 구성입니다. 중복되지 않는 Pod CIDR, 호환 노드 OS/커널, Kubernetes API 접근, 대상 노드 사이 underlay UDP 4789 연결을 준비해야 합니다. VPC CNI policy-only 구성이 아니며 EKS는 [Part 8](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md)을 사용하세요. 다른 overlay/BGP 설계는 [네트워킹 모드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)를 먼저 검토하세요. ### Tigera Operator 매니페스트 Calico 3.32에서는 operator 매니페스트와 별도로 Calico CRD가 필요합니다. ```bash kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/v1_crd_projectcalico_org.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/tigera-operator.yaml kubectl wait --for=condition=Available deployment/tigera-operator \ -n tigera-operator --timeout=300s ``` 다음을 `installation.yaml`로 저장하고 예제 Pod CIDR을 준비한 클러스터에 맞게 변경하세요. Operator의 자동 감지를 사용하려면 MTU를 생략합니다. 실제 경로 측정을 임의 MTU 값으로 대체하지 마세요. ```yaml apiVersion: operator.tigera.io/v1 kind: Installation metadata: name: default spec: variant: Calico cni: type: Calico calicoNetwork: bgp: Disabled linuxDataplane: Iptables ipPools: - cidr: 10.244.0.0/16 blockSize: 26 encapsulation: VXLAN natOutgoing: Enabled nodeSelector: all() nodeAddressAutodetectionV4: kubernetes: NodeInternalIP nodeUpdateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 --- apiVersion: operator.tigera.io/v1 kind: APIServer metadata: name: default spec: {} --- apiVersion: operator.tigera.io/v1 kind: Goldmane metadata: name: default spec: {} --- apiVersion: operator.tigera.io/v1 kind: Whisker metadata: name: default spec: {} ``` ```bash kubectl apply -f installation.yaml ``` 이 VXLAN 예제는 BGP를 비활성화합니다. 선택한 구성에서 BGP를 활성화한 경우에만 BGP 진단이 의미가 있습니다. Calico API 서버와 Goldmane/Whisker는 OSS 컴포넌트입니다. 현재 OSS flow log 가이드는 관측성 기능을 tech preview로 표시하므로 운영 의존성을 결정하기 전에 이 상태를 평가하세요. 측정으로 지원되는 override가 필요하다고 판단하기 전까지 컴포넌트 자원과 Typha 규모는 operator가 관리하도록 두세요. 임의 메모리 제한, 지원되지 않는 `typhaDeployment.spec.replicas`/`minReadySeconds`, 레거시 `componentResources` 목록의 `KubeControllers` 항목은 프로덕션 구성이 아닙니다. 버전별 Installation API와 [아키텍처](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/02-architecture.md), [확장](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md)을 참고하세요. ### 대안: Helm Helm 차트는 같은 operator를 설치합니다. 다음을 `calico-values.yaml`로 저장하고 매니페스트로 두 번째 operator를 설치하는 대신 이 경로를 사용하세요. ```yaml installation: enabled: true variant: Calico cni: type: Calico calicoNetwork: bgp: Disabled linuxDataplane: Iptables ipPools: - cidr: 10.244.0.0/16 blockSize: 26 encapsulation: VXLAN natOutgoing: Enabled nodeSelector: all() nodeAddressAutodetectionV4: kubernetes: NodeInternalIP nodeUpdateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 apiServer: enabled: true goldmane: enabled: true whisker: enabled: true manageCRDs: true ``` ```bash helm repo add projectcalico https://docs.tigera.io/calico/charts helm repo update projectcalico helm template calico projectcalico/tigera-operator \ --namespace tigera-operator --version v3.32.2 \ -f calico-values.yaml > calico-rendered.yaml # 준비한 클러스터와 렌더링 결과를 검토한 후에만 적용합니다. helm install calico projectcalico/tigera-operator \ --namespace tigera-operator --create-namespace --version v3.32.2 \ -f calico-values.yaml ``` 최상위 `podAnnotations`는 operator Pod에 적용됩니다. Felix 메트릭을 활성화하거나 모든 컴포넌트의 Prometheus 수집을 설정하지 않습니다. 아래 모니터링 절에서 명시적으로 설정하세요. `manageCRDs: true`에서는 operator가 시작 후 CRD를 관리합니다. 새 필드에 필요한 업그레이드 순서는 뒤에서 설명합니다. ### 대안: 직접 매니페스트 직접 매니페스트로 관리하는 기존 설치에는 일치하는 릴리스/구성을 사용하고 사용자 변경을 보존하세요. 다운로드한 파일을 적용 전에 검토합니다. ```bash curl -fL https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/calico.yaml \ -o calico.yaml ``` Pod CIDR과 활성 네트워킹 모드 등 실제 설정을 수정하세요. 전역 `sed` 치환은 예제나 주석만 바꾸고 실제 IP pool을 설정하지 않을 수 있습니다. `kube-system`의 직접 매니페스트 리소스와 `calico-system`의 operator 설치를 혼합하지 마세요. ### 설치 검증 ```bash kubectl get tigerastatus kubectl get installation default -o yaml kubectl rollout status daemonset/calico-node -n calico-system --timeout=300s kubectl get pods -n calico-system -o wide kubectl get nodes -o wide ``` 직접 매니페스트 설치라면 실제 네임스페이스와 리소스 이름을 확인하세요. 컴포넌트 Ready만으로 정책 적용이나 애플리케이션 접근이 검증되지는 않습니다. Controller가 관리하는 워크로드로 필요한 Service/DNS 경로와 허용·거부 애플리케이션 연결을 모두 테스트하세요. API 접근을 확인할 때는 실제 API 서버 HTTPS 엔드포인트와 적절한 인증을 사용합니다. `kubernetes.default`에 대한 HTTP 요청은 인증된 API 상태 검사가 아닙니다. ## calicoctl 명령어 레퍼런스 공식 릴리스에서 일치하는 **3.32.2** 바이너리를 설치하고 [설치 장](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/01-introduction.md)의 방식으로 체크섬을 확인하세요. Linux AMD64/ARM64, macOS AMD64/ARM64, Windows AMD64 자산이 있으므로 실제 호스트 아키텍처를 선택합니다. PowerShell 다운로드 명령을 Bash에서 실행하거나 호환성 경고를 확인하지 않고 업그레이드 후 이전 클라이언트를 사용하지 마세요. Kubernetes 데이터스토어에 접근하는 일반적인 Unix shell 설정은 다음과 같습니다. ```bash export DATASTORE_TYPE=kubernetes export KUBECONFIG="$HOME/.kube/config" calicoctl version calicoctl get nodes -o wide calicoctl get networkpolicy -A calicoctl get globalnetworkpolicy calicoctl get tier calicoctl get networkset -A calicoctl get globalnetworkset calicoctl get workloadendpoint -A calicoctl get hostendpoint calicoctl get ippool -o yaml calicoctl get bgpconfiguration default -o yaml calicoctl get bgppeer -o wide calicoctl get felixconfiguration default -o yaml ``` Kubeconfig는 의도한 클러스터와 필요한 RBAC 권한을 선택해야 합니다. Calico API 설정 파일은 `--config`로 명시할 수 있습니다. `~/.config` 아래 임의 경로가 자동 탐색된다고 가정하지 마세요. 직접 etcdv3 데이터스토어에 접근할 때는 해당 배포의 지원 설정과 인증서 검증을 사용합니다. 별도 배포 방식이며 EKS 관리 etcd에 접근할 수 있다는 뜻은 아닙니다. ### 로컬 노드 진단 `calicoctl node status`는 **로컬 노드의 BGP 상태**를 보여줍니다. Kubeconfig가 있다고 원격 클러스터 전체 readiness 검사로 바뀌지는 않습니다. 문서화된 접근 조건으로 대상 노드에서 실행하거나 아래와 같이 해당 노드의 BIRD 소켓을 조회하세요. `calicoctl node diags`는 선택한 노드에서 진단 아카이브를 수집합니다. 지원되는 `--log-dir`은 **입력 로그 디렉터리**이며 3.32.2에는 `--output-dir`이 없습니다. 구현은 root 권한을 요구하며 privileged 진단 컨테이너 실행과 Felix 상태 덤프 signal을 수행할 수 있습니다. 수동적 상태 프로브가 아닌 의도적인 근거 수집으로 취급하세요. 아카이브를 보호하고 명령이 출력하는 실제 경로를 사용합니다. ### Calico IPAM 주소를 **Calico IPAM**이 할당하는 경우에만 다음을 사용합니다. VPC CNI, host-local 등 다른 IPAM은 실제 할당자를 조사해야 합니다. ```bash calicoctl ipam show calicoctl ipam show --show-blocks calicoctl ipam show --show-borrowed calicoctl ipam show --show-configuration calicoctl ipam show --ip=10.244.0.15 calicoctl ipam check --show-problem-ips -o ipam-report.json ``` `--show-blocks`는 블록 사용률을 보여줍니다. 블록과 노드의 연결은 BlockAffinity로 확인하며 Kubernetes Node PodCIDR과 동일하지 않습니다. `--ip`는 읽기 전용 할당 조회입니다. 보고서는 조사 후보를 제시하지만 Pod가 없다는 사실만으로 주소를 안전하게 해제할 수 있다고 판단해서는 안 됩니다. 검토한 CLI에는 `ipam release --block` 또는 `--handle`이 없습니다. 보고서 기반 해제에는 할당 sequence 검사가 있지만 [고급 IPAM](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md)의 정리 절차도 필요합니다. `ipam split NUMBER --cidr=...`는 실제 명령이지만 할당 블록이 아닌 **IP pool**을 분할하며 datastore lock과 2의 거듭제곱 분할 개수가 필요합니다. 멈춘 Pod의 일반적인 해결책이 아닌 계획된 마이그레이션 작업입니다. ### 리소스 변경과 내보내기 `get`, `create`, `apply`, `replace`, `patch`, `delete`는 지원 리소스 타입에 적용됩니다. 타입/네임스페이스를 명확히 하고 전체 정책을 검토하며 patch에서 다른 필드를 보존하세요. 예를 들어 로깅 변경은 기존 FelixConfiguration 또는 GitOps 소유 설정에 반영하며 새 필드만 있는 오브젝트로 교체하지 않습니다. `calicoctl get all`은 모든 리소스의 백업이 아닙니다. 필요한 타입을 나열하고 Kubernetes 정책과 operator 리소스도 별도로 포함해야 합니다. `calicoctl get ... --export`는 존재하지만 검토한 CLI는 리소스 이름이 없으면 이 플래그를 무시합니다. 목록 내보내기를 완전하고 이식 가능한 재해 복구 백업으로 바꾸지는 않습니다. 아래 백업 절을 참고하세요. ## Prometheus 메트릭 **설치한 버전의 `/metrics` 출력**에서 이름, 타입, 레이블을 확인하세요. 데이터플레인별 series가 모든 구성에 존재한다고 보장할 수 없으며 누락은 0이 아닙니다. 아래 이름은 Calico 3.32.2 소스와 공식 메트릭 참조를 대조했습니다. ### 컴포넌트 메트릭 활성화 Felix 메트릭은 기본 비활성화이며 기본 포트는 **9091**입니다. Typha도 기본 비활성화이며 바이너리의 기본 메트릭 포트는 **9091**입니다. 이 operator 예제는 **9093**을 명시적으로 선택합니다. kube-controllers 메트릭은 기본적으로 **9094**에서 활성화됩니다. 기존 operator 설치에서는 설정 소유 경로에서 다음을 merge합니다. ```bash kubectl patch felixconfiguration default --type=merge \ -p '{"spec":{"prometheusMetricsEnabled":true,"prometheusMetricsPort":9091}}' kubectl patch installation default --type=merge \ -p '{"spec":{"typhaMetricsPort":9093}}' kubectl get service calico-typha-metrics -n calico-system kubectl get service calico-kube-controllers-metrics -n calico-system ``` `typhaMetricsPort`를 설정하면 operator가 Typha 메트릭 Service를 생성합니다. 충돌하는 Service로 교체하지 마세요. 직접 매니페스트 설치에는 자체 Typha 환경변수와 Service 설정이 필요합니다. 메트릭 접근을 제한해야 하며 host-network 엔드포인트에는 워크로드 NetworkPolicy 외의 호스트/네트워크 제어가 필요할 수 있습니다. ### 메트릭 이름과 의미 | 메트릭 | 타입 / 의미 | | --- | --- | | `felix_active_local_endpoints` | Gauge, 로컬 워크로드 **및 호스트** 엔드포인트 수, 0도 정상일 수 있어 readiness 검사가 아님 | | `felix_active_local_policies` | Gauge, 해당 노드에서 활성인 정책 수, 합산하면 클러스터 고유 정책 수가 아닌 노드별 정책 인스턴스 수 | | `felix_cluster_num_hosts`, `felix_cluster_num_policies` | 각 Felix가 관측한 클러스터 범위 Gauge, 노드별 같은 값을 합산하지 않음 | | `felix_int_dataplane_failures` | Counter, 재시도할 데이터플레인 업데이트 실패, 검토한 이름에는 `_total` 접미사 없음 | | `felix_int_dataplane_apply_time_seconds` | 증분 업데이트 시간의 **Summary**, histogram bucket 대신 quantile·`_sum`·`_count` 제공 | | `felix_iptables_restore_calls`, `felix_iptables_restore_errors` | iptables 데이터플레인의 iptables-restore 호출/오류 Counter | | `felix_log_errors`, `felix_logs_dropped` | 프로세스 로그 출력 오류/출력 막힘으로 버린 로그, ERROR 레벨 항목이나 거부 패킷 개수가 아님 | | `typha_connections_active` | Gauge, handshake 중인 연결을 포함한 열린 연결 | | `typha_connections_streaming{syncer="..."}` | Gauge, handshake를 마치고 streaming 중인 클라이언트 | | `typha_connections_accepted` | Counter, 수락한 연결 수 | | `typha_connections_dropped` | **재분배를 위해** 끊은 연결 Counter, 일반적인 네트워크 실패 개수가 아님 | | `typha_cache_size{syncer="..."}` | Gauge, 캐시의 key/value 항목 수 | | `typha_updates_total{syncer="..."}` | 데이터스토어 syncer로부터 **받은** 업데이트 Counter | | `ipam_allocations_in_use{ippool="...",node="..."}` | kube-controllers Gauge, 워크로드/인터페이스에 할당된 Calico IPAM 주소 | | `ipam_ippool_size{ippool="..."}` | kube-controllers Gauge, pool CIDR 전체 주소 수 | | `ipam_allocations_gc_candidates` | 조사 중인 잠재적 누수, 주소 해제 허가가 아님 | BIRD 제어 소켓은 Prometheus exporter가 아닙니다. `bird_protocol_up`, `calico_bgp_peer_status` 같은 이름은 레이블과 의미를 검증한 별도 exporter/collector가 필요하며 이 설치가 해당 series를 제공하지 않습니다. 아래 BGP 진단 또는 [CalicoNodeStatus 방식](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md)을 사용하고 실제 출력 확인 후에만 exporter 알람을 추가하세요. ### ServiceMonitor 연결 Prometheus Operator CRD와 `monitoring` 네임스페이스가 이미 있다고 가정합니다. ServiceMonitor 레이블을 Prometheus의 `serviceMonitorSelector`에 맞추고 `serviceMonitorNamespaceSelector`가 해당 네임스페이스를 포함하는지 확인하세요. PrometheusRule 레이블도 `ruleSelector`와 일치해야 합니다. 스키마가 유효해도 선택되지 않은 리소스에서는 수집/규칙이 생성되지 않습니다. 아래 **별도 Service**는 operator 소유 Service를 변경하지 않고 모두 `http-metrics`라는 포트 이름을 제공합니다. 이미 해당 엔드포인트를 수집한다면 중복 scrape를 추가하지 말고 기존 설정을 사용하세요. ServiceMonitor의 `jobLabel`이 아래 쿼리에서 사용하는 `calico-felix`, `calico-typha`, `calico-kube-controllers` job을 만듭니다. ```yaml apiVersion: v1 kind: Service metadata: name: calico-audit-felix-metrics namespace: calico-system labels: audit.calico/component: calico-felix spec: clusterIP: None selector: k8s-app: calico-node ports: - name: http-metrics port: 9091 targetPort: 9091 protocol: TCP --- apiVersion: v1 kind: Service metadata: name: calico-audit-typha-metrics namespace: calico-system labels: audit.calico/component: calico-typha spec: clusterIP: None selector: k8s-app: calico-typha ports: - name: http-metrics port: 9093 targetPort: 9093 protocol: TCP --- apiVersion: v1 kind: Service metadata: name: calico-audit-kube-controllers-metrics namespace: calico-system labels: audit.calico/component: calico-kube-controllers spec: clusterIP: None selector: k8s-app: calico-kube-controllers ports: - name: http-metrics port: 9094 targetPort: 9094 protocol: TCP --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: calico-components namespace: monitoring labels: app.kubernetes.io/part-of: calico-monitoring spec: jobLabel: audit.calico/component selector: matchExpressions: - key: audit.calico/component operator: Exists namespaceSelector: matchNames: - calico-system endpoints: - port: http-metrics interval: 30s scrapeTimeout: 10s path: /metrics ``` 엔드포인트 탐색/RBAC, 네트워크 접근, Prometheus Targets 화면을 확인하세요. ServiceMonitor의 `endpoints.port`는 컨테이너 포트 번호가 아닌 **Service 포트 이름**을 선택합니다. 부하 분산되는 Service 주소 하나를 수집하며 모든 노드를 수집한다고 가정하지 말고 개별 탐색 target을 확인하세요. ## Grafana 대시보드 설정한 Prometheus datasource와 현재 time-series/stat 패널을 사용합니다. 다음은 완성된 import용 대시보드가 아닌 패널 쿼리입니다. 하나의 클러스터 메트릭을 선택해야 하며 여러 클러스터를 모은 datasource는 selector와 집계에 cluster 레이블을 유지해야 합니다. | 패널 | PromQL | | --- | --- | | 노드별 엔드포인트 | `felix_active_local_endpoints{job="calico-felix"}` | | 노드별 활성 정책 | `felix_active_local_policies{job="calico-felix"}` | | 관측한 클러스터 정책 수 | `max(felix_cluster_num_policies{job="calico-felix"})` | | 초당 데이터플레인 재시도 | `rate(felix_int_dataplane_failures{job="calico-felix"}[5m])` | | Typha streaming 연결 | `typha_connections_streaming{job="calico-typha"}` | | 로컬 summary p99 | `felix_int_dataplane_apply_time_seconds{job="calico-felix",quantile="0.99"}` | 프로세스별 Summary quantile은 클러스터 전체 p99가 아니며 `histogram_quantile`로 합칠 수 없습니다. 업데이트가 발생하는 구간의 평균 증분 적용 시간은 다음과 같습니다. ```promql rate(felix_int_dataplane_apply_time_seconds_sum{job="calico-felix"}[5m]) / rate(felix_int_dataplane_apply_time_seconds_count{job="calico-felix"}[5m]) ``` 관측값이 없으면 평균은 정의되지 않으며(`0/0`) 지연 0의 근거가 아닙니다. 이 Summary에 존재하지 않는 `_bucket` series를 만들거나 검증되지 않은 `felix_iptables_restore_time_seconds` 쿼리를 유지하지 마세요. 실제 제공하는 작업 Counter와 데이터플레인 시간 메트릭을 사용합니다. Calico IPAM 주소 사용률은 다음과 같이 볼 수 있습니다. ```promql sum by (ippool) ( max by (ippool, node) (ipam_allocations_in_use{job="calico-kube-controllers",ippool!="no_ippool"}) ) / max by (ippool) (ipam_ippool_size{job="calico-kube-controllers",ippool!="no_ippool"}) ``` pool/node별 `max`로 컨트롤러의 같은 관측값 중복을 피한 뒤 노드별 할당량을 합산합니다. **주소 사용률**이며 블록 소비율이나 특정 노드의 실제 할당 가능 용량을 보장하지 않습니다. Pool selector, strict affinity, 블록 상한, 예약/tunnel 주소 등도 확인해야 합니다. VPC CNI 할당량을 설명하는 메트릭이 아니며 비어 있거나 누락된 series, 용량 0은 별도 조사 대상입니다. ## Alert 규칙 예제는 위 job, 하나의 선택한 클러스터, DaemonSet 메트릭을 위한 kube-state-metrics를 전제로 합니다. 실제 실행해야 하는 컴포넌트에만 target 누락 규칙을 활성화하세요. 기존 모니터링을 재사용하면 selector를 조정하고 임계값·지속시간은 워크로드에 맞게 설정합니다. ```yaml apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: calico-alerts namespace: monitoring labels: app.kubernetes.io/part-of: calico-monitoring spec: groups: - name: calico.rules rules: - alert: CalicoDaemonSetUnavailable expr: kube_daemonset_status_number_unavailable{namespace="calico-system",daemonset="calico-node"} > 0 for: 5m labels: severity: critical annotations: summary: Calico DaemonSet has unavailable Pods description: Inspect the affected node, rollout and kube-state-metrics data. - alert: CalicoMetricsScrapeFailed expr: up{job=~"calico-(felix|typha|kube-controllers)"} == 0 for: 5m labels: severity: warning annotations: summary: Calico scrape failed for {{ $labels.job }} on {{ $labels.instance }} - alert: CalicoMetricsTargetsMissing expr: |- absent(up{job="calico-felix"}) or absent(up{job="calico-typha"}) or absent(up{job="calico-kube-controllers"}) for: 10m labels: severity: warning annotations: summary: No discovered metrics targets for {{ $labels.job }} - alert: CalicoDataplaneRetries expr: rate(felix_int_dataplane_failures{job="calico-felix"}[5m]) > 0 for: 5m labels: severity: warning annotations: summary: Dataplane updates are being retried on {{ $labels.instance }} - alert: CalicoDataplaneMeanSlow expr: |- (rate(felix_int_dataplane_apply_time_seconds_sum{job="calico-felix"}[5m]) / rate(felix_int_dataplane_apply_time_seconds_count{job="calico-felix"}[5m])) > 0.5 and (rate(felix_int_dataplane_apply_time_seconds_count{job="calico-felix"}[5m]) > 0) for: 10m labels: severity: warning annotations: summary: Mean dataplane update time exceeds 0.5s on {{ $labels.instance }} - alert: CalicoIPAMHighAddressUsage expr: |- (sum by (ippool) ( max by (ippool, node) (ipam_allocations_in_use{job="calico-kube-controllers",ippool!="no_ippool"}) ) / max by (ippool) (ipam_ippool_size{job="calico-kube-controllers",ippool!="no_ippool"})) > 0.8 and on (ippool) (max by (ippool) (ipam_ippool_size{job="calico-kube-controllers",ippool!="no_ippool"}) > 0) for: 10m labels: severity: warning annotations: summary: High address utilization in Calico IP pool {{ $labels.ippool }} description: Address utilization is {{ $value | humanizePercentage }}; inspect per-node eligibility and block constraints. ``` `up == 0`은 탐색된 target의 scrape 실패를 감지하지만 target 자체가 사라지면 감지하지 못합니다. `absent` 규칙은 예상 컴포넌트의 **모든** target이 사라진 경우를 감지합니다. 정상 노드 사이에서 한 노드만 누락된 상황은 기대 노드/DaemonSet 목록과 비교해야 합니다. 메트릭이나 규칙이 로드되지 않았다면 알람이 보이지 않는다고 정상이라고 판단할 수 없습니다. Typha 연결 감소나 재분배 Counter 증가는 확장 중 정상적으로 발생할 수 있습니다. 지속되는 streaming/client 지연과 컴포넌트 가용성을 함께 보고 장애를 판단하세요. 로컬 엔드포인트 수 0도 Felix unready를 뜻하지 않습니다. 실제 readiness/rollout 상태와 별도 합성 허용·거부 테스트를 사용하세요. ## 로그 분석과 트러블슈팅 ### 영향받은 워크로드와 노드부터 확인 Pending Pod는 CNI 호출 전에 스케줄링에서 막혔을 수 있습니다. 먼저 이벤트와 `spec.nodeName`을 확인하세요. CNI/IP 할당 오류라면 실제 할당자를 식별하고 해당 노드의 kubelet/CNI 로그를 조사합니다. 모든 Pod IPAM 오류가 Felix 프로세스 로그에 있는 것은 아닙니다. ```bash CALICO_NAMESPACE=calico-system WORKLOAD_NAMESPACE=calico-demo WORKLOAD_POD=replace-with-actual-pod kubectl describe pod "$WORKLOAD_POD" -n "$WORKLOAD_NAMESPACE" CALICO_NODE=$(kubectl get pod "$WORKLOAD_POD" -n "$WORKLOAD_NAMESPACE" \ -o jsonpath='{.spec.nodeName}') test -n "$CALICO_NODE" || { echo "Pod is not scheduled to a node" >&2; exit 1; } kubectl get pods -n "$CALICO_NAMESPACE" -l k8s-app=calico-node \ --field-selector "spec.nodeName=$CALICO_NODE" -o wide # Rollout 중에도 이 노드의 실제 에이전트 Pod를 선택합니다. CALICO_POD=replace-with-actual-calico-node-pod kubectl logs -n "$CALICO_NAMESPACE" "$CALICO_POD" -c calico-node \ --since=15m --tail=200 --timestamps ``` 시간 범위와 tail 제한을 명시하세요. Selector를 사용하면 `kubectl logs`의 기본 tail이 짧을 수 있으므로 시간 범위를 지정했다고 그 구간의 모든 로그를 받았다고 가정하면 안 됩니다. 컨테이너가 재시작했다면 가능한 경우 이전 로그도 확인합니다. 조회 오류를 “오류 없음”으로 바꾸지 말고 보존하세요. Felix 프로세스 로그는 규칙 설정과 컴포넌트 동작을 설명합니다. `logSeverityScreen`을 Debug로 바꿔도 패킷별 정책 결정 로그가 생성되지는 않습니다. 임시 변경 전에 기존 필드 값과 설정 소유자를 기록하고, 이전 값이 Info였다고 가정하지 말고 정확한 값 또는 필드 부재를 복원하세요. 파일/syslog 출력도 설정 경로와 런타임에 따라 달라집니다. ### 주소 할당과 연결 | 증상 | 상태 변경 전에 확인할 사항 | | --- | --- | | 스케줄링된 노드 없음 | Scheduler 이벤트, 용량, affinity, taint 확인, 아직 IPAM 진단 단계가 아님 | | CNI 할당 실패 | 실제 할당자 로그, pool/주소 용량, selector 적격성, 블록/affinity 제한, API 접근 | | Pod IP는 연결되지만 Service 실패 | Endpoints/EndpointSlices, Service 포트, kube-proxy 또는 BPF Service 처리, DNS, 정책 | | 작은 패킷만 성공 | Underlay/overlay MTU, fragmentation/PMTUD, 반환 경로 | | 정책이 의도대로 차단하지 않음 | 실제 엔드포인트 identity/레이블, 방향, namespace selector, tier/order, 앞선 allow, host-network/추가 인터페이스 제약, 기존 연결 | ```bash kubectl exec -n "$CALICO_NAMESPACE" "$CALICO_POD" -c calico-node -- ip route show kubectl exec -n "$CALICO_NAMESPACE" "$CALICO_POD" -c calico-node -- ip -d link show calicoctl get networkpolicy -n "$WORKLOAD_NAMESPACE" -o yaml calicoctl get globalnetworkpolicy -o yaml calicoctl get tier -o yaml calicoctl get workloadendpoint -n "$WORKLOAD_NAMESPACE" -o yaml kubectl get pod "$WORKLOAD_POD" -n "$WORKLOAD_NAMESPACE" --show-labels ``` 클러스터의 첫 번째 `calico-node` Pod를 골라 문제가 있는 워크로드의 노드라고 가정하지 마세요. ICMP 성공/실패만으로 TCP나 HTTP 정책이 검증되지는 않습니다. 도구가 확인된 승인 진단 워크로드와 애플리케이션의 실제 프로토콜/포트를 사용하세요. Calico IPAM에서는 위의 읽기 전용 명령과 [IPAM 정리 절차](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md)를 사용합니다. Pool CIDR과 blockSize는 변경할 수 없습니다. 겹치지 않는 적격 pool 추가는 계획된 용량 변경이며 기존 CIDR의 직접 확장이 아닙니다. 원인을 확인하기 전에 주소를 해제하거나 에이전트를 재시작하지 마세요. Operator 설치의 MTU와 주소 자동 감지는 `Installation.spec.calicoNetwork`에 설정하며 문서화된 경우 터널별 Felix 필드를 사용합니다. `FelixConfiguration.spec.mtu`와 `ipAutoDetectionMethod`는 검토한 API가 아닙니다. [MTU/네트워킹 안내](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)를 따르며 다른 설정을 보존하고 신규/기존 Pod를 각각 검증하세요. ### BGP 진단 BGP를 사용하는 구성에서만 BGP를 검사합니다. 선택한 `calico-node` Pod에서 실제 BIRD 소켓을 사용하세요. ```bash kubectl exec -n "$CALICO_NAMESPACE" "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show protocols all kubectl exec -n "$CALICO_NAMESPACE" "$CALICO_POD" -c calico-node -- \ birdcl -s /var/run/calico/bird.ctl show route calicoctl get bgpconfiguration -o yaml calicoctl get bgppeer -o yaml calicoctl get bgpfilter -o yaml ``` IPv6 데몬이 있다면 해당 `bird6.ctl`을 사용합니다. 로컬/피어 ASN, 선택한 출발지 주소, 양방향 TCP 179, 인증/TTL, 라우트 필터, 기대하는 광고 경로를 확인하세요. TCP 연결 성공만으로 세션 Established나 필요한 prefix 수용이 검증되지는 않습니다. 파일이 있다고 가정하기 전에 패키지의 로그 설정을 확인하세요. 릴리스 컨테이너의 BIRD run/log 스크립트가 출력 위치를 결정합니다. [BGP 심화](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/04-bgp-deep-dive.md)를 참고하세요. ## 헬스체크 자동화 다음 **operator 설치 상태 검사**는 Bash, jq, 호환 kubectl이 있는 관리 환경에서 실행합니다. 읽기 전용 API/로그 요청으로 DaemonSet의 관측 generation과 복제본 가용성을 확인하고 요청이 실패하면 실패를 반환합니다. BGP 소켓, 패킷 전달, 모든 정책의 정확성을 검사하는 스크립트는 아닙니다. ```bash #!/usr/bin/env bash # calico-status-check.sh: operator 컴포넌트 상태와 제한된 로그 수집. set -euo pipefail CALICO_NAMESPACE=${CALICO_NAMESPACE:-calico-system} if ! calico_ds_json=$(kubectl get daemonset calico-node -n "$CALICO_NAMESPACE" \ --request-timeout=20s -o json); then echo "Unable to read calico-node DaemonSet status" >&2 exit 2 fi if ! jq -e ' .status.desiredNumberScheduled as $desired | ($desired > 0) and (.status.observedGeneration >= .metadata.generation) and (.status.updatedNumberScheduled == $desired) and (.status.numberReady == $desired) and (.status.numberAvailable == $desired) and ((.status.numberUnavailable // 0) == 0) ' <<<"$calico_ds_json" >/dev/null; then echo "Calico DaemonSet is not fully observed, updated and available" >&2 exit 1 fi if ! calico_status_json=$(kubectl get tigerastatus --request-timeout=20s -o json); then echo "Unable to read operator component status" >&2 exit 2 fi if ! jq -e ' (.items | length) > 0 and all(.items[]; any(.status.conditions[]?; .type == "Available" and .status == "True") and any(.status.conditions[]?; .type == "Progressing" and .status == "False") and any(.status.conditions[]?; .type == "Degraded" and .status == "False") ) ' <<<"$calico_status_json" >/dev/null; then echo "Operator components are unavailable, progressing, degraded or missing conditions" >&2 exit 1 fi if ! calico_logs=$(kubectl logs -n "$CALICO_NAMESPACE" -l k8s-app=calico-node \ -c calico-node --since=15m --tail=200 --timestamps --prefix \ --request-timeout=20s); then echo "Unable to retrieve selected Calico logs; do not report no errors" >&2 exit 2 fi printf '%s\n' "$calico_logs" echo "Component status checks passed; review these bounded logs and test application policy separately." ``` 비어 있거나 스케줄링되지 않은 DaemonSet, 오래된 상태, 누락된 컴포넌트 condition은 성공이 아닙니다. 로그는 선택한 시간/tail 범위로 제한되며 해석이 필요합니다. Counter나 ERROR 단어 하나가 실제 장애와 같은 의미는 아닙니다. CronJob으로 예약하려면 먼저 필요한 도구와 스크립트를 승인 이미지에 패키징하고 검증하세요. DaemonSet, Pod/Pod 로그, TigeraStatus 읽기 권한을 가진 전용 ServiceAccount를 사용하며 권한이 큰 `calico-node` identity를 재사용하지 마세요. 동시 실행, deadline, 실패 보고도 설정합니다. `calico/ctl` 이미지는 범용 Bash/kubectl 진단 환경이 아니며 일반 Job은 의도적으로 추가 접근을 제공하지 않으면 다른 노드의 BIRD 소켓을 조사할 수 없습니다. 이 로컬 스크립트가 작동하는 클러스터 내부 CronJob까지 제공한다는 뜻은 아닙니다. ## 버전 업그레이드와 복구 ### 전환 준비 ```bash calicoctl version kubectl version --output=yaml kubectl get deployment tigera-operator -n tigera-operator \ -o jsonpath='{.spec.template.spec.containers[*].image}' kubectl get tigerastatus kubectl get daemonset calico-node -n calico-system -o wide helm get values calico -n tigera-operator -o yaml ``` Helm 명령은 Helm 관리 설치에만 적용됩니다. 실제 이미지, CRD, 데이터스토어, 노드 OS/커널, 데이터플레인, Kubernetes 호환성을 확인하세요. 소유 매니페스트/values, 정책, 검증된 복구 계획을 보존합니다. `kubectl version --short`는 현재 명령 옵션이 아닙니다. 실제 출발 버전과 설치 방식에 맞는 [3.32 업그레이드 절차](https://docs.tigera.io/calico/latest/operations/upgrading/kubernetes-upgrade)를 따릅니다. 해당 릴리스를 거칠 때 OwnerReference/UID 마이그레이션 주의사항을 검토하세요. 목표 버전을 고정하고 calicoctl도 맞춥니다. Helm에서는 새 operator보다 먼저 소유 경로로 일치하는 Calico CRD를 적용하거나, `manageCRDs: true`로 operator의 CRD 설치를 기다린 뒤 새 필드를 사용합니다. Operator 변경만을 이유로 `--force-conflicts`로 필드 소유권을 덮어쓰지 마세요. 검토한 변경 후 operator, calico-node, 설정한 다른 컴포넌트를 관찰하고 rollout 중·이후 허용/거부 경로를 테스트합니다. Operator는 자신이 관리하는 DaemonSet을 조정합니다. Affinity patch로 “canary” 노드에서 에이전트를 제거한다고 안전한 canary가 배포되는 것은 아니며 해당 노드가 정책 없이 남을 수 있습니다. 대표성 있는 격리 환경에서 버전/설정을 검증하고 지원되는 rollout 제어를 사용하세요. 경쟁하는 두 번째 노드 DaemonSet을 임의로 만들지 마세요. ### 복구 한계 `helm rollback`, 이전 operator 적용, 설정 export 적용은 CRD/데이터 마이그레이션이나 패킷 처리 상태를 자동으로 되돌리지 않습니다. 출발/목표 릴리스의 지원 다운그레이드 경로와 저장 데이터를 확인한 후 복구를 선택하세요. Installation 리소스가 남아 있다고 데이터 손실이 없다는 뜻은 아닙니다. EKS 컨트롤 플레인 복구는 [Part 8](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md)의 현재 자격과 7일 롤백 한계를 따릅니다. Calico/애드온과 애플리케이션 호환성은 별도 책임입니다. ## 백업 및 재해 복구 ### 설정 목록과 상태 복구 구분 | 자료 | 목적과 한계 | | --- | --- | | Git 관리 매니페스트/Helm values, 버전 기록 | 원하는 설정과 소유권, 일치하는 CRD 정의와 이미지 보존 | | Calico 정책, tier, set, pool, BGP/filter, controller 설정 | 실제 사용한 namespaced/staged/global 리소스를 포함한 설정 목록 | | Kubernetes NetworkPolicy, namespace/ServiceAccount 레이블, 관련 RBAC | Calico 리소스만 export하면 빠지는 정책 identity/의존성 | | Host/node/endpoint와 IPAM 상태 | 런타임/토폴로지에 의존하므로 이전 노드 주소와 할당을 다른 클러스터에 그대로 적용하지 않음 | | 데이터스토어 백업과 애플리케이션 데이터 | 일관된 복구 수단과 별도로 보호할 자격증명/데이터, YAML 목록은 원자적 데이터스토어 snapshot이 아님 | `kubectl export` 명령은 없습니다. `calicoctl get TYPE -o yaml`은 리소스 export이며 `--export`에는 앞서 설명한 이름 지정 제한이 있습니다. 자체 관리 Kubernetes/etcd는 일치하는 버전과 복구 테스트를 포함한 [Kubernetes etcd 백업/복구 절차](https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/)를 따르세요. 관리형 서비스는 해당 서비스의 지원 복구 방식을 사용하며 이 절차로 EKS etcd에 접근할 수는 없습니다. ### 보호된 설정 목록 예제 다음은 Calico IPAM을 사용하는 3.32 operator 설치에서 **명시한 일부 리소스**를 내보내는 스크립트입니다. Bash, calicoctl, kubectl, sha256sum이 필요합니다. 대상 디렉터리는 없어야 하며 부분 실패에는 `STATE=incomplete`가 남습니다. Secret, 외부 IAM/네트워크 장비, 모든 operator 사용자 정의 리소스, 전체 IPAM 할당 상태를 수집하지 않습니다. 실제 기능에 맞게 목록을 확장하고 자격증명은 별도 안전한 백업으로 보호하세요. ```bash #!/usr/bin/env bash # calico-config-inventory.sh: 보호된 설정 목록, 데이터스토어 snapshot이 아님. set -euo pipefail umask 077 CALICO_EXPORT_DIR=${1:?Usage: calico-config-inventory.sh NEW_EXPORT_DIRECTORY} mkdir -m 700 -- "$CALICO_EXPORT_DIR" printf '%s\n' incomplete > "$CALICO_EXPORT_DIR/STATE" for calico_kind in node ippool ipreservation bgpconfiguration bgppeer bgpfilter \ globalnetworkpolicy stagedglobalnetworkpolicy globalnetworkset \ felixconfiguration kubecontrollersconfiguration ipamconfiguration \ tier hostendpoint profile; do calicoctl get "$calico_kind" -o yaml > "$CALICO_EXPORT_DIR/$calico_kind.yaml" done for calico_kind in networkpolicy stagednetworkpolicy stagedkubernetesnetworkpolicy \ networkset workloadendpoint; do calicoctl get "$calico_kind" -A -o yaml > "$CALICO_EXPORT_DIR/$calico_kind.yaml" done kubectl get installation default -o yaml > "$CALICO_EXPORT_DIR/installation.yaml" kubectl get networkpolicies.networking.k8s.io -A -o yaml \ > "$CALICO_EXPORT_DIR/kubernetes-networkpolicies.yaml" kubectl get namespaces -o yaml > "$CALICO_EXPORT_DIR/namespaces.yaml" kubectl get serviceaccounts -A -o yaml > "$CALICO_EXPORT_DIR/serviceaccounts.yaml" ( cd -- "$CALICO_EXPORT_DIR" sha256sum ./*.yaml > SHA256SUMS ) printf '%s\n' complete > "$CALICO_EXPORT_DIR/STATE" echo "Configuration inventory completed: $CALICO_EXPORT_DIR" ``` `complete`는 명시한 조회와 체크섬 작성이 완료되었다는 뜻이며 트랜잭션 일관성이나 재해 복구 테스트 완료를 의미하지 않습니다. Export를 민감한 인프라 자료로 취급하세요. 체크섬을 확인하고 장애 영역 밖에 보관하며 실제 데이터스토어/버전으로 복구를 연습합니다. ### 복구 계획 1. 선택한 방식으로 호환 컨트롤 플레인/데이터스토어와 필요한 CRD/operator를 복구합니다. 새 클러스터 이전과 같은 클러스터 복구의 identity/IPAM 요건은 다릅니다. 2. Namespace/ServiceAccount identity, 레이블, RBAC를 검토하고 tier/set을 의존 정책보다 먼저 복구하는 등 소유 선언적 설정의 의존 순서를 따릅니다. 3. 클러스터별 metadata, 생성형/controller 소유 오브젝트, 이전 노드 주소/할당을 검토합니다. 원본 dump를 이식 가능한 desired-state 매니페스트로 그대로 적용하지 마세요. 4. 정상 변경을 재개하기 전에 IP 할당 중복, 경로, 암호화, Service/DNS, 허용·거부 트래픽을 검증합니다. `calicoctl datastore migrate export/import`는 datastore lock과 rollback 경계가 있는 실제 **etcd→Kubernetes 마이그레이션** 기능입니다. 기존 Kubernetes 데이터스토어의 일반 백업 단축 명령이 아닙니다. Lock은 새 Pod에 영향을 주며 문서화된 마이그레이션은 Kubernetes 데이터스토어 unlock 후 되돌릴 수 없습니다. [마이그레이션 절차](https://docs.tigera.io/calico/latest/operations/datastore-migration)를 참고하세요. ## 운영 모범 사례 ### 정책과 접근 선택한 테스트 네임스페이스에서 DNS, API, identity, 모니터링, 애플리케이션 의존성을 준비한 후 default-deny를 검증하세요. 빈 전역 `all()` 정책이나 존재하지 않는 API-server/노드 레이블 selector는 필수 트래픽을 끊을 수 있습니다. Pod와 host endpoint의 정책 경로도 다릅니다. [Part 5](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md)의 제한된 예제, tier 의미, host endpoint 제어를 사용하세요. 독립적으로 사용할 수 있는 복구 경로를 유지하고 범위를 넓히기 전에 거부 사례를 테스트합니다. ### Flow 관측성 현재 OSS operator/Helm 설치는 Goldmane와 Whisker를 사용할 수 있습니다. [OSS flow log 가이드](https://docs.tigera.io/calico/latest/observability/view-flow-logs)는 이 기능을 tech preview로 표시하며 패킷/연결 하나당 한 레코드가 아닌 집계 flow를 설명합니다. 이전 파일/DNS logger 필드와 존재하지 않는 `FlowLogsFileReporter` 이름은 유효한 OSS 설정이 아닙니다. ```bash kubectl get goldmane,whisker kubectl port-forward -n calico-system service/whisker 8081:8081 ``` Port-forward는 기본적으로 로컬에 바인딩합니다. Whisker/Goldmane에는 민감한 워크로드/네트워크 자료가 있으므로 외부 노출 전 인증과 접근 제어를 설정하세요. 이 컴포넌트가 없던 버전에서 업그레이드했다면 해당 사용자 정의 리소스를 의도적으로 활성화해야 합니다. 프로세스 debug 로그, 정책 Log action, 집계 flow log, Prometheus 메트릭은 서로 다른 질문에 답합니다. ### 성능과 자원 엔드포인트/정책 변경량, 데이터플레인 설정 시간, 대기열, 메모리, 실제 애플리케이션 트래픽을 측정하세요. Resync/refresh 주기는 Kubernetes API polling 주기가 아니며 늘린다고 보편적인 API 부하 최적화가 되지는 않습니다. 이전 `...Secs` 철자 대신 실제 duration 필드 `iptablesPostWriteCheckInterval`을 사용합니다. 설치 소유자의 지원 resource override와 operator 규모 조절을 보존하세요. BPF, DSR, 추측한 인터페이스 패턴을 일반적인 튜닝 프리셋으로 활성화하지 마세요. [Part 6](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/06-ebpf-dataplane.md)에서 커널/플랫폼, Service 처리, kube-proxy 충돌, 복구를 다룹니다. Conntrack map을 키우면 메모리를 사용하며 모든 병목이 사라지는 것은 아닙니다. 데이터플레인이나 자원 변경이 필요하면 관련 워크로드·실패 테스트를 다시 수행하세요. 이 문서의 검사는 오프라인 스키마, 쿼리, 스크립트 fixture 검증입니다. 프로덕션 용량, 성공적인 클러스터 업그레이드나 재해 복구를 입증하지 않습니다. ## 참고 자료 - [Calico 요구사항](https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements) - [Calico Installation API](https://docs.tigera.io/calico/latest/reference/installation/api) - [컴포넌트 메트릭 모니터링](https://docs.tigera.io/calico/latest/operations/monitor/monitor-component-metrics) - [Felix 메트릭](https://docs.tigera.io/calico/latest/reference/felix/prometheus) - [Typha 메트릭](https://docs.tigera.io/calico/latest/reference/typha/prometheus) - [kube-controllers 메트릭](https://docs.tigera.io/calico/latest/reference/kube-controllers/prometheus) - [Calico 트러블슈팅](https://docs.tigera.io/calico/latest/operations/troubleshoot/troubleshooting) - [Calico 업그레이드](https://docs.tigera.io/calico/latest/operations/upgrading/kubernetes-upgrade) - [Prometheus Operator API](https://prometheus-operator.dev/docs/api-reference/api/) ## 다음 단계와 퀴즈 [용어집](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/glossary.md), [고급 주제](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md), [EKS 통합](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md)을 복습하고 [운영 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/09-operations-quiz)를 풀어보세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/calico/glossary ---------------------------------------- # Calico 용어집 > **검토 기준**: Calico 3.32.2, 비교 용어는 Cilium 1.20.1 확인. > **마지막 업데이트**: 2026년 9월 12일 이 문서는 Calico와 관련된 주요 용어 및 약어에 대한 설명을 제공합니다. 용어는 카테고리별로 분류되어 있으며, 각 카테고리 내에서 알파벳/가나다 순으로 정렬되어 있습니다. ## 네트워킹 용어 ### AS (Autonomous System) BGP에서 단일 라우팅 정책을 가진 네트워크 그룹입니다. BGP는 AS 사이의 eBGP와 같은 AS 내부의 iBGP에서 사용합니다. RFC 6996의 private ASN 범위는 **64512–65534**와 **4200000000–4294967294**입니다. Private ASN은 관리 영역 안에서 재사용하며 인터넷 전체에서 고유한 공인 번호로 취급하지 않습니다. ```yaml # 예시: BGPConfiguration에서 AS 번호 설정 apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: asNumber: 64512 # 예시 private ASN; 실제 피어링 설계에 맞춰 선택 ``` ### BGP (Border Gateway Protocol) 자율 시스템 간 라우팅 정보를 교환하는 표준 프로토콜입니다. Calico는 BGP를 사용하는 구성에서 Pod/Service 경로를 배포합니다. 모든 Calico 데이터플레인이나 overlay 구성에 BGP가 필요한 것은 아닙니다. 인터넷의 핵심 라우팅 프로토콜이기도 합니다. ### BIRD (BIRD Internet Routing Daemon) Calico에서 사용하는 오픈소스 BGP 라우팅 데몬입니다. BGP가 활성화된 구성에서 다른 노드 및 외부 라우터와 피어링합니다. Calico 3.32.2는 패치된 BIRD 1.6.8 계열을 사용하므로 임의의 BIRD 2 설정과 동일하게 취급하지 않습니다. ### CIDR (Classless Inter-Domain Routing) IP 주소 블록을 표기하는 방법입니다. 예: `10.244.0.0/16`은 10.244.0.0부터 10.244.255.255까지 65,536개의 IP 주소를 나타냅니다. | CIDR | IP 수 | 용도 예시 | |------|-------|----------| | /16 | 65,536 | 전체 클러스터 Pod CIDR | | /24 | 256 | 노드당 할당 (kubeadm) | | /26 | 64 | Calico 기본 블록 크기 | | /28 | 16 | 소규모 블록 | ### eBPF (extended Berkeley Packet Filter) Linux 커널 프로그래밍 기술입니다. Calico의 BPF 데이터플레인은 네트워킹, 정책, Service 처리에 이를 사용합니다. 커널·플랫폼·워크로드에 따라 호환성과 성능이 달라지므로 항상 iptables보다 효율적이라고 보장할 수 없습니다. ### IPIP (IP-in-IP) IP 패킷을 다른 IP 패킷 내에 캡슐화하는 터널링 프로토콜입니다. Calico의 IPv4 IPIP 모드는 20바이트 outer IPv4 헤더를 추가합니다. Underlay에서 IP protocol 4를 허용해야 하며 UDP 포트를 뜻하지 않습니다. ### MTU (Maximum Transmission Unit) 네트워크에서 전송할 수 있는 최대 패킷 크기입니다. Calico 오버레이 네트워킹 사용 시 캡슐화 오버헤드를 고려하여 MTU를 조정해야 합니다. 다음은 **유효 underlay MTU가 1500인 독립적인 모드 예시**이며 보편적인 권장값이나 성능 측정값이 아닙니다. | 모드 | 예시 Calico MTU | 헤더 오버헤드 | | --- | --- | --- | | Direct | 1500 | 0 | | IPIP IPv4 | 1480 | 20 bytes | | VXLAN IPv4 | 1450 | 50 bytes | | VXLAN IPv6 | 1430 | 70 bytes | | WireGuard IPv4 | 1440 | 60 bytes | | WireGuard IPv6 | 1420 | 80 bytes | 실제 최소 경로 MTU와 플랫폼 제약을 확인하세요. Calico의 WireGuard/overlay 혼합은 피어별 경로가 달라질 수 있으므로 헤더를 무조건 더하지 않고 선택 가능한 경로 중 가장 작은 MTU를 적용합니다. [MTU 안내](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/03-networking-modes.md)를 참고하세요. ### NAT (Network Address Translation) IP 패킷의 소스 또는 목적지 IP 주소를 수정하는 프로세스입니다. Calico는 Pod에서 외부로 나가는 트래픽에 SNAT(Source NAT)를 적용할 수 있습니다. ### VXLAN (Virtual Extensible LAN) Layer 2 네트워크를 Layer 3 네트워크 위에 오버레이하는 네트워크 가상화 기술입니다. UDP 포트 4789를 사용하며, IPIP보다 더 넓은 환경(특히 클라우드)에서 호환성이 좋습니다. ### VTEP (VXLAN Tunnel End Point) VXLAN 패킷의 캡슐화 및 디캡슐화를 담당하는 엔드포인트입니다. Calico에서는 VXLAN이 활성화된 해당 노드가 VTEP 역할을 합니다. --- ### CNI / veth CNI는 컨테이너 네트워크 연결 설정 규격과 플러그인 체계입니다. 일반 Linux Calico CNI는 Pod와 호스트를 연결할 veth, IPAM, 경로 설정을 담당합니다. Felix가 모든 Pod의 veth를 생성하는 것이 아니며 hostNetwork Pod, Windows HNS, 다른 인터페이스 유형은 다릅니다. ### Conntrack / iptables / nftables Conntrack은 상태 기반 정책과 NAT에 사용하는 연결 추적입니다. Linux 기본 conntrack과 BPF conntrack map은 같은 튜닝 대상이 아닙니다. iptables는 Netfilter를 다루는 userspace 인터페이스이며, iptables의 NFT backend와 Calico의 별도 native Nftables 데이터플레인도 구분해야 합니다. ### DSR (Direct Server Return) Service를 처음 전달한 노드를 거치지 않고 backend가 응답하는 방식입니다. Calico BPF에서 지원하지만 네트워크와 출발지 주소 조건이 필요하며 모든 클라우드 로드 밸런서와 호환된다는 뜻은 아닙니다. ## Calico 컴포넌트 ### Felix 각 노드에서 실행되는 Calico의 핵심 정책 적용 에이전트입니다. 주요 역할: - CNI가 생성한 인터페이스 상태 추적과 필요한 데이터플레인 설정 - 라우팅 테이블 프로그래밍 - iptables/eBPF 규칙 관리 - Network Policy 규칙 설정, 커널 데이터플레인이 패킷 평가 ### BIRD BGP 라우팅 데몬입니다. 주요 역할: - BGP 피어 연결 관리 - 라우트 교환 및 전파 - Route Reflector 기능 (설정 시) ### Typha 데이터스토어 변경을 캐시하여 여러 Felix에 배포하는 fan-out 서비스입니다. Felix별 watch 부하를 줄이지만 클러스터 전체가 단일 watch를 공유한다는 뜻은 아닙니다. 여러 syncer 종류와 복제본이 있고 정책을 다른 클러스터에 자동 복제하는 서비스도 아닙니다. Operator 1.42.6의 노드 수 N에 대한 계산은 다음과 같습니다. “50개 이상에서만 필요” 또는 `ceil(N/200)`을 일반 규칙으로 사용하지 마세요. | 조건 | 기대 Typha 복제본 | | --- | --- | | N ≤ 2 | 1 | | 3 ≤ N ≤ 4 | 2 | | N ≥ 5 | max(3, floor(N/200) + 2) | 실제 노드 집계, 배치 조건과 자원 용량은 [고급 주제](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md)에서 확인합니다. ### confd BIRD 설정 파일을 동적으로 생성하는 구성 관리 도구입니다. BGP 설정 변경 시 BIRD 설정을 자동 업데이트합니다. ### kube-controllers Kubernetes/Calico 상태 조정을 담당하는 컨트롤러 집합입니다. 아래는 역할 예시이며 데이터스토어·설치에 따라 활성 집합이 다릅니다. Kubernetes 데이터스토어 구성에서 모두 독립 컨트롤러로 실행된다고 가정하지 마세요: - Policy Controller: NetworkPolicy 동기화 - Namespace Controller: 네임스페이스 프로필 관리 - ServiceAccount Controller: 서비스 계정 동기화 - WorkloadEndpoint Controller: 엔드포인트 정리 - Node Controller: 노드 정보 동기화 ### calicoctl Calico 리소스를 관리하는 CLI 도구입니다. kubectl과 유사하지만 Calico 전용 리소스에 특화되어 있습니다. ```bash calicoctl get networkpolicy -A calicoctl get ippool -o yaml calicoctl ipam show ``` `calicoctl node status`는 필요한 접근 조건을 갖춘 로컬 노드의 BGP 진단입니다. Kubeconfig만 있다고 전체 노드 readiness 검사가 되지는 않습니다. 변경 명령과 IPAM 정리는 [운영 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/09-operations.md)를 따르세요. ### Calico API Server / Dikastes / Goldmane / Whisker Calico API Server는 OSS에서도 제공하는 API 통합 컴포넌트이며 Kubernetes API 서버와 다릅니다. User-facing `projectcalico.org/v3`와 backing CRD/native API 경로는 실제 설치에 따라 구분합니다. Dikastes는 Istio/Envoy가 호출하는 L7 정책 결정 컴포넌트이며 자체 전달 프록시가 아닙니다. Goldmane는 flow 집계 API, Whisker는 웹 UI로, 현재 OSS flow 관측성 가이드는 tech preview로 표시합니다. --- ## 정책 관련 용어 ### GlobalNetworkPolicy 클러스터 범위 Calico API 리소스입니다. Selector에 따라 여러 네임스페이스의 워크로드 또는 host endpoint를 선택하며 모든 Pod에 자동 적용된다는 뜻은 아닙니다. 다음은 **준비된 테스트 네임스페이스만** 선택하는 형태 예시입니다. 실제 적용 전 DNS·애플리케이션 등 허용 의존성을 구성해야 합니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: glossary-demo-default-deny spec: namespaceSelector: projectcalico.org/name == 'calico-glossary-demo' selector: all() # 선택한 네임스페이스의 워크로드만 types: - Ingress - Egress ``` ### NetworkPolicy (Calico) `projectcalico.org/v3`의 네임스페이스 범위 API이며 Kubernetes `networking.k8s.io/v1` NetworkPolicy와 별도 리소스입니다. Calico에는 다음과 같은 규칙이 있습니다: - `action` 필드 (Allow, Deny, Log, Pass) - `order` 필드 (정책 평가 순서) - FQDN/도메인 기반 규칙은 문서화된 상용 에디션 기능 - HTTP 메서드/경로 규칙은 별도 지원 Istio/Envoy/Dikastes 통합 필요, OSS에도 문서화되어 있으며 기본 설치만으로 활성화되지 않음 ### NetworkSet / GlobalNetworkSet IP 주소 집합을 정의하여 재사용할 수 있는 리소스입니다. NetworkSet은 네임스페이스 범위, GlobalNetworkSet은 클러스터 범위의 레이블/CIDR 집합입니다. Policy selector로 선택하며 적절한 global namespace 선택을 사용하면 네임스페이스 정책에서도 GlobalNetworkSet을 참조할 수 있습니다. OSS CIDR 집합은 DNS/도메인 규칙이 아닙니다. 아래 CIDR은 형태 예시이지 실제 신뢰할 상대 목록이 아닙니다. ```yaml apiVersion: projectcalico.org/v3 kind: GlobalNetworkSet metadata: name: trusted-partners labels: partner: trusted spec: nets: - 203.0.113.0/24 - 198.51.100.0/24 ``` ### Tier 정책 평가 계층입니다. 낮은 숫자의 tier order, 이어서 해당 tier 내부 policy order 순으로 평가합니다. Allow/Deny는 최종 결정, Log는 계속 진행, Pass는 현재 tier의 나머지 정책을 건너뛰고 다음 적용 tier로 위임합니다. 마지막 tier의 Pass 이후에는 endpoint profile을 평가합니다. 선택된 tier에서 일치하는 규칙이 없으면 defaultAction(기본 Deny)을 적용합니다. ![트래픽이 Security, Platform, Application Tier를 순서대로 통과하며 각 단계에서 Pass 시 다음 Tier로 넘어가고 Deny 시 Drop으로 이어지는 Calico 정책 평가 흐름을 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-calico-glossary-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-calico-glossary-0.html) 그림은 사용자 정의 tier에서 Pass→Pass→Allow가 일어나는 한 경로입니다. 앞선 tier에서 Allow가 나면 뒤의 tier를 모두 방문하지 않으며, 세 tier 이름이나 이 순서가 필수 기본 구성은 아닙니다. ### HostEndpoint 호스트 인터페이스를 표현하여 호스트 정책을 적용하는 리소스입니다. 설정에 따라 노드를 통과하는 forwarded 트래픽에도 적용합니다. 아래는 형태 예시이며 실제 노드/인터페이스/IP로 생성하기 전에 host 정책, profile, failsafe와 관리 접근을 검토해야 합니다. 생성만으로 관리 연결이 제한될 수 있습니다. ```yaml apiVersion: projectcalico.org/v3 kind: HostEndpoint metadata: name: node1-eth0 labels: host: node1 spec: interfaceName: eth0 node: node1 expectedIPs: ["10.0.1.10"] ``` --- ## 운영 용어 ### IPPool Calico IPAM 등이 사용할 주소 pool을 정의합니다. CIDR, 캡슐화, NAT, node selector, 허용 용도(예: Workload/Tunnel, 지원되는 LoadBalancer IPAM)를 포함합니다. Kubernetes Node PodCIDR과 같은 오브젝트가 아니며 CIDR/blockSize는 변경할 수 없습니다. VPC CNI policy-only는 이 pool로 Pod IP를 할당하지 않습니다. ```yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: default-ipv4-pool spec: cidr: 10.244.0.0/16 blockSize: 26 # /26 = 64 IPs per block ipipMode: Never vxlanMode: CrossSubnet natOutgoing: true nodeSelector: all() ``` ### IPAM (IP Address Management) IP 주소의 계획, 할당, 추적 및 관리를 담당하는 시스템입니다. Calico IPAM은 블록 기반 할당을 사용합니다. 기본 IPv4 /26과 IPv6 /122는 각각 64개 주소이며 Windows 예약 등으로 워크로드 사용 가능 수가 줄 수 있습니다. BlockAffinity는 보통 노드와 블록의 연결입니다. strictAffinity가 아니면 다른 노드 블록에서 빌릴 수 있어 모든 Pod가 자기 노드 소유 블록의 주소를 받는 것은 아닙니다. ### WorkloadEndpoint Pod/VM 등 워크로드 인터페이스의 네임스페이스 범위 표현입니다. 일반적으로 orchestrator/plugin이 수명주기를 관리하며 주소, 레이블, profile 참조가 정책 계산에 사용됩니다. 모든 유효 정책 결정이 저장된 목록이 아니므로 보통 읽기 전용으로 조사합니다. ### BGPPeer BGP 피어링 구성을 정의하는 리소스입니다. 외부 라우터 또는 다른 노드와의 BGP 연결을 설정합니다. ```yaml apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: tor-router spec: peerIP: 192.168.1.1 asNumber: 64513 nodeSelector: rack == 'rack1' ``` ### BGPConfiguration `default`의 클러스터 BGP 기본값 등을 정의하며 AS 번호, node-to-node mesh, 서비스 IP 광고를 설정합니다. 노드 AS 및 지원되는 노드별 override도 별도로 확인해야 합니다. ### FelixConfiguration Felix 기본값과 지원되는 노드별 override를 구성합니다. 로깅·메트릭·데이터플레인 설정을 다루지만 operator의 Installation API를 대체하지는 않습니다. ### Route Reflector 대규모 BGP 네트워크에서 full-mesh 연결 대신 사용되는 경로 반사기입니다. 각 노드가 모든 노드와 피어링하는 대신 Route Reflector를 통해 경로를 전파합니다. 노드 **총수 N에 reflector r개를 포함**하고, 각 client가 모든 reflector에 연결하며 reflector끼리 full mesh인 경우: ```text Full mesh: N(N-1)/2 Reflectors: r(N-r) + r(r-1)/2 N=100, r=2: 197 sessions (full mesh: 4950) N=100, r=1: 99 sessions, reflector redundancy 없음 ``` ### Profile / Staged Policy / Host Policy 옵션 Profile은 endpoint가 레이블을 공유하는 방식입니다. 과거 ingress/egress 규칙도 포함할 수 있지만 새 정책에는 NetworkPolicy/GlobalNetworkPolicy를 사용하도록 해당 기능이 deprecated되어 있습니다. Staged Policy는 비강제 정책 평가 리소스로 OSS에서도 제공되며 관측성 전제가 필요합니다. Host GlobalNetworkPolicy의 `applyOnForward`는 forwarded 트래픽 적용을 제어합니다. `preDNAT`은 ingress에서 NAT 전에, `doNotTrack`은 conntrack 전에 적용하며 둘 다 `applyOnForward`가 필요합니다. `preDNAT`과 `doNotTrack`은 함께 사용할 수 없고 stateless 반환 경로는 별도 허용이 필요합니다. [정책 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/05-network-policy.md)를 참고하세요. ### Dataplane / Metrics / Health Calico는 Linux Iptables/Nftables/BPF와 지원되는 Windows HNS 구성을 제공합니다. 데이터스토어는 Kubernetes API 또는 지원 직접 etcdv3 방식이며 기능별 제약이 다릅니다. 메트릭 포트는 Felix 기본 9091, Typha 바이너리 기본 9091(흔히 9093으로 명시), kube-controllers 기본 9094입니다. Felix health 기본 포트는 9099지만 컴포넌트 상태만으로 애플리케이션 연결과 정책 정확성이 증명되지는 않습니다. --- ## Calico vs Kubernetes 용어 대조 | Calico 용어 | Kubernetes 대응 | 설명 | |------------|----------------|------| | NetworkPolicy | NetworkPolicy | Calico는 확장된 selector 문법과 action 지원 | | GlobalNetworkPolicy | 같은 API 없음 | 표준 Kubernetes NetworkPolicy와 다른 클러스터 범위 API | | NetworkSet | (해당 없음) | IP 그룹 정의, K8s에는 없음 | | GlobalNetworkSet | (해당 없음) | 클러스터 전역 IP 그룹, K8s에는 없음 | | Tier | 같은 API 없음 | Calico의 정책 계층, 다른 확장 정책 API와 의미가 다를 수 있음 | | HostEndpoint | (해당 없음) | 호스트 인터페이스 보호, K8s에는 없음 | | IPPool | (해당 없음) | IP 범위 정의, K8s에는 없음 | | WorkloadEndpoint | Pod 네트워크 인터페이스 | Service Endpoint/EndpointSlice나 전체 적용 정책 목록과 다름 | | Profile | 직접 대응 없음 | 공유 레이블, 과거 규칙 기능은 새 정책에 권장되지 않음 | --- ## Calico vs Cilium 용어 대조 기능상 비교이며 리소스의 상호 교환이나 모든 구성의 지원을 보장하지 않습니다. 비교 기준은 Calico 3.32.2와 Cilium 1.20.1입니다. | 개념 | Calico | Cilium 비교 | | --- | --- | --- | | 노드 에이전트 | Felix | Cilium Agent | | BGP | BGP 구성의 BIRD | 내장 BGP Control Plane의 경로 광고, datapath 프로그래밍이나 내부 라우팅 설정 기능이 아님 | | 데이터스토어 fan-out | Typha | 동일한 Typha 컴포넌트/API 없음 | | Pod IP 할당 | IPPool + 선택한 IPAM | Cilium IPAM 모드에 따라 다르며 단일 pool API로 환산하지 않음 | | 네임스페이스 정책 | Calico NetworkPolicy | CiliumNetworkPolicy, 둘 다 표준 Kubernetes NetworkPolicy와 별개 | | 클러스터 정책 | GlobalNetworkPolicy | CiliumClusterwideNetworkPolicy, 규칙/우선순위 의미 차이 | | 재사용 외부 CIDR | NetworkSet / GlobalNetworkSet | 클러스터 범위 CiliumCIDRGroup과 CIDR 규칙의 cidrGroupRef/cidrGroupSelector, CiliumIPSet이 아님 | | 정책 tier | Calico Tier | 동일한 Calico Tier API 없음, 다른 정책 API의 우선순위 별도 확인 | | 워크로드 인터페이스 | WorkloadEndpoint | CiliumEndpoint, 수명주기/status 의미 차이 | | 호스트 보호 | HostEndpoint와 설정된 forwarding 정책 | Linux host firewall/nodeSelector 정책, forwarding 범위는 동일하지 않음 | | 데이터플레인 | Linux Iptables/Nftables/BPF, Windows HNS | Linux eBPF 요구사항, 이 비교에서 Windows 지원 beta로 설명할 근거 없음 | | 암호화 | OSS WireGuard의 지원 peer 경로 | WireGuard 또는 IPsec, 모드/플랫폼 제약 확인 | | L7 정책 | OSS에도 문서화된 별도 Istio/Envoy/Dikastes 통합 | Envoy 기반 정책 기능, 별도 전제 필요 | | Flow 관측성 | Goldmane/Whisker와 메트릭 | Hubble과 메트릭 | | 컨트롤러 | kube-controllers / Tigera Operator | Cilium Operator, 역할이 일대일 대응하지 않음 | | CLI | calicoctl | cilium과 에이전트 내부 cilium-dbg의 역할 구분 | Calico의 Windows 지원은 Linux 전체 기능과 같지 않습니다. IPv4 HNS와 문서화된 VXLAN/BGP 제한을 따르며 WireGuard/eBPF/host endpoint가 동일하게 제공되지 않습니다. 서비스 메시·L7 기능도 각 통합과 구성을 검토해야 합니다. 성능, 성숙도, 학습 난이도, 커뮤니티 규모를 근거 없이 등급화하기보다 구체적인 워크로드와 운영 요구를 비교하세요. --- ## 약어 정리 | 약어 | 전체 명칭 | 설명 | |------|----------|------| | ASN | Autonomous System Number | BGP에서 네트워크 식별 번호 | | BGP | Border Gateway Protocol | 경로 교환 프로토콜 | | BIRD | BIRD Internet Routing Daemon | BGP 라우팅 데몬 | | CIDR | Classless Inter-Domain Routing | IP 주소 표기법 | | CNI | Container Network Interface | 컨테이너 네트워크 인터페이스 | | DSR | Direct Server Return | 응답 패킷 직접 반환 | | eBPF | extended Berkeley Packet Filter | 커널 내 프로그래밍 | | ENI | Elastic Network Interface | AWS 가상 NIC | | FQDN | Fully Qualified Domain Name | 전체 도메인 이름 | | GNP | GlobalNetworkPolicy | 전역 네트워크 정책 | | HNS | Host Networking Service | Windows 네트워크 서비스 | | IPAM | IP Address Management | IP 주소 관리 | | IPIP | IP-in-IP | IP 캡슐화 터널링 | | MTU | Maximum Transmission Unit | 최대 전송 단위 | | NAT | Network Address Translation | 주소 변환 | | NP | NetworkPolicy | 네트워크 정책 | | RR | Route Reflector | BGP 경로 반사기 | | SNAT | Source NAT | 소스 주소 변환 | | VXLAN | Virtual Extensible LAN | 가상 확장 LAN | | VTEP | VXLAN Tunnel End Point | VXLAN 터널 엔드포인트 | --- ## 관련 문서 더 자세한 내용은 다음 문서를 참조하세요: - [Part 7: 고급 주제](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/07-advanced-topics.md) - IPAM, WireGuard, 대규모 클러스터 설계 - [Part 8: EKS 통합](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/08-eks-integration.md) - Amazon EKS 환경에서의 Calico - [Part 9: 운영 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/networking/calico/09-operations.md) - 설치, 모니터링, 트러블슈팅 ## 참고 자료 - [Calico 리소스 참조](https://docs.tigera.io/calico/latest/reference/resources/) - [Calico Tier](https://docs.tigera.io/calico/latest/reference/resources/tier) - [Calico MTU](https://docs.tigera.io/calico/latest/networking/configuring/mtu) - [RFC 6996 private ASN](https://www.rfc-editor.org/rfc/rfc6996.txt) - [Cilium CIDR group API](https://github.com/cilium/cilium/blob/v1.20.1/pkg/k8s/apis/cilium.io/v2/cidrgroups_types.go) - [Cilium BGP Control Plane](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/bgp-control-plane/bgp-control-plane.rst) - [Cilium host firewall](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/security/host-firewall.rst) ## 퀴즈 이 용어집의 내용을 테스트하려면 [용어집 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/calico/glossary-quiz)를 풀어보세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/02-vpc-lattice ---------------------------------------- # VPC Lattice Amazon VPC Lattice는 VPC와 AWS 계정 간 애플리케이션을 연결합니다. 이 문서는 리소스 모델, EKS 연동, 라우팅, IAM 인가, 모니터링과 문제 해결을 설명합니다. > 2026-09-11에 AWS Gateway API Controller **v2.1.3**과 Gateway API **v1.5.0**을 기준으로 검토했습니다. 예제는 구성 및 검증 절차를 설명하며, 이번 검토에서 실제 AWS 계정에 배포하지는 않았습니다. ## 목차 - [개요](#개요) - [아키텍처](#아키텍처) - [EKS와 VPC Lattice 통합](#eks와-vpc-lattice-통합) - [설치 및 구성](#설치-및-구성) - [서비스 관리](#서비스-관리) - [라우팅 및 트래픽 관리](#라우팅-및-트래픽-관리) - [보안 및 인증](#보안-및-인증) - [모니터링 및 로깅](#모니터링-및-로깅) - [모범 사례](#모범-사례) - [문제 해결](#문제-해결) - [참고 자료](#참고-자료) ## 개요 ### VPC Lattice란? VPC Lattice는 각 애플리케이션 옆에 프록시를 배치하지 않아도 애플리케이션 네트워킹을 제공합니다. **서비스 네트워크**는 서비스와 리소스 구성을 묶고 허용된 소비자와 연결합니다. 서비스는 리스너, 라우팅 규칙, 대상 그룹, 서비스 DNS 이름을 제공합니다. 현재 제품은 리소스 게이트웨이와 **리소스 구성(resource configuration)**을 통해 TCP를 사용하는 RDS 데이터베이스 같은 리소스도 연결합니다. 이 접근 모델은 대상 그룹을 사용하는 HTTP 서비스와 구분해야 합니다. 서비스 네트워크/서비스의 IAM 인증 정책은 리소스 구성 트래픽을 인가하지 않습니다. PrivateLink 기반 **서비스 네트워크 VPC 엔드포인트**를 사용하면 피어링, Transit Gateway, Direct Connect, VPN을 거치는 클라이언트에도 접근 경로를 제공할 수 있습니다. 직접 VPC 연결만으로는 Transit Gateway나 피어링 너머의 클라이언트까지 접근이 확장되지 않습니다. 대표적인 용도는 계정 간 애플리케이션 API, EKS와 다른 컴퓨팅 서비스 간 통신, 공유 데이터 리소스 접근입니다. 연결 관계, 라우팅, 보안 그룹, 인증과 애플리케이션 인가는 여전히 구성해야 합니다. ### 다른 서비스와 비교 | 서비스 | 주요 역할 | 구분할 점 | |---|---|---| | VPC Lattice | 프라이빗 애플리케이션 및 리소스 연결 | HTTP/HTTPS/gRPC 서비스 라우팅과 별도의 TLS/TCP 리소스 기능. 인터넷 API 진입점은 아님 | | API Gateway | 관리형 API 엔드포인트와 API 관리 | REST, HTTP, WebSocket API의 기능이 다름. GraphQL은 API Gateway의 별도 API 유형이 아님 | | AWS App Mesh | Envoy 기반 서비스 메시 | **2026-09-30** 지원 종료 예정. 검토일에는 아직 종료 전이며, 신규 설치보다 마이그레이션을 계획 | | Transit Gateway | IP 라우팅을 통한 네트워크 연결 | 네트워크를 연결하며 서비스별 HTTP 라우팅과 인가를 대체하지 않음 | | Istio / Linkerd / Cilium | 각 데이터 플레인으로 메시 기능 구현 | 기능과 운영 비용이 다르며, 모든 메시 아키텍처에 사이드카가 필수인 것은 아님 | VPC Lattice의 관리형 데이터 플레인을 직접 운영할 필요는 없지만, 총비용 절감이나 다른 메시와 동일한 기능을 보장하지는 않습니다. 실제 워크로드의 요청·데이터·리소스 요금, 컨트롤러 운영, 신원 요구사항, 재시도, 라우팅과 관측성을 비교하세요. [Istio–Lattice 비교](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/comparison/02-istio-vs-lattice.md)도 참고할 수 있습니다. ## 아키텍처 ### 구성 요소와 트래픽 흐름 | 구성 요소 | 역할 | |---|---| | 서비스 네트워크 | 논리적 그룹과 연결 관계, 선택적으로 IAM 인가 경계 제공 | | 서비스 | 고유 DNS 이름을 가진 애플리케이션 엔드포인트 | | 리스너와 규칙 | **서비스**에 속하며 동작과 대상 그룹을 선택 | | 대상 그룹 | 등록한 인스턴스, IP, Lambda, ALB 대상. 대상 유형별 동작이 다름 | | VPC 연결 | 보안 제어를 충족하는 연결 VPC의 클라이언트에 접근 경로 제공 | | 서비스 네트워크 VPC 엔드포인트 | 지원되는 전이 네트워크/온프레미스 경로를 포함한 PrivateLink 기반 접근 | | 리소스 구성 / 리소스 게이트웨이 | TCP·데이터베이스 리소스 등을 위한 별도 접근 모델 | ![두 AWS 계정의 세 VPC가 서비스 네트워크와 연결되며, 각 서비스는 대상 그룹을 통해 EC2, EKS, Lambda 워크로드에 연결됩니다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-02-vpc-lattice-1.png) [인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-02-vpc-lattice-1.html) 그림은 하나의 라우터 프로세스가 아니라 논리적 연결 관계를 나타냅니다. 실제 접근에는 네트워크 도달성과 해당 정책도 필요합니다. 요청은 **서비스의** DNS 이름을 해석하고 리스너에 도착한 뒤, 적용되는 인가 검사를 통과하면 리스너 규칙에 따라 대상으로 전달됩니다. 대상 그룹은 목적지를 정의하는 리소스이며 또 하나의 애플리케이션 홉은 아닙니다. 실제 도메인은 `get-service --query dnsEntry` 또는 컨트롤러의 Route 어노테이션에서 조회하세요. 서비스 이름과 서비스 네트워크 ID로 직접 조합하지 마세요. 할당 이름에는 서비스별 식별자가 포함되며, 서비스를 재생성하면 달라질 수 있습니다. ### 보안 모델 네트워크 접근, IAM 인가와 암호화는 별도 제어입니다. `AWS_IAM`은 지원되는 서명 요청과 적절한 정책을 요구합니다. `NONE`은 해당 계층의 IAM 인증을 비활성화할 뿐 다른 계층의 IAM 정책, 보안 그룹이나 애플리케이션 인가를 우회하지 않습니다. HTTPS는 클라이언트와 Lattice 사이를 보호합니다. 백엔드 TLS를 명시적으로 설정하지 않으면 HTTP 백엔드 구간은 평문입니다. ## EKS와 VPC Lattice 통합 AWS Gateway API Controller는 Kubernetes 리소스를 VPC Lattice 리소스로 조정합니다. | Kubernetes 리소스 | Lattice에서의 의미 | |---|---| | GatewayClass | `application-networking.k8s.aws/gateway-api-controller` 선택 | | Gateway | 네임스페이스를 제외한 **Gateway 이름**으로 서비스 네트워크 참조 | | HTTPRoute / GRPCRoute | 고유 도메인과 리스너·라우팅 구성을 가진 서비스 생성 | | 백엔드 Service와 엔드포인트 | 대상 그룹 및 등록할 파드 엔드포인트 정의 | | TargetGroupPolicy | 대상 그룹 프로토콜과 상태 검사 구성 | | IAMAuthPolicy | Gateway의 네트워크 또는 Route의 서비스에 인증 정책 연결 | | AccessLogPolicy | 대상 리소스의 액세스 로그 목적지 구성 | Kubernetes 네임스페이스가 달라도 이름이 같은 Gateway는 같은 서비스 네트워크를 참조할 수 있습니다. Gateway만으로 네트워크나 공통 인그레스 IP가 생성되지는 않습니다. 네트워크를 외부에서 관리하거나, 단순한 구성에서는 컨트롤러의 `defaultServiceNetwork` 옵션을 사용하거나, 컨트롤러의 ServiceNetwork CRD로 관리할 수 있습니다. 클라우드 리소스마다 관리 주체를 하나로 정하세요. 아래 예제는 네트워크와 VPC 연결을 외부에서 관리합니다. `defaultServiceNetwork`는 설정하지 않고, 해당 연결에 VpcAssociationPolicy도 적용하지 않습니다. CRD 기반 모델을 선택한다면 네트워크, VPC 연결, 인가를 각각 별도 리소스로 관리하며 같은 리소스를 CloudFormation에서도 관리하지 마세요. ## 설치 및 구성 ### 사전 요구사항 컨트롤러 v2.1 업그레이드 가이드는 **Kubernetes 1.31 이상**, **Gateway API 1.5 이상**을 요구합니다. 이 예제는 v2.1이 사용하는 **1.5.0**으로 고정합니다. 이 최소 버전은 EKS 지원 매트릭스도, 이후 모든 Gateway API 릴리스와의 호환성 증명도 아닙니다. 변경 전 EKS 버전 수명주기와 Gateway API CRD를 공유하는 모든 컨트롤러를 확인하세요. 특히 Gateway API 1.5의 TLSRoute 저장/API 버전 전환 후에는 v2.0 컨트롤러가 실패할 수 있습니다. 지원되는 EKS 클러스터, 버전이 맞는 `kubectl`, Helm, AWS CLI v2, 필요한 리소스를 구성할 운영자 권한을 준비합니다. 백엔드 예제는 VPC Lattice에서 IP로 도달 가능한 Linux 파드를 가정합니다. 클러스터의 CNI, 서브넷 용량, 엔드포인트 준비 상태, DNS와 네트워크 정책을 확인하세요. ```bash export AWS_REGION=us-west-2 export CLUSTER_NAME=my-cluster export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)" export VPC_ID="$(aws eks describe-cluster --name "$CLUSTER_NAME" \ --query 'cluster.resourcesVpcConfig.vpcId' --output text)" export NETWORK_NAME=my-network export ASSOCIATION_SG_ID=sg-0123456789abcdef0 kubectl config current-context kubectl version ``` 예시 보안 그룹 ID를 교체하세요. VPC 연결의 보안 그룹은 **허용된 클라이언트**의 TCP 443 접근을 허용해야 합니다. 백엔드 파드/노드 보안 그룹은 실제 백엔드·상태 검사 포트(여기서는 TCP 8080)에 대해 해당 Lattice 관리형 접두사 목록의 접근을 허용해야 합니다. 모든 노드가 EKS 클러스터 보안 그룹을 사용한다고 가정하지 말고 실제 파드 ENI나 노드 ENI의 그룹을 확인하세요. EKS 제어 플레인에서 컨트롤러 웹훅의 필요한 포트로도 접근할 수 있어야 합니다. 모든 포트를 인터넷 전체에 열지 마세요. ### IAM 역할 구성 **컨트롤러 역할**은 클라우드 리소스를 관리합니다. **호출자 역할**은 애플리케이션 요청에 서명하고 `vpc-lattice-svcs:Invoke` 권한을 사용합니다. 두 역할을 구분하세요. 지원되는 노드에서는 EKS Pod Identity를 사용하거나 IRSA를 사용할 수 있습니다. 아래 IRSA 예제는 클러스터의 IAM OIDC 공급자가 이미 존재한다고 가정하고 전용 서비스 계정을 만듭니다. Pod Identity를 선택하면 현재 EKS 애드온과 동일한 네임스페이스/서비스 계정의 연결, 적절한 신뢰 정책을 구성하세요. 같은 예제에서 IRSA 어노테이션에도 동시에 의존하지 마세요. 릴리스의 권장 컨트롤러 정책에는 넓은 `vpc-lattice:*`, 로깅 및 태그 권한이 포함됩니다. 이를 **최소 권한 정책으로 간주하지 말고** upstream 출발점으로 사용하세요. 리소스 범위와 활성화 기능을 검토하고 서비스 연결 역할의 제한 조건을 유지한 검토본을 저장한 뒤 정책을 생성합니다. 이후 실행에서는 정책을 중복 생성하지 말고 기존 검토된 정책 ARN을 재사용하세요. ```bash curl --fail --location --output controller-policy-upstream.json \ https://raw.githubusercontent.com/aws/aws-application-networking-k8s/v2.1.3/files/controller-installation/recommended-inline-policy.json # Use the policy reviewed for this account and the enabled controller features. export REVIEWED_POLICY_FILE=controller-policy-reviewed.json test -s "$REVIEWED_POLICY_FILE" export CONTROLLER_POLICY_ARN="$(aws iam create-policy \ --policy-name VPCLatticeControllerPolicy \ --policy-document "file://$REVIEWED_POLICY_FILE" \ --query Policy.Arn --output text)" # Prerequisite: this cluster's IAM OIDC provider already exists. eksctl create iamserviceaccount \ --cluster "$CLUSTER_NAME" --region "$AWS_REGION" \ --namespace aws-application-networking-system \ --name gateway-api-controller \ --attach-policy-arn "$CONTROLLER_POLICY_ARN" \ --approve ``` 기존 서비스 계정에는 소유권과 역할의 의도적인 마이그레이션이 필요합니다. 예제는 이를 자동으로 덮어쓰지 않습니다. ### 릴리스된 컨트롤러 설치 ```bash curl --fail --location --output gateway-api-v1.5.0.yaml \ https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml # Inspect changes first if any Gateway API controller is already installed. kubectl apply --server-side -f gateway-api-v1.5.0.yaml helm pull oci://public.ecr.aws/aws-application-networking-k8s/aws-gateway-controller-chart \ --version v2.1.3 helm show crds ./aws-gateway-controller-chart-v2.1.3.tgz > lattice-crds.yaml kubectl apply --server-side -f lattice-crds.yaml helm install gateway-api-controller ./aws-gateway-controller-chart-v2.1.3.tgz \ --namespace aws-application-networking-system --create-namespace \ --set serviceAccount.create=false \ --set serviceAccount.name=gateway-api-controller \ --set-string awsRegion="$AWS_REGION" \ --set-string awsAccountId="$AWS_ACCOUNT_ID" \ --set-string clusterVpcId="$VPC_ID" \ --set-string clusterName="$CLUSTER_NAME" \ --wait --timeout 5m kubectl -n aws-application-networking-system get pods kubectl -n aws-application-networking-system logs \ -l control-plane=gateway-api-controller -c manager --tail=100 ``` 기존 Helm 릴리스는 저장된 values와 검토된 `helm upgrade` 계획으로 변경하세요. Helm은 `crds/`의 CRD를 자동 업그레이드하지 않으므로 변경 내용을 별도로 검토해야 합니다. 업그레이드를 통과시키기 위해 공유 Gateway API CRD나 admission 정책을 삭제하지 마세요. 매니페스트 방식이 필요하면 **동일한 차트**를 같은 values와 서비스 계정 설정으로 `helm template --include-crds` 렌더링하고 결과를 검토·적용하세요. 릴리스의 RBAC, EndpointSlice 감시, 리더 선출 권한과 웹훅 구성이 유지됩니다. 오래된 수동 v1.0 Deployment는 사용하지 마세요. 차트는 인증서를 직접 제공하거나 cert-manager 옵션을 쓰지 않으면 웹훅 인증서를 생성합니다. 업그레이드 시 Secret과 CA bundle 중 하나만 따로 재생성하지 말고 일치 상태를 유지해야 합니다. ### 서비스 네트워크 생성 동일한 네트워크에는 **CLI와 CloudFormation 중 하나**를 선택하세요. CLI 예제는 `AWS_IAM` 네트워크를 만듭니다. 적용 가능한 Allow 정책이 설치되고 전파되기 전에는 요청이 거부됩니다. 다음을 `api-auth-policy.json`으로 저장하고 계정과 호출자 역할을 교체하세요. 네트워크 정책은 이 데모의 `/api`와 하위 경로만 허용합니다. 운영 네트워크에는 실제 서비스와 호출자 범위에 맞는 검토된 정책이 필요합니다. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:role/MyAppRole" }, "Action": "vpc-lattice-svcs:Invoke", "Resource": "*", "Condition": { "StringLike": { "vpc-lattice-svcs:RequestPath": [ "/api", "/api/*" ] } } } ] } ``` ```bash aws vpc-lattice create-service-network --name "$NETWORK_NAME" \ --auth-type AWS_IAM > service-network.json export SERVICE_NETWORK_ID="$(python3 -c \ 'import json; print(json.load(open("service-network.json"))["id"])')" export SERVICE_NETWORK_ARN="$(python3 -c \ 'import json; print(json.load(open("service-network.json"))["arn"])')" aws vpc-lattice create-service-network-vpc-association \ --service-network-identifier "$SERVICE_NETWORK_ID" \ --vpc-identifier "$VPC_ID" --security-group-ids "$ASSOCIATION_SG_ID" # Save the reviewed policy below as api-auth-policy.json, then compact it. python3 -c 'import json; print(json.dumps(json.load(open("api-auth-policy.json")),separators=(",",":")))' \ > api-auth-policy.compact.json aws vpc-lattice put-auth-policy --resource-identifier "$SERVICE_NETWORK_ID" \ --policy file://api-auth-policy.compact.json aws vpc-lattice get-service-network --service-network-identifier "$SERVICE_NETWORK_ID" aws vpc-lattice get-auth-policy --resource-identifier "$SERVICE_NETWORK_ID" aws vpc-lattice list-service-network-vpc-associations \ --service-network-identifier "$SERVICE_NETWORK_ID" ``` Route를 노출하기 전에 연결이 `ACTIVE`이고, 네트워크의 `authType`이 여전히 `AWS_IAM`이며, `get-auth-policy` 결과가 의도한 정책인지 확인하세요. 정책 전파에는 수 분이 걸릴 수 있습니다. 같은 **네트워크와 연결**을 정의하는 CloudFormation 템플릿은 다음과 같습니다. ```yaml AWSTemplateFormatVersion: '2010-09-09' Description: VPC Lattice service network and client VPC association Parameters: NetworkName: Type: String Default: my-network MinLength: 3 MaxLength: 63 AllowedPattern: '^[a-z0-9]+(-[a-z0-9]+)*$' Description: Must match the Kubernetes Gateway name VpcId: Type: AWS::EC2::VPC::Id Description: VPC containing the intended clients AssociationSecurityGroupIds: Type: List Description: Existing security groups allowing approved clients on listener ports Resources: ServiceNetwork: Type: AWS::VpcLattice::ServiceNetwork Properties: Name: {Ref: NetworkName} AuthType: AWS_IAM ClientAssociation: Type: AWS::VpcLattice::ServiceNetworkVpcAssociation Properties: ServiceNetworkIdentifier: {Ref: ServiceNetwork} VpcIdentifier: {Ref: VpcId} SecurityGroupIds: {Ref: AssociationSecurityGroupIds} Outputs: ServiceNetworkArn: Description: ARN used for authorization and sharing Value: {Fn::GetAtt: [ServiceNetwork, Arn]} ServiceNetworkId: Description: ID used with VPC Lattice API operations Value: {Fn::GetAtt: [ServiceNetwork, Id]} ``` 이 템플릿은 인증 정책을 연결하지 않습니다. 같은 관리 모델에 인증 정책 리소스를 추가하거나, 요청을 시험하기 전에 검토된 네트워크 정책을 명시적으로 적용하세요. 네트워크 ID/ARN은 스택 출력에서 얻습니다. 배포 전 템플릿 검증과 변경 세트 확인이 필요하며, 이 예제는 VPC나 보안 그룹을 만들지 않습니다. ### Gateway와 애플리케이션 다음을 `gateway.yaml`로 저장하고 적용합니다. Gateway 이름은 앞서 생성한 `my-network`와 같아야 합니다. ```yaml apiVersion: v1 kind: Namespace metadata: name: lattice-demo --- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: amazon-vpc-lattice spec: controllerName: application-networking.k8s.aws/gateway-api-controller --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: my-network namespace: lattice-demo spec: gatewayClassName: amazon-vpc-lattice listeners: - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - name: unused ``` `certificateRefs: [{name: unused}]`는 이 컨트롤러의 공식 예제 구성입니다. Gateway API의 TLS 구성을 충족하지만 이 컨트롤러는 해당 Kubernetes TLS Secret을 읽지 않습니다. 사용자 지정 호스트 이름이 없으면 Lattice가 생성 도메인용 인증서를 제공합니다. 이는 **컨트롤러별 동작**이며 다른 구현에 그대로 적용할 인증서 관리법은 아닙니다. 다음을 `stable.yaml`로 저장하세요. NGINX가 실제로 8080에서 수신하고 `/health`를 제공하도록 구성합니다. `containerPort` 선언만으로 이 동작이 만들어지지는 않습니다. ```yaml apiVersion: v1 kind: ConfigMap metadata: name: service-stable namespace: lattice-demo data: nginx.conf: | worker_processes 1; pid /tmp/nginx.pid; error_log stderr notice; events { worker_connections 1024; } http { access_log /dev/stdout; default_type application/json; client_body_temp_path /tmp/client_temp; proxy_temp_path /tmp/proxy_temp; fastcgi_temp_path /tmp/fastcgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; scgi_temp_path /tmp/scgi_temp; server { listen 8080; location = /health { return 200 '{"status":"ok"}\n'; } location = /api { return 200 '{"version":"stable"}\n'; } location /api/ { return 200 '{"version":"stable"}\n'; } location / { return 404 '{"error":"not found"}\n'; } } } --- apiVersion: apps/v1 kind: Deployment metadata: name: service-stable namespace: lattice-demo spec: replicas: 2 selector: matchLabels: &id001 app: lattice-demo version: stable template: metadata: labels: *id001 spec: automountServiceAccountToken: false securityContext: runAsNonRoot: true runAsUser: 101 runAsGroup: 101 fsGroup: 101 seccompProfile: type: RuntimeDefault containers: - name: app image: nginx:1.30.4-alpine@sha256:dc5069ad14f19660b141b21236140b91656bf89bbc3e2417c70ae650cd66104c command: - nginx args: - -c - /etc/lattice/nginx.conf - -g - daemon off; ports: - name: http containerPort: 8080 readinessProbe: httpGet: path: /health port: http periodSeconds: 5 resources: requests: cpu: 50m memory: 32Mi limits: cpu: 250m memory: 64Mi securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: config mountPath: /etc/lattice readOnly: true - name: tmp mountPath: /tmp volumes: - name: config configMap: name: service-stable - name: tmp emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: service-stable namespace: lattice-demo spec: selector: app: lattice-demo version: stable ports: - name: http port: 8080 targetPort: http ``` 같은 세 리소스로 `canary.yaml`을 만들되 모든 `service-stable` 이름을 `service-canary`로, selector/template의 `version: stable`을 모두 `version: canary`로, JSON 응답 값 `"stable"`을 `"canary"`로 바꾸세요. `app: lattice-demo`, 포트와 상태 검사 경로는 유지합니다. 두 파일을 `lattice-demo`에 적용하세요. 고정된 이미지는 Linux AMD64와 ARM64 변형을 제공합니다. 리소스 요청과 복제본 수는 실습 설정이며 운영 환경의 측정된 적정값이 아닙니다. 아래 TargetGroupPolicy를 저장·적용하고, `service-canary`를 대상으로 하는 같은 구성의 `canary-health` 정책도 만드세요. ```yaml apiVersion: application-networking.k8s.aws/v1alpha1 kind: TargetGroupPolicy metadata: name: stable-health namespace: lattice-demo spec: targetRef: group: '' kind: Service name: service-stable protocol: HTTP protocolVersion: HTTP1 healthCheck: enabled: true protocol: HTTP protocolVersion: HTTP1 port: 8080 path: /health intervalSeconds: 30 timeoutSeconds: 5 healthyThresholdCount: 2 unhealthyThresholdCount: 2 statusMatch: '200' ``` CRD는 `intervalSeconds`, `timeoutSeconds`, `statusMatch`를 사용합니다. 뒤에서 설명하는 AWS CLI 필드 이름과 다릅니다. 프로토콜/버전을 변경하면 대상 그룹이 교체될 수 있고, 정책 삭제 시 HTTP/HTTP1 기본값을 포함한 설정으로 돌아갑니다. ## 서비스 관리 ### HTTPRoute로 서비스 생성 다음을 `api-route.yaml`, 그 아래 IAMAuthPolicy를 `api-iam.yaml`로 저장하세요. 애플리케이션과 상태 검사 정책을 적용한 다음 Route와 인증 정책을 적용합니다. 조정 과정에서 Route의 서비스를 만들고 보호하는 동안 네트워크의 `AWS_IAM` 정책을 유지하세요. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api namespace: lattice-demo spec: parentRefs: - name: my-network sectionName: https rules: - matches: - path: type: PathPrefix value: /api backendRefs: - name: service-stable port: 8080 weight: 90 - name: service-canary port: 8080 weight: 10 ``` ```yaml apiVersion: application-networking.k8s.aws/v1alpha1 kind: IAMAuthPolicy metadata: name: api-caller namespace: lattice-demo spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: api policy: '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789012:role/MyAppRole"},"Action":"vpc-lattice-svcs:Invoke","Resource":"*","Condition":{"StringLike":{"vpc-lattice-svcs:RequestPath":["/api","/api/*"]}}}]}' ``` `spec.policy`는 JSON **문자열**입니다. 이 CRD가 대상 서비스의 `AWS_IAM`을 활성화합니다. auth-type 어노테이션이나 정책을 담은 ConfigMap으로 대체할 수 없습니다. `Gateway`를 대상으로 하면 네트워크 정책을 관리하므로, 이 예제의 외부 관리 네트워크 정책과 충돌하지 않게 해야 합니다. `Accepted` / `ResolvedRefs`, 정책 상태, AWS 리소스 상태와 백엔드 준비 상태를 확인하세요. `kubectl apply` 성공만으로 클라우드 조정이나 로그 전달 성공이 증명되지는 않습니다. ```bash kubectl -n lattice-demo get gateway my-network -o yaml kubectl -n lattice-demo get httproute api -o yaml kubectl -n lattice-demo get iamauthpolicy api-caller -o yaml kubectl -n lattice-demo get endpointslices \ -l kubernetes.io/service-name=service-stable kubectl -n lattice-demo rollout status deployment/service-stable --timeout=120s kubectl -n lattice-demo rollout status deployment/service-canary --timeout=120s export SERVICE_DNS="$(kubectl -n lattice-demo get httproute api \ -o jsonpath='{.metadata.annotations.application-networking\.k8s\.aws/lattice-assigned-domain-name}')" test -n "$SERVICE_DNS" # A caller inside the associated VPC, with MyAppRole credentials, runs: lattice-client/bin/python lattice_get.py --region "$AWS_REGION" "https://${SERVICE_DNS}/api" ``` 마지막 명령 전에 다음 절의 서명 클라이언트를 준비하세요. 허용된 네트워크 위치에서 **호출자 역할** 자격 증명으로 실행해야 합니다. 워크스테이션에도 AWS 자격 증명뿐 아니라 적절한 네트워크 경로가 필요합니다. ### 서명된 HTTPS 클라이언트 다음을 `lattice_get.py`로 저장하세요. 기본 AWS 자격 증명 공급자 체인을 사용하고 요청별로 자격 증명을 고정한 뒤, **`vpc-lattice-svcs`** 서비스 이름으로 서명합니다. VPC Lattice가 요구하는 **`UNSIGNED-PAYLOAD`**를 지정하며 TLS를 검증합니다. 이전 URL의 서명으로 리다이렉트를 따라가거나 요청을 자동 재시도하지 않습니다. ```python import argparse import ssl import sys from urllib.error import HTTPError, URLError from urllib.parse import urlsplit from urllib.request import HTTPRedirectHandler, HTTPSHandler, Request, build_opener from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest from botocore.exceptions import BotoCoreError from botocore.session import Session class NoRedirect(HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None def signed_request(url: str, region: str, credentials) -> Request: parts = urlsplit(url) if (parts.scheme != "https" or not parts.hostname or parts.username or parts.password or parts.fragment): raise ValueError("Use an HTTPS URL without user info or a fragment") request = AWSRequest(method="GET", url=url, headers={ "x-amz-content-sha256": "UNSIGNED-PAYLOAD", }) request.context["payload_signing_enabled"] = False SigV4Auth(credentials, "vpc-lattice-svcs", region).add_auth(request) return Request(url, method="GET", headers=dict(request.headers.items())) def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--region", required=True) parser.add_argument("url") args = parser.parse_args() try: provider = Session().get_credentials() if provider is None: raise ValueError("No AWS credentials available") request = signed_request(args.url, args.region, provider.get_frozen_credentials()) opener = build_opener(NoRedirect(), HTTPSHandler(context=ssl.create_default_context())) with opener.open(request, timeout=10) as response: print(response.status) print(response.read(1048576).decode("utf-8", errors="replace")) return 0 except HTTPError as exc: print(f"HTTP {exc.code}; check the policy and access logs", file=sys.stderr) except (URLError, BotoCoreError, ValueError) as exc: print(f"Request failed: {type(exc).__name__}", file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main()) ``` ```bash python3.12 -m venv lattice-client lattice-client/bin/python -m pip install 'botocore==1.43.93' lattice-client/bin/python lattice_get.py --region "$AWS_REGION" "https://${SERVICE_DNS}/api" ``` GET 전용 예제는 Python 3.12와 botocore 1.43.93으로 확인했습니다. 워크로드는 구성된 Pod Identity 또는 IRSA 자격 증명을 사용해야 합니다. 정적 자격 증명이나 서명 헤더를 매니페스트, 로그, 지원 티켓에 복사하지 마세요. VPC Lattice는 SigV4A도 지원하지만 이 예제는 리전 기반 SigV4를 사용합니다. ### AWS API로 직접 관리 다음은 독립적으로 관리하는 리소스의 **대안**입니다. 8080에서 HTTP와 `/health`를 제공하는 도달 가능한 안정적인 백엔드 IP가 필요합니다. 임시 파드 IP는 교체를 추적할 컨트롤러가 필요합니다. HTTPRoute가 소유한 서비스를 수동 변경한 뒤 컨트롤러가 그 변경을 보존할 것으로 기대하지 마세요. ```bash # Separate API-managed example; do not use for controller-managed resources. export TARGET_IP=10.0.1.25 export TARGET_GROUP_ID="$(aws vpc-lattice create-target-group \ --name api-manual --type IP \ --config "{\"port\":8080,\"protocol\":\"HTTP\",\"protocolVersion\":\"HTTP1\",\"vpcIdentifier\":\"${VPC_ID}\"}" \ --query id --output text)" aws vpc-lattice register-targets --target-group-identifier "$TARGET_GROUP_ID" \ --targets "id=$TARGET_IP,port=8080" export SERVICE_ID="$(aws vpc-lattice create-service \ --name api-manual --auth-type AWS_IAM --query id --output text)" aws vpc-lattice put-auth-policy --resource-identifier "$SERVICE_ID" \ --policy file://api-auth-policy.compact.json export LISTENER_ID="$(aws vpc-lattice create-listener \ --service-identifier "$SERVICE_ID" --name https --protocol HTTPS --port 443 \ --default-action "{\"forward\":{\"targetGroups\":[{\"targetGroupIdentifier\":\"${TARGET_GROUP_ID}\",\"weight\":1}]}}" \ --query id --output text)" aws vpc-lattice create-service-network-service-association \ --service-identifier "$SERVICE_ID" --service-network-identifier "$SERVICE_NETWORK_ID" aws vpc-lattice list-targets --target-group-identifier "$TARGET_GROUP_ID" aws vpc-lattice get-service --service-identifier "$SERVICE_ID" --query dnsEntry ``` 대상 상태와 연결 활성화를 확인한 뒤 조회한 HTTPS 도메인을 호출하세요. 이 예제는 사용자 지정 도메인이 아니라 생성 도메인의 AWS 관리형 인증서를 사용합니다. ### 서비스 변경과 삭제 Kubernetes 소유 리소스는 Route, 백엔드 워크로드 또는 정책 매니페스트를 변경하고 조정 결과를 확인합니다. API 소유 리소스는 해당 update API와 결과 상태를 확인하세요. 계정의 첫 서비스를 임의로 선택하지 말고 응답의 리소스 ID를 저장합니다. 삭제 전 모든 소비자, 네트워크 연결, 리스너/규칙, 대상 그룹 참조와 소유권을 확인하세요. 특정 Route/서비스 연결 및 서비스 리소스를 의존 순서대로 제거한 후 사용하지 않는 대상 그룹을 정리합니다. 공유 Gateway/네트워크는 다른 네임스페이스나 계정에 영향을 줄 수 있습니다. finalizer와 클라우드 정리가 끝날 때까지 컨트롤러를 유지하고 일괄 삭제는 사용하지 마세요. **IAMAuthPolicy를 삭제하면 대상 IAM 인증이 `NONE`으로 비활성화된 후 정책이 분리됩니다.** 접근을 거부하거나 인가를 안전하게 되돌리는 방법이 아닙니다. 서비스를 제거하는 동안 제한적인 정책을 유지하고 남은 네트워크/서비스 제어를 확인하세요. ## 라우팅 및 트래픽 관리 ### 경로와 헤더 매칭 앞선 Route는 `/api`와 그 하위 경로를 매칭합니다. 명시적인 헤더 기반 카나리 규칙을 추가하려면 **같은** HTTPRoute를 다음으로 교체하세요. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api namespace: lattice-demo spec: parentRefs: - name: my-network sectionName: https rules: - matches: - path: type: PathPrefix value: /api headers: - name: x-version value: canary backendRefs: - name: service-canary port: 8080 weight: 1 - matches: - path: type: PathPrefix value: /api backendRefs: - name: service-stable port: 8080 weight: 90 - name: service-canary port: 8080 weight: 10 ``` 컨트롤러는 경로를 대소문자 구분 없이 매칭하며, 규칙당 메서드 매치 하나, 최대 다섯 헤더 매치를 지원하고 쿼리 매개변수 매칭은 지원하지 않는다고 문서화합니다. 모든 Gateway API 필터나 매치가 구현되어 있다고 가정하지 마세요. HTTPRoute를 별도로 만들면 첫 서비스에 규칙이 자동 추가되는 것이 아니라 다른 Lattice 서비스와 도메인이 생깁니다. ### 가중치 라우팅 `backendRefs.weight: 90`과 `10`은 Gateway API의 기본 구성입니다. 별도 가중치 어노테이션은 필요하지 않습니다. 상대적 분배 비율을 뜻하며 열 번의 요청에서 정확한 비율을 보장하지 않습니다. 카나리 비중을 늘리기 전 두 버전의 엔드포인트, 상태, 오류와 지연 시간을 적절한 표본으로 확인하세요. 독립적으로 관리하는 AWS 리소스에서는 다음과 같이 구성합니다. ```bash # TG_STABLE and TG_CANARY are existing target groups managed by this API workflow. aws vpc-lattice create-rule --service-identifier "$SERVICE_ID" \ --listener-identifier "$LISTENER_ID" --name api-canary --priority 10 \ --match '{"httpMatch":{"pathMatch":{"match":{"prefix":"/api"},"caseSensitive":false}}}' \ --action "{\"forward\":{\"targetGroups\":[{\"targetGroupIdentifier\":\"${TG_STABLE}\",\"weight\":90},{\"targetGroupIdentifier\":\"${TG_CANARY}\",\"weight\":10}]}}" ``` CLI의 접두사 매칭은 문자열 접두사이므로 Kubernetes `PathPrefix`와 경계 동작을 별도로 검토하세요. 라우팅 매치는 인가 경계가 아닙니다. 경로 라우팅 시험만으로 IAM 정책이 모든 정규화·인코딩 경로 변형을 보호한다고 판단하지 마세요. ### 상태 검사 Kubernetes 예제는 TargetGroupPolicy를 사용합니다. 같은 API 변경은 다음과 같습니다. ```bash aws vpc-lattice update-target-group --target-group-identifier "$TARGET_GROUP_ID" \ --health-check '{"enabled":true,"protocol":"HTTP","protocolVersion":"HTTP1","port":8080,"path":"/health","healthCheckIntervalSeconds":30,"healthCheckTimeoutSeconds":5,"healthyThresholdCount":2,"unhealthyThresholdCount":2,"matcher":{"httpCode":"200"}}' ``` 상태 검사는 임계값에 따라 준비 상태를 평가하며 가용성이나 무중단을 보장하지 않습니다. HTTP1 대상 그룹은 기본 활성화지만 HTTP2는 명시적인 검토가 필요합니다. gRPC 대상의 상태 검사는 HTTP1/HTTP2를 사용하며 Lambda/ALB 대상 유형은 동작이 다릅니다. 파드 예제를 모든 대상에 적용하지 말고 현재 대상 유형별 문서를 확인하세요. ## 보안 및 인증 ### 인증 정책과 호출자 권한 `put-auth-policy` / `get-auth-policy`는 호출 인가를 관리합니다. `put-resource-policy`는 별도의 관리·공유 API입니다. 호출자에게는 **`vpc-lattice-svcs:Invoke`** 동작을 사용하세요. 네트워크와 서비스가 모두 `AWS_IAM`이면 호출자의 자격 증명 기반 정책과 **두 인증 정책 모두** 접근을 허용해야 합니다. 명시적 Deny가 우선합니다. 한 리소스의 `NONE`은 다른 리소스의 IAM 요구를 취소하지 않습니다. Kubernetes ClusterIP/Pod IP 직접 통신은 Lattice 인증을 우회하므로 적절한 네트워크·애플리케이션 제어로 해당 경로도 보호하세요. `StringEquals`는 `/api/*`를 와일드카드로 해석하지 않습니다. 예제는 `StringLike`와 함께 `/api`, `/api/*`를 모두 포함합니다. IAM 조건 매칭과 애플리케이션의 경로 정규화는 컨트롤러 라우팅과 다를 수 있습니다. 관리 기능에는 관리 역할만 허용하는 전용 서비스를 우선 고려하고 애플리케이션 인가도 유지하세요. 광범위한 일반 Allow를 추가한 뒤 경로 와일드카드가 모든 별칭을 보호한다고 가정하지 마세요. ### 계정 간 접근 RAM 공유는 공유 리소스와의 연결을 허용할 뿐 애플리케이션 호출 권한 자체를 주지 않습니다. 네트워크/서비스 인증 정책, 호출자 권한, 연결 보안 그룹과 네트워크 경로가 모두 요청을 허용해야 합니다. ```bash # Owner account: choose a verified account ID or the actual Organizations ARN. export CONSUMER_ACCOUNT_ID=111122223333 aws ram create-resource-share --name lattice-network-share \ --resource-arns "$SERVICE_NETWORK_ARN" --principals "$CONSUMER_ACCOUNT_ID" # Consumer account: inspect invitations only when the sharing mode requires one. aws ram get-resource-share-invitations # After verifying the owner, resources, and intended permissions: aws ram accept-resource-share-invitation \ --resource-share-invitation-arn "$VERIFIED_INVITATION_ARN" # Run with consumer credentials and that account's VPC/security group values. aws vpc-lattice create-service-network-vpc-association \ --service-network-identifier "$SERVICE_NETWORK_ARN" \ --vpc-identifier "$CONSUMER_VPC_ID" \ --security-group-ids "$CONSUMER_ASSOCIATION_SG_ID" ``` Organizations 공유가 활성화된 조직 내부 소비자는 초대 없이 접근 권한을 받습니다. 그 외 지원되는 공유 구성에서는 초대 수락이 필요합니다. 조직이나 OU에 공유할 때는 멤버 계정 ID로 조합하지 말고 관리 계정 식별자를 포함한 **Organizations의 실제 ARN**을 사용하세요. 서비스, 네트워크와 리소스 구성은 공유할 수 있지만 개별 IAM 역할을 RAM 소비자로 지정할 수는 없습니다. 공유 중단은 새 연결을 막지만 **기존 연결을 제거하지 않습니다**. 접근 철회 시 연결도 명시적으로 검토하세요. ### TLS와 사용자 지정 도메인 예제 Gateway는 HTTPS만 노출합니다. 사용자 지정 호스트 이름은 서비스 생성 시 설정하고 일치하는 ACM 인증서와 실제 할당 도메인을 가리키는 DNS를 구성하세요. 서비스당 사용자 지정 도메인은 하나이며 생성 후 변경할 수 없습니다. 컨트롤러에서는 HTTPRoute의 `spec.hostnames`, Gateway 리스너의 `tls.options["application-networking.k8s.aws/certificate-arn"]`을 설정하거나 문서화된 ACM 탐색을 사용합니다. 어노테이션에 개인 키를 넣지 마세요. ExternalDNS 자동화에는 컨트롤러, 권한과 DNSEndpoint CRD도 필요합니다. 호스트 이름을 설정했다고 DNS 레코드 존재가 증명되지는 않습니다. ```bash # For an API-managed service created with the required custom domain name: aws vpc-lattice update-service --service-identifier "$SERVICE_ID" \ --certificate-arn "$ACM_CERTIFICATE_ARN" # Create an HTTPS listener separately if the service does not already have one. # create-listener uses --protocol HTTPS; there is no --tls mode=STRICT option. ``` 클라이언트 HTTPS와 백엔드 TLS는 별도입니다. 백엔드 TargetGroupPolicy의 `protocol: HTTPS`에는 실제 TLS 백엔드와 호환되는 HTTPS 상태 검사도 필요합니다. VPC Lattice는 **백엔드 인증서를 검증하지 않습니다**. 연결은 암호화하지만 인증서 기반 백엔드 신원 인증은 제공하지 않습니다. TLS 통과가 목적이라면 별도의 TLSRoute/TLS passthrough 모델과 그 기능 제한을 검토하세요. ## 모니터링 및 로깅 ### CloudWatch 지표, 대시보드와 경보 서비스 지표는 **`AWS/VpcLattice`** 네임스페이스를 사용합니다. | 지표 | 의미 / 통계 | |---|---| | `TotalRequestCount` | 요청 수, `Sum` | | `HTTPCode_4XX_Count` | 4xx 응답, `Sum` | | `HTTPCode_5XX_Count` | 5xx 응답, `Sum` | | `RequestTime` | **밀리초** 단위 요청 시간, 평균 또는 적절한 백분위 | 서비스 지표의 차원은 `Service`이며 `AvailabilityZone`이 추가될 수 있습니다. 대상 그룹 지표는 `TargetGroup`을 사용합니다. `ServiceName=my-service` 같은 이름으로는 이 지표가 식별되지 않습니다. 실제 차원 값과 조합을 조회하세요. ```bash aws cloudwatch list-metrics --namespace AWS/VpcLattice \ --metric-name HTTPCode_5XX_Count --dimensions Name=Service > metrics.json python3 - <<'PY' import json for metric in json.load(open("metrics.json"))["Metrics"]: print(json.dumps(metric["Dimensions"])) PY ``` 트래픽으로 지표가 생성된 후 의도한 서비스의 **서비스 전체** 차원 배열을 선택하여 `service-dimensions.json`으로 저장합니다. 첫 결과를 임의로 선택하거나 AZ 지표와 전체 지표를 섞지 마세요. 관측할 서비스의 식별자인지 대조합니다. 다음 코드로 `dashboard.json`을 만드세요. ```python import json import os dimensions = json.load(open("service-dimensions.json")) if {d["Name"] for d in dimensions} != {"Service"}: raise ValueError("Select the service-wide metric, without AvailabilityZone") pairs = [item for d in dimensions for item in (d["Name"], d["Value"])] dashboard = {"widgets": [{ "type": "metric", "width": 12, "height": 6, "properties": { "title": "VPC Lattice requests and errors", "region": os.environ["AWS_REGION"], "period": 60, "stat": "Sum", "metrics": [["AWS/VpcLattice", name, *pairs] for name in ("TotalRequestCount", "HTTPCode_4XX_Count", "HTTPCode_5XX_Count")], }, }]} with open("dashboard.json", "w") as output: json.dump(dashboard, output) ``` ```bash aws cloudwatch put-dashboard --dashboard-name VPCLattice \ --dashboard-body file://dashboard.json aws cloudwatch put-metric-alarm --alarm-name LatticeApi5xx \ --namespace AWS/VpcLattice --metric-name HTTPCode_5XX_Count \ --dimensions file://service-dimensions.json \ --statistic Sum --period 60 --evaluation-periods 3 --datapoints-to-alarm 2 \ --threshold 5 --comparison-operator GreaterThanThreshold \ --treat-missing-data missing ``` 이 경보는 **3개 구간 중 2개에서 분당 5xx가 5건 초과**라는 뜻이며 오류율 5%가 아닙니다. 알림이 필요하면 검토된 경보 동작을 별도로 설정하세요. 누락 데이터 처리도 명시합니다. 트래픽이 시작되어야 지표가 발행되며 NoData를 조용히 정상으로 간주하면 안 됩니다. 대시보드와 경보 값은 예시이며 워크로드별 SLO가 아닙니다. ### 액세스 로깅 CloudWatch Logs의 기존 목적지를 사용하거나 보존 정책이 있는 전용 로그 그룹을 만듭니다. ```bash export LOG_GROUP=/aws/vendedlogs/vpc-lattice/api aws logs create-log-group --log-group-name "$LOG_GROUP" aws logs put-retention-policy --log-group-name "$LOG_GROUP" --retention-in-days 30 export LOG_DESTINATION_ARN="arn:aws:logs:${AWS_REGION}:${AWS_ACCOUNT_ID}:log-group:${LOG_GROUP}:*" # API-managed service only; for an HTTPRoute use AccessLogPolicy below instead. aws vpc-lattice create-access-log-subscription \ --resource-identifier "$SERVICE_ID" --destination-arn "$LOG_DESTINATION_ARN" ``` 설정 주체에는 문서화된 로그 전달 권한도 필요합니다. 필요한 권한이 있으면 AWS가 로그 리소스 정책을 생성·갱신할 수 있고, 없으면 사전 구성이 필요합니다. `delivery.logs.amazonaws.com` 권한과 소스 계정/소스 ARN 조건을 확인하세요. Kubernetes 관리 Route에는 충돌하는 CLI 구독을 만들지 말고 다음을 사용합니다. ```yaml apiVersion: application-networking.k8s.aws/v1alpha1 kind: AccessLogPolicy metadata: name: api-logs namespace: lattice-demo spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: api destinationArn: arn:aws:logs:us-west-2:123456789012:log-group:/aws/vendedlogs/vpc-lattice/api:* ``` ARN을 교체하고 정책 상태뿐 아니라 실제 전달 이벤트도 확인하세요. Gateway는 네트워크 로그, Route는 서비스 로그 대상으로 지정할 수 있습니다. 대상별로 지원되는 각 목적지 유형 하나씩을 사용할 수 있습니다. S3는 Block Public Access, 암호화, 보존/수명주기 규칙과 적절한 전달 권한이 검토된 목적지 버킷을 사용합니다. ```bash # Existing reviewed destination bucket; no policy is overwritten by this snippet. aws vpc-lattice create-access-log-subscription \ --resource-identifier "$SERVICE_ID" --destination-arn "$LOG_BUCKET_ARN" ``` S3 전달에는 `delivery.logs.amazonaws.com`의 문서화된 `s3:GetBucketAcl`, `s3:PutObject` 권한, 전달 접두사, `aws:SourceAccount`, `aws:SourceArn` 조건이 필요합니다. 기존 정책은 덮어쓰지 말고 병합해야 합니다. SSE-KMS는 지원되는 고객 관리형 키와 로그 전달용 키 정책이 필요합니다. `--destination-name`은 액세스 로그 구독의 매개변수가 아닙니다. ### 로그 분석과 추적 HTTP 서비스 액세스 로그에는 `sourceIpPort`, `requestMethod`, `requestPath`, `responseCode`, `durationMS`, `callerPrincipal`, `authDeniedReason` 등이 있습니다. 리소스/TCP 로그의 스키마는 다릅니다. ```bash END_TIME="$(python3 -c 'import time; print(int(time.time()))')" START_TIME="$((END_TIME - 3600))" QUERY_ID="$(aws logs start-query --log-group-name "$LOG_GROUP" \ --start-time "$START_TIME" --end-time "$END_TIME" \ --query-string 'fields @timestamp, sourceIpPort, requestMethod, requestPath, responseCode, durationMS, callerPrincipal, authDeniedReason | filter responseCode >= 400 | sort @timestamp desc | limit 100' \ --query queryId --output text)" aws logs get-query-results --query-id "$QUERY_ID" # Repeat get-query-results until Complete; Failed/Cancelled/Timeout are errors. ``` VPC Lattice에는 애플리케이션을 자동으로 X-Ray 계측하는 `update-service --tracing-config` 옵션이나 컨트롤러 어노테이션이 없습니다. OpenTelemetry/ADOT 또는 적절한 추적 SDK로 애플리케이션을 계측하고 추적 컨텍스트 전파, 내보내기와 샘플링을 구성하세요. 애플리케이션 추적을 액세스 로그·요청 ID와 연관시키되 클라이언트가 제공한 요청 ID를 인증된 신원으로 보지는 마세요. ## 모범 사례 - **설계와 소유권:** 명확한 네트워크/서비스 이름과 환경 경계를 사용합니다. 네임스페이스 간 같은 Gateway 이름, 공유 네트워크 소비자, 할당량과 각 정책·연결의 소유권을 고려하세요. - **배포:** 안정 버전과 카나리 백엔드를 각각 선택할 수 있게 합니다. 가중치 전환 전에 엔드포인트, 대상 상태와 인가를 확인하고 롤백 기준 및 마지막 정상 구성을 보존하세요. - **성능:** 제한된 타임아웃과 적절한 연결 재사용을 설정합니다. 상태 검사 경로는 가볍고 의미 있게 유지하세요. 애플리케이션 의미가 허용할 때만 캐시·배치를 사용합니다. 캐시를 활성화했다고 프라이빗 Lattice 서비스가 CDN 원본이 되지는 않습니다. - **보안:** 관리 역할과 호출자 역할을 분리하고 자격 증명을 매니페스트에 넣지 않습니다. 허용·거부 역할, 루트·하위 경로, 직접 백엔드 접근과 TLS를 시험하세요. 트래픽 거부 목적으로 IAM 정책 CRD를 삭제하지 마세요. - **관측성:** 요청 수, 오류 수/비율, 지연 시간, 대상 상태와 텔레메트리 누락을 각각 관측합니다. 필요한 기간 동안 액세스 로그를 보존하고 애플리케이션 추적을 명시적으로 계측하세요. - **비용:** 선택한 모델의 현재 리전별 서비스/리소스, 요청, 데이터 처리, 엔드포인트와 로그 요금을 검토합니다. 태그를 사용하고 미사용이 확인된 리소스만 제거하며, 백엔드 자동 확장은 관리형 Lattice 데이터 플레인과 별도로 산정하세요. ## 문제 해결 컨트롤러 어노테이션/상태와 AWS 목록에서 식별자를 얻으세요. 직접 API 예제의 `$SERVICE_ID`가 Kubernetes Route의 서비스라고 가정하면 안 됩니다. ```bash aws vpc-lattice list-service-network-vpc-associations \ --service-network-identifier "$SERVICE_NETWORK_ID" aws vpc-lattice list-service-network-service-associations \ --service-network-identifier "$SERVICE_NETWORK_ID" aws vpc-lattice get-service --service-identifier "$SERVICE_ID" aws vpc-lattice get-auth-policy --resource-identifier "$SERVICE_NETWORK_ID" aws vpc-lattice get-auth-policy --resource-identifier "$SERVICE_ID" aws vpc-lattice list-listeners --service-identifier "$SERVICE_ID" aws vpc-lattice list-rules --service-identifier "$SERVICE_ID" \ --listener-identifier "$LISTENER_ID" aws vpc-lattice get-target-group --target-group-identifier "$TARGET_GROUP_ID" aws vpc-lattice list-targets --target-group-identifier "$TARGET_GROUP_ID" ``` | 증상 | 확인할 사항 | |---|---| | DNS/연결 실패 | 실제 할당 DNS, 클라이언트 VPC 연결 또는 엔드포인트 경로, 연결 상태, SG, NACL, 파드 도달성 | | 403/인증 실패 | 호출자 역할, 자격 증명 만료와 서명 리전/서비스, `UNSIGNED-PAYLOAD`, 두 인증 계층, 전파, 거부 사유 로그 | | 잘못된 경로·버전 | Route 조건, 리스너/규칙 우선순위와 매치, 대상 그룹 구성원, 가중치, Route별 도메인 | | 비정상 대상 | 실제 수신 포트, `/health`, HTTP/HTTPS, readiness, SG, 대상 유형과 상태 검사 임계값 | | 로그/지표 없음 | 목적지 권한·전달 상태, 정확한 지표 차원, 초기 트래픽, 보존 기간, 쿼리 상태 | | 컨트롤러 조정 실패 | `manager` 로그, IAM 역할, EndpointSlice, CRD 버전 호환성, 웹훅과 리더 선출 상태 | GNU 전용 `date -d`에 의존하지 않고 제한된 시간 범위의 지표를 조회합니다. ```bash export METRIC_END="$(python3 -c 'from datetime import datetime,timezone; print(datetime.now(timezone.utc).isoformat())')" export METRIC_START="$(python3 -c 'from datetime import datetime,timedelta,timezone; print((datetime.now(timezone.utc)-timedelta(hours=1)).isoformat())')" aws cloudwatch get-metric-statistics --namespace AWS/VpcLattice \ --metric-name HTTPCode_5XX_Count --dimensions file://service-dimensions.json \ --start-time "$METRIC_START" --end-time "$METRIC_END" \ --period 60 --statistics Sum ``` AWS 서비스 사고는 AWS Health와 관련 계정 이벤트를 확인하세요. 계정별 API 접근 및 지원 작업은 해당 플랜과 엔드포인트의 영향을 받습니다. 지원 사례에는 검토한 리소스 ID, 시간 범위, 실패 증상과 민감 정보를 제거한 로그를 포함하세요. 계정에서 현재 제공되는 서비스/분류/심각도 옵션을 선택하고 `urgent`를 고정한 사례 생성 명령을 그대로 실행하지 마세요. ## 참고 자료 - [VPC Lattice 개요](https://docs.aws.amazon.com/vpc-lattice/latest/ug/what-is-vpc-lattice.html) - [서비스 네트워크 연결](https://docs.aws.amazon.com/vpc-lattice/latest/ug/service-network-associations.html) - [컨트롤러 v2.1.3 설치](https://github.com/aws/aws-application-networking-k8s/blob/v2.1.3/docs/guides/deploy.md) - [컨트롤러 v2.1 업그레이드 요구사항](https://github.com/aws/aws-application-networking-k8s/blob/v2.1.3/docs/guides/upgrading-v2-0-x-to-v2-1-y.md) - [컨트롤러 API 참조](https://github.com/aws/aws-application-networking-k8s/tree/v2.1.3/docs/api-types) - [컨트롤러 HTTPS와 백엔드 TLS](https://github.com/aws/aws-application-networking-k8s/blob/v2.1.3/docs/guides/https.md) - [VPC Lattice 인증 정책](https://docs.aws.amazon.com/vpc-lattice/latest/ug/auth-policies.html) - [요청 서명](https://docs.aws.amazon.com/vpc-lattice/latest/ug/sigv4-authenticated-requests.html) - [리소스 공유](https://docs.aws.amazon.com/vpc-lattice/latest/ug/sharing.html) - [CloudWatch 지표](https://docs.aws.amazon.com/vpc-lattice/latest/ug/monitoring-cloudwatch.html) - [액세스 로그](https://docs.aws.amazon.com/vpc-lattice/latest/ug/monitoring-access-logs.html) - [CloudWatch Logs 전달 권한](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AWS-logs-infrastructure-CWL.html) - [S3 전달 권한](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AWS-logs-infrastructure-S3.html) ## 퀴즈 [VPC Lattice 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/02-vpc-lattice-quiz)로 학습 내용을 확인하세요. ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/03-aws-lb-controller ---------------------------------------- # AWS Load Balancer Controller > **검토 기준**: AWS Load Balancer Controller / Helm 차트 v3.5.0 > **마지막 업데이트**: 2026년 9월 11일 ## 개요 AWS Load Balancer Controller는 Kubernetes 클러스터에서 AWS Elastic Load Balancer(ELB)를 관리하는 컨트롤러입니다. Kubernetes Ingress 및 Service 리소스를 AWS Application Load Balancer(ALB) 및 Network Load Balancer(NLB)와 자동으로 연동합니다. ### 주요 기능 - **Application Load Balancer (ALB)**: HTTP/HTTPS 트래픽, 경로 기반 라우팅, 호스트 기반 라우팅 - **Network Load Balancer (NLB)**: TCP/UDP 트래픽, 고성능 L4 로드밸런싱 - **TargetGroupBinding**: 기존 Target Group을 Kubernetes Service와 연결 - **AWS WAF 통합**: 웹 애플리케이션 방화벽 적용 - **AWS Shield**: DDoS 보호 ![EKS 클러스터의 Ingress·Service 리소스가 AWS Load Balancer Controller를 트리거하여 ALB·NLB와 각각의 Target Group을 생성하고, TargetGroupBinding은 기존 Target Group을 직접 연결하며, 두 Target Group이 모두 Pod로 트래픽을 전달하는 구조를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-03-aws-lb-controller-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-03-aws-lb-controller-0.html) ## 아키텍처 ### 컨트롤러 동작 방식 ![사용자가 Ingress/Service를 생성하면 AWS Load Balancer Controller가 ELBv2 API로 ALB/NLB, Target Group, Listener 규칙을 만들고 Status를 갱신한 뒤 Pod 변화에 따라 Target을 계속 등록·해제하는 시퀀스를 보여준다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-03-aws-lb-controller-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-03-aws-lb-controller-1.html) ### 컴포넌트 구성 RBAC, CRD, 프로브와 웹훅 인증서를 포함한 전체 릴리스 차트를 설치하세요. 컨트롤러는 Kubernetes 객체를 감시하고 AWS API를 호출하며, 애플리케이션 트래픽은 컨트롤러 파드가 아니라 로드밸런서와 대상을 통과합니다. 리더 선출로 한 복제본이 조정하고 나머지는 대기 용량과 웹훅 가용성을 제공합니다. 복제본 수만으로 노드·가용 영역 분산 배치가 보장되지는 않습니다. ## 사전 요구사항 ### 관리 주체와 호환성 이 장은 **직접 관리하는 오픈소스 컨트롤러**를 구성합니다. EKS Auto Mode에는 별도의 관리형 로드밸런싱이 있습니다. NLB Service는 `eks.amazonaws.com/nlb`, ALB IngressClass는 `eks.amazonaws.com/alb`를 사용하며 TargetGroupBinding API도 `elbv2.k8s.aws/v1beta1`과 다릅니다. 클래스를 제자리 변경하거나 모든 어노테이션을 복사하지 말고 Auto Mode 마이그레이션 가이드를 확인하세요. 두 모델을 함께 사용할 때는 명시적인 클래스로 관리 주체를 구분합니다. 현재 지원되는 EKS Kubernetes 릴리스를 사용하고 클러스터 전체 CRD를 공유하는 모든 컨트롤러를 확인하세요. LBC **v3.5.0**은 **2026-08-03**에 공개되었으며 검증한 차트 **3.5.0**이 해당 컨트롤러를 포함합니다. Gateway API 사용자는 업그레이드 전에 **v1.6.0** CRD가 필요하고, LBC 전용 Gateway CRD는 이제 `gateway.k8s.aws/v1`을 사용합니다. 임의의 최신 Gateway API나 Kubernetes 릴리스와 호환된다는 뜻은 아닙니다. 예전의 일반적인 “Kubernetes 1.22+” 설치 하한을 현재 EKS 지원 매트릭스로 해석하면 안 됩니다. 컨트롤러 웹훅에는 제어 플레인에서 TCP 9443으로 접근할 수 있어야 합니다. IMDS가 제한되거나 컨트롤러가 Fargate/Hybrid Nodes에서 실행되면 리전/VPC 값을 명시하고 해당 컴퓨팅 유형에서 지원되는 자격 증명 방식을 선택하세요. IP 대상에는 VPC에서 라우팅 가능한 파드 주소와 지원되는 엔드포인트/ENI 탐색이 필요합니다. Amazon VPC CNI가 일반적인 EKS 선택이지만 가능한 유일한 CNI 구성은 아닙니다. Instance 대상에는 NodePort를 사용할 수 있는 Service와 적절한 노드 네트워킹이 필요합니다. ### 1. IAM 정책 생성 **v3.5.0**에 포함된 IAM 정책과 올바른 AWS 파티션을 사용하세요. 넓은 조회·보안 그룹 권한, 리소스/태그 조건과 이 배포에서 활성화할 기능을 검토하고 검토본을 저장한 뒤 정책을 생성합니다. upstream 정책을 최소 권한 보장으로 간주하거나 현재 설치에 오래된 v2.8 정책을 복사하지 마세요. 지원되는 노드에서는 **IRSA 또는 EKS Pod Identity**로 AWS 자격 증명을 제공할 수 있으며 Kubernetes API RBAC와는 별도입니다. ### 2. IRSA 설정 ```bash export AWS_REGION=ap-northeast-2 export CLUSTER_NAME=my-cluster export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)" export VPC_ID="$(aws eks describe-cluster --name "$CLUSTER_NAME" \ --query 'cluster.resourcesVpcConfig.vpcId' --output text)" kubectl config current-context aws eks describe-cluster --name "$CLUSTER_NAME" \ --query cluster.identity.oidc.issuer --output text # Only if this cluster's IAM OIDC provider does not already exist: eksctl utils associate-iam-oidc-provider --cluster "$CLUSTER_NAME" \ --region "$AWS_REGION" --approve curl --fail --location --output iam-policy-upstream.json \ https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v3.5.0/docs/install/iam_policy.json export REVIEWED_POLICY_FILE=iam-policy-reviewed.json test -s "$REVIEWED_POLICY_FILE" export CONTROLLER_POLICY_ARN="$(aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document "file://$REVIEWED_POLICY_FILE" --query Policy.Arn --output text)" eksctl create iamserviceaccount --cluster "$CLUSTER_NAME" --region "$AWS_REGION" \ --namespace kube-system --name aws-load-balancer-controller \ --attach-policy-arn "$CONTROLLER_POLICY_ARN" --approve ``` 기존 검토된 정책/역할은 재생성하지 말고 재사용하세요. IRSA 역할을 재사용하면 이 클러스터의 OIDC 공급자와 의도한 서비스 계정에 대한 신뢰 구문이 필요합니다. 기존 서비스 계정은 변경 전에 소유권과 어노테이션을 검토하세요. Pod Identity에는 별도의 에이전트/연결과 역할 신뢰 구성이 필요하며 정적 액세스 키를 차트 values에 복사하지 마세요. ## 설치 ### Helm을 사용한 설치 ```bash helm repo add eks https://aws.github.io/eks-charts helm repo update eks helm pull eks/aws-load-balancer-controller --version 3.5.0 # Review cluster-wide CRD changes and other controllers before applying. curl --fail --location --output gateway-api-v1.6.0.yaml \ https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.0/standard-install.yaml kubectl apply --server-side -f gateway-api-v1.6.0.yaml helm show crds ./aws-load-balancer-controller-3.5.0.tgz > lbc-crds.yaml kubectl apply --server-side -f lbc-crds.yaml # Save the values below as controller-values.yaml and replace its cluster/region/VPC. helm install aws-load-balancer-controller ./aws-load-balancer-controller-3.5.0.tgz \ -n kube-system -f controller-values.yaml --wait --timeout 5m ``` ```yaml # values.yaml 예시 clusterName: my-cluster serviceAccount: create: false name: aws-load-balancer-controller region: ap-northeast-2 vpcId: vpc-0123456789abcdef0 # 리소스 설정 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256Mi # 복제본 수 replicaCount: 2 # Pod 분산 배치 podDisruptionBudget: minAvailable: 1 # 고가용성을 위한 Anti-Affinity affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: - aws-load-balancer-controller topologyKey: kubernetes.io/hostname # Webhook 인증서 enableCertManager: false # 로그 레벨 logLevel: info # IngressClass 설정 ingressClass: alb createIngressClassResource: true # 추가 설정 enableShield: false enableWaf: false enableWafv2: true # Use explicit Service classes; do not claim unclassified LoadBalancer Services. enableServiceMutatorWebhook: false enableEndpointSlices: true keepTLSSecret: true clusterSecretsPermissions: allowAllSecrets: false ``` 리소스 값은 예시이며 운영 환경에서 측정된 적정값이 아닙니다. 기존 릴리스는 저장된 values로 `helm upgrade`를 검토해야 하며 Helm은 CRD를 자동 업그레이드하지 않습니다. `enableServiceMutatorWebhook: false`를 사용하므로 이 장의 NLB Service는 `service.k8s.aws/nlb`를 명시합니다. 기본 웹훅은 새로 생성하는 LoadBalancer Service를 변경하며 나중에 type을 바꾸는 기존 Service에는 적용되지 않습니다. `keepTLSSecret: true`는 기존 Helm 관리 웹훅 Secret이 있으면 재사용합니다. GitOps/인증서 교체 시 CA bundle과 파드 인증서를 함께 조정하거나 별도로 설치한 호환 cert-manager를 사용하세요. 업그레이드를 강제하기 위해 공유 CRD를 삭제하지 마세요. ### 설치 확인 ```bash # Deployment 상태 확인 kubectl get deployment -n kube-system aws-load-balancer-controller # Pod 상태 확인 kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller # 로그 확인 kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller # IngressClass 확인 kubectl get ingressclass ``` ## Application Load Balancer (ALB) 아래 매니페스트는 각각 독립적인 예제입니다. 계정/리소스 ID, 도메인, 서브넷, 보안 그룹과 인증서 ARN을 올바른 리전의 검증된 값으로 교체하세요. 참조하는 네임스페이스, Service와 준비된 백엔드 워크로드를 먼저 만듭니다. Service 포트 80과 대상 포트 8080은 역할이 다르며 containerPort 선언만으로 애플리케이션 수신이나 /health가 구현되지는 않습니다. 상태 검사 경로, 실제 대상 포트, HTTP/TLS 프로토콜, 보안 그룹과 NetworkPolicy가 맞아야 합니다. 개요 그림은 **IP 대상**을 나타내며 instance 대상은 노드를 등록하고 NodePort를 사용합니다. TargetGroupBinding도 이 컨트롤러가 조정하며 시퀀스 그림은 원자적 트랜잭션이 아니라 설명용입니다. ### 기본 Ingress 설정 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress namespace: default annotations: # ALB 노출 방식 (internet-facing 또는 internal) alb.ingress.kubernetes.io/scheme: internet-facing # Target Type (ip 또는 instance) alb.ingress.kubernetes.io/target-type: ip # 리스너 포트 alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]' # SSL 리다이렉트 alb.ingress.kubernetes.io/ssl-redirect: "443" # ACM 인증서 alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:ACCOUNT:certificate/CERT_ID # 서브넷 지정 alb.ingress.kubernetes.io/subnets: subnet-xxx,subnet-yyy,subnet-zzz # 보안 그룹 alb.ingress.kubernetes.io/security-groups: sg-xxxxxxxxx alb.ingress.kubernetes.io/manage-backend-security-group-rules: "true" # 헬스체크 설정 alb.ingress.kubernetes.io/healthcheck-path: /health alb.ingress.kubernetes.io/healthcheck-interval-seconds: "15" alb.ingress.kubernetes.io/healthcheck-timeout-seconds: "5" alb.ingress.kubernetes.io/healthy-threshold-count: "2" alb.ingress.kubernetes.io/unhealthy-threshold-count: "2" spec: ingressClassName: alb rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: api-service port: number: 80 ``` ### 고급 Ingress 설정 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: advanced-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip # 그룹으로 여러 Ingress를 하나의 ALB로 통합 alb.ingress.kubernetes.io/group.name: my-app-group alb.ingress.kubernetes.io/group.order: "10" # Target group attributes alb.ingress.kubernetes.io/target-group-attributes: >- stickiness.enabled=true, stickiness.lb_cookie.duration_seconds=60, slow_start.duration_seconds=30, deregistration_delay.timeout_seconds=30 # IP 주소 유형 alb.ingress.kubernetes.io/ip-address-type: dualstack # 로드밸런서 속성 alb.ingress.kubernetes.io/load-balancer-attributes: >- idle_timeout.timeout_seconds=60, routing.http2.enabled=true, routing.http.drop_invalid_header_fields.enabled=true, access_logs.s3.enabled=true, access_logs.s3.bucket=my-alb-logs, access_logs.s3.prefix=my-app # 태그 alb.ingress.kubernetes.io/tags: Environment=production,Team=platform # WAF v2 연동 alb.ingress.kubernetes.io/wafv2-acl-arn: arn:aws:wafv2:ap-northeast-2:ACCOUNT:regional/webacl/my-acl/xxx # Shield Advanced alb.ingress.kubernetes.io/shield-advanced-protection: "true" spec: ingressClassName: alb tls: - hosts: - api.example.com - www.example.com rules: - host: api.example.com http: paths: - path: /v1 pathType: Prefix backend: service: name: api-v1 port: number: 80 - path: /v2 pathType: Prefix backend: service: name: api-v2 port: number: 80 - host: www.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-frontend port: number: 80 ``` ### 경로 기반 라우팅 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: path-based-routing annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip # 조건 기반 라우팅 alb.ingress.kubernetes.io/conditions.api-v2: >- [{"field":"http-header","httpHeaderConfig":{"httpHeaderName":"X-Api-Version","values":["v2"]}}] spec: ingressClassName: alb rules: - host: api.example.com http: paths: # 정확한 경로 매칭 - path: /health pathType: Exact backend: service: name: health-service port: number: 80 # API 버전별 라우팅 - path: /api pathType: Prefix backend: service: name: api-v2 port: number: 80 - path: /api pathType: Prefix backend: service: name: api-v1 port: number: 80 # 정적 파일 - path: /static pathType: Prefix backend: service: name: static-service port: number: 80 # 기본 경로 - path: / pathType: Prefix backend: service: name: default-service port: number: 80 ``` ### 인증 설정 예제에는 기존 HTTPS 인증서와 신원 공급자 애플리케이션이 필요합니다. 콜백 `https://app.example.com/oauth2/idpresponse`, authorization-code 흐름, 허용 scope와 필요한 client secret을 구성하세요. ALB는 공급자의 token/user-info 엔드포인트에 IPv4로 도달해야 하며 내부 ALB에는 적절한 egress/NAT 경로가 필요할 수 있습니다. 인증은 HTTPS 리스너에만 적용됩니다. 미인증 요청을 `allow`하면 백엔드를 보호하지 않습니다. 직접 백엔드 접근을 제한하고 애플리케이션 요구에 따라 ALB가 서명한 사용자 claim을 검증하세요. ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: auth-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]' alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:123456789012:certificate/12345678-1234-1234-1234-123456789012 # Cognito 인증 alb.ingress.kubernetes.io/auth-type: cognito alb.ingress.kubernetes.io/auth-idp-cognito: >- {"userPoolARN":"arn:aws:cognito-idp:ap-northeast-2:ACCOUNT:userpool/ap-northeast-2_xxxxx", "userPoolClientID":"xxxxxxxxx", "userPoolDomain":"my-domain"} alb.ingress.kubernetes.io/auth-on-unauthenticated-request: authenticate alb.ingress.kubernetes.io/auth-scope: "openid profile email" alb.ingress.kubernetes.io/auth-session-cookie: "AWSELBAuthSessionCookie" alb.ingress.kubernetes.io/auth-session-timeout: "3600" spec: ingressClassName: alb rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: protected-app port: number: 80 ``` ```yaml # OIDC 인증 예시 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: oidc-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]' alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:123456789012:certificate/12345678-1234-1234-1234-123456789012 # OIDC 인증 alb.ingress.kubernetes.io/auth-type: oidc alb.ingress.kubernetes.io/auth-idp-oidc: >- {"issuer":"https://accounts.google.com", "authorizationEndpoint":"https://accounts.google.com/o/oauth2/v2/auth", "tokenEndpoint":"https://oauth2.googleapis.com/token", "userInfoEndpoint":"https://openidconnect.googleapis.com/v1/userinfo", "secretName":"oidc-secret"} alb.ingress.kubernetes.io/auth-on-unauthenticated-request: authenticate spec: ingressClassName: alb rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: protected-app port: number: 80 --- # OIDC Secret apiVersion: v1 kind: Secret metadata: name: oidc-secret type: Opaque stringData: clientID: your-client-id clientSecret: your-client-secret ``` OIDC Secret은 Ingress와 같은 네임스페이스에 있어야 합니다. 차트 기본값은 `clusterSecretsPermissions.allowAllSecrets: false`이므로 필요한 Secret 접근만 부여하세요. v3.5.0은 `metadata.name` 필드 셀렉터로 Secret을 감시하므로 Role의 `resourceNames`를 제한할 수 있습니다. 실제 Secret은 승인된 비밀 관리 절차로 생성하고 실제 client secret을 커밋하지 마세요. ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: lbc-oidc-secret namespace: default rules: - apiGroups: - '' resources: - secrets resourceNames: - oidc-secret verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: lbc-oidc-secret namespace: default subjects: - kind: ServiceAccount name: aws-load-balancer-controller namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: lbc-oidc-secret ``` ## Network Load Balancer (NLB) ### 기본 NLB Service 설정 ```yaml apiVersion: v1 kind: Service metadata: name: nlb-service annotations: # NLB 유형 지정 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-subnets: subnet-xxx,subnet-yyy # 헬스체크 service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: "HTTP" service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: "/health" service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080" service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "10" service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: "2" service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "2" spec: type: LoadBalancer loadBalancerClass: service.k8s.aws/nlb selector: app: my-app ports: - name: tcp port: 80 targetPort: 8080 protocol: TCP ``` ### 가중치 대상 그룹 아래 Service는 자신의 암시적 대상 그룹에 가중치 90, 기존 `service-canary:8080` 백엔드에 10을 줍니다. 두 Service에는 의도한 준비된 엔드포인트와 호환 대상 설정이 필요하며 어노테이션이 카나리 워크로드를 만들지는 않습니다. 어노테이션 접미사는 리스너 프로토콜과 포트인 **`actions.TCP-80`**입니다. 가중치는 **0~999**의 상대값으로 새 연결에 적용됩니다. 일반적인 가중치 변경은 기존 연결을 유지하지만, **대상 그룹의 가중치를 0으로 설정하면 잠시 후 기존 연결도 닫히고** 새 연결도 중단됩니다. 무중단 드레이닝을 보장한다고 설명하면 안 됩니다. TLS 리스너에는 호환되는 대상 그룹 프로토콜이 필요하며 대상 그룹 고정 세션은 지원하지 않습니다. ```yaml apiVersion: v1 kind: Service metadata: name: nlb-weighted namespace: default annotations: service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip service.beta.kubernetes.io/aws-load-balancer-scheme: internal service.beta.kubernetes.io/actions.TCP-80: '{"type":"forward","forwardConfig":{"baseServiceWeight":90,"targetGroups":[{"serviceName":"service-canary","servicePort":8080,"weight":10}]}}' spec: type: LoadBalancer loadBalancerClass: service.k8s.aws/nlb selector: app: my-app version: stable ports: - name: tcp port: 80 targetPort: 8080 protocol: TCP ``` ### TLS 종료 NLB ```yaml apiVersion: v1 kind: Service metadata: name: nlb-tls-service annotations: service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" # TLS 설정 service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:ap-northeast-2:ACCOUNT:certificate/CERT_ID" service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS13-1-2-2021-06" # Backend은 HTTP service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" spec: type: LoadBalancer loadBalancerClass: service.k8s.aws/nlb selector: app: my-app ports: - name: https port: 443 targetPort: 8080 protocol: TCP ``` ### 내부 NLB ```yaml apiVersion: v1 kind: Service metadata: name: internal-nlb annotations: service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" # 내부 노출 방식 service.beta.kubernetes.io/aws-load-balancer-scheme: "internal" # 크로스 존 로드밸런싱 service.beta.kubernetes.io/aws-load-balancer-attributes: "load_balancing.cross_zone.enabled=true" # 프라이빗 서브넷 service.beta.kubernetes.io/aws-load-balancer-subnets: subnet-private-a,subnet-private-b # 보안 그룹 (선택) service.beta.kubernetes.io/aws-load-balancer-security-groups: sg-xxxxxxxxx service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true" spec: type: LoadBalancer loadBalancerClass: service.k8s.aws/nlb selector: app: internal-service ports: - port: 80 targetPort: 8080 ``` ### UDP 지원 NLB ```yaml apiVersion: v1 kind: Service metadata: name: udp-nlb annotations: service.beta.kubernetes.io/aws-load-balancer-enable-tcp-udp-listener: "true" service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" spec: type: LoadBalancer loadBalancerClass: service.k8s.aws/nlb selector: app: dns-server ports: - name: dns-udp port: 53 targetPort: 53 protocol: UDP - name: dns-tcp port: 53 targetPort: 53 protocol: TCP ``` ### Proxy Protocol v2 Proxy Protocol v2는 원래 클라이언트 주소를 바이너리 연결 메타데이터로 전달하며 **IP 패킷의 소스 주소를 보존하는 것은 아닙니다**. 차이를 명확히 하기 위해 예제는 패킷 수준 원본 IP 보존을 비활성화합니다. 백엔드는 해당 상태 검사 연결을 포함하여 애플리케이션 데이터 전에 Proxy Protocol을 해석해야 합니다. 일반 HTTP/TLS 서버는 별도 구성 없이 이 접두사를 처리할 수 없습니다. `preserve_client_ip.enabled`는 대상 유형·프로토콜·네트워크 경로가 지원하는 NLB 패킷 소스 보존을 제어합니다. Instance/NodePort 대상의 `externalTrafficPolicy: Local`은 이후 kube-proxy SNAT 홉을 피할 수 있지만 NLB 원본 IP 보존을 보편적으로 대체하지 않습니다. IP 계열 변환과 지원되지 않는 전이·헤어핀 경로는 별도로 검토하세요. ```yaml apiVersion: v1 kind: Service metadata: name: proxy-protocol-nlb annotations: service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" # Proxy Protocol v2 활성화 # Target Group 속성 service.beta.kubernetes.io/aws-load-balancer-target-group-attributes: >- proxy_protocol_v2.enabled=true, preserve_client_ip.enabled=false spec: type: LoadBalancer loadBalancerClass: service.k8s.aws/nlb selector: app: proxy-aware-app ports: - port: 80 targetPort: 8080 ``` ## IngressClass 및 IngressClassParams 선택적 클래스 이름을 `alb-platform`으로 하여 차트 소유 `alb` 클래스를 덮어쓰지 않습니다. 대상 네임스페이스에 `alb-enabled=true`를 붙이고 Ingress의 `spec.ingressClassName`을 `alb-platform`으로 설정하세요. 의도한 정책이 아니라면 클러스터 기본 클래스로 지정하지 마세요. IngressClassParams 설정은 대응되는 어노테이션보다 우선합니다. ### IngressClass 정의 ```yaml apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: alb-platform spec: controller: ingress.k8s.aws/alb parameters: apiGroup: elbv2.k8s.aws kind: IngressClassParams name: alb-params ``` ### IngressClassParams 설정 ```yaml apiVersion: elbv2.k8s.aws/v1beta1 kind: IngressClassParams metadata: name: alb-params spec: # 기본 노출 방식 scheme: internet-facing # IP 주소 유형 ipAddressType: dualstack # 네임스페이스 셀렉터 (특정 네임스페이스만 허용) namespaceSelector: matchLabels: alb-enabled: "true" # 기본 태그 tags: - key: Environment value: production - key: ManagedBy value: aws-load-balancer-controller # 로드밸런서 속성 loadBalancerAttributes: - key: idle_timeout.timeout_seconds value: "60" - key: routing.http2.enabled value: "true" # 서브넷 선택 # subnets: # ids: # - subnet-xxx # - subnet-yyy # tags: # kubernetes.io/role/elb: ["1"] # 그룹 설정 group: name: my-default-group ``` ## TargetGroupBinding TargetGroupBinding CRD를 사용하면 기존 AWS Target Group을 Kubernetes Service와 직접 연결할 수 있습니다. ### 기본 TargetGroupBinding ```yaml apiVersion: elbv2.k8s.aws/v1beta1 kind: TargetGroupBinding metadata: name: my-tgb namespace: default spec: # 기존 Target Group ARN targetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-2:ACCOUNT:targetgroup/my-tg/xxxxxxxxxxxx # 연결할 Service serviceRef: name: my-service port: 80 # Target Type (ip 또는 instance) targetType: ip # 네트워킹 설정 networking: ingress: - from: - securityGroup: groupID: sg-xxxxxxxxx ports: - port: 80 protocol: TCP ``` TGB는 등록 대상을 관리하며 기존 로드밸런서/리스너의 수명주기를 관리하지 않습니다. Service 포트, 대상 그룹 프로토콜/IP 계열, 백엔드 대상 포트와 보안 그룹 규칙을 일치시키세요. `nodeSelector`는 **instance** 대상만 필터링하며 IP 모드 파드를 고르지 않습니다. 컨트롤러 IAM 권한으로 계정 내 다른 대상 그룹도 참조할 수 있으므로 TGB 생성/변경은 신뢰할 수 있는 운영자로 제한하세요. 여러 클러스터나 TGB가 하나의 대상 그룹을 공유하면 **모든 참여 TGB를 생성할 때부터** `spec.multiClusterTargetGroup: true`를 설정합니다. 기본값 `false`는 전체 소유권을 가정하므로 다른 클러스터 대상을 등록 해제할 수 있습니다. 생성 후 이 값을 임의로 바꾸면 문서화된 대상 누락 정리 문제가 생길 수 있습니다. 클러스터별 별도 대상 그룹도 하나의 관리 모델입니다. ### 고급 TargetGroupBinding ```yaml apiVersion: elbv2.k8s.aws/v1beta1 kind: TargetGroupBinding metadata: name: advanced-tgb namespace: production spec: targetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-2:ACCOUNT:targetgroup/prod-tg/xxxxxxxxxxxx serviceRef: name: production-service port: 8080 targetType: ip # IP 주소 유형 ipAddressType: ipv4 # VPC ID (자동 감지, 명시적 지정 가능) # vpcID: vpc-xxxxxxxxx # 네트워킹 설정 networking: ingress: # 여러 보안 그룹에서의 트래픽 허용 - from: - securityGroup: groupID: sg-alb-sg - securityGroup: groupID: sg-internal-sg ports: - port: 8080 protocol: TCP - port: 8443 protocol: TCP # Node selector는 instance 대상 선택용이며 IP 모드의 파드 선택 조건이 아님 # nodeSelector: # matchLabels: # node-type: compute ``` ### 멀티포트 TargetGroupBinding ```yaml # 여러 포트를 위한 별도의 TargetGroupBinding --- apiVersion: elbv2.k8s.aws/v1beta1 kind: TargetGroupBinding metadata: name: http-tgb spec: targetGroupARN: arn:aws:elasticloadbalancing:...:targetgroup/http-tg/xxx serviceRef: name: multi-port-service port: 80 targetType: ip --- apiVersion: elbv2.k8s.aws/v1beta1 kind: TargetGroupBinding metadata: name: https-tgb spec: targetGroupARN: arn:aws:elasticloadbalancing:...:targetgroup/https-tg/yyy serviceRef: name: multi-port-service port: 443 targetType: ip ``` ## WAF 및 Shield 통합 ALB와 같은 리전의 기존 regional Web ACL과 의도한 규칙을 사용하세요. 설치 values는 WAF v2를 켜고 Shield 연동은 끕니다. Shield Advanced 예제에는 필요한 구독/권한을 준비하고 컨트롤러의 Shield 연동을 활성화해야 합니다. 어노테이션만으로 유료 구독이 활성화되거나 비활성 컨트롤러 기능이 재정의되지는 않습니다. 이러한 ALB 연동이 임의의 NLB TCP/UDP 트래픽을 WAF가 검사한다는 뜻은 아닙니다. S3 액세스 로그 예제에도 기존 목적지 버킷과 문서화된 ALB 로그 전달 버킷 정책이 필요합니다. ### AWS WAF v2 연동 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: waf-protected-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip # WAF v2 WebACL 연결 alb.ingress.kubernetes.io/wafv2-acl-arn: arn:aws:wafv2:ap-northeast-2:ACCOUNT:regional/webacl/my-webacl/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx spec: ingressClassName: alb rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: api-service port: number: 80 ``` ### AWS Shield Advanced ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: shield-protected-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip # Shield Advanced 보호 활성화 alb.ingress.kubernetes.io/shield-advanced-protection: "true" spec: ingressClassName: alb rules: - host: critical-app.example.com http: paths: - path: / pathType: Prefix backend: service: name: critical-service port: number: 80 ``` ## 버전별 주요 업데이트 - **v2.16.0 — 2025-11-20:** ALB Target Optimizer와 NLB 가중치 대상 그룹. Target Optimizer에는 대상 제어 에이전트와 구성이 필요하며 LBC 설치만으로 활성화되지 않습니다. - **v2.17.0 — 2025-12-19:** 리스너·엔드포인트 그룹·엔드포인트를 포함한 단일 `aga.k8s.aws/v1beta1` `GlobalAccelerator` CRD와 Gateway API GA 릴리스 후보 지원. Global Accelerator에는 추가 IAM 권한과 기능 구성이 필요합니다. - **v3.5.0 — 2026-08-03:** Gateway API v1.6.0 적합성과 안정 v1 TCPRoute/UDPRoute 지원. LBC Gateway 구성 리소스는 `gateway.k8s.aws/v1`을 사용하며, 여전히 제공되는 v1beta1은 deprecated입니다. 현재 v3.5는 QUIC/TCP_QUIC 구성과 ALB JWT 검증을 지원합니다. 서로 다른 기능이며 프로토콜별 제약이 있습니다. JWT 검증은 HTTPS 전용이고 JSON은 `jwksUri`가 아니라 **`jwksEndpoint`**를 사용합니다. 유효한 인증서, 도달 가능한 신뢰하는 JWKS 엔드포인트와 검토된 issuer/claim을 갖춘 HTTPS Ingress 어노테이션에 다음을 추가하세요. ```yaml alb.ingress.kubernetes.io/jwt-validation: >- {"issuer":"https://accounts.example.com","jwksEndpoint":"https://accounts.example.com/.well-known/jwks.json"} ``` 이는 어노테이션 조각이며 완전한 Kubernetes 객체가 아닙니다. 서명 검증만으로 인가가 충분하다고 가정하지 말고 애플리케이션에 필요한 audience/추가 claim을 검증하세요. 별도의 Gateway 구성은 [Gateway API 문서](https://www.atomai.click/kubernetes-docs/llms/ko/networking/04-gateway-api.md)를 참고하세요. ## 주요 Annotation 레퍼런스 ### ALB Ingress Annotations | Annotation | 설명 | 기본값 | |------------|------|--------| | `alb.ingress.kubernetes.io/scheme` | internet-facing 또는 internal | internal | | `alb.ingress.kubernetes.io/target-type` | ip 또는 instance | instance | | `alb.ingress.kubernetes.io/subnets` | 서브넷 ID 또는 이름 | 자동 감지 | | `alb.ingress.kubernetes.io/security-groups` | 보안 그룹 ID | 자동 생성 | | `alb.ingress.kubernetes.io/listen-ports` | 리스너 포트 JSON | HTTP 80, or HTTPS 443 when certificate-arn is specified | | `alb.ingress.kubernetes.io/certificate-arn` | ACM 인증서 ARN | - | | `alb.ingress.kubernetes.io/ssl-redirect` | SSL 리다이렉트 포트 | - | | `alb.ingress.kubernetes.io/ssl-policy` | SSL 정책 | ELBSecurityPolicy-2016-08 | | `alb.ingress.kubernetes.io/healthcheck-path` | 헬스체크 경로 | / | | `alb.ingress.kubernetes.io/healthcheck-port` | 헬스체크 포트 | traffic-port | | `alb.ingress.kubernetes.io/healthcheck-protocol` | 헬스체크 프로토콜 | HTTP | | `alb.ingress.kubernetes.io/healthcheck-interval-seconds` | 헬스체크 간격 | 15 | | `alb.ingress.kubernetes.io/healthcheck-timeout-seconds` | 헬스체크 타임아웃 | 5 | | `alb.ingress.kubernetes.io/healthy-threshold-count` | 정상 임계값 | 2 | | `alb.ingress.kubernetes.io/unhealthy-threshold-count` | 비정상 임계값 | 2 | | `alb.ingress.kubernetes.io/group.name` | Ingress 그룹 이름 | - | | `alb.ingress.kubernetes.io/group.order` | 그룹 내 우선순위 | 0 | | `alb.ingress.kubernetes.io/ip-address-type` | ipv4 또는 dualstack | ipv4 | | `alb.ingress.kubernetes.io/load-balancer-attributes` | LB 속성 | - | | `alb.ingress.kubernetes.io/target-group-attributes` | TG 속성 | - | | `alb.ingress.kubernetes.io/tags` | 리소스 태그 | - | | `alb.ingress.kubernetes.io/wafv2-acl-arn` | WAF v2 WebACL ARN | - | | `alb.ingress.kubernetes.io/shield-advanced-protection` | Shield 보호 | false | | `alb.ingress.kubernetes.io/auth-type` | 인증 유형 (none, cognito, oidc) | none | ### NLB Service Annotations | Annotation | 설명 | 기본값 | |------------|------|--------| | `service.beta.kubernetes.io/aws-load-balancer-type` | external (NLB) 또는 nlb | - | | `service.beta.kubernetes.io/aws-load-balancer-nlb-target-type` | ip 또는 instance | instance | | `service.beta.kubernetes.io/aws-load-balancer-scheme` | internet-facing 또는 internal | internal | | `service.beta.kubernetes.io/aws-load-balancer-subnets` | 서브넷 ID | 자동 감지 | | `service.beta.kubernetes.io/aws-load-balancer-ssl-cert` | ACM 인증서 ARN | - | | `service.beta.kubernetes.io/aws-load-balancer-ssl-ports` | SSL 적용 포트 | - | | `service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy` | SSL 정책 | - | | `service.beta.kubernetes.io/aws-load-balancer-backend-protocol` | 백엔드 프로토콜 | - | | `service.beta.kubernetes.io/aws-load-balancer-proxy-protocol` | Proxy Protocol | - | | `service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled` | Deprecated; aws-load-balancer-attributes 사용 | false | | `service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol` | 헬스체크 프로토콜 | TCP | | `service.beta.kubernetes.io/aws-load-balancer-healthcheck-path` | 헬스체크 경로 | - | | `service.beta.kubernetes.io/aws-load-balancer-healthcheck-port` | 헬스체크 포트 | - | | `service.beta.kubernetes.io/aws-load-balancer-attributes` | LB 속성 | - | | `service.beta.kubernetes.io/aws-load-balancer-target-group-attributes` | TG 속성 | - | | `service.beta.kubernetes.io/aws-load-balancer-security-groups` | 보안 그룹 | 자동 생성 | ## EKS 모범 사례 ### 1. 서브넷 태깅 역할 태그는 의도한 퍼블릭/프라이빗 서브넷을 명확히 선택하는 방법입니다. 직접 관리하는 LBC v2.12.1 이상에서는 일치하는 역할 태그 서브넷이 없으면 기본 `SubnetDiscoveryByReachability` 동작으로 라우팅 테이블에서 분류할 수 있습니다. 명시적 서브넷 ID나 IngressClassParams 태그 필터도 별도 경로입니다. EKS Auto Mode에는 여전히 문서화된 서브넷 태그가 필요합니다. 클러스터 태그 필터, 가용 IP와 선택 AZ별 적격 서브넷을 확인하세요. 일반 ALB에는 최소 두 AZ가 필요합니다. 태그를 붙였다고 라우팅 테이블이 바뀌거나 퍼블릭 서브넷이 되지는 않습니다. ```bash # 퍼블릭 서브넷 (인터넷 연결 ALB/NLB용) aws ec2 create-tags \ --resources subnet-xxx \ --tags Key=kubernetes.io/role/elb,Value=1 # 프라이빗 서브넷 (내부 ALB/NLB용) aws ec2 create-tags \ --resources subnet-yyy \ --tags Key=kubernetes.io/role/internal-elb,Value=1 # 클러스터별 태그 (선택) aws ec2 create-tags \ --resources subnet-xxx subnet-yyy \ --tags Key=kubernetes.io/cluster/my-cluster,Value=shared ``` ### 2. 보안 그룹 관리 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: secure-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip # 명시적 보안 그룹 지정 alb.ingress.kubernetes.io/security-groups: sg-alb-external # 이 명시적 보안 그룹에서 허용할 인바운드 소스를 직접 구성하세요. # security-groups 지정 시 inbound-cidrs는 무시됩니다. # 추가 보안 그룹 (백엔드 통신용) alb.ingress.kubernetes.io/manage-backend-security-group-rules: "true" spec: ingressClassName: alb rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: api-service port: number: 80 ``` ### 3. 비용 최적화 IngressGroup은 ALB와 규칙 공간을 공유합니다. 신뢰 경계 안에서만 사용하세요. 그룹에 참여하는 Ingress를 생성할 수 있는 사용자는 라우팅과 우선순위에 영향을 줄 수 있습니다. RBAC/admission을 적용하고 병합/독점 어노테이션 설정을 검토하세요. 그룹 참여는 네임스페이스 격리 기능이나 무조건적인 비용 절감 보장이 아닙니다. ```yaml # Ingress 그룹을 사용하여 ALB 공유 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app1-ingress annotations: alb.ingress.kubernetes.io/group.name: shared-alb alb.ingress.kubernetes.io/group.order: "1" spec: ingressClassName: alb rules: - host: app1.example.com http: paths: - path: / pathType: Prefix backend: service: name: app1 port: number: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app2-ingress annotations: alb.ingress.kubernetes.io/group.name: shared-alb alb.ingress.kubernetes.io/group.order: "2" spec: ingressClassName: alb rules: - host: app2.example.com http: paths: - path: / pathType: Prefix backend: service: name: app2 port: number: 80 ``` ### 4. 고가용성 구성 ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ha-ingress annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip # 3개 이상의 AZ에 서브넷 지정 alb.ingress.kubernetes.io/subnets: subnet-az-a,subnet-az-b,subnet-az-c # ALB 수준의 크로스 존은 활성화되어 있습니다. # 대상 그룹별 재정의는 별도로 검토하세요. # 헬스체크 최적화 alb.ingress.kubernetes.io/healthcheck-interval-seconds: "10" alb.ingress.kubernetes.io/healthy-threshold-count: "2" alb.ingress.kubernetes.io/unhealthy-threshold-count: "2" # 드레이닝 타임아웃 alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30 spec: ingressClassName: alb rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: api-service port: number: 80 ``` ## 트러블슈팅 아래 이름 변수는 실제 네임스페이스와 리소스 목록에서 설정하세요. 인프라 변경 전에 컨트롤러 이벤트/오류 사유를 확인합니다. 선택적 exec 상태 검사는 애플리케이션 이미지에 curl이 있다고 가정하며 없으면 승인된 진단 컨테이너를 사용하세요. 근거 수집 시 로그와 자격 증명을 보호합니다. 502에는 연결 재설정, 잘못된 응답이나 TLS 원인도 있으므로 모든 비정상 대상이 같은 HTTP 상태를 만든다고 가정하지 말고 ALB 액세스 로그의 오류 세부 내용을 확인하세요. ### 일반적인 문제 #### 1. ALB가 생성되지 않음 ```bash # 컨트롤러 로그 확인 kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller # Ingress 이벤트 확인 kubectl describe ingress "$INGRESS_NAME" -n "$NAMESPACE" # 일반적인 원인: # - IAM 권한 부족 # - 서브넷 태그 누락 # - IngressClass 미지정 ``` #### 2. Target이 Unhealthy ```bash # Target Group 상태 확인 aws elbv2 describe-target-health \ --target-group-arn "$TARGET_GROUP_ARN" # Pod 로그 확인 kubectl logs "$POD_NAME" -n "$NAMESPACE" --tail=100 # 헬스체크 엔드포인트 테스트 kubectl exec "$POD_NAME" -n "$NAMESPACE" -- curl --fail --max-time 5 http://localhost:8080/health # 보안 그룹 확인 aws ec2 describe-security-groups --group-ids "$SECURITY_GROUP_ID" ``` #### 3. 502 Bad Gateway ```bash # 원인 분석: # 1. Pod가 준비되지 않음 kubectl get pods -l app=my-app # 2. Target Group 드레이닝 중 aws elbv2 describe-target-health --target-group-arn "$TARGET_GROUP_ARN" # 3. 헬스체크 실패 # - 헬스체크 경로 확인 # - 헬스체크 타임아웃 조정 # 4. 보안 그룹 규칙 # - ALB -> Pod 통신 허용 확인 ``` #### 4. SSL 인증서 문제 ```bash # ACM 인증서 상태 확인 aws acm describe-certificate --certificate-arn "$ACM_CERTIFICATE_ARN" # 인증서가 ISSUED 상태인지 확인 # 도메인 검증 완료 여부 확인 # 리전 확인 (ALB와 같은 리전이어야 함) ``` ### 디버깅 명령어 ```bash # 컨트롤러 상세 로그 kubectl logs -n kube-system deployment/aws-load-balancer-controller -f # Ingress 상태 확인 kubectl get ingress -o wide kubectl describe ingress "$INGRESS_NAME" -n "$NAMESPACE" # Service 상태 확인 kubectl get svc -o wide kubectl describe svc "$SERVICE_NAME" -n "$NAMESPACE" # TargetGroupBinding 상태 확인 kubectl get targetgroupbindings -A kubectl describe targetgroupbinding "$TGB_NAME" -n "$NAMESPACE" # AWS 리소스 확인 aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s`)]' aws elbv2 describe-target-groups --query 'TargetGroups[?contains(TargetGroupName, `k8s`)]' ``` --- ## 참고 자료 - [AWS Load Balancer Controller 문서](https://kubernetes-sigs.github.io/aws-load-balancer-controller/) - [GitHub 저장소](https://github.com/kubernetes-sigs/aws-load-balancer-controller) - [EKS 사용자 가이드](https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html) - [ALB 문서](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/) - [NLB 문서](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/) - [LBC v3.5.0 release](https://github.com/kubernetes-sigs/aws-load-balancer-controller/releases/tag/v3.5.0) - [LBC v3.5.0 Ingress annotations](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/ingress/annotations.md) - [LBC v3.5.0 Service annotations](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/service/annotations.md) - [TargetGroupBinding ownership](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/targetgroupbinding/targetgroupbinding.md) - [Subnet discovery](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/deploy/subnet_discovery.md) - [NLB listener weights and connections](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-listeners.html) - [ALB authentication prerequisites](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html) - [EKS Auto Mode NLB](https://docs.aws.amazon.com/eks/latest/userguide/auto-configure-nlb.html) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/04-gateway-api ---------------------------------------- # Kubernetes Gateway API > **API 기준**: Gateway API v1.6 Standard. 정확한 번들은 컨트롤러 지원 버전에 맞춰 선택하세요. > **마지막 업데이트**: 2026년 9월 12일 ## 개요 Gateway API는 Kubernetes의 차세대 인그레스 API로, 기존 Ingress API의 한계를 극복하고 더 표현력 있고 확장 가능한 네트워크 라우팅 기능을 제공합니다. SIG-Network에서 개발하며, 다양한 구현체(Istio, Cilium, Envoy Gateway 등)에서 지원됩니다. ### Ingress API의 한계 | 문제 | 설명 | |------|------| | **표현력 부족** | HTTP 라우팅 외 TCP/UDP/gRPC 지원 미흡 | | **책임 결합** | RBAC/IngressClass로 접근을 제한할 수 있지만 리스너·경로 책임이 덜 명시적으로 분리됨 | | **Annotation 남용** | 구현체별 기능을 annotation으로 처리하여 이식성 저하 | | **확장성 제한** | 새로운 프로토콜이나 기능 추가 어려움 | | **크로스 네임스페이스** | 네임스페이스 간 라우팅 복잡 | ### Gateway API의 장점 ![Gateway API의 네 가지 설계 목표: 표현력, 책임 분리, 이식성과 확장성.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-04-gateway-api-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-04-gateway-api-0.html) 표현력, 책임 분리, 이식성과 확장성은 각각의 설계 목표입니다. 실제 기능 지원은 컨트롤러와 적합성 프로파일에 따라 다르며, 각 리소스의 변경 권한은 Kubernetes RBAC와 admission 정책으로 강제합니다. ## 리소스 모델 Gateway API는 계층화된 리소스 모델을 사용합니다. ![일반적인 구현의 GatewayClass, Gateway, Route와 백엔드 Service 관계이며 실제 Gateway 인프라는 컨트롤러에 따라 달라집니다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-04-gateway-api-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-04-gateway-api-1.html) 그림은 리소스 관계와 일반적인 Gateway 배포 모델을 보여줍니다. Gateway가 항상 클라우드 로드밸런서 하나인 것은 아닙니다. Istio는 프록시 Deployment/Service를 만들 수 있고 VPC Lattice는 서비스 네트워크에 매핑합니다. 다른 네임스페이스의 백엔드/Secret 참조는 **참조 대상 네임스페이스의 소유자**가 ReferenceGrant로 허용합니다. ### 역할 분리 | 역할 | 담당 리소스 | 책임 | |------|------------|------| | **인프라 제공자** | GatewayClass | 기본 인프라 구성 정의 | | **클러스터 운영자** | Gateway | Gateway 인프라와 Route 연결 정책 | | **참조 대상 네임스페이스 소유자** | ReferenceGrant | 소유한 백엔드/Secret 참조 인가 | | **애플리케이션 개발자** | HTTPRoute, GRPCRoute 등 | 애플리케이션 라우팅 규칙 정의 | ## GatewayClass GatewayClass는 Gateway를 생성할 때 사용할 컨트롤러와 설정을 정의합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller description: Istio Gateway Controller for production workloads ``` GatewayClass는 이미 설치된 컨트롤러를 선택하며, 클래스 생성이 컨트롤러를 설치하지는 않습니다. 아래 정의는 대안들입니다. `Accepted` 조건이 true인 클래스를 사용하고 예제 클래스 이름은 실제 수락된 이름에 맞추세요. `parametersRef` 지원 여부와 group/kind는 구현체에 따라 다릅니다. Istio 1.31의 Gateway별 ConfigMap은 Gateway와 같은 네임스페이스에 두고 `Gateway.spec.infrastructure.parametersRef`에서 참조합니다. 클래스 전체 기본값은 Istio 루트 네임스페이스에서 `gateway.istio.io/defaults-for-class` 레이블이 있는 ConfigMap을 사용합니다. 뒤의 ALB→Istio 예제가 Gateway별 구성을 보여줍니다. ### 주요 구현체별 GatewayClass ```yaml # Istio apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio spec: controllerName: istio.io/gateway-controller --- # Cilium apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: cilium spec: controllerName: io.cilium/gateway-controller --- # AWS Gateway API Controller apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: amazon-vpc-lattice spec: controllerName: application-networking.k8s.aws/gateway-api-controller --- # Envoy Gateway apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: envoy-gateway spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- # Contour apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: contour spec: controllerName: projectcontour.io/gateway-controller --- # NGINX Gateway Fabric apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: nginx spec: controllerName: gateway.nginx.org/nginx-gateway-controller ``` ## Gateway Gateway는 트래픽 처리 인프라와 리스너를 정의합니다. 프록시 워크로드, 관리형 로드밸런서 또는 서비스 네트워크에 매핑되는 방식은 컨트롤러에 따라 다릅니다. ### 기본 Gateway 설정 아래는 개별 구성 시나리오이며 모든 Route를 함께 적용하는 하나의 묶음이 아닙니다. 같은 호스트/리스너의 겹치는 Route는 우선순위를 바꿀 수 있습니다. 선택한 컨트롤러와 호환 CRD를 먼저 설치하고 `gateway-system`, 이름이 지정된 Service, 준비된 엔드포인트와 TLS Secret을 준비하세요. 인증서는 구성한 DNS 이름을 포함해야 합니다. 플랫폼에 맞는 데이터 플레인 Service 노출, DNS와 네트워크 제어도 필요합니다. GatewayClass나 요청한 IP 주소만으로 외부 주소가 예약되지는 않습니다. HTTP/gRPC/TCP/TLS 예제는 Istio 1.31을 사용합니다. Istio 1.31은 UDP 리스너를 명시적으로 거부하므로 UDP 예제는 별도 Envoy Gateway를 사용합니다. Envoy Gateway 1.9에는 Gateway API 1.6.1과 공식 Kubernetes 버전 조합이 필요합니다. 공유 CRD의 버전/채널을 변경하기 전에 다른 컨트롤러를 검토하세요. 기본 예제의 Namespace에는 `gateway-access: "true"`가 있습니다. 이는 **Namespace 레이블**이며 변경 권한은 Gateway 접근을 관리하는 운영자가 통제해야 합니다. `allowedRoutes`는 애플리케이션 클라이언트를 인증하지 않습니다. ```yaml apiVersion: v1 kind: Namespace metadata: name: production labels: gateway-access: 'true' --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: production-gateway namespace: gateway-system spec: gatewayClassName: istio listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - kind: Secret name: tls-cert namespace: gateway-system allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' ``` ### 고급 Gateway 설정 다음은 `multi-protocol-gateway`라는 별도 Gateway입니다. 아래 gRPC, TLS, TCP Route는 이 Gateway의 일치하는 리스너 이름에 연결됩니다. 데이터베이스와 다른 TCP 예제는 별도 리스너를 사용하므로 하나의 L4 리스너에서 경쟁하지 않습니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: multi-protocol-gateway namespace: gateway-system spec: gatewayClassName: istio listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' - name: https-wildcard protocol: HTTPS port: 443 hostname: '*.example.com' tls: mode: Terminate certificateRefs: - kind: Secret name: wildcard-cert allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' kinds: - kind: HTTPRoute - name: grpc protocol: HTTPS port: 443 hostname: grpc.example.com tls: mode: Terminate certificateRefs: - kind: Secret name: grpc-cert allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' kinds: - kind: GRPCRoute - name: tcp-passthrough protocol: TLS port: 8443 tls: mode: Passthrough allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' kinds: - kind: TLSRoute - name: tcp protocol: TCP port: 9000 allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' kinds: - kind: TCPRoute - name: database protocol: TCP port: 5432 allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' kinds: - kind: TCPRoute ``` ### TLS 모드 Terminate는 게이트웨이에서 다운스트림 TLS 연결을 종료합니다. 백엔드 연결은 별도 설정이며 지원되는 BackendTLSPolicy 등으로 HTTP 또는 TLS를 사용할 수 있습니다. Passthrough는 `TLS` 리스너의 `mode: Passthrough`를 사용하고 백엔드가 TLS를 종료합니다. `HTTPS` 리스너의 mode만 바꿔 passthrough로 사용할 수는 없습니다. | 모드 | 설명 | 사용 사례 | |------|------|----------| | **Terminate** | Gateway에서 TLS 종료 | 일반적인 HTTPS | | **Passthrough** | TLS를 백엔드로 전달 | End-to-end 암호화 | ```yaml # TLS Terminate 예시 listeners: - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - kind: Secret name: server-cert --- # TLS Passthrough 예시 listeners: - name: tls-passthrough protocol: TLS port: 443 tls: mode: Passthrough ``` ## HTTPRoute HTTPRoute는 HTTP/HTTPS 트래픽의 라우팅 규칙을 정의합니다. ### 기본 HTTPRoute ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: basic-route namespace: production spec: # 연결할 Gateway parentRefs: - name: production-gateway namespace: gateway-system sectionName: https # 특정 리스너 지정 # 호스트 매칭 hostnames: - "api.example.com" - "www.example.com" # 라우팅 규칙 rules: - matches: - path: type: PathPrefix value: /api/v1 backendRefs: - name: api-v1-service port: 80 - matches: - path: type: PathPrefix value: /api/v2 backendRefs: - name: api-v2-service port: 80 # 기본 경로 - backendRefs: - name: default-service port: 80 ``` ### 고급 매칭 규칙 한 `matches` 항목의 필드는 AND, 여러 항목은 OR입니다. PathPrefix는 임의 문자열 접두사가 아니라 경로 요소를 매칭합니다. RegularExpression 지원과 문법은 구현체별입니다. 아래 데모 tenant 헤더는 클라이언트가 임의로 제공할 수 있는 라우팅 선택 조건이며 관리 애플리케이션의 인증 수단이 아닙니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: advanced-matching namespace: production spec: parentRefs: - name: production-gateway namespace: gateway-system sectionName: https hostnames: - api.example.com rules: - matches: - path: type: Exact value: /health backendRefs: - name: health-service port: 80 - matches: - path: type: RegularExpression value: /users/[0-9]+ backendRefs: - name: user-service port: 80 - matches: - headers: - name: X-Version value: v2 backendRefs: - name: api-v2-service port: 80 - matches: - queryParams: - name: debug value: 'true' backendRefs: - name: debug-service port: 80 - matches: - method: POST path: type: PathPrefix value: /api/data backendRefs: - name: write-service port: 80 - matches: - method: GET path: type: PathPrefix value: /api/data backendRefs: - name: read-service port: 80 - matches: - path: type: PathPrefix value: /admin headers: - name: X-Demo-Tenant type: Exact value: operations backendRefs: - name: admin-service port: 80 - matches: - path: type: PathPrefix value: /api - path: type: PathPrefix value: /v1 backendRefs: - name: api-service port: 80 ``` ### 필터 (Filters) 헤더 수정자는 리터럴 값을 설정합니다. `X-Example-Source: gateway-demo`는 고정 표식이며 생성된 고유 요청 ID가 아닙니다. ID에는 프록시/애플리케이션의 추적 기능을 사용하세요. 미러링은 별도의 `/mirror` 경로를 사용하여 앞의 `/api` 규칙에 가려지지 않습니다. GET 요청을 섀도우 백엔드로 복사하고 그 백엔드 응답은 무시합니다. 부작용을 격리하고 섀도우 서비스에 복사되는 데이터·자격 증명을 검토하세요. public 캐시 헤더는 실제로 공개 캐시해도 되는 콘텐츠에만 적합합니다. 필터를 사용하여 요청/응답을 수정할 수 있습니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: filtered-route namespace: production spec: parentRefs: - name: production-gateway namespace: gateway-system sectionName: https rules: - matches: - path: type: PathPrefix value: /api filters: - type: RequestHeaderModifier requestHeaderModifier: add: - name: X-Example-Source value: gateway-demo set: - name: X-Api-Version value: v1 remove: - X-Internal-Header backendRefs: - name: api-service port: 80 - matches: - path: type: PathPrefix value: /public filters: - type: ResponseHeaderModifier responseHeaderModifier: add: - name: Cache-Control value: public, max-age=3600 set: - name: X-Content-Type-Options value: nosniff backendRefs: - name: public-service port: 80 - matches: - path: type: PathPrefix value: /old-api filters: - type: URLRewrite urlRewrite: path: type: ReplacePrefixMatch replacePrefixMatch: /new-api hostname: new-api.example.com backendRefs: - name: new-api-service port: 80 - matches: - path: type: PathPrefix value: /legacy filters: - type: RequestRedirect requestRedirect: scheme: https hostname: new.example.com port: 443 statusCode: 301 path: type: ReplacePrefixMatch replacePrefixMatch: /modern - matches: - method: GET path: type: PathPrefix value: /mirror filters: - type: RequestMirror requestMirror: backendRef: name: shadow-service port: 80 backendRefs: - name: main-service port: 80 ``` ### 트래픽 분할 (가중치) ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: canary-route namespace: production spec: parentRefs: - name: production-gateway namespace: gateway-system sectionName: https hostnames: - app.example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: app-stable port: 80 weight: 90 - name: app-canary port: 80 weight: 10 ``` ### 타임아웃 및 재시도 v1.6 Standard 스키마에는 `timeouts`가 있지만 `HTTPRoute.rules.retry`는 없습니다. Experimental 스키마에는 재시도 필드가 추가되며 별도의 admission·구현체 요구사항이 있습니다. 아래 예제는 GET 요청의 시간 예산만 설정합니다. `backendRequest`는 0이 아닌 전체 `request` 예산을 넘을 수 없습니다. 재시도 필드를 생략했다고 클라이언트, Gateway, 메시 프록시나 SDK가 재시도하지 않는다는 뜻은 아닙니다. 특히 비멱등 쓰기는 적용되는 각 계층을 구성·검증하세요. Experimental v1.6의 `retry.attempts` 최소값은 1이므로 0을 넣어 재시도를 비활성화할 수 없습니다. 구현체의 문서화된 제어와 애플리케이션 멱등성 동작을 사용하세요. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: resilient-route namespace: production spec: parentRefs: - name: production-gateway namespace: gateway-system sectionName: https rules: - matches: - path: type: PathPrefix value: /api method: GET timeouts: request: 30s backendRequest: 25s backendRefs: - name: api-service port: 80 ``` ## GRPCRoute 백엔드는 예상한 gRPC/HTTP2 전송 방식과 적절한 TLS 구성을 제공해야 합니다. 포트 번호만으로 그 동작이 설정되지는 않습니다. gRPC 트래픽을 위한 라우팅 규칙을 정의합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: GRPCRoute metadata: name: grpc-route namespace: production spec: parentRefs: - name: multi-protocol-gateway namespace: gateway-system sectionName: grpc hostnames: - grpc.example.com rules: - matches: - method: service: myapp.UserService backendRefs: - name: user-grpc-service port: 50051 - matches: - method: service: myapp.OrderService method: CreateOrder backendRefs: - name: order-grpc-service port: 50052 - matches: - headers: - name: x-environment value: staging backendRefs: - name: staging-grpc-service port: 50051 - backendRefs: - name: default-grpc-service port: 50051 ``` ## TCPRoute TCP 트래픽 라우팅을 정의합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: TCPRoute metadata: name: database-route namespace: production spec: parentRefs: - name: multi-protocol-gateway namespace: gateway-system sectionName: database rules: - backendRefs: - name: database-service port: 5432 --- apiVersion: gateway.networking.k8s.io/v1 kind: TCPRoute metadata: name: tcp-loadbalance namespace: production spec: parentRefs: - name: multi-protocol-gateway namespace: gateway-system sectionName: tcp rules: - backendRefs: - name: tcp-backend-1 port: 9000 weight: 50 - name: tcp-backend-2 port: 9000 weight: 50 ``` ## TLSRoute TLS passthrough 트래픽 라우팅을 정의합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: TLSRoute metadata: name: tls-passthrough-route namespace: production spec: parentRefs: - name: multi-protocol-gateway namespace: gateway-system sectionName: tcp-passthrough hostnames: - secure.example.com rules: - backendRefs: - name: secure-backend port: 8443 ``` ## UDPRoute 이 시나리오에는 설치된 Envoy Gateway 컨트롤러, 수락된 `envoy-gateway` 클래스, 호환 Gateway API 번들과 `dns-service` UDP 백엔드가 필요합니다. UDP 5300을 노출하여 백엔드 53으로 전달합니다. Envoy UDP 프록시는 투명 프록시가 아니므로 백엔드는 Gateway의 소스 IP/포트를 봅니다. 플랫폼 로드밸런서/Service가 이 UDP 노출을 지원하는지도 확인하세요. UDP 트래픽 라우팅을 정의합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: udp-gateway namespace: gateway-system spec: gatewayClassName: envoy-gateway listeners: - name: udp protocol: UDP port: 5300 allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' kinds: - kind: UDPRoute --- apiVersion: gateway.networking.k8s.io/v1 kind: UDPRoute metadata: name: dns-route namespace: production spec: parentRefs: - name: udp-gateway namespace: gateway-system sectionName: udp rules: - backendRefs: - name: dns-service port: 53 ``` ## ReferenceGrant ReferenceGrant는 **참조 대상 Service나 Secret이 있는 네임스페이스**에서 그 소유자가 생성합니다. `from`은 소스 group/kind/namespace를 고르고 `to.name`으로 대상 이름을 제한할 수 있습니다. Grant는 가산적으로 적용되며 애플리케이션 호출자가 아니라 참조를 인가합니다. 네임스페이스가 다른 Route→Gateway 연결은 ReferenceGrant 대신 `parentRefs`와 Gateway 리스너의 `allowedRoutes` 상호 허용을 사용합니다. 백엔드·인증서 참조는 아래처럼 ReferenceGrant를 사용합니다. 지정한 `shared-api` Service와 `shared-tls` Secret은 실제 존재해야 하며 Grant가 이를 만들지는 않습니다. ReferenceGrant를 사용하여 크로스 네임스페이스 참조를 허용합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: name: allow-routes-to-backend namespace: backend-services spec: from: - group: gateway.networking.k8s.io kind: HTTPRoute namespace: production - group: gateway.networking.k8s.io kind: HTTPRoute namespace: staging to: - group: '' kind: Service name: shared-api --- apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: name: allow-gateway-to-secrets namespace: cert-management spec: from: - group: gateway.networking.k8s.io kind: Gateway namespace: gateway-system to: - group: '' kind: Secret name: shared-tls ``` ## 구현체 비교 ### 주요 구현체 | 구현체 | 컨트롤러 | 특징 | |--------|---------|------| | **Istio** | istio.io/gateway-controller | Service Mesh 통합, 고급 트래픽 관리 | | **Cilium** | io.cilium/gateway-controller | Cilium 네트워킹과 Envoy L7 처리 | | **Envoy Gateway** | gateway.envoyproxy.io/gatewayclass-controller | Envoy 기반, 표준 준수 | | **AWS Gateway API Controller** | application-networking.k8s.aws/gateway-api-controller | VPC Lattice 통합 | | **Contour** | projectcontour.io/gateway-controller | Envoy 기반, 간단한 설정 | | **NGINX Gateway Fabric** | gateway.nginx.org/nginx-gateway-controller | NGINX 기반 | | **Traefik** | traefik.io/gateway-controller | 동적 설정 | ### 버전을 명시한 구현체 비교 API 릴리스 채널, 기능의 Core/Extended/구현체별 지원 수준, 컨트롤러의 적합성 프로파일은 서로 다른 개념입니다. CRD가 필드를 수락한다고 컨트롤러의 구현이 증명되지는 않습니다. 공개된 적합성 결과와 해당 리소스의 `Accepted`, `ResolvedRefs`, `Programmed` 등 상태를 확인하세요. | 확인한 구현체 | 검증된 범위와 주요 제약 | |---|---| | Istio **1.31.0** | HTTP/gRPC와 v1 TCP/TLS Route. UDP 리스너는 명시적으로 미지원. 구성 가능한 Envoy 데이터 플레인이지만 모든 Gateway API 확장 지원을 보장하지 않음 | | Cilium **1.20.1** | TCPRoute/UDPRoute를 포함한 Gateway API **1.6.1**. Cilium 네트워킹과 L7 처리용 Envoy를 함께 사용 | | Envoy Gateway **1.9.1** | Gateway API **1.6.1**, 공개된 Kubernetes 매트릭스는 **1.33–1.36**. 문서화된 전송 동작에 따른 UDP 라우팅과 TLS passthrough 지원 | | AWS Load Balancer Controller **3.5.0** | Gateway API **1.6.0**. ALB는 HTTP/gRPC, NLB는 L4 Route 처리. NLB 리스너별 가장 오래된 L4 Route만 적격이므로 리스너당 Route 하나 사용 | | AWS Gateway API Controller **2.1.3** | VPC Lattice 연동. v2.1은 Gateway API **1.5 이상** 요구. HTTPRoute, GRPCRoute, TLSRoute 지원. TCP 리소스 접근은 별도 Lattice 리소스 구성 모델이며 일반 TCPRoute/UDPRoute 지원을 의미하지 않음 | | Contour **1.33.7** | Gateway API **1.3.0**으로 빌드되고 릴리스에서 Kubernetes **1.32–1.34** 시험. HTTP/gRPC/TCP/TLS Route를 문서화하며 최신 번들을 임의 적용하지 말고 맞는 채널/프로비저닝 구성 사용 | | NGINX Gateway Fabric **2.7.0** | Gateway API **1.6.1**, 문서상 최소 Kubernetes **1.32**. v1 TCPRoute/UDPRoute 지원 추가. 지원 종료된 community ingress-nginx와 별도 제품 | 버전 없는 지원/미지원 표 대신 확인한 버전의 범위를 제시합니다. 개별 필터, TLS 정책, 확장과 운영 요구사항은 구현체 문서를 확인하세요. Contour에 포함된 호환성 페이지에는 1.33.7 전용 행이 없어, 위 API 의존성과 Kubernetes 범위는 정확한 릴리스의 모듈 파일과 릴리스 노트에서 확인했습니다. ## AWS Load Balancer Controller의 Gateway API 지원 Gateway API는 **2026-01-23의 LBC v3.0.0**에서 GA가 되었습니다. 기존 Ingress/Service API도 계속 지원하므로 컨트롤러 업그레이드와 Gateway 마이그레이션은 별도로 계획할 수 있습니다. 현재 v3.5.0의 호환 Gateway API 및 LBC Gateway CRD 요구사항은 [LBC 설치 문서](https://www.atomai.click/kubernetes-docs/llms/ko/networking/03-aws-lb-controller.md)를 따르세요. EKS Auto Mode는 별도 관리형 구현이며 직접 관리하는 LBC의 기능을 그대로 Auto Mode 설명으로 사용할 수 없습니다. 지원이 종료된 컨트롤러는 2026년 3월 유지보수가 끝난 Kubernetes community **ingress-nginx**입니다. Kubernetes Ingress API나 F5의 다른 NGINX 제품이 종료되었다는 뜻은 아닙니다. v3.0 릴리스 노트의 `keepTLSSecret=false` 우회 방법은 cert-manager 소유권 버그가 있는 **이전 버전에 남는 사용자**에게 적용되었습니다. v3.0으로 업그레이드하면 추가 작업 없이 수정이 포함됩니다. 과거의 우회 방법을 모든 업그레이드에 적용하지 말고 현재 차트의 인증서 관리 옵션을 따르세요. ### LBC v3.4.0 마이그레이션 도구 **2026-06-03** 릴리스에서 실제 `lbc-migrate` CLI와 Migration Console이 추가되었습니다. 정상 동작 중인 **LBC Ingress**가 대상이며 모든 Ingress 구현을 위한 범용 변환기는 아닙니다. - `lbc-migrate`는 파일을 읽거나 `--from-cluster`로 클러스터 리소스를 list/get합니다. 지원 어노테이션을 변환해 Gateway API 리소스를 출력하고, 기본 출력에는 LBC Gateway dry-run 어노테이션이 있습니다. - Migration Console은 컨트롤러가 생성한 리소스 계획을 비교합니다. 해당 계획 어노테이션, 기능 구성과 읽기 권한이 필요합니다. 계획도 접근 제한·민감 정보 제거가 필요할 수 있는 구성 데이터로 다루세요. - 검토한 live Gateway 매니페스트를 적용하면 **기존 ALB 옆에 새 ALB**를 만듭니다. 새 ALB 검증 후 프런트엔드 트래픽을 별도로 전환합니다. 한 HTTPRoute의 백엔드 가중치가 이 프런트엔드 이전을 수행하지는 않습니다. 선택한 LBC 릴리스에서 빌드한 바이너리가 있으면 파일 기반 변환을 다음처럼 시작할 수 있습니다. ```bash lbc-migrate -f ingress.yaml --output-dir ./gateway-output/ ``` 변환기는 기존 Deployment/Service를 만들거나 모든 Ingress 어노테이션을 재검증하지 않습니다. 미지원 어노테이션, Service/IngressClassParams 재정의, 네임스페이스 간 IngressGroup 구성원, 규칙 우선순위와 TLS를 검토하세요. 기존 ALB에 연결된 외부 대상 그룹을 새 ALB에도 그대로 동시에 연결할 수 없으므로 호환되는 복제/전환 전략이 필요합니다. 도구는 마이그레이션 절차를 제공하며 무중단을 보장하지 않습니다. [버전 고정 마이그레이션 가이드](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/ingress2gateway/migrate_from_ingress.md)와 [CLI 참조](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v3.5.0/docs/guide/ingress2gateway/lbc_migrate_reference.md)를 확인하세요. ## Ingress에서 Gateway API로 마이그레이션 ### 단계별 마이그레이션 가이드 #### 1단계: 기존 Ingress 분석 다음은 Istio Gateway API로 수동 변환하는 방법을 설명하기 위한 **과거 community ingress-nginx 입력**입니다. 새 ingress-nginx 설치 권장이나 앞의 LBC 전용 변환기 입력이 아닙니다. 설정 이름뿐 아니라 실제 요청 동작을 보존해야 합니다. ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/ssl-redirect: 'true' namespace: default spec: tls: - hosts: - api.example.com secretName: api-tls rules: - host: api.example.com http: paths: - path: /api/v1 pathType: Prefix backend: service: name: api-v1 port: number: 80 - path: /api/v2 pathType: Prefix backend: service: name: api-v2 port: number: 80 ``` #### 2단계: Gateway 및 GatewayClass 생성 새 Gateway 이름은 `migration-gateway`입니다. 기존 `api-tls` Secret은 `default`에 유지하며, 그 네임스페이스의 ReferenceGrant가 Gateway 네임스페이스의 참조를 명시적으로 허용합니다. `api.example.com`에 유효한 인증서가 필요합니다. Route 연결 셀렉터에는 `default` Namespace의 기본 이름 레이블을 사용합니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: production spec: controllerName: istio.io/gateway-controller --- apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: name: migration-tls namespace: default spec: from: - group: gateway.networking.k8s.io kind: Gateway namespace: gateway-system to: - group: '' kind: Secret name: api-tls --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: migration-gateway namespace: gateway-system spec: gatewayClassName: production listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: Selector selector: matchLabels: kubernetes.io/metadata.name: default hostname: api.example.com - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - kind: Secret name: api-tls namespace: default allowedRoutes: namespaces: from: Selector selector: matchLabels: kubernetes.io/metadata.name: default hostname: api.example.com ``` #### 3단계: HTTPRoute 생성 리다이렉트 Route는 HTTP 리스너에만 연결하고 HTTPS Route는 애플리케이션 요청을 전달합니다. 기존 `rewrite-target: /` 예제는 매칭한 요청 경로 전체를 `/`로 바꾸므로 변환 예제는 **ReplaceFullPath**를 사용합니다. ReplacePrefixMatch는 접미사를 보존하여 `/api/v1/users`를 `/users`로 보내므로 동작이 달라집니다. 전환 전에 루트·하위 경로, 쿼리 문자열과 리다이렉트를 기존 애플리케이션과 비교하세요. 아래 리다이렉트는 요청 메서드/본문을 보존하는 ingress-nginx 기본 **308**을 가정합니다. `http-redirect-code` 재정의를 확인하세요. 기존 rewrite 어노테이션은 해당 호스트에 대소문자를 구분하지 않는 정규식 location도 활성화하지만 Gateway API PathPrefix는 대소문자를 구분하고 경로 요소를 매칭합니다. 따라서 `/API/V1`, `/api/v10` 등의 결과는 다를 수 있습니다. 예제는 더 엄격한 PathPrefix 정책을 보여주며 완전한 매칭 동등성을 보장하지 않습니다. 기존 동작에 의존하는 클라이언트가 있으면 전환 전에 지원되는 정규식 매치나 명시적 호환 규칙을 설계·시험하세요. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api-route namespace: default spec: parentRefs: - name: migration-gateway namespace: gateway-system sectionName: http hostnames: - api.example.com rules: - matches: - path: type: PathPrefix value: / filters: - type: RequestRedirect requestRedirect: scheme: https statusCode: 308 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: api-route-https namespace: default spec: parentRefs: - name: migration-gateway namespace: gateway-system sectionName: https hostnames: - api.example.com rules: - matches: - path: type: PathPrefix value: /api/v1 filters: - type: URLRewrite urlRewrite: path: type: ReplaceFullPath replaceFullPath: / backendRefs: - name: api-v1 port: 80 - matches: - path: type: PathPrefix value: /api/v2 filters: - type: URLRewrite urlRewrite: path: type: ReplaceFullPath replaceFullPath: / backendRefs: - name: api-v2 port: 80 ``` #### 4단계: 프런트엔드 트래픽 전환 클라이언트를 옮기기 전에 새 Gateway 주소, 인증서, HTTP 리다이렉트, 경로 매칭, 백엔드 동작과 관측성을 검증하세요. 검토된 DNS/로드밸런서 라우팅 등 해당 프런트엔드에 맞는 방식으로 트래픽을 옮기고 실패와 지연 시간을 관측합니다. DNS 캐시, 지속 연결과 세션을 고려하고 이전 프런트엔드로 되돌리는 검증된 경로를 유지하세요. HTTPRoute 백엔드 가중치는 **선택된 Gateway 내부**의 트래픽을 제어하며 전용 트래픽 분할 절에서 설명합니다. 프런트엔드 이전에서는 클라이언트 전환과 필요한 드레이닝/롤백 검증이 끝날 때까지 기존 Ingress/컨트롤러를 유지하세요. ### 마이그레이션 체크리스트 - [ ] 기존 Ingress annotation 분석 - [ ] 해당 구현체 선택 및 GatewayClass 생성 - [ ] Gateway 리소스 생성 및 리스너 설정 - [ ] HTTPRoute로 라우팅 규칙 변환 - [ ] 연결은 allowedRoutes, 백엔드/Secret 참조는 ReferenceGrant로 구성 - [ ] TLS 인증서 마이그레이션 - [ ] 검증된 롤백 경로와 함께 프런트엔드 트래픽 검증·전환 - [ ] 모니터링 및 로깅 설정 - [ ] 전환 및 드레이닝/롤백 검증 후 기존 Ingress 리소스 제거 ## EKS 패턴 ### AWS Gateway API Controller (VPC Lattice) [VPC Lattice 문서](https://www.atomai.click/kubernetes-docs/llms/ko/networking/02-vpc-lattice.md)의 설치된 컨트롤러, 검토된 `AWS_IAM` 정책이 있는 `my-network` 서비스 네트워크, 호출자 권한과 `service-stable:8080` 백엔드를 사용합니다. Gateway 이름은 네트워크를 선택하며 생성하지 않습니다. 이 별도 Route에는 고유 Lattice 서비스와 도메인이 생깁니다. 아래 IAMAuthPolicy는 해당 서비스를 보호하며 조정 중 네트워크 정책도 유지해야 합니다. Route의 할당 도메인을 조회하고 본문의 서명된 HTTPS 클라이언트를 사용하세요. `unused` 인증서 참조는 이 컨트롤러의 문서화된 AWS 관리형 인증서 동작이며 일반적인 Kubernetes Secret 로딩 방식이 아닙니다. ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: amazon-vpc-lattice spec: controllerName: application-networking.k8s.aws/gateway-api-controller --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: my-network namespace: lattice-demo spec: gatewayClassName: amazon-vpc-lattice listeners: - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - name: unused --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: lattice-route namespace: lattice-demo spec: parentRefs: - name: my-network sectionName: https rules: - matches: - path: type: PathPrefix value: /api backendRefs: - name: service-stable port: 8080 --- apiVersion: application-networking.k8s.aws/v1alpha1 kind: IAMAuthPolicy metadata: name: lattice-route-auth namespace: lattice-demo spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: lattice-route policy: '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789012:role/MyAppRole"},"Action":"vpc-lattice-svcs:Invoke","Resource":"*","Condition":{"StringLike":{"vpc-lattice-svcs:RequestPath":["/api","/api/*"]}}}]}' ``` ### ALB Controller와 함께 사용 이 구성은 ALB 뒤에서 Istio Gateway 동작이 필요한 애플리케이션을 위한 구조입니다. ConfigMap은 Istio의 문서화된 infrastructure 매개변수로 생성 Service를 ClusterIP로 설정합니다. ALB Ingress는 해당 Service와 **같은 네임스페이스**에 있고 생성 이름인 `internal-gateway-istio`를 참조합니다. 애플리케이션 HTTPRoute는 레이블이 있는 `production` 네임스페이스에서 기존 `api-service:80`을 참조합니다. ACM ARN과 LBC/네트워크 사전 조건을 맞추세요. 예제 TLS는 ALB에서 종료하며 Istio 구간은 HTTP입니다. 보안 제어에서 실제 게이트웨이 트래픽·상태 검사 포트를 허용해야 합니다. 15021의 `/healthz/ready`는 Gateway 준비 상태를 확인하며 모든 애플리케이션의 상태 검사는 아닙니다. Route 상태와 애플리케이션 응답을 별도로 확인하세요. ```yaml apiVersion: v1 kind: ConfigMap metadata: name: internal-gateway-options namespace: istio-system data: service: | spec: type: ClusterIP --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: internal-gateway namespace: istio-system spec: gatewayClassName: istio listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: Selector selector: matchLabels: gateway-access: 'true' infrastructure: parametersRef: group: '' kind: ConfigMap name: internal-gateway-options --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: alb-internal-route namespace: production spec: parentRefs: - name: internal-gateway namespace: istio-system sectionName: http hostnames: - api.example.com rules: - backendRefs: - name: api-service port: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: alb-to-gateway annotations: alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80},{"HTTPS":443}]' alb.ingress.kubernetes.io/ssl-redirect: '443' alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:123456789012:certificate/12345678-1234-1234-1234-123456789012 alb.ingress.kubernetes.io/healthcheck-port: '15021' alb.ingress.kubernetes.io/healthcheck-path: /healthz/ready namespace: istio-system spec: ingressClassName: alb rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: internal-gateway-istio port: number: 80 ``` ## API 채널 및 성숙도 ### 채널과 API 버전의 구분 | Gateway API v1.6.0 번들 | 포함 리소스/필드 | |---|---| | Standard | GatewayClass, Gateway, HTTPRoute, GRPCRoute, TLSRoute, TCPRoute, UDPRoute, ReferenceGrant, BackendTLSPolicy, ListenerSet | | Experimental | Standard 내용에 HTTPRoute retry/session persistence 같은 실험 필드와 XBackend, XBackendTrafficPolicy, XMesh 추가 | 현재 Standard 예제의 Route 유형은 `v1`을 사용합니다. v1.6.0 Standard 번들은 이전 TLSRoute/TCPRoute/UDPRoute alpha 버전을 더 이상 **제공하지 않습니다**. Experimental 번들은 일부 deprecated 버전을 계속 제공하므로 거기에서 동작하는 매니페스트가 Standard에서도 동작한다고 판단할 수 없습니다. ReferenceGrant는 “Standard이면 v1만 사용한다”의 반례입니다. v1.6.0 번들은 `v1`과 `v1beta1`을 모두 제공하며 저장 버전은 `v1beta1`입니다. 이 문서의 ReferenceGrant 예제는 제공 중인 beta 버전을 유지합니다. 새 실험적 X 리소스는 `gateway.networking.x-k8s.io`를 사용합니다. 기존 리소스의 실험 필드는 여전히 `gateway.networking.k8s.io`에 있을 수 있으므로 Experimental 전체가 다른 그룹으로 옮겨진 것은 아닙니다. Standard와 호환성 보장도 다르며 admission 정책이 채널/필드 경계를 보호합니다. 변경을 강제하려고 공유 CRD나 admission 정책을 삭제하지 말고 공개된 업그레이드 절차를 검토하세요. ### v1.6 릴리스 맥락 Gateway API v1.6.0은 **UTC 2026-06-29 / KST 2026-06-30**에 공개되었습니다. TCPRoute와 UDPRoute가 Standard `v1`으로 승격되었고 GRPCRoute와 TLSRoute도 현재 Standard 번들에 포함됩니다. 최신 카탈로그 버전을 호환성과 동일시하지 말고 선택한 구현체가 지원하는 번들/채널을 사용하세요. ## Ingress API 비교 | 항목 | Ingress | Gateway API | |---|---|---| | 리소스 모델 | Ingress와 IngressClass, 리스너·라우팅 책임이 상당 부분 결합 | GatewayClass, Gateway와 별도 Route 유형 | | 인가 | Kubernetes RBAC/admission으로 소유권 제한 가능 | RBAC/admission과 명시적인 연결·참조 상호 허용 | | HTTP 라우팅 | 표준 HTTP 라우팅 | HTTPRoute 표준 필드와 개별 기능 지원 수준 | | TCP/UDP/gRPC | Ingress API 외 컨트롤러별 확장 | 전용 API 유형, 실제 지원은 컨트롤러/버전에 따라 다름 | | TLS passthrough / 분할 / 리라이트 | 컨트롤러별 구성 | 관련 Route/필터 필드와 구현체 지원 요구사항 | | 크로스 네임스페이스 참조 | 구현체별 동작 | 백엔드/Secret은 ReferenceGrant, Gateway 연결은 allowedRoutes | | 이식성 | 어노테이션 의미 차이의 영향 | 표준 필드·적합성으로 개선되지만 확장은 여전히 다름 | ## 모범 사례 ### 1. 역할 분리 준수 ```yaml # 인프라 팀: GatewayClass 관리 # 플랫폼 팀: Gateway 관리 # 앱 팀: HTTPRoute 관리 ``` ### 2. ReferenceGrant 최소 권한 ```yaml # 필요한 네임스페이스만 명시적으로 허용 apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: name: minimal-access namespace: backend spec: from: - group: gateway.networking.k8s.io kind: HTTPRoute namespace: frontend # 특정 네임스페이스만 to: - group: "" kind: Service name: specific-service # 특정 서비스만 ``` ### 3. Gateway 분리 ```yaml # 환경별 Gateway 분리 # production-gateway, staging-gateway # 프로토콜별 Gateway 분리 # http-gateway, grpc-gateway ``` ### 4. 모니터링 설정 ```yaml # Prometheus 메트릭 수집 설정 (구현체별 상이) # - 요청 수, 지연 시간, 오류율 # - 백엔드 상태 # - TLS 인증서 만료 ``` --- ## 참고 자료 - [Gateway API 공식 문서](https://gateway-api.sigs.k8s.io/) - [Gateway API GitHub](https://github.com/kubernetes-sigs/gateway-api) - [Istio Gateway API 지원](https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/) - [Cilium Gateway API](https://github.com/cilium/cilium/blob/v1.20.1/Documentation/network/servicemesh/gateway-api/gateway-api.rst) - [AWS Gateway API Controller](https://github.com/aws/aws-application-networking-k8s/tree/v2.1.3/docs) - [Envoy Gateway](https://gateway.envoyproxy.io/) - [Gateway API 1.6 versioning](https://github.com/kubernetes-sigs/gateway-api/blob/v1.6.0/site/content/en/docs/concepts/versioning.md) - [ReferenceGrant and attachment exceptions](https://github.com/kubernetes-sigs/gateway-api/blob/v1.6.0/site/content/en/reference/api-types/referencegrant.md) - [Envoy Gateway compatibility](https://github.com/envoyproxy/gateway/blob/v1.9.1/site/content/en/news/releases/matrix.md) - [NGINX Gateway Fabric 2.7 release](https://github.com/nginx/nginx-gateway-fabric/blob/v2.7.0/CHANGELOG.md) - [Contour 1.33.7 release](https://github.com/projectcontour/contour/releases/tag/v1.33.7) - [Community ingress-nginx retirement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/) - [Legacy ingress-nginx redirect and rewrite behavior](https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md) - [Legacy ingress-nginx redirect-code configuration](https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/configmap.md) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/05-cross-org-vpc-connectivity ---------------------------------------- # Cross-Org VPC 연결 > **원 보고서 표기일**: 2026년 9월 1일 > > **마지막 업데이트**: 2026년 9월 12일 기존 환경과 별도로 관리하는 GPU 환경처럼 **서로 다른 AWS Organizations의 계정**을 연결하는 다섯 패턴을 비교합니다. 표의 측정값은 이전 문서가 보고한 값을 유지합니다. 이번 검토는 AWS 동작과 산술을 확인했으며 새 실배포나 벤치마크 재현을 수행했다고 주장하지 않습니다. ## 목차 1. [왜 Cross-Org 연결이 필요한가](#왜-cross-org-연결이-필요한가) 2. [5가지 연결 옵션 비교](#5가지-연결-옵션-비교) 3. [보고된 검증 결과](#보고된-검증-결과) 4. [Latency 실측 (M1~M7)](#latency-실측-m1m7) 5. [운영 시 확인할 사항](#운영-시-확인할-사항) 6. [요구사항별 아키텍처 선택](#요구사항별-아키텍처-선택) 7. [한계와 후속 검증](#한계와-후속-검증) ## 왜 Cross-Org 연결이 필요한가 계약상 소유권, 인수합병, 독립적인 거버넌스나 격리 요구로 GPU 워크로드와 기존 서비스가 서로 다른 Organization에 있을 수 있습니다. 조직 구조는 이 요구를 따라 결정해야 하며, Organization을 하나 더 만들면 GPU 할인·쿼터·규제 준수가 자동으로 개선된다고 가정하면 안 됩니다. EC2 리소스 쿼터는 일반적으로 **계정과 리전** 기준이므로 다른 Organization 없이도 계정을 분리해 해당 범위를 나눌 수 있습니다. 결제 통합, 협상된 할인과 거버넌스 중복 비용도 검토하세요. Organization 경계가 애플리케이션 인가, 네트워크 분리나 감사 제어를 대체하지는 않습니다. EKS에서는 데이터 파이프라인/추론 API의 일반 IP 접근과 GPU 집단 통신을 구분해야 합니다. CPU 인스턴스의 요청/응답 벤치마크는 NCCL, 처리량이나 RDMA 성능을 증명하지 않습니다. **EFA OS-bypass 트래픽은 VPC나 가용 영역을 넘을 수 없으며**, ENA 인터페이스의 일반 IP 트래픽은 라우팅할 수 있습니다. ## 5가지 연결 옵션 비교 PrivateLink와 Lattice 열은 **시험한 NLB 기반 엔드포인트 서비스와 HTTP 서비스 패턴**을 설명합니다. PrivateLink에는 리소스·서비스 네트워크 엔드포인트도 있고 Lattice에는 TCP 리소스 구성도 있습니다. 제품 전체가 각각 “NLB 필수”, “L7 전용”인 것은 아닙니다. | 항목 | ① TGW RAM 공유 | ② VPC Peering | ③ PrivateLink 엔드포인트 서비스 | ④ TGW Peering | ⑤ VPC Lattice HTTP 서비스 | |---|---|---|---|---|---| | 연결 방식 | 외부 계정에 TGW 공유 | VPC 쌍 직접 연결 | 소비자 인터페이스 엔드포인트 → 공급자 NLB/서비스 | 각 소유자의 TGW 연결 | 서비스와 클라이언트 VPC를 서비스 네트워크에 연결 | | 주소 중복 | 직접 라우팅에는 모호하지 않은 주소 계획 필요 | CIDR가 겹치는 VPC는 피어링 불가 | 중복 VPC CIDR 간 서비스 접근 가능 | 직접 라우팅에는 모호하지 않은 주소 계획 필요 | 중복 VPC CIDR 간 서비스 접근 가능 | | 연결 모델 | 허용된 양방향 IP 라우팅 | 허용된 양방향 IP 라우팅 | 소비자가 연결 시작, 같은 연결로 응답 가능 | 허용된 양방향 IP 라우팅 | 클라이언트가 공개한 서비스에 요청, 역방향 접근은 별도 구성 | | 라우팅 구성 | VPC 라우트와 TGW 테이블/연결 | 양측 라우트, VPC 전이 피어링 없음 | 일반 VPC 전이 대신 엔드포인트/서비스 권한과 네트워크 제어 | 피어 정적 라우트와 VPC 라우트 명시 | 일반 VPC 전이 대신 서비스/네트워크 연결과 정책 | | 통제 주체 | 소유자가 TGW 테이블 관리, 소비자는 자신의 VPC 제어 유지 | 각 VPC 소유자 | 공급자는 서비스 권한/대상, 소비자는 자신의 엔드포인트 제어 | 라우트를 조율하는 각 TGW 소유자 | 네트워크/서비스 소유자와 클라이언트 네트워크 제어 | | 원 보고서 구성 소요 | TGW 약 3분과 수락 절차 | 1분 미만 | 엔드포인트 약 3분 | 약 7분 | 약 5분 | 구성 시간은 원 보고서의 관측값이며 SLA나 전체 구축 기간 추정치가 아닙니다. 라우팅 행은 이 장의 두 TGW 구성을 설명하며 임의의 여러 피어 연결을 무제한 전이할 수 있다고 주장하지 않습니다. NAT나 주소 재설계도 중복 주소의 대안이며 별도 설계가 필요합니다. ## 보고된 검증 결과 원 보고서는 서로 다른 두 Organization에서 다섯 패턴을 구축하고 트래픽을 교환했다고 기술합니다. AWS 문서도 이 패턴의 계정 간 구성을 지원하며 같은 Organization이 필수인 것은 아닙니다. 다만 IAM/SCP/공유 제한은 구성을 차단할 수 있고 실제 통신에는 라우트, 보안 그룹, NACL, DNS와 서비스 인가가 적용됩니다. 계정 ID와 수락만으로 충분하지 않습니다. ![원 조직 간 토폴로지에서 Peering·TGW·PrivateLink는 TCP_RR p50, Lattice HTTP 서비스는 HTTP keep-alive p50을 표시합니다.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-05-cross-org-vpc-connectivity-0.png) [인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-05-cross-org-vpc-connectivity-0.html) 그림은 원 관측값을 유지합니다. Lattice 값은 **HTTP KA**, 나머지 표시 값은 **TCP_RR**이므로 동일 지표의 직접 비교가 아닙니다. “GPU”는 제안된 환경을 나타내며 GPU 벤치마크를 뜻하지 않습니다. ## Latency 실측 (M1~M7) **보고된 구성:** `ap-northeast-2`, 계정 간 같은 ZoneId `apne2-az1`, `c7g.large`, nginx로 고정 HTTP 200을 반환하는 EC2 응답자 한 대입니다. 보고서는 경로별 서브넷/리턴 라우트의 ENI 세 개, 라운드로빈 인터리브 다섯 라운드, 경로당 persistent TCP_RR 1,500개·ICMP 100개·HTTP keep-alive 275개 표본을 설명합니다. nginx 설명은 HTTP 응답자를 가리키며 이 페이지에는 TCP_RR 구현이나 메시지 크기가 명시되어 있지 않습니다. 원시 표본, 소프트웨어/커널 버전, 타이머 경계와 Linux 반환 경로 정책 설정도 연결되어 있지 않습니다. 지속 연결은 반복 설정 비용을 줄이려는 설계지만 표만으로 타이머 경계를 독립 검증할 수는 없습니다. **아래 지연 값은 모두 밀리초이며 TTL은 별도 패킷 필드입니다.** TCP_RR과 ICMP는 요청/응답 왕복 측정이고 HTTP KA에는 애플리케이션 처리가 포함됩니다. 두 측정 회차는 별도로 해석해야 합니다. | ID | 경로 | ICMP p50 | TCP_RR p50 | RR p99 | RR sd | HTTP KA p50 | TTL | |---|---|---|---|---|---|---|---| | M1 | 동일 VPC → EC2 (기준선) | 0.121 | **0.049** | 0.062 | 0.007 | 0.087 | 127 | | M2 | ② VPC Peering → EC2 | 0.125 | **0.048** | 0.057 | 0.011 | 0.080 | 127 | | M3 | ① 공유 TGW(RAM) → EC2 | 0.535 | **0.619** | 0.695 | 0.141 | 0.686 | 126 | | M4 | ④ TGW Peering(두 TGW) → EC2 | 0.912 | **0.599** | 0.855 | 0.133 | 0.488 | 125 | | M5 | ③ PrivateLink → NLB → EC2 | 미측정 | **0.961** | 1.084 | 0.035 | 0.711 | — | | M6 | ⑤ VPC Lattice → EC2 타깃 | 미측정 | 해당 HTTP 서비스에서는 미측정 | — | — | **1.635** | — | | M7 | ② Peering → NLB → EC2 (NLB 홉 분리) | 미측정 | **0.841** | 0.909 | 0.119 | 0.883 | — | ### 보고된 중앙값 간 차이 이는 **경로 중앙값의 차이**이며 독립적인 편도 홉 비용이나 ENI/프록시 구성 요소 자체의 측정값이 아닙니다. | 관측 경로 비교 | 차이 | Δ TCP_RR p50 | Δ ICMP p50 | Δ HTTP KA p50 | |---|---|---|---|---| | Peering과 동일 VPC 기준선 | M2 − M1 | -0.001 | +0.004 | -0.007 | | 공유 TGW 경로와 Peering | M3 − M2 | +0.571 | +0.410 | +0.606 | | 두 TGW 경로와 Peering | M4 − M2 | +0.551 | +0.787 | +0.408 | | NLB를 둔 Peering과 직접 Peering | M7 − M2 | +0.793 | — | +0.803 | | PrivateLink/NLB와 Peering/NLB | M5 − M7 | +0.120 | — | -0.172 | | Lattice HTTP 서비스와 직접 Peering HTTP | M6 − M2 | — | — | +1.555 | - M2는 동일 VPC 기준선에 가깝지만 표만으로 통계적 동등성이나 오버헤드 0을 증명할 수 없습니다. - 두 TGW 경로의 TCP_RR 중앙값은 공유 TGW 하나인 경로보다 낮습니다. 따라서 보편적인 “TGW 홉당 0.4~0.6ms”나 선형 홉 비용 공식은 이 자료로 뒷받침되지 않습니다. - M5−M7은 **TCP_RR +0.120ms, HTTP KA −0.172ms**입니다. 이를 순수 PrivateLink ENI 비용이라고 부를 수 없습니다. - Lattice 비교는 TCP_RR가 아니라 **HTTP +1.555ms**이며 이 HTTP 서비스 시험을 설명할 뿐 모든 Lattice 모드의 비용이 아닙니다. - 초기 TTL과 관련 네트워크 동작을 모르면 TTL만으로 경로 홉 수를 알 수 없습니다. ### 별도 서비스 프런트 측정 원 보고서는 각 L3 경로에 NLB를 둔 측정도 제시합니다. 해당 서비스 노출 패턴에는 유용하지만 모든 운영 Peering/TGW 배포에 NLB가 필요한 것은 아닙니다. | 구성 | TCP_RR p50 | HTTP KA p50 | |---|---|---| | ② Peering → NLB → EC2 | **0.622** | 0.648 | | ③ PrivateLink → NLB → EC2 | **0.658** | 0.845 | | ① 공유 TGW → NLB → EC2 | **1.273** | 1.257 | | ④ TGW Peering → NLB → EC2 | **1.425** | 1.279 | | ⑤ Lattice HTTP 서비스 (이 시험에서는 별도 NLB 없음) | — | **1.680** | 이 회차의 PrivateLink/NLB − Peering/NLB 차이는 **TCP_RR +0.036ms, HTTP KA +0.197ms**입니다. 공유 TGW와 peering TGW의 TCP_RR 중앙값은 PrivateLink의 각각 **1.93배, 2.17배**이고 HTTP 비율은 **1.49배, 1.51배**입니다. 이는 지연 비율이지 처리량 배수나 경로 동등성 증명이 아닙니다. Lattice HTTP 중앙값은 공유 TGW/NLB와 peering TGW/NLB보다 각각 **+0.423ms, +0.401ms** 높습니다. 같은 Peering/NLB 중앙값도 회차별로 다르므로 이 회차와 M1~M7을 섞어 구성 요소 비용을 산출하지 마세요. 원 보고서는 버스터블 인스턴스·NLB→ALB·매번 새 curl 연결을 사용한 폐기한 파일럿의 p95 약 **7ms**, 최초 흐름 증가분 **0.6~1.6ms**도 언급합니다. 연결된 원시 표본이 없는 보고서 관측값이며 AWS 보장이 아닙니다. 실제 애플리케이션의 연결 수립과 정상 상태 동작을 구분해 측정하세요. ## 운영 시 확인할 사항 1. **RAM 외부 공유:** 외부 principal이 허용되어야 하고 Organization 외부 계정은 공유 초대를 수락해야 합니다. `CreateResourceShare` API의 `allowExternalPrincipals` 기본값은 **true**입니다. `--allow-external-principals` 명시는 의도를 나타내지만 해당 CLI 플래그 생략이 항상 실패 원인은 아닙니다. 실제 공유 구성과 권한을 확인하세요. 2. **공유 TGW VPC attachment 수락:** 기본값처럼 `AutoAcceptSharedAttachments`가 비활성화되면 TGW 소유자가 공유 attachment를 수락해야 합니다. 활성화하면 흐름이 달라집니다. RAM 공유 수락과 TGW attachment 수락은 별도 단계입니다. 소비자는 소유자의 TGW 라우트 테이블을 바꿀 수 없지만 자신의 VPC 라우트와 보안 설정은 통제합니다. 3. **TGW peering 수락:** 같은 계정의 peering도 수락자 TGW 소유자가 **수락자 리전**에서 pending 요청을 수락합니다. 해당 요청의 `TransitGatewayAttachmentId`를 사용하고 TGW ID나 VPC attachment ID와 혼동하지 마세요. `NotFound` 응답만으로 양측이 다른 ID를 요구한다는 규칙을 만들 수는 없습니다. 원 보고서의 약 2분 가시성 지연은 관측값이며 고정 대기 시간 보장이 아닙니다. 4. **Peering 라우트:** 직접 TGW-to-TGW peering에는 BGP 전파 대신 정적 라우트를 명시적으로 구성합니다. 양방향의 해당 TGW 및 VPC 라우트 테이블을 구성해야 하며 정적 라우트도 자동화로 관리할 수 있습니다. 5. **라우트 우선순위:** 가장 긴 접두사 매칭이 먼저입니다. **같은 목적지 접두사**에서는 정적 라우트가 전파 라우트보다 우선하지만 더 넓은 정적 라우트가 더 구체적인 전파 라우트를 덮어쓰지는 않습니다. 6. **Lattice 대상 보안 그룹:** 문서화된 VPC 연결 서비스 경로는 리전/IP 계열에 맞는 관리형 접두사 목록(`com.amazonaws.REGION.vpc-lattice`, `com.amazonaws.REGION.ipv6.vpc-lattice`)을 실제 대상·상태 검사 포트에 허용합니다. 원 `169.254.171.0/24` 예제는 전체 목록의 보편적 정의가 아니며 관리형 목록에는 link-local 또는 라우팅할 수 없는 public 주소도 포함될 수 있습니다. 엔드포인트/리소스 게이트웨이 경로에는 별도 제어가 있습니다. VPC 연결만으로 IAM 서비스 인증이 활성화되는 것도 아닙니다. 7. **정리 소유권:** 원 보고서는 GuardDuty 관리 네트워킹 의존성, IAM 정책 연결과 남은 Lattice 리소스가 정리에 영향을 준 사례를 기록합니다. 실제 의존 리소스 ID와 소유 서비스를 확인한 뒤 조치하세요. VPC/역할 삭제를 강제하기 위해 관리형 보안 제어를 끄거나 무관한 리소스를 삭제하지 마세요. ## 요구사항별 아키텍처 선택 | 요구사항 | 후보 패턴 | 중요한 확인 사항 | |---|---|---| | 각 Organization이 자신의 TGW 라우팅 권한 유지 | ④ TGW Peering | 정적 라우트 조율, 주소 계획, 처리량, 가용성, 검사와 전송 요금 | | 소수 추론/서비스 엔드포인트 노출 | ③ PrivateLink 엔드포인트 서비스 | 지원 프로토콜/모델, 엔드포인트 수락, 애플리케이션 인증, DNS, 비용과 실제 페이로드/동시성 | | 중복 CIDR 간 서비스 접근 | ③ PrivateLink 또는 ⑤ Lattice | 서비스/리소스 범위, 더 넓은 IP 라우팅에는 NAT/주소 재설계 평가 | | 다른 계정이 중앙 통제 허브 사용 가능 | ① TGW RAM 공유 | 외부 공유 정책, 수락 설정과 소유자의 TGW 제어 모델 | | 적은 수의 직접 VPC 쌍 | ② VPC Peering | 비중복 CIDR, 쌍별 라우트 관리, 쿼터와 데이터 전송 요금 | | 관리형 HTTP 서비스 신원/탐색/거버넌스 필요 | ⑤ VPC Lattice | 명시적 IAM 인증 정책, 서명 요청, 서비스 연결과 워크로드 측정 | TGW peering과 PrivateLink 조합은 독립적인 네트워크 거버넌스와 제한적인 API 노출에 맞을 수 있습니다. 공개된 지연 표가 대부분의 GPU 환경에서 최적임을 증명하지는 않습니다. 필요한 연결과 제어를 기준으로 선택한 뒤 실제 워크로드를 측정하세요. ## 한계와 후속 검증 원 보고서에는 Network Firewall 검사 경로, 리전 간 지연, 처리량/동시성 측정이 없습니다. 주소 중복의 기능 확인을 보고했지만 중복 환경의 지연 값은 공개하지 않았습니다. GPU 집단 통신, EFA/RDMA, 대표 페이로드 크기, 불확실성 추정과 완전한 재현 자료도 이 페이지로 입증되지 않습니다. 보고된 숫자는 과거 결과의 맥락으로 유지하세요. 배포 전 대상 계정의 정책과 지원 연결 모델, 필요한 양방향 라우트 또는 서비스 접근, 장애 동작과 애플리케이션의 지연/처리량 예산을 검증해야 합니다. 이번 검토는 AWS 프로비저닝이나 실시간 벤치마크를 실행하지 않았습니다. ## 참고 자료 - [다중 VPC 네트워킹 백서](https://docs.aws.amazon.com/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/welcome.html) - [계정 간 TGW 공유](https://docs.aws.amazon.com/prescriptive-guidance/latest/integrate-third-party-services/architecture-3-1.html) - [단일/복수 Organizations 선택](https://aws.amazon.com/blogs/architecture/choosing-between-single-or-multiple-organizations-in-aws-organizations/) - [RAM CreateResourceShare API](https://docs.aws.amazon.com/ram/latest/APIReference/API_CreateResourceShare.html) - [TGW 수락 옵션](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_TransitGatewayRequestOptions.html) - [TGW peering 수락](https://docs.aws.amazon.com/vpc/latest/tgw/tgw-peering-accept-reject.html) - [TGW 라우팅과 평가 순서](https://docs.aws.amazon.com/vpc/latest/tgw/how-transit-gateways-work.html) - [PrivateLink 엔드포인트 유형](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html) - [Private NAT와 중복 네트워크](https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-scenarios.html) - [Lattice 보안 그룹](https://docs.aws.amazon.com/vpc-lattice/latest/ug/security-groups.html) - [EC2 계정/리전 쿼터](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-resource-limits.html) - [EFA 제한](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html) - [VPC Lattice 문서](https://www.atomai.click/kubernetes-docs/llms/ko/networking/02-vpc-lattice.md) - [Cross-Org 퀴즈](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/05-cross-org-vpc-connectivity-quiz) ---------------------------------------- Source: https://www.atomai.click/kubernetes-docs/ko/networking/06-pod-network-benchmark ---------------------------------------- # Pod 네트워크 실측 벤치마크 — 같은 노드·같은 AZ·다른 AZ, 그리고 DNS ndots > **기록된 측정 환경**: Kubernetes 1.36 (Amazon EKS), Amazon VPC CNI v1.21.1, kube-proxy iptables 모드 > **측정일**: 2026년 9월 2일 · **마지막 업데이트**: 2026년 9월 12일 이 문서는 서울 리전 `fsi-demo-cluster`의 **2026년 9월 2일** 벤치마크 기록을 보존합니다. Pod 간 RTT, HTTP/gRPC 지연, iperf3 처리량과 DNS 쿼리 수를 다룹니다. 해당 실행에서는 AZ 간 경로의 지연이 더 컸지만 두 노드 간 경로의 처리량은 비슷했습니다. 모든 AZ 경로에서 같은 대역폭이 보장된다는 뜻은 아닙니다. 측정 1·2의 애플리케이션 트래픽은 Pod IP를 직접 사용했고, 측정 3은 그 비용 모델이며, 측정 4는 기존 `kube-dns` ClusterIP를 사용했습니다. DNS 10쿼리 결과는 당시 리졸버·search 목록·응답 순서에 한정됩니다. 과거 버전과 측정값은 유지했으며, 이번 감사에서 EKS 벤치마크를 다시 실행하거나 청구서를 검증하지는 않았습니다. ![ap-northeast-2a의 노드 A에 있는 클라이언트 Pod가 같은 노드의 서버 Pod, 같은 AZ 노드 B의 서버 Pod, ap-northeast-2b 노드 C의 서버 Pod와 통신하는 세 경로를 각 경로의 실측 RTT(0.040 / 0.339 / 0.544 ms)와 단일 플로우 Gbps(29.97 / 4.96 / 4.96)와 함께 보여주는 토폴로지 다이어그램.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-06-pod-network-benchmark-0.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-06-pod-network-benchmark-0.html) 그림은 당시 테스트 배치입니다. +0.21 ms는 해당 실행 간 차이이고, $4.47은 아래에서 설명하는 과거의 십진 GB 비용 모델입니다. 검증된 청구액이나 AZ 하나당 고정 비용이 아닙니다. ## TL;DR — 측정 결과 요약 1. **기록된 RTT**: 같은 노드 **0.040 ms** → 같은 AZ **0.339 ms** → 다른 AZ **0.544 ms**(ping 200회 평균). 두 노드 간 경로의 관측 차이는 +0.21 ms, 같은 노드 대비는 +0.50 ms였습니다. 2. **HTTP p50 / p99** (fortio, 100 qps, 커넥션 4개, keepalive, 60 s): 0.259 / 0.350 ms → 0.461 / 0.667 ms → 0.704 / 0.812 ms. 같은 사다리를 애플리케이션 관점에서 본 값입니다. 3. **기록된 대역폭**: 두 노드 간 경로 모두 단일 TCP 플로우 **4.96 Gbps**, 8개 플로우 **9.94 Gbps**였습니다. 일반적인 클러스터 배치 그룹 외부의 5 Gbps 단일 플로우 한도와 m5.xlarge의 10 Gbps 버스트 피크에 부합하지만, 다른 인스턴스 기능과 경로에는 다른 한도가 적용될 수 있습니다. 4. **같은 노드 Pod 간**: 단일 플로우 **29.97 Gbps**(클라이언트 프로세스 CPU 99.8%로 CPU 부하 가능성을 뒷받침), 8개 플로우 **48.15 Gbps**. 당시 VPC CNI 구성에서는 양쪽 Pod의 veth 경로와 호스트 네트워크 스택을 지나며 물리 NIC를 사용하지 않습니다. 5. **비용 모델**: 180초 AZ 간 전송량은 **223.4 십진 GB**였습니다. 원문의 페이로드 기반 모델은 송신·수신 양 끝에 각각 $0.01/GB를 적용해 **약 $4.47**로 추정하지만, 청구서를 확인한 값은 아닙니다. 180초 안에 1.25 Gbps 베이스라인으로 내려가는 현상은 관측되지 않았습니다. 6. **기록된 DNS**: 당시 glibc Pod의 `ndots:5`에서 `sts.ap-northeast-2.amazonaws.com`은 **10쿼리**(NXDOMAIN 8개), 웜 중앙값 **3.78 ms**였습니다. 끝점을 붙이면 **2쿼리** / 0.80 ms, `ndots:1`이면 2쿼리 / 0.54 ms였습니다. 이 시간은 표본값이지 보장값이 아닙니다. 7. **새 커넥션**: keepalive를 끄자 p50이 0.259 → 0.664, 0.461 → 1.079, 0.704 → **1.517 ms**로 변했습니다. TCP 연결 수립이 증가분에 기여하지만 핸드셰이크·소켓·애플리케이션 비용을 분리한 실험은 아닙니다. ## 테스트 환경 | 항목 | 값 | |------|-----| | 클러스터 | Amazon EKS `fsi-demo-cluster`, ap-northeast-2 (서울), 컨트롤 플레인 `v1.36.2-eks-bca9cf6`, AZ 2개(2a, 2b) 사용 | | 노드 | Karpenter `system` NodePool이 이 테스트를 위해 새로 띄운 **m5.xlarge × 3** — 2a 클라이언트 노드, 2a 서버 노드, 2b 서버 노드. 4 vCPU, Intel Xeon Platinum 8175M @ 2.50GHz | | 노드 OS | Amazon Linux 2023.12.20260817, 커널 `6.18.41-94.142.amzn2023.x86_64`, containerd 2.2.5, kubelet v1.36.3-eks-cb19647 | | CNI | Amazon VPC CNI `v1.21.1-eksbuild.8` (+ network-policy-agent v1.3.4); `ENABLE_PREFIX_DELEGATION=false`, `ENABLE_POD_ENI=false`, `AWS_VPC_K8S_CNI_EXTERNALSNAT=false`, `NETWORK_POLICY_ENFORCING_MODE=standard`, `WARM_ENI_TARGET=1`, `WARM_IP_TARGET=3` | | kube-proxy | `v1.35.3-eksbuild.5`, `mode: "iptables"` | | CoreDNS | `v1.14.2-eksbuild.4`, 2 replicas — AZ마다 1개(`10.0.2.106` / 2a, `10.0.3.14` / 2b); Service `kube-dns` ClusterIP `172.20.0.10`; Corefile `kubernetes cluster.local … { pods insecure }`, `forward . /etc/resolv.conf`, `cache 30`, `loadbalance`; **NodeLocal DNSCache 없음**, `autopath` 플러그인 없음 | | Pod resolv.conf (기본) | `search bench-net.svc.cluster.local svc.cluster.local cluster.local ap-northeast-2.compute.internal` / `nameserver 172.20.0.10` / `options ndots:5` | | Pod NIC | eth0 MTU **9001**(점보 프레임), TCP 혼잡 제어 `cubic`, iperf3 `tcp_mss_default: 8949` | | EC2 네트워크 사양 | m5.xlarge "Up to 10 Gigabit" — 베이스라인 **1.25 Gbps**, 피크 **10 Gbps**, 4 vCPU (비교: m5.large 베이스라인 0.75 Gbps, 피크 10 Gbps, 2 vCPU). `aws ec2 describe-instance-types`로 확인, ENA 필수 | | 요금 | usagetype `APN2-DataTransfer-Regional-Bytes` "Regional Data Transfer - in/out/between AZs or when using public IP or Elastic IP addresses" **$0.01/GB** (`aws pricing get-products --region us-east-1`, 2026-09 조회) | | 도구 | `nicolaka/netshoot:v0.14` — iperf **3.19**, fortio **1.69.5**, iputils ping 20250605, tcpdump 4.99.5; DNS 클라이언트 `python:3.12-slim` (Debian 13, **glibc 2.41**, Python 3.12.14) | | 측정 시각 | 2026-09-02 07:58–08:40 UTC (첫 Pod 07:58:22Z, DNS Pod 08:16:24Z) | AWS는 네트워크 I/O 크레딧이 남아 있어도 버스트 대역폭은 best effort라고 설명하며, 송신과 수신의 크레딧 버킷은 별개입니다. 새 인스턴스는 최대 크레딧으로 시작하지만 피크 가용성과 지속 시간은 달라집니다. 이번 180초 실행은 그 기간에 베이스라인으로의 하락이 없었다는 것만 보여 줍니다. 기록된 m5.xlarge의 베이스라인 1.25 Gbps / 피크 10 Gbps는 공식 [M5 네트워크 사양](https://docs.aws.amazon.com/ec2/latest/instancetypes/gp.html)에도 있으며, 버스트 동작은 [EC2 대역폭 가이드](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-network-bandwidth.html)를 참고하세요. 픽스처 배치는 다음과 같았습니다. | Pod | IP | 노드 | Zone | 역할 / requests | |---|---|---|---|---| | `cli` | 10.0.2.109 | ip-10-0-2-128 (nodeclaim `system-76r87`) | ap-northeast-2a | 클라이언트; 2500m / 1Gi | | `srv-same` | 10.0.2.72 | ip-10-0-2-128 — `cli`와 같은 노드 (required podAffinity) | ap-northeast-2a | 서버; 200m / 256Mi | | `srv-a` | 10.0.2.37 | ip-10-0-2-20 (nodeclaim `system-ksrbg`, `cli`에 podAntiAffinity) | ap-northeast-2a | 서버; 2800m / 1Gi | | `srv-b` | 10.0.3.65 | ip-10-0-3-32 (nodeclaim `system-svdvk`) | ap-northeast-2b | 서버; 2500m / 1Gi | | `dns-default` | 10.0.2.5 | ip-10-0-2-20 (`srv-a`에 podAffinity) | ap-northeast-2a | glibc 리졸버, 기본 `ndots:5` | | `dns-ndots1` | 10.0.2.143 | ip-10-0-2-20 | ap-northeast-2a | glibc 리졸버, `dnsConfig.options ndots=1` | 서버 Pod는 `sh -c "iperf3 -s -p 5201 & exec fortio server -http-port 8080 -grpc-port 8079 -tcp-port 8078"`를 실행하고, 모든 벤치 Pod에 `karpenter.sh/do-not-disrupt: "true"`를 붙였습니다. `srv-a`는 처음에 m5.large / 1500m으로 요청했지만 Karpenter가 `no instance type has enough resources`를 보고했습니다 — m5.large의 allocatable 1930m 중 DaemonSet 오버헤드가 821m이어서 — 그래서 m5.xlarge / 2800m으로 바꿨습니다. ### 배포 매니페스트 아래는 과거의 selector·requests·이미지·명령·annotation을 보존한 측정 픽스처이며, 애플리케이션 Service 객체는 없습니다. selector만으로 새 노드나 격리된 노드가 보장되지는 않습니다. 새 실행에서는 복사본을 승인된 테스트 NodePool·사용 가능한 AZ·리소스 예산에 맞추고, 공유 `system` 풀에 그대로 배포하지 마세요. 이미지 digest와 도구 버전도 기록해야 합니다. 변경 가능한 태그와 netshoot의 빌드 시점 도구 다운로드는 과거 바이너리를 보장하지 않습니다. 아래의 보완된 재현 절차는 이 역사적 매니페스트와 구분합니다. ```yaml apiVersion: v1 kind: Namespace metadata: name: bench-net labels: bench: net --- # 클라이언트 — ap-northeast-2a의 새 m5.xlarge apiVersion: v1 kind: Pod metadata: name: cli namespace: bench-net labels: { app: cli, role: client } annotations: { karpenter.sh/do-not-disrupt: "true" } spec: nodeSelector: topology.kubernetes.io/zone: ap-northeast-2a node.kubernetes.io/instance-type: m5.xlarge karpenter.sh/nodepool: system terminationGracePeriodSeconds: 5 containers: - name: netshoot image: nicolaka/netshoot:v0.14 command: ["sleep", "infinity"] resources: requests: { cpu: "2500m", memory: "1Gi" } --- # same-node — required podAffinity로 cli와 같은 노드에 apiVersion: v1 kind: Pod metadata: name: srv-same namespace: bench-net labels: { app: srv-same, role: server, zone: a } annotations: { karpenter.sh/do-not-disrupt: "true" } spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: { matchLabels: { app: cli } } topologyKey: kubernetes.io/hostname terminationGracePeriodSeconds: 5 containers: - name: netshoot image: nicolaka/netshoot:v0.14 command: ["sh", "-c", "iperf3 -s -p 5201 & exec fortio server -http-port 8080 -grpc-port 8079 -tcp-port 8078"] ports: [{ containerPort: 8080 }, { containerPort: 5201 }] resources: requests: { cpu: "200m", memory: "256Mi" } --- # same-AZ — cli와 같은 AZ, 다른 노드(podAntiAffinity). m5.large는 DaemonSet 오버헤드 때문에 들어가지 않아 m5.xlarge apiVersion: v1 kind: Pod metadata: name: srv-a namespace: bench-net labels: { app: srv-a, role: server, zone: a } annotations: { karpenter.sh/do-not-disrupt: "true" } spec: nodeSelector: topology.kubernetes.io/zone: ap-northeast-2a node.kubernetes.io/instance-type: m5.xlarge karpenter.sh/nodepool: system affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: { matchLabels: { app: cli } } topologyKey: kubernetes.io/hostname terminationGracePeriodSeconds: 5 containers: - name: netshoot image: nicolaka/netshoot:v0.14 command: ["sh", "-c", "iperf3 -s -p 5201 & exec fortio server -http-port 8080 -grpc-port 8079 -tcp-port 8078"] ports: [{ containerPort: 8080 }, { containerPort: 5201 }] resources: requests: { cpu: "2800m", memory: "1Gi" } --- # cross-AZ — ap-northeast-2b의 새 m5.xlarge apiVersion: v1 kind: Pod metadata: name: srv-b namespace: bench-net labels: { app: srv-b, role: server, zone: b } annotations: { karpenter.sh/do-not-disrupt: "true" } spec: nodeSelector: topology.kubernetes.io/zone: ap-northeast-2b node.kubernetes.io/instance-type: m5.xlarge karpenter.sh/nodepool: system terminationGracePeriodSeconds: 5 containers: - name: netshoot image: nicolaka/netshoot:v0.14 command: ["sh", "-c", "iperf3 -s -p 5201 & exec fortio server -http-port 8080 -grpc-port 8079 -tcp-port 8078"] ports: [{ containerPort: 8080 }, { containerPort: 5201 }] resources: requests: { cpu: "2500m", memory: "1Gi" } ``` DNS Pod 두 개는 `srv-a`와 같은 노드에 배치되었다고 기록되어 있습니다. `app` 이미지는 Debian 13 / glibc 2.41로 보고되었으며 musl 등 다른 리졸버는 측정하지 않았습니다. `sniffer`는 같은 Pod 네트워크 네임스페이스를 사용하므로 클러스터가 패킷 캡처를 허용하면 해당 DNS 패킷을 볼 수 있습니다. 아래 예제에서 DNS 객체 두 개를 만들려면 복제한 두 번째 객체의 이름과 `app` 라벨을 `dns-ndots1`로 바꾸고, 그 객체에서만 `dnsConfig` 주석을 해제하세요. 새 실행에서는 두 Pod에 동일한 이미지를 사용하고 기록한 digest로 고정합니다. ```yaml apiVersion: v1 kind: Pod metadata: name: dns-default # 두 번째 Pod는 name: dns-ndots1 + 아래 dnsConfig 블록만 추가 namespace: bench-net labels: { app: dns-default, role: dns } annotations: { karpenter.sh/do-not-disrupt: "true" } spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: { matchLabels: { app: srv-a } } topologyKey: kubernetes.io/hostname # dns-ndots1에만 있는 블록: # dnsConfig: # options: # - name: ndots # value: "1" terminationGracePeriodSeconds: 5 containers: - name: app image: python:3.12-slim command: ["sleep", "infinity"] resources: { requests: { cpu: "50m", memory: "64Mi" } } - name: sniffer image: nicolaka/netshoot:v0.14 command: ["sleep", "infinity"] resources: { requests: { cpu: "50m", memory: "64Mi" } } ``` ## 측정 1 — RTT와 HTTP 레이턴시: 같은 노드 → 같은 AZ → 다른 AZ ICMP(`ping -c 200 -i 0.05 -q`)는 엔드포인트 커널 처리와 스케줄링을 포함한 유휴 경로를 관찰합니다. 이후 같은 경로를 HTTP/1.1과 gRPC로 측정했습니다. 참고용으로 새 연결을 쓰는 `curl` 1회의 connect / total도 적었습니다. | 경로 | RTT min / **avg** / max / mdev (ms) | 손실 | curl 1회 (콜드) connect / total | |---|---|---|---| | 같은 노드 → 10.0.2.72 | 0.021 / **0.040** / 0.089 / 0.007 | 0/200 | 0.194 ms / 0.497 ms | | 같은 AZ → 10.0.2.37 | 0.300 / **0.339** / 0.450 / 0.017 | 0/200 | 0.497 ms / 2.333 ms | | 다른 AZ → 10.0.3.65 | 0.504 / **0.544** / 0.625 / 0.015 | 0/200 | 0.694 ms / 4.038 ms | 관측 차이는 같은 AZ − 같은 노드 = +0.30 ms, 다른 AZ − 같은 AZ = **+0.21 ms**, 다른 AZ − 같은 노드 = +0.50 ms입니다. 이 표본의 mdev는 모두 0.017 ms 이하였습니다. curl의 `time_total`은 전송 작업 시간이며 프로세스 기동 시간은 포함하지 않습니다. 1회 값으로 분포를 판단할 수는 없습니다. [curl 시간 정의](https://curl.se/docs/manpage.html)를 참고하세요. ### HTTP/1.1 — 100 qps, 커넥션 4개, keepalive, 60 s (요청 6,000개), ms | 경로 | avg | **p50** | p90 | p99 | p99.9 | max | min | |---|---|---|---|---|---|---|---| | 같은 노드 | 0.260 | **0.259** | 0.299 | 0.350 | 1.267 | 2.080 | 0.111 | | 같은 AZ | 0.468 | **0.461** | 0.560 | 0.667 | 0.783 | 2.823 | 0.336 | | 다른 AZ | 0.706 | **0.704** | 0.782 | 0.812 | 1.150 | 4.581 | 0.551 | ### gRPC ping — 100 qps, 커넥션 4개, 30 s (요청 3,000개), ms | 경로 | avg | **p50** | p90 | p99 | p99.9 | max | min | |---|---|---|---|---|---|---|---| | 같은 노드 | 0.410 | **0.397** | 0.449 | 0.869 | 1.187 | 1.314 | 0.241 | | 같은 AZ | 0.601 | **0.592** | 0.687 | 0.889 | 1.052 | 1.105 | 0.448 | | 다른 AZ | 0.878 | **0.865** | 0.967 | 1.209 | 2.582 | 2.826 | 0.692 | 기록은 빈 요청 페이로드의 HTTP echo 응답 본문이 약 75바이트이고 실행별 오류가 0건이었다고 설명합니다. 프로토콜별 결과는 구분해야 합니다. HTTP 200, gRPC Ping 결과, gRPC health check의 `SERVING`은 서로 다르며, `-grpc -ping`은 기본 health check가 아닌 Ping 부하를 선택합니다. **읽는 법.** HTTP p50과 ping 평균의 차이는 약 0.22 / 0.12 / 0.16 ms이지만, 서로 다른 프로토콜과 통계량을 빼서 유저 공간 오버헤드를 분리할 수는 없습니다. HTTP p50의 관측 단계는 +0.202 / +0.243 ms이며 노드·AZ당 고정 비용이 아닙니다. gRPC p50은 HTTP보다 0.138 / 0.131 / 0.161 ms 높았지만 HTTP/2·직렬화·스케줄링·구현별 기여도는 측정하지 않았습니다. HTTP p99는 0.350 → 0.667 → 0.812 ms, gRPC p99.9는 1.187 → 1.052 → **2.582 ms**였습니다. 각 셀은 한 번 실행한 분포입니다. > **메시 벤치마크와의 비교.** [Istio sidecar vs ambient 기록](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/comparison/03-sidecar-vs-ambient.md)의 p50 **+1.29 ms**(2.11 − 0.82 ms)는 사이드카 시나리오 전체의 차이이며 프록시 하나의 독립 비용이 아닙니다. Graviton 하드웨어·200 qps·커넥션 16개·Fortio 1.69.4로, 여기의 M5·100 qps·커넥션 4개와 조건이 다릅니다. 두 값을 보편적인 “메시 홉”과 “AZ 홉” 비용으로 더하거나 순위를 매길 수 없습니다. ### 새 커넥션의 비용 — keepalive=false, 100 qps, 커넥션 4개, 30 s (요청 3,000개), ms 요청마다 TCP 커넥션을 새로 맺으면(fortio `-keepalive=false`) 지연은 어떻게 변할까요? | 경로 | avg | **p50** | p90 | p99 | p99.9 | max | min | keepalive p50 대비 | |---|---|---|---|---|---|---|---|---| | 같은 노드 | 0.672 | **0.664** | 0.782 | 0.957 | 1.253 | 1.306 | 0.364 | **+0.405 ms** | | 같은 AZ | 1.066 | **1.079** | 1.185 | 1.369 | 1.582 | 1.795 | 0.769 | **+0.618 ms** | | 다른 AZ | 1.530 | **1.517** | 1.678 | 1.796 | 1.981 | 2.009 | 1.300 | **+0.813 ms** | 관측 증가분은 **+0.405 / +0.618 / +0.813 ms**입니다. 새 TCP 연결은 수립 작업을 추가하지만 이 측정만으로 “RTT 한 번 + 0.3 ms”라는 고정 분해를 입증할 수는 없습니다. 연결 재사용은 실제 부하로 평가할 유용한 최적화이며 AZ 간 호출의 정확성을 위한 필수 조건은 아닙니다. 연결 종료 방식에 따라 active close를 수행한 쪽에 TIME_WAIT가 늘 수 있지만, 소켓 상태나 TIME_WAIT 수는 여기서 측정하지 않았습니다. ### 고정 커넥션 풀의 최대 qps — 지연이 곧 처리량 (closed-loop, 커넥션 16개, 20 s) `-qps 0`(무제한, 닫힌 루프)으로 16개 커넥션이 낼 수 있는 최대 요청률을 재면 지연 차이가 처리량 차이로 바뀝니다. | 경로 | 요청 수 | **달성 qps** | avg ms | p50 | p90 | p99 | p99.9 | max | |---|---|---|---|---|---|---|---|---| | 같은 노드 | 899,827 | **44,991** | 0.355 | 0.249 | 0.733 | 1.695 | 3.389 | 13.593 | | 같은 AZ | 770,156 | **38,507** | 0.415 | 0.396 | 0.537 | 0.728 | 1.147 | 4.502 | | 다른 AZ | 512,060 | **25,602** | 0.624 | 0.597 | 0.770 | 0.949 | 1.293 | 4.725 | 평균 약 16개 요청이 진행 중이고 클라이언트 대기 시간이 작은 정상상태 폐루프에서는 Little의 법칙으로 처리량 ≈ 동시성 / 평균 지연을 얻습니다. 16 / 0.000355 = 45,070(기록 44,991), 16 / 0.000415 = 38,554(38,507), 16 / 0.000624 = 25,641(25,602)입니다. 이 테스트의 다른 AZ 요청률은 같은 AZ보다 **33.5% 낮았습니다**. 이 관계가 지연의 단독 원인이나 보편적인 AZ 페널티를 입증하지는 않습니다. 같은 노드의 꼬리 지연이 더 큰 원인으로 CPU 경합을 의심할 수 있지만 프로파일링으로 확인하지 않았습니다. ## 측정 2 — 처리량: 단일 플로우 5 Gbps 상한과 인스턴스 10 Gbps 상한 iperf3 3.19, TCP, 실행당 20초, `-J`, 클라이언트 `cli`. CPU 열은 iperf3가 보고하는 프로세스별 값으로 100% = vCPU 1개입니다. | 경로 | 플로우 (-P) | 송신 Gbps | 수신 Gbps | 재전송 | 전송 바이트 | 클라이언트 CPU | 서버 CPU | 송신측 TCP 평균 RTT (stream 1) | 최대 snd_cwnd | |---|---|---|---|---|---|---|---|---|---| | 같은 노드 (cli→srv-same) | 1 | **29.97** | 29.97 | 13 | 74,921,541,632 | **99.8 %** | 80.9 % | 34 µs | 1,861,392 B | | 같은 노드 | 8 | **48.15** | 48.08 | 14,567 | 120,375,083,008 | 179.0 % | 186.9 % | 201 µs / 767 µs (stream 1, 2) | 5,888,442 B | | 같은 AZ (cli→srv-a, 2a→2a) | 1 | **4.96** | 4.96 | 4 | 12,411,731,968 | 19.5 % | 15.4 % | **5,641 µs** | 4,349,214 B | | 같은 AZ | 8 | **9.94** | 9.93 | 5,874 | 24,846,139,392 | 36.3 % | 159.3 % | 2,720 µs / 1,626 µs | 1,163,370 B | | 다른 AZ (cli→srv-b, 2a→2b) | 1 | **4.96** | 4.96 | 2 | 12,411,994,112 | 20.0 % | 22.5 % | **5,420 µs** | 4,304,469 B | | 다른 AZ | 8 | **9.94** | 9.93 | 5,979 | 24,845,090,816 | 36.7 % | 138.2 % | 3,671 µs / 3,237 µs | 1,226,013 B | 네 가지를 읽어야 합니다. 1. **같은 노드에서는 물리 NIC를 우회했습니다.** 단일 플로우 29.97 Gbps에서 클라이언트 프로세스 CPU가 99.8%였고, 8개 플로우는 48.15 Gbps였습니다. 호스트 라우팅, 양쪽 Pod의 veth 경로, 커널 처리와 CPU 스케줄링은 여전히 영향을 줍니다. CPU 부하 징후가 있는 네트워크 측정이지 순수 메모리 복사 속도 측정은 아닙니다. 2. **두 노드 간 단일 플로우는 모두 4.96 Gbps였습니다.** 클러스터 배치 그룹 외부의 일반적인 5 Gbps 한도에 부합합니다. AWS는 클러스터 배치 그룹 내부에서는 최대 10 Gbps, 같은 AZ의 지원되는 ENA Express 경로에서는 최대 25 Gbps도 문서화합니다. iperf3 프로세스 CPU가 낮다는 사실만으로 모든 호스트·네트워크 처리 한계를 배제할 수는 없습니다. 3. **8개 플로우는 두 경로 모두 9.94 Gbps였습니다.** 해당 관측 구간에서 비슷한 처리량을 얻었다는 근거입니다. 재전송은 단일 플로우에서도 4 / 2회 있었고 8개에서 5,874 / 5,979회로 늘었습니다. 재전송 수만으로 ENA 셰이핑이나 손실 위치를 특정할 수 없으며, ENA allowance 카운터는 수집하지 않았습니다. 4. **부하 중 TCP RTT가 유휴 ICMP RTT보다 컸습니다.** 단일 플로우 송신측 값은 약 **5.6 / 5.4 ms**, 혼잡 윈도우는 약 4.3 MB였고 유휴 ping 평균은 0.34 / 0.54 ms였습니다. 큐잉이 가능한 설명이지만 프로토콜·표본 방식·부하가 다릅니다. 큐의 위치나 다중화된 모든 RPC에 정확히 5 ms가 추가된다는 주장은 측정하지 않았습니다. 기록된 MSS 8949는 MTU 9001과 당시 IPv4/TCP 오버헤드에 부합하지만, 유효 MSS는 헤더와 경로 MTU에도 좌우됩니다. 전송 바이트는 애플리케이션 전송량이며 별도로 검증된 과금 사용량은 아닙니다. > 당시의 일반 EC2 경로에서는 병렬 플로우가 단일 플로우보다 인스턴스 버스트 대역폭을 더 사용했습니다. 병렬도를 높이면 CPU·혼잡·비용도 바뀝니다. Kafka fetcher나 전송 동시성을 바꾸기 전에 실제 인스턴스·경로 한도를 확인하세요. “모든 커넥션은 5 Gbps 한도”나 “같은 AZ면 대역폭 두 배” 모두 이 결과로 일반화할 수 없습니다. ### 3분 지속 테스트와 버스트 크레딧 기록된 m5.xlarge 베이스라인은 1.25 Gbps, best-effort 피크는 최대 10 Gbps입니다. 과거의 AZ 간 4개 플로우 테스트는 180초 동안 10초 간격으로 관찰했습니다(`iperf3 -c 10.0.3.65 -p 5201 -t 180 -P 4 -i 10 -J`). 이 IP는 당시 픽스처 주소이므로 새 테스트에서는 현재 Pod IP를 조회해야 합니다. | 항목 | 값 | |---|---| | 10초 구간별 Gbps (18구간) | 9.94, 9.93 ×12, 9.92, 9.93 ×4 — **최소 9.92, 최대 9.94** | | 총 전송 | 223,376,179,200 B = **223.4 GB** / 180.0 s (9.93 Gbps) | | 재전송 | 44,842 (≈ 249/s; 10초 구간당 2,273–2,669) | | CPU | 클라이언트 30.7 % (system 30.1 %), 서버 54.2 % (system 52.2 %) | **180초 동안 1.25 Gbps로의 하락은 관측되지 않았습니다.** 무제한 크레딧이나 지속적인 피크 대역폭 보장을 뜻하지는 않습니다. AWS는 가변적인 best-effort 버스트와 크레딧 소진 시 베이스라인 제한을 설명합니다. 장시간 백업과 리밸런스는 이 짧은 실행을 외삽하지 말고 해당 베이스라인과 실제 부하 요구를 기준으로 계획하세요. ## 측정 3 — AZ 간 데이터 전송 비용 모델 이 절은 원문의 비용 산술을 추정 모델로 보존합니다. 벤치마크에 제공된 것은 페이로드 바이트 수와 공개 정가이며, 비용 및 사용량 보고서(CUR)나 청구서가 아닙니다. 같은 리전의 AZ 간 EC2 사설 IP 직접 전송에 대해 [EC2 요금 페이지](https://aws.amazon.com/ec2/pricing/on-demand/)는 양 끝에 각각 $0.01/GB를 문서화합니다. 기록된 공개 Pricing API 항목은 `APN2-DataTransfer-Regional-Bytes`, **$0.0100000000 USD/GB**였습니다. `get-products`는 카탈로그 가격이며 계정의 실제 지불 단가가 아닙니다. 한 방향 페이로드도 송신측 “out”과 수신측 “in”에 과금될 수 있으며, 같은 양의 역방향 전송이 있어야 한다는 뜻은 아닙니다. 다른 AWS 서비스 경로에는 다른 과금 규칙이 적용될 수 있습니다. | 시나리오 | 과거 십진 GB 모델의 페이로드 양 | 추정 비용 (모델 GB × $0.01 × 2) | |---|---|---| | 180초 실행 (페이로드 실측, 비용 추정) | 223.4 GB | 223.4 × $0.01 ≈ **양 끝 각각 $2.23, 합계 $4.47** | | 측정 2의 AZ 간 iperf3 전송 (12.41 + 24.85 + 223.38 GB) | 260.6 GB | 양 끝 각각 ≈ $2.61, **합계 ≈ $5.21** (그 외 트래픽 제외) | | 평균 1 Gbps가 30일 내내 AZ를 넘는다면 (**가정**) | 0.125 GB/s × 86,400 s × 30일 = 324,000 GB ≈ **324 TB** | 324,000 × $0.02 ≈ **$6,480 / 월** | | RF3 StatefulSet를 3개 AZ에 분산, 리더 ingest 100 MiB/s (**가정**, 복제 트래픽만 계산) | 팔로워 2개가 각각 다른 AZ → 2 × 100 MiB/s = 209,715,200 B/s × 2,592,000 s ≈ 543,600 GB ≈ **544 TB / 월** | 543,600 × $0.02 ≈ **$10,870 / 월** | 네 행의 비용은 모두 원문의 **십진 환산, 1 GB = 페이로드 10⁹바이트**를 모델 가정으로 사용합니다. 이번 감사에서는 EC2 과금 사용량이 이 환산과 같다는 근거를 확보하지 못했습니다. 원시 합계는 지속 실행 223,376,179,200 B, AZ 간 iperf3 세 실행 합계 260,633,264,128 B입니다. 실제 계량 단위·단가·양 끝의 사용량·프로토콜 오버헤드와 재전송·크레딧 및 할인을 [CUR 전송 기록](https://docs.aws.amazon.com/cur/latest/userguide/cur-data-transfers-charges.html)과 대조해야 합니다. 아래 두 행은 30일 연속 전송도 가정하며 복제량에서 프로듀서·컨슈머 트래픽은 제외합니다. **$4.47과 $5.21은 추정값이며 실제 지출이 확인된 금액이 아닙니다.** **운영자가 할 일.** - **지원되는 경로에서 적합한 로컬 엔드포인트를 우선합니다.** 현재 Kubernetes 문서의 값은 `Service.spec.trafficDistribution: PreferSameZone`이며 `PreferClose`는 폐기 예정인 이전 별칭입니다. 폴백이 있는 선호도이지 엄격한 존 제한은 아닙니다. API 서버·kube-proxy 버전과 기능 지원을 확인하세요. 당시에는 1.36 컨트롤 플레인과 1.35 kube-proxy를 사용했습니다. 이 선호도와 애플리케이션 Service 경로 모두 측정하지 않았으며 Pod IP 직접 통신에는 적용되지 않습니다. - **지역성과 장애 내성을 함께 고려합니다.** 존 인식 읽기나 클라이언트 배치로 불필요한 전송을 줄일 수 있지만 RF3 복제본을 모두 한 AZ에 모으면 AZ 장애 보호를 잃습니다. 필요한 복제·장애 전환 설계를 유지하세요. [Zonal 클러스터 운영 전략](https://www.atomai.click/kubernetes-docs/llms/ko/ops/15-zonal-operations-guide.md)을 참고하세요. - **과금되는 양 끝을 측정합니다.** 백업·리밸런스·리플레이의 출발/목적 AZ와 계량 사용량을 기록하고 관련 “in”·“out” 항목을 합산합니다. 서로 다른 계정에 청구될 수도 있으므로 페이로드 바이트만으로 최종 요금을 단정하지 마세요. ## 측정 4 — DNS: ndots:5가 만드는 쿼리 증폭 ![glibc 리졸버가 ndots:5에서 search 접미사 4개를 A+AAAA 쿼리로 차례로 시도해 NXDOMAIN 8개를 받은 뒤 마지막에 절대 이름으로 답을 얻는 10쿼리 경로와, 끝에 점을 붙였을 때 A+AAAA 2쿼리로 바로 끝나는 경로를 대비한 시퀀스 다이어그램.](https://raw.githubusercontent.com/Atom-oh/kubernetes-docs/main/ko/.gitbook/assets/ko-networking-06-pod-network-benchmark-1.png) [🔍 인터랙티브 다이어그램 보기](https://www.atomai.click/kubernetes-docs/archmaps/ko-networking-06-pod-network-benchmark-1.html) 그림은 당시 glibc search 순서입니다. 4.37 ms는 기록된 A 응답까지이며 전체 `getaddrinfo` 호출 시간이 아닙니다. Pod 캡처에서 보이지 않은 업스트림 화살표는 설명용입니다. “cache 30”은 TTL 상한이며 30초 동안 캐시 히트를 보장하지 않습니다. 기록된 Pod에는 search 도메인 4개와 `ndots:5`가 있었지만 모든 EKS·DNS 정책·운영체제·노드 설정이 같지는 않습니다. 당시 glibc `AF_UNSPEC` 호출은 후보마다 A와 AAAA를 질의했고 search 후보 4개가 NXDOMAIN인 뒤 절대 STS 이름이 성공했습니다. A/AAAA 동시성과 쿼리 수는 리졸버 옵션·주소 패밀리·조기 성공·재시도·TCP 폴백에 따라 달라집니다. 과거 캡처의 `tcpdump -i eth0 -nn udp port 53`은 UDP DNS만 관측합니다. 기록은 프로세스 첫 조회 1회와 이후 20회 반복을 구분하지만, 첫 호출이라고 CoreDNS나 업스트림 캐시까지 비어 있었다고 볼 수는 없습니다. [Kubernetes Pod DNS 설정](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)을 참고하세요. ### 한 번의 이름 풀이가 보내는 쿼리 수와 웜 지연 (20회 반복), ms | Pod / ndots | 이름 (점 개수) | 보낸 쿼리 | NXDOMAIN 응답 | warm min | **median** | p90 | max | |---|---|---|---|---|---|---|---| | default / 5 | `kubernetes.default` (1) | 4 | 2 | 0.87 | **1.71** | 1.97 | 2.61 | | default / 5 | `kubernetes.default.svc.cluster.local` (4) | **10** | 8 | 1.53 | **3.63** | 4.45 | 6.41 | | default / 5 | `kubernetes.default.svc.cluster.local.` (끝점) | 2 | 0 | 0.33 | **0.46** | 1.09 | 1.58 | | default / 5 | `sts.ap-northeast-2.amazonaws.com` (3) | **10** | 8 | 3.08 | **3.78** | 4.66 | 4.84 | | default / 5 | `sts.ap-northeast-2.amazonaws.com.` (끝점) | 2 | 0 | 0.42 | **0.80** | 1.25 | 2.17 | | default / 5 | `www.amazon.com` (2) | **10** | 8 | 2.51 | **3.46** | 3.74 | 5.86 | | ndots1 / 1 | `kubernetes.default` (1) | **6** | 4 | 1.16 | **2.04** | 2.80 | 4.54 | | ndots1 / 1 | `kubernetes.default.svc.cluster.local` (4) | 2 | 0 | 0.35 | **0.97** | 1.08 | 1.35 | | ndots1 / 1 | `kubernetes.default.svc.cluster.local.` | 2 | 0 | 0.34 | **0.40** | 0.97 | 1.17 | | ndots1 / 1 | `sts.ap-northeast-2.amazonaws.com` (3) | 2 | 0 | 0.45 | **0.54** | 1.22 | 1.42 | | ndots1 / 1 | `sts.ap-northeast-2.amazonaws.com.` | 2 | 0 | 0.47 | **0.75** | 1.20 | 1.30 | | ndots1 / 1 | `www.amazon.com` (2) | 2 | 0 | 0.63 | **0.90** | 1.27 | 2.74 | 기록된 프로세스 첫 조회 시간은 default/`sts` 6.22 ms, default/`sts.` 2.87 ms, default/`www.amazon.com` 9.58 ms, default/`kubernetes.default.svc.cluster.local` 7.40 ms, ndots1/`kubernetes.default` 10.52 ms, ndots1/`sts` 2.84 ms였습니다. 리졸버 초기화 작업을 포함하며 아래 패킷 타임라인과는 다른 값입니다. **읽는 법.** 이 표본의 외부 이름과 끝점 없는 클러스터 FQDN은 **10쿼리 / NXDOMAIN 8개**였습니다. 끝점을 붙인 STS의 중앙값은 3.78 → 0.80 ms, 클러스터 FQDN은 3.63 → 0.46 ms로 줄었습니다. `kubernetes.default`는 두 번째 후보에서 성공해 4쿼리만 필요했으므로 항상 search 목록 전체를 소비하지는 않습니다. [CoreDNS cache](https://coredns.io/plugins/cache/)는 음성 응답도 캐시하지만 `cache 30`은 최대 TTL입니다. 응답 TTL과 최소 TTL이 적용되고 replica마다 캐시가 별개입니다. Kubernetes 플러그인의 기본 TTL은 별도 설정이 없으면 5초입니다. 캐시 히트에서도 순차 질의는 남지만, 이 캡처가 모든 웜 조회에서 업스트림을 피했음을 입증하지는 않습니다. ### 실제 순서 — `sts.ap-northeast-2.amazonaws.com` 콜드 풀이 1회 (ndots:5, tcpdump, 첫 패킷 기준 ms) | t (ms) | 172.20.0.10으로 보낸 후보 (A + AAAA 병렬) | 응답 | |---|---|---| | 0.00 | `sts.ap-northeast-2.amazonaws.com.bench-net.svc.cluster.local.` | NXDomain (권한 응답, CoreDNS kubernetes 플러그인) 0.92 / 1.14 | | 1.21 | `sts.ap-northeast-2.amazonaws.com.svc.cluster.local.` | NXDomain 2.01 / 2.26 | | 2.32 | `sts.ap-northeast-2.amazonaws.com.cluster.local.` | NXDomain 3.15 / 3.41 | | 3.47 | `sts.ap-northeast-2.amazonaws.com.ap-northeast-2.compute.internal.` | NXDomain (VPC 리졸버로 forward — 비권한) 3.68 / 3.93 | | 3.99 | `sts.ap-northeast-2.amazonaws.com.` | **A 10.0.3.84, A 10.0.2.129** 4.37 (AAAA: no data) | 표에는 쿼리 10개와 NXDOMAIN 8개가 기록되어 있습니다. **4.37 ms**는 첫 질의부터 A 응답까지이며 AAAA 완료 시각은 없으므로 프로세스 첫 호출 6.22 ms 전체와 같지 않습니다. 후보별 RTT도 일률적인 0.8–1.1 ms가 아닙니다. 네 번째 쌍은 0.21 / 0.46 ms, 마지막 A는 0.38 ms였습니다. kube-proxy iptables는 패킷마다가 아니라 새 conntrack 플로우에 대해 엔드포인트를 선택하며 A/AAAA가 같은 플로우를 사용할 수 있습니다. 엔드포인트 둘을 같은 확률로 고르면 새 플로우의 절반이라는 모델을 세울 수 있지만, **AZ 간 DNS 쿼리 비율을 측정한 결과는 아닙니다**. Service VIP `172.20.0.10`을 본 Pod 캡처만으로 선택된 백엔드 AZ를 알 수 없습니다. 원문은 STS 사설 주소 둘을 인터페이스 엔드포인트 ENI로 설명하며, 별도로 클러스터 FQDN의 포워딩 후보 2.2 ms / 패킷 walk 5.6 ms와 끝점 사용 시 0.4–0.5 ms를 기록합니다. ### `ndots:1`이 하는 일과 부작용 - **이 표본의 외부 이름**: 10 → **2쿼리**, 중앙값 약 3.5–3.8 → **0.5–0.9 ms**. 이득은 이름과 리졸버 동작에 따라 달라집니다. - **짧은 이름은 실패한 시도가 하나 더 생길 수 있습니다.** 여기서 `kubernetes.default`는 점 1개로 `ndots:1`을 만족해 절대 이름을 먼저 시도했습니다. CoreDNS가 포워딩한 뒤 기록상 1.6 ms에 NXDOMAIN을 받고, 네임스페이스 접미사를 거쳐 `svc.cluster.local`에서 `172.20.0.1`을 얻었습니다. 6쿼리·NXDOMAIN 4개·중앙값 2.04 ms로 기존 1.71 ms보다 컸습니다. 내부 이름이 업스트림에 노출될 수 있으므로 `ndots` 변경 전에 애플리케이션의 이름 사용을 모두 시험하세요. 전체 Service 이름을 쓰면 이런 모호성을 줄일 수 있습니다. - **끝점은 리졸버 이름을 절대 이름으로 만들어** search 확장을 피합니다. 재시도·주소 패밀리 설정·캐시가 달라져도 반드시 2쿼리나 일정한 지연을 보장한다는 뜻은 아닙니다. ### 증폭 산술 (파생) **같은 응답 패턴**에서 애플리케이션 DNS 캐시가 없고 요청마다 한 번 조회한다고 가정하면, 초당 조회 1,000회 × 10쿼리 = 10,000쿼리/s이며 2쿼리 형태는 2,000쿼리/s입니다. 이 중 8,000개(80%)가 NXDOMAIN을 받는다는 쿼리 수 모델이지 CoreDNS CPU나 AZ 간 비율 측정은 아닙니다. 관측 중앙값 차이는 STS 3.78 − 0.80 = 2.98 ms, 클러스터 FQDN 3.63 − 0.46 = 3.17 ms이며 요청마다 고정으로 추가되는 비용은 아닙니다. **실제 애플리케이션과 함께 시험할 선택지:** - 클라이언트가 지원하면 절대 DNS 이름을 사용합니다. HTTPS나 AWS SDK 엔드포인트 URL에 무조건 점을 붙이지 마세요. Host 처리·SNI·인증서 검증·요청 서명이 계속 동작해야 합니다. - `dnsConfig: {options: [{name: ndots, value: "1"}]}`을 짧은 이름 동작 및 애플리케이션 DNS 캐시와 함께 평가합니다. - 적합한 환경에서 [NodeLocal DNSCache](https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/)를 평가합니다. 히트는 로컬이지만 미스는 업스트림으로 갈 수 있습니다. 현재 [EKS Auto Mode](https://docs.aws.amazon.com/eks/latest/userguide/auto-networking.html)는 이미 노드 로컬 CoreDNS 시스템 서비스를 사용합니다. 순수 Auto Mode 클러스터에는 CoreDNS Deployment가 불필요하지만, 혼합 클러스터의 비 Auto 노드에는 여전히 필요합니다. - [CoreDNS autopath](https://coredns.io/plugins/autopath/)는 서버에서 search를 처리할 수 있지만 Kubernetes 연동에는 `pods verified`, 원래 Pod IP 식별, 관련 Pod watch·RBAC·메모리가 필요합니다. 기록된 `pods insecure` 구성은 이 조건을 충족하지 않습니다. 여기서는 이 최적화를 시험하지 않았습니다. ## 재현 방법 — 보완된 절차 승인된 격리 실습 환경과 이 테스트 전용의 사용하지 않는 네임스페이스를 사용합니다. 첫 픽스처를 환경에 맞게 복사해 `bench-net.yaml`, DNS 객체 두 개를 `bench-dns.yaml`로 저장하고 네임스페이스 이름을 맞춥니다. 적용 전에 NodePool 용량·AZ·스케줄링·패킷 캡처 권한을 확인하세요. 실험을 위해 운영 환경의 admission이나 보안 통제를 약화하지 마세요. 시간이 제한된 명령도 노드를 포화시키고 요금을 발생시킬 수 있습니다. 모든 명령은 `cli` 내부 대화형 셸로 들어가지 않고 **운영자 Bash 셸**에서 실행합니다. 감사에서는 구문과 일부 로컬 도구 동작을 확인했지만 이 픽스처를 EKS에 배포하거나 네트워크 부하를 실행하지 않았습니다. **1. 수정한 픽스처를 배포하고 배치를 확인합니다.** ```bash set -euo pipefail BENCH_NS=bench-net kubectl apply -f bench-net.yaml kubectl -n "$BENCH_NS" wait --for=condition=Ready \ pod/cli pod/srv-same pod/srv-a pod/srv-b --timeout=300s kubectl -n "$BENCH_NS" get pods -o wide kubectl get nodes -L topology.kubernetes.io/zone,node.kubernetes.io/instance-type,karpenter.sh/nodepool SAME_IP=$(kubectl -n "$BENCH_NS" get pod srv-same -o jsonpath='{.status.podIP}') AZ_IP=$(kubectl -n "$BENCH_NS" get pod srv-a -o jsonpath='{.status.podIP}') CROSS_IP=$(kubectl -n "$BENCH_NS" get pod srv-b -o jsonpath='{.status.podIP}') : "${SAME_IP:?missing srv-same IP}" "${AZ_IP:?missing srv-a IP}" "${CROSS_IP:?missing srv-b IP}" for bench_pod in srv-same srv-a srv-b; do kubectl -n "$BENCH_NS" logs "$bench_pod" --tail=30 kubectl -n "$BENCH_NS" exec "$bench_pod" -- ss -lnt done for bench_ip in "$SAME_IP" "$AZ_IP" "$CROSS_IP"; do kubectl -n "$BENCH_NS" exec cli -- \ curl --fail --silent --show-error --max-time 5 "http://$bench_ip:8080/" >/dev/null done ``` 계속하기 전에 `cli`와 `srv-same`은 같은 노드, `srv-a`는 같은 AZ의 다른 노드, `srv-b`는 다른 AZ인지 확인합니다. 5201/8080/8079 리스너와 시작 오류도 확인하세요. 과거 픽스처에는 readiness probe가 없으므로 Pod Ready만으로 프로세스 리슨을 보장할 수 없습니다. 노드 ID·IP·image ID와 실제 `iperf3 --version` / `fortio version`을 기록하고, Pod가 재생성되면 중지한 뒤 주소를 다시 조회합니다. **2. RTT와 참고용 HTTP 1회를 측정합니다.** ```bash for bench_ip in "$SAME_IP" "$AZ_IP" "$CROSS_IP"; do kubectl -n "$BENCH_NS" exec cli -- ping -c 200 -i 0.05 -q "$bench_ip" done kubectl -n "$BENCH_NS" exec cli -- curl --fail --silent --show-error --max-time 5 \ -o /dev/null -w 'connect=%{time_connect} total=%{time_total}\n' "http://$CROSS_IP:8080/" ``` **3. 시간을 제한해 처리량을 측정하고 운영자 로컬에 결과를 저장합니다.** ```bash for bench_ip in "$SAME_IP" "$AZ_IP" "$CROSS_IP"; do kubectl -n "$BENCH_NS" exec cli -- iperf3 -c "$bench_ip" -p 5201 -t 20 -P 1 -J > "t1-$bench_ip-P1.json" kubectl -n "$BENCH_NS" exec cli -- iperf3 -c "$bench_ip" -p 5201 -t 20 -P 8 -J > "t1-$bench_ip-P8.json" done kubectl -n "$BENCH_NS" exec cli -- \ iperf3 -c "$CROSS_IP" -p 5201 -t 180 -P 4 -i 10 -J > t1-cross-sustained180-P4.json ``` 종료 코드뿐 아니라 JSON 오류도 확인합니다. `end.sum_sent.bits_per_second`, `end.sum_sent.retransmits`, `end.cpu_utilization_percent.host_total` / `remote_total`, `end.streams[].sender.mean_rtt` / `max_snd_cwnd`를 읽습니다. iperf3 3.19 프로세스 CPU의 100%는 경과 시간 동안 CPU 하나의 시간을 사용한 값으로, 여러 스레드는 100%를 넘을 수 있습니다. TCP RTT 필드는 마이크로초입니다. **4. 요청 지연을 측정합니다. 확인한 서버 주소마다 반복합니다.** ```bash for bench_ip in "$SAME_IP" "$AZ_IP" "$CROSS_IP"; do kubectl -n "$BENCH_NS" exec cli -- fortio load -quiet -r 0.00001 -json - \ -qps 100 -c 4 -t 60s "http://$bench_ip:8080/" > "http-$bench_ip.json" kubectl -n "$BENCH_NS" exec cli -- fortio load -quiet -r 0.00001 -json - \ -qps 100 -c 4 -t 30s -keepalive=false "http://$bench_ip:8080/" > "new-connection-$bench_ip.json" kubectl -n "$BENCH_NS" exec cli -- fortio load -quiet -r 0.00001 -json - \ -qps 0 -c 16 -t 20s "http://$bench_ip:8080/" > "closed-loop-$bench_ip.json" kubectl -n "$BENCH_NS" exec cli -- fortio load -quiet -r 0.00001 -json - \ -grpc -ping -qps 100 -c 4 -t 30s "$bench_ip:8079" > "grpc-$bench_ip.json" done ``` Fortio 1.69.5의 `-r`은 초 단위의 가장 작은 히스토그램 버킷 해상도입니다. 기본 `0.001`은 1 ms, `0.00001`은 10 µs이며 큰 버킷은 폭이 넓어질 수 있습니다. 분위수는 버킷 경계 안에서 보간하고 양 끝에는 관측 최소·최대값이 반영됩니다. 따라서 버킷 하나에 모였다고 **p50이 반드시 0.5 ms인 것은 아닙니다**. 원문은 첫 실행의 거친 분위수를 버리고 10 µs 해상도로 다시 측정했다고 기록합니다. 이 이력은 유지하되 모든 보간 분위수를 가짜라고 부르면 안 됩니다. 표의 일부 꼬리 값과 새 연결 중앙값은 1 ms를 넘습니다. 평균과 함께 전체 히스토그램과 오류 카운터를 저장하세요. **5. DNS: 한 번의 조회 캡처와 반복 시간 측정을 분리합니다.** ```bash kubectl apply -f bench-dns.yaml kubectl -n "$BENCH_NS" wait --for=condition=Ready pod/dns-default pod/dns-ndots1 --timeout=300s for bench_pod in dns-default dns-ndots1; do kubectl -n "$BENCH_NS" exec "$bench_pod" -c app -- cat /etc/resolv.conf kubectl -n "$BENCH_NS" exec "$bench_pod" -c app -- ldd --version done ``` 터미널 1에서 단일 조회 전에 캡처를 시작합니다. 이 필터는 53번 포트의 일반 UDP·TCP DNS를 포함하지만 암호화 DNS나 CoreDNS의 업스트림 구간은 포함하지 않습니다. 한 번의 조회가 끝나면 Ctrl-C로 중지하고 그 뒤 웜 반복을 실행합니다. ```bash kubectl -n bench-net exec -it dns-default -c sniffer -- \ tcpdump -l -i eth0 -nn '(udp or tcp) and port 53' ``` 터미널 2에서 로컬 헬퍼를 만들고 **`kubectl exec -i`**로 입력을 전달합니다. first 모드는 리졸버를 정확히 한 번 호출합니다. warm 모드는 같은 프로세스에서 준비 호출 1회 후 20회를 측정합니다. 이 보완 절차는 캡처 범위를 명확히 구분합니다. ```bash cat > dns-probe.py <<'PY' import json import socket import statistics import sys import time name, mode = sys.argv[1:3] if mode not in ("first", "warm"): raise SystemExit("mode must be first or warm") def one(): started = time.perf_counter() socket.getaddrinfo(name, 80, socket.AF_UNSPEC, socket.SOCK_STREAM) return (time.perf_counter() - started) * 1000 first = one() if mode == "first": print(json.dumps({"name": name, "first_process_ms": first})) else: samples = [one() for _ in range(20)] ordered = sorted(samples) print(json.dumps({ "name": name, "warmup_ms": first, "samples_ms": samples, "min_ms": ordered[0], "median_ms": statistics.median(ordered), "p90_ms": ordered[17], "max_ms": ordered[-1], })) PY BENCH_NS=bench-net DNS_POD=dns-default DNS_NAME=sts.ap-northeast-2.amazonaws.com kubectl -n "$BENCH_NS" exec -i "$DNS_POD" -c app -- \ python3 - "$DNS_NAME" first < dns-probe.py ``` 캡처를 중지한 뒤: ```bash kubectl -n "$BENCH_NS" exec -i "$DNS_POD" -c app -- \ python3 - "$DNS_NAME" warm < dns-probe.py ``` 표의 이름들과 `DNS_POD=dns-ndots1`로 반복하고 캡처 대상도 같이 바꿉니다. first 전용 구간에서 질의·응답을 세고 21번의 조회를 한 번으로 집계하지 마세요. digest·리졸버 버전·설정을 맞춰도 시간과 캐시 상태는 달라질 수 있습니다. 이 페이지에는 원래 이미지 digest와 전체 패킷·JSON 자료가 없어 정확한 재현은 보장할 수 없습니다. **6. 이번 테스트의 리소스만 정리합니다.** `bench-net`을 이 실행 전용으로 만들었다면 결과를 보관한 뒤 `kubectl delete namespace bench-net`으로 삭제합니다. 남은 노드와 비용은 별도로 확인하세요. Karpenter consolidation은 정책·예산·다른 워크로드에 좌우되므로 네임스페이스 삭제가 즉각적인 노드 제거를 보장하지 않습니다. `do-not-disrupt`도 모든 강제 중단을 막지는 않습니다. ## 해석 시 주의사항 - **새 노드였지만 완전히 혼자는 아니었습니다.** Karpenter가 이 테스트용으로 띄운 m5.xlarge 3대에 곧 consolidation이 다른 네임스페이스의 작은 Pod 몇 개를 옮겨 왔습니다(`cli` 노드에 1개, `srv-b` 노드에 3개 — 소규모 내부 서비스와 컨트롤러이며, 벤치마크 트래픽과는 무관합니다). 측정 중 유휴·저트래픽이었고 부하는 최대 180초 버스트로 제한했습니다. `cli` 노드의 CPU *요청*은 3901m / 3920m(99%)였지만 실제 사용량이 그렇다는 뜻은 아닙니다. - **하루에 셀당 한 번(n = 1) 측정했습니다.** 분산 추정을 위한 독립 반복이 없습니다. 순위·비율·인과 설명도 이 표본 크기의 제약을 받으며 SLA로 사용할 수 없습니다. - **애플리케이션 ClusterIP와 트래픽 분산은 측정하지 않았습니다.** 기록에 따르면 벤치마크 네임스페이스의 Service 생성이 `failed calling webhook "mservice.elbv2.k8s.aws": … no endpoints available for service "aws-load-balancer-webhook-service"`로 실패했습니다. `failurePolicy: Fail`인 실패 웹훅은 매칭되는 요청을 거부하며, 범위는 rules·namespace/object selector·match condition에 달려 있습니다. 과거 사건을 현재 클러스터의 모든 Service 생성이 불가능하다는 뜻으로 읽으면 안 됩니다. 웹훅은 우회하지 않았고 DNS는 기존 `kube-dns` Service를 사용했습니다. [트러블슈팅 플레이북](https://www.atomai.click/kubernetes-docs/llms/ko/ops/16-troubleshooting-playbook.md)을 참고하세요. - **ENA allowance 카운터는 수집하지 않았습니다.** `ethtool -S`는 적절한 권한으로 호스트의 실제 ENA 인터페이스를 대상으로 해야 합니다. Pod 자신의 `eth0`는 대개 veth이며 `hostNetwork`만으로 올바른 장치나 권한이 보장되지 않습니다. 관련 카운터는 `bw_in_allowance_exceeded`, `bw_out_allowance_exceeded`, `pps_allowance_exceeded`, `conntrack_allowance_exceeded`, `linklocal_allowance_exceeded`입니다. 재전송 수는 이를 대신하지 못합니다. - **버스트 크레딧 소진은 180초 안에서 관측되지 않았을 뿐입니다.** "Up to" 인스턴스에서 더 긴 지속 전송은 베이스라인(1.25 Gbps) 쪽으로 제한될 수 있습니다. 180초 이상은 테스트하지 않았습니다. - **DNS 캐시 상태를 통제하지 않았습니다.** 프로세스 첫 조회와 반복 조회는 다르지만 `cache 30`이 30초 히트를 보장하지는 않습니다. replica 선택과 업스트림 상태가 두 집단 모두에 영향을 주므로 비교는 관측 결과로 해석해야 합니다. - **같은 노드의 CPU 부하는 가능한 설명입니다.** 클라이언트 프로세스 CPU 99.8%는 29.97 Gbps 결과의 이 해석을 뒷받침하지만 모든 병목을 특정하거나 29.97 / 48.15 Gbps를 다른 인스턴스에 보장하지는 않습니다. - **다른 CNI 모드와 정책 강제는 비교하지 않았습니다.** Prefix delegation과 Security Groups for Pods는 꺼져 있었고 네임스페이스에는 NetworkPolicy가 없었습니다. 이 bare Pod 테스트는 지원되는 컨트롤러 소유 워크로드의 VPC CNI NetworkPolicy 강제를 검증한 것이 아닙니다. ## 함께 읽기 - [Amazon VPC CNI](https://www.atomai.click/kubernetes-docs/llms/ko/networking/01-vpc-cni.md) — 이 측정의 데이터 플레인: Pod가 VPC IP를 직접 받는 구조, prefix delegation, ENI/IP 워밍 - [Zonal 클러스터 운영 전략](https://www.atomai.click/kubernetes-docs/llms/ko/ops/15-zonal-operations-guide.md) — 측정 3의 요금을 줄이는 존 정렬 배치와 AZ 장애 전환 설계 - [트러블슈팅 플레이북](https://www.atomai.click/kubernetes-docs/llms/ko/ops/16-troubleshooting-playbook.md) — 웹훅 실패 진단; 이 문서의 사건은 과거 기록 - [사이드카 vs Ambient 모드 선택 가이드](https://www.atomai.click/kubernetes-docs/llms/ko/service-mesh/istio/comparison/03-sidecar-vs-ambient.md) — 별도의 하드웨어·부하 실험; +1.29 ms는 시나리오 전체의 차이 - [EBS gp2 vs gp3 실측 벤치마크](https://www.atomai.click/kubernetes-docs/llms/ko/storage/01-ebs-gp2-gp3-benchmark.md) — 같은 클러스터의 스토리지 경로 실측 - [Kafka on EKS 실측 벤치마크](https://www.atomai.click/kubernetes-docs/llms/ko/data-on-eks/kafka/09-kafka-benchmark.md) — 복제 트래픽·플로우 한도·가용성의 관계 - [가이드북 로드맵 — 실측 벤치마크 시리즈](https://www.atomai.click/kubernetes-docs/llms/ko/roadmap.md) - [퀴즈: Pod 네트워크 실측 벤치마크](https://www.atomai.click/kubernetes-docs/ko/quizzes/networking/06-pod-network-benchmark-quiz) ### 검토에 사용한 공식 근거 - [Fortio 1.69.5 histogram implementation](https://github.com/fortio/fortio/blob/v1.69.5/stats/stats.go) · [CLI flags](https://github.com/fortio/fortio/blob/v1.69.5/cli/fortio_main.go) - [glibc 2.41 search ordering](https://github.com/bminor/glibc/blob/glibc-2.41/resolv/res_query.c) · [A/AAAA transport](https://github.com/bminor/glibc/blob/glibc-2.41/resolv/res_send.c) - [CoreDNS Kubernetes / autopath requirements](https://coredns.io/plugins/kubernetes/) - [Kubernetes Service traffic distribution](https://kubernetes.io/docs/concepts/services-networking/service/) · [virtual IP handling](https://kubernetes.io/docs/reference/networking/virtual-ips/) - [EC2 ENA network metrics](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-network-performance-ena.html) - [Karpenter disruption and cleanup conditions](https://karpenter.sh/docs/concepts/disruption/)