diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 87ffcd5..67c4b8c 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: socket-firewall description: Socket.dev Registry Firewall - Block vulnerable packages before they reach your cluster type: application -version: 0.4.1 -appVersion: "1.1.335" +version: 0.5.0 +appVersion: "1.1.341" keywords: - security - supply-chain diff --git a/helm/README.md b/helm/README.md index b19cddc..c23019b 100644 --- a/helm/README.md +++ b/helm/README.md @@ -122,6 +122,7 @@ registries: | `ingress.className` | Ingress class (nginx, alb, traefik) | `""` | | `autoscaling.enabled` | Enable HorizontalPodAutoscaler | `false` | | `podDisruptionBudget.enabled` | Keep pods available during node maintenance | `true` | +| `topologySpreadConstraints` | Evenly spread replicas across zones/nodes | `[]` | | `extraContainers` | Sidecar containers (auth proxies, log collectors) | `[]` | | `resources.limits.cpu` | CPU limit | `4` | | `resources.limits.memory` | Memory limit | `8Gi` | @@ -492,6 +493,35 @@ kubectl get hpa socket-firewall kubectl describe hpa socket-firewall ``` +## Spreading Replicas Across Zones + +When you run more than one replica (via `replicaCount` or autoscaling), use +`topologySpreadConstraints` to distribute pods evenly across availability zones +(or nodes) so a single zone/node failure can't take down a disproportionate share +of the fleet. This is preferred over soft pod anti-affinity, which the scheduler is +free to ignore and can pile replicas into one zone. + +```yaml +replicaCount: 3 +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway # best-effort even spread; never blocks scheduling + labelSelector: + matchLabels: + app.kubernetes.io/name: socket-firewall +``` + +- `maxSkew: 1` keeps zones within one pod of each other. +- `whenUnsatisfiable: ScheduleAnyway` is a soft guarantee (recommended). Use + `DoNotSchedule` for a hard guarantee — but be aware pods can stay `Pending` if a + zone is full or you have fewer zones than replicas. +- Pin spreading to a specific revision by adding `matchLabelKeys: [pod-template-hash]` + (requires Kubernetes 1.27+, satisfied by all currently-supported EKS and GKE versions). + +**Compatibility:** `topologySpreadConstraints` is GA since Kubernetes 1.19, so it works +on every currently-supported cluster. The chart sets no `kubeVersion` floor. + ## Using an Existing Secret for API Token ```bash diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 0b510d3..e4c639b 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -369,6 +369,10 @@ spec: affinity: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} diff --git a/helm/values.yaml b/helm/values.yaml index 92f7275..5fcc5e1 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -5,7 +5,7 @@ image: repository: socketdev/socket-registry-firewall # Defaults to the latest published image so deployments pick up new releases # without a chart bump. Pin a version for reproducibility, e.g. tag: "1.1.327". - tag: "1.1.335" + tag: "1.1.341" # Always (not IfNotPresent) so "latest" actually refreshes on pod restart. pullPolicy: Always @@ -432,6 +432,22 @@ tolerations: [] # Affinity affinity: {} +# Topology spread constraints (evenly distribute replicas across failure domains +# such as zones or nodes). Preferred over soft pod anti-affinity for even spreading. +# Empty by default; only takes effect with replicaCount > 1 or autoscaling enabled. +# The base field is GA since Kubernetes 1.19 (universally available). The optional +# matchLabelKeys field needs Kubernetes 1.27+ (satisfied by all current EKS/GKE versions). +topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: ScheduleAnyway # use DoNotSchedule for a hard guarantee + # labelSelector: + # matchLabels: + # app.kubernetes.io/name: socket-firewall + # # matchLabelKeys requires Kubernetes 1.27+ + # # matchLabelKeys: + # # - pod-template-hash + # Extra containers to run alongside the firewall (e.g., auth sidecars, log collectors) extraContainers: [] # - name: auth-sidecar