前言
网上关于kubernetes网络部署的方案非常多,那为什么还要自己写这么一篇教程呢?因为这篇文章中将介绍一种较为少见的部署方式,使用kuberouter作为kubernetes系统网络组件,直接替换掉kubeproxy、flannel/calico等网络组件。使用这种部署方式的目的,是替换kube-proxy,减少iptables依赖,同时还能提供网络服务。
替代分析
Kube-router替代kube-proxy
kube-proxy的作用主要是用于监听API server中 service 和 endpoint的变化情况,并通过iptables等来为服务配置负载均衡(仅支持TCP和UDP)。kube-proxy 可以直接运行在物理机上,也可以以 static pod 或者 daemonset 的方式运行。
Kube-proxy主要有以下技术特点:
-
底层默认使用iptables进行流量的转发
- 通过监听api server服务中的对象变化,实现service发现功能。
而kuberouter采用了基于相同技术但增加了更多负载均衡策略的IPVS来实现流量转发,服务发现的实现与kube-proxy一致。因此可以直接替代kube-proxy。
Kube-router代替flannel/Calico等网络组件
kubernetes 对网络的要求是:容器之间(包括同一台主机上的容器,和不同主机的容器)可以互相通信,容器和集群中所有的节点也能直接通信。
在kube-router出现之前,kubernetes长期以来使用第三方模块来负责网络模块配置,其中flannel是其中较为常用的一个,以flannel为例。
flannel在集群中的功能主要有以下两点:
能够给每个 Node 分配互不冲突的网段,意味着集群内每个容器有着互不冲突的 IP 地址;(指定 docker 启动参数的方式指定网段)
能够建立一个覆盖网络,通过这个覆盖网络,能将数据包原封不动传递到目标容器内。(这里传递通常是 UDP 数据包)
在实际运行过程中,每个节点需要部署一个flannel服务,部署成功后会生成一个flannel.0的虚拟设备,同时会运行flanneld的进程,用以链接flannel.0和物理网卡。传输过程中的数据包通过路由规则判断,如果是本网段的访问,就会走 docker0 网桥;如果是跨网段,也就是要跨宿主机访问,就会将数据发往 flannel0。flanneld将数据包封装成底层通信包,协议包括UDP、Vxlan等,这里往往用的是UDP,这个UDP 的packet 会根据 etcd 存放的路由表(一般会缓存到主机内存里)找到目的容器 IP 所承载的宿主机 IP,不借助第三方路由设备的前提下,数据包发送到了宿主机上。此时目标宿主机的 flanneld 进程会对 UDP 包进行解包操作,转化成数据包原本的协议,最终根据主机内部路由流入到目标容器中。
完成这一系列动作有两个核心的技术点:
flannel分配虚拟网卡是基于TUN/TAP虚拟网卡技术。
flannel是三层设备,利用backend对节点内的二层的数据包封包成UDP,再发送到目的节点的flanneld进行解包。
在kuberouter中,这种网络管理机制由CNI(容器网络接口)+BGP协议实现的。
Kube-router不像flannel一样采用子网管理的方式,而是利用kube-conroller-manager来做子网分配,每一个节点会根据cluster CIDR被分配到集群中独一无二的子网ip。Kube-router运行在集群中每个节点运行IBGP,同时自动将所有节点组成网络。所有的集群中的节点在集群中将组成可配置的私有自治网络。Kube-router将会广播pod CIDR,并且保存从同一个主机命名空间学习到的路由。
子网IP的管理是由CNI的brige插件配合IPAM的host-local插件完成。
部署kube-router
依赖
-
kube-router 能够访问apiserver
这边提供kubeconfig方式
apiVersion: v1
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://k8s.foxchan.com
name: default
contexts:
- context:
cluster: default
namespace: default
user: default
name: default
current-context: default
users:
- name: default
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
- controller-manager必要配置参数
--cluster-cidr=10.244.0.0/16 \
--allocate-node-cidrs=true \
- 直接在主机运行需要有ipset命令
- 以daemonseset 运行需要kube-apiserver 和kubelet 同时开启--allow-privileged=true
- 如果选择pod-to-pod 网络模式,需要部署CNI插件
官方给的简单示例
wget -O /etc/cni/net.d/10-kuberouter.conf https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/cni/10-kuberouter.conf
参数说明
- 使用iptables实现网络策略限制. --run-firewall参数,可透传源IP。
- 通过bgp实现路由策略.--run-router 参数
- 通过lvs实现代理策略。 --run-service-proxy
--run-firewall, --run-router, --run-service-proxy可以有选择地只启用kube-router所需的功能
- 只提供入口防火墙:--run-firewall=true --run-service-proxy=false --run-router=false
- 仅仅替换kube-proxy: --run-service-proxy=true --run-firewall=false --run-router=false
查看现有集群nodeCIDR划分
kubectl get nodes -o json | jq '.items[] | .spec'
命令行汉化
Usage of ./kube-router:
--advertise-cluster-ip 将该服务的集群IP添加到RIB,以便通告给BGP peers.
--advertise-external-ip 将服务的外部IP添加到RIB,以便将其通告给BGP peers.
--cleanup-config 清理iptables规则,ipvs,ipset配置并退出.
--cluster-asn uint 集群节点运行iBGP的ASN编号.
--cluster-cidr string 群集中的CIDR范围。它被用来识别pods的范围.
--config-sync-period duration apiserver配置同步之间的延迟(例如“5s”,“1m”)。必须大于0.(默认1m0s)
--enable-overlay 当enable-overlay设置为true时,IP-in-IP隧道将用于跨不同子网中节点的pod-pod联网。如果设置为false,则不使用隧道,并且路由基础架构预计为不同子网中的节点之间的pod-pod联网路由流量(默认值为true)
--enable-pod-egress 从Pod到群集外的SNAT流量。 (默认为true)
--hairpin-mode 为每个服务端点添加iptable规则以支持流量管控.
-h, --help 打印使用信息.
--hostname-override string 覆盖节点的NodeName。如果kube-router无法自动确定您的NodeName,请设置此项.
--iptables-sync-period duration iptables规则同步之间的延迟(例如'5s','1m')。必须大于0.(默认1m0s)
--ipvs-sync-period duration ipvs config同步之间的延迟(例如'5s','1m','2h22m')。必须大于0.(默认1m0s)
--kubeconfig string 具有授权信息的kubeconfig文件的路径(主位置由主标志设置)。
--masquerade-all SNAT所有流量到群集IP /节点端口。
--master string Kubernetes API服务器的地址(覆盖kubeconfig中的任何值)。
--nodeport-bindon-all-ip 对于NodePort类型的服务,创建监听节点的所有IP的IPVS服务.
--nodes-full-mesh 集群中的每个节点都将建立与其他节点的BGP对等关系。 (默认为true)
--peer-router-asns uintSlice 集群节点将向其通告集群ip和节点的pid cidr的BGP peers的ASN编号。 (默认[])
--peer-router-ips ipSlice 所有节点将对等的外部路由器的IP地址,并通告集群ip和pod cidr。 (默认[])
--peer-router-passwords stringSlice 用“--peer-router-ips”定义的BGP peers进行认证的密码。
--routes-sync-period duration 路线更新与广播之间的延迟(例如“5s”,“1m”,“2h22m”)。必须大于0.(默认1m0s)
--run-firewall 启用网络策略 - 设置iptables为pod提供入口防火墙。 (默认为true)
--run-router 启用Pod网络 - 通过iBGP发布并学习到Pod的路由。 (默认为true)
--run-service-proxy 启用服务代理 - 为Kubernetes服务设置IPVS。 (默认为true)```
安装
集群安装时没有选择kube-proxy插件
从k8s1.14开始,可以在kubeadm init的时候指定组件是否安装
先要创建kube-proxy configmap,然后修改yaml
kubectl create configmap kube-router --namespace=kube-system --from-file=kubeconfig
wget -o kubeadm-kuberouter-all-features.yaml https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter-all-features.yaml
修改yaml,去访问之前生成的kubeconfig
...
- name: cni-conf-dir
mountPath: /etc/cni/net.d
- name: kubeconfig
mountPath: /var/lib/kube-router
readOnly: true
- name: xtables-lock
mountPath: /run/xtables.lock
readOnly: false
...
volumes:
- name: lib-modules
hostPath:
path: /lib/modules
- name: cni-conf-dir
hostPath:
path: /etc/cni/net.d
- name: kube-router-cfg
configMap:
name: kube-router-cfg
- name: kubeconfig
configMap:
name: kube-router
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
...
集群已经有kube-proxy
只是进行替换
kubectl apply -f https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter-all-features.yaml
安装完成后,清理iptables
kubectl -n kube-system delete ds kube-proxy
docker run --privileged -v /lib/modules:/lib/modules --net=host k8s.gcr.io/kube-proxy-amd64:v1.17.9 kube-proxy --cleanup
使用
负载均衡调度算法
Kube-router使用LVS作为服务代理。 LVS支持丰富的调度算法。您可以为该服务添加注释以选择一个调度算法。当一个服务没有注释时,默认情况下选择“轮询”调度策略
For least connection scheduling use:
kubectl annotate service my-service "kube-router.io/service.scheduler=lc"
For round-robin scheduling use:
kubectl annotate service my-service "kube-router.io/service.scheduler=rr"
For source hashing scheduling use:
kubectl annotate service my-service "kube-router.io/service.scheduler=sh"
For destination hashing scheduling use:
kubectl annotate service my-service "kube-router.io/service.scheduler=dh"
Direct server return
请阅读以下博客,了解如何结合使用DSR和“–advertise-external-ip”构建高度可扩展和可用的入口。 https://cloudnativelabs.github.io/post/2017-11-01-kube-high-available-ingress/
您可以为每个服务启用DSR(直接服务器返回)功能。当启用的服务端点将直接响应客户端通过签署服务代理。启用DSR时,Kube-router将使用LVS的隧道模式来实现此功能。
要启用DSR,您需要使用kube-router.io/service.dsr = tunnel注释来注释服务。例如,
kubectl annotate service my-service "kube-router.io/service.dsr=tunnel"
在当前的实现中,当在服务上应用注释时,DSR将仅适用于外部IP。
此外,当使用DSR时,当前的实现不支持端口重新映射。所以你需要使用相同的端口和目标端口的服务
你需要在kube-router守护进程清单中启用hostIPC:true和hostPID:true。并且必须将主路径/var/run/docker.sock设置为kube-router的一个volumemount。
上述更改需要kube-router输入pod namespace,并在pod中创建ipip隧道,并将外部IP分配给VIP。
对于示例清单,请查看启用DSR功能的manifest
暴露服务
- svc clusterip
$ kubectl expose nginx --target-port=80 --port=80
$ kubectl get svc nginx -o template --template='{{.spec.clusterIP}}'
10.254.116.179
在每台机器上查看lvs条目
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr persistent 10800
-> 172.26.6.1:6443 Masq 1 0 0
TCP 10.254.116.179:80 rr 10800
-> 10.254.11.2:80 Masq 1 0 0
发现本机SVCIP代理后端真实podip,使用rr算法,通过ip addr s可以看到每添加一个服务node节点上面的kube-dummy-if网卡就会增加一个虚IP
- svc session-affinity
kubectl delete svc nginx
kubectl expose deploy nginx --target-port=80 --port=80 --session-affinity=ClientIP
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr persistent 10800
-> 172.26.6.1:6443 Masq 1 0 0
TCP 10.254.191.234:80 rr persistent 10800
-> 10.254.11.2:80 Masq 1 0 0
我们可以看到 多个persistent,既lvs里面的持久链接
- svc NodePort
kubectl delete svc nginx
kubectl expose deploy nginx --target-port=80 --port=80 --type=NodePort
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.26.6.3:31117 rr
-> 10.254.11.2:80 Masq 1 0 0
TCP 10.254.0.1:443 rr persistent 10800
-> 172.26.6.1:6443 Masq 1 0 0
TCP 10.254.102.188:80 rr
-> 10.254.11.2:80 Masq 1 0 0
可以看到不仅有虚拟IP条目,还多了对应主机的lvs条目
- 更改算法
kubectl annotate service nginx "kube-router.io/service.scheduler=dh"
network policy
kubectl annotate ns prod "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}"
测试可以看到其他命名空间ping不通该命名空间
查看路由表
ip route s
查看bgp新信息
# kubectl --namespace=kube-system exec -it kube-router-pk7fs /bin/bash
# gobgp neighbor -u 172.26.6.3 #从哪些IP获得更新
Peer AS Up/Down State |#Received Accepted
172.26.6.2 64512 01:03:03 Establ | 1 1
# gobgp global rib -u 172.26.6.3 #global rib相当于路由表
Network Next Hop AS_PATH Age Attrs
*> 10.254.0.0/24 172.26.6.2 01:03:24 [{Origin: i} {LocalPref: 100}]
*> 10.254.2.0/24 172.26.6.3 00:00:32 [{Origin: i}]