https://ithailey.tistory.com/3
[네트워크 기초] 쿠버네티스 패킷의 여정 w/ Flannel
안녕하세요!오늘은 쿠버네티스 공부를 하면서 가장 자주 헷갈렸던 네트워크 통신을 정리해보려고 합니다.Pod에 IP가 붙는다는 건 알겠고, Service를 만들면 이름으로 호출할 수 있다는 것도 알겠는
ithailey.tistory.com
안녕하세요!
오늘은 쿠버네티스 공부를 하면서 가장 자주 헷갈렸던 네트워크 통신을 정리해보려고 합니다.
Pod에 IP가 붙는다는 건 알겠고, Service를 만들면 이름으로 호출할 수 있다는 것도 알겠는데요. 막상 “그래서 패킷이 실제로 어디를 지나가는데?”라고 물으면 모르겠습니다... 저도 그랬습니다ㅎㅎ
특히 아래 질문들이 계속 꼬리를 물었습니다.
· 같은 Pod 안의 컨테이너끼리는 왜 localhost로 통신할 수 있을까?
· 다른 노드의 Pod IP를 물리 네트워크는 어떻게 찾아갈까?
· Pod가 재생성돼 IP가 바뀌는데 서비스는 왜 계속 멀쩡할까?
· curl http://web 한 줄 뒤에서 DNS, iptables, VXLAN은 무슨 일을 할까?
결국 쿠버네티스 통신을 이해하려면 기능 이름을 따로 외우기보다, Flannel을 기준으로 패킷 하나가 출발지 컨테이너에서 목적지 Pod까지 이동하는 장면을 순서대로 따라가보는게 좋을 것 같아서 이번 글에서는 컨테이너의 Namespace부터 CNI, Service, kube-proxy까지 하나의 흐름으로 이어보겠습니다.
1. 시작은 Namespace: 어디까지 격리하고 어디까지 공유할까?
컨테이너는 Linux Namespace를 사용해 서로의 자원을 격리합니다.
· UTS Namespace: hostname 분리
· IPC Namespace: 프로세스 간 통신 자원 분리
· Network Namespace: 네트워크 인터페이스, 라우팅 테이블, 포트 공간 분리
· PID Namespace: 프로세스 ID 분리
· Mount Namespace: 파일 시스템의 마운트 지점 분리
· User Namespace: 사용자와 그룹 ID 분리
여기까지만 보면 컨테이너마다 모든 것을 따로 쓰면 될 것 같습니다. 그런데 쿠버네티스의 최소 배포 단위는 컨테이너가 아니라 Pod입니다.
하나의 Pod 안에는 애플리케이션 컨테이너와 사이드카처럼 서로 긴밀하게 붙어 움직여야 하는 컨테이너가 함께 들어갈 수 있습니다. 이 컨테이너들이 네트워크까지 완전히 분리되어 있으면 매번 서로의 IP를 찾고 연결해야 하겠죠. 쿠버네티스는 여기서 조금 다른 선택을 합니다.
쉽게 말하면 같은 Pod의 컨테이너들은 한집에 사는 룸메이트입니다. 각자 방과 짐은 따로 관리할 수 있지만, 집 주소와 현관은 같이 씁니다. 그래서 같은 IP와 라우팅 테이블, 포트 공간을 공유하고 localhost로 서로 통신할 수 있습니다.
이 때문에 주의할 점도 있습니다. 같은 Pod의 컨테이너 A가 8080 포트를 이미 사용하고 있다면 컨테이너 B는 같은 8080 포트를 사용할 수 없습니다. 컨테이너가 둘이어도 네트워크 관점에서는 한 호스트처럼 보이기 때문입니다.
그런데 여기서 또 질문이 생깁니다.
애플리케이션 컨테이너가 죽었다가 다시 생기면, 공유하던 네트워크 공간도 함께 사라지는 것 아닐까?
그래서 Pod에는 흔히 pause container라고 부르는 아주 작은 인프라 컨테이너가 먼저 만들어집니다. 이 컨테이너가 Pod의 Network Namespace를 잡고 있고, 실제 애플리케이션 컨테이너들이 그 공간에 참여합니다. 앱 컨테이너가 재시작돼도 Pod라는 그릇과 네트워크 정체성은 유지할 수 있는 이유입니다.

2. Pod에 IP를 붙여주는 CNI
Pod 안의 통신은 이해했습니다. 이제 Pod 바깥으로 나가보겠습니다.
1. Pod의 Network Namespace에 네트워크 인터페이스를 만든다.
2. Pod에 IP 주소를 할당한다.
3. Pod 쪽 인터페이스와 노드 쪽 인터페이스를 veth pair로 연결한다.
4. 라우팅 정보나 오버레이 네트워크 구성을 설정한다.

CNI 브리지와 veth 연결
veth pair는 양쪽이 붙어 있는 가상 랜 케이블이라고 생각하면 편합니다. 한쪽 끝은 Pod 안에서 eth0로 보이고, 다른 한쪽 끝은 Worker Node의 브리지에 붙습니다. PPT의 예시에서는 cni0가 가상 스위치 역할을 합니다.
같은 노드 안의 Pod끼리는 비교적 단순합니다. 출발지 Pod의 패킷이 veth를 타고 cni0 브리지에 도착하고, 브리지는 목적지 Pod와 연결된 다른 veth로 패킷을 전달합니다.
문제는 다른 Worker Node에 있는 Pod입니다.
3. 다른 노드의 Pod CIDR을 물리 스위치가 알 수 있을까?
예를 들어 Worker Node 1은 10.244.1.0/24, Worker Node 2는 10.244.2.0/24를 Pod CIDR로 사용한다고 해보겠습니다.
Node 1의 Pod가 10.244.2.10으로 패킷을 보냅니다. Node 1 내부까지는 라우팅할 수 있는데, 그 다음부터가 문제입니다.

서로 다른 노드의 Pod 통신 문제
물리 스위치나 기존 네트워크 장비는 보통 Worker Node의 실제 IP 대역은 알아도 쿠버네티스가 내부에서 만든 10.244.2.0/24 대역까지 자동으로 알지는 못합니다.
“목적지가 10.244.2.10인데… 그 주소가 어느 노드 뒤에 있지?”가 되는 겁니다.
이 문제를 푸는 대표적인 방법은 크게 두 가지입니다.
방법 1. Native Routing
각 Worker Node와 물리 네트워크가 모든 Pod CIDR의 경로를 알게 만드는 방식입니다.
예를 들면 Node 1의 라우팅 테이블에 아래와 같은 경로가 들어갑니다.
10.244.2.0/24 via 192.168.100.12

Native Routing 방식
Pod IP를 그대로 유지하기 때문에 출발지와 목적지를 추적하기 쉽고, 별도의 캡슐화 오버헤드도 적습니다. VPC 라우팅이나 BGP와 자연스럽게 통합할 수 있다는 장점도 있습니다.
다만 모든 네트워크 구간이 Pod CIDR을 알아야 합니다. 노드가 늘고 경로가 많아질수록 라우팅 정보를 어떻게 배포하고 관리할지가 중요해집니다.
방법 2. Overlay Network
기존 물리 네트워크가 Pod 대역을 모른다면, Pod 패킷을 Node 패킷 안에 한 번 더 포장해서 보낼 수도 있습니다.

Overlay와 캡슐화
택배로 비유하면 원래 목적지인 Pod 주소가 적힌 작은 상자를, Worker Node 주소가 적힌 큰 상자에 넣는 방식입니다. 물리 네트워크는 바깥 상자의 Node IP만 보고 목적지 노드까지 배송합니다. 목적지 노드에 도착하면 바깥 포장을 벗기고 원래 Pod로 전달합니다.
VXLAN 모드가 대표적인 예입니다. 기존 인프라 라우팅을 크게 건드리지 않아도 되는 대신 캡슐화에 따른 헤더와 처리 비용이 생깁니다.
정리하면 Native Routing은 “모두가 Pod 주소를 알게 하자”, Overlay는 “모르는 주소는 Node 주소로 포장해 보내자”에 가깝습니다.
4. 그런데 Pod IP를 직접 호출하면 안 되는 이유
여기까지 오면 Pod끼리 통신은 됩니다. 그러면 애플리케이션이 상대 Pod IP를 직접 저장해두고 요청하면 끝일까요?
Pod는 영구적인 서버가 아닙니다. 장애가 나거나 배포가 갱신되면 사라졌다가 새로운 IP로 다시 생성될 수 있습니다. Replica가 1개에서 4개로 늘어날 수도 있고, 반대로 줄어들 수도 있습니다.
오늘의 backend Pod가 10.244.1.20이었다고 해서 내일도 그 주소라는 보장은 없습니다. 음식점 직원에게 주문하려고 매번 주방 직원 개인 휴대폰 번호를 외우는 것과 비슷합니다. 직원이 바뀌면 주문 자체가 끊깁니다.
그래서 쿠버네티스는 Service라는 고정된 창구를 제공합니다.
Service가 제공하는 핵심은 세 가지입니다.
· 변하지 않는 가상 IP(ClusterIP)
· 변하지 않는 DNS 이름
여러 Pod Endpoint로의 로드밸런싱
Service는 selector와 일치하는 Pod들을 Endpoint로 묶습니다. 예를 들어 app: backend 라벨을 가진 Pod가 2개라면 둘 다 같은 Service 뒤에 붙습니다. Pod가 교체되거나 추가되면 Endpoint 목록도 바뀝니다.
클라이언트는 변화하는 Pod IP를 몰라도 됩니다. 그냥 backend-api라는 Service만 바라보면 됩니다.
5. `curl http://backend-api` 뒤에서 벌어지는 일
이제 실제 요청 한 번을 따라가 보겠습니다.
Order Pod에서 아래처럼 요청했다고 가정합니다.
curl http://backend-api/api
1) CoreDNS가 Service 이름을 IP로 바꾼다
Pod 안의 DNS 설정은 클러스터 DNS를 바라봅니다. backend-api라는 짧은 이름은 같은 Namespace를 기준으로 탐색되고, 최종적으로 backend-api.default.svc.cluster.local 같은 FQDN으로 해석될 수 있습니다.
CoreDNS는 이 이름에 대응하는 Service의 ClusterIP를 돌려줍니다.
2) 클라이언트는 Service IP를 목적지로 패킷을 만든다
예를 들어 Service IP가 10.96.0.50, 포트가 80이라면 클라이언트는 그 주소로 요청을 보냅니다.
그런데 ClusterIP는 특정 네트워크 카드에 실제로 붙어 있는 일반 IP가 아닙니다. 말 그대로 가상 IP입니다. 그렇다면 이 패킷을 누가 가로채서 실제 Pod로 보내줄까요?
3) kube-proxy가 만든 규칙이 실제 Pod로 DNAT한다
전통적인 iptables 모드에서는 kube-proxy가 Service와 Endpoint 변화를 감시하면서 노드의 iptables 규칙을 계속 갱신합니다.

Service 요청의 DNS와 DNAT 흐름
패킷이 Service IP와 포트에 해당하면 Service 전용 체인으로 이동하고, 거기서 여러 Endpoint 중 하나가 선택됩니다. 이후 목적지 주소가 실제 Pod IP와 포트로 바뀝니다. 이것이 DNAT입니다.
예를 들면 아래처럼 바뀔 수 있습니다.
10.244.1.10:41000 → 10.96.0.50:80
↓ DNAT
10.244.1.10:41000 → 10.244.2.30:8080
목적지가 실제 Pod IP로 바뀐 다음부터는 앞에서 본 Pod-to-Pod 통신 방식이 다시 등장합니다. 목적지 Pod가 다른 노드에 있다면 Native Routing으로 가거나, Flannel 같은 CNI가 VXLAN 터널로 전달합니다.
즉 curl http://backend-api 한 줄 안에는 DNS 조회 → Service IP 매칭 → Endpoint 선택 → DNAT → 노드 간 라우팅이 연달아 들어 있습니다. 한 줄 치고 끝났는데 뒤에서는 꽤 많은 분들이 일하고 계십니다..
이 다음은 CNI별로 어떤식으로 동작이 바뀌는지 차이점이 뭔지 궁금하여 관련하여 테스트해본걸 작성하도록 하겠습니다.
감사합니다.
'IT Note > IT Basics' 카테고리의 다른 글
| [Network 기초] 2. L2 스위치 기본동작 이해하기 - 왜 L2 스위치가 필요하지? (0) | 2026.04.18 |
|---|---|
| [Network 기초] 1. 동일 서브넷 내 통신 과정 이해하기 (0) | 2026.02.22 |
| 한국 IT 연봉 오체분시 (with 사람인 & Jumpit) (0) | 2025.11.07 |
| 나만 빼고 다 아는 퀀텀 컴퓨팅 (0) | 2025.06.22 |
| 데이터 매쉬: 중앙 집중형에서 분산형으로 (0) | 2024.08.17 |