Repository navigation
feat(manifests): Support high availability for the controller manager in the Helm chart - #4147
myselfkunal wants to merge 4 commits into
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
andreyvelich
left a comment
There was a problem hiding this comment.
Thanks @myselfkunal for this PR! Note: this review was done with the help of AI tools.
| matchLabels: | ||
| app.kubernetes.io/part-of: kubeflow | ||
| app.kubernetes.io/component: manager |
There was a problem hiding this comment.
Use the chart's manager selector labels so the anti-affinity only matches this release's manager pods (it currently also matches other Kubeflow component: manager pods), and keeps working when manager.selectorLabels is set. Please update the expected labels in deployment_test.yaml to match.
| matchLabels: | |
| app.kubernetes.io/part-of: kubeflow | |
| app.kubernetes.io/component: manager | |
| matchLabels: | |
| {{- include "trainer.manager.selectorLabels" . | nindent 12 }} |
There was a problem hiding this comment.
Agreed, will apply this along with the expected labels in deployment_test.yaml. Holding the push until we settle the default location in the deployment.yaml thread, since that decides whether this stays in the helper.
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} | ||
| {{- with .Values.manager.affinity }} | ||
| {{- if .Values.manager.affinity }} |
There was a problem hiding this comment.
Neither affinity: {} nor affinity: null removes the default, so users can't opt out (e.g. if they want to rely only on topologySpreadConstraints). Can you document how to disable it, or add a toggle with a test?
There was a problem hiding this comment.
@myselfkunal we can put the default in the values.yaml rather than in the helper.
Please update the inline doc - e.g.
# -- Affinity for manager pods. Defaults to soft pod anti-affinity on node.
# Set to null to disable the default affinity rules.
There was a problem hiding this comment.
@andreyvelich @robert-bell thanks for the review. Before I push, I want to check one thing, since the two suggestions pull in different directions.
Moving the default into values.yaml makes null a clean opt-out, since Helm drops the key and if .Values.manager.affinity becomes false. But values.yaml cannot run templates, so the anti-affinity can no longer use trainer.manager.selectorLabels. The selector would go back to part-of: kubeflow + component: manager, which also matches other Kubeflow managers. A user-supplied affinity would also be deep-merged with the default instead of replacing it. For example, setting only nodeAffinity keeps the soft anti-affinity.
Two options:
- Default in values.yaml, rendered with
tplso the selector can use release-scoped labels (e.g.app.kubernetes.io/instance: "{{ .Release.Name }}"). Settingnulldisables it. - Keep the default in the helper with
trainer.manager.selectorLabels, and addmanager.defaultAffinity.enabled(defaulttrue) as the opt-out, with a unit test.
I lean towards option 2, since only the helper can use trainer.manager.selectorLabels (including user-set manager.selectorLabels), which Andrey asked for in the _helpers.tpl thread. Happy to go with option 1 if you prefer keeping the default in values.yaml. WDYT?
There was a problem hiding this comment.
Ah good point about needing to template the default affinity.
In which case I'd lean towards not providing any default and instead just documenting the HA antiaffinity in the readme.
Both a default and an additional configuration option introduce some maintenance and complexity for a use case that users can easily configure themselves. Wdyt?
There was a problem hiding this comment.
@robert-bell makes sense, agreed. It also means default installs render exactly the same as master again.
Plan:
- Drop
trainer.manager.defaultAffinityand keepmanager.affinity: {}as on master. - Add a README section with a recommended soft pod anti-affinity for HA, using the release's selector labels (
app.kubernetes.io/name,app.kubernetes.io/instance,app.kubernetes.io/component: manager). - Keep
podDisruptionBudget,topologySpreadConstraintsandpriorityClassNameas they are.
@andreyvelich this also covers the opt-out and selector label threads, since there is no default left to disable. Let me know if you would still prefer a default, otherwise I'll push this.
| # -- Affinity for manager pods. Empty by default, which falls back to a soft (preferred) | ||
| # pod anti-affinity that spreads manager replicas across different nodes by hostname | ||
| # (see `trainer.manager.defaultAffinity` in templates/manager/_helpers.tpl). This is a | ||
| # scheduling preference only: it never blocks a pod from being scheduled, including on a | ||
| # single-node cluster. Setting any value here fully replaces the default (it is not | ||
| # merged with it), so your own affinity is used exactly as given. |
There was a problem hiding this comment.
Can we keep this short, since helm-docs puts it in the README table?
| # -- Affinity for manager pods. Empty by default, which falls back to a soft (preferred) | |
| # pod anti-affinity that spreads manager replicas across different nodes by hostname | |
| # (see `trainer.manager.defaultAffinity` in templates/manager/_helpers.tpl). This is a | |
| # scheduling preference only: it never blocks a pod from being scheduled, including on a | |
| # single-node cluster. Setting any value here fully replaces the default (it is not | |
| # merged with it), so your own affinity is used exactly as given. | |
| # -- Affinity for manager pods. If empty, defaults to a soft pod anti-affinity that spreads | |
| # manager replicas across nodes. Setting a value fully replaces the default. |
There was a problem hiding this comment.
Will do. I'll shorten it once we settle the default location in the deployment.yaml thread, since the opt-out line depends on that (null vs a defaultAffinity.enabled toggle).
|
/cc @robert-bell @Sridhar1030 @tariq-hasan appreciate your quick look too if you can! |
c28b60e to
7653e8e
Compare
… in the Helm chart Add four opt-in `manager` values so the controller manager can run highly available: podDisruptionBudget (disabled by default, fails the template if enabled with replicas < 2), topologySpreadConstraints (empty by default), priorityClassName (empty by default, no PriorityClass is created), and a soft preferred podAntiAffinity shipped as the new default for affinity. Leader election already allows multiple replicas, but the webhooks default to failurePolicy: Fail, so any gap with zero Ready manager pods rejects every TrainJob/TrainingRuntime create and update. These values close that gap without changing behavior for existing installs: `helm template` on this branch vs. upstream/master, same values, differs only in the new affinity default, which is a soft scheduling preference and never blocks a pod from being scheduled. Verified webhook cert management holds up at replicas=2 on a 3-node kind cluster, including after force-deleting the leader pod mid-flight: caBundle stayed in sync with the cert Secret throughout, and TrainJob admission had no gap. Fixes kubeflow#4083 Signed-off-by: myselfkunal <kunalshawip@gmail.com>
Signed-off-by: myselfkunal <kunalshawip@gmail.com>
…ti-affinity Signed-off-by: myselfkunal <kunalshawip@gmail.com>
7653e8e to
22750e6
Compare
|
Thanks @myselfkunal! Could you address #4147 (comment) please? |
…Budget Signed-off-by: myselfkunal <kunalshawip@gmail.com>
|
The |
|
Thanks @myselfkunal! You can rerun tests using a comment /retest |
Summary
Adds three opt-in
managervalues so the controller manager can run highly available, and documents a recommended anti-affinity in the chart README:manager.podDisruptionBudget: disabled by default. Recommended withmanager.replicasof 2 or more, since a PDB on a single replica blocks node drains. The chart does not reject any combination; Kubernetes validates the PDB itself.manager.topologySpreadConstraints: empty by default, for stricter node or zone spreading.manager.priorityClassName: empty by default. The chart never creates aPriorityClass.manager.affinityis unchanged and stays{}. Instead, the new "High availability" section in the chart README shows a soft (preferred) pod anti-affinity scoped to the release's selector labels (app.kubernetes.io/name,app.kubernetes.io/instance,app.kubernetes.io/component).Why
Leader election already supports multiple replicas, but the webhooks default to
failurePolicy: Fail. Any moment with zero Ready manager pods rejects every TrainJob/TrainingRuntime create and update. That happens during a routinekubectl drain, an autoscaler scale-down, or GPU cluster preemption, since the manager runs at priority 0 today. These values give operators a documented way to avoid it.Backwards compatibility
No change at default values. I rendered the chart from
upstream/masterand from this branch with identical default values, then diffed the two. Both outputs are 32,738 lines, anddiffreports no differences.podDisruptionBudget,topologySpreadConstraints, andpriorityClassNameonly render when set, andaffinitybehaves exactly as onmaster.Testing
helm-unittest: 107/107 passing across 23 suites, including a new
Test manager PodDisruptionBudgetsuite covering the PDB not rendering by default or when disabled, rendering when enabled, and a customminAvailable.Test manager Deploymentcovers affinity not rendering by default, and render-only-when-set fortopologySpreadConstraintsandpriorityClassName.helm lint: clean at defaults.
kind cluster (3 nodes: 1 control-plane, 2 workers),
manager.replicas=2. This ran on an earlier revision of this PR, where a soft anti-affinity was the default. It is the same rule as the README example, with apart-of/componentselector instead of the release-scoped one.caBundleagainst the cert Secret. SHA-256 fingerprints matched across the Secret, both webhook configs, and both pods' mounted cert files, immediately after install and again about 4.5 minutes later.examples/yaml/01-multi-node.yaml. It admitted cleanly and the mutating webhook defaultedmanagedBycorrectly."no cert refresh needed"from the cert-rotation logger, so it adopts the existing cert from the Secret rather than generating its own. That is why this does not race.Rebased onto
masteratb514f69f. Tests and lint re-run clean after the rebase and the review follow-ups.Review follow-ups
manager.replicas < 2. The README and values.yaml recommend 2 or more replicas instead.Fixes #4083
Checklist: