Kubernetes 外部数据库暴露至集群内的方法

2026-07-11 23:51:00
丁国栋
原创 17
摘要:本文介绍如何将 Kubernetes 集群外部的数据库(如 `dev.thedf.cc:3306`)引入集群服务发现体系,使所有命名空间的 Pod 都能通过集群内部 DNS 名称访问。针对域名型外部数据库,推荐使用 `ExternalName` 类型的 Service,通过 DNS CNAME 映射实现零代理、零 IP 硬编码的访问方式。

本文介绍如何将 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 的设计理念。

发表评论
博客分类