我是 Docker 的初学者。我在docker run命令中找不到任何关于这个选项作用的清晰描述,对此有点困惑。
我们能用它来访问运行在 docker 容器上的应用程序而不指定端口吗?举个例子,如果我在 docker run 命令中使用选项 -p 8080:8080
,在 8080 端口运行通过 docker 镜像部署的网络应用程序,我知道我必须在 Docker 容器 ip /theWebAppName 上通过 8080 端口访问它。但我实在想不出"--net=host "选项是如何工作的。
安装完 docker 后,默认情况下有 3 个网络:
docker network ls
NETWORK ID NAME DRIVER SCOPE
f3be8b1ef7ce bridge bridge local
fbff927877c1 host host local
023bb5940080 none null local
我试图让这一切保持简单。因此,如果启动一个容器,默认情况下它会在桥接(docker0)网络中创建。
$ docker run -d jenkins
1498e581cdba jenkins "/bin/tini -- /usr..." 3 minutes ago Up 3 minutes 8080/tcp, 50000/tcp friendly_bell
在 jenkins 的 dockerfile 中,"8080 "和 "50000 "这两个端口是暴露的。这些端口是为桥接网络中的容器开放的。因此,桥接网络中的所有东西都可以通过端口 8080
和 50000
访问容器。桥接网络中的所有设备都在""子网":"172.17.0.0/16" "的私有范围内,如果要从外部访问它们,就必须用"-p 8080:8080 "来映射端口。这将把容器的端口映射到真实服务器(主机网络)的端口。因此,通过 8080
访问服务器将路由到端口为 8080
的桥接网络。
现在你也有了主机网络。它不会将容器网络容器化。因此,如果你在主机网络中启动一个容器,它将看起来像这样(这是第一个):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1efd834949b2 jenkins "/bin/tini -- /usr..." 6 minutes ago Up 6 minutes eloquent_panini
1498e581cdba jenkins "/bin/tini -- /usr..." 10 minutes ago Up 10 minutes 8080/tcp, 50000/tcp friendly_bell
不同之处在于端口。容器现在位于主机网络中。因此,如果在主机上打开8080
端口,就能立即访问容器。
$ sudo iptables -I INPUT 5 -p tcp -m tcp --dport 8080 -j ACCEPT
我在防火墙上打开了8080
端口,现在我通过8080
端口访问我的服务器时,我正在访问我的 jenkins。我认为 本博客 也有助于更好地理解它。
net=host "选项用于让 Docker 容器内的程序从网络角度看就像在主机上运行一样。 它允许容器获得比正常情况下更大的网络访问权限。
通常情况下,你必须将端口从主机转发到容器中,但当容器共享主机网络时,任何网络活动都会直接发生在主机上,就像程序在本地而非容器中运行时一样。
虽然这确实意味着你不必再暴露端口并将它们映射到容器端口,但也意味着你必须编辑 Dockerfile 来调整每个容器监听的端口,以避免冲突,因为你不能让两个容器在同一个主机端口上运行。 不过,使用该选项的真正原因是,运行的应用程序需要网络访问,而这种访问很难在端口级别转发给容器。
例如,如果要运行 DHCP 服务器,就需要能够监听网络上的广播流量,并从数据包中提取 MAC 地址。 这一信息会在端口转发过程中丢失,因此在 Docker 内运行 DHCP 服务器的唯一方法就是以 --net=host
的方式运行容器。
一般来说,只有当你运行的程序有非常特殊、不寻常的网络需求时,才需要使用 --net=host
。
最后,从安全角度来看,尽管 Docker 容器只公布(暴露)一个端口,但它们可以监听多个端口。 通常情况下,这没什么问题,因为你只转发了预期的单个端口,但如果你使用 --host=net
,那么你就会得到**容器在主机上监听的所有端口,甚至是那些没有在 Dockerfile 中列出的端口。 这意味着你需要仔细检查容器(特别是如果它不是你的容器,例如由软件项目提供的官方容器),以确保不会无意中在机器上暴露额外的服务。