在之前的文章中已经介绍过单主机Docker容器的网络互联,但是实际生产中我们很多时候都是多台主机部署Docker环境,且每台主机上都运行数量不等的容器,如果需要这些容器共同提供服务,就需要解决跨主机容器间的网络通信问题,所以这里就来记录一下常用的跨主机容器间的网络通信方案。

Docker主机之间容器通信解决方案:

  • 桥接宿主机网络

  • 端口映射

  • Docker网络驱动

        ○  Overlay:基于VXLAN封装实现Docker原生Overlay网络

        ○  Macvlan:Docker主机网卡接口逻辑上分为多个子接口,每个子接口标识一个VLAN。容器接口直接连接Docker主机网卡接口,通过路由策略转发到另一台Docker主机

  • 第三方网络项目

        ○  隧道方案

            ☑  Flannel:支持UDP和VXLAN封装传输方式

            ☑  Weave:支持UDP(sleeve模式)和VXLAN(优先fastdp模式)

            ☑  OpenvSwitch:支持VXLAN和GRE协议

        ○  路由方案

            ☑  Calico:支持BGP协议和IPIP隧道。每台宿主机作为虚拟路由,通过BGP协议实现不同主机容器间通信


环境准备:


IP地址 主机名 Docker版本 系统(内核版本)
节点1
192.168.49.41 docker01.contoso.com
1.13.1 CentOS 7(3.10.0-693.el7.x86_64) 
节点2 192.168.49.42 docker02.contoso.com 1.13.1 CentOS 7(3.10.0-693.el7.x86_64)


桥接模式:

对桥接模式而言,宿主机位于同一个局域网,将每一个宿主机上的容器网络桥接到宿主机网络中,容器和宿主机同在一个局域网中,因而可以互相通信。

1)在宿主机上创建网桥(如不特别指明,以下命令均需在两台主机上执行)

yum -y install bridge-utils
brctl addbr br0
brctl addif br0 eth0

2)给网桥配置IP地址

docker01:
        ifconfig eth0 0.0.0.0
        ifconfig br0 192.168.49.41 up
docker02:
        ifconfig eth0 0.0.0.0
        ifconfig br0 192.168.49.42 up

3)查看网桥信息

[root@docker01 ~]# brctl show br0
bridge namebridge idSTP enabledinterfaces
br08000.000c2952f550noeth0
[root@docker02 ~]# brctl show br0
bridge namebridge idSTP enabledinterfaces
br08000.000c29efedb3noeth0

4)配置Docker启动选项

[root@docker01 ~]# cat /etc/sysconfig/docker-network 
# /etc/sysconfig/docker-network
DOCKER_NETWORK_OPTIONS="-b=br0 --fixed-cidr=192.168.49.64/27"    
# 这里我使用--fixed-cidr指定了容器的IP地址范围,因为我是用VMware虚拟机的NAT网络模式,如果不指定容器会从192.168.49.1的地址开始获取,\
# 而这个地址是我VMware宿主机的网卡地址,会导致SSH无法连接到Docker主机,所以这里指定一下容器的IP地址范围。
[root@docker02 ~]# cat /etc/sysconfig/docker-network 
# /etc/sysconfig/docker-network
DOCKER_NETWORK_OPTIONS="-b=br0 --fixed-cidr=192.168.49.96/27"
# 这里为了两台Docker主机上的容器不会获取相同的IP地址,所以在两台Docker主机上配置不同的CIDR地址段,这样就不会产生IP地址冲突。

5)重启docker服务

systemctl restart docker

6)创建容器并验证

在Docker01上执行:

[root@docker01 ~]# docker run -itd --name node01 centos:latest /bin/bash
d0e17f02f815d639aab538f4541ff93650db056eda75b35c026ccf5bfa35bc3d
[root@docker01 ~]# docker exec -it node01 /bin/bash
[root@d0e17f02f815 /]# ifconfig eth0 |grep -A1 eth0
eth0: flags=4163  mtu 1500
        inet 192.168.49.64  netmask 255.255.255.0  broadcast 0.0.0.0

在Docker02上执行:

[root@docker02 ~]# docker run -itd --name node02 centos:latest /bin/bash
15376d306ef5c302ebe98439dba40d75230ad0ebe8037941b39823b01c19d561
[root@docker02 ~]# docker exec -it node02 /bin/bash
[root@15376d306ef5 /]# ifconfig eth0 |grep -A1 eth0
eth0: flags=4163  mtu 1500
        inet 192.168.49.96  netmask 255.255.255.0  broadcast 0.0.0.0

在Docker01上上验证:

[root@d0e17f02f815 /]# ping 192.168.49.96 -c 2
PING 192.168.49.96 (192.168.49.96) 56(84) bytes of data.
64 bytes from 192.168.49.96: icmp_seq=1 ttl=64 time=0.664 ms
64 bytes from 192.168.49.96: icmp_seq=2 ttl=64 time=0.377 ms
--- 192.168.49.96 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.377/0.520/0.664/0.145 ms
[root@d0e17f02f815 /]# ping 192.168.49.41 -c 2
PING 192.168.49.41 (192.168.49.41) 56(84) bytes of data.
64 bytes from 192.168.49.41: icmp_seq=1 ttl=64 time=0.029 ms
64 bytes from 192.168.49.41: icmp_seq=2 ttl=64 time=0.037 ms
--- 192.168.49.41 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.029/0.033/0.037/0.004 ms

在Docker02上上验证:

[root@15376d306ef5 /]# ping 192.168.49.64 -c 2
PING 192.168.49.64 (192.168.49.64) 56(84) bytes of data.
64 bytes from 192.168.49.64: icmp_seq=1 ttl=64 time=0.347 ms
64 bytes from 192.168.49.64: icmp_seq=2 ttl=64 time=0.400 ms
--- 192.168.49.64 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.347/0.373/0.400/0.032 ms
[root@15376d306ef5 /]# ping 192.168.49.42 -c 2
PING 192.168.49.42 (192.168.49.42) 56(84) bytes of data.
64 bytes from 192.168.49.42: icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from 192.168.49.42: icmp_seq=2 ttl=64 time=0.039 ms
--- 192.168.49.42 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.039/0.045/0.052/0.009 ms

结论:桥接模式是跨主机容器网络互联较简答的一种方式,直接将容器网络桥接到局域网中,这样容器和宿主机就在同一网段,方便进行容器操作。但因为每台主机上的容器都直接从局域网中获取IP地址,却没有统一对不同主机上容器获取的IP地址进行登记,很容易导致IP地址冲突,虽然可以通过使用--fixed-cidr来指定,但是这样隔离网段也不太方便,网络划分需注意。


端口映射:

端口映射,顾名思义就是将容器的服务所运行的端口映射到宿主机的某一个端口,然后其他的容器通过宿主机的对应端口进行访问。

1)创建带端口映射的容器

因为使用端口映射的方式,所以不需要单独创建容器网络,我们使用默认的docker0网络即可。

[root@docker01 ~]# docker run -itd --name nat01 -p 4180:80 mynginx:v1 
edc990164c797213a866bdd09a5f914360cbd6308fe3142325ea7013a0ea0a69
[root@docker02 ~]# docker run -itd --name nat02 -p 4280:80 mynginx:v1
bea87fe30cd4e56e7048305e0b0cef4a0886c9e24e33fe34a37244a509ab6325

2)在宿主机的防火墙中查看端口映射

[root@docker01 ~]# iptables -t nat -nL |grep 4180
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:4180 to:172.17.0.2:80
[root@docker02 ~]# iptables -t nat -nL |grep 4280
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:4280 to:172.17.0.2:80

3)测试容器间通信

在nat01上执行:

[root@docker01 ~]# docker exec -it nat01 /bin/bash
[root@edc990164c79 nginx]# curl -I http://192.168.49.42:4280
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Sun, 21 Apr 2019 08:27:30 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Sun, 03 Mar 2019 06:18:30 GMT
Connection: close
ETag: "5c7b71b6-264"
Accept-Ranges: bytes

在nat02上执行:

[root@docker02 ~]# docker exec -it nat02 /bin/bash
[root@bea87fe30cd4 nginx]# curl -I http://192.168.49.41:4180
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Sun, 21 Apr 2019 08:28:43 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Sun, 03 Mar 2019 06:18:30 GMT
Connection: close
ETag: "5c7b71b6-264"
Accept-Ranges: bytes

结论:使用端口映射的方式,只是将容器的端口通过NAT方式映射到宿主机网络的某一个端口,只要宿主机能互相通信,容器之间就能通过宿主机的指定端口进行通信。但是这种方式需要对每一个容器都映射端口,而且宿主机的端口也是有限的(不可复用),所以会有一定的局限性。


Overlay网络:

overlay 网络驱动程序在多个 Docker 守护进程主机之间创建一个分布式网络。这个网络在允许容器连接并进行安全通信的主机专用网络之上(overlay 覆盖在上面)。Docker 透明地处理每个 Docker 守护进程与目标容器之间的数据包的路由。

Docker通过Overlay网络驱动程序支持多主机容器网络通信。要想使用Docker原生Overlay网络,需要满足以下任意条件:

  • Docker运行在Swarm模式

  • 使用键值存储的Docker主机集群

这里我选择第二种方式,需满足以下条件:

1)集群中主机连接到键值存储,Docker支持Consul、Etcd和Zookeeper

2)集群中主机运行一个Docker守护进程

3)集群中主机必须具有唯一的主机名,因为键值存储使用主机名来标识集群成员

4)集群中Linux主机内核版本3.12+,支持VXLAN数据包处理,否则可能无法通信

环境准备:

        节点1:docker01      192.168.49.41         键值存储

        节点2:docker02      192.168.49.42         

实现过程:

1)在节点1上下载并安装consul

wget https://releases.hashicorp.com/consul/1.4.4/consul_1.4.4_linux_amd64.zip 
unzip consul_1.4.4_linux_amd64.zip 
mv consul /usr/bin/
chmod +x /usr/bin/consul

2)在节点1上启动consul服务

nohup consul agent -server -bootstrap -ui -data-dir /var/lib/consul -client=192.168.49.41 -bind=192.168.49.41 &>/var/log/consul.log &

3)修改节点1上的docker守护进程

[root@docker01 ~]# vi /etc/sysconfig/docker
[root@docker01 ~]# tail -1 /etc/sysconfig/docker
OPTIONS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store consul://192.168.49.41:8500 --cluster-advertise 192.168.49.41:2375"
# 这里是CentOS 7上的修改方法,并不适用于Ubuntu
[root@docker01 ~]# systemctl daemon-reload
[root@docker01 ~]# systemctl restart docker
[root@docker01 ~]# ps -ef|grep docker
root       4201      1  0 22:38 ?        00:00:00 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=/usr/libexec/docker/docker-proxy-current --init-path=/usr/libexec/docker/docker-init-current --seccomp-profile=/etc/docker/seccomp.json -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store consul://192.168.49.41:8500 --cluster-advertise 192.168.49.41:2375 --storage-driver overlay2
root       4206   4201  0 22:38 ?        00:00:00 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc --runtime-args --systemd-cgroup=true

4)同样的方法修改节点2的docker守护进程

[root@docker02 ~]# vi /etc/sysconfig/docker
[root@docker02 ~]# tail -1 /etc/sysconfig/docker
OPTIONS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store consul://192.168.49.41:8500 --cluster-advertise 192.168.49.42:2375"
[root@docker02 ~]# systemctl daemon-reload
[root@docker02 ~]# systemctl restart docker
[root@docker02 ~]# ps -ef|grep docker
root       2442      1  0 22:39 ?        00:00:01 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=/usr/libexec/docker/docker-proxy-current --init-path=/usr/libexec/docker/docker-init-current --seccomp-profile=/etc/docker/seccomp.json -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store consul://192.168.49.41:8500 --cluster-advertise 192.168.49.42:2375 --storage-driver overlay2
root       2447   2442  0 22:39 ?        00:00:00 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc --runtime-args --systemd-cgroup=true

