feat: add support for the keda-http-add-on
Externally, it is treated the same as any other scaling trigger, but requires that the chart be installed in order for it to be supported.
This commit is contained in:
@@ -584,6 +584,13 @@ func getAutoscaling(input GetAutoscalingInput) (ProcessAutoscaling, error) {
|
||||
"DEPLOYMENT_NAME": fmt.Sprintf("%s-%s", input.AppName, input.ProcessType),
|
||||
}
|
||||
|
||||
validHttpScaleMethods := map[string]bool{
|
||||
"request_rate": true,
|
||||
"concurrency": true,
|
||||
}
|
||||
|
||||
hasHttpTrigger := false
|
||||
httpTrigger := ProcessAutoscalingTrigger{}
|
||||
triggers := []ProcessAutoscalingTrigger{}
|
||||
for idx, trigger := range config.Triggers {
|
||||
if trigger.Type == "" {
|
||||
@@ -625,15 +632,55 @@ func getAutoscaling(input GetAutoscalingInput) (ProcessAutoscaling, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if autoscalingTrigger.Type == "http" {
|
||||
if hasHttpTrigger {
|
||||
return ProcessAutoscaling{}, errors.New("Only one http trigger is allowed")
|
||||
}
|
||||
|
||||
hasHttpTrigger = true
|
||||
httpTrigger = autoscalingTrigger
|
||||
|
||||
if _, ok := metadata["scale_by"]; !ok {
|
||||
httpTrigger.Metadata["scale_by"] = "request_rate"
|
||||
}
|
||||
|
||||
if !validHttpScaleMethods[metadata["scale_by"]] {
|
||||
return ProcessAutoscaling{}, fmt.Errorf("Invalid http scale method: %s", metadata["scale_by"])
|
||||
}
|
||||
|
||||
if _, ok := metadata["scaledown_period_seconds"]; !ok {
|
||||
httpTrigger.Metadata["scaledown_period_seconds"] = "300"
|
||||
}
|
||||
|
||||
if _, ok := metadata["request_rate_granularity_seconds"]; !ok {
|
||||
httpTrigger.Metadata["request_rate_granularity_seconds"] = "1"
|
||||
}
|
||||
|
||||
if _, ok := metadata["request_rate_target_value"]; !ok {
|
||||
httpTrigger.Metadata["request_rate_target_value"] = "100"
|
||||
}
|
||||
|
||||
if _, ok := metadata["request_rate_window_seconds"]; !ok {
|
||||
httpTrigger.Metadata["request_rate_window_seconds"] = "60"
|
||||
}
|
||||
|
||||
if _, ok := metadata["concurrency_target_value"]; !ok {
|
||||
httpTrigger.Metadata["concurrency_target_value"] = "100"
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
triggers = append(triggers, autoscalingTrigger)
|
||||
}
|
||||
|
||||
autoscaling := ProcessAutoscaling{
|
||||
CooldownPeriodSeconds: ptr.Deref(config.CooldownPeriodSeconds, 300),
|
||||
Enabled: len(triggers) > 0,
|
||||
Enabled: len(triggers) > 0 || hasHttpTrigger,
|
||||
MaxReplicas: ptr.Deref(config.MaxQuantity, 0),
|
||||
MinReplicas: ptr.Deref(config.MinQuantity, 0),
|
||||
PollingIntervalSeconds: ptr.Deref(config.PollingIntervalSeconds, 30),
|
||||
HttpTrigger: httpTrigger,
|
||||
Triggers: triggers,
|
||||
Type: "keda",
|
||||
}
|
||||
|
||||
@@ -61,8 +61,14 @@ type GlobalImage struct {
|
||||
}
|
||||
|
||||
type GlobalNetwork struct {
|
||||
// IngressClass is the default ingress class to use
|
||||
IngressClass string `yaml:"ingress_class"`
|
||||
PrimaryPort int32 `yaml:"primary_port"`
|
||||
|
||||
// PrimaryPort is the primary port to use
|
||||
PrimaryPort int32 `yaml:"primary_port"`
|
||||
|
||||
// PrimaryServicePort is the primary service port to use
|
||||
PrimaryServicePort int32 `yaml:"primary_service_port"`
|
||||
}
|
||||
|
||||
// GlobalKedaValues contains the global keda configuration
|
||||
@@ -116,6 +122,8 @@ type ProcessAnnotations struct {
|
||||
IngressAnnotations map[string]string `yaml:"ingress,omitempty"`
|
||||
JobAnnotations map[string]string `yaml:"job,omitempty"`
|
||||
KedaScalingObjectAnnotations map[string]string `yaml:"keda_scaled_object,omitempty"`
|
||||
KedaHTTPScaledObjectAnnotations map[string]string `yaml:"keda_http_scaled_object,omitempty"`
|
||||
KedaInterceptorProxyAnnotations map[string]string `yaml:"keda_interceptor_proxy,omitempty"`
|
||||
KedaSecretAnnotations map[string]string `yaml:"keda_secret,omitempty"`
|
||||
KedaTriggerAuthenticationAnnotations map[string]string `yaml:"keda_trigger_authentication,omitempty"`
|
||||
PodAnnotations map[string]string `yaml:"pod,omitempty"`
|
||||
@@ -143,6 +151,9 @@ type ProcessAutoscaling struct {
|
||||
// PollingIntervalSeconds is the number of seconds between polling for new metrics
|
||||
PollingIntervalSeconds int `yaml:"polling_interval_seconds,omitempty"`
|
||||
|
||||
// HttpTrigger is the http trigger config to use for autoscaling
|
||||
HttpTrigger ProcessAutoscalingTrigger `yaml:"http_trigger,omitempty"`
|
||||
|
||||
// Triggers is a list of triggers to use for autoscaling
|
||||
Triggers []ProcessAutoscalingTrigger `yaml:"triggers,omitempty"`
|
||||
|
||||
@@ -224,17 +235,22 @@ const (
|
||||
)
|
||||
|
||||
type ProcessLabels struct {
|
||||
CertificateLabels map[string]string `yaml:"certificate,omitempty"`
|
||||
CronJobLabels map[string]string `yaml:"cronjob,omitempty"`
|
||||
DeploymentLabels map[string]string `yaml:"deployment,omitempty"`
|
||||
IngressLabels map[string]string `yaml:"ingress,omitempty"`
|
||||
JobLabels map[string]string `yaml:"job,omitempty"`
|
||||
PodLabels map[string]string `yaml:"pod,omitempty"`
|
||||
SecretLabels map[string]string `yaml:"secret,omitempty"`
|
||||
ServiceAccountLabels map[string]string `yaml:"serviceaccount,omitempty"`
|
||||
ServiceLabels map[string]string `yaml:"service,omitempty"`
|
||||
TraefikIngressRouteLabels map[string]string `yaml:"traefik_ingressroute,omitempty"`
|
||||
TraefikMiddlewareLabels map[string]string `yaml:"traefik_middleware,omitempty"`
|
||||
CertificateLabels map[string]string `yaml:"certificate,omitempty"`
|
||||
CronJobLabels map[string]string `yaml:"cronjob,omitempty"`
|
||||
DeploymentLabels map[string]string `yaml:"deployment,omitempty"`
|
||||
IngressLabels map[string]string `yaml:"ingress,omitempty"`
|
||||
JobLabels map[string]string `yaml:"job,omitempty"`
|
||||
KedaScalingObjectLabels map[string]string `yaml:"keda_scaled_object,omitempty"`
|
||||
KedaHTTPScaledObjectLabels map[string]string `yaml:"keda_http_scaled_object,omitempty"`
|
||||
KedaInterceptorProxyLabels map[string]string `yaml:"keda_interceptor_proxy,omitempty"`
|
||||
KedaSecretLabels map[string]string `yaml:"keda_secret,omitempty"`
|
||||
KedaTriggerAuthenticationLabels map[string]string `yaml:"keda_trigger_authentication,omitempty"`
|
||||
PodLabels map[string]string `yaml:"pod,omitempty"`
|
||||
SecretLabels map[string]string `yaml:"secret,omitempty"`
|
||||
ServiceAccountLabels map[string]string `yaml:"serviceaccount,omitempty"`
|
||||
ServiceLabels map[string]string `yaml:"service,omitempty"`
|
||||
TraefikIngressRouteLabels map[string]string `yaml:"traefik_ingressroute,omitempty"`
|
||||
TraefikMiddlewareLabels map[string]string `yaml:"traefik_middleware,omitempty"`
|
||||
}
|
||||
|
||||
type ProcessWeb struct {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
{{- $processName := "PROCESS_NAME" }}
|
||||
{{- $config := index .Values.processes "PROCESS_NAME" }}
|
||||
{{- $routeToKeda := false }}
|
||||
{{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }}
|
||||
{{- if and (.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") $config.autoscaling.http_trigger.type }}
|
||||
{{- $routeToKeda = true }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and $config.web.domains (eq $.Values.global.network.ingress_class "nginx") }}
|
||||
{{- range $pdx, $domain := $config.web.domains }}
|
||||
---
|
||||
@@ -36,11 +42,19 @@ spec:
|
||||
paths:
|
||||
{{- if $config.web.port_maps }}
|
||||
- backend:
|
||||
{{- if $routeToKeda}}
|
||||
service:
|
||||
name: kip-{{ $.Values.global.app_name}}-{{ $processName }}
|
||||
port:
|
||||
number: 8080
|
||||
pathType: Prefix
|
||||
{{- else }}
|
||||
service:
|
||||
name: {{ $.Values.global.app_name }}-{{ $processName }}
|
||||
port:
|
||||
name: {{ include "primary.port" $config.web.port_maps }}
|
||||
pathType: ImplementationSpecific
|
||||
{{- end }}
|
||||
path: /
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
{{- $processName := "PROCESS_NAME" }}
|
||||
{{- $config := index .Values.processes "PROCESS_NAME" }}
|
||||
{{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }}
|
||||
{{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}}
|
||||
{{- if $config.autoscaling.http_trigger.type }}
|
||||
{{- $mappings := dict }}
|
||||
{{- range $pdx, $port_map := $config.web.port_maps }}
|
||||
{{- $mappings := set $mappings $port_map.name "true" }}
|
||||
{{- end }}
|
||||
---
|
||||
kind: HTTPScaledObject
|
||||
apiVersion: http.keda.sh/v1alpha1
|
||||
metadata:
|
||||
annotations:
|
||||
app.kubernetes.io/version: {{ $.Values.global.deployment_id | quote }}
|
||||
dokku.com/managed: "true"
|
||||
httpscaledobject.keda.sh/skip-scaledobject-creation: "true"
|
||||
{{ include "print.annotations" (dict "config" $.Values.global "key" "keda_http_scaled_object") | indent 4 }}
|
||||
{{ include "print.annotations" (dict "config" $config "key" "keda_http_scaled_object") | indent 4 }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}
|
||||
app.kubernetes.io/name: {{ $processName }}
|
||||
app.kubernetes.io/part-of: {{ $.Values.global.app_name }}
|
||||
{{ include "print.labels" (dict "config" $.Values.global "key" "keda_http_scaled_object") | indent 4 }}
|
||||
{{ include "print.labels" (dict "config" $config "key" "keda_http_scaled_object") | indent 4 }}
|
||||
name: {{ $.Values.global.app_name}}-{{ $processName }}
|
||||
namespace: {{ $.Values.global.namespace }}
|
||||
spec:
|
||||
hosts:
|
||||
{{- range $pdx, $domain := $config.web.domains }}
|
||||
- {{ $domain.name | quote }}
|
||||
{{- end }}
|
||||
scaleTargetRef:
|
||||
name: {{ $.Values.global.app_name }}-{{ $processName }}
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
service: {{ $.Values.global.app_name }}-{{ $processName }}
|
||||
port: {{ $.Values.global.network.primary_service_port }}
|
||||
replicas:
|
||||
min: {{ $config.autoscaling.min_replicas }}
|
||||
max: {{ $config.autoscaling.max_replicas }}
|
||||
scaledownPeriod: {{ get $config.autoscaling.http_trigger.metadata "scaledown_period_seconds" }}
|
||||
scalingMetric:
|
||||
{{- if eq (get $config.autoscaling.http_trigger.metadata "scale_by") "request_rate" }}
|
||||
requestRate:
|
||||
granularity: {{ get $config.autoscaling.http_trigger.metadata "request_rate_granularity_seconds" }}s
|
||||
targetValue: {{ get $config.autoscaling.http_trigger.metadata "request_rate_target_value" }}
|
||||
window: {{ get $config.autoscaling.http_trigger.metadata "request_rate_window_seconds" }}s
|
||||
{{- else }}
|
||||
concurrency:
|
||||
targetValue: {{ get $config.autoscaling.http_trigger.metadata "concurrency_target_value" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{- $processName := "PROCESS_NAME" }}
|
||||
{{- $config := index .Values.processes "PROCESS_NAME" }}
|
||||
{{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }}
|
||||
{{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}}
|
||||
---
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
annotations:
|
||||
app.kubernetes.io/version: {{ $.Values.global.deployment_id | quote }}
|
||||
dokku.com/managed: "true"
|
||||
{{ include "print.annotations" (dict "config" $.Values.global "key" "keda_interceptor_proxy_service") | indent 4 }}
|
||||
{{ include "print.annotations" (dict "config" $config "key" "keda_interceptor_proxy_service") | indent 4 }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }}
|
||||
app.kubernetes.io/name: {{ $processName }}
|
||||
app.kubernetes.io/part-of: {{ $.Values.global.app_name }}
|
||||
{{ include "print.labels" (dict "config" $.Values.global "key" "keda_interceptor_proxy_service") | indent 4 }}
|
||||
{{ include "print.labels" (dict "config" $config "key" "keda_interceptor_proxy_service") | indent 4 }}
|
||||
name: kip-{{ $.Values.global.app_name}}-{{ $processName }}
|
||||
namespace: {{ $.Values.global.namespace }}
|
||||
spec:
|
||||
type: ExternalName
|
||||
externalName: keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -250,8 +250,10 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
||||
}
|
||||
|
||||
primaryPort := int32(5000)
|
||||
primaryServicePort := int32(80)
|
||||
for _, portMap := range portMaps {
|
||||
primaryPort = portMap.ContainerPort
|
||||
primaryServicePort = portMap.HostPort
|
||||
if primaryPort != 0 {
|
||||
break
|
||||
}
|
||||
@@ -350,8 +352,9 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
||||
Labels: globalLabels,
|
||||
Namespace: namespace,
|
||||
Network: GlobalNetwork{
|
||||
IngressClass: getGlobalIngressClass(),
|
||||
PrimaryPort: primaryPort,
|
||||
IngressClass: getGlobalIngressClass(),
|
||||
PrimaryPort: primaryPort,
|
||||
PrimaryServicePort: primaryServicePort,
|
||||
},
|
||||
Secrets: map[string]string{},
|
||||
},
|
||||
@@ -504,7 +507,7 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
||||
|
||||
templateFiles := []string{"deployment", "keda-scaled-object"}
|
||||
if processType == "web" {
|
||||
templateFiles = append(templateFiles, "service", "certificate", "ingress", "ingress-route", "https-redirect-middleware")
|
||||
templateFiles = append(templateFiles, "service", "certificate", "ingress", "ingress-route", "https-redirect-middleware", "keda-http-scaled-object", "keda-interceptor-proxy-service")
|
||||
}
|
||||
for _, templateName := range templateFiles {
|
||||
b, err := templates.ReadFile(fmt.Sprintf("templates/chart/%s.yaml", templateName))
|
||||
|
||||
Reference in New Issue
Block a user