使用覆盖网络联网

本系列教程讨论了针对 Swarm 服务的网络。有关独立容器的网络,请参阅 独立容器的网络。如果您需要了解有关 Docker 网络的更多信息,请参阅 概述

本页包含以下教程。您可以在 Linux、Windows 或 Mac 上运行每个教程,但对于最后一个教程,您需要另一个在其他位置运行的 Docker 主机。

  • 使用默认覆盖网络 演示了如何使用 Docker 在您初始化或加入 Swarm 时自动为您设置的默认覆盖网络。此网络不是生产系统的最佳选择。

  • 使用用户定义的覆盖网络 展示了如何创建和使用您自己的自定义覆盖网络来连接服务。建议在生产环境中运行的服务使用此方法。

  • 将覆盖网络用于独立容器 展示了如何使用覆盖网络在不同 Docker 守护程序上的独立容器之间进行通信。

先决条件

这些教程要求您至少拥有单个节点 Swarm,这意味着您已启动 Docker 并在主机上运行了 docker swarm init。您也可以在多节点 Swarm 上运行这些示例。

使用默认覆盖网络

在此示例中,您启动 alpine 服务并从各个服务容器的角度检查网络的特性。

本教程不会深入介绍有关覆盖网络实现方式的操作系统特定细节,而是重点介绍覆盖网络从服务角度的功能。

先决条件

本教程需要三个物理或虚拟 Docker 主机,这些主机可以彼此通信。本教程假设这三个主机在同一个网络上运行,并且没有涉及防火墙。

这些主机将分别称为 managerworker-1worker-2manager 主机将同时充当管理器和工作节点,这意味着它可以同时运行服务任务和管理 Swarm。worker-1worker-2 将仅充当工作节点。

如果您没有三个主机,一个简单的解决方案是在像 Amazon EC2 这样的云提供商上设置三个 Ubuntu 主机,这些主机都在同一个网络上,并且所有主机之间都允许通信(使用 EC2 安全组等机制),然后按照 Ubuntu 上 Docker Engine - Community 的安装说明 进行操作。

演练

创建 Swarm

完成此过程后,所有三个 Docker 主机都将加入 Swarm,并将使用名为 ingress 的覆盖网络连接在一起。

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

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

    记下打印的文本,因为其中包含用于将 worker-1worker-2 加入 Swarm 的令牌。建议将令牌存储在密码管理器中。

  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. 列出 managerworker-1worker-2 上的 Docker 网络,并注意它们现在都具有一个名为 ingress 的覆盖网络和一个名为 docker_gwbridge 的桥接网络。这里只显示了 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_gwbridgeingress 网络连接到 Docker 主机的网络接口,以便流量可以流向和流出 Swarm 管理器和工作节点。如果您创建 Swarm 服务并且没有指定网络,它们将连接到 ingress 网络。建议您为每个应用程序或将协同工作的应用程序组使用单独的覆盖网络。在下一个过程中,您将创建两个覆盖网络并将服务连接到每个网络。

创建服务

  1. manager 上,创建一个名为 nginx-net 的新覆盖网络

    $ docker network create -d overlay nginx-net
    

    您无需在其他节点上创建覆盖网络,因为当其中一个节点开始运行需要它的服务任务时,它将自动创建。

  2. manager 上,创建一个连接到 nginx-net 的 5 个副本的 Nginx 服务。该服务将端口 80 发布到外部世界。所有服务任务容器都可以相互通信,而无需打开任何端口。

    注意

    服务只能在管理器上创建。

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

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

  3. 运行 docker service ls 以监控服务启动的进度,这可能需要几秒钟。

  4. 检查 managerworker-1worker-2 上的 nginx-net 网络。请记住,您无需在 worker-1worker-2 上手动创建它,因为 Docker 会为您创建它。输出将很长,但请注意 ContainersPeers 部分。Containers 列出了从该主机连接到覆盖网络的所有服务任务(或独立容器)。

  5. manager,使用 docker service inspect my-nginx 检查服务,并注意有关服务使用的端口和端点的信息。

  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 运行相同的命令,并注意所有服务任务容器都连接到它。

    注意

    即使覆盖网络在需要时会在 Swarm 工作节点上自动创建,但它们不会自动删除。

  8. 清理服务和网络。从 manager 运行以下命令。管理器将指示工作节点自动删除网络。

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

使用用户定义的覆盖网络

先决条件

本教程假设 Swarm 已设置,并且您位于管理器上。

演练

  1. 创建用户定义的覆盖网络。

    $ docker network create -d overlay my-overlay
    
  2. 使用覆盖网络启动服务,并将端口 80 发布到 Docker 主机上的端口 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 并验证 my-nginx 服务任务是否连接到它,方法是查看 Containers 部分。

  4. 删除服务和网络。

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

将覆盖网络用于独立容器

此示例演示了 DNS 容器发现,具体来说,是如何使用覆盖网络在不同 Docker 守护程序上的独立容器之间进行通信。步骤如下

  • host1 上,将节点初始化为 Swarm(管理器)。
  • host2 上,将节点加入 Swarm(工作节点)。
  • host1 上,创建一个可附加的覆盖网络(test-net)。
  • host1 上,运行一个交互式 alpine 容器(alpine1)在 test-net 上。
  • host2上,运行一个交互式且分离的alpine 容器 (alpine2) 在 test-net 上。
  • host1上,从alpine1会话中,ping alpine2

先决条件

对于此测试,您需要两个可以相互通信的不同 Docker 主机。每个主机必须在两个 Docker 主机之间打开以下端口

  • TCP 端口 2377
  • TCP 和 UDP 端口 7946
  • UDP 端口 4789

设置此操作的一种简单方法是拥有两台虚拟机(本地或在 AWS 等云提供商上),每台都安装并运行 Docker。如果您使用 AWS 或类似的云计算平台,最简单的配置是使用安全组,该组在两个主机之间打开所有传入端口以及来自您客户端 IP 地址的 SSH 端口。

此示例将我们 swarm 中的两个节点称为host1host2。此示例还使用 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上,创建一个名为test-net 的可附加覆盖网络

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

    注意返回的 **NETWORK 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 是否已创建(并且具有与host1 上的test-net 相同的 NETWORK ID)

    $ 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
    

    两个容器通过连接两个主机的覆盖网络进行通信。如果您在host2 上运行另一个非分离 的 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 守护程序独立运行,这些是独立的容器。您只需要在host1 上删除网络,因为当您在host2 上停止alpine2 时,test-net 会消失。

    a. 在host2 上,停止alpine2,检查test-net 是否已删除,然后删除alpine2

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

    a. 在host1 上,删除alpine1test-net

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

其他网络教程