Traefik Kubernetes CRD 指南:使用 ServersTransportTCP 精细控制 Traefik 与 TCP 后端服务的连接
ServersTransportTCP 是 Traefik 在 Kubernetes 场景下以 CRD(Custom Resource Definition)方式提供的 TCP ServersTransport 实现,用于精细配置 Traefik 与 TCP 后端(backend)之间建立连接时的拨号超时、keep-alive 探测、PROXY Protocol、TLS/mTLS 校验以及连接终止延迟等行为。通过本文你将掌握:如何创建并引用 ServersTransportTCP 资源、如何跨命名空间或跨 Provider 引用它、每个配置字段(含 proxyProtocol、terminationDelay、tls.rootCAs、peerCertSANs、spiffe 等)的真实语义与取值范围,以及它从 CRD 到 Traefik 内部动态配置的转换原理。
前置条件:先注册 Traefik CRD
ServersTransportTCP 属于 traefik.io/v1alpha1 API 组。在创建任何 ServersTransportTCP 对象之前,需要先将 Traefik Kubernetes CRDs 应用到你的集群,从而注册 ServersTransportTCP kind 以及 IngressRouteTCP、Middleware、IngressRouteUDP 等 Traefik 专有资源。
官方发行时通过 kubectl apply -f 安装一份聚合的 CRD 清单;在本仓库中可参考同源清单 integration/fixtures/k8s/01-traefik-crd.yml(其中定义了 traefik.io/v1alpha1 下的全部资源,包括 ServersTransportTCP)。CRD 的类型定义本身位于源码 pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go,其类型注册见同目录的 register.go。
CRD 注册完成后,即可在任意命名空间内创建与 ServersTransportTCP kind 对应的资源对象,随后由运行在集群中的 Traefik 通过 CRD Provider(kubernetescrd)监听并转换为实际路由配置。
注意:
ServersTransportTCP与 HTTP 场景下的ServersTransport是两个不同的 CRD。本页描述的是 TCP 场景的版本;其配置字段与 TCP 场景的 ServersTransport 参考文档 一一对应,只是把文件路径/数据形式的证书换成了 Kubernetes Secret/ConfigMap 引用。
未指定时的默认行为:default@internal
如果某个 TCP 服务没有显式指定 serversTransport,Traefik 会使用名为 default@internal 的内置默认 transport。default@internal 是在安装配置(install configuration,旧称静态配置 static configuration)中构建出来的。
源码中的对应注释也印证了这一语义(serverstransporttcp.go):If no tcpServersTransport is specified, a default one named default@internal will be used. 换句话说,默认 TCP transport 的各项超时与 TLS 参数由 Traefik 进程自身的安装配置决定,而不是由某个 CRD 对象决定。
命名空间与 Provider 引用规则
ServersTransportTCP 是一种可被引用的资源,引用它的通常是 IngressRouteTCP 中某个 service 的 serversTransport 字段。默认情况下存在三条约束:
- 同命名空间引用:
ServersTransportTCPCRD 必须与被引用的 TCP 服务定义在同一个 Kubernetes 命名空间中,引用时直接写资源名(如mytransport)。 - 跨命名空间引用:若需要引用其他命名空间的
ServersTransportTCP,值必须写成namespace-name@kubernetescrd形式(例如cross-ns@kubernetescrd),并且必须在 CRD Provider 上启用allowCrossNamespace选项,否则该引用会被拒绝。 - 跨 Provider 引用:如果
ServersTransportTCP是由其他 Provider 提供的(例如文件 Provider、Consul 等),则应使用跨 Provider 的name@provider形式。
上述解析逻辑在源码中有完整实现:kubernetes_tcp.go 中的 makeTCPServersTransportKey 会检查引用名是否包含命名空间分隔符 @:当存在跨命名空间引用但 AllowCrossNamespace 未开启时返回错误 namespace-name@kubernetescrd format is not allowed when crossnamespace is disallowed;合法情况下则把 namespace/name 规范化成 Traefik 内部的 key。
仓库中的 fixture 也覆盖了这三种引用形态(tcp/with_servers_transport.yml、tcp/with_servers_transport_cross_namespace.yml),例如跨命名空间示例使用 serversTransport: cross-ns-st-cross-ns@kubernetescrd 引用位于 cross-ns 命名空间的资源。
配置示例
下面是一个最小的可运行示例:它创建一个名为 mytransport 的 ServersTransportTCP,启用 PROXY Protocol 版本 2、把连接终止延迟设为 100ms,并在与后端建连做 TLS 握手时使用 SNI example.org 且跳过证书校验。
apiVersion: traefik.io/v1alpha1
kind: ServersTransportTCP
metadata:
name: mytransport
namespace: default
spec:
proxyProtocol:
version: 2
terminationDelay: 100ms
tls:
serverName: example.org
insecureSkipVerify: true
把上面内容保存为 servers-transport.yaml 后执行:
kubectl apply -f servers-transport.yaml
如何把 ServersTransportTCP 绑定到 TCP 服务
ServersTransportTCP 本身并不直接承载路由,它必须由 IngressRouteTCP 中的服务引用才生效。引用的字段是 TCP 服务的 serversTransport,在 ingressroutetcp.go 中定义为 ServersTransport string,注释明确说明它指向 ServersTransportTCP 资源名:
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: my-tcp-route
namespace: default
spec:
entryPoints:
- tcpservice
routes:
- match: HostSNI(`tcp.example.org`)
services:
- name: my-backend
port: 9000
serversTransport: mytransport
绑定后,Traefik 在转发到 my-backend:9000 时会依据 mytransport 的 spec 建立连接。关于 IngressRouteTCP 中 terminationDelay 与 proxyProtocol 字段,ingressroutetcp.go 中的注释指出它们已被标记为 Deprecated,未来 API 版本将不再支持,官方建议统一改用 ServersTransport 来配置 TerminationDelay 与 ProxyProtocol —— 这正是使用 ServersTransportTCP 的价值所在。
配置选项总表
以下是 ServersTransportTCP 支持的完整字段说明,与官方参考文档一致:
| Field | Description | Default | Required |
|---|---|---|---|
dialTimeout |
等待与后端服务器建立连接的时间上限;如果为零,表示不存在超时。 | 30s | No |
dialKeepAlive |
活跃网络连接的 keep-alive 探测间隔。设为 0 时使用默认值(当前为 15 秒,前提是协议与操作系统支持);不支持 keep-alive 的协议/操作系统会忽略该字段;设为负数则关闭 keep-alive 探测。 | 15s | No |
proxyProtocol |
定义 PROXY Protocol 配置。空的 proxyProtocol 段即启用 PROXY Protocol 版本 2。 |
– | No |
proxyProtocol.version |
Traefik 在 TCP 服务上支持 PROXY Protocol 版本 1 与 2。 | – | No |
terminationDelay |
在某一端对等方关闭其写能力(发送 FIN)后,代理等待多久才完全终止连接。 | 100ms | No |
tls.serverName |
用于连接服务器的 ServerName(SNI)。 | "" | No |
tls.insecureSkipVerify |
是否校验服务器的证书链与主机名。 | false | No |
tls.peerCertSANs |
对端证书校验时用于匹配的 SANs(Subject Alternative Names)列表。 | [] | No |
tls.peerCertSANs[].type |
SAN 类型,取值为 URI 或 DNSName。 |
"" | No |
tls.peerCertSANs[].value |
用于匹配对端证书 SAN 的值。 | "" | No |
tls.rootCAs |
用于校验服务器证书的 CA 证书 Secret 或 ConfigMap 列表。每一项可通过 secret 或 configMap 按名称引用;被引用资源必须在 tls.ca 或 ca.crt key 下包含证书。 |
– | No |
tls.rootCAsSecrets |
已废弃:请改用 tls.rootCAs。 用于校验服务器证书的根 CA 集合;CA Secret 必须在 tls.ca 或 ca.crt key 下包含 base64 编码的证书。 |
"" | No |
tls.certificatesSecrets |
用于 mTLS 向服务器出示的客户端证书。 | "" | No |
spiffe |
配置 SPIFFE 相关选项。 | "" | No |
spiffe.ids |
允许的 SPIFFE ID 列表,优先级高于 SPIFFE trustDomain。 |
"" | No |
spiffe.trustDomain |
允许的 SPIFFE trust domain。 | "" | No |
关键字段深入解析
terminationDelay:半开连接防护
作为客户端与服务器之间的代理,可能出现某一侧(例如客户端侧)主动关闭自身写能力(即发出 FIN 包)的情况。此时代理需要把这一意图传播给另一侧,于是也会在与另一侧(后端侧)的连接上执行同样的 FIN。
但如果另一侧出于实现缺陷或恶意目的始终不照做,连接就会长期停留在**半开(half-open)**状态,白白占用资源。为此,代理一旦进入该终止序列,就会对两侧连接的完全终止设置一个 deadline——terminationDelay 控制的正是这个 deadline。默认值为 100ms。
需要注意其取值的边界语义:
- 设置为负数表示无限 deadline(即代理本身永远不会主动彻底终止该连接);
- 对比参考文档可知,TCP ServersTransport 允许为终止序列设置无期限的等待,这在某些对优雅关闭有严格要求的场景中是有意为之。
proxyProtocol.version:保留客户端真实来源
Traefik 在 TCP 服务上支持 PROXY Protocol 版本 1 与版本 2,通过 serversTransport 上的 proxyProtocol.version 指定使用的协议版本(取值只能是 1 或 2)。设置 PROXY Protocol 后,Traefik 向后端发起连接时会携带原始客户端地址信息,方便后端程序在没有标准转发头(TCP 层没有 HTTP 头)的情况下还原客户端真实 IP。
从 serverstransporttcp.go 可见,该字段的类型直接复用 dynamic.ProxyProtocol;而空白的 proxyProtocol 段即等价于启用 PROXY Protocol 版本 2,这也与总表中的默认说明一致。
tls.rootCAs 与证书密钥约定
当后端使用自签名或私有 CA 签发证书时,需要通过 tls.rootCAs 告诉 Traefik 信任哪些 CA。该字段的每一项有两种写法,二选一:
spec:
tls:
rootCAs:
- secret: my-ca-secret # 引用 Secret
- configMap: my-ca-configmap # 引用 ConfigMap
被引用的 Secret 或 ConfigMap 中必须包含证书内容,支持的 key 为 tls.ca 或 ca.crt。从仓库测试 fixture tcp/with_servers_transport.yml 可以确认:当同一 Secret 同时包含 ca.crt 与 tls.ca 两个 key 时,tls.ca 会被优先使用(fixture 中明确标注 “This should be the preferred one”)。
同时注意:
- 旧字段
tls.rootCAsSecrets只接受 Secret 引用,已被标记废弃,请迁移到tls.rootCAs; tls.certificatesSecrets用于 mTLS 场景,即 Traefik 需要向后端出示客户端证书。Secret 需要包含tls.crt与tls.key两个 key(见 fixture 中的mtls1/mtls2示例)。
tls.peerCertSANs:按 SAN 精确校验对端证书
在某些安全要求较高的场景(如 SPIFFE/mTLS)中,仅信任某个 CA 还不够,还需要校验对端证书里的 Subject Alternative Name。tls.peerCertSANs 的每个条目包含两个字段:
spec:
tls:
peerCertSANs:
- type: DNSName
value: foo.com
- type: URI
value: spiffe://example.org/peer
type取值为URI或DNSName;value为要匹配的 SAN 值。
其类型在源码中复用 pkg/tls 包中定义的 SAN 结构(见 serverstransporttcp.go)。早期版本提供过一个单独的 tls.peerCertURI 字符串字段(只能匹配单个 URI),该字段同样已被标记废弃,官方建议改用 peerCertSANs。
spiffe:基于 SPIFFE ID 的鉴权
当后端启用 SPIFFE 工作负载身份时,可用 spiffe 段限制 Traefik 只能与持有特定身份的服务器建连:
spec:
tls:
spiffe:
ids:
- spiffe://example.org/id1
- spiffe://example.org/id2
trustDomain: example.org
spiffe.ids定义允许的 SPIFFE ID,优先级高于trustDomain;spiffe.trustDomain定义允许的 trust domain。
需要特别强调的是:SPIFFE 必须先在 安装配置的 TLS 部分(旧称静态配置)中启用,才能用于保护 Traefik 与后端之间的连接。单纯在 CRD 上声明 spiffe 段而安装配置未开启时不会生效。
源码级原理:CRD 如何转换为内部配置
理解 ServersTransportTCP 的完整链路,有助于排查“配置了但没生效”的问题。整条转换发生在 CRD Provider 的配置加载函数中(kubernetes.go),大致过程如下:
- Provider 通过 informer 获取集群中全部
ServersTransportTCP对象(client.GetServersTransportTCPs()); - 对每个对象,先构造内部的
TCPServersTransport,并按字段依次赋值:dialTimeout、dialKeepAlive、terminationDelay三个时间字段在 CRD 中声明为*intstr.IntOrString(同时带+kubebuilder:validation:Pattern="^([0-9]+(ns|us|µs|ms|s|m|h)?)+$"与+kubebuilder:validation:XIntOrString注解,见 serverstransporttcp.go),因此在 YAML 中既可以写42这样的整数,也可以写100ms/30s这样的 Go duration 字符串;proxyProtocol直接整体赋值给内部对象;tls段中,从rootCAsSecrets/rootCAs引用的 Secret/ConfigMap 会被读取出 PEM 证书内容(分别通过loadCASecret、loadCAConfigMap加载),从certificatesSecrets读取客户端证书/私钥;
- 最终用
provider.Normalize(makeID(namespace, name))生成规范化 key(例如default-mytransport@kubernetescrd)存入动态配置,供IngressRouteTCP引用。
可见,ServersTransportTCP 里声明的 Secret/ConfigMap 名称都是在该 CRD 自身的命名空间内解析的,这从侧面解释了为什么跨命名空间引用需要显式开启 allowCrossNamespace。
如何验证你的配置
仓库为 CRD Provider 提供了覆盖上述全部字段的测试资产,可直接作为“教科书级”示例研读:
- tcp/with_servers_transport.yml:覆盖
serverName、insecureSkipVerify、peerCertURI、rootCAsSecrets(多个 Secret、同时含tls.ca与ca.crt的 Secret)、rootCAs(Secret 与 ConfigMap、同一项同时引用两者应失败的边界)、certificatesSecrets、spiffe、三个整数形式的时间字段,以及同命名空间引用的IngressRouteTCP; - tcp/with_servers_transport_cross_namespace.yml:覆盖
namespace-name@kubernetescrd形式的跨命名空间引用,以及dialKeepAlive: 0这一“关闭 keep-alive 自定义、退回默认探测”的写法; - 对应的 Provider 单元测试位于 pkg/provider/kubernetes/crd/kubernetes_test.go。
小结
ServersTransportTCP 把原本位于安装配置/文件配置中的 TCP transport 参数(拨号超时、keep-alive、PROXY Protocol、TLS 与 mTLS 校验、SPIFFE、连接终止延迟)下沉为 Kubernetes 原生 CRD,使集群使用者可以在不触碰 Traefik 安装配置的前提下,按服务精细化控制反向代理与 TCP 后端的连接行为。使用时只需记住三点:先安装 CRD、在 IngressRouteTCP 服务上通过 serversTransport 字段引用、跨命名空间引用需开启 allowCrossNamespace 并采用 name@kubernetescrd 命名。更完整的字段定义与动态配置(YAML/TOML/标签形式)对照可继续查阅 TCP ServersTransport 参考 及 CRD 类型源码。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00