diff --git a/charts/selenium-grid/Chart.yaml b/charts/selenium-grid/Chart.yaml index c7e446434..da48e009a 100644 --- a/charts/selenium-grid/Chart.yaml +++ b/charts/selenium-grid/Chart.yaml @@ -5,3 +5,8 @@ type: application version: 0.18.1 appVersion: 4.10.0-20230607 icon: https://github.com/SeleniumHQ/docker-selenium/raw/trunk/logo.png +dependencies: +- repository: https://kedacore.github.io/charts + version: 2.10.2 + name: keda + condition: autoscaling.enabled diff --git a/charts/selenium-grid/README.md b/charts/selenium-grid/README.md index 7c84e4067..528401e6e 100644 --- a/charts/selenium-grid/README.md +++ b/charts/selenium-grid/README.md @@ -29,6 +29,30 @@ helm install selenium-grid docker-selenium/selenium-grid --version helm install selenium-grid --set ingress.hostname=selenium-grid.k8s.local docker-selenium/chart/selenium-grid/. ``` +## Enable Selenium Grid Autoscaling + +Selenium Grid has the ability to autoscale browser nodes up/down based on the pending requests in the +session queue. + +To do this [KEDA](https://keda.sh/docs/2.10/scalers/selenium-grid-scaler/) is used. When enabling +autoscaling using `autoscaling.enabling` KEDA is installed automatically. To instead use an existing +installation of KEDA you can enable autoscaling with `autoscaling.enableWithExistingKEDA` instead. + +KEDA can scale either with +[deployments](https://keda.sh/docs/2.10/concepts/scaling-deployments/#scaling-of-deployments-and-statefulsets) +or [jobs](https://keda.sh/docs/2.10/concepts/scaling-jobs/) and the charts support both types. This +chart support both modes. It is controlled with `autoscaling.scalingType` that can be set to either +job (default) or deployment. + +### Settings when scaling with deployments + +The `terminationGracePeriodSeconds` is set to 30 seconds by default. When scaling using deployments +the HPA choose pods to terminate randomly. If the chosen pod is currently executing a test rather +than being idle, then there is 30 seconds before the test is expected to complete. If your test is +still executing after 30 seconds, it would result in failure as the pod will be killed. If you want +to give more time for your tests to complete, you may set `terminationGracePeriodSeconds` to value +upto 3600 seconds. + ## Updating Selenium-Grid release Once you have a new chart version, you can update your selenium-grid running: @@ -51,152 +75,174 @@ For now, global configuration supported is: | Parameter | Default | Description | | ----------------------------------- | ---------------------------------- | ------------------------------------- | -| `global.seleniumGrid.imageTag` | `4.10.0-20230607` | Image tag for all selenium components | -| `global.seleniumGrid.nodesImageTag` | `4.10.0-20230607` | Image tag for browser's nodes | +| `global.seleniumGrid.imageTag` | `4.10.0-20230607` | Image tag for all selenium components | +| `global.seleniumGrid.nodesImageTag` | `4.10.0-20230607` | Image tag for browser's nodes | | `global.seleniumGrid.imagePullSecret` | `""` | Pull secret to be used for all images | | `global.seleniumGrid.imagePullSecret` | `""` | Pull secret to be used for all images | -| `global.seleniumGrid.affinity` | `{}` | Affinity assigned globally | +| `global.seleniumGrid.affinity` | `{}` | Affinity assigned globally | This table contains the configuration parameters of the chart and their default values: -| Parameter | Default | Description | -|---------------------------------------------|-----------------------------|----------------------------------------------------------------------------------------------------------------------------| -| `isolateComponents` | `false` | Deploy Router, Distributor, EventBus, SessionMap and Nodes separately | -| `busConfigMap.name` | `selenium-event-bus-config` | Name of the configmap that contains SE_EVENT_BUS_HOST, SE_EVENT_BUS_PUBLISH_PORT and SE_EVENT_BUS_SUBSCRIBE_PORT variables | -| `ingress.enabled` | `true` | Enable or disable ingress resource | -| `ingress.className` | `""` | Name of ingress class to select which controller will implement ingress resource | -| `ingress.annotations` | `{}` | Custom annotations for ingress resource | -| `ingress.hostname` | `selenium-grid.local` | Default host for the ingress resource | -| `ingress.path` | `/` | Default host path for the ingress resource | -| `ingress.tls` | `[]` | TLS backend configuration for ingress resource | -| `busConfigMap.annotations` | `{}` | Custom annotations for configmap | -| `chromeNode.enabled` | `true` | Enable chrome nodes | -| `chromeNode.deploymentEnabled` | `true` | Enable creation of Deployment for chrome nodes | -| `chromeNode.replicas` | `1` | Number of chrome nodes | -| `chromeNode.imageName` | `selenium/node-chrome` | Image of chrome nodes | -| `chromeNode.imageTag` | `4.10.0-20230607` | Image of chrome nodes | -| `chromeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `chromeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `chromeNode.ports` | `[5555]` | Port list to enable on container | -| `chromeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | -| `chromeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | -| `chromeNode.annotations` | `{}` | Annotations for chrome-node pods | -| `chromeNode.labels` | `{}` | Labels for chrome-node pods | -| `chromeNode.resources` | `See values.yaml` | Resources for chrome-node pods | -| `chromeNode.securityContext` | `See values.yaml` | Security context for chrome-node pods | -| `chromeNode.tolerations` | `[]` | Tolerations for chrome-node pods | -| `chromeNode.nodeSelector` | `{}` | Node Selector for chrome-node pods | -| `chromeNode.affinity` | `{}` | Affinity for chrome-node pods | -| `chromeNode.hostAliases` | `nil` | Custom host aliases for chrome nodes | -| `chromeNode.priorityClassName` | `""` | Priority class name for chrome-node pods | -| `chromeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for chrome nodes | -| `chromeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for chrome nodes | -| `chromeNode.service.enabled` | `true` | Create a service for node | -| `chromeNode.service.type` | `ClusterIP` | Service type | -| `chromeNode.service.annotations` | `{}` | Custom annotations for service | -| `chromeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | -| `chromeNode.startupProbe` | `{}` | Probe to check pod is started successfully | -| `chromeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | -| `chromeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | -| `chromeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | -| `chromeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | -| `firefoxNode.enabled` | `true` | Enable firefox nodes | -| `firefoxNode.deploymentEnabled` | `true` | Enable creation of Deployment for firefox nodes | -| `firefoxNode.replicas` | `1` | Number of firefox nodes | -| `firefoxNode.imageName` | `selenium/node-firefox` | Image of firefox nodes | -| `firefoxNode.imageTag` | `4.10.0-20230607` | Image of firefox nodes | -| `firefoxNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `firefoxNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `firefoxNode.ports` | `[5555]` | Port list to enable on container | -| `firefoxNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | -| `firefoxNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | -| `firefoxNode.annotations` | `{}` | Annotations for firefox-node pods | -| `firefoxNode.labels` | `{}` | Labels for firefox-node pods | -| `firefoxNode.resources` | `See values.yaml` | Resources for firefox-node pods | -| `firefoxNode.securityContext` | `See values.yaml` | Security context for firefox-node pods | -| `firefoxNode.tolerations` | `[]` | Tolerations for firefox-node pods | -| `firefoxNode.nodeSelector` | `{}` | Node Selector for firefox-node pods | -| `firefoxNode.affinity` | `{}` | Affinity for -firefox-node pods | -| `firefoxNode.hostAliases` | `nil` | Custom host aliases for firefox nodes | -| `firefoxNode.priorityClassName` | `""` | Priority class name for firefox-node pods | -| `firefoxNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | -| `firefoxNode.extraEnvFrom` | `nil` | Custom environment variables taken from `configMap` or `secret` for firefox nodes | -| `firefoxNode.service.enabled` | `true` | Create a service for node | -| `firefoxNode.service.type` | `ClusterIP` | Service type | -| `firefoxNode.service.annotations` | `{}` | Custom annotations for service | -| `firefoxNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | -| `firefoxNode.startupProbe` | `{}` | Probe to check pod is started successfully | -| `firefoxNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | -| `firefoxNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | -| `firefoxNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | -| `firefoxNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | -| `edgeNode.enabled` | `true` | Enable edge nodes | -| `edgeNode.deploymentEnabled` | `true` | Enable creation of Deployment for edge nodes | -| `edgeNode.replicas` | `1` | Number of edge nodes | -| `edgeNode.imageName` | `selenium/node-edge` | Image of edge nodes | -| `edgeNode.imageTag` | `4.10.0-20230607` | Image of edge nodes | -| `edgeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `edgeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `edgeNode.ports` | `[5555]` | Port list to enable on container | -| `edgeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | -| `edgeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | -| `edgeNode.annotations` | `{}` | Annotations for edge-node pods | -| `edgeNode.labels` | `{}` | Labels for edge-node pods | -| `edgeNode.resources` | `See values.yaml` | Resources for edge-node pods | -| `edgeNode.securityContext` | `See values.yaml` | Security context for edge-node pods | -| `edgeNode.tolerations` | `[]` | Tolerations for edge-node pods | -| `edgeNode.nodeSelector` | `{}` | Node Selector for edge-node pods | -| `edgeNode.affinity` | `{}` | Affinity for -edge-node -pods | -| `edgeNode.hostAliases` | `nil` | Custom host aliases for edge nodes | -| `edgeNode.priorityClassName` | `""` | Priority class name for edge-node pods | -| `edgeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | -| `edgeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for firefox nodes | -| `edgeNode.service.enabled` | `true` | Create a service for node | -| `edgeNode.service.type` | `ClusterIP` | Service type | -| `edgeNode.service.annotations` | `{}` | Custom annotations for service | -| `edgeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | -| `edgeNode.startupProbe` | `{}` | Probe to check pod is started successfully | -| `edgeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | -| `edgeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | -| `edgeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | -| `edgeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | -| `customLabels` | `{}` | Custom labels for k8s resources | +| Parameter | Default | Description | +| --------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `isolateComponents` | `false` | Deploy Router, Distributor, EventBus, SessionMap and Nodes separately | +| `busConfigMap.name` | `selenium-event-bus-config` | Name of the configmap that contains SE_EVENT_BUS_HOST, SE_EVENT_BUS_PUBLISH_PORT and SE_EVENT_BUS_SUBSCRIBE_PORT variables | +| `busConfigMap.annotations` | `{}` | Custom annotations for configmap | +| `nodeConfigMap.name` | `selenium-node-config` | Name of the configmap that contains common environment variables for browser nodes | +| `nodeConfigMap.annotations` | `{}` | Custom annotations for configmap | +| `ingress.enabled` | `true` | Enable or disable ingress resource | +| `ingress.className` | `""` | Name of ingress class to select which controller will implement ingress resource | +| `ingress.annotations` | `{}` | Custom annotations for ingress resource | +| `ingress.hostname` | `selenium-grid.local` | Default host for the ingress resource | +| `ingress.path` | `/` | Default host path for the ingress resource | +| `ingress.tls` | `[]` | TLS backend configuration for ingress resource | +| `autoscaling.enableWithExistingKEDA` | `false` | Enable autoscaling of browser nodes. | +| `autoscaling.enabled` | `false` | Same as above plus installation of KEDA | +| `autoscaling.scalingType` | `job` | Which typ of KEDA scaling to use: job or deployment | +| `autoscaling.scaledJobOptions` | See `values.yaml` | Options for KEDA ScaledJobs | +| `autoscaling.deregisterLifecycle | See `values.yaml` | Lifecycle applied to pods of deployments controlled by KEDA. Makes the node deregister from selenium hub | +| `chromeNode.enabled` | `true` | Enable chrome nodes | +| `chromeNode.deploymentEnabled` | `true` | Enable creation of Deployment for chrome nodes | +| `chromeNode.replicas` | `1` | Number of chrome nodes | +| `chromeNode.imageName` | `selenium/node-chrome` | Image of chrome nodes | +| `chromeNode.imageTag` | `4.10.0-20230607` | Image of chrome nodes | +| `chromeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `chromeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `chromeNode.ports` | `[5555]` | Port list to enable on container | +| `chromeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | +| `chromeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | +| `chromeNode.annotations` | `{}` | Annotations for chrome-node pods | +| `chromeNode.labels` | `{}` | Labels for chrome-node pods | +| `chromeNode.resources` | `See values.yaml` | Resources for chrome-node pods | +| `chromeNode.securityContext` | `See values.yaml` | Security context for chrome-node pods | +| `chromeNode.tolerations` | `[]` | Tolerations for chrome-node pods | +| `chromeNode.nodeSelector` | `{}` | Node Selector for chrome-node pods | +| `chromeNode.affinity` | `{}` | Affinity for chrome-node pods | +| `chromeNode.hostAliases` | `nil` | Custom host aliases for chrome nodes | +| `chromeNode.priorityClassName` | `""` | Priority class name for chrome-node pods | +| `chromeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for chrome nodes | +| `chromeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for chrome nodes | +| `chromeNode.service.enabled` | `true` | Create a service for node | +| `chromeNode.service.type` | `ClusterIP` | Service type | +| `chromeNode.service.annotations` | `{}` | Custom annotations for service | +| `chromeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | +| `chromeNode.startupProbe` | `{}` | Probe to check pod is started successfully | +| `chromeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `chromeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | +| `chromeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `chromeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `chromeNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | +| `chromeNode.hpa.browserName` | `chrome` | BrowserName from the capability | +| `chromeNode.hpa.browserVersion` | `` | BrowserVersion from the capability | +| `chromeNode.maxReplicaCount` | `8` | Max number of replicas that this browsernode can auto scale up to | +| `firefoxNode.enabled` | `true` | Enable firefox nodes | +| `firefoxNode.deploymentEnabled` | `true` | Enable creation of Deployment for firefox nodes | +| `firefoxNode.replicas` | `1` | Number of firefox nodes | +| `firefoxNode.imageName` | `selenium/node-firefox` | Image of firefox nodes | +| `firefoxNode.imageTag` | `4.10.0-20230607` | Image of firefox nodes | +| `firefoxNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `firefoxNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `firefoxNode.ports` | `[5555]` | Port list to enable on container | +| `firefoxNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | +| `firefoxNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | +| `firefoxNode.annotations` | `{}` | Annotations for firefox-node pods | +| `firefoxNode.labels` | `{}` | Labels for firefox-node pods | +| `firefoxNode.resources` | `See values.yaml` | Resources for firefox-node pods | +| `firefoxNode.securityContext` | `See values.yaml` | Security context for firefox-node pods | +| `firefoxNode.tolerations` | `[]` | Tolerations for firefox-node pods | +| `firefoxNode.nodeSelector` | `{}` | Node Selector for firefox-node pods | +| `firefoxNode.affinity` | `{}` | Affinity for firefox-node pods | +| `firefoxNode.hostAliases` | `nil` | Custom host aliases for firefox nodes | +| `firefoxNode.priorityClassName` | `""` | Priority class name for firefox-node pods | +| `firefoxNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | +| `firefoxNode.extraEnvFrom` | `nil` | Custom environment variables taken from `configMap` or `secret` for firefox nodes | +| `firefoxNode.service.enabled` | `true` | Create a service for node | +| `firefoxNode.service.type` | `ClusterIP` | Service type | +| `firefoxNode.service.annotations` | `{}` | Custom annotations for service | +| `firefoxNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | +| `firefoxNode.startupProbe` | `{}` | Probe to check pod is started successfully | +| `firefoxNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `firefoxNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | +| `firefoxNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `firefoxNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `firefoxNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | +| `firefoxNode.hpa.browserName` | `firefox` | BrowserName from the capability | +| `firefoxNode.hpa.browserVersion` | `` | BrowserVersion from the capability | +| `firefoxNode.maxReplicaCount` | `8` | Max number of replicas that this browsernode can auto scale up to | +| `edgeNode.enabled` | `true` | Enable edge nodes | +| `edgeNode.deploymentEnabled` | `true` | Enable creation of Deployment for edge nodes | +| `edgeNode.replicas` | `1` | Number of edge nodes | +| `edgeNode.imageName` | `selenium/node-edge` | Image of edge nodes | +| `edgeNode.imageTag` | `4.10.0-20230607` | Image of edge nodes | +| `edgeNode.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `edgeNode.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `edgeNode.ports` | `[5555]` | Port list to enable on container | +| `edgeNode.seleniumPort` | `5900` | Selenium port (spec.ports[0].targetPort in kubernetes service) | +| `edgeNode.seleniumServicePort` | `6900` | Selenium port exposed in service (spec.ports[0].port in kubernetes service) | +| `edgeNode.annotations` | `{}` | Annotations for edge-node pods | +| `edgeNode.labels` | `{}` | Labels for edge-node pods | +| `edgeNode.resources` | `See values.yaml` | Resources for edge-node pods | +| `edgeNode.securityContext` | `See values.yaml` | Security context for edge-node pods | +| `edgeNode.tolerations` | `[]` | Tolerations for edge-node pods | +| `edgeNode.nodeSelector` | `{}` | Node Selector for edge-node pods | +| `edgeNode.affinity` | `{}` | Affinity for edge-node pods | +| `edgeNode.hostAliases` | `nil` | Custom host aliases for edge nodes | +| `edgeNode.priorityClassName` | `""` | Priority class name for edge-node pods | +| `edgeNode.extraEnvironmentVariables` | `nil` | Custom environment variables for firefox nodes | +| `edgeNode.extraEnvFrom` | `nil` | Custom environment taken from `configMap` or `secret` variables for firefox nodes | +| `edgeNode.service.enabled` | `true` | Create a service for node | +| `edgeNode.service.type` | `ClusterIP` | Service type | +| `edgeNode.service.annotations` | `{}` | Custom annotations for service | +| `edgeNode.dshmVolumeSizeLimit` | `1Gi` | Size limit for DSH volume mounted in container (if not set, default is "1Gi") | +| `edgeNode.startupProbe` | `{}` | Probe to check pod is started successfully | +| `edgeNode.terminationGracePeriodSeconds` | `30` | Time to graceful terminate container (default: 30s) | +| `edgeNode.lifecycle` | `{}` | hooks to make pod correctly shutdown or started | +| `edgeNode.extraVolumeMounts` | `[]` | Extra mounts of declared ExtraVolumes into pod | +| `edgeNode.extraVolumes` | `[]` | Extra Volumes declarations to be used in the pod (can be any supported volume type: ConfigMap, Secret, PVC, NFS, etc.) | +| `edgeNode.hpa.url` | `{{ include "seleniumGrid.graphqlURL" . }}` | Graphql Url of the hub or the router | +| `edgeNode.hpa.browserName` | `edge` | BrowserName from the capability | +| `edgeNode.hpa.browserVersion` | `` | BrowserVersion from the capability | +| `edgeNode.maxReplicaCount` | `8` | Max number of replicas that this browsernode can auto scale up to | +| `customLabels` | `{}` | Custom labels for k8s resources | +| `customLabels` | `{}` | Custom labels for k8s resources | + + +### Configuration of KEDA +If you are setting `autoscaling.enabled` to `true` KEDA is installed and can be configured with +values with the prefix `keda`. So you can for example set `keda.prometheus.metricServer.enabled` to +`true` to enable the metrics server for KEDA. See +https://github.com/kedacore/charts/blob/main/keda/README.md for more details. ### Configuration for Selenium-Hub You can configure the Selenium Hub with this values: -| Parameter | Default | Description | -|---------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| -| `hub.imageName` | `selenium/hub` | Selenium Hub image name | -| `hub.imageTag` | `nil` | Selenium Hub image tag (this overwrites `.global.seleniumGrid.imageTag` value) | -| `hub.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | -| `hub.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | -| `hub.annotations` | `{}` | Custom annotations for Selenium Hub pod | -| `hub.labels` | `{}` | Custom labels for Selenium Hub pod | -| `hub.publishPort` | `4442` | Port where events are published | -| `hub.subscribePort` | `4443` | Port where to subscribe for events | -| `hub.port` | `4444` | Selenium Hub port | -| `hub.livenessProbe` | `See values.yaml` | Liveness probe settings | -| `hub.readinessProbe` | `See values.yaml` | Readiness probe settings | -| `hub.tolerations` | `[]` | Tolerations for selenium-hub pods | -| `hub.nodeSelector` | `{}` | Node Selector for selenium-hub pods | -| `hub.affinity` | `{}` | Affinity for -selenium-hub -pods | -| `hub.priorityClassName` | `""` | Priority class name for selenium-hub pods | -| `hub.subPath` | `/` | Custom sub path for the hub deployment | -| `hub.extraEnvironmentVariables` | `nil` | Custom environment variables for selenium-hub | -| `hub.extraEnvFrom` | `nil` | Custom environment variables for selenium taken from `configMap` or `secret`-hub | -| `hub.resources` | `{}` | Resources for selenium-hub container | -| `hub.securityContext` | `See values.yaml` | Security context for selenium-hub container | -| `hub.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | -| `hub.loadBalancerIP` | `nil` | Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) | -| `hub.serviceAnnotations` | `{}` | Custom annotations for Selenium Hub service | +| Parameter | Default | Description | +|---------------------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| `hub.imageName` | `selenium/hub` | Selenium Hub image name | +| `hub.imageTag` | `nil` | Selenium Hub image tag (this overwrites `.global.seleniumGrid.imageTag` value) | +| `hub.imagePullPolicy` | `IfNotPresent` | Image pull policy (see https://kubernetes.io/docs/concepts/containers/images/#updating-images) | +| `hub.imagePullSecret` | `""` | Image pull secret (see https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry) | +| `hub.annotations` | `{}` | Custom annotations for Selenium Hub pod | +| `hub.labels` | `{}` | Custom labels for Selenium Hub pod | +| `hub.publishPort` | `4442` | Port where events are published | +| `hub.subscribePort` | `4443` | Port where to subscribe for events | +| `hub.port` | `4444` | Selenium Hub port | +| `hub.livenessProbe` | `See values.yaml` | Liveness probe settings | +| `hub.readinessProbe` | `See values.yaml` | Readiness probe settings | +| `hub.tolerations` | `[]` | Tolerations for selenium-hub pods | +| `hub.nodeSelector` | `{}` | Node Selector for selenium-hub pods | +| `hub.affinity` | `{}` | Affinity for selenium-hub pods | +| `hub.priorityClassName` | `""` | Priority class name for selenium-hub pods | +| `hub.subPath` | `/` | Custom sub path for the hub deployment | +| `hub.extraEnvironmentVariables` | `nil` | Custom environment variables for selenium-hub | +| `hub.extraEnvFrom` | `nil` | Custom environment variables for selenium taken from `configMap` or `secret`-hub | +| `hub.resources` | `{}` | Resources for selenium-hub container | +| `hub.securityContext` | `See values.yaml` | Security context for selenium-hub container | +| `hub.serviceType` | `ClusterIP` | Kubernetes service type (see https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) | +| `hub.loadBalancerIP` | `nil` | Set specific loadBalancerIP when serviceType is LoadBalancer (see https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) | +| `hub.serviceAnnotations` | `{}` | Custom annotations for Selenium Hub service | ### Configuration for isolated components @@ -220,9 +266,7 @@ If you implement selenium-grid with separate components (`isolateComponents: tru | `components.router.serviceAnnotations` | `{}` | Custom annotations for router service | | `components.router.tolerations` | `[]` | Tolerations for router pods | | `components.router.nodeSelector` | `{}` | Node Selector for router pods | -| `components.router.affinity` | `{}` | Affinity for -router -pods | +| `components.router.affinity` | `{}` | Affinity for router pods | | `components.router.priorityClassName` | `""` | Priority class name for router pods | | `components.distributor.imageName` | `selenium/distributor` | Distributor image name | | `components.distributor.imageTag` | `nil` | Distributor image tag (this overwrites `.global.seleniumGrid.imageTag` value) | @@ -236,9 +280,7 @@ pods | `components.distributor.serviceAnnotations` | `{}` | Custom annotations for Distributor service | | `components.distributor.tolerations` | `[]` | Tolerations for Distributor pods | | `components.distributor.nodeSelector` | `{}` | Node Selector for Distributor pods | -| `components.distributor.affinity` | `{}` | Affinity for -Distributor -pods | +| `components.distributor.affinity` | `{}` | Affinity for Distributor pods | | `components.distributor.priorityClassName` | `""` | Priority class name for Distributor pods | | `components.eventBus.imageName` | `selenium/event-bus` | Event Bus image name | | `components.eventBus.imageTag` | `nil` | Event Bus image tag (this overwrites `.global.seleniumGrid.imageTag` value) | @@ -254,9 +296,7 @@ pods | `components.eventBus.serviceAnnotations` | `{}` | Custom annotations for Event Bus service | | `components.eventBus.tolerations` | `[]` | Tolerations for Event Bus pods | | `components.eventBus.nodeSelector` | `{}` | Node Selector for Event Bus pods | -| `components.eventBus.affinity` | `{}` | Affinity for -Event Bus -pods | +| `components.eventBus.affinity` | `{}` | Affinity for Event Bus pods | | `components.eventBus.priorityClassName` | `""` | Priority class name for Event Bus pods | | `components.sessionMap.imageName` | `selenium/sessions` | Session Map image name | | `components.sessionMap.imageTag` | `nil` | Session Map image tag (this overwrites `.global.seleniumGrid.imageTag` value) | @@ -269,9 +309,7 @@ pods | `components.sessionMap.serviceAnnotations` | `{}` | Custom annotations for Session Map service | | `components.sessionMap.tolerations` | `[]` | Tolerations for Session Map pods | | `components.sessionMap.nodeSelector` | `{}` | Node Selector for Session Map pods | -| `components.sessionMap.affinity` | `{}` | Affinity for -Session Map -pods | +| `components.sessionMap.affinity` | `{}` | Affinity for Session Map pods | | `components.sessionMap.priorityClassName` | `""` | Priority class name for Session Map pods | | `components.sessionQueue.imageName` | `selenium/session-queue` | Session Queue image name | | `components.sessionQueue.imageTag` | `nil` | Session Queue image tag (this overwrites `.global.seleniumGrid.imageTag` value) | @@ -285,9 +323,7 @@ pods | `components.sessionQueue.serviceAnnotations` | `{}` | Custom annotations for Session Queue service | | `components.sessionQueue.tolerations` | `[]` | Tolerations for Session Queue pods | | `components.sessionQueue.nodeSelector` | `{}` | Node Selector for Session Queue pods | -| `components.sessionQueue.affinity` | `{}` | Affinity for -Session Queue -pods | +| `components.sessionQueue.affinity` | `{}` | Affinity for Session Queue pods | | `components.sessionQueue.priorityClassName` | `""` | Priority class name for Session Queue pods | | `components.subPath` | `/` | Custom sub path for all components | | `components.extraEnvironmentVariables` | `nil` | Custom environment variables for all components | diff --git a/charts/selenium-grid/templates/_helpers.tpl b/charts/selenium-grid/templates/_helpers.tpl index 5aa20aaf0..25d0577fb 100644 --- a/charts/selenium-grid/templates/_helpers.tpl +++ b/charts/selenium-grid/templates/_helpers.tpl @@ -79,3 +79,130 @@ Ingress fullname {{- define "seleniumGrid.ingress.fullname" -}} {{- default "selenium-ingress" .Values.ingress.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} + +{{/* +Is autoscaling using KEDA enabled +*/}} +{{- define "seleniumGrid.useKEDA" -}} +{{- or .Values.autoscaling.enabled .Values.autoscaling.enableWithExistingKEDA | ternary "true" "" -}} +{{- end -}} + + +{{/* +Common pod template +*/}} +{{- define "seleniumGrid.podTemplate" -}} +template: + metadata: + labels: + app: {{.name}} + app.kubernetes.io/name: {{.name}} + {{- include "seleniumGrid.commonLabels" . | nindent 6 }} + {{- with .node.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 6 }} + {{- end }} + annotations: + checksum/event-bus-configmap: {{ include (print $.Template.BasePath "/event-bus-configmap.yaml") . | sha256sum }} + {{- with .node.annotations }} + {{ toYaml . | nindent 6 }} + {{- end }} + spec: + restartPolicy: {{ and (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "job") | ternary "Never" "Always" }} + {{- with .node.hostAliases }} + hostAliases: {{ toYaml . | nindent 6 }} + {{- end }} + containers: + - name: {{.name}} + {{- $imageTag := default .Values.global.seleniumGrid.nodesImageTag .node.imageTag }} + image: {{ printf "%s:%s" .node.imageName $imageTag }} + imagePullPolicy: {{ .node.imagePullPolicy }} + {{- with .node.extraEnvironmentVariables }} + env: {{- tpl (toYaml .) $ | nindent 10 }} + {{- end }} + envFrom: + - configMapRef: + name: {{ .Values.busConfigMap.name }} + - configMapRef: + name: {{ .Values.nodeConfigMap.name }} + {{- with .node.extraEnvFrom }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if gt (len .node.ports) 0 }} + ports: + {{- range .node.ports }} + - containerPort: {{ . }} + protocol: TCP + {{- end }} + {{- end }} + volumeMounts: + - name: dshm + mountPath: /dev/shm + {{- if .node.extraVolumeMounts }} + {{- toYaml .node.extraVolumeMounts | nindent 10 }} + {{- end }} + {{- with .node.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + {{- include "seleniumGrid.lifecycle" . | nindent 8 -}} + {{- with .node.startupProbe }} + startupProbe: {{- toYaml . | nindent 10 }} + {{- end }} + {{- if or .Values.global.seleniumGrid.imagePullSecret .node.imagePullSecret }} + imagePullSecrets: + - name: {{ default .Values.global.seleniumGrid.imagePullSecret .node.imagePullSecret }} + {{- end }} + {{- with .node.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .node.tolerations }} + tolerations: + {{ toYaml . | nindent 4 }} + {{- end }} + {{- with .node.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + terminationGracePeriodSeconds: {{ .node.terminationGracePeriodSeconds }} + volumes: + - name: dshm + emptyDir: + medium: Memory + sizeLimit: {{ default "1Gi" .node.dshmVolumeSizeLimit }} + {{- if .node.extraVolumes }} + {{ toYaml .node.extraVolumes | nindent 6 }} + {{- end }} +{{- end -}} + +{{/* +Get the url of the grid. If the external url can be figured out from the ingress use that, otherwise the cluster internal url +*/}} +{{- define "seleniumGrid.url" -}} +{{- if and .Values.ingress.enabled .Values.ingress.hostname (ne .Values.ingress.hostname "selenium-grid.local") -}} +http{{if .Values.ingress.tls}}s{{end}}://{{.Values.ingress.hostname}} +{{- else -}} +http://{{ include ($.Values.isolateComponents | ternary "seleniumGrid.router.fullname" "seleniumGrid.hub.fullname") $ }}.{{ .Release.Namespace }}:{{ $.Values.components.router.port }} +{{- end }} +{{- end -}} + +{{/* +Graphql Url of the hub or the router +*/}} +{{- define "seleniumGrid.graphqlURL" -}} +http://{{ include ($.Values.isolateComponents | ternary "seleniumGrid.router.fullname" "seleniumGrid.hub.fullname") $ }}.{{ .Release.Namespace }}:{{ $.Values.components.router.port }}/graphql +{{- end -}} + +{{/* +Get the lifecycle of the pod. When KEDA is activated and the lifecycle is used for a pod of a +deployment preStop hook to deregister from the selenium hub. +*/}} +{{- define "seleniumGrid.lifecycle" }} +{{ $lifecycle := tpl (toYaml (default (dict) .node.lifecycle)) $ }} +{{- if and (eq .Values.autoscaling.scalingType "deployment") (eq (include "seleniumGrid.useKEDA" .) "true") -}} +{{ $lifecycle = merge ($lifecycle | fromYaml ) .Values.autoscaling.deregisterLifecycle | toYaml }} +{{- end -}} +{{ if and $lifecycle (ne $lifecycle "{}") -}} +lifecycle: {{ $lifecycle | nindent 2 }} +{{- end -}} +{{- end -}} diff --git a/charts/selenium-grid/templates/chrome-node-deployment.yaml b/charts/selenium-grid/templates/chrome-node-deployment.yaml index 1fa349332..e519b11ce 100644 --- a/charts/selenium-grid/templates/chrome-node-deployment.yaml +++ b/charts/selenium-grid/templates/chrome-node-deployment.yaml @@ -1,10 +1,10 @@ -{{- if and .Values.chromeNode.enabled .Values.chromeNode.deploymentEnabled }} +{{- if and .Values.chromeNode.enabled ((eq (include "seleniumGrid.useKEDA" .) "true") | ternary (eq .Values.autoscaling.scalingType "deployment") .Values.chromeNode.deploymentEnabled) }} apiVersion: apps/v1 kind: Deployment metadata: name: {{ template "seleniumGrid.chromeNode.fullname" . }} namespace: {{ .Release.Namespace }} - labels: &chrome_node_labels + labels: app: selenium-chrome-node app.kubernetes.io/name: selenium-chrome-node {{- include "seleniumGrid.commonLabels" . | nindent 4 }} @@ -20,81 +20,8 @@ spec: matchLabels: app: selenium-chrome-node app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: *chrome_node_labels - annotations: - checksum/event-bus-configmap: {{ include (print $.Template.BasePath "/event-bus-configmap.yaml") . | sha256sum }} - {{- with .Values.chromeNode.annotations }} - {{ toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.chromeNode.hostAliases }} - hostAliases: {{ toYaml . | nindent 8 }} - {{- end }} - containers: - - name: selenium-chrome-node - {{- $imageTag := default .Values.global.seleniumGrid.nodesImageTag .Values.chromeNode.imageTag }} - image: {{ printf "%s:%s" .Values.chromeNode.imageName $imageTag }} - imagePullPolicy: {{ .Values.chromeNode.imagePullPolicy }} - {{- with .Values.chromeNode.extraEnvironmentVariables }} - env: {{- tpl (toYaml .) $ | nindent 12 }} - {{- end }} - envFrom: - - configMapRef: - name: {{ .Values.busConfigMap.name }} - {{- with .Values.chromeNode.extraEnvFrom }} - {{- toYaml . | nindent 12 }} - {{- end }} - {{- if gt (len .Values.chromeNode.ports) 0 }} - ports: - {{- range .Values.chromeNode.ports }} - - containerPort: {{ . }} - protocol: TCP - {{- end }} - {{- end }} - volumeMounts: - - name: dshm - mountPath: /dev/shm - {{- if .Values.chromeNode.extraVolumeMounts }} - {{- toYaml .Values.chromeNode.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- with .Values.chromeNode.resources }} - resources: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.chromeNode.securityContext }} - securityContext: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.chromeNode.lifecycle }} - lifecycle: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.chromeNode.startupProbe }} - startupProbe: {{- toYaml . | nindent 12 }} - {{- end }} - {{- if or .Values.global.seleniumGrid.imagePullSecret .Values.chromeNode.imagePullSecret }} - imagePullSecrets: - - name: {{ default .Values.global.seleniumGrid.imagePullSecret .Values.chromeNode.imagePullSecret }} - {{- end }} - {{- with .Values.chromeNode.nodeSelector }} - nodeSelector: {{- toYaml . | nindent 8 }} - {{- end }} - {{- if or .Values.global.seleniumGrid.affinity .Values.chromeNode.affinity }} - {{- $affinityYaml := default .Values.global.seleniumGrid.affinity .Values.chromeNode.affinity }} - affinity: {{- toYaml $affinityYaml | nindent 8 }} - {{- end }} - {{- with .Values.chromeNode.tolerations }} - tolerations: {{ toYaml . | nindent 6 }} - {{- end }} - {{- with .Values.chromeNode.priorityClassName }} - priorityClassName: {{ . }} - {{- end }} - terminationGracePeriodSeconds: {{ .Values.chromeNode.terminationGracePeriodSeconds }} - volumes: - - name: dshm - emptyDir: - medium: Memory - sizeLimit: {{ default "1Gi" .Values.chromeNode.dshmVolumeSizeLimit }} - {{- if .Values.chromeNode.extraVolumes }} - {{ toYaml .Values.chromeNode.extraVolumes | nindent 8 }} - {{- end }} +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" "selenium-chrome-node" -}} +{{- $_ = set $podScope "node" .Values.chromeNode -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/chrome-node-hpa.yaml b/charts/selenium-grid/templates/chrome-node-hpa.yaml new file mode 100644 index 000000000..5e93781ee --- /dev/null +++ b/charts/selenium-grid/templates/chrome-node-hpa.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.chromeNode.enabled (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "deployment") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: selenium-grid-chrome-scaledobject + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + deploymentName: {{ template "seleniumGrid.chromeNode.fullname" . }} +spec: + maxReplicaCount: {{ .Values.chromeNode.maxReplicaCount }} + scaleTargetRef: + name: {{ template "seleniumGrid.chromeNode.fullname" . }} + triggers: + - type: selenium-grid + {{- with .Values.chromeNode.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml b/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml new file mode 100644 index 000000000..4971aec74 --- /dev/null +++ b/charts/selenium-grid/templates/chrome-node-scaledjobs.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.chromeNode.enabled (include "seleniumGrid.useKEDA" .) (eq .Values.autoscaling.scalingType "job") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledJob +metadata: + name: {{ template "seleniumGrid.chromeNode.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app: selenium-chrome-node + app.kubernetes.io/name: selenium-chrome-node + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.chromeNode.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + maxReplicaCount: {{ .Values.chromeNode.maxReplicaCount }} + {{- with .Values.autoscaling.scaledJobOptions -}} + {{ toYaml . | nindent 2 }} + {{- end }} + triggers: + - type: selenium-grid + {{- with .Values.chromeNode.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + jobTargetRef: + parallelism: 1 + completions: 1 + backoffLimit: 0 +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" "selenium-chrome-node" -}} +{{- $_ = set $podScope "node" .Values.chromeNode -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 4 }} +{{- end }} diff --git a/charts/selenium-grid/templates/edge-node-deployment.yaml b/charts/selenium-grid/templates/edge-node-deployment.yaml index 78774c99c..55ef07783 100644 --- a/charts/selenium-grid/templates/edge-node-deployment.yaml +++ b/charts/selenium-grid/templates/edge-node-deployment.yaml @@ -1,10 +1,10 @@ -{{- if and .Values.edgeNode.enabled .Values.edgeNode.deploymentEnabled }} +{{- if and .Values.edgeNode.enabled ((eq (include "seleniumGrid.useKEDA" .) "true") | ternary (eq .Values.autoscaling.scalingType "deployment") .Values.edgeNode.deploymentEnabled) }} apiVersion: apps/v1 kind: Deployment metadata: name: {{ template "seleniumGrid.edgeNode.fullname" . }} namespace: {{ .Release.Namespace }} - labels: &edge_node_labels + labels: app: selenium-edge-node app.kubernetes.io/name: selenium-edge-node {{- include "seleniumGrid.commonLabels" . | nindent 4 }} @@ -20,81 +20,8 @@ spec: matchLabels: app: selenium-edge-node app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: *edge_node_labels - annotations: - checksum/event-bus-configmap: {{ include (print $.Template.BasePath "/event-bus-configmap.yaml") . | sha256sum }} - {{- with .Values.edgeNode.annotations }} - {{ toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.edgeNode.hostAliases }} - hostAliases: {{ toYaml . | nindent 8 }} - {{- end }} - containers: - - name: selenium-edge-node - {{- $imageTag := default .Values.global.seleniumGrid.nodesImageTag .Values.edgeNode.imageTag }} - image: {{ printf "%s:%s" .Values.edgeNode.imageName $imageTag }} - imagePullPolicy: {{ .Values.edgeNode.imagePullPolicy }} - {{- with .Values.edgeNode.extraEnvironmentVariables }} - env: {{- tpl (toYaml .) $ | nindent 12 }} - {{- end }} - envFrom: - - configMapRef: - name: {{ .Values.busConfigMap.name }} - {{- with .Values.edgeNode.extraEnvFrom }} - {{- toYaml . | nindent 12 }} - {{- end }} - {{- if gt (len .Values.edgeNode.ports) 0 }} - ports: - {{- range .Values.edgeNode.ports }} - - containerPort: {{ . }} - protocol: TCP - {{- end }} - {{- end }} - volumeMounts: - - name: dshm - mountPath: /dev/shm - {{- if .Values.edgeNode.extraVolumeMounts }} - {{- toYaml .Values.edgeNode.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- with .Values.edgeNode.resources }} - resources: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.edgeNode.securityContext }} - securityContext: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.edgeNode.lifecycle }} - lifecycle: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.edgeNode.startupProbe }} - startupProbe: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.edgeNode.nodeSelector }} - nodeSelector: {{- toYaml . | nindent 8 }} - {{- end }} - {{- if or .Values.global.seleniumGrid.affinity .Values.edgeNode.affinity }} - {{- $affinityYaml := default .Values.global.seleniumGrid.affinity .Values.edgeNode.affinity }} - affinity: {{- toYaml $affinityYaml | nindent 8 }} - {{- end }} - {{- with .Values.edgeNode.tolerations }} - tolerations: {{ toYaml . | nindent 6 }} - {{- end }} - {{- if or .Values.global.seleniumGrid.imagePullSecret .Values.edgeNode.imagePullSecret }} - imagePullSecrets: - - name: {{ default .Values.global.seleniumGrid.imagePullSecret .Values.edgeNode.imagePullSecret }} - {{- end }} - {{- with .Values.edgeNode.priorityClassName }} - priorityClassName: {{ . }} - {{- end }} - terminationGracePeriodSeconds: {{ .Values.edgeNode.terminationGracePeriodSeconds }} - volumes: - - name: dshm - emptyDir: - medium: Memory - sizeLimit: {{ default "1Gi" .Values.edgeNode.dshmVolumeSizeLimit }} - {{- if .Values.edgeNode.extraVolumes }} - {{ toYaml .Values.edgeNode.extraVolumes | nindent 8 }} - {{- end }} +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" "selenium-edge-node" -}} +{{- $_ = set $podScope "node" .Values.edgeNode -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/edge-node-hpa.yaml b/charts/selenium-grid/templates/edge-node-hpa.yaml new file mode 100644 index 000000000..6e7d94001 --- /dev/null +++ b/charts/selenium-grid/templates/edge-node-hpa.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.edgeNode.enabled (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "deployment") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: selenium-grid-edge-scaledobject + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + deploymentName: {{ template "seleniumGrid.edgeNode.fullname" . }} +spec: + maxReplicaCount: {{ .Values.edgeNode.maxReplicaCount }} + scaleTargetRef: + name: {{ template "seleniumGrid.edgeNode.fullname" . }} + triggers: + - type: selenium-grid + {{- with .Values.edgeNode.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/selenium-grid/templates/edge-node-scaledjob.yaml b/charts/selenium-grid/templates/edge-node-scaledjob.yaml new file mode 100644 index 000000000..6d113d186 --- /dev/null +++ b/charts/selenium-grid/templates/edge-node-scaledjob.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.edgeNode.enabled (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "job") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledJob +metadata: + name: {{ template "seleniumGrid.edgeNode.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app: selenium-edge-node + app.kubernetes.io/name: selenium-edge-node + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.edgeNode.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + maxReplicaCount: {{ .Values.edgeNode.maxReplicaCount }} + {{- with .Values.autoscaling.scaledJobOptions -}} + {{ toYaml . | nindent 2 }} + {{- end }} + triggers: + - type: selenium-grid + {{- with .Values.edgeNode.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + jobTargetRef: + parallelism: 1 + completions: 1 + backoffLimit: 0 +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" "selenium-edge-node" -}} +{{- $_ = set $podScope "node" .Values.edgeNode -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 4 }} +{{- end }} diff --git a/charts/selenium-grid/templates/firefox-node-deployment.yaml b/charts/selenium-grid/templates/firefox-node-deployment.yaml index 03e57a990..9b3433ca5 100644 --- a/charts/selenium-grid/templates/firefox-node-deployment.yaml +++ b/charts/selenium-grid/templates/firefox-node-deployment.yaml @@ -1,10 +1,10 @@ -{{- if and .Values.firefoxNode.enabled .Values.firefoxNode.deploymentEnabled }} +{{- if and .Values.firefoxNode.enabled ((eq (include "seleniumGrid.useKEDA" .) "true") | ternary (eq .Values.autoscaling.scalingType "deployment") .Values.firefoxNode.deploymentEnabled) }} apiVersion: apps/v1 kind: Deployment metadata: name: {{ template "seleniumGrid.firefoxNode.fullname" . }} namespace: {{ .Release.Namespace }} - labels: &firefox_node_labels + labels: app: selenium-firefox-node app.kubernetes.io/name: selenium-firefox-node {{- include "seleniumGrid.commonLabels" . | nindent 4 }} @@ -20,81 +20,8 @@ spec: matchLabels: app: selenium-firefox-node app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: *firefox_node_labels - annotations: - checksum/event-bus-configmap: {{ include (print $.Template.BasePath "/event-bus-configmap.yaml") . | sha256sum }} - {{- with .Values.firefoxNode.annotations }} - {{ toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.firefoxNode.hostAliases }} - hostAliases: {{ toYaml . | nindent 8 }} - {{- end }} - containers: - - name: selenium-firefox-node - {{- $imageTag := default .Values.global.seleniumGrid.nodesImageTag .Values.firefoxNode.imageTag }} - image: {{ printf "%s:%s" .Values.firefoxNode.imageName $imageTag }} - imagePullPolicy: {{ .Values.firefoxNode.imagePullPolicy }} - {{- with .Values.firefoxNode.extraEnvironmentVariables }} - env: {{- tpl (toYaml .) $ | nindent 12 }} - {{- end }} - envFrom: - - configMapRef: - name: {{ .Values.busConfigMap.name }} - {{- with .Values.firefoxNode.extraEnvFrom }} - {{- toYaml . | nindent 12 }} - {{- end }} - {{- if gt (len .Values.firefoxNode.ports) 0 }} - ports: - {{- range .Values.firefoxNode.ports }} - - containerPort: {{ . }} - protocol: TCP - {{- end }} - {{- end }} - volumeMounts: - - name: dshm - mountPath: /dev/shm - {{- if .Values.firefoxNode.extraVolumeMounts }} - {{- toYaml .Values.firefoxNode.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- with .Values.firefoxNode.resources }} - resources: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.firefoxNode.securityContext }} - securityContext: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.firefoxNode.lifecycle }} - lifecycle: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.firefoxNode.startupProbe }} - startupProbe: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.firefoxNode.nodeSelector }} - nodeSelector: {{- toYaml . | nindent 8 }} - {{- end }} - {{- if or .Values.global.seleniumGrid.affinity .Values.firefoxNode.affinity }} - {{- $affinityYaml := default .Values.global.seleniumGrid.affinity .Values.firefoxNode.affinity }} - affinity: {{- toYaml $affinityYaml | nindent 8 }} - {{- end }} - {{- with .Values.firefoxNode.tolerations }} - tolerations: {{ toYaml . | nindent 6 }} - {{- end }} - {{- if or .Values.global.seleniumGrid.imagePullSecret .Values.firefoxNode.imagePullSecret }} - imagePullSecrets: - - name: {{ default .Values.global.seleniumGrid.imagePullSecret .Values.firefoxNode.imagePullSecret }} - {{- end }} - {{- with .Values.firefoxNode.priorityClassName }} - priorityClassName: {{ . }} - {{- end }} - terminationGracePeriodSeconds: {{ .Values.firefoxNode.terminationGracePeriodSeconds }} - volumes: - - name: dshm - emptyDir: - medium: Memory - sizeLimit: {{ default "1Gi" .Values.firefoxNode.dshmVolumeSizeLimit }} - {{- if .Values.firefoxNode.extraVolumes }} - {{ toYaml .Values.firefoxNode.extraVolumes | nindent 8 }} - {{- end }} +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" "selenium-firefox-node" -}} +{{- $_ = set $podScope "node" .Values.firefoxNode -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 2 }} {{- end }} diff --git a/charts/selenium-grid/templates/firefox-node-hpa.yaml b/charts/selenium-grid/templates/firefox-node-hpa.yaml new file mode 100644 index 000000000..3f3001f66 --- /dev/null +++ b/charts/selenium-grid/templates/firefox-node-hpa.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.firefoxNode.enabled (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "deployment") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: selenium-grid-firefox-scaledobject + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + deploymentName: {{ template "seleniumGrid.firefoxNode.fullname" . }} +spec: + maxReplicaCount: {{ .Values.firefoxNode.maxReplicaCount }} + scaleTargetRef: + name: {{ template "seleniumGrid.firefoxNode.fullname" . }} + triggers: + - type: selenium-grid + {{- with .Values.firefoxNode.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/selenium-grid/templates/firefox-node-scaledjob.yaml b/charts/selenium-grid/templates/firefox-node-scaledjob.yaml new file mode 100644 index 000000000..de90024c7 --- /dev/null +++ b/charts/selenium-grid/templates/firefox-node-scaledjob.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.firefoxNode.enabled (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "job") }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledJob +metadata: + name: {{ template "seleniumGrid.firefoxNode.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + {{- with .Values.autoscaling.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + app: selenium-firefox-node + app.kubernetes.io/name: selenium-firefox-node + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.firefoxNode.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + maxReplicaCount: {{ .Values.firefoxNode.maxReplicaCount }} + {{- with .Values.autoscaling.scaledJobOptions -}} + {{ toYaml . | nindent 2 }} + {{- end }} + triggers: + - type: selenium-grid + {{- with .Values.firefoxNode.hpa }} + metadata: {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + jobTargetRef: + parallelism: 1 + completions: 1 + backoffLimit: 0 +{{- $podScope := deepCopy . -}} +{{- $_ := set $podScope "name" "selenium-firefox-node" -}} +{{- $_ = set $podScope "node" .Values.firefoxNode -}} +{{- include "seleniumGrid.podTemplate" $podScope | nindent 4 }} +{{- end }} diff --git a/charts/selenium-grid/templates/node-configmap.yaml b/charts/selenium-grid/templates/node-configmap.yaml new file mode 100644 index 000000000..08d6d4981 --- /dev/null +++ b/charts/selenium-grid/templates/node-configmap.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.nodeConfigMap.name }} + namespace: {{ .Release.Namespace }} +{{- with .Values.nodeConfigMap.annotations }} + annotations: {{- toYaml . | nindent 4 }} +{{- end }} + labels: + {{- include "seleniumGrid.commonLabels" . | nindent 4 }} + {{- with .Values.customLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +data: + DRAIN_AFTER_SESSION_COUNT: '{{- and (eq (include "seleniumGrid.useKEDA" .) "true") (eq .Values.autoscaling.scalingType "job") | ternary "1" "0" -}}' + SE_NODE_GRID_URL: '{{ include "seleniumGrid.url" .}}' diff --git a/charts/selenium-grid/values.yaml b/charts/selenium-grid/values.yaml index ec6c7f8d2..d51c3d258 100644 --- a/charts/selenium-grid/values.yaml +++ b/charts/selenium-grid/values.yaml @@ -24,6 +24,7 @@ ingress: path: / # TLS backend configuration for ingress resource tls: [] + path: / # ConfigMap that contains SE_EVENT_BUS_HOST, SE_EVENT_BUS_PUBLISH_PORT and SE_EVENT_BUS_SUBSCRIBE_PORT variables busConfigMap: @@ -32,6 +33,12 @@ busConfigMap: # Custom annotations for configmap annotations: {} +# ConfigMap that contains common environment variables for browser nodes +nodeConfigMap: + name: selenium-node-config + # Custom annotations for configmap + annotations: {} + # Configuration for isolated components (applied only if `isolateComponents: true`) components: @@ -305,11 +312,38 @@ hub: # Priority class name for selenium-hub pods priorityClassName: "" +# Keda scaled object configuration +autoscaling: + # Enable autoscaling. Implies installing KEDA + enabled: false + # Enable autoscaling without automatically installing KEDA + enableWithExistingKEDA: false + # Which typ of KEDA scaling to use: job or deployment + scalingType: job + # Annotations for KEDA resources: ScaledObject and ScaledJob + annotations: + helm.sh/hook: post-install,post-upgrade + # Options for KEDA ScaledJobs + scaledJobOptions: + pollingInterval: 10 + scalingStrategy: + strategy: accurate + deregisterLifecycle: + preStop: + exec: + command: + - bash + - -c + - | + curl -X POST 127.0.0.1:5555/se/grid/node/drain --header 'X-REGISTRATION-SECRET;' && \ + while curl 127.0.0.1:5555/status; do sleep 1; done; + # Configuration for chrome nodes chromeNode: # Enable chrome nodes enabled: true + # NOTE: Only used when autoscaling.enabled is false # Enable creation of Deployment # true (default) - if you want long living pods # false - for provisioning your own custom type such as Jobs @@ -397,17 +431,7 @@ chromeNode: # periodSeconds: 5 # Time to wait for pod termination terminationGracePeriodSeconds: 30 - # Allow pod correctly shutdown lifecycle: {} - # preStop: - # exec: - # command: - # - bash - # - -c - # - | - # curl -X POST 127.0.0.1:5555/se/grid/node/drain --header 'X-REGISTRATION-SECRET;' && \ - # while curl 127.0.0.1:5555/status; do sleep 1; done - extraVolumeMounts: [] # - name: my-extra-volume # mountPath: /home/seluser/Downloads @@ -419,11 +443,19 @@ chromeNode: # persistentVolumeClaim: # claimName: my-pv-claim + maxReplicaCount: 8 + hpa: + url: '{{ include "seleniumGrid.graphqlURL" . }}' + browserName: chrome + # browserVersion: '91.0' # Optional. Only required when supporting multiple versions of browser in your Selenium Grid. + unsafeSsl : 'true' # Optional + # Configuration for firefox nodes firefoxNode: # Enable firefox nodes enabled: true + # NOTE: Only used when autoscaling.enabled is false # Enable creation of Deployment # true (default) - if you want long living pods # false - for provisioning your own custom type such as Jobs @@ -511,17 +543,7 @@ firefoxNode: # periodSeconds: 5 # Time to wait for pod termination terminationGracePeriodSeconds: 30 - # Allow pod correctly shutdown lifecycle: {} - # preStop: - # exec: - # command: - # - bash - # - -c - # - | - # curl -X POST 127.0.0.1:5555/se/grid/node/drain --header 'X-REGISTRATION-SECRET;' && \ - # while curl 127.0.0.1:5555/status; do sleep 1; done - extraVolumeMounts: [] # - name: my-extra-volume # mountPath: /home/seluser/Downloads @@ -532,12 +554,17 @@ firefoxNode: # - name: my-extra-volume-from-pvc # persistentVolumeClaim: # claimName: my-pv-claim + maxReplicaCount: 8 + hpa: + url: '{{ include "seleniumGrid.graphqlURL" . }}' + browserName: firefox # Configuration for edge nodes edgeNode: # Enable edge nodes enabled: true + # NOTE: Only used when autoscaling.enabled is false # Enable creation of Deployment # true (default) - if you want long living pods # false - for provisioning your own custom type such as Jobs @@ -625,17 +652,7 @@ edgeNode: # periodSeconds: 5 # Time to wait for pod termination terminationGracePeriodSeconds: 30 - # Allow pod correctly shutdown lifecycle: {} - # preStop: - # exec: - # command: - # - bash - # - -c - # - | - # curl -X POST 127.0.0.1:5555/se/grid/node/drain --header 'X-REGISTRATION-SECRET;' && \ - # while curl 127.0.0.1:5555/status; do sleep 1; done - extraVolumeMounts: [] # - name: my-extra-volume # mountPath: /home/seluser/Downloads @@ -646,6 +663,11 @@ edgeNode: # - name: my-extra-volume-from-pvc # persistentVolumeClaim: # claimName: my-pv-claim + maxReplicaCount: 8 + hpa: + url: '{{ include "seleniumGrid.graphqlURL" . }}' + browserName: MicrosoftEdge + sessionBrowserName: 'msedge' # Custom labels for k8s resources customLabels: {}