Skip to content

Commit e97fb91

Browse files
Pulling in do-not-disrupt grace period
1 parent 6613010 commit e97fb91

4 files changed

Lines changed: 65 additions & 14 deletions

File tree

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ require (
123123
golang.org/x/oauth2 v0.34.0 // indirect
124124
golang.org/x/sys v0.40.0 // indirect
125125
golang.org/x/term v0.39.0 // indirect
126-
golang.org/x/text v0.33.0 // indirect
126+
golang.org/x/text v0.34.0 // indirect
127127
golang.org/x/time v0.14.0 // indirect
128128
golang.org/x/tools v0.41.0 // indirect
129129
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
@@ -140,3 +140,5 @@ require (
140140
sigs.k8s.io/randfill v1.0.0 // indirect
141141
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect
142142
)
143+
144+
replace sigs.k8s.io/karpenter => github.com/AndrewMitchell25/karpenter v0.0.0-20260304191417-cd640b0054df

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/AndrewMitchell25/karpenter v0.0.0-20260304191417-cd640b0054df h1:k96lLlCzT/ZoVsT8OWtmgzI8eErOFy6KdkhVKLwZJzA=
2+
github.com/AndrewMitchell25/karpenter v0.0.0-20260304191417-cd640b0054df/go.mod h1:7HVTLcR8uNwHcnwjfaCqV2ICF3aOPvngK/J8CBXZraU=
13
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
24
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
35
github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg=
@@ -323,8 +325,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
323325
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
324326
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
325327
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
326-
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
327-
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
328+
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
329+
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
328330
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
329331
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
330332
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -375,8 +377,6 @@ sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327U
375377
sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
376378
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
377379
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
378-
sigs.k8s.io/karpenter v1.9.1-0.20260220232539-5e12af134257 h1:Z7WZW+Hw8Naj3kOcHIZbHyIKwTDtzQzm0N9tgqdGZbY=
379-
sigs.k8s.io/karpenter v1.9.1-0.20260220232539-5e12af134257/go.mod h1:5NVeUwDmwHGnGIiqZhYCVfRx1uE5f9zdZsUYI34isIo=
380380
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
381381
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
382382
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E=

website/content/en/preview/concepts/disruption.md

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -360,20 +360,51 @@ In this scenario, Karpenter cannot voluntary disrupt the node because:
360360
361361
As seen in this example, the more PDBs there are affecting a Node, the more difficult it will be for Karpenter to find an opportunity to perform voluntary disruption actions.
362362
363-
Secondly, you can block Karpenter from voluntarily disrupting and draining pods by adding the `karpenter.sh/do-not-disrupt: "true"` annotation to the pod.
364-
You can treat this annotation as a single-pod, permanently blocking PDB.
363+
Secondly, you can block Karpenter from voluntarily disrupting and draining pods by adding the `karpenter.sh/do-not-disrupt` annotation to the pod.
364+
This annotation supports two formats:
365+
366+
| Format | Example | Behavior |
367+
|--------|---------|----------|
368+
| **Boolean** | `karpenter.sh/do-not-disrupt: "true"` | Provides permanent protection from disruption |
369+
| **Duration** | `karpenter.sh/do-not-disrupt: "30m"` | Provides time-based protection for the specified duration after the pod starts running |
370+
371+
#### Duration-Based Protection
372+
373+
When using the duration format, the annotation will be "active" and pods will be protected from disruption for the specified time period after they start running (based on `pod.status.startTime`).
374+
Once the duration expires, the annotation becomes inactive and the pod becomes eligible for disruption like any other pod.
375+
This is useful for workloads that need protection during startup or critical phases but can be safely disrupted later.
376+
377+
The duration value must be a valid Go `time.Duration` string. Supported formats include:
378+
379+
| Duration | Description |
380+
|----------|-------------|
381+
| `"5m"` | 5 minutes |
382+
| `"1h"` | 1 hour |
383+
| `"2h30m"` | 2 hours and 30 minutes |
384+
| `"24h"` | 24 hours |
385+
| `"1h30m45s"` | 1 hour, 30 minutes, and 45 seconds |
386+
387+
{{% alert title="Note" color="primary" %}}
388+
If an invalid duration is specified, the annotation will be ignored and an event will be emitted on the pod indicating that the duration format is invalid.
389+
{{% /alert %}}
390+
391+
#### Behavior and Consequences
392+
393+
You can treat this annotation as a single-pod blocking PDB that is active either permanently (boolean format) or temporarily while the duration hasn't elapsed (duration format).
365394
This has the following consequences:
366-
- Nodes with `karpenter.sh/do-not-disrupt` pods will be excluded from [Consolidation]({{<ref "#consolidation" >}}), and conditionally excluded from [Drift]({{<ref "#drift" >}}).
395+
- Nodes with active `karpenter.sh/do-not-disrupt` pods will be excluded from [Consolidation]({{<ref "#consolidation" >}}), and conditionally excluded from [Drift]({{<ref "#drift" >}}).
367396
- If the Node's owning NodeClaim has a [`terminationGracePeriod`]({{<ref "#terminationgraceperiod" >}}) configured, it will still be eligible for disruption via drift.
368-
- Like pods with a blocking PDB, pods with the `karpenter.sh/do-not-disrupt` annotation will **not** be gracefully evicted by the [Termination Controller]({{<ref "#termination-controller">}}).
397+
- Like pods with a blocking PDB, pods with an active `karpenter.sh/do-not-disrupt` annotation will **not** be gracefully evicted by the [Termination Controller]({{<ref "#termination-controller">}}).
369398
Karpenter will not be able to complete termination of the node until one of the following conditions is met:
370-
- All pods with the `karpenter.sh/do-not-disrupt` annotation are removed.
399+
- All pods with the `karpenter.sh/do-not-disrupt` annotation are removed, or their annotation becomes inactive (duration has elapsed).
371400
- All pods with the `karpenter.sh/do-not-disrupt` annotation have entered a [terminal phase](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase) (`Succeeded` or `Failed`).
372401
- The owning NodeClaim's [`terminationGracePeriod`]({{<ref "#terminationgraceperiod" >}}) has elapsed.
373402
374-
This is useful for pods that you want to run from start to finish without disruption.
375-
Examples of pods that you might want to opt-out of disruption include an interactive game that you don't want to interrupt or a long batch job (such as you might have with machine learning) that would need to start over if it were interrupted.
403+
#### Examples
376404
405+
This is useful for pods that you want to run from start to finish without disruption, or that need protection during critical startup phases.
406+
407+
**Permanent protection** - useful for interactive games or long-running batch jobs:
377408
```yaml
378409
apiVersion: apps/v1
379410
kind: Deployment
@@ -384,6 +415,18 @@ spec:
384415
karpenter.sh/do-not-disrupt: "true"
385416
```
386417
418+
**Duration-based protection** - useful for workloads with critical startup phases:
419+
```yaml
420+
apiVersion: apps/v1
421+
kind: Deployment
422+
spec:
423+
template:
424+
metadata:
425+
annotations:
426+
# Protect for 30 minutes after pod starts running
427+
karpenter.sh/do-not-disrupt: "30m"
428+
```
429+
387430
{{% alert title="Note" color="primary" %}}
388431
The `karpenter.sh/do-not-disrupt` annotation does **not** exclude nodes from the forceful disruption methods: [Expiration]({{<ref "#expiration" >}}), [Interruption]({{<ref "#interruption" >}}), [Node Repair](<ref "#node-repair" >), and manual deletion (e.g. `kubectl delete node ...`).
389432
While both interruption and node repair have implicit upper-bounds on termination time, expiration and manual termination do not.

website/content/en/preview/troubleshooting.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,9 +482,15 @@ Review what [disruptions are](https://kubernetes.io/docs/concepts/workloads/pods
482482

483483
#### `karpenter.sh/do-not-disrupt` Annotation
484484

485-
If a pod exists with the annotation `karpenter.sh/do-not-disrupt: true` on a node, and a request is made to delete the node, Karpenter will not drain any pods from that node or otherwise try to delete the node. Nodes that have pods with a `do-not-disrupt` annotation are not considered for consolidation, though their unused capacity is considered for the purposes of running pods from other nodes which can be consolidated.
485+
If a pod exists with an active `karpenter.sh/do-not-disrupt` annotation on a node, and a request is made to delete the node, Karpenter will not drain any pods from that node or otherwise try to delete the node. The annotation is considered "active" when:
486+
- Set to `"true"` (permanent protection)
487+
- Set to a valid duration (e.g., `"30m"`) and the pod has been running for less than that duration
486488

487-
If you want to terminate a node with a `do-not-disrupt` pod, you can simply remove the annotation and the deprovisioning process will continue.
489+
Nodes that have pods with an active `do-not-disrupt` annotation are not considered for consolidation, though their unused capacity is considered for the purposes of running pods from other nodes which can be consolidated.
490+
491+
If you want to terminate a node with a `do-not-disrupt` pod, you can either remove the annotation from the pod or wait for duration-based protection to expire naturally, and the deprovisioning process will continue.
492+
493+
For more details on how this annotation works, see [Pod-Level Controls]({{<ref "./disruption#pod-level-controls" >}}) in the Disruption documentation.
488494

489495
#### Scheduling Constraints (Consolidation Only)
490496

0 commit comments

Comments
 (0)