[KANS] 3기 3주 – Calico CNI & Network Mode

CloudNet@팀에서 진행하는 쿠버네티스 네트워크 스터디 3기 참가글입니다.

‘쿠버네티스’ 네트워크에 대해서 직접 실습을 통해 장애 시 해결 능력을 갖추고, 학습 내용을 전파 및 공유합니다!


1. Calico 기본 통신 이해

Calico is a networking and security solution that enables Kubernetes workloads and non-Kubernetes/legacy workloads to communicate seamlessly and securely.

https://www.tigera.io/project-calico/

Calico는 쿠버네티스 CNI 를 준수하여, 파드를 위한 네트워크 통신 환경을 마련한다.
Calico CNI (Container Network Interface)는 Kubernetes와 같은 클러스터 환경에서 네트워크 통신을 관리하기 위한 솔루션입니다. Calico는 클러스터 상의 워크로드간의 네트워크 통신과, 보안 기능 등을 수행한다.

1.1. Calico CNI Overview

Calico Components

Calico components
스터디 자료


위의 다이어그램에서, Calico가 구성되고 작동되기 위한 여러 구성요소들과 그 관계를 파악할 수 있다.

주요 구성요소

A) Calico Datastore plugin

  • Calico 동작을 위한 데이터 저장소
  • Kubernetes API datastore (kdd) / etcd 중 택 1

B) Bird

  • Calico CNI에서 라우팅을 수행하는 오픈소스 인터넷 라우팅 데몬
  • BGP Peer를 통해 라우팅 정보 전파 및 수신

C) Felix

  • 경로 및 접근제어(ACL) 정책을 확인하고 각 엔드포인트로 라우팅 실행
  • 엔드포인트가 있는 각 노드에서 실행되는 에이전트 데몬

D) confd

  • A) Calico Datastore에 저장된 설정값 /BGP 구성의 변경사항등을 실제 각 구성요소(Bird)에 적용

E) IPAM plugin

  • Calico의 IP pool resource (대역대?)에 따라 실제 클러스터 내부의 pod에 ip할당
  • IPAM=IP Address Management

기타 구성요소

  • dikasted
    • istio 서비스메시 환경에서 network policy 수행
    • Enforces network policy for Istio service mesh. Runs on a cluster as a sidecar proxy to Istio Envoy.
  • CNI Plugin
    • (온프레미스가아닌?)쿠버네티스 환경에서 Calico 네트워크 환경 제공
    • Provides Calico networking for Kubernetes clusters.
  • kube-controllers
    • Kubernetes API datastore 를 모니터링하여 클러스터 상태에 따른 액션 수행
    • Monitors the Kubernetes API and performs actions based on cluster state.
  • Typha
    • 데이터스토어와 felix 간 데몬으로 실행되어, 각 노드가 데이터스토어에 대한 부하를 감소시켜줌(state caching/event 중복 제거 등) (리버스프록시?)
    • Increases scale by reducing each node’s impact on the datastore. Runs as a daemon between the datastore and instances of Felix. Installed by default, but not configured. Typha description, and Typha component.
  • calicoctl
    • Calico 오브젝트를 관리할 수 있는 cli 도구
    • Command line interface to create, read, update, and delete Calico objects. calicoctl command line is available on any host with network access to the Calico datastore as either a binary or a container. Requires separate installation.

실습환경 구성 (On AWS, using cloud formation)

K8S v1.30.X, 노드 OS(Ubuntu 22.04 LTS) , CNI(Calico v3.28.1, IPIP, NAT enable) , IPTABLES proxy mode

  • cloudformation 구성
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-3w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides KeyName=nasir-personal SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

## Tip. 인스턴스 타입 변경 : MyInstanceType=t2.micro
예시) aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t2.micro KeyName=nasir-personal SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 k8s-m EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# k8s-m EC2 SSH 접속
ssh -i ~/.ssh/nasir-personal.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
  • Calico CNI 설치
# 모니터링
watch -d 'kubectl get pod -A -owide'

# calico cni install
## kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml - 서브넷 24bit 추가
# 기본 yaml 에 4946줄 이동 후 아래 내용 추가 해둠
vi calico.yaml
...
            # Block size to use for the IPv4 POOL created at startup. Block size for IPv4 should be in the range 20-32. default 24
            - name: CALICO_IPV4POOL_BLOCK_SIZE
              value: "24"
kubectl apply -f https://raw.githubusercontent.com/gasida/KANS/main/kans3/calico-kans.yaml

#
tree /opt/cni/bin/
ls -l /opt/cni/bin/
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l

# 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

# CNI 설치 후 파드 상태 확인
kubectl get pod -A -o wide
  • Calico 구성요소의 확인
  • 위의 구성요소에서 살펴보았듯, BGP등의 라우팅 정보를 Bird가 관리
  • felix가 해당 규칙에 맞게 로컬 노드의 route table, iptables 업데이트


실습 스크립트

  • 기본 정보 확인
# 버전 확인 - 링크
## kdd 의미는 쿠버네티스 API 를 데이터저장소로 사용 : k8s API datastore(kdd)
calicoctl version

# calico 관련 정보 확인
kubectl get daemonset -n kube-system
kubectl get pod -n kube-system -l k8s-app=calico-node -owide

kubectl get deploy -n kube-system calico-kube-controllers
kubectl get pod -n kube-system -l k8s-app=calico-kube-controllers -owide

# 칼리코 IPAM 정보 확인 : 칼리코 CNI 를 사용한 파드가 생성된 노드에 podCIDR 네트워크 대역 확인 - 링크
calicoctl ipam show

# Block 는 각 노드에 할당된 podCIDR 정보
calicoctl ipam show --show-blocks
calicoctl ipam show --show-borrowed
calicoctl ipam show --show-configuration
## strictAffinity True 설정 시 : 각 노드는 할당된 IP 블록에서만 IP 주소를 사용, 특정 pod들에 해당해둔 pod ip/mac을 고정할경우, 해당 대역들을 무작위로 배치하는 IPAM 대역에서 제외 -> 이로써 중복으로 같은 IP 할당 방지
## strictAffinity False 설정 시 : 노드가 부족한 경우 다른 노드에서 IP를 공유 가능
## When StrictAffinity is true, borrowing IP addresses is not allowed - https://docs.tigera.io/calico/latest/reference/resources/ipamconfig
## 관련 코드
### https://github.com/projectcalico/libcalico-go/blob/v3.9.0-0.dev/lib/ipam/ipam.go
### https://github.com/projectcalico/libcalico-go/blob/v3.9.0-0.dev/lib/ipam/ipam_types.go#L78

# calico crd 정보로 확인
kubectl get crds
kubectl get ippools
kubectl get ippools default-ipv4-ippool -o jsonpath={.spec} | jq
kubectl get clusterinformations
kubectl get clusterinformations default -o jsonpath={.spec} | jq
kubectl get blockaffinities
kubectl get blockaffinities -o jsonpath="{.items[*].spec}" | jq

Quiz. 바로 위 Calico IPAM 과 아래 출력되는 IPAM 의 차이는 무엇일까요? 우선순위 확인 - 링크 , Flannel 과 차이
- 별도의 IPAM 이 있다면 무엇을 할 수 있을까요? (예. 특정 파드/네임스페이스에 파드의 네트워크 대역을 추가 및 삭제)
1. Kubernetes annotations
2. CNI configuration (Calico IPAM)
3. IP pool node selectors

# host-local IPAM 정보 확인 : k8s-m 노드의 podCIDR 은 host-local 대신 칼리코 IPAM 를 사용함
## 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
kubectl get node k8s-m -o json | jq '.spec.podCIDR'

# CNI Plugin 정보 확인 - 링크
tree /etc/cni/net.d/
cat /etc/cni/net.d/10-calico.conflist | jq
...
			"datastore_type": "kubernetes", # 칼리코 데이터저장소는 쿠버네티스 API 를 사용
      "ipam": { 
          "type": "calico-ipam" # IPAM 은 칼리코 자체 IPAM 을 사용
      },
...

# calicoctl node 정보 확인 : Bird 데몬(BGP)을 통한 BGP 네이버 연결 정보(bgp peer 는 노드의 IP로 연결) - 링크
calicoctl node status
calicoctl node checksystem

# ippool 정보 확인 : 클러스터가 사용하는 IP 대역 정보와 칼리코 모드 정보 확인
calicoctl get ippool -o wide

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

kubectl get cm -n kube-system kubeadm-config -oyaml | grep -i subnet
 podSubnet: 172.16.0.0/16
 serviceSubnet: 10.96.0.0/12

# calico endpoint (파드)의 정보 확인 : WORKLOAD 는 파드 이름이며, 어떤 노드에 배포되었고 IP 와 cali 인터페이스와 연결됨을 확인
calicoctl get workloadEndpoint
calicoctl get workloadEndpoint -A
calicoctl get workloadEndpoint -o wide -A

# 노드에서 컨테이너(프로세스) 확인 : pstree
ps axf
   4405 ?        Sl     0:09 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id dd532e5efaad436ebe7d10cdd3bb2ffe5a2873a0604ce3b
   4425 ?        Ss     0:00  \_ /pause
   4740 ?        Ss     0:00  \_ /usr/local/bin/runsvdir -P /etc/service/enabled
   4811 ?        Ss     0:00      \_ runsv allocate-tunnel-addrs
   4819 ?        Sl     0:00      |   \_ calico-node -allocate-tunnel-addrs
   4812 ?        Ss     0:00      \_ runsv bird
   4994 ?        S      0:00      |   \_ bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg
   4813 ?        Ss     0:00      \_ runsv cni
   4822 ?        Sl     0:00      |   \_ calico-node -monitor-token
   4814 ?        Ss     0:00      \_ runsv bird6
   4993 ?        S      0:00      |   \_ bird6 -R -s /var/run/calico/bird6.ctl -d -c /etc/calico/confd/config/bird6.cfg
   4815 ?        Ss     0:00      \_ runsv confd
   4820 ?        Sl     0:00      |   \_ calico-node -confd
   4816 ?        Ss     0:00      \_ runsv felix
   4824 ?        Sl     0:54      |   \_ calico-node -felix
   4817 ?        Ss     0:00      \_ runsv node-status-reporter
   4823 ?        Sl     0:00      |   \_ calico-node -status-reporter
   4818 ?        Ss     0:00      \_ runsv monitor-addresses
   4825 ?        Sl     0:00          \_ calico-node -monitor-addresses

결과

  • caclico 관련 데몬셋/디플로이먼트 확인
  • calico ipam 정보 확인
  • calico crd 정보 확인
  • CNI plugin 정보 확인

  • calico node 정보 확인
  • pod/service 사용 네트워크대역 정보 확인

  • felix (필릭스) : Host의 Network Inteface, Route Table, iptables을 관리

확인

cali- prefix로 시작하는 iptables의 filter, nat table 조회

iptables -t filter -S | grep cali
...

iptables -t nat -S | grep cali
...

  • 결과
  • bird (버드) : bird는 라우팅 프로토콜 동작을 하는 데몬이며, 각 노드들의 파드 네트워크 대역 정보를 전파 및 전달- 링크
    • bird는 birdcl 별도의 cli 툴 제공

확인

# calico-node 정보 확인 : 컨테이너 네트워크는 host 모드를 사용하여 해당 노드의 네트워크를 사용
kubectl get pod -n kube-system -l k8s-app=calico-node -o wide
kubectl get pod -n kube-system -l k8s-app=calico-node -oname
 
# 각 파드에 exec 로 birdcl 에 정보 확인
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
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 all; echo; done
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 status; echo; done

#------------------------------------------
# (옵션) 직접 파드 이름 지정 후 birdcl 로 접속 후 정보 확인
kubectl exec -it $CALICONODE1 -n kube-system -- birdcl
root@k8s-m:~# kubectl exec -it calico-node-95tlj -n kube-system -- birdcl
Defaulted container "calico-node" out of: calico-node, upgrade-ipam (init), install-cni (init), flexvol-driver (init)
BIRD v0.3.3+birdv1.6.8 ready.
bird>

# 라우팅 정보 확인 : 다른 노드로부터 BGP 로 전달받은 네트워크 경로 정보를 아래 빨간색 부분! (ip주소, ()숫자부분)
show route
bird> show route
0.0.0.0/0          via 10.0.2.2 on enp0s3 [kernel1 04:18:24] * (10)
10.0.2.2/32        dev enp0s3 [kernel1 04:18:24] * (10)
192.168.100.0/24   dev enp0s8 [direct1 04:18:23] * (240)
10.0.2.0/24        dev enp0s3 [direct1 04:18:23] * (240)
172.16.228.64/26   blackhole [static1 04:18:23] * (200)
172.16.228.64/32   dev tunl0 [direct1 04:18:23] * (240)
172.16.197.0/26    via 192.168.100.103 on enp0s8 [Mesh_192_168_100_103 04:20:00] * (100/0) [i]
172.16.46.0/26     via 192.168.100.102 on enp0s8 [Mesh_192_168_100_102 04:19:04] * (100/0) [i]
172.16.29.0/26     via 192.168.100.10 on enp0s8 [Mesh_192_168_100_10 04:18:25] * (100/0) [i]
172.17.0.0/16      dev docker0 [direct1 04:18:23] * (240)

# 기타 확인 명령어
bird> show protocol
bird> show route all
bird> show status

# 빠져나가기
bird> exit

결과

  • 각 calico node별 birdcl show route
    • 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

  • 각 calico node별 birdcl show route all (사진은 일부만)
    • 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 all ; echo; done
  • 각 calico node별 birdcl show status
  • 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 status; echo; done

1.2. Pod <> Pod 간 통신

동일 노드 내 파드 간 통신은 내부에서 직접 통신됨

  • iptables FORWARD Rule 에 정책(허용) 사용
  • calice# 인터페이스에 proxy arp 설정으로 파드에서 바라보는 게이트웨이의 MAC 정보를 파드가 전달 받음
  • 동일 노드 내에 파드 간 통신에서는 tunnel 인터페이스는 미 관여

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

pod 배포 이전, 워커노드의 네트워크 설정을 확인한다

  • bird 데몬의 BGP 라우팅 프로토콜을 통해 전달받은 파드 네트워크 대역
  • felix 데몬이 실제 route/iptables 에 설정한 값들 확인

파드 생성 전 노드(k8s-w1) Shell 에서 기본 정보 확인

# 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
ip -c -d addr show tunl0
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0 minmtu 0 maxmtu 0
    ipip any remote any local any ttl inherit nopmtudisc numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.16.158.0/32 scope global tunl0
       valid_lft forever preferred_lft forever

# 네트워크 네임스페이스 확인
lsns -t net

# 네트워크 라우팅 경로 정보 확인
# 이중 bird 는 bird 데몬이 BGP 라우팅 프로토콜에 의해 파드 네트워크 대역을 전달받거나 전달하는 경로 → 각각 노드의 파드 대역입니다
Quiz. blackhole 라우팅은 왜 있을까요? 
ip -c route | grep bird
blackhole 172.16.158.0/24 proto bird
172.16.34.0/24 via 192.168.20.100 dev tunl0 proto bird onlink
172.16.116.0/24 via 192.168.10.10 dev tunl0 proto bird onlink
172.16.184.0/24 via 192.168.10.102 dev tunl0 proto bird onlink

# 아래 tunl0 Iface 에 목적지 네트워크 대역은 ipip 인캡슐레이션에 의해서 각 노드에 전달됩니다 → 각각 노드의 파드 대역입니다
route -n
root@k8s-w1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.34.0     192.168.20.100  255.255.255.0   UG    0      0        0 tunl0
172.16.116.0    192.168.10.10   255.255.255.0   UG    0      0        0 tunl0
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 tunl0
172.16.158.0    0.0.0.0         255.255.255.0   U     0      0        0 *
...

# (옵션) iptables rule 갯수 확인 >> iptables rule 이 모든 노드에서 똑같나요? 다른가요?
iptables -t filter -S | grep cali | wc -l
iptables -t nat -S | grep cali | wc -l

결과

  • tunl0에 ipip 인터페이스 존재 확인
  • bird를 통해 다른 노드간 라우팅 정보 설정 확인
  • blackhole 라우팅(172.16.158.0/24)은 자기자신 대역을 지정 > 내부통신용?
    • ip -c route | grep bird
      • blackhole 172.16.158.0/24 proto bird
    • route -n
      • 172.16.158.0 0.0.0.0 255.255.255.0 U 0 0 0 *

1.2.2. 파드 배포 이후 상태 확인

  • 새로 생성된 pod에 대해 cali로 시작하는 네트워크 인터페이스가 생성 및 부착됨
  • 해당 인터페이스를 향한 calico endpoint 생성 및 연결됨

노드(k8s-w1)에 파드 2개 생성

  • pod manifest
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
  • 생성 및 확인
# [터미널1] k8s-m 모니터링
watch -d calicoctl get workloadEndpoint

# [터미널2] k8s-m 모니터링
# 파드 생성 : 노드의 이름이 다를 경우 아래 yaml 파일 다운로드 후 수정해서 사용하시면 됩니다
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml
kubectl apply -f node1-pod2.yaml

# 생성된 파드 정보 확인
kubectl get pod -o wide
root@k8s-m:~# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          34s   172.16.228.78   k8s-w1   <none>           <none>
pod2   1/1     Running   0          34s   172.16.228.79   k8s-w1   <none>           <none>

# calicoctl 이용한 endpoint 확인 : veth 정보도 출력!
calicoctl get workloadendpoints
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.2/32   calice0906292e2
pod2       k8s-w1   172.16.158.1/32   calibd2348b4f67

  • pod 생성 이후
# 네트워크 인터페이스 정보 확인 : calice#~ 2개 추가됨!, 각각 net ns(네임스페이스) 0, 1로 호스트와 구별됨
ip -c link
root@k8s-w1:~# ip -c link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:70:da:68:71:43 brd ff:ff:ff:ff:ff:ff
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:1b:e3:db brd ff:ff:ff:ff:ff:ff
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:34:f0:2c:88 brd ff:ff:ff:ff:ff:ff
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
8: calibd2348b4f67@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid cni-348afd47-fece-7c08-4488-9046da1e6139
9: calice0906292e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid cni-3342c988-81a5-ebfa-058d-193dbbcb1d81

# 네트워크 네임스페이스 확인 : 아래 2개 pause(infra 컨테이너)가 각각 파드별로 생성됨 - 위 link-netnsid 0, link-netnsid 1 매칭됨
lsns -t net
root@k8s-w1:~# lsns -t net
        NS TYPE NPROCS   PID USER     NETNSID NSFS                                                COMMAND
4026531840 net     131     1 root  unassigned                                                     /sbin/init
4026532216 net       2 31336 65535          0 /run/netns/cni-348afd47-fece-7c08-4488-9046da1e6139 /pause
4026532277 net       2 31372 65535          1 /run/netns/cni-3342c988-81a5-ebfa-058d-193dbbcb1d81 /pause

# 파드의 IP/32bit 호스트 라우팅 대역이 라우팅 테이블에 추가됨
ip -c route
root@k8s-w1:~# ip -c route
172.16.158.1 dev calibd2348b4f67 scope link
172.16.158.2 dev calice0906292e2 scope link
...

# (옵션) iptables rule 갯수 확인 : 필터(filter)에 기본 39 에서 91개로 룰(rule)이 많이 추가 되었다 >> 파드가 많아지면 rule 갯수는? >> 노드별로 똑같나요? 다른가요?
root@k8s-w1:~# iptables -t filter -S | grep cali | wc -l
91
root@k8s-w1:~# iptables -t nat -S | grep cali | wc -l
15
  • pod shell 접근 후 확인

pod1)

# 마스터 노드에서 아래 실행
kubectl exec pod1 -it -- zsh
...
# 아래 처럼 호스트 네트워크 인터페이스 9번인 caliceY와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod1> ip -c addr
...
4: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
    link/ether ee:2f:83:31:9b:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.158.2/32 brd 172.16.158.2 scope global eth0
       valid_lft forever preferred_lft forever

# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod1> route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

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


pod2)

# 마스터 노드에서 아래 실행
kubectl exec pod2 -it -- zsh
...
# 아래 처럼 if12 는 호스트 네트워크 인터페이스 12번인 calice0906292e2와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod2> ip -c addr
...
4: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
    link/ether 7e:61:c6:fe:4f:8f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.228.79/32 brd 172.16.228.79 scope global eth0
       valid_lft forever preferred_lft forever

# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod2> route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

# ARP 정보는 현재 아무것도 없다
ip neighbor show

결과

  • master node에서, 파드 및 caclico workloadendpoints 확인
  • (생성이후) worker node1에서, 네트워크 정보 확인
  • 생성된 pod에서의 정보 확인

pod1 > pod2 ping 통신 및 확인

노드에서 실행)
# iptables 필터 테이블에 FORWARD 리스트 중 cali-FORWARD 룰 정보를 필터링해서 watch 로 확인
watch -d -n 1 "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'" 

# (마스터노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1
VETH2=$(calicoctl get workloadEndpoint | grep pod2 | awk '{print $4}')
echo $VETH2

# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=calice0906292e2
VETH2=calibd2348b4f67

# 노드1 calice# 인터페이스의 proxy arp 설정 확인
# cat /proc/sys/net/ipv4/conf/<자신의 pod1에 연결된 calice# 이름>/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH1/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH2/proxy_arp

# 파드1 혹은 파드2에 veth 로 연결된 호스트 네트워크 인터페이스 calice# 중 1개 선택해서 tcpdump
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn
# 파드1 Shell 에서 실행 : 정상 통신!
kubectl exec pod1 -it -- zsh
--------------------
ping -c 10 <파드2 IP>

# 게이트웨이 169.254.1.1 의 MAC 주소를 ARP 에 의해서 학습되었다
ip -c -s neigh

# 노드에서 확인
# iptables 에 기본 FORWARD 는 DROP 이지만, 아래 cali-FORWARD Rule에 의해 허용되며, pkts 카운트가 증가한다
iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'
watch -d "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"

# 파드1에서 게이트웨이의 IP인 169.254.1.1 의 MAC 주소를 알기 위해서 ARP Request 를 보낸다
# 이때 veth 연결된 calice#~ 에 proxy arp 설정이 되어 있고, 자신의 mac 주소(ee:ee:ee:ee:ee:ee)를 알려주고, 이후 정상 통신됨
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn
root@k8s-w1:~# tcpdump -i calice0906292e2 -nn
08:25:44.216545 ARP, Request who-has 169.254.1.1 tell 172.16.228.78, length 28
08:25:44.216566 ARP, Reply 169.254.1.1 is-at ee:ee:ee:ee:ee:ee, length 28
08:25:45.144618 IP 172.16.228.78 > 172.16.228.79: ICMP echo request, id 45451, seq 7, length 64
08:25:45.144674 IP 172.16.228.79 > 172.16.228.78: ICMP echo reply, id 45451, seq 7, length 64

# 호스트에서 calice0906292e2 의 MAC 주소 다시 확인
root@k8s-w1:~# ip -c -d link
...
12: calice0906292e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0 minmtu 68 maxmtu 65535
    veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

결과

  • pod1(172.16.158.1)에서 pod2 (172.16.158.2)로 ping 3번 요청
  • cali#인터페이스에 proxy arp 설정이 되어있어, 게이트웨이 (169.254.1.1)의 MAC 주소(ee:ee:ee:ee:ee:ee)를 pod1이 전달받아 사용함 확인

1.3. Pod <> Public(인터넷)간 통신

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

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

1.3.1. pod 배포 전 기본 상태 확인

노드(192.168.~)와 파드(172.16~)의 네트워크 대역이 다르기 때문에, 파드가 노드를 지나 외부를 나가기 위해서는 iptables의 MASQUERADE 규칙을 타고 나가야한다.

이는 calico 설정의 natOutgoing, iptables 설정의 MASQUERADE 규칙을 통해 확인해 볼 수 있다.

calico 설정 정보 확인 & 노드에 iptables 확인

# 마스터 노드에서 확인 : natOutgoing 의 기본값은 true 이다
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Always     Never       false      all()

# 노드에서 확인 : 노드에서 외부로 통신 시 MASQUERADE 동작 Rule 확인
iptables -n -t nat --list cali-nat-outgoing
root@k8s-w1:~# iptables -n -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
target      prot opt source               destination
MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

ipset list
ipset list cali40masq-ipam-pools

결과

  • 마스터 노드의 natOutgoing 값 true
  • 워커노드 iptables의 nat table에서, MASQUERADE 설정이 있는 Chain 확인

1.3.2. 파드 배포 및 외부 통신 확인

테스트를 위해 신규 pod 1개를 배포하면, 생성된 파드(172.16~)에는 추가로 cali#인터페이스가 생성되고 연결된다.

이후 워커노드1의 any, cali#, tunl0, ens5 각 인터페이스 tcpdump로 패킷을 확인해보면, pod가 외부로 통신시 tunnel을 거치지 않음을 확인할 수 있다

노드(k8s-w1)에 파드 1개 생성

  • pod manifest
# node1-pod1.yaml
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
  • 생성 및 확인
# 파드 생성
curl -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod1.yaml
kubectl apply -f node1-pod1.yaml

# 생성된 파드 정보 확인
kubectl get pod -o wide

pod1 외부통신 확인

  • 실행 전 준비
# 노드에서 실행
# iptables NAT MASQUERADE 모니터링 : pkts 증가 확인
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'

# (컨트롤플레인 노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1

# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=<각자 실습 환경에 따라 다름>
VETH1=calice0906292e2

# 패킷 덤프 실행 >> 어떤 인터페이스에서 패킷 캡쳐 확인이 되나요?
tcpdump -i any -nn icmp
tcpdump -i $VETH1 -nn icmp
tcpdump -i tunl0 -nn icmp
tcpdump -i ens5 -nn icmp    # [실습환경 A Type]
tcpdump -i enp0s8 -nn icmp  # [실습환경 B Type]
tcpdump -i enp0s3 -nn icmp  # [실습환경 B Type]
  • 통신 실행 및 확인
# 파드에서 외부 정상 통신 확인
kubectl exec pod1 -it -- zsh
----------------------------

# 혹은 통신 확인 
pod1> ping -c 10 8.8.8.8

# The right way to check the weather - 링크
curl wttr.in/seoul
curl 'wttr.in/seoul?format=3'
curl 'wttr.in/busan?format=3'
curl -s 'wttr.in/{London,Busan}'
curl v3.wttr.in/Seoul.sxl
curl wttr.in/Moon
curl wttr.in/:help

# 패킷 덤프 내용 확인
tcpdump -i <각자 실습 환경에 따라 다름> -nn icmp
root@k8s-w1:~# tcpdump -i calice0906292e2 -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on calice0906292e2, link-type EN10MB (Ethernet), capture size 262144 bytes
09:27:29.236878 IP 172.16.228.82 > 8.8.8.8: ICMP echo request, id 56376, seq 1, length 64
09:27:29.311810 IP 8.8.8.8 > 172.16.228.82: ICMP echo reply, id 56376, seq 1, length 64


# [실습환경 A Type] 아래 192.168.10.101는 노드의 외부(인터넷) 연결된 네트워크 인터페이스의 IP이며, 출발지 IP가 변경되어서 외부로 나감
root@k8s-w1:~# tcpdump -i ens5 -nn icmp
15:37:56.579286 IP 192.168.10.101 > 8.8.8.8: ICMP echo request, id 57122, seq 1, length 64
15:37:56.610585 IP 8.8.8.8 > 192.168.10.101: ICMP echo reply, id 57122, seq 1, length 64

# [실습환경 B Type] 아래 10.0.2.15는 VM의 1번 네트워크 인터페이스의 IP이며, 출발지 IP가 변경되어서 외부로 나감
root@k8s-w1:~# tcpdump -i enp0s3 -nn icmp
06:05:12.356260 IP 10.0.2.15 > 8.8.8.8: ICMP echo request, id 15671, seq 5, length 64
06:05:12.402586 IP 8.8.8.8 > 10.0.2.15: ICMP echo reply, id 15671, seq 5, length 64


# nat MASQUERADE rule 카운트(pkts)가 증가!
## 출발지 매칭은 cali40masq-ipam-pools 을 사용
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
root@k8s-w1:~# iptables -n -v -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
 pkts bytes target     prot opt in     out     source               destination
    3   252 MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

# IPSET 으로 의 cali40masq-ipam-pools IP 대역 정보 확인 : 172.16.0.0/16 대역임을 확인
ipset list cali40masq-ipam-pools
Name: cali40masq-ipam-pools
Type: hash:net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0x97754149
Size in memory: 504
References: 1
Number of entries: 1
Members:
172.16.0.0/16

결과

  • ping 요청시 각 nic에 대한 tcpdump
    • kubectl exec pod1 -it — ping -c 2 8.8.8.8
  • cali-nat-outgoing chain에 대한 pkts 증가

1.4. 다른 노드에서 Pod <> Pod 간 통신

다른 노드 환경에서 파드 간 통신 시에는 IPIP 터널(기본값) 모드를 통해서 이루어짐

  • 각 노드에 파드 네트워크 대역은 Bird 에 의해서 BGP 로 광고 전파/전달 되며, Felix 에 의해서 호스트의 라우팅 테이블에 자동으로 추가 및 삭제
  • 다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신 – 링크
  • 아래 실제 패킷 확인 시 Outer IP 헤더와 Inner IP 헤더 확인 가능!

1.4.1. pod 배포 전 기본 상태 확인

노드 정보 확인

  • 노드에서 BGP 에 의해서 전달 받은 정보가 호스트 라우팅 테이블에 존재하는지 확인
# 아래 명령어로 확인 시 나머지 노드들의 파드 대역을 자신의 호스트 라우팅 테이블에 가지고 있고, 해당 경로는 tunl0 인터페이스로 보내게 된다
route | head -2 ; route -n | grep tunl0

# 마스터노드, 노드1~
root@k8s-m:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 tunl0
172.16.228.64   192.168.100.101 255.255.255.192 UG    0      0        0 tunl0

# 노드1
root@k8s-w1:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.29.0     192.168.100.10  255.255.255.192 UG    0      0        0 tunl0
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 tunl0
...
  • 노드의 tunl0 정보 확인
# 터널 인터페이스에 IP가 할당되어 있고, MTU는 1480(IP헤더 20바이트 추가를 고려)을 사용하며, 현재 TX/RX 카운트는 0 이다
# Calico 사용 시 파드의 인터페이스도 기본 MTU 1480 을 사용한다
ifconfig tunl0

# 노드1
root@k8s-w1:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

# 노드2
root@k8s-w2:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.46.10  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

결과

  • 마스터/노드1/노드2 각 노드의 라우팅 테이블에 타 노드의 파드 대역이 등록되어있음과 tunl0 인터페이스에 할당되어있음을 확인할 수 있다.
  • 마스터
  • 워커노드1
  • 워커노드2

1.4.2. 파드 배포 후, 통신 실행 및 확인

지금까지의 통신은 파드nic+게이트웨이를 통한 직접 통신(동일노드/파드간통신), 파드nic+노드nic을 통한 nat통신(외부통신)이였고 이때는 tunnel이 사용되지않았다.

다른 노드의 파드간 통신시에는 tunnel 인터페이스가 사용된다.

워커노드1,워커노드2 각 노드에 파드1개씩 생성

  • pod manifest
# node2-pod2.yaml
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-w2
  containers:
  - name: pod2
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
  • 생성 및 확인
# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node2-pod2.yaml
kubectl apply -f node2-pod2.yaml

# calicoctl 이용한 endpoint 확인
calicoctl get workloadendpoints
  • 파드간 통신처리를 위한 라우팅 정보 확인 > 각 상대방 노드의 IP를 게이트웨이로 전달
route -n | head -2 ; route -n | grep 172.16.

# 노드1
root@k8s-w1:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
...

# 노드2
root@k8s-w2:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.228.64   192.168.100.101 255.255.255.192 UG    0      0        0 tunl0
...

파드 생성 결과

  • 파드 IP, 인터페이스 확인
  • 라우팅 정보 확인

통신 사전 준비 및 실행 결과

  • 실행 전 준비 (워커노드1 기준)
# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 실행
watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
root@k8s-w1:~# watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        RX packets 0  bytes 0 (0.0 B)
        TX packets 0  bytes 0 (0.0 B)

# (옵션) 패킷 덤프 : calice#
tcpdump -i -nn <calice#>

# 패킷 덤프 : tunl0
tcpdump -i tunl0 -nn
혹은 패킷을 파일로 추출시
tcpdump -i tunl0 -nn -w /tmp/calico-tunl0.pcap

# 패킷 덤프 : IP 헤더에 상위 프로토콜을 IPIP(4)인 패킷만 필터
#tcpdump -i <각자 자신의 노드의 eth0> -nn proto 4
tcpdump -i ens5 -nn proto 4    # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4  # [실습환경 B Type]
혹은 패킷을 파일로 추출시
tcpdump -i ens5 -nn proto 4 -w /tmp/calico-ipip.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap # [실습환경 B Type]


# [실습환경 A Type] 자신의 PC로 패킷 복사
scp ubuntu@<node 유동공인 IP>:/tmp/calico-ipip.pcap .
scp ubuntu@3.35.229.92:/tmp/calico-ipip.pcap .


# [실습환경 B Type] 자신의 PC로 패킷 복사 : https://github.com/invernizzi/vagrant-scp
## vagrant scp 플러그인 설치
vagrant plugin install vagrant-scp
vagrant plugin list

## k8s-w1 VM 내부의 파일을 자신의 PC(호스트)에 복사
vagrant scp k8s-w1:/tmp/calico-ipip.pcap ./
  • pod1 > pod2 ping 통신 및 확인
# 마스터 노드에서 pod1 Shell 접속
kubectl exec pod1 -it -- zsh

# pod1 에서 pod2 로 핑 통신 : 정상 통신!
ping -c 10 <pod2 IP>
pod1> ping -c 10 172.16.46.12
PING 172.16.46.12 (172.16.46.12) 56(84) bytes of data.
64 bytes from 172.16.46.12: icmp_seq=1 ttl=62 time=0.781 ms
64 bytes from 172.16.46.12: icmp_seq=2 ttl=62 time=0.803 ms
...

# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 확인 : TX/RX 패킷 카운트가 각각 10개로 증가했다
watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        RX packets 10  bytes 840 (840.0 B)
        TX packets 10  bytes 840 (840.0 B)

# 패킷 덤프 : tunl0 - 터널 인터페이스에 파드간 IP 패킷 정보 확인!
tcpdump -i tunl0 -nn
root@k8s-w1:~# tcpdump -i tunl0 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tunl0, link-type RAW (Raw IP), capture size 262144 bytes
23:36:36.621795 IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 1, length 64
23:36:36.622470 IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 1, length 64
23:36:37.623274 IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 2, length 64
23:36:37.623977 IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 2, length 64
...

# 패킷 덤프 : eth0(enp#~) - IP Outer 헤더 안쪽에 IP 헤더 1개가 더 있음을 알 수 있다!
tcpdump -i ens5 -nn proto 4   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 # [실습환경 B Type]
root@k8s-w2:~# tcpdump -i ens5 -nn proto 4   # [실습환경 A Type]
root@k8s-w2:~# tcpdump -i enp0s8 -nn proto 4 # [실습환경 B Type]
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
23:36:36.625169 IP 192.168.100.101 > 192.168.100.102: IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 1, length 64 (ipip-proto-4)
23:36:36.625413 IP 192.168.100.102 > 192.168.100.101: IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 1, length 64 (ipip-proto-4)
23:36:37.626715 IP 192.168.100.101 > 192.168.100.102: IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 2, length 64 (ipip-proto-4)
23:36:37.626866 IP 192.168.100.102 > 192.168.100.101: IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 2, length 64 (ipip-proto-4)
...

tcpdump -i ens5 -nn proto 4 -w /tmp/calico-ipip.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap # [실습환경 B Type]
ls -l /tmp


# [실습환경 A Type] 자신의 PC로 패킷 복사
scp ubuntu@<node 유동공인 IP>:/tmp/calico-ipip.pcap .
scp ubuntu@3.35.229.92:/tmp/calico-ipip.pcap .


# [실습환경 B Type] 자신의 PC로 패킷 복사 : https://github.com/invernizzi/vagrant-scp
## vagrant scp 플러그인 설치
vagrant plugin install vagrant-scp
vagrant plugin list

## k8s-w1 VM 내부의 파일을 자신의 PC(호스트)에 복사
vagrant scp k8s-w1:/tmp/calico-ipip.pcap ./

결과

  • tunnel의 tcmdump에는(node1,calico-tunl0.pcap) pod1(172.16.158.4) 에서 pod2(172.16.184.4)로의 ICMP 헤더값 확인

calico-tunl0.pcap

  • node의 인터페이스에는(node2,calico-ipip.pcap) 파드간 통신을 위한 ICMP 헤더값 외부에, 노드간 전달을 위한 IPIP 프로토콜을 사용한 헤더가 추가로 존재함을 확인

calico-ipip.pcap


Leave a Reply