본문으로 바로가기 본문 바로가기
WIKI 기술 지식 베이스

BGP 없이 파드와 서비스를 위한 L2 도달성 확보

원문 보기 위키 갱신

BGP 설정 없이 로컬 L2 네트워크에서 파드와 LoadBalancer 서비스 IP에 도달하게 만드는 방법이에요. Calico Cloud가 그 IP들에 대한 ARP와 NDP 요청을 직접 대답하는 원리를 다뤄요. 온프레미스나 엣지처럼 BGP를 쓰기 어려운 플랫 L2 환경에서 특히 유용해요.

출처: 문서

본문

전체 그림

Calico Cloud가 해당 IP들에 대한 ARP와 NDP 요청을 직접 대답하게 해서, BGP를 설정하지 않고도 파드와 LoadBalancer 서비스 IP를 로컬 layer 2(L2) 네트워크에서 도달 가능하게 만들어요.

가치

파드나 LoadBalancer VIP가 노드 물리 인터페이스와 같은 서브넷의 IP를 할당받으면, 그 L2 세그먼트의 외부 호스트는 ARP(IPv4)나 Neighbor Solicitation(IPv6)으로 접근을 시도해요. 기본값에서는 아무도 대답하지 않아요. 파드 IP는 veth 뒤 네트워크 네임스페이스 안에 있고, LoadBalancer VIP는 어느 인터페이스에도 존재하지 않기 때문이에요.

전통적으로 이 IP들을 도달 가능하게 만드는 유일한 방법은 BGP로 광고하는 것이었고, 이는 네트워크 인프라와 피어링을 맺어야 함을 뜻해요. 플랫 L2 환경 — 온프레미스 환경, 엣지 배포, kind 클러스터, 업스트림 라우터를 설정할 수 없는 네트워크 — 에서는 BGP가 선택지가 아닐 수 있어요.

localSubnetL2Reachability를 켜면 Calico Cloud가 관련 호스트 인터페이스에서 유저스페이스 ARP/NDP 응답기(responder)를 돌리고, 호스트 서브넷 안에 든 로컬 파드 IP와 선별된 LoadBalancer VIP에 대해 대신 대답해 줘요. BGP도, 오버레이 캡슐화도, 네트워크 인프라 변경도 필요 없어요.

개념

유저스페이스 ARP/NDP 응답기

Calico Cloud는 자기 서브넷 안에 최소 하나의 파드 IP나 LoadBalancer VIP를 둔 호스트 물리 인터페이스마다 raw socket을 열고, 그 IP들에 대한 ARP/NDP 요청에 대답해요. 응답에는 노드의 MAC 주소가 담기기 때문에, 외부 호스트는 워크로드의 트래픽을 그 노드로 보내요. 거기서부터는 Calico Cloud의 평소 데이터 플레인이 나머지 경로를 전달해요. 파드 IP라면 로컬 파드로 곧장, LoadBalancer VIP라면 서비스 백엔드로 로드 밸런싱해요. 응답기는 ARP/NDP에만 대답해요. 트래픽이 노드에 도달한 이후의 전달 방식을 바꾸지는 않아요.

LoadBalancer VIP마다 한 노드가 대답해요

LoadBalancer VIP에는 정확히 한 노드가 ARP에 대답해요. Calico Cloud는 consistent-hash ring으로 클러스터 전체에서 일관되게 그 노드를 선출하기 때문에, 노드들이 조율 없이도 각자 독립적으로 소유자에 대해 같은 결론에 도달해요. 소유 노드의 Node 오브젝트가 삭제되면 VIP가 다른 노드로 재할당되고, 그 노드가 gratuitous ARP / unsolicited Neighbor Advertisement로 외부 캐시를 능동적으로 갱신해요.

캡슐화 없는 IP 풀만 자격이 있어요

Calico Cloud는 캡슐화가 없는 IP 풀(ipipMode와 vxlanMode가 둘 다 Never)에 속한 IP에만 대답해요. 캡슐화된 풀은 사설 파드 네트워크로 취급되고, 호스트 L2 세그먼트에 절대 광고되지 않아요.

시작하기 전에...

