Docker 容器名 ping 不通

镜像载入了,.env 里的连接地址也改了,和面板里的数据库容器名字一样。

docker compose up -d 跑完,三个容器全绿,docker ps 看了也很正常。

然后我去看健康接口,返回的 mode 是 mock,不是 db。

翻开日志。

复制代码
failed to connect to `host=app-postgres user=... database=...`:
dial tcp: lookup app-postgres on 127.0.0.11:53: no such host

容器名 ping 不通。

这个名字不是瞎编的。docker ps --format '{``{.Names}}' 里它就叫这个。同一台机器、同一个 Docker、甚至就在同一块硬盘上,为什么互相找不着。我一开始没想明白。

先把术语说清楚。Docker 的 bridge 网络,可以理解成 Docker 在宿主机内部虚拟出来的一台交换机。接进同一台交换机的容器之间,能用容器名互相访问。因为 Docker 在这张网络里内置了一个 DNS 服务,容器名就是域名,解析出来的 IP 是容器在这张网里的地址,通常是 172.x.x.x 这样的私网段。

关键的一句是,不同 bridge 网络之间默认不转发。就像两台交换机之间没连线,你喊破喉咙对面也听不见。

麻烦在于,两套编排系统都会自己建网络。

面板应用商店给托管的 PG 容器建了一张 1panel-network。Redis 容器也在 1panel-network 里,同样是面板建的。我们的三个业务容器 app-gin、app-admin、app-h5 在项目自己的网络上,不声明时默认叫 <项目目录名>_default,我们这份编排里显式起过名。

两边都没做错任何事,结果就是不通。

这不是面板的 bug,也不是 compose 的 bug。它是两套编排各自为政的必然结果。只要你决定把数据层从 compose 里拿出去交给面板托管,这一层就一定会存在。它是架构决策的副产品,不是一次配置失误。

明白这一点,排查方向就定了。不去改 DSN,不去查密码,不去重启容器。去看这两个容器到底挂在哪张网上。

三条命令能把网络这层看穿。

先看数据容器在哪张网。

bash 复制代码
for c in app-postgres app-redis; do
  printf "%-14s → " "$c"
  docker inspect "$c" --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
done
# 期望两行都输出:1panel-network

.NetworkSettings.Networks 是一个 map,key 就是网络名。这条命令把容器接入的所有网络名一次性打出来。空输出,或者输出里没有 1panel-network,就是第一现场。

再看业务容器在哪张网。同一条命令换成 app-gin。如果输出里只有项目自己的网络、没有 1panel-network,那就确诊了,两边各在各的网上。

第三条是决定性的验证,它用临时容器完全复刻真实的连接场景。它好在可以在业务服务还不存在的时候执行。

bash 复制代码
docker run --rm --network 1panel-network postgres:18.6-alpine \
  psql "postgresql://<用户>:<密码>@app-postgres:5432/<库名>?sslmode=disable" \
  -c "\conninfo" -c "select version();"

拆开看。--rm 表示跑完就删,不留垃圾。--network 1panel-network 让这个临时容器以和真实业务容器完全相同的网络身份入场。镜像直接借用数据库官方镜像,省得自己造工具。

它一次压缩了四件事。网络通不通、容器名能不能解析、账号密码对不对、服务端放不放行。

回显就是判据。

打印 You are connected to database "..." as user "...",三层全通。打印 could not translate host name "app-postgres",病在网络层,容器不在一个网络里,回到前面两条命令。打印 no pg_hba.conf entry for host "172.x.x.x",病在授权层,网络通了但服务端不放行。打印 password authentication failed,病在认证层,账号密码不符。

这次迁移里的实测输出是,容器名解析到 172.18.0.4,Password Used=true,版本 18.6。

注意这个时间点。当时服务还处于中断态,业务容器一个都没起。这条命令先给了绿灯,后面才敢导数据,才敢起服务。

这一手的价值一句话说完。排障顺序里,先证明能连上,必须排在先把服务起来前面。服务起不来你会立刻知道,连不上你可能要查半天。

compose 里要表达的是,这个网络已经存在了,别新建,直接加入。

yaml 复制代码
# docker-compose.server.yml(服务器版编排)
services:
  gin:
    image: app-gin:latest
    networks:
      - 1panel-network
networks:
  1panel-network:
    external: true        # 复用面板已创建的网络,不要重复创建

这里有个我写之前最担心的点。1panel-network 这个名字是以数字 1 开头的。YAML 里裸写 1panel-network:,会不会被解析成别的东西。

不要靠推理,跑一遍 docker compose config。它打印的是 compose 文件被解析后的最终结果,是校验编排最权威的手段。

bash 复制代码
$ docker compose -f docker-compose.server.yml config
name: app
services:
  gin:
    image: app-gin:latest
    ports: [{host_ip: 127.0.0.1, target: 8080, published: "18080"}]
    networks: {1panel-network: null}
    deploy: {resources: {limits: {memory: "402653184"}}}     # 384M
networks:
  1panel-network:
    name: 1panel-network
    external: true
volumes:
  uploads: {name: app_uploads}

一段输出证明了三件事。

以数字开头的网络名被正确解析了。networks 段原样保留了 name: 1panel-network 和 external: true,key 没有被当成数字或布尔值吃掉。

内存上限也生效了。402653184 字节等于 384M。三个业务容器分别是 384M、512M、64M,合计 960M。同机还跑着 6 个别人的线上站点,这个上限是给领导的交代。内存无上限的容器触发 OOM 时,被 kill 的未必是自己。

端口只绑了 127.0.0.1。host_ip: 127.0.0.1 意味着公网天然不可达,比绑 0.0.0.0 再靠防火墙收口更彻底。

这三条都是我以为会生效的事,而 config 把它们变成了我看见它生效了。

网络打通之后还有第二个选择。DSN 里写容器名 app-postgres:5432,还是走宿主机映射端口加 host.docker.internal。我们选了前者,三条理由。

端口映射变动不影响容器名直连,容器内 5432 恒定,映射一改就断的是走宿主机端口那条路。流量路径上,容器名直连全程在 Docker 网络内,走宿主机端口要绕出容器、经宿主机再回来。数据库的宿主机端口,容器名直连那条路可以一个都不开,走宿主机端口那条路必须开。

第三条是安全上的净收益。数据库完全不映射宿主机端口,公网上就没有任何入口。

备选方案在 Linux 上还要额外写 extra_hosts: - "host.docker.internal:host-gateway"。多一处配置,就多一处会忘的地方。

我把这些固化成一份清单。

数据容器的网络归属,用 docker inspect ... .NetworkSettings.Networks 看,输出要含 1panel-network。业务容器同理,要同时含 1panel-network,不是只有项目自己的网。编排声明有没有被正确解析,跑 docker compose -f xxx.yml config,networks 段要出现 external: true。内存和端口约束生效没有,同一份输出里要出现 memory 和 host_ip: 127.0.0.1。还剩端到端连通,跑 docker run --rm --network 1panel-network <镜像> psql ...。要打印 You are connected to database。

这五项里,端到端连通是唯一不可替代的。前四项都在证明配置写对了,只有它在证明真的连得上。

复盘几句。容器名解析靠的是 Docker 内置 DNS,前提是同一个 bridge 网络,跟 hosts 文件、公网 DNS 都没关系。面板一套网、compose 一套网,这是默认结果,不是故障,谁都没配错,就是不互通。external: true 是唯一的那座桥,写完必须用 docker compose config 亲眼验一次。docker run --rm --network 起临时容器,是启动业务之前最便宜的一次实战演练。

Docker 网络不是 ping 通就行。ping 通只是结果,同一个网络才是原因。而原因这一层,从来不会自动帮你搭好。

相关推荐
秋名山小白1 小时前
UI自动化-Airtest框架落地实战
运维·ui·自动化
a5986521 小时前
Docker多开云手机,六边云独立端口隔离访问实战
android·运维·docker·容器·智能手机·云手机
戴西软件1 小时前
企业如何搭建自己的AI智能体?戴西智能体管理与戴西SKILL管理实战解析
运维·jvm·人工智能·自动化·rpa
wei091302 小时前
2026 企业 AI 办公工具选型指南:搭建 AI 与现有办公流程融合体系
大数据·运维·人工智能
ggaofeng2 小时前
如何配置snat(让内网访问外网)和dnat(将外网请求转发到内网服务器)
运维·服务器·snat·dnat·局域网访问外网·外网访问局域网
yyuuuzz2 小时前
云服务器搭建个人博客记录:一次访问超时的复盘
运维·服务器·数据库
吴声子夜歌2 小时前
Docker入门与实战——安全防护与配置
安全·docker
自变量yjs2 小时前
Linux --- 线程控制
linux·运维·服务器
暖核2 小时前
kubeadm 搭建 K8s v1.30 集群:从系统模板到 Calico 网络全流程
网络·容器·kubernetes