K3s 是一个轻量的 Kubernetes 集群,Multus 是一个用于给 Pod 创建多个网络接口的 CNI (Container Network Interface) 插件,其创建的接口支持 macvlan


啥是 Macvlan

字面意思,根据 MAC 地址划分的虚拟子网 (Vlan) 就是 macvlan,网上能搜到很多有关 Macvlan 的介绍,这里不再过多描述。

与之相对应的还有一个叫 ipvlan,是通过 IP 地址划分的虚拟子网。

Macvlan 和 ipvlan 都是 Linux 系统的特性,其他系统不支持这个功能。

Prerequisites

可以用 modinfo macvlan 检查系统是否有安装 macvlan 模块,根据 Docker 文档 中描述的建议是使用 Linux 3.9 或 4.0 及更新的内核版本。

可以用以下指令检查系统是否支持 Macvlan(这里使用桥接模式):

sudo ip link add macvlan0 link enp1s0 type macvlan mode bridge  # 这里替换 enp1s0 为网卡接口名称
sudo ip address add 192.168.122.205/24 broadcast 192.168.122.255 dev macvlan0 # 注意 IP 地址冲突

之后可尝试使用其他处于同一个网络(CIDR)的设备 ping 这个 192.168.122.205 IP 地址,能 Ping 通就说明你的防火墙没有屏蔽不同设备之间的二层数据转发。

安装 K3s

根据 Multus 的 QuickStart 手册,准备一个新版本的 Kubernetes 集群(这里用的是 v1.27.8+k3s2),K3s 默认的 CNI 插件使用的是 Flannel。

在国内的话需要先创建 /etc/rancher/k3s/registries.yaml 配置 Registry Mirror:

mirrors:
    docker.io:
        endpoint:
        - "https://docker.nju.edu.cn"
    ghcr.io:
        endpoint:
        - "https://ghcr.nju.edu.cn"

之后使用国内源一键安装 K3s:

#!/bin/bash

curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | \
	INSTALL_K3S_VERSION=v1.27.8+k3s2 \
	INSTALL_K3S_MIRROR=cn \
	sh -s - server \
	--cluster-init \
	--system-default-registry "docker.nju.edu.cn"

安装 Multus CNI

接下来安装 Multus CNI 插件,下载 multus-daemonset.yml 配置,需要编辑 kube-multus-ds DaemonSet hostPath 的路径到 K3s 对应的路径上去。

wget 'https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml'

编辑 kube-multus-ds DaemonSet 的 hostPath 的配置为 K3s 的路径。

...
    volumes:
        - name: cni
          hostPath:
            path: /var/lib/rancher/k3s/agent/etc/cni/net.d
        - name: cnibin
          hostPath:
            path: /var/lib/rancher/k3s/data/current/bin
...

还要编辑 kube-multus-ds DaemonSet 的 Container 配置,增添一条 command arg:

...
    containers:
      - name: kube-multus
        image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot
        command: ["/thin_entrypoint"]
        args:
        - "--multus-conf-file=auto"
        - "--multus-autoconfig-dir=/host/etc/cni/net.d"
        - "--cni-conf-dir=/host/etc/cni/net.d"
        # ADD THIS LINE:
        - "--multus-kubeconfig-file-host=/var/lib/rancher/k3s/agent/etc/cni/net.d/multus.d/multus.kubeconfig"
...

之后 kubectl apply 上面的 Multus Daemonset 配置,等待 kube-multus-ds DaemonSet 跑起来后,可以看到 /var/lib/rancher/k3s/data/current/bin 目录下有新增 multus 可执行文件。

$ sudo ls /var/lib/rancher/k3s/data/current/bin | grep multus
multus

自定义 Multus CNI 配置文件

新建一个名为 macvlan-confNetworkAttachmentDefinition Custom Resource,自定义 multus 配置文件:

这里需要注意 config 中的 master 网卡接口要设置为物理机上对应的网卡接口名。

咱把 K3s Server 安装在了 QEMU 虚拟机中,虚拟机使用的是 libvirt 创建的默认网卡,CIDR 编址为 192.168.122.0/24,网关 192.168.122.1。 为了能在其他虚拟机 / 物理机上也能访问到虚拟机中使用了 macvlan 的 pod,multus macvlan 配置文件也使用 libvirt 网卡的 CIDR。

apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
  name: macvlan-conf
spec:
  config: '{
      "cniVersion": "0.3.1",
      "type": "macvlan",
      "master": "enp1s0",
      "mode": "bridge",
      "ipam": {
        "type": "host-local",
        "subnet": "192.168.122.0/24",
        "rangeStart": "192.168.122.200",
        "rangeEnd": "192.168.122.210",
        "routes": [
          { "dst": "0.0.0.0/0" }
        ],
        "gateway": "192.168.122.1"
      }
    }'
$ kubectl apply -f macvlan-conf.yaml
$ kubectl get net-attach-def
NAME           AGE
macvlan-conf   59s

创建 Macvlan Pod

K3s 将安装包体积做了精简移除了 macvlan CNI 插件,所以创建 Pod 之前需要手动下载 macvlan CNI 插件放到 K3s 的 data bin 目录。

$ mkdir -p cni-plugin && cd cni-plugin
$ wget https://github.com/containernetworking/plugins/releases/download/v1.4.0/cni-plugins-linux-amd64-v1.4.0.tgz
$ tar -zxvf cni-plugins-linux-amd64-v1.4.0.tgz
$ sudo cp ./macvlan /var/lib/rancher/k3s/data/current/bin/

之后创建 Pod,使用 Annotation 指定网络的配置文件,并让 Pod 被 Multus CNI 识别。

apiVersion: v1
kind: Pod
metadata:
  name: nginx-macvlan
  annotations:
    k8s.v1.cni.cncf.io/networks: macvlan-conf
spec:
  containers:
  - name: nginx
    image: nginx

如果一切顺利的话,kubectl describe pod nginx-macvlan 能看到以下的 Events:

Events:
  Type    Reason          Age   From               Message
  ----    ------          ----  ----               -------
  Normal  Scheduled       2s    default-scheduler  Successfully assigned default/nginx-macvlan to archlinux-k3s-1
  Normal  AddedInterface  2s    multus             Add eth0 [10.42.0.26/24] from cbr0
  Normal  AddedInterface  2s    multus             Add net1 [192.168.122.200/24] from default/macvlan-conf

因为 K3s 服务器跑在了 QEMU KVM 虚拟机里面,libvirt 默认网卡 CIDR 是 192.168.122.0/24。所以咱在物理机上访问虚拟机内的 Macvlan Pod IP 192.168.122.200,是能正常访问的。

$ curl 192.168.122.200
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

然后因为 Macvlan 的子接口 (sub interface) 无法与父接口 (parent interface) 直接访问,所以在节点的主机上访问运行在这个节点内的 macvlan pod 是访问不通的,也就是说无法通过节点主机的接口访问到 macvlan pod 的子接口,除非使用 ipvlan,可以参考以下这几篇讨论: