Docker高效实践(下)

简介: 前面说过,容器创建的文件保存在容器可写层中,这意味着:移除容器后数据不存在,并且别的进程很难访问容器中的数据容器可写层与运行主机紧密耦合,很难迁移数据容器可写层需要存储驱动来管理,比数据卷直接访问主机文件系统性能低下


五、数据



前面说过,容器创建的文件保存在容器可写层中,这意味着:

  • 移除容器后数据不存在,并且别的进程很难访问容器中的数据
  • 容器可写层与运行主机紧密耦合,很难迁移数据
  • 容器可写层需要存储驱动来管理,比数据卷直接访问主机文件系统性能低下

为了实现数据持久化,Docker提供了三种方式:

  • Volums:数据卷是使用主机文件系统的一部分来存储数据的,数据卷的目录为 /var/lib/docker/volumes/,它是Docker数据持久化最好的方式,不建议非Docker进程修改这部分存储。
  • Bind mount:绑定挂载可以在主机文件系统的任何地方存储数据,包括重要的系统文件或目录。非Docker进程或Docker容器随时可以修改绑定的文件和目录。
  • tmpfs mounttmpfs 只能保存在主机的内存中,适合保存非持久化的状态数据,所以决不会向主机的文件系统中写数据。

微信图片11.png


1. 数据卷


数据卷是Docker容器数据持久化的优先选择方案。不像 bind mount 依赖主机的目录结构,数据卷完全由Docker管理,它相比 bind mount 有以下优势:

  • 数据卷更容易备份或迁移
  • 数据卷可以使用Docker CLI命令或Docker API管理
  • 数据卷可以在Linux和Windows容器下使用
  • 多个容器安全共享数据卷
  • 使用数据卷驱动可以在远程主机或云存储数据,可以加密数据内容,或添加更多的功能
  • 新的数据卷可以通过容器来预先填充内容

此外,使用数据卷不会增加容器的大小,因为它独立于容器的生命周期之外。


1.1 数据卷


创建和管理数据卷

你可以独立于容器之外对数据卷进行操作。比如,我们创建一个数据卷来管理Nginx默认代理的静态文件目录,以方便我们可以持久化保存静态页面。

# 创建一个名为 nginx-html 的数据卷
$ docker volume create nginx-html
# 列出所有数据卷
$ docker volume ls
DRIVER             VOLUME NAME
local              nginx-html
# 查看数据卷的详情
$ docker volume inspect nginx-html
[
    {
        "CreatedAt": "2019-08-25T13:48:03+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/nginx-html/_data",
        "Name": "nginx-html",
        "Options": {},
        "Scope": "local"
    }
]
# 删除一个数据卷
$ docker volume rm <volumn-name>
# 删除所有未使用的本地数据卷
$ docker volume prune


使用数据卷启动容器

在启动容器的时候,可以使用 -v--mount 参数来指定数据卷和需要挂载的容器目录。如果指定的数据卷不存在,Docker会在启动容器的时候自动创建,如果创建的数据卷没有指定名称,Docker会创建一个匿名数据卷,我们使用刚才创建的数据卷启动容器。

# 使用 -v 指定数据卷启动容器
$ docker run -d --name nginx -p 8080:80 -v nginx-html:/usr/share/nginx/html nginx
# 使用 --mount 指定数据卷启动容器
$ docker run -d --name nginx -p 8080:80 --mount source=nginx-html,target=/usr/share/nginx/html nginx

启动成功后可以使用 docker inspect nginx 来查看容器的详情,可以查看 Mounts 信息如下:

"Mounts": [
    {
        "Type": "volume",
        "Name": "nginx-html",
        "Source": "/var/lib/docker/volumes/nginx-html/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "z",
        "RW": true,
        "Propagation": ""
    }
],

我们可以进入Docker默认的数据卷目录(/var/lib/docker/volumes/)下,查看并修改文件信息:

# 查看数据卷目录
$ ls /var/lib/docker/volumes/nginx-html/_data
50x.html index.html
# 修改静态页面
$ cd /var/lib/docker/volumes/nginx-html/_data
$ sudo vim index.html

修改保存后可以在浏览器中查看页面变化,而且不管是重启还是删除再创建容器,只要在启动容器时指定的数据卷还是刚才那一个,数据就不会丢失。


使用只读数据卷启动容器

如果只需要给容器提供一个只读权限的数据卷,我们可以用下面的方式启动容器:

# 使用 -v 指定数据卷启动容器
$ docker run -d --name nginx -p 8080:80 -v nginx-html:/usr/share/nginx/html:ro nginx
# 使用 --mount 指定数据卷启动容器
$ docker run -d --name nginx -p 8080:80 --mount source=nginx-html,target=/usr/share/nginx/html,readonly nginx


1.2 数据卷容器


数据卷容器也是一个普通的容器,只是我们只拿它来存储数据,供别的容器使用,如果有持续更新的数据需要在容器之间共享,建议使用数据卷容器。


创建数据卷容器

我们基于前面的数据卷 nginx-html 来创建一个数据卷容器来供 nginx 容器使用,同样实现静态页面持久化保存。

# 指定数据卷启动一个数据卷容器
$ docker run -v nginx-html:/usr/share/nginx/html -td --name dbstore ubuntu
# 指定数据卷容器启动一个nginx容器
$ docker run --volumes-from dbstore -d --name nginx -p 8080:80 nginx

相比前面直接使用数据卷,现在只是在中间添加了一个容器(dbstore)来使用数据卷。


备份数据

创建好了数据卷容器后,我们可以基于它来进行备份和还原数据,执行下面命令将静态页面数据备份到主机当前目录下:

# 备份数据到当前目录下
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /usr/share/nginx/html
tar: Removing leading `/' from member names
/usr/share/nginx/html/
/usr/share/nginx/html/index.html
/usr/share/nginx/html/50x.html
# 查看备份的数据
$ ls
backup.tar

启动一个临时容器(使用 --rm 参数可以在容器执行结束后自动移除)将数据卷容器中需要备份的数据压缩,并通过挂载当前目录来将压缩包放在主机当前目录下完成备份。


还原数据

停止并移除所有数据卷和容器,现在只剩前面备份的压缩包,没有启动任何容器,也没有数据卷,我们再次通过创建数据卷容器来还原数据:

# 停止和移除所有数据卷和容器,演示还原
$ docker container stop nginx dbstore
$ docker rm nginx dbstore
$ docker volume rm nginx-html
# 还原数据
# 1. 创建数据卷容器
$ docker run -v nginx-html:/usr/share/nginx/html -td --name dbstore ubuntu
# 2. 解压备份的数据
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu bash -c "cd / && tar xvf /backup/backup.tar"
usr/share/nginx/html/
usr/share/nginx/html/index.html
usr/share/nginx/html/50x.html
# 3. 启动nginx容器
$ docker run --volumes-from dbstore -d --name nginx -p 8080:80 nginx

总的来说,我们对数据卷容器进行还原备份操作,最后还是间接操作的数据卷,为啥要使用中间的数据卷容器而不直接对数据卷目录进行备份操作呢?有兴趣的同学可以在留言栏讨论这个问题。


2. bind mount


bind mount 使用和理解都比较简单,就是直接将主机的某个目录绑定到容器中的某个目录下,实现容器的数据持久化,并且可以很方便的直接在主机上修改绑定到容器中的文件。

使用 bind mount 启动容器

使用 bind mount 来完成前面的例子,使nginx默认代理的静态文件目录持久化,同样,在启动容器的时候,可以使用 -v--mount 参数来指定主机目录和需要挂载的容器目录。

# 使用 -v 指定绑定的主机目录
$ docker run -d -p 8080:80 -v /home/ajn/docker:/usr/share/nginx/html --name nginx nginx
# 使用 --mount 指定绑定的主机目录
$ docker run -d -p 8080:80 --mount type=bind,source=/home/ajn/docker,target=/usr/share/nginx/html --name nginx nginx

使用 -v 的参数格式是 :[:ro],指定主机目录和容器目录都使用绝对路径。

启动成功后可以使用 docker inspect nginx 来查看容器的详情,可以查看 Mounts 信息如下:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/home/ajn/docker",
        "Destination": "/usr/share/nginx/html",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

使用只读 bind mount 启动容器

如果只需要给容器提供一个只读权限的挂载目录,我们可以用下面的方式启动容器:

# 使用 -v 指定绑定的主机目录
$ docker run -d -p 8080:80 -v /home/ajn/docker:/usr/share/nginx/html:ro --name nginx nginx
# 使用 --mount 指定绑定的主机目录
$ docker run -d -p 8080:80 --mount type=bind,source=/home/ajn/docker,target=/usr/share/nginx/html,readonly --name nginx nginx


3. tmpfs mount


与数据卷和 bind mount 不同,tmpfs mount 是基于内存的,当容器停止数据会丢失,可以用于存放敏感的文件,它不能被多个容器之间共享,而且只能在Linux环境下的Docker才能使用。

使用 tmpfs mount 启动容器

我们可以使用 --tmpfs--mount 来指定需要挂载到主机内存的容器目录,比如:

# 使用 --tmpfs 来挂载到主机内存
$ docker run -d -p 8080:80 --tmpfs /usr/share/nginx/html --name nginx nginx
# 使用 --mount 来挂载到主机内存
$ docker run -d -p 8080:80 --mount type=tmpfs,destination=/usr/share/nginx/html --name nginx nginx


4. 优选方案


使用数据卷,Docker容器和服务实现数据持久化下面场景优先使用数据卷:

  • 在多个运行的容器之间共享数据,多个容器可以同时挂载同一个数据卷,不管是可读还是可写。数据卷在容器第一次挂载的时候会被创建,当容器停止或移除,数据卷仍会存在,它只能被手动移除。
  • 当主机不保证提供目录或文件结构,数据卷可以帮你主机的配置与容器运行时解耦。
  • 当你想要把容器中的数据存储到远程主机或云上,而不是本地。
  • 当你需要备份、还原和迁移数据时,停止使用数据卷运行的容器,备份数据卷目录(/var/lib/docker/volumes/)。

使用 bind mount,一般情况下使用数据卷,下面场景可以考虑使用绑定:

  • 主机与容器共享配置文件,Docker默认给容器提供DNS解析的方法,就是通过主机向每个容器挂载 /etc/resolv.conf 文件 。
  • 在开发环境,主机与容器共享源代码或构建包时,例如,你可以绑定Maven的 target/ 目录到容器中,然后你每次在主机使用Maven构建,容器都会访问到你最新构建的包。但这只适用于开发环境,在生产环境或其他环境应该使用Dockerfile构建镜像。
  • 当主机的文件目录结构与容器所需绑定一致时,可以考虑使用 bind mount。

使用 tmpfs mount,如果不想在主机持久化保存数据,也不想在容器里面持久保存,可以选择 tmpfs 挂载。当你的应用需要写入大量非持久化状态数据的时候,这种方式可以保护容器的性能。

使用数据卷和 bind mount 需要注意:

  • 如果你挂载一个空数据卷到容器中的非空目录时,该非空目录下的所有文件会复制到数据卷中。同样,如果你启动容器指定了一个不存在的数据卷时,Docker会自动为你创建一个空数据卷。
  • 如果你使用bind mount 或一个非空数据卷到容器中的非空目录时,该非空目录下的所有文件会先隐藏(不是移除或修改,只是不能访问),直到取消挂载时,然后只能访问数据卷文件或主机文件。


六、网络



Docker的网络系统是可插拔的,默认的驱动提供了核心的网络功能,这里我们只讲常用的两种:

  • bridge:默认的网络驱动,桥接网络通常用于你的应用运行在一个单节点容器内。
  • host:对于一个单节点容器,去除容器与主机的所有网络隔离,直接使用主机的网络。
  • none:禁用网络。

其他的网络模式用于集群模式,以后有机会再深入研究。


1. 桥接模式


1.1 使用默认桥接网络

在这个示例中,我们创建两个不同的 alpine 容器来测试和理解它们之间是如何通讯的。首先查看Docker默认的网络:

$ docker network ls
NETWORK ID         NAME               DRIVER             SCOPE
fc1fbea419cd       bridge             bridge             local
2335a2fd7619       host               host               local
f534d018c2a2       none               null               local

启动两个 alpine 容器并运行 ash,它是 alpine 默认的 shell 而不是 bash,并且使用 -dit 参数启动。

因为没有指定 --network 参数,所以容器使用默认的 bridge 网络。

$ docker run -dit --name alpine1 alpine ash
7816fcd49d90497e4ead333836d86066e7e62b22ea626609f0b1cf00cd59ab61
$ docker run -dit --name alpine2 alpine ash
2d9eb49acb2b6fcd25dba0a6e911172fc0d950723161a38bedf7ed5dc25b773e

启动成功后可以查看 bridge 网络的详情,来看是哪些容器连接了它。

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "fc1fbea419cd02bd680342e9d5d7b43538f4e31d9a78a872cd05d32c5d1d2bc9",
        "Created": "2019-08-25T13:30:54.626212304+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"
                }
            ]
        },
        ......
        "Containers": {
            "2d9eb49acb2b6fcd25dba0a6e911172fc0d950723161a38bedf7ed5dc25b773e": {
                "Name": "alpine2",
                "EndpointID": "3cf05f435662a296d4d5021216238364b1c02b2fe63971f2e2ebb33d6f9256a3",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "7816fcd49d90497e4ead333836d86066e7e62b22ea626609f0b1cf00cd59ab61": {
                "Name": "alpine1",
                "EndpointID": "9e005fa1eefca9dcadff6b1eb16d86dd9eba4ca279c1cfd27580f50179507283",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        ......
    }
]

上面是一些关于 bridge 网络的信息,包括网关的IP地址(172.17.0.1),在 Containers 键下是连接的容器的信息,也包括它们的IP地址(alpine1:172.17.0.2, alpine2:172.17.0.3)。

现在两个容器都在后台运行,使用 docker attach 命令连接 alpine1 容器:

$ docker attach alpine1
/ #

然后在 alpine1 容器内使用命令 ip addr show 来查看网络接口:

/ # ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
86: eth0@if87: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
        valid_lft forever preferred_lft forever

第一个接口是本地环回接口,可以忽略它,看第二个接口的IP地址是 172.17.0.2,这和我们上一步操作时看到的IP地址一样。然后我们使用 ping 命令来测试网络:

/ # ping -c 2 baidu.com
PING baidu.com (39.156.69.79): 56 data bytes
64 bytes from 39.156.69.79: seq=0 ttl=39 time=34.457 ms
64 bytes from 39.156.69.79: seq=1 ttl=39 time=38.430 ms
/ # ping -c 2 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.117 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.384 ms
/ # ping -c 2 alpine2
ping: bad address 'alpine2'

第一次我们ping百度,第二次我们ping另一个容器(alpine2)的IP,第三次直接ping另一个容器的名称,前两次我们都成功了,只有第三次异常,说明默认的 bridge 网络不支持直接使用容器名称访问容器。最后停止并移除所有容器:

$ docker container stop alpine1 alpine2
$ docker rm alpine1 alpine2

默认的 bridge 网络不建议在生产环境使用。


1.2 使用自定义桥接网络


我们首先创建一个名称为 alpine-net 的自定义 bridge 网络,然后创建4个 alpine 容器按照前面的方法来演示自定义网络。

$ docker network create --driver bridge alpine-net
5598884881af117abc8c3e977e500ce70795211ac517f312291008b4fc01d463
$ docker network ls
NETWORK ID         NAME               DRIVER             SCOPE
5598884881af       alpine-net         bridge             local
fc1fbea419cd       bridge             bridge             local
2335a2fd7619       host               host               local
f534d018c2a2       none               null               local

查看自定义 alpine-net 网络的详情,此时还没有一个容器连接,请注意它的网关IP已经变成了 172.18.0.1

$ docker network inspect alpine-net
[
    {
        "Name": "alpine-net",
        "Id": "5598884881af117abc8c3e977e500ce70795211ac517f312291008b4fc01d463",
        "Created": "2019-08-25T22:55:29.585657801+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        ......
    }
]

启动4个容器,并使用 --network 参数来指定需要连接的网络驱动,如下方式启动,第1,2,4个使用自定义 alpine-net 网络,第3个使用默认的 bridge 网络,然后再使用 docker network connect 命令将 alpine4 也连接上默认的 bridge 网络:

$ docker run -dit --name alpine1 --network alpine-net alpine ash
$ docker run -dit --name alpine2 --network alpine-net alpine ash
$ docker run -dit --name alpine3 alpine ash
$ docker run -dit --name alpine4 --network alpine-net alpine ash
$ docker network connect bridge alpine4

然后查看 bridge 网络和自定义 alpine-net 网络的详情:

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "fc1fbea419cd02bd680342e9d5d7b43538f4e31d9a78a872cd05d32c5d1d2bc9",
        "Created": "2019-08-25T13:30:54.626212304+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"
                }
            ]
        },
        ......
        "Containers": {
            "46ca15e7f79b1aaa1111be7caa5ec6b7c2ce580140c45ae3f709e79f9c241112": {
                "Name": "alpine4",
                "EndpointID": "8a5e76e2f8a0de5df5ef8bfd851a375f692d116ff71d9e9b6756be8d4cdbb629",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "9287e0f27c882c693ac1533030fe9ef268b8cf49171d83e4d489e084c8fdd016": {
                "Name": "alpine3",
                "EndpointID": "96e27c5eb14d8922707e50e80d2f09b15781a246660cbff29db2069f2f64d95b",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        ......
    }
]
$ docker network inspect alpine-net
[
    {
        "Name": "alpine-net",
        "Id": "5598884881af117abc8c3e977e500ce70795211ac517f312291008b4fc01d463",
        "Created": "2019-08-25T22:55:29.585657801+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        ......
        "Containers": {
            "3ddd7cd62881ac0e4b36c2a48f80ba25fb71833cc175b088fbdcbd6703128150": {
                "Name": "alpine2",
                "EndpointID": "74f708a30c292c0cdf8c715e8cbed69316c7a0b24e0e519243a50cfcbb0ebb7e",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "46ca15e7f79b1aaa1111be7caa5ec6b7c2ce580140c45ae3f709e79f9c241112": {
                "Name": "alpine4",
                "EndpointID": "eb14bf4b4dcf9e4647f6c77148cba902e2003d3f3a4921e294c216215d61c0ac",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "7e7a5227d1f78fb7aaecb9bded1dfd95e2eabcddd1e0b9bff63c8e843b205dd3": {
                "Name": "alpine1",
                "EndpointID": "f4fa0ace4022cabb0e0e8f655c29205cf4b4db117e20ea6f3f4772a96af35a3d",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

从上面我们可以看出它们的的IP关系:

IP Gateway alpine1 alpine2 alpine3 alpine4
bridge 172.17.0.1 - - 172.17.0.2 172.17.0.3
alpine-net 172.18.0.1 172.18.0.2 172.18.0.3 - 172.18.0.4

首先进入 alpine1 容器中测试网络,在自定义 alpine-net 网络下,不仅可以使用IP地址来通讯,还可以直接使用容器名称来通讯:

$ docker attach alpine1
/ # ping -c 2 alpine2
PING alpine2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.153 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.212 ms
/ # ping -c 2 alpine4
PING alpine4 (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.204 ms
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.218 ms
/ # ping -c 2 alpine1
PING alpine1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.045 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.230 ms

但是在 alpine1 容器中,我们不能通过容器名称来访问 alpine3,因为它不在自定义 alpine-net 网络中。不止如此,就算使用IP地址,你也不可以使 alpine1alpine3 互相访问,因为它们不在同一个网关下:

/ # ping -c 2 alpine3
ping: bad address 'alpine3'
/ # ping -c 2 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

使用 Ctrl+P, Ctrl+Q 来退出 alpine1 容器,然后再用同样的方法进入 alpine4 容器来测试网络,记住 alpine4 既连接了 bridge 网络,也连接了自定义 alpine-net 网络,它可以访问其他所有容器,但是只能使用IP地址访问 alpine3 容器,因为它们两个是通过默认 bridge 网络连接的。

$ docker attach alpine4
/ # ping -c 2 alpine1
PING alpine1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.132 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.214 ms
/ # ping -c 2 alpine2
PING alpine2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.159 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.195 ms
/ # ping -c 2 alpine3
ping: bad address 'alpine3'
/ # ping -c 2 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.133 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.381 ms
/ # ping -c 2 alpine4
PING alpine4 (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.073 ms
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.147 ms

最后演示完成,停止并移除所有容器和自定义网络。

$ docker container stop alpine1 alpine2 alpine3 alpine4
$ docker rm alpine1 alpine2 alpine3 alpine4
$ docker network rm alpine-net


2. 主机模式


主机(host)模式网络很简单,它可以去除容器和主机网络的隔离,让你感觉访问容器就像访问主机一样。我们再次拿 nginx 容器来举例,它默认暴露的是80端口,使用 host 网络的条件是:

  • 主机上80端口没有被占用
  • host 网络驱动只能用于Linux主机,不能用于Mac或Windows主机

运行下面命令启动 nginx 容器,此时容器暴露的80端口已经与主机的80端口就映射了:

$ docker run -d --network host --name nginx nginx

直接使用浏览器访问 localhost,可以看到Nginx的默认欢迎页面。

微信图片11.jpg

Docker默认的两种常用网络连接方式就讲到这了,Docker的基础知识也讲到这为止,以后可以讲一些关于Dockfile以及Docker的实战,Docker集群部署,Kubernetes容器管理技术,以及大规模弹性容器云平台设计。

目录
相关文章
|
17天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
59 2
|
19天前
|
Cloud Native 持续交付 Docker
Docker容器化技术:从入门到实践
Docker容器化技术:从入门到实践
|
2月前
|
Kubernetes 持续交付 Docker
探索DevOps实践:利用Docker与Kubernetes实现微服务架构的自动化部署
【10月更文挑战第18天】探索DevOps实践:利用Docker与Kubernetes实现微服务架构的自动化部署
89 2
|
2月前
|
存储 运维 云计算
探索Docker容器化:从入门到实践
在这个快速发展的云计算时代,Docker容器化技术正在改变应用的开发、部署和管理方式。本文旨在为初学者提供一个关于Docker的全面入门指南,并通过实践案例展示Docker在实际开发中的应用。我们将一起了解Docker的核心概念、基本操作、网络和存储,以及如何构建和部署一个简单的Web应用。无论你是开发者还是运维人员,本文都会帮助你快速掌握Docker的核心技能。
|
19天前
|
数据中心 开发者 Docker
理解并实践Docker容器化技术
理解并实践Docker容器化技术
|
2月前
|
运维 JavaScript 虚拟化
探索容器化技术:Docker的实践与应用
【10月更文挑战第9天】探索容器化技术:Docker的实践与应用
54 3
|
2月前
|
Ubuntu Linux 虚拟化
Docker入门实践(一)
Docker入门实践(一)
|
2月前
|
运维 Kubernetes 监控
掌握Docker容器化技术:构建、部署与管理的高效实践
【10月更文挑战第14天】掌握Docker容器化技术:构建、部署与管理的高效实践
53 0
|
2月前
|
JavaScript 前端开发 Docker
拿下奇怪的前端报错(二):nvm不可用报错`GLIBC_2.27‘‘GLIBCXX_3.4.20‘not Found?+ 使用docker构建多个前端项目实践
本文介绍了在多版本Node.js环境中使用nvm进行版本管理和遇到的问题,以及通过Docker化构建流程来解决兼容性问题的方法。文中详细描述了构建Docker镜像、启动临时容器复制构建产物的具体步骤,有效解决了不同项目对Node.js版本的不同需求。
|
2月前
|
运维 监控 Cloud Native
深入了解容器化技术:Docker 的应用与实践
【10月更文挑战第6天】深入了解容器化技术:Docker 的应用与实践
57 0