5)到consul的UI界面查看节点是否添加

        Docker系列(十二):Docker 跨主机容器间网络通信(一)_第1张图片

6)在docker主机上创建overlay网络

[root@docker01 ~]# docker network create -d overlay overlay_net
edf675e1a8b58a0b8fee9888ebd380690d022b1b4c02bf8da710f5fad96ce9be
# 只需在其中一台docker主机上创建overlay网络即可,它会自动同步到另外一个节点上。如果尝试到另外一个节点创建overlay网络,就会出现如下错误:
[root@docker02 ~]# docker network create -d overlay overlay_net
Error response from daemon: network with name overlay_net already exists

7)分别在两个节点上创建容器

[root@docker01 ~]# docker run -it --net=overlay_net --name busybox01 busybox:latest 
[root@docker02 ~]# docker run -it --net=overlay_net --name busybox02 busybox:latest

检测容器是否可以互相通信:

        Docker系列(十二):Docker 跨主机容器间网络通信(一)_第2张图片

        

       Docker系列(十二):Docker 跨主机容器间网络通信(一)_第3张图片

工作原理:

在讲解原理之前,先看看容器上的网络信息,这会给我们容器间通信的一些提示。

节点1上的容器网络:

Docker系列(十二):Docker 跨主机容器间网络通信(一)_第4张图片

节点2上的容器网络:

Docker系列(十二):Docker 跨主机容器间网络通信(一)_第5张图片

我们看到每个容器都有2张网卡,而这个172.20.0.0/24的网段是哪里来的呢?再来看看docker主机的网络信息:

在节点1上:

[root@docker01 ~]# ifconfig 
......
docker_gwbridge: flags=4163  mtu 1500
        inet 172.20.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
        inet6 fe80::42:f1ff:fe59:46c  prefixlen 64  scopeid 0x20
        ether 02:42:f1:59:04:6c  txqueuelen 0  (Ethernet)
        RX packets 12  bytes 894 (894.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 12  bytes 1024 (1024.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
......

在节点2上:

[root@docker02 ~]# ifconfig
......
docker_gwbridge: flags=4163  mtu 1500
        inet 172.20.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
        inet6 fe80::42:d0ff:fe7e:b42  prefixlen 64  scopeid 0x20
        ether 02:42:d0:7e:0b:42  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
......

或许我们查看网桥信息会更直接一些:

[root@docker01 ~]# brctl show docker_gwbridge
bridge name     bridge id        STP enabled   interfaces
docker_gwbridge  8000.0242f159046c no             vethf22303e
[root@docker02 ~]# brctl show docker_gwbridge
bridge name     bridge id        STP enabled   interfaces
docker_gwbridge  8000.0242d07e0b42 no             vetheb307a8

我们再查看namespace:

由于容器和overlay的网络的网络命名空间文件不在操作系统默认的/var/run/netns下,只能手动通过软连接的方式查看。

ln -s/var/run/docker/netns /var/run/netns

可以看见两个节点主机上都有这个“1-”开头的的namespace

[root@docker01 ~]# ip netns
90ead4cedbe0 (id: 1)
1-edf675e1a8 (id: 0)
[root@docker02 ~]# ip netns
eba2816aa5c4 (id: 1)
1-edf675e1a8 (id: 0)

查看这个namespace中的网卡信息(以其中一台为例):

[root@docker01 ~]# ip netns exec 1-edf675e1a8 ip a 
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: br0:  mtu 1450 qdisc noqueue state UP 
    link/ether 76:eb:2a:ac:a8:2c brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.1/24 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::f80b:63ff:feb9:5229/64 scope link 
       valid_lft forever preferred_lft forever
8: vxlan1:  mtu 1450 qdisc noqueue master br0 state UNKNOWN 
    link/ether 8e:92:3d:8f:48:28 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::8c92:3dff:fe8f:4828/64 scope link 
       valid_lft forever preferred_lft forever
10: veth2@if9:  mtu 1450 qdisc noqueue master br0 state UP 
    link/ether 76:eb:2a:ac:a8:2c brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::74eb:2aff:feac:a82c/64 scope link 
       valid_lft forever preferred_lft forever

我们看到namespace中有一个网桥br0,再查看这个网桥的信息:

[root@docker01 ~]# ip netns exec 1-edf675e1a8 brctl show
bridge namebridge id                STP enabledinterfaces
br0        8000.76eb2aaca82cno        veth2
                                vxlan1

查看VNI(Vxlan Network identifier):

[root@docker01 ~]# ip netns exec 1-edf675e1a8 ip -d link show vxlan1
8: vxlan1:  mtu 1450 qdisc noqueue master br0 state UNKNOWN mode DEFAULT 
    link/ether 8e:92:3d:8f:48:28 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 1 
    vxlan id 256 srcport 0 0 dstport 4789 proxy l2miss l3miss ageing 300 
    bridge_slave state forwarding priority 32 cost 100 hairpin off guard off root_block off fastleave off learning on flood on port_id 0x8001 port_no 0x1 designated_port 32769 designated_cost 0 designated_bridge 8000.76:eb:2a:ac:a8:2c designated_root 8000.76:eb:2a:ac:a8:2c hold_timer    0.00 message_age_timer    0.00 forward_delay_timer    0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on addrgenmode eui64

查看vlan设备上的静态mac地址表:

[root@docker01 ~]# ip netns exec 1-edf675e1a8 bridge fdb show vxlan1
33:33:00:00:00:01 dev br0 self permanent
01:00:5e:00:00:01 dev br0 self permanent
33:33:ff:b9:52:29 dev br0 self permanent
8e:92:3d:8f:48:28 dev vxlan1 master br0 permanent
8e:92:3d:8f:48:28 dev vxlan1 vlan 1 master br0 permanent
02:42:0a:00:00:03 dev vxlan1 dst 192.168.49.42 link-netnsid 0 self permanent
76:eb:2a:ac:a8:2c dev veth2 master br0 permanent
76:eb:2a:ac:a8:2c dev veth2 vlan 1 master br0 permanent
33:33:00:00:00:01 dev veth2 self permanent
01:00:5e:00:00:01 dev veth2 self permanent
33:33:ff:ac:a8:2c dev veth2 self permanent

综上,overlay网络的拓补如下:

Docker系列(十二):Docker 跨主机容器间网络通信(一)_第6张图片

这里数据包的发送流程如下(以从左侧的容器发送到右侧的容器为例):

  1. 容器Container1会通过Container eth0 将这个数据包发送到 10.0.0.1 的网关。

  2. 网关将数据包发送出去后到达br0网桥。

  3. br0网桥针对VXLAN设备,主要用于捕获对外的数据包通过VETP进行数据包封装。

  4. 封装好将VXLAN格式数据包交给eth0,通过UDP方式交给Container2的eth0。

  5. Container2收到数据包后通过VETP将数据包解封装。

  6. 网桥通过网关将解封装的数据包转发给Container eth0,完毕通信。

因此,Docker容器的overlay网络的实现原理是:

    1、docker会为每个overlay网络创建个单独的命名空间,在这个命名空间里创建了个br0的bridge。
    2、在这个命名空间内创建两张网卡并挂载到br0上,创建一对veth pair端口 和vxlan设备。
    3、veth pair一端接在namespace的br0上,另一端接在container上。
    4、vxlan设备用于建立vxlan tunnel,vxlan端口的vni由docker-daemon在创建时分配,具有相同vni的设备才能通信。
    5、docker主机集群通过key/value存储(我们这里用的是consul)共享数据,在7946端口上,相互之间通过gossip协议学习各个宿主机上运行了哪些容器。守护进程根据这些数据来在vxlan设备上生成静态MAC转发表。

    6、vxlan设备根据静态mac转发表,通过host上的4789端口将数据发到目标节点。
    7、根据流量包中的vxlan隧道ID,将流量转发到对端宿主机的overlay网络的网络命名空间中。

    8、对端宿主机的overlay网络的网络命名空间中br0网桥,起到虚拟交换机的作用,将流量根据MAC地址转发到对应容器内部。

(上述原理分析部分参考:https://www.bladewan.com/2017/11/17/docker_network_overlay/)

补充:如需详细了解docker overlay网络的实现过程,可以参考:http://chenchun.github.io/docker/2015/12/29/km-docker-overlay



Macvlan网络:

macvlan本身是linxu kernel的模块,本质上是一种网卡虚拟化技术。其功能是允许在同一个物理网卡上虚拟出多个网卡,通过不同的MAC地址在数据链路层进行网络数据的转发,一块网卡上配置多个 MAC 地址(即多个 interface),每个interface可以配置自己的IP,Docker的macvlan网络实际上就是使用了Linux提供的macvlan驱动.在物理网络看来,每张虚拟网卡都是一个单独的接口。

1)创建macvlan网络

节点1:

[root@docker01 ~]# docker network create -d macvlan --subnet=172.24.100.0/24 --gateway=172.24.100.1 -o parent=eth0 macvlan_net 
3482af4cb39408840e6ee46225acf2754cfa4632d8ace28c34e7912fe4208946
[root@docker01 ~]# docker network ls |grep macvlan
3482af4cb394        macvlan_net             macvlan             local

节点2:

[root@docker02 ~]# docker network create -d macvlan --subnet=172.24.100.0/24 --gateway=172.24.100.1 -o parent=eth0 macvlan_net
94b4f197fd20f96a3cf3d7e363e16760a43e42a3d806eed9e1814ba649470d9f
[root@docker02 ~]# docker network ls |grep macvlan
94b4f197fd20        macvlan_net             macvlan             local

2)创建容器并指定IP地址

节点1:

[root@docker01 ~]# docker run -it --net macvlan_net --ip=172.24.100.41 --name busybox01 busybox:latest
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:18:64:29  
          inet addr:172.24.100.41  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::42:acff:fe18:6429/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:508 (508.0 B)

节点2:

[root@docker02 ~]# docker run -it --net macvlan_net --ip=172.24.100.42 --name busybox02 busybox:latest
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:18:64:2A  
          inet addr:172.24.100.42  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::42:acff:fe18:642a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:508 (508.0 B)

3)测试容器通信

节点1:

[root@docker01 ~]# docker exec -it busybox01 ping 172.24.100.42
PING 172.24.100.42 (172.24.100.42): 56 data bytes
64 bytes from 172.24.100.42: seq=0 ttl=64 time=0.327 ms
64 bytes from 172.24.100.42: seq=1 ttl=64 time=0.550 ms
64 bytes from 172.24.100.42: seq=2 ttl=64 time=3.868 ms
^C
--- 172.24.100.42 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.327/1.581/3.868 ms

节点2:

[root@docker02 ~]# docker exec -it busybox02 ping 172.24.100.41
PING 172.24.100.41 (172.24.100.41): 56 data bytes
64 bytes from 172.24.100.41: seq=0 ttl=64 time=0.483 ms
64 bytes from 172.24.100.41: seq=1 ttl=64 time=0.375 ms
64 bytes from 172.24.100.41: seq=2 ttl=64 time=0.436 ms
^C
--- 172.24.100.41 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.375/0.431/0.483 ms

Macvlan VLAN Bridge模式:

因为之前的操作都是在VMware Workstation虚拟机中进行,自己尝试在VMware中的Centos7中做macvlan网卡子接口vlan网络容器互通的测试,但是无一例外全部都无法通信,即使是开启了网卡的混杂模式,也都不行。网上给出的方案是使用VirtualBox,但是个人使用VirtualBox+CentOS7仍然无法通信,所以改用VirtualBox+Ubuntu 16.04,测试成功,所以下面改用Ubuntu 16.04进行演示。

环境准备:


IP地址 主机名 Docker版本 系统(内核版本)
节点1
172.16.3.11 docker01
17.05.0-ce Ubuntu 16.04.3 LTS(4.4.0-87-generic) 
节点2 172.16.3.22 docker02 17.05.0-ce Ubuntu 16.04.3 LTS(4.4.0-87-generic) 

1)创建vlan

在两个节点上操作:

ip link add link enp0s3 name enp0s3.10 type vlan id 10
ip link add link enp0s3 name enp0s3.20 type vlan id 20

2)创建macvlan网络

在两个节点上操作:

docker network create -d macvlan --subnet=192.168.10.0/24 --gateway=192.168.10.1 -o parent=enp0s3.10 macvlan_net10
docker network create -d macvlan --subnet=192.168.20.0/24 --gateway=192.168.20.1 -o parent=enp0s3.20 macvlan_net20
# 创建完毕后,执行ifconfig看能否看到enp0s3.10和enp0s3.20这两个虚拟网卡,如果没有则需要重启一下节点

3)使用macvlan创建容器

在节点1上:

docker run -itd --net=macvlan_net10 --ip=192.168.10.11 --name bbox01 busybox:latest
docker run -itd --net=macvlan_net20 --ip=192.168.20.11 --name bbox02 busybox:latest

在节点2上:

docker run -itd --net=macvlan_net10 --ip=192.168.10.22 --name bbox03 busybox:latest
docker run -itd --net=macvlan_net20 --ip=192.168.20.22 --name bbox04 busybox:latest

4)测试网络是否能通信

在节点1上:

root@docker01:~# docker exec -it bbox01 ping 192.168.10.22 -c2 -w2
PING 192.168.10.22 (192.168.10.22): 56 data bytes
64 bytes from 192.168.10.22: seq=0 ttl=64 time=0.444 ms
64 bytes from 192.168.10.22: seq=1 ttl=64 time=0.544 ms
--- 192.168.10.22 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.444/0.494/0.544 ms
root@docker01:~# docker exec -it bbox02 ping 192.168.20.22 -c2 -w2
PING 192.168.20.22 (192.168.20.22): 56 data bytes
64 bytes from 192.168.20.22: seq=0 ttl=64 time=0.417 ms
64 bytes from 192.168.20.22: seq=1 ttl=64 time=0.381 ms
--- 192.168.20.22 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.381/0.399/0.417 ms

在节点2上:

root@docker02:~# docker exec -it bbox03 ping 192.168.10.11 -c2 -w2
PING 192.168.10.11 (192.168.10.11): 56 data bytes
64 bytes from 192.168.10.11: seq=0 ttl=64 time=0.335 ms
64 bytes from 192.168.10.11: seq=1 ttl=64 time=0.263 ms
--- 192.168.10.11 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.263/0.299/0.335 ms
root@docker02:~# docker exec -it bbox04 ping 192.168.20.11 -c2 -w2
PING 192.168.20.11 (192.168.20.11): 56 data bytes
64 bytes from 192.168.20.11: seq=0 ttl=64 time=0.280 ms
64 bytes from 192.168.20.11: seq=1 ttl=64 time=0.336 ms
--- 192.168.20.11 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.280/0.308/0.336 ms

这里,同一个vlan的两个节点上的容器可以通信,不同vlan的容器(无论是同一个节点还是不同节点)均不能通信,当然可以通过添加路由的方式让不同vlan的容器通信,但是vlan划分的目的就是隔绝网络,所以这里不再演示让不同vlan容器通信的方法,网上类似的教程也很多,大家自行查找即可。