Docker网络教程(三)overlay 网络教程

使用 overlay 网络联网

本系列的教程处理有关于 swarm 服务有关的网络。对于独立容器的网络,参考 独立容器联网教程。 如果想了解更多 Docker 网络的理论知识,参考概述

本文包括四个不同的教程。你可以在Linux、Windows或Mac上运行它们中的每一个,但对于最后两个,你需要在其他地方运行第二个Docker主机。

  • 使用默认的 overlay 网络演示了如何使用 Docker 在你初始化或加入 swarm 时为你自动设置的默认 overlay 网络。这个网络不是生产环境中的最佳选择。

  • 使用用户定义的 overlay 网络演示了如何创建和使用你自己的自定义 overlay 网络,以连接服务。建议在生产环境中运行的服务使用这个方法。

  • 给独立容器用上 overlay 网络显示了如何使用 overlay 网络在不同Docker daemon 的独立容器之间进行通信。

  • 容器和 swarm 服务之间的通信在独立的容器和 swarm 服务之间建立通信,使用了一个可附加的 overlay 网络。

先决条件

这些要求你至少有一个单节点的 swarm,这意味着你已经启动了 Docker 并在宿主机上运行 docker swarm init 。你也可以在多节点 swarm 上运行这些例子。

使用默认的 overlay 网络

在这个例子中,你启动一个 alpine 服务,并在各个服务容器里检查网络的特性。

本教程不涉及关于如何实现 overlay 网络的操作系统的具体细节,而是侧重于从服务的角度看 overlay 如何运作。

先决条件

本教程需要三个物理或虚拟的Docker主机,它们都可以相互通信。本教程假设这三个主机运行在同一个网络上,并且没有防火墙干扰。

这些主机将被称为 managerworker-1跟 worker-2。 manager 主机将同时作为管理者和 worker 发挥作用,这意味着它既可以运行服务任务,也可以管理 swarm;worker-1 和 worker-2 将只作为 worker 发挥作用。

如果你手头没有三台主机,一个简单的解决方案是在云服务提供商(如亚马逊EC2)上设置三台Ubuntu主机,它们都在同一个网络上,并且允许该网络上的所有主机进行通信(使用EC2安全组等机制),然后按照Docker Engine 安装说明 - Community on Ubuntu的安装说明操作。

演练

创建 swarm

在这个过程结束时,所有三个Docker主机都将加入到 swarm 中,并将使用一个名为ingress的覆盖网络连接在一起。

  1. 在 manager上初始化 swarm。如果宿主机只有一个网络接口,则 --advertise-addr 标志是可选的。

    $ docker swarm init --advertise-addr=<IP-ADDRESS-OF-MANAGER>
    

    记下打印出来的文本,因为它包含您将用于将 worker-1 和 worker-2 加入蜂群的令牌。将该令牌存储在某个密码管理器中是个好主意。

  2. 在 worker-1上加入swarm。如果主机只有一个网络接口,则 --advertise-addr 标志是可选的。

    $ docker swarm join --token <TOKEN> \
      --advertise-addr <IP-ADDRESS-OF-WORKER-1> \
      <IP-ADDRESS-OF-MANAGER>:2377
    
  3. 在 worker-2  上加入swarm。如果主机只有一个网络接口,则 --advertise-addr 标志是可选的。

    $ docker swarm join --token <TOKEN> \
      --advertise-addr <IP-ADDRESS-OF-WORKER-2> \
      <IP-ADDRESS-OF-MANAGER>:2377
    
  4. 在 manager 上列出所有的节点。此命令只会在管理节点上生效。

    $ docker node ls
    
    ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
    d68ace5iraw6whp7llvgjpu48 *   ip-172-31-34-146    Ready               Active              Leader
    nvp5rwavvb8lhdggo8fcf7plg     ip-172-31-35-151    Ready               Active
    ouvx2l7qfcxisoyms8mtkgahw     ip-172-31-36-89     Ready               Active
    

    你也可以用 --filter 标志来根据节点的角色进行过滤显示:

    $ docker node ls --filter role=manager
    
    ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
    d68ace5iraw6whp7llvgjpu48 *   ip-172-31-34-146    Ready               Active              Leader
    
    $ docker node ls --filter role=worker
    
    ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
    nvp5rwavvb8lhdggo8fcf7plg     ip-172-31-35-151    Ready               Active
    ouvx2l7qfcxisoyms8mtkgahw     ip-172-31-36-89     Ready               Active
    
  5. 列出 manager、 worker-1 和 worker-2 上的 Docker 网络,并注意到它们现在都有一个名为  ingress 的 overlay 网络和一个名为 docker_gwbridge 的 bridge 网络。这里只显示 manager 的:

    $ docker network ls
    
    NETWORK ID          NAME                DRIVER              SCOPE
    495c570066be        bridge              bridge              local
    961c6cae9945        docker_gwbridge     bridge              local
    ff35ceda3643        host                host                local
    trtnl4tqnc3n        ingress             overlay             swarm
    c8357deec9cb        none                null                local
    

 docker_gwbridge 将 ingress 网络连接到 Docker 宿主机的网络接口上,这样流量就可以在 swarm管理者和 worker 之间流动。如果你创建了 swarm 服务,但没有指定网络,它们就会被连接到 ingress 网络上。建议你为每个应用程序或将一起工作的应用程序组使用单独的 overlay 网络。在接下来的步骤中,你将创建两个覆盖网络,并将一个服务连接到每个网络。

