--- title: Multus CNI + NetworkAttachmentDefinition updated: 2026-04-19 tags: [infra, k3s, multus, cni, networking, longhorn] --- ## 개요 K3s 클러스터에 **2차 네트워크 인터페이스**를 Pod에 붙여줄 수 있게 해주는 meta-CNI. Flannel(기본 eth0)은 그대로 두고 `k8s.v1.cni.cncf.io/networks` 어노테이션으로 추가 인터페이스(`net1`, `net2`, ...) 연결. **주 용도**: Longhorn storage network 분리 (192.168.205.0/24 2.5G 전용망). [[longhorn-storage-network]]도 참고. ## 아키텍처 ``` Pod ├── eth0 (10.42.x.x, Flannel, 1GbE via br-uplink) └── net1 (192.168.205.240-254, macvlan via ens2, 2.5GbE, MTU 9000) ``` - **Multus CNI (thick daemonset)**: Pod 생성 시 Flannel 기본 네트워크에 추가로 NAD 기반 네트워크를 붙임 - **Whereabouts**: 범위 기반 IPAM (클러스터 전역 IP 충돌 방지, CRD 기반 추적) - **macvlan CNI**: Pod에 ens2 부모 인터페이스의 MAC 기반 가상 인터페이스 생성 ## K3s 경로 특이점 K3s CNI 구조가 표준 `/opt/cni/bin`과 다르고 대부분 **심볼릭 링크가 multicall 바이너리를 가리키는 형태**라 Multus thick chroot과 충돌함. 아래 방식으로 해결: | 경로 | 역할 | |------|------| | `/var/lib/rancher/k3s/agent/etc/cni/net.d/` | k3s CNI conf dir (kubelet이 여기서 NetConf 읽음) | | `/var/lib/rancher/k3s/data/cni/` | k3s CNI bin dir (flannel/bridge/host-local이 multicall `cni` 바이너리 심볼릭) | | `/var/lib/rancher/k3s/data/current/bin/cni` | 실제 k3s multicall CNI 바이너리 (이름에 따라 flannel/bridge/호출 분기) | | **`/opt/cni/bin/`** (신규, 실제 디렉토리) | **Multus/Whereabouts가 사용**하는 CNI 바이너리 저장소. 모두 real binary | **`/opt/cni/bin/` 내용** (각 노드 kr1/kr2/hp1/hp2): - `containernetworking/plugins v1.6.2` 풀 셋: `bandwidth`, `bridge`, `dhcp`, `dummy`, `firewall`, `host-device`, `host-local`, `ipvlan`, `loopback`, `macvlan`, `portmap`, `ptp`, `sbr`, `static`, `tap`, `tuning`, `vlan`, `vrf` - `flannel` ← k3s multicall 바이너리 복사본 (`cp /var/lib/rancher/k3s/data/current/bin/cni /opt/cni/bin/flannel`) - `multus-shim`, `passthru` ← Multus DS init container가 배포 - `whereabouts` ← Whereabouts DS가 배포 ## 심볼릭 링크 (노드별) K3s 기본 CNI dir과 표준 경로 브리지: ```sh # kubelet이 k3s CNI dir에서 multus-shim 찾을 수 있게 /var/lib/rancher/k3s/data/cni/multus-shim → /opt/cni/bin/multus-shim /var/lib/rancher/k3s/data/cni/passthru → /opt/cni/bin/passthru # whereabouts 바이너리가 /etc/cni/net.d/whereabouts.d 기본 경로를 기대함 /etc/cni/net.d/whereabouts.d → /var/lib/rancher/k3s/agent/etc/cni/net.d/whereabouts.d ``` ## 배포 정보 | 컴포넌트 | Kind | 위치 | |---------|------|------| | `kube-multus-ds` | DaemonSet | kube-system | | `whereabouts` | DaemonSet | kube-system | | `NetworkAttachmentDefinition` CRD | CRD | cluster-scope | | `ippools`, `overlappingrangeipreservations`, `nodeslicepools` CRDs | CRD | cluster-scope (whereabouts.cni.cncf.io) | **버전**: - Multus CNI: `v4.2.2` (thick daemonset) - Whereabouts: `v0.9.1` - containernetworking/plugins: `v1.6.2` **매니페스트 커스터마이징**: 상류 manifest에서 hostPath 2개만 k3s 경로로 sed 치환: ```sh sed -e 's|path: /etc/cni/net.d$|path: /var/lib/rancher/k3s/agent/etc/cni/net.d|g' \ -e 's|path: /etc/cni/multus/net.d$|path: /var/lib/rancher/k3s/agent/etc/cni/multus/net.d|g' \ multus-daemonset-thick.yml > multus-k3s.yml ``` Whereabouts도 동일(conf dir만 패치, `/opt/cni/bin`은 실제 디렉토리라 default 유지). ## NetworkAttachmentDefinition ### `longhorn-system/storage-205` Longhorn 전용 2.5G 스토리지 네트워크. ```yaml apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: name: storage-205 namespace: longhorn-system spec: config: | { "cniVersion": "0.3.1", "type": "macvlan", "master": "ens2", "mode": "bridge", "mtu": 9000, "ipam": { "type": "whereabouts", "range": "192.168.205.0/24", "range_start": "192.168.205.240", "range_end": "192.168.205.254" } } ``` | 항목 | 값 | |------|-----| | 부모 인터페이스 | `ens2` (각 노드 2.5G NIC, 192.168.205.x/24) | | 모드 | `bridge` (macvlan) | | MTU | 9000 (jumbo frames) | | IP 풀 | 192.168.205.240 - 192.168.205.254 (15개) | | IPAM | whereabouts (클러스터 전역 IP 중복 체크) | **192.168.205.0/24 점유 현황** (이 NAD 도입 시점): - `.100` NAS (Synology) — 2.5G 스토리지 세그먼트 - `.134` hp2 / `.135` kr2 / `.214` kr1 / `.227` hp1 — 각 노드 ens2 - `.240 ~ .254` NAD 풀 (Longhorn instance-manager 등) ## 사용 방법 Pod 정의에 어노테이션 추가: ```yaml metadata: annotations: k8s.v1.cni.cncf.io/networks: storage-205 # 다른 네임스페이스면: k8s.v1.cni.cncf.io/networks: longhorn-system/storage-205 ``` Pod 안에 `net1` 인터페이스가 whereabouts가 할당한 IP(`.240-.254`)로 붙음. `eth0`(Flannel)은 그대로. ## 업그레이드 시 주의 K3s 업그레이드 시 `/var/lib/rancher/k3s/data/cni/` 심볼릭이 재생성될 수 있음. 아래 심볼릭 링크가 살아있는지 확인: ```sh ls -la /var/lib/rancher/k3s/data/cni/multus-shim ls -la /var/lib/rancher/k3s/data/cni/passthru ``` 끊어졌으면 재생성: ```sh sudo ln -sfn /opt/cni/bin/multus-shim /var/lib/rancher/k3s/data/cni/multus-shim sudo ln -sfn /opt/cni/bin/passthru /var/lib/rancher/k3s/data/cni/passthru ``` `/opt/cni/bin/`는 k3s와 무관한 경로라 업그레이드 영향 없음. ## 트러블슈팅 ### `failed to find plugin "X" in path [/opt/cni/bin]` Multus thick의 chroot가 `/hostroot`로 들어간 뒤 `/opt/cni/bin/X` 찾음. 원인: - `/opt/cni/bin`이 심볼릭이면 대상 디렉토리의 binary가 pod에 마운트 안 돼 실행 불가 - **해결**: `/opt/cni/bin`을 real directory로 만들고 모든 플러그인을 real binary로 배치 (이 구성이 그렇게 해결됨) ### `config file not found` (macvlan + whereabouts) whereabouts 바이너리가 `/host/etc/cni/net.d/whereabouts.d/whereabouts.conf`를 찾지만 k3s는 conf를 `/var/lib/rancher/k3s/agent/etc/cni/net.d/`에 둠. - **해결**: `/etc/cni/net.d/whereabouts.d` 심볼릭 링크 생성 ### Multus DS pod 로그 확인 ```sh sudo kubectl -n kube-system logs -l app=multus --tail=50 ``` ### 테스트 Pod 예시 ```yaml apiVersion: v1 kind: Pod metadata: name: nad-test namespace: longhorn-system annotations: k8s.v1.cni.cncf.io/networks: storage-205 spec: containers: - name: probe image: alpine:3.20 command: ["sh","-c","sleep 600"] ``` ```sh sudo kubectl -n longhorn-system exec nad-test -- ip -br addr # eth0=10.42.x.x, net1=192.168.205.24X (whereabouts 할당) ``` ## 관련 문서 - [[openwrt]] — 192.168.9.0/24 라우터 - [[metallb]] — k3s LoadBalancer (별개, service IP용) - [[infra-hosts]] — 각 노드 ens2 IP 현황