我们知道,docker0网桥上的IP地址在Node网络上是看不到的。从一个Node到一个Node内的docker0是不通的,因为它不知道某个IP地址在哪里。如果能够让这些机器知道对端docker0地址在哪里,就可以让这些docker0互相通信了。这样,在所有Node上运行的Pod就都可以互相通信了。
我们可以通过部署MultiLayer Switch(MLS)来实现这一点,在MLS中配置每个docker0子网地址到Node地址的路由项,通过MLS将docker0的IP寻址定向到对应的Node上。
另外,我们可以将这些docker0和Node的匹配关系配置在Linux操作系统的路由项中,这样通信发起的Node就能够根据这些路由信息直接找到目标Pod所在的Node,将数据传输过去。
我们在每个Node的路由表中增加对方所有docker0的路由项。
例如,Pod1所在docker0网桥的IP子网是10.1.10.0,Node的地址为192.168.1.128;而Pod2所在docker0网桥的IP子网是10.1.20.0,Node的地址为192.168.1.129。
在Node1上用route add命令增加一条到Node2上docker0的静态路由规则:
route -net .20.0 netmask .255.0 gw .1.129
同样,在Node2上增加一条到Node1上docker0的静态路由规则:
route -net .10.0 netmask .255.0 gw .1.128
在Node1上通过ping命令验证到Node2上docker0的网络连通性。这里10.1.20.1为Node2上docker0网桥自身的IP地址.
可以看到,路由转发规则生效,Node1可以直接访问Node2上的docker0网桥,进一步就可以访问属于docker0网段的容器应用了。
在大规模集群中,在每个Node上都需要配置到其他docker0/Node的路由项,这会带来很大的工作量;并且在新增机器时,对所有Node都需要修改配置;在重启机器时,如果docker0的地址有变化,则也需要修改所有Node的配置,这显然是非常复杂的。
为了管理这些动态变化的docker0地址,动态地让其他Node都感知到它,还可以使用动态路由发现协议来同步这些变化。在运行动态路由发现协议代理的Node时,会将本机LOCAL路由表的IP地址通过组播协议发布出去,同时监听其他Node的组播包。通过这样的信息交换,Node上的路由规则就都能够相互学习。当然,路由发现协议本身还是很复杂的,感兴趣的话,可以查阅相关规范。在实现这些动态路由发现协议的开源软件中,常用的有Quagga(http://www.quagga.net)、Zebra等。下面简单介绍直接路由的操作过程。
首先,手工分配Docker bridge的地址,保证它们在不同的网段是不重叠的。建议最好不用Docker Daemon自动创建的docker0(因为我们不需要它的自动管理功能),而是单独建立一个bridge,给它配置规划好的IP地址,然后使用--bridge=XX来指定网桥。
然后,在每个节点上都运行Quagga。
完成这些操作后,我们很快就能得到一个Pod和Pod直接互相访问的环境了。由于路由发现能够被网络上的所有设备接收,所以如果网络上的路由器也能打开RIP协议选项,则能够学习到这些路由信息。通过这些路由器,我们甚至可以在非Node上使用Pod的IP地址直接访问Node上的Pod了。
除了在每台服务器上安装Quagga软件并启动,还可以使用Quagga容器运行(例如index.alauda.cn/georce/router)。在每个Node上下载该Docker镜像:
docker pull index.alauda.cn/georce/router
在运行Quagga容器之前,需要确保每个Node上docker0网桥的子网地址不能重叠,也不能与物理机所在的网络重叠,这需要网络管理员的仔细规划。
下面以3个Node为例,每个Node的docker0网桥的地址如下(前提是Node物理机的IP地址不是10.1.X.X地址段):
node1:node2:
node3:
在每个Node上启动Quagga容器。需要说明的是,Quagga需要以--privileged特权模式运行,并且指定--net=host,表示直接使用物理机的网络:
docker run -itd --namerouter --privileged --nethost index.alauda.cn/georce/router
启动成功后,各Node上的Quagga会相互学习来完成到其他机器的docker0路由规则的添加。
一段时间后,在Node1上使用route -n命令来查看路由表,可以看到Quagga自动添加了两条到Node2和到Node3上docker0的路由规则:
route -n
在Node2上查看路由表,可以看到自动添加了两条到Node1和Node3上docker0的路由规则:
route -n
至此,所有Node上的docker0就都可以互联互通了。
当然,聪明的你还会有新的疑问:这样做的话,由于每个Pod的地址都会被路由发现协议广播出去,会不会存在路由表过大的情况?实际上,路由表通常都会有高速缓存,查找速度会很快,不会对性能产生太大的影响。当然,如果你的集群容量在数千个Node以上,则仍然需要测试和评估路由表的效率问题。
Calico容器网络和网络策略实战
本节以Calico为例讲解kubernetes中CNI插件和网络策略的原理和应用。
Calico简介
Calico是一个基于BGP的纯三层的网络方案,与OpenStack、Kubernetes、AWS、GCE等云平台都能够良好地集成。Calico在每个计算节点都利用Linux Kernel实现了一个高效的vRouter来负责数据转发。每个vRouter都通过BGP1协议把在本节点上运行的容器的路由信息向整个Calico网络广播,并自动设置到达其他节点的路由转发规则。Calico保证所有容器之间的数据流量都是通过IP路由的方式完成互联互通的。Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者Overlay Network,没有额外的封包解包,能够节约CPU运算,提高网络效率。
Calico在小规模集群中可以直接互联,在大规模集群中可以通过额外的BGP route reflector来完成。
此外,Calico基于iptables还提供了丰富的网络策略,实现了Kubernetes的Network Policy策略,提供容器间网络可达性限制的功能。
Calico的系统架构如下图所示:
Calico的主要组件如下。
◎ Felix:Calico Agent,运行在每个Node上,负责为容器设置网络资源(IP地址、路由规则、iptables规则等),保证跨主机容器网络互通。
◎ etcd:Calico使用的后端存储。
◎ BGP Client:负责把Felix在各Node上设置的路由信息通过BGP协议广播到Calico网络。
◎ Route Reflector:通过一个或者多个BGP Route Reflector来完成大规模集群的分级路由分发。
◎ CalicoCtl:Calico命令行管理工具。
部署Calico服务
在Kubernetes中部署Calico的主要步骤如下。
(1)修改Kubernetes服务的启动参数,并重启服务。
◎ 设置Master上kube-apiserver服务的启动参数:--allow-privileged=true(因为calico-node需要以特权模式运行在各Node上)。
本例中的Kubernetes集群包括两个Node:k8s-node-1(IP地址为192.168.18.3)和k8s-node-2(IP地址为192.168.18.4)。
(2)创建Calico服务,主要包括calico-node和calico policy controller。需要创建的资源对象如下。
◎ 创建ConfigMap calico-config,包含Calico所需的配置参数。
◎ 创建Secret calico-etcd-secrets,用于使用TLS方式连接etcd。
◎ 在每个Node上都运行calico/node容器,部署为DaemonSet。
◎ 在每个Node上都安装Calico CNI二进制文件和网络配置参数(由install-cni容器完成)。
◎ 部署一个名为calico/kube-policy-controller的Deployment,以对接Kubernetes集群中为Pod设置的Network Policy。
从Calico官网下载Calico的YAML配置文件,下载地址为:
http://docs.projectcalico.org/v3.5/getting-started/kubernetes/installation/hosted/calico.yaml
该配置文件包括启动Calico所需的全部资源对象的定义,下面对它们逐个进行说明。
(1)Calico所需的配置,以ConfigMap对象进行创建:
kind: ConfigMapapiVersion: v1
metadata:
name: calico-config
namespace: kube-system
data:
etcd_endpoints:
etcd_ca:
etcd_cert:
etcd_key:
typha_service_name:
calico_backend:
veth_mtu:
cni_network_config: -
,
,
,
,
,
,
,
,
__CNI_MTU__,
,
,
,
,
true,
true
对主要参数说明如下。
◎ etcd_endpoints:Calico使用etcd来保存网络拓扑和状态,该参数指定etcd服务的地址。
◎ calico_backend:Calico的后端,默认为bird。
◎ cni_network_config:符合CNI规范的网络配置。其中type=calico表示kubelet将从/opt/cni/bin目录下搜索名为calico的可执行文件,并调用它来完成容器网络的设置。ipam中的type=calico-ipam表示kubelet将在/opt/cni/bin目录下搜索名为calico-ipam的可执行文件,用于完成容器IP地址的分配。
如果etcd服务配置了TLS安全认证,则还需指定相应的ca、cert、key等文件。
(2)访问etcd所需的secret,对于无TLS的etcd服务,将data设置为空即可:
apiVersion: v1kind: Secret
type: Opaque
metadata:
name: calico-etcd-secrets
namespace: kube-system
data:
(3)calico-node,以DaemonSet方式在每个Node上都运行一个calico-node服务和一个install-cni服务:
route -net .10.0 netmask .255.0 gw .1.128
0
在该Pod中包括如下两个容器。
◎ install-cni:在Node上安装CNI二进制文件到/opt/cni/bin目录下,并安装相应的网络配置文件到/etc/cni/net.d目录下,设置为initContainers并在运行完成后退出。
◎ calico-node:Calico服务程序,用于设置Pod的网络资源,保证Pod的网络与各Node互联互通。它还需要以hostNetwork模式运行,直接使用宿主机网络。
calico-node服务的主要参数如下:
◎ CALICO_IPV4POOL_CIDR:Calico IPAM的IP地址池,Pod的IP地址将从该池中进行分配。
◎ CALICO_IPV4POOL_IPIP:是否启用IPIP模式。启用IPIP模式时,Calico将在Node上创建一个名为tunl0的虚拟隧道。
◎ IP_AUTODETECTION_METHOD:获取Node IP地址的方式,默认使用第1个网络接口的IP地址,对于安装了多块网卡的Node,可以使用正则表达式选择正确的网卡,例如"interface=ens.*"表示选择名称以ens开头的网卡的IP地址。
◎ FELIX_IPV6SUPPORT:是否启用IPv6。
◎ FELIX_LOGSEVERITYSCREEN:日志级别。
◎ securityContext.privileged=true:以特权模式运行。
另外,如果启用RBAC权限控制,则可以设置ServiceAccount。
IP Pool可以使用两种模式:BGP或IPIP。使用IPIP模式时,设置CALICO_IPV4POOL_IPIP="always",不使用IPIP模式时,设置CALICO_IPV4POOL_IPIP="off",此时将使用BGP模式。
IPIP是一种将各Node的路由之间做一个tunnel,再把两个网络连接起来的模式,启用IPIP模式时,Calico将在各Node上创建一个名为tunl0的虚拟网络接口。
BGP模式则直接使用物理机作为虚拟路由器(vRouter),不再创建额外的tunnel。
(4)calico-kube-controllers容器,用于对接Kubernetes集群中为Pod设置的Network Policy:
route -net .10.0 netmask .255.0 gw .1.128
1
如果启用RBAC权限控制,则可以设置ServiceAccount。
用户在Kubernetes集群中设置了Pod的Network Policy之后,calico-kube-controllers就会自动通知各Node上的calico-node服务,在宿主机上设置相应的iptables规则,完成Pod间网络访问策略的设置。
route -net .10.0 netmask .255.0 gw .1.128
2
确保Calico的个服务正确运行,calico-node在正常运行之后,会根据CNI规范,在/etc/cni/net.d/目录下生成如下文件和目录,并在/opt/cni/bin/目录下安装二进制文件calico和calico-ipam,供kubelet调用。
◎ 10-calico.conf:符合CNI规范的网络配置,其中type=calico表示该插件的二进制文件名为calico。
◎ calico-kubeconfig:Calico所需的kubeconfig文件。
◎ calico-tls目录:以TLS方式连接etcd的相关文件。
查看k8s-node-1服务器的网络接口设置,可以看到一个新的名为tunl0的接口,并设置了网络地址为10.1.109.64/32。
查看k8s-node-2服务器的网络接口设置,同样可以看到一个新的名为tunl0的接口,网络地址为10.1.140.64/32。
这两个子网都是从calico-node设置的IP地址池(CALICO_IPV4POOL_CIDR="10.1.0.0/16")中进行分配的。同时,docker0对于Kubernetes设置Pod的IP地址将不再起作用。
查看两台主机的路由表。首先,查看k8s-node-1服务器的路由表,可以看到一条到k8s-node-2的私网10.1.140.64的路由转发规则:
route -net .10.0 netmask .255.0 gw .1.128
3
然后,查看k8s-node-2服务器的路由表,可以看到一条到k8s-node-1的私网10.1.109.64/26的路由转发规则:
route -net .10.0 netmask .255.0 gw .1.128
3
这样,通过Calico就完成了Node间容器网络的设置。在后续的Pod创建过程中,kubelet将通过CNI接口调用Calico进行Pod网络的设置,包括IP地址、路由规则、iptables规则等。
如果设置CALICO_IPV4POOL_IPIP="off",即不使用IPIP模式,则Calico将不会创建tunl0网络接口,路由规则直接使用物理机网卡作为路由器进行转发。
查看k8s-node-1服务器的路由表,可以看到一条到k8s-node-2的私网10.1.140.64的路由转发规则,将通过本机ens33网卡进行转发.
查看k8s-node-2服务器的路由表,可以看到一条到k8s-node-1的私网10.1.109.64/26的路由转发规则,将通过本机ens33网卡进行转发
3.Calico设置容器IP地址,跨主机容器网络连通性验证
下面创建几个Pod,验证Calico对它们的网络设置。以mysql和myweb为例。
mysql-rc.yaml
route -net .10.0 netmask .255.0 gw .1.128
5
myweb-rc.yaml
route -net .10.0 netmask .255.0 gw .1.128
6
route -net .10.0 netmask .255.0 gw .1.128
7
查看各Pod的IP地址,可以看到是通过Calico设置的以10.1开头的IP地址。
进入运行在k8s-node-2上的Pod,在容器内访问运行在k8s-node-1上的Pod“mysql”的IP地址。
route -net .10.0 netmask .255.0 gw .1.128
8
在容器内访问物理机k8s-node-1的IP地址192.168.18.3
route -net .10.0 netmask .255.0 gw .1.128
9
这说明跨主机容器间、容器与宿主机之间的网络都能互联互通了。
查看k8s-node-2物理机的网络接口和路由表,可以看到Calico为Pod“myweb-s86sk”新建了一个网络接口cali439924adc43,并为其设置了一条路由规则:
docker pull index.alauda.cn/georce/router
0
另外,Calico为该网络接口cali439924adc43设置了一系列iptables规则:
docker pull index.alauda.cn/georce/router
1
4.使用网络策略实现Pod间的访问策略
下面以一个提供服务的Nginx Pod为例,为两个客户端Pod设置不同的网络访问权限,允许包含Label“role=nginxclient”的Pod访问Nginx容器,不包含该Label的容器则拒绝访问。为了实现这个需求,需要通过以下步骤完成。
(1)创建Nginx Pod,并添加Label“app=nginx”:
docker pull index.alauda.cn/georce/router
2
(2)为Nginx设置网络策略,编辑文件networkpolicy-allow-nginxclient.yaml,内容如下。
docker pull index.alauda.cn/georce/router
3
(3)创建两个客户端Pod,一个包含Label“role=nginxclient”,另一个无此Label。分别进入各Pod,访问Nginx容器,验证网络策略的效果。
docker pull index.alauda.cn/georce/router
4
登录Pod“client1”,尝试连接Nginx容器的80端口:成功访问到Nginx的服务,说明NetworkPolicy生效。
登录Pod“client2”,尝试连接Nginx容器的80端口:访问超时,说明NetworkPolicy生效,对没有Label“role=nginxclient”的客户端Pod拒绝访问。
本例中的网络策略是由calico-kube-controllers具体实现的,calico-kube-controllers持续监听Kubernetes中NetworkPolicy的定义,与各Pod通过Label进行关联,将允许访问或拒绝访问的策略通知到各calico-node服务,最终calico-node完成对Pod间网络访问的设置,实现应用的网络隔离。
小结:
本节内容到此结束,谢谢大家的支持!
还没有评论,来说两句吧...