Skip to content

feat: allow RollingUpdate to be specified for k8s Deployment - #497

Open
otan wants to merge 3 commits into
temporalio:mainfrom
otan:feat/deployment-rolling-update-strategy
Open

feat: allow RollingUpdate to be specified for k8s Deployment#497
otan wants to merge 3 commits into
temporalio:mainfrom
otan:feat/deployment-rolling-update-strategy

Conversation

@otan

@otan otan commented Aug 3, 2026

Copy link
Copy Markdown

What

Adds an optional spec.deploymentStrategy field on WorkerDeployment that mirrors apps/v1 Deployment.spec.strategy. The controller sets it on owned versioned Deployments on create/update and reconciles drift so settings like maxUnavailable / maxSurge stick.

This is distinct from spec.rollout.strategy, which controls Temporal traffic routing across versions.

RollingUpdate settings mainly matter for in-place pod rolls on an existing version Deployment (for example kubectl rollout restart, or a pod-template change while unsafeCustomBuildID stays the same). The common new-build-ID path creates a fresh Deployment scaled up from zero, so those settings have little practical effect there even though the field is still written onto the object.

Why

Owned Deployments currently inherit the Kubernetes default rolling update (25% / 25%). On large fleets, in-place restarts of a Current version (same build ID) can take too many pollers offline at once and spike schedule_to_start. Users need a conservative strategy such as maxUnavailable: 5%.

Testing

  • make generate manifests
  • go test ./internal/k8s/ ./internal/planner/ -run 'Strategy|ApplyDeploymentStrategyDefaults|ReplicasNilPreserved'

Resolves #496

@otan
otan requested review from a team, eniko-dif and jlegrone as code owners August 3, 2026 19:39
@otan
otan force-pushed the feat/deployment-rolling-update-strategy branch from a728390 to baf447c Compare August 3, 2026 19:42
@CLAassistant

CLAassistant commented Aug 3, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ jaypipes
❌ otan
You have signed the CLA already but the status is still pending? Let us recheck it.

@otan
otan force-pushed the feat/deployment-rolling-update-strategy branch 2 times, most recently from f171c37 to e880e0e Compare August 3, 2026 19:43
@jaypipes

jaypipes commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

@otan thanks so much for this contribution! I'll review a little later today or tomorrow morning. Been looking into some bug fixes earlier this week.

@jaypipes jaypipes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@otan thanks for this! :) Code looks good to me except for a couple requests (see inline). Address those small issues and I'll aim to get this into the v1.9.0 release (set to be cut on August 18th)

Comment thread api/v1alpha1/deprecated_temporalworkerdeployment_types.go Outdated
Comment thread api/v1alpha1/workerdeployment_types.go Outdated
Comment thread internal/k8s/deployments.go
@otan
otan requested a review from jaypipes August 6, 2026 21:05
@otan

otan commented Aug 6, 2026

Copy link
Copy Markdown
Author

all good!

@jaypipes jaypipes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesomesauce, thank you so much @otan :)

Comment thread internal/planner/planner.go
Comment thread internal/k8s/deployments.go
@otan
otan requested a review from eniko-dif August 7, 2026 20:19
@otan
otan force-pushed the feat/deployment-rolling-update-strategy branch from 9f11f30 to 7b3824e Compare August 7, 2026 20:19
@otan

otan commented Aug 7, 2026

Copy link
Copy Markdown
Author

all done

@otan
otan force-pushed the feat/deployment-rolling-update-strategy branch from 7b3824e to 8672c08 Compare August 7, 2026 20:19

@jaypipes jaypipes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @otan!

Comment thread api/v1alpha1/workerdeployment_types.go Outdated
Comment on lines +84 to +90
// DeploymentStrategy describes how to replace Pods for each versioned
// Deployment this controller owns. Mirrors apps/v1 Deployment.spec.strategy.
// When omitted, Kubernetes defaults apply (RollingUpdate with
// maxUnavailable/maxSurge 25%). This is distinct from spec.rollout.strategy,
// which controls Temporal traffic routing across worker versions.
// +optional
DeploymentStrategy *appsv1.DeploymentStrategy `json:"deploymentStrategy,omitempty"`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two concerns with this:

  1. Having both deploymentStrategy and rolloutStrategy may be confusing.
  2. So far we haven't committed to making the underlying deployment resource type part of the public API. Ie. we could switch to statefulsets or replicasets instead in the future. Ideally we should keep the worker spec agnostic to the underlying k8s resource types.

I also think it needs to be clarified in the documentation when deploymentStrategy would apply, since the controller computes a new build ID (and thus creates a fresh k8s deployment object) for each new worker version. For example does this only matter if you manually perform a rolling update via kubectl rollout restart for a specific version?

Do we actually need to support the Recreate deployment strategy? If not, what would it look like if we only exposed maxUnavailable as an option? I think my concerns would be resolved if the new config was named like spec.maxUnavailablePerVersion which doesn't tie us to deployments as the underlying resource type.

@otan otan Aug 10, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having both deploymentStrategy and rolloutStrategy may be confusing.

For example does this only matter if you manually perform a rolling update via kubectl rollout restart for a specific version?

yep. we just have no control over kubectl rollout restart at the moment (and we do a manual HPA patch to get around it). i can amend that - i originally wanted to keep it to just strategy to meet the original k8s Worker definition but was told to change it. i'm happy to rename it to whatever makes you more comfortable.

So far we haven't committed to making the underlying deployment resource type part of the public API. Ie. we could switch to statefulsets or replicasets instead in the future. Ideally we should keep the worker spec agnostic to the underlying k8s resource types.

AFAICT, this deployment.strategy is an option available to both statefulsets or replicasets

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, this deployment.strategy is an option available to both statefulsets or replicasets

Could you share what gives you that impression? I did some reading on both, and it seems that ReplicaSets (https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) don't have such a strategy at all; if you update the pod template of a ReplicaSet, the ReplicaSet won't change or replace any of its Pods that are already running with a different template. If, later on, the ReplicaSet wants more replicas, it will use the new pod template to create new pods (still not touching the existing pods).

StatefulSets (https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies) have a rolling update strategy option which goes one-by-one by default. In k8s v1.35, there is a maxUnavailable field, but it is in beta.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we just have no control over kubectl rollout restart at the moment (and we do a manual HPA patch to get around it).

Could you explain more what you mean by these two things? Curious about your use case (so we can try to support it without re-packaging Deployment fields if we can)

@otan otan Aug 17, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I purely want to be able to do a kubectl rollout restart deployment <name> without having it restart up to 25% pods at a time. I have to manually patch my deployment to do this at the moment.

we ... currently have to do restarts regularly to clean up some in-app-worker-state that can get borked because, e.g. node memory leaks. forcing a new deployment to do this means we have extra pinned worker versions for no reason.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for elaborating! I was hung up on trying to understand how a "manual HPA patch" would help you, but patching the Deployments makes sense. And I agree folks should be able to do a rolling restart with conservative settings.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a crazy idea: what if instead of exposing a new knob here, we just make the rolling update settings for all worker-controller-managed deployments conservative by default (ie. maxUnavailable=1)?

That is what the default StatefulSet update strategy does, fwiw

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, this deployment.strategy is an option available to both statefulsets or replicasets

Could you share what gives you that impression? I did some reading on both, and it seems that ReplicaSets (https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) don't have such a strategy at all; if you update the pod template of a ReplicaSet, the ReplicaSet won't change or replace any of its Pods that are already running with a different template. If, later on, the ReplicaSet wants more replicas, it will use the new pod template to create new pods (still not touching the existing pods).

Yeah, because ReplicaSets are kind of a hidden behind-the-scenes resource grouping mechanism. The Deployment is the thing that has multiple ReplicaSets that correspond to the unique hash of the Deployment's pod template spec so when referring to deployment.strategy it's really just referring to the DeploymentSpec.strategy...

@jaypipes

Copy link
Copy Markdown
Collaborator

@otan we merged a couple fixes in internal/planner/planner.go that have caused some conflicts for this patch. would you mind pushing a quick conflict-resolving patch? If you want me to do it, let me know, I just didn't want to step on your toes. :)

Expose maxUnavailable/maxSurge on owned version Deployments for less disruptive in-place restarts.
@otan
otan force-pushed the feat/deployment-rolling-update-strategy branch from 7d290f8 to 6e62107 Compare August 17, 2026 11:44
@otan

otan commented Aug 17, 2026

Copy link
Copy Markdown
Author

all done i think

@jaypipes jaypipes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@otan I think the PR summary might need to be updated to reflect the reality of the patch. See inline...

Comment thread api/v1alpha1/workerdeployment_types.go Outdated
Comment thread docs/configuration.md Outdated

### Deployment Rolling Update Strategy

Controls how Pods are replaced **within a single versioned Kubernetes `Deployment`**. It does **not** affect Temporal traffic routing across worker versions (`spec.rollout.strategy`), and it does **not** apply when the controller creates a new version.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the PR summary seemingly contradicting this:

and it does not apply when the controller creates a new version.

@otan otan Aug 17, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hehe, sorry a bit of a kubernetes noob and this slid through me (deployment version vs temporal version so I thought they made sense) - updating accordingly.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, these comments are too verbose / detail oriented (AI justifying itself) - to cut the crap i'll just remove them

@otan
otan force-pushed the feat/deployment-rolling-update-strategy branch from 16ba7f8 to f32cda5 Compare August 17, 2026 12:25
@otan

otan commented Aug 17, 2026

Copy link
Copy Markdown
Author

i'm about to nod off (sydney time), feel free to amend accordingly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose Deployment rollingUpdate strategy (maxUnavailable/maxSurge) on TemporalWorkerDeployment

6 participants