一、端口冲突的“坑”,你肯定踩过
做后端服务的人,多半都遇过这种糟心事:新开发的服务要上线,本地跑没问题,一部署到服务器上就报错,查来查去才发现是端口冲突——服务器上已经跑了个服务占了这个端口,新服务死活起不来。 要是单台服务器部署也就罢了,大不了改个端口再配反向代理。但要是集群部署,比如用Docker Swarm管一堆服务器,端口冲突的麻烦就会翻倍:你得给每个服务单独分配不重复的端口,还要记清楚哪个服务用了哪个端口,哪天集群里加了新服务,还得重新梳理端口表,稍不注意就又撞了。 其实Docker Swarm里有两种专门解决集群端口的发布模式:host模式和ingress模式,搞懂它们的区别和用法,就能从根源上避开端口冲突的坑。
二、Docker Swarm的两种端口发布模式
要讲这两种模式,得先铺垫个小前提:Docker Swarm是用来把一堆Docker服务器拼成一个集群的工具,集群里的每台服务器叫“节点”,你可以在节点上跑服务,也可以把服务分成多个副本(比如跑3个副本,提高可用性)。服务对外暴露端口时,就靠这两种模式来定端口怎么分配、怎么访问。
2.1 host模式:端口直接“贴”在节点上
host模式的逻辑特别简单:你给服务指定一个端口,Swarm就会让这个服务的每个副本,直接占用它所在节点的这个端口。换句话说,每个副本的端口和节点的端口是一一对应的,没有中间商。
2.1.1 具体示例
我们用Nginx做示例,先明确这个示例的技术栈:Docker Swarm + Nginx(所有操作都基于Docker Swarm集群,示例命令都是Docker Swarm的标准命令)。 首先,得确保你的Docker已经开启了Swarm模式,执行这个命令初始化集群(如果已经有集群可以跳过):
# 初始化Docker Swarm集群,当前节点作为管理节点
docker swarm init
然后我们创建一个Nginx服务,用host模式发布端口:
# 创建名为nginx-host的服务,副本数设为2,用host模式发布80端口
docker service create \
--name nginx-host \
--replicas 2 \ # 服务跑2个副本
--publish mode=host,target=80,published=80 \ # host模式:把容器内的80端口发布到节点的80端口
nginx:alpine
这里的--publish参数是核心:mode=host指定用host模式,target=80是容器内部的端口(Nginx默认监听80),published=80是要占用的节点端口。
创建完后,我们看服务的状态:
# 查看服务的副本分布和状态
docker service ps nginx-host
假设你的集群有2个节点(主节点和1个工作节点),那么输出会显示:一个副本在主节点,一个在工作节点。这时候你去访问主节点的IP加80端口,或者工作节点的IP加80端口,都能访问到Nginx,因为每个节点的80端口都被这个服务的副本占了。
2.1.2 应用场景
host模式适合什么情况呢?比如你有个服务,必须独占某个固定端口,而且你能保证这个端口在集群里的每个节点上都不被其他服务占用。举个例子:集群里的监控服务,每个节点都要跑一个收集器,收集器需要占用节点的9100端口(Prometheus的默认监控端口),而且每个节点的9100端口都没被别的服务用,这时候用host模式就很合适,不用额外做端口映射,访问也直接。
2.1.3 优缺点
优点:访问速度快,因为没有中间转发,直接连到节点上的服务;配置简单,不用考虑端口冲突的规则。 缺点:太“挑”端口,每个节点的指定端口必须空着,集群里只要有一个节点的这个端口被占,服务就会部署失败;如果服务有多个副本,必须给每个副本分配不同的端口(不然同节点的副本会撞),集群大了之后端口管理会乱。
2.2 ingress模式:集群统一的“端口代理”
ingress模式和host模式完全相反,它的逻辑是:Swarm集群会搞一个统一的“代理层”,你给服务指定一个端口,集群里所有节点的这个端口都会被代理到服务的所有副本上。换句话说,不管你访问集群里哪个节点的这个端口,Swarm都会帮你把请求转发到服务的某个副本上。
2.2.1 具体示例
还是用Nginx做示例,技术栈不变:Docker Swarm + Nginx。 我们创建一个用ingress模式发布的Nginx服务:
# 创建名为nginx-ingress的服务,副本数设为3,用ingress模式发布80端口
docker service create \
--name nginx-ingress \
--replicas 3 \ # 服务跑3个副本
--publish mode=ingress,target=80,published=80 \ # ingress模式:把容器内的80端口发布到集群的80端口
nginx:alpine
这里的mode=ingress就是核心,和host模式的区别就在这。
创建完后,我们看服务的状态:
# 查看服务的副本分布和状态
docker service ps nginx-ingress
假设集群有2个节点,那么3个副本会分布在两个节点上(比如主节点2个,工作节点1个)。这时候你访问主节点的IP加80端口,或者工作节点的IP加80端口,都能访问到Nginx,而且Swarm会帮你做负载均衡,把请求轮流分配到3个副本上。
2.2.2 应用场景
ingress模式适合什么情况呢?比如你有个服务,需要对外提供访问,而且集群里的节点端口经常有冲突,或者你不想给每个服务单独分配端口。举个例子:集群里的多个Web服务,你想让它们都用80端口对外访问,只要用ingress模式,每个服务都可以发布80端口,Swarm会帮你把不同服务的请求转发到对应的副本上;再比如你要给服务做负载均衡,ingress模式天生自带负载均衡的能力,不用额外配置Nginx或者HAProxy。
2.2.3 优缺点
优点:端口不冲突,多个服务可以同时发布同一个端口;自带负载均衡,访问集群里任何节点的这个端口都能访问到服务;适合大规模集群,不用管理每个节点的端口。 缺点:访问速度比host模式慢一点,因为多了一层代理转发;配置相对复杂一点,需要理解Swarm的代理层逻辑。
三、两种模式的核心区别,别再搞混
为了让你更清楚,我们把两种模式的核心区别列出来:
- 端口占用方式:host模式是每个副本占用所在节点的指定端口;ingress模式是集群所有节点的指定端口被代理到服务的所有副本。
- 端口冲突情况:host模式要求每个节点的指定端口必须空闲;ingress模式允许多个服务同时发布同一个端口。
- 负载均衡:host模式没有自带负载均衡,需要自己配置;ingress模式自带负载均衡。
- 访问方式:host模式访问指定节点的指定端口;ingress模式访问集群任何节点的指定端口。
四、选对模式,避开端口冲突的坑
怎么选这两种模式呢?给你两个简单的判断标准:
- 如果你能保证某个端口在集群里的每个节点上都不被其他服务占用,而且服务需要独占这个端口,就用host模式。
- 如果你不能保证端口空闲,或者需要对外提供访问、做负载均衡,就用ingress模式。
举个实际的例子:你有个集群,里面有两个服务,一个是监控服务,每个节点都要跑一个,需要占用9100端口;另一个是Web服务,需要对外提供访问。这时候监控服务用host模式,因为每个节点的9100端口都空着;Web服务用ingress模式,因为你不想给Web服务单独分配端口,而且需要负载均衡。
再举个反面例子:如果你给Web服务用了host模式,发布80端口,那么集群里所有节点的80端口都必须空着,只要有一个节点的80端口被别的服务占了,Web服务就会部署失败;而且如果Web服务有多个副本,同节点的副本会因为都要占80端口而冲突,只能把副本数设为1,可用性就差了。
五、注意事项
- host模式的副本数不能超过节点数:因为每个副本都要占用一个节点的端口,如果你有2个节点,副本数设为3,那么第三个副本就会因为没有可用节点而部署失败。
- ingress模式的端口可以复用:多个服务可以同时发布同一个端口,Swarm会根据服务的名称来区分请求,所以不用怕冲突。
- 两种模式不能同时用:一个服务的端口发布模式只能选一种,不能同时用host和ingress。
- 端口的target和published可以不一样:比如你可以把容器内的80端口发布到节点的8080端口,只要在
--publish参数里改published的值就行。
六、总结
端口冲突是集群部署里很常见的问题,Docker Swarm的host和ingress模式就是专门用来解决这个问题的。host模式适合独占端口、能保证端口空闲的场景,配置简单但限制多;ingress模式适合对外访问、需要负载均衡的场景,灵活但需要理解代理逻辑。只要搞懂它们的区别,选对模式,就能避开端口冲突的坑,让集群部署更顺畅。
评论
围绕“服务发布遭遇端口冲突让人头疼,Docker Swarm中host与ingress发布模式的使用时机辨析,理清不同方式差别,需要正确选择部署避免踩坑”参与讨论