home..

Calico에 대하여

Docker Container Pod Container Kubernetes Networking

Calico

Prerequsite

Calico 기반의 CNI에서의 Kubernetes Networking 실습을 진행해봅니다.

image.png

image.png

두개 요구조건에 맞는 클러스터로

를 선택했습니다.

AWS 환경에서 k8s-rtr 은 없고, AWS 내부 라우터가 대신 라우팅 처리

AWS 환경에서 k8s-rtr 은 없고, AWS 내부 라우터가 대신 라우팅 처리

해당 구조와 동일하게 vagrant로 생성하여 테스트를 진행하기로 했습니다

홈서버의 virtualbox - vagrant를 이용하여 프로비저닝하였고

vbox의 defautl 대역이 192.168.56.X 와 57.X 대역으로 수정하여 진행하였습니다.

외에도 일부 수정이 필요한 코드들이 있어, 가시다님 github 코드를 일부 수정하여 업로드해놨습니다.

link

추가적으로 해당 조합으로 프로비저닝을 진행할때, 일부 ssh config의 문제가 있어 중간중간에 저런 설정들을 직접 주입하여 프로비저닝을 완료했습니다.

vagrant ssh k8s-m
sudo su
echo 'PubkeyAcceptedKeyTypes=+ssh-rsa' >> /etc/ssh/sshd_config
systemctl restart sshd

사실 프로비저닝을 kubespray를 이용해서만 진행했었는데,

https://www.youtube.com/watch?v=zWNHG1NSagg

https://speakerdeck.com/kakao/kubernetes-provisioning

이 영상을 보기도했었고, 직접 배포를 해보고싶어졌는데 직접 join을 해서 클러스터링 할 기회가 생겼네요.

프로비저닝 완료후 Calico CNI를 설치합니다.

위에 나온 버전중 1.30을 위한 v3.28을 설치했습니다.

CALICO_IPV4POOL_BLOCK_SIZE 라는 변수가 26으로 되어있어 24로 수정한 파일을 apply 했습니다.

kubectl apply -f https://raw.githubusercontent.com/idoyo7/KANS/main/kans3/calico-kans.yaml

# calicoctl install
curl -L https://github.com/projectcalico/calico/releases/download/v3.28.1/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && mv calicoctl /usr/bin
calicoctl version

k8s 클러스터링과 cni 설치가 완료되었으며, 필수 애드온인 metrics-server 설치를 진행합니다.

helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system

[https://docs.tigera.io/calico/latest/reference/architecture/overview](https://docs.tigera.io/calico/latest/reference/architecture/overview)

https://docs.tigera.io/calico/latest/reference/architecture/overview

image.png

CNI를 구성하는 대표적인 네트워크 인터페이스인 Calico의 구성요소들은 다음과 같습니다.

Felix : 인터페이스 관리, 라우팅 정보 관리, ACL 관리, 상태 체크

BIRD : BGP Peer 에 라우팅 정보 전파 및 수신, BGP RR(Route Reflector)

Confd: calico global 설정과 BGP 설정 변경 시(트리거) BIRD 에 적용해줌

Calico 에서 사용하는 데이터들은 Datastore plugin 으로 관리됩니다.

k8s API datastore(kdd) 혹은 etcd 중 선택하여 저장이 가능하며, 기본적으로는 k8s api datastore를 사용합니다.

IPAM : 클러스터 내에서 파드에 할당할 IP대역을 설정하는 기능입니다. 전 게시글에서 다뤘던 Flannel은 자체적으로 IPAM을 다루지않지만, Calico 에서는 별도로 관리가 가능합니다.

Calico 구성요소 확인하기

Untitled1.png

  • 데몬셋으로 각 노드에 calico-node 파드가 동작하여, 해당 파드에 bird, felix, confd 등이 동작 + Calico 컨트롤러 파드디플로이먼트로 생성

버전 확인 - 링크

대역 확인 - 링크

## kdd 의미는 쿠버네티스 API 를 데이터저장소로 사용 : k8s API datastore(kdd)
calicoctl version
# 칼리코 IPAM 정보 확인 : 칼리코 CNI 를 사용한 파드가 생성된 노드에 podCIDR 네트워크 대역 확인
calicoctl ipam show

calicoctl ipam show --show-blocks
calicoctl ipam show --show-borrowed
calicoctl ipam show --show-configuration

image.png

위에 잠깐 설명했던 Strict Affinity 또한 비활성화 되어있는것이 보입니다

Quiz. 바로 위 Calico IPAM 과 아래 출력되는 IPAM 의 차이는 무엇일까요? 우선순위 확인 - 링크 , Flannel 과 차이

  • 별도의 IPAM 이 있다면 무엇을 할 수 있을까요? (예. 특정 파드/네임스페이스에 파드의 네트워크 대역을 추가 및 삭제)
    1. Kubernetes annotations
    2. CNI configuration (Calico IPAM)
    3. IP pool node selectors
  • Kubernetes annotations: 파드에 어노테이션을 사용하여 네트워크 정책을 적용할 수 있습니다. 예를 들어 특정 파드에 특정 IP 풀에서만 IP가 할당되도록 어노테이션을 설정할 수 있습니다.
  • CNI configuration (Calico IPAM): Calico의 CNI 구성에서 IPAM 옵션을 설정할 수 있으며, 이를 통해 다양한 IP 풀을 구성하거나 노드 선택기를 사용할 수 있습니다. Calico CNI 구성 파일을 통해 각 노드나 네임스페이스 별로 서로 다른 IP 풀을 설정할 수 있습니다.
  • IP pool node selectors: 노드 선택기를 사용하여 특정 노드에 특정 IP 풀을 할당할 수 있습니다. 예를 들어, 레이블을 기반으로 노드를 구분하여 특정 노드 그룹에만 특정 IP 대역을 적용할 수 있습니다.

Calico IPAM은 이미 실행 중인 워크로드에 IP 주소를 재할당하지 않습니다. 새로 구성된 IP 풀의 IP 주소로 실행 중인 워크로드를 업데이트하려면 다시 만들어야 합니다. 프로덕션에 들어가기 전이나 유지 관리 기간 동안 이 작업을 수행하는 것이 좋습니다.

kubectl get node k8s-m -o json | jq '.spec.podCIDR'

kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo

image.png

특정 노드 혹은 클러스터에 있는 노드들의 pod에 할당될 cidr 대역들을 확인합니다.

host-local IPAM 정보 확인 : k8s-m 노드의 podCIDR 은 host-local 대신 칼리코 IPAM 를 사용하기때문에 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인이 가능하죠.


# 파드와 서비스 사용 네트워크 대역 정보 확인 
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"

kubectl get cm -n kube-system kubeadm-config -oyaml | grep -i subnet

image.png

service 와 pod 의 cidr 대역을 분리했습니다.

kubeadm config와 cluster-info에서 모두 확인이 가능합니다

Felix

  • felix (필릭스) : Host의 Network Inteface, Route Table, iptables을 관리를 합니다
# 칼리코 필릭스를 통한 IPTables 규칙 설정 확인
iptables -t filter -S | grep cali
...

iptables -t nat -S | grep cali
...
  • bird (버드) : bird는 라우팅 프로토콜 동작을 하는 데몬이며, 각 노드들의 파드 네트워크 대역 정보를 전파 및 전달 받습니다 - 링크
  • 아래 BGP 패킷은 실제 노드에서 자신의 파드 네트워크 대역 정보를 BGP Update 메시지로 전파하는 정보

    calico-bgp-packet.png

노드에 파드를 생성할경우, iptables filter에 추가되는 정책을 확인합니다

iptables -L -n -v
iptables -v --numeric --table filter --list cali-tw-<자신의 파드에 매핑되는 calice# 인터페이스 이름>

root@k8s-w0:~# iptables -v --numeric --table filter --list cali-tw-cali152481d57a3

image.png

Calico Node 정보 상세확인

find / -name bird.cfg | grep snapshotter | sudo xargs cat

해당 명령어로 bird.cfg 파일을 확인합니다.


function apply_communities ()
{
}

# Generated by confd
include "bird_aggr.cfg";
include "bird_ipam.cfg";

router id 192.168.57.100;

# Configure synchronization between routing tables and kernel.
protocol kernel {
  learn;             # Learn all alien routes from the kernel
  persist;           # Don't remove routes on bird shutdown
  scan time 2;       # Scan kernel routing table every 2 seconds
  import all;
  export filter calico_kernel_programming; # Default is export none
  graceful restart;  # Turn on graceful restart to reduce potential flaps in
                     # routes when reloading BIRD configuration.  With a full
                     # automatic mesh, there is no way to prevent BGP from
                     # flapping since multiple nodes update their BGP
                     # configuration at the same time, GR is not guaranteed to
                     # work correctly in this scenario.
  merge paths on;    # Allow export multipath routes (ECMP)
}

# Watch interface up/down events.
protocol device {
  debug { states };
  scan time 2;    # Scan interfaces every 2 seconds
}

protocol direct {
  debug { states };
  interface -"cali*", -"kube-ipvs*", "*"; # Exclude cali* and kube-ipvs* but
                                          # include everything else.  In
                                          # IPVS-mode, kube-proxy creates a
                                          # kube-ipvs0 interface. We exclude
                                          # kube-ipvs0 because this interface
                                          # gets an address for every in use
                                          # cluster IP. We use static routes
                                          # for when we legitimately want to
                                          # export cluster IPs.
}

# Template for all BGP clients
template bgp bgp_template {
  debug { states };
  description "Connection to BGP peer";
  local as 64512;
  gateway recursive; # This should be the default, but just in case.
  add paths on;
  graceful restart;  # See comment in kernel section about graceful restart.
  connect delay time 2;
  connect retry time 5;
  error wait time 5,30;
}

# -------------- BGP Filters ------------------
# No v4 BGPFilters configured

# ------------- Node-to-node mesh -------------

# For peer /bgp/v1/host/k8s-m/ip_addr_v4
protocol bgp Mesh_192_168_56_10 from bgp_template {
  neighbor 192.168.56.10 as 64512;
  source address 192.168.57.100;  # The local address we use for the TCP connection
  import all;        # Import all routes, since we don't know what the upstream
                     # topology is and therefore have to trust the ToR/RR.
  export filter {
    calico_export_to_bgp_peers(true);
    reject;
  };  # Only want to export routes for workloads.
}

# For peer /bgp/v1/host/k8s-w0/ip_addr_v4
# Skipping ourselves (192.168.57.100)

# For peer /bgp/v1/host/k8s-w1/ip_addr_v4
protocol bgp Mesh_192_168_56_101 from bgp_template {
  neighbor 192.168.56.101 as 64512;
  source address 192.168.57.100;  # The local address we use for the TCP connection
  import all;        # Import all routes, since we don't know what the upstream
                     # topology is and therefore have to trust the ToR/RR.
  export filter {
    calico_export_to_bgp_peers(true);
    reject;
  };  # Only want to export routes for workloads.
}

# For peer /bgp/v1/host/k8s-w2/ip_addr_v4
protocol bgp Mesh_192_168_56_102 from bgp_template {
  neighbor 192.168.56.102 as 64512;
  source address 192.168.57.100;  # The local address we use for the TCP connection
  import all;        # Import all routes, since we don't know what the upstream
                     # topology is and therefore have to trust the ToR/RR.
  export filter {
    calico_export_to_bgp_peers(true);
    reject;
  };  # Only want to export routes for workloads.
}

# ------------- Global peers -------------
# No global peers configured.

# ------------- Node-specific peers -------------

# No node-specific peers configured.

물론 ip 대역이 조금 달라 강의안 내용과는 조금 차이가 있습니다.

 for pod in $(kubectl get pod -n kube-system -l k8s-app=calico-node -oname); do echo $pod; kubectl exec $pod -n kube-system -- birdcl show route; echo; done

image.png

Pod 간 통신

CleanShot 2024-09-08 at 15.19.38.png

Untitled2.png

pod간 통신은 다음과 같은 과정으로 이루어지며, iptables FORWARD Rule에서 allow를 사용하여 라우팅이 이루어집니다.

calice# 인터페이스의 proxy arp 설정으로 파드에서 바라보는 Gateway의 MAC 정보를 파드가 전달받습니다.

ProxyARP와_삼테

오리뎅이님의 ProxyARP에 관련된 이야기를 읽고오시면 보다 더 쉽게 이해 할 수 있습니다.

파드의 배포 전 기본 상태 확인

Untitled3.png

네트워크 인터페이스 정보확인

# 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
ip -c -d addr show tunl0
# 네트워크 네임스페이스 확인
lsns -t net
# 아래 tunl0 Iface  목적지 네트워크 대역은 ipip 인캡슐레이션에 의해서  노드에 전달됩니다  각각 노드의 파드 대역입니다
route -n
root@k8s-w1:~# route -n
Kernel IP routing table

image.png

파드 생성

노드1, 노드2에 각각 파드를 한개씩 생성합니다.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  nodeName: k8s-w1  # 노드의 호스트이름을 직접 지정했습니다
  containers:
  - name: pod1
    image: nicolaka/netshoot  # 이미지는 네트워크 장애 처리에 유용한 이미지를 사용합니다
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  nodeName: k8s-w1
  containers:
  - name: pod2
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0

파드 생성 후 확인

Untitled4.png

실시간으로 변경되는 엔드포인트 / 정보 확인


watch -d calicoctl get workloadEndpoint

kubectl get pod -o wide

calicoctl get workloadendpoints

image.png

# 네트워크 인터페이스 정보 확인 : calice#~ 2 추가됨!, 각각 net ns(네임스페이스) 0, 1 호스트와 구별됨
ip -c link

# 네트워크 네임스페이스 확인 : 아래 2 pause(infra 컨테이너) 각각 파드별로 생성됨 -  **link-netnsid 0, link-netnsid 1** 매칭됨
lsns -t net

****# 파드의 IP/32bit 호스트 라우팅 대역이 라우팅 테이블에 추가됨
ip -c route

****# iptables rule 갯수 확인 : 필터(filter)    변경 확인
iptables -t filter -S | grep cali | wc -l
iptables -t nat -S | grep cali | wc -l

image.png

파드 생성후 iptables rule 갯수의 변화

image.png

파드에서 확인

# 마스터 노드에서 아래 실행
kubectl exec pod1 -it -- zsh

# 아래 처럼 호스트 네트워크 인터페이스 9번인 caliceY와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
ip -c addr

# 라우팅 정보에 169.254.1.1  디폴트 게이트웨이 주소로 사용
route -n

# ARP 정보는 현재 아무것도 없다
ip -c neigh

image.png

파드의 외부통신

파드에서 외부(인터넷) 통신 시에는 해당 노드의 네트워크 인터페이스 IP 주소로 MASQUERADE(출발지 IP가 변경) 되어서 외부에 연결됨

Untitled5.png

  • calico 기본 설정은 natOutgoing: true 이다. 즉, iptables 에 MASQUERADE Rule(룰) 에 의해서 외부에 연결됨
  • calice# 인터페이스에 proxy arp 설정
  • 파드와 외부간 직접 통신에서는 tunnel 인터페이스는 미 관여

calico 설정 정보 확인

및 노드에 iptables 확인

Untitled6.png

# 마스터 노드에서 확인 : natOutgoing  기본값은 true 이다
calicoctl get ippool -o wide

image.png

ipset list cali40masq-ipam-pools

image.png

natOutgoing

calico 설정 중 natOutgoing : False 로 변경시

image.png

# 기존에 존재하던 MASQ Rule  삭제되었다!
iptables -n -v -t nat --list cali-nat-outgoing

image.png

MASQ Rule이 삭제되어 외부통신이 불가능해지는 상태로 변경됩니다.

© 2024 mont kim   •  Powered by Soopr   •  Theme  Moonwalk