Kubernetes基于名称的TCP路由实现:多数据库同端口暴露方案咨询
问题描述
我想在Kubernetes中创建多个数据库,通过类似HTTP应用的基于名称虚拟主机的方式暴露,但数据库使用TCP协议。目前用ingress-nginx配置TCP负载均衡时,一个端口只能对应一个数据库,示例配置如下:
kubectl get cm -n ingress-nginx ingress-nginx-tcp -oyaml apiVersion: v1 data: "1521": oracle/oracle-19c-ee-svc:1521 kind: ConfigMap metadata: annotations: meta.helm.sh/release-name: ingress-nginx meta.helm.sh/release-namespace: ingress-nginx creationTimestamp: "2023-03-27T11:52:08Z" labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.7.0 helm.sh/chart: ingress-nginx-4.6.0 name: ingress-nginx-tcp namespace: ingress-nginx resourceVersion: "3997675" uid: 7c477751-1ee1-462a-a4d6-ad78b003abf7
我期望实现的效果是:
db1.example.com:1521 -> 指向服务db1 db2.example.com:1521 -> 指向服务db2
其中example.com解析到负载均衡器IP,db1和db2是对应不同数据库的Kubernetes Service。想知道有没有可行方案,包括使用其他Ingress控制器或Gateway API的可能。
可行方案
1. 使用支持SNI的TCP路由的Ingress控制器(Traefik)
Traefik支持基于SNI(Server Name Indication)的TCP路由——TCP本身没有Host头,但SNI是TLS握手阶段传递服务器名称的扩展,只要数据库连接启用TLS,就能利用它实现基于名称的路由。
配置步骤:
- 确保Traefik已启用TCP路由功能(最新版本默认支持)。
- 创建TCP路由的IngressRoute资源:
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRouteTCP metadata: name: db-tcp-routes spec: entryPoints: - db-tcp # 对应Traefik配置的TCP入口点(绑定1521端口) routes: - match: HostSNI(`db1.example.com`) services: - name: db1-svc port: 1521 - match: HostSNI(`db2.example.com`) services: - name: db2-svc port: 1521 tls: secretName: db-tls-secret # 包含覆盖所有数据库域名的证书和密钥的Secret
- 配置Traefik入口点,将1521端口映射到
db-tcp入口点:
# Traefik ConfigMap示例 apiVersion: v1 kind: ConfigMap metadata: name: traefik-config data: traefik.yaml: | entryPoints: db-tcp: address: ":1521"
2. 使用Kubernetes Gateway API(TCPRoute + SNI)
Gateway API的TCPRoute支持基于SNI的路由,需要搭配支持Gateway API的控制器(如Traefik、NGINX Gateway Fabric)。
配置步骤:
- 安装Gateway API CRDs及对应控制器(以NGINX Gateway Fabric为例)。
- 创建Gateway资源,暴露1521端口:
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: db-gateway spec: gatewayClassName: nginx # 对应控制器的gatewayClassName listeners: - name: db-tcp port: 1521 protocol: TLS tls: mode: Terminate certificateRefs: - name: db-tls-secret # 覆盖所有数据库域名的证书
- 创建TCPRoute资源,基于SNI路由到不同服务:
apiVersion: gateway.networking.k8s.io/v1alpha2 kind: TCPRoute metadata: name: db1-tcp-route spec: parentRefs: - name: db-gateway sectionName: db-tcp rules: - matches: - sniHosts: ["db1.example.com"] backendRefs: - name: db1-svc port: 1521 --- apiVersion: gateway.networking.k8s.io/v1alpha2 kind: TCPRoute metadata: name: db2-tcp-route spec: parentRefs: - name: db-gateway sectionName: db-tcp rules: - matches: - sniHosts: ["db2.example.com"] backendRefs: - name: db2-svc port: 1521
3. 使用HAProxy Ingress Controller
HAProxy Ingress同样支持基于SNI的TCP路由,配置方式如下:
配置示例:
- 创建ConfigMap启用TCP端口和SNI路由:
apiVersion: v1 kind: ConfigMap metadata: name: haproxy-ingress namespace: ingress-nginx data: tcp-services: | 1521: default/placeholder-svc:1521 # 占位用,实际路由通过SNI配置覆盖 ssl-passthrough: "true" # 若需将TLS直接透传到数据库,而非在Ingress层终止
- 创建Ingress资源,通过注解配置SNI路由:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: db-ingress annotations: haproxy.org/tcp-sni-routes: | db1.example.com:1521 -> default/db1-svc:1521 db2.example.com:1521 -> default/db2-svc:1521 spec: tls: - hosts: - db1.example.com - db2.example.com secretName: db-tls-secret
关键注意事项
- 所有基于名称的TCP路由都依赖TLS + SNI:纯TCP没有传递主机名的机制,只有在TLS握手阶段才能通过SNI获取客户端请求的域名。如果数据库不支持TLS连接,该方案无法实现,只能通过不同端口暴露不同数据库。
- 证书需覆盖所有用于路由的数据库域名(可使用通配符证书
*.example.com)。
内容的提问来源于stack exchange,提问作者Miklós
相关产品推荐
相关产品推荐

