
本文详解如何通过 Docker 自定义网络与服务发现机制,让两个独立的开发环境(mgmt-service 和 api-service)在容器内安全、可靠地互相调用,重点解决 ENOTFOUND 域名解析失败与跨服务 404/500 错误问题。
本文详解如何通过 docker 自定义网络与服务发现机制,让两个独立的开发环境(mgmt-service 和 api-service)在容器内安全、可靠地互相调用,重点解决 `enotfound` 域名解析失败与跨服务 404/500 错误问题。
在 Docker 多服务开发中,“容器间无法通信”是最常见却易被低估的问题。你遇到的 Error: getaddrinfo ENOTFOUND ocd-api-service-app-1 并非代码逻辑错误,而是典型的 DNS 服务发现失效——Docker 默认不会自动为跨 Compose 文件的服务注册主机名,尤其当两个 docker-compose.yml(或 compose-dev.yaml)彼此隔离时,ocd-api-service-app-1 这类由 Docker 自动生成的容器名仅在其所属 Compose 网络内有效,对 mgmt-service 完全不可见。
✅ 正确解法:统一网络 + 显式服务别名
核心原则是:让两个服务接入同一 Docker 网络,并通过用户定义的服务名(而非容器名)相互访问。以下是分步实操方案:
1. 创建共享外部网络(一次执行)
docker network create ocd-network
✅ 注意:该网络必须为 bridge 驱动(默认),且不能由任一 compose 文件自动创建,否则会生成同名但互不相通的“影子网络”。
2. 修改 mgmt-service/compose-dev.yaml
networks:
ocd-network:
external: true # 关键!声明复用已存在网络
services:
app:
image: docker/dev-environments-javascript:stable-1
networks:
- ocd-network # 加入共享网络
# 移除原 volumes 中的 /var/run/docker.sock(非必需,且有安全风险)3. 修改 api-service/compose-dev.yaml
networks:
ocd-network:
external: true
services:
api: # ? 关键重命名:避免与 mgmt 的 app 冲突
image: docker/dev-environments-javascript:stable-1
networks:
- ocd-network
ports:
- "3001:3001" # 可选:便于宿主机调试
# 同样移除 /var/run/docker.sock 绑定⚠️ 重要提醒:Docker Compose 会将服务名(api)自动注册为 DNS 主机名。因此 axios.get("http://api:3001/test") 中的 api 即指向该服务,无需硬编码容器名或 IP。
4. 更新 mgmt-service 的代理逻辑(app.cjs)
server.get("/api/proxy", async (req, res) => {
console.log("✅ hit1: proxy route triggered");
try {
// 使用服务名 'api' + 暴露端口 3001(非容器内端口!)
const response = await axios.get("http://api:3001/test", {
timeout: 5000,
headers: { "User-Agent": "mgmt-proxy" }
});
console.log("✅ Success: received from api service", response.data);
res.json(response.data);
} catch (error) {
console.error("❌ API call failed:", error.response?.status, error.message);
res.status(500).json({
error: "API unreachable",
detail: error.code === 'ENOTFOUND'
? "Check if 'api' service is running and on same network"
: error.message
});
}
});5. 启动顺序与验证
# 先启动 api-service(确保服务就绪) cd api-service && docker compose -f compose-dev.yaml up -d # 再启动 mgmt-service cd mgmt-service && docker compose -f compose-dev.yaml up -d # 验证网络连通性(进入 mgmt 容器测试) docker exec -it ocd-mgmt-service-app-1 sh -c "ping -c 2 api" docker exec -it ocd-mgmt-service-app-1 sh -c "curl -v http://api:3001/test"
? 为什么原配置失败?
| 问题点 | 原因 | 修复方式 |
|---|---|---|
| ocd-api-service-app-1 解析失败 | 该名称仅在 api-service 的 Compose 网络内有效,mgmt 未加入该网络 | 使用 external: true 共享网络 |
| 服务名冲突(均为 app) | Docker 为同名服务生成不同容器前缀,导致 DNS 注册混乱 | 将 API 服务显式命名为 api |
| 容器内端口 vs 映射端口混淆 | api 容器内监听 process.env.PORT,但 mgmt 调用需用容器内端口(非 3001:3001 中的宿主机端口) | 确保 api 应用实际监听 3001(或通过 EXPOSE 3001 声明) |
?️ 安全与最佳实践建议
- 禁用 2375 明文端口:生产环境务必使用 TLS 加密的 2376 端口(参考 Portainer 多主机配置),避免远程 API 被恶意利用。
- 避免 sleep infinity:开发调试可用,但应替换为真实入口命令(如 npm start),确保应用进程作为 PID 1 运行。
-
启用健康检查:在 compose-dev.yaml 中添加 healthcheck,防止代理请求发往未就绪的服务:
api: # ... 其他配置 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3001/"] interval: 30s timeout: 10s retries: 3
完成以上配置后,点击 UI 按钮将稳定触发 /api/proxy → http://api:3001/test → 返回 { message: "Winner, winner chicken dinner", ... },真正实现跨环境服务协同开发。


















