From d9d7d0be74be9567c141a0edb6dce7aeabbf49d3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 24 Aug 2026 21:02:37 -0700 Subject: [PATCH] helm: serve the Lance catalog and deploy the Rust worker (#10936) * helm: serve the S3 gateway's Lance Namespace, on by default Standalone `weed s3` serves the Lance Namespace API on 9101 unless told not to, so the chart defaulting s3.lancePort to 9101 matches weed's own posture instead of hiding the port behind a null. The flag is always rendered, so lancePort: 0 reaches weed as -port.lance=0 and genuinely disables the namespace rather than silently falling back to the binary default; 0 also drops the service port and the optional lanceIngress, which otherwise mirror the iceberg wiring. The NetworkPolicy admits the port the same way it admits icebergPort. Claude-Session: https://claude.ai/code/session_01Rkp1Mw5E89Jp6dzJFYiMrm * helm: run the Lance maintenance worker beside the Go worker The Go and Rust workers have no overlapping jobs - Go serves vacuum, balance, EC and iceberg_maintenance, only /usr/bin/weed-worker serves the lance_* family - so a cluster serving Lance tables needs both, not an either/or switch. The worker deployment now adds a worker-lance container whenever the namespace is reachable: worker.namespaceUrl, or derived from the release's S3 service and s3.lancePort. Untouched Go container; admin address derived the same way; mTLS flags point at the already-mounted worker cert when security is on; metrics on their own worker.lanceMetricsPort (9328, next in the 932x convention) with the same health probes, service port and scrape endpoint the Go container gets, and the worker NetworkPolicy admits that port exactly when the container renders. The image carries an empty placeholder on armv7/386 where exec falls back to the shell and exits 0, so the command refuses those platforms by name; s3.lancePort: 0 is the escape hatch there. Claude-Session: https://claude.ai/code/session_01Rkp1Mw5E89Jp6dzJFYiMrm --- .../seaweedfs/templates/s3/s3-deployment.yaml | 5 ++ .../templates/s3/s3-lance-ingress.yaml | 61 +++++++++++++++ .../seaweedfs/templates/s3/s3-service.yaml | 9 +++ .../seaweedfs/templates/shared/_helpers.tpl | 9 +++ .../templates/shared/networkpolicy.yaml | 6 ++ .../templates/worker/worker-deployment.yaml | 75 +++++++++++++++++++ .../templates/worker/worker-service.yaml | 11 ++- .../worker/worker-servicemonitor.yaml | 10 ++- k8s/charts/seaweedfs/values.yaml | 31 ++++++++ 9 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 k8s/charts/seaweedfs/templates/s3/s3-lance-ingress.yaml diff --git a/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml b/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml index 1fff9dbc4..d0e4fe7f3 100644 --- a/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/s3/s3-deployment.yaml @@ -149,6 +149,7 @@ spec: {{- if .Values.s3.icebergPort }} -port.iceberg={{ .Values.s3.icebergPort }} \ {{- end }} + -port.lance={{ .Values.s3.lancePort | default 0 }} \ {{- range .Values.s3.extraArgs }} {{ . }} \ {{- end }} @@ -198,6 +199,10 @@ spec: - containerPort: {{ .Values.s3.icebergPort }} name: swfs-iceberg {{- end }} + {{- if .Values.s3.lancePort }} + - containerPort: {{ .Values.s3.lancePort }} + name: swfs-lance + {{- end }} {{- if .Values.s3.metricsPort }} - containerPort: {{ .Values.s3.metricsPort }} name: metrics diff --git a/k8s/charts/seaweedfs/templates/s3/s3-lance-ingress.yaml b/k8s/charts/seaweedfs/templates/s3/s3-lance-ingress.yaml new file mode 100644 index 000000000..32033a8f1 --- /dev/null +++ b/k8s/charts/seaweedfs/templates/s3/s3-lance-ingress.yaml @@ -0,0 +1,61 @@ +{{- define "seaweedfs.s3.lance.ingress.paths" -}} +paths: +- path: {{ .Values.s3.lanceIngress.path | quote }} + pathType: {{ .Values.s3.lanceIngress.pathType | quote }} + backend: + {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} + service: + name: {{ include "seaweedfs.componentName" (list . "s3") }} + port: + number: {{ .Values.s3.lancePort }} + {{- else }} + serviceName: {{ include "seaweedfs.componentName" (list . "s3") }} + servicePort: {{ .Values.s3.lancePort }} + {{- end }} +{{- end -}} +{{- if and .Values.s3.enabled .Values.s3.lancePort .Values.s3.lanceIngress.enabled }} +{{- $hosts := list }} +{{- if kindIs "slice" .Values.s3.lanceIngress.host }} + {{- $hosts = .Values.s3.lanceIngress.host }} +{{- else if .Values.s3.lanceIngress.host }} + {{- $hosts = list .Values.s3.lanceIngress.host }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: ingress-{{ include "seaweedfs.fullname" . }}-s3-lance + namespace: {{ .Release.Namespace }} + {{- with .Values.s3.lanceIngress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + 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: s3-lance +spec: + {{- if .Values.s3.lanceIngress.className }} + ingressClassName: {{ .Values.s3.lanceIngress.className | quote }} + {{- end }} + tls: + {{ .Values.s3.lanceIngress.tls | default list | toYaml | nindent 6}} + rules: +{{- if $hosts }} +{{- range $host := $hosts }} + - host: {{ $host | quote }} + http: + {{- include "seaweedfs.s3.lance.ingress.paths" $ | nindent 6 }} +{{- end }} +{{- else }} + - http: + {{- include "seaweedfs.s3.lance.ingress.paths" . | nindent 4 }} +{{- end }} +{{- end }} diff --git a/k8s/charts/seaweedfs/templates/s3/s3-service.yaml b/k8s/charts/seaweedfs/templates/s3/s3-service.yaml index 4a4e6a410..b5aba6a86 100644 --- a/k8s/charts/seaweedfs/templates/s3/s3-service.yaml +++ b/k8s/charts/seaweedfs/templates/s3/s3-service.yaml @@ -44,6 +44,15 @@ spec: {{- end }} protocol: TCP {{- end }} +{{- if and .Values.s3.enabled .Values.s3.lancePort }} + - name: "swfs-lance" + port: {{ .Values.s3.lancePort }} + targetPort: {{ .Values.s3.lancePort }} +{{- if $nodePorts.lance }} + nodePort: {{ $nodePorts.lance }} +{{- end }} + protocol: TCP +{{- end }} {{- if and .Values.s3.enabled .Values.s3.httpsPort }} - name: "swfs-s3-tls" port: {{ .Values.s3.httpsPort }} diff --git a/k8s/charts/seaweedfs/templates/shared/_helpers.tpl b/k8s/charts/seaweedfs/templates/shared/_helpers.tpl index 71ac9c62b..3ac343075 100644 --- a/k8s/charts/seaweedfs/templates/shared/_helpers.tpl +++ b/k8s/charts/seaweedfs/templates/shared/_helpers.tpl @@ -148,6 +148,15 @@ true {{- end -}} {{- end -}} +{{/* Lance namespace URL the worker's Lance container maintains; empty when unreachable */}} +{{- define "seaweedfs.worker.lanceNamespaceUrl" -}} +{{- if .Values.worker.namespaceUrl -}} +{{- .Values.worker.namespaceUrl -}} +{{- else if and .Values.s3.enabled .Values.s3.lancePort -}} +{{- printf "http://%s.%s:%d" (include "seaweedfs.componentName" (list . "s3")) .Release.Namespace (int .Values.s3.lancePort) -}} +{{- end -}} +{{- end -}} + {{/* Return the proper volume image */}} {{- define "seaweedfs.volume.image" -}} {{- if .Values.volume.imageOverride -}} diff --git a/k8s/charts/seaweedfs/templates/shared/networkpolicy.yaml b/k8s/charts/seaweedfs/templates/shared/networkpolicy.yaml index 91f41bee1..92404a754 100644 --- a/k8s/charts/seaweedfs/templates/shared/networkpolicy.yaml +++ b/k8s/charts/seaweedfs/templates/shared/networkpolicy.yaml @@ -73,6 +73,9 @@ {{- if .Values.s3.icebergPort }} {{- $ports = append $ports .Values.s3.icebergPort }} {{- end }} +{{- if .Values.s3.lancePort }} +{{- $ports = append $ports .Values.s3.lancePort }} +{{- end }} {{- if .Values.s3.metricsPort }} {{- $ports = append $ports .Values.s3.metricsPort }} {{- end }} @@ -97,6 +100,9 @@ {{- if .Values.worker.metricsPort }} {{- $ports = append $ports .Values.worker.metricsPort }} {{- end }} +{{- if and .Values.worker.lanceMetricsPort (include "seaweedfs.worker.lanceNamespaceUrl" .) }} +{{- $ports = append $ports .Values.worker.lanceMetricsPort }} +{{- end }} {{- $targets = append $targets (dict "component" "worker" "ports" $ports) }} {{- end }} diff --git a/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml b/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml index b2b4892da..75b438cd9 100644 --- a/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml +++ b/k8s/charts/seaweedfs/templates/worker/worker-deployment.yaml @@ -223,6 +223,81 @@ spec: {{- if .Values.worker.containerSecurityContext.enabled }} securityContext: {{- omit .Values.worker.containerSecurityContext "enabled" | toYaml | nindent 12 }} {{- end }} + {{- if include "seaweedfs.worker.lanceNamespaceUrl" . }} + - name: worker-lance + image: {{ template "seaweedfs.worker.image" . }} + imagePullPolicy: {{ default "IfNotPresent" .Values.global.seaweedfs.imagePullPolicy }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + # The URL reaches the shell below; a value with metacharacters must + # arrive as data, not syntax. + - name: LANCE_NAMESPACE_URL + value: {{ include "seaweedfs.worker.lanceNamespaceUrl" . | quote }} + command: + - "/bin/sh" + - "-ec" + - | + if [ ! -s /usr/bin/weed-worker ]; then + echo "the Rust worker is not available on this platform ($(uname -m)); it ships for amd64 and arm64" >&2 + exit 1 + fi + exec /usr/bin/weed-worker \ + --id="$POD_NAME" \ + {{- if .Values.worker.adminServer }} + --admin={{ .Values.worker.adminServer }} \ + {{- else }} + --admin={{ template "seaweedfs.fullname" . }}-admin.{{ .Release.Namespace }}:{{ .Values.admin.port }}{{ if .Values.admin.grpcPort }}.{{ .Values.admin.grpcPort }}{{ end }} \ + {{- end }} + --namespace="$LANCE_NAMESPACE_URL" \ + {{- if .Values.global.seaweedfs.enableSecurity }} + --tls-ca=/usr/local/share/ca-certificates/ca/tls.crt \ + --tls-cert=/usr/local/share/ca-certificates/worker/tls.crt \ + --tls-key=/usr/local/share/ca-certificates/worker/tls.key \ + {{- end }} + {{- if .Values.worker.lanceMetricsPort }} + --metrics-port={{ .Values.worker.lanceMetricsPort }} \ + --metrics-ip=0.0.0.0 \ + {{- end }} + --max-concurrency={{ .Values.worker.maxExecute }} + {{- if .Values.global.seaweedfs.enableSecurity }} + volumeMounts: + - name: ca-cert + readOnly: true + mountPath: /usr/local/share/ca-certificates/ca/ + - name: worker-cert + readOnly: true + mountPath: /usr/local/share/ca-certificates/worker/ + {{- end }} + {{- if .Values.worker.lanceMetricsPort }} + ports: + - containerPort: {{ .Values.worker.lanceMetricsPort }} + name: lance-metrics + livenessProbe: + httpGet: + path: /health + port: lance-metrics + initialDelaySeconds: 30 + periodSeconds: 60 + successThreshold: 1 + failureThreshold: 5 + timeoutSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: lance-metrics + initialDelaySeconds: 20 + periodSeconds: 15 + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 10 + {{- end }} + {{- if .Values.worker.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.worker.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- end }} {{- if .Values.worker.sidecars }} {{- include "seaweedfs.tplvalues.render" (dict "value" .Values.worker.sidecars "context" $) | nindent 8 }} {{- end }} diff --git a/k8s/charts/seaweedfs/templates/worker/worker-service.yaml b/k8s/charts/seaweedfs/templates/worker/worker-service.yaml index d78e0cf78..6c0ae8b70 100644 --- a/k8s/charts/seaweedfs/templates/worker/worker-service.yaml +++ b/k8s/charts/seaweedfs/templates/worker/worker-service.yaml @@ -12,13 +12,22 @@ metadata: app.kubernetes.io/component: worker spec: clusterIP: None # Headless service - {{- if .Values.worker.metricsPort }} + {{- $lanceMetrics := and .Values.worker.lanceMetricsPort (include "seaweedfs.worker.lanceNamespaceUrl" .) }} + {{- if or .Values.worker.metricsPort $lanceMetrics }} ports: + {{- if .Values.worker.metricsPort }} - name: "metrics" port: {{ .Values.worker.metricsPort }} targetPort: {{ .Values.worker.metricsPort }} protocol: TCP {{- end }} + {{- if $lanceMetrics }} + - name: "lance-metrics" + port: {{ .Values.worker.lanceMetricsPort }} + targetPort: {{ .Values.worker.lanceMetricsPort }} + protocol: TCP + {{- end }} + {{- end }} selector: app.kubernetes.io/name: {{ template "seaweedfs.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/k8s/charts/seaweedfs/templates/worker/worker-servicemonitor.yaml b/k8s/charts/seaweedfs/templates/worker/worker-servicemonitor.yaml index 8ec4be870..50cb04f48 100644 --- a/k8s/charts/seaweedfs/templates/worker/worker-servicemonitor.yaml +++ b/k8s/charts/seaweedfs/templates/worker/worker-servicemonitor.yaml @@ -1,6 +1,7 @@ {{- include "seaweedfs.compat" . -}} {{- if .Values.worker.enabled }} -{{- if .Values.worker.metricsPort }} +{{- $lanceMetrics := and .Values.worker.lanceMetricsPort (include "seaweedfs.worker.lanceNamespaceUrl" .) }} +{{- if or .Values.worker.metricsPort $lanceMetrics }} {{- if .Values.global.seaweedfs.monitoring.enabled }} apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor @@ -22,9 +23,16 @@ metadata: {{- end }} spec: endpoints: + {{- if .Values.worker.metricsPort }} - interval: 30s port: metrics scrapeTimeout: 5s + {{- end }} + {{- if $lanceMetrics }} + - interval: 30s + port: lance-metrics + scrapeTimeout: 5s + {{- end }} selector: matchLabels: app.kubernetes.io/name: {{ template "seaweedfs.name" . }} diff --git a/k8s/charts/seaweedfs/values.yaml b/k8s/charts/seaweedfs/values.yaml index 460fc8eb3..10878d43d 100644 --- a/k8s/charts/seaweedfs/values.yaml +++ b/k8s/charts/seaweedfs/values.yaml @@ -1006,6 +1006,10 @@ s3: # Iceberg catalog REST port (Apache Iceberg REST Catalog API) # Set to a port number to enable, or 0/null to disable icebergPort: null + # Lance Namespace REST port (Lance Namespace API), on by default like + # standalone `weed s3`. Set to 0 to disable; that also drops the worker's + # Lance maintenance container. + lancePort: 9101 loggingOverrideLevel: null # enable user & permission to s3 (need to inject to all services) enableAuth: false @@ -1193,6 +1197,7 @@ s3: http: null https: null iceberg: null + lance: null metrics: null icebergIngress: @@ -1204,6 +1209,15 @@ s3: annotations: {} tls: [] + lanceIngress: + enabled: false + className: "" + host: "seaweedfs-lance.cluster.local" + path: "/" + pathType: Prefix + annotations: {} + tls: [] + sftp: enabled: false imageOverride: null @@ -1460,6 +1474,23 @@ worker: metricsPort: 9327 metricsIp: "" # If empty, defaults to 0.0.0.0 + # The Lance maintenance worker (/usr/bin/weed-worker, Rust) runs as a + # second container beside `weed worker` whenever its namespace is + # reachable: namespaceUrl set, or s3 enabled with a nonzero s3.lancePort. + # It runs alongside rather than instead, because the job families are + # disjoint: the Go worker serves vacuum, balance, EC and + # iceberg_maintenance; only the Rust worker serves lance_compact, + # lance_optimize_indices and lance_cleanup_versions. The binary ships for + # amd64 and arm64; on an armv7/386-only cluster set s3.lancePort: 0, or + # the container exits at startup naming the platform. + + # Lance namespace URL override. When empty, derived from this release's + # S3 service and s3.lancePort. + namespaceUrl: "" + + # Metrics port for the Lance worker container; the Go worker keeps metricsPort + lanceMetricsPort: 9328 + # Admin server to connect to adminServer: ""