[platform] Switch cozystack-api from DaemonSet to Deployment with PreferClose (#2041)

## What this PR does

Replace DaemonSet-based deployment of cozystack-api with a regular
Deployment
using Service `trafficDistribution: PreferClose`. Previously,
cozystack-api ran
as a DaemonSet on control-plane nodes with `internalTrafficPolicy:
Local` and
direct host kube-apiserver access, providing strict node-local routing
with no
fallback. The new approach prefers routing to the nearest pod but
gracefully
falls back to remote pods when the local one is unavailable.

Key changes:
- Always deploy as Deployment instead of conditional
DaemonSet/Deployment
- Use `trafficDistribution: PreferClose` on the Service (matching
seaweedfs pattern)
- Remove `KUBERNETES_SERVICE_HOST`/`KUBERNETES_SERVICE_PORT` override
- Replace hard `nodeSelector` with soft node affinity for control-plane
nodes
- Simplify the migration hook to always clean up leftover DaemonSet

### Release note

```release-note
[platform] Switch cozystack-api from DaemonSet to Deployment with trafficDistribution: PreferClose for improved resilience
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Breaking Changes**
* Removed localK8sAPIEndpoint configuration option; existing deployments
relying on this setting require reconfiguration.

* **Infrastructure Updates**
* Simplified deployment resource strategy and updated service traffic
distribution behavior to fixed configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Andrei Kvapil 2026-02-13 14:16:02 +01:00 committed by GitHub
commit 0faab1fa98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 12 additions and 46 deletions

View file

@ -1,18 +1,12 @@
apiVersion: apps/v1
{{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
kind: DaemonSet
{{- else }}
kind: Deployment
{{- end }}
metadata:
name: cozystack-api
namespace: cozy-system
labels:
app: cozystack-api
spec:
{{- if not .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
replicas: {{ .Values.cozystackAPI.replicas }}
{{- end }}
selector:
matchLabels:
app: cozystack-api
@ -24,27 +18,19 @@ spec:
tolerations:
- operator: Exists
serviceAccountName: cozystack-api
{{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
{{- with .Values.cozystackAPI.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
containers:
- name: cozystack-api
args:
- --tls-cert-file=/tmp/cozystack-api-certs/tls.crt
- --tls-private-key-file=/tmp/cozystack-api-certs/tls.key
{{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
env:
- name: KUBERNETES_SERVICE_HOST
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: KUBERNETES_SERVICE_PORT
value: "6443"
{{- end }}
image: "{{ .Values.cozystackAPI.image }}"
ports:
- containerPort: 443

View file

@ -1,16 +1,5 @@
{{- $shouldRunUpdateHook := false }}
{{- $previousKind := "Deployment" }}
{{- $previousKindPlural := "deployments" }}
{{- if not .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
{{- $previousKind = "DaemonSet" }}
{{- $previousKindPlural = "daemonsets" }}
{{- end }}
{{- $previous := lookup "apps/v1" $previousKind .Release.Namespace "cozystack-api" }}
{{- $previous := lookup "apps/v1" "DaemonSet" .Release.Namespace "cozystack-api" }}
{{- if $previous }}
{{- $shouldRunUpdateHook = true }}
{{- end }}
{{- if $shouldRunUpdateHook }}
---
apiVersion: batch/v1
kind: Job
@ -36,7 +25,7 @@ spec:
- -exc
- |-
kubectl --namespace={{ .Release.Namespace }} delete --ignore-not-found \
{{ $previousKindPlural }}.apps cozystack-api
daemonsets.apps cozystack-api
restartPolicy: Never
---
apiVersion: rbac.authorization.k8s.io/v1
@ -51,7 +40,7 @@ rules:
- apiGroups:
- "apps"
resources:
- "{{ $previousKindPlural }}"
- "daemonsets"
verbs:
- get
- delete

View file

@ -4,9 +4,7 @@ metadata:
name: cozystack-api
namespace: cozy-system
spec:
{{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
internalTrafficPolicy: Local
{{- end }}
trafficDistribution: PreferClose
ports:
- port: 443
protocol: TCP

View file

@ -1,10 +1,3 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-beta.3@sha256:a263702859c36f85d097c300c1be462aaa45a3955d69e9ae72a37cabf6dadaa9
localK8sAPIEndpoint:
enabled: true
replicas: 2
# nodeSelector for DaemonSet mode (localK8sAPIEndpoint.enabled: true)
# Talos uses empty value: "node-role.kubernetes.io/control-plane": ""
# Generic k8s (k3s, kubeadm) uses: "node-role.kubernetes.io/control-plane": "true"
nodeSelector:
node-role.kubernetes.io/control-plane: ""