diff --git a/README.md b/README.md index 23601cd..b9355be 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The `lightrun` Helm chart provides a reliable and efficient way to deploy and ma | [**Router**](docs/components/router/index.md) | Directs traffic into the cluster. | | [**Frontend**](docs/components/frontend.md) | Serves the user interface. | | [**Backend**](docs/components/backend.md) | Acts as the control plane for Lightrun. | +| [**Crons**](docs/components/crons.md) | Handles background processing tasks such as scheduled jobs and batch operations. | | [**Artifacts**](docs/components/artifacts.md) | Serves Lightrun artifacts. | | [**Keycloak**](docs/components/keycloak.md) | Manages authentication. | | [**Redis**](docs/components/redis.md) | Handles caching and pub/sub channels (can be local or external). | @@ -97,7 +98,7 @@ To ensure a smooth and secure production deployment of Lightrun, follow these ke #### **13. Choose Your Deployment Option** -Decide where to run your Kubernetes cluster based on your infrastructure needs. Consider whether you’ll deploy all services locally (including RabbitMQ, Redis, and MySQL) or use managed cloud services. +Decide where to run your Kubernetes cluster based on your infrastructure needs. Consider whether you'll deploy all services locally (including RabbitMQ, Redis, and MySQL) or use managed cloud services. ##### **Supported Kubernetes Platforms** diff --git a/chart/templates/backend-deployment.yaml b/chart/templates/backend-deployment.yaml index a01e12c..6ab4bdb 100644 --- a/chart/templates/backend-deployment.yaml +++ b/chart/templates/backend-deployment.yaml @@ -60,49 +60,14 @@ spec: {{- end }} serviceAccountName: {{ template "lightrun-be.serviceAccountName" . }} volumes: - {{- if and .Values.general.system_config.content .Values.general.system_config.signature }} - - name: system-config - configMap: - name: {{ include "lightrun.fullname" . }}-system-config - {{- end }} - - name: encryption-keys - secret: - secretName: {{ include "secrets.backend.name" . }} - optional: true - items: - # Only select items that start with encryption-key- - {{- include "encryption.key.items" . | nindent 18 }} - {{- include "lightrun-backend.volumes.asyncProfiler" . | nindent 8 }} - {{- if and .Values.general.internal_tls.enabled .Values.general.internal_tls.certificates.existing_ca_secret_name }} - - name: ca-cert - secret: - secretName: {{ .Values.general.internal_tls.certificates.existing_ca_secret_name }} - {{- end }} - - name: jcache-config - configMap: - name: {{ include "lightrun-be.name" . }}-jcache-config - items: - - key: "redisson-jcache-single.yaml" - path: "redisson-jcache-single.yaml" - - key: "redisson-jcache-replicated.yaml" - path: "redisson-jcache-replicated.yaml" - - name: certificates - secret: - secretName: {{ include "secrets.certificate.name" . }} - - name: p12 - emptyDir: - sizeLimit: 50Mi + {{- include "lightrun-backend-crons.volumes" . | nindent 8 }} + {{- include "lightrun-backend.volumes.asyncProfiler" . | nindent 8 }} - name: dumps emptyDir: sizeLimit: {{ .Values.deployments.backend.dumpsEmptyDirSizeLimit }} {{- with .Values.deployments.backend.extraVolumes }} {{ toYaml . | nindent 8 }} {{- end }} - {{- if .Values.general.readOnlyRootFilesystem }} - - name: tmpfs - emptyDir: - sizeLimit: {{ .Values.general.readOnlyRootFilesystem_tmpfs_sizeLimit }} - {{- end }} {{- if .Values.secrets.defaults.dockerhub_config }} imagePullSecrets: {{- if .Values.secrets.defaults.dockerhub_config.existingSecret }} @@ -135,31 +100,11 @@ spec: "/usr/src/lightrun/{{ .Values.deployments.backend.jar_name }}" ] volumeMounts: - {{- if and .Values.general.system_config.content .Values.general.system_config.signature }} - - name: system-config - mountPath: "/opt/lightrun/system_config.json" - subPath: "system_config.json" - readOnly: true - {{- end }} - - name: encryption-keys - mountPath: /encryption-keys - readOnly: true + {{- include "lightrun-backend-crons.volumeMounts" . | nindent 12 }} {{- include "lightrun-backend.volumeMounts.asyncProfiler" . | nindent 12 }} - - name: jcache-config - mountPath: "/jcache-config" - - name: certificates - mountPath: /usr/src/lightrun/helm/tls - - name: p12 - mountPath: /p12 - - name: dumps - mountPath: /dumps {{- with .Values.deployments.backend.extraVolumeMounts }} {{ toYaml . | nindent 12 }} {{- end }} - {{- if .Values.general.readOnlyRootFilesystem }} - - name: tmpfs - mountPath: /tmp - {{- end }} startupProbe: timeoutSeconds: {{ .Values.deployments.backend.startupProbe.timeoutSeconds }} periodSeconds: {{ .Values.deployments.backend.startupProbe.periodSeconds }} @@ -204,138 +149,9 @@ spec: - secretRef: name: {{ include "secrets.backend.name" . }} env: - {{- if and .Values.general.system_config.content .Values.general.system_config.signature }} - - name: LIGHTRUN_SYSTEM_CONFIG_JSON_FILE_PATH - value: "/opt/lightrun/system_config.json" - - name: LIGHTRUN_SYSTEM_CONFIG_JSON_SIGNATURE - value: {{ .Values.general.system_config.signature }} - {{- end }} - - name: SERVER_SECURITY_ENCRYPTION-KEYS-PATH - value: file:/encryption-keys - - name: LIGHTRUN_HOSTNAME - value: {{ .Values.general.name }} - - name: POD_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - - name: INFO_DEPLOYMENT - {{ if eq .Values.general.deployment_type "saas" }} - value: "SaaS" - {{ else if eq .Values.general.deployment_type "single-tenant" }} - value: "single-tenant" - {{ else }} - value: "on-prem" - {{ end }} + {{- include "lightrun-backend-crons.environmentVariables" . | nindent 12 }} - name: SPRING_PROFILES_ACTIVE value: "prod,swagger,cluster" - - name: SPRING_DATASOURCE_URL - {{- if eq .Values.deployments.backend.dbConnector "mysql" }} - value: "jdbc:p6spy:mysql://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}" - {{- else if eq .Values.deployments.backend.dbConnector "mariadb" }} - value: "jdbc:mariadb://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}&rewriteBatchedStatements=true" - {{- end }} - - name: SPRING_CACHE_JCACHE_CONFIG - value: "file:///jcache-config/redisson-jcache-{{ .Values.deployments.redis.architecture }}.yaml" - - name: SPRING_FLYWAY_URL - {{- if eq .Values.deployments.backend.dbConnector "mysql" }} - value: "jdbc:mysql://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}" - {{- else if eq .Values.deployments.backend.dbConnector "mariadb" }} - value: "jdbc:mariadb://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}" - {{- end }} - - name: SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KEYCLOAK_ISSUER-URI - value: "https://{{ .Values.general.lightrun_endpoint }}/auth/realms/lightrun" - - name: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER-URI - value: "https://{{ .Values.general.lightrun_endpoint }}/auth/realms/lightrun" - - name: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK-SET-URI - value: "{{ include "http.scheme" . }}://{{ include "lightrun-keycloak.name" . }}:8080/auth/realms/lightrun/protocol/openid-connect/certs" - - name: SPRING_SECURITY_KEYCLOAK_URL - value: "{{ include "http.scheme" . }}://{{ include "lightrun-keycloak.name" . }}:8080/auth" - {{ if or .Values.general.internal_tls.enabled .Values.deployments.redis.encryption.enabled }} - - name: SPRING_REDIS_SSL - value: "true" - - name: REDIS_PROTOCOL_NAME - value: "rediss" - {{ end }} - {{ if .Values.general.internal_tls.enabled }} - - name: SERVER_SSL_ENABLED - value: "true" - {{ if .Values.general.mq.enabled }} - - name: SPRING_RABBITMQ_SSL_ENABLED - value: "true" - {{ end }} - - name: TLS_SKIPCLIENTCERTIFICATEVERIFICATION - {{ if .Values.general.internal_tls.certificates.verification }} - value: "false" - {{ else }} - value: "true" - {{ if .Values.general.mq.enabled }} - - name: SPRING_RABBITMQ_SSL_VALIDATE-SERVER-CERTIFICATE - value: "false" - {{ end }} - {{ end }} - {{ end }} - {{- if not .Values.deployments.redis.auth.enabled }} - - name: SPRING_REDIS_PASSWORD #to disable password auth even if secret contains password, but auth_enabled is false - value: null - {{- end }} - - name: SPRING_SECURITY_KEYCLOAK_PORT - value: "8080" - - name: SPRING_SECURITY_KEYCLOAK_EXTRA-REDIRECT-URLS - value: "https://{{ .Values.general.lightrun_endpoint }}/*" - - name: SPRING_SECURITY_KEYCLOAK_EXTERNAL-URL - value: "https://{{ .Values.general.lightrun_endpoint }}/auth" - - name: JHIPSTER_SLEEP - value: "5000" # gives time for other services to boot before the application - - name: INTEGRATIONS_ENABLE - value: "true" - - name: INTEGRATIONS_DATADOG_ENABLE - value: "false" - - name: SERVER_EXTERNAL_HOST - value: "{{ .Values.general.lightrun_endpoint }}" - - name: SERVER_EXTERNAL_PORT - value: "443" - - name: SPRING_REDIS_HOST - value: {{ include "lightrun-redis.endpoint" . }} - - name: SPRING_REDIS_PORT - value: "{{ .Values.deployments.redis.port }}" - - name: SERVER_DISPLAY_PORT - value: "443" - - name: SPRING_FLYWAY_SCHEMAS - value: {{ .Values.general.db_database }} - - name: KEYSTORE_PATH - value: "file:/p12/lightrun.p12" - - name: SYSTEM_DEFAULT_USER_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "secrets.backend.name" . }} - key: SPRING_SECURITY_KEYCLOAK_CLI_PASSWORD - - name: ARTIFACTS_ENABLE_S3_FEATURE #TODO: deprecated in favor of ARTIFACTS_ENABLE in 2.1.7 - value: {{ .Values.deployments.backend.artifacts.enable | quote }} - - name: ARTIFACTS_ENABLE - value: {{ .Values.deployments.backend.artifacts.enable | quote }} - - name: ARTIFACTS_S3_URL #TODO: deprecated in favor of ARTIFACTS_REPOSITORY_URL in 2.1.7 - value: "{{ .Values.deployments.backend.artifacts.s3_url }}" - - name: ARTIFACTS_REPOSITORY_URL - value: "{{ .Values.deployments.backend.artifacts.repository_url }}" - - name: ARTIFACTS_SUPPORTED_VERSIONS_URL - value: "{{ .Values.deployments.backend.artifacts.supported_versions_url }}" - - name: ARTIFACTS_DOWNLOAD_PRERELEASE #TODO: deprecated in favor of ARTIFACTS_VERSION_RESOLUTION_MODE in 2.1.7 - value: "{{ .Values.deployments.backend.artifacts.download_prerelease }}" - - name: ARTIFACTS_VERSION_RESOLUTION_MODE - value: "{{ .Values.deployments.backend.artifacts.resolution_mode }}" - - name: LIGHTRUN_ARTIFACTS_URL - value: "{{ include "http.scheme" . }}://{{ include "artifacts.name" . }}:8080" - {{- if .Values.general.mq.enabled }} - - name: SPRING_RABBITMQ_HOST - value: {{ include "lightrun-mq.endpoint" . }} - - name: SPRING_RABBITMQ_PORT - value: {{ .Values.general.mq.port | quote }} - - name: TRACKING_MIXPANEL_QUEUE_NAME - value: {{ include "lightrun-mq.getQueueNameByPrefix" (dict "prefix" "mixpanel-events" "Values" .Values) | quote }} - - name: KEYCLOAK_QUEUE_NAME - value: {{ include "lightrun-mq.getQueueNameByPrefix" (dict "prefix" "keycloak-events" "Values" .Values) | quote }} - {{- end }} {{- if .Values.deployments.backend.extraEnvs }} {{- toYaml .Values.deployments.backend.extraEnvs | nindent 12 }} {{- if not (include "list-of-maps-contains" (list .Values.deployments.backend.extraEnvs "_JAVA_OPTIONS") ) }} @@ -346,87 +162,12 @@ spec: - name: "_JAVA_OPTIONS" value: {{- toYaml (include "calculate-heap-size" .Values.deployments.backend) | nindent 21 }} {{- end }} - - name: LOGGING_USE-JSON-FORMAT - value: "{{ .Values.deployments.backend.useJsonLogFormat }}" - {{- if .Values.general.data_streamer.enabled }} - - name: INTEGRATIONS_SIEM_STREAMING-SERVICE_URL - value: "{{ include "http.scheme" . }}://{{ include "data_streamer.name" . }}:8080/events/post" - {{- end }} # waiting for mysql, rabbitmq and keycloak initialization initContainers: + {{- include "lightrun-backend-crons.initContainers" . | nindent 6 }} {{- include "lightrun-backend.initContainer.download-async-profiler" . | nindent 6 }} - - name: wait-for-keycloak - image: "{{ .Values.deployments.backend.initContainers.wait_for_keycloak.image.repository }}:{{ .Values.deployments.backend.initContainers.wait_for_keycloak.image.tag }}" - imagePullPolicy: {{ .Values.deployments.backend.initContainers.wait_for_keycloak.image.pullPolicy }} - securityContext: {{ include "lightrun-be.containerSecurityContext" . | indent 10 }} - command: - - sh - - /scripts/wait-for-200.sh - resources: - limits: - memory: "100Mi" - cpu: "100m" - requests: - memory: "100Mi" - cpu: "100m" - env: - - name: URL - value: {{ include "http.scheme" . }}://{{ include "lightrun-keycloak.name" . }}:9000/auth/health/started {{ if .Values.general.internal_tls.enabled }}--no-check-certificate{{ end }} - - {{ if .Values.general.mq.enabled }} - {{- include "lightrun-mq.initContainer.wait-for-rabbitmq" (merge (dict "imageConfig" .Values.deployments.backend.initContainers.wait_for_rabbitmq "securityContext" "lightrun-be.containerSecurityContext") .) | nindent 6 }} - {{- end }} - - name: p12-creator - image: "{{ .Values.deployments.backend.initContainers.p12_creator.image.repository }}:{{ .Values.deployments.backend.initContainers.p12_creator.image.tag }}" - imagePullPolicy: {{ .Values.deployments.backend.initContainers.p12_creator.image.pullPolicy }} - securityContext: {{ include "lightrun-be.containerSecurityContext" . | indent 10 }} - resources: - limits: - memory: "200Mi" - cpu: "200m" - requests: - memory: "200Mi" - cpu: "200m" - env: - - name: KEYSTORE_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "secrets.backend.name" . }} - key: KEYSTORE_PASSWORD - volumeMounts: - - name: certificates - mountPath: /tls - - name: p12 - mountPath: /p12 - command: ['sh', '-c', 'cp /tls/tls.crt /p12/crt.pem && cp /tls/tls.key /p12/key.pem && openssl pkcs12 -export -out /p12/lightrun.p12 -inkey /p12/key.pem -in /p12/crt.pem -passin pass:$KEYSTORE_PASSWORD -passout pass:$KEYSTORE_PASSWORD'] - - {{- if and .Values.general.internal_tls.enabled .Values.general.internal_tls.certificates.existing_ca_secret_name }} - - name: root-ca-creator - image: "{{ .Values.deployments.backend.image.repository }}:{{ .Values.deployments.backend.image.tag }}" - securityContext: {{ include "lightrun-be.containerSecurityContext" . | indent 10 }} - resources: - limits: - memory: "100Mi" - cpu: "100m" - requests: - memory: "100Mi" - cpu: "100m" - env: - - name: KEYSTORE_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "secrets.backend.name" . }} - key: KEYSTORE_PASSWORD - volumeMounts: - - name: ca-cert - mountPath: /tmp/ca-certificates/ - - name: p12 - mountPath: /p12 - command: ['sh', '-c', 'keytool -import -trustcacerts -alias internalCa -keystore /p12/internalca -file /tmp/ca-certificates/ca.crt -noprompt -storepass $KEYSTORE_PASSWORD && keytool -importkeystore -srckeystore /usr/lib/jvm/default-jvm/jre/lib/security/cacerts -destkeystore /p12/internalca -srcstorepass changeit -deststorepass $KEYSTORE_PASSWORD'] - {{- end }} - {{- if .Values.general.tolerations }} tolerations: {{ toYaml .Values.general.tolerations | nindent 8 }} diff --git a/chart/templates/crons/deployment.yaml b/chart/templates/crons/deployment.yaml new file mode 100644 index 0000000..d555286 --- /dev/null +++ b/chart/templates/crons/deployment.yaml @@ -0,0 +1,180 @@ +{{- if .Values.general.crons.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "lightrun-crons.name" . }} + labels: + app: {{ include "lightrun-crons.name" . }} +spec: + strategy: + type: Recreate + replicas: 1 + selector: + matchLabels: + app: {{ include "lightrun-crons.name" . }} + template: + metadata: +{{- if or .Values.deployments.crons.appMetrics.exposeToDatadog .Values.deployments.crons.podAnnotations }} + annotations: + {{- with .Values.deployments.crons.podAnnotations }} + {{ toYaml . | nindent 8 }} + {{- end }} +{{- if .Values.deployments.crons.appMetrics.exposeToDatadog }} +{{- $last := sub (len .Values.deployments.crons.appMetrics.includeMetrics ) 1 }} + ad.datadoghq.com/{{ include "lightrun-crons.name" . }}.checks: | + { + "openmetrics": { + "instances": [ + { + "openmetrics_endpoint": "http://%%host%%:%%port%%/management/prometheus", + "namespace": "lightrun", + "metrics": [ + {{- range $index ,$metricName:= .Values.deployments.crons.appMetrics.includeMetrics }} + {{- if ne $index $last }} + {"{{ $metricName }}": "crons.{{ $metricName }}"}, + {{- else }} + {"{{ $metricName }}": "crons.{{ $metricName }}"} + {{- end }} + {{- end }} + ] + } + ] + } + } +{{- end }} +{{- end }} + labels: + app: {{ include "lightrun-crons.name" . }} + {{- with .Values.deployments.crons.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.deployments.crons.topologySpreadConstraints }} + topologySpreadConstraints: + {{ toYaml .Values.deployments.crons.topologySpreadConstraints | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "lightrun-crons.serviceAccountName" . }} + volumes: + {{- include "lightrun-backend-crons.volumes" . | nindent 8 }} + {{- include "lightrun-crons.volumes.asyncProfiler" . | nindent 8 }} + - name: dumps + emptyDir: + sizeLimit: {{ .Values.deployments.crons.dumpsEmptyDirSizeLimit }} + {{- with .Values.deployments.crons.extraVolumes }} + {{ toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.secrets.defaults.dockerhub_config }} + imagePullSecrets: + {{- if .Values.secrets.defaults.dockerhub_config.existingSecret }} + - name: {{ .Values.secrets.defaults.dockerhub_config.existingSecret }} + {{- else }} + - name: {{ include "secrets.dockerhub.name" . }} + {{- end -}} + {{- end }} + {{- if .Values.deployments.crons.podSecurityContext }} + securityContext: + {{- toYaml .Values.deployments.crons.podSecurityContext | nindent 8 }} + {{- end }} + {{ with .Values.deployments.crons.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ . }} + {{ end }} + containers: + {{- include "lightrun-crons.container.persist-async-profiler-output-files" . | nindent 8 }} + - name: {{ include "lightrun-crons.name" . }} + securityContext: {{- include "lightrun-crons.containerSecurityContext" . | indent 12 }} + image: "{{ .Values.deployments.crons.image.repository }}:{{ .Values.deployments.crons.image.tag }}" + imagePullPolicy: {{ .Values.deployments.crons.image.pullPolicy }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + command: ["/usr/lib/jvm/default-jvm/bin/java"] + args: [ + {{- if and .Values.general.internal_tls.enabled .Values.general.internal_tls.certificates.existing_ca_secret_name }} + "-Djavax.net.ssl.trustStore=/p12/internalca", + "-Djavax.net.ssl.trustStorePassword=$(KEYSTORE_PASSWORD)", + {{- end }} + {{- include "lightrun-crons.java.argument.asyncProfiler" . | nindent 12 }} + "-jar", + "/usr/src/lightrun/{{ .Values.deployments.crons.jar_name }}" + ] + volumeMounts: + {{- include "lightrun-backend-crons.volumeMounts" . | nindent 12 }} + {{- include "lightrun-crons.volumeMounts.asyncProfiler" . | nindent 12 }} + {{- with .Values.deployments.crons.extraVolumeMounts }} + {{ toYaml . | nindent 12 }} + {{- end }} + startupProbe: + timeoutSeconds: {{ .Values.deployments.crons.startupProbe.timeoutSeconds }} + periodSeconds: {{ .Values.deployments.crons.startupProbe.periodSeconds }} + failureThreshold: {{ .Values.deployments.crons.startupProbe.failureThreshold }} + successThreshold: {{ .Values.deployments.crons.startupProbe.successThreshold }} + httpGet: + scheme: {{ include "http.scheme" . | upper }} + path: {{ .Values.deployments.crons.startupProbe.path }} + port: 8080 + livenessProbe: + initialDelaySeconds: {{ .Values.deployments.crons.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.deployments.crons.livenessProbe.timeoutSeconds }} + periodSeconds: {{ .Values.deployments.crons.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.deployments.crons.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.deployments.crons.livenessProbe.failureThreshold }} + httpGet: + scheme: {{ include "http.scheme" . | upper }} + path: {{ .Values.deployments.crons.livenessProbe.path }} + port: 8080 + readinessProbe: + initialDelaySeconds: {{ .Values.deployments.crons.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.deployments.crons.readinessProbe.timeoutSeconds }} + periodSeconds: {{ .Values.deployments.crons.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.deployments.crons.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.deployments.crons.readinessProbe.failureThreshold }} + httpGet: + scheme: {{ include "http.scheme" . | upper }} + path: {{ .Values.deployments.crons.readinessProbe.path }} + port: 8080 + {{- with .Values.deployments.crons.lifecycle }} + lifecycle: + {{ toYaml . | nindent 12 }} + {{- end }} + resources: + requests: + cpu: {{ .Values.deployments.crons.resources.cpu }} + memory: {{ .Values.deployments.crons.resources.memory }} + limits: + cpu: {{ .Values.deployments.crons.resources.cpu }} + memory: {{ .Values.deployments.crons.resources.memory }} + envFrom: + - secretRef: + name: {{ include "secrets.backend.name" . }} + env: + {{- include "lightrun-backend-crons.environmentVariables" . | nindent 12 }} + - name: SPRING_PROFILES_ACTIVE + value: "prod,swagger,cluster,cron" + {{- $mergedExtraEnvs := include "lightrun-crons.mergedExtraEnvs" . }} + {{- if $mergedExtraEnvs }} +{{ $mergedExtraEnvs | nindent 12 }} + {{- end }} + {{- if not (include "lightrun-crons.hasJavaOptions" .) }} + - name: "_JAVA_OPTIONS" + value: {{- toYaml (include "calculate-heap-size" .Values.deployments.crons) | nindent 21 }} + {{- end }} + + # waiting for mysql, rabbitmq and keycloak initialization + initContainers: + {{- include "lightrun-backend-crons.initContainers" . | nindent 6 }} + {{- include "lightrun-crons.initContainer.download-async-profiler" . | nindent 6 }} + + {{- if .Values.general.tolerations }} + tolerations: + {{ toYaml .Values.general.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.general.nodeSelector }} + nodeSelector: + {{ toYaml .Values.general.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.deployments.crons.affinity }} + affinity: + {{ toYaml .Values.deployments.crons.affinity | nindent 8 }} + {{- end }} +{{- end }} diff --git a/chart/templates/crons/serviceaccount.yaml b/chart/templates/crons/serviceaccount.yaml new file mode 100644 index 0000000..8a5937a --- /dev/null +++ b/chart/templates/crons/serviceaccount.yaml @@ -0,0 +1,10 @@ +{{- if and .Values.general.crons.enabled .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: {{ include "lightrun-crons.name" . }} + name: {{ template "lightrun-crons.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ or .Values.serviceAccount.automountServiceAccountToken .Values.general.system_diagnostics_k8s_api.enabled }} +{{- end }} diff --git a/chart/templates/helpers/_helpers.tpl b/chart/templates/helpers/_helpers.tpl index f2e105a..dae99d4 100644 --- a/chart/templates/helpers/_helpers.tpl +++ b/chart/templates/helpers/_helpers.tpl @@ -728,3 +728,449 @@ Get encryption key for Lightrun deployment. Handles existing keys, random genera path: {{ include "secrets.encryption-key-name" . }} {{- end }} {{- end }} + +{{/* +######################################## +### Shared Backend/Background Tasks ### +######################################## +*/}} + +{{/* +Shared environment variables for backend and crons services +*/}} +{{- define "lightrun-backend-crons.environmentVariables" -}} +{{- if and .Values.general.system_config.content .Values.general.system_config.signature }} +- name: LIGHTRUN_SYSTEM_CONFIG_JSON_FILE_PATH + value: "/opt/lightrun/system_config.json" +- name: LIGHTRUN_SYSTEM_CONFIG_JSON_SIGNATURE + value: {{ .Values.general.system_config.signature }} +{{- end }} +- name: SERVER_SECURITY_ENCRYPTION-KEYS-PATH + value: file:/encryption-keys +- name: LIGHTRUN_HOSTNAME + value: {{ .Values.general.name }} +- name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name +- name: INFO_DEPLOYMENT +{{ if eq .Values.general.deployment_type "saas" }} + value: "SaaS" +{{ else if eq .Values.general.deployment_type "single-tenant" }} + value: "single-tenant" +{{ else }} + value: "on-prem" +{{ end }} +- name: SPRING_DATASOURCE_URL + {{- if eq .Values.deployments.backend.dbConnector "mysql" }} + value: "jdbc:p6spy:mysql://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}" + {{- else if eq .Values.deployments.backend.dbConnector "mariadb" }} + value: "jdbc:mariadb://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}&rewriteBatchedStatements=true" + {{- end }} +- name: SPRING_CACHE_JCACHE_CONFIG + value: "file:///jcache-config/redisson-jcache-{{ .Values.deployments.redis.architecture }}.yaml" +- name: SPRING_FLYWAY_URL + {{- if eq .Values.deployments.backend.dbConnector "mysql" }} + value: "jdbc:mysql://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}" + {{- else if eq .Values.deployments.backend.dbConnector "mariadb" }} + value: "jdbc:mariadb://{{ include "mysql.db_endpoint" . }}:3306/{{ .Values.general.db_database }}?allowPublicKeyRetrieval=true&trustServerCertificate=true&useSSL={{ .Values.general.db_require_secure_transport }}" + {{- end }} +- name: SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KEYCLOAK_ISSUER-URI + value: "https://{{ .Values.general.lightrun_endpoint }}/auth/realms/lightrun" +- name: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER-URI + value: "https://{{ .Values.general.lightrun_endpoint }}/auth/realms/lightrun" +- name: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK-SET-URI + value: "{{ include "http.scheme" . }}://{{ include "lightrun-keycloak.name" . }}:8080/auth/realms/lightrun/protocol/openid-connect/certs" +- name: SPRING_SECURITY_KEYCLOAK_URL + value: "{{ include "http.scheme" . }}://{{ include "lightrun-keycloak.name" . }}:8080/auth" +{{ if or .Values.general.internal_tls.enabled .Values.deployments.redis.encryption.enabled }} +- name: SPRING_REDIS_SSL + value: "true" +- name: REDIS_PROTOCOL_NAME + value: "rediss" +{{ end }} +{{ if .Values.general.internal_tls.enabled }} +- name: SERVER_SSL_ENABLED + value: "true" +{{ if .Values.general.mq.enabled }} +- name: SPRING_RABBITMQ_SSL_ENABLED + value: "true" +{{ end }} +- name: TLS_SKIPCLIENTCERTIFICATEVERIFICATION +{{ if .Values.general.internal_tls.certificates.verification }} + value: "false" +{{ else }} + value: "true" +{{ if .Values.general.mq.enabled }} +- name: SPRING_RABBITMQ_SSL_VALIDATE-SERVER-CERTIFICATE + value: "false" +{{ end }} +{{ end }} +{{ end }} +{{- if not .Values.deployments.redis.auth.enabled }} +- name: SPRING_REDIS_PASSWORD #to disable password auth even if secret contains password, but auth_enabled is false + value: null +{{- end }} +- name: SPRING_SECURITY_KEYCLOAK_PORT + value: "8080" +- name: SPRING_SECURITY_KEYCLOAK_EXTRA-REDIRECT-URLS + value: "https://{{ .Values.general.lightrun_endpoint }}/*" +- name: SPRING_SECURITY_KEYCLOAK_EXTERNAL-URL + value: "https://{{ .Values.general.lightrun_endpoint }}/auth" +- name: JHIPSTER_SLEEP + value: "5000" # gives time for other services to boot before the application +- name: INTEGRATIONS_ENABLE + value: "true" +- name: INTEGRATIONS_DATADOG_ENABLE + value: "false" +- name: SERVER_EXTERNAL_HOST + value: "{{ .Values.general.lightrun_endpoint }}" +- name: SERVER_EXTERNAL_PORT + value: "443" +- name: SPRING_REDIS_HOST + value: {{ include "lightrun-redis.endpoint" . }} +- name: SPRING_REDIS_PORT + value: "{{ .Values.deployments.redis.port }}" +- name: SERVER_DISPLAY_PORT + value: "443" +- name: SPRING_FLYWAY_SCHEMAS + value: {{ .Values.general.db_database }} +- name: KEYSTORE_PATH + value: "file:/p12/lightrun.p12" +- name: SYSTEM_DEFAULT_USER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "secrets.backend.name" . }} + key: SPRING_SECURITY_KEYCLOAK_CLI_PASSWORD +- name: ARTIFACTS_ENABLE_S3_FEATURE #TODO: deprecated in favor of ARTIFACTS_ENABLE in 2.1.7 + value: {{ .Values.deployments.backend.artifacts.enable | quote }} +- name: ARTIFACTS_ENABLE + value: {{ .Values.deployments.backend.artifacts.enable | quote }} +- name: ARTIFACTS_S3_URL #TODO: deprecated in favor of ARTIFACTS_REPOSITORY_URL in 2.1.7 + value: "{{ .Values.deployments.backend.artifacts.s3_url }}" +- name: ARTIFACTS_REPOSITORY_URL + value: "{{ .Values.deployments.backend.artifacts.repository_url }}" +- name: ARTIFACTS_SUPPORTED_VERSIONS_URL + value: "{{ .Values.deployments.backend.artifacts.supported_versions_url }}" +- name: ARTIFACTS_DOWNLOAD_PRERELEASE #TODO: deprecated in favor of ARTIFACTS_VERSION_RESOLUTION_MODE in 2.1.7 + value: "{{ .Values.deployments.backend.artifacts.download_prerelease }}" +- name: ARTIFACTS_VERSION_RESOLUTION_MODE + value: "{{ .Values.deployments.backend.artifacts.resolution_mode }}" +- name: LIGHTRUN_ARTIFACTS_URL + value: "{{ include "http.scheme" . }}://{{ include "artifacts.name" . }}:8080" +{{- if .Values.general.mq.enabled }} +- name: SPRING_RABBITMQ_HOST + value: {{ include "lightrun-mq.endpoint" . }} +- name: SPRING_RABBITMQ_PORT + value: {{ .Values.general.mq.port | quote }} +- name: TRACKING_MIXPANEL_QUEUE_NAME + value: {{ include "lightrun-mq.getQueueNameByPrefix" (dict "prefix" "mixpanel-events" "Values" .Values) | quote }} +- name: KEYCLOAK_QUEUE_NAME + value: {{ include "lightrun-mq.getQueueNameByPrefix" (dict "prefix" "keycloak-events" "Values" .Values) | quote }} +{{- end }} +- name: LOGGING_USE-JSON-FORMAT + value: "{{ .Values.deployments.backend.useJsonLogFormat }}" +{{- if .Values.general.data_streamer.enabled }} +- name: INTEGRATIONS_SIEM_STREAMING-SERVICE_URL + value: "{{ include "http.scheme" . }}://{{ include "data_streamer.name" . }}:8080/events/post" +{{- end }} +{{- end -}} + +{{/* +Shared volumes for backend and crons services +*/}} +{{- define "lightrun-backend-crons.volumes" -}} +{{- if and .Values.general.system_config.content .Values.general.system_config.signature }} +- name: system-config + configMap: + name: {{ include "lightrun.fullname" . }}-system-config +{{- end }} +- name: encryption-keys + secret: + secretName: {{ include "secrets.backend.name" . }} + optional: true + items: + # Only select items that start with encryption-key- + {{- include "encryption.key.items" . | nindent 6 }} + +{{- if and .Values.general.internal_tls.enabled .Values.general.internal_tls.certificates.existing_ca_secret_name }} +- name: ca-cert + secret: + secretName: {{ .Values.general.internal_tls.certificates.existing_ca_secret_name }} +{{- end }} +- name: jcache-config + configMap: + name: {{ include "lightrun-be.name" . }}-jcache-config + items: + - key: "redisson-jcache-single.yaml" + path: "redisson-jcache-single.yaml" + - key: "redisson-jcache-replicated.yaml" + path: "redisson-jcache-replicated.yaml" +- name: certificates + secret: + secretName: {{ include "secrets.certificate.name" . }} +- name: p12 + emptyDir: + sizeLimit: 50Mi + +{{- if .Values.general.readOnlyRootFilesystem }} +- name: tmpfs + emptyDir: + sizeLimit: {{ .Values.general.readOnlyRootFilesystem_tmpfs_sizeLimit }} +{{- end }} +{{- end -}} + +{{/* +Shared volume mounts for backend and crons services +*/}} +{{- define "lightrun-backend-crons.volumeMounts" -}} +{{- if and .Values.general.system_config.content .Values.general.system_config.signature }} +- name: system-config + mountPath: "/opt/lightrun/system_config.json" + subPath: "system_config.json" + readOnly: true +{{- end }} +- name: encryption-keys + mountPath: /encryption-keys + readOnly: true +- name: jcache-config + mountPath: "/jcache-config" +- name: certificates + mountPath: /usr/src/lightrun/helm/tls +- name: p12 + mountPath: /p12 +- name: dumps + mountPath: /dumps +{{- if .Values.general.readOnlyRootFilesystem }} +- name: tmpfs + mountPath: /tmp +{{- end }} +{{- end -}} + +{{/* +Shared init containers for backend and crons services +*/}} +{{- define "lightrun-backend-crons.initContainers" -}} + + +- name: wait-for-keycloak + image: "{{ .Values.deployments.backend.initContainers.wait_for_keycloak.image.repository }}:{{ .Values.deployments.backend.initContainers.wait_for_keycloak.image.tag }}" + imagePullPolicy: {{ .Values.deployments.backend.initContainers.wait_for_keycloak.image.pullPolicy }} + securityContext: {{ include "lightrun-be.containerSecurityContext" . | indent 4 }} + command: + - sh + - /scripts/wait-for-200.sh + resources: + limits: + memory: "100Mi" + cpu: "100m" + requests: + memory: "100Mi" + cpu: "100m" + env: + - name: URL + value: {{ include "http.scheme" . }}://{{ include "lightrun-keycloak.name" . }}:9000/auth/health/started {{ if .Values.general.internal_tls.enabled }}--no-check-certificate{{ end }} + +{{ if .Values.general.mq.enabled }} +{{- include "lightrun-mq.initContainer.wait-for-rabbitmq" (merge (dict "imageConfig" .Values.deployments.backend.initContainers.wait_for_rabbitmq "securityContext" "lightrun-be.containerSecurityContext") .) }} +{{- end }} +- name: p12-creator + image: "{{ .Values.deployments.backend.initContainers.p12_creator.image.repository }}:{{ .Values.deployments.backend.initContainers.p12_creator.image.tag }}" + imagePullPolicy: {{ .Values.deployments.backend.initContainers.p12_creator.image.pullPolicy }} + securityContext: {{ include "lightrun-be.containerSecurityContext" . | indent 4 }} + resources: + limits: + memory: "200Mi" + cpu: "200m" + requests: + memory: "200Mi" + cpu: "200m" + env: + - name: KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "secrets.backend.name" . }} + key: KEYSTORE_PASSWORD + volumeMounts: + - name: certificates + mountPath: /tls + - name: p12 + mountPath: /p12 + command: ['sh', '-c', 'cp /tls/tls.crt /p12/crt.pem && cp /tls/tls.key /p12/key.pem && openssl pkcs12 -export -out /p12/lightrun.p12 -inkey /p12/key.pem -in /p12/crt.pem -passin pass:$KEYSTORE_PASSWORD -passout pass:$KEYSTORE_PASSWORD'] + +{{- if and .Values.general.internal_tls.enabled .Values.general.internal_tls.certificates.existing_ca_secret_name }} +- name: root-ca-creator + image: "{{ .Values.deployments.backend.image.repository }}:{{ .Values.deployments.backend.image.tag }}" + securityContext: {{ include "lightrun-be.containerSecurityContext" . | indent 4 }} + resources: + limits: + memory: "100Mi" + cpu: "100m" + requests: + memory: "100Mi" + cpu: "100m" + env: + - name: KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "secrets.backend.name" . }} + key: KEYSTORE_PASSWORD + volumeMounts: + - name: ca-cert + mountPath: /tmp/ca-certificates/ + - name: p12 + mountPath: /p12 + command: ['sh', '-c', 'keytool -import -trustcacerts -alias internalCa -keystore /p12/internalca -file /tmp/ca-certificates/ca.crt -noprompt -storepass $KEYSTORE_PASSWORD && keytool -importkeystore -srckeystore /usr/lib/jvm/default-jvm/jre/lib/security/cacerts -destkeystore /p12/internalca -srcstorepass changeit -deststorepass $KEYSTORE_PASSWORD'] +{{- end }} +{{- end -}} + +{{/* +#################### +### Crons Service ### +#################### +*/}} + +{{- define "lightrun-crons.name" -}} +{{ include "lightrun.fullname" . }}-crons +{{- end -}} + +{{/* +Create the name of the lightrun crons service account to use +*/}} +{{- define "lightrun-crons.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "lightrun-crons.name" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Container SecurityContext of lightrun crons +*/}} +{{- define "lightrun-crons.containerSecurityContext" -}} +{{- $readOnlyRootFilesystem := dict "readOnlyRootFilesystem" (.Values.general.readOnlyRootFilesystem ) -}} +{{- $baseSecurityContext := include "baseSecurityContext" . | fromYaml -}} +{{- $localSecurityContext := mustMerge $baseSecurityContext $readOnlyRootFilesystem -}} +{{/*If user provided values for containerSecurityContext, merge them with the baseSecurityContext*/}} +{{/*Values passed by user will override defaults*/}} +{{- if .Values.deployments.crons.containerSecurityContext -}} +{{- $mergedSecurityContext := mergeOverwrite $localSecurityContext (.Values.deployments.crons.containerSecurityContext | default dict) -}} +{{- $mergedSecurityContext | toYaml -}} +{{- else if kindIs "invalid" .Values.deployments.crons.containerSecurityContext -}} +{{ default dict | toYaml -}} +{{- else -}} +{{/*use default values from baseSecurityContext*/}} +{{- $localSecurityContext | toYaml -}} +{{- end -}} +{{- end -}} + +{{/* +Cron-specific asyncProfiler helpers +*/}} +{{- define "lightrun-crons.volumes.asyncProfiler" -}} +{{- if .Values.deployments.crons.asyncProfiler.enabled -}} +{{ include "asyncProfiler.volumes" .Values.deployments.crons.asyncProfiler }} +{{- end -}} +{{- end -}} + +{{- define "lightrun-crons.volumeMounts.asyncProfiler" -}} +{{- if .Values.deployments.crons.asyncProfiler.enabled }} +{{- include "asyncProfiler.volumeMounts" . }} +{{- end -}} +{{- end -}} + +{{- define "lightrun-crons.initContainer.download-async-profiler" -}} +{{- if .Values.deployments.crons.asyncProfiler.enabled -}} +{{ include "asyncProfiler.initContainer.download-async-profiler" .Values.deployments.crons.asyncProfiler }} + securityContext: {{- include "lightrun-crons.containerSecurityContext" . | indent 4 }} +{{- end -}} +{{- end -}} + +{{- define "lightrun-crons.container.persist-async-profiler-output-files" -}} +{{- if and .Values.deployments.crons.asyncProfiler.enabled .Values.deployments.crons.asyncProfiler.persistence.enabled }} +{{ include "asyncProfiler.container.persist-async-profiler-output-files" . }} + securityContext: {{- include "lightrun-crons.containerSecurityContext" . | indent 4 }} +{{- end -}} +{{- end -}} + +{{- define "lightrun-crons.java.argument.asyncProfiler" -}} +{{- if .Values.deployments.crons.asyncProfiler.enabled -}} +"{{- include "asyncProfiler.java.agentpath" .Values.deployments.crons.asyncProfiler -}}", +{{- end -}} +{{- end -}} + +{{/* +Merge extraEnvs from backend and crons with crons taking precedence for duplicate keys +*/}} +{{- define "lightrun-crons.mergedExtraEnvs" -}} +{{- $backendExtraEnvs := .Values.deployments.backend.extraEnvs | default list -}} +{{- $cronsExtraEnvs := .Values.deployments.crons.extraEnvs | default list -}} +{{- $mergedEnvs := list -}} + +{{/* First, add all backend extraEnvs */}} +{{- range $backendExtraEnvs -}} +{{- $backendEnv := . -}} +{{- $isOverridden := false -}} +{{/* Check if this env var is overridden in crons */}} +{{- range $cronsExtraEnvs -}} +{{- if eq .name $backendEnv.name -}} +{{- $isOverridden = true -}} +{{- end -}} +{{- end -}} +{{/* Only add backend env if not overridden by crons */}} +{{- if not $isOverridden -}} +{{- $mergedEnvs = append $mergedEnvs $backendEnv -}} +{{- end -}} +{{- end -}} + +{{/* Then, add all crons extraEnvs (these take precedence) */}} +{{- range $cronsExtraEnvs -}} +{{- $mergedEnvs = append $mergedEnvs . -}} +{{- end -}} + +{{/* Output merged envs as YAML if any exist */}} +{{- if $mergedEnvs -}} +{{- toYaml $mergedEnvs -}} +{{- end -}} +{{- end -}} + +{{/* +Check if merged extraEnvs contain _JAVA_OPTIONS +*/}} +{{- define "lightrun-crons.hasJavaOptions" -}} +{{- $backendExtraEnvs := .Values.deployments.backend.extraEnvs | default list -}} +{{- $cronsExtraEnvs := .Values.deployments.crons.extraEnvs | default list -}} +{{- $javaOptionsEnvs := list -}} + +{{/* Check backend extraEnvs for _JAVA_OPTIONS */}} +{{- range $backendExtraEnvs -}} +{{- if eq .name "_JAVA_OPTIONS" -}} +{{- $isOverridden := false -}} +{{/* Check if this env var is overridden in crons */}} +{{- range $cronsExtraEnvs -}} +{{- if eq .name "_JAVA_OPTIONS" -}} +{{- $isOverridden = true -}} +{{- end -}} +{{- end -}} +{{/* Only consider it if not overridden by crons */}} +{{- if not $isOverridden -}} +{{- $javaOptionsEnvs = append $javaOptionsEnvs . -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* Check crons extraEnvs for _JAVA_OPTIONS (these take precedence) */}} +{{- range $cronsExtraEnvs -}} +{{- if eq .name "_JAVA_OPTIONS" -}} +{{- $javaOptionsEnvs = append $javaOptionsEnvs . -}} +{{- end -}} +{{- end -}} + +{{- if $javaOptionsEnvs -}} +true +{{- end -}} +{{- end -}} + + diff --git a/chart/values.yaml b/chart/values.yaml index 8933fec..8f03a95 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -141,6 +141,15 @@ general: ## List of response status codes upon which the data streamer will not retry - 403 + ##################### + ## Crons + ##################### + crons: + enabled: false + ## If set to `true`, will deploy a crons service for processing crons and other background operations + ## This service runs the same application as the backend but with crons profiles activated + ## It does not expose any external ports and focuses on background processing to improve backend performance + ################################################ ## Enable TLS communication between pods ################################################ @@ -666,6 +675,72 @@ deployments: # deployments.backend.asyncProfiler.persistence.existingClaim -- Name of an existing PersistentVolumeClaim to use. existingClaim: "" + crons: + image: + repository: "lightruncom/server" + tag: "" + pullPolicy: IfNotPresent + resources: + cpu: 1000m + memory: 2Gi + podLabels: {} + podAnnotations: {} + appMetrics: + exposeToDatadog: false + includeMetrics: [] + #example: + ## includeMetrics: ["connected_agents_per_runtime","connected_agents_total","actions_count_active_total","companies_count_total"] + jar_name: LightrunServer.jar + extraEnvs: [] + podSecurityContext: {} + containerSecurityContext: {} + extraVolumes: [] + extraVolumeMounts: [] + podDisruptionBudget: {} # [minAvailable|maxUnavailable] either integer or percentage + topologySpreadConstraints: [] + affinity: {} + lifecycle: + preStop: + exec: + command: ["sh", "-c", "sleep 10"] + terminationGracePeriodSeconds: 45 + startupProbe: + path: /management/health/readiness + failureThreshold: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + livenessProbe: + initialDelaySeconds: 200 + periodSeconds: 50 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 3 + path: /version + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 3 + path: /management/health/readiness + dumpsEmptyDirSizeLimit: 5Gi + asyncProfiler: + # deployments.crons.asyncProfiler.enabled -- Run async-profiler on every crons pod + ## ref: https://github.com/async-profiler/async-profiler + enabled: false + # Download URL end point for async profiler binary, ref: https://github.com/async-profiler/async-profiler/blob/master/CHANGELOG.md + downloadUrl: "https://github.com/async-profiler/async-profiler/releases/download/v3.0/async-profiler-3.0-linux-x64.tar.gz" + # deployments.crons.asyncProfiler.arguments -- Configure the async profiler arguments + ## ref: https://github.com/async-profiler/async-profiler/blob/v3.0/src/arguments.cpp#L44 + arguments: "start,event=ctimer,loop=10m,chunksize=1m,file=/async-profiler-tmp/profile-%t.jfr,log=/tmp/asyncprofiler.log" + # deployments.crons.asyncProfiler.persistence -- Configure persistence for the async profiler output files + persistence: + # deployments.crons.asyncProfiler.persistence.enabled -- If **true**, use existing PersistentVolumeClaim. + enabled: false + # deployments.crons.asyncProfiler.persistence.existingClaim -- Name of an existing PersistentVolumeClaim to use. + existingClaim: "" + keycloak: useJsonLogFormat: false # For clusters with more than 3 pods, consider changing the number of "owner nodes" as described in diff --git a/docs/components/crons.md b/docs/components/crons.md new file mode 100644 index 0000000..ac6664a --- /dev/null +++ b/docs/components/crons.md @@ -0,0 +1,170 @@ +# Crons Service + +The crons component is responsible for handling background processing tasks such as scheduled jobs, batch operations, and other background activities. This service runs the same application as the backend but with different Spring profiles activated to focus on background processing, improving overall system performance by isolating background tasks. + +Configuration is defined under **`deployments.crons`** in the **`values.yaml`** + +## Service Overview + +The crons service provides: +- **Background Task Processing**: Dedicated processing of scheduled jobs and background operations +- **Performance Isolation**: Separates background processing from user-facing backend operations +- **Reliability**: Independent failure domain from the main backend service + +## Enabling/Disabling the Service + +The crons service is controlled by the global enable flag: + +```yaml +general: + crons: + enabled: false # Set to true to deploy the crons service +``` + +When enabled, the crons service will be deployed alongside the backend service with no external network exposure. + +### Configuration Options + +#### Image Configuration + +```yaml + image: + repository: "lightruncom/server" + tag: "" + pullPolicy: IfNotPresent +``` + +#### Resource Allocation + +```yaml + resources: + cpu: 1000m # CPU limit (optimized for background processing) + memory: 2Gi # Memory limit (optimized for background processing) +``` + +#### Pod Labels & Annotations + +```yaml + podLabels: {} + podAnnotations: {} +``` + +#### Application Metrics (Datadog Integration) + +```yaml + appMetrics: + exposeToDatadog: false + includeMetrics: [] + # Example: + # includeMetrics: ["connected_agents_per_runtime","connected_agents_total","actions_count_active_total","companies_count_total"] +``` + +#### Extra Environment Variables + +```yaml + extraEnvs: [] +``` + +> **Note:** Environment variables defined in `extraEnvs` will be merged with backend `extraEnvs`. Crons-specific variables take precedence over backend variables with the same name. + +#### Security Contexts + +```yaml + podSecurityContext: {} + containerSecurityContext: {} +``` + +#### Volumes + +```yaml + extraVolumes: [] + extraVolumeMounts: [] + dumpsEmptyDirSizeLimit: 5Gi # Size limit for dumps volume +``` + +#### Pod Disruption Budget & Scheduling + +```yaml + podDisruptionBudget: {} + topologySpreadConstraints: [] + affinity: {} +``` + +#### Lifecycle & Graceful Shutdown + +```yaml + lifecycle: + preStop: + exec: + command: ["sh", "-c", "sleep 10"] + terminationGracePeriodSeconds: 45 +``` + +#### Health Probes + +```yaml + startupProbe: + path: /management/health/readiness + failureThreshold: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + + livenessProbe: + initialDelaySeconds: 200 + periodSeconds: 50 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 3 + path: /version + + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 3 + path: /management/health/readiness +``` + +#### AsyncProfiler Configuration + +```yaml + asyncProfiler: + enabled: false # Run async-profiler on every crons pod + downloadUrl: "https://github.com/async-profiler/async-profiler/releases/download/v3.0/async-profiler-3.0-linux-x64.tar.gz" + arguments: "start,event=ctimer,loop=10m,chunksize=1m,file=/async-profiler-tmp/profile-%t.jfr,log=/tmp/asyncprofiler.log" + persistence: + enabled: false + existingClaim: "" +``` + +## Deployment Characteristics + +### Replica Configuration +- **Fixed Replicas**: Always runs exactly 1 replica (no HPA support) +- **Deployment Strategy**: Uses `Recreate` strategy to ensure clean pod replacement +- **Purpose**: Single instance prevents duplicate execution of scheduled tasks + +### Network Isolation +- **No Service Object**: The crons service does not expose any Kubernetes Service +- **No External Access**: No Ingress or external network routing +- **Internal Processing Only**: Communicates only with shared backend services (DB, Redis, etc.) + +## Architecture Integration + +The crons service shares infrastructure components with the backend service through shared Helm templates: + +- **Environment Variables**: Shares database, Redis, Keycloak, and other service connections +- **Volumes**: Shares encryption keys, certificates, and configuration volumes +- **Init Containers**: Uses the same initialization logic (wait-for-keycloak, p12-creator, etc.) +- **Security**: Identical security contexts and RBAC configurations + +This shared architecture ensures consistency and reduces maintenance overhead while providing service isolation. + +## Monitoring and Observability + +The crons service exposes the same monitoring endpoints as the backend: +- **Health Endpoints**: `/management/health/readiness`, `/version` +- **Metrics**: Available at `/management/prometheus` (when enabled) +- **Container Port**: 8080 (internal monitoring only) \ No newline at end of file diff --git a/docs/images/architecture-diagram.excalidraw.png b/docs/images/architecture-diagram.excalidraw.png index 5a84548..fa857bf 100644 Binary files a/docs/images/architecture-diagram.excalidraw.png and b/docs/images/architecture-diagram.excalidraw.png differ