Kubernetes 外部数据库暴露至集群内的方法
- 2026-07-11 23:51:00
- 丁国栋
- 原创 17
本文介绍如何将 Kubernetes 集群外部的数据库(如 dev.thedf.cc:3306)引入集群服务发现体系,使所有命名空间的 Pod 都能通过集群内部 DNS 名称访问。针对域名型外部数据库,推荐使用 ExternalName 类型的 Service,通过 DNS CNAME 映射实现零代理、零 IP 硬编码的访问方式。
场景描述
外部 MySQL 数据库位于 dev.thedf.cc:3306(域名形式),需要让集群内所有命名空间的 Pod 都能通过集群内部 DNS 名称访问它。
方案一:公共命名空间 + FQDN 访问
创建一个专门存放外部依赖的公共命名空间(如 infra),在其中创建 ExternalName 类型的 Service:
# external-mysql.yaml
apiVersion: v1
kind: Namespace
metadata:
name: infra
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: infra
labels:
app: mysql
managed-by: external
spec:
type: ExternalName
externalName: dev.thedf.cc
ports:
- port: 3306
targetPort: 3306
protocol: TCP
应用配置:
kubectl apply -f external-mysql.yaml
集群内任何命名空间的 Pod 都可以使用以下 DNS 名称连接数据库:
mysql.infra.svc.cluster.local:3306
连接参数配置如下:
| 参数 | 值 |
|---|---|
| Host | mysql.infra.svc.cluster.local |
| Port | 3306 |
| User | root |
| Password | zentao |
| DB名 | 你的数据库名称 |
验证 DNS 解析是否正常:
kubectl run dns-test --rm -it --image=busybox --restart=Never \
-- nslookup mysql.infra.svc.cluster.local
验证连通性:
kubectl run net-test --rm -it --image=busybox --restart=Never -- \
sh -c 'nc -zv mysql.infra.svc.cluster.local 3306'
优点:只维护一份资源;清晰表达"这是外部依赖";所有团队共用同一个 FQDN
注意:短名称
mysql只在同命名空间内解析,跨命名空间访问必须使用完整的 FQDN
方案二:每个命名空间使用短名称访问
如果需要在每个命名空间直接使用 mysql:3306(无需 FQDN),需要在每个命名空间都创建同名的 Service。可以通过脚本批量创建:
# 把所有非系统命名空间都创建一份
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}' | tr ' ' '\n' | grep -v '^kube-\|apiserver\|kube-public'); do
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: ${ns}
spec:
type: ExternalName
externalName: dev.thedf.cc
ports:
- port: 3306
protocol: TCP
EOF
done
生产环境中,推荐使用 Helm、ArgoCD 或 Terraform 将此配置模板化,确保新增 namespace 时自动同步,避免手动维护。
方案三:Service + Endpoints(IP 直连模式)
如果外部数据库可以解析到固定 IP,且需要走 kube-proxy 的 ClusterIP 路径以获得更好的可观测性和网络策略控制,可以使用无 selector 的 Service 配合 Endpoints:
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: infra
spec:
type: ClusterIP
ports:
- port: 3306
targetPort: 3306
protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
name: mysql
namespace: infra
subsets:
- addresses:
- ip: 1.2.3.4 # 填 dev.thedf.cc 解析出来的实际 IP
ports:
- port: 3306
查询 dev.thedf.cc 的 IP:
nslookup dev.thedf.cc
缺点:Endpoints 不支持填写域名,只能填写 IP。如果外部数据库 IP 发生变化(云数据库常见场景),需要额外的控制器或脚本来定时刷新 Endpoints,维护成本较高。因此,域名场景优先选择
ExternalName方案。
安全建议:密码管理
数据库密码不应直接写入 YAML 文件,应使用 Secret 存储并通过环境变量注入:
apiVersion: v1
kind: Secret
metadata:
name: mysql-external-secret
namespace: infra
type: Opaque
stringData:
host: mysql.infra.svc.cluster.local
port: "3306"
user: root
password: zentao
应用侧通过环境变量引用:
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: mysql-external-secret
key: host
- name: DB_PORT
valueFrom:
secretKeyRef:
name: mysql-external-secret
key: port
- name: DB_USER
valueFrom:
secretKeyRef:
name: mysql-external-secret
key: user
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-external-secret
key: password
方案对比
| 特性 | ExternalName | Service + Endpoints |
|---|---|---|
| 目标为域名 | 原生支持 | 需先解析 IP,IP 变更需更新 |
| 是否走 kube-proxy | 否(DNS CNAME 直连) | 是(ClusterIP 代理) |
| 端口映射 | 不支持 | 支持 |
| 维护成本 | 低 | 较高 |
| 网络策略可触达性 | 需按外部 IP 配置 egress 策略 | 可对 ClusterIP 配置策略 |
对于域名型外部数据库(如 dev.thedf.cc:3306),推荐方案一:在公共 namespace 创建 ExternalName Service,全集群通过 mysql.infra.svc.cluster.local:3306 访问。该方案配置简单、维护成本低,符合 Kubernetes 的设计理念。