ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • K8s 네트워크 탐색해보기(Calico 1/2) - 작성중
    K8s 2021. 12. 24. 03:25

    안녕하세요. 

    첫번째는 기본적인 CNI + Flannel에 대한 내용을 썻다면 두번째로는 Calico에 대한 내용을 써보려고 합니다.

     

    Calico

    https://projectcalico.docs.tigera.io/about/about-calico

    가장 대중적으로 사용하고 있는 CNI라고 생각하지만 제가 현업 종사자는 아니기 때문에 "아닌데요?" 라고 하면 또 할말은 없습니다ㅎㅎ...

    다만 K8s 생태계가 업데이트도 빠르고 변화와 다양한 시도가 항상 이루어지는 곳이기 때문에 언제 밀릴지 모른다고 생각합니다. (Cilium만 보더라도....)

    하지만 Calico 역시 그에 발맞춰서 변화에 대응하려고 하는 것 같습니다.

    https://projectcalico.docs.tigera.io/about/about-calico

    실제로 Calico에 대한 소개를 보더라도 K8s에서만 사용하는 것이 아닌 여러 플랫폼을 지원하며 데이터플레인에 대한 선택지에 eBPF를 포함하여 지원하는 부분을 보면 다양한 환경에서 여러 니즈에 모두 맞추려고 지속적으로 개발 및 변화를 하려고 하는 것 같습니다.

     

    Calico 구성요소

    https://projectcalico.docs.tigera.io/reference/architecture/overview

    대중적인 만큼 많은 오브젝트가 존재하지만 몇가지만 짚고 넘어가도록 하겠습니다. (더 궁금하신 사항부분은 출처에...)

     

    Felix - 주로 인터페이스 / 라우팅 정보 / Network Policy를 관리하며, Pod의 연결 정보 및 포워딩 정보는 모두 Felix에 의해 작성됩니다.

     

    Bird - Felix가 작성한 정보를 토대로 라우팅을 교환하며, 노드간 통신을 위해 BGP를 사용 합니다. 기본적으로는 노드간 Full Mesh를 사용하지만 노드의 숫자가 증가함에 따라 Full Mesh의 한계가 존재하기 때문에 이 경우는 Route-Reflector를 사용해야 합니다.

    (Route-reflector : https://www.juniper.net/documentation/us/en/software/junos/bgp/topics/topic-map/bgp-rr.html)

     

    IPAM - Pod에 할당하는 IP대역을 관리합니다. 

    - Flannel의 경우 IPAM을 사용하지 않지만, 사실 대부분의 CNI들은 IPAM을 사용하여 노드에서 Pod에 할당하는 IP대역들을 관리합니다.

     

    confd - BGP구성에 대한 변동사항(AS, IPAM등)이 생기면 confd가 Bird를 트리거하여 BGP 정보를 업데이트합니다.

     

     

    Calico Pod Network(In Node)

    Flannel의 경우 Pod Network 구조가 일반적인 네트워크의 개념을 그대로 가져왔기 때문에 네트워크 인프라에 종사하시는 분들도 쉽게 이해를 할 수 있는 구조이지만, Calico의 경우 효율성을 중시하는건지는 모르겠으나 일반적인 네트워크에서도 잘 사용하지 않는 부분을 사용하고 있습니다.

    우선 가장 특이한 부분중 하나는 /32 서브넷을 가진 호스트 루트로 구성 되어 있습니다.

    "/32서브넷이면 브로드캐스트도 없고 ARP도 없는데 라우팅은 어떻게?" 라는 의문을 가지실 수 있지만, 호스트 루트에서는 다른 대역의 ARP를 가지고 올 수 있으며 라우터에서 unnumbered interface를 사용하는것과 동일한 매커니즘입니다.

    (참조자료 : https://blog.ipspace.net/2021/06/unnumbered-ethernet-interfaces.html)

     

    ARP / Route에 대한 정보를 확인해보면 GW IP / MAC은 169.254.1.1 ee:ee:ee:ee:ee:ee로 IP/MAC을 응답을 했습니다.

    여기서 동일 노드에 있는 다른 Pod와 통신을 해서 추가되는지 확인해보겠습니다.

    ICMP 통신에는 아무런 문제가 없으나, ARP는 여전히 변함이 없습니다.

    또한 같은 Pod CIDR으로 구성되어 있으나 Pod는 다른 Pod에 대하여 ARP를 포함한 어떠한 정보도 받지 않고 오로지 GW에 대한 정보만을 받습니다.

     

    ICMP를 받았던 다른 Pod에서도 정보를 확인 해보면 인터페이스에 할당된 IP만 다를뿐 그 외 다른 네트워크 정보는 모두 동일합니다.

    Pod CIDR이 동일한 상태에서 이러한 구성이 가능한 이유는 Calico에서 Pod 네트워크는 L3 라우팅이라는 부분, 그리고 Proxy-ARP를 이용하여 구현했기 때문입니다.

     

    일반적으로 Proxy-ARP를 사용할 경우 동일한 브로드캐스트에 있는 호스트의 정보를 알 수 없지만 브로드캐스트 패킷(dst mac ff:ff:ff:ff:ff:ff)은 모든 호스트가 수신하게끔 되어 있습니다.

    하지만 Calico에서는 기본적으로 Pod는 호스트 루트를 구성 및 모든 구간을 L3 라우팅 구간으로 설정하였으며 GW에 대한 ARP 응답에서만 Proxy-ARP를 사용하면서 Pod1은 Pod2와 동일한 브로드캐스트로 구성되지 않게 되었습니다.

     

    Process를 보면 아래와 같습니다.

    1. Pod가 생성되며, Calico로 부터 IP를 할당 받는다. (Ex. 172.1.1.1/32)

     

    2. 해당 Pod가 생성된 노드에서는 할당한 IP를 라우팅 테이블에 인스톨(172.1.1.1/32 dev caliXXXX)

     

    3. 생성된 Pod에서는 GW에 대한 IP를 169.254.1.1로 세팅되며, GW에 대한 ARP요청 시 ee:ee:ee:ee:ee:ee로 응답(모든 Pod 동일)

     

    4. Pod에서 패킷을 전송하기 위해 L2헤더의 목적지를 ee:ee:ee:ee:ee:ee로 적어서 보내면 Calico vRouter에서 목적지를 보고 포워딩

     

    사실상 Pod CIDR 범위 내에서 생성되는 모든 Pod는 L2 통신이 없는 L3 라우팅 통신이며, 나름대로의 일장일단이 있다고 생각합니다.

    - 장점 : 모든 구간이 라우팅이기 때문에 브로드캐스트로 인한 장애는 없을 것이며, Pod의 네트워크 구성이 단순해져서 편함

    - 단점 : Pod 1개가 추가될때마다 늘어나는 라우팅 테이블과 iptables는 어떻게.....

     

    ## 개인적으로는 정말 충격을 많이 받았던 부분이 Pod 네트워크 구성에 대한 부분이였습니다.

    Proxy ARP도 잘 쓰이지 않는 기술이긴 하지만, 호스트루트를 같이 사용하면서 브로드캐스트로 발생할 수 있는 많은 문제들을 원천 차단해 버린 구조이기 때문에 설계한 사람은 정말 대단한 사람이라고 생각합니다

     

     

    Calico Pod Network(Node to Node)

    Flannel이 다른 노드에 존재하는 Pod와 통신하기 위해 VXLAN 터널을 이용하여 데이터를 전달하는 한가지의 구조만 가지고 있었다면 Calico는 더 복잡하면서 다양한 모드를 지원하고 있습니다.

    (Flannel도 여러가지는 있었으나 사실상 사용할 수 있는 환경이 아니기에...)

     

    1. BGP + IP in IP를 사용하여 노드간 Overlay 통신

    2. BGP만을 사용하여 노드간 Routing 통신(BGP Direct Routing)

    3. VXLAN을 통한 노드간 Overlay 통신(Flannel과 매우 비슷)

     

    가장 대표적으로 3가지가 있지만 실상 사용되는건 BGP Direct Routing / VXLAN을 상황에 맞게 사용하는 것 같습니다.

     

     

    1. BGP + IP in IP Overlay

    IP in IP의 경우 네트워크 현업에서도 사실 많이 사용하지는 않고 있습니다. 쓰는곳이 있을수는 있겠으나...일반적으로 Overlay가 필요한 경우 GRE 혹은 MPLS, 최근에는 VXLAN을 위주로 사용하기 때문에 IP in IP를 사용할만한 메리트가 전혀 없었습니다.

    오버헤드가 적다는 장점은 있지만... 하드웨어의 스펙이 비약적으로 발달된 현대 네트워크에서는 이는 장점으로 내세우지 못했고, 또한 가장 크리티컬한 부분은 IP만을 사용하는 Overlay라는 점에서 한계치가 분명했기 때문에 구 시대의 기술이 되어버렸습니다.

     

    히스토리 및 IP in IP에 대한 설명은 이쯤하고, 본격적으로 동작에 대해 확인 해보겠습니다.

    기본적인 구조는 위 그림과 같습니다.

    노드간 BGP 네이버를 맺게 되며, BGP를 통해 서로간의 Pod Network를 공유하게 되며, 이는 Bird와 Felix에 의해 수행 됩니다.

    Flannel의 VXLAN과 방법론의 차이는 있을지언정 "내부 네트워크를 별도로 구성 -> 터널링을 이용하여 Overlay 통신"의 대전제는 비슷한 부분이 많습니다.

    (물론 대전제만 비슷할 뿐, BGP를 쓴다는것 자체가 이미............)

    IP in IP 모드를 사용하게 되면 위 그림과 같이 IPIPMODE에서 Always로 표시가 됩니다. 

    Calico에서 제공하는 Onpremise Menifest 파일로 설치를 하게되면 이는 디폴트 설정이며, 변경이 가능합니다.

    - Calico Manifest : https://docs.projectcalico.org/manifests/calico.yaml (CALICO_IPV4POOL_IPIP 검색)

     

    그리고 위와 같이 IPIP Tunnel 인터페이스가 생성되며, IPAM에 기반하여 다른 노드의 Pod대역을 Routing table에 Install, Pod로부터 발생된 패킷의 목적지를 확인하고 Routing table을 Lookup하여 전달하는 동작을 수행하게 됩니다.

     

    IP in IP 동작에 대해 조금 더 상세하게 보겠습니다.

    1. Pod로 부터 생성된 패킷의 목적지를 확인, Routing table을 참조

    2. Routing table을 참조하여 tunl0(IPIP Tunnel) 인터페이스로 패킷을 전달

    3. 패킷을 전달받은 tunl0 인터페이스는 목적지를 확인 후 Capsulation 동작 수행 및 Forwarding

    4. 리모트 노드에서는 IP in IP 헤더 정보를 확인 후 Decapsulation 동작 수행 및 Pod에게 전달

     

    위와 같은 프로세스로 동작을 하게 되며 오로지 다른 노드에 있는 Pod와 통신을 하는 과정에서만 IP in IP 터널을 사용합니다.

    (외부 통신 및 SVC 호출시는 IPIP모드로 되어 있더라도 IP in IP 터널을 사용하진 않습니다.)

     

    Wireshark

    1. tunl0 인터페이스에서 다른 노드 Pod의 ICMP 패킷 캡쳐

    다른 노드 Pod로 전달되는 ICMP Packet을 캡쳐하였으며, 특이한 것은 L2헤더를 제대로 파싱하지 못했다는 부분입니다.

     

    이는 Tunl0 인터페이스가 IP만을 가지고 있을뿐, MAC에 대한 정보는 가지고 있지 않기 때문에 위와 같이 L2헤더에 대한 부분은 제대로 파싱을 하지 못하게 되며, Pod가 가지고 있는 veth 인터페이스와 노드가 가지고 있는 Tunl0 인터페이스는 분명한 라우팅 구간이지만 L2 헤더의 Hop-by-Hop 스위칭은 발생하지 않습니다.

     

    이는 로컬 노드에서 Tunl0 인터페이스는 라우팅 처럼 보이지만, 실제로는 라우팅보다는 "어떠한 동작을 위한 인터페이스" 라고 보는게 더 가깝기 때문입니다.

    즉 "Tunl0 인터페이스로 라우팅을 하였으며, Tunl0 인터페이스는 추가 헤더를 붙여서 ens192로 라우팅을 한다" 처럼 보이지만

    실제로는 "Tunl0 인터페이스로 전달받은 패킷은 ens192의 L2헤더, ens192의 src IP를 붙여서 패킷을 완성해서 보낸다" 의 동작을 하는 것 뿐입니다.

     

    결론적으로 Pod가 L2/L3/Data의 포맷을 가지고 데이터를 생성해서 노드의 veth로 전달했다면, 노드에서는 해당 패킷을 Tunl0 인터페이스로 전달하게 되며, 그 과정에서 L2헤더는 Tunl0에게 전달될때는 미리 삭제되서 전달하게 됩니다.

    반대로 ens192(physical)로 부터 패킷을 전달 받을때도 동일하게 ens192와 Tunl0은 분명한 라우팅 구간이지만, Tunl0은 MAC정보를 가지고 있지 않기 때문에 L2헤더를 제외하고 전달받게 됩니다.

    (물론 다음 동작에서는 당연히 L2헤더가 붙게 됩니다.)

     

     

    2. ens192(Physical) 인터페이스에서 다른 노드 Pod의 ICMP 패킷 캡쳐

    Tunl0 인터페이스에서 캡쳐를 했을 경우 L2헤더가 보이지 않았지만, Physical NIC에서는 당연하게도 확인할 수 있습니다.

    ens192의 MAC / IP를 가지고 Outer L2헤더, Outer L3헤더를 만들었으며, L3헤더에서는 Protocol 타입에 IPIP라는 마킹이 되어 있습니다.

    또한 위 Tunl0에서 캡쳐했던 raw데이터는 모두 Inner헤더로 구분되어 외부에서는 보이지 않게끔 되어있는 것을 확인할 수 있습니다.

     

     

    BGP + IP in IP Overlay 끝


    개인적인 의견 - IP in IP를 사용하지 않는 이유

     

    네트워크 디자인은 서버의 구성을 따라가는 케이스가 정말 많은 것 같습니다. 가장 중요한 서비스의 효율적인 운영을 위해 네트워크 인프라는 어떻게 구성되어야 하는가는 어찌보면 당연한 결론으로 귀결되는게 맞다는 생각도 합니다.

    기존 온프렘 환경에서는 서버 구성에 따른 L4/L7을 포함하여 L2/L3 구성도 그러한 과정 중에 하나였고, 그 이후 ECMP, 안정적인 이중화, 장애발생 시 리스크의 최소화, Scale Out등등 각종 니즈가 고려된 횡 기반 네트워크 인프라의 필요성도 대두가 되기 시작하면서 현재의 가장 대중적인 Leaf - Spine의 횡 기반 2 Tier 인프라 구조가 등장했다고 생각합니다. 

     

    현재 많은 데이터센터에서 서버팜의 네트워크는 Leaf - Spine의 횡 기반 2Tier로 구성되어 있는데 이러한 구조에서 얻을 수 있는 장점 중 하나인 ECMP(Equal Cost Multi Path)를 사용하게 되면서 높은 고가용성과 퍼포먼스를 기대할 수 있게 됩니다.

    본론으로 돌아와서 IP in IP를 사용하지 않는 이유중 가장 큰 이유는 ECMP에 적합한 터널링 프로토콜이 아니기 때문입니다.

    퍼포먼스 및 고가용성의 측면을 고려하는 Leaf - Spine의 디자인과 방향성에 ECMP의 제약이 있는 터널링 프로토콜을 사용한다? 특정한 서비스에 필요할 수도 있겠지만 일반적인 환경에서는 전혀 권고되지 않는 프로토콜입니다.

     

    IP in IP를 사용하는데 왜 ECMP의 이슈가 생길까?

    일반적인 스위치 혹은 라우터에서 ECMP 설정을 보신분들이라면 바로 답을 아실거라고 생각합니다. 이 ECMP의 설정값을 잘 건드리지는 않지만 대부분의 제품군은 디폴트로 설정된 값중 하나가 Src Port & Dst Port입니다. 즉 L4헤더의 포트를 참조하여 정해진 해쉬값에 따라 ECMP동작을 하게 되는데 L4헤더가 없는 경우는 ECMP동작에 제약이 생기게 됩니다. 

    IP헤더만을 참조할 경우 IP라는 프로토콜의 한계가 나타나게 되는데 Src IP와 Dst IP가 고정 되는 경우 이 패킷은 고정된 링크만을 사용하여 포워딩 하기 때문에 고가용성, 퍼포먼스 모두 이슈가 될 수 있습니다. 물론 인입되는 트래픽의 규모가 적다면 큰 문제는 생기지 않겠지만, 근본적으로 추구하는 방향성과 맞지 않기에 VXLAN 및 기타 Overlay를 지원하는 프로토콜들이 UDP를 사용하는 것도 이러한 이유가 포함되어 있습니다.

     

    IP in IP 헤더 구조

    다시 IP in IP로 돌아와서 헤더 정보를 보겠습니다.

    https://tanzu.vmware.com/developer/guides/container-networking-calico-refarch/

    이더넷 - IP(Outer) - IP(Inner) - Payload(Data)의 구조를 가지고 있으며 Outer IP 뒤에 바로 Inner IP헤더가 연결되는 구조기 때문에 오버헤드는 적지만 대부분의 터널링 프로토콜이 사용하는 UDP도 제외됬습니다(-_-...)

     

    위의 내용을 종합하여 IP in IP를 사용할 경우 K8s에서는 아래 그림과 같은 상황이 발생하게 됩니다.

    K8s에서는 East - West(Pod to Pod) 트래픽이 발생하지 않을 수 없습니다. MSA구조에서는 당연하다면 당연한 현상인데 IP in IP 터널을 사용할 경우 다른 노드에 존재하는 Pod를 호출 하는 과정에서 Tor 역할을 하는 Leaf에서는 ECMP가 되지 않습니다.(알고리즘을 변경하면 가능하지만 절대로 권고하지는 않습니다.)

    즉 node1과 node2의 Pod가 통신할때는 한쪽 링크만 사용할뿐 다른 링크는 전혀 사용하지 않기 때문에 현대 네트워크의 디자인과 방향성에 맞지 않을 뿐더러 고가용성 및 퍼포먼스에서 이슈가 발생할 여지는 분명히 존재합니다.

     

    결론을 요약하면 다음과 같습니다.

    1. 일반적인 터널링 프로토콜에서 사용하는 UDP헤더가 없기 때문에 ECMP는 IP만을 참조하게 된다.

    2. IP만을 참조하는 ECMP는 K8s 네트워크 인프라에서 여러가지 측면에서 적합하지 않다.

    3. 그러므로 실질적으로는 VXLAN 혹은 Direct Routing을 사용하여 구성하는것이 더 바람직하다.

Designed by Tistory.