文章目录
根据《flannel host-gw 网络概述》,感觉 hostgw 网络模型还是比较简单的,于是萌生了一个想法,手动配置 hostgw 环境,不启用 flanneld,(也就是不启动 flannel daemon程序),手动配置路由,iptables 规则等,看看是不是行得通,以此更好地理解下 k8s 的网络模型。
实现环境共两个节点,hostname 和 ip 对应如下:
节点 | ip |
---|---|
HostGW-Master | 192.168.31.201 |
HostGW-Node | 192.168.31.202 |
部署集群
通过 kubeadm 部署,参考《使用 kubeadm 部署 Kubernetes》
kubeadm init --pod-network-cidr=10.244.0.0/16
启动之后,节点是 NotReady 状态,原因为: message: 'runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady, message:docker: network plugin is not ready: cni config uninitialized'
,这个错误原因是 dockershim 在初始化 defaultnetwork 的时候,没有找到网络配置。这个问题在通过添加 cni 网络配置文件解决,即下面的“配置网络”部分,理论上这个是需要 flannel 配置的,因为我们没有 flanneld,所以手动配置。
[decent@HostGW-Master ~]$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
hostgw-master NotReady master 9m18s v1.16.9
hostgw-node NotReady <none> 3m28s v1.16.9
配置网络
在每个节点的 /etc/cni/net.d
目录配置网络,内容如下,每个节点都一样。这个目录对应 kubelet 的 --cni-conf-dir
参数。我们这里只配置了使用 flannel 作为我们的网络插件,其实现就是 /opt/cni/bin 目录下的 flannel 二进制文件。
注意,这个配置文件一定要用
.conflist
作为后缀,dockershim 的 cni 实现会查找后缀名为.conf
.conflist
.json
的配置文件,我们这里是一个插件列表,所以要用.conflist
结尾。dockershim 的实现的路径为:pkg/kubelet/dockershim/network/cni/cni.go
{
"name": "cbr0",
"cniVersion":"0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
配置这个文件之后,节点就为 Ready 状态了。但是很明显现在网络还是不能正常工作的。
[decent@HostGW-Master net.d]$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
hostgw-master Ready master 19m v1.16.9 192.168.31.201 <none> CentOS Linux 7 (Core) 3.10.0-1160.76.1.el7.x86_64 docker://20.10.17
hostgw-node Ready <none> 13m v1.16.9 192.168.31.202 <none> CentOS Linux 7 (Core) 3.10.0-1160.76.1.el7.x86_64 docker://20.10.17
另外需要注意下 /opt/cni/bin
目录,是否有 cni 的 binary,如果没有的话,从 flannel 的 init 容器中拷贝一份。
配置 subnet.env
这个是 flanneld 生成的,根据每个节点的 Node CIDR 生成 Pod 的子网,因为我们没有 flanneld,所以我们手动生成,路径和文件名是固定的。全路径为:/var/run/flannel/subnet.env
。这里 flanneld 与 flannel 插件可以看成构成了一个生产者和消费者的模型,flanneld 生产这个文件,flannel 插件消费这个文件。同时,flanneld 除了生成这个文件,还会配置路由和 iptables 规则,就是我们下面要操作的内容。
首先看下 pod cidr:
[decent@HostGW-Master net.d]$ kubectl get node hostgw-master -o yaml | grep CIDR
podCIDR: 10.244.0.0/24
podCIDRs:
[decent@HostGW-Master net.d]$ kubectl get node hostgw-node -o yaml | grep CIDR
podCIDR: 10.244.1.0/24
podCIDRs:
[decent@HostGW-Master net.d]$
对于 hostgw-master
,其配置为:(这里 mtu 设置成 1500 应该也没有问题,主要是在 overlay 网络下,要加上封包带来的开销,比如 vxlan 下要设置成 1450)
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
对于 hostgw-node
,其配置为
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
配置路由
根据 hostgw 工作方式,在每个节点上,配置 pod 网段的网管,以我们的环境为例,HostGW-Master
这个节点的网段为 10.244.0.0/24
,那么所有其他所有的节点都要配置一条路由,即 10.244.0.0/24
的下一条为 HostGW-Master
的地址。
配置路由之前的路由列表如下(HostGW-Master 节点),路由表中,还有个路由比较重要 10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
,这个路由是 flannel 插件配置的,对于所有的目标地址为 10.244.0.0/24
的报文,都发送到 cni0
这个网桥,再由网桥发生给具体的 pod。
[decent@HostGW-Master net.d]$ ip route
default via 192.168.31.1 dev ens33 proto static metric 100
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.31.0/24 dev ens33 proto kernel scope link src 192.168.31.201 metric 100
在两个节点分别添加路由:
# master 节点
sudo ip route add 10.244.1.0/24 via 192.168.31.202 dev ens33
# worker 节点
sudo ip route add 10.244.0.0/24 via 192.168.31.201 dev ens33
添加 iptables 规则
在 filter 表的 forward 链添加对 pod 子网的转发,对 pod 所有ip 都进行转发。确认添加之前的 FORWARD 链。
[decent@HostGW-Master ~]$ sudo iptables -v -L FORWARD
Chain FORWARD (policy DROP 14 packets, 630 bytes)
pkts bytes target prot opt in out source destination
4649 210K KUBE-FORWARD all -- any any anywhere anywhere /* kubernetes forwarding rules */
4649 210K KUBE-SERVICES all -- any any anywhere anywhere ctstate NEW /* kubernetes service portals */
4649 210K DOCKER-USER all -- any any anywhere anywhere
4649 210K DOCKER-ISOLATION-STAGE-1 all -- any any anywhere anywhere
0 0 ACCEPT all -- any docker0 anywhere anywhere ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- any docker0 anywhere anywhere
0 0 ACCEPT all -- docker0 !docker0 anywhere anywhere
0 0 ACCEPT all -- docker0 docker0 anywhere anywhere
添加如下的 iptables 规则。因为针对的是集群整个 pod 的 CIDR,所以每个节点都是一样的。
sudo iptables -A FORWARD -s 10.244.0.0/16 -j ACCEPT
sudo iptables -A FORWARD -d 10.244.0.0/16 -j ACCEPT
一开始在实验过程中并没有设置这个 iptables 规则,所以网络一直不通。因此在需要 linux 执行转发的时候,除了打开 ip_forward,可能还需要在对应的 forward 链添加规则。
查看添加之后的 FORWARD 链,最关键的是后面两条。
[decent@HostGW-Master ~]$ sudo iptables -v -L FORWARD
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
4854 220K KUBE-FORWARD all -- any any anywhere anywhere /* kubernetes forwarding rules */
4854 220K KUBE-SERVICES all -- any any anywhere anywhere ctstate NEW /* kubernetes service portals */
4854 220K DOCKER-USER all -- any any anywhere anywhere
4854 220K DOCKER-ISOLATION-STAGE-1 all -- any any anywhere anywhere
0 0 ACCEPT all -- any docker0 anywhere anywhere ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- any docker0 anywhere anywhere
0 0 ACCEPT all -- docker0 !docker0 anywhere anywhere
0 0 ACCEPT all -- docker0 docker0 anywhere anywhere
73 3285 ACCEPT all -- any any 10.244.0.0/16 anywhere
0 0 ACCEPT all -- any any anywhere 10.244.0.0/16
测试
创建几个 pod,在 master 节点 ping worker 节点的 pod,看看能不能 ping 通,证明方案是可行的。
[decent@HostGW-Master ~]$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-574b87c764-fffsl 1/1 Running 0 5s 10.244.1.2 hostgw-node <none> <none>
nginx-deployment-574b87c764-px7g5 1/1 Running 0 5s 10.244.0.4 hostgw-master <none> <none>
nginx-deployment-574b87c764-xwbpp 1/1 Running 0 5s 10.244.1.3 hostgw-node <none> <none>
[decent@HostGW-Master ~]$ ping 10.244.1.3
PING 10.244.1.3 (10.244.1.3) 56(84) bytes of data.
64 bytes from 10.244.1.3: icmp_seq=1 ttl=63 time=0.509 ms
64 bytes from 10.244.1.3: icmp_seq=2 ttl=63 time=0.361 ms
^C
--- 10.244.1.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.361/0.435/0.509/0.074 ms
# 访问也是成功的
[decent@HostGW-Master ~]$ curl 10.244.1.2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
其他
用到的一些 iptables 命令,查看 iptables (某个表)的某个链,并显示行号,并可以根据这些行号来删除规则。
sudo iptables -L FORWARD -n -v --line-numbers
# 删除 FORWARD 链的第三条规则
sudo iptables -D FORWARD 3
添加、查看网桥,具体看《Kubernetes 网络权威指南》
brctl addbr br0
# 或者
ip link add name br0 type bridge
ip link set br0 up
使用 traceroute
查看 ip 路由过程,https://serverfault.com/questions/334029/what-does-mean-when-traceroute
[decent@HostGW-Master ~]$ traceroute 10.42.1.97
traceroute to 10.42.1.97 (10.42.1.97), 30 hops max, 60 byte packets
1 192.168.31.202 (192.168.31.202) 0.254 ms 0.260 ms 0.218 ms
2 * * *
3 * * *
4 * * *
5 * * *
6 * * *
7 *^C
从网卡 ping IP。
[decent@HostGW-Node tcpdump]$ ping -c 1 -I ens33 10.244.1.2
PING 10.244.1.2 (10.244.1.2) from 192.168.31.202 ens33: 56(84) bytes of data.
--- 10.244.1.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms