二进制安装的k8s使用cni插件

大番茄 2020年03月07日 2,305次浏览

先来说明一下flannel有两种工作方式。

  1. 二进制安装,flannel是独立工作的,使用etcd存储网段。
  2. 以pod运行,flannel集成到k8s集群里面,直接从集群获取网段数据。二进制方式应该也可以直接连接k8s集群,只是没有试成功过。

第一种:
docker通过指定flannel在节点生成的网段配置,确定docker网桥的地址,然后以docker自己的方式给容器分配ip.

第二种:
这种方式flannel也会在节点生成网段配置,但是docker却不能使用,因为需要重启docker, flannel总是在docker启动以后才开始运行,有很多不确定性。
所以需要使用cni的网络。

cni网络是Kubernetes的网络模型, 这种方式下,nodes里会包含ip信息。
如我这里查看k8s-node01的信息。
kubectl get nodes/k8s-node1 -o yaml
其中的spec段就包含ip信息。

spec:
  podCIDR: 10.5.2.0/24
  podCIDRs:
  - 10.5.2.0/24

这样对于查找节点的容器网段也方便。

CNI(Container Network Interface): 是一种容器网络接口规范。与Docker的CNM(Container Network Model)的具体区别也说不清。

cni运行方式就是在创建容器的时候,先创建容器使用的网络命名空间,然后调用cni插件为命名空间做网络配置,最后启动容器内的进程。

kubelet通过cni配置找到对应的cni插件,由插件配置容器网络。
cni插件获取对应服务提供的信息,然后配置网络。如: flannel插件会通过flannel服务获取容器应该配置什么地址。

flannel的方式我觉得就是在主机上生成包含网络配置的文件/var/run/flannel/subnet.env,然后插件去读取。

配置

除了指定的其他都与二进制安装中的一样。

1、kube-controller-manager添加参数

添加--allocate-node-cidrs--cluster-cidr
如:

--allocate-node-cidrs
--cluster-cidr=10.5.0.0/16

--allocate-node-cidrs: 允许给pod分配地址。
--cluster-cidr: 分配给集群中的可用pod范围。 之后的flannel就是使用的这个范围。

2、下载flannel的yaml文件

git主页:
https://github.com/coreos/flannel/tree/d893bcbfe6b04791054aea6c7569dea4080cc289
下载地址:
https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
或者:
https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml

3、各节点准备cni插件

插件从这里下载:
https://github.com/containernetworking/plugins/releases
包含了这些插件:

[root@k8s-node1 cni]# ls bin
bandwidth  bridge  dhcp  firewall  flannel  host-device  host-local  ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tuning  vlan
[root@k8s-node1 cni]#

里面没有calico插件,calico插件是在安装calico的时候自动添加的。
我们这里使用flannel,已经包含了。

[root@k8s-node1 k8s]# ls
bin  cni  conf  logs  ssl
[root@k8s-node1 k8s]# ls bin
kubelet  kube-proxy
[root@k8s-node1 k8s]# ls cni
bandwidth  bridge  dhcp  firewall  flannel  host-device  host-local  ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tuning  vlan

4、各节点准备cni配置

上面下载的flannel yaml文件里已经包含了cni配置。这个不用动,只是需要知道有这个配置。

   {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }

5、修改kubelet启动参数

有三个参数需要指定:

--network-plugin   # 调用的网络插件。如: --network-plugin=cni。
--cni-bin-dir      # cni插件目录
--cni-conf-dir     # cni配置目录,kubelet通过这个配置确定使用什么cni插件。然后在--cni-bin-dir目录里查找需要的插件。

其中--cni-conf-dir默认是/etc/cni/net.d目录,flannel.yaml包含的cni配置也是写到这个地方,所以默认就可以。kubelet会去这个目录查找配置文件,来确定使用什么cni插件。
这个目录如果原来有其它插件的配置,最好清理一下,最简单的就是yaml文件里面放个清理目录的初始化容器。

注意,如果要手动创建cni配置文件,文件的扩展名需要是conflist,自动生成的也是这个扩展名。

--cni-bin-dir 默认位置是/opt/cni/bin/,这里为了方便传到所有机器,修改一下路径,放到kubelet的所在的目录里。

最简单的也是把cni插件做成镜像,放到yaml的初始化容器里,最后做个例子。

注意:如果以后会用到calico就不要修改这个参数了,因为calico会自动在默认目录里生成calico的cni插件,如果改了的话还要再改回来。

最终添加的参数:

--network-plugin=cni
--cni-bin-dir=/opt/k8s/cni

重启kubelet以后,因为节点暂时还没有cni配置文件,kubelet会报类似这种错误。

==> kubelet.k8s-node1.root.log.WARNING.20200307-172910.22981 <==
W0307 17:31:00.940854   22981 cni.go:237] Unable to update cni config: no networks found in /etc/cni/net.d
E0307 17:31:01.509817   22981 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized

==> kubelet.WARNING <==
W0307 17:31:00.940854   22981 cni.go:237] Unable to update cni config: no networks found in /etc/cni/net.d
E0307 17:31:01.509817   22981 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized

查看节点是notready状态。

[root@k8s-master ~]# kubectl get nodes
NAME        STATUS     ROLES    AGE     VERSION
k8s-node1   NotReady   <none>   5h19m   v1.16.7
k8s-node2   NotReady   <none>   4h28m   v1.16.7

不过这个没关系,这只是因为cni网络出错导致的,kubelet其他功能没有问题。
而且flannel.yaml里面有污点容忍的配置,可以正常应用到节点。

6、安装flannel

flannel.yaml 应用以后会在节点产生两个文件
/etc/cni/net.d/10-flannel.conflist
cni配置文件,这个是yaml文件里的初始化容器放到主机上的。

/var/run/flannel/subnet.env
flannel跟集群交互获取到节点可以使用的网段放到这个文件里,这个文件也是flannel插件需要用到的,flannel插件默认也是会去这个路径找这个文件,来确定分配给容器的ip。插件也会检查cni网桥是否存在,没有就创建,并设置对应的ip。

flannel.yaml文件里的镜像是海外的,可能会下载失败,可以改成registry.cn-beijing.aliyuncs.com/yangxingxing/flannel,我下载下来然后传到阿里云的。如:

image: registry.cn-beijing.aliyuncs.com/yangxingxing/flannel:v0.11.0-amd64

文件中还有一个地方:

    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }

网络模式可以看情况修改, 集群pod地址范围也是会输出在/var/run/flannel/subnet.env文件里,如果文件里的配置会用到,可以改一下。 我这里暂时先不改, 看看效果再说。

应用文件

kubectl apply -f flannel.yml

稍等一下,kubelet日志:

==> kubelet.k8s-node1.root.log.INFO.20200307-180116.30451 <==
I0307 18:02:12.148070   30451 cni.go:206] Using CNI configuration file /etc/cni/net.d/10-flannel.conflist
I0307 18:02:12.667479   30451 kubelet.go:2184] Container runtime status: Runtime Conditions: RuntimeReady=true reason: message:, NetworkReady=true reason: message:

节点状态:

[root@k8s-master ~]# kubectl get nodes
NAME        STATUS   ROLES    AGE     VERSION
k8s-node1   Ready    <none>   5h54m   v1.16.7
k8s-node2   Ready    <none>   5h3m    v1.16.7

看一下/var/run/flannel/subnet.env文件:

[root@k8s-node1 logs]# cat /var/run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.5.0.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=true

就是这样子,FLANNEL_SUBNET网段就是从集群获取的,因为上面controller-manager的参数指定的是10.5.0.0/16,分到当前节点是10.5.0.*/24, 再起一个节点可能就是10.5.1.*/24

FLANNEL_NETWORK 这个网段会添加iptables的规则,如:
POSTROUTING:

2        0     0 RETURN     all  --  *      *       10.244.0.0/16        10.244.0.0/16       
3        0     0 MASQUERADE  all  --  *      *       10.244.0.0/16       !224.0.0.0/4         
4        0     0 RETURN     all  --  *      *      !10.244.0.0/16        10.5.0.0/24         
5        0     0 MASQUERADE  all  --  *      *      !10.244.0.0/16        10.244.0.0/16 

FORWARD:

num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 ACCEPT     all  --  *      *       10.244.0.0/16        0.0.0.0/0           
2        0     0 ACCEPT     all  --  *      *       0.0.0.0/0            10.244.0.0/16 

需要在flannel.yaml文件里改一下,如:

   {
      "Network": "10.5.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }

然后删除flannel在重建就可以了。

7、最后

节点多了一个网卡:

7: cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 22:2d:99:b2:dc:46 brd ff:ff:ff:ff:ff:ff
    inet 10.5.0.1/24 brd 10.5.0.255 scope global cni0
       valid_lft forever preferred_lft forever
    inet6 fe80::202d:99ff:feb2:dc46/64 scope link
       valid_lft forever preferred_lft forever

如果没有,就创建一下pod或deployment之类的,让他在节点上跑一下。
原因可能是kubelet只有在创建容器的时候才会调用cni插件。
cni0docker0一样都是bridge类型的网卡。

插件为容器配置网络环境,veth peer应该也是它挂到这个网卡的。
而非cni的模式是由docker来完成的。


看这个:

[root@k8s-master ~]# kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE    IP           NODE        NOMINATED NODE   READINESS GATES
test1-788f856d48-5hzkq   1/1     Running   0          58s    10.5.0.6     k8s-node1   <none>           <none>
test1-788f856d48-f42fx   1/1     Running   0          37s    10.5.0.8     k8s-node1   <none>           <none>
test1-788f856d48-tfkpw   1/1     Running   0          37s    10.5.0.7     k8s-node1   <none>           <none>
test1-788f856d48-vvvn7   1/1     Running   0          37s    10.5.1.3     k8s-node2   <none>           <none>
test1-788f856d48-wfvdg   1/1     Running   0          37s    10.5.1.2     k8s-node2   <none>           <none>
test2-78597966d7-jtjbm   1/1     Running   0          123m   172.17.0.5   k8s-node2   <none>           <none>
test2-78597966d7-k2h5n   1/1     Running   0          154m   172.17.0.4   k8s-node2   <none>           <none>
test2-78597966d7-n6zrc   1/1     Running   0          154m   172.17.0.2   k8s-node2   <none>           <none>
test2-78597966d7-r4hmd   1/1     Running   0          123m   172.17.0.6   k8s-node2   <none>           <none>
test2-78597966d7-zx6gv   1/1     Running   0          154m   172.17.0.3   k8s-node2   <none>           <none>

test2是之前创建的, 都在node2是因为node1做实验停了段时间。

[root@k8s-node2 logs]# brctl show
bridge name	bridge id		STP enabled	interfaces
cni0		8000.6af0a81db5e0	no		veth8fe78c44
							vethd9ea07f0
docker0		8000.02421e8c8c03	no		veth0dfed0a
							veth134a1c3
							veth268742c
							veth3cfdc7b
							veth606a09b
[root@k8s-node2 logs]#

test2还是挂载docker0上的, 在本节点运行也正常,只是不能跨节点,这个跟cni插件没关系。只是因为flannel配置的网段原因。

总之flannel cni插件与flannel这是两个不同的东西,只是cni需要flannel提供一些信息。 而flannel的主业就是解决跨节点问题。就算flannel没了,cni插件还是正常配置容器网络。

把cni插件集成到yml

后边加的,跟上面的文件版本不一样。

1、作镜像

因为不想破坏flannel自己的镜像。所以就找个小镜像,把flannel的cni插件放进去。
反正就只是一个承载cni插件的镜像,所以Dockerfile非常简单, 这里选择了busybox做基础镜像,因为够小。只需要插件中的flannelportmap

我直接把插件的压缩包放里面了,因为简单测试了一下发现好几个插件都需要。
需要各种插件初始化容器的网络环境。

[root@docker1 dockerfile]# ls
cni.tgz  Dockerfile
[root@docker1 dockerfile]# cat Dockerfile 
FROM busybox
COPY cni.tgz /cni/

最终生成的镜像, tag是cni插件的版本号。

harbor.atest.pub/k8s/flannel-cni:v0.9.0

2、修改flannel.yaml文件

就两个地方,一个是添加volume。 一个是添加初始化容器。
1、
image.png
2、
image.png