LV010-Host模式
Host模式需要docker run --net host 或者 --network host 来手动指定。
一、Host 模式的网络拓扑
它相当于 Vmware 中的 NAT 模式,与宿主机在同一个网络中,但没有独立 IP 地址。一个 Docker 容器一般会分配一个独立的 Network Namespace。但如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。
容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。可以通过 --net = host 指定使用 host 网络。

二、有什么特点
(1)host 模式,它没有独立的网络空间。
(2)host 模式下的容器,完全和宿主机共用一个网络空间(端口、IP 等),所以该模式下的容器不会虚拟出容器自身的虚拟网卡,也不会配置自己的虚拟 IP。
(3)host 模式下的容器,除了网络和宿主机共享,其他的资源,如文件系统、进程列表等,容器之间依然是相互隔离的。
弊端就是同一个端口,比如宿主机的 80 端口,只能被一个服务占用,如果被某个容器占用了,宿主机就不能用,后续的容器也不能用,直到优先抢到 80 端口的服务,停止提供服务(放弃 80 端口)。
三、Host 模式示例
首先重开一个 CNB 开发环境,或者删掉之前运行的容器和镜像。
docker ps -a #列出所有容器列表
docker rm -f $(docker ps -qa) #强制移除所有容器
docker ps -a1. 未启动容器
1.1 宿主机网关信息
我们执行以下命令查看:
docker inspect host # 查看 host 网络模式 docker0 虚拟网卡的详细信息(子网网段、网关等)【例】

1.2 宿主机网卡信息
ifconfig查看目的:通过 ifconfig 查看网卡列表信息,以便验证稍后以 Host 模式启动 nginx 容器后,是否会产生新的虚拟网卡。
Tips:实际情况 Host 模式下,各容器不产生新的虚拟网卡!
【例】
➜ /workspace git:(main) ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:d1:19:07:10 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.247.2 netmask 255.255.255.0 broadcast 172.19.247.255
ether 02:42:ac:13:f7:02 txqueuelen 0 (Ethernet)
RX packets 1650 bytes 568077 (554.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1551 bytes 5437380 (5.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 62 bytes 4748 (4.6 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 62 bytes 4748 (4.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 01.3 宿主机 80 端口
先用命令 ss -lntup 或者 netstat -nalpt 验证一下,是否有未知程序占用 80,有的话 kill it,依次确保下面实验的准确性(nginx 占用的是 80 端口,稍后启动的 nginx 容器会占用宿主机的 80 端口)。:
docker ps -a # 实验前,查看是否有启动的容器,如果有暂时先停掉,特别是 nginx 容器
netstat -nalpt # 实验前,先查看宿主机的(80)端口占用情况【例】
➜ /workspace git:(main) netstat -nalpt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.11:43157 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:36000 0.0.0.0:* LISTEN 88/sshd: /usr/sbin/
tcp 0 0 0.0.0.0:8686 0.0.0.0:* LISTEN 72/node
tcp 0 0 172.19.247.2:8686 172.19.247.1:52020 ESTABLISHED 72/node
tcp 0 0 172.19.247.2:8686 172.19.247.1:52024 ESTABLISHED 266/node会发现这里并没有任何服务占用 80 端口。
2. 启动两个 nginx 容器
这里我们先后启动两个 nginx 服务,这里先启动第一个。预期的情况是第一个 nginx 容器被启动后,会占用宿主机的 80 端口。因为宿主机只有 1 个 80 端口,那么第二个 nginx 自然是因为没有 80 端口可用,而启动失败了。
2.1 启动 nginxV1 容器
- docker run 以 Host 模式,启动第一个 nginx 容器 nginxV1
docker run -d --net=host --name nginxV1 nginx:alpine # 以 host 模式启动 nginxV1【例】
➜ /workspace git:(main) docker run -d --net=host --name nginxV1 nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
#...
Status: Downloaded newer image for nginx:alpine
342e6d721db7ccc397aef2f30fe32e48848d65e3fec8c72ece009fb9963fc11f2.2 查看宿主机端口
netstat -nalpt # 查看宿主机的端口占用情况(主要看 80 端口)【例】
➜ /workspace git:(main) netstat -nalpt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.11:43157 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:36000 0.0.0.0:* LISTEN 88/sshd: /usr/sbin/
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:8686 0.0.0.0:* LISTEN 72/node
tcp 0 0 172.19.247.2:8686 172.19.247.1:52020 ESTABLISHED 72/node
tcp 0 0 172.19.247.2:8686 172.19.247.1:52024 ESTABLISHED 266/node
tcp6 0 0 :::80 :::* LISTEN -可以看到宿主机的 80 端口已经被占用。
2.3 查看宿主机网卡信息
ifconfig【例】
➜ /workspace git:(main) ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:d1:19:07:10 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.247.2 netmask 255.255.255.0 broadcast 172.19.247.255
ether 02:42:ac:13:f7:02 txqueuelen 0 (Ethernet)
RX packets 4188 bytes 23593631 (22.5 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3855 bytes 5615089 (5.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 132 bytes 10537 (10.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 132 bytes 10537 (10.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0可以发现,Host 模式下的容器启动后,不会创建新的网卡(桥接模式下的容器,会自动创建以 veth 开头的虚拟网卡) 。
2.4 查看 nginxV1 容器网络信息
docker ps # 查看正在运行的容器
docker inspect container_id # 根据容器 id 的首字母也可以查看容器详细信息【例】

可以发现,Host 模式开启的容器,容器自身是没有任何网络资源信息的,都是空的,它用的一切网络资源都是宿主机的.
2.5 访问容器的 nginx 服务
那么下面继续通过宿主机访问一下 nginxV1 吧:
ifconfig # 查看宿主机(本机)IP
curl 物理网卡IP # 直接访问宿主机的 IP(eth0/ens33),注:80 默认可以不写【例】
➜ /workspace git:(main) ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
#...
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.247.2 netmask 255.255.255.0 broadcast 172.19.247.255
#...
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
#...
➜ /workspace git:(main) curl 172.19.247.2
#...
<h1>Welcome to nginx!</h1>
#...尽管 host 模式下的 nginx 容器没有任何网络资源,但是以该模式启动的容器,依然可以直接通过宿主机的 IP(+端口,不同的服务,端口也不同)直接访问。
为什么呢?上面已经提及,Host 模式运行的容器,用的一切网络资源,是占用的宿主机的网络资源。
2.6 nginxV1 能联网吗?
我们使用下面的命令进入 nginxV1 容器:
docker ps
docker exec -it container_id /bin/sh
ifconfig
ping www.baidu.com【例】
➜ /workspace git:(main) docker exec -it 342e6d721db7 /bin/sh
/ # ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:D1:19:07:10
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
#...
eth0 Link encap:Ethernet HWaddr 02:42:AC:13:F7:02
inet addr:172.19.247.2 Bcast:172.19.247.255 Mask:255.255.255.0
#...
lo Link encap:Local Loopback
#...
/ # ping www.baidu.com
PING www.baidu.com (183.2.172.177): 56 data bytes
64 bytes from 183.2.172.177: seq=0 ttl=49 time=5.764 ms
^C
--- www.baidu.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 5.670/5.732/5.764 ms发现是可以联网的,容器的 eth0 的 ip 和宿主机完全一样。
2.7 再启动一个 nginxV2
既然宿主机的 80 端口被 nginxV1 霸占了,那么再开启 nginxV2,是否可以启动呢?那肯定是不能启动,宿主机的 80 端口就 1 个,已经被 V1 占用了。
docker ps # 查看正在运行的容器
docker run -d --net=host --name nginxV2 nginx:alpine #再次以 host 模式,开启 nginxV2
docker ps -a # 查看 nginxV2 是否启动成功(发现,没有启动成功)【例】

这里 nginxV2 启动失败,我们可以用下面的命令看一下日志:
docker logs nginxV2【例】

发现,nginxV2 失败的原因是因为 80 端口已经被占用,至此就验证了,上面提到的,Host 网络模式下宿主机中容器的网络资源是占用的宿主机的,特别是端口,先占先得,除非先得到的容器放弃了该端口的占用。
2.8 其他宿主机
开启其他主机, curl host 模式下的 nginx:

这里照搬一张图,因为我是在 CNB 云原生开发中验证,暂时还不知道怎么搞一个同网段的其他主机,这里就没有尝试了。
会发现,其他同一网段的虚拟主机可以直接访问宿主机内部的容器。这一点很神奇,然而 bridge 模式下,同一网段的主机,是不能直接访问宿主机内部容器的。
之所以 Host 模式下,同一网段的主机可以直接访问宿主机内部容器,是因为容器霸占的是宿主机的资源。访问容器,相当于访问的就是宿主机,给人的感觉就是:“宿主机就是容器,容器就是宿主机”。然而除了网络资源,容器占用的是宿主机的,容器之间的其他资源依然是相互隔离的,如文件系统、进程列表等。
四、总结
Host模式的优点和缺点,都十分明显,容器内的服务只要部署成功,可以被同一网段内的局域网的其他任一主机访问,给人的错觉就是“宿主机就是容器,容器就是宿主机”。缺点就是,一个端口只能被一个服务占用,且容器的网络也缺少隔离性,故该使用场景十分有限。
因为该模式,不是默认的网络模式,所以在docker run的时候,要添加参数--net=host 或者 --network=host 来手动指定。
参考资料:
Docker 四种网络模式(Bridge,Host,Container,None) - wq9 - 博客园
Docker 学习:容器五种(3+2)网络模式 | bridge 模式 | host 模式 | none 模式 | container 模式 | 自定义网络模式详解-CSDN 博客