5

Docker | 跨主机通信

 2 years ago
source link: https://ijayer.github.io/post/tech/devops/docker/20170919-%E8%B7%A8%E4%B8%BB%E6%9C%BA%E9%80%9A%E4%BF%A1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Docker | 跨主机通信

2017-09-19 5095 words 11 mins read 7 times read

Overlay Network?

An overlay network is a computer network that is built on top of another network. 覆盖网络,是一个建立在另一个网络上的计算机网络。覆盖网络中的节点被认为是通过虚拟或逻辑链接相连的,其中每一条链接对应一条路径(PATH)。Wikipedia基于多租户的云计算Overlay网络

Overlay的主要技术标准:VXLAN

  • VXLAN(Virtual Extensible LAN: 虚拟可扩展局域网):是一种Overlay网络(在网络之上构建的一层网络),基于隧道技术实现。
  • Linux Bridge默认支持VXLAN, OpenVSwitch也支持VXLAN
  • VxLan 类似于 VLan技术,最大的不同点是,VxLan可以拥有更多的网段,远远大于VLan的网段。 VLan只支持4094个网段。
  • VXLAN 的数据包是封装到UDP通过三层网络转发,可使用所有的网络路径; VXLAN的传输协议是 IP+UDP

Docker网络架构和libnetwork

libnetwork 是Docker容器网络库,最核心的内容是其定义的 Container Network Model(CNM), 这个模型对容器网络进行了抽象

CNM 主要由以下三类组件构成:

  • Sandbox(沙箱):容器的网络栈,包含容器的Interface、路由表和DNS设置。Linux Network Namespace 是sandbox的标准实现。sandbox可以包含来自不同Network的Endpoint

  • Endpoint(端点):用于将sandbox接入Network。Endpoint的典型实现是 veth pair。一个 Endpoint只能属于一个网络,也只能属于一个sandbox

  • Network(网络):Network包含一组Endpoint,同一Network的Endpoint可以直接通信。

架构示意图:

libnetwork中的5种内置驱动:

  • bridge 驱动:Docker默认的设置,使用这个驱动的时候,libnetwork将创建出来的Docker容器链接到Docker网桥上。

  • host 驱动:使用这种驱动的时候,libnetwork将不会为Docker容器创建网络协议栈,也就是不会创建独立的network namespace。Docker容器中的进程处于宿主机的网络环境中。

  • overlay 驱动:该驱动采用标准的VxLANFANGSHI, 并且VxLan被普遍认为是最适合大规模的云计算虚拟化环境的SDN controller模式。在使用过程中,需要一个额外的配置存储服务

  • remote 驱动:这个驱动并没有做真正的网络实现,而是调用了用户自行实现的网络驱动插件,使libnetwork实现了驱动的可插件化。

  • null 驱动:使用这种驱动的时候,Docker容器拥有自己的network namespace,但是不会进行任何网络配置。

相关方案简介

基于桥接的跨主机通信

基于桥接的跨主机通信

目前, Docker默认网络模式(bridge)下,单台主机上的Docker容器间可通过docker0网桥通信, 而不同主机上的Docker容器间只能通过在主机上做端口映射的方法通信。这种方案在Docker集群应用时很不方便, 如果可以直接使用容器IP跨主机通信则会简便很多。

基于Overlay的跨主机通信

Docker支持使用Overlay网络驱动实现跨主机网络通信

不同于 bridge 网络,使用overlay网络需要满足一下条件中的任意一个:

  • Docker运行在 swarm 模式
  • 一个键值存储集群(ZooKeeper | etcd | consul)

Note: 官方文档中Docker overlay网络是和Swarm共存的

基于Overlay网络和一个外部Key-Value存储数据库搭建Docker跨主机通信

参考方案·Docker Overlay跨主机通信

Overview

使用Docker Overlay网络,需要注意一下几点:

  • 一个可访问的键值存储系统:Docker支持 ConsulEtcdZooKeeper(分布式)键值存储。

  • 一个Docker Host集群,他们要链接到键值存储,并且集群内每个Docker Host的主机名必须要唯一。因为Docker守护进程与consul通信时,会以主机名相互区分。

  • 配置正确的Docker Daemon启动参数。让所有的Docker Host都可以访问集群key-value的服务端口

通过官网给出的需求,可以知道:

  • Docker Daemon要开启远程管理的端口(tcp xxx),这个端口可以用来远程给Docker(极度危险)

  • Docker Daemon还要开启一个tcp/udp的7946端口,Docker通过这个端口,用gossip协议学习各个宿主机上运行了那些容器。

  • VxLan本身也需要一个UDP的4789端口

Note:要在防火墙开启这些端口

实验环境说明

实验主机:

Host OS Description
192.168.1.180 Centos7 Kernel 3.10.0 Docker主机_A(搭建consul数据库)
192.168.1.181 Centos7 Kernel 3.10.0 Docker主机_B

