当Kubernetes遇见Macvlan—网络互通

最近在研究KubeVirt和Virtink两个项目,计划做一个Operator管理虚拟机。Kubernetes API有望成为云计算基础设施管理的事实标准,IaaS关注计算、存储和网络,相比OpenStack,Kubernetes是一个可塑性更强的项目。

最佳实践
当Kubernetes遇见Macvlan——网络互通.png

最近在研究KubeVirt和Virtink两个项目,计划做一个Operator管理虚拟机。Kubernetes API有望成为云计算基础设施管理的事实标准,IaaS关注计算、存储和网络,相比OpenStack,Kubernetes是一个可塑性更强的项目。

在Kubernetes管理虚拟机的场景下,容器网络就是虚拟机网络。目前没有做双网卡方案,我需要把容器网络拉平到物理网络,方便直接登录虚拟机。为了保持架构简单清爽,我放弃了multus、coredns和kube-proxy。

实验准备

我的实验设备是一台Dell机架服务器,它的基础信息如下。如果你打算自己实验,下文中用到内网IP的地方换成自己的IP就可以。

系统 主机名 内网IP 网卡
ubuntu 20.04 lyr620 192.168.0.5/16 eno1

安装Kubernetes

你可以使用Sealos丝滑安装Kubernetes。Sealos是一个轻量的二进制工具,可以搞定Kubernetes安装中的一些常见痛点。包括但不限于Linux配置、组件镜像和高可用集群。 Sealos的安装和使用请参考官方文档,这里不做赘述。我使用的Sealos版本为4.1.3,Kubernetes版本为1.23.13,下面是我的Clusterfile。

apiVersion: apps.sealos.io/v1beta1
kind: Cluster
metadata:
  name: default
spec:
  # 我的环境只有一台机器
  hosts:
  - ips:
    - 192.168.0.5:22
    roles:
    - master
    - amd64
  # 匹配sealos 4.1.3版本的kubernetes镜像
  image:
  - labring/kubernetes:v1.23.13-4.1.3
  # 请确保ssh可以通过公钥直接登录
  ssh:
    pk: /root/.ssh/id_rsa
    port: 22

---

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
networking:
  # 这里设置为和物理网络相同的网段
  podSubnet: 192.168.0.0/16
---

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
# 跳过coredns和kube-proxy的安装
skipPhases:
- addon/coredns
- addon/kube-proxy

你只需要一条命令就可以安装Kubernetes集群。

$ sealos apply -f Clusterfile

macvlan虚拟的设备插入到容器network namespace以后,其中的流量就不经过主机network namespace了,依赖iptables/ipvs实现的service全然无用,kube-proxy和coredns自然也成了摆设。
我是单节点Kubernetes,这里需要去掉master上的taint,使得Pod可以调度到master。

$ kubectl taint node lyr620 node-role.kubernetes.io/master-

配置CNI Plugin

你需要下载官方出品的CNI Plugins,我使用了1.1.1版本。
将下载的压缩包解压到Kubernetes默认的CNI插件目录。

$ mkdir -p /opt/cni/bin
$ tar -zxvf cni-plugins-linux-amd64-v1.1.1.tgz -C /opt/cni/bin

在/etc/cni/net.d目录下创建一个00-default.conflist文件,写入如下内容。

{
    "cniVersion": "0.4.0",
    "name": "default",
    "plugins": [
        {
            "type": "macvlan",
            "master": "eno1",
            "ipam": {
                "type": "host-local",
                "ranges": [
                    [
                        {
                            "subnet": "192.168.0.0/16",
                            "rangeStart": "192.168.1.2",
                            "rangeEnd": "192.168.1.254",
                            "gateway": "192.168.0.1"
                        }
                    ]
                ],
                "routes": [
                    {"dst": "0.0.0.0/0"}
                ]
            }
        }
    ]
}

我的机器上只有eno1这个网卡连接到了公司内网,所以指定macvlan插件在eno1上创建子设备分配给容器。这里的IPAM子网配置到主机网络192.168.0.0/16,但是将IP池限制在192.168.1.0/24这个子网的IP范围。
之前也尝试过将subnet设置为192.168.1.0/24,主机为之添加相应的路由,但是这个方案没能成功,其中有些问题以我的内功暂时还玩不动......

验证Pod网络

检查Kubernetes Node是否就绪。

$ kubectl get nodes
NAME     STATUS   ROLES                  AGE     VERSION
lyr620   Ready    control-plane,master   5h33m   v1.23.13

创建一个Nginx Deployment,观察Pod是否Running,查看容器的路由表。

$ kubectl create deployment nginx --image nginx:stable-alpine

$ kubectl get pods -o wide
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE    IP   
default       nginx-7bd849c599-cbgqn           1/1     Running   0          5s     192.168.1.2

$ kubectl exec nginx-7bd849c599-cbgqn -it -- ip route
default via 192.168.0.1 dev eth0
192.168.0.0/16 dev eth0 scope link  src 192.168.1.2

虽然Pod已经Running,但这时从主机ping容器是不通的。

$ ping -c 3 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
From 192.168.0.5 icmp_seq=1 Destination Host Unreachable
From 192.168.0.5 icmp_seq=2 Destination Host Unreachable
From 192.168.0.5 icmp_seq=3 Destination Host Unreachable

--- 192.168.1.2 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2027ms
pipe 3

从容器ping主机也不行。

$ kubectl exec -it nginx-7bd849c599-cbgqn -- ping -c 3 192.168.0.5
PING 192.168.0.5 (192.168.0.5): 56 data bytes

--- 192.168.0.5 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

为了满足Kubernetes主机和容器网络互通的需求,我们需要在主机的network namespace创建并启动一个bridge模式的macvlan子设备。

$ ip link add eno1.host link eno1 type macvlan mode bridge
$ ip link set dev eno1.host up

然后在主机上写一条直通Pod IP的路由。

$ ip route add 192.168.1.2 dev eno1.host
$ ip route
default via 192.168.0.1 dev eno1 proto static
192.168.0.0/16 dev eno1 proto kernel scope link src 192.168.0.5
192.168.1.2 dev eno1.host scope link

这时容器和主机就互通了,你不但可以ping通Pod IP,而且访问Pod IP还可以看见Nginx的网页。

$ curl http://192.168.1.2
<!DOCTYPE html>
<html>
...
</html>

参考文献

  • 《图解几个与Linux网络虚拟化相关的虚拟网卡-VETH/MACVLAN/MACVTAP/IPVLAN》
  • 《kubernetes的clusterip机制调研及macvlan网络下的clusterip坑解决方案》
  • 《Docker使用macvlan网络容器与宿主机的通信过程》
Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting