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

    Docker用户指南(10) – Docker容器网络

    本文对Docker提供的几个默认网络的行为做个简单介绍。描述默认创建的网络类型,以及如何创建自己的用户定义的网络。同时说明在单台主机或跨主机集群创建网络所需的资源。

    默认网络

    当你安装Docker后,它自动创建了三个网络。你可以使用docker network ls命令来列出这些网络。

    1. $ docker network ls
    2.  
    3. NETWORK ID          NAME                DRIVER
    4. 7fca4eb8c647        bridge              bridge
    5. 9f904ee27bf5        none                null
    6. cf03ee007fb4        host                host

    历史上,这三个网络是Docker的一部分。当你运行一个容器时,你可以使用–network参数来指定你想运行容器在哪个网络上。这三个网络仍然可用。
    称为docker0的bridge网络出现在所有Docker的安装中。除非你使用docker run –network=来指定其它网络,否则Docker daemon默认连接容器到这个网络。你可以在主机使用ifconfig命令看到这个bridge是主机网络堆栈的一部分。

    1. $ ifconfig
    2.  
    3. docker0   Link encap:Ethernet  HWaddr 02:42:47:bc:3a:eb
    4.           inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
    5.           inet6 addr: fe80::42:47ff:febc:3aeb/64 Scope:Link
    6.           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
    7.           RX packets:17 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:1100 (1.1 KB)  TX bytes:648 (648.0 B)

    none网络添加一个容器到一个容器特定网络堆栈。这样会使那个容器缺少网络接口。附加到这样一个容器,按如下来查看:

    1. $ docker attach nonenetcontainer
    2.  
    3. root@0cb243cd1293:/# cat /etc/hosts
    4. 127.0.0.1   localhost
    5. ::1 localhost ip6-localhost ip6-loopback
    6. fe00::0 ip6-localnet
    7. ff00::0 ip6-mcastprefix
    8. ff02::1 ip6-allnodes
    9. ff02::2 ip6-allrouters
    10. root@0cb243cd1293:/# ifconfig
    11. lo        Link encap:Local Loopback
    12.           inet addr:127.0.0.1  Mask:255.0.0.0
    13.           inet6 addr: ::1/128 Scope:Host
    14.           UP LOOPBACK RUNNING  MTU:65536  Metric:1
    15.           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
    16.           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
    17.           collisions:0 txqueuelen:0
    18.           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    19.  
    20. root@0cb243cd1293:/#

    注意:你按下CTRL-p CTRL-q与容器分离。

    host网络添加一个容器到主机的网络堆栈。你会发现容器内的网络配置与主机的相同。
    除了bridge网络,你不需要与这些默认的网络交互。虽然你能列出和查看它们,你不能删除它们。因为Docker的安装依赖它们。不过你可以添加你自己自定义的网络并且不再需要它们时可以删除。在学习创建你自己的网络之前,值得再看下默认的bridge网络。

    默认的bridge网络详细信息

    默认的bridge网络存在于所有的Docker主机中。docker network inspect网络返回关于网络的信息:

    1. $ docker network inspect bridge
    2.  
    3. [
    4.    {
    5.        "Name": "bridge",
    6.        "Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
    7.        "Scope": "local",
    8.        "Driver": "bridge",
    9.        "IPAM": {
    10.            "Driver": "default",
    11.            "Config": [
    12.                {
    13.                    "Subnet": "172.17.0.1/16",
    14.                    "Gateway": "172.17.0.1"
    15.                }
    16.            ]
    17.        },
    18.        "Containers": {},
    19.        "Options": {
    20.            "com.docker.network.bridge.default_bridge": "true",
    21.            "com.docker.network.bridge.enable_icc": "true",
    22.            "com.docker.network.bridge.enable_ip_masquerade": "true",
    23.            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
    24.            "com.docker.network.bridge.name": "docker0",
    25.            "com.docker.network.driver.mtu": "9001"
    26.        }
    27.    }
    28. ]

    Docker Engine自动给这个网络创建一个子网和网关。docker run命令自动地增加新容器到这个网络。

    1. $ docker run -itd –name=container1 busybox
    2.  
    3. 3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c
    4.  
    5. $ docker run -itd –name=container2 busybox
    6.  
    7. 94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c

    启动这两个容器后重新查看bridge网络会看到在这个网络有两个新启动的容器。它们的id显示在docker network inspect命令输出的“Containers”区块中:

    1. $ docker network inspect bridge
    2.  
    3. {[
    4.     {
    5.         "Name": "bridge",
    6.         "Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
    7.         "Scope": "local",
    8.         "Driver": "bridge",
    9.         "IPAM": {
    10.             "Driver": "default",
    11.             "Config": [
    12.                 {
    13.                     "Subnet": "172.17.0.1/16",
    14.                     "Gateway": "172.17.0.1"
    15.                 }
    16.             ]
    17.         },
    18.         "Containers": {
    19.             "3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c": {
    20.                 "EndpointID": "647c12443e91faf0fd508b6edfe59c30b642abb60dfab890b4bdccee38750bc1",
    21.                 "MacAddress": "02:42:ac:11:00:02",
    22.                 "IPv4Address": "172.17.0.2/16",
    23.                 "IPv6Address": ""
    24.             },
    25.             "94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c": {
    26.                 "EndpointID": "b047d090f446ac49747d3c37d63e4307be745876db7f0ceef7b311cbba615f48",
    27.                 "MacAddress": "02:42:ac:11:00:03",
    28.                 "IPv4Address": "172.17.0.3/16",
    29.                 "IPv6Address": ""
    30.             }
    31.         },
    32.         "Options": {
    33.             "com.docker.network.bridge.default_bridge": "true",
    34.             "com.docker.network.bridge.enable_icc": "true",
    35.             "com.docker.network.bridge.enable_ip_masquerade": "true",
    36.             "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
    37.             "com.docker.network.bridge.name": "docker0",
    38.             "com.docker.network.driver.mtu": "9001"
    39.         }
    40.     }
    41. ]

    上面的docker network inspect命令显示所有连接的容器和它们的网络资源。在这个默认网络的容器能够使用IP地址来互相通信。在这个默认的bridge网络Docker不支持自动服务发现。如果你想在这个默认的bridge网络使用容器名称来通信,你必须通过旧的docker run –link选项来连接容器。
    你可以attach一个运行中的容器来查看它的配置:

    1. $ docker attach container1
    2.  
    3. root@0cb243cd1293:/# ifconfig
    4. eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
    5.           inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
    6.           inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
    7.           UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
    8.           RX packets:16 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:1296 (1.2 KiB)  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)

    然后使用ping来发送三个ICMP请求测试在bridge网络的容器之间的连接性。

    1. root@0cb243cd1293:/# ping -w3 172.17.0.3
    2.  
    3. PING 172.17.0.3 (172.17.0.3): 56 data bytes
    4. 64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.096 ms
    5. 64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.080 ms
    6. 64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.074 ms
    7.  
    8. — 172.17.0.3 ping statistics —
    9. 3 packets transmitted, 3 packets received, 0% packet loss
    10. round-trip min/avg/max = 0.074/0.083/0.096 ms

    最后,使用cat命令检查container1网络配置:

    1. root@0cb243cd1293:/# cat /etc/hosts
    2.  
    3. 172.17.0.2  3386a527aa08
    4. 127.0.0.1   localhost
    5. ::1 localhost ip6-localhost ip6-loopback
    6. fe00::0 ip6-localnet
    7. ff00::0 ip6-mcastprefix
    8. ff02::1 ip6-allnodes
    9. ff02::2 ip6-allrouters

    按下CTRL-p CTRL-q离开container1,再attach到container2和重复这三个命令。

    1. $ docker attach container2
    2.  
    3. root@0cb243cd1293:/# 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:15 errors:0 dropped:0 overruns:0 frame:0
    9.           TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
    10.           collisions:0 txqueuelen:0
    11.           RX bytes:1166 (1.1 KiB)  TX bytes:1026 (1.0 KiB)
    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. root@0cb243cd1293:/# ping -w3 172.17.0.2
    23.  
    24. PING 172.17.0.2 (172.17.0.2): 56 data bytes
    25. 64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.067 ms
    26. 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
    27. 64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.072 ms
    28.  
    29. — 172.17.0.2 ping statistics —
    30. 3 packets transmitted, 3 packets received, 0% packet loss
    31. round-trip min/avg/max = 0.067/0.071/0.075 ms
    32. / # cat /etc/hosts
    33. 172.17.0.3  94447ca47985
    34. 127.0.0.1   localhost
    35. ::1 localhost ip6-localhost ip6-loopback
    36. fe00::0 ip6-localnet
    37. ff00::0 ip6-mcastprefix
    38. ff02::1 ip6-allnodes
    39. ff02::2 ip6-allrouters

    默认的docker0 bridge网络支持端口映射的使用,docker run –link允许在docker0网络中的容器之间通信。这些技术设置起来很麻烦,容易出错。不过它们仍然可用,最后定义自己的bridge网络来避免它们。

    自定义网络

    你可以创建自己定义的网络来更好的隔离容器。Docker为方便创建这些网络提供了一些默认的网络驱动。你可以创建一个bridge网络,overlay网络或MACVLAN网络。你也可以创建一个写入自己规格的网络插件(network plugin)或远程网络(remote network)。
    你既能创建多个网络,也能添加容器到多个网络。容器只能在它加入的网络内通信不过跨网络。一个容器附加到两个网络能与这两个网络的所有成员通信。当一个容器连接多个网络时,由第一个非内部网络提供外部连接。

    bridge网络

    创建最简单的自定义网络是bridge网络。这个网络类似于历史默认的docker0网络。不过增加了一些新的功能和一些旧的功能不再可用。

    1. $ docker network create –driver bridge isolated_nw
    2. 1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b
    3.  
    4. $ docker network inspect isolated_nw
    5.  
    6. [
    7.     {
    8.         "Name": "isolated_nw",
    9.         "Id": "1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
    10.         "Scope": "local",
    11.         "Driver": "bridge",
    12.         "IPAM": {
    13.             "Driver": "default",
    14.             "Config": [
    15.                 {
    16.                     "Subnet": "172.21.0.0/16",
    17.                     "Gateway": "172.21.0.1/16"
    18.                 }
    19.             ]
    20.         },
    21.         "Containers": {},
    22.         "Options": {}
    23.     }
    24. ]
    25.  
    26. $ docker network ls
    27.  
    28. NETWORK ID          NAME                DRIVER
    29. 9f904ee27bf5        none                null
    30. cf03ee007fb4        host                host
    31. 7fca4eb8c647        bridge              bridge
    32. c5ee82f76de3        isolated_nw         bridge

    创建网络后,你可以使用docker run –network=选项来加入这个网络。

    1. $ docker run –network=isolated_nw -itd –name=container3 busybox
    2.  
    3. 8c1a0a5be480921d669a073393ade66a3fc49933f08bcc5515b37b8144f6d47c
    4.  
    5. $ docker network inspect isolated_nw
    6. [
    7.     {
    8.         "Name": "isolated_nw",
    9.         "Id": "1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
    10.         "Scope": "local",
    11.         "Driver": "bridge",
    12.         "IPAM": {
    13.             "Driver": "default",
    14.             "Config": [
    15.                 {}
    16.             ]
    17.         },
    18.         "Containers": {
    19.             "8c1a0a5be480921d669a073393ade66a3fc49933f08bcc5515b37b8144f6d47c": {
    20.                 "EndpointID": "93b2db4a9b9a997beb912d28bcfc117f7b0eb924ff91d48cfa251d473e6a9b08",
    21.                 "MacAddress": "02:42:ac:15:00:02",
    22.                 "IPv4Address": "172.21.0.2/16",
    23.                 "IPv6Address": ""
    24.             }
    25.         },
    26.         "Options": {}
    27.     }
    28. ]

    你要加入到这个网络的容器必须在同一台docker主机上。在这个网络的每一个容器能马上与在这个网络的其它容器通信。虽然网络本身将容器与外部网络隔离了。
    虚拟化技术
    在一个自定义的bridge网络中,linking是不支持的。你可以在这个网络的容器公开和发布容器端口。如果你想使bridge的一部分容器可用于一个外部的网络会非常有用。
    虚拟化技术
    你想在一个主机运行一个相关的小的网络这种场景下,可以使用bridge网络。

    docker_gwbridge网络

    在两种不同的情况下docker会自动创建一个本地bridge网络docker_gwbridge:

  • 当你初始化或加入一个swarm,docker创建docker_gwbridge网络并用它来与不同的主机上的swarm节点之间通信。
  • 当没有一个容器网络能提供外部连接时,docker连接容器到docker_gwbridge网络以及容器的其它网络,以使得容器能连接外部网络或其它swarm节点。
  • 如果你需要一个自定义的配置你可以提前创建docker_gwbridge网络,否则docker会根据需要创建它。下面的示例使用一些自定义选项来创建docker_gwbridge网络。

    1. $ docker network create –subnet 172.30.0.0/16
    2.                         –opt com.docker.network.bridge.name=docker_gwbridge
    3.             –opt com.docker.network.bridge.enable_icc=false
    4.             docker_gwbridge

    当你使用overlay网络时docker_gwbridge始终存在。

    Docker Engine swarm模式的overylay网络

    你可以在运行着swarm模式的管理节点上创建一个不需要外部键值存储的overylay网络。overlay网络只可用于服务依赖它的swarm节点上。当你创建一个服务使用overlay网络时,管理节点自动扩展overlay网络到运行服务任务的节点上。
    下面的示例显示如何创建一个网络并设置一个服务使用这个网络:

    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

    依赖外部键值存储的overlay网络

    如果你的Docker Engine没有运行在swarm模式中,overlay网络则需要一个有效的键值存储服务。支持键值存储包括Consul, Etcd和ZooKeeper(分布式存储)。在这个Engine版本创建这个网络前,你必须安装和配置一个键值存储服务。要加入到这个网络的docker主机和服务必须能与这个键值存储服务通信。

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

    虚拟化技术
    在这个网络的每一个主机必须运行一个docker engine实例。
    虚拟化技术
    在每一个主机之间需要开放如下端口。

    协议 端口 描述
    udp 4789 Data plane (VXLAN)
    tcp/udp 7946 Control plane

    要创建一个overlay网络,还需要在每个主机的docker daemon配置如下选项:

    选项 描述
    –cluster-store=PROVIDER://URL 键值服务地址
    –cluster-advertise=HOST_IP|HOST_IFACE:PORT 用于集群的IP地址,接口和端口
    –cluster-store-opt=KEY-VALUE OPTIONS TLS证书或调整发现时间等选项

    在集群中一个机器上创建一个overlay网络。

    1. $ docker network create –driver overlay my-multi-host-network

    这使得一个网络可以跨越多个主机。overlay网络为容器提供完全的隔离。
    虚拟化技术
    然后,在每个主机上运行容器时确保指定网络名称。

    1. $ docker run -itd –network=my-multi-host-network busybox

    一旦容器连接到这个网络,每个容器就有权限访问这个网络中的所有容器,不管容器是在哪个主机运行的。
    虚拟化技术

    自定义网络插件

    如果你想,你如果编写自己的网络驱动插件。网络驱动插件使用Docker的插件基础设施。在这个基础设施中,插件是在与Docker守护程序相同的Docker主机上运行的进程。网络插件遵循与其他插件相同的限制和安装规则。所有插件都使用插件API。它们具有包括安装,启动,停止和激活的生命周期。一旦创建并安装了自定义网络驱动程序,就可以像内置网络驱动一样使用它。 例如:

    1. $ docker network create –driver weave mynet

    你可以查看这个自定义网络,添加容器到这个网络等等。

    Docker内置的DNS服务器

    Docker daemon运行一个内置的DNS服务器为连接到自定义网络的容器提供自动服务发现功能。从容器发来的域名解析请求首先由内置的DNS服务器处理。如果内置的DNS服务器无法解析这个请求,那么它就将此请求转发到容器配置的外部DNS服务器。

    Docker用户指南(9) – ZFS存储驱动实践

    ZFS是支持许多高级存储技术(如卷管理,快照,检验和,压缩,去重,复制等)的下一代文件系统。由Microsystems(现在是Oracle Corporation)创建,以CDDL许多可证开源。由于CDDL和GPL的许可证不兼容,ZFS无法作为Linux内核主线的一部分提供。不过,Linux的ZFS(ZoL)提供了在内核树之外的模块并且可以单独安装用户空间工具。
    Linux的ZFS(ZoL)目前已经是稳定成熟了。不过,除非你在Linux上拥有丰富的ZFS使用经验,否则不建议目前在生产环境使用zfs Docker存储驱动。

    镜像分层和ZFS共享

    Docker zfs存储驱动大量使用三个ZFS数据集:

  • 文件系统
  • 快照
  • 克隆
  • ZFS文件系统是精简置备的(thinly provisioned)并通过按需分配操作从一个ZFS池(zpool)分配空间。快照和克隆是节省空间的某个时间点的ZFS文件系统副本。快照是只读的。克隆(clones)是可读写的。克隆只能从快照创建。它们简单的关系如下图.
    虚拟化技术
    上图中的实线部分是创建一个克隆的过程。步骤1创建文件系统的一个快照,步骤2是从快照创建克隆。虚线表示克隆和文件系统之间的关系,通过快照,所有三个ZFS数据集从相同的底层zpool中申请空间。
    在使用zfs存储驱动的Docker主机上,一个镜像的base层是一个ZFS文件系统。每一个子层是基于该层下面层的快照的克隆。一个容器是基于从其创建的镜像顶层的快照的ZFS克隆。所有的ZFS数据集从同一个zpool申请空间。下图显示zpool,三个数据集,一个基于两层镜像的容器的关系。
    虚拟化技术
    下面的过程解释了镜像是如何分层的和如何创建容器。该过程基于上图。
    1.镜像的base数据层作为ZFS文件系统存在于Docker主机上。
    此文件系统消耗用于在/var/lib/docker上创建Docker主机的本地存储区的zpool空间。
    2.其它的镜像数据层是在其下方的镜像层的数据集的克隆。
    在这个图中,”Layer 1″是通过创建base层的一个ZFS快照,然后从这个快照创建一个克隆增加。这个克隆是可写的并消耗从zpool按需分配的空间。快照是只读的,将base层保持为一个不可变的对象。
    3.当一个容器创建后,一个可读写的数据层增加在镜像的上面。在上图中,这个容器的可读写数据层是通过创建镜像顶层(Layer1)的快照,然后从这个快照创建一个克隆创建的。
    所有对容器的更改,将通过按需分配操作从zpool中分配空间。默认下,ZFS以128K大小的数据块分配空间。
    从只读快照创建子层和容器的此过程允许保持镜像为一个不可变对象。

    使用ZFS读写容器

    使用zfs存储驱动进行容器的读取是非常简单的。一个新创建的容器基于一个ZFS克隆。此克隆最初与从其创建的数据集共享其所有数据。这意味着使用zfs存储驱动的读操作是非常快的 – 即使要读取的数据还没有复制到容器中。下面显示这个数据块的分享。
    虚拟化技术
    写入新数据到容器是通过按需分配操作完成。每次需要写数据到容器的新区域,一个新的数据块从zpool分配。这意味着写入新数据到容器的新区域将消耗额外的空间。从底层zpool分配新空间给容器(ZFS克隆)。
    更新容器存在的数据通过分配新数据块给容器克隆和存储更改的数据到这些新数据块完成。原始的数据块没有更改,允许底层镜像数据集保持不变。这与写入正常的ZFS文件系统相同,并且都执行了写时拷贝操作。

    配置Docker使用ZFS存储驱动

    zfs存储驱动只支持/var/lib/docker挂载为一个ZFS文件系统的Docker主机。本节介绍如何在Ubuntu 14.04系统上安装和配置原生Linux ZFS(ZoL)。

    先决条件

    如果你已经在你docker主机上使用docker daemon并且有你想保留的镜像,在执行下面的步骤之前先push它们到docker hub或你的私有docker registry。
    停止docker daemon。然后确保在/dev/xvdb有一个空闲的块设备。块设备的标识符可能与你的不同,请替换成你自己的。

    在Ubuntu 16.04 LTS安装ZFS

    1.先停止docker deamon。
    2.安装zfs软件包。

    1. $ sudo apt-get install -y zfs
    2.  
    3.  Reading package lists… Done
    4.  Building dependency tree
    5.  <output truncated>

    3.验证zfs模块是否已经正确加载。

    1. $ lsmod | grep zfs
    2.  
    3.  zfs                  2813952  3
    4.  zunicode              331776  1 zfs
    5.  zcommon                57344  1 zfs
    6.  znvpair                90112  2 zfs,zcommon
    7.  spl                   102400  3 zfs,zcommon,znvpair
    8.  zavl                   16384  1 zfs

    在Ubuntu 14.04安装ZFS

    1.先停止docker daemon。
    2.安装add-apt-repository命令依赖的software-properties-common软件。

    1. $ sudo apt-get install -y software-properties-common
    2.  
    3.  Reading package lists… Done
    4.  Building dependency tree
    5.  <output truncated>

    3.增加zfs-native软件包ppa源。

    1. $ sudo add-apt-repository ppa:zfs-native/stable
    2.  
    3.   The native ZFS filesystem for Linux. Install the ubuntu-zfs package.
    4.  <output truncated>
    5.  gpg: key F6B0FC61: public key "Launchpad PPA for Native ZFS for Linux" imported
    6.  gpg: Total number processed: 1
    7.  gpg:               imported: 1  (RSA: 1)
    8.  OK

    4.获取最新的软件包列表。

    1. $ sudo apt-get update
    2.  
    3.  Ign http://us-west-2.ec2.archive.ubuntu.com trusty InRelease
    4.  Get:1 http://us-west-2.ec2.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
    5.  <output truncated>
    6.  Fetched 10.3 MB in 4s (2,370 kB/s)
    7.  Reading package lists… Done

    5.安装ubuntu-zfs软件包

    1. $ sudo apt-get install -y ubuntu-zfs
    2.  
    3.  Reading package lists… Done
    4.  Building dependency tree
    5.  <output truncated>

    6.加载zfs模块。

    1. $ sudo modprobe zfs

    7.验证模块是否已正确加载。

    1. $ lsmod | grep zfs
    2.  
    3.  zfs                  2768247  0
    4.  zunicode              331170  1 zfs
    5.  zcommon                55411  1 zfs
    6.  znvpair                89086  2 zfs,zcommon
    7.  spl                    96378  3 zfs,zcommon,znvpair
    8.  zavl                   15236  1 zfs

    配置Docker ZFS

    一旦ZFS安装并加载完成,你可以继续配置docker ZFS。
    1.创建一个新的zpool。

    1. $ sudo zpool create -f zpool-docker /dev/xvdb

    该命令创建名为”zpool-docker”的zpool。名称可以是任意的。
    2.检查zpool是否存在。

    1. $ sudo zfs list
    2.  
    3.  NAME            USED  AVAIL    REFER  MOUNTPOINT
    4.  zpool-docker    55K   3.84G    19K    /zpool-docker

    3.创建并挂载一个新的ZFS文件系统到/var/lib/docker。

    1. $ sudo zfs create -o mountpoint=/var/lib/docker zpool-docker/docker

    4.检查是否已经挂载成功。

    1. $ sudo zfs list -t all
    2.  
    3.  NAME                 USED  AVAIL  REFER  MOUNTPOINT
    4.  zpool-docker         93.5K  3.84G    19K  /zpool-docker
    5.  zpool-docker/docker  19K    3.84G    19K  /var/lib/docker

    现在你已经把一个ZFS文件系统挂载到/var/lib/docker,docker daemon应该可以自动加载zfs存储驱动了。
    5.启动docker daemon。

    1. $ sudo service docker start
    2.  
    3.  docker start/running, process 2315

    6.验证docker daemon是否已经在使用zfs存储驱动了。

    1. $ sudo docker info
    2.  
    3.  Containers: 0
    4.  Images: 0
    5.  Storage Driver: zfs
    6.   Zpool: zpool-docker
    7.   Zpool Health: ONLINE
    8.   Parent Dataset: zpool-docker/docker
    9.   Space Used By Parent: 27648
    10.   Space Available: 4128139776
    11.   Parent Quota: no
    12.   Compression: off
    13.  Execution Driver: native-0.2
    14.  […]

    上面命令的输出显示docker daemon的存储驱动为zfs,父数据集为之前创建的zpool-docker/docker文件系统。

    ZFS与Docker性能

    有几个影响使用zfs存储驱动的docker性能的因素。

  • 内存。内存对ZFS性能有很大影响。这是因为ZFS刚开始是为有大量内存的Sun Solaris服务器设计的。使用ZFS时注意其内存使用情况。
  • ZFS功能。使用ZFS功能如去重,可能会明显增加ZFS内存使用量。出于内存消耗和性能考虑,建议关闭ZFS去重功能。不过,仍然可以使用堆栈中其他层(如SAN或NAS阵列)中的去重功能,因为这些不会影响ZFS内存使用和性能。
  • ZFS缓存。ZFS在一个称为自适应替换缓存(ARC)的内存结构中缓存硬盘数据块。ZFS的单拷贝ARC功能允许数据块的单个缓存副本由文件系统的多个克隆共享。这意味着多个正在运行的容器可以共享缓存数据块的单个副本。意味着ZFS是PaaS和其他类似用例的好选择。
  • 碎片。 碎片是像ZFS这样的写时拷贝文件系统的自然副产品。 然而,ZFS写入128K数据块中,并将slab(多个128K块)分配给CoW操作,以尝试减少碎片。 ZFS intent日志(ZIL)和合并写入(延迟写入)也有助于减少碎片。
  • 使用原生的Linux ZFS驱动。虽然Docker zfs存储驱动支持ZFS FUSE,当在需要高性能的场景下时不推荐使用。Linux的原生ZFS驱动的性能优于FUSE。
  • Docker用户指南(8) – OverlayFS存储驱动实践

    OverlayFS是一个与AUFS类似的现代联合(union)文件系统。与AUFS比较,OverlayFS存在如下优势:

  • 更简单的设计
  • 从3.18版本开始已经进入Linux内核主线
  • 可能更快
  • 因此,OverlayFS在Docker社区迅速普及,许多人认为这可以代替AUFS。虽然OverlayFS发展前景很好,但仍然不够成熟。因此在生产环境上部署前,请慎重考虑。
    Docker的overlay存储驱动利用OverlayFS几个功能来构建和管理镜像和容器在硬盘的结构。
    从1.12版本起,Docker也提供了overlay2存储驱动,它比overlay在inode的使用方面更高效。不过overlay2只能用在Linux内核 4.0和更高的版本。
    本文使用OverlayFS表示文件系统,overlay/overlay2表示存储驱动。

    镜像分层及OverlayFS(overlay)共享

    OverLayFS在一台Linux主机维护两个目录,一个在另一个上面,并提供一个统一的视图。这些目录通常称为数据层,用来叠加它们的技术叫做联合挂载(union mount)。OverlayFS底层称为“lowerdir”,顶层称为“upperdir”。通过它自己的”merged”目录提供一个统一视图。
    下图显示一个Docker镜像和Docker容器是如何分层的。镜像数据层是“lowerdir”,容器数据层是”uppperdir”。通过目录为”merged”提供一个统一视图,该目录是容器的挂载点。下图显示Docker结构是如何映射到OverlayFS结构的。
    虚拟化技术
    注意看容器数据层和镜像数据层能包含同样的文件。当出现此情况时,容器数据层”upperdir”的这个文件是显性的并隐藏了在镜像数据层“lowerdir”的相同文件的存在。容器挂载“merged”呈现一个统一视图。
    overlay驱动只在两个层工作。这意味着多层的镜像无法实现为多个OverlayFS层。而是每个镜像数据层在自己的/var/lib/docker/overlay目录下实现多层关系。然后使用硬链接引用与更低层共享的数据。从Docker 1.10起,镜像数据层ID不再与/var/lib/docker下的目录名称相关。
    要创建一个容器,overlay驱动将镜像顶层与一个新目录结合。镜像顶层是overlay的只读“lowerdir”。容器新的目录是可写的“upperdir”。

    镜像和容器硬盘上的结构(overlay)示例

    下面的docker pull命令显示Docker主机下载一个由5个层组成的Docker镜像。

    1. $ sudo docker pull ubuntu
    2.  
    3. Using default tag: latest
    4. latest: Pulling from library/ubuntu
    5.  
    6. 5ba4f30e5bea: Pull complete
    7. 9d7d19c9dc56: Pull complete
    8. ac6ad7efd0f9: Pull complete
    9. e7491a747824: Pull complete
    10. a3ed95caeb02: Pull complete
    11. Digest: sha256:46fb5d001b88ad904c5c732b086b596b92cfb4a4840a3abd0e35dbb6870585e4
    12. Status: Downloaded newer image for ubuntu:latest

    每个镜像数据层在/var/lib/docker/overlay/下都有自己的对应目录。这些目录存储着每个镜像数据层的数据。
    下面的命令输出显示5个存储着刚才下载的每个镜像数据层的数据的目录。不过就如你看到的镜像数据层ID没有匹配/var/lib/docker/overlay下目录的名称。

    1. $ ls -l /var/lib/docker/overlay/
    2.  
    3. total 20
    4. drwx—— 3 root root 4096 Jun 20 16:11 38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8
    5. drwx—— 3 root root 4096 Jun 20 16:11 55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
    6. drwx—— 3 root root 4096 Jun 20 16:11 824c8a961a4f5e8fe4f4243dab57c5be798e7fd195f6d88ab06aea92ba931654
    7. drwx—— 3 root root 4096 Jun 20 16:11 ad0fe55125ebf599da124da175174a4b8c1878afe6907bf7c78570341f308461
    8. drwx—— 3 root root 4096 Jun 20 16:11 edab9b5e5bf73f2997524eebeac1de4cf9c8b904fa8ad3ec43b3504196aa3801

    镜像数据层目录包含该层唯一的文件和与更低层共享目录的硬链接。这样可以提升空间有效利用率。

    1. $ ls -i /var/lib/docker/overlay/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
    2.  
    3. 19793696 /var/lib/docker/overlay/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
    4.  
    5. $ ls -i /var/lib/docker/overlay/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
    6.  
    7. 19793696 /var/lib/docker/overlay/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls

    容器也在位于/var/lib/docker/overlay的Docker主机文件系统硬盘上。如果你使用ls -l命令查看与运行容器相关的目录,你会看到如下文件和目录。

    1. $ ls -l /var/lib/docker/overlay/<directory-of-running-container>
    2.  
    3. total 16
    4. -rw-r–r– 1 root root   64 Jun 20 16:39 lower-id
    5. drwxr-xr-x 1 root root 4096 Jun 20 16:39 merged
    6. drwxr-xr-x 4 root root 4096 Jun 20 16:39 upper
    7. drwx—— 3 root root 4096 Jun 20 16:39 work

    这四个文件系统对象是OverlayFS的部件。lower-id文件包含容器基于的镜像的顶层ID。在OverlayFS上称为“loverdir”。

    1. $ cat /var/lib/docker/overlay/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
    2.  
    3. 55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358

    upper目录是容器的可写数据层。所有对容器的更改都是写到这个目录。
    merged目录是容器的挂载点。这是镜像”loverdir”和容器“upperdir”的统一视图。任何对这个容器的修改将马上反映到这个目录。
    work目录是OverlayFS执行操作时所需的目录。如执行copy_up操作。
    你可以从mount命令的输出来验证这些结构。

    1. $ mount | grep overlay
    2.  
    3. overlay on /var/lib/docker/overlay/ec444863a55a…/merged
    4. type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay/55f1e14c361b…/root,
    5. upperdir=/var/lib/docker/overlay/ec444863a55a…/upper,
    6. workdir=/var/lib/docker/overlay/ec444863a55a…/work)

    镜像分层和OverlayFS共享(overlay2)

    overlay驱动仅支持单个lower OverlayFS层并且需要硬链接来实现多层的镜像,overlay2驱动原生支持多个lower OverlayFS层(最大到128层)。
    因此overlay2驱动为与层相关的docker命令(如docker build和docker commit)提供了更好的性能,并且比overlay驱动消耗更少的inodes。

    示例:镜像和容器在硬盘上的结构(overlay2)

    使用docker pull ubuntu命令下载一个5层的镜像后,你可以在/var/lib/docker/overlay2目录下看到6个目录。

    1. $ ls -l /var/lib/docker/overlay2
    2.  
    3. total 24
    4. drwx—— 5 root root 4096 Jun 20 07:36 223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
    5. drwx—— 3 root root 4096 Jun 20 07:36 3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b
    6. drwx—— 5 root root 4096 Jun 20 07:36 4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1
    7. drwx—— 5 root root 4096 Jun 20 07:36 e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5
    8. drwx—— 5 root root 4096 Jun 20 07:36 eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed
    9. drwx—— 2 root root 4096 Jun 20 07:36 l

    l目录包含缩短的层标识符的软链接。这些缩短的标识符用于避免挂载参数的页大小限制。

    1. $ ls -l /var/lib/docker/overlay2/l
    2.  
    3. total 20
    4. lrwxrwxrwx 1 root root 72 Jun 20 07:36 6Y5IM2XC7TSNIJZZFLJCS6I4I4 -> ../3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
    5. lrwxrwxrwx 1 root root 72 Jun 20 07:36 B3WWEFKBG3PLLV737KZFIASSW7 -> ../4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1/diff
    6. lrwxrwxrwx 1 root root 72 Jun 20 07:36 JEYMODZYFCZFYSDABYXD5MF6YO -> ../eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed/diff
    7. lrwxrwxrwx 1 root root 72 Jun 20 07:36 NFYKDW6APBCCUCTOUSYDH4DXAT -> ../223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff
    8. lrwxrwxrwx 1 root root 72 Jun 20 07:36 UL2MW33MSE3Q5VYIKBRN4ZAGQP -> ../e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5/diff

    最低层包含一个link文件,该文件包含缩短的标识符名称,diff目录包含其数据。

    1. $ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/
    2.  
    3. diff  link
    4.  
    5. $ cat /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/link
    6.  
    7. 6Y5IM2XC7TSNIJZZFLJCS6I4I4
    8.  
    9. $ ls  /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
    10.  
    11. bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

    第二层包含一个lower文件,该文件包含该层下一层的位置,diff目录包含该层数据。同时也包含了merged和work目录。

    1. $ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
    2.  
    3. diff  link  lower  merged  work
    4.  
    5. $ cat /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/lower
    6.  
    7. l/6Y5IM2XC7TSNIJZZFLJCS6I4I4
    8.  
    9. $ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff/
    10.  
    11. etc  sbin  usr  var

    运行中的容器的目录也有类似的文件和目录。注意lower列表以冒号:分隔,并且从高层到低层排序。

    1. $ ls -l /var/lib/docker/overlay/<directory-of-running-container>
    2.  
    3. $ cat /var/lib/docker/overlay/<directory-of-running-container>/lower
    4.  
    5. l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4

    mount的命令输出如下:

    1. $ mount | grep overlay
    2.  
    3. overlay on /var/lib/docker/overlay2/9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/merged
    4. type overlay (rw,relatime,
    5. lowerdir=l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4,
    6. upperdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/diff,
    7. workdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/work)

    使用overlay进行容器的读和写

    下面是容器使用ovelay对文件进行的读取操作的三个场景。

  • 文件不在容器数据层。如果容器要打开读取一个文件且这个文件不在容器的upperdir目录,那它就从镜像的lowerdir读取。这个操作对性能影响很小。
  • 文件只存在于容器数据层。如果一个容器打开读取一个文件且这个在容器的upperdir不在镜像的lowerdir,那么直接从容器读取文件。
  • 文件既存在于容器数据层也存在于镜像数据层。如果一个容器打开一个既存在于镜像数据层也存在于容器数据层的文件,那么就读取在容器数据层的文件。这是因为在容器数据层upperdir的文件隐藏了在镜像数据层lowerdir的同一个文件。
  • 下面是容器中更改文件的几个场景。

  • 首次写入一个文件。首次在容器中写入一个存在的文件且文件不在容器upperdir。overlay/overlay2驱动执行一个copy_up操作来从镜像lowerdir复制文件到容器upperdir。然后容器把更改的数据写入到新复制过来的文件中。
    不过OverlayFS工作在文件级别而不是块级别上。意味着所有的OverlayFS copy-up操作复制的是整个文件,即使是文件很大但只修改一小部分。这会对容器的写性能造成显着的影响。不过,下面两点值得注意:
    1.copy_up操作只在对任意文件的首次写入时发生。随便对相同文件的写操作不再涉及到copy_up操作。
    2.OverlayFS只工作在两层。这意味着它的性能应该比AUFS对很多层的镜像搜索一个文件时的性能要好。
  • 删除文件和目录。当删除容器中的一个文件时,相当于在容器的upperdir创建了一个对应的空白(whiteout)文件。在镜像数据层lowerdir的文件版本不会被删除。只不过是容器中的空白(whiteout)文件隐藏了它。删除容器中的目录会在“upperdir”中创建不透明(opaque)的目录。这与一个空白(whiteout)文件的作用一样。这个不透明(opaque)目录有效地隐藏了镜像lowerdir同一个目录的存在。
  • 重命名目录。仅当源和目标路径都位于顶层(upperdir)时,才允许为目录调用rename(2)。否则返回EXDEV (“cross-device link not permitted”).
  • 配置Docker使用overlay/overlay2存储驱动

    要配置Docker使用ovelay存储驱动,你的Docker主机必须运行在加载有overlay内核模式的3.18版本的Linux内核或更高的版本上。对于overlay2驱动,内核版本必须为4.0或更高版本。OverlayFS能用在大多数支持的Linux文件系统上。不过在生产环境中推荐使用ext4。
    下面是配置Docker主机使用OverlayFS的步骤。假设你已经停止docker daemon。
    1.如果docker在运行,先停止它。
    2.验证内核版本和overlay内核模式是否加载

    1. $ uname -r
    2.  
    3.  3.19.0-21-generic
    4.  
    5.  $ lsmod | grep overlay
    6.  
    7.  overlay

    3.带overlay/overlay2存储驱动启动docker。

    1. $ dockerd –storage-driver=overlay &
    2.  
    3.  [1] 29403
    4.  root@ip-10-0-0-174:/home/ubuntu# INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)
    5.  INFO[0000] Option DefaultDriver: bridge
    6.  INFO[0000] Option DefaultNetwork: bridge
    7.  <output truncated>

    另外,你可以通过编辑docker的配置文件增加–storage-driver=overlay到DOCKER_OPTS行来强制docker自动启动时启用overlay/overlay2驱动。
    4.验证docker是否已经是使用overlay/overlay2存储驱动。

    1. $ docker info
    2.  
    3.  Containers: 0
    4.  Images: 0
    5.  Storage Driver: overlay
    6.   Backing Filesystem: extfs
    7.  <output truncated>

    OverlayFS与Docker性能

    一般来说,overlay/overlay2驱动性能应该很好。大多时候会比aufs和devicemapper要好。在一些场景下可能比btrfs还高。我们在使用overlay/overlay2存储驱动时需要注意几点与性能相关的事项。

  • 页缓存。OverlayFS支持页缓存共享。意味着多个容器访问同一个文件可以共享单个页缓存条目。这使得overlay/overlay2驱动在内存利用方面高效,且是PaaS和其它类似场景的一个好选择。
  • copy_up。AUFS和OverlayFS在首次对容器的一个文件写入时都会执行copy-up操作。这会增加写操作的延迟 – 特别是如果要拷贝的文件非常大时。不过,一旦文件已经复制上去,随便的所有写操作都不需要copy-up操作了。
    OverlayFS的copy_up操作应该比AUFS同样的操作会快。因为AUFS比OverlayFS支持更多的层,在AUFS需要对非常多层的镜像搜索一个文件时就会出现这种情况。
  • Inode限制。使用overlay存储驱动可能会消耗更多的inode。特别是Docker主机上的镜像和容器数量很多时。一个Docker主机上有大量的镜像和大量的启动和停止容器时会更多消耗完inodes。overlay2没有这种问题。
  • 你只能在文件系统创建时指定inodes的数量。因此你可能希望把/var/lib/docker放置在有一个自己的文件系统的不同设备,或者在文件系统创建时手动指定inodes的数量。
    下面的两点性能优化同样适合于OverlayFS。

  • 固态设备(SSD)。 为了获得最佳性能,使用快速存储介质(如固态设备(SSD))总是个好主意。
  • 使用数据卷。 数据卷提供最佳和最可预测的性能。这是因为他们绕过存储驱动,并且不会引入任何通过精简置备和写时拷贝的潜在开销。