Docker网络管理

本节主要是介绍docker默认的网络行为,包含创建的默认网络类型以及如何创建用户自定义网络,也会介绍如何在单一主机或者跨主机集群上创建网络的资源需求。

一. 网络驱动(Network drivers)

docker网络子系统使用驱动程序来进行插件设计,默认情况下存在几个驱动程序,它们提供核心网络功能:

  • bridge:默认的网络驱动程序。如果未指定驱动程序,创建的就是这种网络类型,当你的应用运行在独立的容器中,并需要网络通信时,通常选择这种网络类型。
  • host:对于独立的容器,直接使用主机的网络,容器和主机之间没有网络隔离;
  • overlay:覆盖网络将多个Docker守护程序连接在一起,并使群集服务能够相互通信;
  • macvlan:Macvlan网络允许您为容器分配MAC地址,使其在网络上显示为物理设备。Docker守护程序通过其MAC地址将流量路由到容器。
  • none:对于这样的容器,禁用所有联网。通常与自定义网络驱动程序一起使用。

网络选择建议

  • 当您需要多个容器在同一Docker主机上进行通信时,最好使用用户定义的网桥网络。
  • 当网络堆栈不应与Docker主机隔离时,但您希望容器的其他方面隔离时,主机网络是最佳选择。
  • 当您需要在不同Docker主机上运行的容器进行通信时,或者当多个应用程序使用集群服务一起工作时,覆盖网络是最好的。
  • 从VM设置迁移或需要容器看起来像网络上的物理主机时,Macvlan网络是最好的,每个主机都有唯一的MAC地址。
  • 第三方网络插件使您可以将Docker与专用网络堆栈集成。

二. 网络类型原理

当你安装了docker,它自动创建了3个网络,可以使用docker network命令来查看