创建服务

  1. 在 manager 上,创建一个新的 overlay 网络,取名为 nginx-net:

    $ docker network create -d overlay nginx-net
    

    你不需要在其他节点上创建 overlay 网络,因为当这些节点开始运行一个需要它的服务任务时,它将自动创建。

  2. 在 manager 上创建有5个副本的 Niginx 服务,并连接到 nginx-net 。此服务会发布80端口到外界。所有任务下的容器,都可以自由通讯,而不需端口发布。

    注意: 服务只能在管理节点上创建

    $ docker service create \
      --name my-nginx \
      --publish target=80,published=80 \
      --replicas=5 \
      --network nginx-net \
      nginx
    

    当你没有用 --publish 标志指定发布模式时, ingress会启用默认模式。默认模式下,你在  managerworker-1, 或者 worker-2 上浏览到端口 80,你将被连接到 5 个服务任务中的一个的 80 端口,即使你浏览到的节点上当前没有任务在运行。如果你想使用 host 模式发布端口,你可以在 --publish 输出中添加 mode=host 。然而,在这种情况下,你也应该使用 --mode global 而不是 --replicas=5 ,因为在一个给定的节点上只有一个服务任务可以绑定一个给定的端口。

  3. 运行 docker service ls 指令,来监控服务启动起来的进程,这也许需要几分钟。

  4. 在 managerworker-1, 和 worker-2 上 inspect 一下 nginx-net 网络。记得你不需要在 worker-1 和 worker-2 上创建它,Docker 会自动帮你创建。输出会很长,关注  Containers 跟 Peers 部分。 Containers 列出在这台主机上所有连接到此 overlay 网络的服务任务(或者独立的容器)。

  5. 在 manager 上,用 docker service inspect my-nginx 指令 inspect 一下服务,注意服务用到的端口(ports)跟端点(endpoints)。

  6. 创建新的网络 nginx-net-2 ,接着更新服务连接到它而不是 nginx-net:

    $ docker network create -d overlay nginx-net-2
    
    $ docker service update \
      --network-add nginx-net-2 \
      --network-rm nginx-net \
      my-nginx
    
  7. 运行 docker service ls 来验证服务是否已经更新,所有任务是否已经重新部署。运行 docker network inspect nginx-net ,确认没有容器连接到它。对 nginx-net-2 运行同样的命令,注意所有的服务任务容器都连接到了它。

    注意: 即使 overlay 网络是根据需要在 swarm 工作节点上自动创建的,它们也不会被自动删除。

  8. 清理服务跟网络。在 manager 上运行下面的命令。管理节点会指引 worker 自动删除掉对应的网络。

    $ docker service rm my-nginx
    $ docker network rm nginx-net nginx-net-2
    

使用用户定义的 overlay 网络

先决条件

此教程会假设 swarm 已经设置好并且你在管理节点上。

演练

  1. 创建用户定义的 overlay 网络

    $ docker network create -d overlay my-overlay
    
  2. 启动一个服务,使用 overlay 网络并且在 Docker 宿主机上发布 80 跟 8080 端口。

    $ docker service create \
      --name my-nginx \
      --network my-overlay \
      --replicas 1 \
      --publish published=8080,target=80 \
      nginx:latest
    
  3. 运行 docker network inspect my-overlay 命令,通过观察 Containers 部分,验证 my-nginx 服务下的任务已经连接到它。

  4. 删除测试的服务以及网络。

    $ docker service rm my-nginx
    
    $ docker network rm my-overlay
    

给独立容器用上 overlay 网络

这个例子演示了 DNS 容器发现——具体来说,就是如何使用 overlay 网络在不同 Docker daemon 的独立容器之间进行通信。步骤是:

  • 在 host1,初始化为 swarm 的管理节点。
  • 在 host2,加入 swarm 成为 worker 节点。
  • 在 host1,创建一个可附加(attachable)overlay 网络(test-net)。
  • 在 host1,于 test-net网络上运行一个交互式un an interactive alpine 容器 (alpine1) 。
  • 在 host2,于 test-net网络上运行一个交互式、分离的(亦即后台) alpine 容器(alpine2) 。
  • 在 host1, 在 alpine1 的会话里 ping alpine2。

先决条件

在此项测试中,你需要两个独立的、可以通讯的 Docker 宿主机。每个主机需要对彼此开放以下的端口:

  • TCP port 2377
  • TCP and UDP port 7946
  • UDP port 4789

一个简单的把这些配置起来的方法,是拥有两台虚拟机(无论是本地虚拟的还是在云厂商比如亚马逊云那里购买),每一台都已经安装并运行好 Docker。如果你正在使用亚马逊云或者其他类似的云计算平台,最简单的配置方法是使用一个安全组,安全组里开放两个主机之间的所有端口以及对你 ssh 客户端的IP开放 ssh 端口。

这个例子中,我们把两个 swarm 节点分别称为 host1 和 host2。同事,例子使用的是 Linux 主机,但同样的命令也可以在 Windows 下面起作用。

演练

  1. 设置好 swarm。

    a. 在 host1, 初始化一个 swarm (如果有提示,使用 --advertise-addr 来指定与 swarm 中其他主机通信的接口的IP地址,例如,AWS的私有IP地址):

    $ docker swarm init
    Swarm initialized: current node (vz1mm9am11qcmo979tlrlox42) is now a manager.
    
    To add a worker to this swarm, run the following command:
    
        docker swarm join --token SWMTKN-1-5g90q48weqrtqryq4kj6ow0e8xm9wmv9o6vgqc5j320ymybd5c-8ex8j0bc40s6hgvy5ui5gl4gy 172.31.47.252:2377
    
    To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
    

    b. 在 host2, 按照上面的指示,加入创建好的 swarm:

    $ docker swarm join --token <your_token> <your_ip_address>:2377
    This node joined a swarm as a worker.
    

    如果节点加入 swarm 失败, docker swarm join 命令会超时。要解决这个问题,可以在 host2运行  docker swarm leave --force ,验证一下你的网络跟防火墙的设置是否正常,然后再试一次。

  2. 在 host1, 船舰一个可附加的 overlay 网络,叫 test-net:

    $ docker network create --driver=overlay --attachable test-net
    uqsof8phj3ak0rq9k86zta6ht
    

    注意返回的 网络 ID -- 当你从 host2连接到它时,你会再次看到它。

  3. 在 host1, 启动一个可交互 (-it) 容器(alpine1) ,连接到 test-net:

    $ docker run -it --name alpine1 --network test-net alpine
    / #
    
  4. 在 host2, 列出所有可用的网络,注意到 test-net 还没有创建:

    $ docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    ec299350b504        bridge              bridge              local
    66e77d0d0e9a        docker_gwbridge     bridge              local
    9f6ae26ccb82        host                host                local
    omvdxqrda80z        ingress             overlay             swarm
    b65c952a4b2b        none                null                local
    
  5. 在 host2, 启动一个分离式 (-d) 和交互式 (-it) 容器 (alpine2) ,连接到 test-net:

    $ docker run -dit --name alpine2 --network test-net alpine
    fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342
    

    自动的容器 DNS 发现功能,只会在容器名唯一时有效。

  6. 在 host2, 验证一下 test-net 已经被创建出来 (它的网络 ID 跟刚刚在 host1 上的 test-net 看到的一致):

     $ docker network ls
     NETWORK ID          NAME                DRIVER              SCOPE
     ...
     uqsof8phj3ak        test-net            overlay             swarm
    
  7. 在 host1, 用 alpine1 容器的交互终端 ping alpine2 :

    / # ping -c 2 alpine2
    PING alpine2 (10.0.0.5): 56 data bytes
    64 bytes from 10.0.0.5: seq=0 ttl=64 time=0.600 ms
    64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.555 ms
    
    --- alpine2 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.555/0.577/0.600 ms
    

    这两个容器与连接两台主机的 overlay 网络进行通信。如果你在 host2 上运行另一个没有分离not detached)的 alpine 容器,你可以从 host2 上ping alpine1 (这里我们添加了 移除选项,用于自动清理容器):

    $ docker run -it --rm --name alpine3 --network test-net alpine
    / # ping -c 2 alpine1
    / # exit
    
  8. 在 host1, 关掉 alpine1 会话 (容器会跟着停止掉):

    / # exit
    
  9. 清理你的容器跟网络:

    你必须分别在不同的主机上停止跟移除容器,因为两个 Docker daemon 是独立运行的,并且容器也是独立的容器。你只需要在 host1 上移除网络,因为当你在 host2 上停止 alpine2 时, test-net 也跟着消失了。

    a. 在 host2 停止 alpine2, 检查 test-net 已经被移除,接着移除 alpine2:

    $ docker container stop alpine2
    $ docker network ls
    $ docker container rm alpine2
    

    a. 在 host1 移除 alpine1 和 test-net:

    $ docker container rm alpine1
    $ docker network rm test-net
    

容器和 swarm 服务之间的通信

在这个例子中,你在同一台 Docker 宿主机上启动两个不同的 alpine 容器,并做测试,以了解它们如何相互通信。你需要安装并运行 Docker。

  1. 打开一个终端窗口。在你做其他事情之前,列出当前的网络。如果你从未在这个 Docker daemon上添加过网络或初始化过 swarm,你应该看到以下内容。你可能会看到不同的网络,但你至少应该看到这些(网络ID会不同):

    $ docker network ls
    
    NETWORK ID          NAME                DRIVER              SCOPE
    17e324f45964        bridge              bridge              local
    6ed54d316334        host                host                local
    7092879f2cc8        none                null                local
    

    默认的 bridge 网络在 host 和 none 中被列出。后两者并不是“完整意义”上的网络,只是用来启动一个直接连接到宿主机的网络栈的容器,又或者是一个没有网络设备的容器。此教程会把两个容器连接到 bridge 网络上。

  2. 启动两个运行 ash 的 alpine 容器,这是 Alpine 的默认shell而不是 bash 。 -dit 标志意味着启动的容器是分离的(在后台)、交互式的(能够向其中输入文字)和有一个TTY(所以你可以看到输入和输出)。由于你是以分离方式启动的,所以你不会立即与容器连接。相反,容器的ID将被打印出来。因为你没有指定任何 --network 标志,所以容器会连接到默认的 bridge 网络。

    $ docker run -dit --name alpine1 alpine ash
    
    $ docker run -dit --name alpine2 alpine ash
    

    检查两个容器都已经正常启动:

    $ docker container ls
    
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    602dbf1edc81        alpine              "ash"               4 seconds ago       Up 3 seconds                            alpine2
    da33b7aa74b0        alpine              "ash"               17 seconds ago      Up 16 seconds                           alpine1
    
  3. Inspect 一下 bridge 网络,看看哪些容器连接到了它。

    $ docker network inspect bridge
    
    [
        {
            "Name": "bridge",
            "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
            "Created": "2017-06-22T20:27:43.826654485Z",
            "Scope": "local",
            "Driver": "bridge",
            "EnableIPv6": false,
            "IPAM": {
                "Driver": "default",
                "Options": null,
                "Config": [
                    {
                        "Subnet": "172.17.0.0/16",
                        "Gateway": "172.17.0.1"
                    }
                ]
            },
            "Internal": false,
            "Attachable": false,
            "Containers": {
                "602dbf1edc81813304b6cf0a647e65333dc6fe6ee6ed572dc0f686a3307c6a2c": {
                    "Name": "alpine2",
                    "EndpointID": "03b6aafb7ca4d7e531e292901b43719c0e34cc7eef565b38a6bf84acf50f38cd",
                    "MacAddress": "02:42:ac:11:00:03",
                    "IPv4Address": "172.17.0.3/16",
                    "IPv6Address": ""
                },
                "da33b7aa74b0bf3bda3ebd502d404320ca112a268aafe05b4851d1e3312ed168": {
                    "Name": "alpine1",
                    "EndpointID": "46c044a645d6afc42ddd7857d19e9dcfb89ad790afb5c239a35ac0af5e8a5bc5",
                    "MacAddress": "02:42:ac:11:00:02",
                    "IPv4Address": "172.17.0.2/16",
                    "IPv6Address": ""
                }
            },
            "Options": {
                "com.docker.network.bridge.default_bridge": "true",
                "com.docker.network.bridge.enable_icc": "true",
                "com.docker.network.bridge.enable_ip_masquerade": "true",
                "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
                "com.docker.network.bridge.name": "docker0",
                "com.docker.network.driver.mtu": "1500"
            },
            "Labels": {}
        }
    ]
    

    在顶部附近,列出了 bridge 网络的信息,包括Docker主机和 bridge 网络之间的网关的IP地址(172.17.0.1)。在 Containers 键下,列出了每个连接的容器,以及关于其IP地址的信息(172.17.0.2 为 alpine1 , 172.17.0.3 为 alpine2)。

  4. 两个容器都在后台运行着,用 docker attach 命令连接 alpine1.

    $ docker attach alpine1
    
    / #
    

    提示符变成了 # ,表示你是容器内的 root 用户。使用 ip addr show 命令来显示 alpine1 的网络接口,就像这样:

    # ip addr show
    
    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
    27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
        link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.2/16 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::42:acff:fe11:2/64 scope link
           valid_lft forever preferred_lft forever
    

    第一个接口是回环设备,暂时忽略。注意第二个接口的IP地址是 172.17.0.2,这与上一步中显示的 alpine1 的地址相同。

  5. 在 alpine1 中,通过 ping 百度 baidu.com 来确认你已经连接到互联网。-c 2 标志限定了命令执行两次 ping 。

    # ping baidu.com -c 2
    PING baidu.com (39.156.66.10): 56 data bytes
    64 bytes from 39.156.66.10: seq=0 ttl=47 time=42.544 ms
    64 bytes from 39.156.66.10: seq=1 ttl=47 time=42.523 ms
    
    --- baidu.com ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 42.523/42.533/42.544 ms
  6. 现在试试 ping 第二个容器。首先用它的IP地址  172.17.0.3  试试:

    # ping -c 2 172.17.0.3
    
    PING 172.17.0.3 (172.17.0.3): 56 data bytes
    64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.086 ms
    64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.094 ms
    
    --- 172.17.0.3 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.086/0.090/0.094 ms
    

    成功了。接着,试试用容器名去 ping alpine2 容器。这应该会失败。

    # ping -c 2 alpine2
    
    ping: bad address 'alpine2'
    
  7. 用 Detach 操作序列从 alpine1 断开, CTRL + p CTRL + q (按下 CTRL 然后依次键入 p 、 q)。 如果你愿意,也可以连接进 alpine2 来重复步骤4、5、6,用 alpine1 代替 alpine2。 

  8. 停止并移除所有的容器。

    $ docker container stop alpine1 alpine2
    $ docker container rm alpine1 alpine2
    

记住,默认的 bridge 网络不建议用在生产环境中。了解更多的用户定义 bridge 网络,请继续下一个教程

其他的网络教程

现在你已经完成了 overlay 网络联网的教程,你可能想跑一下其他的:

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值