
지난 시간에 이어 이번에는 subnet/kube 경로의 소스코드를 분석하게 되었다.

시작하기전에.. 네트워크 관련 핵심 사항들을 좀 짚어보자
쿠버네티스에서 Flannel을 통한 컨테이너간 패킷 경로는 이렇다.
1. Container A(10.1.1.2)에서 Container B(10.1.2.3)로 패킷 전송
2. 패킷이 veth를 통해 docker0 브릿지로 이동
3. docker0에서 flannel0로 전달
4. flannel0이 패킷을 캡슐화하여 호스트 네트워크로 전송
5. 물리 네트워크를 통해 대상 호스트로 전달
6. 대상 호스트의 flannel0이 패킷을 디캡슐화
7. docker0 브릿지를 통해 대상 컨테이너의 veth로 전달
8. 최종적으로 Container B에 도달
veth는 격리된 컨테이너 네임스페이스와 호스트 네트워크를 연결하는 가상의 '네트워크 케이블' 역할을 한다.
docker 브릿지는 컨테이너당 존재하는 veth 들을 모두 연결하는 hub 역할을 한다.
[컨테이너 A] [컨테이너 B] [컨테이너 C]
↕ ↕ ↕
[vethA] [vethB] [vethC]
↓ ↓ ↓
================================
docker0
================================
↓
호스트 네트워크
[컨테이너 네트워크 레이어]
컨테이너 A (10.1.1.2) → 컨테이너 B (10.1.2.3)
[호스트 네트워크 레이어]
호스트 1 (192.168.1.10) → 호스트 2 (192.168.1.20)
Flannel 서브넷 디렉토리는 요약하면 이런 역할을 하는 소스코드들을 담고 있다.
새로운 노드가 클러스터에 참여할 때 서브넷 범위를 할당한다.
lease + channel을 통해 중복 할당 방지를 위한 동시성을 제어한다.
만료된 서브넷을 회수하고 서브넷 충돌을 감지하고 적절한 액션을 취한다.
서브넷 정보를 저장하며 클러스터 전체의 서브넷 상태 동기화 한다.
Kubernetes 노드에 Pod들이 사용할 CIDR을 할당하고 관리한다.
subnet/kube 디렉토리는 Kubernetes 관련 서브넷 관리를 위한 소스코드들을 담고 있다.
Flannel은 CNI를 지원하는 모든 환경에서 동작할 수 있기 때문에 Mesos, Docker Swarm 같은 다른 컨테이너 오케스트레이션 툴에도 쓸 수 있다고 한다. (쿠버네티스 전용 인줄 알았다...)
어쩃든 하위 경로에는 kube.go, annotation.go 파일이 있는데 핵심적인 kube.go 에서 어떻게 쿠버네티스 노드당 서브넷을 할당하는지 분석해보자
Flannel은 쿠버네티스에서 데몬셋으로 배포된다. 쿠버네티스는 Downward API를 통해 파드 생성시 노드의 이름을 환경 변수로 넣을 수 있다. (EKS에서는 인스턴스 네임이 주입된다.)

그래서 KubeSubnetManager 함수 인자로 노드이름을 넣음으로서 해당 노드에 서브넷 매니저를 생성한다.

kubeSubnetManager에는 흥미롭게도 go channel을 이용해서 이벤트큐를 생성한다. (기본적으로 버퍼 사이즈가 5000이다.)

handleAddLeaseEvent는 결국에 다 서브넷매니저 이벤트 채널로 이벤트를 보낸다.

그럼 이제 Node에서 발생하는 이벤트를 수집하기위해 Node를 감시해야한다! 만약 클라우드 업체가 자체적으로 Node 감시를 제공한다면 NodeInformer 옵션을 disable 하고 아니면 자체적으로 쿠버네티스 API를 사용하여 노드를 감시하는 듯 하다.

Flannel에서 서브넷은 리스로 관리된다. 따라서 서브넷 할당을 위해 AcquireLease 함수를 호출한다.

쿠버네티스에서 Flaneel은 직접 CIDR을 할당하는 것이 아니라, 쿠버네티스가 파드에 할당한 CIDR을 확인하고 이를 기반으로 서브넷을 구성한다.
노드에 스케줄링되어있는
Node1 (PodCIDR: 10.244.1.0/24)
├── Pod1: 10.244.1.2
├── Pod2: 10.244.1.3
└── Pod3: 10.244.1.4
Node2 (PodCIDR: 10.244.2.0/24)
├── Pod4: 10.244.2.2
├── Pod5: 10.244.2.3
└── Pod6: 10.244.2.4
쿠버네티스 API를 사용해서 cacheNode에 노드 정보를 저장하고 이를 사용한다. 그리고 노드 정보를 깊은 복사해서 따로 선언한다.
그리고 node annotation을 변경할 때, 신규 struct로 patch하는 방식을 사용한다.
node 정보는 다른 API에서 접근할 수 있는 shared resource 이므로 이러한 방식을 채택하는 것 같은데 흥미롭다..


이 노드 정보에는 PodCIDR가 들어있고, Flannel은 이것을 사용하는 것이다.



Flannel 설정:
- 전체 네트워크: 10.244.0.0/16
Kubernetes가 할당한 PodCIDR:
- 노드1: 10.244.1.0/24
- 노드2: 10.244.2.0/24
- 노드3: 10.244.3.0/24
Flannel의 검증:
1. 10.244.1.0/24가 10.244.0.0/16 안에 있는지 확인
2. 해당 CIDR로 Lease 생성
3. 노드에 애노테이션으로 서브넷 정보 기록
요약하자면
1. Kubernetes가 노드별 PodCIDR 할당
2. Flannel이 이 CIDR을 확인하고 검증
3. 검증된 CIDR로 Lease 생성
4. 노드에 애노테이션으로 서브넷 정보 기록 (이 때 노드정보를 깊은 복사해놨다가 PATCH)
5. 이 정보를 기반으로 실제 네트워크 구성
이 함수들은 주석에서 보다시피,Flannel이 재시작할 때 network configuration을 재생하기위한 것이다. VXLAN 모드에서 사용하는 가상 MAC 주소와 노드의 public IP 주소를 복원하는 함수이다.
해당 값들은 실제 노드의 NIC에서 읽어와야하기 때문에, Flannel을 재시작하면 다시 읽어와서 등록해주는 것 같다

여기는 Flannel이 쿠버네티스 노드에 추가하는 애노테이션을 관리하는 파트라서 아주 간단하다.
annotation 구조체를 선언하고 만드는 함수가 있다.

실제로는 이런 형태로 노드 애노테이션이 생기고, 이를 기반으로 또 여러 노드를 구분하고 분류하여 관리할 수 있게 한다
flannel.alpha.coreos.com/backend-type: "vxlan"
flannel.alpha.coreos.com/public-ip: "192.168.1.10"
flannel.alpha.coreos.com/backend-data: '{"VNI":1,"VtepMAC":"12:34:56:78:9a:bc"}'