Docker Swarm 网络互通 — 一个从故障到解决的简明实战教程(初学者友好)

下面把你这次的排查与解决流程精简、结构化地整理成一篇小 blog。目标是让初学者能一步步看懂:为什么容器间不能用服务名通信、如何修复、以及浏览器访问与服务名的区别。文中穿插命令、表格和 > 注解,便于复制执行和复用。


一、问题概述(一句话)

容器间无法通过 服务名 互相访问(报 no such host),并且有 0.0.0.0:50052 作为拨号目标导致连接失败;此外浏览器重定向尝试访问 http://mediahub:5174/ 也失败。

实际症状归为两类:

  1. Swarm 内服务 DNS 不生效 → lookup xxx on 127.0.0.11:53: no such host。
  2. 浏览器访问外部主机时使用了 Swarm 内部服务名 → 浏览器无法解析 mediahub。

二、核心概念速览(非常重要,读懂就好)

  • Overlay 网络(Swarm):Swarm 的容器互通依赖 overlay 网络与内建 DNS。只有加入同一 overlay 网络的服务,才能互相通过 service-name 解析到对方。

  • 服务名解析范围:service-name 仅在 Swarm overlay 网络内部可解析,浏览器/宿主机不认识该域名。

  • 监听地址 vs 拨号目标:

    • 0.0.0.0 用于监听(表示“所有网卡”),不能作为客户端拨号目标;
    • 客户端拨号应使用 service-name:port(跨容器)或 127.0.0.1:port(同容器/同主机)。
  • Config 挂载(docker config):如果配置是通过 docker config 挂载的,可以不重建镜像,按 service update --config-rm → docker config rm → docker config create → service update --config-add 的顺序更新。

小提示:Alpine 镜像没有 bash,用 sh 进入容器;在容器内用 netstat / ss 检查监听地址。


三、你做了什么(关键命令与结果)

  1. 创建 overlay 网络并加入服务:
docker network create --driver overlay app-net

# 把服务加到同一网络(示例)
docker service update --network-add app-net user
docker service update --network-add app-net user-crontab
docker service update --network-add app-net mediahub
docker service update --network-add app-net shorturl
docker service update --network-add app-net shorturl-proxy
  1. 验证服务是否加入网络:
docker service inspect user --format '{{json .Spec.TaskTemplate.Networks}}'
# 期望结果包含 app-net 的 network id
  1. 在容器内看监听地址(示例):
docker exec -it <container_id> sh
# 容器内执行
netstat -tlnp
# 例如看到:127.0.0.1:8080 -> 说明只能容器内部 loopback 访问
  1. 测试服务名解析(在容器内):
docker exec -it <user_container_id> ping -c2 user-crontab

四、为什么你一开始会遇到那些错误(归纳)

报错/现象根本原因说明
no such host(lookup user-crontab …)服务未加入同一个 overlay 网络 / 没有 Swarm DNS 路径Swarm DNS 只在 overlay 网络生效
dial tcp 0.0.0.0:50052: connect: connection refused客户端拨号使用 0.0.0.0(错误)0.0.0.0 只能监听,不能作为目标拨号地址
浏览器访问 http://mediahub:5174 失败mediahub 是 Swarm 内部名,外部 DNS/主机无法解析浏览器需访问宿主 IP 或域名(例如 http://192.168.132.134:8080)

注:在本地开发(server 和 client 在同一台机器)0.0.0.0 / localhost 有时“蒙对”导致看似正常,但分布式环境下会暴露问题。


五、操作步骤(按顺序复制执行) — 把你的集群弄通用这 7 步

下面写出的命令基于你现有的服务名(mediahub、user、user-crontab、shorturl、shorturl-crontab、shorturl-proxy)。你已经执行了部分操作(创建 app-net 并加入服务),这里列出完整核查与补充步骤。

  1. 创建 overlay 网络(如果还没创建):
docker network create --driver overlay app-net
  1. 把所有相关 service 加入同一个网络:
docker service update --network-add app-net user
docker service update --network-add app-net user-crontab
docker service update --network-add app-net mediahub
docker service update --network-add app-net shorturl
docker service update --network-add app-net shorturl-crontab
docker service update --network-add app-net shorturl-proxy
  1. 验证服务已在网络上(任意服务举例):
docker service inspect user --format '{{json .Spec.TaskTemplate.Networks}}'
  1. 在容器内测试 DNS 分辨(在 user 容器里 ping user-crontab):
# 找到 user 的运行容器 id(示例)
CID=$(docker ps --filter "name=user.1" -q)
docker exec -it $CID ping -c2 user-crontab
  1. 检查服务监听地址(防止只监听 localhost):
docker exec -it $CID sh
# 容器内
netstat -tlnp
# 或
ss -tln

期望:应用监听在 0.0.0.0:PORT 或 :PORT(这两者等效,均可被外部访问)。

  1. 修复 gRPC / 内部拨号代码(示例,Go):
// server 应该 listen ":50052"
lis, _ := net.Listen("tcp", ":50052")

// client 在 Swarm 内跨服务拨号
conn, err := grpc.Dial("user-crontab:50052", grpc.WithInsecure())

注:如果 client 与 server 在同容器中,拨 127.0.0.1:50052;如果跨容器则拨 service-name:port。

  1. 浏览器回调/重定向配置(internalSystemEntry)
  • 开发(本地前端):http://localhost:5174
  • 集群/局域网部署:填写宿主机 IP 或域名,例如:
internalSystemEntry:
  mediahub: "http://192.168.132.134:5174"

浏览器必须能解析该地址 —— mediahub:5174 只对容器内部有效,不可直接作为浏览器 URL。


六、配置管理:docker config 的快速更新命令

如果你是通过 docker config 挂载配置文件,更新流程如下(以 user 为例):

# 1. 解除服务对旧 config 的使用
docker service update --config-rm user-conf user

# 2. 删除旧 config(只在上一步成功后允许删除)
docker config rm user-conf

# 3. 创建新 config(本地 dev.config.yaml 的路径视情况而定)
docker config create user-conf user/dev.config.yaml

# 4. 把新 config 挂回服务
docker service update --config-add source=user-conf,target=/app/config.yaml user

注:docker config rm 会在 config 正被 service 使用时失败,因此必须先 --config-rm。


七、常见问题与快速排查(Checklist)

  • 服务名解析失败 → 检查两个服务是否在同一 overlay 网络:docker service inspect <svc> --format '{{json .Spec.TaskTemplate.Networks}}'。
  • 容器间能 DNS 解析但连接被拒绝 → 检查目标容器是否在 相应端口 上监听 0.0.0.0:netstat -tlnp。
  • 使用 0.0.0.0 作为拨号目标 → 改为 service-name:port 或 127.0.0.1:port。
  • 浏览器访问 http://service-name:port 失败 → 这是正常,因为该 name 仅作用于 Swarm 网内 DNS;改为宿主机 IP/域名。
  • docker config rm 报错 → 先 docker service update --config-rm 解绑定。

八、简短示例

  • 内部服务调用(推荐)

    • user → user-crontab:grpc.Dial("user-crontab:50052")
    • 两个服务在 app-net overlay 网络上。
  • 浏览器重定向(外部访问)

    • 回调重定向到:http://192.168.132.134:8080/(或域名),不要用 mediahub:5174。

添加链接描述

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