diff --git a/swh/templates/loader-metadata/configmap-utils.yaml b/swh/templates/loader-metadata/configmap-utils.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b9e4933f07624dc0c7ecdbb994849800a3060a77 --- /dev/null +++ b/swh/templates/loader-metadata/configmap-utils.yaml @@ -0,0 +1,31 @@ +{{ if .Values.loader_metadata.enabled -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: loader-metadata-utils + namespace: {{ .Values.namespace }} +data: + pre-stop-idempotent.sh: | + #!/bin/bash + + # pre-stop hook can be triggered multiple times but we want it to be applied only + # once so container can warm-shutdown properly. + + # When celery receives multiple times the sigterm signal, this ends up doing an + # immediate shutdown which prevents long-standing tasks to finish properly. + + set -ex + + WITNESS_FILE=/tmp/already-stopped + + # Seed awk with the number of nanoseconds since epoch + # and have it generate a number between 0 and 1 + sleep $(date +%s%N | awk '{srand($1); print rand()}') + + if [ ! -e $WITNESS_FILE ]; then + touch $WITNESS_FILE + # journal clients expect a SIGINT, not a SIGTERM + kill -INT 1 + fi +{{ end }} diff --git a/swh/templates/loader-metadata/configmap.yaml b/swh/templates/loader-metadata/configmap.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f036770d7eddef63b248089021e53ff614e80ad3 --- /dev/null +++ b/swh/templates/loader-metadata/configmap.yaml @@ -0,0 +1,54 @@ +{{ if .Values.loader_metadata.enabled -}} +{{- $journalUser := .Values.loader_metadata.journalBrokers.user -}} +{{- $consumerGroup := .Values.loader_metadata.consumerGroup -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: loader-metadata-template + namespace: {{ .Values.namespace }} +data: + config.yml.template: | + storage: + cls: pipeline + steps: + - cls: retry + - cls: remote + url: http://{{ .Values.loader_metadata.storage.host }}:{{ .Values.loader_metadata.storage.port }}/ + scheduler: + cls: remote + url: http://{{ .Values.loader_metadata.scheduler.host }}:{{ .Values.loader_metadata.scheduler.port }}/ + journal: + brokers: {{ toYaml .Values.loader_metadata.journalBrokers.hosts | nindent 8 }} + {{ if $journalUser }} + group_id: {{ $journalUser }}-{{ $consumerGroup }} + {{ else }} + group_id: {{ $consumerGroup }} + {{ end -}} + prefix: {{ .Values.loader_metadata.prefix }} + + {{ if $journalUser }} + sasl.mechanism: SCRAM-SHA-512 + security.protocol: SASL_SSL + sasl.username: {{ $journalUser }} + sasl.password: ${JOURNAL_PASSWORD} + {{ end -}} + metadata_fetcher_credentials: + + init-container-entrypoint.sh: | + #!/bin/bash + + set -e + + CONFIG_FILE=/etc/swh/config.yml + + # substitute environment variables when creating the default config.yml + eval echo \""$(</etc/swh/configuration-template/config.yml.template)"\" \ + > $CONFIG_FILE + + CREDS_PATH=/etc/credentials/metadata-fetcher/credentials + [ -f $CREDS_PATH ] && \ + sed 's/^/ /g' $CREDS_PATH >> $CONFIG_FILE + + exit 0 +{{ end }} diff --git a/swh/templates/loader-metadata/deployment.yaml b/swh/templates/loader-metadata/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fcc5156615b49f316683f02597e21c3dfbee9a21 --- /dev/null +++ b/swh/templates/loader-metadata/deployment.yaml @@ -0,0 +1,116 @@ +{{ if .Values.loader_metadata.enabled -}} +{{- $configurationChecksum := include (print .Template.BasePath "/loader-metadata/configmap.yaml") . -}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loader-metadata + namespace: {{ .Values.namespace }} + labels: + app: loader-metadata +spec: + revisionHistoryLimit: 2 + selector: + matchLabels: + app: loader-metadata + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + template: + metadata: + labels: + app: loader-metadata + annotations: + # Force a rollout upgrade if the configuration changes + checksum/config: {{ $configurationChecksum | sha256sum }} + spec: + {{- if .Values.loader_metadata.affinity }} + affinity: + {{ toYaml .Values.loader_metadata.affinity | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: 3600 + initContainers: + - name: prepare-configuration + image: debian:bullseye + imagePullPolicy: Always + env: + - name: JOURNAL_PASSWORD + valueFrom: + secretKeyRef: + name: common-secrets + key: journal-password + optional: true + command: + - /entrypoint.sh + volumeMounts: + - name: configuration-template + mountPath: /entrypoint.sh + subPath: "init-container-entrypoint.sh" + readOnly: true + - name: configuration + mountPath: /etc/swh + - name: configuration-template + mountPath: /etc/swh/configuration-template + containers: + - name: loader_metadata + image: {{ .Values.swh_loader_metadata_image }}:{{ .Values.swh_loader_metadata_image_version }} + imagePullPolicy: Always + command: + - /opt/swh/entrypoint.sh + resources: + requests: + memory: {{ .Values.loader_metadata.requestedMemory | default "512Mi" }} + cpu: {{ .Values.loader_metadata.requestedCpu | default "500m" }} + lifecycle: + preStop: + exec: + command: ["/pre-stop.sh"] + env: + - name: STATSD_HOST + value: {{ .Values.statsdExternalHost | default "prometheus-statsd-exporter" }} + - name: STATSD_PORT + value: {{ .Values.statsdPort | default "9125" | quote }} + - name: LOGLEVEL + value: {{ .Values.loader_metadata.logLevel | default "INFO" | quote }} + - name: SWH_CONFIG_FILENAME + value: /etc/swh/config.yml + - name: SWH_SENTRY_ENVIRONMENT + value: {{ .Values.sentry.environment }} + - name: SWH_MAIN_PACKAGE + value: {{ .Values.loader_metadata.sentrySwhPackage }} + - name: SWH_SENTRY_DSN + valueFrom: + secretKeyRef: + name: common-secrets + key: loader-metadata-sentry-dsn + # 'name' secret must exist & include key "host" + optional: true + volumeMounts: + - name: loader-metadata-utils + mountPath: /pre-stop.sh + subPath: "pre-stop.sh" + - name: configuration + mountPath: /etc/swh + - name: localstorage + mountPath: /tmp + volumes: + - name: configuration + emptyDir: {} + - name: configuration-template + configMap: + name: loader-metadata-template + defaultMode: 0777 + items: + - key: "config.yml.template" + path: "config.yml.template" + - key: "init-container-entrypoint.sh" + path: "init-container-entrypoint.sh" + - name: loader-metadata-utils + configMap: + name: loader-metadata-utils + defaultMode: 0777 + items: + - key: "pre-stop-idempotent.sh" + path: "pre-stop.sh" +{{ end }} diff --git a/swh/templates/loader-metadata/keda-autoscaling.yaml b/swh/templates/loader-metadata/keda-autoscaling.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e3f79886f118cc1b7a52b94e01289116c2df7e01 --- /dev/null +++ b/swh/templates/loader-metadata/keda-autoscaling.yaml @@ -0,0 +1,36 @@ +{{ if .Values.loader_metadata.enabled -}} +{{- $autoscalingConfig := $.Values.loader_metadata.autoScaling -}} +{{ if $autoscalingConfig }} +{{- $journalUser := .Values.loader_metadata.journalBrokers.user -}} +{{- $consumerGroup := .Values.loader_metadata.consumerGroup -}} +--- +# FIXME: Look into autoscaling from prometheus depending on api authentication +# token use metrics. See: https://keda.sh/docs/2.9/scalers/prometheus/ +# https://docs.softwareheritage.org/devel/statsd.html#outgoing-requests +# https://grafana.softwareheritage.org/d/FR9JAYhVk/outgoing-api-requests?orgId=1 +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: loader-metadata-scaledobject + namespace: {{ .Values.namespace }} +spec: + scaleTargetRef: + name: loader-metadata + pollingInterval: {{ get $autoscalingConfig "poolInterval" | default 120 }} + minReplicaCount: {{ get $autoscalingConfig "minReplicaCount" | default 1 }} + maxReplicaCount: {{ get $autoscalingConfig "maxReplicaCount" | default 5 }} + triggers: + - type: kafka + metadata: + bootstrapServers: {{ first .Values.loader_metadata.journalBrokers.hosts }} + {{ if $journalUser }} + consumerGroup: {{ $journalUser }}-{{ $consumerGroup }} + {{ else }} + consumerGroup: {{ $consumerGroup }} + {{ end }} + lagThreshold: {{ get $autoscalingConfig "lagThreshold" | default 1000 | quote }} + offsetResetPolicy: earliest + authenticationRef: + name: {{ .Values.loader_metadata.authenticationRef }} +{{ end }} +{{ end }} diff --git a/swh/values.yaml b/swh/values.yaml index 3c11b620f2f818e1c2e0c36d55785bc00b9dd79c..00cb78b5c41624265799a4f17d132ed1cde7d97b 100644 --- a/swh/values.yaml +++ b/swh/values.yaml @@ -102,6 +102,28 @@ storage_replayer: # lagThreashold: 1000 # minReplicaCount: 1 # maxReplicaCount: 10 + +loader_metadata: + enabled: false + authenticationRef: keda-storage-replayer-trigger-authentication + # storage: + # host: ... + # port: 5002 + # scheduler: + # host: ... + # port: 5008 + # consumerGroup: ... + # prefix: swh.journal.objects + # journalBrokers: + # hosts: + # - ... + # user: ... + # autoScaling: + # poolInterval: 120 + # lagThreashold: 1000 + # minReplicaCount: 1 + # maxReplicaCount: 10 + loaders: enabled: false deployments: diff --git a/swh/values/default.yaml b/swh/values/default.yaml index da5afba88661cb06e90bcb562e4b48a9c89f9e58..3e758c8e05a77064ca65cbe41bb602ae0de85c87 100644 --- a/swh/values/default.yaml +++ b/swh/values/default.yaml @@ -44,6 +44,18 @@ cookers: values: - "true" +loader_metadata: + sentrySwhPackage: swh.loader.metadata + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: "swh/loader_metadata" + operator: In + values: + - "true" + indexers: sentrySwhPackage: swh.indexer affinity: diff --git a/swh/values/staging.yaml b/swh/values/staging.yaml index 4f83aa649fd4cf950748ded7a600e06af9861f48..720c81950f3f771ea001559d78bfbf7818ff723c 100644 --- a/swh/values/staging.yaml +++ b/swh/values/staging.yaml @@ -339,6 +339,22 @@ checker_deposit: autoScaling: maxReplicaCount: 2 +loader_metadata: + enabled: true + storage: + host: storage1.internal.staging.swh.network + port: 5002 + scheduler: + host: scheduler0.internal.staging.swh.network + port: 5008 + consumerGroup: swh.loader_metadata.journal_client + prefix: swh.journal.objects + journalBrokers: + hosts: + - journal1.internal.staging.swh.network:9092 + autoScaling: + maxReplicaCount: 2 + indexers: enabled: true storage: diff --git a/values-swh-application-versions.yaml b/values-swh-application-versions.yaml index 7a91353ee9e4fc26bb505f71f3e7d3d0cfb5ede7..ec465c500c5ffec2b10b8680c6cf25284364bbdc 100644 --- a/values-swh-application-versions.yaml +++ b/values-swh-application-versions.yaml @@ -16,6 +16,8 @@ swh_loader_highpriority_image: container-registry.softwareheritage.org/swh/infra swh_loader_highpriority_image_version: '20230313.1' swh_loader_mercurial_image: container-registry.softwareheritage.org/swh/infra/swh-apps/loader_mercurial swh_loader_mercurial_image_version: '20230203.1' +swh_loader_metadata_image: container-registry.softwareheritage.org/swh/infra/swh-apps/loader_metadata +swh_loader_metadata_image_version: '20230309.1' swh_loader_package_image: container-registry.softwareheritage.org/swh/infra/swh-apps/loader_package swh_loader_package_image_version: '20230220.1' swh_loader_svn_image: container-registry.softwareheritage.org/swh/infra/swh-apps/loader_svn