90%的K8s中ThinkPHP连不上数据库是因服务发现、环境变量或配置位置错误:hostname必须用Service DNS名(如mysql),不能写localhost或127.0.0.1;.env需在根目录且APP_DEBUG=true;Db::connect()动态传入环境变量更可靠,避免硬编码。

ThinkPHP 应用在 Kubernetes 中连不上数据库,90% 不是代码问题,而是服务发现没对上、环境变量没生效、或配置没进对的地方。直接改 database.php 文件硬编码 IP 或域名,在 K8s 里必然失效。
为什么 config/database.php 里的 hostname 写 localhost 或 127.0.0.1 会失败
Kubernetes Pod 是隔离网络命名空间,localhost 指的是当前 PHP 容器自身,不是 MySQL 容器。即使两个 Pod 在同一节点,也不能直连 IP —— 必须通过 Service 的 ClusterIP 或 DNS 名访问。
- MySQL Pod 启动后,需先创建对应
Service(如名为mysql),K8s 才会生成内部 DNS 记录mysql.default.svc.cluster.local - ThinkPHP 的
hostname配置项必须填这个 DNS 名(例如mysql),不能填 Pod IP、Node IP 或localhost - 若使用
env('database.hostname'),确保.env文件中该值被正确注入,且未被 config/database.php 中的静态值覆盖 - 验证方式:进入 PHP 容器执行
nslookup mysql,能解析出 ClusterIP 才算服务发现就绪
Db::connect() 动态连接比 config/database.php 更适合 K8s 场景
硬编码在 config/database.php 里的配置无法随环境自动切换,而 K8s 的 ConfigMap/Secret + 运行时动态连接更可控、更安全。
- 在控制器或服务类中调用
Db::connect(),传入从环境变量读取的配置数组,例如:$db = Db::connect([ 'type' => 'mysql', 'hostname' => $_ENV['DB_HOST'] ?? 'mysql', 'database' => $_ENV['DB_NAME'] ?? 'blog', 'username' => $_ENV['DB_USER'] ?? 'root', 'password' => $_ENV['DB_PASS'] ?? '', 'hostport' => $_ENV['DB_PORT'] ?? '3306', 'charset' => 'utf8mb4', ]); - 这样可把数据库地址、账号密码全交给 K8s 的
ConfigMap和Secret管理,避免敏感信息写死在代码或镜像中 - 注意:不要在
Db::connect()数组里漏掉'type',否则 ThinkPHP 5.x 会 fallback 到默认驱动但不报错,6.x 则可能抛出异常 - 若需复用连接,建议封装成单例或依赖注入,避免每次请求都新建 PDO 实例
Env 变量注入到 ThinkPHP 的三个关键检查点
很多团队以为写了 env: DB_HOST: mysql 就万事大吉,其实 K8s 注入环境变量到容器,和 ThinkPHP 能否读到,中间有三层过滤。
立即学习“PHP免费学习笔记(深入)”;
- 确认 Deployment YAML 中
env字段已正确定义,且valueFrom.secretKeyRef或configMapKeyRef的key名与 PHP 里$_ENV读取名完全一致(区分大小写) - 确认容器内 PHP 已启用
variables_order = "EGPCS"(默认开启),否则$_ENV为空;可通过phpinfo()页面检查Environment Variables区域是否列出你的变量 - 确认 ThinkPHP 没有在
config/database.php中用env('database.hostname', '127.0.0.1')这种写法把环境变量兜底成固定值——应改为env('DB_HOST', 'mysql'),并确保.env文件未覆盖该变量
SSL 连接与连接池在 K8s 中的真实影响
生产环境启用了 MySQL SSL,或想用连接池提升并发能力?这两项在 K8s 下不是“开了就行”,得看实际链路。
- MySQL Service 类型为
ClusterIP时,SSL 连接无意义:流量全程在集群内,不经过公网,加 SSL 只增开销不增安全;只有 Service 类型为LoadBalancer或走 Ingress 外部访问时,才需考虑 TLS 终止位置 - ThinkPHP 的连接池(
pool配置)依赖 Swoole 或协程环境,在标准 FPM 模式下无效;K8s 中若用 PHP-FPM,连接复用靠的是 FPM 的pm.max_children和 MySQL 的wait_timeout,不是框架池 - 真正有效的连接管理,是在 K8s 层面做:用
readinessProbe检查数据库连通性(例如 curl -f http://localhost/health?db=1),避免流量打到尚未连上 DB 的 Pod
最常被忽略的一点:K8s 中 MySQL Pod 重启后,Service 的 ClusterIP 不变,但 DNS 缓存可能让 PHP 容器短暂解析失败。别只依赖 nslookup 一次成功,要在应用层加重试逻辑或用 mysql.default.svc.cluster.local:3306 这种完整域名减少缓存歧义。



