제약 사항

  • LoadBalancer VIP당 노드 하나. active-active 고가용성은 없어요. 소유 노드가 내려가면, 그 Node 오브젝트가 제거되고 새 소유자가 선출될 때까지 VIP에 도달할 수 없어요.
  • 클라우드 공급자의 ARP 필터링. 일부 클라우드 네트워크(예: AWS VPC)는 하이퍼바이저에서 ARP를 걸러 내요. VIP/파드 IP가 노드의 클라우드 인터페이스에 할당되지 않으면 응답기가 동작하지 않아요. 이는 Calico Cloud가 제어할 수 없는 인프라 제약이에요.
  • 이기종 서브넷. LoadBalancer VIP의 노드 선택은 노드가 VIP 서브넷에 인터페이스를 갖고 있는지 확인 없이 모든 노드를 대상으로 해시해요. 노드들이 같은 서브넷을 공유하지 않을 때만 문제가 돼요.
  • 같은 L2 세그먼트 위의 여러 호스트 인터페이스. Calico Cloud는 프록시 중인 IP가 서브넷에 속한 모든 호스트 인터페이스에서 대답해요. 한 노드에 같은 L2 세그먼트(같은 브로드캐스트 도메인)에 놓인 인터페이스가 둘 이상 있고 프록시 중인 IP가 그 공유 서브넷에 속하면, 그 인터페이스 각각이 자기 MAC 주소로 ARP/NDP에 대답해요. 그래서 외부 호스트는 그 IP가 여러 MAC으로 풀리는 걸 보게 되고, neighbor 캐시가 그 사이를 오가게 돼요.
  • LoadBalancer 서비스는 externalTrafficPolicy: Cluster만 지원해요. Calico Cloud는 서비스 백엔드가 어디에 있든 무관하게, 모든 노드에 대한 consistent hash로 VIP에 대답할 노드를 선출해요. externalTrafficPolicy: Local에서는 선출된 노드에 로컬 백엔드가 없을 수 있고, 그 노드에 도달한 트래픽은 버려져요. externalTrafficPolicy는 기본값인 Cluster로 두세요.
  • IPVS 모드의 kube-proxy는 strictARP가 필요해요. IPVS 모드에서 kube-proxy는 모든 LoadBalancer VIP를 모든 노드의 kube-ipvs0 더미(dummy) 인터페이스에 바인딩해요. Linux 커널은 기본적으로 호스트에 설정된 모든 IP에 대해 ARP에 대답하기 때문에, Calico Cloud가 선출한 노드가 아니라 모든 노드가 VIP에 대답하고, 외부 호스트는 여러 MAC 주소에서 상충하는 응답을 받게 돼요. kube-proxy를 iptables나 nftables 모드로 돌리거나, IPVS 모드를 유지하면서 kube-proxy 설정(kube-proxy ConfigMap 안에 YAML로 저장되는 KubeProxyConfiguration의 ipvs.strictARP 필드)에 strictARP: true를 설정해서, 커널이 더미 인터페이스 VIP에 대해 ARP에 대답하지 않고 Calico Cloud의 응답기에 맡기도록 하세요.

참고(SECONDARY): ARP/NDP 응답기는 Felix(calico-node) 안에서 돌아가요. 노드의 Felix가 재시작하는 동안에는 잠깐 ARP나 NDP에 대답하지 않아요. 업스트림 라우터와 스위치가 그 IP들에 대한 노드 MAC을 여전히 캐시하고 있으므로 기존 연결은 계속 동작하지만, 업스트림에 아직 캐시되지 않은 IP로의 새 연결은 Felix가 시작을 마치고(보통 1분 미만) 응답기 상태를 다시 만들기 전까지는 실패할 수 있어요.

영향을 줄이려면 업스트림 라우터와 스위치의 ARP/neighbor 캐시 타임아웃(aging time)을 늘려서, 캐시된 항목이 Felix 재시작을 견뎌 내게 하세요.

방법

로컬 서브넷 L2 도달성 켜기

기본 FelixConfiguration에 localSubnetL2Reachability를 PodsAndLoadBalancers로 설정해요:

kubectl patch felixconfiguration default --type='merge' \
   -p '{"spec": {"localSubnetL2Reachability": "PodsAndLoadBalancers"}}'

이 기능의 기본값은 Disabled예요. 이 설정은 데이터 플레인이 시작될 때 읽혀요. 그래서 값을 바꾸면 새 값을 적용하기 위해 Felix(calico-node)가 자동으로 재시작해요. 수동 재시작은 필요 없어요. 기능은 Felix가 재시작을 마치면 효력을 발휘해요. 전체 필드 참고 문서는 Felix 설정 리소스를 보세요.

참고(SECONDARY): 기능만 켠다고 아무 일도 일어나지 않아요. Calico Cloud는 호스트 서브넷과 겹치는, 캡슐화 없는 IP 풀 안의 IP에만 대답해요. 그러니 그 풀도 반드시 만들어야 해요.

선택 사항: 주기적 재광고 조정하기. 기능이 켜져 있는 동안 Felix는 프록시 중인 모든 IP를 주기적으로 gratuitous ARP / unsolicited Neighbor Advertisement로 다시 광고해요. 덕분에 프록시 중인 IP 집합이 바뀌지 않았어도 업스트림 neighbor 캐시와 스위치 포워딩 테이블이 계속 살아 있어요. localSubnetL2ReachabilityRefreshInterval이 이 주기를 조절해요(기본값 120s). 업스트림 장비가 ARP/NDP 항목을 빨리 만료시킨다면 값을 낮추고, 주기적 재광고를 끄고 IP가 처음 프록시될 때 한 번만 보내는 광고만 유지하려면 0으로 설정하세요:

kubectl patch felixconfiguration default --type='merge' \
   -p '{"spec": {"localSubnetL2ReachabilityRefreshInterval": "30s"}}'

호스트 서브넷 안에 전용 IP 풀 만들기

호스트 인터페이스의 서브넷 안에 CIDR이 들어가고, 캡슐화가 없는 IP 풀을 만들어요.

주의(WARNING): 이 풀에는 호스트 서브넷의 부분 범위를 예약하되, DHCP 범위, 고정 호스트 할당, 인프라 주소(라우터, 스위치, 관리 인터페이스)와 겹치지 않게 하세요. 예를 들어 호스트 서브넷이 10.0.0.0/23이라면, 호스트에는 10.0.0.2–10.0.0.255를 쓰고 풀에는 별도로 10.0.1.0/24를 쓰는 식이에요.

파드용:

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: host-subnet-pod-pool
spec:
  cidr: 10.0.1.0/24
  ipipMode: Never
  vxlanMode: Never
  natOutgoing: false
  disabled: false
  assignmentMode: Manual

assignmentMode: Manual은 이 풀을 옵트인 방식으로 유지해요. Calico Cloud는 이 풀에서 절대 자동으로 할당하지 않으므로, 명시적으로 이 풀로 유도한 워크로드만 호스트 서브넷 IP를 받아요. 이 설정이 없다면 Calico Cloud가 아무 파드에나 이 풀의 IP를 할당할 수 있어요.

LoadBalancer 서비스용으로는 allowedUses: [LoadBalancer]를 추가해요:

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: host-subnet-lb-pool
spec:
  cidr: 10.0.2.0/24
  ipipMode: Never
  vxlanMode: Never
  natOutgoing: false
  disabled: false
  assignmentMode: Manual
  allowedUses:
    - LoadBalancer

assignmentMode: Manual 덕분에 평범한 LoadBalancer 서비스는 여전히 기존 자동 풀에서 VIP를 받아요. LoadBalancer kube-controller의 assignIPs 모드를 바꿀 필요가 없어요. 여러분이 어노테이션을 붙인 서비스만 이 호스트 서브넷 풀에서 VIP를 가져요.

클러스터의 기본 워크로드 풀은 그대로이고, 그 풀에서 할당된 워크로드는 이전과 정확히 같게 동작해요. LoadBalancer IP 풀에 대해 더 알고 싶으면 LoadBalancer IP 주소 관리 설정을 보세요.

파드나 서비스를 풀로 유도하기

파드와 서비스는 새 풀을 자동으로 쓰지 않아요. 호스트 L2 세그먼트에서 도달 가능하기를 원하는 특정 워크로드를 풀로 유도하세요.

파드: 파드나 그 네임스페이스에 풀 이름으로 어노테이션을 붙여요:

cni.projectcalico.org/ipv4pools: '["host-subnet-pod-pool"]'

LoadBalancer 서비스: Service에 풀 이름으로 어노테이션을 붙여요:

projectcalico.org/ipv4pools: '["host-subnet-lb-pool"]'

IPv6라면 projectcalico.org/ipv6pools를 쓰세요. 이 어노테이션이 없는 서비스는 이전처럼 계속 자동 LoadBalancer 풀에서 VIP를 받아요. 더 많은 옵션은 LoadBalancer IP 주소 관리 설정을 보세요.

도달성 확인하기

노드에서 ARP 요청이 도착하고 노드의 MAC으로 응답이 나가는지 확인해요:

tcpdump -i <iface> arp

IPv6 NDP solicitation과 advertisement는 이렇게 봐요:

tcpdump -i <iface> 'icmp6 and (icmp6.type == 135 or icmp6.type == 136)'

같은 L2 세그먼트의 외부 호스트에서는 end-to-end 해석을 확인해요:

arping -I <client-iface> <pod-or-vip>

응답이 없다면, 그 IP가 호스트 인터페이스 서브넷과 캡슐화 없는 IP 풀 양쪽에 속하는지, 기능이 켜져 있는지, 그리고 네트워크가 ARP를 걸러 내지 않는지(제약 사항 참고) 확인해 보세요.

추가 리소스

더 알아보기 (Learn more)

이 기능은 "BGP 없이 L2 도달성"이라는 틈새를 정확히 채워 주지만, LoadBalancer VIP당 노드 하나라는 제약과 클라우드의 ARP 필터링이라는 환경 조건을 반드시 짚고 넘어가야 해요. 조건이 맞는지 먼저 확인하고 풀 설계(DHCP와 겹치지 않는 서브범위 예약)를 하는 게 순서예요.