From 2480c2521ab9365f836632a7bc0d55489f014462 Mon Sep 17 00:00:00 2001 From: MorezMartin Date: Fri, 3 Jul 2026 19:55:06 +0200 Subject: [PATCH] feat(k8s): add Traefik IngressRouteTCP for gRPC with TLS passthrough (#10223) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(k8s): add Traefik IngressRouteTCP for gRPC with TLS passthrough Re-introduce Traefik support for the gRPC filer ingress that was lost when the original ingress PR was merged. Previous attempts to make the chart controller-agnostic using Ingress + ServersTransport + TLSOption CRDs were fragile — they required 2 separate services (HTTP and gRPC), still failed with connection resets, and forced Traefik to terminate and re-encrypt TLS traffic. This approach uses a single IngressRouteTCP CRD with TLS passthrough when enableSecurity is true, keeping the TLS stream intact. No ServersTransport, no TLSOption, no service annotations, no values.yaml structure changes. Fully backward compatible. Refs: seaweedfs/seaweedfs#10205 Co-Authored-By: Athena 🏛️ (custom / Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf) * refactor(k8s): only render standard gRPC Ingress when className is not Traefik When className contains 'traefik', the IngressRouteTCP is the only source of truth. The standard Kubernetes Ingress becomes superfluous and potentially confusing for debugging. Now: - className: traefik → only IngressRouteTCP - className: nginx/contour/... → only standard Ingress - className: "" (default) → neither No values.yaml changes. Fully backward compatible. Co-Authored-By: Athena 🏛️ (custom / Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf) * k8s: fix Traefik gRPC IngressRouteTCP for non-TLS and all-in-one modes A non-TLS TCP router can only match HostSNI(`*`), so the default enableSecurity=false path never matched. Use HostSNI(`*`) when security is off and keep host-based SNI for TLS passthrough. Route to the all-in-one service in all-in-one mode via the same ternary the standard ingress uses; the hardcoded filer-client service is absent when filer.enabled is false. Also require grpc.enabled to render, align labels with the sibling ingress, and put the comments in English. --------- Co-authored-by: Chris Lu --- .../filer/filer-grpc-ingressroutetcp.yaml | 47 +++++++++++++++++++ .../templates/filer/filer-ingress.yaml | 4 +- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 k8s/charts/seaweedfs/templates/filer/filer-grpc-ingressroutetcp.yaml diff --git a/k8s/charts/seaweedfs/templates/filer/filer-grpc-ingressroutetcp.yaml b/k8s/charts/seaweedfs/templates/filer/filer-grpc-ingressroutetcp.yaml new file mode 100644 index 000000000..c1c2f7d78 --- /dev/null +++ b/k8s/charts/seaweedfs/templates/filer/filer-grpc-ingressroutetcp.yaml @@ -0,0 +1,47 @@ +{{- /* Traefik IngressRouteTCP for the filer gRPC port. Rendered instead of the + standard Ingress when the gRPC ingress className starts with "traefik", + since a Kubernetes Ingress can't do raw TCP/gRPC passthrough. + + global.seaweedfs.enableSecurity controls the mode: + false: plain TCP, Traefik forwards h2c to the pod. A non-TLS TCP + router can only match HostSNI(`*`) (SNI needs TLS). + true: TLS passthrough, matched by SNI so it can route on the host. */}} + +{{- $filerEnabled := or .Values.filer.enabled .Values.allInOne.enabled }} +{{- $isTraefik := hasPrefix "traefik" (default "" .Values.filer.ingresses.grpc.className) }} +{{- $securityEnabled := .Values.global.seaweedfs.enableSecurity }} + +{{- if and $filerEnabled .Values.filer.ingresses.grpc.enabled $isTraefik }} +{{- $serviceName := ternary (include "seaweedfs.componentName" (list . "all-in-one")) (include "seaweedfs.componentName" (list . "filer")) .Values.allInOne.enabled }} +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: {{ include "seaweedfs.fullname" . }}-filer-grpc + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ template "seaweedfs.name" . }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: filer +spec: + {{- if $securityEnabled }} + tls: + passthrough: true + {{- end }} + entryPoints: + {{- with .Values.filer.ingresses.grpc.entryPoint }} + - {{ . | quote }} + {{- else }} + - filer-grpc + {{- end }} + routes: + {{- if $securityEnabled }} + - match: HostSNI(`{{ .Values.filer.ingresses.grpc.host }}`) + {{- else }} + - match: HostSNI(`*`) + {{- end }} + services: + - name: {{ $serviceName }} + port: {{ .Values.filer.grpcPort }} +{{- end }} diff --git a/k8s/charts/seaweedfs/templates/filer/filer-ingress.yaml b/k8s/charts/seaweedfs/templates/filer/filer-ingress.yaml index f6ca28130..b747cd2d8 100644 --- a/k8s/charts/seaweedfs/templates/filer/filer-ingress.yaml +++ b/k8s/charts/seaweedfs/templates/filer/filer-ingress.yaml @@ -59,8 +59,8 @@ spec: --- -{{- /* gRPC Ingress */}} -{{- if and $filerEnabled .Values.filer.ingresses.grpc.enabled }} +{{- /* gRPC Ingress (standard Kubernetes Ingress, NOT for Traefik) */}} +{{- if and $filerEnabled .Values.filer.ingresses.grpc.enabled (not (hasPrefix "traefik" (default "" .Values.filer.ingresses.grpc.className))) }} {{- /* Determine service name based on deployment mode */}} {{- $serviceName := ternary (include "seaweedfs.componentName" (list . "all-in-one")) (include "seaweedfs.componentName" (list . "filer")) .Values.allInOne.enabled }} {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}