Calico(이하 ”칼리코“) 알아보기
Calico에 대한 공식 문서의 설명에 따르면, calico는 네트워크, 보안 솔루션으로 이용된다고 합니다.
Calico is a networking and security solution that enables Kubernetes workloads and non-Kubernetes/legacy workloads to communicate seamlessly and securely.
그렇다면 쿠버네티스에서는 어떤 네트워킹, 보안적인 문제가 있길래 칼리코와 같은 해결책이 필요한 걸까요?
기본적으로, 쿠버네티스에서 모든 네트워크 통신은 허용합니다. 칼리코는 쿠버네티스 내에서 네트워크 통신을 보안적으로 제한하는 도구로 사용될 수 있습니다. 칼리코가 쿠버네티스에서 네트워크 동작을 제한하는 하는 방식을 칼리코의 구성요소를 통해 한 번 알아보겠습니다.
칼리코에는 2가지 구성이 있습니다. Calico Network, Calico Network Policy Suite
Calico Network는 L3, L4 에 네트워크를 제어합니다. iptables, eBPF 등을 통해 파드나 컨테이너로 전달되는 통신을 제어합니다.
Calico Network polict suite는 네트워크 정책을 제어합니다.
Calico 구성요소
칼리코는 다음 4가지 구성요소가 있습니다.
칼리코 데이터저장소: 칼리코 동작을 위한 데이터들을 저장하는 곳입니다. 데이터 저장소는 k8s API datastore(기본값), etcd을 사용할 수 있습니다.
Felix: 인퍼테이스, 라우팅, ACL을 관리하고 상태를 체크합니다.
BIRD: BGP peer에 라우팅 정보를 전파하고 수신합니다.
confd: 칼리코 데이터저장소의 BGP 구성등을 모니터링해서 변경 내용을 bird에 적용합니다.
Calico 구성 확인
datastore 확인
cat /etc/cni/net.d/10-calico.conflist | jq
datastore로 kubernetes API datastore(kdd)를 사용하고 있음을 확인할 수 있습니다.
네트워크 대역
calicoctl 명령어를 사용하여 각 노드별 할당된 podCIDR를 확인할 수 있습니다.
아래 calicoctl 명령어 결과에서 Block은 각 노드에 할당된 podCIDR를 뜻합니다.
StrictAffinitys는 노드에 할당된 block내에서만 ip를 할당하도록 하는 요소입니다.
True로 설정한 경우 (기본 값): 해당 노드에서 파드가 생성될 때, 노드의 block 내에서 pod ip가 구성됩니다.
False로 설정한 경우: 노드가 부족한 경우 다른 노드로부터 ip 공유 가능합니다.
BGP 연결 정보 확인
calicoctl node status
bird 데몬을 통해, bgp neighbor 연결 정보 ( BGP Peer는 노드의 IP로 연결)를 확인할 수 있습니다.
네트워크 모드
calicoctl get ippool -o wide
calicoctl 명령어를 사용하여 네트워크 모드 및 네트워크 대역을 확인할 수 있습니다.
파드 생성시에 calico는 어떻게 동작할까요?
kubectl -> kubernetes API server : 파드 생성 요청
kubernetes API server -> kube-controller ( calico API server)
kube-controller ( calico API server) -> calico datastore : 생성할 파드 정보 반영
containerd: cni plugin, ipam plugin을 통해서 파드 네트워크 설정
conf -> calico datastore : calico datastore의 변경점 확인
conf -> bird : 설정 파일 업데이트
bird: 생성된 파드의 네트워크 대역 정보 (podCIDR)를 BGP 라우팅을 통해 전파
felix: 파드의 네트워크 대역 정보를 호스트 노드의 routing table에 반영
Calico Network
칼리코가 구성된 쿠버네티스에서 발생하는 통신은 3가지 경우로 생각해볼 수 있습니다.
- 동일한 노드 내에서 pod-pod 통신
- 서로 다른 노드에서 pod-pod 통신
- pod-외부(인터넷) 사이의 통신
네트워크를 확인하기 전에 노드의 정보를 확인하고, 파드를 생성했을 때 노드의 정보가 어떻게 변경되는지 확인해보겠습니다.
이전 게시글에서 칼리코를 구성하고 살펴본 것과 같이 칼리코를 구성하게되면 아래와 같은 인터페이스가 추가된 것을 확인할 수 있습니다.
이중 tunl 인터페이스는 터널(ipip) 인터페이스가 존재하는 것을 확인할 수 있습니다.
라우팅 테이블을 함께 살펴보면 172.16.0.0/16 대역이 각각 tunl0 인터페이스를 통해 각각 노드에 전달되는 것을 확인할 수 있습니다.
그리고 한 가지 재밌는 점은 각 노드에서 아래 명령어로 확인해보면 서로 다른 갯수의 iptables chainig rule이 적용된 것을 확인할 수 있습니다.
iptables -t filter -S | grep cali | wc -l ; iptables -t nat -S | grep cali | wc -l
이유는, 각 노드별로 배포된 파드갯수가 다르기 때문입니다.
k8s-m: 9대
k8s-w0: 3대
k8s-w1: 3대
k8s-w2: 2대
간단히 파드를 배포하기 전에 노드의 네트워크를 확인하였습니다. 이제 파드를 배포한 뒤, 어떤 차이점이 존재하는지 살펴보겠습니다.
pod-to-pod 통신
calico가 구성되어 있을 때 파드간 통신은 어떻게 동작하는지 살펴보겠습니다.
같은 노드
동일한 노드 내에서 파드간 통신에서는 가시다님이 제공해주신 그림과 같이 동작할 것입니다. 이를 확인하기 위해 다음과 같은 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-w1
containers:
- name: pod2
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
파드가 생성되면서 k8s-w1 노드에 iptables를 다시 조회해보면
filter의 chain이 69 -> 127로 증가하였습니다.
calicoctl을 사용하여 파드의 ip, interface를 조회해보겠습니다.
calicoctl get workloadendpoints
각 pod는 calice0906292e2, calibd2348b4f67 인터페이스를 사용한다고 출력이 됩니다.
k8s-w1 노드에 배포했으니 k8s-w1 노드에 접속하여 확인해보겠습니다.
- 파드가 사용하는 네트워크 인터페이스가 2개 추가되었습니다.
7: calice0906292e2@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8981 qdisc noqueue state UP group default qlen 1000
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-87d64f33-1808-0637-7591-17df5c93a223
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
8: calibd2348b4f67@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8981 qdisc noqueue state UP group default qlen 1000
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-b0acd3b8-eeb4-0829-c9e2-c6d69984b378
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
- 라우팅 테이블에 파드 ip 대역이 추가되어 있습니다.
172.16.158.2 dev calice0906292e2 scope link
172.16.158.3 dev calibd2348b4f67 scope link
파드 내에도 접속하여 살펴보겠습니다.
네트워크 인터페이스는 eth0이 활성화 되어 있습니다.
라우팅 테이블은 다음과 같이 구성되어 있습니다.
- 기본 게이트웨이 169.254.1.1
이를 통해 알 수 있는 부분은 172.16.158.2/32 (32bit host routing) 와 통신하지 않는 모든 대역은 기본 게이트웨이로 보내는 것을 알 수 있습니다.
결론: 같은 노드에서 파드간 통신은 직접 통신한다.
서로 다른 노드
서로 다른 노드에서는 각 파드는 어떻게 통신할까요? 이전 글에서 Calico의 구성에 대해 살펴본 것과 같이 bird가 파드 정보를 전달하고, felix가 라우팅 테이블을 변경한다면 과연 노드간 통신이 어떻게 작동되는지 살펴보겠습니다.
파드를 생성해보겠습니다.
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
생성된 파드의 노드 정보과 인터페이스를 확인합니다.
pod2 에서 접속한 다음, pod1로 ping 을 보내보겠습니다.
tcpdump -i tunl0 -nn # 좌측 상단
tcpdump -i ens5 -nn proto 4 # 우측 상단, 좌측 하단 IP 헤더에 프로토콜을 IPIP인 패킷만 필터해 캡쳐하도록 하였습니다.
pod2 -> pod1 로 ping 통신시 위 그림과 같이 요청이 파드에서 호스트로 전달된 다음, tunl0 인터페이스에서 IPIP 프로토콜을 통해 도착지 노드로 전달됩니다.
결론: 노드간 통신에는 IPIP 터널로 요청이 전달됩니다.
pod-to-external 통신
칼리코가 구성된 상황에서 파드가 외부와 통신을 어떻게 하는지 살펴보겠습니다.
파드가 외부와 통신할때는 어떻게 통신할까요? 많은 설명보다 실습을 해보겠습니다.
다음 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
아래와 같은 화면 구성을 한 뒤 파드 내에서 ping으로 외부로 요청해보겠습니다.
상단은 파드가 배포된 노드에서 iptables의 nat table의 cali-nat-outgoing rule에 해당하는 패킷을 모니터링합니다.
가운데 화면 (좌측) icmp 통신하는 모든 네트워크 인터페이스의 패킷을 캡쳐, (우측) 배포한 파드가 사용하는 인터페이스 'calice0906292e2' 만을 캡쳐하도록 구성하였습니다.
하단 화면은 pod 내에서 8.8.8.8로 icmp 통신을 시도합니다.
3번의 요청은 정상적으로 통신되었습니다. 각각 빨강, 초록, 파랑색으로 표시해둔 영역입니다. 이를 자세히 좀 더 자세히 살펴보기 위해 1건만 다시 시도해보겠습니다.
좀 더 보기 편하기 위해 표로 정리해보았습니다.
파드 내에서 인터넷으로 요청하게 되면
1. 172.16.158.4 -> 8.8.8.8 : pod 내에서 8.8.8.8로 요청을 합니다.
2. source ip가 변경됩니다. 172.16.158.4 -> 192.168.10.101
3. 요청이 외부로 나갑니다.
4. 응답이 들어옵니다.
5. 응답의 destination ip가 변경됩니다. 192.168.10.101 -> 172.16.158.4
6. 응답이 pod 내부로 전달됩니다.
결론: 노드의 cali-nat-outgoing chain의 패킷(pkts)이 증가한 것으로 알 수 있듯이 파드의 통신은 외부 통신시 노드의 ip로 masquerade 되어 외부에 연결되는 것을 알 수 있습니다.
'스터디' 카테고리의 다른 글
KCD 참석후기 - 쿠버네티스에서 스케줄링 작동 방식 (0) | 2024.09.28 |
---|---|
[KANS 3기] 3주차 Calico 네트워크 모드와 접근통제(3/3) (0) | 2024.09.21 |
[KANS 3기] 3주차 실습환경 구성하기 (1/3) (1) | 2024.09.21 |
[KANS 3기] 2주차 스터디 내용 정리 (2) | 2024.09.07 |
[KANS 3기] 내가 쓰는 도커 이미지는 어떻게 구성되어 있나요 ? (0) | 2024.08.31 |