[root@nextcloud ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
6ae630a60420        bridge              bridge              local
90ae88ff8547        host                host                local
dc10f0907833        none                null

这三个网络被docker内建。当你运行一个容器的时候,可以使用–network参数来指定你的容器连接到哪一个网络。

1. bridge网络

默认连接到docker0这个网桥上。

[root@nextcloud ~]# ip addr show docker0 
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:34:1d:06:91 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

[root@nextcloud ~]# brctl show docker0
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242341d0691	no

注:brctl 命令在centos中可以使用yum install bridge-utils来安装启动并运行一个容器

[root@nextcloud ~]# docker run -itd --name test01 nginx:latest 
d3487007f1965a4e93efb851115a2c975db890b6fd3623c08af33dab193db834

root@d3487007f196:/# apt-get update
root@d3487007f196:/# apt-get install net-tools iproute2 -y

root@d3487007f196:/# ip addr show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

可以看到test01容器已经获取了一个地址172.17.0.3,和主机的docker0接口地址在同一网络,并将主机的docker0接口地址设置为了网关。

root@d3487007f196:/# ip route show 
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3

在物理主机上,查看网桥docker0,可以看到已经多了一个接口

[root@nextcloud ~]# brctl show docker0
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242341d0691	no		veth3d69c3f

Docker 容器默认使用 bridge 模式的网络。其特点如下:

  • 使用一个 linux bridge,默认为 docker0
  • 使用 veth 对,一头在容器的网络 namespace 中,一头在 docker0 上
  • 该模式下Docker Container不具有一个公有IP,因为宿主机的IP地址与veth pair的 IP地址不在同一个网段内
  • Docker采用 NAT 方式,将容器内部的服务监听的端口与宿主机的某一个端口port 进行“绑定”,使得宿主机以外的世界可以主动将网络报文发送至容器内部
  • 外界访问容器内的服务时,需要访问宿主机的 IP 以及宿主机的端口 port
  • NAT 模式由于是在三层网络上的实现手段,故肯定会影响网络的传输效率。
  • 容器拥有独立、隔离的网络栈;让容器和宿主机以外的世界通过NAT建立通信效果是这样的: 示意图如下:

在物理主机上查看iptables的nat表,可以看到在POSTROUTING链中做了地址伪装:MASQUERADE动作,这样容器就可以通过源地址转换NAT访问外部网络了。通过iptables -t nat -vnL命令查看到:

[root@nextcloud ~]# iptables -t nat -vnL
....略...

Chain POSTROUTING (policy ACCEPT 354 packets, 25260 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   35  2141 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
  363 25887 POSTROUTING_direct  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
  363 25887 POSTROUTING_ZONES_SOURCE  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
  363 25887 POSTROUTING_ZONES  all  --  *      *       0.0.0.0/0            0.0.0.0/0  

...略...

可以使用docker network inspect bridge命令来查看bridge网络情况:

[root@nextcloud ~]# docker network inspect bridge 
[
    {
        "Name": "bridge",
        "Id": "6ae630a60420afd6675443258c4391750db463c8ab90145539ad779cbdfffd0f",
        "Created": "2020-12-14T09:26:05.149953821+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "d3487007f1965a4e93efb851115a2c975db890b6fd3623c08af33dab193db834": {
                "Name": "test01",
                "EndpointID": "1205b528d1722adb7695b7ca0abce8d2cbccc8432751916b714bd4bf093f006c",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

2. none网络模式:

网络模式为 none,即不为Docker容器构造任何网络环境,不会为容器创建网络接口,一旦Docker容器采用了none网络模式,那么容器内部就只能使用loopback网络设备,不会再有其他的网络资源。Docker Container的none网络模式意味着不给该容器创建任何网络环境,容器只能使用127.0.0.1的本机网络。

启动一个容器,设为none网络

[root@nextcloud ~]# docker run -itd --network none --name test02 centos:latest 
3549f096f41195c043c7f6d34fb28bbfc92f6272123e59d4c1cb032f0f511612

进入容器,查看网络情况:

[root@nextcloud ~]# docker exec -it test02 /bin/bash

[root@3549f096f411 /]# ip addr show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever

[root@3549f096f411 /]# ip route show

3. host网络模式

Host模式并没有为容器创建一个隔离的网络环境。而之所以称之为host模式,是因为该模式下的Docker 容器会和host宿主机共享同一个网络namespace,故Docker Container可以和宿主机一样,使用宿主机的eth0,实现和外界的通信。换言之,Docker Container的IP地址即为宿主机 eth0的IP地址。

其特点包括:

  • 这种模式下的容器没有隔离的 network namespace
  • 容器的 IP 地址同 Docker host 的 IP 地址
  • 需要注意容器中服务的端口号不能与 Docker host 上已经使用的端口号相冲突
  • host 模式能够和其它模式共存

示意图:

例如,我们在192.168.1.102/24 的机器上用 host 模式启动一个含有 web 应用的 Docker 容器,监听 tcp 80 端口。当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用192.168.1.102:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。启动容器前,查看物理主机的httpd进程

[root@nextcloud ~]# pgrep httpd
[root@nextcloud ~]#

启动一个容器:

[root@nextcloud ~]# docker run -itd --privileged --name web01 --network host centos:latest init
201429428e727520310cd250303aaa642675cf6e62ea9be430acab9b818c1177

进入容器,安装httpd服务,并启动

[root@nextcloud ~]# docker exec -it web01 /bin/bash
[root@nextcloud /]# yum install httpd -y
[root@nextcloud /]# systemctl start httpd
[root@nextcloud /]# echo 'test docker host network' > /var/www/html/index.html

退出容器,再次查看httpd进程

[root@nextcloud /]# exit
exit
[root@nextcloud ~]# pgrep httpd
8712
8713
8714
8715
8716

访问主机的80端口,可以访问到容器test7中的网站服务:

注意防火墙:

[root@nextcloud ~]# firewall-cmd --add-port=80/tcp
success

4. container 模式

这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

Container 网络模式是 Docker 中一种较为特别的网络的模式。这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。

注意:因为此时两个容器要共享一个network namespace,因此需要注意端口冲突情况,否则第二个容器将无法被启动。

示意图:

运行一个容器:查看容器的IP

[root@nextcloud ~]# docker run -itd --name test03 centos:latest 
ba2c8a9ad8b5123ae4558e7f3f340e046ed65d75aa24d129957661b01db8be91

[root@nextcloud ~]# docker exec -it test03 /bin/bash
[root@ba2c8a9ad8b5 /]# ip add show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

启动另外一个容器,使用test03容器的网络

[root@nextcloud ~]# docker run -itd --name test04 \
> --network container:test03 \
> centos:latest
72a3f6ed22bb7a847ddb23850756dd8e9f2eef64963137c21ceb12824ec7c85d

进入容器test04,查看网络情况,可以看到两个容器地址信息相同,是共享的

[root@nextcloud ~]# docker exec -it test04 /bin/bash
[root@ba2c8a9ad8b5 /]# ip addr show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@ba2c8a9ad8b5 /]#

三. 外部访问容器

通过上面的docker网络学习,已经可以实现容器和外部网络通信了,但是如何让外部网络来访问容器呢?

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。

  • 当使用–P(大写)标记时,Docker 会随机映射一个随机的端口到内部容器开放的网络端口。

注:-P使用时需要指定–expose选项或dockerfile中用expose指令容器要暴露的端口,指定需要对外提供服务的端口。

下载nginx镜像并启动一个容器:

[root@nextcloud ~]# docker run -itd -P --name web02  nginx:latest 
7989036e8288c5144f29a7c1b26ea7688f81a919d333760052065ea44eb84b20

[root@nextcloud ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
7989036e8288        nginx:latest        "/docker-entrypoint.…"   11 seconds ago      Up 10 seconds       0.0.0.0:32768->80/tcp   web02

[root@nextcloud ~]# docker port web02 
80/tcp -> 0.0.0.0:32768

可以看到容器的80端口被随机映射到主机的32768端口;

访问主机IP地址的32768端口,就可以访问到容器的http服务

  • -p(小写)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有:
    • ip:hostPort:containerPort
    • ip::containerPort
    • hostPort:containerPort

注意:容器有自己的内部网络和 ip 地址(使用docker inspect 可以获取所有的变量。);-p 标记可以多次使用来绑定多个端口

[root@nextcloud ~]# docker run -itd -p 8000:80 --name web03 nginx:latest 
6dc831d65d5847af16ab9acc98504c1132c02d5bd31812639b8c943d2dd450dd
[root@nextcloud ~]# docker port web03
80/tcp -> 0.0.0.0:8000
[root@nextcloud ~]# 

##可以看到主机的8000端口已经和容器web03容器的80端口做了映射

docker端口映射实质上是在iptables 的nat表中添加了DNAT规则

[root@nextcloud ~]# iptables -t nat -vnL
....略....

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:32768 to:172.17.0.3:80
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8000 to:172.17.0.4:80

....略....

并在防火墙中自动放行了这些流量

[root@nextcloud ~]# iptables -nvL
....略....

Chain DOCKER (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.3           tcp dpt:80
    0     0 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.4           tcp dpt:80

....略....

四. 用户自定义网络(User-defined networks)

建议使用用户定义的桥接网络来控制容器之间彼此通信,用户定义的网桥网络优于默认bridge 网络:

  • 用户定义的网桥可在容器之间提供自动DNS解析,缺省桥接网络上的容器只能通过IP地址相互访问,除非您使用传统的–link选项;在用户定义的网桥网络上,容器可以通过名称或别名相互解析。
  • 用户定义的网桥可提供更好的隔离。所有未指定–network的容器都将连接到默认网桥网络,这可能是一种风险;
  • 容器可以随时随地从用户定义的网络连接和分离。在容器的生存期内,您可以即时将其与用户定义的网络连接或断开连接。要从默认网桥网络中删除容器,您需要停止容器并使用其他网络选项重新创建它;
  • 每个用户定义的网络都会创建一个可配置的网桥。
  • 默认网桥网络上的链接容器共享环境变量。

docker网络管理命令如下:

  • docker network create
  • docker network connect
  • docker network ls
  • docker network rm
  • docker network disconnect
  • docker network inspect
  • 默认创建的是bridge网络:
[root@docker01 ~]# docker network create simple-network
b3e3c58a844e486644e0bd44a67fc983532a36e854208e8256cbb0e164f50480
[root@docker01 ~]# docker network inspect simple-network 
[
    {
        "Name": "simple-network",
        "Id": "b3e3c58a844e486644e0bd44a67fc983532a36e854208e8256cbb0e164f50480",
        "Created": "2018-01-04T16:53:21.071813651+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
  • 当你创建了一个网络,默认会创建一个不重叠的子网,使用–subnet选项可以直接指定子网络,在bridge网络中只可以指定一个子网络,而在overlay网络中支持多个子网络。
$ docker network create --driver=bridge --subnet=192.168.0.0/16 br0
  • 除了--subnet,还可以指定:--gateway,--ip-range,--aux-address等选项。
$ docker network create \
  --driver=bridge \
  --subnet=172.28.0.0/16 \
  --ip-range=172.28.5.0/24 \
  --gateway=172.28.5.254 \
  br0
  • 创建自定义网络时,可以向驱动程序传递附加选项,bridge驱动接受以下选项:

    选项 等效于 描述
    com.docker.network.bridge.name 创建Linux网桥时要使用的网桥名称
    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.container_interface_prefix 为容器接口设置自定义前缀
  • 可以将以下参数传递给docker network create任何网络驱动程序:

    参数 等效于 描述
    –gateway 主子网的IPv4或IPv6网关
    –ip-range –fixed-cidr 分配的IP范围
    –internal 限制外部访问网络
    –ipv6 –ipv6 启用IPv6网络
    –subnet –bip 网络子网
  • 例如,使用-o或者–opt指定发布端口时绑定的IP地址:

[root@nextcloud ~]# docker network create -o \
> "com.docker.network.bridge.host_binding_ipv4"="192.168.154.136" \
> my-network
4c76caa683adb07847193f7112146e61cb709fae242de842186cd898726eb31f

[root@nextcloud ~]# docker network inspect my-network
[
    {
        "Name": "my-network",
        "Id": "4c76caa683adb07847193f7112146e61cb709fae242de842186cd898726eb31f",
        "Created": "2020-12-20T08:12:43.813056658+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {  ##这里是选项配置的结果
            "com.docker.network.bridge.host_binding_ipv4": "192.168.154.136"
        },
        "Labels": {}
    }
]
  • 启动一个容器并发布端口:可以看到绑定的地址
[root@nextcloud ~]# docker run -itd -P --network my-network --name web008 nginx:latest 
1ab2ed1cdf2eb7da4f42c84461b4eb64c12fa79eacda0e920f294312092eaa29

[root@nextcloud ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
1ab2ed1cdf2e        nginx:latest        "/docker-entrypoint.…"   3 seconds ago       Up 2 seconds        192.168.154.136:32768->80/tcp   web008
  • 可以连接已存在的容器到一个或者多个网络中。一个容器可以连接到多个不同网络驱动的网络中。
  • 当连接一旦建立,容器便可以和其他的容器通讯,通过IP或者容器名称。
  • 对于支持多主机连接的overlay网络或者自定义插件,连接到同一个多主机网络的不同主机上的容器,也可以用这种方式通信。

基本容器网络示例:

  1. 首先,创建和运行两个容器:container1和container2;
[root@nextcloud ~]# docker run -itd --name continer1 centos:latest 
e0e9b4dc9423165ee41a659f6af677919d4782cd41f268b98b6f46c8bb0bcdb5

[root@nextcloud ~]# docker run -itd --name continer2 centos:latest 
72c32ac162594c6c2ae1dbfb675d5c3fcc1070bd91b61d766c225a45c0b52919
  1. 创建一个用户自定义网络bridge网络isolated_nw;
[root@nextcloud ~]# docker network create -d bridge --subnet 172.25.0.0/16 \
> --gateway 172.25.0.1 isolated_nw
5404c88e0ccf2652af06ebe5458dfb6f53fdde33d09d0b077b4ae04797538237
  1. 连接continer2容器到这个新创建的网络,并inspect这个网络,检查网络的连接;
[root@nextcloud ~]# docker network connect isolated_nw continer2
[root@nextcloud ~]# docker network  inspect isolated_nw 
[
    {
        "Name": "isolated_nw",
        "Id": "5404c88e0ccf2652af06ebe5458dfb6f53fdde33d09d0b077b4ae04797538237",
        "Created": "2020-12-20T08:24:38.291128747+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.25.0.0/16",
                    "Gateway": "172.25.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "72c32ac162594c6c2ae1dbfb675d5c3fcc1070bd91b61d766c225a45c0b52919": {
                "Name": "continer2",
                "EndpointID": "e26a7fde962cd4b43c06c64ec26aa21769a588b7d397bb26fab31bf5bdf206d8",
                "MacAddress": "02:42:ac:19:00:02",
                "IPv4Address": "172.25.0.2/16", ##获取到自定义网络的IP地址
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
  1. 创建容器continer3,并分配一个固定IP172.25.3.3;
[root@nextcloud ~]# docker run -itd --name continer3 --network isolated_nw --ip=172.25.3.3 centos:latest 
a2bff343556b83f75ccff4f64a79452e035567fd553356f0cc8e1c9906492bb3
  1. 检查continer3的网络资源;
[root@nextcloud ~]# docker inspect continer3
[
    {
        ....略.....
            "Networks": {
                "isolated_nw": {
                    "IPAMConfig": {
                        "IPv4Address": "172.25.3.3"
                    },
                    "Links": null,
                    "Aliases": [
                        "a2bff343556b"
                    ],
                    "NetworkID": "5404c88e0ccf2652af06ebe5458dfb6f53fdde33d09d0b077b4ae04797538237",
                    "EndpointID": "fe7d8f13520a7edec541a3e3bb79562fd1c02514264c56984e1191757b3b079a",
                    "Gateway": "172.25.0.1",
                    "IPAddress": "172.25.3.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:19:03:03",
                    "DriverOpts": null
                }
            }
        }
    }
]
  1. 检查continer2的网络:
[root@nextcloud ~]# docker inspect continer2
[
    {
...........略..............
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "0363fa1fec426ee394fbb16c29c6e7373b16a40e6b2eaeb861ae241f049faf78",
                    "EndpointID": "90a323b89f46db594621a61a5ed91322f3b8d7bf6fe0fc1f401314ff873605e3",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:03",
                    "DriverOpts": null
                },
                "isolated_nw": {
                    "IPAMConfig": {},
                    "Links": null,
                    "Aliases": [
                        "72c32ac16259"
                    ],
                    "NetworkID": "5404c88e0ccf2652af06ebe5458dfb6f53fdde33d09d0b077b4ae04797538237",
                    "EndpointID": "e26a7fde962cd4b43c06c64ec26aa21769a588b7d397bb26fab31bf5bdf206d8",
                    "Gateway": "172.25.0.1",
                    "IPAddress": "172.25.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:19:00:02",
                    "DriverOpts": {}
                }
            }
        }
    }
]
  1. 现在continer2属于两个网络:
  2. 进入容器continer2,并尝试和continer3及continer1通信;
[root@nextcloud ~]# docker exec -it continer2 /bin/bash
[root@72c32ac16259 /]# ip addr show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
12: eth1@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:19:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.25.0.2/16 brd 172.25.255.255 scope global eth1
       valid_lft forever preferred_lft forever

[root@72c32ac16259 /]# ip route show 
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3 
172.25.0.0/16 dev eth1 proto kernel scope link src 172.25.0.2
  • 有两个IP,网关是连接到的第一个网络的网关。docker内嵌了DNS服务,意思是链接到同一个网络的容器,可以使用容器名进行通信。
[root@72c32ac16259 /]# ping -c 3 continer3
PING continer3 (172.25.3.3) 56(84) bytes of data.
64 bytes from continer3.isolated_nw (172.25.3.3): icmp_seq=1 ttl=64 time=0.063 ms
64 bytes from continer3.isolated_nw (172.25.3.3): icmp_seq=2 ttl=64 time=0.195 ms
64 bytes from continer3.isolated_nw (172.25.3.3): icmp_seq=3 ttl=64 time=0.094 ms

--- continer3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.063/0.117/0.195/0.057 ms
  • 默认的bridge网络是不可以使用名称通信的,IP可以
[root@72c32ac16259 /]# ping -c 3 continer1
ping: continer1: Name or service not known

[root@72c32ac16259 /]# ping -c 3 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.056 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.068 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.173 ms

--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.056/0.099/0.173/0.052 ms
  1. 如果想让默认网络中,也可以使用容器名进行通信呢?
    • 使用link的特性(传统方式),这是唯一推荐使用link的场景。应该使用自定义的网络来替代它的。
    • 在默认网络中使用link增加了一下特性:
      • 解析容器名到IP地址
      • 定义网络别名:–link=CONTAINER-NAME:ALIAS
      • 增强网络连接的安全性
      • 环境变量注入
[root@nextcloud ~]# docker run -itd --name container4 --link continer1:c1 centos:latest /bin/bash
5326bd282fa7800dfbfeb143c340a6949de7415b0440b1bb18f7ba4fa800c0c8

[root@nextcloud ~]# docker exec -it container4 /bin/bash
[root@5326bd282fa7 /]# ping -c 2  continer1
PING c1 (172.17.0.2) 56(84) bytes of data.
64 bytes from c1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.063 ms
64 bytes from c1 (172.17.0.2): icmp_seq=2 ttl=64 time=0.149 ms

--- c1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1ms
rtt min/avg/max/mdev = 0.063/0.106/0.149/0.043 ms

[root@5326bd282fa7 /]# ping -c 2 c1
PING c1 (172.17.0.2) 56(84) bytes of data.
64 bytes from c1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from c1 (172.17.0.2): icmp_seq=2 ttl=64 time=0.154 ms

--- c1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.071/0.112/0.154/0.042 ms
  1. 取消网络连接
[root@nextcloud ~]# docker network disconnect isolated_nw continer2
[root@nextcloud ~]# docker network disconnect isolated_nw continer3
  1. 删除网络
[root@nextcloud ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
0363fa1fec42        bridge              bridge              local
90ae88ff8547        host                host                local
5404c88e0ccf        isolated_nw         bridge              local
4c76caa683ad        my-network          bridge              local
dc10f0907833        none                null                local

[root@nextcloud ~]# docker network rm isolated_nw 
isolated_nw

[root@nextcloud ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
0363fa1fec42        bridge              bridge              local
90ae88ff8547        host                host                local
4c76caa683ad        my-network          bridge              local
dc10f0907833        none                null                local

在Docker中管理数据

默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着:

  • 当该容器不再存在时,数据将不会持久保存,并且如果另一个进程需要它,则可能很难从容器中取出数据。
  • 容器的可写层与运行容器的主机紧密耦合,您不能轻易地将数据移动到其他地方。
  • 写入容器的可写层需要 存储驱动程序来管理文件系统。存储驱动程序支持联合文件系统,需要使用Linux内核。与使用直接写入主机文件系统的数据卷相比,这种额外的抽象降低了性能 。

如何希望容器停止后文件也可以持久存储,docker提供了2种方式:

  • volumes
  • bind mounts

如果你是在Linux上运行docker,还可以选择tmpfs mount,如果你是在Windows上运行docker,你也可以使用named pipe(命名管道)。

无论您选择使用哪种类型,容器中的数据看起来都是相同的。它在容器的文件系统中显示为目录或单个文件。

  • volumes: 数据存储在由docker管理的主机文件系统的一部分上(默认位置//var/lib/docker/volumes/,对于linux系统来说),非Docker进程不应修改文件系统的这一部分。volumes是在Docker中持久保存数据的最佳方法。
  • bind mounts: 可以存储在主机系统上的任何位置。它们甚至可能是重要的系统文件或目录。Docker主机上的非Docker进程或一个Docker容器可以随时对其进行修改。
  • tmpfs mount:仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统中。

一. 使用 volumes

卷是将数据持久存储在Docker容器和服务中的首选方法。

当启动一个容器时,使用了一个新创建的volumes,如果容器的目录中有内容,则该目录中的内容会被复制到volumes里面,然后容器挂载并使用volumes,其它使用该volumes的容器也可以访问预填充的内容。

1. volumes使用场景

卷的一些用例包括:

  • 在多个运行中的容器之间共享数据。如果未显式创建卷,则在首次将卷安装到容器中时将创建该卷。当该容器停止或卸下时,该卷仍然存在。多个容器可以同时装载相同的卷(可读写或只读)。仅在显式删除卷时才将它们删除。
  • 当您要将容器的数据存储在远程主机或云提供商上,而不是在本地时,可以使用volumes;
  • 当您需要将数据从一台Docker主机备份,还原或迁移到另一台Docker主机时,卷是一个更好的选择。停止使用该卷的容器,然后备份该卷的目录(例如/var/lib/docker/volumes/)。

2. volumes的优点:

  • 与bind mounts相比,卷更易于备份或迁移。
  • 可以使用Docker CLI命令或Docker API管理卷。
  • 卷在Linux和Windows容器上均可工作。
  • 可以在多个容器之间更安全地共享卷。
  • 卷驱动程序使您可以将卷存储在远程主机或云提供程序上,以加密卷内容或添加其他功能。
  • 可以通过容器预填充新卷的内容。

3. volumes的使用

创建和管理volumes

与bind mounts不同,您可以在任何容器范围之外创建和管理卷。

  • 创建一个volumes
[root@python-study ~]# docker volume create my-vol
my-vol
  • 列出volumes
[root@python-study ~]# docker volume ls
DRIVER              VOLUME NAME
local               my-vol
  • 检查volumes的详细信息
[root@python-study ~]# docker volume inspect my-vol 
[
    {
        "CreatedAt": "2020-09-01T10:38:34+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]
  • 移除一个volumes
[root@python-study ~]# docker volume rm my-vol 
my-vol

启动容器时使用volumes

启动容器时,如果想使用volumes,可以使用-v 或 –mount 选项。如果指定的volumes不存在,则启动的时候会自动创建。

  • -v or –volume:用于独立的容器管理卷,使用冒号分隔;
    • 第一个字段是卷的名称;
    • 第二个字段是容器中的文件或目录路径;
    • 第三个字段是可选的,并且是用逗号分隔的选项列表,例如ro
$ docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest
  • –mount:从docker 17.06 版本后,可以用于集群也可以用于独立容器。包含多个键值对,以逗号分隔,每个键值对都由一个 元组组成。
<key>=<value>
  • type:指定挂载的类型,可以是bind,volume,tmpfs;
  • source或src:指定volume的名字;
  • destination或者dst或target:指定容器中的文件或目录路径;
  • readonly:只读;
  • volume-opt:指定多个key-value对可用选项;
$ docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest

可以使用docker inspect devtest命令查看容器详细信息,在Mounts部分可以看到卷的信息:

        "Mounts": [
            {
                "Type": "volume",
                "Name": "myvol2",
                "Source": "/var/lib/docker/volumes/myvol2/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],

删除卷

删除卷是一个独立的步骤,删除容器并不能移除卷。

$ docker container stop devtest

$ docker container rm devtest

$ docker volume rm myvol2

4. 备份、还原和迁移数据卷

volumes对于备份、还原和迁移来说是很有用的,创建一个新容器时,可以使用–volumes-from来挂载这些volumes。

备份一个容器

  • 为了演示,先创建一个新的容器,名字为dbstore:
[root@python-study ~]# docker run -itd -v /dbdata --name dbstore ubuntu /bin/bash
490d16687f0a2e61e88b4157251a33ee5acea0991b36d2ae4a2e47fe1c3e50f6
  • 进入容器,在容器内的/dbdata目录中创建一些文件,用于测试
[root@python-study ~]# docker exec -it dbstore /bin/bash
root@490d16687f0a:/# cd /dbdata/
root@490d16687f0a:/dbdata# echo 'this is backup test!' > file1.txt
root@490d16687f0a:/dbdata# ls
file1.txt
  • 然后执行备份命令
    • 启动一个新容器挂载容器dbstore的volumes
    • 挂载本地的主机目录到容器内部/backup目录
    • 执行tar命令打包备份/dbdata目录中的内容到/backup目录,文件名为backup.tar
[root@python-study ~]# docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
tar: Removing leading `/' from member names
/dbdata/
/dbdata/file1.txt
  • 查看主机目录,验证备份文件已经存在
[root@python-study ~]# ls backup.tar 
backup.tar
[root@python-study ~]# tar -tf backup.tar 
dbdata/
dbdata/file1.txt

从备份还原到容器

可以将备份文件还原到相同的容器中或在其他位置的另一个容器中。

  • 例如创建一个新的容器dbstore2。
[root@python-study ~]# docker run -itd -v /dbdata --name dbstore2 ubuntu /bin/bash
17b5e3d2c85d79338c705bf5880a4078106a7167b58827c1a34c67e30a4d0157
  • 还原操作:将备份文件解压缩到新容器的数据卷中:
[root@python-study ~]# docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu \
> bash -c 'cd /dbdata && tar xvf /backup/backup.tar --strip 1'
dbdata/file1.txt
  • 进入新容器进行检查
[root@python-study ~]# docker exec -it dbstore2 ls /dbdata
file1.txt

5. 删除volumes

删除容器后,Docker数据卷仍然存在。

  • 删除命名卷:需要在删除容器时,指示Docker Engine将其删除
  • 命名卷:可以使用docker volume管理命令删除
##此命令创建一个匿名/foo卷。删除容器后,Docker Engine会删除该/foo卷,但不会删除该awesome卷。
docker run --rm -v /foo -v awesome:/bar busybox top
  • 删除所有卷
[root@python-study ~]# docker volume  prune

二. 使用 bind mounts

自Docker诞生以来,bind mounts就已经存在。与volumes相比,bind mounts安装的功能有限。使用bind mounts时,会将主机上的文件或目录安装到容器中。

  • -v或者–volume:由三个字段组成,以冒号(:)分隔
    • 第一个字段是主机上文件或目录的路径
    • 第二个字段是文件或目录在容器中的安装路径。
    • 第三个字段是可选的,并且是用逗号分隔的选项,诸如列表ro,consistent,delegated,cached,z,和Z。
##主机上如果不存在目录,会自动创建目录
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app \
  nginx:latest
  • –mount:包含多个键值对,以逗号分隔,每个键值对都由一个元组组成。
<key>=<value>
  • type:其值可以是bind,volume,tmpfs;
  • source: 主机上文件或者目录的路径;
  • destination:容器中的文件或目录的路径,也可以是dst,target;
  • readonly:只读形式
  • bind-propagation:绑定传播,可以是rprivate(默认), private,rshared,shared,rslave,slave;
$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

注意:如果容器中的目录为非空目录,则会被主机目录中的内容覆盖。