五、数据
前面说过,容器创建的文件保存在容器可写层中,这意味着:
- 移除容器后数据不存在,并且别的进程很难访问容器中的数据
- 容器可写层与运行主机紧密耦合,很难迁移数据
- 容器可写层需要存储驱动来管理,比数据卷直接访问主机文件系统性能低下
为了实现数据持久化,Docker提供了三种方式:
- Volums:数据卷是使用主机文件系统的一部分来存储数据的,数据卷的目录为
/var/lib/docker/volumes/
,它是Docker数据持久化最好的方式,不建议非Docker进程修改这部分存储。 - Bind mount:绑定挂载可以在主机文件系统的任何地方存储数据,包括重要的系统文件或目录。非Docker进程或Docker容器随时可以修改绑定的文件和目录。
- tmpfs mount:
tmpfs
只能保存在主机的内存中,适合保存非持久化的状态数据,所以决不会向主机的文件系统中写数据。
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地址,你也不可以使 alpine1
和 alpine3
互相访问,因为它们不在同一个网关下:
/ # 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的默认欢迎页面。
Docker默认的两种常用网络连接方式就讲到这了,Docker的基础知识也讲到这为止,以后可以讲一些关于Dockfile以及Docker的实战,Docker集群部署,Kubernetes容器管理技术,以及大规模弹性容器云平台设计。