docker-overylay

overlay网络转发原理

  • 创建好docker-overlay类型的容器后,docker会创建一个命名空间,里面会有一个br0网卡
  • 而这个命名空间内veth0桥接着br0,它的作用是和容器的eth0进行veth pair连接通信,
  • 这个命名空间内还有一个vxlan0网卡,它的作用是进行跨主机通信,只要两端的 命名空间内的 vxlan_id相同就可以通信
  • 要相同的 id才能通信,那么就需要consul来同步两台docker主机的数据了,也就是说,只要有consul,其中一台docker创建一个块新的网卡,马上就会同步到,第二台主机中,两台主机的容器只要使用相同的网卡就能够进行通信了

准备overlay环境

  • 做之前,需要跨网络的其中一台主机需要下载consul镜像

    docker1主机下载镜像

    [root@localhost ~]# docker pull progrium/consul
    
  • 因为consul在主机间传输数据依靠主机名来做,所以我们先改一下两台的主机名,以便区分

    [root@localhost ~]# hostname docker1
    [root@localhost ~]# bash
    [root@docker1 ~]#
    
    # docker2
    [root@localhost ~]# hostname docker2
    [root@localhost ~]# bash
    [root@docker2 ~]#
  • 两台主机放行端口

    docker管理端口:2376/tcp 2376/udp
    docker集群通信:2733/tcp 2733/udp
    docker主机间通信:7946/tcp 7946/udp
    docker overlay网络:4789/tcp 4789/udp

两台主机放行端口,打开路由转发

# 两台docker都要放行
[root@docker1 ~]# firewall-cmd --add-port=2733/tcp --permanent
[root@docker1 ~]# firewall-cmd --add-port=2733/udp --permanent
[root@docker1 ~]# firewall-cmd --add-port=2376/udp --permanent
[root@docker1 ~]# firewall-cmd --add-port=2376/tcp --permanent
[root@docker1 ~]# firewall-cmd --add-port=7946/tcp --permanent
[root@docker1 ~]# firewall-cmd --add-port=7946/udp --permanent
[root@docker1 ~]# firewall-cmd --add-port=4789/udp --permanent
[root@docker1 ~]# firewall-cmd --add-port=4789/tcp --permanent
[root@docker1 ~]# firewall-cmd --reload
[root@docker1 ~]# firewall-cmd --list-port

[root@localhost ~]# echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf 
[root@localhost ~]# sysctl -p
net.ipv4.ip_forward = 1

docker1运行consul

[root@docker1 ~]# docker run -d --restart always -p 8400:8400 -p 8500:8500 \
-p 8600:53/udp progrium/consul -server -bootstrap -ui-dir /ui
44bd33f2b9911b86c0479efb6169c39090b0503c28b9ff2c7edd32bed4cc7586

consul加入docker启动文件,让docker加入到consul

[root@docker1 ~]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376 --containerd=/run/containerd/containerd.sock --cluster-store=consul://192.168.100.211:8500 --cluster-advertise=ens33:2376

#--cluster-store 指定 consul 的地址。
#--cluster-advertise 告知 consul 自己的连接地址。

两台主机重启docker

# docker1重启
[root@docker1 ~]# systemctl daemon-reload 
[root@docker1 ~]# systemctl restart docker

# docker2重启
[root@docker2 ~]# systemctl daemon-reload 
[root@docker2 ~]# systemctl restart docker
  • 打开浏览器,访问http://192.168.100.211:8500
  • 找到以下两个节点,也就是docker1和docker2的ip:2376docker管理节点

创建overlay网络

  • 需要先开启网卡的混杂模式

    docker1

    [root@docker1 ~]# ifconfig ens33 promisc
    [root@docker1 ~]# ip a  | grep PROMISC
    2: ens33: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP>

    创建overylay网络

    [root@docker1 ~]# docker network create --driver overlay --attachable ov_net1
    a921406c8f1e6c3899b39c3ecc862d806107f7abbab4f93e9b529896dd23b66d
    # 默认情况下overlay只能用于docker swarm集群环境,使用--attachable可以在集群之外单独使用

    查看创建成功的overlay网络
    这个网卡是基于global(全局范围),也就是所有添加过consul端口的docker服务器都会创建这块网卡了

    # docker1
    [root@docker1 ~]# docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    49670536d26e        bridge              bridge              local
    dc8bfdbda464        host                host                local
    ecbab8a758e6        none                null                local
    a921406c8f1e        ov_net1             overlay             global
    # docker2
    [root@docker2 ~]# docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    b1240547891e        bridge              bridge              local
    fb1387ab7fc3        host                host                local
    a728ac0f7801        none                null                local
    a921406c8f1e        ov_net1             overlay             global
    

    查看overlay的网络信息

    [root@docker1 ~]# docker network inspect ov_net1 
                        "Subnet": "10.0.0.0/24",  # 网段
                        "Gateway": "10.0.0.1"  # 网关

使用overlay网络运行容器

docker1

[root@docker1 ~]# docker run -itd --name bbox1 --network ov_net1 busybox
d74c87e1bd18e1624207d162f75cce6c13892d86a2c13e41bfdc225602eefd23
[root@docker1 ~]# docker exec bbox1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    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
47: eth0@if48: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
    link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.2/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
50: eth1@if51: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1
       valid_lft forever preferred_lft forever
  • 会发现这个容器上除了lo网络还有两块网卡,eth0是overlay的网络的网卡,eth1默认路由要去使用的网卡
  • 这时候docker还会多出一块网卡docker_gwbridge,用来为所有的连接到overlay网络的容器提供了访问外网的能力
    [root@docker1 ~]# docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    49670536d26e        bridge              bridge              local
    da3b5eb55718        docker_gwbridge     bridge              local
    dc8bfdbda464        host                host                local
    ecbab8a758e6        none                null                local
    a921406c8f1e        ov_net1             overlay             global
  • 查看该网卡网段,与使用overlay网络的容器中是同一个网段,他本身也是一个overlay的网络
    [root@docker1 ~]# docker network inspect docker_gwbridge
                       "Subnet": "172.18.0.0/16",
                       "Gateway": "172.18.0.1"

    查看桥接网卡

  • 注意看,veth20a968a被桥接在docker_gwbridge网卡上
    [root@docker1 ~]# ip a
    ...
    51: veth20a968a@if50: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default 
        link/ether ee:16:6e:72:93:e6 brd ff:ff:ff:ff:ff:ff link-netnsid 2
        inet6 fe80::ec16:6eff:fe72:93e6/64 scope link 
           valid_lft forever preferred_lft forever
    [root@docker1 ~]# brctl show
    bridge name	bridge id		STP enabled	interfaces
    docker0		8000.024217bee795	no		veth3ed4284
    docker_gwbridge		8000.0242dbe46d3e	no		veth20a968a
    virbr0		8000.525400d7704d	yes		virbr0-nic
  • docker_gwbridge就是为容器连接外网的
     [root@docker1 ~]# docker exec bbox1 ping www.baidu.com
    PING www.baidu.com (61.135.169.121): 56 data bytes
    64 bytes from 61.135.169.121: seq=0 ttl=127 time=4.131 ms
  • 使用overlay网络的容器内映射端口也是没有问题的
    [root@docker1 ~]# docker run -p 80:80 -d --network ov_net1 --name web1 httpd:latest
    6b60e837f2b9984b0b9c0ae6ffa88e97f0ece4b8628a9fa5f4a64af9204aed44
    [root@docker1 ~]# curl 192.168.100.211
    <html><body><h1>It works!</h1></body></html>

跨主机容器通信

  • 要开启路由转发
  • 上面在docker1已经运行一个容器,现在在docker2中运行容器

    docker2

  • 使用overlay网络运行容器,并查看ip
    [root@docker2 ~]# docker run -itd --name bbox2 --network on_net1 busybox
    0723927f22394709d4d0deb20bae90a728dfd144083d474d65e058ac8e6731b6
    
    [root@docker2 ~]# docker exec bbox2 ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        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
    8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
        link/ether 02:42:0a:00:00:04 brd ff:ff:ff:ff:ff:ff
        inet 10.0.0.4/24 brd 10.0.0.255 scope global eth0
           valid_lft forever preferred_lft forever
    11: eth1@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
        link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
        inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1
           valid_lft forever preferred_lft forever
  • 通过ip发现,与docker1中同样的网卡网段,这由consul统一的数据分配的
  • 尝试与docker1的bbox1容器通信,成功~
    [root@docker2 ~]# docker exec bbox2 ping -c 2 bbox1
    PING bbox1 (10.0.0.2): 56 data bytes
    64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.739 ms
    64 bytes from 10.0.0.2: seq=1 ttl=64 time=1.134 ms
     
    --- bbox1 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.739/0.936/1.134 ms
  • 所有在consul集群中同一overlay网络中的容器可以互相通信
  • docker 会为每个 overlay 网络创建一个独立的 network namespace

    docker网络的存放位置

    [root@docker1 ~]# cd /var/run/docker/netns/
    [root@docker1 netns]# ll
    total 0
    -r--r--r--. 1 root root 0 Apr  2 22:09 1-a921406c8f
    -r--r--r--. 1 root root 0 Apr  2 22:22 244cf69f028a
    -r--r--r--. 1 root root 0 Apr  2 21:43 663ad80947aa
    -r--r--r--. 1 root root 0 Apr  2 22:09 e8cb0000018a
    
  • 为了可让ip netns直接可以查看docker网络,做一个软链接
    # docker1 and docker2
    ln -s /var/run/docker/netns/ /var/run/netns

    docker1

    [root@docker1 ~]# ip netns
    244cf69f028a (id: 3)
    e8cb0000018a (id: 2)
    1-a921406c8f (id: 1)
    663ad80947aa (id: 0)

    docker2

    [root@docker2 ~]# ip netns
    c8a0a325610f (id: 1)
    1-a921406c8f (id: 0)
  • 观察可以看到两台主机都有相同的网络1-a921406c8f,这就是两台主机所在的相同的网络命名空间,为什么说在同一个命名空间。

    docker1

      [root@docker1 ~]# docker exec bbox1 ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        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
    47: eth0@if48: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
        link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff
        inet 10.0.0.2/24 brd 10.0.0.255 scope global eth0
           valid_lft forever preferred_lft forever
    50: eth1@if51: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
        link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
        inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1
           valid_lft forever preferred_lft forever
  • 使用的overlay网卡是47@48的,再看物理机中是否有这样的网卡(没有)
  • 那这两块网卡就在另外一个网络命名空间范围

    进入ip netns查看到的相同命名空间可以看到如下

      [root@docker1 ~]# ip netns exec 1-a921406c8f ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        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
    2: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
        link/ether 46:1f:b7:ea:30:4e brd ff:ff:ff:ff:ff:ff
        inet 10.0.0.1/24 brd 10.0.0.255 scope global br0
           valid_lft forever preferred_lft forever
    46: vxlan0@if46: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN group default 
        link/ether d6:9f:87:b1:84:32 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    48: veth0@if47: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default 
        link/ether 46:1f:b7:ea:30:4e brd ff:ff:ff:ff:ff:ff link-netnsid 1
    53: veth1@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default 
        link/ether 56:41:d1:39:8b:e8 brd ff:ff:ff:ff:ff:ff link-netnsid 2
  • 再来看47@48桥接在这个命名空间内br0的网卡上,可以看到上方br0也是10网段,还存在一个vxlan0
  • 也就是说 vxlan0是跨主机通信的网卡,而veth0是访问外网的网卡,他们都桥接到br0上了
    [root@docker1 ~]# ip netns exec 1-a921406c8f brctl show
    bridge name	bridge id		STP enabled	interfaces
    br0		8000.461fb7ea304e	no		veth0
    							        veth1
    							        vxlan0
    
  • 跨主机通讯的前提是两个容器内的vxlan的域id相同
    [root@docker1 ~]# ip netns exec 1-a921406c8f ip -d l show vxlan0
    46: vxlan0@if46: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default 
        link/ether d6:9f:87:b1:84:32 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 noudpcsum noudp6zerocsumtx noudp6zerocsumrx
  • 可以看到是256
  • 如图所示,在创建ov_net1的时候,两台主机都有了这个overlay网卡,当在1.12中使用ov_net1运行bbox1时,生成了47@48的overlay网络和50@51的与1.12物理机通信的网络,同时也生成了一个网络的命名空间(1-a921406c8f)。
  • 在网络命名空间中,br网卡通过桥接veth0与bbox1的overlay网络相连,和桥接vxlan0实现跨主机通信
  • 与此同时,1.13中的bbox2是同理,他们之间的互相通信,因为他们的网络命名空间的相同和vxlan id相同,进而可以进行通信

overlay的隔离

  • 不同的 overlay 网络是相互隔离的。创建第二个overlay网络ov_net2来运行容器

创建第二个overlay网络
docker1

 # 创建一个新网卡
 [root@docker1 ~]# docker network create --driver overlay ov_net2
e2ddd9d7d1a609576c217d685cf8eb7e95b7ba74742afce5c31b8c7a1f53b0b1

运行容器

[root@docker1 ~]# docker run -itd --name bbox3 --network ov_net2 busybox
09a1c1a651aa4ffeb2ee8885f896c308d4a038fd47011ccfa3dcaa623d892edb

查看bbox3容器ip

[root@docker1 ~]# docker exec bbox3 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
    link/ether 02:42:0a:00:01:02 brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.2/24 brd 10.0.1.255 scope global eth0
       valid_lft forever preferred_lft forever
16: eth1@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
       valid_lft forever preferred_lft forever

bbox3 ping bbox1 和 bbox2

[root@docker1 ~]# docker exec -it bbox3 ping -c 2 bbox1
ping: bad address 'bbox1'
[root@docker1 ~]# docker exec -it bbox3 ping -c 2 bbox2
ping: bad address 'bbox2'

overlay跨网段通信

  • 接上面的bbox3
  • 通过个 bbox3的容器添加 一个 ov_net1网卡
    [root@docker1 ~]# docker network connect ov_net1 bbox3  # 将ov_net1网络连接到bbox3的网络
    [root@docker1 ~]# docker exec -it bbox3 ping -c 2 bbox2
    PING bbox2 (10.0.0.4): 56 data bytes
    64 bytes from 10.0.0.4: seq=0 ttl=64 time=0.189 ms
    --- bbox2 ping statistics ---
    1 packets transmitted, 1 packets received, 0% packet loss
    round-trip min/avg/max = 0.189/0.189/0.189 ms
    [root@docker1 ~]# docker exec -it bbox3 ping -c 2 bbox1
    PING bbox1 (10.0.0.2): 56 data bytes
    64 bytes from 10.0.0.2: seq=0 ttl=64 time=1.686 ms
    64 bytes from 10.0.0.2: seq=1 ttl=64 time=1.411 ms
    --- bbox1 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 1.411/1.548/1.686 ms

    查看bbox3的网卡变化

  • 该容器有了三块网卡 14 和 18 是用来连接 ov_net1 和 ov_net2的网卡 16 是连接 本地自动创建的网卡的 这三个都是新创建的
    [root@docker2 ~]# docker exec -it bbox3 ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        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
    14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
        link/ether 02:42:0a:00:01:02 brd ff:ff:ff:ff:ff:ff
        inet 10.0.1.2/24 brd 10.0.1.255 scope global eth0
           valid_lft forever preferred_lft forever
    16: eth1@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
        link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
        inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
           valid_lft forever preferred_lft forever
    18: eth2@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue 
        link/ether 02:42:0a:00:00:05 brd ff:ff:ff:ff:ff:ff
        inet 10.0.0.5/24 brd 10.0.0.255 scope global eth2
           valid_lft forever preferred_lft forever

手动指定overlay网段

[root@docker1 ~]# docker network create --driver overlay --subnet 10.22.1.0/24 --gateway 10.22.1.1 ov_net3
03a6a6ce682d4e911ea6f0ec328eb0efddd92f5724258f01bb4fb7d0c0acbdc6
[root@docker1 ~]# docker network inspect ov_net3
                    "Subnet": "10.22.1.0/24",

补充

  • 跨主机:消息能传到路由上(在物理网卡上联通,再传到路由上)
  • Docker network分为两种类型:本机通讯类型(none/host/bridge)
  • 跨主机通讯类型:(macvlan/pverlay)

跨主机通讯概念

  • 前面已经有了 Docker 的几种网络方案:none、host、bridge 和 joined 容器,它们解决了单个 Docker主机内容器通信的问题。
  • libnetwork & CNM
  • libnetwork 是 docker 容器网络库,最核心的内容是其定义的 Container Network Model (CNM),这个模型对容器网络进行了抽象,由以下三类组件组成:

    sandbox、endpoint、network

  • 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 等。
  • 如图所示两个容器,一个容器一个 Sandbox,每个 Sandbox 都有一个 Endpoint 连接到 Network,第二个 Sandbox 还有一个 Endpoint 将其接入 Network 2.
  • 由network的概念知道,同一network的endpoint可以直接通信,也就是与图中network向量的三个endpoint的sandbox以及web1都能够通信,而network2连接的sandbox就不可以,因为network2只有一个endpoint

本博客所有文章是以学习为目的,如果有不对的地方可以一起交流沟通共同学习 邮箱:1248287831@qq.com!