启动Consul数据库

Consul 的相关介绍

Consul:一个用于服务发现和配置共享的服务软件,由HashiCorp公司用Go开发;Consul支持健康检查、并允许HTTP和DNS协议调用API存储键值对

在这里,我们使用Consul来保存Overlay的网络状态信息,包括Endpoint、Network & IP等;当然,我们不需要写代码,只需要安装Consul, 之后Docker会自动进行状态存储等。

https://blog.coding.net/blog/intro-consul

  • 安装Consul

最简单的安装方式即运行Docker.Consul容器哦, 这里 consul 服务搭建在主机:192.168.1.180

# 拉取镜像
$ docker pull progrium/consul

# 启动consul服务
$ docker run --resatrt=always -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap

# 查看consul容器是否运行成功
docker ps 

启动后,可在浏览器输入 host:port 可以看到Nodes节点已经添加了Consul;这里host:port=192.168.1.180:8500

配置Docker服务

Note: 所有加入Overlay网络的Docker Host都需要完成如下配置,且所有Docker Host主机名必须唯一

  • 修改Docker启动参数 ```bash

    修改 docekr.service

    $ vim /usr/lib/systemd/system/docker.service

修改 ExecStart 选项如下

ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock –cluster-store=consul://192.168.1.180:8500 –cluster-advertise=ens33:2376

> Note: 
> 
> - --cluster-store: 指定键值存储的地址(Consul节点IP)
> - --cluster-advertise: 告诉存储服务自己的连接地址(ens33为docker节点IP地址所在的网卡名)

- 重启Docker服务

```bash
$ systemctl daemon-reload
$ systemctl restart docker.service

Note: 可以看到 docker/nodes下已经有两个节点

创建Overlay网络

  • 在HostA(192.168.1.180)上创建Overlay网络
# 创建 Overlay 网络 ovnet1, -d 指定网络模式
$ docker network create -d overlay ovnet1

# 创建 Overlay 网络时, 指定其子网和网关
$ docker network create -d overlay ovnet2 --subnet 172.19.0.0/24 --gateway 172.19.0.1

Note: 在之后运行容器时指定 –network=ovnet1即可使Overlay网络内的Docker主机实现通信

  • 在HostA(192.168.180)上查看网络
# Lookup HostA Docker Network
$ docker network ls 
NETWORK ID          NAME                DRIVER              SCOPE
2315b20ebe40        bridge              bridge              local
45fcb93505c0        docker_gwbridge     bridge              local
9ccf08201f10        host                host                local
e15bf8515ecd        none                null                local
0c7ad0717a77        ovnet1              overlay             global

Note: 刚才创建的ovnet1网络作用域(SCOPE)是global, 而其他为local

  • 在HostB(192.168.1.181)上查看网络
# Lookup HostB Docker Network
$ docker network ls 
NETWORK ID          NAME                DRIVER              SCOPE
2315b20ebe40        bridge              bridge              local
45fcb93505c0        docker_gwbridge     bridge              local
9ccf08201f10        host                host                local
e15bf8515ecd        none                null                local
0c7ad0717a77        ovnet1              overlay             global

Note:

  • 我们没有在HostB上创建网络,而这里可以发现HostA上创建的网络 ovnet1;这是因为创建ovnet1时HostA将overlay的网络信息写入了consul,而HostB从consul中读取到了网络数据完成了配置; 之后ovnet1的任何变化都会同步到HostA和HostB
  • 查看网络详细配置
$ docker inspect ovnet1

