Merge pull request #8522 from dokku/fix-keda-scaling

Only emit keda fallback when a non-cpu/memory trigger exists
This commit is contained in:
Jose Diaz-Gonzalez
2026-04-29 02:27:03 -04:00
committed by GitHub
3 changed files with 127 additions and 0 deletions

View File

@@ -476,6 +476,8 @@ Each value in the `metadata` stanza can use the following interpolated strings:
- `DOKKU_PROCESS_TYPE`: The name of the process being scaled
- `DOKKU_APP_NAME`: The name of the app being scaled
Dokku configures a Keda `fallback` (with a failure threshold of `3` and the configured replica count) on the generated `ScaledObject` so that scaling falls back to the configured replica count when a scaler's metric source becomes unavailable. Keda only allows `fallback` when at least one trigger is something other than `cpu` or `memory`, so Dokku omits the `fallback` block when every configured trigger is a `cpu` or `memory` scaler.
##### HTTP Autoscaling
In addition to the built-in scalers that Keda provides, Dokku also supports Keda's HTTP Add On. This requires that the addon be properly installed and configured. For existing k3s clusters, this can be performed by the `scheduler-k3s:ensure-charts` command:

View File

@@ -33,9 +33,17 @@ spec:
pollingInterval: {{ $config.autoscaling.polling_interval_seconds }}
minReplicaCount: {{ $config.autoscaling.min_replicas }}
maxReplicaCount: {{ $config.autoscaling.max_replicas }}
{{- $hasFallbackTrigger := false }}
{{- range $config.autoscaling.triggers }}
{{- if and (ne .type "cpu") (ne .type "memory") }}
{{- $hasFallbackTrigger = true }}
{{- end }}
{{- end }}
{{- if $hasFallbackTrigger }}
fallback:
failureThreshold: 3
replicas: {{ $config.replicas }}
{{- end }}
{{- if $config.autoscaling.triggers }}
triggers:
{{- with $config.autoscaling.triggers }}

View File

@@ -52,6 +52,66 @@ teardown() {
assert_output "5000"
}
@test "(scheduler-k3s) deploy autoscaling cpu-only trigger [keda-fallback]" {
if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then
skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN"
fi
INGRESS_CLASS=nginx install_k3s
run /bin/bash -c "dokku apps:create $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP inject_cpu_autoscaling_app_json
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "kubectl get scaledobjects.keda.sh $TEST_APP-web -o=jsonpath='{.spec.fallback}'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output ""
run /bin/bash -c "kubectl get scaledobjects.keda.sh $TEST_APP-web -o=jsonpath='{.spec.triggers[0].type}'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "cpu"
}
@test "(scheduler-k3s) deploy autoscaling external trigger [keda-fallback]" {
if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then
skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN"
fi
INGRESS_CLASS=nginx install_k3s
run /bin/bash -c "dokku apps:create $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP inject_prometheus_autoscaling_app_json
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "kubectl get scaledobjects.keda.sh $TEST_APP-web -o=jsonpath='{.spec.fallback.failureThreshold}'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "3"
run /bin/bash -c "kubectl get scaledobjects.keda.sh $TEST_APP-web -o=jsonpath='{.spec.fallback.replicas}'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "1"
}
inject_app_json() {
local APP="$1"
local APP_REPO_DIR="$2"
@@ -68,3 +128,60 @@ inject_app_json() {
}
EOF
}
inject_cpu_autoscaling_app_json() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
cat <<EOF >"$APP_REPO_DIR/app.json"
{
"formation": {
"web": {
"autoscaling": {
"min_quantity": 1,
"max_quantity": 3,
"triggers": [
{
"name": "cpu-trigger",
"type": "cpu",
"metadata": {
"value": "80"
}
}
]
}
}
}
}
EOF
}
inject_prometheus_autoscaling_app_json() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
cat <<EOF >"$APP_REPO_DIR/app.json"
{
"formation": {
"web": {
"autoscaling": {
"min_quantity": 1,
"max_quantity": 3,
"triggers": [
{
"name": "prom-trigger",
"type": "prometheus",
"metadata": {
"serverAddress": "http://prometheus.example.com:9090",
"metricName": "http_requests_total",
"threshold": "100",
"query": "sum(rate(http_requests_total{deployment=\"[[ .DEPLOYMENT_NAME ]]\"}[2m]))"
}
}
]
}
}
}
}
EOF
}