
Traefik Kubernetes CRD 中的 Service 配置详解IngressRoute 与 TraefikService 服务引用实战【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefikService是 Traefik 在 Kubernetes CRD 动态配置体系中对上游 HTTP 服务的统一描述它并不存在独立的 CRD而是作为IngressRoute与TraefikService资源的一部分内联声明本质上是 Traefik HTTP 负载均衡服务 在 Kubernetes 环境下的映射实现。本文以 Kubernetes Service 官方文档 为骨架结合仓库中 CRD 类型定义与 Provider 构建源码完整讲解在 IngressRoute 路由规则、TraefikService 加权/镜像等聚合场景中如何声明、引用并调优一个 Kubernetes Service读完你将能够独立编写从简单转发到 ExternalName 健康检查、NativeLB、粘性会话、服务级中间件在内的全部 Service 配置。Service为什么它不是一个独立 CRD在 Traefik 的 Kubernetes CRD 提供者中Service不是通过kubectl apply创建的独立资源而是一个结构化的内联配置块。它出现在两类对象内部IngressRoute的spec.routes[].services[]直接定义某个 Rule 命中后将流量转发的后端服务TraefikService的spec.weighted.services[]、spec.mirroring、spec.highestRandomWeight或spec.failover等聚合字段中作为被加权、镜像或故障转移的子单元被引用。也就是说Service块同时承担两种角色引用一个原生 Kubernetes Service端点来自 Pod IP或引用另一个 TraefikService实现 WRR 加权、Mirroring 镜像、Failover 故障转移等组合逻辑二者通过kind字段区分。创建任何包含 Service 的IngressRoute或TraefikService对象之前必须先向集群应用 Traefik Kubernetes CRDs 清单注册traefik.ioAPI 组下的 Traefik 专属资源。仓库中可以直接找到这类 CRD 清单及配套 RBAC例如 integration/fixtures/k8s/01-traefik-crd.yml 与 integration/fixtures/k8s/03-ingressroute.yml。从源码类型结构上可以印证这一点LoadBalancerSpec负载均衡器公共规范是所有 Service 声明共用的核心结构位于 pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go#L117-L174而Service只是将其内联展开的空壳结构同文件 L226-L229。TraefikService的类型定义则位于 pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go其Weighted、Mirroring、HighestRandomWeight、Failover字段同样以LoadBalancerSpec为基座见 service.go#L42-L51。配置示例两种声明位置作为 IngressRoute 的路由后端在IngressRoute中声明 Service 时services数组直接挂在 Rule 之下。下面示例覆盖了该结构下的大多数可调字段apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test-name namespace: apps spec: entryPoints: - web routes: - kind: Rule # Rule on the Host match: Host(test.example.com) services: # Target a Kubernetes Service - kind: Service name: foo namespace: apps # Customize the connection between Traefik and the backend passHostHeader: true port: 80 responseForwarding: flushInterval: 1ms scheme: https sticky: cookie: httpOnly: true name: cookie secure: true strategy: wrr # Attach middlewares to this service middlewares: - name: my-middleware namespace: apps作为 TraefikService 加权负载均衡的子单元在TraefikService中声明 Service 时通常处于weighted.services这类聚合场景此时每个子项可以通过weight字段参与比例调度weight定义于 LoadBalancerSpec仅在引用 TraefikService 对象时生效apiVersion: traefik.io/v1alpha1 kind: TraefikService metadata: name: wrr1 namespace: apps spec: weighted: services: # Target a Kubernetes Service - kind: Service name: foo namespace: apps # Customize the connection between Traefik and the backend passHostHeader: true port: 80 responseForwarding: flushInterval: 1ms scheme: https sticky: cookie: httpOnly: true name: cookie secure: true strategy: wrr配置项总览与默认值下表为 Service 块的全部可用字段。除kind、name、namespace、weight、middlewares[n].name/namespace外其余字段仅在kind: Service即引用原生 Kubernetes Service时才会被评估。字段说明默认值是否必需kind被引用服务的种类允许值ServiceKubernetes Service、TraefikServiceTraefik Service详见 ExternalName Service 一节Service否name服务名称字符不被允许是namespace服务所在命名空间与服务所属的 IngressRoute 同命名空间时可省略否port服务端口端口号或端口名仅在kind: Service时评估否responseForwarding.flushInterval将响应体复制给客户端时两次 flush 之间的毫秒间隔负值表示每次写入后立即 flush流式响应会忽略该配置并即时 flush100ms否scheme请求上游 Kubernetes Service 时使用的协议仅在kind: Service时评估http当port为443或端口名含https时为https否serversTransport用于配置 Traefik 与后端服务器之间传输行为的 ServersTransport 资源名称仅在kind: Service时评估否passHostHeader是否将客户端 Host 头转发给后端服务器仅在kind: Service时评估true否healthCheck.scheme健康检查端点的服务器 URL 协议仅限 ExternalName 类型 Kubernetes Service否healthCheck.mode健康检查模式设为grpc时使用 gRPC 健康检查协议探测http否healthCheck.path健康检查端点的 URL 路径否healthCheck.interval对健康目标执行健康检查的调用频率30s否healthCheck.unhealthyInterval对不健康目标执行健康检查的频率未定义时默认取interval值30s否healthCheck.method健康检查端点使用的 HTTP 方法GET否healthCheck.status期望的健康检查响应状态码未设置时接受 200~399 之间的状态否healthCheck.port健康检查端点的 URL 端口否healthCheck.timeout判定服务器不健康前的最长等待时长5s否healthCheck.hostname健康检查请求 Host 头的取值否healthCheck.followRedirect健康检查期间是否跟随重定向true否healthCheck.headers发送到健康检查端点的自定义请求头映射否sticky.cookie.name粘性会话所用 cookie 的名称启用粘性后首响应会写Set-Cookie客户端后续请求携带该值以保持会话落到同一服务器若 cookie 指向的服务器变不健康请求会被转发到新服务器并更新 cookie否sticky.cookie.httpOnly禁止 JavaScript 等客户端 API 访问该 cookiefalse否sticky.cookie.secure仅允许 cookie 通过加密连接HTTPS传输false否sticky.cookie.sameSiteSameSite 策略允许值none、lax、strict否sticky.cookie.maxAgecookie 到期前的秒数负数立即过期0表示永不过期0否sticky.cookie.path浏览器发送 Cookie 前请求 URL 必须匹配的路径未提供时对域上每个请求都发送/否sticky.cookie.domaincookie 将要发送到的主机否strategy服务器间的负载均衡策略支持wrr加权轮询、p2c随机二选一/两次随机选择、hrw最高随机权重、leasttime最小时延仅在kind: Service时评估wrr否nativeLB使用 Kubernetes Service 自身的负载均衡直接引用 clusterIP而不用 Traefik 在 Pod IP 层面构建的负载均衡器仅在kind: Service时评估false否nodePortLB当 Service 类型为 NodePort 时直接使用各节点的内部 IP nodePort 作为后端使 Traefik 运行在集群外部、但与节点同网段时仍可访问服务仅在kind: Service时评估false否middlewares应用到该服务的 Middleware 引用列表对该服务处理的所有请求生效无论经哪个 Router 转发仅在kind: Service时评估否middlewares[n].name中间件名称字符不被允许是middlewares[n].namespace中间件命名空间与 IngressRoute 同命名空间时可省略否需要说明上表中部分健康检查字段scheme、mode、path、interval、unhealthyInterval、method、status、port、timeout、hostname、followRedirect、headers的完整定义可对照 Go 侧类型ServerHealthCheck见 pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go#L185-L217字段的校验枚举如kind仅允许Service;TraefikService、strategy允许wrr;p2c;hrw;leasttime;RoundRobin同样在该文件内通过 kubebuilder 注解声明。关于与命名空间规则的底层限制name与middlewares[n].name中不允许出现是因为在 Traefik 动态配置中作为跨 Provider 引用的命名空间分隔符保留如namespace-namekubernetescrd。以serversTransport的解析函数 makeServersTransportKey 为例若名称含且末尾为kubernetescrd当 Provider 不允许跨命名空间引用时会被直接拒绝正常情况下同命名空间的引用会被规范化拼接为父命名空间/资源名形式的 key。这解释了为何普通名称中禁止也提示你在跨命名空间引用 ServersTransport、Middleware 时需要显式给出namespace。ExternalName Service 与端口定义Traefik 创建后端必须要有端口而 Kubernetes 的ExternalName类型 Service 允许不声明任何端口它本质上只是把流量的目标替换为spec.externalName指向的外部域名。为兼容这一场景Traefik 支持两种端口定义方式仅在IngressRoute的 Service 上定义端口两侧IngressRoute 与 Kubernetes Service都定义端口——此时若两边端口不匹配Traefik 会给出告警并以IngressRouteService 中声明的端口为准。也就是说当两侧都声明端口时Traefik 期望二者匹配以避免端口解析歧义。端口定义在 IngressRoute资源上apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test.route namespace: apps spec: entryPoints: - foo routes: - match: Host(example.net) kind: Rule services: - name: external-svc port: 80apiVersion: v1 kind: Service metadata: name: external-svc namespace: apps spec: externalName: external.domain type: ExternalName端口定义在 Kubernetes Service 上apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test.route namespace: apps spec: entryPoints: - foo routes: - match: Host(example.net) kind: Rule services: - name: external-svcapiVersion: v1 kind: Service metadata: name: external-svc namespace: apps spec: externalName: external.domain type: ExternalName ports: - port: 80两侧同时定义端口apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test.route namespace: apps spec: entryPoints: - foo routes: - match: Host(example.net) kind: Rule services: - name: external-svc port: 80apiVersion: v1 kind: Service metadata: name: external-svc namespace: apps spec: externalName: external.domain type: ExternalName ports: - port: 80从源码看ExternalName 的处理集中在 kubernetes_http.go 的 loadServers首先通过getServicePort解析端口兼容数字端口与命名端口随后拼接externalName:port作为唯一后端服务器地址并利用parseServiceProtocol依据 scheme/端口名/端口号决定协议前缀。同时值得注意的是两处硬性校验L531-L538healthCheck相关字段只允许用于 ExternalName 类型服务对其他类型声明会直接报错引用 ExternalName 服务的前提是 Provider 开启了allowExternalNameServices开关否则会返回 externalName services not allowed。配置后端协议三种方式与源码判定逻辑Traefik 与后端 Pod 之间使用什么协议http/https/h2c共有三种配置途径在 Service 上显式设置scheme可取值http、https、h2c将Kubernetes Service 端口的名称以https开头端口名形如https-api将Kubernetes Service 端口号设为443。若三者都未配置Traefik 默认按http连接处理。这些规则与源码中的 parseServiceProtocol 一一对应providedScheme非空时仅接受http、https、h2c其余值直接报invalid schemescheme 为空时若端口号等于443或端口名以https为前缀则推导为https否则推导为http。这也印证了文档默认值一栏中 httpsifportis 443 or contains the stringhttps 的描述。apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: protocol-demo namespace: default spec: entryPoints: - web routes: - match: Host(api.example.com) kind: Rule services: - name: api port: 443 # 端口为 443未显式 scheme 时自动按 https - match: Host(grpc.example.com) kind: Rule services: - name: h2c-svc scheme: h2c # 显式指定 h2cHTTP/2 Cleartext port: 80多服务负载均衡与 TraefikService 组合同一个 Rule 的services列表中可以声明多个 Kubernetes ServiceTraefik 会按所选strategy在它们之间调度单列多服务时各条目也可通过weight参与加权——不过严格说多条目间带权重的标准做法是使用 TraefikService 的 weighted 结构apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: ingressroutebar namespace: default spec: entryPoints: - web routes: - match: Host(example.com) PathPrefix(/foo) kind: Rule services: - name: svc1 namespace: default - name: svc2 namespace: defaultapiVersion: v1 kind: Service metadata: name: svc1 namespace: default spec: ports: - name: http port: 80 selector: app: traefiklabs task: app1 --- apiVersion: v1 kind: Service metadata: name: svc2 namespace: default spec: ports: - name: http port: 80 selector: app: traefiklabs task: app2strategy字段在 LoadBalancerSpec 定义 中的注释支持wrr、p2c、hrw、leasttime其中历史遗留值RoundRobin已被废弃但为兼容性保留未来随废弃周期移除后默认值才由 kubebuilder 显式落到wrr。当需要更复杂的路由拓扑时可把多个 Service 放入 TraefikService 的weighted.services并在引用处通过kind: TraefikService指向聚合体。NativeLB让 Kubernetes Service 自己负载均衡默认情况下nativeLB: falseTraefik 会读取 Endpoint/EndpointSlice 拿到全部 Pod IP用自己的负载均衡器在后端服务器之间分发请求。而将nativeLB置为true后Traefik 不再展开 Pod IP而是把Kubernetes Service 的 clusterIP 作为唯一后端由 kube-proxy 完成到 Pod 的实际分发重要提示为获得更优性能Traefik 默认会复用与后端建立的连接。当启用nativeLB时连接复用可能使 Pod 副本之间的请求均衡效果不符合预期——请留意这一点。默认nativeLB为false。--- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test.route namespace: default spec: entryPoints: - foo routes: - match: Host(example.net) kind: Rule services: - name: svc port: 80 # Here, nativeLB instructs to build the server load-balancer with the Kubernetes Service clusterIP only. nativeLB: true --- apiVersion: v1 kind: Service metadata: name: svc namespace: default spec: type: ClusterIP ports: - port: 80源码层面loadServerskubernetes_http.go#L550-L566的处理顺序是先取 Provider 级别的默认开关nativeLBByDefault若 Service 自身声明了nativeLB则以其覆盖命中 native 模式时调用getNativeServiceAddress拿到 clusterIP 并构造唯一的scheme://clusterIP:port后端。Provider 全局默认开关位于 pkg/provider/kubernetes/crd/kubernetes.go#L64nativeLBByDefault也就是说你可以通过 Provider 配置让集群内所有服务默认进入 Native 模式再在单个 Service 上用nativeLB: false精准回退——仓库的单元测试覆盖了这一“全局开启 单点关闭”的场景见 pkg/provider/kubernetes/crd/kubernetes_test.go 中 HTTP with global native Service LB but service reference has nativeLB disabled 相关用例。NodePortLB集群外访问 NodePort 服务当被引用的 Kubernetes Service 类型为NodePort且 Traefik 运行在集群外部但与节点处于同一网络时直接使用 Pod IP 可能不可达。此时把nodePortLB设为trueTraefik 就会把后端的负载均衡器子节点设置为各节点的内部 IP nodePort从而绕开 clusterIP 仅集群内可达的限制。该字段的语义在 LoadBalancerSpec 的类型注释中有明确描述。apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: nodeport-demo namespace: default spec: entryPoints: - web routes: - match: Host(np.example.com) kind: Rule services: - name: nodeport-svc port: 80 nodePortLB: trueSticky Sessions会话粘性与 Cookie 细节当需要同一客户端的连续请求始终落到同一后端时在 Service 上声明sticky.cookie。启用后Traefik 会在首个响应中写入Set-Cookie告知客户端由哪个服务器处理了首响应后续请求客户端携带该 cookieTraefik 据此保持会话若 cookie 中记录的服务器转为不健康请求会被交给新的健康服务器并同步更新 cookie。apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: sticky-demo namespace: default spec: entryPoints: - web routes: - match: Host(sticky.example.com) kind: Rule services: - name: whoami port: 80 sticky: cookie: name: _my_session httpOnly: true secure: true sameSite: lax maxAge: 3600 path: / domain: example.com实现上kubernetes_http.go#L459-L475 会把 CRD 中的sticky.cookie逐字段搬运到动态配置dynamic.Cookie然后调用SetDefaults()补齐未显式声明的 cookie 属性例如路径默认/、maxAge默认0表示永不过期若显式给出了path则以显式值为准。文档表格中sticky.cookie.name的默认值为空字符串实际启用粘性后由SetDefaults依据算法自动生成名称因此生产环境建议显式命名以便与客户端、前端联调对齐。服务级 Middlewares挂载到 Service 而非 RouterMiddleware 既可以挂在 Router 上也可以直接挂在某个 Service 上。挂在 Service 上的中间件对经由该服务处理的所有请求生效与请求由哪个 Router 转发无关。关于服务级中间件的通用语义可进一步阅读 HTTP 服务级中间件说明。apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test-name namespace: default spec: entryPoints: - web routes: - kind: Rule match: Host(example.com) services: - kind: Service name: whoami port: 80 middlewares: - name: add-header namespace: defaultapiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: add-header namespace: default spec: headers: customRequestHeaders: X-Custom-Header: service-middlewareapiVersion: v1 kind: Service metadata: name: whoami namespace: default spec: ports: - port: 80 selector: app: whoami在 kubernetes_http.go#L482-L489 中可以看到Service 构建器发现len(svc.Middlewares) 0时会调用makeMiddlewareKeys把带命名空间的 Middleware 引用解析成中间件链 key 并挂到服务上——这意味着被引用的 Middleware 必须实际存在否则会出现 could not create middleware keys 一类的构建错误。Middleware 本身的可用类型与配置请参考 Middleware 文档。ServersTransport定制 Traefik 与后端的传输层若需对后端连接做更细粒度的传输层控制如 TLS 握手参数、连接池大小、转发超时等可在 Service 上通过serversTransport指定一个 ServersTransport 资源。该字段只在引用原生 Kubernetes Servicekind: Service时有效且仅能填写 ServersTransport 名称apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: st-demo namespace: default spec: entryPoints: - web routes: - match: Host(grpc.example.com) kind: Rule services: - name: grpc-svc port: 443 scheme: h2c serversTransport: my-serverstransportapiVersion: traefik.io/v1alpha1 kind: ServersTransport metadata: name: my-serverstransport namespace: default spec: forwardTimeouts: dialTimeout: 30s responseHeaderTimeout: 10s引用解析走的是前面提到的 makeServersTransportKey同命名空间引用会被解析成父命名空间/名称跨命名空间/跨 Provider 引用则受 Provider 的allowCrossNamespace与crossProviderNamespaces策略约束。结语在 Traefik 的 Kubernetes CRD 体系中Service 是一个“身份”多重的内联结构它既是 IngressRoute 转发链的终端也是 TraefikService 构建加权、镜像、故障转移等复合路由时的基本单元。掌握kind/name/port三要素、端口与协议的推导规则443/端口名 https/显式 scheme、ExternalName 的特殊端口与健康检查约束、NativeLB 与 NodePortLB 两种集群网络适配模式以及服务级中间件与 ServersTransport 的挂载方式就能把大部分生产流量编排需求落到可校验的 YAML 上。仓库内的 CRD 类型定义ingressroute.go、service.go、Provider 构建逻辑kubernetes_http.go以及测试与集成样例kubernetes_test.go、integration/k8s_test.go 与 integration/fixtures/k8s都提供了进一步核验行为与排查问题的路径。【免费下载链接】traefikThe Cloud Native Application Proxy项目地址: https://gitcode.com/GitHub_Trending/tr/traefik创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考