仅将Docker Swarm模式端口发布到本地主机

9
我已经创建了一个docker swarm,并在其中包含一个网站,将端口8080发布到外部。我想使用在swarm之外运行的Nginx消耗该端口,Nginx运行在80端口上,它将执行服务器名称解析和托管静态文件。
问题是,swarm自动使用iptables将端口8080发布到互联网,我不知道是否可能仅允许本地nginx实例使用它?因为目前用户可以在80和8080端口访问站点,第二个端口是损坏的(没有图像)。
我尝试使用ufw进行操作,但它不起作用。手动更改iptables将是一场噩梦,因为每次更新后我都必须在每个swarm节点上这样做。有什么解决方案吗?
编辑:我不能在swarm和swarm之外使用相同的网络,因为叠加网络与普通的单主机容器不兼容。理论上,我可以将nginx放到swarm中,但我更喜欢将其保持独立,位于包含静态文件的相同主机上。
3个回答

8

2

如果容器在同一网络中,则不需要为容器发布端口以相互访问。

对于您的情况,您可以从nginx容器发布端口80,并且不需要从website容器发布任何端口。 只要这两个容器在同一个Docker网络中,Nginx仍然可以通过端口8080访问网站容器。


1
问题在于它们不能在同一网络中。Swarm 网络类型是覆盖层,如果您尝试使用 "docker run --net swarm_net" 运行普通的 Docker 容器,则会收到网络不兼容的错误。我知道在使用普通的 Docker 容器时可以工作,但 Swarm 改变了一切。此外,“docker service create”--publish 命令不支持接口,因此不可能执行 --publish 127.0.0.1:8080:8080。 - Valar
@Valvar,@Elton-Stoneman是正确的。您只需要将每个容器作为服务运行,并且如果需要,可以使用--publish选项,但不需要在服务中指定IP地址。在SWARM集群上使用服务时,当您使用--publish PORT:PORT时,它会设置映射到集群中每个节点上的端口,因为您不知道(除非强制执行)服务将驻留在集群中的哪台机器上。因此,在执行docker service create命令时,请消除IP地址,仅发布端口映射:--publish 8080:8080 - Chris Townsend
@Valar,例如使用以下命令运行测试nginx服务:docker service create --publish 80:80 --replicas 1 --name test-nginx nginx 等待docker service ls显示它正在运行,然后尝试从集群中的每台机器上的http://127.0.0.1或使用它们的IP地址,您应该可以从每台机器上获得欢迎来到nginx页面,即使您只有一个实例在运行。 - Chris Townsend
感谢您的建议,您所说的一切都是正确的,我也理解了。但是这种发布方式的问题在于,我无法控制谁可以访问已发布的端口,以及在什么情况下进行访问等。这就是为什么我想在外部世界和已发布端口之间放置nginx的原因。从我所看到的情况来看,这几乎是不可能的,因此唯一的解决方案是将nginx放置在Swarm内部,并仅发布nginx端口。 - Valar
1
我更喜欢将Nginx放在Swarm中 - 否则你的公共入口只在单个主机上,没有故障转移,而你的上游有HA。但是,如果你真的想这样做,那就成为一个基础设施问题 - 在Swarm上暴露应用程序端口,并使用防火墙规则,以便只有Nginx框可以访问Swarm端口。 - Elton Stoneman

1

我正在使用的“临时”解决方案是依赖于 alpine/socat 镜像。

思路:

  • 使用额外的轻量级容器,在群集之外运行,并使用某些端口转发工具(例如这里使用的是 socat)
  • 将该容器添加到要仅向本地主机公开的群集服务的相同网络中
  • 将此辅助容器公开在 localhost:HOST_PORT:INTERNAL_PORT
  • 使用此容器的 socat 将流量转发到群集的机器

命令:

docker run --name socat-elasticsearch -p 127.0.0.1:9200:9200 --network elasticsearch --rm -it alpine/socat tcp-listen:9200,reuseaddr,fork tcp:elasticsearch:9200

如果你确认一切正常工作,可以删除标志-it。另外,添加-d以使其在后台运行。

守护进程命令:

docker run --name socat-elasticsearch -d -p 127.0.0.1:9200:9200 --network elasticsearch --rm alpine/socat tcp-listen:9200,reuseaddr,fork tcp:elasticsearch:9200
我的使用场景:

有时我需要直接访问ES,因此这种方法对我来说非常适合。 不过我想看到一些docker原生的解决方案。

P.S. 如果需要在主机重启后继续运行,请使用docker的自动重启功能。

请参见重启策略文档:

https://docs.docker.com/engine/reference/commandline/run/#restart-policies---restart


1
通过socat在一个独立的容器和一个Swarm服务之间进行TCP端口转发对我来说非常有效。只需要为这两个容器创建一个Docker覆盖/可附加网络即可。这是一种“解决方法”,直到https://github.com/moby/moby/issues/32299有朝一日得到解决... - joseluisq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接