Skip to content

Commit 35de286

Browse files
authored
Merge pull request #160 from samuel-esp/feat/pdb-with-percentage
Feat: Scale PodDisruptionBudgets With Percentage Values
2 parents 80e88d7 + 3e3b96c commit 35de286

7 files changed

Lines changed: 475 additions & 61 deletions

File tree

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,11 +617,21 @@ The feature to scale DaemonSets can be very useful for reducing the base occupan
617617
618618
The feature to scale PodDisruptionBudgets can be useful to relax availability constraints on workloads. If enabled, the PodDisruptionBudget algorithm works like this:
619619
620-
1. Downtime Hours: Kube Downscaler will bring `minAvailable` and `maxUnavailable` to 0
620+
1. Downtime Hours: Kube Downscaler will bring `minAvailable` and `maxUnavailable` to downtime replicas value
621621
2. Uptime Hours: Kube Downscaler will bring back `minAvailable` and `maxUnavailable` to their original value
622622
623-
**Important**: Kube Downscaler, actually, cannot process values written in percentages. Resources with `minAvailable` or `maxUnavailable` written as percentages will be automatically excluded from scaling
623+
**Percentage Values**: Kube Downscaler can process PodDisruptionBudgets with `minAvailable` or `maxUnavailable`. In this case, during downtime hours, the following behavior applies:
624+
1. The `downscaler/original-replicas` annotation will store the original percentage value (e.g., "75%") as a string.
625+
2. The `minAvailable`/`maxUnavailable` field of the object will be updated to the target downtime replicas value (e.g., 0) as an integer.
624626
627+
The original percentage value in the `minAvailable`/`maxUnavailable` field will be restored once the downtime hours end
628+
629+
**Downtime Replicas**: Kube Downscaler can process downtime replicas specified as a percentage. The behavior will be the following:
630+
- If `--include-resources` only scales PodDisruptionBudgets, the value assigned to downtime replicas can be set as a percentage inside the global argument value `--downtime-replicas` or as a namespace/workload
631+
annotation `downscaler/donwtime-replicas` (e.g. `--donwtime-replicas="20%"` or `downscaler/donwtime-replicas: "10%"`)
632+
- If `--include-resources` scales PodDisruptionBudgets and other resources, using a percentage value inside the global argument value `--downtime-replicas` or as a namespace
633+
annotation `downscaler/donwtime-replicas` will result in all other resources under its scope being ignored (e.g. if you set `--downtime-replicas="10%"` all resources inside the cluster other than PodDisruptionBudgets will be ignored,
634+
if you set `downscaler/donwtime-replicas: "10%"` at namespace level, all resources other than PodDisruptionBudgets in that namespace will be ignored)
625635
### Scaling ScaledObjects
626636
627637
The ability to downscale ScaledObjects is very useful for workloads that use Keda to support

kube_downscaler/cmd.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ def get_parser():
111111
)
112112
parser.add_argument(
113113
"--downtime-replicas",
114-
type=int,
115-
help="Default amount of replicas when downscaling (default: 0)",
116-
default=int(os.getenv("DOWNTIME_REPLICAS", 0)),
114+
type=str,
115+
help="Default value used when downscaling (default: '0')",
116+
default=os.getenv("DOWNTIME_REPLICAS", "0"),
117117
)
118118
parser.add_argument(
119119
"--deployment-time-annotation",

kube_downscaler/helper.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,30 @@ def get_kube_api(timeout: int):
7171
return api
7272

7373

74+
def parse_int_or_percent(value, context, allow_negative):
75+
s = str(value).strip()
76+
77+
if s.endswith("%"):
78+
number_part = s[:-1].strip()
79+
if number_part.isdigit():
80+
val = int(number_part)
81+
if 0 <= val <= 100:
82+
return val, True
83+
else:
84+
raise ValueError(f"Percentage in {context} must be between 0 and 100.")
85+
else:
86+
raise ValueError(f"Invalid percentage format in {context}: must be digits before '%'.")
87+
88+
if allow_negative:
89+
if (s.startswith("-") and s[1:].isdigit()) or s.isdigit():
90+
return int(s), False
91+
else:
92+
if s.isdigit():
93+
return int(s), False
94+
95+
raise ValueError(f"Invalid format for {context}: must be an integer like '10' or a percentage like '10%'.")
96+
97+
7498
def add_event(resource, message: str, reason: str, event_type: str, dry_run: bool):
7599
event = (
76100
pykube.objects.Event.objects(resource.api)

kube_downscaler/main.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@
77
from kube_downscaler import cmd
88
from kube_downscaler import shutdown
99
from kube_downscaler.scaler import scale
10+
from kube_downscaler import helper
1011

1112
logger = logging.getLogger("downscaler")
1213

1314

15+
def parse_downtime_replicas(downtime_replicas):
16+
value, is_percentage = helper.parse_int_or_percent(downtime_replicas, context="--downtime-replicas", allow_negative=False)
17+
return value, is_percentage
18+
1419
def main(args=None):
1520
parser = cmd.get_parser()
1621
args = parser.parse_args(args)
@@ -87,6 +92,8 @@ def run_loop(
8792
else:
8893
constrained_downscaler = False
8994

95+
downtime_replicas, is_downtime_replicas_percentage = parse_downtime_replicas(downtime_replicas)
96+
9097
while True:
9198
try:
9299
scale(
@@ -108,6 +115,7 @@ def run_loop(
108115
api_server_timeout=api_server_timeout,
109116
max_retries_on_conflict=max_retries_on_conflict,
110117
downtime_replicas=downtime_replicas,
118+
is_downtime_replicas_percentage=is_downtime_replicas_percentage,
111119
deployment_time_annotation=deployment_time_annotation,
112120
enable_events=enable_events,
113121
matching_labels=frozenset(

0 commit comments

Comments
 (0)