与覆盖网络联网

预计阅读时间:20分钟

本系列教程介绍了群集服务的网络。有关使用独立容器进行联网的信息,请参见 使用独立容器进行联网。如果您需要总体上了解有关Docker网络的更多信息,请参阅概述

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

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

  • 使用用户定义的覆盖网络显示了如何创建和使用自己的自定义覆盖网络来连接服务。建议将其用于生产中运行的服务。

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

  • 容器与群集服务 之间的通信使用可连接的覆盖网络在独立容器与群集服务之间建立通信。

先决条件

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

使用默认的覆盖网络

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

本教程不涉及有关如何实现重叠网络的特定于操作系统的详细信息,而是从服务的角度着重介绍重叠的功能。

先决条件

本教程需要三台可以相互通信的物理或虚拟Docker主机。本教程假定这三台主机在同一网络上运行,并且不涉及防火墙。

这些主机将被称为managerworker-1worker-2。该 manager主机将作为既是经理和工人,这意味着它可以运行服务任务和管理群。worker-1并且worker-2将作为唯一的工人,

如果您没有三台主机,一个简单的解决方案是在云提供商(例如Amazon EC2)上设置三台Ubuntu主机,它们全部位于同一网络上,并允许与该网络上的所有主机进行所有通信(使用诸如EC2安全组),然后按照Ubuntu上Docker Engine-Community安装说明进行操作

演练

创建群

在此过程结束时,所有三个Docker主机都将加入集群,并将使用一个名为的覆盖网络将其连接在一起ingress

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

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

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

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

    $ docker swarm join --token <TOKEN> \
      --advertise-addr <IP-ADDRESS-OF-WORKER-1> \
      <IP-ADDRESS-OF-MANAGER>:2377
    
  3. 在上worker-2,加入群体。如果主机只有一个网络接口,则该--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-1以及worker-2和通知他们每个人现在有所谓的覆盖网络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_gwbridge该连接ingress网络到主机多克尔的网络接口,以便通信可以流入和流出群经理和工人。如果创建群集服务但未指定网络,则它们将连接到ingress网络。建议对将一起使用的每个应用程序或一组应用程序使用单独的覆盖网络。在下一个过程中,您将创建两个覆盖网络并将服务连接到每个覆盖网络。

创建服务

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

    $ docker network create -d overlay nginx-net
    

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

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

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

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

    默认发布的模式ingress,这是用来当你没有指定mode--publish标志,这意味着,如果你浏览到端口80上managerworker-1或者worker-2,你会被连接到端口80上的5项服务任务之一,即使没有任务当前正在您浏览到的节点上运行。如果要使用host模式发布端口 ,则可以将其添加mode=host--publish输出中。但是,在这种情况下,也应使用--mode global代替--replicas=5,因为只有一个服务任务可以绑定给定节点上的给定端口。

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

  4. 检查nginx-net网络managerworker-1worker-2。请记住,您不需要手动创建它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,请注意所有服务任务容器都已连接到该命令。

    注意:即使根据需要在群集工作程序节点上自动创建了覆盖网络,也不会自动将其删除。

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

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

使用用户定义的覆盖网络

先决条件

本教程假定已经建立了集群,并且您正在管理中。

演练

  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,将节点初始化为群集(管理器)。
  • 在上host2,将节点加入群集(工作人员)。
  • 在上host1,创建一个可附加的覆盖网络(test-net)。
  • 在上host1运行一个交互式高山容器(alpine1test-net
  • 在上host2运行,并在上运行一个独立的交互式高山容器(alpine2test-net
  • 在pinghost1会话中进行。alpine1alpine2

先决条件

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

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

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

本示例将群中的两个节点称为host1host2。此示例还使用Linux主机,但是在Windows上也可以使用相同的命令。

演练

  1. 设置群。

    一种。在上host1,初始化群集(如果出现提示,请使用它--advertise-addr 来指定与群集中其他主机进行通信的接口的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,按照上述说明加入群组:

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

    如果节点无法加入群集,则该docker swarm join命令超时。要解决此问题,请在docker swarm leave --force上运行host2,验证您的网络和防火墙设置,然后重试。

  2. 在上host1,创建一个名为的可连接覆盖网络test-net

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

    请注意返回的NETWORK ID-当您从连接到它时,您将再次看到它host2

  3. 在上host1,启动一个与(-italpine1连接的交互式()容器()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为test-nethost1):

     $ docker network ls
     NETWORK ID          NAME                DRIVER              SCOPE
     ...
     uqsof8phj3ak        test-net            overlay             swarm
    
  7. host1alpine2的以下交互式终端中ping alpine1

    / # 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,则可以从ping (在这里,我们添加了 remove选项以自动清除容器):alpine1host2

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

    / # exit
    
  9. 清理您的容器和网络:

    您必须在每个主机上分别停止和删除容器,因为Docker守护程序是独立运行的,并且它们是独立的容器。您只需要删除网络就可以了,host1因为当您停止 alpine2host2,会test-net消失。

    一种。在上host2,停止alpine2,检查是否test-net已删除,然后删除alpine2

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

    一种。在上host1,删除alpine1test-net

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

在容器和集群服务之间进行通信

在此示例中,您alpine将在同一Docker主机上启动两个不同的容器,并进行一些测试以了解它们如何相互通信。您需要安装并运行Docker。

  1. 打开一个终端窗口。在执行其他任何操作之前,请先列出当前网络。如果您从未在此Docker守护程序上添加网络或初始化过集群,则应该看到以下内容。您可能会看到不同的网络,但至少应该看到以下内容(网络ID会有所不同):

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

    bridge列出了默认网络,以及hostnone。后两个不是完全成熟的网络,但是用于启动直接连接到Docker守护程序主机的网络堆栈的容器,或者用于启动不具有网络设备的容器。本教程将把两个容器连接到bridge网络。

  2. 启动两个alpine运行的容器ash,这是Alpine的默认外壳程序,而不是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. 检查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.2for alpine1172.17.0.3for 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连接到Internet google.com。该-c 2标志将命令限制两次两次ping

    # ping -c 2 google.com
    
    PING google.com (172.217.3.174): 56 data bytes
    64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.841 ms
    64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.897 ms
    
    --- google.com ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 9.841/9.869/9.897 ms
    
  6. 现在尝试对第二个容器执行ping操作。首先,通过其IP地址对其进行ping操作 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
    

    这样成功了。接下来,尝试alpine2按容器名称ping容器。这将失败。

    # ping -c 2 alpine2
    
    ping: bad address 'alpine2'
    
  7. 分离从alpine1没有通过使用分离的序列,停止它 CTRL+ p CTRL+ q(按住CTRL和类型p,随后q)。如果你愿意,重视alpine2并重复步骤4,5和6出现,取代alpine1alpine2

  8. 停止并取出两个容器。

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

请记住,bridge不建议将默认网络用于生产。要了解用户定义的桥接网络,请继续 阅读下一个教程

其他网络教程

既然您已经完成了覆盖网络的网络教程,则可能需要遍历以下其他网络教程:

网络路由端口覆盖