diff --git a/content/docs/1.4/concepts/external-scalers.md b/content/docs/1.4/concepts/external-scalers.md index 5bf6b7495..f02eeeb5d 100644 --- a/content/docs/1.4/concepts/external-scalers.md +++ b/content/docs/1.4/concepts/external-scalers.md @@ -27,11 +27,12 @@ type Scaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#4-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#5-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates replicaCount based on metric value and target value. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates replicaCount based on metric value and target value. ### External Scaler GRPC interface @@ -51,6 +52,7 @@ service ExternalScaler { - `IsActive` maps to the `IsActive` method on the `Scaler` interface. Few things to notice: + - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. For example the following `ScaledObject`: @@ -85,6 +87,7 @@ KEDA will attempt a connection to `service-address.svc.local:9090` and calls `Is } } ``` + ## Implementing KEDA external scaler GRPC interface ### Implementing an external scaler: @@ -110,6 +113,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -146,9 +150,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -178,6 +181,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -224,12 +228,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -262,79 +268,84 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -345,9 +356,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -362,31 +375,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -409,9 +426,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -436,38 +455,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/1.5/concepts/external-scalers.md b/content/docs/1.5/concepts/external-scalers.md index 271e5ef34..712967345 100644 --- a/content/docs/1.5/concepts/external-scalers.md +++ b/content/docs/1.5/concepts/external-scalers.md @@ -27,11 +27,12 @@ type Scaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#4-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#5-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates replicaCount based on metric value and target value. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates replicaCount based on metric value and target value. ### External Scaler GRPC interface @@ -51,6 +52,7 @@ service ExternalScaler { - `IsActive` maps to the `IsActive` method on the `Scaler` interface. Few things to notice: + - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. For example the following `ScaledObject`: @@ -85,6 +87,7 @@ KEDA will attempt a connection to `service-address.svc.local:9090` and calls `Is } } ``` + ## Implementing KEDA external scaler GRPC interface ### Implementing an external scaler: @@ -110,6 +113,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -146,9 +150,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -178,6 +181,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -224,12 +228,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -262,79 +268,84 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -345,9 +356,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -362,31 +375,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -409,9 +426,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -436,38 +455,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/2.0/concepts/external-scalers.md b/content/docs/2.0/concepts/external-scalers.md index 27ed9e0c6..1945394d3 100644 --- a/content/docs/2.0/concepts/external-scalers.md +++ b/content/docs/2.0/concepts/external-scalers.md @@ -33,15 +33,15 @@ type PushScaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds `Run` method. The `Run` method receives a push channel (`active`), that the scaler can push `true` to any time to force scaling action independently from `pollingInterval`. - ### External Scaler GRPC interface KEDA comes with 2 external scalers [`external`](../scalers/external.md) and [`external-push`](../scalers/external-push.md). @@ -62,6 +62,7 @@ service ExternalScaler { - `StreamIsActive` maps to the `Run` method on the `PushScaler` interface. Few things to notice: + - Lack of `Close` method as the GRPC connection defines the lifetime of the scaler - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. @@ -125,6 +126,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -161,9 +163,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -193,6 +194,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -239,12 +241,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -277,73 +281,77 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `StreamIsActive` @@ -351,8 +359,8 @@ Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `Sca This implementation creates a timer and queries USGS APIs on that timer, effectively ignoring `pollingInterval` set in the scaledObject. Alternatively any other asynchronous event can be used instead of a timer, like an HTTP request, or a network connection. - {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsServer pb.ExternalScaler_StreamIsActiveServer) error { longitude := scaledObject.ScalerMetadata["longitude"] @@ -380,9 +388,11 @@ func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsSer } } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task StreamIsActive(ScaledObjectRef request, IServerStreamWriter responseStream, ServerCallContext context) { @@ -410,49 +420,51 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -463,9 +475,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -480,31 +494,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -527,9 +545,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -554,38 +574,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/2.0/migration.md b/content/docs/2.0/migration.md index b7e2cbad5..fe54ad324 100644 --- a/content/docs/2.0/migration.md +++ b/content/docs/2.0/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for it's Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.0/operate/cluster.md b/content/docs/2.0/operate/cluster.md index 96925a55e..3620e816a 100644 --- a/content/docs/2.0/operate/cluster.md +++ b/content/docs/2.0/operate/cluster.md @@ -15,7 +15,7 @@ KEDA is designed, tested and supported to be run on any Kubernetes cluster that The KEDA runtime require the following resources in a production-ready setup: | Deployment | CPU | Memory | -|----------------|-------------------------|-------------------------------| +| -------------- | ----------------------- | ----------------------------- | | Operator | Limit: 1, Request: 100m | Limit: 1000Mi, Request: 100Mi | | Metrics Server | Limit: 1, Request: 100m | Limit: 1000Mi, Request: 100Mi | @@ -40,7 +40,7 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 1 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.

This is only supported as of KEDA v2.6 if you are using our Helm chart. | +| Operator | 1 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.This is only supported as of KEDA v2.6 if you are using our Helm chart. | diff --git a/content/docs/2.1/concepts/external-scalers.md b/content/docs/2.1/concepts/external-scalers.md index 27ed9e0c6..1945394d3 100644 --- a/content/docs/2.1/concepts/external-scalers.md +++ b/content/docs/2.1/concepts/external-scalers.md @@ -33,15 +33,15 @@ type PushScaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds `Run` method. The `Run` method receives a push channel (`active`), that the scaler can push `true` to any time to force scaling action independently from `pollingInterval`. - ### External Scaler GRPC interface KEDA comes with 2 external scalers [`external`](../scalers/external.md) and [`external-push`](../scalers/external-push.md). @@ -62,6 +62,7 @@ service ExternalScaler { - `StreamIsActive` maps to the `Run` method on the `PushScaler` interface. Few things to notice: + - Lack of `Close` method as the GRPC connection defines the lifetime of the scaler - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. @@ -125,6 +126,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -161,9 +163,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -193,6 +194,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -239,12 +241,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -277,73 +281,77 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `StreamIsActive` @@ -351,8 +359,8 @@ Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `Sca This implementation creates a timer and queries USGS APIs on that timer, effectively ignoring `pollingInterval` set in the scaledObject. Alternatively any other asynchronous event can be used instead of a timer, like an HTTP request, or a network connection. - {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsServer pb.ExternalScaler_StreamIsActiveServer) error { longitude := scaledObject.ScalerMetadata["longitude"] @@ -380,9 +388,11 @@ func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsSer } } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task StreamIsActive(ScaledObjectRef request, IServerStreamWriter responseStream, ServerCallContext context) { @@ -410,49 +420,51 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -463,9 +475,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -480,31 +494,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -527,9 +545,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -554,38 +574,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/2.1/migration.md b/content/docs/2.1/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.1/migration.md +++ b/content/docs/2.1/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.1/operate/cluster.md b/content/docs/2.1/operate/cluster.md index c4ccbb85f..fab8edc37 100644 --- a/content/docs/2.1/operate/cluster.md +++ b/content/docs/2.1/operate/cluster.md @@ -15,7 +15,7 @@ KEDA is designed, tested and supported to be run on any Kubernetes cluster that The KEDA runtime require the following resources in a production-ready setup: | Deployment | CPU | Memory | -|----------------|-------------------------|-------------------------------| +| -------------- | ----------------------- | ----------------------------- | | Operator | Limit: 1, Request: 100m | Limit: 1000Mi, Request: 100Mi | | Metrics Server | Limit: 1, Request: 100m | Limit: 1000Mi, Request: 100Mi | @@ -40,10 +40,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.

This is only supported as of KEDA v2.6 if you are using our Helm chart. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.This is only supported as of KEDA v2.6 if you are using our Helm chart. | ## HTTP Timeouts diff --git a/content/docs/2.10/concepts/external-scalers.md b/content/docs/2.10/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.10/concepts/external-scalers.md +++ b/content/docs/2.10/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.10/migration.md b/content/docs/2.10/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.10/migration.md +++ b/content/docs/2.10/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.10/operate/cluster.md b/content/docs/2.10/operate/cluster.md index fe08db931..b705893e8 100644 --- a/content/docs/2.10/operate/cluster.md +++ b/content/docs/2.10/operate/cluster.md @@ -14,12 +14,12 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.10 | v1.24 - v1.26 | -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ----- | ------------- | +| v2.10 | v1.24 - v1.26 | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -41,12 +41,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -54,10 +52,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -79,7 +77,7 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu ## HTTP connection disable keep alive -Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. +Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. You can disable keep alive for every HTTP connection by adding the relevant environment variable to both the KEDA Operator, and KEDA Metrics Server deployments: @@ -108,6 +106,7 @@ Our industry has seen an evolution of TLS versions and some are more secure than By default, KEDA uses TLS1.2 as a minimum TLS version given it is the lowest version without vulnerabilities. However, if you need to support another version you can configure it by using the environment variable `KEDA_HTTP_MIN_TLS_VERSION`. For example: + ```yaml - env: KEDA_HTTP_MIN_TLS_VERSION: TLS13 @@ -119,17 +118,18 @@ The following values are allowed: `TLS13`, `TLS12`, `TLS11` and `TLS10`. The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -137,15 +137,16 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | -| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | -------------- | ------------- | ----------------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -168,25 +169,29 @@ To learn more please refer to [security section](./security#use-your-own-tls-cer ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`. diff --git a/content/docs/2.11/concepts/external-scalers.md b/content/docs/2.11/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.11/concepts/external-scalers.md +++ b/content/docs/2.11/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.11/migration.md b/content/docs/2.11/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.11/migration.md +++ b/content/docs/2.11/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.11/operate/cluster.md b/content/docs/2.11/operate/cluster.md index 9cdadfe80..ffb15a12a 100644 --- a/content/docs/2.11/operate/cluster.md +++ b/content/docs/2.11/operate/cluster.md @@ -14,13 +14,13 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.11 | v1.25 - v1.27 | -| v2.10 | v1.24 - v1.26 | -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ----- | ------------- | +| v2.11 | v1.25 - v1.27 | +| v2.10 | v1.24 - v1.26 | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -42,12 +42,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -55,10 +53,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -80,7 +78,7 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu ## HTTP connection disable keep alive -Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. +Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. You can disable keep alive for every HTTP connection by adding the relevant environment variable to both the KEDA Operator, and KEDA Metrics Server deployments: @@ -109,6 +107,7 @@ Our industry has seen an evolution of TLS versions and some are more secure than By default, KEDA uses TLS1.2 as a minimum TLS version given it is the lowest version without vulnerabilities. However, if you need to support another version you can configure it by using the environment variable `KEDA_HTTP_MIN_TLS_VERSION`. For example: + ```yaml - env: KEDA_HTTP_MIN_TLS_VERSION: TLS13 @@ -120,17 +119,18 @@ The following values are allowed: `TLS13`, `TLS12`, `TLS11` and `TLS10`. The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -138,14 +138,15 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | ---------- | ------------- | ---------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -168,25 +169,29 @@ To learn more please refer to [security section](./security#use-your-own-tls-cer ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`. diff --git a/content/docs/2.12/concepts/external-scalers.md b/content/docs/2.12/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.12/concepts/external-scalers.md +++ b/content/docs/2.12/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.12/migration.md b/content/docs/2.12/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.12/migration.md +++ b/content/docs/2.12/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.12/operate/cluster.md b/content/docs/2.12/operate/cluster.md index e60759578..4f200e90a 100644 --- a/content/docs/2.12/operate/cluster.md +++ b/content/docs/2.12/operate/cluster.md @@ -14,14 +14,14 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.12 | v1.26 - v1.28 | -| v2.11 | v1.25 - v1.27 | -| v2.10 | v1.24 - v1.26 | -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ----- | ------------- | +| v2.12 | v1.26 - v1.28 | +| v2.11 | v1.25 - v1.27 | +| v2.10 | v1.24 - v1.26 | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -43,12 +43,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -56,10 +54,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -81,7 +79,7 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu ## HTTP connection disable keep alive -Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. +Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. You can disable keep alive for every HTTP connection by adding the relevant environment variable to both the KEDA Operator, and KEDA Metrics Server deployments: @@ -110,6 +108,7 @@ Our industry has seen an evolution of TLS versions and some are more secure than By default, KEDA uses TLS1.2 as a minimum TLS version given it is the lowest version without vulnerabilities. However, if you need to support another version you can configure it by using the environment variable `KEDA_HTTP_MIN_TLS_VERSION`. For example: + ```yaml - env: KEDA_HTTP_MIN_TLS_VERSION: TLS13 @@ -121,17 +120,18 @@ The following values are allowed: `TLS13`, `TLS12`, `TLS11` and `TLS10`. The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -139,14 +139,15 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | ---------- | ------------- | ---------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -169,25 +170,29 @@ To learn more please refer to [security section](./security#use-your-own-tls-cer ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`. diff --git a/content/docs/2.13/concepts/external-scalers.md b/content/docs/2.13/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.13/concepts/external-scalers.md +++ b/content/docs/2.13/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.13/migration.md b/content/docs/2.13/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.13/migration.md +++ b/content/docs/2.13/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.13/operate/cluster.md b/content/docs/2.13/operate/cluster.md index 70f84c8f4..4f9c61068 100644 --- a/content/docs/2.13/operate/cluster.md +++ b/content/docs/2.13/operate/cluster.md @@ -14,15 +14,15 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.13 | v1.27 - v1.29 | -| v2.12 | v1.26 - v1.28 | -| v2.11 | v1.25 - v1.27 | -| v2.10 | v1.24 - v1.26 | -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ----- | ------------- | +| v2.13 | v1.27 - v1.29 | +| v2.12 | v1.26 - v1.28 | +| v2.11 | v1.25 - v1.27 | +| v2.10 | v1.24 - v1.26 | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -44,12 +44,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -57,10 +55,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -82,7 +80,7 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu ## HTTP connection disable keep alive -Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. +Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. You can disable keep alive for every HTTP connection by adding the relevant environment variable to both the KEDA Operator, and KEDA Metrics Server deployments: @@ -111,6 +109,7 @@ Our industry has seen an evolution of TLS versions and some are more secure than By default, KEDA uses TLS1.2 as a minimum TLS version given it is the lowest version without vulnerabilities. However, if you need to support another version you can configure it by using the environment variable `KEDA_HTTP_MIN_TLS_VERSION`. For example: + ```yaml - env: KEDA_HTTP_MIN_TLS_VERSION: TLS13 @@ -122,17 +121,18 @@ The following values are allowed: `TLS13`, `TLS12`, `TLS11` and `TLS10`. The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -140,14 +140,15 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | ---------- | ------------- | ---------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -170,25 +171,29 @@ To learn more please refer to [security section](./security#use-your-own-tls-cer ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`. diff --git a/content/docs/2.14/concepts/external-scalers.md b/content/docs/2.14/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.14/concepts/external-scalers.md +++ b/content/docs/2.14/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.14/migration.md b/content/docs/2.14/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.14/migration.md +++ b/content/docs/2.14/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.14/operate/cluster.md b/content/docs/2.14/operate/cluster.md index bf0541bba..4ec1e1049 100644 --- a/content/docs/2.14/operate/cluster.md +++ b/content/docs/2.14/operate/cluster.md @@ -14,16 +14,16 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.14 | v1.27 - v1.29 | -| v2.13 | v1.27 - v1.29 | -| v2.12 | v1.26 - v1.28 | -| v2.11 | v1.25 - v1.27 | -| v2.10 | v1.24 - v1.26 | -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ----- | ------------- | +| v2.14 | v1.27 - v1.29 | +| v2.13 | v1.27 - v1.29 | +| v2.12 | v1.26 - v1.28 | +| v2.11 | v1.25 - v1.27 | +| v2.10 | v1.24 - v1.26 | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -45,12 +45,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -58,10 +56,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -112,6 +110,7 @@ Our industry has seen an evolution of TLS versions and some are more secure than By default, KEDA uses TLS1.2 as a minimum TLS version given it is the lowest version without vulnerabilities. However, if you need to support another version you can configure it by using the environment variable `KEDA_HTTP_MIN_TLS_VERSION`. For example: + ```yaml - env: KEDA_HTTP_MIN_TLS_VERSION: TLS13 @@ -123,26 +122,27 @@ The following values are allowed: `TLS13`, `TLS12`, `TLS11` and `TLS10`. The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## gRPC Metrics Service Parameters The gRPC Metrics Service is part of the KEDA Operator deployment and serves scaling events and metrics from the scalers over gRPC to the Metrics API Service, that in turn serves them to the Kubernetes API Server. The gRPC Metrics Service config used by the KEDA Metrics Adapter to connect to the KEDA Operator can be adjusted by passing the following command-line flags to the Adapter binary: -| Adapter Flag | Default Value | Description | -| ----------------------------------- | ------------------------------------------------- | ---------------------------------------------- | -| metrics-service-address | keda-operator.keda.svc.cluster.local:9666 | The address of the gRPC Metrics Service Server | -| metrics-service-grpc-authority | "" | Host Authority override for the Metrics Service if the Host Authority is not the same as the address used for the gRPC Metrics Service Server. This is required for mutual TLS when the identity of the adapter server as presented in its TLS certificate is not the same as the metrics-service-address | +| Adapter Flag | Default Value | Description | +| ------------------------------ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| metrics-service-address | keda-operator.keda.svc.cluster.local:9666 | The address of the gRPC Metrics Service Server | +| metrics-service-grpc-authority | "" | Host Authority override for the Metrics Service if the Host Authority is not the same as the address used for the gRPC Metrics Service Server. This is required for mutual TLS when the identity of the adapter server as presented in its TLS certificate is not the same as the metrics-service-address | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -150,14 +150,15 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | ---------- | ------------- | ---------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -194,25 +195,29 @@ To learn more please refer to [security section](./security#use-your-own-tls-cer ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`. diff --git a/content/docs/2.15/concepts/external-scalers.md b/content/docs/2.15/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.15/concepts/external-scalers.md +++ b/content/docs/2.15/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.15/migration.md b/content/docs/2.15/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.15/migration.md +++ b/content/docs/2.15/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.15/operate/cluster.md b/content/docs/2.15/operate/cluster.md index 905782f9f..4de7f51ed 100644 --- a/content/docs/2.15/operate/cluster.md +++ b/content/docs/2.15/operate/cluster.md @@ -14,17 +14,17 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.15 | v1.28 - v1.30 | -| v2.14 | v1.27 - v1.29 | -| v2.13 | v1.27 - v1.29 | -| v2.12 | v1.26 - v1.28 | -| v2.11 | v1.25 - v1.27 | -| v2.10 | v1.24 - v1.26 | -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ----- | ------------- | +| v2.15 | v1.28 - v1.30 | +| v2.14 | v1.27 - v1.29 | +| v2.13 | v1.27 - v1.29 | +| v2.12 | v1.26 - v1.28 | +| v2.11 | v1.25 - v1.27 | +| v2.10 | v1.24 - v1.26 | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -46,12 +46,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -59,10 +57,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -113,6 +111,7 @@ Our industry has seen an evolution of TLS versions and some are more secure than By default, KEDA uses TLS1.2 as a minimum TLS version given it is the lowest version without vulnerabilities. However, if you need to support another version you can configure it by using the environment variable `KEDA_HTTP_MIN_TLS_VERSION`. For example: + ```yaml - env: KEDA_HTTP_MIN_TLS_VERSION: TLS13 @@ -124,26 +123,27 @@ The following values are allowed: `TLS13`, `TLS12`, `TLS11` and `TLS10`. The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## gRPC Metrics Service Parameters The gRPC Metrics Service is part of the KEDA Operator deployment and serves scaling events and metrics from the scalers over gRPC to the Metrics API Service, that in turn serves them to the Kubernetes API Server. The gRPC Metrics Service config used by the KEDA Metrics Adapter to connect to the KEDA Operator can be adjusted by passing the following command-line flags to the Adapter binary: -| Adapter Flag | Default Value | Description | -| ----------------------------------- | ------------------------------------------------- | ---------------------------------------------- | -| metrics-service-address | keda-operator.keda.svc.cluster.local:9666 | The address of the gRPC Metrics Service Server | -| metrics-service-grpc-authority | "" | Host Authority override for the Metrics Service if the Host Authority is not the same as the address used for the gRPC Metrics Service Server. This is required for mutual TLS when the identity of the adapter server as presented in its TLS certificate is not the same as the metrics-service-address | +| Adapter Flag | Default Value | Description | +| ------------------------------ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| metrics-service-address | keda-operator.keda.svc.cluster.local:9666 | The address of the gRPC Metrics Service Server | +| metrics-service-grpc-authority | "" | Host Authority override for the Metrics Service if the Host Authority is not the same as the address used for the gRPC Metrics Service Server. This is required for mutual TLS when the identity of the adapter server as presented in its TLS certificate is not the same as the metrics-service-address | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -151,14 +151,15 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | ---------- | ------------- | ---------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -195,25 +196,29 @@ To learn more please refer to [security section](./security#use-your-own-tls-cer ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`. diff --git a/content/docs/2.2/concepts/external-scalers.md b/content/docs/2.2/concepts/external-scalers.md index 27ed9e0c6..1945394d3 100644 --- a/content/docs/2.2/concepts/external-scalers.md +++ b/content/docs/2.2/concepts/external-scalers.md @@ -33,15 +33,15 @@ type PushScaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds `Run` method. The `Run` method receives a push channel (`active`), that the scaler can push `true` to any time to force scaling action independently from `pollingInterval`. - ### External Scaler GRPC interface KEDA comes with 2 external scalers [`external`](../scalers/external.md) and [`external-push`](../scalers/external-push.md). @@ -62,6 +62,7 @@ service ExternalScaler { - `StreamIsActive` maps to the `Run` method on the `PushScaler` interface. Few things to notice: + - Lack of `Close` method as the GRPC connection defines the lifetime of the scaler - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. @@ -125,6 +126,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -161,9 +163,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -193,6 +194,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -239,12 +241,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -277,73 +281,77 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `StreamIsActive` @@ -351,8 +359,8 @@ Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `Sca This implementation creates a timer and queries USGS APIs on that timer, effectively ignoring `pollingInterval` set in the scaledObject. Alternatively any other asynchronous event can be used instead of a timer, like an HTTP request, or a network connection. - {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsServer pb.ExternalScaler_StreamIsActiveServer) error { longitude := scaledObject.ScalerMetadata["longitude"] @@ -380,9 +388,11 @@ func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsSer } } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task StreamIsActive(ScaledObjectRef request, IServerStreamWriter responseStream, ServerCallContext context) { @@ -410,49 +420,51 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -463,9 +475,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -480,31 +494,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -527,9 +545,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -554,38 +574,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/2.2/migration.md b/content/docs/2.2/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.2/migration.md +++ b/content/docs/2.2/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.2/operate/cluster.md b/content/docs/2.2/operate/cluster.md index 862719278..f010cc0f2 100644 --- a/content/docs/2.2/operate/cluster.md +++ b/content/docs/2.2/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,10 +40,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.

This is only supported as of KEDA v2.6 if you are using our Helm chart. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.This is only supported as of KEDA v2.6 if you are using our Helm chart. | ## HTTP Timeouts diff --git a/content/docs/2.3/concepts/external-scalers.md b/content/docs/2.3/concepts/external-scalers.md index 27ed9e0c6..1945394d3 100644 --- a/content/docs/2.3/concepts/external-scalers.md +++ b/content/docs/2.3/concepts/external-scalers.md @@ -33,15 +33,15 @@ type PushScaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds `Run` method. The `Run` method receives a push channel (`active`), that the scaler can push `true` to any time to force scaling action independently from `pollingInterval`. - ### External Scaler GRPC interface KEDA comes with 2 external scalers [`external`](../scalers/external.md) and [`external-push`](../scalers/external-push.md). @@ -62,6 +62,7 @@ service ExternalScaler { - `StreamIsActive` maps to the `Run` method on the `PushScaler` interface. Few things to notice: + - Lack of `Close` method as the GRPC connection defines the lifetime of the scaler - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. @@ -125,6 +126,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -161,9 +163,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -193,6 +194,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -239,12 +241,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -277,73 +281,77 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `StreamIsActive` @@ -351,8 +359,8 @@ Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `Sca This implementation creates a timer and queries USGS APIs on that timer, effectively ignoring `pollingInterval` set in the scaledObject. Alternatively any other asynchronous event can be used instead of a timer, like an HTTP request, or a network connection. - {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsServer pb.ExternalScaler_StreamIsActiveServer) error { longitude := scaledObject.ScalerMetadata["longitude"] @@ -380,9 +388,11 @@ func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsSer } } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task StreamIsActive(ScaledObjectRef request, IServerStreamWriter responseStream, ServerCallContext context) { @@ -410,49 +420,51 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -463,9 +475,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -480,31 +494,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -527,9 +545,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -554,38 +574,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/2.3/migration.md b/content/docs/2.3/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.3/migration.md +++ b/content/docs/2.3/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.3/operate/cluster.md b/content/docs/2.3/operate/cluster.md index 4caa62f3d..f010cc0f2 100644 --- a/content/docs/2.3/operate/cluster.md +++ b/content/docs/2.3/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,14 +40,14 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.

This is only supported as of KEDA v2.6 if you are using our Helm chart. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.This is only supported as of KEDA v2.6 if you are using our Helm chart. | ## HTTP Timeouts -Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. +Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. You can override this default by setting the `KEDA_HTTP_DEFAULT_TIMEOUT` environment variable to your desired timeout in milliseconds. For example, on Linux/Mac/Windows WSL2 operating systems, you'd use this command to set to 1 second: diff --git a/content/docs/2.4/concepts/external-scalers.md b/content/docs/2.4/concepts/external-scalers.md index 27ed9e0c6..1945394d3 100644 --- a/content/docs/2.4/concepts/external-scalers.md +++ b/content/docs/2.4/concepts/external-scalers.md @@ -33,15 +33,15 @@ type PushScaler interface { ``` The `Scaler` interface defines 4 methods: + - `IsActive` is called on `pollingInterval` defined in the ScaledObject/ScaledJob CRDs and scaling to 1 happens if this returns true. - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds `Run` method. The `Run` method receives a push channel (`active`), that the scaler can push `true` to any time to force scaling action independently from `pollingInterval`. - ### External Scaler GRPC interface KEDA comes with 2 external scalers [`external`](../scalers/external.md) and [`external-push`](../scalers/external-push.md). @@ -62,6 +62,7 @@ service ExternalScaler { - `StreamIsActive` maps to the `Run` method on the `PushScaler` interface. Few things to notice: + - Lack of `Close` method as the GRPC connection defines the lifetime of the scaler - `IsActive`, `StreamIsActive`, and `GetMetricsSpec` are called with a `ScaledObjectRef` that contains the scaledObject name/namespace as well as the content of `metadata` defined in the trigger. @@ -125,6 +126,7 @@ go mod init example.com/external-scaler/sample mkdir externalscaler protoc externalscaler.proto --go_out=plugins=grpc:externalscaler ``` + {{< /collapsible >}} {{< collapsible "C#" >}} @@ -161,9 +163,8 @@ mkdir Services ```bash npm install --save grpc request ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 3. Implementing `IsActive` @@ -193,6 +194,7 @@ spec: Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `main.go` + ```golang func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) { // request.Scalermetadata contains the `metadata` defined in the ScaledObject @@ -239,12 +241,14 @@ func (e *ExternalScaler) IsActive(ctx context.Context, scaledObject *pb.ScaledOb }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} Full implementation can be found here: https://github.com/kedacore/external-scaler-samples `Services/ExternalScalerService.cs` + ```csharp public class ExternalScalerService : ExternalScaler.ExternalScalerBase { @@ -277,73 +281,77 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} `index.js` + ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 4. Implementing `StreamIsActive` @@ -351,8 +359,8 @@ Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `Sca This implementation creates a timer and queries USGS APIs on that timer, effectively ignoring `pollingInterval` set in the scaledObject. Alternatively any other asynchronous event can be used instead of a timer, like an HTTP request, or a network connection. - {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsServer pb.ExternalScaler_StreamIsActiveServer) error { longitude := scaledObject.ScalerMetadata["longitude"] @@ -380,9 +388,11 @@ func (e *ExternalScaler) StreamIsActive(scaledObject *pb.ScaledObjectRef, epsSer } } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task StreamIsActive(ScaledObjectRef request, IServerStreamWriter responseStream, ServerCallContext context) { @@ -410,49 +420,51 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream } } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*pb.GetMetricSpecResponse, error) { return &pb.GetMetricSpecResponse{ @@ -463,9 +475,11 @@ func (e *ExternalScaler) GetMetricSpec(context.Context, *pb.ScaledObjectRef) (*p }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetricSpec(ScaledObjectRef request, ServerCallContext context) { @@ -480,31 +494,35 @@ public override async Task GetMetricSpec(ScaledObjectRef return Task.FromResult(resp); } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` -{{< /collapsible >}} -
+{{< /collapsible >}} #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. {{< collapsible "Golang" >}} + ```golang func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) { longitude := metricRequest.ScaledObjectRef.ScalerMetadata["longitude"] @@ -527,9 +545,11 @@ func (e *ExternalScaler) GetMetrics(_ context.Context, metricRequest *pb.GetMetr }, nil } ``` + {{< /collapsible >}} {{< collapsible "C#" >}} + ```csharp public override async Task GetMetrics(GetMetricsRequest request, ServerCallContext context) { @@ -554,38 +574,43 @@ public override async Task GetMetrics(GetMetricsRequest requ return resp; } ``` + {{< /collapsible >}} {{< collapsible "Javascript" >}} + ```js server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` + {{< /collapsible >}} diff --git a/content/docs/2.4/migration.md b/content/docs/2.4/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.4/migration.md +++ b/content/docs/2.4/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.4/operate/cluster.md b/content/docs/2.4/operate/cluster.md index 9c5ee2895..8ace09018 100644 --- a/content/docs/2.4/operate/cluster.md +++ b/content/docs/2.4/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,14 +40,14 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.

This is only supported as of KEDA v2.6 if you are using our Helm chart. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover.This is only supported as of KEDA v2.6 if you are using our Helm chart. | ## HTTP Timeouts -Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. +Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. You can override this default by setting the `KEDA_HTTP_DEFAULT_TIMEOUT` environment variable to your desired timeout in milliseconds. For example, on Linux/Mac/Windows WSL2 operating systems, you'd use this command to set to 1 second: @@ -69,9 +67,7 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu The Kubernetes client config used within KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| -------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | - - +| Adapter Flag | Client Config Setting | Default Value | Description | +| -------------- | --------------------- | ------------- | -------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | diff --git a/content/docs/2.5/concepts/external-scalers.md b/content/docs/2.5/concepts/external-scalers.md index 47c4a45a9..214107edc 100644 --- a/content/docs/2.5/concepts/external-scalers.md +++ b/content/docs/2.5/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -34,12 +34,12 @@ type PushScaler interface { The `Scaler` interface defines 4 methods: -- `IsActive` is called on `pollingInterval`. When `isActive` returns `true`, KEDA will scale to what is returned by `GetMetricSpec` limited by `maxReplicaCount` on the ScaledObject/ScaledJob. - When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/2.6/concepts/scaling-deployments/#scaledobject-spec). +- `IsActive` is called on `pollingInterval`. When `isActive` returns `true`, KEDA will scale to what is returned by `GetMetricSpec` limited by `maxReplicaCount` on the ScaledObject/ScaledJob. + When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/2.6/concepts/scaling-deployments/#scaledobject-spec). - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -109,7 +109,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -176,8 +176,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -304,70 +302,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -444,38 +443,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -521,19 +518,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -600,32 +597,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.5/migration.md b/content/docs/2.5/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.5/migration.md +++ b/content/docs/2.5/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.5/operate/cluster.md b/content/docs/2.5/operate/cluster.md index c685303b3..2c7b818ee 100644 --- a/content/docs/2.5/operate/cluster.md +++ b/content/docs/2.5/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,14 +40,14 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts -Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. +Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. You can override this default by setting the `KEDA_HTTP_DEFAULT_TIMEOUT` environment variable to your desired timeout in milliseconds. For example, on Linux/Mac/Windows WSL2 operating systems, you'd use this command to set to 1 second: @@ -69,16 +67,17 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu The Kubernetes client config used within KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| -------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| Adapter Flag | Client Config Setting | Default Value | Description | +| -------------- | --------------------- | ------------- | -------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -86,8 +85,8 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | -| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | -------------- | ------------- | ----------------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | diff --git a/content/docs/2.6/concepts/external-scalers.md b/content/docs/2.6/concepts/external-scalers.md index 64d7bffb4..214107edc 100644 --- a/content/docs/2.6/concepts/external-scalers.md +++ b/content/docs/2.6/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -39,7 +39,7 @@ The `Scaler` interface defines 4 methods: - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA uses the metric target type `AverageValue` for external metrics. This will cause the metric value returned by the external scaler to be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -109,7 +109,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -176,8 +176,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -304,70 +302,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -444,38 +443,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -521,19 +518,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -600,32 +597,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.6/migration.md b/content/docs/2.6/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.6/migration.md +++ b/content/docs/2.6/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.6/operate/cluster.md b/content/docs/2.6/operate/cluster.md index 5932357b8..10e6a4043 100644 --- a/content/docs/2.6/operate/cluster.md +++ b/content/docs/2.6/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,14 +40,14 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts -Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. +Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. You can override this default by setting the `KEDA_HTTP_DEFAULT_TIMEOUT` environment variable to your desired timeout in milliseconds. For example, on Linux/Mac/Windows WSL2 operating systems, you'd use this command to set to 1 second: @@ -69,16 +67,17 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu The Kubernetes client config used within KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| -------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| Adapter Flag | Client Config Setting | Default Value | Description | +| -------------- | --------------------- | ------------- | -------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -86,11 +85,11 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | -| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | -------------- | ------------- | ----------------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | ## Certificates used by KEDA Metrics Server @@ -99,20 +98,19 @@ By default, KEDA Metrics Server uses self-signed certificates while communicatin Certificates and CA bundle can be referenced in `args` section in KEDA Metrics Server Deployment: ```yaml -... +--- args: - - '--client-ca-file=/cabundle/service-ca.crt' - - '--tls-cert-file=/certs/tls.crt' - - '--tls-private-key-file=/certs/tls.key' -... + - "--client-ca-file=/cabundle/service-ca.crt" + - "--tls-cert-file=/certs/tls.crt" + - "--tls-private-key-file=/certs/tls.key" ``` -The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). +The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). You should also make sure that `insecureSkipTLSVerify` is not set to `true`. ```yaml -... +--- spec: service: namespace: keda @@ -124,5 +122,4 @@ spec: YOURCABUNDLE... groupPriorityMinimum: 100 versionPriority: 100 -... ``` diff --git a/content/docs/2.7/concepts/external-scalers.md b/content/docs/2.7/concepts/external-scalers.md index 16f721117..d360d39cd 100644 --- a/content/docs/2.7/concepts/external-scalers.md +++ b/content/docs/2.7/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -39,7 +39,7 @@ The `Scaler` interface defines 4 methods: - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -109,7 +109,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -176,8 +176,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -304,70 +302,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -444,38 +443,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -521,19 +518,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -600,32 +597,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.7/migration.md b/content/docs/2.7/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.7/migration.md +++ b/content/docs/2.7/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.7/operate/cluster.md b/content/docs/2.7/operate/cluster.md index dc1a201d6..dfb78df81 100644 --- a/content/docs/2.7/operate/cluster.md +++ b/content/docs/2.7/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,14 +40,14 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts -Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. +Some scalers issue HTTP requests to external servers (i.e. cloud services). Each applicable scaler uses its own dedicated HTTP client with its own connection pool, and by default each client is set to time out any HTTP request after 3 seconds. You can override this default by setting the `KEDA_HTTP_DEFAULT_TIMEOUT` environment variable to your desired timeout in milliseconds. For example, on Linux/Mac/Windows WSL2 operating systems, you'd use this command to set to 1 second: @@ -80,16 +78,17 @@ Some scalers issue HTTP requests to external servers (i.e. cloud services). As c The Kubernetes client config used within KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| -------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| Adapter Flag | Client Config Setting | Default Value | Description | +| -------------- | --------------------- | ------------- | -------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -97,11 +96,11 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | -| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | -------------- | ------------- | ----------------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | ## Certificates used by KEDA Metrics Server @@ -110,20 +109,19 @@ By default, KEDA Metrics Server uses self-signed certificates while communicatin Certificates and CA bundle can be referenced in `args` section in KEDA Metrics Server Deployment: ```yaml -... +--- args: - - '--client-ca-file=/cabundle/service-ca.crt' - - '--tls-cert-file=/certs/tls.crt' - - '--tls-private-key-file=/certs/tls.key' -... + - "--client-ca-file=/cabundle/service-ca.crt" + - "--tls-cert-file=/certs/tls.crt" + - "--tls-private-key-file=/certs/tls.key" ``` -The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). +The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). You should also make sure that `insecureSkipTLSVerify` is not set to `true`. ```yaml -... +--- spec: service: namespace: keda @@ -135,5 +133,4 @@ spec: YOURCABUNDLE... groupPriorityMinimum: 100 versionPriority: 100 -... ``` diff --git a/content/docs/2.8/concepts/external-scalers.md b/content/docs/2.8/concepts/external-scalers.md index 16f721117..d360d39cd 100644 --- a/content/docs/2.8/concepts/external-scalers.md +++ b/content/docs/2.8/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -39,7 +39,7 @@ The `Scaler` interface defines 4 methods: - `Close` is called to allow the scaler to clean up connections or other resources. - `GetMetricSpec` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`. For more details refer to [Implementing `GetMetrics`](#6-implementing-getmetrics). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -109,7 +109,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -176,8 +176,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -304,70 +302,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -444,38 +443,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -521,19 +518,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -600,32 +597,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.8/migration.md b/content/docs/2.8/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.8/migration.md +++ b/content/docs/2.8/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.8/operate/cluster.md b/content/docs/2.8/operate/cluster.md index 47365ca41..01a4f368d 100644 --- a/content/docs/2.8/operate/cluster.md +++ b/content/docs/2.8/operate/cluster.md @@ -29,12 +29,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -42,10 +40,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -80,16 +78,17 @@ Some scalers issue HTTP requests to external servers (i.e. cloud services). As c The Kubernetes client config used within KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| -------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| Adapter Flag | Client Config Setting | Default Value | Description | +| -------------- | --------------------- | ------------- | -------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -97,15 +96,16 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | -| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | -------------- | ------------- | ----------------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -128,20 +128,19 @@ By default, KEDA Metrics Server uses self-signed certificates while communicatin Certificates and CA bundle can be referenced in `args` section in KEDA Metrics Server Deployment: ```yaml -... +--- args: - - '--client-ca-file=/cabundle/service-ca.crt' - - '--tls-cert-file=/certs/tls.crt' - - '--tls-private-key-file=/certs/tls.key' -... + - "--client-ca-file=/cabundle/service-ca.crt" + - "--tls-cert-file=/certs/tls.crt" + - "--tls-private-key-file=/certs/tls.key" ``` -The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). +The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). You should also make sure that `insecureSkipTLSVerify` is not set to `true`. ```yaml -... +--- spec: service: namespace: keda @@ -153,5 +152,4 @@ spec: YOURCABUNDLE... groupPriorityMinimum: 100 versionPriority: 100 -... ``` diff --git a/content/docs/2.9/concepts/external-scalers.md b/content/docs/2.9/concepts/external-scalers.md index 5badde098..aa00a18a0 100644 --- a/content/docs/2.9/concepts/external-scalers.md +++ b/content/docs/2.9/concepts/external-scalers.md @@ -9,7 +9,7 @@ Built-in scalers run in the KEDA process/pod, while external scalers require an This document describes the external scaler interfaces and how to implement them in Go, Node, and .NET; however for more details on GRPC refer to [the official GRPC documentation](https://grpc.io/docs/) ->Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). +> Want to learn about existing external scalers? Explore our [external scaler community](https://github.com/kedacore/external-scalers). ## Overview @@ -45,7 +45,7 @@ The `Scaler` interface defines 3 methods: - `GetMetricSpecForScaling` returns the target value for the HPA definition for the scaler. For more details refer to [Implementing `GetMetricSpec`](#5-implementing-getmetricspec). - `GetMetricsAndActivity` is called on `pollingInterval` and. When activity returns `true`, KEDA will scale to what is returned by the metric limited by `maxReplicaCount` on the ScaledObject/ScaledJob. When `false` is returned, KEDA will scale to `minReplicaCount` or optionally `idleReplicaCount`. More details around the defaults and how these options work together can be found on the [ScaledObjectSpec](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaledobject-spec). -> Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. + > Refer to the [HPA docs](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) for how HPA calculates `replicaCount` based on metric value and target value. KEDA supports both `AverageValue` and `Value` metric target types for external metrics. When `AverageValue` (the default metric type) is used, the metric value returned by the external scaler will be divided by the number of replicas. The `PushScaler` interface adds a `Run` method. This method receives a push channel (`active`), on which the scaler can send `true` at any time. The purpose of this mechanism is to initiate a scaling operation independently from `pollingInterval`. @@ -115,7 +115,7 @@ KEDA will attempt a GRPC connection to `service-address.svc.local:9090` immediat } ``` ->**Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. +> **Note**: KEDA will issue all of the above RPC calls except `StreamIsActive` if `spec.triggers.type` is `external`. It _must_ be `external-push` for `StreamIsActive` to be called. ## Implementing KEDA external scaler GRPC interface @@ -182,8 +182,6 @@ npm install --save grpc request {{< /collapsible >}} -
- #### 3. Implementing `IsActive` Just like `IsActive(ctx context.Context) (bool, error)` in the go interface, the `IsActive` method in the GRPC interface is called every `pollingInterval` with a `ScaledObjectRef` object that contains the scaledObject name, namespace, and scaler metadata. @@ -310,70 +308,71 @@ public class ExternalScalerService : ExternalScaler.ExternalScalerBase Put the following code into your `index.js` file: ```js -const grpc = require('grpc') -const request = require('request') -const externalScalerProto = grpc.load('externalscaler.proto') +const grpc = require("grpc"); +const request = require("request"); +const externalScalerProto = grpc.load("externalscaler.proto"); -const server = new grpc.Server() +const server = new grpc.Server(); server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { isActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { - const now = new Date() - const yesterday = new Date(new Date().setDate(new Date().getDate()-1)); - - const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}` - const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}` - const radiusKm = 500 - const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}` - - request.get({ - url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, - json: true, - }, (err, resp, data) => { - if (err) { - callback({ - code: grpc.status.INTERNAL, - details: err, - }) - } else if (resp.statusCode !== 200) { - callback({ - code: grpc.status.INTERNAL, - details: `expected status 200, got ${resp.statusCode}` - }) - } else { - // count how many earthquakes with mag > 1.0 - let count = 0 - data.features.forEach(i => { - if (i.properties.mag > 1.0) { - count++ - } - }) - callback(null, { - result: count > 2, - }) + const now = new Date(); + const yesterday = new Date(new Date().setDate(new Date().getDate() - 1)); + + const startTime = `${yesterday.getUTCFullYear()}-${yesterday.getUTCMonth()}-${yesterday.getUTCDay()}`; + const endTime = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDay()}`; + const radiusKm = 500; + const query = `format=geojson&starttime=${startTime}&endtime=${endTime}&longitude=${longitude}&latitude=${latitude}&maxradiuskm=${radiusKm}`; + + request.get( + { + url: `https://earthquake.usgs.gov/fdsnws/event/1/query?${query}`, + json: true, + }, + (err, resp, data) => { + if (err) { + callback({ + code: grpc.status.INTERNAL, + details: err, + }); + } else if (resp.statusCode !== 200) { + callback({ + code: grpc.status.INTERNAL, + details: `expected status 200, got ${resp.statusCode}`, + }); + } else { + // count how many earthquakes with mag > 1.0 + let count = 0; + data.features.forEach((i) => { + if (i.properties.mag > 1.0) { + count++; + } + }); + callback(null, { + result: count > 2, + }); + } } - }) + ); } - } -}) + }, +}); -server.bind('127.0.0.1:9090', grpc.ServerCredentials.createInsecure()) -console.log('Server listening on 127.0.0.1:9090') +server.bind("127.0.0.1:9090", grpc.ServerCredentials.createInsecure()); +console.log("Server listening on 127.0.0.1:9090"); -server.start() +server.start(); ``` {{< /collapsible >}} -
- #### 4. Implementing `StreamIsActive` Unlike `IsActive`, `StreamIsActive` is called once when KEDA reconciles the `ScaledObject`, and expects the external scaler to maintain a long-lived connection and push `IsActiveResponse` objects whenever the scaler needs KEDA to activate the deployment. @@ -450,38 +449,36 @@ public override async Task StreamIsActive(ScaledObjectRef request, IServerStream server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... streamIsActive: (call, callback) => { - const longitude = call.request.scalerMetadata.longitude - const latitude = call.request.scalerMetadata.latitude + const longitude = call.request.scalerMetadata.longitude; + const latitude = call.request.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { const interval = setInterval(() => { getEarthquakeCount((err, count) => { if (err) { - console.error(err) + console.error(err); } else if (count > 2) { call.write({ result: true, - }) + }); } - }) + }); }, 1000 * 60 * 60); - call.on('end', () => { - clearInterval(interval) - }) + call.on("end", () => { + clearInterval(interval); + }); } - } -}) + }, +}); ``` {{< /collapsible >}} -
- #### 5. Implementing `GetMetricSpec` `GetMetricSpec` returns the `target` value for [the HPA definition for the scaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/). This scaler will define a static target of 10, but the threshold value is often specified in the metadata for other scalers. @@ -527,19 +524,19 @@ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetricSpec: (call, callback) => { callback(null, { - metricSpecs: [{ - metricName: 'earthquakeThreshold', - targetSize: 10, - }] - }) - } -}) + metricSpecs: [ + { + metricName: "earthquakeThreshold", + targetSize: 10, + }, + ], + }); + }, +}); ``` {{< /collapsible >}} -
- #### 6. Implementing `GetMetrics` `GetMetrics` returns the value of the metric referred to from `GetMetricSpec`, in this example it's `earthquakeThreshold`. @@ -606,32 +603,34 @@ public override async Task GetMetrics(GetMetricsRequest requ server.addService(externalScalerProto.externalscaler.ExternalScaler.service, { // ... getMetrics: (call, callback) => { - const longitude = call.request.scaledObjectRef.scalerMetadata.longitude - const latitude = call.request.scaledObjectRef.scalerMetadata.latitude + const longitude = call.request.scaledObjectRef.scalerMetadata.longitude; + const latitude = call.request.scaledObjectRef.scalerMetadata.latitude; if (!longitude || !latitude) { callback({ code: grpc.status.INVALID_ARGUMENT, - details: 'longitude and latitude must be specified', - }) + details: "longitude and latitude must be specified", + }); } else { getEarthquakeCount((err, count) => { if (err) { callback({ code: grpc.status.INTERNAL, details: err, - }) + }); } else { callback(null, { - metricValues: [{ - metricName: 'earthquakeThreshold', - metricValue: count, - }] - }) + metricValues: [ + { + metricName: "earthquakeThreshold", + metricValue: count, + }, + ], + }); } - }) + }); } - } -}) + }, +}); ``` {{< /collapsible >}} diff --git a/content/docs/2.9/migration.md b/content/docs/2.9/migration.md index 5ee8a6455..29e8408c8 100644 --- a/content/docs/2.9/migration.md +++ b/content/docs/2.9/migration.md @@ -11,15 +11,17 @@ Please note that you **can not** run both KEDA v1 and v2 on the same Kubernetes KEDA v2 is using a new API namespace for its Custom Resources Definitions (CRD): `keda.sh` instead of `keda.k8s.io` and introduces a new Custom Resource for scaling of Jobs. See full details on KEDA Custom Resources [here](../concepts/#custom-resources-crd). Here's an overview of what's changed: + - [Scaling of Deployments](#scaling-of-deployments) - [Scaling of Jobs](#scaling-of-jobs) - [Improved flexibility & usability of trigger metadata](#improved-flexibility--usability-of-trigger-metadata) - [Scalers](#scalers) - [TriggerAuthentication](#triggerauthentication) - ### Scaling of Deployments + In order to scale `Deployments` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Rename property `spec.scaleTargetRef.deploymentName` to `spec.scaleTargetRef.name` - Rename property `spec.scaleTargetRef.containerName` to `spec.scaleTargetRef.envSourceContainerName` @@ -27,22 +29,23 @@ In order to scale `Deployments` with KEDA v2, you need to do only a few modifica Please see the examples below or refer to the full [v2 ScaledObject Specification](../concepts/scaling-deployments/#scaledobject-spec) - **Example of v1 ScaledObject** +**Example of v1 ScaledObject** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } labels: - deploymentName: {deployment-name} + deploymentName: { deployment-name } spec: scaleTargetRef: - deploymentName: {deployment-name} - containerName: {container-name} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + deploymentName: { deployment-name } + containerName: { container-name } + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -50,18 +53,18 @@ spec: **Example of v2 ScaledObject** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed kind: ScaledObject -metadata: # <--- labels.deploymentName is not needed - name: {scaled-object-name} +metadata: # <--- labels.deploymentName is not needed + name: { scaled-object-name } spec: scaleTargetRef: - name: {deployment-name} # <--- Property name was changed - envSourceContainerName: {container-name} # <--- Property name was changed - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + name: { deployment-name } # <--- Property name was changed + envSourceContainerName: { container-name } # <--- Property name was changed + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to activate the deployment} ``` @@ -69,34 +72,36 @@ spec: ### Scaling of Jobs In order to scale `Jobs` with KEDA v2, you need to do only a few modifications to existing v1 `ScaledObjects` definitions, so they comply with v2: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` - Change the value of `kind` property from `ScaledObject` to `ScaledJob` - Remove property `spec.scaleType` -- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` +- Remove properties `spec.cooldownPeriod` and `spec.minReplicaCount` You can configure `successfulJobsHistoryLimit` and `failedJobsHistoryLimit`. They will remove the old job histories automatically. Please see the examples below or refer to the full [v2 ScaledJob Specification](../concepts/scaling-jobs/#scaledjob-spec) **Example of v1 ScaledObject for Jobs scaling** + ```yaml apiVersion: keda.k8s.io/v1alpha1 kind: ScaledObject metadata: - name: {scaled-object-name} + name: { scaled-object-name } spec: scaleType: job jobTargetRef: - parallelism: 1 + parallelism: 1 completions: 1 activeDeadlineSeconds: 600 - backoffLimit: 6 + backoffLimit: 6 template: # {job template} - pollingInterval: 30 - cooldownPeriod: 300 - minReplicaCount: 0 - maxReplicaCount: 100 + pollingInterval: 30 + cooldownPeriod: 300 + minReplicaCount: 0 + maxReplicaCount: 100 triggers: # {list of triggers to create jobs} ``` @@ -104,21 +109,21 @@ spec: **Example of v2 ScaledJob** ```yaml -apiVersion: keda.sh/v1alpha1 # <--- Property value was changed -kind: ScaledJob # <--- Property value was changed +apiVersion: keda.sh/v1alpha1 # <--- Property value was changed +kind: ScaledJob # <--- Property value was changed metadata: - name: {scaled-job-name} -spec: # <--- spec.scaleType is not needed + name: { scaled-job-name } +spec: # <--- spec.scaleType is not needed jobTargetRef: - parallelism: 1 - completions: 1 + parallelism: 1 + completions: 1 activeDeadlineSeconds: 600 backoffLimit: 6 template: # {job template} - pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed - successfulJobsHistoryLimit: 5 # <--- property is added - failedJobsHistoryLimit: 5 # <--- Property is added + pollingInterval: 30 # <--- spec.cooldownPeriod and spec.minReplicaCount are not needed + successfulJobsHistoryLimit: 5 # <--- property is added + failedJobsHistoryLimit: 5 # <--- Property is added maxReplicaCount: 100 triggers: # {list of triggers to create jobs} @@ -132,68 +137,74 @@ We've introduced more options to configure trigger metadata to give users more f Here's an overview: -| Scaler | 1.x | 2.0 | -|--------|--------|--------| -| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | -| `azure-monitor` | `activeDirectoryClientId`
`activeDirectoryClientPassword` | `activeDirectoryClientId`
`activeDirectoryClientIdFromEnv`
`activeDirectoryClientPasswordFromEnv` | -| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | -| `azure-servicebus` | `connection` | `connectionFromEnv` | -| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`)
`connection` (**Default**: `EventHub`) | `storageConnectionFromEnv`
`connectionFromEnv` | -| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`)
`awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID`
`awsAccessKeyIDFromEnv`
`awsSecretAccessKeyFromEnv` | -| `kafka` | _(none)_ | _(none)_ -| `rabbitmq` | `apiHost`
`host` | ~~`apiHost`~~
`host`
`hostFromEnv` | -| `prometheus` | _(none)_ | _(none)_ | -| `cron` | _(none)_ | _(none)_ | -| `redis` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `redis-streams` | `address`
`host`
`port`
`password` | `address`
`addressFromEnv`
`host`
`hostFromEnv`
~~`port`~~
`passwordFromEnv` -| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | -| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ -| `liiklus` | _(none)_ | _(none)_ | -| `stan` | _(none)_ | _(none)_ | -| `huawei-cloudeye` | | _(none)_ | _(none)_ | -| `postgresql` | `connection`
`password` | `connectionFromEnv`
`passwordFromEnv` | -| `mysql` | `connectionString`
`password` | `connectionStringFromEnv`
`passwordFromEnv` | +| Scaler | 1.x | 2.0 | +| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- | +| `azure-blob` | `connection` (**Default**: `AzureWebJobsStorage`) | `connectionFromEnv` | +| `azure-monitor` | `activeDirectoryClientId` `activeDirectoryClientPassword` | `activeDirectoryClientId` `activeDirectoryClientIdFromEnv` `activeDirectoryClientPasswordFromEnv` | +| `azure-queue` | `connection` (**Default**: AzureWebJobsStorage) | `connectionFromEnv` | +| `azure-servicebus` | `connection` | `connectionFromEnv` | +| `azure-eventhub` | `storageConnection` (**Default**: `AzureWebJobsStorage`) `connection` (**Default**: `EventHub`) | `storageConnectionFromEnv` `connectionFromEnv` | +| `aws-cloudwatch` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-kinesis-stream` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `aws-sqs-queue` | `awsAccessKeyID` (**Default**: `AWS_ACCESS_KEY_ID`) `awsSecretAccessKey` (**Default**: `AWS_SECRET_ACCESS_KEY`) | `awsAccessKeyID` `awsAccessKeyIDFromEnv` `awsSecretAccessKeyFromEnv` | +| `kafka` | _(none)_ | _(none)_ | +| `rabbitmq` | `apiHost` `host` | ~~`apiHost`~~ `host` `hostFromEnv` | +| `prometheus` | _(none)_ | _(none)_ | +| `cron` | _(none)_ | _(none)_ | +| `redis` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `redis-streams` | `address` `host` `port` `password` | `address` `addressFromEnv` `host` `hostFromEnv` ~~`port`~~ `passwordFromEnv` | +| `gcp-pubsub` | `credentials` | `credentialsFromEnv` | +| `external` | _(any matching value)_ | _(any matching value with `FromEnv` suffix)_ | +| `liiklus` | _(none)_ | _(none)_ | +| `stan` | _(none)_ | _(none)_ | +| `huawei-cloudeye` | | _(none)_ | _(none)_ | +| `postgresql` | `connection` `password` | `connectionFromEnv` `passwordFromEnv` | +| `mysql` | `connectionString` `password` | `connectionStringFromEnv` `passwordFromEnv` | ### Scalers **Azure Service Bus** - - `queueLength` was renamed to `messageCount` + +- `queueLength` was renamed to `messageCount` **Kafka** - - `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. + +- `authMode` property was replaced with `sasl` and `tls` properties. Please refer [documentation](../scalers/apache-kafka/#authentication-parameters) for Kafka Authentication Parameters details. **RabbitMQ** In KEDA 2.0 the RabbitMQ scaler has only `host` parameter, and the protocol for communication can be specified by -`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP -protocol. +`protocol` (http or amqp). The default value is `amqp`. The behavior changes only for scalers that were using HTTP +protocol. Example of RabbitMQ trigger before 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - includeUnacked: 'true' - apiHost: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + includeUnacked: "true" + apiHost: "https://guest:password@localhost:443/vhostname" +``` The same trigger in 2.0: + ```yaml triggers: -- type: rabbitmq - metadata: - queueLength: '20' - queueName: testqueue - protocol: 'http' - host: 'https://guest:password@localhost:443/vhostname' -``` + - type: rabbitmq + metadata: + queueLength: "20" + queueName: testqueue + protocol: "http" + host: "https://guest:password@localhost:443/vhostname" +``` ### TriggerAuthentication + In order to use Authentication via `TriggerAuthentication` with KEDA v2, you need to change: + - Change the value of `apiVersion` property from `keda.k8s.io/v1alpha1` to `keda.sh/v1alpha1` For more details please refer to the full diff --git a/content/docs/2.9/operate/cluster.md b/content/docs/2.9/operate/cluster.md index 5cc6024bb..18bca5f61 100644 --- a/content/docs/2.9/operate/cluster.md +++ b/content/docs/2.9/operate/cluster.md @@ -14,11 +14,11 @@ However, maintainers can decide to extend this by supporting more minor versions As a reference, this compatibility matrix shows supported k8s versions per KEDA version: -| KEDA | Kubernetes | -|-----------|---------------| -| v2.9 | v1.23 - v1.25 | -| v2.8 | v1.17 - v1.25 | -| v2.7 | v1.17 - v1.25 | +| KEDA | Kubernetes | +| ---- | ------------- | +| v2.9 | v1.23 - v1.25 | +| v2.8 | v1.17 - v1.25 | +| v2.7 | v1.17 - v1.25 | ### Cluster Capacity @@ -39,12 +39,10 @@ KEDA requires to be accessible inside the cluster to be able to autoscale. Here is an overview of the required ports that need to be accessible for KEDA to work: - | Port | Why? | Remarks | | ------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication.

This is not applicable for Google Cloud. | +| `443` | Used by Kubernetes API server to get metrics | Required for all platforms because it uses Control Plane → port 443 on the Service IP range communication. This is not applicable for Google Cloud. | | `6443` | Used by Kubernetes API server to get metrics | Only required for Google Cloud because it uses Control Plane → port 6443 on the Pod IP range for communication | - ## High Availability @@ -52,10 +50,10 @@ KEDA does not provide full support for high-availability due to upstream limitat Here is an overview of all KEDA deployments and the HA notes: -| Deployment | Support Replicas | Note | -| -------------- | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Deployment | Support Replicas | Note | +| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Metrics Server | 1 | You can run multiple replicas of our metrics sever, and it is recommended to add the `--enable-aggregator-routing=true` CLI flag to the kube-apiserver so that requests sent to our metrics servers are load balanced. However, [you can only run one active metric server in a Kubernetes cluster serving external.metrics.k8s.io](https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/70) which has to be the KEDA metric server. | -| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | +| Operator | 2 | While you can run multiple replicas of our operator, only one operator instance will be active. The rest will be standing by, which may reduce downtime during a failure. Multiple replicas will not improve the performance of KEDA, it could only reduce a downtime during a failover. | ## HTTP Timeouts @@ -77,7 +75,7 @@ All applicable scalers will use this timeout. Setting a per-scaler timeout is cu ## HTTP connection disable keep alive -Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. +Keep alive behaviour is enabled by default for every HTTP connection, this could stack a huge amount of connections (one per scaler) in some scenarios. You can disable keep alive for every HTTP connection by adding the relevant environment variable to both the KEDA Operator, and KEDA Metrics Server deployments: @@ -103,17 +101,18 @@ Some scalers issue HTTP requests to external servers (i.e. cloud services). As c The Kubernetes client config used within KEDA Operator and KEDA Metrics Adapter can be adjusted by passing the following command-line flags to the binary: -| Adapter Flag | Client Config Setting | Default Value | Description | -| ------------------- | ----------------------- | ------------- | -------------------------------------------------------------- | -| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | -| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | -| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | +| Adapter Flag | Client Config Setting | Default Value | Description | +| ------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| kube-api-qps | cfg.QPS | 20.0 | Set the QPS rate for throttling requests sent to the apiserver | +| kube-api-burst | cfg.Burst | 30 | Set the burst for throttling requests sent to the apiserver | +| disable-compression | cfg.DisableCompression | true | Disable compression for response in k8s restAPI in client-go, see [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/112296) for details | ## Configure `MaxConcurrentReconciles` for Controllers To implement internal controllers KEDA uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime), that enables configuration of [MaxConcurrentReconciles property](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller#Options), ie. the maximum number of concurrent reconciles which can be run for a controller. KEDA Operator exposes properties for specifying `MaxConcurrentReconciles` for following controllers/reconcilers: + - `ScaledObjectReconciler` - responsible for watching and managing `ScaledObjects`, ie. validates input trigger specification, starts scaling logic and manages dependent HPA. - `ScaledJobReconciler` - responsible for watching and managing `ScaledJobs` and dependent Kubernetes Jobs @@ -121,15 +120,16 @@ KEDA Metrics Server exposes property for specifying `MaxConcurrentReconciles` fo To modify this properties you can set environment variables on both KEDA Operator and Metrics Server Deployments: -| Environment variable name | Deployment | Default Value | Affected reconciler | -| ------------------------------------- | -------------- | ------------- | -------------------------------------------------------------- | -| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | -| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | -| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | +| Environment variable name | Deployment | Default Value | Affected reconciler | +| ------------------------------------- | -------------- | ------------- | ----------------------------- | +| KEDA_SCALEDOBJECT_CTRL_MAX_RECONCILES | Operator | 5 | ScaledObjectReconciler | +| KEDA_SCALEDJOB_CTRL_MAX_RECONCILES | Operator | 1 | ScaledJobReconciler | +| KEDA_METRICS_CTRL_MAX_RECONCILES | Metrics Server | 1 | MetricsScaledObjectReconciler | ## Configure Leader Election Like reconciliation, KEDA also uses the [controller-runtime project](https://github.com/kubernetes-sigs/controller-runtime) for electing the leader replica. The following properties can be configured for either the Operator and Metrics Server Deployment: + - [`LeaseDuration`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.LeaseDuration) - [`RenewDeadline`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RenewDeadline) - [`RetryPeriod`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options.RetryPeriod) @@ -152,20 +152,19 @@ By default KEDA Metrics Server uses self-signed certificates while communicating Certificates and CA bundle can be referenced in `args` section in KEDA Metrics Server Deployment: ```yaml -... +--- args: - - '--client-ca-file=/cabundle/service-ca.crt' - - '--tls-cert-file=/certs/tls.crt' - - '--tls-private-key-file=/certs/tls.key' -... + - "--client-ca-file=/cabundle/service-ca.crt" + - "--tls-cert-file=/certs/tls.crt" + - "--tls-private-key-file=/certs/tls.key" ``` -The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). +The custom CA bundle should be also referenced in the `v1beta1.external.metrics.k8s.io` [APIService](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/api-service-v1/#APIServiceSpec) resource (which is created during the installation of KEDA). You should also make sure that `insecureSkipTLSVerify` is not set to `true`. ```yaml -... +--- spec: service: namespace: keda @@ -177,31 +176,34 @@ spec: YOURCABUNDLE... groupPriorityMinimum: 100 versionPriority: 100 -... ``` ## Restrict Secret Access By default, KEDA requires adding `secrets` to the cluster role as following: + ```yaml - apiGroups: - - "" + - "" resources: - - external - - pods - - secrets - - services + - external + - pods + - secrets + - services verbs: - - get - - list - - watch + - get + - list + - watch ``` + However, this might lead to security risk (especially in production environment) since it will grant permission to read `secrets` from all namespaces. To restrict `secret` access and limited to KEDA namespace, you could add `KEDA_RESTRICT_SECRET_ACCESS` as environment variable to both KEDA Operator and KEDA Metrics Server: + ```yaml env: - name: KEDA_RESTRICT_SECRET_ACCESS value: "true" ``` + This allows you to omit `secrets` from the cluster role, which will disallow `TriggerAuthentication` to be used for your triggers if the `TriggerAuthentication` is using secrets. You can, however, still use `ClusterTriggerAuthentication`.