hyperf容器连不上redis或mysql主因是默认bridge网络下服务名无法解析,必须创建自定义网络(如hyperf-net)并让所有服务加入该网络,才能通过服务名(如redis)被docker内嵌dns正确解析为容器ip。

Hyperf 项目在 Docker 中连不上 Redis 或 MySQL,八成不是代码或配置写错了,而是容器压根没解析出服务名对应的 IP —— 默认网络下,redis、mysql 这类服务名根本不会自动变成 IP 地址。
docker run 不加 --network 时,服务名解析必然失败
默认使用 bridge 网络的容器,/etc/resolv.conf 里只有宿主机 DNS,且 /etc/hosts 不会自动注入其它容器名。也就是说,你在 hyperf 配置里写 host: redis,PHP 调用 gethostbyname('redis') 会直接返回 127.0.0.1 或抛错,而不是目标 Redis 容器的 IP。
常见错误现象:
-
Connection refused(连到了自己或 localhost) php_network_getaddresses: getaddrinfo failed- Redis 连接超时,但用
172.17.0.x(Docker 网桥 IP)就能通
解决方式只有一条路:别用默认网络。
- 必须显式创建自定义网络:
docker network create hyperf-net - 所有服务(
hyperf、redis、mysql)启动时都加--network hyperf-net - 此时
redis这个名字才能被 Docker 内嵌 DNS(127.0.0.11)正确解析为同网段容器 IP
不要用 --link,它已过时且不兼容自定义网络
--link 是 Docker 1.10 之前的老机制,现在仅在默认 bridge 下有效,且已被官方标记为 deprecated。它不能和 --network 共存,强行混用会导致容器启动失败或 DNS 行为不可预测。
如果你看到文档或脚本里还写 --link redis:redis,请立刻替换为:
- 统一用自定义网络 + 服务名直连
- 或改用
docker-compose.yml的services依赖声明(底层仍是自定义网络)
另外,--link 不支持动态扩容、不支持 IPv6、无法跨节点 —— 在 Hyperf 这种需要多实例或未来上 Swarm/K8s 的场景下,它只会拖后腿。
docker-compose.yml 中 DNS 配置优先级很低,一般不用动
除非你遇到内网域名(如 auth.internal)解析失败,否则不需要在 docker-compose.yml 里写 dns: 字段。Docker 自定义网络自带的内嵌 DNS(127.0.0.11)已经能处理同网容器名解析,公网域名会自动转发给上游 DNS(比如你 daemon.json 里配的 114.114.114.114)。
真正要检查的是:
- 确认
hyperf和redis在docker-compose.yml中属于同一networks段,且没被network_mode: host覆盖 - 避免在 service 下误加
dns_search: .这类空搜索域,它可能干扰短名称解析 - 如果用了
extra_hosts,注意它会写死/etc/hosts,覆盖 DNS 解析结果
Hyperf 启动前加 DNS 调试,比看日志快十倍
别等请求进来再抓包。在 bin/hyperf.php 最开头插入几行,验证 DNS 是否就绪:
$start = microtime(true);
$ip = gethostbyname('redis');
$time = microtime(true) - $start;
var_dump($ip, $time); // 如果 $ip === 'redis' 或 $time > 1,说明 DNS 没生效
这个检查比翻 docker logs 或改 resolv.conf 直观得多。只要 $ip 是真实 IP(如 172.20.0.3),且 $time ,后续 Redis 连接基本不会卡在 DNS 上。
容易被忽略的一点是:Hyperf 的 redis 组件在连接池重建时仍会反复调用 gethostbyname —— 所以 DNS 必须稳定,不能靠首次连接缓存撑过整个生命周期。











