diff --git a/CHANGELOG.md b/CHANGELOG.md index a4562879a..958d3b282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,12 @@ and this project adheres to bearer token - ✨(helm) add a dedicated ingress for the prometheus metrics of the backend and of yhub +- ✨(helm) add a ServiceMonitor and a PodMonitor per component whose metrics + are enabled, and `backend.metrics.enabled` +- 🔧(helm) scrape the metrics of the dev cluster with a trimmed + kube-prometheus-stack and the ServiceMonitors of the chart +- 🔧(helm) serve the example console of django-prometheus on the dev + Prometheus - ✨(backend) add a service generating cached RS256 JWT tokens - ✨(backend) publish the JWT public key on a JWKS endpoint - 🔧(dev) generate the JWT signing key when bootstrapping the dev stack diff --git a/bin/Tiltfile b/bin/Tiltfile index 86e824cc6..ffdbe5f2e 100644 --- a/bin/Tiltfile +++ b/bin/Tiltfile @@ -63,8 +63,43 @@ k8s_resource('impress-docs-celery-worker', resource_deps=['valkey-docs']) k8s_resource('impress-docs-yhub', resource_deps=['valkey-yhub']) k8s_resource('impress-docs-yhub-worker', resource_deps=['valkey-yhub']) -# the valkey chart leaves its objects without a namespace -k8s_yaml(namespace_inject(local('cd ../src/helm && helmfile -n impress -e dev template .'), 'impress')) +# The Prometheus Operator carries the CRDs of its release, and the Prometheus +# it runs (a workload of its own kind, whose pods the operator creates). The +# ServiceMonitors of the application are a resource apart, applied once the +# operator is up: their kind does not exist before its CRDs do, and nothing of +# the application waits for them. The kind is a regular expression, matched +# anywhere in the name: anchored, or it takes the PrometheusRule too. +k8s_kind('^Prometheus$', pod_readiness='ignore') +k8s_resource('prometheus-operator', objects=[ + 'alertmanagerconfigs.monitoring.coreos.com:customresourcedefinition', + 'alertmanagers.monitoring.coreos.com:customresourcedefinition', + 'podmonitors.monitoring.coreos.com:customresourcedefinition', + 'probes.monitoring.coreos.com:customresourcedefinition', + 'prometheusagents.monitoring.coreos.com:customresourcedefinition', + 'prometheuses.monitoring.coreos.com:customresourcedefinition', + 'prometheusrules.monitoring.coreos.com:customresourcedefinition', + 'scrapeconfigs.monitoring.coreos.com:customresourcedefinition', + 'servicemonitors.monitoring.coreos.com:customresourcedefinition', + 'thanosrulers.monitoring.coreos.com:customresourcedefinition', +]) +k8s_resource('prometheus-prometheus', objects=[ + 'prometheus-prometheus:ingress', + # the example console of django-prometheus and the rules feeding it + 'django-console:configmap', + 'django:prometheusrule', +], resource_deps=['prometheus-operator'], links=[ + 'https://docs-prometheus.127.0.0.1.nip.io/targets', + 'https://docs-prometheus.127.0.0.1.nip.io/consoles/django.html', +]) +k8s_resource(new_name='impress-docs-monitors', objects=[ + 'impress-docs-backend:servicemonitor', + 'impress-docs-yhub:servicemonitor', + 'impress-docs-yhub-worker:servicemonitor', +], resource_deps=['prometheus-operator']) + +# the valkey chart leaves its objects without a namespace, and the CRDs of the +# prometheus release only come with --include-crds +k8s_yaml(namespace_inject(local('cd ../src/helm && helmfile -n impress -e dev template --include-crds .'), 'impress')) migration = ''' set -eu diff --git a/documentation/metrics.md b/documentation/metrics.md index 4fc2437d6..cd630e9d1 100644 --- a/documentation/metrics.md +++ b/documentation/metrics.md @@ -24,6 +24,12 @@ PROMETHEUS_API_KEY= # or PROMETHEUS_API_KEY_FILE The application **refuses to start** with the metrics enabled and no key: the endpoint would otherwise answer to anybody. +In production the endpoint follows `SECURE_SSL_REDIRECT` like every other +path: a scrape over plain http is redirected to https, so that the key never +travels in clear. `PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT=True` lifts that for +`/metrics` only, for a scraper that reaches the process past the proxy +terminating TLS, see [several replicas](#several-replicas-behind-one-address). + Then scrape `GET /metrics` with the key as a bearer token: ```yaml @@ -133,19 +139,101 @@ average: use a short scrape interval and rate windows of several minutes, and aggregate with `sum without (hostname) (rate(...[5m]))`. This degrades as the number of replicas grows. A Prometheus running inside the -cluster should scrape each pod directly instead (a `PodMonitor` or pod -discovery on the `http` port, same path, same bearer token). +cluster should scrape each pod directly instead: same path, same bearer token, +one target per pod. The chart builds the `ServiceMonitor` or `PodMonitor` for +it, see [below](#kubernetes-helm-chart). Such a scrape reaches the pod over +plain http, past the ingress that terminates TLS, and the Production settings +redirect it to https like anything else — where it gets nothing. Set +`PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT=True` to take `/metrics` out of that +redirect, the way the probes are; the pod's own address is already in +`ALLOWED_HOSTS`. Leave it off wherever the application is reached directly, +without a proxy in front: the redirect is then what keeps the bearer token off +the wire in clear. ## Kubernetes (Helm chart) ```yaml backend: + metrics: + enabled: true # PROMETHEUS_METRICS_ENABLED on the web pods, not on celery envVars: - PROMETHEUS_METRICS_ENABLED: "True" PROMETHEUS_API_KEY: secretKeyRef: name: backend key: PROMETHEUS_API_KEY + +yhub: + envVars: + PROMETHEUS_API_KEY: # the worker inherits it + secretKeyRef: + name: yhub + key: PROMETHEUS_API_KEY + metrics: + enabled: true # a port of its own, in the server and in the worker +``` + +`backend.metrics.enabled` sets `PROMETHEUS_METRICS_ENABLED` on the django +container only: the celery worker serves no request, so its metrics would never +be read. It sets `PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT` there too, since a pod +is only ever scraped over plain http. A value given in `backend.envVars` still +wins, and still reaches celery. + +### Prometheus Operator, inside the cluster + +```yaml +serviceMonitor: + enabled: true + labels: + release: kube-prometheus-stack # whatever your Prometheus selects monitors by +``` + +`serviceMonitor.enabled` (or `podMonitor.enabled`, or both) creates one monitor +per component whose metrics are enabled: + +| Monitor | Scrapes | Port | Path | Job | +|---|---|---|---|---| +| `-backend` | every backend web pod | `http` | `/metrics` | `backend` | +| `-yhub` | every yhub server pod | `metrics` | `yhub.metrics.path` | `yhub` | +| `-yhub-worker` | every yhub worker pod, when `yhub.worker.enabled` | `metrics` | `yhub.metrics.workerPath` | `yhub-worker` | + +Each pod is a target of its own, so every sample of every replica is taken at +every interval, nothing goes through an ingress, and the `hostname` label is +simply the pod. The scrape presents the bearer token of the component, which +the Prometheus Operator reads from the Secret `PROMETHEUS_API_KEY` comes from +in the `envVars` above (or from the one named in `backend.metrics.apiKeySecret` +and `yhub.metrics.apiKeySecret`, for a token given as `PROMETHEUS_API_KEY_FILE`). +A token that is not in a Secret is refused at render time. That Secret has to +live in the namespace of the monitors, and the service account of the operator +be allowed to read it, as the kube-prometheus-stack one is. + +A `ServiceMonitor` finds the pods through their Services, a `PodMonitor` through +their labels; they give the same targets. `interval`, `scrapeTimeout`, +`honorLabels`, `relabelings`, `metricRelabelings`, `labels`, `annotations` and +`namespace` are the same on both. + +The dev cluster (`make start-tilt`, or `helmfile -e dev apply`) does exactly +this: its helmfile installs a trimmed +[kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) +(`src/helm/env.d/dev/values.prometheus.yaml.gotmpl`: the operator, its CRDs +and one Prometheus, nothing else), the metrics of the backend and of yhub are +on with a token in the `docs-metrics` Secret, and `serviceMonitor.enabled` +builds the three monitors. The targets are at +https://docs-prometheus.127.0.0.1.nip.io/targets. + +That Prometheus also serves the +[example console of django-prometheus](https://github.com/django-commons/django-prometheus/tree/master/examples/prometheus) +at https://docs-prometheus.127.0.0.1.nip.io/consoles/django.html: requests per +second, by view, median and tail latency, model writes and database queries, +drawn from its recording rules. Both files are in +`src/helm/env.d/dev/prometheus/`, the rules verbatim and the console with its +job renamed to `backend`. Console templates draw with the classic UI, which +Prometheus 3 removed, so the dev Prometheus is the last 2.x release. + +### A Prometheus outside of the cluster + +```yaml +backend: + envVars: DJANGO_ALLOWED_HOSTS: docs.example.com,metrics.docs.example.com ingressMetrics: @@ -156,21 +244,9 @@ ingressMetrics: ``` `ingressMetrics` routes the exact path `/metrics` of that host to the backend -and nothing else. Its host has to be in `DJANGO_ALLOWED_HOSTS`. - -With yhub, the same ingress also publishes the server and the worker, each on an -exact path of its own: - -```yaml -yhub: - envVars: - PROMETHEUS_API_KEY: # the worker inherits it - secretKeyRef: - name: yhub - key: PROMETHEUS_API_KEY - metrics: - enabled: true # /metrics/yhub and /metrics/yhub-worker -``` +and nothing else. Its host has to be in `DJANGO_ALLOWED_HOSTS`. With +`yhub.metrics.enabled`, the same ingress also publishes the server and the +worker, each on an exact path of its own: | Path | Served by | |---|---| @@ -180,8 +256,5 @@ yhub: That is three scrape jobs on one host, differing by `metrics_path`. What is said above about [several replicas](#several-replicas-behind-one-address) applies to -each of them: yhub labels its samples with `hostname` too. - -The celery worker receives `backend.envVars` too. It serves no request, so its -metrics are never read: turn them off there with -`backend.celery.envVars.PROMETHEUS_METRICS_ENABLED: "False"`. +each of them: yhub labels its samples with `hostname` too. Prefer the monitors +whenever the Prometheus can reach the pods. diff --git a/src/backend/core/tests/test_prometheus_metrics.py b/src/backend/core/tests/test_prometheus_metrics.py index 1f6cf699b..b8f232b57 100644 --- a/src/backend/core/tests/test_prometheus_metrics.py +++ b/src/backend/core/tests/test_prometheus_metrics.py @@ -14,6 +14,7 @@ from prometheus_client import REGISTRY, Counter, values from rest_framework.test import APIClient from impress import urls +from impress.settings import Production pytestmark = pytest.mark.django_db @@ -124,6 +125,38 @@ def test_prometheus_metrics_served_with_the_api_key(): assert not response.cookies +@pytest.mark.usefixtures("metrics_enabled") +def test_prometheus_metrics_redirected_to_https_by_default(settings): + """ + Where the application is reached directly, a scrape over plain http should be sent + to https like everything else: the bearer token must not travel in clear. + """ + settings.SECURE_SSL_REDIRECT = True + settings.SECURE_REDIRECT_EXEMPT = Production.SECURE_REDIRECT_EXEMPT + + response = APIClient().get("/metrics", HTTP_AUTHORIZATION=f"Bearer {API_KEY}") + + assert response.status_code == 301 + assert response["Location"] == "https://testserver/metrics" + + +@pytest.mark.usefixtures("metrics_enabled") +def test_prometheus_metrics_exempt_from_the_ssl_redirect(settings): + """ + With PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT (what setup_prometheus_metrics adds to + the exemptions), a Prometheus reaching the pods over plain http should be served, + and every other path still sent to https. + """ + settings.SECURE_SSL_REDIRECT = True + settings.SECURE_REDIRECT_EXEMPT = [*Production.SECURE_REDIRECT_EXEMPT, "^metrics$"] + + response = APIClient().get("/metrics", HTTP_AUTHORIZATION=f"Bearer {API_KEY}") + assert response.status_code == 200 + + response = APIClient().get("/api/v1.0/config/") + assert response.status_code == 301 + + @pytest.mark.usefixtures("metrics_enabled") def test_prometheus_metrics_is_read_only(): """Only GET and HEAD should be answered.""" diff --git a/src/backend/core/tests/test_settings.py b/src/backend/core/tests/test_settings.py index 3f55d6583..37ac9f905 100644 --- a/src/backend/core/tests/test_settings.py +++ b/src/backend/core/tests/test_settings.py @@ -7,7 +7,7 @@ import tempfile import pytest -from impress.settings import Base +from impress.settings import Base, Production def test_invalid_settings_oidc_email_configuration(): @@ -89,6 +89,7 @@ def _prometheus_settings(**attributes): "PROMETHEUS_API_KEY": "a-key", "INSTALLED_APPS": list(Base.INSTALLED_APPS), "MIDDLEWARE": list(Base.MIDDLEWARE), + "SECURE_REDIRECT_EXEMPT": list(Production.SECURE_REDIRECT_EXEMPT), "DATABASES": {"default": {"ENGINE": "django.db.backends.postgresql"}}, **attributes, }, @@ -167,6 +168,26 @@ def test_settings_prometheus_metrics_enabled(monkeypatch, tmp_path): assert middleware.count(PROMETHEUS_AFTER_MIDDLEWARE) == 1 +@pytest.mark.parametrize("exempt", [False, True]) +def test_settings_prometheus_metrics_ssl_redirect_exempt(monkeypatch, tmp_path, exempt): + """ + /metrics should leave the redirect to https only when asked to, for a scraper that + reaches the process past the proxy terminating TLS, and the probes should stay. + """ + monkeypatch.setenv("PROMETHEUS_MULTIPROC_DIR", str(tmp_path)) + test_settings = _prometheus_settings(PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT=exempt) + exemptions = test_settings.SECURE_REDIRECT_EXEMPT + + test_settings.post_setup() + # twice: the pattern must not be added a second time + test_settings.post_setup() + + # the very object Django was handed before post_setup ran + assert test_settings.SECURE_REDIRECT_EXEMPT is exemptions + expected = ["^__lbheartbeat__", "^__heartbeat__"] + assert exemptions == expected + (["^metrics$"] if exempt else []) + + def test_settings_prometheus_multiproc_dir_default(monkeypatch, tmp_path): """ Without a directory, a fixed one should be used: every uvicorn worker computes it on diff --git a/src/backend/impress/settings.py b/src/backend/impress/settings.py index 99f312e8d..bb78a41c2 100755 --- a/src/backend/impress/settings.py +++ b/src/backend/impress/settings.py @@ -79,6 +79,9 @@ class Base(Configuration): # Security ALLOWED_HOSTS = values.ListValue([]) + # Django's default, made explicit because setup_prometheus_metrics may add + # to it in place: Production redefines it with the probes + SECURE_REDIRECT_EXEMPT = [] SECRET_KEY = SecretFileValue(None) SERVER_TO_SERVER_API_TOKENS = values.ListValue([]) @@ -1264,6 +1267,17 @@ class Base(Configuration): PROMETHEUS_API_KEY = SecretFileValue( None, environ_name="PROMETHEUS_API_KEY", environ_prefix=None ) + # Let /metrics be scraped over plain http where SECURE_SSL_REDIRECT is on, + # i.e. take it out of the redirect to https the way the probes are. For a + # scraper that reaches the process itself, past the proxy terminating TLS + # — a Prometheus inside a kubernetes cluster calling the pods. Leave it off + # when the application is reached directly: the redirect is then what keeps + # the bearer token off the wire in clear. + PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT = values.BooleanValue( + False, + environ_name="PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT", + environ_prefix=None, + ) # Also count and time the SQL queries, by swapping the database engine for # django-prometheus' instrumented subclass of it. PROMETHEUS_DB_METRICS_ENABLED = values.BooleanValue( @@ -1371,6 +1385,16 @@ class Base(Configuration): ): default_database["ENGINE"] = "django_prometheus.db.backends.postgresql" + # in place, like the lists above: a reassignment would go unseen. The + # path is core.middleware.METRICS_PATH, not imported here: the settings + # are read before the applications are. + metrics_pattern = "^metrics$" + if ( + cls.PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT + and metrics_pattern not in cls.SECURE_REDIRECT_EXEMPT + ): + cls.SECURE_REDIRECT_EXEMPT.append(metrics_pattern) + @classmethod def post_setup(cls): """Post setup configuration. @@ -1613,6 +1637,9 @@ class Production(Base): SECURE_HSTS_PRELOAD = True SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_SSL_REDIRECT = True + # The probes are called on the process itself, over plain http: a redirect + # to https is a probe that fails. /metrics joins them only with + # PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT, see setup_prometheus_metrics SECURE_REDIRECT_EXEMPT = [ "^__lbheartbeat__", "^__heartbeat__", diff --git a/src/helm/env.d/dev/prometheus/django.html b/src/helm/env.d/dev/prometheus/django.html new file mode 100644 index 000000000..ac48b362f --- /dev/null +++ b/src/helm/env.d/dev/prometheus/django.html @@ -0,0 +1,201 @@ +{{/* + The example console of django-prometheus, from + https://github.com/django-commons/django-prometheus/blob/master/examples/prometheus/consoles/django.html + with two changes: the job is `backend` here (the ServiceMonitor names it after + the component), and the connection errors graph read a metric that does not + exist (`erros`). The graphs come from the recording rules of django.rules. +*/}} +{{template "head" .}} + +{{template "prom_right_table_head"}} + + Django + {{ template "prom_query_drilldown" (args "sum(up{job='backend'})") }} + / {{ template "prom_query_drilldown" (args "count(up{job='backend'})") }} + + + + avg CPU + {{ template "prom_query_drilldown" (args "avg by(job)(rate(process_cpu_seconds_total{job='backend'}[5m]))" "s/s" "humanizeNoSmallPrefix") }} + + + + avg Memory + {{ template "prom_query_drilldown" (args "avg by(job)(process_resident_memory_bytes{job='backend'})" "B" "humanize1024") }} + + +{{template "prom_right_table_tail"}} + + +{{template "prom_content_head" .}} +

Django

+ +

Requests

+

Total

+
+ + +

By view

+
+ + +

Latency (median)

+
+ + +

Latency (99.9th percentile)

+
+ + +

Models

+

Insertions/s

+
+ + +

Updates/s

+
+ + +

Deletions/s

+
+ + +

Database

+

Connections/s

+
+ + +

Connections errors/s

+
+ + +

Queries/s

+
+ + +

Errors/s

+
+ + +{{template "prom_content_tail" .}} + +{{template "tail"}} diff --git a/src/helm/env.d/dev/prometheus/django.rules b/src/helm/env.d/dev/prometheus/django.rules new file mode 100644 index 000000000..54209951e --- /dev/null +++ b/src/helm/env.d/dev/prometheus/django.rules @@ -0,0 +1,108 @@ +# The example recording rules of django-prometheus, verbatim, from +# https://github.com/django-commons/django-prometheus/blob/master/examples/prometheus/django.rules +# They are what the graphs of django.html read. +groups: +- name: django.rules + rules: + - record: job:django_http_requests_before_middlewares_total:sum_rate30s + expr: sum(rate(django_http_requests_before_middlewares_total[30s])) BY (job) + - record: job:django_http_requests_unknown_latency_total:sum_rate30s + expr: sum(rate(django_http_requests_unknown_latency_total[30s])) BY (job) + - record: job:django_http_ajax_requests_total:sum_rate30s + expr: sum(rate(django_http_ajax_requests_total[30s])) BY (job) + - record: job:django_http_responses_before_middlewares_total:sum_rate30s + expr: sum(rate(django_http_responses_before_middlewares_total[30s])) BY (job) + - record: job:django_http_requests_unknown_latency_including_middlewares_total:sum_rate30s + expr: sum(rate(django_http_requests_unknown_latency_including_middlewares_total[30s])) + BY (job) + - record: job:django_http_requests_body_total_bytes:sum_rate30s + expr: sum(rate(django_http_requests_body_total_bytes[30s])) BY (job) + - record: job:django_http_responses_streaming_total:sum_rate30s + expr: sum(rate(django_http_responses_streaming_total[30s])) BY (job) + - record: job:django_http_responses_body_total_bytes:sum_rate30s + expr: sum(rate(django_http_responses_body_total_bytes[30s])) BY (job) + - record: job:django_http_requests_total:sum_rate30s + expr: sum(rate(django_http_requests_total_by_method[30s])) BY (job) + - record: job:django_http_requests_total_by_method:sum_rate30s + expr: sum(rate(django_http_requests_total_by_method[30s])) BY (job, method) + - record: job:django_http_requests_total_by_transport:sum_rate30s + expr: sum(rate(django_http_requests_total_by_transport[30s])) BY (job, transport) + - record: job:django_http_requests_total_by_view:sum_rate30s + expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) BY (job, + view) + - record: job:django_http_requests_total_by_view_transport_method:sum_rate30s + expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) BY (job, + view, transport, method) + - record: job:django_http_responses_total_by_templatename:sum_rate30s + expr: sum(rate(django_http_responses_total_by_templatename[30s])) BY (job, templatename) + - record: job:django_http_responses_total_by_status:sum_rate30s + expr: sum(rate(django_http_responses_total_by_status[30s])) BY (job, status) + - record: job:django_http_responses_total_by_status_name_method:sum_rate30s + expr: sum(rate(django_http_responses_total_by_status_name_method[30s])) BY (job, + status, name, method) + - record: job:django_http_responses_total_by_charset:sum_rate30s + expr: sum(rate(django_http_responses_total_by_charset[30s])) BY (job, charset) + - record: job:django_http_exceptions_total_by_type:sum_rate30s + expr: sum(rate(django_http_exceptions_total_by_type[30s])) BY (job, type) + - record: job:django_http_exceptions_total_by_view:sum_rate30s + expr: sum(rate(django_http_exceptions_total_by_view[30s])) BY (job, view) + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.5, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "50" + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "95" + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "99" + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "99.9" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.5, sum(rate(django_http_requests_latency_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "50" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "95" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "99" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_seconds_bucket[30s])) + BY (job, le)) + labels: + quantile: "99.9" + - record: job:django_model_inserts_total:sum_rate1m + expr: sum(rate(django_model_inserts_total[1m])) BY (job, model) + - record: job:django_model_updates_total:sum_rate1m + expr: sum(rate(django_model_updates_total[1m])) BY (job, model) + - record: job:django_model_deletes_total:sum_rate1m + expr: sum(rate(django_model_deletes_total[1m])) BY (job, model) + - record: job:django_db_new_connections_total:sum_rate30s + expr: sum(rate(django_db_new_connections_total[30s])) BY (alias, vendor) + - record: job:django_db_new_connection_errors_total:sum_rate30s + expr: sum(rate(django_db_new_connection_errors_total[30s])) BY (alias, vendor) + - record: job:django_db_execute_total:sum_rate30s + expr: sum(rate(django_db_execute_total[30s])) BY (alias, vendor) + - record: job:django_db_execute_many_total:sum_rate30s + expr: sum(rate(django_db_execute_many_total[30s])) BY (alias, vendor) + - record: job:django_db_errors_total:sum_rate30s + expr: sum(rate(django_db_errors_total[30s])) BY (alias, vendor, type) + - record: job:django_migrations_applied_total:max + expr: max(django_migrations_applied_total) BY (job, connection) + - record: job:django_migrations_unapplied_total:max + expr: max(django_migrations_unapplied_total) BY (job, connection) diff --git a/src/helm/env.d/dev/values.impress.yaml.gotmpl b/src/helm/env.d/dev/values.impress.yaml.gotmpl index af89e7943..08cd3083e 100644 --- a/src/helm/env.d/dev/values.impress.yaml.gotmpl +++ b/src/helm/env.d/dev/values.impress.yaml.gotmpl @@ -14,7 +14,14 @@ image: backend: replicas: 1 + # scraped by the Prometheus of the cluster, see serviceMonitor below + metrics: + enabled: true envVars: + PROMETHEUS_API_KEY: + secretKeyRef: + name: docs-metrics + key: PROMETHEUS_API_KEY CONVERSION_UPLOAD_ENABLED: True DJANGO_CSRF_TRUSTED_ORIGINS: https://docs.127.0.0.1.nip.io DJANGO_CONFIGURATION: Feature @@ -203,6 +210,11 @@ yhub: enabled: true replicas: 2 + # scraped by the Prometheus of the cluster, server and worker apart, see + # serviceMonitor below + metrics: + enabled: true + image: repository: localhost:5001/impress-yhub pullPolicy: Always @@ -216,6 +228,10 @@ yhub: # valkey-docs REDIS: redis://default:pass@valkey-yhub:6379/0 REDIS_PREFIX: yhub + PROMETHEUS_API_KEY: + secretKeyRef: + name: docs-metrics + key: PROMETHEUS_API_KEY COLLABORATION_BACKEND_BASE_URL: https://docs.127.0.0.1.nip.io COLLABORATION_SERVER_ORIGIN: https://docs.127.0.0.1.nip.io NODE_EXTRA_CA_CERTS: /cert/cacert.pem @@ -295,3 +311,11 @@ ingressMedia: serviceMedia: host: dev-backend-minio.impress.svc.cluster.local port: 9000 + +# One ServiceMonitor per component whose metrics are enabled above — backend, +# yhub, yhub-worker — for the Prometheus of the prometheus release +# (values.prometheus.yaml.gotmpl), which selects every monitor of the +# namespace. The docs-metrics Secret the scrapes present is created there too. +serviceMonitor: + enabled: true + interval: 15s diff --git a/src/helm/env.d/dev/values.prometheus.yaml.gotmpl b/src/helm/env.d/dev/values.prometheus.yaml.gotmpl new file mode 100644 index 000000000..448a9243e --- /dev/null +++ b/src/helm/env.d/dev/values.prometheus.yaml.gotmpl @@ -0,0 +1,129 @@ +# A Prometheus scraping the dev cluster, to see the metrics of the backend and +# of yhub through the ServiceMonitors the impress chart builds. Trimmed down to +# the Prometheus Operator, its CRDs and one Prometheus: no Grafana, no +# Alertmanager, no exporter, and none of the monitors and rules of the +# kubernetes control plane, which a kind cluster has nothing to show for. +# UI: https://docs-prometheus.127.0.0.1.nip.io (Status > Targets lists the pods) +# +# It also serves the example console of django-prometheus, the graphs of +# https://docs-prometheus.127.0.0.1.nip.io/consoles/django.html, fed by its +# recording rules: both are in the prometheus/ directory next to this file. +# Console templates draw with the classic UI, which Prometheus 3 removed along +# with the console libraries, so this Prometheus is the last 2.x release. +fullnameOverride: prometheus + +defaultRules: + create: false +alertmanager: + enabled: false +grafana: + enabled: false +nodeExporter: + enabled: false +kubeStateMetrics: + enabled: false +kubernetesServiceMonitors: + enabled: false +kubeApiServer: + enabled: false +kubelet: + enabled: false +kubeControllerManager: + enabled: false +coreDns: + enabled: false +kubeDns: + enabled: false +kubeEtcd: + enabled: false +kubeScheduler: + enabled: false +kubeProxy: + enabled: false + +prometheusOperator: + # the webhooks validate the monitors and rules: not worth the certificate + # patch jobs in a dev cluster + admissionWebhooks: + enabled: false + tls: + enabled: false + kubeletService: + enabled: false + serviceMonitor: + selfMonitor: false + +prometheus: + serviceMonitor: + selfMonitor: false + prometheusSpec: + image: + tag: v2.55.1 + # the console, in the directory the operator points --web.console.templates + # at (/etc/prometheus/consoles is a symlink to it in the image). Only the + # console libraries of the image are left next to it, which is all it needs + volumes: + - name: django-console + configMap: + name: django-console + volumeMounts: + - name: django-console + mountPath: /usr/share/prometheus/consoles + readOnly: true + # every ServiceMonitor and PodMonitor of the namespace, whatever its labels + serviceMonitorSelectorNilUsesHelmValues: false + podMonitorSelectorNilUsesHelmValues: false + ruleSelectorNilUsesHelmValues: false + probeSelectorNilUsesHelmValues: false + scrapeConfigSelectorNilUsesHelmValues: false + scrapeInterval: 15s + retention: 2d + resources: + requests: + cpu: 50m + memory: 256Mi + limits: + memory: 1Gi + ingress: + enabled: true + hosts: + - docs-prometheus.127.0.0.1.nip.io + paths: + - / + pathType: Prefix + tls: + - secretName: docs-tls + hosts: + - docs-prometheus.127.0.0.1.nip.io + +# The bearer token the backend and yhub check on their metrics endpoints, and +# the Prometheus Operator reads to scrape them with (values.impress: the +# PROMETHEUS_API_KEY of both, and the monitors). A Secret of the release that +# needs it first, dev only: a real deployment keeps it with its other secrets. +extraManifests: + - apiVersion: v1 + kind: Secret + metadata: + name: docs-metrics + stringData: + PROMETHEUS_API_KEY: ThisIsAnExampleMetricsTokenForDevPurposeOnly + # The recording rules the console reads, selected like the monitors are + - apiVersion: monitoring.coreos.com/v1 + kind: PrometheusRule + metadata: + name: django + spec: + {{- readFile "prometheus/django.rules" | nindent 6 }} + # The console itself. The chart passes every extra manifest through helm's + # tpl, which would render the actions of the console template: each opening + # delimiter is escaped so that tpl writes it back as is (the closing one is + # spelled in two halves so that it does not end this action) + {{- $open := "{{" }} + {{- $escaped := printf "%s %q %s" $open $open (print "}" "}") }} + - apiVersion: v1 + kind: ConfigMap + metadata: + name: django-console + data: + django.html: | + {{- readFile "prometheus/django.html" | replace $open $escaped | nindent 8 }} diff --git a/src/helm/helmfile.yaml.gotmpl b/src/helm/helmfile.yaml.gotmpl index ff7210889..5cce13c57 100644 --- a/src/helm/helmfile.yaml.gotmpl +++ b/src/helm/helmfile.yaml.gotmpl @@ -1,7 +1,9 @@ environments: dev: values: - - version: 6.0.0-alpha.1 + - version: 6.0.0-alpha.2 + # a Prometheus scraping the metrics of the backend and of yhub + monitoring: true feature: values: - version: 6.0.0-alpha.1 @@ -14,6 +16,8 @@ repositories: url: https://suitenumerique.github.io/helm-dev-backend - name: valkey url: https://valkey.io/valkey-helm/ +- name: prometheus-community + url: https://prometheus-community.github.io/helm-charts --- releases: @@ -40,9 +44,24 @@ releases: values: - env.d/{{ .Environment.Name }}/values.valkey.yaml.gotmpl + # The Prometheus Operator and a Prometheus, in the environments that ask for + # it: what the ServiceMonitors of the impress release are for + - name: prometheus + namespace: {{ .Namespace }} + chart: prometheus-community/kube-prometheus-stack + version: 91.4.1 + installed: {{ .Values | get "monitoring" false }} + values: + - env.d/{{ .Environment.Name }}/values.prometheus.yaml.gotmpl + - name: impress version: {{ .Values.version }} namespace: {{ .Namespace }} chart: ./impress + {{- if .Values | get "monitoring" false }} + # its ServiceMonitors need the CRDs of the operator + needs: + - {{ .Namespace }}/prometheus + {{- end }} values: - env.d/{{ .Environment.Name }}/values.impress.yaml.gotmpl diff --git a/src/helm/impress/Chart.yaml b/src/helm/impress/Chart.yaml index d8d90dc66..31aea3643 100644 --- a/src/helm/impress/Chart.yaml +++ b/src/helm/impress/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 type: application name: docs -version: 6.0.0-alpha.1 +version: 6.0.0-alpha.2 appVersion: latest diff --git a/src/helm/impress/README.md b/src/helm/impress/README.md index e7bda10f1..57ca800eb 100644 --- a/src/helm/impress/README.md +++ b/src/helm/impress/README.md @@ -4,229 +4,250 @@ ### General configuration -| Name | Description | Value | -| ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| `image.repository` | Repository to use to pull impress's container image | `lasuite/impress-backend` | -| `image.tag` | impress's container tag | `latest` | -| `image.pullPolicy` | Container image pull policy | `IfNotPresent` | -| `image.credentials.username` | Username for container registry authentication | | -| `image.credentials.password` | Password for container registry authentication | | -| `image.credentials.registry` | Registry url for which the credentials are specified | | -| `image.credentials.name` | Name of the generated secret for imagePullSecrets | | -| `nameOverride` | Override the chart name | `""` | -| `fullnameOverride` | Override the full application name | `""` | -| `ingress.enabled` | whether to enable the Ingress or not | `false` | -| `ingress.className` | IngressClass to use for the Ingress | `nil` | -| `ingress.host` | Host for the Ingress | `impress.example.com` | -| `ingress.path` | Path to use for the Ingress | `/` | -| `ingress.hosts` | Additional host to configure for the Ingress | `[]` | -| `ingress.tls.enabled` | Whether to enable TLS for the Ingress | `true` | -| `ingress.tls.secretName` | Secret name for TLS config | `nil` | -| `ingress.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingress.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingress.customBackends` | Add custom backends to ingress | `[]` | -| `ingressCollaborationWS.enabled` | whether to enable the Ingress or not | `false` | -| `ingressCollaborationWS.className` | IngressClass to use for the Ingress | `nil` | -| `ingressCollaborationWS.host` | Host for the Ingress | `impress.example.com` | -| `ingressCollaborationWS.path` | Path to use for the Ingress | `/collaboration/ws/` | -| `ingressCollaborationWS.hosts` | Additional host to configure for the Ingress | `[]` | -| `ingressCollaborationWS.tls.enabled` | Whether to enable TLS for the Ingress | `true` | -| `ingressCollaborationWS.tls.secretName` | Secret name for TLS config | `nil` | -| `ingressCollaborationWS.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingressCollaborationWS.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingressCollaborationWS.customBackends` | Add custom backends to ingress | `[]` | -| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/enable-websocket` | | `true` | -| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout` | | `86400` | -| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout` | | `86400` | -| `ingressRedirects.enabled` | whether to enable the Ingress Redirects or not | `false` | -| `ingressRedirects.className` | IngressClass to use for the Ingress Redirects | `nil` | -| `ingressRedirects.host` | Host for the Ingress Redirects | `impress.example.com` | -| `ingressRedirects.tls.enabled` | Whether to enable TLS for the Ingress Redirects | `true` | -| `ingressRedirects.tls.secretName` | Secret name for TLS config | `nil` | -| `ingressRedirects.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingressRedirects.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingressRedirects.rules` | Rules for the Ingress Redirects | `[]` | -| `ingressCollaborationApi.enabled` | whether to enable the Ingress or not | `false` | -| `ingressCollaborationApi.className` | IngressClass to use for the Ingress | `nil` | -| `ingressCollaborationApi.host` | Host for the Ingress | `impress.example.com` | -| `ingressCollaborationApi.path` | Path to use for the Ingress | `/collaboration/api/` | -| `ingressCollaborationApi.paths` | Paths to route to the collaboration server, one rule each | `["/collaboration/ydoc/","/collaboration/jwks/"]` | -| `ingressCollaborationApi.hosts` | Additional host to configure for the Ingress | `[]` | -| `ingressCollaborationApi.tls.enabled` | Whether to enable TLS for the Ingress | `true` | -| `ingressCollaborationApi.tls.secretName` | Secret name for TLS config | `nil` | -| `ingressCollaborationApi.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingressCollaborationApi.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingressCollaborationApi.customBackends` | Add custom backends to ingress | `[]` | -| `ingressAdmin.enabled` | whether to enable the Ingress or not | `false` | -| `ingressAdmin.className` | IngressClass to use for the Ingress | `nil` | -| `ingressAdmin.host` | Host for the Ingress | `impress.example.com` | -| `ingressAdmin.path` | Path to use for the Ingress | `/admin` | -| `ingressAdmin.hosts` | Additional host to configure for the Ingress | `[]` | -| `ingressAdmin.tls.enabled` | Whether to enable TLS for the Ingress | `true` | -| `ingressAdmin.tls.secretName` | Secret name for TLS config | `nil` | -| `ingressAdmin.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingressAdmin.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingressMetrics.enabled` | whether to enable the Ingress publishing the Prometheus metrics of the backend (requires PROMETHEUS_METRICS_ENABLED and PROMETHEUS_API_KEY in backend.envVars) and, with yhub.metrics.enabled, those of yhub | `false` | -| `ingressMetrics.className` | IngressClass to use for the Ingress | `nil` | -| `ingressMetrics.host` | Host for the Ingress. Use a host of its own, and add it to DJANGO_ALLOWED_HOSTS | `metrics.impress.example.com` | -| `ingressMetrics.path` | Path of the backend metrics, matched exactly. The yhub ones are published at yhub.metrics.path and yhub.metrics.workerPath | `/metrics` | -| `ingressMetrics.hosts` | Additional host to configure for the Ingress | `[]` | -| `ingressMetrics.tls.enabled` | Whether to enable TLS for the Ingress | `true` | -| `ingressMetrics.tls.secretName` | Secret name for TLS config | `nil` | -| `ingressMetrics.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingressMetrics.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingressMetrics.annotations` | Annotations of the Ingress. Restrict the callers by address here | `{}` | -| `ingressMedia.enabled` | whether to enable the Ingress or not | `false` | -| `ingressMedia.className` | IngressClass to use for the Ingress | `nil` | -| `ingressMedia.host` | Host for the Ingress | `impress.example.com` | -| `ingressMedia.path` | Path to use for the Ingress | `/media/(.*)` | -| `ingressMedia.hosts` | Additional host to configure for the Ingress | `[]` | -| `ingressMedia.tls.enabled` | Whether to enable TLS for the Ingress | `true` | -| `ingressMedia.tls.secretName` | Secret name for TLS config | `nil` | -| `ingressMedia.tls.additional[].secretName` | Secret name for additional TLS config | | -| `ingressMedia.tls.additional[].hosts[]` | Hosts for additional TLS config | | -| `ingressMedia.annotations.nginx.ingress.kubernetes.io/auth-url` | | `https://impress.example.com/api/v1.0/documents/media-auth/` | -| `ingressMedia.annotations.nginx.ingress.kubernetes.io/auth-response-headers` | | `Authorization, X-Amz-Date, X-Amz-Content-SHA256` | -| `ingressMedia.annotations.nginx.ingress.kubernetes.io/upstream-vhost` | | `minio.impress.svc.cluster.local:9000` | -| `ingressMedia.annotations.nginx.ingress.kubernetes.io/configuration-snippet` | | `add_header Content-Security-Policy "default-src 'none'" always; -` | -| `serviceMedia.host` | Hostname of the media storage backend (S3/MinIO) used by the media ingress | `minio.impress.svc.cluster.local` | -| `serviceMedia.port` | Port of the media storage backend | `9000` | -| `serviceMedia.annotations` | Annotations to add to the media ExternalName Service | `{}` | +| Name | Description | Value | +| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `image.repository` | Repository to use to pull impress's container image | `lasuite/impress-backend` | +| `image.tag` | impress's container tag | `latest` | +| `image.pullPolicy` | Container image pull policy | `IfNotPresent` | +| `image.credentials.username` | Username for container registry authentication | | +| `image.credentials.password` | Password for container registry authentication | | +| `image.credentials.registry` | Registry url for which the credentials are specified | | +| `image.credentials.name` | Name of the generated secret for imagePullSecrets | | +| `nameOverride` | Override the chart name | `""` | +| `fullnameOverride` | Override the full application name | `""` | +| `ingress.enabled` | whether to enable the Ingress or not | `false` | +| `ingress.className` | IngressClass to use for the Ingress | `nil` | +| `ingress.host` | Host for the Ingress | `impress.example.com` | +| `ingress.path` | Path to use for the Ingress | `/` | +| `ingress.hosts` | Additional host to configure for the Ingress | `[]` | +| `ingress.tls.enabled` | Whether to enable TLS for the Ingress | `true` | +| `ingress.tls.secretName` | Secret name for TLS config | `nil` | +| `ingress.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingress.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingress.customBackends` | Add custom backends to ingress | `[]` | +| `ingressCollaborationWS.enabled` | whether to enable the Ingress or not | `false` | +| `ingressCollaborationWS.className` | IngressClass to use for the Ingress | `nil` | +| `ingressCollaborationWS.host` | Host for the Ingress | `impress.example.com` | +| `ingressCollaborationWS.path` | Path to use for the Ingress | `/collaboration/ws/` | +| `ingressCollaborationWS.hosts` | Additional host to configure for the Ingress | `[]` | +| `ingressCollaborationWS.tls.enabled` | Whether to enable TLS for the Ingress | `true` | +| `ingressCollaborationWS.tls.secretName` | Secret name for TLS config | `nil` | +| `ingressCollaborationWS.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingressCollaborationWS.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingressCollaborationWS.customBackends` | Add custom backends to ingress | `[]` | +| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/enable-websocket` | | `true` | +| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout` | | `86400` | +| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout` | | `86400` | +| `ingressRedirects.enabled` | whether to enable the Ingress Redirects or not | `false` | +| `ingressRedirects.className` | IngressClass to use for the Ingress Redirects | `nil` | +| `ingressRedirects.host` | Host for the Ingress Redirects | `impress.example.com` | +| `ingressRedirects.tls.enabled` | Whether to enable TLS for the Ingress Redirects | `true` | +| `ingressRedirects.tls.secretName` | Secret name for TLS config | `nil` | +| `ingressRedirects.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingressRedirects.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingressRedirects.rules` | Rules for the Ingress Redirects | `[]` | +| `ingressCollaborationApi.enabled` | whether to enable the Ingress or not | `false` | +| `ingressCollaborationApi.className` | IngressClass to use for the Ingress | `nil` | +| `ingressCollaborationApi.host` | Host for the Ingress | `impress.example.com` | +| `ingressCollaborationApi.path` | Path to use for the Ingress | `/collaboration/api/` | +| `ingressCollaborationApi.paths` | Paths to route to the collaboration server, one rule each | `["/collaboration/ydoc/","/collaboration/activity/","/collaboration/changeset/","/collaboration/rollback/","/collaboration/jwks/"]` | +| `ingressCollaborationApi.hosts` | Additional host to configure for the Ingress | `[]` | +| `ingressCollaborationApi.tls.enabled` | Whether to enable TLS for the Ingress | `true` | +| `ingressCollaborationApi.tls.secretName` | Secret name for TLS config | `nil` | +| `ingressCollaborationApi.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingressCollaborationApi.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingressCollaborationApi.customBackends` | Add custom backends to ingress | `[]` | +| `ingressAdmin.enabled` | whether to enable the Ingress or not | `false` | +| `ingressAdmin.className` | IngressClass to use for the Ingress | `nil` | +| `ingressAdmin.host` | Host for the Ingress | `impress.example.com` | +| `ingressAdmin.path` | Path to use for the Ingress | `/admin` | +| `ingressAdmin.hosts` | Additional host to configure for the Ingress | `[]` | +| `ingressAdmin.tls.enabled` | Whether to enable TLS for the Ingress | `true` | +| `ingressAdmin.tls.secretName` | Secret name for TLS config | `nil` | +| `ingressAdmin.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingressAdmin.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingressMetrics.enabled` | whether to enable the Ingress publishing the Prometheus metrics of the backend (backend.metrics.enabled) and of yhub (yhub.metrics.enabled) to a Prometheus outside of the cluster. One inside it scrapes the pods directly with serviceMonitor or podMonitor instead | `false` | +| `ingressMetrics.className` | IngressClass to use for the Ingress | `nil` | +| `ingressMetrics.host` | Host for the Ingress. Use a host of its own, and add it to DJANGO_ALLOWED_HOSTS | `metrics.impress.example.com` | +| `ingressMetrics.path` | Path of the backend metrics, matched exactly. The yhub ones are published at yhub.metrics.path and yhub.metrics.workerPath | `/metrics` | +| `ingressMetrics.hosts` | Additional host to configure for the Ingress | `[]` | +| `ingressMetrics.tls.enabled` | Whether to enable TLS for the Ingress | `true` | +| `ingressMetrics.tls.secretName` | Secret name for TLS config | `nil` | +| `ingressMetrics.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingressMetrics.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingressMetrics.annotations` | Annotations of the Ingress. Restrict the callers by address here | `{}` | +| `serviceMonitor.enabled` | Create a ServiceMonitor for each component whose metrics are enabled | `false` | +| `serviceMonitor.namespace` | Namespace the ServiceMonitors are created in, the release one when empty | `""` | +| `serviceMonitor.labels` | Labels of the ServiceMonitors, which the Prometheus Operator selects them by (e.g. release: kube-prometheus-stack) | `{}` | +| `serviceMonitor.annotations` | Annotations of the ServiceMonitors | `{}` | +| `serviceMonitor.interval` | Scrape interval, the Prometheus default when empty | `""` | +| `serviceMonitor.scrapeTimeout` | Scrape timeout, the Prometheus default when empty | `""` | +| `serviceMonitor.honorLabels` | Keep the labels of the scraped samples when they clash with the target ones | `false` | +| `serviceMonitor.relabelings` | Relabelings of the targets, before the scrape | `[]` | +| `serviceMonitor.metricRelabelings` | Relabelings of the samples, after the scrape | `[]` | +| `podMonitor.enabled` | Create a PodMonitor for each component whose metrics are enabled | `false` | +| `podMonitor.namespace` | Namespace the PodMonitors are created in, the release one when empty | `""` | +| `podMonitor.labels` | Labels of the PodMonitors, which the Prometheus Operator selects them by (e.g. release: kube-prometheus-stack) | `{}` | +| `podMonitor.annotations` | Annotations of the PodMonitors | `{}` | +| `podMonitor.interval` | Scrape interval, the Prometheus default when empty | `""` | +| `podMonitor.scrapeTimeout` | Scrape timeout, the Prometheus default when empty | `""` | +| `podMonitor.honorLabels` | Keep the labels of the scraped samples when they clash with the target ones | `false` | +| `podMonitor.relabelings` | Relabelings of the targets, before the scrape | `[]` | +| `podMonitor.metricRelabelings` | Relabelings of the samples, after the scrape | `[]` | +| `ingressMedia.enabled` | whether to enable the Ingress or not | `false` | +| `ingressMedia.className` | IngressClass to use for the Ingress | `nil` | +| `ingressMedia.host` | Host for the Ingress | `impress.example.com` | +| `ingressMedia.path` | Path to use for the Ingress | `/media/(.*)` | +| `ingressMedia.hosts` | Additional host to configure for the Ingress | `[]` | +| `ingressMedia.tls.enabled` | Whether to enable TLS for the Ingress | `true` | +| `ingressMedia.tls.secretName` | Secret name for TLS config | `nil` | +| `ingressMedia.tls.additional[].secretName` | Secret name for additional TLS config | | +| `ingressMedia.tls.additional[].hosts[]` | Hosts for additional TLS config | | +| `ingressMedia.annotations.nginx.ingress.kubernetes.io/auth-url` | | `https://impress.example.com/api/v1.0/documents/media-auth/` | +| `ingressMedia.annotations.nginx.ingress.kubernetes.io/auth-response-headers` | | `Authorization, X-Amz-Date, X-Amz-Content-SHA256` | +| `ingressMedia.annotations.nginx.ingress.kubernetes.io/upstream-vhost` | | `minio.impress.svc.cluster.local:9000` | +| `ingressMedia.annotations.nginx.ingress.kubernetes.io/configuration-snippet` | | `add_header Content-Security-Policy "default-src 'none'" always; +` | +| `serviceMedia.host` | Hostname of the media storage backend (S3/MinIO) used by the media ingress | `minio.impress.svc.cluster.local` | +| `serviceMedia.port` | Port of the media storage backend | `9000` | +| `serviceMedia.annotations` | Annotations to add to the media ExternalName Service | `{}` | ### backend -| Name | Description | Value | -| ----------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `backend.command` | Override the backend container command | `[]` | -| `backend.args` | Override the backend container args | `[]` | -| `backend.replicas` | Amount of backend replicas | `3` | -| `backend.shareProcessNamespace` | Enable share process namespace between containers | `false` | -| `backend.sidecars` | Add sidecars containers to backend deployment | `[]` | -| `backend.migrateJobAnnotations` | Annotations for the migrate job | `{}` | -| `backend.jobs.ttlSecondsAfterFinished` | Period to wait before remove jobs | `30` | -| `backend.jobs.backoffLimit` | Numbers of jobs retries | `2` | -| `backend.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the backend container | `false` | -| `backend.securityContext.capabilities.drop` | List of capabilities to drop for the backend container | `["ALL"]` | -| `backend.securityContext.runAsNonRoot` | Whether to run the backend container as a non-root user | `true` | -| `backend.securityContext.seccompProfile.type` | Seccomp profile type for the backend container | `RuntimeDefault` | -| `backend.envVars` | Configure backend container environment variables | `undefined` | -| `backend.envVars.BY_VALUE` | Example environment variable by setting value directly | | -| `backend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | | -| `backend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | | -| `backend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | | -| `backend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | | -| `backend.django.envVars` | Backend web deployment specific environment variables (not shared with celery worker) | | -| `backend.podAnnotations` | Annotations to add to the backend Pod | `{}` | -| `backend.dpAnnotations` | Annotations to add to the backend Deployment | `{}` | -| `backend.service.type` | backend Service type | `ClusterIP` | -| `backend.service.port` | backend Service listening port | `80` | -| `backend.service.targetPort` | backend container listening port | `8000` | -| `backend.service.annotations` | Annotations to add to the backend Service | `{}` | -| `backend.migrate.command` | backend migrate command | `["/bin/sh","-c","attempt=0\nuntil output=$(python manage.py check --database default 2>&1)\ndo\n attempt=$((attempt + 1))\n echo \"Database check failed (attempt $attempt), retrying in 2s:\"\n echo \"$output\"\n sleep 2\ndone\n\necho \"Database is ready\"\n\npython manage.py migrate --no-input\n"]` | -| `backend.migrate.restartPolicy` | backend migrate job restart policy | `Never` | -| `backend.createsuperuser.command` | backend migrate command | `["/bin/sh","-c","attempt=0\nuntil output=$(python manage.py check --database default 2>&1)\ndo\n attempt=$((attempt + 1))\n echo \"Database check failed (attempt $attempt), retrying in 2s:\"\n echo \"$output\"\n sleep 2\ndone\n\necho \"Database is ready\"\n\npython manage.py createsuperuser --email $DJANGO_SUPERUSER_EMAIL --password $DJANGO_SUPERUSER_PASSWORD\n"]` | -| `backend.createsuperuser.restartPolicy` | backend migrate job restart policy | `Never` | -| `backend.job` | job dedicated to run a random management command, for example after a deployment | | -| `backend.job.name` | The name to use to describe this job | `""` | -| `backend.job.command` | The management command to execute | `[]` | -| `backend.job.restartPolicy` | The restart policy for the job. | `Never` | -| `backend.job.annotations` | Annotations to add to the job [default: argocd.argoproj.io/hook: PostSync] | | -| `backend.cronjobs` | Cronjob name, schedule, command | `[]` | -| `backend.probes.liveness.path` | Configure path for backend HTTP liveness probe | `/__lbheartbeat__` | -| `backend.probes.liveness.targetPort` | Configure port for backend HTTP liveness probe | `nil` | -| `backend.probes.liveness.initialDelaySeconds` | Configure initial delay for backend liveness probe | `10` | -| `backend.probes.liveness.timeoutSeconds` | Configure timeout for backend liveness probe | `nil` | -| `backend.probes.startup.path` | Configure path for backend HTTP startup probe | | -| `backend.probes.startup.targetPort` | Configure port for backend HTTP startup probe | | -| `backend.probes.startup.initialDelaySeconds` | Configure initial delay for backend startup probe | | -| `backend.probes.startup.timeoutSeconds` | Configure timeout for backend startup probe | | -| `backend.probes.readiness.path` | Configure path for backend HTTP readiness probe | `/__heartbeat__` | -| `backend.probes.readiness.targetPort` | Configure port for backend HTTP readiness probe | `nil` | -| `backend.probes.readiness.initialDelaySeconds` | Configure initial delay for backend readiness probe | `10` | -| `backend.probes.readiness.timeoutSeconds` | Configure timeout for backend readiness probe | `nil` | -| `backend.resources` | Resource requirements for the backend container | `{}` | -| `backend.nodeSelector` | Node selector for the backend Pod | `{}` | -| `backend.tolerations` | Tolerations for the backend Pod | `[]` | -| `backend.affinity` | Affinity for the backend Pod | `{}` | -| `backend.persistence` | Additional volumes to create and mount on the backend. Used for debugging purposes | `{}` | -| `backend.persistence.volume-name.size` | Size of the additional volume | | -| `backend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | | -| `backend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | | -| `backend.extraVolumeMounts` | Additional volumes to mount on the backend. | `[]` | -| `backend.extraVolumes` | Additional volumes to mount on the backend. | `[]` | -| `backend.pdb.enabled` | Enable pdb on backend | `true` | -| `backend.serviceAccountName` | Optional service account name to use for backend pods | `nil` | -| `backend.themeCustomization.enabled` | Enable theme customization | `false` | -| `backend.themeCustomization.file_content` | Content of the theme customization file. Must be a json object. | `""` | -| `backend.themeCustomization.mount_path` | Path where the customization file will be mounted in the backend deployment. | `/app/impress/configuration/theme` | -| `backend.celery.replicas` | Amount of celery replicas | `1` | -| `backend.celery.command` | Override the celery container command | `[]` | -| `backend.celery.args` | Override the celery container args | `["celery","-A","impress.celery_app","worker","-l","INFO","-n","impress@%h"]` | -| `backend.celery.resources` | Resource requirements for the celery container | `{}` | -| `backend.celery.probes.liveness.exec.command` | Override the celery container liveness probe command | `["/bin/sh","-c","celery -A impress.celery_app inspect ping -d impress@$HOSTNAME"]` | -| `backend.celery.probes.liveness.initialDelaySeconds` | Initial delay for the celery container liveness probe | `60` | -| `backend.celery.probes.liveness.timeoutSeconds` | Timeout for the celery container liveness probe | `5` | -| `backend.celery.probes.readiness.exec.command` | Override the celery container readiness probe command | `["/bin/sh","-c","celery -A impress.celery_app inspect ping -d impress@$HOSTNAME"]` | -| `backend.celery.probes.readiness.initialDelaySeconds` | Initial delay for the celery container readiness probe | `15` | -| `backend.celery.probes.readiness.timeoutSeconds` | Timeout for the celery container readiness probe | `5` | -| `backend.celery.envVars` | Celery worker specific environment variables (not shared with the backend web deployment) | | +| Name | Description | Value | +| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `backend.command` | Override the backend container command | `[]` | +| `backend.args` | Override the backend container args | `[]` | +| `backend.replicas` | Amount of backend replicas | `3` | +| `backend.shareProcessNamespace` | Enable share process namespace between containers | `false` | +| `backend.sidecars` | Add sidecars containers to backend deployment | `[]` | +| `backend.migrateJobAnnotations` | Annotations for the migrate job | `{}` | +| `backend.jobs.ttlSecondsAfterFinished` | Period to wait before remove jobs | `30` | +| `backend.jobs.backoffLimit` | Numbers of jobs retries | `2` | +| `backend.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the backend container | `false` | +| `backend.securityContext.capabilities.drop` | List of capabilities to drop for the backend container | `["ALL"]` | +| `backend.securityContext.runAsNonRoot` | Whether to run the backend container as a non-root user | `true` | +| `backend.securityContext.seccompProfile.type` | Seccomp profile type for the backend container | `RuntimeDefault` | +| `backend.envVars` | Configure backend container environment variables | `undefined` | +| `backend.envVars.BY_VALUE` | Example environment variable by setting value directly | | +| `backend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | | +| `backend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | | +| `backend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | | +| `backend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | | +| `backend.django.envVars` | Backend web deployment specific environment variables (not shared with celery worker) | | +| `backend.metrics.enabled` | Serve the prometheus metrics of the backend on /metrics of the web pods, scraped over plain http inside the cluster. Sets PROMETHEUS_METRICS_ENABLED and PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT on the django container, and not on celery, unless backend.envVars sets them. Requires PROMETHEUS_API_KEY in backend.envVars | `false` | +| `backend.metrics.apiKeySecret.name` | Secret the monitors read the bearer token from. Defaults to the secretKeyRef of backend.envVars.PROMETHEUS_API_KEY | `""` | +| `backend.metrics.apiKeySecret.key` | Key of the bearer token in that Secret | `""` | +| `backend.podAnnotations` | Annotations to add to the backend Pod | `{}` | +| `backend.dpAnnotations` | Annotations to add to the backend Deployment | `{}` | +| `backend.service.type` | backend Service type | `ClusterIP` | +| `backend.service.port` | backend Service listening port | `80` | +| `backend.service.targetPort` | backend container listening port | `8000` | +| `backend.service.annotations` | Annotations to add to the backend Service | `{}` | +| `backend.migrate.command` | backend migrate command | `["/bin/sh","-c","attempt=0\nuntil output=$(python manage.py check --database default 2>&1)\ndo\n attempt=$((attempt + 1))\n echo \"Database check failed (attempt $attempt), retrying in 2s:\"\n echo \"$output\"\n sleep 2\ndone\n\necho \"Database is ready\"\n\npython manage.py migrate --no-input\n"]` | +| `backend.migrate.restartPolicy` | backend migrate job restart policy | `Never` | +| `backend.createsuperuser.command` | backend migrate command | `["/bin/sh","-c","attempt=0\nuntil output=$(python manage.py check --database default 2>&1)\ndo\n attempt=$((attempt + 1))\n echo \"Database check failed (attempt $attempt), retrying in 2s:\"\n echo \"$output\"\n sleep 2\ndone\n\necho \"Database is ready\"\n\npython manage.py createsuperuser --email $DJANGO_SUPERUSER_EMAIL --password $DJANGO_SUPERUSER_PASSWORD\n"]` | +| `backend.createsuperuser.restartPolicy` | backend migrate job restart policy | `Never` | +| `backend.job` | job dedicated to run a random management command, for example after a deployment | | +| `backend.job.name` | The name to use to describe this job | `""` | +| `backend.job.command` | The management command to execute | `[]` | +| `backend.job.restartPolicy` | The restart policy for the job. | `Never` | +| `backend.job.annotations` | Annotations to add to the job [default: argocd.argoproj.io/hook: PostSync] | | +| `backend.cronjobs` | Cronjob name, schedule, command | `[]` | +| `backend.probes.liveness.path` | Configure path for backend HTTP liveness probe | `/__lbheartbeat__` | +| `backend.probes.liveness.targetPort` | Configure port for backend HTTP liveness probe | `nil` | +| `backend.probes.liveness.initialDelaySeconds` | Configure initial delay for backend liveness probe | `10` | +| `backend.probes.liveness.timeoutSeconds` | Configure timeout for backend liveness probe | `nil` | +| `backend.probes.startup.path` | Configure path for backend HTTP startup probe | | +| `backend.probes.startup.targetPort` | Configure port for backend HTTP startup probe | | +| `backend.probes.startup.initialDelaySeconds` | Configure initial delay for backend startup probe | | +| `backend.probes.startup.timeoutSeconds` | Configure timeout for backend startup probe | | +| `backend.probes.readiness.path` | Configure path for backend HTTP readiness probe | `/__heartbeat__` | +| `backend.probes.readiness.targetPort` | Configure port for backend HTTP readiness probe | `nil` | +| `backend.probes.readiness.initialDelaySeconds` | Configure initial delay for backend readiness probe | `10` | +| `backend.probes.readiness.timeoutSeconds` | Configure timeout for backend readiness probe | `nil` | +| `backend.resources` | Resource requirements for the backend container | `{}` | +| `backend.nodeSelector` | Node selector for the backend Pod | `{}` | +| `backend.tolerations` | Tolerations for the backend Pod | `[]` | +| `backend.affinity` | Affinity for the backend Pod | `{}` | +| `backend.persistence` | Additional volumes to create and mount on the backend. Used for debugging purposes | `{}` | +| `backend.persistence.volume-name.size` | Size of the additional volume | | +| `backend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | | +| `backend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | | +| `backend.extraVolumeMounts` | Additional volumes to mount on the backend. | `[]` | +| `backend.extraVolumes` | Additional volumes to mount on the backend. | `[]` | +| `backend.pdb.enabled` | Enable pdb on backend | `true` | +| `backend.serviceAccountName` | Optional service account name to use for backend pods | `nil` | +| `backend.themeCustomization.enabled` | Enable theme customization | `false` | +| `backend.themeCustomization.file_content` | Content of the theme customization file. Must be a json object. | `""` | +| `backend.themeCustomization.mount_path` | Path where the customization file will be mounted in the backend deployment. | `/app/impress/configuration/theme` | +| `backend.celery.replicas` | Amount of celery replicas | `1` | +| `backend.celery.command` | Override the celery container command | `[]` | +| `backend.celery.args` | Override the celery container args | `["celery","-A","impress.celery_app","worker","-l","INFO","-n","impress@%h"]` | +| `backend.celery.resources` | Resource requirements for the celery container | `{}` | +| `backend.celery.probes.liveness.exec.command` | Override the celery container liveness probe command | `["/bin/sh","-c","celery -A impress.celery_app inspect ping -d impress@$HOSTNAME"]` | +| `backend.celery.probes.liveness.initialDelaySeconds` | Initial delay for the celery container liveness probe | `60` | +| `backend.celery.probes.liveness.timeoutSeconds` | Timeout for the celery container liveness probe | `5` | +| `backend.celery.probes.readiness.exec.command` | Override the celery container readiness probe command | `["/bin/sh","-c","celery -A impress.celery_app inspect ping -d impress@$HOSTNAME"]` | +| `backend.celery.probes.readiness.initialDelaySeconds` | Initial delay for the celery container readiness probe | `15` | +| `backend.celery.probes.readiness.timeoutSeconds` | Timeout for the celery container readiness probe | `5` | +| `backend.celery.envVars` | Celery worker specific environment variables (not shared with the backend web deployment) | | ### frontend -| Name | Description | Value | -| ------------------------------------------------------ | ----------------------------------------------------------------------------------- | -------------------------- | -| `frontend.image.repository` | Repository to use to pull impress's frontend container image | `lasuite/impress-frontend` | -| `frontend.image.tag` | impress's frontend container tag | `latest` | -| `frontend.image.pullPolicy` | frontend container image pull policy | `IfNotPresent` | -| `frontend.command` | Override the frontend container command | `[]` | -| `frontend.args` | Override the frontend container args | `[]` | -| `frontend.replicas` | Amount of frontend replicas | `3` | -| `frontend.shareProcessNamespace` | Enable share process namespace between containers | `false` | -| `frontend.sidecars` | Add sidecars containers to frontend deployment | `[]` | -| `frontend.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the frontend container | `false` | -| `frontend.securityContext.capabilities.drop` | List of capabilities to drop for the frontend container | `["ALL"]` | -| `frontend.securityContext.runAsNonRoot` | Whether to run the frontend container as a non-root user | `true` | -| `frontend.securityContext.seccompProfile.type` | Seccomp profile type for the frontend container | `RuntimeDefault` | -| `frontend.envVars` | Configure frontend container environment variables | `undefined` | -| `frontend.envVars.BY_VALUE` | Example environment variable by setting value directly | | -| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | | -| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | | -| `frontend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | | -| `frontend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | | -| `frontend.podAnnotations` | Annotations to add to the frontend Pod | `{}` | -| `frontend.dpAnnotations` | Annotations to add to the frontend Deployment | `{}` | -| `frontend.service.type` | frontend Service type | `ClusterIP` | -| `frontend.service.port` | frontend Service listening port | `80` | -| `frontend.service.targetPort` | frontend container listening port | `8080` | -| `frontend.service.annotations` | Annotations to add to the frontend Service | `{}` | -| `frontend.probes` | Configure probe for frontend | `{}` | -| `frontend.probes.liveness.path` | Configure path for frontend HTTP liveness probe | | -| `frontend.probes.liveness.targetPort` | Configure port for frontend HTTP liveness probe | | -| `frontend.probes.liveness.initialDelaySeconds` | Configure initial delay for frontend liveness probe | | -| `frontend.probes.liveness.initialDelaySeconds` | Configure timeout for frontend liveness probe | | -| `frontend.probes.startup.path` | Configure path for frontend HTTP startup probe | | -| `frontend.probes.startup.targetPort` | Configure port for frontend HTTP startup probe | | -| `frontend.probes.startup.initialDelaySeconds` | Configure initial delay for frontend startup probe | | -| `frontend.probes.startup.initialDelaySeconds` | Configure timeout for frontend startup probe | | -| `frontend.probes.readiness.path` | Configure path for frontend HTTP readiness probe | | -| `frontend.probes.readiness.targetPort` | Configure port for frontend HTTP readiness probe | | -| `frontend.probes.readiness.initialDelaySeconds` | Configure initial delay for frontend readiness probe | | -| `frontend.probes.readiness.initialDelaySeconds` | Configure timeout for frontend readiness probe | | -| `frontend.resources` | Resource requirements for the frontend container | `{}` | -| `frontend.nodeSelector` | Node selector for the frontend Pod | `{}` | -| `frontend.tolerations` | Tolerations for the frontend Pod | `[]` | -| `frontend.affinity` | Affinity for the frontend Pod | `{}` | -| `frontend.persistence` | Additional volumes to create and mount on the frontend. Used for debugging purposes | `{}` | -| `frontend.persistence.volume-name.size` | Size of the additional volume | | -| `frontend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | | -| `frontend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | | -| `frontend.extraVolumeMounts` | Additional volumes to mount on the frontend. | `[]` | -| `frontend.extraVolumes` | Additional volumes to mount on the frontend. | `[]` | -| `frontend.pdb.enabled` | Enable pdb on frontend | `true` | -| `frontend.serviceAccountName` | Optional service account name to use for frontend pods | `nil` | -| `frontend.robotsTxt.enabled` | Enable serving a custom robots.txt file from the frontend, e.g. to disallow indexing of non-production instances | `false` | -| `frontend.robotsTxt.content` | Content of the robots.txt file | `"User-agent: *\nAllow: /\n"` | +| Name | Description | Value | +| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | -------------------------- | +| `frontend.image.repository` | Repository to use to pull impress's frontend container image | `lasuite/impress-frontend` | +| `frontend.image.tag` | impress's frontend container tag | `latest` | +| `frontend.image.pullPolicy` | frontend container image pull policy | `IfNotPresent` | +| `frontend.command` | Override the frontend container command | `[]` | +| `frontend.args` | Override the frontend container args | `[]` | +| `frontend.replicas` | Amount of frontend replicas | `3` | +| `frontend.shareProcessNamespace` | Enable share process namespace between containers | `false` | +| `frontend.sidecars` | Add sidecars containers to frontend deployment | `[]` | +| `frontend.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the frontend container | `false` | +| `frontend.securityContext.capabilities.drop` | List of capabilities to drop for the frontend container | `["ALL"]` | +| `frontend.securityContext.runAsNonRoot` | Whether to run the frontend container as a non-root user | `true` | +| `frontend.securityContext.seccompProfile.type` | Seccomp profile type for the frontend container | `RuntimeDefault` | +| `frontend.envVars` | Configure frontend container environment variables | `undefined` | +| `frontend.envVars.BY_VALUE` | Example environment variable by setting value directly | | +| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | | +| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | | +| `frontend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | | +| `frontend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | | +| `frontend.podAnnotations` | Annotations to add to the frontend Pod | `{}` | +| `frontend.dpAnnotations` | Annotations to add to the frontend Deployment | `{}` | +| `frontend.service.type` | frontend Service type | `ClusterIP` | +| `frontend.service.port` | frontend Service listening port | `80` | +| `frontend.service.targetPort` | frontend container listening port | `8080` | +| `frontend.service.annotations` | Annotations to add to the frontend Service | `{}` | +| `frontend.probes` | Configure probe for frontend | `{}` | +| `frontend.probes.liveness.path` | Configure path for frontend HTTP liveness probe | | +| `frontend.probes.liveness.targetPort` | Configure port for frontend HTTP liveness probe | | +| `frontend.probes.liveness.initialDelaySeconds` | Configure initial delay for frontend liveness probe | | +| `frontend.probes.liveness.initialDelaySeconds` | Configure timeout for frontend liveness probe | | +| `frontend.probes.startup.path` | Configure path for frontend HTTP startup probe | | +| `frontend.probes.startup.targetPort` | Configure port for frontend HTTP startup probe | | +| `frontend.probes.startup.initialDelaySeconds` | Configure initial delay for frontend startup probe | | +| `frontend.probes.startup.initialDelaySeconds` | Configure timeout for frontend startup probe | | +| `frontend.probes.readiness.path` | Configure path for frontend HTTP readiness probe | | +| `frontend.probes.readiness.targetPort` | Configure port for frontend HTTP readiness probe | | +| `frontend.probes.readiness.initialDelaySeconds` | Configure initial delay for frontend readiness probe | | +| `frontend.probes.readiness.initialDelaySeconds` | Configure timeout for frontend readiness probe | | +| `frontend.resources` | Resource requirements for the frontend container | `{}` | +| `frontend.nodeSelector` | Node selector for the frontend Pod | `{}` | +| `frontend.tolerations` | Tolerations for the frontend Pod | `[]` | +| `frontend.affinity` | Affinity for the frontend Pod | `{}` | +| `frontend.persistence` | Additional volumes to create and mount on the frontend. Used for debugging purposes | `{}` | +| `frontend.persistence.volume-name.size` | Size of the additional volume | | +| `frontend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | | +| `frontend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | | +| `frontend.extraVolumeMounts` | Additional volumes to mount on the frontend. | `[]` | +| `frontend.extraVolumes` | Additional volumes to mount on the frontend. | `[]` | +| `frontend.pdb.enabled` | Enable pdb on frontend | `true` | +| `frontend.serviceAccountName` | Optional service account name to use for frontend pods | `nil` | +| `frontend.robotsTxt.enabled` | Enable serving a custom robots.txt file from the frontend, e.g. to disallow indexing of non-production instances | `false` | +| `frontend.robotsTxt.content` | Content of the robots.txt file | `"User-agent: *\nAllow: /\n"`| ### posthog @@ -351,129 +372,131 @@ ### yhub -| Name | Description | Value | -| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | -| `yhub.enabled` | Enable the yhub collaboration server, its service and its init-db job | `true` | -| `yhub.image.repository` | Repository to use to pull the yhub container image | `lasuite/impress-yhub` | -| `yhub.image.tag` | yhub container tag | `latest` | -| `yhub.image.pullPolicy` | yhub container image pull policy | `IfNotPresent` | -| `yhub.command` | Override the yhub container command | `[]` | -| `yhub.args` | Override the yhub container args | `[]` | -| `yhub.replicas` | Amount of yhub replicas | `3` | -| `yhub.metrics.enabled` | Serve the prometheus metrics of yhub on a port of their own, in the server and in the worker. Requires PROMETHEUS_API_KEY in yhub.envVars | `false` | -| `yhub.metrics.port` | Port the metrics are served on. Added to the yhub Service, and to a Service of its own for the worker | `9464` | -| `yhub.metrics.path` | Path the server serves its metrics on, which is the path ingressMetrics publishes them at | `/metrics/yhub` | -| `yhub.metrics.workerPath` | Path the worker serves its metrics on, which is the path ingressMetrics publishes them at | `/metrics/yhub-worker` | -| `yhub.worker.enabled` | Deploy the worker apart from the server, each scaling on its own | `false` | -| `yhub.worker.replicas` | Amount of yhub worker replicas | `1` | -| `yhub.worker.resources` | Resource requirements for the yhub worker container, the server ones when empty | `{}` | -| `yhub.worker.podAnnotations` | Annotations to add to the yhub worker Pod, the server ones when empty | `{}` | -| `yhub.worker.dpAnnotations` | Annotations to add to the yhub worker Deployment, the server ones when empty | `{}` | -| `yhub.worker.nodeSelector` | Node selector for the yhub worker Pod, the server one when empty | `{}` | -| `yhub.worker.tolerations` | Tolerations for the yhub worker Pod, the server ones when empty | `[]` | -| `yhub.worker.affinity` | Affinity for the yhub worker Pod, the server one when empty | `{}` | -| `yhub.worker.terminationGracePeriodSeconds` | Grace period given to a worker pod to finish its task, the server one when empty | `nil` | -| `yhub.worker.pdb.enabled` | Enable pdb on the yhub worker | `true` | -| `yhub.shareProcessNamespace` | Enable share process namespace between containers | `false` | -| `yhub.sidecars` | Add sidecars containers to yhub deployment | `[]` | -| `yhub.terminationGracePeriodSeconds` | Grace period given to a yhub pod to drain before it is killed | `60` | -| `yhub.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the yhub container | `false` | -| `yhub.securityContext.capabilities.drop` | List of capabilities to drop for the yhub container | `["ALL"]` | -| `yhub.securityContext.runAsNonRoot` | Whether to run the yhub container as a non-root user | `true` | -| `yhub.securityContext.runAsUser` | User the yhub container runs as | `1000` | -| `yhub.securityContext.runAsGroup` | Group the yhub container runs as | `1000` | -| `yhub.securityContext.seccompProfile.type` | Seccomp profile type for the yhub container | `RuntimeDefault` | -| `yhub.envVars` | Configure yhub container environment variables | `undefined` | -| `yhub.envVars.REDIS` | Required, redis/valkey url holding the live document state (e.g. redis://valkey:6379/0) | | -| `yhub.envVars.POSTGRES` | Required, url of the yhub database, created by the init-db job (e.g. postgres://user:pass@postgres:5432/yhub) | | -| `yhub.envVars.REDIS_PREFIX` | Namespace of the redis keys, when the instance is shared (default: yhub) | | -| `yhub.envVars.COLLABORATION_BACKEND_BASE_URL` | Base url of the Docs backend, which yhub asks about users and document access rights | | -| `yhub.envVars.COLLABORATION_SERVER_ORIGIN` | Comma separated list of the origins allowed to open a websocket | | -| `yhub.envVars.YHUB_JWT_PRIVATE_KEY_FILE` | Path to the RSA private key (PEM) yhub signs its calls to the backend with, mounted from a secret | | -| `yhub.envVars.YHUB_TASK_CONCURRENCY` | Tasks one worker process claims at once, times the replicas running a worker (default: 5) | | -| `yhub.envVars.YHUB_TASK_DEBOUNCE_MS` | How long an update waits on the redis stream before a worker persists it, in ms (default: 10000) | | -| `yhub.envVars.YHUB_MIN_MESSAGE_LIFETIME_MS` | How long persisted updates stay replayable from redis, in ms (default: 60000) | | -| `yhub.envVars.SOFT_MIGRATION` | Set to "true" to seed rooms from the legacy Django/S3 document store on first access | | -| `yhub.envVars.LEGACY_S3_ENDPOINT_URL` | Required by SOFT_MIGRATION, endpoint of the legacy Django media bucket, without a path (e.g. https://s3.example.com) | | -| `yhub.envVars.LEGACY_S3_ACCESS_KEY_ID` | Required by SOFT_MIGRATION, read access to the legacy bucket (or LEGACY_S3_ACCESS_KEY_ID_FILE) | | -| `yhub.envVars.LEGACY_S3_SECRET_ACCESS_KEY` | Required by SOFT_MIGRATION, secret of the key above (or LEGACY_S3_SECRET_ACCESS_KEY_FILE) | | -| `yhub.envVars.LEGACY_S3_REGION_NAME` | Region of the legacy bucket, when its provider needs one | | -| `yhub.envVars.LEGACY_S3_BUCKET_NAME` | Name of the legacy Django media bucket (default: impress-media-storage) | | -| `yhub.envVars.LEGACY_S3_SIGNATURE_VERSION` | How the calls to the legacy bucket are signed, s3v4 or v4 (default: s3v4) | | +| Name | Description | Value | +| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| `yhub.enabled` | Enable the yhub collaboration server, its service and its init-db job | `true` | +| `yhub.image.repository` | Repository to use to pull the yhub container image | `lasuite/impress-yhub` | +| `yhub.image.tag` | yhub container tag | `latest` | +| `yhub.image.pullPolicy` | yhub container image pull policy | `IfNotPresent` | +| `yhub.command` | Override the yhub container command | `[]` | +| `yhub.args` | Override the yhub container args | `[]` | +| `yhub.replicas` | Amount of yhub replicas | `3` | +| `yhub.metrics.enabled` | Serve the prometheus metrics of yhub on a port of their own, in the server and in the worker. Requires PROMETHEUS_API_KEY in yhub.envVars | `false` | +| `yhub.metrics.port` | Port the metrics are served on. Added to the yhub Service, and to a Service of its own for the worker | `9464` | +| `yhub.metrics.path` | Path the server serves its metrics on, which is the path ingressMetrics publishes them at | `/metrics/yhub` | +| `yhub.metrics.workerPath` | Path the worker serves its metrics on, which is the path ingressMetrics publishes them at | `/metrics/yhub-worker` | +| `yhub.metrics.apiKeySecret.name` | Secret the monitors read the bearer token from. Defaults to the secretKeyRef of PROMETHEUS_API_KEY in yhub.envVars (yhub.worker.envVars for the worker) | `""` | +| `yhub.metrics.apiKeySecret.key` | Key of the bearer token in that Secret | `""` | +| `yhub.worker.enabled` | Deploy the worker apart from the server, each scaling on its own | `false` | +| `yhub.worker.replicas` | Amount of yhub worker replicas | `1` | +| `yhub.worker.resources` | Resource requirements for the yhub worker container, the server ones when empty | `{}` | +| `yhub.worker.podAnnotations` | Annotations to add to the yhub worker Pod, the server ones when empty | `{}` | +| `yhub.worker.dpAnnotations` | Annotations to add to the yhub worker Deployment, the server ones when empty | `{}` | +| `yhub.worker.nodeSelector` | Node selector for the yhub worker Pod, the server one when empty | `{}` | +| `yhub.worker.tolerations` | Tolerations for the yhub worker Pod, the server ones when empty | `[]` | +| `yhub.worker.affinity` | Affinity for the yhub worker Pod, the server one when empty | `{}` | +| `yhub.worker.terminationGracePeriodSeconds` | Grace period given to a worker pod to finish its task, the server one when empty | `nil` | +| `yhub.worker.pdb.enabled` | Enable pdb on the yhub worker | `true` | +| `yhub.shareProcessNamespace` | Enable share process namespace between containers | `false` | +| `yhub.sidecars` | Add sidecars containers to yhub deployment | `[]` | +| `yhub.terminationGracePeriodSeconds` | Grace period given to a yhub pod to drain before it is killed | `60` | +| `yhub.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the yhub container | `false` | +| `yhub.securityContext.capabilities.drop` | List of capabilities to drop for the yhub container | `["ALL"]` | +| `yhub.securityContext.runAsNonRoot` | Whether to run the yhub container as a non-root user | `true` | +| `yhub.securityContext.runAsUser` | User the yhub container runs as | `1000` | +| `yhub.securityContext.runAsGroup` | Group the yhub container runs as | `1000` | +| `yhub.securityContext.seccompProfile.type` | Seccomp profile type for the yhub container | `RuntimeDefault` | +| `yhub.envVars` | Configure yhub container environment variables | `undefined` | +| `yhub.envVars.REDIS` | Required, redis/valkey url holding the live document state (e.g. redis://valkey:6379/0) | | +| `yhub.envVars.POSTGRES` | Required, url of the yhub database, created by the init-db job (e.g. postgres://user:pass@postgres:5432/yhub) | | +| `yhub.envVars.REDIS_PREFIX` | Namespace of the redis keys, when the instance is shared (default: yhub) | | +| `yhub.envVars.COLLABORATION_BACKEND_BASE_URL` | Base url of the Docs backend, which yhub asks about users and document access rights | | +| `yhub.envVars.COLLABORATION_SERVER_ORIGIN` | Comma separated list of the origins allowed to open a websocket | | +| `yhub.envVars.YHUB_JWT_PRIVATE_KEY_FILE` | Path to the RSA private key (PEM) yhub signs its calls to the backend with, mounted from a secret | | +| `yhub.envVars.YHUB_TASK_CONCURRENCY` | Tasks one worker process claims at once, times the replicas running a worker (default: 5) | | +| `yhub.envVars.YHUB_TASK_DEBOUNCE_MS` | How long an update waits on the redis stream before a worker persists it, in ms (default: 10000) | | +| `yhub.envVars.YHUB_MIN_MESSAGE_LIFETIME_MS` | How long persisted updates stay replayable from redis, in ms (default: 60000) | | +| `yhub.envVars.SOFT_MIGRATION` | Set to "true" to seed rooms from the legacy Django/S3 document store on first access | | +| `yhub.envVars.LEGACY_S3_ENDPOINT_URL` | Required by SOFT_MIGRATION, endpoint of the legacy Django media bucket, without a path (e.g. https://s3.example.com) | | +| `yhub.envVars.LEGACY_S3_ACCESS_KEY_ID` | Required by SOFT_MIGRATION, read access to the legacy bucket (or LEGACY_S3_ACCESS_KEY_ID_FILE) | | +| `yhub.envVars.LEGACY_S3_SECRET_ACCESS_KEY` | Required by SOFT_MIGRATION, secret of the key above (or LEGACY_S3_SECRET_ACCESS_KEY_FILE) | | +| `yhub.envVars.LEGACY_S3_REGION_NAME` | Region of the legacy bucket, when its provider needs one | | +| `yhub.envVars.LEGACY_S3_BUCKET_NAME` | Name of the legacy Django media bucket (default: impress-media-storage) | | +| `yhub.envVars.LEGACY_S3_SIGNATURE_VERSION` | How the calls to the legacy bucket are signed, s3v4 or v4 (default: s3v4) | | | `yhub.envVars.YHUB_S3_PERSISTENCE` | Set to "true" to write new document blobs to a bucket instead of the yhub database — the YHUB_S3_* settings attach the bucket whether or not this is on, read src/yhub-server/README.md first | | -| `yhub.envVars.YHUB_S3_ENDPOINT_URL` | Required by YHUB_S3_PERSISTENCE, endpoint of the bucket the blobs are stored in, without a path (e.g. https://s3.example.com) | | -| `yhub.envVars.YHUB_S3_ACCESS_KEY_ID` | Required by YHUB_S3_PERSISTENCE, read/write/delete access to that bucket (or YHUB_S3_ACCESS_KEY_ID_FILE) | | -| `yhub.envVars.YHUB_S3_SECRET_ACCESS_KEY` | Required by YHUB_S3_PERSISTENCE, secret of the key above (or YHUB_S3_SECRET_ACCESS_KEY_FILE) | | -| `yhub.envVars.YHUB_S3_BUCKET_NAME` | Required by YHUB_S3_PERSISTENCE, name of that bucket, created on startup when missing | | -| `yhub.envVars.YHUB_S3_REGION_NAME` | Region of that bucket, when its provider needs one | | -| `yhub.envVars.BY_VALUE` | Example environment variable by setting value directly | | -| `yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | | -| `yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | | -| `yhub.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | | -| `yhub.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | | -| `yhub.podAnnotations` | Annotations to add to the yhub Pod | `{}` | -| `yhub.dpAnnotations` | Annotations to add to the yhub Deployment | `{}` | -| `yhub.initDbJobAnnotations` | Annotations for the yhub init-db job | `{}` | -| `yhub.jobs.ttlSecondsAfterFinished` | Period to wait before removing the init-db job | `30` | -| `yhub.jobs.backoffLimit` | Numbers of init-db job retries | `2` | -| `yhub.initDb.enabled` | Run the job creating and upgrading the yhub schema | `true` | -| `yhub.initDb.command` | Override the command creating and upgrading the yhub schema | `[]` | -| `yhub.initDb.retries` | How many times the schema script is retried while the postgres server does not answer | `60` | -| `yhub.initDb.retryDelaySeconds` | Seconds between two attempts | `5` | -| `yhub.initDb.restartPolicy` | Restart policy of the init-db job | `Never` | -| `yhub.service.type` | yhub Service type | `ClusterIP` | -| `yhub.service.port` | yhub Service listening port | `443` | -| `yhub.service.targetPort` | yhub container listening port | `3002` | -| `yhub.service.annotations` | Annotations to add to the yhub Service | `{}` | -| `yhub.probes.liveness.path` | Configure path for yhub HTTP liveness probe | `/collaboration/ping/v1` | -| `yhub.probes.liveness.initialDelaySeconds` | Configure initial delay for yhub liveness probe | `10` | -| `yhub.probes.liveness.timeoutSeconds` | Configure timeout for yhub liveness probe | `2` | -| `yhub.probes.readiness.path` | Configure path for yhub HTTP readiness probe | `/collaboration/ready/v1` | -| `yhub.probes.readiness.initialDelaySeconds` | Configure initial delay for yhub readiness probe | `5` | -| `yhub.probes.readiness.timeoutSeconds` | Configure timeout for yhub readiness probe | `3` | -| `yhub.probes.liveness.targetPort` | Configure port for yhub HTTP liveness probe | | -| `yhub.probes.readiness.targetPort` | Configure port for yhub HTTP readiness probe | | -| `yhub.probes.startup.path` | Configure path for yhub HTTP startup probe | | -| `yhub.probes.startup.targetPort` | Configure port for yhub HTTP startup probe | | -| `yhub.probes.startup.initialDelaySeconds` | Configure initial delay for yhub startup probe | | -| `yhub.probes.startup.timeoutSeconds` | Configure timeout for yhub startup probe | | -| `yhub.resources` | Resource requirements for the yhub container | `{}` | -| `yhub.nodeSelector` | Node selector for the yhub Pod | `{}` | -| `yhub.tolerations` | Tolerations for the yhub Pod | `[]` | -| `yhub.affinity` | Affinity for the yhub Pod | `{}` | -| `yhub.persistence` | Additional volumes to create and mount on the yhub. Used for debugging purposes | `{}` | -| `yhub.persistence.volume-name.size` | Size of the additional volume | | -| `yhub.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | | -| `yhub.persistence.volume-name.mountPath` | Path where the volume should be mounted to | | -| `yhub.extraVolumeMounts` | Additional volumes to mount on the yhub. Mounted on the init-db job too | `[]` | -| `yhub.extraVolumes` | Additional volumes to mount on the yhub. Mounted on the init-db job too | `[]` | -| `yhub.pdb.enabled` | Enable pdb on yhub | `true` | -| `yhub.serviceAccountName` | Optional service account name to use for yhub pods | `nil` | +| `yhub.envVars.YHUB_S3_ENDPOINT_URL` | Required by YHUB_S3_PERSISTENCE, endpoint of the bucket the blobs are stored in, without a path (e.g. https://s3.example.com) | | +| `yhub.envVars.YHUB_S3_ACCESS_KEY_ID` | Required by YHUB_S3_PERSISTENCE, read/write/delete access to that bucket (or YHUB_S3_ACCESS_KEY_ID_FILE) | | +| `yhub.envVars.YHUB_S3_SECRET_ACCESS_KEY` | Required by YHUB_S3_PERSISTENCE, secret of the key above (or YHUB_S3_SECRET_ACCESS_KEY_FILE) | | +| `yhub.envVars.YHUB_S3_BUCKET_NAME` | Required by YHUB_S3_PERSISTENCE, name of that bucket, created on startup when missing | | +| `yhub.envVars.YHUB_S3_REGION_NAME` | Region of that bucket, when its provider needs one | | +| `yhub.envVars.BY_VALUE` | Example environment variable by setting value directly | | +| `yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | | +| `yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | | +| `yhub.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | | +| `yhub.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | | +| `yhub.podAnnotations` | Annotations to add to the yhub Pod | `{}` | +| `yhub.dpAnnotations` | Annotations to add to the yhub Deployment | `{}` | +| `yhub.initDbJobAnnotations` | Annotations for the yhub init-db job | `{}` | +| `yhub.jobs.ttlSecondsAfterFinished` | Period to wait before removing the init-db job | `30` | +| `yhub.jobs.backoffLimit` | Numbers of init-db job retries | `2` | +| `yhub.initDb.enabled` | Run the job creating and upgrading the yhub schema | `true` | +| `yhub.initDb.command` | Override the command creating and upgrading the yhub schema | `[]` | +| `yhub.initDb.retries` | How many times the schema script is retried while the postgres server does not answer | `60` | +| `yhub.initDb.retryDelaySeconds` | Seconds between two attempts | `5` | +| `yhub.initDb.restartPolicy` | Restart policy of the init-db job | `Never` | +| `yhub.service.type` | yhub Service type | `ClusterIP` | +| `yhub.service.port` | yhub Service listening port | `443` | +| `yhub.service.targetPort` | yhub container listening port | `3002` | +| `yhub.service.annotations` | Annotations to add to the yhub Service | `{}` | +| `yhub.probes.liveness.path` | Configure path for yhub HTTP liveness probe | `/collaboration/ping/v1` | +| `yhub.probes.liveness.initialDelaySeconds` | Configure initial delay for yhub liveness probe | `10` | +| `yhub.probes.liveness.timeoutSeconds` | Configure timeout for yhub liveness probe | `2` | +| `yhub.probes.readiness.path` | Configure path for yhub HTTP readiness probe | `/collaboration/ready/v1` | +| `yhub.probes.readiness.initialDelaySeconds` | Configure initial delay for yhub readiness probe | `5` | +| `yhub.probes.readiness.timeoutSeconds` | Configure timeout for yhub readiness probe | `3` | +| `yhub.probes.liveness.targetPort` | Configure port for yhub HTTP liveness probe | | +| `yhub.probes.readiness.targetPort` | Configure port for yhub HTTP readiness probe | | +| `yhub.probes.startup.path` | Configure path for yhub HTTP startup probe | | +| `yhub.probes.startup.targetPort` | Configure port for yhub HTTP startup probe | | +| `yhub.probes.startup.initialDelaySeconds` | Configure initial delay for yhub startup probe | | +| `yhub.probes.startup.timeoutSeconds` | Configure timeout for yhub startup probe | | +| `yhub.resources` | Resource requirements for the yhub container | `{}` | +| `yhub.nodeSelector` | Node selector for the yhub Pod | `{}` | +| `yhub.tolerations` | Tolerations for the yhub Pod | `[]` | +| `yhub.affinity` | Affinity for the yhub Pod | `{}` | +| `yhub.persistence` | Additional volumes to create and mount on the yhub. Used for debugging purposes | `{}` | +| `yhub.persistence.volume-name.size` | Size of the additional volume | | +| `yhub.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | | +| `yhub.persistence.volume-name.mountPath` | Path where the volume should be mounted to | | +| `yhub.extraVolumeMounts` | Additional volumes to mount on the yhub. Mounted on the init-db job too | `[]` | +| `yhub.extraVolumes` | Additional volumes to mount on the yhub. Mounted on the init-db job too | `[]` | +| `yhub.pdb.enabled` | Enable pdb on yhub | `true` | +| `yhub.serviceAccountName` | Optional service account name to use for yhub pods | `nil` | ### docSpec -| Name | Description | Value | -| -------------------------------------------------- | --------------------------------------------------------------- | ----------------------- | -| `docSpec.enabled` | Enable docSpec deployment | `false` | -| `docSpec.image.repository` | Repository to use to pull docSpec container image | `ghcr.io/docspecio/api` | -| `docSpec.image.tag` | docSpec container tag | `3.0.1` | -| `docSpec.image.pullPolicy` | docSpec container image pull policy | `IfNotPresent` | -| `docSpec.command` | Override the docSpec container command | `[]` | -| `docSpec.args` | Override the docSpec container args | `[]` | -| `docSpec.replicas` | Amount of docSpec replicas | `1` | -| `docSpec.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the docSpec container | `false` | -| `docSpec.securityContext.capabilities.drop` | List of capabilities to drop for the docSpec container | `["ALL"]` | -| `docSpec.securityContext.runAsNonRoot` | Whether to run the docSpec container as a non-root user | `true` | -| `docSpec.securityContext.seccompProfile.type` | Seccomp profile type for the docSpec container | `RuntimeDefault` | -| `docSpec.envVars` | Configure docSpec container environment variables | `{}` | -| `docSpec.service.type` | docSpec Service type | `ClusterIP` | -| `docSpec.service.port` | docSpec Service listening port | `4000` | -| `docSpec.service.targetPort` | docSpec container listening port | `3000` | -| `docSpec.probes.liveness.path` | Configure path for docSpec HTTP liveness probe | `/health` | -| `docSpec.probes.readiness.path` | Configure path for docSpec HTTP readiness probe | `/health` | -| `docSpec.resources` | docSpec resources | `{}` | -| `docSpec.nodeSelector` | Node selector for the docSpec Pod | `{}` | -| `docSpec.tolerations` | Tolerations for the docSpec Pod | `[]` | -| `docSpec.affinity` | Affinity for the docSpec Pod | `{}` | -| `docSpec.extraVolumeMounts` | Additional volumes to mount on docSpec | `[]` | -| `docSpec.extraVolumes` | Additional volumes to mount on docSpec | `[]` | +| Name | Description | Value | +| -------------------------------------------------- | --------------------------------------------------------------- | --------------------- | +| `docSpec.enabled` | Enable docSpec deployment | `false` | +| `docSpec.image.repository` | Repository to use to pull docSpec container image | `ghcr.io/docspec/api` | +| `docSpec.image.tag` | docSpec container tag | `1.21.4` | +| `docSpec.image.pullPolicy` | docSpec container image pull policy | `IfNotPresent` | +| `docSpec.command` | Override the docSpec container command | `[]` | +| `docSpec.args` | Override the docSpec container args | `[]` | +| `docSpec.replicas` | Amount of docSpec replicas | `1` | +| `docSpec.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the docSpec container | `false` | +| `docSpec.securityContext.capabilities.drop` | List of capabilities to drop for the docSpec container | `["ALL"]` | +| `docSpec.securityContext.runAsNonRoot` | Whether to run the docSpec container as a non-root user | `true` | +| `docSpec.securityContext.seccompProfile.type` | Seccomp profile type for the docSpec container | `RuntimeDefault` | +| `docSpec.envVars` | Configure docSpec container environment variables | `{}` | +| `docSpec.service.type` | docSpec Service type | `ClusterIP` | +| `docSpec.service.port` | docSpec Service listening port | `4000` | +| `docSpec.service.targetPort` | docSpec container listening port | `3000` | +| `docSpec.probes.liveness.path` | Configure path for docSpec HTTP liveness probe | `/health` | +| `docSpec.probes.readiness.path` | Configure path for docSpec HTTP readiness probe | `/health` | +| `docSpec.resources` | docSpec resources | `{}` | +| `docSpec.nodeSelector` | Node selector for the docSpec Pod | `{}` | +| `docSpec.tolerations` | Tolerations for the docSpec Pod | `[]` | +| `docSpec.affinity` | Affinity for the docSpec Pod | `{}` | +| `docSpec.extraVolumeMounts` | Additional volumes to mount on docSpec | `[]` | +| `docSpec.extraVolumes` | Additional volumes to mount on docSpec | `[]` | diff --git a/src/helm/impress/templates/_helpers.tpl b/src/helm/impress/templates/_helpers.tpl index 98026bd9c..48233f279 100644 --- a/src/helm/impress/templates/_helpers.tpl +++ b/src/helm/impress/templates/_helpers.tpl @@ -405,3 +405,107 @@ is told to serve its metrics on the path it is published at. {{- end }} {{- end }} {{- end }} + +{{/* +Environment enabling the prometheus metrics of the backend on the django +container, and on it only: the celery worker serves no request, so its metrics +would never be read. A pod is only ever scraped over plain http — TLS ends at +the ingress, and a Prometheus of the cluster calls the pods themselves — so the +path is also taken out of the redirect to https. Each variable is skipped when +the deployment sets it by hand, in either env map — an explicit value wins, as +everywhere else here. The bearer token (PROMETHEUS_API_KEY) is not set here: it +is a secret, given through `backend.envVars`. + +Requires top level scope +*/}} +{{- define "impress.backend.metrics.env" -}} +{{- if .Values.backend.metrics.enabled -}} +{{- $named := merge (dict) ((.Values.backend.django | default dict).envVars | default dict) (.Values.backend.envVars | default dict) -}} +{{- range $variable := list "PROMETHEUS_METRICS_ENABLED" "PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT" -}} +{{- if not (hasKey $named $variable) }} +- name: {{ $variable | quote }} + value: "True" +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +The Secret a monitor reads the bearer token of a scrape from: the one named in +`.metrics.apiKeySecret`, or else the one PROMETHEUS_API_KEY is taken +from in the env of the scraped process. A token that comes from nowhere the +Prometheus Operator can read is refused at render time, rather than left to +fail at scrape time with a 401. + +Requires a dict with "name" (of the monitor, for the message), "metrics" (the +`.metrics` values) and "envVars" (the env map of the scraped process) +*/}} +{{- define "impress.metrics.apiKeySecret" -}} +{{- $ref := .metrics.apiKeySecret | default dict -}} +{{- if not $ref.name -}} +{{- $fromEnv := index (.envVars | default dict) "PROMETHEUS_API_KEY" -}} +{{- $ref = (kindIs "map" $fromEnv) | ternary $fromEnv dict -}} +{{- $ref = $ref.secretKeyRef | default dict -}} +{{- end -}} +{{- if not (and $ref.name $ref.key) -}} +{{- fail (printf "%s: a monitor scrapes with the bearer token of PROMETHEUS_API_KEY, which has to come from a Secret. Give it as a secretKeyRef in the envVars of the component, or name the Secret in its metrics.apiKeySecret" .name) -}} +{{- end -}} +name: {{ $ref.name | quote }} +key: {{ $ref.key | quote }} +{{- end }} + +{{/* +The components whose metrics are enabled, as the monitors see them: one entry +per pod kind to scrape, with the labels selecting it (its Service and its pods +carry the same ones), the name of the port — the same on the Service and on the +container — and the path the process serves on. + +Requires top level scope +*/}} +{{- define "impress.metrics.targets" -}} +{{- $targets := list -}} +{{- if .Values.backend.metrics.enabled -}} +{{- $envVars := merge (dict) ((.Values.backend.django | default dict).envVars | default dict) (.Values.backend.envVars | default dict) -}} +{{- $targets = append $targets (dict "name" (include "impress.backend.fullname" .) "component" "backend" "port" "http" "path" "/metrics" "metrics" .Values.backend.metrics "envVars" $envVars) -}} +{{- end -}} +{{- if and .Values.yhub.enabled .Values.yhub.metrics.enabled -}} +{{- $targets = append $targets (dict "name" (include "impress.yhub.fullname" .) "component" "yhub" "port" "metrics" "path" .Values.yhub.metrics.path "metrics" .Values.yhub.metrics "envVars" (.Values.yhub.envVars | default dict)) -}} +{{- if .Values.yhub.worker.enabled -}} +{{- $envVars := merge (dict) ((.Values.yhub.worker | default dict).envVars | default dict) (.Values.yhub.envVars | default dict) -}} +{{- $targets = append $targets (dict "name" (include "impress.yhub.worker.fullname" .) "component" "yhub-worker" "port" "metrics" "path" .Values.yhub.metrics.workerPath "metrics" .Values.yhub.metrics "envVars" $envVars) -}} +{{- end -}} +{{- end -}} +{{- toJson $targets -}} +{{- end }} + +{{/* +One endpoint of a ServiceMonitor or a PodMonitor: the port and path of a target, +the scrape settings of the monitor and the bearer token of the component. + +Requires a dict with "target" (an entry of impress.metrics.targets) and +"monitor" (the serviceMonitor or podMonitor values) +*/}} +{{- define "impress.metrics.endpoint" -}} +- port: {{ .target.port }} + path: {{ .target.path | quote }} + scheme: http + {{- with .monitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .monitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + honorLabels: {{ .monitor.honorLabels }} + authorization: + type: Bearer + credentials: + {{- include "impress.metrics.apiKeySecret" (dict "name" .target.name "metrics" .target.metrics "envVars" .target.envVars) | nindent 6 }} + {{- with .monitor.relabelings }} + relabelings: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .monitor.metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/src/helm/impress/templates/backend_deployment.yaml b/src/helm/impress/templates/backend_deployment.yaml index 41657ee2a..87245609c 100644 --- a/src/helm/impress/templates/backend_deployment.yaml +++ b/src/helm/impress/templates/backend_deployment.yaml @@ -49,12 +49,14 @@ spec: args: {{- toYaml . | nindent 12 }} {{- end }} - {{- if or $envVars .Values.jwtKeys.enabled }} + {{- $metricsEnv := include "impress.backend.metrics.env" . }} + {{- if or $envVars .Values.jwtKeys.enabled $metricsEnv }} env: {{- $envVars | indent 12 }} {{- if .Values.jwtKeys.enabled }} {{- include "impress.jwtKeys.backendEnv" . | nindent 12 }} {{- end }} + {{- $metricsEnv | nindent 12 }} {{- end }} {{- $envFrom := concat (.Values.backend.envFrom | default list) ((.Values.backend.django | default dict).envFrom | default list) }} {{- if $envFrom }} diff --git a/src/helm/impress/templates/podmonitor.yaml b/src/helm/impress/templates/podmonitor.yaml new file mode 100644 index 000000000..e792ad588 --- /dev/null +++ b/src/helm/impress/templates/podmonitor.yaml @@ -0,0 +1,36 @@ +{{- /* +One PodMonitor per component whose metrics are enabled — the backend, the yhub +server, the yhub worker — for a Prometheus Operator inside the cluster. The +same targets as the ServiceMonitors, found by the labels of the pods rather +than through their Services. The job is named after the component. +*/ -}} +{{- if .Values.podMonitor.enabled -}} +{{- $monitor := .Values.podMonitor -}} +{{- range $target := include "impress.metrics.targets" . | fromJsonArray }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: {{ $target.name }} + namespace: {{ $monitor.namespace | default $.Release.Namespace | quote }} + labels: + {{- include "impress.common.labels" (list $ $target.component) | nindent 4 }} + {{- with $monitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $monitor.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + jobLabel: app.kubernetes.io/component + namespaceSelector: + matchNames: + - {{ $.Release.Namespace | quote }} + selector: + matchLabels: + {{- include "impress.common.selectorLabels" (list $ $target.component) | nindent 6 }} + podMetricsEndpoints: + {{- include "impress.metrics.endpoint" (dict "target" $target "monitor" $monitor) | nindent 4 }} +{{- end }} +{{- end }} diff --git a/src/helm/impress/templates/servicemonitor.yaml b/src/helm/impress/templates/servicemonitor.yaml new file mode 100644 index 000000000..d2647a51f --- /dev/null +++ b/src/helm/impress/templates/servicemonitor.yaml @@ -0,0 +1,37 @@ +{{- /* +One ServiceMonitor per component whose metrics are enabled — the backend, the +yhub server, the yhub worker — for a Prometheus Operator inside the cluster. +Each pod behind the Service becomes a target of its own, scraped with the bearer +token the component checks, so nothing goes through the ingress and no replica +is missed. The job is named after the component. +*/ -}} +{{- if .Values.serviceMonitor.enabled -}} +{{- $monitor := .Values.serviceMonitor -}} +{{- range $target := include "impress.metrics.targets" . | fromJsonArray }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ $target.name }} + namespace: {{ $monitor.namespace | default $.Release.Namespace | quote }} + labels: + {{- include "impress.common.labels" (list $ $target.component) | nindent 4 }} + {{- with $monitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $monitor.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + jobLabel: app.kubernetes.io/component + namespaceSelector: + matchNames: + - {{ $.Release.Namespace | quote }} + selector: + matchLabels: + {{- include "impress.common.selectorLabels" (list $ $target.component) | nindent 6 }} + endpoints: + {{- include "impress.metrics.endpoint" (dict "target" $target "monitor" $monitor) | nindent 4 }} +{{- end }} +{{- end }} diff --git a/src/helm/impress/values.yaml b/src/helm/impress/values.yaml index 5740f7fe6..cf01b7185 100644 --- a/src/helm/impress/values.yaml +++ b/src/helm/impress/values.yaml @@ -186,7 +186,7 @@ ingressAdmin: secretName: null additional: [] -## @param ingressMetrics.enabled whether to enable the Ingress publishing the Prometheus metrics of the backend (requires PROMETHEUS_METRICS_ENABLED and PROMETHEUS_API_KEY in backend.envVars) and, with yhub.metrics.enabled, those of yhub +## @param ingressMetrics.enabled whether to enable the Ingress publishing the Prometheus metrics of the backend (backend.metrics.enabled) and of yhub (yhub.metrics.enabled) to a Prometheus outside of the cluster. One inside it scrapes the pods directly with serviceMonitor or podMonitor instead ## @param ingressMetrics.className IngressClass to use for the Ingress ## @param ingressMetrics.host Host for the Ingress. Use a host of its own, and add it to DJANGO_ALLOWED_HOSTS ## @param ingressMetrics.path Path of the backend metrics, matched exactly. The yhub ones are published at yhub.metrics.path and yhub.metrics.workerPath @@ -213,6 +213,60 @@ ingressMetrics: ## nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.10/32" annotations: {} +## Prometheus Operator. A monitor is created for each component whose metrics +## are enabled — the backend (backend.metrics.enabled), the yhub server and the +## yhub worker (yhub.metrics.enabled) — so that a Prometheus inside the cluster +## scrapes every pod directly, each pod a target of its own, rather than one +## replica at random through the ingress. The scrape presents the bearer token +## of the component, read from the Secret its PROMETHEUS_API_KEY comes from +## (backend.metrics.apiKeySecret, yhub.metrics.apiKeySecret). The Prometheus +## Operator reads that Secret itself: it has to live in the namespace of the +## monitors, and the operator's service account be allowed to read it, as the +## kube-prometheus-stack one is. +## +## A ServiceMonitor finds the pods through their Services, a PodMonitor through +## their labels. They give the same targets: pick the kind your Prometheus is +## configured to select, or both. +## @param serviceMonitor.enabled Create a ServiceMonitor for each component whose metrics are enabled +## @param serviceMonitor.namespace Namespace the ServiceMonitors are created in, the release one when empty +## @param serviceMonitor.labels Labels of the ServiceMonitors, which the Prometheus Operator selects them by (e.g. release: kube-prometheus-stack) +## @param serviceMonitor.annotations Annotations of the ServiceMonitors +## @param serviceMonitor.interval Scrape interval, the Prometheus default when empty +## @param serviceMonitor.scrapeTimeout Scrape timeout, the Prometheus default when empty +## @param serviceMonitor.honorLabels Keep the labels of the scraped samples when they clash with the target ones +## @param serviceMonitor.relabelings Relabelings of the targets, before the scrape +## @param serviceMonitor.metricRelabelings Relabelings of the samples, after the scrape +serviceMonitor: + enabled: false + namespace: "" + labels: {} + annotations: {} + interval: "" + scrapeTimeout: "" + honorLabels: false + relabelings: [] + metricRelabelings: [] + +## @param podMonitor.enabled Create a PodMonitor for each component whose metrics are enabled +## @param podMonitor.namespace Namespace the PodMonitors are created in, the release one when empty +## @param podMonitor.labels Labels of the PodMonitors, which the Prometheus Operator selects them by (e.g. release: kube-prometheus-stack) +## @param podMonitor.annotations Annotations of the PodMonitors +## @param podMonitor.interval Scrape interval, the Prometheus default when empty +## @param podMonitor.scrapeTimeout Scrape timeout, the Prometheus default when empty +## @param podMonitor.honorLabels Keep the labels of the scraped samples when they clash with the target ones +## @param podMonitor.relabelings Relabelings of the targets, before the scrape +## @param podMonitor.metricRelabelings Relabelings of the samples, after the scrape +podMonitor: + enabled: false + namespace: "" + labels: {} + annotations: {} + interval: "" + scrapeTimeout: "" + honorLabels: false + relabelings: [] + metricRelabelings: [] + ## @param ingressMedia.enabled whether to enable the Ingress or not ## @param ingressMedia.className IngressClass to use for the Ingress ## @param ingressMedia.host Host for the Ingress @@ -319,6 +373,19 @@ backend: envVars: {} envFrom: [] + ## @param backend.metrics.enabled Serve the prometheus metrics of the backend on /metrics of the web pods, scraped over plain http inside the cluster. Sets PROMETHEUS_METRICS_ENABLED and PROMETHEUS_METRICS_SSL_REDIRECT_EXEMPT on the django container, and not on celery, unless backend.envVars sets them. Requires PROMETHEUS_API_KEY in backend.envVars + ## @param backend.metrics.apiKeySecret.name Secret the monitors read the bearer token from. Defaults to the secretKeyRef of backend.envVars.PROMETHEUS_API_KEY + ## @param backend.metrics.apiKeySecret.key Key of the bearer token in that Secret + ## + ## The token has to come from a Secret for a monitor to present it: either a + ## secretKeyRef in backend.envVars.PROMETHEUS_API_KEY, which is enough, or + ## PROMETHEUS_API_KEY_FILE pointing at a mounted one, named here. + metrics: + enabled: false + apiKeySecret: + name: "" + key: "" + ## @param backend.podAnnotations Annotations to add to the backend Pod podAnnotations: {} @@ -1002,11 +1069,16 @@ yhub: ## @param yhub.metrics.port Port the metrics are served on. Added to the yhub Service, and to a Service of its own for the worker ## @param yhub.metrics.path Path the server serves its metrics on, which is the path ingressMetrics publishes them at ## @param yhub.metrics.workerPath Path the worker serves its metrics on, which is the path ingressMetrics publishes them at + ## @param yhub.metrics.apiKeySecret.name Secret the monitors read the bearer token from. Defaults to the secretKeyRef of PROMETHEUS_API_KEY in yhub.envVars (yhub.worker.envVars for the worker) + ## @param yhub.metrics.apiKeySecret.key Key of the bearer token in that Secret metrics: enabled: false port: 9464 path: /metrics/yhub workerPath: /metrics/yhub-worker + apiKeySecret: + name: "" + key: "" ## @param yhub.worker.enabled Deploy the worker apart from the server, each scaling on its own ## @param yhub.worker.replicas Amount of yhub worker replicas