Docker 网络通信中“服务名”与“容器名”的底层逻辑
零、核心疑惑在使用 Docker Compose 时“容器间通信通常把‘服务名’当作域名来用”比如直接ping nacos。但习惯了纯docker run命令的人会产生疑惑纯 Docker 里根本没有“服务名”概念难道不应该用“容器名”吗如果在 Compose 里自定义了网络且代码里坚持用“容器名”通信行不行一、 纯 Docker 环境下的网络通信在没有 Docker Compose 的情况下纯靠docker run能不能用容器名通信取决于使用了什么网络默认 Bridge 网络不推荐场景直接docker run不加任何网络参数。规则Docker 内置 DNS不支持通过容器名互相解析。只能通过 IP 访问或者使用过时的--link参数。自定义网络推荐场景先docker network create my-net启动时指定--network my-net --name my_nacos。规则在自定义网络中Docker DNS 会自动将容器名Container Name注册为域名。同网络下的其他容器直接访问my_nacos:8848即可通畅。Docker 在自定义网络中内嵌了一个 DNS 服务器固定 IP 为 127.0.0.11当容器去 ping 容器名或服务名时其实都是由这个内置 DNS 拦截并解析成真实容器 IP 的。结论 1在纯 Docker 环境的自定义网络下“容器名”就是域名。二、 Docker Compose 下的“双保险”解析当引入docker-compose.yml后Compose 会在启动时自动创建一个自定义内部网络。在这个网络中Docker 内部 DNS 会提供“双重解析”机制假设docker-compose.yml如下services:nacos-service:#【服务名 Service Name】image:nacos/nacos-servercontainer_name:my_nacos_container#【容器名 Container Name】在这个环境下同一个 Compose 项目中的其他容器想要访问 Nacos方式 A访问nacos-service服务名 ➔通方式 B访问my_nacos_container容器名 ➔也通结论 2在 Compose 语境下既可以用服务名也可以用容器名两者都会被解析到同一个容器 IP 上。三、 既然都能通为什么官方强烈推荐用“服务名”虽然在 Compose 中用自定义的“容器名”通信完全合法但会带来严重的局限性。1. 丧失“水平扩展Scale”能力最核心的区别如果写死容器名容器名在全局必须是唯一的。如果在配置里写死container_name: my_nacos_container那么这个服务永远只能单机运行。一旦执行扩展命令如docker compose up --scale nacos-service3会因为容器重名而报错。如果使用服务名Compose 会自动生成带后缀的不重复容器名如项目名_nacos-service_1/2/3。此时代码里依然请求的是nacos-serviceDocker 内部 DNS 会自动对这 3 个容器进行轮询Round-Robin负载均衡。2. 代码与部署解耦使用服务名代码层面的配置如spring.datasource.urljdbc:mysql://mysql-service:3306不需要关心实际跑起来的容器到底叫什么名字哪怕项目夹杂了目录前缀、随机后缀都不影响通信。四、 例外什么时候更适合用“容器名”既然“服务名”这么好那为什么 Docker Compose 还要保留container_name这个配置项呢在以下特殊场景下指定并使用“容器名”反而更优1. 外部独立容器或脚本的介入假设有一个没有被编排进 Compose 的旧应用单独docker run跑起来的手动将它加入了当前 Compose 的网络中。或者需要编写外部的 Shell 脚本去定期备份数据库。如果没定义容器名Compose 会生成类似mall_nacos-service_1这样带有动态后缀的名字外部脚本很难精准定位。此时明确指定container_name: my_nacos_container外部独立容器通过固定的容器名去通信或执行命令如docker exec my_nacos_container ...会更加直观、稳定且可控。2. 明确的单例中间件如果非常确定某个服务比如本地开发测试用的 MySQL、Redis永远只会跑一个实例绝不存在集群扩展需求。此时指定一个简短的container_name会让终端敲docker ps或查看日志时更加清爽。五、 总结根据不同场景总结最佳实践方案如下场景企业级微服务部署、可能有集群扩展需求做法在 YAML 中不要指定container_name让 Docker 自动生成服务间调用统一只用Service Name。场景个人本地开发、明确永远只跑单节点如单机 MySQL 测试做法可以在 YAML 中指定container_name让docker ps看得更顺眼通信时用容器名或服务名皆可。场景有外部独立的 Docker 容器需要连入 Compose 网络做法对于未被 Compose 管理的旧容器它可能“认不出” Compose 注入的服务别名。此时让它通过明确的自定义容器名来直连更加直观稳定。