Docker管理指南(4) – daemon停止时保持容器继续运行

默认下,当docker daemon停止时,随即停止正在运行的容器。从Docker Engine 1.12开始,你可以配置daemon如果daemon变为不可用时容器保持继续运行。这个减少由于daemon崩溃,计划中断或升级导致的容器停机时间。

启用live-restore选项

有两种方式来启用live-restore设置来保持容器当daemon变为不可用时继续运行:

  • 如果daemon已经运行且你不想停止它,你可以把配置添加到daemon配置文件。例如,linux系统默认配置文件是/etc/docker/daemon.json。

    1. {
    2. "live-restore": true
    3. }

    编辑好之后需要发送一个SIGHUP信号到daemon进程来重载配置。

  • 启动docker daemon时,传递–live-restore参数:

    1. sudo dockerd –live-restore
  • 升级期间的live restore

    live restore功能支持在从一个小版本升级到下一个版本时恢复容器到daemon。例如从docker engine 1.12.1升级到1.13.2。
    如果在升级期间跳过版本,daemon可能无法恢复容器的连接。如果daemon无法恢复连接,则它将忽略正在运行的容器,这时你必须手动管理它们。daemon不会关闭断开连接的容器。

    重启时的live restore

    live restore选项仅可以恢复daemon重启前后配置相同选项的daemon。例如,如果daemon重启之后使用了不同的bridge IP或不同的graphdrive,则live restore无法工作。

    live restore对正在运行的容器的影响

    daemon长时间离线会影响到运行中的容器。容器进行向daemon写FIFO日志。如果由于daemon变不可用而无法处理这个输出,buffer将会被填满进而阻塞容器进一步写日志。默认的buffer大小为64K
    你必须重启docker来刷新buffers。
    可以通过改变/proc/sys/fs/pipe-max-size值来更改内核buffer大小。

    live restore与swarm模式

    live restore选项与docker engine swarm模式不兼容。当docker engine运行在swarm模式,编排功能管理任务并使容器根据服务规范运行。

    Docker管理指南(3) – 限制容器资源使用

    默认一个容器没有资源限制,能使用与主机内核调度器允许的给定资源一样多的资源。Docker提供了一个控制一个容器能使用多个内存,CPU或块IO的方法,就是在docker run命令设置运行时的配置参数。本文详细介绍你什么时候应该设置这些限制和设置它们时可能存在的影响。

    内存

    Docker可以设置内存硬限制,只允许使用不超过用户或系统内存给定的内存数量,或者软限制,允许容器使用它所需的尽可能多的内存,除非是遇到了一些条件,如内核检测下系统处理低内存状态或一些竞争情况。当单独使用或当使用超过一个选项,这些选项中的一些会有不同的影响。
    这些选项的大多数允许指定正整数,后面跟着一个后缀b,k,m,g来表示字节,千字节,兆字节或千兆字节。

  • -m or –memory=:容器可以使用的最大内存量。设置的值不能低于4m
  • –memory-swap*:允许容器交换到硬盘的最大内存量
  • –memory-swappiness:默认情况下,主机内核可以交换容器使用的匿名页面的百分比。你可以设置–memory-swappiness的值为0到100之间。
  • –memory-reservation:允许你指定一个比–memory小的软限制,在docker探测到主机低内存时激活。如果你使用–memory-reservation,必须设置一个比–memory小的值,以便软限制优先到达。因为软限制不保证容器不会超过这个限制。
  • –kernel-memory:容器能使用的最大内核内存量。最小可以设置到4m。因为内核内存不能被交换出去,一个缺乏内核内存的容器可能会阻塞主机资源,这会对主机和其他容器产生副作用。
  • –oom-kill-disable:默认下,当out-of-memory(OOM)错误产生时,内核会kill掉容器中的进程。可以使用–oom-kill-disableg来改变这个行为。如果设置了这个选项,那么仅在设置了-m/–memory的容器禁止OOM killer,如果-m没有设置,那么主机就能够用完所有内存,然后内核可能会杀死主机系统的进程来释放内存。
  • 关于cgroups和内存最多的信息请到https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt。

    –memory-swap详情

  • 如果这个选项没有设置,设置了–memory,容器能够使用-memory值的两倍swap。例如,–memory=”300m”,–memory-swap没有设置,那么容器能够使用300m内存和600m swap。
  • 如果–memory和–memory-swap都设置了,–memory-swap表示能够使用内存和swap的总数,–memory控制非swap内存的数量。所以如果–memory=”300m”,–memory-swap=”1g”,那么容器能够使用300m的内存和700m(1g -300m)的swap
  • 如果设置为-1(默认),容器就可以无限制使用swap。
  • –memory-swappiness详情

  • 值为0将关闭匿名页面交换。
  • 值100将所有匿名页面设置为可交换。
  • 默认情况下,如果不设置–memory-swappiness,将继承主机计算机的值。
  • –kernel-memory详情

    有以下几种情况:

  • 无限制的内存,无限制的内核内存:这个是默认行为。
  • 无限制的内存,有限制的内核内存:当所有的cgroups所需的内存量超过主机实际拥有的内存量时,适合设置为这样的。你可以配置内核内存不超过主机可用内存,容器需要更多的内存时只能等待了。
  • 有限制的内存,无限制的内核内存:总内存是有限制的,不过内核内核无限制。
  • 有限制的内存,有限制的内核内存:用户和内核限制都限制时对调试内存相关的问题会有帮助。如果一个容器用完了这两种内存其中的一种,它不会影响到其它的容器或主机。如果内核内存限制比用户内存低,使用完内核内存后会导致容器产生OOM错误。反之,不会遇到OOM。
  • CPU

    默认情况下,每个容器使用主机的CPU无限制。你可以通过下面的选项来对容器的CPU使用设置限制。

  • –cpu-shares:设置此参数比默认的1024大或小会增加或减小容器的权重,给它更大或更小主机CPU周期的比例的访问。这个只在CPU周期受到限制时才强制执行。当大量的CPU可用时,所有的容器都能够使用它们所需尽量多的CPU资源。所以这个是软限制。–cpu-shares不会阻容器在swarm模式下的调度。它会优先从可用CPU周期满足容器的CPU使用,但不过保证或保留特定的CPU周期。
  • –cpu-period:一个容器的一个逻辑CPU的调度周期。默认值是100000(100ms)
  • –cpu-quota:在由–cpu-period设置的时间段内容器可以调度的最大时间量。
  • –cpuset-cpus:使用此选项将容器固定到一个或多个CPU核心,以逗号分隔。
  • –cpu-period和–cpu-qota示例

    如果你有一个1 vCPU的系统,容器以–cpu-period=100000和–cpu-quota=50000运行,那么容器最大能消耗1个CPU的50%资源。

    1. $ docker run -ti –cpu-period=10000 –cpu-quota=50000 busybox

    如果你有一个4 vCPU系统,容器以–cpu-period=100000和–cpu-quota=200000运行,那么你的容器能最大消耗2个逻辑CPU(200%的–cpu-period)。

    1. $ docker run -ti –cpu-period=100000 –cpu-quota=200000

    –cpuset-cpus示例

    设置容器最大使用4 CPU,执行如下命令:

    1. $ docker run -ti –cpuset-cpus=4 busybox

    Block IO (blkio)

    有两个选项可用于调整给定容器对直接块IO设备的访问。你还可以按照每秒的字节数或每秒的IO操作来指定带宽限制。

  • blkio-weight:默认情况下,每个容器可以使用相同比例的块IO带宽(blkio)。默认权重是500。要提高或降低给定容器使用的blkio的比例,可以设置–blkio-weight为介于10和1000之间的值。此设置将平等地影响所有块IO设备。
  • blkio-weight-device:与–blkio-weight相同,但你可以使用语法–blkio-weight-device =“DEVICE_NAME:WEIGHT”为每个设备设置权重。DEVICE_NAME:WEIGHT是一个字符串,包含冒号分隔的设备名称和权重 。
  • –device-read-bps和–device-write-bps:根据大小限制设备的读取或写入速率,使用kb,mb或gb后缀。
  • –device-read-iops or –device-write-iops:通过IO操作/秒限制设备的读取或写入速率。
  • 块IO权限示例

    注意:–blkio-weight只影响直接IO,对缓冲IO没有影响。

    如果指定–blkio-weight和-blkio-weight-device,Docker使用–blkio-weight作为默认权重,并使用–blkio-weight-device重写命名设备上的默认值。
    要将/dev/sda的容器的设备权重设置为200,而不指定默认blkio-weight:

    1. $ docker run -it
    2.   –blkio-weight-device "/dev/sda:200"
    3.   ubuntu

    块带宽限制示例

    此示例将ubuntu容器对/dev/sda的最大写入速度限制为为1mbps:

    1. $ docker run -it –device-write-bps /dev/sda:1mb ubuntu

    此示例将ubuntu容器对/dev/sda的最大读取速率限制为每秒1000次IO操作:

    1. $ docker run -ti –device-read-iops /dev/sda:1000 ubuntu

    Docker管理指南(2) – 自动启动容器

    docker 1.2版本,docker机制内置了当容器退出时重启它们的重启策略。如果设置了,重启策略会在docker daemon启动时使用,如经典场景系统启动时。同时重启策略会确保链接容器以正确的顺序启动。
    如果重启策略没有满足你的需求(如你可能有非docker进程依赖docker容器),你可以使用进程管理器,如upstart,systemd或supervisor。

    使用进程管理器

    docker默认不会设置任何的重启策略,不过需要注意它们会与大多数的进程管理器冲突。所以如果你使用了进程管理器就不要设置重启策略了。
    当你设置好你的镜像并已经运行容器,你可以把它们附加到进程管理器来管理。当你执行docker start -a,docker就会自动附着所有运行的容器,或按需启动并转向所有信号以便进程管理器在一个容器停止能探测到并正确重启它。

    示例

    下面的示例显示两个进程管理器的配置,upstart和systemd。在这个示例中我们假设你已经启动了一个Redis容器,命名为redis_server。这些文件定义了当docker daemon启动时自动启动容器。

    upstart

    1. description "Redis container"
    2. author "Me"
    3. start on filesystem and started docker
    4. stop on runlevel [!2345]
    5. respawn
    6. script
    7.   /usr/bin/docker start -a redis_server
    8. end script

    systemd

    1. [Unit]
    2. Description=Redis container
    3. Requires=docker.service
    4. After=docker.service
    5.  
    6. [Service]
    7. Restart=always
    8. ExecStart=/usr/bin/docker start -a redis_server
    9. ExecStop=/usr/bin/docker stop -t 2 redis_server
    10.  
    11. [Install]
    12. WantedBy=default.target

    如果你想设置为一个系统服务,把上面的内容放到/etc/systemd/system目录下的一个文件,如/etc/systemd/system/docker-redis_server.service。
    如果你需要传递选项到redis容器,如–env,那么你需要使用docker run而不是docker start来启动容器。这个会每次服务启动时创建一个新的容器,这容器当服务停止时会自动停止和删除。

    1. [Service]
    2. ExecStart=/usr/bin/docker run –env foo=bar –name redis_server redis
    3. ExecStop=/usr/bin/docker stop -t 2 redis_server
    4. ExecStopPost=/usr/bin/docker rm -f redis_server

    要使用这个服务,重载systemd和启动服务:

    1. systemctl daemon-reload
    2. systemctl start docker-redis_server.service

    设置开机启动:

    1. systemctl enable docker-redis_server.service

    Docker管理指南(1) – 配置和运行Docker

    成功安装Docker后,docker daemon以默认配置运行。
    在生产环境中,系统管理员通常要根据具体环境来配置docker deamon。大多数情况下,系统管理员会使用如SysVinit, Upstart或systemd来管理docker daemon的启动和停止。

    直接运行和配置docker daemon

    直接运行docker daemon

    docker daemon能直接使用dockerd执行。默认监听unix socket unix:///var/run/docker.sock。

    1. $ dockerd
    2.  
    3. INFO[0000] +job init_networkdriver()
    4. INFO[0000] +job serveapi(unix:///var/run/docker.sock)
    5. INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)

    直接配置docker daemon

    如果你不是使用进程管理器而是直接使用dockerd运行的docker,你就可以在dockerd命令中设置参数来配置docker。
    docker daemon一些可用参数如下:
    -D, –debug=false:启用或禁用debug模式。默认这个是false。
    -H,–host=[]:指定docker daemon监听的socket。
    –tls=false:启用或禁用TLS。默认这个是false。
    这是个直接在命令行配置docker的示例:

    1. dockerd -D –tls=true –tlscert=/var/docker/server.pem –tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376

    解释如下:

  • 启用了debug模式
  • 设置tls为true并指定服务器证书和私钥
  • 在tcp://192.168.59.3:2376监听
  • daemon调试

    如上所述,我们可以设置日志等级为debug或使用-d启用debug模式来帮助我们获取daemon更详细的信息来查找问题。如果你遇到daemon无响应的情况,可以通过发送SIGUSR1到Docker daemon来强制获取所有线程完整的堆栈跟踪信息到daemon日志文件。通常使用linux系统的kill命令来发送这个信息。例如,kill -USR1 。

    Ubuntu

    Ubuntu 14.04使用Upstart作为进程管理器。默认情况下,Upstart jobs在/etc/init,docker的Upstart job在/etc/init/docker.conf。
    当docker安装完成后,可以以下面这种方式使用Upstart来检查docker的运行状态:

    1. $ sudo status docker
    2.  
    3. docker start/running, process 989

    运行docker

    你可以使用如下命令来启动/停止/重启docker daemon

    1. $ sudo start docker
    2.  
    3. $ sudo stop docker
    4.  
    5. $ sudo restart docker

    配置docker

    下面是描述在使用upstart作为进程管理器的系统上配置docker daemon。Ubuntu 15.04系统使用了systemd作为它的进程管理器。
    在系统上的/etc/default/docker文件配置docker daemon。在这个文件找到DOCKER_OPTS并配置相关daemon参数。
    配置daemon步骤如下:
    1.登录进你的主机并切换到root权限。
    2.如果系统没有/etc/default/docker文件,先创建,不过应该都有了。
    3.打开这个文件

    1. $ sudo vi /etc/default/docker

    4.把下面的参数添加到DOCKER_OPTS变量。这些参数会在docker daemon运行时附加上去。

    1. DOCKER_OPTS="-D –tls=true –tlscert=/var/docker/server.pem –tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376"

    5.保存并关闭文件。
    6.重启docker daemon
    $ sudo restart docker

    7.检查docker daemon是否以你所设置的参数运行

    1. ps aux | grep docker | grep -v grep

    日志

    默认下upstart job的日志在/var/log/upstart,docker daemon的日志在/var/log/upstart/docker.log

    1. $ tail -f /var/log/upstart/docker.log
    2.  
    3. INFO[0000] Loading containers: done.
    4. INFO[0000] Docker daemon commit=1b09a95-unsupported graphdriver=aufs version=1.11.0-dev
    5. INFO[0000] +job acceptconnections()
    6. INFO[0000] -job acceptconnections() = OK (0)
    7. INFO[0000] Daemon has completed initialization

    CentOS / Red Hat Enterprise Linux / Fedora

    CentOS/RHEL 7.x和Fedora 21使用systemd作为进程管理器。
    docker安装好后,你可以使用如下命令检查运行状态:

    1. $ sudo systemctl status docker

    运行docker

    使用如下命令启动/停止/重启docker

    1. $ sudo systemctl start docker
    2.  
    3. $ sudo systemctl stop docker
    4.  
    5. $ sudo systemctl restart docker

    设置docker开机启动

    1. $ sudo systemctl enable docker

    配置docker

    此小节我们使用centos 7.x,即使用进程管理器systemd作为示例配置docker。
    1.登录主机并切换到root权限。
    2.创建目录/etc/systemd/system/docker.service.d

    1. $ sudo mkdir /etc/systemd/system/docker.service.d

    3.创建文件/etc/systemd/system/docker.service.d/docker.conf
    4.打开此文件

    1. $ sudo vi /etc/systemd/system/docker.service.d/docker.conf

    5.覆盖docker.service文件中的ExecStart配置项来自定义你的daemon。配置如下:
    [Service]

    1. ExecStart=
    2. ExecStart=/usr/bin/dockerd -H fd:// -D –tls=true –tlscert=/var/docker/server.pem –tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376

    6.保存关闭文件
    7.应用配置

    1. $ sudo systemctl daemon-reload

    8.重启daemon

    1. $ sudo systemctl restart docker

    9.验证daemon是否以你所设置的参数运行

    1. $ ps aux | grep docker | grep -v grep

    日志

    systemd有它自己的日志系统,称为journal。docker daemon的日志可以使用journalctl -u docker查看

    1. $ sudo journalctl -u docker
    2. May 06 00:22:05 localhost.localdomain systemd[1]: Starting Docker Application Container Engine…
    3. May 06 00:22:05 localhost.localdomain docker[2495]: time="2015-05-06T00:22:05Z" level="info" msg="+job serveapi(unix:///var/run/docker.sock)"
    4. May 06 00:22:05 localhost.localdomain docker[2495]: time="2015-05-06T00:22:05Z" level="info" msg="Listening for HTTP on unix (/var/run/docker.sock)"
    5. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="+job init_networkdriver()"
    6. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="-job init_networkdriver() = OK (0)"
    7. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="Loading containers: start."
    8. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="Loading containers: done."
    9. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="Docker daemon commit=1b09a95-unsupported graphdriver=aufs version=1.11.0-dev"
    10. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="+job acceptconnections()"
    11. May 06 00:22:06 localhost.localdomain docker[2495]: time="2015-05-06T00:22:06Z" level="info" msg="-job acceptconnections() = OK (0)"

    Docker用户指南(16) – 默认bridge网络 – 理解容器通信

    本文解析在docker默认bridge内容器的通信。这个bridge网络是你安装docker时自动创建的,名称为bridge。

    与外部网络通信

    容器是否能与外部网络通信取决于两个因素。第一个因素是主机是否转发IP数据包。第二个因素是主机的iptables是否允许这个连接。
    ip数据包是否转发由ip_forward系统参数决定。数据包只能在参数为1的情况下才能转发。通常情况你会使docker默认设置–ip-forward=true,docker将在启动时设置ip_forward为1。如果你设置–ip-forward=false且你的系统内核已经启用转发,那么–ip-forward=false不会有影响。检查你系统这个参数的设置或手动启用该参数:

    1. $ sysctl net.ipv4.conf.all.forwarding
    2.  
    3.   net.ipv4.conf.all.forwarding = 0
    4.  
    5.   $ sysctl net.ipv4.conf.all.forwarding=1
    6.  
    7.   $ sysctl net.ipv4.conf.all.forwarding
    8.  
    9.   net.ipv4.conf.all.forwarding = 1

    这个设置不会对使用network=host的容器产生影响。

    如果你设置daemon的参数–iptables=false,docker将不会更改你的iptables规则。否则docker server会附加forwarding规则到DOCKER过滤链。
    Docker不会删除或更改DOCKER过滤链中预存在的规则。这允许用户创建限制访问容器所需的任意高级规则。
    Docker的forward规则默认允许所有的外部源IP。要只允许一个指定的IP或网络访问容器,在DOCKER过滤链顶部插入一条DROP规则。例如,只允许IP 8.8.8.8访问容器:

    1. $ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

    其中ext_if是提供外部连接的接口名称。

    容器间通信

    在操作系统级别两个容器是否能够通信取决于两个因素:

  • 网络拓扑是否连接容器的网络接口?默认下Docker会附加所有的容器到docker0 bridge,为数据包提供一个路径来在容器间传输。
  • iptables是否允许这些连接?如果你设置–iptables=false,那么docker将不会对你系统的iptables规则进行任何更改。否则,且如果你保持默认–icc=true,docker server会添加一个默认的ACCEPT规则到FORWARD链,或者如果设置–icc=false就添加DROP规则。
  • 如果你设置–icc=false,那么当你想让两个容器间能够访问该怎么办呢?答案是使用–link=CONTAINER_NAME_或_ID:ALIAS选项。如果docker daemon的参数–icc=false和–iptables=true,那么当docker run调用–link=选项时,docker server将插入一对iptables ACCEPT规则以使得新创建的容器能够连接到另一个容器。
    你可以执行iptables命令来查看FORWARD链默认策略是ACCEPT还是DROP:

    1. # When –icc=false, you should see a DROP rule:
    2.  
    3. $ sudo iptables -L -n
    4.  
    5. Chain FORWARD (policy ACCEPT)
    6. target     prot opt source               destination
    7. DOCKER     all  —  0.0.0.0/0            0.0.0.0/0
    8. DROP       all  —  0.0.0.0/0            0.0.0.0/0
    9.  
    10. # When a –link= has been created under –icc=false,
    11. # you should see port-specific ACCEPT rules overriding
    12. # the subsequent DROP policy for all other packets:
    13.  
    14. $ sudo iptables -L -n
    15.  
    16. Chain FORWARD (policy ACCEPT)
    17. target     prot opt source               destination
    18. DOCKER     all  —  0.0.0.0/0            0.0.0.0/0
    19. DROP       all  —  0.0.0.0/0            0.0.0.0/0
    20.  
    21. Chain DOCKER (1 references)
    22. target     prot opt source               destination
    23. ACCEPT     tcp  —  172.17.0.2           172.17.0.3           tcp spt:80
    24. ACCEPT     tcp  —  172.17.0.3           172.17.0.2           tcp dpt:80

    Docker用户指南(15) – 默认bridge网络 – 绑定容器端口到主机

    本文介绍如何在docker默认bridge网络绑定容器端口。这个bridge网络名称为bridge,是安装docker时自动创建的。
    默认下docker容器能连接到外部网络,但外部网络无法连接到容器。由于docker服务器启动时在主机创建的iptables伪装(masquerading)规则,所以容器的每个出站连接显示的来源IP地址是主机自己的IP地址:

    1. $ sudo iptables -t nat -L -n
    2.  
    3. Chain POSTROUTING (policy ACCEPT)
    4. target     prot opt source               destination
    5. MASQUERADE  all  —  172.17.0.0/16       0.0.0.0/0

    docker server创建的masquerade规则使得容器能够连接外部网络的IP地址。
    如果你想让容器接收来自外部网络的连接,需要在调用docker run时提供一些指定的参数。有两个方法。
    第一个,你可以在docker run命令中提供-P或–publish-all=true|false,这个命令会识别在镜像Dockerfile中的EXPOSE的端口或命令行中的–expose 端口并映射到主机的临时端口范围内的随机端口。可以使用docker port命令来查看创建的端口映射。临时端口范围是在/proc/sys/net/ipv4/ip_local_port_range内核参数配置,一般是32768到61000。
    可以使用-p SPEC or –publish=SPEC参数来指定映射的端口。它允许你指定docker server的端口 – 可以是任意的端口而不是在临时端口范围的一个随机端口。
    你可以通过iptable的NAT表来查看绑定了哪些端口。

    1. # What your NAT rules might look like when Docker
    2. # is finished setting up a -P forward:
    3.  
    4. $ iptables -t nat -L -n
    5.  
    6. Chain DOCKER (2 references)
    7. target     prot opt source               destination
    8. DNAT       tcp  —  0.0.0.0/0            0.0.0.0/0            tcp dpt:49153 to:172.17.0.2:80
    9.  
    10. # What your NAT rules might look like when Docker
    11. # is finished setting up a -p 80:80 forward:
    12.  
    13. Chain DOCKER (2 references)
    14. target     prot opt source               destination
    15. DNAT       tcp  —  0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.2:80

    –userland-proxy默认是true,为内部容器间和外部到容器的通信提供了一个用户级的实现。当关闭时,docker使用一个MASQUERADE iptable规则和net.ipv4.route_localnet内核参数允许主机使用常见的loopback地址来连接本地容器暴露的端口。这种方法完全可以代替使用docker-proxy进程对流量转发,性能没有使用iptables转发好。推荐设置–userland-proxy=false。不过由于此参数设置为false时有可能存在一些问题,所以目前还继续保留docker-pxoy并设置此参数为true。

    Docker用户指南(14) – 默认bridge网络 – 旧的容器链接

    本文介绍安装docker时自动创建的docker默认bridge网络内的旧的容器链接(links)。–link标志是docker废弃的旧功能。它可能会最终被删除。除非你真的需要继续使用它,否则我们推荐你使用用户定义网络来连通两个容器之间的网络,而不是使用–link。用户定义网络有一个功能不支持而可以使用–link来启用的是容器之间环境变量的共享。不过你可以使用其它的机制如volumes来以更可控的方式在容器之间共享环境变量。

    使用网络端口映射连接

    下面是创建一个容器来运行一个Python Flask应用:

    1. $ docker run -d -P training/webapp python app.py

    当容器创建后,-P参数用来自动地将创建容器的Dockerfile中EXPOSE的端口映射到你docker主机的临时端口范围中的一个随机端口。下面,运行docker ps时,你会看到容器中的5000端口绑定到了主机的49155端口。

    1. $ docker ps nostalgic_morse
    2.  
    3. CONTAINER ID  IMAGE                   COMMAND       CREATED        STATUS        PORTS                    NAMES
    4. bc533791f3f5  training/webapp:latest  python app.py 5 seconds ago  Up 2 seconds  0.0.0.0:49155->5000/tcp  nostalgic_morse

    你也可以使用-p参数把容器端口绑定到主机上的一个指定端口。
    下面示例中容器的5000端口映射到主机的80端口:

    1. $ docker run -d -p 80:5000 training/webapp python app.py

    或者你也可以指定容器的一个端口绑定到与主机默认的临时端口范围不一样的端口范围:

    1. $ docker run -d -p 8000-9000:5000 training/webapp python app.py

    这个会把容器的5000端口绑定到主机的8000-9000端口范围中的一个随机端口。
    默认下-p参数指定绑定一个固定的端口到所有的网络接口,你也可以指定绑定一个特定接口,如只绑定到localhost。

    1. $ docker run -d -p 127.0.0.1:80:5000 training/webapp python app.py

    或者绑定主机接口localhost,而端口随机:

    1. $ docker run -d -p 127.0.0.1::5000 training/webapp python app.py

    你也可以使用/udp绑定UDP端口,如:

    1. $ docker run -d -p 127.0.0.1:80:5000/udp training/webapp python app.py

    我们可以使用docker port来显示容器指定的端口绑定详情。

    1. $ docker port nostalgic_morse 5000
    2.  
    3. 127.0.0.1:49155

    注意:-p参数可以使用多次来绑定多个端口。

    使用链接系统连接

    注意:本节只覆盖默认的bridge网络下的旧链接功能,不涉及到用户定义网络下的链接(link)。

    网络端口映射不是docker容器能彼此连接的唯一一种方式。Docker还有一个链接系统(linking system)允许你把多个容器链接到一起并彼此发送连接信息。当容器被链接时,这个容器的连接信息将发送到主动链接的容器中。

    命名的重要性

    要建立链接,Docker依赖容器的名称。如果不指定容器名称,docker将自动创建一个随机的名称;容器名称有两个有用的功能:
    1.你可以根据容器所做的任务来命名它帮助你容易地记住这个容器是做什么的,如命名一个web应用程序为web。
    2.你可以使用容器名称来引用它,如可以指定链接web容器到db容器。
    通过–name参数来对容器命名,如:

    1. $ docker run -d -P –name web training/webapp python app.py

    上面命令启动了一个新的容器并使用–name参数命名为web。可以使用docker ps命令来查看容器名称。

    1. $ docker ps -l
    2.  
    3. CONTAINER ID  IMAGE                  COMMAND        CREATED       STATUS       PORTS                    NAMES
    4. aed84ee21bde  training/webapp:latest python app.py  12 hours ago  Up 2 seconds 0.0.0.0:49154->5000/tcp  web

    链接间通信

    链接允许容器彼此发现并安全地传输关于彼此连接的信息。当你配置一个链接时,你就在被链接容器和链接容器之间创建了一个管道。然后链接容器就能访问关于被链接容器的数据。我们使用–link参数创建一个链接。首先创建一个新容器。

    1. $ docker run -d –name db training/postgres

    这时从training/postgres镜像创建了一个名为db的新容器,容器中包含一个PostgreSQL数据库。
    现在你需要先删除之间创建的web容器以便你能替换为一个链接容器:

    1. $ docker rm -f web

    现在创建一个新的web容器并链接到db容器。

    1. $ docker run -d -P –name web –link db:db training/webapp python app.py

    链接标志的格式:

    1. –link <name or id>:alias

    其中name是我们要链接到的容器名称,alias是链接名称的别名。也有缩写的格式,如:

    1. –link <name or id>

    在这种情况下别名就与名称相同了。以上的示例可以这样写:

    1. $ docker run -d -P –name web –link db training/webapp python app.py

    现在使用docker inspect来查看链接容器:

    1. $ docker inspect -f "{{ .HostConfig.Links }}" web
    2.  
    3.  
    4. [/db:/web/db]

    你会看到web容器链接到了db容器web/db。使用链接,容器间通信就不需要通过-p或-P来把端口绑定到主机上。docker在容器间创建一个安全的隧道用来链接容器间通信。
    docker通过以下两种方式来把被链接容器的连接信息暴露给链接容器:

  • 环境变量
  • 更新/etc/hosts文件
  • 环境变量

    当你链接容器时Docker将创建一些环境变量。Docker根据–link参数来在链接容器中自动创建环境变量。它也把被链接的容器从Docker设置的环境变量设置给链接容器。这些环境变量包括:

  • 被链接容器的Dockerfile的ENV命令
  • 被链接容器启动时的docker run命令的-e,–env和–env-file选项
  • Docker为每个链接容器设置一个_NAME环境变量。例如,如果一个新的容器称为web通过–link db:webdb链接到一个数据库容器称为db,那么Docker将在web容器创建一个WEBDB_NAME=/web/webdb变量。
    Docker也根据被链接容器暴露的每一个端口设置了一组环境变量。每个变量有一个唯一的前缀,格式为:
    _PORT__
    前缀的组成是:

  • 在–link参数指定的别名(例如,webdb)
  • 暴露的端口号
  • TCP或UDP 协议
  • docker使用这个前缀来定义三个唯一的环境变量:

  • prefix_ADDR变量包含了一个IP地址,如
    WEBDB_PORT_5432_TCP_ADDR=172.17.0.82
  • prefix_PORT变量包含了一个端口号,如
    WEBDB_PORT_5432_TCP_PORT=5432
  • prefix_PROTO变量包含了一个协议,如
    WEBDB_PORT_5432_TCP_PROTO=tcp
  • 如果一个容器暴露了多个端口,那么将会为每个端口设置一组环境变量。例如,一个容器暴露4个端口,那么docker将创建12个环境变量,每个端口3个。
    此外,docker创建一个环境变量称为_PORT。这个变量包含容器第一个暴露的端口的URL。如WEBDB_PORT=tcp://172.17.0.82:5432。
    最后,docker也把被链接容器的每一个原始环境变量设置为链接容器的环境变量,变量格式为_ENV_。
    你可以执行env命令来显示所有的环境变量。

    1. $ docker run –rm –name web2 –link db:db training/webapp env
    2.  
    3.     . . .
    4.     DB_NAME=/web2/db
    5.     DB_PORT=tcp://172.17.0.5:5432
    6.     DB_PORT_5432_TCP=tcp://172.17.0.5:5432
    7.     DB_PORT_5432_TCP_PROTO=tcp
    8.     DB_PORT_5432_TCP_PORT=5432
    9.     DB_PORT_5432_TCP_ADDR=172.17.0.5
    10.     . . .

    Docker环境变量重要提示

    不像在/etc/hosts文件的主机条目,存储在环境变量的IP地址在被链接容器重启时不会自动更新。我们推荐使用/etc/hosts文件中的主机条目来解析被链接容器的IP地址。
    这些环境变量只为容器的第一个进程设置。一些daemon,如sshd,当为连接新起一个shell时会清除这些变量。

    更新/etc/hosts文件

    除了环境变量,docker添加一个被链接容器的主机条目到/etc/hosts文件。下面是在web容器的主机条目:

    1. $ docker run -t -i –rm –link db:webdb training/webapp /bin/bash
    2.  
    3. root@aed84ee21bde:/opt/webapp# cat /etc/hosts
    4.  
    5. 172.17.0.7  aed84ee21bde
    6. . . .
    7. 172.17.0.5  webdb 6e5cdeb2d300 db

    你可以看到两个相关的条目。第一个是web容器使用容器ID作为主机名的条目。第二个是使用链接别名来引用db容器的IP地址。除了链接别名,如果链接别名与被链接容器名不一样,那么还添加被链接容器名与其IP地址的条目。你可以尝试ping这些条目:

    1. root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
    2.  
    3. root@aed84ee21bde:/opt/webapp# ping webdb
    4.  
    5. PING webdb (172.17.0.5): 48 data bytes
    6. 56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
    7. 56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
    8. 56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms

    如果你重启了被链接容器,那么链接容器的/etc/hosts文件会自动使用被链接容器的新IP地址更新。

    1. $ docker restart db
    2.  
    3. db
    4.  
    5. $ docker run -t -i –rm –link db:db training/webapp /bin/bash
    6.  
    7. root@aed84ee21bde:/opt/webapp# cat /etc/hosts
    8.  
    9. 172.17.0.7  aed84ee21bde
    10. . . .
    11. 172.17.0.9  db

    Docker用户指南(13) – 用户定义网络中的内置DNS服务器

    本文介绍自定义网络的容器的内置DNS服务器操作。连接到用户定义网络的容器与连接到默认bridge网络的容器的DNS查询方法不太一样。
    从Docker 1.10起,docker daemon为使用有效名称或网络别名或link别名创建的容器实现了一个内置的DNS服务器来提供内置的服务发现功能。
    下面是是影响容器域名名称服务的各种容器选项。

  • –name=CONTAINER-NAME: 使用–name配置的容器名称用来在一个用户定义的docker网络内发现容器。内置的DNS服务器维护容器名称与它的IP地址的映射关系。
  • –network-alias=ALIAS:除了以上提供的–name,还可以在用户定义网络通过它配置的一个或多个–network-alias(或docker network connect命令的–alias)发现容器。内置的DNS服务器维护在一个指定用户定义网络的所有容器别名和它的IP地址之间的映射关系。在docker network connect命令中使用–alias选项容器可以在不同的网络中有不同的别名。
  • –link=CONTAINER_NAME:ALIAS:在你运行一个容器时使用这个选项给了内置DNS一条称为ALIAS额外的条目,这个ALIAS指向CONTAINER_NAME识别出来的IP地址。当使用–link时,内置的DNS会保证ALIAS只在使用–link的容器中可用。这使得新容器中的进程不需要知识容器的名称或IP就能够连接容器。
  • –dns=[IP_ADDRESS…]:此选项指定的DNS服务器IP地址用来当内置的DNS服务器无法解析来自容器的DNS解析请求时就把它的请求转向到这个指定的外部DNS服务器。这些–dns IP地址由内置的DNS服务器管理且不会更新到容器的/etc/resolv.conf文件。
  • –dns-search=DOMAIN…:设置当容器内部请求一个不合格的主机名时再次搜索的域名。这些dns-search选项由内置的DNS服务器管理且不会更新到容器的/etc/resolv.conf文件。当一个容器进程尝试访问host且搜索域名设置为example.com时,DNS不仅会查询host,也会查询host.example.com。
  • –dns-opt=OPTION…:设置用于DNS解析器的选项。这些选项由内置的DNS服务器管理且不会更新到容器的/etc/resolv.conf文件。
  • 当没有指定–dns=IP_ADDRESS…, –dns-search=DOMAIN…, 或–dns-opt=OPTION…选项时,Docker使用主机(运行docker daemon的那台主机)的/etc/resolv.conf。如果是这种情况,daemon将从原始的文件过滤所有localhost IP地址nameserver条目。
    过滤是必要的因为从容器的网络无法到达主机的所有本地地址。过滤之后,如果/etc/resolv.conf文件没有条目了,那么daemon将添加Google的公共DNS服务器 (8.8.8.8和8.8.4.4)到容器的DNS配置。如果IPv6启用了,将添加Google的IPv6 DNS服务器(2001:4860:4860::8888 和 2001:4860:4860::8844)。

    Docker用户指南(12) – 多主机网络

    本文借助一个示例来解释创建一个多主机网络的基础。Docker Engine通过overlay网络驱动支持多主机网络开箱即用。不像bridge网络,在你创建一个overlay网络时需要一些前提条件:

  • 运行在swarm模式的docker engine
  • 使用一个键值存储的集群
  • overlay网络和swarm模式

    使用运行在swarm模式的docker engine,你可以在管理节点上创建一个overlay网络。
    overlay网络只在依赖其的服务的swarm节点上可用。当你创建一个使用overlay网络的服务时,管理节点自动扩展overlay网络到运行服务的节点上。
    下面的示例展示如何创建一个网络并在swarm管理节点上设置它用于一个服务:

    1. # Create an overlay network `my-multi-host-network`.
    2. $ docker network create
    3.   –driver overlay
    4.   –subnet 10.0.9.0/24
    5.   my-multi-host-network
    6.  
    7. 400g6bwzd68jizzdx5pgyoe95
    8.  
    9. # Create an nginx service and extend the my-multi-host-network to nodes where
    10. # the service’s tasks run.
    11. $ docker service create –replicas 2 –network my-multi-host-network –name my-web nginx
    12.  
    13. 716thylsndqma81j6kkkb5aus

    swarm的overlay网络在不受管理的容器上不可用。

    使用外部键值存储的overlay网络

    要使用带外部键值存储的docker engine,你需要满足如下条件:

  • 一个键值存储。docker支持Consul, Etcd和ZooKeeper(分布式存储)键值存储。
  • 集群的主机能够访问键值存储。
  • 正确地配置集群中的每台主机的engine daemon。
  • 由于键值存储使用主机名辨别集群成员,集群中的主机必须有一个唯一的主机名。
  • 虽然体验使用键值存储的docker多主机网络不强制使用Docker Machine和Docker Swarm,本示例使用它们来说明如何架设这样的一个多主机网络。
    我们将使用Machine来创建键值存储和主机集群。此示例创建一个swarm集群。

    注意:运行在swarm模式的docker engine与使用外部键值存储的网络不兼容。

    前提条件

    在开始之前,确保你的系统安装有最新版本的docker engine和docker machine。此示例也依赖VirtualBox。如果你在Mac或Windows安装了Docker Toolbox,这些应该都安装好了。

    配置一个键值存储

    overlay网络需要一个键值存储。这个键值存储维护关于网络状态的信息,包括发现(discovery),网络,endpoints,ip地址等。Docker支持Consul, Etcd和ZooKeeper键值存储。本示例使用Consul。
    1.登录进安装有Docker Engine, Docker Machine和VirtualBox的系统。
    2.配置一个VirtualBox机器,名称为mh-keystore。

    1. $ docker-machine create -d virtualbox mh-keystore

    当你配置一个新的主机时,该进程会自动添加一个Docker Engine到这个主机。意味着我们不手动安装Consul,而是使用从docker hub的consul镜像创建一个实例。
    3.进入mh-keystore主机。

    1. $  eval "$(docker-machine env mh-keystore)"

    4.在mh-keystore主机启动一个progrium/consul容器。

    1. $  docker run -d
    2.      -p "8500:8500"
    3.      -h "consul"
    4.      progrium/consul -server -bootstrap

    5.执行docker ps命令来查看consul容器。

    1. $ docker ps
    2.  
    3.  CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                            NAMES
    4.  4d51392253b3        progrium/consul     "/bin/start -server -"   25 minutes ago      Up 25 minutes       53/tcp, 53/udp, 8300-8302/tcp, 0.0.0.0:8500->8500/tcp, 8400/tcp, 8301-8302/udp   admiring_panini

    创建一个swarm集群

    在本步骤中,将使用docker-machine来配置你网络的主机。在这时候你实际上还没有创建网络,只是在VirtualBox创建了几个主机。其中一个主机作为swarm master,然后创建每一个主机。最后传递overlay网络驱动需要的参数到那主机的Engine。
    1.创建一个swarm master。

    1. $ docker-machine create
    2.  -d virtualbox
    3.  –swarm –swarm-master
    4.  –swarm-discovery="consul://$(docker-machine ip mh-keystore):8500"
    5.  –engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500"
    6.  –engine-opt="cluster-advertise=eth1:2376"
    7.  mhs-demo0

    以上的命令中,–cluster-store选项告知Engine键值存储的位置。$(docker-machine ip mh-keystore)变量是取得Consul服务器的IP地址。–cluster-advertise选项这台主机的网络地址。
    2.创建另一台主机并增加到swarm集群。

    1. $ docker-machine create -d virtualbox
    2.      –swarm
    3.      –swarm-discovery="consul://$(docker-machine ip mh-keystore):8500"
    4.      –engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500"
    5.      –engine-opt="cluster-advertise=eth1:2376"
    6.    mhs-demo1

    3.列出所有的主机来确认它们都处于运行状态。

    1. $ docker-machine ls
    2.  
    3.  NAME         ACTIVE   DRIVER       STATE     URL                         SWARM
    4.  default      –        virtualbox   Running   tcp://192.168.99.100:2376
    5.  mh-keystore  *        virtualbox   Running   tcp://192.168.99.103:2376
    6.  mhs-demo0    –        virtualbox   Running   tcp://192.168.99.104:2376   mhs-demo0 (master)
    7.  mhs-demo1    –        virtualbox   Running   tcp://192.168.99.105:2376   mhs-demo0

    这个时候你已经在你的网络配置了一组主机。下面将准备使用这些主机为你的容器创建一个多主机网络。

    创建overlay网络

    1.进入swarm master主机。

    1. $ eval $(docker-machine env –swarm mhs-demo0)

    2.使用docker info命令来查看swarm。

    1. $ docker info
    2.  
    3.  Containers: 3
    4.  Images: 2
    5.  Role: primary
    6.  Strategy: spread
    7.  Filters: affinity, health, constraint, port, dependency
    8.  Nodes: 2
    9.  mhs-demo0: 192.168.99.104:2376
    10.  └ Containers: 2
    11.  └ Reserved CPUs: 0 / 1
    12.  └ Reserved Memory: 0 B / 1.021 GiB
    13.  └ Labels: executiondriver=native-0.2, kernelversion=4.1.10-boot2docker, operatingsystem=Boot2Docker 1.9.0 (TCL 6.4); master : 4187d2c – Wed Oct 14 14:00:28 UTC 2015, provider=virtualbox, storagedriver=aufs
    14.  mhs-demo1: 192.168.99.105:2376
    15.  └ Containers: 1
    16.  └ Reserved CPUs: 0 / 1
    17.  └ Reserved Memory: 0 B / 1.021 GiB
    18.  └ Labels: executiondriver=native-0.2, kernelversion=4.1.10-boot2docker, operatingsystem=Boot2Docker 1.9.0 (TCL 6.4); master : 4187d2c – Wed Oct 14 14:00:28 UTC 2015, provider=virtualbox, storagedriver=aufs
    19.  CPUs: 2
    20.  Total Memory: 2.043 GiB
    21.  Name: 30438ece0915

    从以上的信息我们可以得出集群运行着三个容器和master有两个镜像。
    3.创建overlay网络。

    1. $ docker network create –driver overlay –subnet=10.0.9.0/24 my-net

    你只需要在集群中的一台主机创建这个网络。在这个示例中我们在swarm master创建网络,不过你可以很容易地在任意一台主机创建。
    1.检查网络是否在运行:

    1. $ docker network ls
    2.  
    3.  NETWORK ID          NAME                DRIVER
    4.  412c2496d0eb        mhs-demo1/host      host
    5.  dd51763e6dd2        mhs-demo0/bridge    bridge
    6.  6b07d0be843f        my-net              overlay
    7.  b4234109bd9b        mhs-demo0/none      null
    8.  1aeead6dd890        mhs-demo0/host      host
    9.  d0bb78cbe7bd        mhs-demo1/bridge    bridge
    10.  1c0eb8f69ebb        mhs-demo1/none      null

    由于你处于swarm master主机上,你会看到所有swarm节点的网络。
    2.依次切换到每台swarm节点并列出网络。

    1. $ eval $(docker-machine env mhs-demo0)
    2.  
    3.  $ docker network ls
    4.  
    5.  NETWORK ID          NAME                DRIVER
    6.  6b07d0be843f        my-net              overlay
    7.  dd51763e6dd2        bridge              bridge
    8.  b4234109bd9b        none                null
    9.  1aeead6dd890        host                host
    10.  
    11.  $ eval $(docker-machine env mhs-demo1)
    12.  
    13.  $ docker network ls
    14.  
    15.  NETWORK ID          NAME                DRIVER
    16.  d0bb78cbe7bd        bridge              bridge
    17.  1c0eb8f69ebb        none                null
    18.  412c2496d0eb        host                host
    19.  6b07d0be843f        my-net              overlay

    两个节点都有一个ID 6b07d0be843f的my-net网络。你现在已经运行了一个多主机容器网络。

    在你的网络上运行一个应用

    一旦你的网络创建好后,你就可以在任意一台主机上启动一个容器且它自动成为网络的一部分。
    1.登录入swarm master。

    1. $ eval $(docker-machine env –swarm mhs-demo0)

    2.在mhs-demo0主机上启动一个nginx。

    1. $ docker run -itd –name=web –network=my-net –env="constraint:node==mhs-demo0" nginx

    3.在mhs-demo1主机运行一个BusyBox实例并获取nginx服务器的主页内容。

    1. $ docker run -it –rm –network=my-net –env="constraint:node==mhs-demo1" busybox wget -O- http://web
    2.  
    3.  Unable to find image ‘busybox:latest’ locally
    4.  latest: Pulling from library/busybox
    5.  ab2b8a86ca6c: Pull complete
    6.  2c5ac3f849df: Pull complete
    7.  Digest: sha256:5551dbdfc48d66734d0f01cafee0952cb6e8eeecd1e2492240bf2fd9640c2279
    8.  Status: Downloaded newer image for busybox:latest
    9.  Connecting to web (10.0.0.2:80)
    10.  <!DOCTYPE html>
    11.  <html>
    12.  <head>
    13.  <title>Welcome to nginx!</title>
    14.  <style>
    15.  body {
    16.          width: 35em;
    17.          margin: 0 auto;
    18.          font-family: Tahoma, Verdana, Arial, sans-serif;
    19.  }
    20.  </style>
    21.  </head>
    22.  <body>
    23.  <h1>Welcome to nginx!</h1>
    24.  <p>If you see this page, the nginx web server is successfully installed and
    25.  working. Further configuration is required.</p>
    26.  
    27.  <p>For online documentation and support please refer to
    28.  <a href="http://nginx.org/">nginx.org</a>.<br/>
    29.  Commercial support is available at
    30.  <a href="http://nginx.com/">nginx.com</a>.</p>
    31.  
    32.  <p><em>Thank you for using nginx.</em></p>
    33.  </body>
    34.  </html>
    35.  –                    100% |*******************************|   612   0:00:00 ETA

    检查外部连接

    如你所看到的,Docker内置的overlay网络驱动为在同一个网络的多个主机的容器之间提供了开箱即用的连接。此外,连接到多主机网络的容器会自动的连接到docker_gwbridge网络。这个网络允许容器连接集群外部的网络。
    1.登录到mhs-demo1主机。

    1. $ eval $(docker-machine env mhs-demo1)

    2.使用docker network ls列出所有网络来查看docker_gwbridge。

    1. $ docker network ls
    2.  
    3.  NETWORK ID          NAME                DRIVER
    4.  6b07d0be843f        my-net              overlay
    5.  dd51763e6dd2        bridge              bridge
    6.  b4234109bd9b        none                null
    7.  1aeead6dd890        host                host
    8.  e1dbd5dff8be        docker_gwbridge     bridge

    3.在swarm master重复1和2步。

    1. $ eval $(docker-machine env mhs-demo0)
    2.  
    3.  $ docker network ls
    4.  
    5.  NETWORK ID          NAME                DRIVER
    6.  6b07d0be843f        my-net              overlay
    7.  d0bb78cbe7bd        bridge              bridge
    8.  1c0eb8f69ebb        none                null
    9.  412c2496d0eb        host                host
    10.  97102a22e8d2        docker_gwbridge     bridge

    4.检查nginx容器网络接口。

    1. $ docker exec web ip addr
    2.  
    3.  1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    4.  link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    5.  inet 127.0.0.1/8 scope host lo
    6.      valid_lft forever preferred_lft forever
    7.  inet6 ::1/128 scope host
    8.      valid_lft forever preferred_lft forever
    9.  22: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    10.  link/ether 02:42:0a:00:09:03 brd ff:ff:ff:ff:ff:ff
    11.  inet 10.0.9.3/24 scope global eth0
    12.      valid_lft forever preferred_lft forever
    13.  inet6 fe80::42:aff:fe00:903/64 scope link
    14.      valid_lft forever preferred_lft forever
    15.  24: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    16.  link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
    17.  inet 172.18.0.2/16 scope global eth1
    18.      valid_lft forever preferred_lft forever
    19.  inet6 fe80::42:acff:fe12:2/64 scope link
    20.      valid_lft forever preferred_lft forever

    容器的eth0接口连接到my-net overlay网络,eth1连接到docker_gwbridge网络。

    Docker用户指南(11) – Docker网络命令

    本文介绍与docker网络和在其中的容器的交互的网络子命令示例。这些命令是:

  • docker network create
  • docker network connect
  • docker network ls
  • docker network rm
  • docker network disconnect
  • docker network inspect
  • 创建网络

    当你安装Docker Engine时,它自动创建一个bridge网络。此网络是docker engine依赖的docker0 bridge。除了这个网络,你可以创建自己的bridge或overlay网络。
    一个bridge网络存在于运行着docker engine实例的单个主机上。一个overlay网络能跨越运行着它们自己engine的多个主机。如果你执行docker network create并只提供一个网络名称,它将为你创建一个bridge网络。

    1. $ docker network create simple-network
    2.  
    3. 69568e6336d8c96bbf57869030919f7c69524f71183b44d80948bd3927c87f6a
    4.  
    5. $ docker network inspect simple-network
    6. [
    7.     {
    8.         "Name": "simple-network",
    9.         "Id": "69568e6336d8c96bbf57869030919f7c69524f71183b44d80948bd3927c87f6a",
    10.         "Scope": "local",
    11.         "Driver": "bridge",
    12.         "IPAM": {
    13.             "Driver": "default",
    14.             "Config": [
    15.                 {
    16.                     "Subnet": "172.22.0.0/16",
    17.                     "Gateway": "172.22.0.1/16"
    18.                 }
    19.             ]
    20.         },
    21.         "Containers": {},
    22.         "Options": {}
    23.     }
    24. ]

    不像bridge网络,在创建overlay网络之前需要一些提前存在的条件。这些条件是:

  • 有一个能访问的键值存储。Engine支持Consul, Etcd和ZooKeeper(分布式存储)键值存储。
  • 集群的主机能够访问键值存储。
  • 在集群的每一个主机正确配置Engine daemon。
  • 支持overlay网络的dockerd选项是:

  • –cluster-store
  • –cluster-store-opt
  • –cluster-advertise
  • 当你创建网络时,Engine默认会为你的网络创建一个不重叠的子网。你可以直接使用–subnet选项来指定一个子网覆盖默认的行为。在一个bridge网络中你只能指定一个子网,而overlay网络支持多个子网。

    创建网络时强烈推荐使用–subnet选项。如果–subnet选项没有指定,docker daemon会自动为你的网络选择和分配一个子网,它有可能与另一个不是docker控制的子网重叠。当容器连接到这样的网络时会导致引引连接问题。

    除了–subnet选项,你也可以指定–gateway,–ip-range和–aux-address选项。

    1. $ docker network create -d overlay
    2.   –subnet=192.168.0.0/16
    3.   –subnet=192.170.0.0/16
    4.   –gateway=192.168.0.100
    5.   –gateway=192.170.0.100
    6.   –ip-range=192.168.1.0/24
    7.   –aux-address="my-router=192.168.1.5" –aux-address="my-switch=192.168.1.6"
    8.   –aux-address="my-printer=192.170.1.5" –aux-address="my-nas=192.170.1.6"
    9.   my-multihost-network

    确保你的子网不会重叠,如果重叠了,网络创建会失败,Engine返回一个错误。
    当你创建一个自定义网络时,你可以传递额外的选项到驱动。bridge驱动接受如下选项:

    选项 等于 描述
    com.docker.network.bridge.name 创建Linux bridge时使用的bridge名称
    com.docker.network.bridge.enable_ip_masquerade –ip-masq 启用IP伪装
    com.docker.network.bridge.enable_icc –icc 启用或禁用容器间连接
    com.docker.network.bridge.host_binding_ipv4 –ip 绑定容器端口时的默认IP
    com.docker.network.driver.mtu –mtu 设置容器网络MTU

    com.docker.network.driver.mtu选项同样被overlay驱动支持。
    下面的参数可以传递到docker network create创建任何网络驱动时。

    参数 等于 描述
    –internal 限制对网络的外部访问
    –ipv6 –ipv6 启用IPv6网络

    下面的示例使用-o绑定一个特定ip地址用来绑定端口,然后使用docker network inspect来查看网络,最后附加一个新容器到这个新网络。

    1. $ docker network create -o "com.docker.network.bridge.host_binding_ipv4"="172.23.0.1" my-network
    2.  
    3. b1a086897963e6a2e7fc6868962e55e746bee8ad0c97b54a5831054b5f62672a
    4.  
    5. $ docker network inspect my-network
    6.  
    7. [
    8.     {
    9.         "Name": "my-network",
    10.         "Id": "b1a086897963e6a2e7fc6868962e55e746bee8ad0c97b54a5831054b5f62672a",
    11.         "Scope": "local",
    12.         "Driver": "bridge",
    13.         "IPAM": {
    14.             "Driver": "default",
    15.             "Options": {},
    16.             "Config": [
    17.                 {
    18.                     "Subnet": "172.23.0.0/16",
    19.                     "Gateway": "172.23.0.1/16"
    20.                 }
    21.             ]
    22.         },
    23.         "Containers": {},
    24.         "Options": {
    25.             "com.docker.network.bridge.host_binding_ipv4": "172.23.0.1"
    26.         }
    27.     }
    28. ]
    29.  
    30. $ docker run -d -P –name redis –network my-network redis
    31.  
    32. bafb0c808c53104b2c90346f284bda33a69beadcab4fc83ab8f2c5a4410cd129
    33.  
    34. $ docker ps
    35.  
    36. CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                        NAMES
    37. bafb0c808c53        redis               "/entrypoint.sh redis"   4 seconds ago       Up 3 seconds        172.23.0.1:32770->6379/tcp   redis

    连接容器

    你可以连接一个存在的容器到一个或多个网络。一个容器能连接到使用不同网络驱动的网络。一旦连接,容器就能够使用另一个容器IP地址或名称为通信。
    对于支持多主机连接的overlay网络或自定义插件,从不同主机启动的容器连接到相同的多主机网络也能够也这种方式通信。

    基本的容器网络示例

    1.首先创建和运行两个容器,container1和container2:

    1. $ docker run -itd –name=container1 busybox
    2.  
    3. 18c062ef45ac0c026ee48a83afa39d25635ee5f02b58de4abc8f467bcaa28731
    4.  
    5. $ docker run -itd –name=container2 busybox
    6.  
    7. 498eaaaf328e1018042c04b2de04036fc04719a6e39a097a4f4866043a2c2152

    2.创建一个隔离的bridge网络来测试。

    1. $ docker network create -d bridge –subnet 172.25.0.0/16 isolated_nw
    2.  
    3. 06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8

    3.连接container2到上步创建的网络,然后查看这个网络:

    1. $ docker network connect isolated_nw container2
    2.  
    3. $ docker network inspect isolated_nw
    4.  
    5. [
    6.     {
    7.         "Name": "isolated_nw",
    8.         "Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
    9.         "Scope": "local",
    10.         "Driver": "bridge",
    11.         "IPAM": {
    12.             "Driver": "default",
    13.             "Config": [
    14.                 {
    15.                     "Subnet": "172.25.0.0/16",
    16.                     "Gateway": "172.25.0.1/16"
    17.                 }
    18.             ]
    19.         },
    20.         "Containers": {
    21.             "90e1f3ec71caf82ae776a827e0712a68a110a3f175954e5bd4222fd142ac9428": {
    22.                 "Name": "container2",
    23.                 "EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
    24.                 "MacAddress": "02:42:ac:19:00:02",
    25.                 "IPv4Address": "172.25.0.2/16",
    26.                 "IPv6Address": ""
    27.             }
    28.         },
    29.         "Options": {}
    30.     }
    31. ]

    注意到container2自动分配了一个IP地址。因为你创建网络时指定了–subnet,IP地址从这个子网选择。
    提醒一下,目前container1只连接到了默认的bridge网络。
    4.启动第三个容器,不过这次使用–ip参数来指定一个IP地址和使用docker run命令的–network参数来连接到isolated_nw网络:

    1. $ docker run –network=isolated_nw –ip=172.25.3.3 -itd –name=container3 busybox
    2.  
    3. 467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551

    只要你为容器指定的Ip地址是网络子网的一部分,你就可以当连接到一个网络时使用–ip或–ip6参数来分配一个IPv4或IPv6地址给容器。当你以这样的方式在一个使用自定义网络容器中指定一个IP地址时,配置将作为容器配置的一部分保留,并在重新加载容器时应用。当使用非用户定义的网络时,保留分配的IP地址,因为除非你使用用户定义的网络,否则当Docker守护程序重新启动时,不能保证容器的子网不会更改。
    5.查看container3使用的网络资源。

    1. $ docker inspect –format=”  container3
    2.  
    3. {"isolated_nw":
    4.   {"IPAMConfig":
    5.     {
    6.       "IPv4Address":"172.25.3.3"},
    7.       "NetworkID":"1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
    8.       "EndpointID":"dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103",
    9.       "Gateway":"172.25.0.1",
    10.       "IPAddress":"172.25.3.3",
    11.       "IPPrefixLen":16,
    12.       "IPv6Gateway":"",
    13.       "GlobalIPv6Address":"",
    14.       "GlobalIPv6PrefixLen":0,
    15.       "MacAddress":"02:42:ac:19:03:03"}
    16.     }
    17.   }
    18. }

    因为你启动container3容器时指定了isolated_nw网络,它不再连接到默认的bridge网络。
    6.查看container2使用的网络资源。

    1. $ docker inspect –format=”  container2 | python -m json.tool
    2.  
    3. {
    4.     "bridge": {
    5.         "NetworkID":"7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
    6.         "EndpointID": "0099f9efb5a3727f6a554f176b1e96fca34cae773da68b3b6a26d046c12cb365",
    7.         "Gateway": "172.17.0.1",
    8.         "GlobalIPv6Address": "",
    9.         "GlobalIPv6PrefixLen": 0,
    10.         "IPAMConfig": null,
    11.         "IPAddress": "172.17.0.3",
    12.         "IPPrefixLen": 16,
    13.         "IPv6Gateway": "",
    14.         "MacAddress": "02:42:ac:11:00:03"
    15.     },
    16.     "isolated_nw": {
    17.         "NetworkID":"1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
    18.         "EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
    19.         "Gateway": "172.25.0.1",
    20.         "GlobalIPv6Address": "",
    21.         "GlobalIPv6PrefixLen": 0,
    22.         "IPAMConfig": null,
    23.         "IPAddress": "172.25.0.2",
    24.         "IPPrefixLen": 16,
    25.         "IPv6Gateway": "",
    26.         "MacAddress": "02:42:ac:19:00:02"
    27.     }
    28. }

    注意看container2属于两个网络。当你启动它时加入到了默认的bridge网络和在第3步连接到了isolated_nw网络。
    虚拟化技术
    eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
    eth1 Link encap:Ethernet HWaddr 02:42:AC:15:00:02
    7.使用docker attach命令连接到运行中的container2并查看它的网络配置。

    1. $ sudo ifconfig -a
    2.  
    3. eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03
    4.           inet addr:172.17.0.3  Bcast:0.0.0.0  Mask:255.255.0.0
    5.           inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
    6.           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
    7.           RX packets:8 errors:0 dropped:0 overruns:0 frame:0
    8.           TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
    9.           collisions:0 txqueuelen:0
    10.           RX bytes:648 (648.0 B)  TX bytes:648 (648.0 B)
    11.  
    12. eth1      Link encap:Ethernet  HWaddr 02:42:AC:15:00:02
    13.           inet addr:172.25.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
    14.           inet6 addr: fe80::42:acff:fe19:2/64 Scope:Link
    15.           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
    16.           RX packets:8 errors:0 dropped:0 overruns:0 frame:0
    17.           TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
    18.           collisions:0 txqueuelen:0
    19.           RX bytes:648 (648.0 B)  TX bytes:648 (648.0 B)
    20.  
    21. lo        Link encap:Local Loopback
    22.           inet addr:127.0.0.1  Mask:255.0.0.0
    23.           inet6 addr: ::1/128 Scope:Host
    24.           UP LOOPBACK RUNNING  MTU:65536  Metric:1
    25.           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
    26.           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
    27.           collisions:0 txqueuelen:0
    28.           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    8.Docker内置的DNS服务器为连接到给定网络的容器启用名称解析。这意味着任意连接的容器能通过容器的名称ping在同一网络另一个容器。从container2内,你能通过container3的名称ping它。

    1. / # ping -w 4 container3
    2. PING container3 (172.25.3.3): 56 data bytes
    3. 64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.070 ms
    4. 64 bytes from 172.25.3.3: seq=1 ttl=64 time=0.080 ms
    5. 64 bytes from 172.25.3.3: seq=2 ttl=64 time=0.080 ms
    6. 64 bytes from 172.25.3.3: seq=3 ttl=64 time=0.097 ms
    7.  
    8. — container3 ping statistics —
    9. 4 packets transmitted, 4 packets received, 0% packet loss
    10. round-trip min/avg/max = 0.070/0.081/0.097 ms

    这个功能对于默认的bridge网络不可用。container1和container2都是连接到bridge网络,你不能使用容器名称来从container2 ping container1。

    1. / # ping -w 4 container1
    2. ping: bad address ‘container1’

    不过你仍然可以直接ping ip地址:

    1. / # ping -w 4 172.17.0.2
    2. PING 172.17.0.2 (172.17.0.2): 56 data bytes
    3. 64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.095 ms
    4. 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
    5. 64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.072 ms
    6. 64 bytes from 172.17.0.2: seq=3 ttl=64 time=0.101 ms
    7.  
    8. — 172.17.0.2 ping statistics —
    9. 4 packets transmitted, 4 packets received, 0% packet loss
    10. round-trip min/avg/max = 0.072/0.085/0.101 ms

    9.目前container2附加到了bridge和isolated_nw这两个网络,所以你能与container1和container3两个通信。不过container3和container1没有在任何一个共同的网络,所以它们无法通信。要验证这个,可以attach到container3并尝试ping container1的ip地址。

    1. $ docker attach container3
    2.  
    3. $ ping 172.17.0.2
    4. PING 172.17.0.2 (172.17.0.2): 56 data bytes
    5. ^C
    6.  
    7. — 172.17.0.2 ping statistics —
    8. 10 packets transmitted, 0 packets received, 100% packet loss

    链接(linking)不使用自定义网络的容器

    当你完成了上面的步骤后,container2能自动地解析container3的名称,是因为两个容器都连接到了isolated_nw网络。不过连接到默认bridge网络的容器无法解析彼此的容器名称。如果你需要在默认的bridge网络能够解析彼此的容器名称,你需要使用旧的链接(legacy link)功能。这是使用–link唯一推荐的一个用例。你应该考虑使用自定义网络替代。
    使用旧的link参数对在默认bridge网络的通信增加了如下功能:

  • 解析容器名称为IP地址
  • 使用-link=CONTAINER-NAME:ALIAS定义一个网络别名作为引用链接的容器的另一个方式
  • 安全容器连接
  • 环境变量注入
  • 重申一下,当你使用一个自定义的网络时,下面的所有功能不需要额外的配置默认提供。此外,你能够动态地附加到多个网络和从多个网络脱离。

  • 使用DNS自动名称解析
  • 支持使用–link选项为链接的容器提供网络名称别名
  • 为网络中的容器自动提供安全的隔离环境
  • 环境变量注入
  • 下面的示例简单的介绍如何使用–link。
    1.继续上面的示例,创建一个container4容器并连接它到isolated_nw网络。此外,使用–link参数链接它到容器container5(目前还不存在)。

    1. $ docker run –network=isolated_nw -itd –name=container4 –link container5:c5 busybox
    2.  
    3. 01b5df970834b77a9eadbaff39051f237957bd35c4c56f11193e0594cfd5117c

    这有点棘手,因为container5还不存在。当创建container5后,container4就能够解析c5为container5的ip地址。

    注意:使用旧的链接创建的容器之间的任意链接是静态并且硬绑定容器的别名。它不允许链接的容器重启。在自定义网络的链接功能支持容器之间动态链接并允许重启和链接的容器ip地址更改。

    因为你没有创建容器container5,所以尝试ping它的话会返回错误。附加到container4并尝试ping container5或c5:

    1. $ docker attach container4
    2.  
    3. $ ping container5
    4.  
    5. ping: bad address ‘container5’
    6.  
    7. $ ping c5
    8.  
    9. ping: bad address ‘c5’

    2.创建另一个容器container5,并链接它到使用c4别名的container4.

    1. $ docker run –network=isolated_nw -itd –name=container5 –link container4:c4 busybox
    2.  
    3. 72eccf2208336f31e9e33ba327734125af00d1e1d2657878e2ee8154fbb23c7a

    现在附着到container4并尝试ping c5和container5。

    1. $ docker attach container4
    2.  
    3. / # ping -w 4 c5
    4. PING c5 (172.25.0.5): 56 data bytes
    5. 64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
    6. 64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
    7. 64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
    8. 64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
    9.  
    10. — c5 ping statistics —
    11. 4 packets transmitted, 4 packets received, 0% packet loss
    12. round-trip min/avg/max = 0.070/0.081/0.097 ms
    13.  
    14. / # ping -w 4 container5
    15. PING container5 (172.25.0.5): 56 data bytes
    16. 64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
    17. 64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
    18. 64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
    19. 64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
    20.  
    21. — container5 ping statistics —
    22. 4 packets transmitted, 4 packets received, 0% packet loss
    23. round-trip min/avg/max = 0.070/0.081/0.097 ms

    3.最后,附着到container5并验证你能ping container4.

    1. $ docker attach container5
    2.  
    3. / # ping -w 4 c4
    4. PING c4 (172.25.0.4): 56 data bytes
    5. 64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
    6. 64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
    7. 64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
    8. 64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
    9.  
    10. — c4 ping statistics —
    11. 4 packets transmitted, 4 packets received, 0% packet loss
    12. round-trip min/avg/max = 0.065/0.070/0.082 ms
    13.  
    14. / # ping -w 4 container4
    15. PING container4 (172.25.0.4): 56 data bytes
    16. 64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
    17. 64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
    18. 64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
    19. 64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
    20.  
    21. — container4 ping statistics —
    22. 4 packets transmitted, 4 packets received, 0% packet loss
    23. round-trip min/avg/max = 0.065/0.070/0.082 ms

    网络别名作用域示例

    当你链接容器,不论使用的是旧的link方式或者使用自定义网络,你指定的任何别名只对链接的容器有效,不会对在默认bridge网络的其它容器有用。
    此外,如果一个容器属于多个网络,一个给定的链接别名的作用域是一个给定的网络。因此一个容器在不同的网络能被链接到不同的别名,且不在同一个网络的容器别名不会有效。
    下面的示例说明了这些点。
    1.创建另一个网络local_alias

    1. $ docker network create -d bridge –subnet 172.26.0.0/24 local_alias
    2. 76b7dc932e037589e6553f59f76008e5b76fa069638cd39776b890607f567aaa

    2.下一步连接container4和container5到一个新的带有别名foo和bar的网络local_alias:

    1. $ docker network connect –link container5:foo local_alias container4
    2. $ docker network connect –link container4:bar local_alias container5

    3.附着container4并尝试ping container4的别名foo,然后尝试ping container5的别名c5:

    1. $ docker attach container4
    2. / # ping -w 4 foo
    3. PING foo (172.26.0.3): 56 data bytes
    4. 64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.070 ms
    5. 64 bytes from 172.26.0.3: seq=1 ttl=64 time=0.080 ms
    6. 64 bytes from 172.26.0.3: seq=2 ttl=64 time=0.080 ms
    7. 64 bytes from 172.26.0.3: seq=3 ttl=64 time=0.097 ms
    8. — foo ping statistics —
    9. 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.070/0.081/0.097 ms
    10. / # ping -w 4 c5
    11. PING c5 (172.25.0.5): 56 data bytes
    12. 64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
    13. 64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
    14. 64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
    15. 64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
    16. — c5 ping statistics —
    17. 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.070/0.081/0.097 ms

    两个ping都成功了,不过子网不同,意味着网络不一样。
    4.从isolated_nw网络断开container5的连接。附着container4并尝试ping c5和foo。

    1. $ docker network disconnect isolated_nw container5
    2.  
    3. $ docker attach container4
    4.  
    5. / # ping -w 4 c5
    6. ping: bad address ‘c5’
    7.  
    8. / # ping -w 4 foo
    9. PING foo (172.26.0.3): 56 data bytes
    10. 64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.070 ms
    11. 64 bytes from 172.26.0.3: seq=1 ttl=64 time=0.080 ms
    12. 64 bytes from 172.26.0.3: seq=2 ttl=64 time=0.080 ms
    13. 64 bytes from 172.26.0.3: seq=3 ttl=64 time=0.097 ms
    14.  
    15. — foo ping statistics —
    16. 4 packets transmitted, 4 packets received, 0% packet loss
    17. round-trip min/avg/max = 0.070/0.081/0.097 ms

    你不再能通过属于isolated_nw网络的container5的别名c5到达此容器,不过你仍然能够使用别名foo到达container4。

    docker网络的限制

    虽然docker network命令是控制你的容器使用的网络的推荐方法,不过它有一些限制。

    环境变量注入

    环境变量本质上是静态的并且容器启动之后不再能更改环境变量。旧的–link参数共享所有的环境变量给链接的容器,不过docker network命令没有等同的设置。当你使用docker network连接到一个网络,容器之间的环境变量无法动态的存在。

    理解网络范围的别名

    旧的link提供了在配置别名的容器隔离的名称解析。网络范围的别名不允许这种方式的隔离,但是此别名在网络中的所有成员可用。
    下面的示例说明了这些限制。
    1.创建另一个容器container6,指定网络为isolated_nw,网络别名为app。

    1. $ docker run –network=isolated_nw -itd –name=container6 –network-alias app busybox
    2.  
    3. 8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17

    2.附着到container4。尝试ping container6和网络别名app。注意这两个ip是相同的。

    1. $ docker attach container4
    2.  
    3. / # ping -w 4 app
    4. PING app (172.25.0.6): 56 data bytes
    5. 64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
    6. 64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
    7. 64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
    8. 64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms
    9.  
    10. — app ping statistics —
    11. 4 packets transmitted, 4 packets received, 0% packet loss
    12. round-trip min/avg/max = 0.070/0.081/0.097 ms
    13.  
    14. / # ping -w 4 container6
    15. PING container5 (172.25.0.6): 56 data bytes
    16. 64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
    17. 64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
    18. 64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
    19. 64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms
    20.  
    21. — container6 ping statistics —
    22. 4 packets transmitted, 4 packets received, 0% packet loss
    23. round-trip min/avg/max = 0.070/0.081/0.097 ms

    3.连接container6到local_alias,设置别名scoped-app。

    1. $ docker network connect –alias scoped-app local_alias container6

    现在container6在网络isolated_nw的别名为app,在local_alias网络的别名为scoped-app。
    4.尝试从container4(都连接到了上面的两个网络)和container5(只连接到isolated_nw网络)ping这些别名。

    1. $ docker attach container4
    2.  
    3. / # ping -w 4 scoped-app
    4. PING foo (172.26.0.5): 56 data bytes
    5. 64 bytes from 172.26.0.5: seq=0 ttl=64 time=0.070 ms
    6. 64 bytes from 172.26.0.5: seq=1 ttl=64 time=0.080 ms
    7. 64 bytes from 172.26.0.5: seq=2 ttl=64 time=0.080 ms
    8. 64 bytes from 172.26.0.5: seq=3 ttl=64 time=0.097 ms
    9.  
    10. — foo ping statistics —
    11. 4 packets transmitted, 4 packets received, 0% packet loss
    12. round-trip min/avg/max = 0.070/0.081/0.097 ms

    再附着到container5。

    1. $ docker attach container5
    2.  
    3. / # ping -w 4 scoped-app
    4. ping: bad address ‘scoped-app’

    这说明了别名的作用域是在它指定的网络并且只有连接到这个网络的容器才能够访问这些别名。

    将多个容器解析到同一个别名

    在同一个网络中多个容器可以共享同一个网络范围别名(network-scoped alias)。下面的示例解释是如何工作的。
    1.启动container7,在isolated_nw网络中设置一个与container6同样的别名app。

    1. $ docker run –network=isolated_nw -itd –name=container7 –network-alias app busybox
    2.  
    3. 3138c678c123b8799f4c7cc6a0cecc595acbdfa8bf81f621834103cd4f504554

    当多个容器共享同一个别名时,这个别名将解析到这些容器的其中一个。如果那个容器不可用,别名将解析到另一个可用的。这在集群中提供了一种高可用的方法。

    当别名解析出多个ip时,容器将随机的选择一个。由于这个原因,下面的示例的结果可能与你的不一样。

    2.在container4持续地ping多几个app。

    1. $ docker attach container4
    2.  
    3. $ ping app
    4. PING app (172.25.0.6): 56 data bytes
    5. 64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
    6. 64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
    7. 64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
    8. 64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms

    目前解析到了container6。
    3.在另一个终端,停止container6。

    1. $ docker stop container6

    然后附着到container4观察ping的输出,当container4停机时ping会停止。
    4.退出ping命令,再执行一次。

    1. $ ping app
    2.  
    3. PING app (172.25.0.7): 56 data bytes
    4. 64 bytes from 172.25.0.7: seq=0 ttl=64 time=0.095 ms
    5. 64 bytes from 172.25.0.7: seq=1 ttl=64 time=0.075 ms
    6. 64 bytes from 172.25.0.7: seq=2 ttl=64 time=0.072 ms
    7. 64 bytes from 172.25.0.7: seq=3 ttl=64 time=0.101 ms

    现在app别名解析到了container7的ip地址。
    5.最后一次测试,重启container6。

    1. $ docker start container6

    在终端中附着container4,再次执行ping命令。它可能会再次解析到container6。如果你多次启动和停止ping命令,你会发现有时解析到container6,有时是container7。

    1. $ docker attach container4
    2.  
    3. $ ping app
    4. PING app (172.25.0.6): 56 data bytes
    5. 64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
    6. 64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
    7. 64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
    8. 64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms

    断开容器网络

    你可以在任何时候使用docker network disconnect命令从一个网络断开容器。
    1.从isolated_nw网络断开container2,然后查看container2和isolated_nw网络。

    1. $ docker network disconnect isolated_nw container2
    2.  
    3. $ docker inspect –format=”  container2 | python -m json.tool
    4.  
    5. {
    6.     "bridge": {
    7.         "NetworkID":"7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
    8.         "EndpointID": "9e4575f7f61c0f9d69317b7a4b92eefc133347836dd83ef65deffa16b9985dc0",
    9.         "Gateway": "172.17.0.1",
    10.         "GlobalIPv6Address": "",
    11.         "GlobalIPv6PrefixLen": 0,
    12.         "IPAddress": "172.17.0.3",
    13.         "IPPrefixLen": 16,
    14.         "IPv6Gateway": "",
    15.         "MacAddress": "02:42:ac:11:00:03"
    16.     }
    17. }
    18.  
    19.  
    20. $ docker network inspect isolated_nw
    21.  
    22. [
    23.     {
    24.         "Name": "isolated_nw",
    25.         "Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
    26.         "Scope": "local",
    27.         "Driver": "bridge",
    28.         "IPAM": {
    29.             "Driver": "default",
    30.             "Config": [
    31.                 {
    32.                     "Subnet": "172.21.0.0/16",
    33.                     "Gateway": "172.21.0.1/16"
    34.                 }
    35.             ]
    36.         },
    37.         "Containers": {
    38.             "467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551": {
    39.                 "Name": "container3",
    40.                 "EndpointID": "dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103",
    41.                 "MacAddress": "02:42:ac:19:03:03",
    42.                 "IPv4Address": "172.25.3.3/16",
    43.                 "IPv6Address": ""
    44.             }
    45.         },
    46.         "Options": {}
    47.     }
    48. ]

    2.当一个容器从一个网络断开,它就不再能够与其它连接到这个网络的容器通信,除非它还有与这些容器相同的网络。验证一下在isolated_nw网络中,container2不再能够达到container3。

    1. $ docker attach container2
    2.  
    3. / # ifconfig
    4. eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03 
    5.           inet addr:172.17.0.3  Bcast:0.0.0.0  Mask:255.255.0.0
    6.           inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
    7.           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
    8.           RX packets:8 errors:0 dropped:0 overruns:0 frame:0
    9.           TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
    10.           collisions:0 txqueuelen:0
    11.           RX bytes:648 (648.0 B)  TX bytes:648 (648.0 B)
    12.  
    13. lo        Link encap:Local Loopback 
    14.           inet addr:127.0.0.1  Mask:255.0.0.0
    15.           inet6 addr: ::1/128 Scope:Host
    16.           UP LOOPBACK RUNNING  MTU:65536  Metric:1
    17.           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
    18.           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
    19.           collisions:0 txqueuelen:0
    20.           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    21.  
    22. / # ping container3
    23. PING container3 (172.25.3.3): 56 data bytes
    24. ^C
    25. — container3 ping statistics —
    26. 2 packets transmitted, 0 packets received, 100% packet loss

    3.验证container2仍然能够访问默认的bridge网络。

    1. / # ping container1
    2. PING container1 (172.17.0.2): 56 data bytes
    3. 64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.119 ms
    4. 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.174 ms
    5. ^C
    6. — container1 ping statistics —
    7. 2 packets transmitted, 2 packets received, 0% packet loss
    8. round-trip min/avg/max = 0.119/0.146/0.174 ms
    9. / #

    处理过时的网络endpoints

    在一些场景中,如docker daemon在多主机网络意外的重启,daemon无法清理过时的连接endpoint。当一个新的容器连接到与过时endpoint有相同名称的网络时会导致错误。

    1. ERROR: Cannot start container bc0b19c089978f7845633027aa3435624ca3d12dd4f4f764b61eac4c0610f32e: container already connected to network multihost

    要清理这些过时的endpoints,先删除容器再强制地从网络断开它。

    1. $ docker run -d –name redis_db –network multihost redis
    2.  
    3. ERROR: Cannot start container bc0b19c089978f7845633027aa3435624ca3d12dd4f4f764b61eac4c0610f32e: container already connected to network multihost
    4.  
    5. $ docker rm -f redis_db
    6.  
    7. $ docker network disconnect -f multihost redis_db
    8.  
    9. $ docker run -d –name redis_db –network multihost redis
    10.  
    11. 7d986da974aeea5e9f7aca7e510bdb216d58682faa83a9040c2f2adc0544795a

    删除网络

    当在一个网络的容器都停止或断开了,你可以删除这个网络。
    1.从isolated_nw断开container3。

    1. $ docker network disconnect isolated_nw container3

    2.查看isolated_nw网络验证已经没有容器连接它了。

    1. $ docker network inspect isolated_nw
    2.  
    3. [
    4.     {
    5.         "Name": "isolated_nw",
    6.         "Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
    7.         "Scope": "local",
    8.         "Driver": "bridge",
    9.         "IPAM": {
    10.             "Driver": "default",
    11.             "Config": [
    12.                 {
    13.                     "Subnet": "172.21.0.0/16",
    14.                     "Gateway": "172.21.0.1/16"
    15.                 }
    16.             ]
    17.         },
    18.         "Containers": {},
    19.         "Options": {}
    20.     }
    21. ]

    3.删除isolated_nw网络。

    1. $ docker network rm isolated_nw

    4.列出所有网络并验证isolated_nw已经不存在。

    1. docker network ls
    2.  
    3. NETWORK ID          NAME                DRIVER              SCOPE
    4. 4bb8c9bf4292        bridge              bridge              local
    5. 43575911a2bd        host                host                local
    6. 76b7dc932e03        local_alias         bridge              local
    7. b1a086897963        my-network          bridge              local
    8. 3eb020e70bfd        none                null                local
    9. 69568e6336d8        simple-network      bridge              local