Network - 정리(2)

2026. 1. 5. 22:12·CKA

쿠버네티스 네트워킹은 “Pod에 IP를 하나씩 준다”에서 시작해,
CNI로 연결을 자동화하고, Service/kube-proxy로 VIP(가상 IP)를 구현하고,
DNS로 이름 기반 통신을 만들고, Ingress로 L7 진입점을 정리,
마지막으로 Gateway API로 멀티테넌시/표준화된 라우팅 모델을 완성합니다.


0) 전체 큰 그림

쿠버네티스 네트워킹을 기능별로 나누면 이렇게입니다.

  1. Pod 네트워킹(데이터 플레인)
    • Pod마다 고유 IP
    • Pod ↔ Pod (같은 노드/다른 노드) 통신 보장
    • NAT 없이 “Pod IP 그대로” 통신
  2. CNI + IPAM(자동화/주소 관리)
    • Pod 생성/삭제 시 네트워크 연결 작업을 자동으로 수행
    • IP 중복 없이 할당/회수
  3. Service 네트워킹(kube-proxy)
    • Service IP(ClusterIP)는 “실재하는 인터페이스 IP”가 아니라 가상 VIP
    • kube-proxy가 iptables/ipvs 규칙을 만들어 Service → Pod로 바꿔치기(DNAT)
  4. 클러스터 DNS(CoreDNS)
    • service.namespace.svc.cluster.local 같은 규칙으로 이름 해석
    • Pod는 기본적으로 “이름=PodName” 레코드가 아니라, 옵션에 따라 “IP 기반 레코드”
  5. Ingress(L7 진입점)
    • 외부 트래픽을 HTTP(S) 기준으로 서비스들에 라우팅
    • TLS/경로 기반 분기/리라이트 등
  6. Gateway API(차세대 표준 라우팅 + 멀티테넌시)
    • GatewayClass/Gateway/Route로 역할 분리
    • annotation 의존을 줄이고, 표준 스펙으로 고급 라우팅 제공

1) Pod 네트워킹: “Pod마다 IP 하나”를 어떻게 현실로 만들까?

쿠버네티스가 기대하는 Pod 네트워킹 요구사항은 단순하지만 강력합니다.

  • 모든 Pod는 고유한 IP를 가져야 한다.
  • 같은 노드의 Pod끼리 Pod IP로 통신 가능해야 한다.
  • 다른 노드의 Pod끼리도 Pod IP로 통신 가능해야 한다.
  • 그리고 가능하면 NAT 없이 이 조건을 만족하라.

쿠버네티스는 “이걸 어떻게 구현할지”는 직접 제공하지 않고, 요구사항만 제시합니다. (그래서 CNI 플러그인들이 존재)

1.1 노드 내부: 네트워크 네임스페이스 + veth + bridge

컨테이너(=Pod)가 만들어지면, Linux 관점에서는 보통 이런 형태가 됩니다.

  • Pod마다 네트워크 네임스페이스(netns) 생성
  • 호스트와 Pod netns 사이를 veth pair(가상 케이블)로 연결
  • 호스트 쪽 veth들을 bridge에 붙여 L2 네트워크를 구성
  • Pod netns 안쪽 veth에 IP를 주고, default route를 bridge 쪽 게이트웨이로 설정

개념적으로는 이런 그림입니다.

[Pod(netns)] veth0 -- veth-host ----(bridge cni0 같은)---- (host)
                 IP: 10.244.1.2/24       GW: 10.244.1.1

네임스페이스 ping 문제에서 중요한 포인트: IP 설정할 때 NETMASK(/24 등) 를 제대로 주지 않으면 라우팅/ARP가 꼬일 수 있습니다.
예: ip -n red addr add 192.168.1.10/24 dev veth-red

또한 실습 환경에서 통신이 안 되면, FirewallD/iptables 정책이 네임스페이스 트래픽을 막는 경우가 많습니다.


1.2 노드 간: 라우팅(또는 오버레이)로 “10.244.0.0/16”을 하나로 묶기

노드마다 Pod CIDR을 따로 준다면 예를 들어:

  • Node1: 10.244.1.0/24
  • Node2: 10.244.2.0/24
  • Node3: 10.244.3.0/24

이때 Node1의 Pod(10.244.1.2)가 Node2의 Pod(10.244.2.2)로 가려면,
Node1이 “10.244.2.0/24는 Node2로 가라”는 라우팅 정보를 알아야 합니다.

단순한 방식은 각 노드에 static route를 넣는 것:

# Node1에서: 10.244.2.0/24는 Node2(192.168.1.12)로
ip route add 10.244.2.0/24 via 192.168.1.12

하지만 노드/Pod가 수백/수천이면, 이 방식은 라우팅 테이블이 커지고 운영이 불가능해집니다.

그래서 실제 CNI 솔루션들은 보통:

  • 라우팅을 자동으로 전파(BGP 등)하거나
  • 오버레이(캡슐화) 로 “노드 간 네트워크를 하나처럼” 보이게 합니다.

2) CNI: 이 모든 과정을 누가 “자동으로” 해주나?

여기서 CNI(Container Network Interface)가 등장합니다.

  • 쿠버네티스(정확히는 노드의 런타임/에이전트)가
  • “Pod 만들었으니 네트워크 연결해라”를 표준 방식으로 플러그인에게 요청할 수 있게 만든 규격

2.1 왜 kubelet runtime endpoint 질문이 중요했나?

실습에서 자주 나오는 질문:

“Inspect the kubelet service and identify the container runtime endpoint value…”

이게 중요한 이유는:

  • CNI 플러그인을 실제로 실행하는 주체는 “컨테이너 런타임” 계층이기 때문입니다.
  • kubelet은 CRI를 통해 containerd/cri-o 같은 런타임에게 “컨테이너 만들기”를 시키고,
  • 런타임은 설정된 CNI를 통해 “네트워크 붙이기(Add)”를 호출합니다.

즉, runtime endpoint는 “내 클러스터가 실제로 어떤 런타임을 통해 Pod 생성/네트워크 구성을 트리거하는지”를 확인하는 열쇠입니다.
트러블슈팅할 때도 런타임이 바뀌면 CNI 동작 지점과 로그/설정 위치가 달라질 수 있습니다.


2.2 “이 클러스터는 어떤 CNI 플러그인을 쓰나?”가 중요한 이유

CNI 플러그인은 곧 Pod-to-Pod 연결 방식(라우팅/오버레이/IPAM/NetworkPolicy 지원 여부) 를 결정합니다.

확인 루틴은 다음이 실무적으로 가장 빠릅니다.

# 1) 설치된 CNI(대부분 DaemonSet)
kubectl get pods -A | grep -E "calico|cilium|flannel|weave|antrea"

# 2) 노드에서 CNI 설정 파일(환경에 따라 접근 방식 다름)
ls /etc/cni/net.d/
ls /opt/cni/bin/

