kubernetes中mysql连接失败主因是环境配置与驱动行为失配:secret/configmap须正确注入连接参数,go应用需用setconnmaxlifetime等调优连接池,并确保mysql服务启用mysql_native_password插件且监听0.0.0.0。

能直接部署,但连接 MySQL 的关键不在 Deployment 本身,而在环境注入方式、连接字符串构造和驱动行为控制——稍有偏差就会出现 connection refused、dial tcp: lookup mysql-service: no such host 或连接池耗尽。
Deployment 中如何安全注入 MySQL 连接参数
不能把数据库地址、密码硬编码进镜像或 YAML;必须通过 Secret 和 ConfigMap 分离敏感与非敏感配置。Kubernetes 不会自动解码 base64,所以 Secret 的 data 字段值必须是 base64 编码后的字符串(比如 password 要先 echo -n "mypass" | base64)。
常见错误是把 value 和 valueFrom 混用:一个字段只能选其一。例如下面写法是错的:
env:
- name: MYSQL_USER
value: "user" # ❌ 与下面的 valueFrom 冲突
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
正确做法是只用 valueFrom:
-
MYSQL_HOST和MYSQL_PORT推荐走ConfigMap,方便多环境切换 -
MYSQL_USER、MYSQL_PASSWORD必须走Secret,且 Secret 需提前创建(kubectl create secret generic mysql-secret --from-literal=username=user --from-literal=password=xxx -n your-ns) - Go 应用里读取时,别拼接裸字符串,用
url.URL或mysql.ParseDSN构造连接串,避免特殊字符(如密码含@或/)导致解析失败
Go 代码里怎么写才不崩在 Kubernetes 环境
本地跑通的连接逻辑,在 K8s 里常因 DNS 解析延迟、Pod 启动顺序、连接池未调优而失败。go-sql-driver/mysql 支持 context,但默认不启用超时,必须手动设。
关键配置缺一不可:
-
db.SetConnMaxLifetime(3 * time.Minute):防止连接被 Kubernetes Service 的 iptables 规则或云厂商 LB 断连后长期滞留 -
db.SetMaxOpenConns(20):容器内存有限,盲目设高(如 100+)易触发 OOMKilled -
db.SetMaxIdleConns(10):配合SetConnMaxIdleTime控制空闲连接回收,避免冷启动后大量重连 - 所有
db.Query、db.Exec都应带context.WithTimeout,否则 Pod 终止时可能卡住 shutdown 流程
示例片段:
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&loc=UTC",
os.Getenv("MYSQL_USER"),
os.Getenv("MYSQL_PASSWORD"),
os.Getenv("MYSQL_HOST"),
os.Getenv("MYSQL_PORT"),
os.Getenv("MYSQL_DB"))
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
db.SetConnMaxLifetime(3 * time.Minute)
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
db.SetConnMaxIdleTime(1 * time.Minute)
MySQL 服务本身要怎么暴露才让 Go 应用连得上
不是所有 MySQL 部署方式都适合被 Go 应用直连。用 ClusterIP Service 是最常用也最安全的选择;用 NodePort 或 LoadBalancer 反而增加攻击面且无必要。
注意三个硬性前提:
- MySQL Pod 必须打上
app: mysql这类 label,否则 Service 的selector匹配不到 - MySQL 容器的
containerPort必须是3306,且 Service 的targetPort要与之对应(默认同名端口可省略,但显式写更稳) - MySQL 启动命令需加
--default-authentication-plugin=mysql_native_password,否则新版 MySQL 8.0+ 默认用caching_sha2_password,go-sql-driver/mysql 旧版不兼容
Service 示例(务必和 MySQL Pod 在同一 namespace):
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
调试连不上时先看哪几个地方
90% 的连接失败不是代码问题,而是网络或配置链路断在某环。按顺序快速验证:
- 进 Go Pod 执行
nslookup mysql或telnet mysql 3306:不通说明 Service 或 DNS 有问题(检查kubectl get svc -n xxx、kubectl get endpoints mysql -n xxx) - 查 Go 应用日志是否输出
invalid DSN或access denied:前者是环境变量没读到,后者是 Secret 挂载失败或密码错 - 查 MySQL Pod 日志:
kubectl logs -l app=mysql,看是否有Access denied for user或Can't connect to local MySQL server - 确认 MySQL 是否监听
0.0.0.0:3306而非127.0.0.1:3306(Dockerfile 中mysqld --bind-address=0.0.0.0)
最易被忽略的是:MySQL Pod 启动慢于 Go Pod,而 Go 应用没做连接重试或就绪探针,导致启动即报错退出。务必给 Go Deployment 加 readinessProbe,并指向一个真校验 DB 连通性的 endpoint(比如 /health?check=db),而不是只返回 HTTP 200 的假探针。











