Docker Swarm环境下单A-record实现服务内部路由方案咨询
嘿,你的需求完全能实现!而且刚好匹配你只用Docker不用K8s的场景,核心就是靠Docker Swarm内置的路由网格(Routing Mesh)+统一入口代理来搞定,甚至还能顺便实现故障转移。下面给你一步步拆解:
核心思路
简单说就是:
- 把所有服务都用Swarm的服务模式部署,让Swarm自动帮你做内部路由——不管服务跑在哪个worker节点,集群里所有节点的指定端口都会自动转发到服务所在的机器。
- 只把所有域名的A记录指向你指定的那个Judge Worker Target 1的公网IP,然后在这个节点(或者新增个专门的入口节点)部署一个统一的反向代理,把域名请求转发到Swarm服务的内部域名,剩下的路由和故障转移全交给Swarm处理。
具体操作步骤
1. 先确保Swarm集群正常跑起来
如果还没初始化Swarm,先在manager节点跑:
docker swarm init --advertise-addr <你的manager公网IP>
然后把4个worker节点用manager输出的docker swarm join命令加入集群就行。
2. 确认Swarm路由网格是开启的(默认已经开了,放心)
路由网格是Swarm的核心能力之一,默认启用,它能让集群内所有节点的发布端口都映射到服务实例。可以跑个命令验证下:
docker swarm inspect | grep -i routing-mesh
输出里Enabled要是true就没问题。
3. 用Swarm服务模式部署你的服务
别再用docker run了,改用docker service create,这样服务会被Swarm管理,还能自动生成内部域名。比如部署grav:
docker service create \ --name grav-service \ --publish 80:80 \ --replicas 2 \ # 可以多开几个副本,提升可用性 grav/grav
这里的grav-service就是集群内部的DNS域名,集群里任何节点访问这个域名,都会自动路由到运行grav的worker节点。
4. 部署统一入口代理(推荐Traefik,比Jwilder Nginx Proxy更适配Swarm)
你之前用的Jwilder Nginx Proxy也能改,但Traefik对Swarm的服务发现支持更原生,故障转移也更省心。下面是Traefik的部署步骤:
(1)先写个Traefik的配置文件traefik.yml
api: dashboard: true insecure: true # 生产环境建议关了,改用HTTPS加认证 entryPoints: web: address: ":80" websecure: address: ":443" providers: docker: endpoint: "unix:///var/run/docker.sock" swarmMode: true # 必须开这个,支持Swarm模式 exposedByDefault: false # 只处理我们标注了traefik.enable=true的服务 certificatesResolvers: letsencrypt: acme: email: your-email@example.com # 填你的邮箱,用于Let's Encrypt证书申请 storage: acme.json httpChallenge: entryPoint: web
(2)把Traefik部署成Swarm服务
# 先创建acme.json文件,权限要对,不然证书生成失败 touch acme.json && chmod 600 acme.json # 部署Traefik docker service create \ --name traefik \ --constraint node.role==manager \ # 可以部署在manager,或者专门的入口节点 --publish 80:80 \ --publish 443:443 \ --publish 8080:8080 \ # Traefik的后台管理端口,方便看路由状态 --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ --mount type=bind,source=$(pwd)/traefik.yml,target=/etc/traefik/traefik.yml \ --mount type=bind,source=$(pwd)/acme.json,target=/acme.json \ traefik:v2.10
(3)给你的grav服务加Traefik标签,自动绑定域名
更新已经部署的grav服务,加上路由规则:
docker service update \ --label-add traefik.enable=true \ --label-add traefik.http.routers.grav.rule=Host(`grav.yourdomain.com`) \ # 填你的grav域名 --label-add traefik.http.routers.grav.entrypoints=web \ --label-add traefik.http.services.grav.loadbalancer.server.port=80 \ # grav的容器端口 grav-service
这样用户访问grav.yourdomain.com时,请求会先到Traefik,Traefik通过Swarm的服务发现找到运行grav的worker节点,自动转发过去,完全不用管服务在哪台机器。
5. 最后配置域名A记录
把所有服务的域名(比如grav.yourdomain.com、judge.yourdomain.com等)的A记录都指向你指定的那个Judge Worker Target 1的公网IP就行——如果Traefik部署在其他节点,就指向Traefik所在节点的IP。
故障转移怎么实现?
Swarm本身就自带故障转移能力:如果某个worker节点挂了,Swarm会自动把该节点上的服务实例调度到其他健康的worker节点,Traefik会通过服务发现自动更新路由,用户完全感知不到,连刷新都不用。
替代方案:如果不想用Swarm路由网格
要是你因为某些原因不想用Swarm的路由网格,也可以搞个专门的入口代理服务器:
- 把所有域名的A记录指向这台新服务器的IP。
- 在上面部署Nginx或Traefik,配置反向代理到各个服务的内部IP(Swarm服务的虚拟IP或者节点IP)。
- 配合Docker的服务发现工具(比如Consul),让入口代理自动感知服务的节点变化,实现动态路由。不过这个方案比Swarm路由网格复杂多了,除非你有特殊需求,不然不推荐。
几个注意点
- 所有集群节点的防火墙要开放Swarm需要的端口:
2377/tcp(集群管理)、7946/tcp/udp(节点间通信)、4789/udp(覆盖网络),还有80、443这些服务端口。 - 用Traefik的话,
acme.json的权限必须是600,不然Let's Encrypt没法生成证书。 - 要是你坚持用Jwilder Nginx Proxy,得把它部署成Swarm服务,然后配置它用Swarm的内部域名(比如
grav-service)作为后端,这样它就能自动找到服务所在的节点了。
内容的提问来源于stack exchange,提问作者Dream