그리고 CNI 설정 파일(/etc/cni/net.d/*.conf 또는 *.conflist)에서

  • 어떤 plugin(type)이 사용되는지,
  • IPAM이 host-local인지, dhcp인지,
  • 어떤 CIDR을 쓰는지 확인합니다.

여러 파일이 있으면 “알파벳 순으로” 선택되는 케이스가 실습/환경에 따라 존재할 수 있으니, 실제로는 conflist 우선/설정 방식은 배포 도구에 따라 확인이 필요합니다.


3) IPAM: Pod IP는 누가, 어디서 관리하나?

Pod IP 관리의 책임은 “쿠버네티스”가 아니라 CNI 플러그인(또는 그 IPAM 플러그인) 입니다.

강의의 핵심 포인트:

  • “IP 중복 없이 할당/회수”만 잘 되면 Kubernetes는 방식에 크게 관여하지 않음
  • 로컬 파일로 IP 사용 현황을 관리할 수도 있고,
  • CNI가 제공하는 IPAM 플러그인(host-local, dhcp 등)에게 위임할 수도 있음

3.1 host-local IPAM의 핵심

  • 노드 로컬에서 IP 풀을 관리(파일 기반)
  • 노드가 관리하는 Pod들에 대해 “중복 없는 IP 할당”을 보장

CNI config의 iPam 섹션에서 보통 이런 형태가 등장합니다.

{
  "ipam": {
    "type": "host-local",
    "subnet": "10.244.1.0/24",
    "routes": [
      { "dst": "0.0.0.0/0" }
    ]
  }
}

4) Weave 같은 오버레이 CNI는 라우팅 문제를 어떻게 풀까?

우리가 수동으로 했던 방식은 “각 노드 라우팅 테이블에 모든 Pod CIDR을 등록”하는 방식이었고,
큰 규모에서 비현실적입니다.

Weave Net(예시 CNI)의 설명 흐름은 다음과 같습니다.

  • 각 노드에 Weave peer(에이전트) 를 배치(보통 DaemonSet)
  • peer들이 서로 통신하며 토폴로지를 공유(다른 노드의 Pod IP 정보 인지)
  • Pod 간 트래픽이 다른 노드로 가야 하면,
    • 패킷을 캡슐화(overlay) 해서 노드 간 네트워크로 보내고
    • 목적지 노드에서 디캡슐화 후 해당 Pod로 전달

이 방식의 장점:

  • 외부 라우터/노드 라우팅 테이블에 “Pod CIDR 전체”를 직접 관리하지 않아도 됨
  • 노드 수가 늘어나도 비교적 확장 가능

5) NetworkPolicy: flannel은 안 된다던데, 다른 건?

핵심만 정리하면:

  • NetworkPolicy는 “Kubernetes 리소스”지만, 실제로 enforced(차단/허용)를 수행하는 건 CNI 구현체입니다.
  • 그래서 CNI가 NetworkPolicy를 구현하지 않으면, YAML을 써도 “동작하지 않는 것처럼” 보입니다.

실무적으로는:

  • Calico, Cilium 같은 CNI는 NetworkPolicy(또는 확장 정책)를 적극 지원하는 편
  • flannel은 전통적으로 “기본 네트워킹 제공”에 초점이고, NetworkPolicy는 별도 조합이 필요하거나 제한이 있는 구성들이 흔합니다.

결론:

  • “보안/격리/제로트러스트”가 중요하면, CNI 선택 기준에 NetworkPolicy 지원을 반드시 포함해야 합니다.

6) Service 네트워킹: Service IP는 “실존”하는가?

이 파트는 쿠버네티스 네트워킹에서 가장 오해가 많은 지점입니다.

6.1 Service는 Pod처럼 “어딘가에 떠 있는 서버”가 아니다

  • Pod는 netns/인터페이스/IP가 “실제”로 존재
  • Service는 클러스터에 “오브젝트”로 존재하지만,
    • Service IP에 대응되는 “프로세스/네임스페이스/인터페이스”는 없습니다.

그럼에도 Service IP로 접속이 되는 이유는?

6.2 kube-proxy가 “규칙을 깔아두기” 때문이다

kube-proxy는 Service가 생성/변경되면,
각 노드에 포워딩 규칙(iptables/ipvs 등) 을 설치합니다.

바꿔치기(DNAT)는 왜 하나?

Service의 핵심 목적은:

  • Pod는 늘었다 줄었다 하고 IP도 바뀔 수 있음
  • 그런데 클라이언트(다른 Pod)는 “DB”를 언제나 같은 주소로 접근하고 싶음

그래서 쿠버네티스는:

  • Service IP(고정된 VIP) 를 클라이언트에게 주고,
  • 실제 백엔드 Pod로는 kube-proxy가 설치한 규칙을 통해 목적지 주소를 바꿔치기 합니다.

예시 개념:

  • Client → 10.103.132.104:3306 (Service VIP)
  • 커널 iptables NAT 규칙에 의해
  • 실제로는 → 10.244.1.2:3306 (Pod IP) 로 전달

“실제 라우팅 주체가 프록시인가?”

iptables 모드 기준으로 정확히 말하면:

  • kube-proxy가 매 요청을 “중간에서 프록시”하는 게 아니라
  • kube-proxy는 커널에 규칙을 프로그래밍하고,
  • 실제 패킷 포워딩/변환(DNAT)은 리눅스 커널(netfilter/iptables) 이 수행합니다.

즉:

  • 제어면(control plane): kube-proxy(규칙 생성/삭제)
  • 데이터면(data plane): 커널(규칙 적용, 실제 패킷 처리)

7) DNS: 같은 네임스페이스면 짧게, 다른 네임스페이스면 길게

DNS 파트에서 핵심 질문이 이거였습니다.

같은 네임스페이스면 web-server
다른 네임스페이스면 web-server.apps
그 뒤의 .svc.cluster.local은 언제 붙이냐?

정리하면:

7.1 기본 FQDN 규칙

Service의 정규 FQDN은 보통:

  • service-name.namespace.svc.cluster.local

예:

  • web-service.default.svc.cluster.local
  • web-service.apps.svc.cluster.local

7.2 왜 같은 네임스페이스면 짧게 되나?

Pod의 /etc/resolv.conf에는 보통 search 도메인이 들어갑니다.

  • search <namespace>.svc.cluster.local svc.cluster.local cluster.local

그래서 같은 네임스페이스에서 web-service만 쳐도,
DNS가 자동으로 뒤를 붙여가며 질의합니다.

7.3 다른 네임스페이스면 web-service.apps가 필요한 이유

다른 네임스페이스의 서비스는 search 첫 후보로 안 잡히므로,
최소한 namespace까지는 명시해야 충돌 없이 정확히 찾습니다.

7.4 .svc.cluster.local은 “다른 노드/다른 클러스터라서” 붙이는 게 아니다

  • .svc.cluster.local은 클러스터 내부 DNS의 루트 도메인 관례(기본값)입니다.
  • 노드가 달라도 같은 클러스터면 동일 규칙.
  • “다른 클러스터”는 애초에 DNS 영역이 다르거나 네트워크가 분리된 케이스가 대부분이라, 단순히 suffix를 붙인다고 해결되는 개념이 아닙니다.

7.5 CoreDNS 구현: kube-dns vs coredns 둘 다 있는 건가?

네 출력에서 관찰된 전형적 패턴:

  • Pod는 coredns-... 가 실제로 떠 있음(Deployment)
  • Service 이름은 kube-dns 로 존재함(ClusterIP: 53/UDP,TCP)

즉,

  • DNS 서버 구현체는 CoreDNS Pod
  • kube-dns는 그 CoreDNS를 가리키는 Service 이름(호환 관례)

Pod 입장에서는 /etc/resolv.conf의 nameserver가 kube-dns Service IP를 가리키고,
그 트래픽이 CoreDNS Pod로 전달됩니다.


8) Ingress: 서비스와 뭐가 다르고, 왜 필요한가?

Service(NodePort/LoadBalancer)만으로도 외부 노출은 가능합니다.
하지만 운영 요구사항이 올라가면 급격히 복잡해집니다.

  • 포트 숨기기(80/443)
  • URL 경로 기반 라우팅(/watch는 video, /wear는 apparel)
  • 도메인 기반 라우팅(watch.my-online-store.com)
  • TLS(HTTPS) 중앙 처리
  • 서비스 늘어날 때마다 LB를 늘리고 비용 증가 문제

Ingress는 이를 “클러스터 안에서”, “Kubernetes 리소스로”, “한 번의 외부 진입점으로” 정리합니다.

8.1 Ingress의 구성요소 2개

  1. Ingress Controller(nginx, traefik, HAProxy, …)
  2. Ingress Resource(규칙: host/path/backend)

Ingress 리소스만 만들고 컨트롤러가 없으면 동작하지 않습니다.

8.2 rewrite-target(리라이트)이 왜 필요했나?

실습에서 가장 흔한 함정:

  • 사용자는 /watch로 들어오고 싶어서 Ingress에 /watch → watch-service를 설정
  • 그런데 백엔드 앱은 /watch라는 path를 모르고 /만 제공

rewrite-target 없이 동작하면:

  • /watch 요청이 백엔드에 /watch로 그대로 전달 → 404

그래서 Ingress에서:

  • /watch로 들어온 요청을 백엔드에는 /로 바꿔서 전달해야 합니다.

NGINX Ingress에서는 대표적으로:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /

9) 네임스페이스 운영: Ingress를 쓰면 더 중요해진다

Ingress를 도입하면 결국 host/path를 누가 소유하느냐가 운영의 핵심이 됩니다.

권장 구조(현실적인 운영 패턴):

  • 인프라(컨트롤러) 전용 네임스페이스: ingress-nginx
  • 앱 네임스페이스: prod-store, prod-video (환경+도메인 조합 추천)
  • Ingress 규칙은 보통:
    • 각 팀이 소유하는 서비스 네임스페이스에 두는 편이 RBAC/운영이 깔끔
    • 단, “한 도메인/한 Ingress에 다수 팀이 얽히는 구조”라면 중앙 조율 정책 필요

TLS Secret은 네임스페이스 스코프이므로,
Ingress와 인증서 관리 전략(cert-manager 등)을 함께 설계해야 합니다.


10) Gateway API: Ingress의 한계를 “역할 분리 + 표준 스펙”으로 해결

Ingress의 대표적인 약점:

  • 멀티테넌시: 여러 팀이 하나의 Ingress 리소스를 같이 편집해야 충돌
  • 고급 기능: 컨트롤러별 annotation으로 흩어지고, 쿠버네티스가 검증 불가
  • HTTP 위주: TCP/UDP/gRPC 등 확장 표현이 제한적

Gateway API는 이를 공식 프로젝트로 해결하려고 합니다.

10.1 3개의 오브젝트로 “3개의 페르소나” 분리

  • GatewayClass: 인프라 제공자(어떤 컨트롤러/구현체를 쓸지)
  • Gateway: 클러스터 운영자(리스너/포트/허용 라우트 범위)
  • Route(HTTPRoute/TCPRoute/…): 앱 개발자(서비스별 라우팅 규칙)

예: GatewayClass

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: nginx
spec:
  controllerName: nginx.org/gateway-controller

예: Gateway(Listener + allowedRoutes)

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: nginx-gateway
  namespace: default
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All

예: HTTPRoute(경로 라우팅)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: basic-route
  namespace: default
spec:
  parentRefs:
  - name: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /app
    backendRefs:
    - name: my-app
      port: 80

10.2 Gateway API의 핵심 장점: annotation 없이 “스펙으로” 표현

  • HTTP→HTTPS 리다이렉트: RequestRedirect
  • Path rewrite: URLRewrite
  • 헤더 조작: RequestHeaderModifier / ResponseHeaderModifier
  • 트래픽 분할: backendRefs.weight
  • 미러링: RequestMirror

예: 트래픽 분할 80/20

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: traffic-split
spec:
  parentRefs:
  - name: nginx-gateway
  rules:
  - backendRefs:
    - name: v1-service
      port: 80
      weight: 80
    - name: v2-service
      port: 80
      weight: 20

이렇게 하면 “어디로 어떻게 보내는지”가 YAML만 봐도 드러나고,
컨트롤러가 달라져도(지원 범위 내라면) 일관되게 적용되도록 설계되었습니다.

10.3 L4/L7 확장

Ingress는 주로 HTTP 중심이지만,
Gateway API는 TCP/UDP/TLS/gRPC 같은 라우트 모델을 확장 가능한 형태로 제공합니다(컨트롤러 구현/지원 범위에 따라 다름).


11) 실전 점검/트러블슈팅 체크리스트

Pod 네트워크/CNI

kubectl get pods -A | grep -E "calico|cilium|flannel|weave|antrea"
ls /etc/cni/net.d/
ls /opt/cni/bin/

Service/kube-proxy (iptables 확인)

kubectl -n kube-system get pods | grep kube-proxy
# 노드에서:
iptables -t nat -L -n | grep <service-name>

DNS(CoreDNS/kube-dns)

kubectl -n kube-system get deploy,pod,svc | grep -E "coredns|kube-dns"
kubectl exec -it <pod> -- cat /etc/resolv.conf
kubectl exec -it <pod> -- nslookup web-service

Ingress

kubectl get ingress -A
kubectl describe ingress <name> -n <ns>
kubectl logs -n ingress-nginx deploy/<controller-deploy>

Gateway API

kubectl get crd | grep gateway.networking.k8s.io
kubectl get gatewayclass
kubectl get gateway -A
kubectl get httproute -A
kubectl describe gateway <name> -n <ns>
kubectl describe httproute <name> -n <ns>

12) 결론: 무엇부터 확실히 잡아야 하나?

네트워킹을 “순서대로” 제대로 이해하려면 이 순서가 가장 안정적입니다.

  1. Pod 네트워크 요구사항(Pod IP 고유 + Pod-to-Pod 전 구간 통신)
  2. CNI가 Pod 생성 시 네트워크를 어떻게 붙이는지(Add/Del)
  3. IPAM이 IP 중복을 어떻게 막는지
  4. Service VIP가 ‘실체 없음’에도 되는 이유(kube-proxy가 규칙 설치 → 커널 DNAT)
  5. DNS가 짧은 이름을 어떻게 FQDN으로 확장하는지(search domain)
  6. Ingress가 L7 진입점에서 운영 복잡도를 어떻게 줄이는지(TLS/라우팅/리라이트)
  7. Gateway API가 멀티테넌시/표준화/확장성을 어떻게 해결하는지

원하면, 이 총정리 글을 기반으로 (1) 실습용 미니 랩 시나리오(flannel 환경에서 어디까지 되는지, NetworkPolicy를 쓰려면 CNI를 무엇으로 바꿔야 하는지 포함)와 (2) 운영 표준 템플릿(네임스페이스/권한/RBAC/allowedRoutes/TLS 전략)을 “회사에서 그대로 쓰는 문서” 스타일로 추가 확장해줄 수 있습니다.

'CKA' 카테고리의 다른 글

Design Kubernetes - Choosing Infrastructure  (0) 2026.01.06
Design Kubernetes  (0) 2026.01.06
Network - Gateway API  (0) 2026.01.05
Network - Ingress  (1) 2026.01.05
Network - Service Networking  (0) 2026.01.05
'CKA' 카테고리의 다른 글
  • Design Kubernetes - Choosing Infrastructure
  • Design Kubernetes
  • Network - Gateway API
  • Network - Ingress
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (242)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (118)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      조회 성능 최적화
      @discriminatorvalue
      단방향 맵핑
      @args
      양방향 맵핑
      버퍼
      requset scope
      자바
      hibernate5module
      WAS
      스레드
      빈 후처리기
      jpq
      프록시
      락
      고급
      Target
      JPQL
      @discriminatorcolumn
      cglib
      log trace
      @within
      Thread
      프록시 팩토리
      페치 조인
      typequery
      reentarantlock
      김영한
      gesingleresult
      jdk 동적 프록시
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    Network - 정리(2)
    상단으로

    티스토리툴바