前言
Docker网络模式
在讨论Kubernetes网络之前,让我们先来看一下Docker网络。Docker采用插件化的网络模式,默认提供bridge、host、none、overlay、maclan和Network plugins这几种网络模式,运行容器时可以通过–network参数设置具体使用那一种模式。
bridge:这是Docker默认的网络驱动,此模式会为每一个容器分配Network Namespace和设置IP等,并将容器连接到一个虚拟网桥上。如果未指定网络驱动,这默认使用此驱动。
host:此网络驱动直接使用宿主机的网络。
none:此驱动不构造网络环境。采用了none 网络驱动,那么就只能使用loopback网络设备,容器只能使用127.0.0.1的本机网络。
overlay:此网络驱动可以使多个Docker daemons连接在一起,并能够使用swarm服务之间进行通讯。也可以使用overlay网络进行swarm服务和容器之间、容器之间进行通讯,
macvlan:此网络允许为容器指定一个MAC地址,允许容器作为网络中的物理设备,这样Docker daemon就可以通过MAC地址进行访问的路由。对于希望直接连接网络网络的遗留应用,这种网络驱动有时可能是最好的选择。
Network plugins:可以安装和使用第三方的网络插件。可以在Docker Store或第三方供应商处获取这些插件。
在默认情况,Docker使用bridge网络模式,bridge网络驱动的示意图如下,此文以bridge模式对Docker的网络进行说明。
安装Docker时,创建一个名为docke0的虚拟网桥,虚拟网桥使用“10.0.0.0 -10.255.255.255 “、”172.16.0.0-172.31.255.255″和“192.168.0.0——192.168.255.255”这三个私有网络的地址范围。
通过 ifconfig 命令可以查看docker0网桥的信息
通过 docker network inspect bridge 可以查看网桥的子网网络范围和网关
运行容器时,在宿主机上创建虚拟网卡veth pair设备,veth pair设备是成对出现的,从而组成一个数据通道,数据从一个设备进入,就会从另一个设备出来。将veth pair设备的一端放在新创建的容器中,命名为eth0;另一端放在宿主机的docker0中,以veth为前缀的名字命名。通过 brctl show 命令查看放在docker0中的veth pair设备
bridge的docker0是虚拟出来的网桥,因此无法被外部的网络访问。因此需要在运行容器时通过-p和-P参数对将容器的端口映射到宿主机的端口。实际上Docker是采用 NAT的 方式,将容器内部的服务监听端口与宿主机的某一个端口port 进行绑定,使得宿主机外部可以将网络报文发送至容器。
Kubernetes网络模式
Kubernetes网络需要解决下面的4个问题:
集群内:
容器与容器之间的通信
Pod和Pod之间的通信
Pod和服务之间的通信
集群外:
外部应用与服务之间的通信
因此,Kubernetes假设Pod之间能够进行通讯,这些Pod可能部署在不同的宿主机上。每一个Pod都拥有自己的IP地址,因此能够将Pod看作为物理主机或者虚拟机,从而能实现端口设置、命名、服务发现、负载均衡、应用配置和迁移。为了满足上述需求,则需要通过集群网络来实现。
在本文主要分析容器与容器之间,以及Pod和Pod之间的通信;Pod和服务之间,以及外部应用与服务之间的通信请参考Kubernetes-核心资源之Service 、Kubernetes-核心资源之Ingress。
1 )同一个Pod中容器之间的通信
这种场景对于Kubernetes来说没有任何问题,根据Kubernetes的架构设计。Kubernetes创建Pod时,首先会创建一个pause容器,为Pod指派一个唯一的IP地址。然后,以pause的网络命名空间为基础,创建同一个Pod内的其它容器(–net=container:xxx)。因此,同一个Pod内的所有容器就会共享同一个网络命名空间,在同一个Pod之间的容器可以直接使用localhost进行通信。
2) 不同Pod中容器之间的通信
对于此场景,情况现对比较复杂一些,这就需要解决Pod间的通信问题。在Kubernetes通过flannel、calic等网络插件解决Pod间的通信问题。本文以flannel为例说明在Kubernetes中网络模型。
Flannel Network
flannel是kubernetes默认提供网络插件。Flannel是由CoreOs团队开发社交的网络工具,CoreOS团队采用L3 Overlay模式设计flannel, 规定宿主机下各个Pod属于同一个子网,不同宿主机下的Pod属于不同的子网。
实现原理
flannel会在每一个宿主机上运行名为flanneld代理,其负责为宿主机预先分配一个子网,并为Pod分配IP地址。Flannel使用Kubernetes或etcd来存储网络配置、分配的子网和主机公共IP等信息。数据包则通过VXLAN、UDP或host-gw这些类型的后端机制进行转发。
安装Flannel
~]# cd /opt/src/
src]# wget “https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz”
opt]# mkdir flannel-v0.11.0
src]# tar xf flannel-v0.11.0-linux-amd64.tar.gz -C /opt/flannel-v0.11.0
src]# cd /opt/
opt]# ln -s /opt/flannel-v0.11.0 flannel
拷贝证书到flannel目录
opt]# cd flannel
flannel]# mkdir cert
flannel]# cd cert/
cert]# ls
ca.pem client-key.pem client.pem
配置文件
cert]# cd …
flannel]# vim subnet.env
flannel]# cat subnet.env
FLANNEL_NETWORK=172.7.0.0/16
FLANNEL_SUBNET=172.7.21.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=false
启动脚本
flannel]# vi flanneld.sh
flannel]# cat flanneld.sh
#!/bin/sh
./flanneld \
--public-ip=10.4.7.21 \
--etcd-endpoints=https://10.4.7.12:2379,https://10.4.7.21:2379,https://10.4.7.22:2379 \
--etcd-keyfile=./cert/client-key.pem \
--etcd-certfile=./cert/client.pem \
--etcd-cafile=./cert/ca.pem \
--iface=ens33 \
--subnet-file=./subnet.env \
--healthz-port=2401
flannel]# chmod u+x flanneld.sh
host-gw模式
flannel]# cd /opt/etcd/
etcd]# ./etcdctl set /coreos.com/network/config ‘{“Network”: “172.7.0.0/16”, “Backend”: {“Type”: “host-gw”}}’
etcd]# ./etcdctl get /coreos.com/network/config
启动flannel
etcd]# vim /etc/supervisord.d/flannel.ini
[program:flanneld-7-21]
command=/opt/flannel/flanneld.sh ; the program (relative uses PATH, can take args)
numprocs=1 ; number of processes copies to start (def 1)
directory=/opt/flannel ; directory to cwd to before exec (def no cwd)
autostart=true ; start at supervisord start (default: true)
autorestart=true ; retstart at unexpected quit (default: true)
startsecs=30 ; number of secs prog must stay running (def. 1)
startretries=3 ; max # of serial start failures (default 3)
exitcodes=0,2 ; 'expected' exit codes for process (default 0,2)
stopsignal=QUIT ; signal used to kill process (default TERM)
stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
user=root ; setuid to this UNIX account to run the program
redirect_stderr=true ; redirect proc stderr to stdout (default false)
stdout_logfile=/data/logs/flanneld/flanneld.stdout.log ; stderr log path, NONE for none; default AUTO
stdout_logfile_maxbytes=64MB ; max # logfile bytes b4 rotation (default 50MB)
stdout_logfile_backups=4 ; # of stdout logfile backups (default 10)
stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
stdout_events_enabled=false ; emit events on stdout writes (default false)
etcd]# mkdir -p /data/logs/flanneld/
etcd]# supervisorctl update
etcd]# supervisorctl status
查看路由
flannel]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.4.7.254 0.0.0.0 UG 0 0 0 ens33
10.4.7.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 ens33
172.7.21.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
172.7.22.0 10.4.7.22 255.255.255.0 UG 0 0 0 ens33
host-gw是最简单的backend,原理非常简单,直接添加路由,将目的主机当做网关,直接路由原始封包。
此例中,我们从etcd中监听到一个EventAdded事件subnet为172.7.22.0/24被分配给主机Public IP 10.4.7.22,hostgw要做的工作就是在本主机上添加一条目的地址为172.7.22.0/24,网关地址为10.4.7.22,输出设备为上文中选择的集群间交互的网卡即可。对于EventRemoved事件,只需删除对应的路由。
因为没有了封包和拆包,host-gw的性能是最好的。不过host-gw 要求主机网络二层直接互联。
vxlan模式

数据传递过程
在源容器宿主机中的数据传递过程:
1)源容器向目标容器发送数据,数据首先发送给docker0网桥
2)docker0网桥接受到数据后,将其转交给flannel.1虚拟网卡处理
3)flannel.1接受到数据后,对数据进行封装,并发给宿主机的eth0
flannel.1收到数据后,flannelid会将数据包封装成二层以太包。
Ethernet Header的信息:
From:{源容器flannel.1虚拟网卡的MAC地址}
To:{目标容器flannel.1虚拟网卡的MAC地址}
4)对在flannel路由节点封装后的数据,进行再封装后,转发给目标容器Node的eth0
由于目前的数据包只是vxlan tunnel上的数据包,因此还不能在物理网络上进行传输。因此,需要将上述数据包再次进行封装,才能源容器节点传输到目标容器节点,这项工作在由linux内核来完成。
Ethernet Header的信息:
From:{源容器Node节点网卡的MAC地址}
To:{目标容器Node节点网卡的MAC地址}
IP Header的信息:
From:{源容器Node节点网卡的IP地址}
To:{目标容器Node节点网卡的IP地址}
通过此次封装,就可以通过物理网络发送数据包。
在目标容器宿主机中的数据传递过程:
5)目标容器宿主机的eth0接收到数据后,对数据包进行拆封,并转发给flannel.1虚拟网卡;
6)flannel.1 虚拟网卡接受到数据,将数据发送给docker0网桥;
7)最后,数据到达目标容器,完成容器之间的数据通信。
设置vxlan模式
~]# /opt/etcd/etcdctl ls coreos.com/network/config/
/coreos.com/network/config
~]# /opt/etcd/etcdctl rm coreos.com/network/config/
PrevNode.Value: {"Network": "172.7.0.0/16", "Backend": {"Type": "host-gw"}}
[root@node7-21 ~]# /opt/etcd/etcdctl ls coreos.com/network/subnets/
/coreos.com/network/subnets/172.7.21.0-24
/coreos.com/network/subnets/172.7.22.0-24
[root@node7-21 ~]# /opt/etcd/etcdctl rm /coreos.com/network/subnets/172.7.21.0-24
PrevNode.Value: {"PublicIP":"10.4.7.21","BackendType":"host-gw"}
[root@node7-21 ~]# /opt/etcd/etcdctl rm /coreos.com/network/subnets/172.7.22.0-24
Error: x509: certificate signed by unknown authority
[root@node7-22 ~]# /opt/etcd/etcdctl ls coreos.com/network/subnets
/coreos.com/network/subnets/172.7.22.0-24
[root@node7-22 ~]# /opt/etcd/etcdctl rm /coreos.com/network/subnets/172.7.22.0-24
PrevNode.Value: {"PublicIP":"10.4.7.22","BackendType":"host-gw"}
[root@node7-22 ~]# /opt/etcd/etcdctl ls coreos.com/network/subnets/
~]# etcdctl set /coreos.com/network/config ‘{“Network”: “172.16.0.0/16”, “Backend”: {“Type”: “VxLAN”}}’
{"Network": "172.7.0.0/16", "Backend": {"Type": "VxLAN"}}
~]# etcdctl get /coreos.com/network/config
{"Network": "172.7.0.0/16", "Backend": {"Type": "VxLAN"}}
重新启动flannel
flannel]# supervisorctl start flanneld-7-21
flannel]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.4.7.254 0.0.0.0 UG 0 0 0 ens33
10.4.7.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 ens33
172.7.21.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
172.7.22.0 172.7.22.0 255.255.255.0 UG 0 0 0 flannel.1
flannel]#kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-ds-lx7k9 1/1 Running 0 3d21h 172.7.22.2 node7-22.host.com <none> <none>
nginx-ds-xpr8b 1/1 Running 0 3d21h 172.7.21.2 node7-21.host.com <none> <none>
flannel]# ]# ping 172.7.22.2
PING 172.7.22.2 (172.7.22.2) 56(84) bytes of data.
64 bytes from 172.7.22.2: icmp_seq=5 ttl=63 time=1.07 ms
64 bytes from 172.7.22.2: icmp_seq=6 ttl=63 time=0.501 ms
直接路由模式
当node不在同一个物理网关下,走vxaln模型,在同一个网关下,走host-gw模型
]# etcdctl set /coreos.com/network/config ‘{“Network”: “172.7.0.0/16”, “Backend”: {“Type”: “VxLAN”,“Directrouting”: true}}’