[
    {
        "Name": "ovnet1",
        "Id": "0c7ad0717a77b329357a251f79f43f8889be819313334249be45a4f04677a0de",
        "Created": "2017-09-21T08:51:50.324849876+08:00",
        "Scope": "global",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

Note: IPAM, 表示IP Address Management

创建容器并接入Overlay网络

$ docker run --name cotan1 --network ovnet1 -itd busybox
  • 查看容器内部网络
$ docker exec -it cotan1 sh
$ ip address

1: lo: <LOOPBACK,UP,LOWER_UP> 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
16: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP 
    link/ether 02:42:0a:00:00:05 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.5/24 scope global eth0
       valid_lft forever preferred_lft forever
26: eth1@if27: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:12:00:05 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.5/16 scope global eth1
       valid_lft forever preferred_lft forever

$ ip route 

default via 172.18.0.1 dev eth1 
10.0.0.0/24 dev eth0  src 10.0.0.5 
172.18.0.0/16 dev eth1  src 172.18.0.5

Note: 可以看到容器内部有两块网卡, eth0连接overlay网络,eth1连接172.18.0.5; 且默认路由是从eth1网卡出去的

  • 查看宿主机网桥信息
# 在HostA执行
$ ip address

1: lo: <LOOPBACK,UP,LOWER_UP> 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:c5:85:c4 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.180/24 brd 192.168.1.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::c517:b883:c99a:290c/64 scope link 
       valid_lft forever preferred_lft forever
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN qlen 1000
    link/ether 52:54:00:9e:02:43 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN qlen 1000
    link/ether 52:54:00:9e:02:43 brd ff:ff:ff:ff:ff:ff
5: docker_gwbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:31:81:ce:4e brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 scope global docker_gwbridge
       valid_lft forever preferred_lft forever
    inet6 fe80::42:31ff:fe81:ce4e/64 scope link 
       valid_lft forever preferred_lft forever
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:a8:41:f3:9e brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:a8ff:fe41:f39e/64 scope link 
       valid_lft forever preferred_lft forever
8: veth7f764b0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP 
    link/ether 66:03:b1:8f:c2:af brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::6403:b1ff:fe8f:c2af/64 scope link 
       valid_lft forever preferred_lft forever

可以看到多了 docker_gwbridge 网卡; 且Docker容器的eth1网卡连接的是docker_gwbridge网桥

测试连通性

  • 在HostB创建容器

    $ docker run --name cotan2 --network ovnet1 -itd busybox
  • 查看容器IP地址并测试连接

$ docker exec -it cotan2 sh
$ ip address 

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
9: eth0@if10: <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 scope global eth0
       valid_lft forever preferred_lft forever
13: eth1@if14: <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 scope global eth1
       valid_lft forever preferred_lft forever

$ ping 10.0.0.5

64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.588 ms
64 bytes from 10.0.0.2: seq=1 ttl=64 time=0.417 ms
 
--- container1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.417/0.502/0.588 ms

可见overlay网络中的容器可以直接通信,同时Docker也实现了DNS服务

该方案存在的问题

http://www.chongchonggou.com/g_6115686.html

宿主机如何简单高效的访问Docker自带的Overlay网络中的所有容器(包括其他宿主机上的)

在Docker自带的Overlay网络中,容器内部访问外网使用的是自动创建的docker_gwbridge桥,那么 docker 宿主机如何访问overlay网络内部呢(比如其他宿主机上同时加入该overlay网络的容器)?

解决方法有如下几种:

  • 尝试把到overlay路由指到docker_gwbridge,但是无法访问其他宿主机上加入到这个overlay的容器,因为没有回程路由,给每台启动的容器加回程路由也是很不现实的。
  • 宿主机上,容器内都开反向代理。
  • 容器内开启iptables,做nat。
  • 传统模式给容器做nat映射宿主机端口。
  • 如果能指定overlay中的某个容器为整个overlay的默认路由的话,一切都好办了,奈何目前到1.10.2,gateway都会被自动指到某个网桥上,只能每台容器去修改,但是给每台容器赋予这么高的权限,个人是不愿意的。

Overlay网络实现原理

总结Overlay网络

  • Overlay网络会自动创建一个docker_gwbridge网卡,作用是为Docker容器提供上外网需求
  • Docker容器不能通过docker_gwbridge网卡互相通信,即使在同一台Docker主机, 同一个Overlay网络也不行
  • Docker容器间通信只能通过overlay网络
  • Overlay网络间是相互隔离的,通过VXLan隔离
  • Docker容器的网络命名空间与Overlay网络的命名空间通过一对veth pair连接起来
  • Docker容器的veth pair对端veth0与vxlan0设备通过br0这个Linux bridge桥接在一起, br0在同一宿主机上起到虚拟交换机的作用,如果目标地址在同一宿主机上,则直接通信,如果不在则通过设置vxlan0这个VXLan设备进行跨主机通信
  • Overlay网络独立的命名空间里面会有一个网桥br0
  • VXLan0设备会在创建时,由Docker daemon 为其分配vxlan隧道ID,起到网络隔离的作用
  • Docker Host集群会通过key/value存储共享数据,在7496端口上,互相之间通过gossip协议学习各个宿主机上运行了那些容器。守护进程根据这些数据在vxlan0设备上生成静态MAC转发表
  • 根据静态MAC转发表的设置,通过UDP端口4789,将流量转发到对端宿主机网卡上
  • 根据流量包中的VxLan隧道ID,将流量转发到对端宿主机的overlay网络的网络命名空间中。
  • 对端宿主机的overlay网络的网络命名空间中br0网桥,起到虚拟交换机的作用,将流量根据MAC地址转发到对应容器内部。

Issue

  • Issue1
[root@zhe0 ~]# docker run --name cotan1 --network ovnet1 --ip 10.0.0.10 -d busybox
402fb4295afa1fcf9ba7cc5493c9dedecf50edee1983fbbad376e8b93e560d20
docker: Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.
  • Issue2
在 ExecStart 最后添加: --cluster-store=consul://<consul_ip>:8500 --cluster-advertise=ens3:2376
其中 表示运行 consul 容器的节点IP。ens3为当前节点的ip地址对应的网卡

注意: ens3换成IP地址可能导致容器跨主机通信失败

See Also

Thanks to the authors 🙂


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK