From 3e493e16a289eb70fcb2ab35217dfd9d9f31d202 Mon Sep 17 00:00:00 2001 From: Kyriakos Oikonomakos Date: Tue, 1 Sep 2026 12:37:17 +0300 Subject: [PATCH] Send traffic to the arm64 pods from one Service The arm64 variant has its own `app.kubernetes.io/name`, so the chart made a second Service for it. The HTTPRoute backs only the amd64 Service, so the arm64 pods never received traffic. Every pod template now carries `app.kubernetes.io/component`, which holds the workload name and is the same for all architectures. When arm64 is enabled, the Service and the PodDisruptionBudget select that label, so one Service reaches the pods of both architectures and the load balancer sends traffic to both. When arm64 is off, the selector does not change. No Deployment selector changes, so no workload is recreated. The label is added before it is selected, so an environment that enables arm64 later gets no interval without endpoints. Bump the chart first, then enable arm64. In one sync the amd64 pods would still roll to get the label while the selector already moved. --- charts/lago-rails/templates/_helpers.tpl | 36 ++++++++------ charts/lago-rails/templates/deployment.yaml | 7 ++- charts/lago-rails/templates/hpa.yaml | 3 +- charts/lago-rails/templates/pdb.yaml | 28 +++-------- charts/lago-rails/templates/service.yaml | 28 +++-------- charts/lago/tests/architecture_test.yaml | 54 +++++++++++++++++++++ 6 files changed, 96 insertions(+), 60 deletions(-) diff --git a/charts/lago-rails/templates/_helpers.tpl b/charts/lago-rails/templates/_helpers.tpl index f799f87..0b8775f 100644 --- a/charts/lago-rails/templates/_helpers.tpl +++ b/charts/lago-rails/templates/_helpers.tpl @@ -37,7 +37,7 @@ Common labels */}} {{- define "lago-rails.labels" -}} helm.sh/chart: {{ include "lago-rails.chart" . }} -{{ include "lago-rails.selectorLabels" . }} +{{ include "lago-rails.architecture.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -73,12 +73,13 @@ amd64 name. {{- end }} {{/* -Create architecture-specific selector labels. The amd64 result delegates to -the existing helper so its rendered labels do not change. +Create architecture-specific selector labels. Each architecture keeps its own +`app.kubernetes.io/name`, so a Deployment selector matches only its own pods. +The Deployment selector is immutable, so this helper must not change. */}} {{- define "lago-rails.architecture.selectorLabels" -}} {{- if eq (include "lago-rails.architecture.isArm64" .) "true" -}} -app.kubernetes.io/name: {{ printf "%s-arm64" .Values.internalArchitectureName | trunc 63 | trimSuffix "-" }} +app.kubernetes.io/name: {{ printf "%s-arm64" (include "lago-rails.name" .) | trunc 63 | trimSuffix "-" }} app.kubernetes.io/instance: {{ .Release.Name }} {{- else -}} {{- include "lago-rails.selectorLabels" . -}} @@ -86,19 +87,26 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* -Create architecture-specific labels. The amd64 result delegates to the -existing helper so its rendered labels do not change. +The architecture-independent label that every pod of this workload carries. The +Service and the PodDisruptionBudget select it, so they reach the pods of all +architectures. */}} -{{- define "lago-rails.architecture.labels" -}} -{{- if eq (include "lago-rails.architecture.isArm64" .) "true" -}} -helm.sh/chart: {{ include "lago-rails.chart" . }} -{{ include "lago-rails.architecture.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- define "lago-rails.componentLabel" -}} +app.kubernetes.io/component: {{ include "lago-rails.name" . }} {{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} + +{{/* +Selector labels for the Service and the PodDisruptionBudget. arm64 pods have a +different `app.kubernetes.io/name`, so the selector moves to the `component` +label when arm64 is enabled. Every pod already carries that label, because the +chart adds it to all pod templates. +*/}} +{{- define "lago-rails.service.selectorLabels" -}} +{{- if .Values.global.architectures.arm64.enabled -}} +app.kubernetes.io/instance: {{ .Release.Name }} +{{ include "lago-rails.componentLabel" . }} {{- else -}} -{{- include "lago-rails.labels" . -}} +{{- include "lago-rails.selectorLabels" . -}} {{- end -}} {{- end }} diff --git a/charts/lago-rails/templates/deployment.yaml b/charts/lago-rails/templates/deployment.yaml index 1bc076a..c07d696 100644 --- a/charts/lago-rails/templates/deployment.yaml +++ b/charts/lago-rails/templates/deployment.yaml @@ -8,7 +8,7 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} labels: - {{- include "lago-rails.architecture.labels" . | nindent 4 }} + {{- include "lago-rails.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} @@ -23,7 +23,8 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "lago-rails.architecture.labels" . | nindent 8 }} + {{- include "lago-rails.labels" . | nindent 8 }} + {{- include "lago-rails.componentLabel" . | nindent 8 }} {{- with .Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} @@ -480,7 +481,6 @@ spec: {{- $_ := set $amd64Values "tolerations" (concat .Values.tolerations $amd64.tolerations) }} {{- $amd64Context := dict "Values" $amd64Values "Chart" .Chart "Release" .Release "Capabilities" .Capabilities "Template" .Template "Files" .Files }} {{- $architectureFullname := include "lago-rails.fullname" $amd64Context }} -{{- $architectureName := include "lago-rails.name" $amd64Context }} {{- if .Values.global.architectures.amd64.enabled }} {{ include "lago-rails.deployment" $amd64Context }} {{- end }} @@ -492,7 +492,6 @@ spec: {{- $arm64Values := deepCopy .Values }} {{- $_ := set $arm64Values "internalArchitecture" "arm64" }} {{- $_ := set $arm64Values "internalArchitectureFullname" $architectureFullname }} -{{- $_ := set $arm64Values "internalArchitectureName" $architectureName }} {{- $_ := set $arm64Values "replicaCount" $arm64.replicaCount }} {{- $_ := set $arm64Values "nodeSelector" (mustMergeOverwrite (deepCopy .Values.nodeSelector) $arm64.nodeSelector) }} {{- $_ := set $arm64Values "tolerations" (concat .Values.tolerations $arm64.tolerations) }} diff --git a/charts/lago-rails/templates/hpa.yaml b/charts/lago-rails/templates/hpa.yaml index 27748a5..05fae22 100644 --- a/charts/lago-rails/templates/hpa.yaml +++ b/charts/lago-rails/templates/hpa.yaml @@ -4,7 +4,7 @@ kind: HorizontalPodAutoscaler metadata: name: {{ include "lago-rails.architecture.fullname" . }} labels: - {{- include "lago-rails.architecture.labels" . | nindent 4 }} + {{- include "lago-rails.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 @@ -42,7 +42,6 @@ spec: {{- $arm64Values := deepCopy .Values }} {{- $_ := set $arm64Values "internalArchitecture" "arm64" }} {{- $_ := set $arm64Values "internalArchitectureFullname" (include "lago-rails.fullname" .) }} -{{- $_ := set $arm64Values "internalArchitectureName" (include "lago-rails.name" .) }} {{- $_ := set $arm64Values "autoscaling" $arm64.autoscaling }} {{- $arm64Context := dict "Values" $arm64Values "Chart" .Chart "Release" .Release "Capabilities" .Capabilities "Template" .Template "Files" .Files }} {{ include "lago-rails.hpa" $arm64Context }} diff --git a/charts/lago-rails/templates/pdb.yaml b/charts/lago-rails/templates/pdb.yaml index a31c93f..0ec7520 100644 --- a/charts/lago-rails/templates/pdb.yaml +++ b/charts/lago-rails/templates/pdb.yaml @@ -1,10 +1,14 @@ -{{- define "lago-rails.pdb" -}} +{{/* +One PodDisruptionBudget for all architectures. The pods of both serve the same +Service, so one budget applies to them together. +*/}} +{{- if .Values.podDisruptionBudget.enabled }} apiVersion: policy/v1 kind: PodDisruptionBudget metadata: - name: {{ include "lago-rails.architecture.fullname" . }} + name: {{ include "lago-rails.fullname" . }} labels: - {{- include "lago-rails.architecture.labels" . | nindent 4 }} + {{- include "lago-rails.labels" . | nindent 4 }} spec: {{- with .Values.podDisruptionBudget.minAvailable }} minAvailable: {{ . }} @@ -14,21 +18,5 @@ spec: {{- end }} selector: matchLabels: - {{- include "lago-rails.architecture.selectorLabels" . | nindent 6 }} -{{- end }} - -{{- if .Values.podDisruptionBudget.enabled }} -{{- $amd64Values := deepCopy .Values }} -{{- $amd64Context := dict "Values" $amd64Values "Chart" .Chart "Release" .Release "Capabilities" .Capabilities "Template" .Template "Files" .Files }} -{{ include "lago-rails.pdb" $amd64Context }} -{{- $arm64 := .Values.architectures.arm64 }} -{{- if and .Values.global.architectures.arm64.enabled (or (gt (int $arm64.replicaCount) 0) $arm64.autoscaling.enabled) }} ---- -{{- $arm64Values := deepCopy .Values }} -{{- $_ := set $arm64Values "internalArchitecture" "arm64" }} -{{- $_ := set $arm64Values "internalArchitectureFullname" (include "lago-rails.fullname" .) }} -{{- $_ := set $arm64Values "internalArchitectureName" (include "lago-rails.name" .) }} -{{- $arm64Context := dict "Values" $arm64Values "Chart" .Chart "Release" .Release "Capabilities" .Capabilities "Template" .Template "Files" .Files }} -{{ include "lago-rails.pdb" $arm64Context }} -{{- end }} + {{- include "lago-rails.service.selectorLabels" . | nindent 6 }} {{- end }} diff --git a/charts/lago-rails/templates/service.yaml b/charts/lago-rails/templates/service.yaml index 6273ca3..890269b 100644 --- a/charts/lago-rails/templates/service.yaml +++ b/charts/lago-rails/templates/service.yaml @@ -1,10 +1,14 @@ -{{- define "lago-rails.service" -}} +{{/* +One Service for all architectures. The selector has no +`app.kubernetes.io/component`, so it matches amd64 pods and arm64 pods. +*/}} +{{- if .Values.service.enabled }} apiVersion: v1 kind: Service metadata: - name: {{ include "lago-rails.architecture.fullname" . }} + name: {{ include "lago-rails.fullname" . }} labels: - {{- include "lago-rails.architecture.labels" . | nindent 4 }} + {{- include "lago-rails.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: @@ -13,21 +17,5 @@ spec: protocol: TCP name: http selector: - {{- include "lago-rails.architecture.selectorLabels" . | nindent 4 }} -{{- end }} - -{{- if .Values.service.enabled }} -{{- $amd64Values := deepCopy .Values }} -{{- $amd64Context := dict "Values" $amd64Values "Chart" .Chart "Release" .Release "Capabilities" .Capabilities "Template" .Template "Files" .Files }} -{{ include "lago-rails.service" $amd64Context }} -{{- $arm64 := .Values.architectures.arm64 }} -{{- if and .Values.global.architectures.arm64.enabled (or (gt (int $arm64.replicaCount) 0) $arm64.autoscaling.enabled) }} ---- -{{- $arm64Values := deepCopy .Values }} -{{- $_ := set $arm64Values "internalArchitecture" "arm64" }} -{{- $_ := set $arm64Values "internalArchitectureFullname" (include "lago-rails.fullname" .) }} -{{- $_ := set $arm64Values "internalArchitectureName" (include "lago-rails.name" .) }} -{{- $arm64Context := dict "Values" $arm64Values "Chart" .Chart "Release" .Release "Capabilities" .Capabilities "Template" .Template "Files" .Files }} -{{ include "lago-rails.service" $arm64Context }} -{{- end }} + {{- include "lago-rails.service.selectorLabels" . | nindent 4 }} {{- end }} diff --git a/charts/lago/tests/architecture_test.yaml b/charts/lago/tests/architecture_test.yaml index 9d0a61f..23c8a1d 100644 --- a/charts/lago/tests/architecture_test.yaml +++ b/charts/lago/tests/architecture_test.yaml @@ -2,6 +2,7 @@ suite: Test amd64 and arm64 API and worker deployments templates: - charts/api/templates/deployment.yaml - charts/worker/templates/deployment.yaml + - charts/api/templates/service.yaml set: global: @@ -201,6 +202,59 @@ tests: - hasDocuments: count: 1 + - it: keeps a separate name in each deployment selector + template: charts/api/templates/deployment.yaml + asserts: + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/name: lago-api + app.kubernetes.io/instance: RELEASE-NAME + documentIndex: 0 + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/name: lago-api-arm64 + app.kubernetes.io/instance: RELEASE-NAME + documentIndex: 1 + + - it: gives the pods of both architectures the same component label + template: charts/api/templates/deployment.yaml + asserts: + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/component"] + value: lago-api + documentIndex: 0 + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/component"] + value: lago-api + documentIndex: 1 + + - it: renders one API service that selects the component when arm64 is on + template: charts/api/templates/service.yaml + asserts: + - hasDocuments: + count: 1 + - equal: + path: spec.selector + value: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/component: lago-api + + - it: keeps the original API service selector when arm64 is off + template: charts/api/templates/service.yaml + set: + global: + architectures: + arm64: + enabled: false + asserts: + - equal: + path: spec.selector + value: + app.kubernetes.io/name: lago-api + app.kubernetes.io/instance: RELEASE-NAME + - it: renders an arm64 worker without replicas when KEDA controls it template: charts/worker/templates/deployment.yaml set: