해당 문서는 Claude 3.7을 사용하여 작성된 문서입니다. 모든 내용이 검증되지 않았으며, 할루시네이션이 존재할 수 있습니다.
CNI 스펙부터 Flannel 구현까지: 종합 가이드
이 가이드는 Container Network Interface(CNI) 스펙의 기본 개념부터 Flannel에서의 실제 구현까지 종합적으로 설명합니다. Flannel 데몬(flanneld)과 CNI 플러그인이 어떻게 상호작용하는지 이해하는 데 도움이 될 것입니다.
목차
- CNI 스펙 개요
- CNI 작동 방식
- Flannel 아키텍처 개요
- Flannel 데몬(flanneld)
- Flannel CNI 플러그인
- Flannel 데몬과 CNI 플러그인의 연결
- Kubernetes와 Flannel의 통합
- 전체 흐름: Pod 네트워킹 예시
1. CNI 스펙 개요
1.1 CNI란 무엇인가?
Container Network Interface(CNI)는 리눅스 컨테이너를 위한 네트워킹 솔루션으로, 컨테이너 런타임과 네트워크 플러그인 간의 표준 인터페이스를 제공합니다. CNI는 다음과 같은 핵심 용어를 정의합니다:
- 컨테이너(Container): 네트워크 격리 도메인으로, 일반적으로 네트워크 네임스페이스를 의미합니다.
- 네트워크(Network): 서로 통신할 수 있는 고유하게 주소가 지정된 엔드포인트 그룹입니다.
- 런타임(Runtime): CNI 플러그인 실행을 담당하는 프로그램입니다(예: Docker, Kubernetes).
- 플러그인(Plugin): 지정된 네트워크 구성을 적용하는 프로그램입니다.
1.2 CNI 스펙의 주요 구성 요소
CNI 스펙은 다음 5가지 영역을 정의합니다:
- 네트워크 구성 형식: 관리자가 네트워크를 정의하는 JSON 형식
- 실행 프로토콜: 컨테이너 런타임이 네트워크 플러그인에 요청하는 방법
- 플러그인 실행 절차: 구성에 따라 플러그인을 실행하는 방법
- 플러그인 위임 절차: 플러그인이 다른 플러그인에 기능을 위임하는 방법
- 결과 타입: 플러그인이 런타임에 결과를 반환하는 데이터 유형
2. CNI 작동 방식
2.1 네트워크 구성 형식
CNI 네트워크 구성은 JSON 형식으로 정의됩니다. 기본 구조는 다음과 같습니다:
{
"cniVersion": "1.0.0",
"name": "my-network",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"ipam": {
"type": "host-local",
"subnet": "10.1.0.0/16"
}
}
]
}
주요 필드:
- cniVersion: CNI 스펙 버전
- name: 네트워크 이름
- plugins: 플러그인 구성 객체 목록
2.2 실행 프로토콜
CNI 플러그인은 바이너리 실행 기반으로 동작합니다. 런타임은 플러그인을 실행할 때 환경 변수를 통해 다음 파라미터를 전달합니다:
- CNI_COMMAND: 원하는 작업(ADD, DEL, CHECK, VERSION 등)
- CNI_CONTAINERID: 컨테이너 ID
- CNI_NETNS: 컨테이너의 네트워크 네임스페이스 경로
- CNI_IFNAME: 컨테이너 내부에 생성할 인터페이스 이름
- CNI_ARGS: 추가 인자
- CNI_PATH: CNI 플러그인 실행 파일을 찾을 경로 목록
또한 표준 입력(stdin)을 통해 네트워크 구성을 JSON 형식으로 전달합니다.
2.3 CNI 작업
CNI는 다음과 같은 주요 작업을 정의합니다:
- ADD: 네트워크에 컨테이너 추가
- DEL: 네트워크에서 컨테이너 제거
- CHECK: 컨테이너 네트워킹 상태 확인
- VERSION: 플러그인 버전 지원 확인
- GC: 오래된 리소스 정리
- STATUS: 플러그인 상태 확인
3. Flannel 아키텍처 개요
3.1 Flannel이란?
Flannel은 Kubernetes와 같은 컨테이너 오케스트레이션 플랫폼을 위한 네트워크 솔루션으로, 서로 다른 노드에 있는 컨테이너 간 통신을 가능하게 합니다. Flannel은 각 노드에 서브넷을 할당하고, 이 서브넷 간 통신을 위한 오버레이 네트워크나 라우팅 규칙을 설정합니다.
3.2 Flannel의 주요 컴포넌트
Flannel은 두 가지 주요 컴포넌트로 구성됩니다:
- Flannel 데몬(flanneld):
- 서브넷 할당 및 관리
- 네트워크 오버레이 관리
- 백엔드 네트워킹 메커니즘 구현
- Flannel CNI 플러그인:
- Flannel 데몬이 설정한 네트워크를 활용
- 컨테이너에 네트워크 인터페이스 설정
- CNI 스펙에 따른 네트워킹 작업 수행
3.3 Flannel 네트워크 백엔드
Flannel은 다양한 네트워크 백엔드를 지원합니다:
- VXLAN: Virtual Extensible LAN 기반 오버레이 네트워크
- host-gw: 호스트 게이트웨이 기반 직접 라우팅
- UDP: UDP 터널링 (성능이 낮아 권장되지 않음)
- IPSec: 암호화된 IPSec 터널
- WireGuard: WireGuard 기반 암호화된 VPN
- 공용 클라우드: AWS, GCE, Azure 등의 라우팅 API 활용
4. Flannel 데몬(flanneld)
4.1 역할 및 구조
Flannel 데몬(flanneld)은 flannel-io/flannel 레포지토리에 구현되어 있으며, 주요 역할은 다음과 같습니다:
- 서브넷 할당 및 관리
- 노드 간 트래픽 라우팅 설정
- 백엔드 설정 및 관리
레포지토리의 주요 구조:
- main.go: Flannel 데몬의 진입점
- pkg/backend/: 다양한 네트워킹 백엔드 구현
- pkg/subnet/: 서브넷 관리 및 할당 로직
4.2 서브넷 관리
Flannel은 전체 클러스터의 큰 CIDR 블록(예: 10.244.0.0/16)에서 각 노드에 작은 서브넷(예: 10.244.1.0/24)을 할당합니다. 이 정보는 다음과 같은 방식으로 저장 및 공유됩니다:
- etcd/Kubernetes API: 클러스터 전체 상태 저장
- 로컬 파일: /run/flannel/subnet.env 파일에 로컬 노드 정보 저장
subnet.env 파일 형식 예시:
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.0/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
4.3 네트워크 백엔드 구현
각 백엔드는 pkg/backend/ 디렉토리에 구현되어 있습니다:
- pkg/backend/vxlan/: VXLAN 백엔드 구현
- pkg/backend/hostgw/: host-gw 백엔드 구현
- pkg/backend/udp/: UDP 백엔드 구현
- pkg/backend/wireguard/: WireGuard 백엔드 구현
각 백엔드는 BackendType 인터페이스를 구현하여 일관된 방식으로 네트워크를 설정합니다.
5. Flannel CNI 플러그인
5.1 메타-플러그인 개념
Flannel CNI 플러그인은 flannel-io/cni-plugin 레포지토리에 구현되어 있으며, 메타-플러그인으로 설계되었습니다. 즉:
- Flannel 데몬이 생성한 서브넷 정보를 읽음
- 자체 구성과 결합
- 실제 네트워크 설정을 다른 CNI 플러그인(bridge, ipvlan 등)에 위임
5.2 CNI 스펙 구현
Flannel CNI 플러그인은 flannel.go 파일에서 CNI 스펙의 핵심 명령을 구현합니다:
func main() {
fullVer := fmt.Sprintf("CNI Plugin %s version %s (%s/%s) commit %s built on %s", Program, Version, runtime.GOOS, runtime.GOARCH, Commit, buildDate)
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, cni.All, fullVer)
}
각 명령은 다음과 같이 구현됩니다:
- cmdAdd: 컨테이너에 네트워크 인터페이스 추가
- cmdDel: 컨테이너에서 네트워크 인터페이스 제거
- cmdCheck: 컨테이너 네트워크 상태 확인
5.3 플러그인 위임 메커니즘
Flannel CNI 플러그인은 delegateAdd 함수를 통해 실제 네트워크 설정을 다른 플러그인에 위임합니다:
func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error {
// 구성 직렬화
netconfBytes, err := json.Marshal(netconf)
// ...
// 구성 저장
if err = saveScratchNetConf(cid, dataDir, netconfBytes); err != nil {
return err
}
// 위임된 플러그인 호출
result, err := invoke.DelegateAdd(context.TODO(), netconf["type"].(string), netconfBytes, nil)
// ...
return result.Print()
}
이 방식은 CNI 스펙의 플러그인 체이닝(chaining) 기능을 활용한 것입니다.
6. Flannel 데몬과 CNI 플러그인의 연결
6.1 통신 메커니즘
Flannel 데몬과 CNI 플러그인은 직접적인 프로세스 간 통신을 하지 않고, 파일 시스템을 통해 정보를 공유합니다:
- Flannel 데몬: /run/flannel/subnet.env 파일에 서브넷 정보 기록
- CNI 플러그인: 이 파일을 읽어 서브넷 정보 활용
예를 들어, loadFlannelSubnetEnv 함수는 서브넷 파일에서 다음 정보를 읽습니다:
- FLANNEL_NETWORK: 전체 네트워크 CIDR
- FLANNEL_SUBNET: 노드에 할당된 서브넷
- FLANNEL_MTU: 최대 전송 단위
- FLANNEL_IPMASQ: IP 마스커레이딩 활성화 여부
6.2 설정 파일 생성
Flannel 데몬은 다음 함수를 통해 CNI 플러그인이 사용할 설정 파일을 생성합니다:
// HandleSubnetFile writes the configuration file used by the CNI flannel plugin
func (ksm *kubeSubnetManager) HandleSubnetFile() error {
// ...
return ioutil.WriteFile(ksm.subnetFile, contents, 0644)
}
이 파일은 /run/flannel/subnet.env에 저장되며, CNI 플러그인이 네트워크 설정 시 참조합니다.
7. Kubernetes와 Flannel의 통합
7.1 Flannel 배포
Kubernetes에서 Flannel은 일반적으로 DaemonSet으로 배포됩니다. 주요 구성 요소는 다음과 같습니다:
- Flannel 데몬 컨테이너: 각 노드에서 실행되는 주요 프로세스
- CNI 플러그인 설치 컨테이너: CNI 바이너리와 구성 파일 설치
- ConfigMap: Flannel 네트워크 구성 정보
# Flannel ConfigMap 예시
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
7.2 설치 구조
Flannel 설치는 다음과 같이 구성됩니다:
- flanneld 데몬: /opt/bin/flanneld에 설치
- CNI 플러그인: /opt/cni/bin/flannel에 설치
- CNI 구성 파일: /etc/cni/net.d/10-flannel.conflist에 설치
이 설치는 kube-flannel.yml 매니페스트에 정의된 DaemonSet을 통해 수행됩니다.
8. 전체 흐름: Pod 네트워킹 예시
Pod가 생성되어 네트워크가 설정되는 전체 흐름을 단계별로 살펴보겠습니다:
8.1 초기 설정
- Flannel 배포:
- DaemonSet을 통해 각 노드에 flanneld 실행
- CNI 플러그인 및 구성 파일 설치
- Flannel 데몬 초기화:
- 전체 네트워크 CIDR에서 노드 서브넷 할당
- /run/flannel/subnet.env 파일 생성
- 선택된 백엔드(예: VXLAN)에 따라 네트워크 설정
8.2 Pod 생성 및 네트워크 설정
- kubelet이 Pod 생성:
- 컨테이너 런타임이 네트워크 네임스페이스 생성
- kubelet이 CNI 플러그인 호출
- CNI 플러그인 체인 실행:
- kubelet이 /etc/cni/net.d/10-flannel.conflist의 첫 번째 플러그인(flannel) 실행
- CNI 명령: CNI_COMMAND=ADD
- Flannel CNI 플러그인 처리:
- /run/flannel/subnet.env에서 서브넷 정보 로드
- 위임할 플러그인(bridge) 구성 준비
- bridge 플러그인 호출
- Bridge 플러그인 실행:
- veth 쌍 생성 (컨테이너와 호스트 연결)
- 브릿지에 veth 인터페이스 연결
- IPAM 플러그인 호출하여 IP 할당
- IPAM 처리:
- Flannel 서브넷 내에서 IP 주소 할당
- 라우팅 설정
- 결과 반환:
- CNI 플러그인이 결과를 kubelet에 반환
- Pod에 할당된 IP 주소 및 라우팅 정보 포함
8.3 Pod 간 통신
- 같은 노드의 Pod 간 통신:
- 로컬 브릿지를 통해 직접 통신
- 다른 노드의 Pod 간 통신:
- 백엔드에 따라 다름:
- VXLAN: 패킷을 캡슐화하여 원격 노드로 전송
- host-gw: 직접 라우팅을 통해 패킷 전달
- WireGuard: 암호화된 터널을 통해 패킷 전달
- 백엔드에 따라 다름:
이러한 방식으로 Flannel은 CNI 스펙을 구현하면서 Kubernetes 클러스터 전체에 걸쳐 Pod 간 통신을 가능하게 합니다.
결론
이 가이드를 통해 CNI 스펙의 기본 개념부터 Flannel에서의 구현까지 살펴보았습니다. Flannel은 두 개의 주요 컴포넌트(flanneld 데몬과 CNI 플러그인)로 구성되며, 이들은 파일 시스템을 통해 정보를 공유하면서 협력합니다.
Flannel의 구현은 CNI 스펙을 충실히 따르면서도, 메타-플러그인 개념과 위임 메커니즘을 통해 확장성과 유연성을 제공합니다. 이러한 접근 방식은 다양한 네트워크 환경과 요구 사항에 적응할 수 있는 강력한 네트워킹 솔루션을 만들어 냅니다.