最近在研究 KubeVirt 和 Virtink 两个项目,计划做一个 Operator 管理虚拟机。Kubernetes API 有望成为云计算基础设施管理的事实标准,IaaS 关注计算、存储和网络,相比 OpenStack,Kubernetes 是一个可塑性更强的项目。
在 Kubernetes 管理虚拟机的场景下,容器网络就是虚拟机网络。目前没有做双网卡方案,我需要把容器网络拉平到物理网络,方便直接登录虚拟机。为了保持架构简单清爽,我放弃了 multus、coredns 和 kube-proxy。这个方案仅为兴趣使然,并不适用于生产环境。
实验准备
我的实验设备是一台 Dell 机架服务器,它的基础信息如下。如果你打算自己实验,下文中用到内网 IP 的地方换成自己的 IP 就可以。
安装 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>
复制代码
参考资料
评论