您的位置:首页 > 运维架构 > Docker

Docker实战(三)Docker 跨主机网络overlay、macvlan和flannel

2018-06-12 22:02 681 查看
版权声明:转载请注明出处! https://blog.csdn.net/wfs1994/article/details/80659232

跨主机网络概述

docker跨主机网络方案包括:

  • docker 原生的
    overlay
    macvlan
  • 第三方方案:常用的包括
    flannel
    、weave 和 calico。

docker通过

libnetwork
以及
CNM
将众多方案集成在一起。

libnetwork
是 docker 容器网络库,最核心的内容是其定义的
Container Network Model (CNM)
,这个模型对容器网络进行了抽象,由以下三类组件组成:

  • Sandbox
    :Sandbox 是容器的网络栈,包含容器的 interface、路由表和 DNS 设置。 Linux Network Namespace 是 Sandbox 的标准实现。Sandbox 可以包含来自不同 Network 的 Endpoint。
  • Endpoint
    :Endpoint 的作用是将 Sandbox 接入 Network。Endpoint 的典型实现是 veth pair。一个 Endpoint 只能属于一个网络,也只能属于一个 Sandbox。
  • Network
    :Network 包含一组 Endpoint,同一 Network 的 Endpoint 可以直接通信。Network的实现可以是 Linux Bridge、VLAN 等。

CNM示例:

libnetwork CNM 定义了 docker 容器的网络模型,按照该模型开发出的 driver 就能与 docker daemon 协同工作,实现容器网络。docker 原生的 driver 包括 none、bridge、overlay 和 macvlan;第三方 driver 包括 flannel、weave、calico 等。

接下来直接使用docker-machine创建的实验环境。在docker 主机 host1(192.168.20.201)和 host2(192.168.20.202)上实践各种跨主机网络方案,在 192.168.20.203上部署支持的组件。

overlay

Docker 跨节点容器网络互联,最通用的是使用 overlay 网络。
一代 Swarm 已经不再使用,它要求使用 overlay 网络前先准备好分布式键值库,比如

etcd
,
consul
zookeeper
。然后在每个节点的 Docker 引擎中,配置
--cluster-store
--cluster-advertise
参数。这样才可以互连。

在测试一代swarm网络时,配置各节点的Docker引擎时,加入

--cluster-store
--cluster-advertise
参数后,重启docker服务启动失败。

现在都在使用二代 Swarm,也就是

Docker Swarm Mode
,非常简单,只要
docker swarm init
建立集群,其它节点
docker swarm join
加入集群后,集群内的服务就自动建立了 overlay 网络互联能力。

需要注意的是,如果是多网卡环境,无论是 docker swarm init 还是 docker swarm join,都不要忘记使用参数

--advertise-addr
指定宣告地址,否则自动选择的地址很可能不是你期望的,从而导致集群互联失败。格式为
--advertise-addr <地址>:<端口>
,地址可以是 IP 地址,也可以是网卡接口,比如 eth0。端口默认为 2377,如果不改动可以忽略。

此外,这是供服务使用的 overlay,因此所有 docker service create 的服务容器可以使用该网络,而 docker run 不可以使用该网络,除非明确该网络为

--attachable

关于 overlay 网络的进一步信息,可以参考官网文档
参考链接

macvlan

macvlan
本身是 linxu kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址,即多个 interface,每个 interface 可以配置自己的 IP。macvlan 本质上是一种
网卡虚拟化技术

macvlan 的最大优点是性能极好,相比其他实现,macvlan 不需要创建 Linux bridge,而是直接通过以太 interface 连接到物理网络。

1.准备实验环境:

使用 host1 和 host2 上单独的网卡 eth1 创建 macvlan。为保证多个 MAC 地址的网络包都可以从 eth1 通过,我们需要打开网卡的混杂模式(

promisc
)。
参考链接
设置支持promisc:

ip link set eth1 promisc on  |
ifconfig eth1 promisc

设置不支持promisc:

ifconfig eth0 -promisc

查看:

# ip link show eth1
6: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:79:9a:8e brd ff:ff:ff:ff:ff:ff

2.创建machine网络:

(1)在 host1 和 host2 中创建 macvlan 网络 mac_net1:

[root@host1 ~]# docker network create -d macvlan --subnet=192.168.100.0/24 --gateway=192.168.100.1 -o parent=eth1 mac_net1
690bfd5aee8cb279ca78617343d9227d40d5583703ade6398e96d99556dca78e

[root@host2 ~]# docker network create -d macvlan --subnet=192.168.100.0/24 --gateway=192.168.100.1 -o parent=eth1 mac_net1
02eca5ba535994564dfeb746c5f2d03af7a2a0e3870398b897bc3770aaa725c5
  • -d macvlan
    指定 driver 为 macvlan。
  • macvlan 网络是 local 网络,为了保证跨主机能够通信,用户需要自己管理 IP subnet。
  • 与其他网络不同,docker 不会为 macvlan 创建网关,这里的网关应该是真实存在的,否则容器无法路由。
  • -o parent
    指定使用的网络 interface。

(2)在 host1 中运行容器 bbox1 并连接到 mac_net1。

[root@host1 ~]# docker run -itd --name bbox1 --ip=192.168.100.10 --network mac_net1 busybox
1c3fd10d23edc32d5b69c5ddd2877e624e312a4310b83e801a0a3f9746d5099b

由于 host1 中的 mac_net1 与 host2 中的 mac_net1 本质上是独立的,为了避免自动分配造成 IP 冲突,我们最好通过

--ip
指定 bbox1 地址为192.168.100.10。

WARNING: IPv4 forwarding is disabled. Networking will not work.
1.临时开启,(写入内存,在内存中开启)

echo "1" > /proc/sys/net/ipv4/ip_forward

2.永久开启,(写入内核)
在 vim /sysctl.conf 下
加入此行
net.ipv4.ip_forward = 1

sysctl -p
—-加载一下
[root@localhost ~]#
sysctl -a |grep "ip_forward"
—-查看一下

(3)在 host2 中运行容器 bbox2,指定 IP 192.168.100.11。

[root@host2 ~]# docker run -itd --name bbox2 --ip=192.168.100.11 --network mac_net1 busybox
bbb62a6a58e454ea166b484e477f31be90d96dfa3cb883af853a93298bcef197

网络结构如下所示:

(4)验证 bbox1 和 bbox1 的连通性

[root@host2 ~]# docker exec bbox2 ping -c 2 192.168.100.10
PING 192.168.100.10 (192.168.100.10): 56 data bytes
64 bytes from 192.168.100.10: seq=0 ttl=64 time=2.457 ms
64 bytes from 192.168.100.10: seq=1 ttl=64 time=0.456 ms

注意:bbox2 能够 ping 到 bbox1 的 IP地址,但是无法解析”bbox1”的主机名。docker 没有为 macvlan 提供 DNS 服务,这点与 overlay 网络是不同的

3.macvlan 网络结构分析:


macvlan 不依赖 Linux bridge,除了 lo,容器只有一个 eth0,请注意 eth0 后面的 @if5,这表明该 interface 有一个对应的 interface,其全局的编号为 5。通过

ip link show eth1
可以发现容器的eth0就是eth1通过 macvlan 虚拟出来的 interface。容器的 interface 直接与主机的网卡连接,这种方案使得容器无需通过 NAT 和端口映射就能与外网直接通信(只要有网关),在网络上与其他独立主机没有区别。

4.用 sub-interface 实现多 macvlan 网络:

macvlan 会独占主机的网卡,也就是说一个网卡只能创建一个 macvlan 网络,否则会报错:

Error response from daemon: network dm-690bfd5aee8c is already using parent interface eth1

但主机的网卡数量是有限的,如何支持更多的 macvlan 网络呢?
macvlan 不仅可以连接到 interface(如 eth1),也可以连接到 sub-interface(如 eth1.xxx)。

VLAN
是现代网络常用的网络虚拟化技术,它可以将物理的二层网络划分成多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。

Linux 的网卡也能支持 VLAN(ubuntu:apt-get install vlan),同一个 interface 可以收发多个 VLAN 的数据包,不过前提是要创建 VLAN 的 sub-interface。

比如希望 eth1 同时支持 VLAN10 和 VLAN20,则需创建 sub-interface eth1.10 和 eth1.20。

在交换机上,如果某个 port 只能收发单个 VLAN 的数据,该 port 为 Access 模式,如果支持多 VLAN,则为 Trunk 模式,如果需要不同vlan下的容器互通,则eth1要接在交换机的

trunk
口上(VirtualBox 虚拟机不需要额外的配置)

centos7下的用法:
参考链接

创建 VLAN 设备:
# ip link add link eth1 name eth1.10 type vlan id 10
# ip link add link eth1 name eth1.20 type vlan id 20
执行 ip link 命令确认 VLAN 已创建。

添加 IP:
# ip addr add 192.168.100.1/24 brd 192.168.100.255 dev eth1.10

启用设备:
# ip link set dev eth1.10 up

关闭设备:
# ip link set dev eth1.10 down

移除设备:
# ip link delete eth1.10

(1)首先配置host1和host2的sub-interface:

# ip link add link eth1 name eth1.10 type vlan id 10
# ip link add link eth1 name eth1.20 type vlan id 20
# ip link set dev eth1.10 up
# ip link set dev eth1.20 up

(2)在host1和host2主机上创建 macvlan 网络:

# docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=eth1.10 mac_net10
# docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=eth1.20 mac_net20

(3)在host1中运行容器:

# docker run -itd --name bbox1 --ip=172.16.10.10 --network mac_net10 busybox
# docker run -itd --name bbox2 --ip=172.16.20.10 --network mac_net20 busybox

(4)在host2中运行容器:

# docker run -itd --name bbox3 --ip=172.16.10.11 --network mac_net10 busybox
# docker run -itd --name bbox4 --ip=172.16.20.11 --network mac_net20 busybox

网络结构图如下所示:

(5)验证 macvlan 之间的连通性:

注意:由于我的是在esxi的虚拟机上面做的实验,需要在esxi中设置eth1为trunk口,允许所有vlan通过后试验才会生效
(没有设置esxi,主机间互不连通,后续补充吧,欢迎大神留言指教)

参考链接
Vm ESXI 网络支持Trunk的设置

flannel

flannel 是 CoreOS 开发的容器网络解决方案。flannel 为每个 host 分配一个 subnet,容器从此 subnet 中分配 IP,这些 IP 可以在 host 间路由,容器间无需 NAT 和 port mapping 就可以跨主机通信。

每个 subnet 都是从一个更大的 IP 池中划分的,flannel 会在每个主机上运行一个叫 flanneld 的 agent,其职责就是从池子中分配 subnet。为了在各个主机间共享信息,flannel 用 etcd(与 consul 类似的 key-value 分布式数据库)存放网络配置、已分配的 subnet、host 的 IP 等信息。

数据包如何在主机间转发是由 backend 实现的。flannel 提供了多种 backend,最常用的有 vxlan 和 host-gw。其他 backend 请参考 https://github.com/coreos/flannel
flannel支持多种的backend:目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式,默认使用的是udp的方式

实验环境:
继续使用docker-machine的实验环境,etcd部署在192.168.20.203上,host1和host2上运行flanneld。

安装配置etcd:

GitHub地址
可以根据官网提供的二进制方式包或通过docker的方式搭建,也可以直接yum安装。

1.安装etcd:

yum install -y etcd

2.修改配置文件:

/etc/etcd/etcd.conf

# grep ^[A-Z] /etc/etcd/etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://192.168.20.203:2379"
ETCD_NAME="default"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.20.203:2379"
  • --name
    :方便理解的节点名称,默认为 default ,在集群中应该保持唯一,可以使用 hostname
  • --data-dir
    :服务运行数据保存的路径,默认为 ${name}.etcd
  • --listen-client-urls
    :对外提供服务的地址:比如 http://ip:2379,http://127.0.0.1:2379 ,客户端会连接到这里和 etcd 交互
  • --advertise-client-urls
    :对外公告的该节点客户端监听地址,这个值会告诉集群中其他节点

详细参数和用法可以参考:etcd使用简介

3.编辑一个文件:etcd.sh,设置host主机的subnet:

{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}
  • Network
    :定义host主机的IP地址池为10.2.0.0/16,注:由于etcd并不是动态保存host上flannel网络的,比如:当有节点被删除后,etcd中的关于这个节点的subnet网络并不会被删除,所以使用10.X.X.X的网络,保证有足够的网络可用
  • SubnetLen
    :指定每个主机分配的subnet大小为24位,即
    10.2.x.0/24
  • Backend
    为vxlan,即主机之间通过vxlan通信,我们主要讨论vxlan和host-gw这两种方式

4.将配置保存到etcd

etcdctl --endpoints=http://192.168.20.203:2379 set /usr/local/bin/network/config </root/etcd.sh
#--endpoints=http://192.168.7.222:2379 指定etcd的url
#/usr/local/bin/network/config  保存key的地方,flanneld会读取这个配置,保证自己能获取到subnet,这个key可以任意指定,当host主机分配到subnet后,key会修改docker的启动参数

查看:

# etcdctl --endpoints=http://192.168.20.203:2379 get /usr/local/bin/network/config
{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}

安装配置fannel:

GitHub地址
1.安装flannel

yum install -y flannel

2.修改配置文件:

/etc/sysconfig/flanneld

FLANNEL_ETCD_ENDPOINTS="http://192.168.20.203:2379"   #  连接etcd的地址
FLANNEL_ETCD_PREFIX="/usr/local/bin/network"      # key的地址

3.启动flanneld:

systemctl start flanneld

host1和host2上都安装启动完flannel,就可以看见每个主机上起了一个flanneld1.1的网络

两个host都会出现一个flannel1.1的路由,路由都是10.2.0.0的

10.2.0.0        0.0.0.0         255.255.0.0     U     0      0        0 flannel.1

配置docker连接flannel:

编辑docker的配置文件:

/etc/systemd/system/docker.service.d/10-machine.conf
添加
--bip=    --mtu=

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock  --storage-driver devicemapper --tlsverify --tlscacert /etc/docker/ca.pem --tlscert /etc/docker/server.pe --tlskey /etc/docker/server-key.pem --label provider=generic --bip=10.2.54.1/24 --mtu=1450
Environment=

这两个参数要与

/run/flannel/subnet.env
中保持一致

# cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.2.0.0/16
FLANNEL_SUBNET=10.2.54.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

重启docker服务

systemctl daemon-reload
systemctl restart docker

查看docker0的变化

Docker 会将 10.2.54.1 配置到 Linux bridge docker0 上,并添加 10.2.54.0/24 的路由。

在host2上,做相同的操作,修改配置文件并重启

验证容器的连通性:

#host1
# docker run -dit --name b1 busybox

# docker exec b1 ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:02:36:02
inet addr:10.2.54.2  Bcast:10.2.54.255  Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B)  TX bytes:0 (0.0 B)

#host2
# docker run -dit --name b2 busybox

# docker exec b2 ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:02:1D:02
inet addr:10.2.29.2  Bcast:10.2.29.255  Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B)  TX bytes:0 (0.0 B)

测试b1到b2的连通性:

# docker exec b1 ping -c 2 10.2.29.2
PING 10.2.29.2 (10.2.29.2): 56 data bytes
64 bytes from 10.2.29.2: seq=0 ttl=62 time=3.008 ms
64 bytes from 10.2.29.2: seq=1 ttl=62 time=0.670 ms

b1能够 ping 到位于不同 subnet 的 b2,通过 traceroute 分析一下 b1 到 b2 的路径。

# docker exec b1 traceroute 10.2.29.2
traceroute to 10.2.29.2 (10.2.29.2), 30 hops max, 46 byte packets
1  10.2.54.1 (10.2.54.1)  0.027 ms  0.024 ms  0.058 ms
2  10.2.29.0 (10.2.29.0)  0.410 ms  0.470 ms  0.275 ms
3  10.2.29.2 (10.2.29.2)  0.205 ms  0.720 ms  0.371 ms
  1. b1与b2不是一个subnet,数据包发送给默认网关10.2.54.1(docker0)
  2. 根据 host1 的路由表,数据包会发给 flannel.1。(10.2.0.0/16 dev flannel.1)
  3. flannel.1 将数据包封装成 VxLAN,通过 eth0发送给 host2。
  4. host2 收到包解封装,发现数据包目的地址为 10.2.29.2,根据路由表将数据包发送给 flannel.1,并通过 docker0
    到达 b2。(如下所示)
#host2路由表
10.2.0.0/16 dev flannel.1
10.2.29.0/24 dev docker0 proto kernel scope link src 10.2.29.1

flannel网络隔离:

flannel为每个主机分配了独立的subnet,但是flannel1.1将这些subnet链接额起来,相互之间可以路由。本质上,flannel将个主机上相互独立的docker0容器网络组成了一个互通的大网络,实现了容器跨主机通信。flannel没有实现隔离

flannel与外网连通性:
因为flannel网络是将flannel1.1桥接到docker0上,所以容器与外部通信的方式与docker0默认的bridge网络一样,即:

  • 容器通过NAT连接外网
  • 外网通过端口映射的方式访问容器

使用 flannel host-gw

与vxlan不同,

host-gw
不会封装数据包,而是在主机的路由表中创建到其他主机的subnet的路由条目,从而实现容器跨主机通信

1.要使用host-gw首先修改etcd key:

前边我们定义过vclan的方式:

{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}

现在修改backend的type为host-wg:

{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"host-gw"}}

更新 etcd 数据库:

# etcdctl --endpoints=http://192.168.20.203:2379 set /usr/local/bin/network/config </root/etcd.sh
{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"host-gw"}}

# etcdctl --endpoints=http://192.168.20.203:2379 get /usr/local/bin/network/config
{"Network":"10.2.0.0/16","SubnetLen":24,"Backend":{"Type":"host-gw"}}

2.重启host1和host2上的flanneld服务

systemctl restart flanneld

与之前 vxlan backend 启动时有几点不同:
① flanneld 检查到原先已分配的 subnet 10.2.54.0/24,重用之。
② flanneld 从 etcd 数据库中检索到 host2 的 subnet 10.2.29.0/24,但因为其 type=vxlan,立即忽略。
③ 稍后,再次发现 subnet 10.2.29.0/24,将其加到路由表中。这次没有忽略 subnet 的原因是此时我们在 host2 上重启了 flanneld,根据当前 etcd 的配置使用 host-gw backend。

查看host1、host2的路由表可以发现,host1路由表增加了一条到10.2.29.0/24的路由,网关为host2的IP 192.168.20.202,类似的host2启动 flanneld 时会重用 subnet 10.2.29.0/24,并将 host1 的 subnet 10.2.54.0/24 添加到路由表中,网关为 host1 IP 192.168.20.201。

#host1
10.2.29.0/24 via 192.168.20.202 dev eth0
#host2
10.2.54.0/24 via 192.168.20.201 dev eth0

3.修改docker的配置
根据

/run/flanneld/subnet.env
,修改docker配置,重启服务

FLANNEL_NETWORK=10.2.0.0/16
FLANNEL_SUBNET=10.2.29.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=false

subnet会重用,所以只需修改docker启动参数–mtu=1450为–mtu=1500即可。

traceroute 查看一下:

[root@host1 ~]# docker exec b1 traceroute 10.2.29.2
traceroute to 10.2.29.2 (10.2.29.2), 30 hops max, 46 byte packets
1  10.2.54.1 (10.2.54.1)  0.027 ms  0.024 ms  0.058 ms
2  192.168.20.202 (192.168.20.202)  0.831 ms  0.470 ms  0.275 ms
3  10.2.29.2 (10.2.29.2)  0.205 ms  0.720 ms  0.371 ms

vxlan和 host-gw的区别

  • host-gw把每个主机都配置成网关,主机知道其他主机的subnet和转发地址。vxlan则是在主机之间创建隧道,不同的主机的容器都在一个大的网段内,比如:10.2.0.0/16
  • 虽然vxlan与host-gw使用不同的机制,但对于容器之间还是可以相互通信的
  • 由于vxlan需要对数据进行打包和拆包,性能可能稍逊于host-gw

参考链接

参考链接:

https://blog.lab99.org/post/docker-2016-07-14-faq.html
https://docs.docker.com/network/overlay/
https://mp.weixin.qq.com/s/7o8QxGydMTUe4Q7Tz46Diw
https://www.jianshu.com/p/3eb7448adea0
https://www.geek-share.com/detail/2721043708.html
https://www.geek-share.com/detail/2592731243.html
https://wiki.archlinux.org/index.php/VLAN_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
https://www.geek-share.com/detail/2721132923.html
http://blog.chinaunix.net/uid-1730385-id-3087725.html
http://m.chuansong.me/n/1793376951623?page=2
https://www.geek-share.com/detail/2721726622.html

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: