From cb7615b7dfd4f1f135b3c285c1f049f972a5d0d8 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 4 May 2026 16:03:45 +0200 Subject: [PATCH 01/24] feat: introduce alloy role to replace promtail in control-plane --- common/roles/defaults/defaults/main.yaml | 2 + control-plane/roles/logging/README.md | 1 + .../roles/logging/defaults/main.yaml | 6 + control-plane/roles/logging/tasks/main.yaml | 14 + .../roles/logging/templates/alloy-values.yaml | 567 ++++++++++++++++++ 5 files changed, 590 insertions(+) create mode 100644 control-plane/roles/logging/templates/alloy-values.yaml diff --git a/common/roles/defaults/defaults/main.yaml b/common/roles/defaults/defaults/main.yaml index 1efc47f29..ac217f6c9 100644 --- a/common/roles/defaults/defaults/main.yaml +++ b/common/roles/defaults/defaults/main.yaml @@ -163,6 +163,8 @@ metal_stack_release: metal_helm_chart_tag: "helm-charts.metal-stack.metal-control-plane.tag" logging_chart_version: "helm-charts.logging.loki.version" logging_chart_repo: "helm-charts.logging.loki.repository" + logging_alloy_version: "helm-charts.logging.alloy.version" + logging_alloy_repo: "helm-charts.logging.alloy.repository" logging_promtail_chart_version: "helm-charts.logging.promtail.version" logging_promtail_chart_repo: "helm-charts.logging.promtail.repository" gardener_logging_promtail_chart_version: "helm-charts.logging.promtail.version" diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index b3b36c57f..430604275 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -29,3 +29,4 @@ The following variables can be set to configure the role: | logging_ingress_loki_basic_auth_password_salt | | The basic auth password salt used for stable password hashes | | logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | | logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | +| logging_alloy_config | | The config to use for alloy | diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index 114d4eaab..db868a413 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -1,5 +1,11 @@ --- logging_namespace: monitoring +logging_alloy_port: 12345 +logging_alloy_config: | + logging { + level = "info" + format = "logfmt" + } logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" logging_ingress_loki_tls: true logging_ingress_loki_basic_auth_user: promtail diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index e761869a0..782b51810 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -11,6 +11,8 @@ - logging_chart_repo is defined - logging_promtail_chart_version is defined - logging_promtail_chart_repo is defined + - logging_alloy_chart_version is defined + - logging_alloy_chart_repo is defined - name: Create namespace {{ logging_namespace }} k8s: @@ -33,6 +35,17 @@ helm_chart_version: "{{ logging_chart_version }}" helm_value_file_template: "loki-values.yaml" +- name: Deploy alloy + include_role: + name: ansible-common/roles/helm-chart + vars: + helm_repo: "{{ logging_alloy_chart_repo }}" + helm_chart: "alloy" + helm_release_name: alloy + helm_target_namespace: "{{ logging_namespace }}" + helm_chart_version: "{{ logging_alloy_chart_version }}" + helm_value_file_template: "alloy-values.yaml" + - name: Deploy Promtail include_role: name: ansible-common/roles/helm-chart @@ -43,3 +56,4 @@ helm_target_namespace: "{{ logging_namespace }}" helm_chart_version: "{{ logging_promtail_chart_version }}" helm_value_file_template: "promtail-values.yaml" + diff --git a/control-plane/roles/logging/templates/alloy-values.yaml b/control-plane/roles/logging/templates/alloy-values.yaml new file mode 100644 index 000000000..201c7a915 --- /dev/null +++ b/control-plane/roles/logging/templates/alloy-values.yaml @@ -0,0 +1,567 @@ +# -- Overrides the chart's name. Used to change the infix in the resource names. +nameOverride: null + +# -- Overrides the chart's namespace. +namespaceOverride: null + +# -- Overrides the chart's computed fullname. Used to change the full prefix of +# resource names. +fullnameOverride: null + +## Global properties for image pulling override the values defined under `image.registry` and `configReloader.image.registry`. +## If you want to override only one image registry, use the specific fields but if you want to override them all, use `global.image.registry` +global: + image: + # -- Global image registry to use if it needs to be overridden for some specific use cases (e.g local registries, custom images, ...) + registry: "" + + # -- Optional set of global image pull secrets. + pullSecrets: [] + + # -- Global image pull policy to apply to all containers. Overrides `image.pullPolicy` and `configReloader.image.pullPolicy`. + pullPolicy: "" + + # -- Security context to apply to the Grafana Alloy pod. + podSecurityContext: {} + +crds: + # -- Whether to install CRDs for monitoring. + create: true + +## Various Alloy settings. For backwards compatibility with the grafana-agent +## chart, this field may also be called "agent". Naming this field "agent" is +## deprecated and will be removed in a future release. +alloy: + configMap: + # -- Create a new ConfigMap for the config file. + create: true + # -- Content to assign to the new ConfigMap. This is passed into `tpl` allowing for templating from values. + content: |- + {{ logging_alloy_config | indent(6) }} + + # -- Name of existing ConfigMap to use. Used when create is false. + name: null + # -- Key in ConfigMap to get config from. + key: null + + clustering: + # -- Deploy Alloy in a cluster to allow for load distribution. + enabled: false + + # -- Name for the Alloy cluster. Used for differentiating between clusters. + name: "" + + # -- Name for the port used for clustering, useful if running inside an Istio Mesh + portName: http + + # -- Minimum stability level of components and behavior to enable. Must be + # one of "experimental", "public-preview", or "generally-available". + stabilityLevel: "generally-available" + + # -- Path to where Grafana Alloy stores data (for example, the Write-Ahead Log). + # By default, data is lost between reboots. + storagePath: /tmp/alloy + + # -- Enables Grafana Alloy container's http server port. + enableHttpServerPort: true + + # -- Address to listen for traffic on. 0.0.0.0 exposes the UI to other + # containers. + listenAddr: 0.0.0.0 + + # -- Port to listen for traffic on. + listenPort: {{ logging_alloy_port }} + + # -- Scheme is needed for readiness probes. If enabling tls in your configs, set to "HTTPS" + listenScheme: HTTP + + # -- Initial delay for readiness probe. + initialDelaySeconds: 10 + + # -- Timeout for readiness probe. + timeoutSeconds: 1 + + # -- Base path where the UI is exposed. + uiPathPrefix: / + + # -- Enables sending Grafana Labs anonymous usage stats to help improve Grafana + # Alloy. + enableReporting: true + + # -- Extra environment variables to pass to the Alloy container. + extraEnv: [] + + # -- Maps all the keys on a ConfigMap or Secret as environment variables. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#envfromsource-v1-core + envFrom: [] + + # -- Extra args to pass to `alloy run`: https://grafana.com/docs/alloy/latest/reference/cli/run/ + extraArgs: [] + + # -- Extra ports to expose on the Alloy container. + # If `service.type` is `NodePort`, each item may set `nodePort` to choose the Service NodePort for that port. + extraPorts: [] + # - name: "faro" + # nodePort: 31129 + # port: 12347 + # targetPort: 12347 + # protocol: "TCP" + # appProtocol: "h2c" + + # -- Host aliases to add to the Alloy container. + hostAliases: [] + # - ip: "20.21.22.23" + # hostnames: + # - "company.grafana.net" + + mounts: + # -- Mount /var/log from the host into the container for log collection. + varlog: false + # -- Mount /var/lib/docker/containers from the host into the container for log + # collection. + dockercontainers: false + + # -- Extra volume mounts to add into the Grafana Alloy container. Does not + # affect the watch container. + extra: [] + + # -- Security context to apply to the Grafana Alloy container. + securityContext: {} + + # -- Resource requests and limits to apply to the Grafana Alloy container. + resources: {} + + # -- Set lifecycle hooks for the Grafana Alloy container. + lifecycle: {} + # preStop: + # exec: + # command: + # - /bin/sleep + # - "10" + + # -- Set livenessProbe for the Grafana Alloy container. + livenessProbe: {} + +image: + # -- Grafana Alloy image registry (defaults to docker.io) + registry: "docker.io" + # -- Grafana Alloy image repository. + repository: grafana/alloy + # -- (string) Grafana Alloy image tag. When empty, the Chart's appVersion is + # used. + tag: null + # -- Grafana Alloy image's SHA256 digest (either in format "sha256:XYZ" or "XYZ"). When set, will override `image.tag`. + digest: null + # -- Grafana Alloy image pull policy. + pullPolicy: IfNotPresent + # -- Optional set of image pull secrets. + pullSecrets: [] + +rbac: + # -- Whether to create RBAC resources for Alloy. + create: true + + # -- If set, only create Roles and RoleBindings in the given list of namespaces, rather than ClusterRoles and + # ClusterRoleBindings. If not using ClusterRoles, bear in mind that Alloy will not be able to discover cluster-scoped + # resources such as Nodes. + namespaces: [] + + # -- The rules to create for the ClusterRole or Role objects. + rules: + # -- Rules required for the `discovery.kubernetes` component. + - apiGroups: ["", "discovery.k8s.io", "networking.k8s.io"] + resources: ["endpoints", "endpointslices", "ingresses", "pods", "services"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `loki.source.kubernetes` component. + - apiGroups: [""] + resources: ["pods", "pods/log", "namespaces"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `loki.source.podlogs` component. + - apiGroups: ["monitoring.grafana.com"] + resources: ["podlogs"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `mimir.rules.kubernetes` component. + - apiGroups: ["monitoring.coreos.com"] + resources: ["prometheusrules"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `mimir.alerts.kubernetes` component. + - apiGroups: ["monitoring.coreos.com"] + resources: ["alertmanagerconfigs"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `prometheus.operator.*` components. + - apiGroups: ["monitoring.coreos.com"] + resources: ["podmonitors", "servicemonitors", "probes", "scrapeconfigs"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `loki.source.kubernetes_events` component. + - apiGroups: [""] + resources: ["events"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `remote.kubernetes.*` components. + - apiGroups: [""] + resources: ["configmaps", "secrets"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `otelcol.processor.k8sattributes` component. + - apiGroups: ["apps", "extensions"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + + # -- The rules to create for the ClusterRole objects. + clusterRules: + # -- Rules required for the Nodes role in the `discovery.kubernetes` component. + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch"] + # -- Rules required for the `discovery.kubelet` component. + - apiGroups: [""] + resources: ["nodes/pods"] + verbs: ["get", "list", "watch"] + # -- Rules required accessing metric endpoints on the Node (e.g. Kubelet, cAdvisor, etc...). + - apiGroups: [""] + resources: ["nodes/metrics"] + verbs: ["get", "list", "watch"] + # -- Rules required for accessing metrics endpoint. + - nonResourceURLs: ["/metrics"] + verbs: ["get"] + +serviceAccount: + # -- Whether to create a service account for the Grafana Alloy deployment. + create: true + # -- Additional labels to add to the created service account. + additionalLabels: {} + # -- Annotations to add to the created service account. + annotations: {} + # -- The name of the existing service account to use when + # serviceAccount.create is false. + name: null + # Whether the Alloy pod should automatically mount the service account token. + automountServiceAccountToken: true + +# Options for the extra controller used for config reloading. +configReloader: + # -- Enables automatically reloading when the Alloy config changes. + enabled: true + image: + # -- Config reloader image registry (defaults to docker.io) + registry: "quay.io" + # -- Repository to get config reloader image from. + repository: prometheus-operator/prometheus-config-reloader + # -- Tag of image to use for config reloading. + tag: v0.81.0 + # -- SHA256 digest of image to use for config reloading (either in format "sha256:XYZ" or "XYZ"). When set, will override `configReloader.image.tag` + digest: "" + # -- Config reloader image pull policy. + pullPolicy: IfNotPresent + # -- Override the args passed to the container. + customArgs: [] + # -- Resource requests and limits to apply to the config reloader container. + resources: + requests: + cpu: "10m" + memory: "50Mi" + # -- Security context to apply to the Grafana configReloader container. + securityContext: {} + +controller: + # -- Type of controller to use for deploying Grafana Alloy in the cluster. + # Must be one of 'daemonset', 'deployment', or 'statefulset'. + type: 'daemonset' + + # -- Number of pods to deploy. Ignored when controller.type is 'daemonset'. + replicas: 1 + + # -- Extra labels to add to the controller. + extraLabels: {} + + # -- Annotations to add to controller. + extraAnnotations: {} + + # -- Whether to deploy pods in parallel. Only used when controller.type is + # 'statefulset'. + parallelRollout: true + + # -- How many additional seconds to wait before considering a pod ready. + minReadySeconds: 10 + + # -- Configures Pods to use the host network. When set to true, the ports that will be used must be specified. + hostNetwork: false + + # -- Configures Pods to use the host PID namespace. + hostPID: false + + # -- Configures the DNS policy for the pod. https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy + dnsPolicy: ClusterFirst + + # -- Termination grace period in seconds for the Grafana Alloy pods. + # The default value used by Kubernetes if unspecifed is 30 seconds. + terminationGracePeriodSeconds: null + + # -- The maximum number of revisions that will be maintained in the Controllers's revision history. The history consists of all revisions not represented by a currently applied reversion. + revisionHistoryLimit: 10 + + # -- Update strategy for updating deployed Pods. + updateStrategy: {} + + # -- nodeSelector to apply to Grafana Alloy pods. + nodeSelector: {} + + # -- Tolerations to apply to Grafana Alloy pods. + tolerations: [] + + # -- Topology Spread Constraints to apply to Grafana Alloy pods. + topologySpreadConstraints: [] + + # -- priorityClassName to apply to Grafana Alloy pods. + priorityClassName: '' + + # -- Extra pod annotations to add. + podAnnotations: {} + + # -- Extra pod labels to add. + podLabels: {} + + # -- PodDisruptionBudget configuration. + podDisruptionBudget: + # -- Whether to create a PodDisruptionBudget for the controller. + enabled: false + # -- Minimum number of pods that must be available during a disruption. + # Note: Only one of minAvailable or maxUnavailable should be set. + minAvailable: null + # -- Maximum number of pods that can be unavailable during a disruption. + # Note: Only one of minAvailable or maxUnavailable should be set. + maxUnavailable: null + + # -- Whether to enable automatic deletion of stale PVCs due to a scale down operation, when controller.type is 'statefulset'. + enableStatefulSetAutoDeletePVC: false + + autoscaling: + # -- Creates a HorizontalPodAutoscaler for controller type deployment. + # Deprecated: Please use controller.autoscaling.horizontal instead + enabled: false + # -- The lower limit for the number of replicas to which the autoscaler can scale down. + minReplicas: 1 + # -- The upper limit for the number of replicas to which the autoscaler can scale up. + maxReplicas: 5 + # -- Average CPU utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetCPUUtilizationPercentage` to 0 will disable CPU scaling. + targetCPUUtilizationPercentage: 0 + # -- Average Memory utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetMemoryUtilizationPercentage` to 0 will disable Memory scaling. + targetMemoryUtilizationPercentage: 80 + + scaleDown: + # -- List of policies to determine the scale-down behavior. + policies: [] + # - type: Pods + # value: 4 + # periodSeconds: 60 + # -- Determines which of the provided scaling-down policies to apply if multiple are specified. + selectPolicy: Max + # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling down. + stabilizationWindowSeconds: 300 + + scaleUp: + # -- List of policies to determine the scale-up behavior. + policies: [] + # - type: Pods + # value: 4 + # periodSeconds: 60 + # -- Determines which of the provided scaling-up policies to apply if multiple are specified. + selectPolicy: Max + # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling up. + stabilizationWindowSeconds: 0 + + # -- Configures the Horizontal Pod Autoscaler for the controller. + horizontal: + # -- Enables the Horizontal Pod Autoscaler for the controller. + enabled: false + + # -- The lower limit for the number of replicas to which the autoscaler can scale down. + minReplicas: 1 + # -- The upper limit for the number of replicas to which the autoscaler can scale up. + maxReplicas: 5 + # -- Average CPU utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetCPUUtilizationPercentage` to 0 will disable CPU scaling. + targetCPUUtilizationPercentage: 0 + # -- Average Memory utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetMemoryUtilizationPercentage` to 0 will disable Memory scaling. + targetMemoryUtilizationPercentage: 80 + + scaleDown: + # -- List of policies to determine the scale-down behavior. + policies: [] + # - type: Pods + # value: 4 + # periodSeconds: 60 + # -- Determines which of the provided scaling-down policies to apply if multiple are specified. + selectPolicy: Max + # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling down. + stabilizationWindowSeconds: 300 + + scaleUp: + # -- List of policies to determine the scale-up behavior. + policies: [] + # - type: Pods + # value: 4 + # periodSeconds: 60 + # -- Determines which of the provided scaling-up policies to apply if multiple are specified. + selectPolicy: Max + # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling up. + stabilizationWindowSeconds: 0 + # -- Configures the Vertical Pod Autoscaler for the controller. + vertical: + # -- Enables the Vertical Pod Autoscaler for the controller. + enabled: false + + # -- List of recommenders to use for the Vertical Pod Autoscaler. + # Recommenders are responsible for generating recommendation for the object. + # List should be empty (then the default recommender will generate the recommendation) + # or contain exactly one recommender. + recommenders: [] + # recommenders: + # - name: custom-recommender-performance + + # -- Configures the resource policy for the Vertical Pod Autoscaler. + resourcePolicy: + # -- Configures the container policies for the Vertical Pod Autoscaler. + containerPolicies: + - containerName: alloy + # -- The controlled resources for the Vertical Pod Autoscaler. + controlledResources: + - cpu + - memory + # -- The controlled values for the Vertical Pod Autoscaler. Needs to be either RequestsOnly or RequestsAndLimits. + controlledValues: "RequestsAndLimits" + # -- The maximum allowed values for the pods. + maxAllowed: {} + # cpu: 200m + # memory: 100Mi + # -- Defines the min allowed resources for the pod + minAllowed: {} + # cpu: 200m + # memory: 100Mi + + # -- Configures the update policy for the Vertical Pod Autoscaler. + updatePolicy: + # -- Specifies minimal number of replicas which need to be alive for VPA Updater to attempt pod eviction + # minReplicas: 1 + # -- Specifies whether recommended updates are applied when a Pod is started and whether recommended updates + # are applied during the life of a Pod. Possible values are "Off", "Initial", "Recreate", and "Auto". + # updateMode: Auto + + # -- Affinity configuration for pods. + affinity: {} + + volumes: + # -- Extra volumes to add to the Grafana Alloy pod. + extra: [] + + # -- volumeClaimTemplates to add when controller.type is 'statefulset'. + volumeClaimTemplates: [] + + ## -- Additional init containers to run. + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## + initContainers: [] + + # -- Additional containers to run alongside the Alloy container and initContainers. + extraContainers: [] + +networkPolicy: + enabled: false + flavor: kubernetes + + policyTypes: + - Ingress + - Egress + + # Default allow all traffic because Alloy is so configurable + # It is recommended to change this before deploying to production + # To disable each policyType, set value to `null` + ingress: + - {} + egress: + - {} + +service: + # -- Creates a Service for the controller's pods. + enabled: true + # -- Service type + type: ClusterIP + # -- NodePort port. Only takes effect when `service.type: NodePort` + nodePort: 31128 + # -- Cluster IP, can be set to None, empty "" or an IP address + clusterIP: '' + # -- Value for internal traffic policy. 'Cluster' or 'Local' + internalTrafficPolicy: Cluster + annotations: {} + # cloud.google.com/load-balancer-type: Internal + +serviceMonitor: + enabled: false + # -- Additional labels for the service monitor. + additionalLabels: {} + # -- Scrape interval. If not set, the Prometheus default scrape interval is used. + interval: "" + # -- MetricRelabelConfigs to apply to samples after scraping, but before ingestion. + # ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#relabelconfig + metricRelabelings: [] + # - action: keep + # regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+' + # sourceLabels: [__name__] + + # -- Customize tls parameters for the service monitor + tlsConfig: {} + + # -- RelabelConfigs to apply to samples before scraping + # ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#relabelconfig + relabelings: [] + # - sourceLabels: [__meta_kubernetes_pod_node_name] + # separator: ; + # regex: ^(.*)$ + # targetLabel: nodename + # replacement: $1 + # action: replace +ingress: + # -- Enables ingress for Alloy (Faro port) + enabled: false + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + # ingressClassName: nginx + # Values can be templated + annotations: + {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + labels: {} + path: / + faroPort: 12347 + + # pathType is only for k8s >= 1.1= + pathType: Prefix + + hosts: + - chart-example.local + ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services. + extraPaths: [] + # - path: /* + # backend: + # serviceName: ssl-redirect + # servicePort: use-annotation + ## Or for k8s > 1.19 + # - path: /* + # pathType: Prefix + # backend: + # service: + # name: ssl-redirect + # port: + # name: use-annotation + + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +# -- Extra k8s manifests to deploy +extraObjects: [] +# - apiVersion: v1 +# kind: Secret +# metadata: +# name: grafana-cloud +# stringData: +# PROMETHEUS_HOST: 'https://prometheus-us-central1.grafana.net/api/prom/push' +# PROMETHEUS_USERNAME: '123456' From 19537b474466829cb2c3075ed33668fe7b02fd22 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Tue, 5 May 2026 03:31:30 +0200 Subject: [PATCH 02/24] feat: configure control-plane alloy similar to promtail --- common/roles/defaults/defaults/main.yaml | 6 +- .../roles/gardener-logging/README.md | 103 +++- .../roles/gardener-logging/defaults/main.yaml | 29 + .../tasks/gardener-shooted-seed.yaml | 15 + .../roles/gardener-logging/tasks/main.yaml | 24 + .../templates/seed-alloy-config.alloy.j2 | 121 ++++ .../templates/seed-alloy-values.yaml | 41 ++ control-plane/roles/logging/README.md | 110 +++- .../roles/logging/defaults/main.yaml | 32 +- control-plane/roles/logging/tasks/main.yaml | 6 +- .../logging/templates/alloy-config.alloy.j2 | 121 ++++ .../roles/logging/templates/alloy-values.yaml | 570 +----------------- 12 files changed, 591 insertions(+), 587 deletions(-) create mode 100644 control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 create mode 100644 control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml create mode 100644 control-plane/roles/logging/templates/alloy-config.alloy.j2 diff --git a/common/roles/defaults/defaults/main.yaml b/common/roles/defaults/defaults/main.yaml index ac217f6c9..f5c70fdb6 100644 --- a/common/roles/defaults/defaults/main.yaml +++ b/common/roles/defaults/defaults/main.yaml @@ -163,8 +163,10 @@ metal_stack_release: metal_helm_chart_tag: "helm-charts.metal-stack.metal-control-plane.tag" logging_chart_version: "helm-charts.logging.loki.version" logging_chart_repo: "helm-charts.logging.loki.repository" - logging_alloy_version: "helm-charts.logging.alloy.version" - logging_alloy_repo: "helm-charts.logging.alloy.repository" + logging_alloy_chart_version: "helm-charts.logging.alloy.version" + logging_alloy_chart_repo: "helm-charts.logging.alloy.repository" + gardener_logging_alloy_chart_version: "helm-charts.logging.alloy.version" + gardener_logging_alloy_chart_repo: "helm-charts.logging.alloy.repository" logging_promtail_chart_version: "helm-charts.logging.promtail.version" logging_promtail_chart_repo: "helm-charts.logging.promtail.repository" gardener_logging_promtail_chart_version: "helm-charts.logging.promtail.version" diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 9f46e287b..5bc0636a2 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -1,6 +1,14 @@ # gardener-logging -This role deploys a promtail into a Gardener shooted seed. It is expected that the [logging role](../logging/) was deployed into the metal-stack control plane before executing this role. +Deploys [Grafana Alloy](https://grafana.com/docs/alloy/latest/) into Gardener shooted seeds and optionally into the garden cluster itself. Alloy collects pod logs via the Kubernetes API and forwards them to the Loki instance in the metal-stack control plane. + +Expects the [logging role](../logging/) to have been deployed first. + +This role previously used Promtail as the log collector. It has been migrated to use Grafana Alloy instead. See [Migration from Promtail](#migration-from-promtail) for details. + +## Configuration + +The Alloy River config is generated from structured variables at deploy time. Override individual variables to customize behavior, or bypass the template entirely with `gardener_logging_alloy_config_raw`. ## Variables @@ -10,13 +18,86 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| ------------------------------------------------- | --------- | ---------------------------------------------------------------- | -| gardener_logging_promtail_chart_version | yes | Helm chart version for promtail specified under release vector | -| gardener_logging_promtail_chart_repo | yes | Repository for promtail specified under release vector | -| gardener_logging_namespace | | The deployment's target namespace | -| gardener_logging_ingress_dns | | DNS for loki ingress | -| gardener_logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | -| gardener_logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | -| gardener_logging_deploy_to_garden_cluster | | Deploys promtail also into the garden cluster | -| gardener_logging_shooted_seeds | | Shooted seed names on which to deploy promtails that log to loki | +| Name | Mandatory | Description | +| ------------------------------------------------- | --------- | ------------------------------------------------------------- | +| gardener_logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | +| gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | +| gardener_logging_namespace | | The deployment's target namespace | +| gardener_logging_ingress_dns | | DNS for loki ingress | +| gardener_logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | +| gardener_logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | +| gardener_logging_deploy_to_garden_cluster | | Deploys Alloy also into the garden cluster (default: `true`) | +| gardener_logging_shooted_seeds | | Shooted seed names on which to deploy Alloy that logs to loki | + +### Alloy + +| Name | Mandatory | Description | +| -------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | +| gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | +| gardener_logging_alloy_port | | Alloy listen port (default: `12345`) | +| gardener_logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: HTTPS to `gardener_logging_ingress_dns`) | +| gardener_logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `gardener_logging_shooted_seed.name`) | +| gardener_logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. When set, Alloy exports its own metrics via `prometheus.exporter.self` and pushes them. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: `[]`, disabled) | +| gardener_logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | +| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | +| gardener_logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | + +Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). + +## Labels + +### Pod logs (`loki.source.kubernetes`) + +| Label | Source | +| ----------- | ------------------------------------------------------------------------------ | +| `cluster` | `gardener_logging_alloy_cluster_label` (external label) | +| `namespace` | `__meta_kubernetes_namespace` | +| `pod` | `__meta_kubernetes_pod_name` | +| `container` | `__meta_kubernetes_pod_container_name` | +| `pod_uid` | `__meta_kubernetes_pod_uid` | +| `node_name` | `__meta_kubernetes_pod_node_name` | +| `job` | `namespace/app` (from pod `app` label; empty suffix if pod has no `app` label) | + +### Kubernetes events (`loki.source.kubernetes_events`) + +| Label | Value | +| ----------- | ------------------------------------------------------- | +| `cluster` | `gardener_logging_alloy_cluster_label` (external label) | +| `job` | `kubernetes-events` | +| `namespace` | Namespace of the event | + +Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. + +## Meta-monitoring + +### Metrics + +Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Seed clusters typically do not have a local Prometheus, so metrics are pushed out via `prometheus.remote_write`. Set `gardener_logging_alloy_prometheus_write_endpoints` to enable — the typical target is the Thanos receive ingress on the metal-stack control plane: + +```yaml +gardener_logging_alloy_prometheus_write_endpoints: + - url: "https://thanos-receive.{{ metal_control_plane_ingress_dns }}/api/v1/receive" + remote_timeout: 30s + basic_auth: + username: "..." + password: "..." +``` + +Requires `monitoring_thanos_receive_ingress_enabled: true` in the monitoring role. + +### Logs + +Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki.source.kubernetes` automatically — no additional configuration is needed. + +## Migration from Promtail + +Alloy replaces Promtail as the log collector. Key differences: + +| Promtail | Alloy | +| -------------------------------------------- | -------------------------------------------------------------------------- | +| `config.clients[].url` + `basic_auth` | `gardener_logging_alloy_loki_write_endpoints[].url` + `basic_auth` | +| `-client.external-labels=cluster=…` extraArg | `gardener_logging_alloy_cluster_label` → `external_labels` in River config | +| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API | + +**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Once you have verified that logs arrive with the correct labels and dashboards show data correctly, remove the Promtail Helm releases. diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 73c96577a..7dc298dbd 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -6,6 +6,35 @@ gardener_logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" gardener_logging_garden_name: "{{ gardener_defaults_garden_name }}" gardener_logging_ingress_loki_basic_auth_user: promtail gardener_logging_ingress_loki_basic_auth_password: + +# Loki push endpoints (same format as the partition alloy role) +gardener_logging_alloy_loki_write_endpoints: + - url: "https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push" + basic_auth: + username: "{{ gardener_logging_ingress_loki_basic_auth_user }}" + password: "{{ gardener_logging_ingress_loki_basic_auth_password }}" + +# Value for the cluster= external label attached to all log streams +gardener_logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" + +gardener_logging_alloy_port: 12345 + +# Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). +# When set, Alloy scrapes its own /metrics and pushes them via remote_write. +# Typical value: the Thanos receive ingress on the metal-stack control plane. +# Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} +gardener_logging_alloy_prometheus_write_endpoints: [] + +# WAL retention for prometheus.remote_write. Controls how long undelivered samples +# are buffered on disk before being dropped during a remote endpoint outage. +# truncate_frequency: how often the WAL is compacted (default: Alloy built-in 2h) +# max_keepalive_time: samples older than this are dropped even if not yet delivered (default: Alloy built-in 8h) +gardener_logging_alloy_prometheus_wal_truncate_frequency: "2h" +gardener_logging_alloy_prometheus_wal_max_keepalive_time: "8h" + +# Full Alloy River config override. When set, bypasses the seed-alloy-config.alloy.j2 template. +# gardener_logging_alloy_config_raw: | + gardener_logging_deploy_to_garden_cluster: true gardener_logging_shooted_seeds: [] # - name: my-shooted-seed diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index 3d6ecb8f9..7bf6de49d 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -7,6 +7,21 @@ set_fact: _shoot_kubeconfig: "{{ virtual_garden_kubeconfig | string | shoot_admin_kubeconfig('garden', gardener_logging_shooted_seed.name) | from_yaml }}" +- name: Build alloy config + set_fact: + gardener_logging_alloy_config: "{{ lookup('template', 'seed-alloy-config.alloy.j2') if (gardener_logging_alloy_config_raw | default('') | length == 0) else gardener_logging_alloy_config_raw }}" + +- name: Deploy alloy + kubernetes.core.helm: + name: alloy + chart_repo_url: "{{ gardener_logging_alloy_chart_repo }}" + chart_version: "{{ gardener_logging_alloy_chart_version }}" + chart_ref: alloy + namespace: "{{ gardener_logging_namespace }}" + values: "{{ lookup('template', 'seed-alloy-values.yaml') | from_yaml }}" + kubeconfig: "{{ _shoot_kubeconfig }}" + create_namespace: true + - name: Deploy Promtail kubernetes.core.helm: name: promtail diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 252957a80..6508c4f52 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -9,6 +9,30 @@ that: - gardener_logging_promtail_chart_repo is defined - gardener_logging_promtail_chart_version is defined + - gardener_logging_alloy_chart_repo is defined + - gardener_logging_alloy_chart_version is defined + +- name: Build Alloy config for garden cluster + set_fact: + gardener_logging_alloy_config: "{{ lookup('template', 'seed-alloy-config.alloy.j2') if (gardener_logging_alloy_config_raw | default('') | length == 0) else gardener_logging_alloy_config_raw }}" + when: gardener_logging_deploy_to_garden_cluster + vars: + gardener_logging_shooted_seed: + name: "{{ gardener_logging_garden_name }}" + +- name: Deploy Alloy to garden cluster + kubernetes.core.helm: + name: alloy + chart_repo_url: "{{ gardener_logging_alloy_chart_repo }}" + chart_version: "{{ gardener_logging_alloy_chart_version }}" + chart_ref: alloy + namespace: "{{ gardener_logging_namespace }}" + values: "{{ lookup('template', 'seed-alloy-values.yaml') | from_yaml }}" + create_namespace: true + when: gardener_logging_deploy_to_garden_cluster + vars: + gardener_logging_shooted_seed: + name: "{{ gardener_logging_garden_name }}" - name: Deploy Promtail kubernetes.core.helm: diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 new file mode 100644 index 000000000..035a2e1e6 --- /dev/null +++ b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 @@ -0,0 +1,121 @@ +logging { + level = "info" + format = "logfmt" +} + +discovery.kubernetes "pods" { + role = "pod" +} + +discovery.relabel "pods" { + targets = discovery.kubernetes.pods.targets + + rule { + source_labels = ["__meta_kubernetes_namespace"] + target_label = "namespace" + } + + rule { + source_labels = ["__meta_kubernetes_pod_name"] + target_label = "pod" + } + + rule { + source_labels = ["__meta_kubernetes_pod_container_name"] + target_label = "container" + } + + rule { + source_labels = ["__meta_kubernetes_pod_uid"] + target_label = "pod_uid" + } + + rule { + source_labels = ["__meta_kubernetes_pod_node_name"] + target_label = "node_name" + } + + rule { + source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_label_app"] + separator = "/" + target_label = "job" + } +} + +loki.source.kubernetes "pods" { + targets = discovery.relabel.pods.output + forward_to = [loki.write.default.receiver] +} + +loki.source.kubernetes_events "events" { + job_name = "kubernetes-events" + log_format = "json" + forward_to = [loki.relabel.events.receiver] +} + +loki.relabel "events" { + forward_to = [loki.write.default.receiver] + + rule { + action = "labeldrop" + regex = "instance" + } +} + +// default loki endpoints +loki.write "default" { +{% for endpoint in gardener_logging_alloy_loki_write_endpoints %} + endpoint { + url = "{{ endpoint.url }}" +{% if endpoint.remote_timeout is defined %} + remote_timeout = "{{ endpoint.remote_timeout }}" +{% endif %} +{% if endpoint.basic_auth is defined %} + basic_auth { + username = "{{ endpoint.basic_auth.username }}" + password = "{{ endpoint.basic_auth.password }}" + } +{% endif %} + } +{% endfor %} + external_labels = { + cluster = "{{ gardener_logging_alloy_cluster_label }}", + } +} + +// write metrics to remote Prometheus endpoint +{% if gardener_logging_alloy_prometheus_write_endpoints %} +prometheus.exporter.self "alloy" {} + +prometheus.scrape "alloy_self" { + targets = prometheus.exporter.self.alloy.targets + forward_to = [prometheus.remote_write.default.receiver] +} + +prometheus.remote_write "default" { +{% for endpoint in gardener_logging_alloy_prometheus_write_endpoints %} + endpoint { + url = "{{ endpoint.url }}" +{% if endpoint.remote_timeout is defined %} + remote_timeout = "{{ endpoint.remote_timeout }}" +{% endif %} +{% if endpoint.basic_auth is defined %} + basic_auth { + username = "{{ endpoint.basic_auth.username }}" + password = "{{ endpoint.basic_auth.password }}" + } +{% endif %} + } +{% endfor %} + wal { + // How often to truncate the WAL. Samples older than max_keepalive_time are dropped. + truncate_frequency = "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" + // Maximum time to keep undelivered samples in the WAL before dropping them. + // Increase if you expect control-plane outages longer than the default. + max_keepalive_time = "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" + } + external_labels = { + cluster = "{{ gardener_logging_alloy_cluster_label }}", + } +} +{% endif %} diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml b/control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml new file mode 100644 index 000000000..d2ce8b4ff --- /dev/null +++ b/control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml @@ -0,0 +1,41 @@ +# Source with all the defaults: https://raw.githubusercontent.com/grafana/alloy/main/operations/helm/charts/alloy/values.yaml +alloy: + configMap: + # -- Create a new ConfigMap for the config file. + create: true + # -- Content to assign to the new ConfigMap. This is passed into `tpl` allowing for templating from values. + content: |- + {{ gardener_logging_alloy_config | indent(6) }} + + # -- Port to listen for traffic on. + listenPort: {{ gardener_logging_alloy_port }} + + # -- Path to where Alloy stores its state (e.g. log positions file for loki.source.kubernetes). + # Backed by a hostPath volume so positions survive pod restarts on the same node. + storagePath: /var/lib/alloy/data + + mounts: + extra: + - name: alloy-data + mountPath: /var/lib/alloy/data + + # -- Enables sending Grafana Labs anonymous usage stats to help improve Grafana + # Alloy. + enableReporting: false + +controller: + volumes: + extra: + - name: alloy-data + hostPath: + path: /var/lib/alloy/data + type: DirectoryOrCreate + + # -- Tolerations to apply to Grafana Alloy pods. + tolerations: + - key: node-role.kubernetes.io/master + operator: Exists + effect: NoSchedule + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 430604275..9ce2a965e 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -1,32 +1,102 @@ # logging -This role is designed to set up logging using Ansible. -The role includes tasks to install and configure the following logging tools: +Deploys the control-plane logging stack into the Kubernetes control-plane cluster. -- Loki -- Logging ingress for Loki -- Promtail for monitoring the control plane cluster +Components: + +- **Loki** — log storage and query backend +- **Alloy** — log collector (DaemonSet), collects pod logs via the Kubernetes API (`loki.source.kubernetes`) and forwards them to Loki +- Loki ingress with optional TLS and basic auth + +This role previously used Promtail as the log collector. It has been migrated to use Grafana Alloy instead. See [Migration from Promtail](#migration-from-promtail) for details. + +## Configuration + +The Alloy River config is generated from structured variables at deploy time. Override individual variables to customize behavior, or bypass the template entirely with `logging_alloy_config_raw`. ## Variables -This role uses variables from [control-plane-defaults](/control-plane). So, make sure you define them adequately as well. +This role uses variables from [control-plane-defaults](/control-plane). Make sure you define them adequately as well. You can look up all the default values of this role [here](defaults/main.yaml). + The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| --------------------------------------------- | --------- | -------------------------------------------------------------- | -| logging_chart_version | yes | Helm chart version for loki specified under release vector | -| logging_chart_repo | yes | Repository for loki specified under release vector | -| logging_promtail_chart_version | yes | Helm chart version for promtail specified under release vector | -| logging_promtail_chart_repo | yes | Repository for promtail specified under release vector | -| logging_namespace | | The deployment's target namespace | -| logging_loki_size | | The size of the volume that loki will use for storing logs | -| logging_ingress_dns | | DNS for loki ingress | -| logging_ingress_loki_tls | | If enabled, exposes loki through HTTPS on the ingress | -| logging_ingress_loki_basic_auth_password_salt | | The basic auth password salt used for stable password hashes | -| logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | -| logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | -| logging_alloy_config | | The config to use for alloy | +| Name | Mandatory | Description | +| --------------------------------------------- | --------- | ------------------------------------------------------------ | +| logging_chart_version | yes | Helm chart version for loki (release vector) | +| logging_chart_repo | yes | Repository for loki (release vector) | +| logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | +| logging_alloy_chart_repo | yes | Repository for alloy (release vector) | +| logging_namespace | | The deployment's target namespace | +| logging_loki_size | | The size of the volume that loki will use for storing logs | +| logging_ingress_dns | | DNS for loki ingress | +| logging_ingress_loki_tls | | If enabled, exposes loki through HTTPS on the ingress | +| logging_ingress_loki_basic_auth_password_salt | | The basic auth password salt used for stable password hashes | +| logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | +| logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | + +### Alloy + +| Name | Mandatory | Description | +| ----------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | +| logging_alloy_chart_repo | yes | Repository for alloy (release vector) | +| logging_alloy_port | | Alloy listen port (default: `12345`) | +| logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: `http://loki:3100/loki/api/v1/push`) | +| logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `{{ metal_control_plane_stage_name }}`) | +| logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. When set, Alloy exports its own metrics via `prometheus.exporter.self` and pushes them. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: `[]`, disabled) | +| logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | +| logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | +| logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | + +Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). + +## Labels + +### Pod logs (`loki.source.kubernetes`) + +| Label | Source | +| ----------- | ------------------------------------------------------------------------------ | +| `cluster` | `logging_alloy_cluster_label` (external label) | +| `namespace` | `__meta_kubernetes_namespace` | +| `pod` | `__meta_kubernetes_pod_name` | +| `container` | `__meta_kubernetes_pod_container_name` | +| `pod_uid` | `__meta_kubernetes_pod_uid` | +| `node_name` | `__meta_kubernetes_pod_node_name` | +| `job` | `namespace/app` (from pod `app` label; empty suffix if pod has no `app` label) | + +### Kubernetes events (`loki.source.kubernetes_events`) + +| Label | Value | +| ----------- | ---------------------------------------------- | +| `cluster` | `logging_alloy_cluster_label` (external label) | +| `job` | `kubernetes-events` | +| `namespace` | Namespace of the event | + +Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. + +## Meta-monitoring + +### Metrics + +Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. To collect them without a ServiceMonitor dependency, set `logging_alloy_prometheus_write_endpoints` — Alloy will scrape its own metrics via `prometheus.exporter.self` and push them via `prometheus.remote_write` to the configured endpoint (e.g. the in-cluster Prometheus or Thanos receive). + +### Logs + +Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki.source.kubernetes` automatically — no additional configuration is needed. + +## Migration from Promtail + +Alloy replaces Promtail as the log collector. Key differences: + +| Promtail | Alloy | +| -------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `config.clients[].url` | `logging_alloy_loki_write_endpoints[].url` | +| `-client.external-labels=cluster=…` extraArg | `logging_alloy_cluster_label` → `external_labels` in River config | +| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API, CRI framing is already stripped | +| `pipelineStages: [match(eventrouter)]` | `loki.source.kubernetes_events` (built-in, always enabled) | + +**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Once you have verified that logs arrive with the correct labels and dashboards show data correctly, remove the Promtail Helm release. diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index db868a413..c04e309d7 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -1,11 +1,33 @@ --- logging_namespace: monitoring logging_alloy_port: 12345 -logging_alloy_config: | - logging { - level = "info" - format = "logfmt" - } + +# Loki push endpoints (same format as the partition alloy role) +logging_alloy_loki_write_endpoints: + - url: "http://loki:3100/loki/api/v1/push" + # basic_auth: + # username: "promtail" + # password: "secret" + +# Value for the cluster= external label attached to all log streams +logging_alloy_cluster_label: "{{ metal_control_plane_stage_name }}" + +# Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). +# When set, Alloy scrapes its own /metrics and pushes them via remote_write. +# Typical value: the in-cluster Prometheus remote_write receiver or Thanos receive. +# Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} +logging_alloy_prometheus_write_endpoints: [] + +# WAL retention for prometheus.remote_write. Controls how long undelivered samples +# are buffered on disk before being dropped during a remote endpoint outage. +# truncate_frequency: how often the WAL is compacted (default: Alloy built-in 2h) +# max_keepalive_time: samples older than this are dropped even if not yet delivered (default: Alloy built-in 8h) +logging_alloy_prometheus_wal_truncate_frequency: "2h" +logging_alloy_prometheus_wal_max_keepalive_time: "8h" + +# Full Alloy River config override. When set, bypasses all structured vars above. +# logging_alloy_config_raw: | + logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" logging_ingress_loki_tls: true logging_ingress_loki_basic_auth_user: promtail diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index 782b51810..49b70c295 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -35,7 +35,11 @@ helm_chart_version: "{{ logging_chart_version }}" helm_value_file_template: "loki-values.yaml" -- name: Deploy alloy +- name: Build Alloy config + set_fact: + logging_alloy_config: "{{ lookup('template', 'alloy-config.alloy.j2') if (logging_alloy_config_raw | default('') | length == 0) else logging_alloy_config_raw }}" + +- name: Deploy Alloy include_role: name: ansible-common/roles/helm-chart vars: diff --git a/control-plane/roles/logging/templates/alloy-config.alloy.j2 b/control-plane/roles/logging/templates/alloy-config.alloy.j2 new file mode 100644 index 000000000..7e0491966 --- /dev/null +++ b/control-plane/roles/logging/templates/alloy-config.alloy.j2 @@ -0,0 +1,121 @@ +logging { + level = "info" + format = "logfmt" +} + +discovery.kubernetes "pods" { + role = "pod" +} + +discovery.relabel "pods" { + targets = discovery.kubernetes.pods.targets + + rule { + source_labels = ["__meta_kubernetes_namespace"] + target_label = "namespace" + } + + rule { + source_labels = ["__meta_kubernetes_pod_name"] + target_label = "pod" + } + + rule { + source_labels = ["__meta_kubernetes_pod_container_name"] + target_label = "container" + } + + rule { + source_labels = ["__meta_kubernetes_pod_uid"] + target_label = "pod_uid" + } + + rule { + source_labels = ["__meta_kubernetes_pod_node_name"] + target_label = "node_name" + } + + rule { + source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_label_app"] + separator = "/" + target_label = "job" + } +} + +loki.source.kubernetes "pods" { + targets = discovery.relabel.pods.output + forward_to = [loki.write.default.receiver] +} + +loki.source.kubernetes_events "events" { + job_name = "kubernetes-events" + log_format = "json" + forward_to = [loki.relabel.events.receiver] +} + +loki.relabel "events" { + forward_to = [loki.write.default.receiver] + + rule { + action = "labeldrop" + regex = "instance" + } +} + +// default loki endpoints +loki.write "default" { +{% for endpoint in logging_alloy_loki_write_endpoints %} + endpoint { + url = "{{ endpoint.url }}" +{% if endpoint.remote_timeout is defined %} + remote_timeout = "{{ endpoint.remote_timeout }}" +{% endif %} +{% if endpoint.basic_auth is defined %} + basic_auth { + username = "{{ endpoint.basic_auth.username }}" + password = "{{ endpoint.basic_auth.password }}" + } +{% endif %} + } +{% endfor %} + external_labels = { + cluster = "{{ logging_alloy_cluster_label }}", + } +} + +// write metrics to remote Prometheus endpoint +{% if logging_alloy_prometheus_write_endpoints %} +prometheus.exporter.self "alloy" {} + +prometheus.scrape "alloy_self" { + targets = prometheus.exporter.self.alloy.targets + forward_to = [prometheus.remote_write.default.receiver] +} + +prometheus.remote_write "default" { +{% for endpoint in logging_alloy_prometheus_write_endpoints %} + endpoint { + url = "{{ endpoint.url }}" +{% if endpoint.remote_timeout is defined %} + remote_timeout = "{{ endpoint.remote_timeout }}" +{% endif %} +{% if endpoint.basic_auth is defined %} + basic_auth { + username = "{{ endpoint.basic_auth.username }}" + password = "{{ endpoint.basic_auth.password }}" + } +{% endif %} + } +{% endfor %} + wal { + // How often to truncate the WAL. Samples older than max_keepalive_time are dropped. + truncate_frequency = "{{ logging_alloy_prometheus_wal_truncate_frequency }}" + // Maximum time to keep undelivered samples in the WAL before dropping them. + // Increase if you expect control-plane outages longer than the default. + max_keepalive_time = "{{ logging_alloy_prometheus_wal_max_keepalive_time }}" + } + external_labels = { + cluster = "{{ logging_alloy_cluster_label }}", + } +} +{% endif %} diff --git a/control-plane/roles/logging/templates/alloy-values.yaml b/control-plane/roles/logging/templates/alloy-values.yaml index 201c7a915..662b2ce97 100644 --- a/control-plane/roles/logging/templates/alloy-values.yaml +++ b/control-plane/roles/logging/templates/alloy-values.yaml @@ -1,36 +1,4 @@ -# -- Overrides the chart's name. Used to change the infix in the resource names. -nameOverride: null - -# -- Overrides the chart's namespace. -namespaceOverride: null - -# -- Overrides the chart's computed fullname. Used to change the full prefix of -# resource names. -fullnameOverride: null - -## Global properties for image pulling override the values defined under `image.registry` and `configReloader.image.registry`. -## If you want to override only one image registry, use the specific fields but if you want to override them all, use `global.image.registry` -global: - image: - # -- Global image registry to use if it needs to be overridden for some specific use cases (e.g local registries, custom images, ...) - registry: "" - - # -- Optional set of global image pull secrets. - pullSecrets: [] - - # -- Global image pull policy to apply to all containers. Overrides `image.pullPolicy` and `configReloader.image.pullPolicy`. - pullPolicy: "" - - # -- Security context to apply to the Grafana Alloy pod. - podSecurityContext: {} - -crds: - # -- Whether to install CRDs for monitoring. - create: true - -## Various Alloy settings. For backwards compatibility with the grafana-agent -## chart, this field may also be called "agent". Naming this field "agent" is -## deprecated and will be removed in a future release. +# Source with all the defaults: https://raw.githubusercontent.com/grafana/alloy/main/operations/helm/charts/alloy/values.yaml alloy: configMap: # -- Create a new ConfigMap for the config file. @@ -39,529 +7,35 @@ alloy: content: |- {{ logging_alloy_config | indent(6) }} - # -- Name of existing ConfigMap to use. Used when create is false. - name: null - # -- Key in ConfigMap to get config from. - key: null - - clustering: - # -- Deploy Alloy in a cluster to allow for load distribution. - enabled: false - - # -- Name for the Alloy cluster. Used for differentiating between clusters. - name: "" - - # -- Name for the port used for clustering, useful if running inside an Istio Mesh - portName: http - - # -- Minimum stability level of components and behavior to enable. Must be - # one of "experimental", "public-preview", or "generally-available". - stabilityLevel: "generally-available" - - # -- Path to where Grafana Alloy stores data (for example, the Write-Ahead Log). - # By default, data is lost between reboots. - storagePath: /tmp/alloy - - # -- Enables Grafana Alloy container's http server port. - enableHttpServerPort: true - - # -- Address to listen for traffic on. 0.0.0.0 exposes the UI to other - # containers. - listenAddr: 0.0.0.0 - # -- Port to listen for traffic on. listenPort: {{ logging_alloy_port }} - # -- Scheme is needed for readiness probes. If enabling tls in your configs, set to "HTTPS" - listenScheme: HTTP + # -- Path to where Alloy stores its state (e.g. log positions file for loki.source.kubernetes). + # Backed by a hostPath volume so positions survive pod restarts on the same node. + storagePath: /var/lib/alloy/data - # -- Initial delay for readiness probe. - initialDelaySeconds: 10 - - # -- Timeout for readiness probe. - timeoutSeconds: 1 - - # -- Base path where the UI is exposed. - uiPathPrefix: / + mounts: + extra: + - name: alloy-data + mountPath: /var/lib/alloy/data # -- Enables sending Grafana Labs anonymous usage stats to help improve Grafana # Alloy. - enableReporting: true - - # -- Extra environment variables to pass to the Alloy container. - extraEnv: [] - - # -- Maps all the keys on a ConfigMap or Secret as environment variables. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#envfromsource-v1-core - envFrom: [] - - # -- Extra args to pass to `alloy run`: https://grafana.com/docs/alloy/latest/reference/cli/run/ - extraArgs: [] - - # -- Extra ports to expose on the Alloy container. - # If `service.type` is `NodePort`, each item may set `nodePort` to choose the Service NodePort for that port. - extraPorts: [] - # - name: "faro" - # nodePort: 31129 - # port: 12347 - # targetPort: 12347 - # protocol: "TCP" - # appProtocol: "h2c" - - # -- Host aliases to add to the Alloy container. - hostAliases: [] - # - ip: "20.21.22.23" - # hostnames: - # - "company.grafana.net" - - mounts: - # -- Mount /var/log from the host into the container for log collection. - varlog: false - # -- Mount /var/lib/docker/containers from the host into the container for log - # collection. - dockercontainers: false - - # -- Extra volume mounts to add into the Grafana Alloy container. Does not - # affect the watch container. - extra: [] - - # -- Security context to apply to the Grafana Alloy container. - securityContext: {} - - # -- Resource requests and limits to apply to the Grafana Alloy container. - resources: {} - - # -- Set lifecycle hooks for the Grafana Alloy container. - lifecycle: {} - # preStop: - # exec: - # command: - # - /bin/sleep - # - "10" - - # -- Set livenessProbe for the Grafana Alloy container. - livenessProbe: {} - -image: - # -- Grafana Alloy image registry (defaults to docker.io) - registry: "docker.io" - # -- Grafana Alloy image repository. - repository: grafana/alloy - # -- (string) Grafana Alloy image tag. When empty, the Chart's appVersion is - # used. - tag: null - # -- Grafana Alloy image's SHA256 digest (either in format "sha256:XYZ" or "XYZ"). When set, will override `image.tag`. - digest: null - # -- Grafana Alloy image pull policy. - pullPolicy: IfNotPresent - # -- Optional set of image pull secrets. - pullSecrets: [] - -rbac: - # -- Whether to create RBAC resources for Alloy. - create: true - - # -- If set, only create Roles and RoleBindings in the given list of namespaces, rather than ClusterRoles and - # ClusterRoleBindings. If not using ClusterRoles, bear in mind that Alloy will not be able to discover cluster-scoped - # resources such as Nodes. - namespaces: [] - - # -- The rules to create for the ClusterRole or Role objects. - rules: - # -- Rules required for the `discovery.kubernetes` component. - - apiGroups: ["", "discovery.k8s.io", "networking.k8s.io"] - resources: ["endpoints", "endpointslices", "ingresses", "pods", "services"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `loki.source.kubernetes` component. - - apiGroups: [""] - resources: ["pods", "pods/log", "namespaces"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `loki.source.podlogs` component. - - apiGroups: ["monitoring.grafana.com"] - resources: ["podlogs"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `mimir.rules.kubernetes` component. - - apiGroups: ["monitoring.coreos.com"] - resources: ["prometheusrules"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `mimir.alerts.kubernetes` component. - - apiGroups: ["monitoring.coreos.com"] - resources: ["alertmanagerconfigs"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `prometheus.operator.*` components. - - apiGroups: ["monitoring.coreos.com"] - resources: ["podmonitors", "servicemonitors", "probes", "scrapeconfigs"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `loki.source.kubernetes_events` component. - - apiGroups: [""] - resources: ["events"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `remote.kubernetes.*` components. - - apiGroups: [""] - resources: ["configmaps", "secrets"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `otelcol.processor.k8sattributes` component. - - apiGroups: ["apps", "extensions"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - - # -- The rules to create for the ClusterRole objects. - clusterRules: - # -- Rules required for the Nodes role in the `discovery.kubernetes` component. - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "list", "watch"] - # -- Rules required for the `discovery.kubelet` component. - - apiGroups: [""] - resources: ["nodes/pods"] - verbs: ["get", "list", "watch"] - # -- Rules required accessing metric endpoints on the Node (e.g. Kubelet, cAdvisor, etc...). - - apiGroups: [""] - resources: ["nodes/metrics"] - verbs: ["get", "list", "watch"] - # -- Rules required for accessing metrics endpoint. - - nonResourceURLs: ["/metrics"] - verbs: ["get"] - -serviceAccount: - # -- Whether to create a service account for the Grafana Alloy deployment. - create: true - # -- Additional labels to add to the created service account. - additionalLabels: {} - # -- Annotations to add to the created service account. - annotations: {} - # -- The name of the existing service account to use when - # serviceAccount.create is false. - name: null - # Whether the Alloy pod should automatically mount the service account token. - automountServiceAccountToken: true - -# Options for the extra controller used for config reloading. -configReloader: - # -- Enables automatically reloading when the Alloy config changes. - enabled: true - image: - # -- Config reloader image registry (defaults to docker.io) - registry: "quay.io" - # -- Repository to get config reloader image from. - repository: prometheus-operator/prometheus-config-reloader - # -- Tag of image to use for config reloading. - tag: v0.81.0 - # -- SHA256 digest of image to use for config reloading (either in format "sha256:XYZ" or "XYZ"). When set, will override `configReloader.image.tag` - digest: "" - # -- Config reloader image pull policy. - pullPolicy: IfNotPresent - # -- Override the args passed to the container. - customArgs: [] - # -- Resource requests and limits to apply to the config reloader container. - resources: - requests: - cpu: "10m" - memory: "50Mi" - # -- Security context to apply to the Grafana configReloader container. - securityContext: {} + enableReporting: false controller: - # -- Type of controller to use for deploying Grafana Alloy in the cluster. - # Must be one of 'daemonset', 'deployment', or 'statefulset'. - type: 'daemonset' - - # -- Number of pods to deploy. Ignored when controller.type is 'daemonset'. - replicas: 1 - - # -- Extra labels to add to the controller. - extraLabels: {} - - # -- Annotations to add to controller. - extraAnnotations: {} - - # -- Whether to deploy pods in parallel. Only used when controller.type is - # 'statefulset'. - parallelRollout: true - - # -- How many additional seconds to wait before considering a pod ready. - minReadySeconds: 10 - - # -- Configures Pods to use the host network. When set to true, the ports that will be used must be specified. - hostNetwork: false - - # -- Configures Pods to use the host PID namespace. - hostPID: false - - # -- Configures the DNS policy for the pod. https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy - dnsPolicy: ClusterFirst - - # -- Termination grace period in seconds for the Grafana Alloy pods. - # The default value used by Kubernetes if unspecifed is 30 seconds. - terminationGracePeriodSeconds: null - - # -- The maximum number of revisions that will be maintained in the Controllers's revision history. The history consists of all revisions not represented by a currently applied reversion. - revisionHistoryLimit: 10 - - # -- Update strategy for updating deployed Pods. - updateStrategy: {} - - # -- nodeSelector to apply to Grafana Alloy pods. - nodeSelector: {} - - # -- Tolerations to apply to Grafana Alloy pods. - tolerations: [] - - # -- Topology Spread Constraints to apply to Grafana Alloy pods. - topologySpreadConstraints: [] - - # -- priorityClassName to apply to Grafana Alloy pods. - priorityClassName: '' - - # -- Extra pod annotations to add. - podAnnotations: {} - - # -- Extra pod labels to add. - podLabels: {} - - # -- PodDisruptionBudget configuration. - podDisruptionBudget: - # -- Whether to create a PodDisruptionBudget for the controller. - enabled: false - # -- Minimum number of pods that must be available during a disruption. - # Note: Only one of minAvailable or maxUnavailable should be set. - minAvailable: null - # -- Maximum number of pods that can be unavailable during a disruption. - # Note: Only one of minAvailable or maxUnavailable should be set. - maxUnavailable: null - - # -- Whether to enable automatic deletion of stale PVCs due to a scale down operation, when controller.type is 'statefulset'. - enableStatefulSetAutoDeletePVC: false - - autoscaling: - # -- Creates a HorizontalPodAutoscaler for controller type deployment. - # Deprecated: Please use controller.autoscaling.horizontal instead - enabled: false - # -- The lower limit for the number of replicas to which the autoscaler can scale down. - minReplicas: 1 - # -- The upper limit for the number of replicas to which the autoscaler can scale up. - maxReplicas: 5 - # -- Average CPU utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetCPUUtilizationPercentage` to 0 will disable CPU scaling. - targetCPUUtilizationPercentage: 0 - # -- Average Memory utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetMemoryUtilizationPercentage` to 0 will disable Memory scaling. - targetMemoryUtilizationPercentage: 80 - - scaleDown: - # -- List of policies to determine the scale-down behavior. - policies: [] - # - type: Pods - # value: 4 - # periodSeconds: 60 - # -- Determines which of the provided scaling-down policies to apply if multiple are specified. - selectPolicy: Max - # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling down. - stabilizationWindowSeconds: 300 - - scaleUp: - # -- List of policies to determine the scale-up behavior. - policies: [] - # - type: Pods - # value: 4 - # periodSeconds: 60 - # -- Determines which of the provided scaling-up policies to apply if multiple are specified. - selectPolicy: Max - # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling up. - stabilizationWindowSeconds: 0 - - # -- Configures the Horizontal Pod Autoscaler for the controller. - horizontal: - # -- Enables the Horizontal Pod Autoscaler for the controller. - enabled: false - - # -- The lower limit for the number of replicas to which the autoscaler can scale down. - minReplicas: 1 - # -- The upper limit for the number of replicas to which the autoscaler can scale up. - maxReplicas: 5 - # -- Average CPU utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetCPUUtilizationPercentage` to 0 will disable CPU scaling. - targetCPUUtilizationPercentage: 0 - # -- Average Memory utilization across all relevant pods, a percentage of the requested value of the resource for the pods. Setting `targetMemoryUtilizationPercentage` to 0 will disable Memory scaling. - targetMemoryUtilizationPercentage: 80 - - scaleDown: - # -- List of policies to determine the scale-down behavior. - policies: [] - # - type: Pods - # value: 4 - # periodSeconds: 60 - # -- Determines which of the provided scaling-down policies to apply if multiple are specified. - selectPolicy: Max - # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling down. - stabilizationWindowSeconds: 300 - - scaleUp: - # -- List of policies to determine the scale-up behavior. - policies: [] - # - type: Pods - # value: 4 - # periodSeconds: 60 - # -- Determines which of the provided scaling-up policies to apply if multiple are specified. - selectPolicy: Max - # -- The duration that the autoscaling mechanism should look back on to make decisions about scaling up. - stabilizationWindowSeconds: 0 - # -- Configures the Vertical Pod Autoscaler for the controller. - vertical: - # -- Enables the Vertical Pod Autoscaler for the controller. - enabled: false - - # -- List of recommenders to use for the Vertical Pod Autoscaler. - # Recommenders are responsible for generating recommendation for the object. - # List should be empty (then the default recommender will generate the recommendation) - # or contain exactly one recommender. - recommenders: [] - # recommenders: - # - name: custom-recommender-performance - - # -- Configures the resource policy for the Vertical Pod Autoscaler. - resourcePolicy: - # -- Configures the container policies for the Vertical Pod Autoscaler. - containerPolicies: - - containerName: alloy - # -- The controlled resources for the Vertical Pod Autoscaler. - controlledResources: - - cpu - - memory - # -- The controlled values for the Vertical Pod Autoscaler. Needs to be either RequestsOnly or RequestsAndLimits. - controlledValues: "RequestsAndLimits" - # -- The maximum allowed values for the pods. - maxAllowed: {} - # cpu: 200m - # memory: 100Mi - # -- Defines the min allowed resources for the pod - minAllowed: {} - # cpu: 200m - # memory: 100Mi - - # -- Configures the update policy for the Vertical Pod Autoscaler. - updatePolicy: - # -- Specifies minimal number of replicas which need to be alive for VPA Updater to attempt pod eviction - # minReplicas: 1 - # -- Specifies whether recommended updates are applied when a Pod is started and whether recommended updates - # are applied during the life of a Pod. Possible values are "Off", "Initial", "Recreate", and "Auto". - # updateMode: Auto - - # -- Affinity configuration for pods. - affinity: {} - volumes: - # -- Extra volumes to add to the Grafana Alloy pod. - extra: [] - - # -- volumeClaimTemplates to add when controller.type is 'statefulset'. - volumeClaimTemplates: [] - - ## -- Additional init containers to run. - ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ - ## - initContainers: [] - - # -- Additional containers to run alongside the Alloy container and initContainers. - extraContainers: [] - -networkPolicy: - enabled: false - flavor: kubernetes + extra: + - name: alloy-data + hostPath: + path: /var/lib/alloy/data + type: DirectoryOrCreate - policyTypes: - - Ingress - - Egress - - # Default allow all traffic because Alloy is so configurable - # It is recommended to change this before deploying to production - # To disable each policyType, set value to `null` - ingress: - - {} - egress: - - {} - -service: - # -- Creates a Service for the controller's pods. - enabled: true - # -- Service type - type: ClusterIP - # -- NodePort port. Only takes effect when `service.type: NodePort` - nodePort: 31128 - # -- Cluster IP, can be set to None, empty "" or an IP address - clusterIP: '' - # -- Value for internal traffic policy. 'Cluster' or 'Local' - internalTrafficPolicy: Cluster - annotations: {} - # cloud.google.com/load-balancer-type: Internal - -serviceMonitor: - enabled: false - # -- Additional labels for the service monitor. - additionalLabels: {} - # -- Scrape interval. If not set, the Prometheus default scrape interval is used. - interval: "" - # -- MetricRelabelConfigs to apply to samples after scraping, but before ingestion. - # ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#relabelconfig - metricRelabelings: [] - # - action: keep - # regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+' - # sourceLabels: [__name__] - - # -- Customize tls parameters for the service monitor - tlsConfig: {} - - # -- RelabelConfigs to apply to samples before scraping - # ref: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#relabelconfig - relabelings: [] - # - sourceLabels: [__meta_kubernetes_pod_node_name] - # separator: ; - # regex: ^(.*)$ - # targetLabel: nodename - # replacement: $1 - # action: replace -ingress: - # -- Enables ingress for Alloy (Faro port) - enabled: false - # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName - # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress - # ingressClassName: nginx - # Values can be templated - annotations: - {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - labels: {} - path: / - faroPort: 12347 - - # pathType is only for k8s >= 1.1= - pathType: Prefix - - hosts: - - chart-example.local - ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services. - extraPaths: [] - # - path: /* - # backend: - # serviceName: ssl-redirect - # servicePort: use-annotation - ## Or for k8s > 1.19 - # - path: /* - # pathType: Prefix - # backend: - # service: - # name: ssl-redirect - # port: - # name: use-annotation - - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -# -- Extra k8s manifests to deploy -extraObjects: [] -# - apiVersion: v1 -# kind: Secret -# metadata: -# name: grafana-cloud -# stringData: -# PROMETHEUS_HOST: 'https://prometheus-us-central1.grafana.net/api/prom/push' -# PROMETHEUS_USERNAME: '123456' + # -- Tolerations to apply to Grafana Alloy pods. + tolerations: + - key: node-role.kubernetes.io/master + operator: Exists + effect: NoSchedule + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule From d9a422b3f394ac93de608160b4f5a9ac772dbf8c Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Fri, 8 May 2026 15:30:57 +0200 Subject: [PATCH 03/24] docs: improve migration recommendations --- control-plane/roles/gardener-logging/README.md | 6 +++++- control-plane/roles/logging/README.md | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 5bc0636a2..86799efce 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -100,4 +100,8 @@ Alloy replaces Promtail as the log collector. Key differences: | `-client.external-labels=cluster=…` extraArg | `gardener_logging_alloy_cluster_label` → `external_labels` in River config | | `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API | -**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Once you have verified that logs arrive with the correct labels and dashboards show data correctly, remove the Promtail Helm releases. +**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm releases, verify: +- Logs arrive correctly in Loki +- Dashboards that filter by log labels (e.g. `job`, `app`) still work — the label set has changed, see [Labels](#labels) +- Alerts that query log streams by label still fire as expected +- Any custom LogQL queries saved in Grafana still return results diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 9ce2a965e..93b5a6296 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -99,4 +99,8 @@ Alloy replaces Promtail as the log collector. Key differences: | `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API, CRI framing is already stripped | | `pipelineStages: [match(eventrouter)]` | `loki.source.kubernetes_events` (built-in, always enabled) | -**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Once you have verified that logs arrive with the correct labels and dashboards show data correctly, remove the Promtail Helm release. +**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm release, verify: +- Logs arrive correctly in Loki +- Dashboards that filter by log labels (e.g. `job`, `app`) still work — the label set has changed, see [Labels](#labels) +- Alerts that query log streams by label still fire as expected +- Any custom LogQL queries saved in Grafana still return results From a4f3ea7d27fef6a8cd6e3365e1bbc0fb37cdc96e Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Fri, 8 May 2026 15:41:01 +0200 Subject: [PATCH 04/24] fix: add app label --- control-plane/roles/gardener-logging/README.md | 2 ++ .../gardener-logging/templates/seed-alloy-config.alloy.j2 | 5 +++++ control-plane/roles/logging/README.md | 2 ++ control-plane/roles/logging/templates/alloy-config.alloy.j2 | 5 +++++ 4 files changed, 14 insertions(+) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 86799efce..f7922fe11 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -57,6 +57,7 @@ Alloy's positions file (tracking the read offset for each container log) is pers | `container` | `__meta_kubernetes_pod_container_name` | | `pod_uid` | `__meta_kubernetes_pod_uid` | | `node_name` | `__meta_kubernetes_pod_node_name` | +| `app` | `__meta_kubernetes_pod_label_app` (empty if pod has no `app` label) | | `job` | `namespace/app` (from pod `app` label; empty suffix if pod has no `app` label) | ### Kubernetes events (`loki.source.kubernetes_events`) @@ -101,6 +102,7 @@ Alloy replaces Promtail as the log collector. Key differences: | `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API | **Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm releases, verify: + - Logs arrive correctly in Loki - Dashboards that filter by log labels (e.g. `job`, `app`) still work — the label set has changed, see [Labels](#labels) - Alerts that query log streams by label still fire as expected diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 index 035a2e1e6..ec73122bb 100644 --- a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 +++ b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 @@ -35,6 +35,11 @@ discovery.relabel "pods" { target_label = "node_name" } + rule { + source_labels = ["__meta_kubernetes_pod_label_app"] + target_label = "app" + } + rule { source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_label_app"] separator = "/" diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 93b5a6296..1a9c062d1 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -66,6 +66,7 @@ Alloy's positions file (tracking the read offset for each container log) is pers | `container` | `__meta_kubernetes_pod_container_name` | | `pod_uid` | `__meta_kubernetes_pod_uid` | | `node_name` | `__meta_kubernetes_pod_node_name` | +| `app` | `__meta_kubernetes_pod_label_app` (empty if pod has no `app` label) | | `job` | `namespace/app` (from pod `app` label; empty suffix if pod has no `app` label) | ### Kubernetes events (`loki.source.kubernetes_events`) @@ -100,6 +101,7 @@ Alloy replaces Promtail as the log collector. Key differences: | `pipelineStages: [match(eventrouter)]` | `loki.source.kubernetes_events` (built-in, always enabled) | **Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm release, verify: + - Logs arrive correctly in Loki - Dashboards that filter by log labels (e.g. `job`, `app`) still work — the label set has changed, see [Labels](#labels) - Alerts that query log streams by label still fire as expected diff --git a/control-plane/roles/logging/templates/alloy-config.alloy.j2 b/control-plane/roles/logging/templates/alloy-config.alloy.j2 index 7e0491966..74e457143 100644 --- a/control-plane/roles/logging/templates/alloy-config.alloy.j2 +++ b/control-plane/roles/logging/templates/alloy-config.alloy.j2 @@ -35,6 +35,11 @@ discovery.relabel "pods" { target_label = "node_name" } + rule { + source_labels = ["__meta_kubernetes_pod_label_app"] + target_label = "app" + } + rule { source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_label_app"] separator = "/" From 17344c471884d2ce2924afef9bbbc44585d538b5 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 11 May 2026 10:27:40 +0200 Subject: [PATCH 05/24] fix: use correct defaults for prometheus write endpoints --- .../roles/gardener-logging/README.md | 35 +++++++------------ .../roles/gardener-logging/defaults/main.yaml | 14 +++++--- control-plane/roles/logging/README.md | 30 +++++++++------- .../roles/logging/defaults/main.yaml | 13 ++++--- control-plane/roles/monitoring/README.md | 26 +++++++++++++- .../roles/monitoring/defaults/main.yaml | 4 ++- .../roles/monitoring/tasks/main.yaml | 11 ++++++ .../monitoring/templates/thanos-values.yaml | 10 +++--- 8 files changed, 90 insertions(+), 53 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index f7922fe11..38029210e 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -31,17 +31,17 @@ The following variables can be set to configure the role: ### Alloy -| Name | Mandatory | Description | -| -------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| gardener_logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | -| gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | -| gardener_logging_alloy_port | | Alloy listen port (default: `12345`) | -| gardener_logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: HTTPS to `gardener_logging_ingress_dns`) | -| gardener_logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `gardener_logging_shooted_seed.name`) | -| gardener_logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. When set, Alloy exports its own metrics via `prometheus.exporter.self` and pushes them. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: `[]`, disabled) | -| gardener_logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | -| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | -| gardener_logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Description | +| -------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | +| gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | +| gardener_logging_alloy_port | | Alloy listen port (default: `12345`) | +| gardener_logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Default: `https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push` with `basic_auth` using `gardener_logging_ingress_loki_basic_auth_user/password`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `gardener_logging_shooted_seed.name`) | +| gardener_logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. Default: Thanos receive ingress (`{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive`). Requires `monitoring_thanos_receive_ingress_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | +| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | +| gardener_logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -74,18 +74,7 @@ Alloy watches events in all namespaces, which requires cluster-scope RBAC. The A ### Metrics -Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Seed clusters typically do not have a local Prometheus, so metrics are pushed out via `prometheus.remote_write`. Set `gardener_logging_alloy_prometheus_write_endpoints` to enable — the typical target is the Thanos receive ingress on the metal-stack control plane: - -```yaml -gardener_logging_alloy_prometheus_write_endpoints: - - url: "https://thanos-receive.{{ metal_control_plane_ingress_dns }}/api/v1/receive" - remote_timeout: 30s - basic_auth: - username: "..." - password: "..." -``` - -Requires `monitoring_thanos_receive_ingress_enabled: true` in the monitoring role. +Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Seed clusters have no local Prometheus, so metrics are pushed to the control-plane Thanos Receive ingress. The endpoint and credentials are wired automatically when `monitoring_thanos_receive_ingress_enabled: true` — credentials are taken from `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password` in the monitoring role. Override `gardener_logging_alloy_prometheus_write_endpoints` only if you need custom credentials or a different URL. ### Logs diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 7dc298dbd..11df459de 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -7,7 +7,7 @@ gardener_logging_garden_name: "{{ gardener_defaults_garden_name }}" gardener_logging_ingress_loki_basic_auth_user: promtail gardener_logging_ingress_loki_basic_auth_password: -# Loki push endpoints (same format as the partition alloy role) +# Loki push endpoints. gardener_logging_alloy_loki_write_endpoints: - url: "https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push" basic_auth: @@ -20,10 +20,16 @@ gardener_logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" gardener_logging_alloy_port: 12345 # Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). -# When set, Alloy scrapes its own /metrics and pushes them via remote_write. -# Typical value: the Thanos receive ingress on the metal-stack control plane. +# Alloy scrapes its own /metrics and pushes them via remote_write. +# Seed clusters have no local Prometheus, so metrics are pushed to the control-plane Thanos Receive. # Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -gardener_logging_alloy_prometheus_write_endpoints: [] +# Endpoint is included automatically when monitoring_thanos_receive_ingress_enabled: true. +# Credentials come from monitoring_thanos_receive_ingress_basic_auth_user/password (monitoring role). +gardener_logging_alloy_prometheus_write_endpoints: >- + {{ [{'url': 'https://' ~ monitoring_thanos_receive_ingress_dns ~ '/api/v1/receive', + 'basic_auth': {'username': monitoring_thanos_receive_ingress_basic_auth_user, + 'password': monitoring_thanos_receive_ingress_basic_auth_password}}] + if monitoring_thanos_receive_ingress_enabled | default(false) | bool else [] }} # WAL retention for prometheus.remote_write. Controls how long undelivered samples # are buffered on disk before being dropped during a remote endpoint outage. diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 1a9c062d1..719d9e182 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -40,17 +40,17 @@ The following variables can be set to configure the role: ### Alloy -| Name | Mandatory | Description | -| ----------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | -| logging_alloy_chart_repo | yes | Repository for alloy (release vector) | -| logging_alloy_port | | Alloy listen port (default: `12345`) | -| logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: `http://loki:3100/loki/api/v1/push`) | -| logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `{{ metal_control_plane_stage_name }}`) | -| logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. When set, Alloy exports its own metrics via `prometheus.exporter.self` and pushes them. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` (default: `[]`, disabled) | -| logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | -| logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | -| logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Description | +| ----------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | +| logging_alloy_chart_repo | yes | Repository for alloy (release vector) | +| logging_alloy_port | | Alloy listen port (default: `12345`) | +| logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Default: `[{url: "http://loki:3100/loki/api/v1/push"}]` (in-cluster Loki). Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `{{ metal_control_plane_stage_name }}`) | +| logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. Auto-populated with in-cluster Thanos Receive when `monitoring_thanos_receive_enabled: true`, otherwise `[]`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | +| logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | +| logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -83,7 +83,13 @@ Alloy watches events in all namespaces, which requires cluster-scope RBAC. The A ### Metrics -Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. To collect them without a ServiceMonitor dependency, set `logging_alloy_prometheus_write_endpoints` — Alloy will scrape its own metrics via `prometheus.exporter.self` and push them via `prometheus.remote_write` to the configured endpoint (e.g. the in-cluster Prometheus or Thanos receive). +Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. When `monitoring_thanos_receive_enabled: true` in the monitoring role, Alloy automatically pushes metrics to the in-cluster Thanos Receive. Otherwise the list is empty and self-metrics are disabled. Override to customise: + +```yaml +logging_alloy_prometheus_write_endpoints: + # automatically included when monitoring_thanos_receive_enabled: true + - url: "http://thanos-receive.{{ logging_namespace }}.svc.cluster.local:19291/api/v1/receive" +``` ### Logs diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index c04e309d7..95cff41ec 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -2,21 +2,20 @@ logging_namespace: monitoring logging_alloy_port: 12345 -# Loki push endpoints (same format as the partition alloy role) +# Loki push endpoints. logging_alloy_loki_write_endpoints: - url: "http://loki:3100/loki/api/v1/push" - # basic_auth: - # username: "promtail" - # password: "secret" # Value for the cluster= external label attached to all log streams logging_alloy_cluster_label: "{{ metal_control_plane_stage_name }}" # Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). -# When set, Alloy scrapes its own /metrics and pushes them via remote_write. -# Typical value: the in-cluster Prometheus remote_write receiver or Thanos receive. +# Alloy scrapes its own /metrics and pushes them via remote_write. # Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -logging_alloy_prometheus_write_endpoints: [] +# Thanos Receive endpoint is included automatically when monitoring_thanos_receive_enabled: true. +logging_alloy_prometheus_write_endpoints: >- + {{ [{'url': 'http://thanos-receive.' ~ logging_namespace ~ '.svc.cluster.local:19291/api/v1/receive'}] + if monitoring_thanos_receive_enabled | default(false) | bool else [] }} # WAL retention for prometheus.remote_write. Controls how long undelivered samples # are buffered on disk before being dropped during a remote endpoint outage. diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index 1be572ba3..bd810ef5c 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -65,7 +65,8 @@ The following variables can be set to configure the role: | monitoring_thanos_receive_enabled | | Enable Thanos Receive component | | monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | | monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | -| monitoring_thanos_receive_ingress_basic_auth | | Set basic authentication on the Ingress for Thanos Receive | +| monitoring_thanos_receive_ingress_basic_auth_user | | Basic auth username for the Thanos Receive ingress (default: `thanos-receive`). Used by nginx and automatically by `gardener-logging` Alloy for `prometheus.remote_write`. | +| monitoring_thanos_receive_ingress_basic_auth_password | | Basic auth password for the Thanos Receive ingress. When set, nginx basic auth is enabled and the htpasswd entry is generated automatically. Also used by `gardener-logging` Alloy for `prometheus.remote_write`. | | monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | | monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | | monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | @@ -83,3 +84,26 @@ The following variables can be set to configure the role: | monitoring_gardener_metrics_exporter_image_name | | gardener-metrics-exporter image name | | monitoring_gardener_metrics_exporter_image_tag | | gardener-metrics-exporter image tag | | monitoring_gardener_virtual_garden_kubeconfig | | The kubeconfig for the kube-apiserver of the virtual garden | + +## Migration + +### `monitoring_thanos_receive_ingress_basic_auth` removed + +The old `monitoring_thanos_receive_ingress_basic_auth` variable (a raw htpasswd-format string) has been replaced by `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password`. The htpasswd entry for nginx is now generated automatically from these, following the same pattern as Alertmanager. + +> **The role will fail immediately** with a clear error message if the old variable is still set. Nothing is deployed until you migrate. + +**Before:** + +```yaml +monitoring_thanos_receive_ingress_basic_auth: "myuser:$apr1$..." +``` + +**After:** + +```yaml +monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-receive +monitoring_thanos_receive_ingress_basic_auth_password: mysecret +``` + +The `gardener-logging` role automatically picks up these credentials for Alloy's `prometheus.remote_write` when `monitoring_thanos_receive_ingress_enabled: true`. diff --git a/control-plane/roles/monitoring/defaults/main.yaml b/control-plane/roles/monitoring/defaults/main.yaml index fbd775dcb..120818713 100644 --- a/control-plane/roles/monitoring/defaults/main.yaml +++ b/control-plane/roles/monitoring/defaults/main.yaml @@ -70,7 +70,9 @@ monitoring_thanos_receive_enabled: false monitoring_thanos_receive_ingress_enabled: false monitoring_thanos_receive_ingress_dns: "thanos-receive.{{ metal_control_plane_ingress_dns }}" monitoring_thanos_receive_ingress_annotations: [] -monitoring_thanos_receive_ingress_basic_auth: {} +monitoring_thanos_receive_ingress_basic_auth_user: thanos-receive +monitoring_thanos_receive_ingress_basic_auth_password: +monitoring_thanos_receive_ingress_basic_auth_password_salt: "{{ monitoring_thanos_receive_ingress_basic_auth_password | hash('sha1') | truncate(22, True, '') }}" monitoring_thanos_receive_ingress_tls: {} monitoring_thanos_receive_size: 50Gi monitoring_thanos_receive_resource_preset: "large" diff --git a/control-plane/roles/monitoring/tasks/main.yaml b/control-plane/roles/monitoring/tasks/main.yaml index 46e59b483..512aa39b0 100644 --- a/control-plane/roles/monitoring/tasks/main.yaml +++ b/control-plane/roles/monitoring/tasks/main.yaml @@ -14,6 +14,17 @@ - gardener_metrics_exporter_image_name is defined - gardener_metrics_exporter_image_tag is defined +- name: Check deprecated variables are not set + assert: + fail_msg: > + 'monitoring_thanos_receive_ingress_basic_auth' has been removed. + Please migrate to 'monitoring_thanos_receive_ingress_basic_auth_user' and + 'monitoring_thanos_receive_ingress_basic_auth_password' — the htpasswd entry + is now generated automatically. See the Migration section in the role README. + quiet: yes + that: + - monitoring_thanos_receive_ingress_basic_auth is not defined or monitoring_thanos_receive_ingress_basic_auth == {} + - name: Create namespace {{ monitoring_namespace }} k8s: definition: diff --git a/control-plane/roles/monitoring/templates/thanos-values.yaml b/control-plane/roles/monitoring/templates/thanos-values.yaml index c2813031d..12ea33f42 100644 --- a/control-plane/roles/monitoring/templates/thanos-values.yaml +++ b/control-plane/roles/monitoring/templates/thanos-values.yaml @@ -89,14 +89,14 @@ receive: enabled: {{ monitoring_thanos_receive_ingress_enabled }} hostname: {{ monitoring_thanos_receive_ingress_dns }} ingressClassName: nginx -{% if monitoring_thanos_receive_ingress_annotations or monitoring_thanos_receive_ingress_basic_auth or monitoring_thanos_receive_ingress_tls %} +{% if monitoring_thanos_receive_ingress_annotations or monitoring_thanos_receive_ingress_basic_auth_password or monitoring_thanos_receive_ingress_tls %} annotations: {% endif %} {% if monitoring_thanos_receive_ingress_tls %} nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/ssl-redirect: "true" {% endif %} -{% if monitoring_thanos_receive_ingress_basic_auth %} +{% if monitoring_thanos_receive_ingress_basic_auth_password %} nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: thanos-receive-basic-auth {% endif %} @@ -119,13 +119,13 @@ receive: replicaCount: 2 -{% if monitoring_thanos_receive_ingress_basic_auth %} +{% if monitoring_thanos_receive_ingress_basic_auth_password %} extraDeploy: - apiVersion: v1 kind: Secret metadata: name: thanos-receive-basic-auth type: Opaque - data: - auth: "{{ monitoring_thanos_receive_ingress_basic_auth | b64encode }}" + stringData: + auth: "{{ monitoring_thanos_receive_ingress_basic_auth_user ~ ':' ~ monitoring_thanos_receive_ingress_basic_auth_password | string | password_hash('bcrypt', salt=monitoring_thanos_receive_ingress_basic_auth_password_salt) }}" {% endif %} From 1d124195b5241564c91bb43fe199f0e6f2de9fdf Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 11 May 2026 12:14:18 +0200 Subject: [PATCH 06/24] fix: avoid default cluster=loki label --- .../roles/gardener-logging/README.md | 24 ++++++++--------- .../roles/gardener-logging/defaults/main.yaml | 9 ++++--- .../roles/gardener-logging/meta/main.yaml | 2 +- .../templates/seed-alloy-config.alloy.j2 | 26 ++++++++++++++----- control-plane/roles/logging/README.md | 26 +++++++++---------- .../roles/logging/defaults/main.yaml | 2 +- .../logging/templates/alloy-config.alloy.j2 | 26 ++++++++++++++----- 7 files changed, 72 insertions(+), 43 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 38029210e..f08077bb2 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -37,7 +37,7 @@ The following variables can be set to configure the role: | gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | | gardener_logging_alloy_port | | Alloy listen port (default: `12345`) | | gardener_logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Default: `https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push` with `basic_auth` using `gardener_logging_ingress_loki_basic_auth_user/password`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `gardener_logging_shooted_seed.name`) | +| gardener_logging_alloy_cluster_label | | Value for the `cluster=` label set on all log and metric streams via relabel rules (default: `gardener_logging_shooted_seed.name`) | | gardener_logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. Default: Thanos receive ingress (`{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive`). Requires `monitoring_thanos_receive_ingress_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | | gardener_logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | | gardener_logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | @@ -51,7 +51,7 @@ Alloy's positions file (tracking the read offset for each container log) is pers | Label | Source | | ----------- | ------------------------------------------------------------------------------ | -| `cluster` | `gardener_logging_alloy_cluster_label` (external label) | +| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | | `namespace` | `__meta_kubernetes_namespace` | | `pod` | `__meta_kubernetes_pod_name` | | `container` | `__meta_kubernetes_pod_container_name` | @@ -62,11 +62,11 @@ Alloy's positions file (tracking the read offset for each container log) is pers ### Kubernetes events (`loki.source.kubernetes_events`) -| Label | Value | -| ----------- | ------------------------------------------------------- | -| `cluster` | `gardener_logging_alloy_cluster_label` (external label) | -| `job` | `kubernetes-events` | -| `namespace` | Namespace of the event | +| Label | Value | +| ----------- | ----------------------------------------------------------------------- | +| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | +| `job` | `kubernetes-events` | +| `namespace` | Namespace of the event | Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. @@ -84,11 +84,11 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. Alloy replaces Promtail as the log collector. Key differences: -| Promtail | Alloy | -| -------------------------------------------- | -------------------------------------------------------------------------- | -| `config.clients[].url` + `basic_auth` | `gardener_logging_alloy_loki_write_endpoints[].url` + `basic_auth` | -| `-client.external-labels=cluster=…` extraArg | `gardener_logging_alloy_cluster_label` → `external_labels` in River config | -| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API | +| Promtail | Alloy | +| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| `config.clients[].url` + `basic_auth` | `gardener_logging_alloy_loki_write_endpoints[].url` + `basic_auth` | +| `-client.external-labels=cluster=…` extraArg | `gardener_logging_alloy_cluster_label` → relabel rule in `discovery.relabel` / `loki.relabel` / `prometheus.relabel` | +| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API | **Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm releases, verify: diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 11df459de..9e9897547 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -14,7 +14,7 @@ gardener_logging_alloy_loki_write_endpoints: username: "{{ gardener_logging_ingress_loki_basic_auth_user }}" password: "{{ gardener_logging_ingress_loki_basic_auth_password }}" -# Value for the cluster= external label attached to all log streams +# Value for the cluster= label attached to all log and metric streams via relabel rules gardener_logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" gardener_logging_alloy_port: 12345 @@ -26,9 +26,10 @@ gardener_logging_alloy_port: 12345 # Endpoint is included automatically when monitoring_thanos_receive_ingress_enabled: true. # Credentials come from monitoring_thanos_receive_ingress_basic_auth_user/password (monitoring role). gardener_logging_alloy_prometheus_write_endpoints: >- - {{ [{'url': 'https://' ~ monitoring_thanos_receive_ingress_dns ~ '/api/v1/receive', - 'basic_auth': {'username': monitoring_thanos_receive_ingress_basic_auth_user, - 'password': monitoring_thanos_receive_ingress_basic_auth_password}}] + {{ [{'url': 'https://' ~ monitoring_thanos_receive_ingress_dns ~ '/api/v1/receive'} | + combine({'basic_auth': {'username': monitoring_thanos_receive_ingress_basic_auth_user, + 'password': monitoring_thanos_receive_ingress_basic_auth_password}} + if monitoring_thanos_receive_ingress_basic_auth_password else {})] if monitoring_thanos_receive_ingress_enabled | default(false) | bool else [] }} # WAL retention for prometheus.remote_write. Controls how long undelivered samples diff --git a/control-plane/roles/gardener-logging/meta/main.yaml b/control-plane/roles/gardener-logging/meta/main.yaml index 6c7d70b13..6f2132b35 100644 --- a/control-plane/roles/gardener-logging/meta/main.yaml +++ b/control-plane/roles/gardener-logging/meta/main.yaml @@ -2,7 +2,7 @@ galaxy_info: role_name: gardener-logging author: metal-stack - description: Deploys promtail into a Gardener seed. + description: Deploys Alloy into a Gardener seed. license: MIT min_ansible_version: "2.18" galaxy_tags: [] diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 index ec73122bb..58e2896a0 100644 --- a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 +++ b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 @@ -45,6 +45,12 @@ discovery.relabel "pods" { separator = "/" target_label = "job" } + + // Force cluster label to prevent pod kubernetes labels from overriding this value. + rule { + target_label = "cluster" + replacement = "{{ gardener_logging_alloy_cluster_label }}" + } } loki.source.kubernetes "pods" { @@ -65,6 +71,11 @@ loki.relabel "events" { action = "labeldrop" regex = "instance" } + + rule { + target_label = "cluster" + replacement = "{{ gardener_logging_alloy_cluster_label }}" + } } // default loki endpoints @@ -83,9 +94,6 @@ loki.write "default" { {% endif %} } {% endfor %} - external_labels = { - cluster = "{{ gardener_logging_alloy_cluster_label }}", - } } // write metrics to remote Prometheus endpoint @@ -94,7 +102,16 @@ prometheus.exporter.self "alloy" {} prometheus.scrape "alloy_self" { targets = prometheus.exporter.self.alloy.targets + forward_to = [prometheus.relabel.alloy_self.receiver] +} + +prometheus.relabel "alloy_self" { forward_to = [prometheus.remote_write.default.receiver] + + rule { + target_label = "cluster" + replacement = "{{ gardener_logging_alloy_cluster_label }}" + } } prometheus.remote_write "default" { @@ -119,8 +136,5 @@ prometheus.remote_write "default" { // Increase if you expect control-plane outages longer than the default. max_keepalive_time = "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" } - external_labels = { - cluster = "{{ gardener_logging_alloy_cluster_label }}", - } } {% endif %} diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 719d9e182..fb4c5658c 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -46,7 +46,7 @@ The following variables can be set to configure the role: | logging_alloy_chart_repo | yes | Repository for alloy (release vector) | | logging_alloy_port | | Alloy listen port (default: `12345`) | | logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Default: `[{url: "http://loki:3100/loki/api/v1/push"}]` (in-cluster Loki). Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_cluster_label | | Value for the `cluster=` external label on all log streams (default: `{{ metal_control_plane_stage_name }}`) | +| logging_alloy_cluster_label | | Value for the `cluster=` label set on all log and metric streams via relabel rules (default: `{{ metal_control_plane_stage_name }}`) | | logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. Auto-populated with in-cluster Thanos Receive when `monitoring_thanos_receive_enabled: true`, otherwise `[]`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | | logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | | logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | @@ -60,7 +60,7 @@ Alloy's positions file (tracking the read offset for each container log) is pers | Label | Source | | ----------- | ------------------------------------------------------------------------------ | -| `cluster` | `logging_alloy_cluster_label` (external label) | +| `cluster` | `logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | | `namespace` | `__meta_kubernetes_namespace` | | `pod` | `__meta_kubernetes_pod_name` | | `container` | `__meta_kubernetes_pod_container_name` | @@ -71,11 +71,11 @@ Alloy's positions file (tracking the read offset for each container log) is pers ### Kubernetes events (`loki.source.kubernetes_events`) -| Label | Value | -| ----------- | ---------------------------------------------- | -| `cluster` | `logging_alloy_cluster_label` (external label) | -| `job` | `kubernetes-events` | -| `namespace` | Namespace of the event | +| Label | Value | +| ----------- | -------------------------------------------------------------- | +| `cluster` | `logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | +| `job` | `kubernetes-events` | +| `namespace` | Namespace of the event | Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. @@ -99,12 +99,12 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. Alloy replaces Promtail as the log collector. Key differences: -| Promtail | Alloy | -| -------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| `config.clients[].url` | `logging_alloy_loki_write_endpoints[].url` | -| `-client.external-labels=cluster=…` extraArg | `logging_alloy_cluster_label` → `external_labels` in River config | -| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API, CRI framing is already stripped | -| `pipelineStages: [match(eventrouter)]` | `loki.source.kubernetes_events` (built-in, always enabled) | +| Promtail | Alloy | +| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `config.clients[].url` | `logging_alloy_loki_write_endpoints[].url` | +| `-client.external-labels=cluster=…` extraArg | `logging_alloy_cluster_label` → relabel rule in `discovery.relabel` / `loki.relabel` / `prometheus.relabel` | +| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API, CRI framing is already stripped | +| `pipelineStages: [match(eventrouter)]` | `loki.source.kubernetes_events` (built-in, always enabled) | **Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm release, verify: diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index 95cff41ec..9f9cd9439 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -6,7 +6,7 @@ logging_alloy_port: 12345 logging_alloy_loki_write_endpoints: - url: "http://loki:3100/loki/api/v1/push" -# Value for the cluster= external label attached to all log streams +# Value for the cluster= label attached to all log and metric streams via relabel rules logging_alloy_cluster_label: "{{ metal_control_plane_stage_name }}" # Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). diff --git a/control-plane/roles/logging/templates/alloy-config.alloy.j2 b/control-plane/roles/logging/templates/alloy-config.alloy.j2 index 74e457143..e19da61d8 100644 --- a/control-plane/roles/logging/templates/alloy-config.alloy.j2 +++ b/control-plane/roles/logging/templates/alloy-config.alloy.j2 @@ -45,6 +45,12 @@ discovery.relabel "pods" { separator = "/" target_label = "job" } + + // Force cluster label to prevent pod kubernetes labels from overriding this value. + rule { + target_label = "cluster" + replacement = "{{ logging_alloy_cluster_label }}" + } } loki.source.kubernetes "pods" { @@ -65,6 +71,11 @@ loki.relabel "events" { action = "labeldrop" regex = "instance" } + + rule { + target_label = "cluster" + replacement = "{{ logging_alloy_cluster_label }}" + } } // default loki endpoints @@ -83,9 +94,6 @@ loki.write "default" { {% endif %} } {% endfor %} - external_labels = { - cluster = "{{ logging_alloy_cluster_label }}", - } } // write metrics to remote Prometheus endpoint @@ -94,7 +102,16 @@ prometheus.exporter.self "alloy" {} prometheus.scrape "alloy_self" { targets = prometheus.exporter.self.alloy.targets + forward_to = [prometheus.relabel.alloy_self.receiver] +} + +prometheus.relabel "alloy_self" { forward_to = [prometheus.remote_write.default.receiver] + + rule { + target_label = "cluster" + replacement = "{{ logging_alloy_cluster_label }}" + } } prometheus.remote_write "default" { @@ -119,8 +136,5 @@ prometheus.remote_write "default" { // Increase if you expect control-plane outages longer than the default. max_keepalive_time = "{{ logging_alloy_prometheus_wal_max_keepalive_time }}" } - external_labels = { - cluster = "{{ logging_alloy_cluster_label }}", - } } {% endif %} From 683380a325c5b8a94c6761ba96b32db31588cdc3 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 11 May 2026 15:38:12 +0200 Subject: [PATCH 07/24] feat: support instance and component labels --- .../roles/gardener-logging/README.md | 36 +++++++++++++------ .../templates/seed-alloy-config.alloy.j2 | 27 ++++++++++++-- control-plane/roles/logging/README.md | 22 ++++++------ .../logging/templates/alloy-config.alloy.j2 | 27 ++++++++++++-- 4 files changed, 87 insertions(+), 25 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index f08077bb2..96c3fe73c 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -57,8 +57,10 @@ Alloy's positions file (tracking the read offset for each container log) is pers | `container` | `__meta_kubernetes_pod_container_name` | | `pod_uid` | `__meta_kubernetes_pod_uid` | | `node_name` | `__meta_kubernetes_pod_node_name` | -| `app` | `__meta_kubernetes_pod_label_app` (empty if pod has no `app` label) | -| `job` | `namespace/app` (from pod `app` label; empty suffix if pod has no `app` label) | +| `app` | `app.kubernetes.io/name` pod label, falling back to `app` label, controller name (hash stripped), then pod name | +| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | +| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | +| `job` | `namespace/app` (using the computed `app` value above) | ### Kubernetes events (`loki.source.kubernetes_events`) @@ -82,17 +84,31 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. ## Migration from Promtail -Alloy replaces Promtail as the log collector. Key differences: +Alloy replaces Promtail as the log collector. The following things have changed: -| Promtail | Alloy | -| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -| `config.clients[].url` + `basic_auth` | `gardener_logging_alloy_loki_write_endpoints[].url` + `basic_auth` | -| `-client.external-labels=cluster=…` extraArg | `gardener_logging_alloy_cluster_label` → relabel rule in `discovery.relabel` / `loki.relabel` / `prometheus.relabel` | -| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API | +- **Log labels are consistent with Promtail.** The label derivation rules (`app`, `instance`, `component`, `job`) are identical to Promtail's chart defaults. The only addition is `pod_uid`. See [Labels](#labels) for the full set. +- **Kubernetes events are now built-in.** Promtail required a separate `eventrouter` sidecar and a pipeline stage to capture events. Alloy collects events natively via `loki.source.kubernetes_events`. Events now appear under `job="kubernetes-events"` instead of `job="monitoring/event-exporter"` — update any queries or dashboards accordingly. +- **Alloy pushes its own metrics.** Promtail exposed metrics, but enabling the ServiceMonitor was impractical because Prometheus typically deploys after the logging stack (the option was intentionally commented out). Alloy scrapes itself and pushes metrics via `prometheus.remote_write` to the control-plane Thanos Receive ingress instead, removing the ordering dependency. When `monitoring_thanos_receive_ingress_enabled: true`, this is wired automatically. +- **Metric WAL is new.** Alloy buffers undelivered self-metrics in a WAL on disk (default retention: 8h). Promtail had no equivalent — if the remote endpoint was unreachable, metric data was simply lost. -**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm releases, verify: +**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing Promtail, verify: - Logs arrive correctly in Loki -- Dashboards that filter by log labels (e.g. `job`, `app`) still work — the label set has changed, see [Labels](#labels) +- Dashboards and alerts that filter by log labels work as expected — labels are consistent with Promtail - Alerts that query log streams by label still fire as expected - Any custom LogQL queries saved in Grafana still return results + +### Thanos Receive credentials + +If you push Alloy self-metrics to Thanos Receive (`monitoring_thanos_receive_ingress_enabled: true`), the monitoring role's basic auth configuration has changed. The old raw htpasswd string `monitoring_thanos_receive_ingress_basic_auth` has been replaced by two plaintext variables: + +```yaml +# Before +monitoring_thanos_receive_ingress_basic_auth: "myuser:$apr1$..." + +# After +monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-receive +monitoring_thanos_receive_ingress_basic_auth_password: mysecret +``` + +The htpasswd entry is now generated automatically. The monitoring role will fail immediately if the old variable is still set — see the [monitoring role migration guide](../monitoring/README.md#migration) for details. diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 index 58e2896a0..321edbc3e 100644 --- a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 +++ b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 @@ -35,13 +35,36 @@ discovery.relabel "pods" { target_label = "node_name" } + // Derive app: app.kubernetes.io/name → app label → controller name (hash stripped) → pod name rule { - source_labels = ["__meta_kubernetes_pod_label_app"] + source_labels = ["__meta_kubernetes_pod_controller_name"] + regex = "([0-9a-z-.]+?)(-[0-9a-f]{8,10})?" + target_label = "__tmp_controller_name" + } + + rule { + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name", "__meta_kubernetes_pod_label_app", "__tmp_controller_name", "__meta_kubernetes_pod_name"] + regex = "^;*([^;]+)(;.*)?$" + replacement = "$1" target_label = "app" } rule { - source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_label_app"] + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_instance", "__meta_kubernetes_pod_label_instance"] + regex = "^;*([^;]+)(;.*)?$" + replacement = "$1" + target_label = "instance" + } + + rule { + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_component", "__meta_kubernetes_pod_label_component"] + regex = "^;*([^;]+)(;.*)?$" + replacement = "$1" + target_label = "component" + } + + rule { + source_labels = ["namespace", "app"] separator = "/" target_label = "job" } diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index fb4c5658c..61ac928b9 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -66,8 +66,10 @@ Alloy's positions file (tracking the read offset for each container log) is pers | `container` | `__meta_kubernetes_pod_container_name` | | `pod_uid` | `__meta_kubernetes_pod_uid` | | `node_name` | `__meta_kubernetes_pod_node_name` | -| `app` | `__meta_kubernetes_pod_label_app` (empty if pod has no `app` label) | -| `job` | `namespace/app` (from pod `app` label; empty suffix if pod has no `app` label) | +| `app` | `app.kubernetes.io/name` pod label, falling back to `app` label, controller name (hash stripped), then pod name | +| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | +| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | +| `job` | `namespace/app` (using the computed `app` value above) | ### Kubernetes events (`loki.source.kubernetes_events`) @@ -97,18 +99,16 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. ## Migration from Promtail -Alloy replaces Promtail as the log collector. Key differences: +Alloy replaces Promtail as the log collector. The following things have changed: -| Promtail | Alloy | -| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| `config.clients[].url` | `logging_alloy_loki_write_endpoints[].url` | -| `-client.external-labels=cluster=…` extraArg | `logging_alloy_cluster_label` → relabel rule in `discovery.relabel` / `loki.relabel` / `prometheus.relabel` | -| `pipelineStages: [cri, docker]` | Not needed — `loki.source.kubernetes` uses the Kubernetes API, CRI framing is already stripped | -| `pipelineStages: [match(eventrouter)]` | `loki.source.kubernetes_events` (built-in, always enabled) | +- **Log labels are consistent with Promtail.** The label derivation rules (`app`, `instance`, `component`, `job`) are identical to Promtail's chart defaults. The only addition is `pod_uid`. See [Labels](#labels) for the full set. +- **Kubernetes events are now built-in.** Promtail required a separate `eventrouter` sidecar and a pipeline stage to capture events. Alloy collects events natively via `loki.source.kubernetes_events`. Events now appear under `job="kubernetes-events"` instead of `job="monitoring/event-exporter"` — update any queries or dashboards accordingly. +- **Alloy pushes its own metrics.** Promtail exposed metrics, but enabling the ServiceMonitor was impractical because Prometheus typically deploys after the logging stack (the option was intentionally commented out). Alloy scrapes itself and pushes metrics via `prometheus.remote_write` instead, removing the ordering dependency. When `monitoring_thanos_receive_enabled: true`, this is wired automatically. +- **Metric WAL is new.** Alloy buffers undelivered self-metrics in a WAL on disk (default retention: 8h). Promtail had no equivalent — if Loki was unreachable, metric data was simply lost. -**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing the Promtail Helm release, verify: +**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing Promtail, verify: - Logs arrive correctly in Loki -- Dashboards that filter by log labels (e.g. `job`, `app`) still work — the label set has changed, see [Labels](#labels) +- Dashboards and alerts that filter by log labels work as expected — labels are consistent with Promtail - Alerts that query log streams by label still fire as expected - Any custom LogQL queries saved in Grafana still return results diff --git a/control-plane/roles/logging/templates/alloy-config.alloy.j2 b/control-plane/roles/logging/templates/alloy-config.alloy.j2 index e19da61d8..4eba7628e 100644 --- a/control-plane/roles/logging/templates/alloy-config.alloy.j2 +++ b/control-plane/roles/logging/templates/alloy-config.alloy.j2 @@ -35,13 +35,36 @@ discovery.relabel "pods" { target_label = "node_name" } + // Derive app: app.kubernetes.io/name → app label → controller name (hash stripped) → pod name rule { - source_labels = ["__meta_kubernetes_pod_label_app"] + source_labels = ["__meta_kubernetes_pod_controller_name"] + regex = "([0-9a-z-.]+?)(-[0-9a-f]{8,10})?" + target_label = "__tmp_controller_name" + } + + rule { + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name", "__meta_kubernetes_pod_label_app", "__tmp_controller_name", "__meta_kubernetes_pod_name"] + regex = "^;*([^;]+)(;.*)?$" + replacement = "$1" target_label = "app" } rule { - source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_label_app"] + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_instance", "__meta_kubernetes_pod_label_instance"] + regex = "^;*([^;]+)(;.*)?$" + replacement = "$1" + target_label = "instance" + } + + rule { + source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_component", "__meta_kubernetes_pod_label_component"] + regex = "^;*([^;]+)(;.*)?$" + replacement = "$1" + target_label = "component" + } + + rule { + source_labels = ["namespace", "app"] separator = "/" target_label = "job" } From f6659b6451bf1bed69726278a5d379f75acc079a Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 11 May 2026 16:03:47 +0200 Subject: [PATCH 08/24] feat: conditional or parallel deployment of alloy or promtail with guide --- .../roles/gardener-logging/README.md | 105 +++++++++------- .../roles/gardener-logging/defaults/main.yaml | 10 ++ .../tasks/gardener-shooted-seed.yaml | 5 +- .../roles/gardener-logging/tasks/main.yaml | 35 ++++-- .../templates/seed-alloy-config.alloy.j2 | 5 + control-plane/roles/logging/README.md | 116 ++++++++++-------- .../roles/logging/defaults/main.yaml | 12 +- control-plane/roles/logging/tasks/main.yaml | 32 +++-- .../logging/templates/alloy-config.alloy.j2 | 5 + control-plane/roles/monitoring/README.md | 10 +- .../roles/monitoring/tasks/exporters.yaml | 1 + .../roles/monitoring/tasks/main.yaml | 2 - 12 files changed, 220 insertions(+), 118 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 96c3fe73c..795fe4c8a 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -4,7 +4,7 @@ Deploys [Grafana Alloy](https://grafana.com/docs/alloy/latest/) into Gardener sh Expects the [logging role](../logging/) to have been deployed first. -This role previously used Promtail as the log collector. It has been migrated to use Grafana Alloy instead. See [Migration from Promtail](#migration-from-promtail) for details. +This role supports deploying Alloy and/or Promtail as log collectors. Both `gardener_logging_alloy_enabled` and `gardener_logging_promtail_enabled` **must be set explicitly** — there are no defaults. See [Migration from Promtail](#migration-from-promtail) for guidance. ## Configuration @@ -18,30 +18,32 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| ------------------------------------------------- | --------- | ------------------------------------------------------------- | -| gardener_logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | -| gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | -| gardener_logging_namespace | | The deployment's target namespace | -| gardener_logging_ingress_dns | | DNS for loki ingress | -| gardener_logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | -| gardener_logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | -| gardener_logging_deploy_to_garden_cluster | | Deploys Alloy also into the garden cluster (default: `true`) | -| gardener_logging_shooted_seeds | | Shooted seed names on which to deploy Alloy that logs to loki | +| Name | Mandatory | Default | Description | +| ------------------------------------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_enabled | yes | | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. | +| gardener_logging_promtail_enabled | yes | | Deploy Promtail. Set `true` to keep existing behavior (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | +| gardener_logging_alloy_chart_version | | | Helm chart version for alloy — required when `gardener_logging_alloy_enabled: true` | +| gardener_logging_alloy_chart_repo | | | Repository for alloy — required when `gardener_logging_alloy_enabled: true` | +| gardener_logging_promtail_chart_version | | | Helm chart version for promtail — required when `gardener_logging_promtail_enabled: true` | +| gardener_logging_promtail_chart_repo | | | Repository for promtail — required when `gardener_logging_promtail_enabled: true` | +| gardener_logging_namespace | | | The deployment's target namespace | +| gardener_logging_ingress_dns | | | DNS for loki ingress | +| gardener_logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | +| gardener_logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | +| gardener_logging_deploy_to_garden_cluster | | `true` | Deploys Alloy also into the garden cluster | +| gardener_logging_shooted_seeds | | | Shooted seed names on which to deploy Alloy that logs to loki | ### Alloy -| Name | Mandatory | Description | -| -------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| gardener_logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | -| gardener_logging_alloy_chart_repo | yes | Repository for alloy (release vector) | -| gardener_logging_alloy_port | | Alloy listen port (default: `12345`) | -| gardener_logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Default: `https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push` with `basic_auth` using `gardener_logging_ingress_loki_basic_auth_user/password`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_cluster_label | | Value for the `cluster=` label set on all log and metric streams via relabel rules (default: `gardener_logging_shooted_seed.name`) | -| gardener_logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. Default: Thanos receive ingress (`{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive`). Requires `monitoring_thanos_receive_ingress_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | -| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | -| gardener_logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Default | Description | +| -------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_port | | `12345` | Alloy listen port | +| gardener_logging_alloy_loki_write_endpoints | | `https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push` with basic auth | List of Loki push endpoints. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_cluster_label | | `gardener_logging_shooted_seed.name` | Value for the `cluster=` label set on all log and metric streams via relabel rules | +| gardener_logging_alloy_prometheus_write_endpoints | | Thanos receive ingress (`{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive`) | List of Prometheus remote_write endpoints for Alloy self-metrics. Requires `monitoring_thanos_receive_ingress_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | +| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | +| gardener_logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -49,25 +51,25 @@ Alloy's positions file (tracking the read offset for each container log) is pers ### Pod logs (`loki.source.kubernetes`) -| Label | Source | -| ----------- | ------------------------------------------------------------------------------ | -| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | -| `namespace` | `__meta_kubernetes_namespace` | -| `pod` | `__meta_kubernetes_pod_name` | -| `container` | `__meta_kubernetes_pod_container_name` | -| `pod_uid` | `__meta_kubernetes_pod_uid` | -| `node_name` | `__meta_kubernetes_pod_node_name` | +| Label | Source | +| ----------- | --------------------------------------------------------------------------------------------------------------- | +| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | +| `namespace` | `__meta_kubernetes_namespace` | +| `pod` | `__meta_kubernetes_pod_name` | +| `container` | `__meta_kubernetes_pod_container_name` | +| `pod_uid` | `__meta_kubernetes_pod_uid` | +| `node_name` | `__meta_kubernetes_pod_node_name` | | `app` | `app.kubernetes.io/name` pod label, falling back to `app` label, controller name (hash stripped), then pod name | -| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | -| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | -| `job` | `namespace/app` (using the computed `app` value above) | +| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | +| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | +| `job` | `namespace/app` (using the computed `app` value above) | ### Kubernetes events (`loki.source.kubernetes_events`) | Label | Value | | ----------- | ----------------------------------------------------------------------- | | `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | -| `job` | `kubernetes-events` | +| `job` | `monitoring/event-exporter` (relabelled for Promtail compatibility) | | `namespace` | Namespace of the event | Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. @@ -84,19 +86,32 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. ## Migration from Promtail -Alloy replaces Promtail as the log collector. The following things have changed: +Alloy is the recommended log collector. Both `gardener_logging_alloy_enabled` and `gardener_logging_promtail_enabled` must be **explicitly set in your inventory** — the role will fail immediately on upgrade if they are missing, forcing a conscious migration decision. -- **Log labels are consistent with Promtail.** The label derivation rules (`app`, `instance`, `component`, `job`) are identical to Promtail's chart defaults. The only addition is `pod_uid`. See [Labels](#labels) for the full set. -- **Kubernetes events are now built-in.** Promtail required a separate `eventrouter` sidecar and a pipeline stage to capture events. Alloy collects events natively via `loki.source.kubernetes_events`. Events now appear under `job="kubernetes-events"` instead of `job="monitoring/event-exporter"` — update any queries or dashboards accordingly. -- **Alloy pushes its own metrics.** Promtail exposed metrics, but enabling the ServiceMonitor was impractical because Prometheus typically deploys after the logging stack (the option was intentionally commented out). Alloy scrapes itself and pushes metrics via `prometheus.remote_write` to the control-plane Thanos Receive ingress instead, removing the ordering dependency. When `monitoring_thanos_receive_ingress_enabled: true`, this is wired automatically. -- **Metric WAL is new.** Alloy buffers undelivered self-metrics in a WAL on disk (default retention: 8h). Promtail had no equivalent — if the remote endpoint was unreachable, metric data was simply lost. +> **Promtail is deprecated.** Setting `gardener_logging_promtail_enabled: true` emits a deprecation warning on every run. Promtail support will be removed in a future release. -**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing Promtail, verify: +Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and LogQL queries continue to work without changes. What has changed compared to Promtail: -- Logs arrive correctly in Loki -- Dashboards and alerts that filter by log labels work as expected — labels are consistent with Promtail -- Alerts that query log streams by label still fire as expected -- Any custom LogQL queries saved in Grafana still return results +- **Kubernetes events are now built-in.** Promtail required a separate event-exporter Deployment. Alloy collects events natively via `loki.source.kubernetes_events` and labels them `job="monitoring/event-exporter"` for full backward compatibility. +- **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to the control-plane Thanos Receive ingress, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_ingress_enabled: true`. +- **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. +- **One new label.** `pod_uid` is added to pod log streams. All other labels (`app`, `instance`, `component`, `job`, `namespace`, etc.) are identical to Promtail's chart defaults. + +| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | +| --------------------------------- | -------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Fresh deployment** | `true` | `false` | Alloy only. | +| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. Deprecated — emits a warning on every run. | +| **Cutover complete** | `true` | `false` | Remove Promtail from each seed: `helm uninstall promtail -n {{ gardener_logging_namespace }}`. | + +**To migrate an existing Promtail installation:** + +1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see [Thanos Receive credentials](#thanos-receive-credentials) below. +2. Add to your inventory: `gardener_logging_promtail_enabled: true, gardener_logging_alloy_enabled: false`. The role will continue deploying Promtail and emit a deprecation warning each run. +3. When ready: set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. +4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. +5. Set `gardener_logging_promtail_enabled: false` and re-run. Remove Promtail from each seed: `helm uninstall promtail -n {{ gardener_logging_namespace }}`. +6. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). ### Thanos Receive credentials @@ -107,7 +122,7 @@ If you push Alloy self-metrics to Thanos Receive (`monitoring_thanos_receive_ing monitoring_thanos_receive_ingress_basic_auth: "myuser:$apr1$..." # After -monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-receive +monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-receive monitoring_thanos_receive_ingress_basic_auth_password: mysecret ``` diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 9e9897547..35204d79a 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -1,6 +1,10 @@ --- gardener_logging_namespace: monitoring +# Set to true to deploy Alloy. Set to false to skip Alloy deployment entirely +# (e.g. when keeping only Promtail before migrating). +# gardener_logging_alloy_enabled: true + gardener_logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" gardener_logging_garden_name: "{{ gardener_defaults_garden_name }}" @@ -42,6 +46,12 @@ gardener_logging_alloy_prometheus_wal_max_keepalive_time: "8h" # Full Alloy River config override. When set, bypasses the seed-alloy-config.alloy.j2 template. # gardener_logging_alloy_config_raw: | +# Set to true to also deploy Promtail alongside Alloy during a migration window. +# Both DaemonSets will ship logs simultaneously — Loki will receive duplicate entries. +# Requires gardener_logging_promtail_chart_version and gardener_logging_promtail_chart_repo to be set. +# See the Migration from Promtail section in README.md. +# gardener_logging_promtail_enabled: false + gardener_logging_deploy_to_garden_cluster: true gardener_logging_shooted_seeds: [] # - name: my-shooted-seed diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index 7bf6de49d..cdba64a1f 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -10,6 +10,7 @@ - name: Build alloy config set_fact: gardener_logging_alloy_config: "{{ lookup('template', 'seed-alloy-config.alloy.j2') if (gardener_logging_alloy_config_raw | default('') | length == 0) else gardener_logging_alloy_config_raw }}" + when: gardener_logging_alloy_enabled - name: Deploy alloy kubernetes.core.helm: @@ -21,8 +22,9 @@ values: "{{ lookup('template', 'seed-alloy-values.yaml') | from_yaml }}" kubeconfig: "{{ _shoot_kubeconfig }}" create_namespace: true + when: gardener_logging_alloy_enabled -- name: Deploy Promtail +- name: Deploy Promtail (parallel migration window) kubernetes.core.helm: name: promtail chart_repo_url: "{{ gardener_logging_promtail_chart_repo }}" @@ -32,3 +34,4 @@ values: "{{ lookup('template', 'seed-promtail-values.yaml') | from_yaml }}" kubeconfig: "{{ _shoot_kubeconfig }}" create_namespace: true + when: gardener_logging_promtail_enabled diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 6508c4f52..a4602b353 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -4,18 +4,35 @@ - name: Check mandatory variables for this role are set assert: - fail_msg: "not all mandatory variables given, check role documentation" + fail_msg: >- + gardener_logging_alloy_enabled and gardener_logging_promtail_enabled must be + explicitly set — there are no defaults, to force a conscious migration decision. + At least one must be true. + Fresh install: set gardener_logging_alloy_enabled: true, gardener_logging_promtail_enabled: false. + Existing Promtail install: set gardener_logging_promtail_enabled: true to keep current + behavior (deprecated), then migrate to Alloy at your own pace. + See README.md for the full migration guide. quiet: true that: - - gardener_logging_promtail_chart_repo is defined - - gardener_logging_promtail_chart_version is defined - - gardener_logging_alloy_chart_repo is defined - - gardener_logging_alloy_chart_version is defined + - gardener_logging_alloy_enabled is defined + - gardener_logging_promtail_enabled is defined + - gardener_logging_alloy_enabled | default(false) | bool or gardener_logging_promtail_enabled | default(false) | bool + - not gardener_logging_alloy_enabled | default(false) | bool or gardener_logging_alloy_chart_version is defined + - not gardener_logging_alloy_enabled | default(false) | bool or gardener_logging_alloy_chart_repo is defined + - not gardener_logging_promtail_enabled | default(false) | bool or gardener_logging_promtail_chart_version is defined + - not gardener_logging_promtail_enabled | default(false) | bool or gardener_logging_promtail_chart_repo is defined + +- name: Promtail deprecation warning + ansible.builtin.debug: + msg: >- + DEPRECATION: gardener_logging_promtail_enabled is true. Promtail support is deprecated + and will be removed in a future release. Migrate to Alloy — see README.md. + when: gardener_logging_promtail_enabled | default(false) | bool - name: Build Alloy config for garden cluster set_fact: gardener_logging_alloy_config: "{{ lookup('template', 'seed-alloy-config.alloy.j2') if (gardener_logging_alloy_config_raw | default('') | length == 0) else gardener_logging_alloy_config_raw }}" - when: gardener_logging_deploy_to_garden_cluster + when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled vars: gardener_logging_shooted_seed: name: "{{ gardener_logging_garden_name }}" @@ -29,12 +46,12 @@ namespace: "{{ gardener_logging_namespace }}" values: "{{ lookup('template', 'seed-alloy-values.yaml') | from_yaml }}" create_namespace: true - when: gardener_logging_deploy_to_garden_cluster + when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled vars: gardener_logging_shooted_seed: name: "{{ gardener_logging_garden_name }}" -- name: Deploy Promtail +- name: Deploy Promtail to garden cluster (parallel migration window) kubernetes.core.helm: name: promtail chart_repo_url: "{{ gardener_logging_promtail_chart_repo }}" @@ -43,7 +60,7 @@ namespace: "{{ gardener_logging_namespace }}" values: "{{ lookup('template', 'seed-promtail-values.yaml') | from_yaml }}" create_namespace: true - when: gardener_logging_deploy_to_garden_cluster + when: gardener_logging_deploy_to_garden_cluster and gardener_logging_promtail_enabled vars: gardener_logging_shooted_seed: name: "{{ gardener_logging_garden_name }}" diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 index 321edbc3e..41524f3d7 100644 --- a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 +++ b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 @@ -99,6 +99,11 @@ loki.relabel "events" { target_label = "cluster" replacement = "{{ gardener_logging_alloy_cluster_label }}" } + + rule { + target_label = "job" + replacement = "monitoring/event-exporter" + } } // default loki endpoints diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 61ac928b9..d39c680a9 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -8,7 +8,7 @@ Components: - **Alloy** — log collector (DaemonSet), collects pod logs via the Kubernetes API (`loki.source.kubernetes`) and forwards them to Loki - Loki ingress with optional TLS and basic auth -This role previously used Promtail as the log collector. It has been migrated to use Grafana Alloy instead. See [Migration from Promtail](#migration-from-promtail) for details. +This role supports deploying Alloy and/or Promtail as log collectors. Both `logging_alloy_enabled` and `logging_promtail_enabled` **must be set explicitly** — there are no defaults. See [Migration from Promtail](#migration-from-promtail) for guidance. ## Configuration @@ -24,33 +24,35 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| --------------------------------------------- | --------- | ------------------------------------------------------------ | -| logging_chart_version | yes | Helm chart version for loki (release vector) | -| logging_chart_repo | yes | Repository for loki (release vector) | -| logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | -| logging_alloy_chart_repo | yes | Repository for alloy (release vector) | -| logging_namespace | | The deployment's target namespace | -| logging_loki_size | | The size of the volume that loki will use for storing logs | -| logging_ingress_dns | | DNS for loki ingress | -| logging_ingress_loki_tls | | If enabled, exposes loki through HTTPS on the ingress | -| logging_ingress_loki_basic_auth_password_salt | | The basic auth password salt used for stable password hashes | -| logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | -| logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | +| Name | Mandatory | Default | Description | +| --------------------------------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| logging_chart_version | yes | | Helm chart version for loki (release vector) | +| logging_chart_repo | yes | | Repository for loki (release vector) | +| logging_alloy_enabled | yes | | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `logging_alloy_chart_version` and `logging_alloy_chart_repo`. | +| logging_promtail_enabled | yes | | Deploy Promtail. Set `true` to keep existing behavior (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | +| logging_alloy_chart_version | | | Helm chart version for alloy — required when `logging_alloy_enabled: true` | +| logging_alloy_chart_repo | | | Repository for alloy — required when `logging_alloy_enabled: true` | +| logging_promtail_chart_version | | | Helm chart version for promtail — required when `logging_promtail_enabled: true` | +| logging_promtail_chart_repo | | | Repository for promtail — required when `logging_promtail_enabled: true` | +| logging_namespace | | | The deployment's target namespace | +| logging_loki_size | | | The size of the volume that loki will use for storing logs | +| logging_ingress_dns | | | DNS for loki ingress | +| logging_ingress_loki_tls | | | If enabled, exposes loki through HTTPS on the ingress | +| logging_ingress_loki_basic_auth_password_salt | | | The basic auth password salt used for stable password hashes | +| logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | +| logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | ### Alloy -| Name | Mandatory | Description | -| ----------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| logging_alloy_chart_version | yes | Helm chart version for alloy (release vector) | -| logging_alloy_chart_repo | yes | Repository for alloy (release vector) | -| logging_alloy_port | | Alloy listen port (default: `12345`) | -| logging_alloy_loki_write_endpoints | | List of Loki push endpoints. Default: `[{url: "http://loki:3100/loki/api/v1/push"}]` (in-cluster Loki). Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_cluster_label | | Value for the `cluster=` label set on all log and metric streams via relabel rules (default: `{{ metal_control_plane_stage_name }}`) | -| logging_alloy_prometheus_write_endpoints | | List of Prometheus remote_write endpoints for Alloy self-metrics. Auto-populated with in-cluster Thanos Receive when `monitoring_thanos_receive_enabled: true`, otherwise `[]`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_prometheus_wal_truncate_frequency | | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped (default: `2h`) | -| logging_alloy_prometheus_wal_max_keepalive_time | | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window (default: `8h`) | -| logging_alloy_config_raw | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Default | Description | +| ----------------------------------------------- | --------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| logging_alloy_port | | `12345` | Alloy listen port | +| logging_alloy_loki_write_endpoints | | `[{url: "http://loki:3100/loki/api/v1/push"}]` | List of Loki push endpoints (in-cluster Loki). Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_cluster_label | | `{{ metal_control_plane_stage_name }}` | Value for the `cluster=` label set on all log and metric streams via relabel rules | +| logging_alloy_prometheus_write_endpoints | | auto (Thanos Receive when `monitoring_thanos_receive_enabled: true`, else `[]`) | List of Prometheus remote_write endpoints for Alloy self-metrics. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | +| logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | +| logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -58,26 +60,26 @@ Alloy's positions file (tracking the read offset for each container log) is pers ### Pod logs (`loki.source.kubernetes`) -| Label | Source | -| ----------- | ------------------------------------------------------------------------------ | -| `cluster` | `logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | -| `namespace` | `__meta_kubernetes_namespace` | -| `pod` | `__meta_kubernetes_pod_name` | -| `container` | `__meta_kubernetes_pod_container_name` | -| `pod_uid` | `__meta_kubernetes_pod_uid` | -| `node_name` | `__meta_kubernetes_pod_node_name` | +| Label | Source | +| ----------- | --------------------------------------------------------------------------------------------------------------- | +| `cluster` | `logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | +| `namespace` | `__meta_kubernetes_namespace` | +| `pod` | `__meta_kubernetes_pod_name` | +| `container` | `__meta_kubernetes_pod_container_name` | +| `pod_uid` | `__meta_kubernetes_pod_uid` | +| `node_name` | `__meta_kubernetes_pod_node_name` | | `app` | `app.kubernetes.io/name` pod label, falling back to `app` label, controller name (hash stripped), then pod name | -| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | -| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | -| `job` | `namespace/app` (using the computed `app` value above) | +| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | +| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | +| `job` | `namespace/app` (using the computed `app` value above) | ### Kubernetes events (`loki.source.kubernetes_events`) -| Label | Value | -| ----------- | -------------------------------------------------------------- | -| `cluster` | `logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | -| `job` | `kubernetes-events` | -| `namespace` | Namespace of the event | +| Label | Value | +| ----------- | ------------------------------------------------------------------- | +| `cluster` | `logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | +| `job` | `monitoring/event-exporter` (relabelled for Promtail compatibility) | +| `namespace` | Namespace of the event | Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. @@ -99,16 +101,28 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. ## Migration from Promtail -Alloy replaces Promtail as the log collector. The following things have changed: +Alloy is the recommended log collector. Both `logging_alloy_enabled` and `logging_promtail_enabled` must be **explicitly set in your inventory** — the role will fail immediately on upgrade if they are missing, forcing a conscious migration decision. -- **Log labels are consistent with Promtail.** The label derivation rules (`app`, `instance`, `component`, `job`) are identical to Promtail's chart defaults. The only addition is `pod_uid`. See [Labels](#labels) for the full set. -- **Kubernetes events are now built-in.** Promtail required a separate `eventrouter` sidecar and a pipeline stage to capture events. Alloy collects events natively via `loki.source.kubernetes_events`. Events now appear under `job="kubernetes-events"` instead of `job="monitoring/event-exporter"` — update any queries or dashboards accordingly. -- **Alloy pushes its own metrics.** Promtail exposed metrics, but enabling the ServiceMonitor was impractical because Prometheus typically deploys after the logging stack (the option was intentionally commented out). Alloy scrapes itself and pushes metrics via `prometheus.remote_write` instead, removing the ordering dependency. When `monitoring_thanos_receive_enabled: true`, this is wired automatically. -- **Metric WAL is new.** Alloy buffers undelivered self-metrics in a WAL on disk (default retention: 8h). Promtail had no equivalent — if Loki was unreachable, metric data was simply lost. +> **Promtail is deprecated.** Setting `logging_promtail_enabled: true` emits a deprecation warning on every run. Promtail support will be removed in a future release. -**Recommended approach — parallel run:** Deploy Alloy alongside the existing Promtail installation first. Both will ship logs to Loki simultaneously, so expect duplicate log entries during the transition window. Before removing Promtail, verify: +Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and LogQL queries continue to work without changes. What has changed compared to Promtail: -- Logs arrive correctly in Loki -- Dashboards and alerts that filter by log labels work as expected — labels are consistent with Promtail -- Alerts that query log streams by label still fire as expected -- Any custom LogQL queries saved in Grafana still return results +- **Kubernetes events are now built-in.** Promtail required a separate event-exporter Deployment. Alloy collects events natively via `loki.source.kubernetes_events` and labels them `job="monitoring/event-exporter"` for full backward compatibility. +- **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to in-cluster Thanos Receive, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_enabled: true`. +- **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. +- **One new label.** `pod_uid` is added to pod log streams. All other labels (`app`, `instance`, `component`, `job`, `namespace`, etc.) are identical to Promtail's chart defaults. + +| Scenario | `logging_alloy_enabled` | `logging_promtail_enabled` | Notes | +| --------------------------------- | ----------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Fresh deployment** | `true` | `false` | Alloy only. | +| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. Deprecated — emits a warning on every run. | +| **Cutover complete** | `true` | `false` | Remove Promtail: `helm uninstall promtail -n {{ logging_namespace }}`. | + +**To migrate an existing Promtail installation:** + +1. Add to your inventory: `logging_promtail_enabled: true, logging_alloy_enabled: false`. The role will continue deploying Promtail and emit a deprecation warning each run. +2. When ready: set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. +3. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. +4. Set `logging_promtail_enabled: false` and re-run. Remove the Promtail release: `helm uninstall promtail -n {{ logging_namespace }}`. +5. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index 9f9cd9439..c8e89218e 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -2,6 +2,10 @@ logging_namespace: monitoring logging_alloy_port: 12345 +# Set to true to deploy Alloy. Set to false to skip Alloy deployment entirely +# (e.g. when keeping only Promtail before migrating). +# logging_alloy_enabled: true + # Loki push endpoints. logging_alloy_loki_write_endpoints: - url: "http://loki:3100/loki/api/v1/push" @@ -27,9 +31,15 @@ logging_alloy_prometheus_wal_max_keepalive_time: "8h" # Full Alloy River config override. When set, bypasses all structured vars above. # logging_alloy_config_raw: | +# Set to true to also deploy Promtail alongside Alloy during a migration window. +# Both DaemonSets will ship logs simultaneously — Loki will receive duplicate entries. +# Requires logging_promtail_chart_version and logging_promtail_chart_repo to be set. +# See the Migration from Promtail section in README.md. +# logging_promtail_enabled: false + logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" logging_ingress_loki_tls: true -logging_ingress_loki_basic_auth_user: promtail +logging_ingress_loki_basic_auth_user: promtail # TODO rename to alloy or generic logging_ingress_loki_basic_auth_password: logging_ingress_loki_basic_auth_password_salt: "{{ logging_ingress_loki_basic_auth_password | hash('sha1') | truncate(22, True, '') }}" logging_ingress_annotations: {} diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index 49b70c295..b4fd54958 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -4,15 +4,31 @@ - name: Check mandatory variables for this role are set assert: - fail_msg: "not all mandatory variables given, check role documentation" + fail_msg: >- + logging_alloy_enabled and logging_promtail_enabled must be explicitly set — there + are no defaults, to force a conscious migration decision. At least one must be true. + Fresh install: set logging_alloy_enabled: true, logging_promtail_enabled: false. + Existing Promtail install: set logging_promtail_enabled: true to keep current + behavior (deprecated), then migrate to Alloy at your own pace. + See README.md for the full migration guide. quiet: true that: - logging_chart_version is defined - logging_chart_repo is defined - - logging_promtail_chart_version is defined - - logging_promtail_chart_repo is defined - - logging_alloy_chart_version is defined - - logging_alloy_chart_repo is defined + - logging_alloy_enabled is defined + - logging_promtail_enabled is defined + - logging_alloy_enabled | default(false) | bool or logging_promtail_enabled | default(false) | bool + - not logging_alloy_enabled | default(false) | bool or logging_alloy_chart_version is defined + - not logging_alloy_enabled | default(false) | bool or logging_alloy_chart_repo is defined + - not logging_promtail_enabled | default(false) | bool or logging_promtail_chart_version is defined + - not logging_promtail_enabled | default(false) | bool or logging_promtail_chart_repo is defined + +- name: Promtail deprecation warning + ansible.builtin.debug: + msg: >- + DEPRECATION: logging_promtail_enabled is true. Promtail support is deprecated + and will be removed in a future release. Migrate to Alloy — see README.md. + when: logging_promtail_enabled | default(false) | bool - name: Create namespace {{ logging_namespace }} k8s: @@ -38,6 +54,7 @@ - name: Build Alloy config set_fact: logging_alloy_config: "{{ lookup('template', 'alloy-config.alloy.j2') if (logging_alloy_config_raw | default('') | length == 0) else logging_alloy_config_raw }}" + when: logging_alloy_enabled - name: Deploy Alloy include_role: @@ -49,8 +66,9 @@ helm_target_namespace: "{{ logging_namespace }}" helm_chart_version: "{{ logging_alloy_chart_version }}" helm_value_file_template: "alloy-values.yaml" + when: logging_alloy_enabled -- name: Deploy Promtail +- name: Deploy Promtail (parallel migration window) include_role: name: ansible-common/roles/helm-chart vars: @@ -60,4 +78,4 @@ helm_target_namespace: "{{ logging_namespace }}" helm_chart_version: "{{ logging_promtail_chart_version }}" helm_value_file_template: "promtail-values.yaml" - + when: logging_promtail_enabled diff --git a/control-plane/roles/logging/templates/alloy-config.alloy.j2 b/control-plane/roles/logging/templates/alloy-config.alloy.j2 index 4eba7628e..07e4d33db 100644 --- a/control-plane/roles/logging/templates/alloy-config.alloy.j2 +++ b/control-plane/roles/logging/templates/alloy-config.alloy.j2 @@ -99,6 +99,11 @@ loki.relabel "events" { target_label = "cluster" replacement = "{{ logging_alloy_cluster_label }}" } + + rule { + target_label = "job" + replacement = "monitoring/event-exporter" + } } // default loki endpoints diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index bd810ef5c..51161986e 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -22,8 +22,8 @@ The following variables can be set to configure the role: | ------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | rethinkdb_exporter_name | yes | rethinkdb exporter image name | | rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | -| event_exporter_name | yes | event exporter image name | -| event_exporter_tag | yes | event exporter image tag | +| event_exporter_name | | event exporter image name — only deployed when set; not needed when using Alloy for log collection | +| event_exporter_tag | | event exporter image tag — only deployed when set; not needed when using Alloy for log collection | | gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | | gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | | prometheus_chart_version | yes | version of the prometheus stack chart | @@ -107,3 +107,9 @@ monitoring_thanos_receive_ingress_basic_auth_password: mysecret ``` The `gardener-logging` role automatically picks up these credentials for Alloy's `prometheus.remote_write` when `monitoring_thanos_receive_ingress_enabled: true`. + +### event-exporter is now opt-in + +The event-exporter Deployment (which wrote Kubernetes events to stdout for Promtail to scrape) is now only deployed when `event_exporter_name` and `event_exporter_tag` are set. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. + +Once you have verified that Alloy is collecting events correctly, remove `event_exporter_name` and `event_exporter_tag` from your config. The monitoring role will skip the event-exporter Deployment on the next run. diff --git a/control-plane/roles/monitoring/tasks/exporters.yaml b/control-plane/roles/monitoring/tasks/exporters.yaml index 0bf33da17..609e6a358 100644 --- a/control-plane/roles/monitoring/tasks/exporters.yaml +++ b/control-plane/roles/monitoring/tasks/exporters.yaml @@ -16,6 +16,7 @@ definition: "{{ lookup('template', 'metrics-exporters/event-exporter.yaml') }}" namespace: "{{ monitoring_namespace }}" apply: true + when: event_exporter_tag is defined # TODO: Fix to make compatible with gardener-operator # - name: Deploy gardener-metrics-exporter diff --git a/control-plane/roles/monitoring/tasks/main.yaml b/control-plane/roles/monitoring/tasks/main.yaml index 512aa39b0..a9441fdcc 100644 --- a/control-plane/roles/monitoring/tasks/main.yaml +++ b/control-plane/roles/monitoring/tasks/main.yaml @@ -9,8 +9,6 @@ that: - rethinkdb_exporter_name is defined - rethinkdb_exporter_tag is defined - - event_exporter_name is defined - - event_exporter_tag is defined - gardener_metrics_exporter_image_name is defined - gardener_metrics_exporter_image_tag is defined From b81023a11c248016ed739771d05a4ce1e9cb7ca0 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Tue, 19 May 2026 14:12:04 +0200 Subject: [PATCH 09/24] feat: provide tasks to cleanup promtail --- .../roles/gardener-logging/README.md | 6 +- .../tasks/gardener-shooted-seed.yaml | 8 ++ .../roles/gardener-logging/tasks/main.yaml | 7 ++ control-plane/roles/logging/README.md | 6 +- control-plane/roles/logging/tasks/main.yaml | 7 ++ control-plane/roles/monitoring/README.md | 117 +++++++++--------- .../roles/monitoring/tasks/exporters.yaml | 29 +++++ 7 files changed, 116 insertions(+), 64 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 795fe4c8a..7581a19ee 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -26,6 +26,7 @@ The following variables can be set to configure the role: | gardener_logging_alloy_chart_repo | | | Repository for alloy — required when `gardener_logging_alloy_enabled: true` | | gardener_logging_promtail_chart_version | | | Helm chart version for promtail — required when `gardener_logging_promtail_enabled: true` | | gardener_logging_promtail_chart_repo | | | Repository for promtail — required when `gardener_logging_promtail_enabled: true` | +| gardener_logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release from the garden cluster and all shooted seeds. Set `true` after cutover to let the role remove the releases automatically. Idempotent — safe to run even if the releases are already gone. | | gardener_logging_namespace | | | The deployment's target namespace | | gardener_logging_ingress_dns | | | DNS for loki ingress | | gardener_logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | @@ -95,7 +96,6 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and - **Kubernetes events are now built-in.** Promtail required a separate event-exporter Deployment. Alloy collects events natively via `loki.source.kubernetes_events` and labels them `job="monitoring/event-exporter"` for full backward compatibility. - **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to the control-plane Thanos Receive ingress, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_ingress_enabled: true`. - **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. -- **One new label.** `pod_uid` is added to pod log streams. All other labels (`app`, `instance`, `component`, `job`, `namespace`, etc.) are identical to Promtail's chart defaults. | Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | | --------------------------------- | -------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -110,8 +110,8 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 2. Add to your inventory: `gardener_logging_promtail_enabled: true, gardener_logging_alloy_enabled: false`. The role will continue deploying Promtail and emit a deprecation warning each run. 3. When ready: set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. -5. Set `gardener_logging_promtail_enabled: false` and re-run. Remove Promtail from each seed: `helm uninstall promtail -n {{ gardener_logging_namespace }}`. -6. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). +5. Set `gardener_logging_promtail_enabled: false` and `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release from the garden cluster and every shooted seed automatically. Remove `gardener_logging_promtail_migrate_cleanup` from your inventory afterwards. +6. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). ### Thanos Receive credentials diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index cdba64a1f..af17adc37 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -35,3 +35,11 @@ kubeconfig: "{{ _shoot_kubeconfig }}" create_namespace: true when: gardener_logging_promtail_enabled + +- name: Remove Promtail from seed + kubernetes.core.helm: + name: promtail + namespace: "{{ gardener_logging_namespace }}" + kubeconfig: "{{ _shoot_kubeconfig }}" + state: absent + when: gardener_logging_promtail_migrate_cleanup | default(false) | bool diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index a4602b353..11bb91adf 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -65,6 +65,13 @@ gardener_logging_shooted_seed: name: "{{ gardener_logging_garden_name }}" +- name: Remove Promtail from garden cluster + kubernetes.core.helm: + name: promtail + namespace: "{{ gardener_logging_namespace }}" + state: absent + when: gardener_logging_deploy_to_garden_cluster and gardener_logging_promtail_migrate_cleanup | default(false) | bool + - name: Loop over Gardener shooted seeds include_tasks: gardener-shooted-seed.yaml loop: "{{ gardener_logging_shooted_seeds }}" diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index d39c680a9..56382c3c8 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -34,6 +34,7 @@ The following variables can be set to configure the role: | logging_alloy_chart_repo | | | Repository for alloy — required when `logging_alloy_enabled: true` | | logging_promtail_chart_version | | | Helm chart version for promtail — required when `logging_promtail_enabled: true` | | logging_promtail_chart_repo | | | Repository for promtail — required when `logging_promtail_enabled: true` | +| logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release. Set `true` after cutover to let the role remove the release automatically. Idempotent — safe to run even if the release is already gone. | | logging_namespace | | | The deployment's target namespace | | logging_loki_size | | | The size of the volume that loki will use for storing logs | | logging_ingress_dns | | | DNS for loki ingress | @@ -110,7 +111,6 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and - **Kubernetes events are now built-in.** Promtail required a separate event-exporter Deployment. Alloy collects events natively via `loki.source.kubernetes_events` and labels them `job="monitoring/event-exporter"` for full backward compatibility. - **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to in-cluster Thanos Receive, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_enabled: true`. - **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. -- **One new label.** `pod_uid` is added to pod log streams. All other labels (`app`, `instance`, `component`, `job`, `namespace`, etc.) are identical to Promtail's chart defaults. | Scenario | `logging_alloy_enabled` | `logging_promtail_enabled` | Notes | | --------------------------------- | ----------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -124,5 +124,5 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 1. Add to your inventory: `logging_promtail_enabled: true, logging_alloy_enabled: false`. The role will continue deploying Promtail and emit a deprecation warning each run. 2. When ready: set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 3. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. -4. Set `logging_promtail_enabled: false` and re-run. Remove the Promtail release: `helm uninstall promtail -n {{ logging_namespace }}`. -5. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). +4. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. +5. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index b4fd54958..5ea64ac75 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -79,3 +79,10 @@ helm_chart_version: "{{ logging_promtail_chart_version }}" helm_value_file_template: "promtail-values.yaml" when: logging_promtail_enabled + +- name: Remove Promtail + kubernetes.core.helm: + name: promtail + namespace: "{{ logging_namespace }}" + state: absent + when: logging_promtail_migrate_cleanup | default(false) | bool diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index 51161986e..c144975d8 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -18,63 +18,64 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| ------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| rethinkdb_exporter_name | yes | rethinkdb exporter image name | -| rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | -| event_exporter_name | | event exporter image name — only deployed when set; not needed when using Alloy for log collection | -| event_exporter_tag | | event exporter image tag — only deployed when set; not needed when using Alloy for log collection | -| gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | -| gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | -| prometheus_chart_version | yes | version of the prometheus stack chart | -| prometheus_stack_repo | yes | chart of the prometheus stack | -| monitoring_namespace | | Name of the monitoring namespace | -| monitoring_ingress_grafana_tls | | If enabled, exposes Grafana through HTTPS on the ingress | -| monitoring_grafana_ingress_dns | | The dns name used for exposing Grafana via ingress | -| monitoring_prometheus_ingress_dns | | If enabled, exposes Prometheus through HTTPS on the ingress | -| monitoring_prometheus_image_tag | | Prometheus container image tag, defaults to chart's default | -| monitoring_prometheus_ingress_enabled | | Enables ingress for prometheus | -| monitoring_prometheus_storage_spec | | Prometheus storage spec, see [Storage Configuration](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) | -| monitoring_grafana_ingress_enabled | | Enables ingress for grafana | -| monitoring_prometheus_core_dns_enabled | | Enables CoreDns monitoring | -| monitoring_prometheus_kube_dns_enabled | | Enables KubeDns monitoring | -| monitoring_prometheus_kube_proxy_enabled | | Enables KubeProxy monitoring | -| monitoring_prometheus_kube_proxy_service_selector | | The service selector for kube-proxy | -| monitoring_prometheus_kube_scheduler_enabled | | Enables KubeScheduler monitoring | -| monitoring_prometheus_kube_etcd_enabled | | Enables KubeEtcd monitoring | -| monitoring_prometheus_kube_controller_manager_enabled | | Enables KubeControllerManager monitoring | -| monitoring_grafana_admin_password | | Sets the admin password for Grafana | -| monitoring_grafana_dashboard_timezone | | Sets the default's dashboard timezone for Grafana | -| monitoring_grafana_additional_datasources | | Configures additional datasources for Grafana | -| monitoring_grafana_github_oauth | | [Configure GitHub OAuth2 authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/github/) | -| monitoring_grafana_extra_secret_mounts | | Extra secret mounts | -| monitoring_slack_api_url | | The URL for the Slack API, required to send alerts. This must be a valid and accessible Slack webhook URL specific to the channel where alerts will be sent. | -| monitoring_slack_notification_channel | | The name or ID of the Slack channel where the alerts will be posted. This must correspond to an existing Slack channel within the workspace defined by the `monitoring_slack_api_url`. | -| monitoring_slack_notification_icon_url | | The URL of an image that will be used as the icon for notifications in Slack. This is optional, and if not provided, the default icon will be used. The URL must be accessible by Slack and should point to a valid image file. | -| monitoring_slack_notification_title | | The title of the Slack notification. This will be used as the headline or subject of the alert and can be a static text or include dynamic content based on the alert data. | -| monitoring_slack_notification_text | | The main text body of the Slack notification. This should include the detailed information about the alert and can support Slack's formatting and templating for more structured and readable notifications. | -| monitoring_alertmanager_additional_routes | | Additional [routes](https://prometheus.io/docs/alerting/latest/configuration/#route-related-settings) for the alertmanager. | -| monitoring_alertmanager_additional_receivers | | Additional [receivers](https://prometheus.io/docs/alerting/latest/configuration/#general-receiver-related-settings) for the alertmanager. | -| monitoring_alertmanager_webhook_logger_repeat_interval | | The alertmanager repeat interval for the webhook-logger | -| monitoring_alertmanager_webhook_slack_repeat_interval | | The alertmanager repeat interval for slack | -| monitoring_metal_api_url | | The URL where to reach metal-api | -| monitoring_metal_api_hmac | | The hmac to authenticate against metal-api | -| monitoring_thanos_object_store_config | | Object storage used by Thanos, see [Official Documentation](https://thanos.io/tip/thanos/storage.md/#supported-clients) | -| monitoring_thanos_object_store_resource_preset | | The resource preset to use for the object store (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | -| monitoring_thanos_object_store_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the storegateway pod (init may take quite long depending on the bucket size) | -| monitoring_thanos_receive_enabled | | Enable Thanos Receive component | -| monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | -| monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | -| monitoring_thanos_receive_ingress_basic_auth_user | | Basic auth username for the Thanos Receive ingress (default: `thanos-receive`). Used by nginx and automatically by `gardener-logging` Alloy for `prometheus.remote_write`. | -| monitoring_thanos_receive_ingress_basic_auth_password | | Basic auth password for the Thanos Receive ingress. When set, nginx basic auth is enabled and the htpasswd entry is generated automatically. Also used by `gardener-logging` Alloy for `prometheus.remote_write`. | -| monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | -| monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | -| monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | -| monitoring_thanos_receive_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the receiver pod (init may take quite long depending on the bucket size) | -| monitoring_thanos_receive_resource_preset | | The resource preset to use for the receiver (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | -| monitoring_thanos_compactor_size | | The PVC size of the Thanos Compactor component | -| monitoring_thanos_compactor_storage_class | | The storage class used for the compactor | -| monitoring_thanos_compactor_resource_preset | | The resource preset to use for the compactor (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| Name | Mandatory | Description | +| ------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| rethinkdb_exporter_name | yes | rethinkdb exporter image name | +| rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | +| event_exporter_name | | event exporter image name — only deployed when set; not needed when using Alloy for log collection | +| event_exporter_tag | | event exporter image tag — only deployed when set; not needed when using Alloy for log collection | +| event_exporter_migrate_cleanup | | Remove all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Set `true` after removing `event_exporter_tag` to let the role clean up the running resources automatically. Idempotent — safe to run even if the resources are already gone. | +| gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | +| gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | +| prometheus_chart_version | yes | version of the prometheus stack chart | +| prometheus_stack_repo | yes | chart of the prometheus stack | +| monitoring_namespace | | Name of the monitoring namespace | +| monitoring_ingress_grafana_tls | | If enabled, exposes Grafana through HTTPS on the ingress | +| monitoring_grafana_ingress_dns | | The dns name used for exposing Grafana via ingress | +| monitoring_prometheus_ingress_dns | | If enabled, exposes Prometheus through HTTPS on the ingress | +| monitoring_prometheus_image_tag | | Prometheus container image tag, defaults to chart's default | +| monitoring_prometheus_ingress_enabled | | Enables ingress for prometheus | +| monitoring_prometheus_storage_spec | | Prometheus storage spec, see [Storage Configuration](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) | +| monitoring_grafana_ingress_enabled | | Enables ingress for grafana | +| monitoring_prometheus_core_dns_enabled | | Enables CoreDns monitoring | +| monitoring_prometheus_kube_dns_enabled | | Enables KubeDns monitoring | +| monitoring_prometheus_kube_proxy_enabled | | Enables KubeProxy monitoring | +| monitoring_prometheus_kube_proxy_service_selector | | The service selector for kube-proxy | +| monitoring_prometheus_kube_scheduler_enabled | | Enables KubeScheduler monitoring | +| monitoring_prometheus_kube_etcd_enabled | | Enables KubeEtcd monitoring | +| monitoring_prometheus_kube_controller_manager_enabled | | Enables KubeControllerManager monitoring | +| monitoring_grafana_admin_password | | Sets the admin password for Grafana | +| monitoring_grafana_dashboard_timezone | | Sets the default's dashboard timezone for Grafana | +| monitoring_grafana_additional_datasources | | Configures additional datasources for Grafana | +| monitoring_grafana_github_oauth | | [Configure GitHub OAuth2 authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/github/) | +| monitoring_grafana_extra_secret_mounts | | Extra secret mounts | +| monitoring_slack_api_url | | The URL for the Slack API, required to send alerts. This must be a valid and accessible Slack webhook URL specific to the channel where alerts will be sent. | +| monitoring_slack_notification_channel | | The name or ID of the Slack channel where the alerts will be posted. This must correspond to an existing Slack channel within the workspace defined by the `monitoring_slack_api_url`. | +| monitoring_slack_notification_icon_url | | The URL of an image that will be used as the icon for notifications in Slack. This is optional, and if not provided, the default icon will be used. The URL must be accessible by Slack and should point to a valid image file. | +| monitoring_slack_notification_title | | The title of the Slack notification. This will be used as the headline or subject of the alert and can be a static text or include dynamic content based on the alert data. | +| monitoring_slack_notification_text | | The main text body of the Slack notification. This should include the detailed information about the alert and can support Slack's formatting and templating for more structured and readable notifications. | +| monitoring_alertmanager_additional_routes | | Additional [routes](https://prometheus.io/docs/alerting/latest/configuration/#route-related-settings) for the alertmanager. | +| monitoring_alertmanager_additional_receivers | | Additional [receivers](https://prometheus.io/docs/alerting/latest/configuration/#general-receiver-related-settings) for the alertmanager. | +| monitoring_alertmanager_webhook_logger_repeat_interval | | The alertmanager repeat interval for the webhook-logger | +| monitoring_alertmanager_webhook_slack_repeat_interval | | The alertmanager repeat interval for slack | +| monitoring_metal_api_url | | The URL where to reach metal-api | +| monitoring_metal_api_hmac | | The hmac to authenticate against metal-api | +| monitoring_thanos_object_store_config | | Object storage used by Thanos, see [Official Documentation](https://thanos.io/tip/thanos/storage.md/#supported-clients) | +| monitoring_thanos_object_store_resource_preset | | The resource preset to use for the object store (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| monitoring_thanos_object_store_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the storegateway pod (init may take quite long depending on the bucket size) | +| monitoring_thanos_receive_enabled | | Enable Thanos Receive component | +| monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | +| monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | +| monitoring_thanos_receive_ingress_basic_auth_user | | Basic auth username for the Thanos Receive ingress (default: `thanos-receive`). Used by nginx and automatically by `gardener-logging` Alloy for `prometheus.remote_write`. | +| monitoring_thanos_receive_ingress_basic_auth_password | | Basic auth password for the Thanos Receive ingress. When set, nginx basic auth is enabled and the htpasswd entry is generated automatically. Also used by `gardener-logging` Alloy for `prometheus.remote_write`. | +| monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | +| monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | +| monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | +| monitoring_thanos_receive_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the receiver pod (init may take quite long depending on the bucket size) | +| monitoring_thanos_receive_resource_preset | | The resource preset to use for the receiver (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| monitoring_thanos_compactor_size | | The PVC size of the Thanos Compactor component | +| monitoring_thanos_compactor_storage_class | | The storage class used for the compactor | +| monitoring_thanos_compactor_resource_preset | | The resource preset to use for the compactor (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | ### Gardener @@ -112,4 +113,4 @@ The `gardener-logging` role automatically picks up these credentials for Alloy's The event-exporter Deployment (which wrote Kubernetes events to stdout for Promtail to scrape) is now only deployed when `event_exporter_name` and `event_exporter_tag` are set. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. -Once you have verified that Alloy is collecting events correctly, remove `event_exporter_name` and `event_exporter_tag` from your config. The monitoring role will skip the event-exporter Deployment on the next run. +Once you have verified that Alloy is collecting events correctly, remove `event_exporter_name` and `event_exporter_tag` from your config and set `event_exporter_migrate_cleanup: true`. On the next run the monitoring role will skip the Deployment and delete all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Remove `event_exporter_migrate_cleanup` from your inventory afterwards. diff --git a/control-plane/roles/monitoring/tasks/exporters.yaml b/control-plane/roles/monitoring/tasks/exporters.yaml index 609e6a358..05fdbb3d1 100644 --- a/control-plane/roles/monitoring/tasks/exporters.yaml +++ b/control-plane/roles/monitoring/tasks/exporters.yaml @@ -18,6 +18,35 @@ apply: true when: event_exporter_tag is defined +- name: Remove event-exporter + k8s: + state: absent + definition: + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: event-exporter + namespace: "{{ monitoring_namespace }}" + - apiVersion: v1 + kind: ServiceAccount + metadata: + name: event-exporter + namespace: "{{ monitoring_namespace }}" + - apiVersion: v1 + kind: ConfigMap + metadata: + name: event-exporter-cfg + namespace: "{{ monitoring_namespace }}" + - apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: event-exporter + - apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: event-exporter + when: event_exporter_migrate_cleanup | default(false) | bool + # TODO: Fix to make compatible with gardener-operator # - name: Deploy gardener-metrics-exporter # k8s: From d55af1a4f4aa2c30f60759ea4dfe0feb948eacb7 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Wed, 20 May 2026 13:14:31 +0200 Subject: [PATCH 10/24] chore: add flag for event exporter deployment --- .../roles/gardener-logging/README.md | 2 +- control-plane/roles/logging/README.md | 2 +- control-plane/roles/monitoring/README.md | 121 +++++++++--------- .../roles/monitoring/defaults/main.yaml | 3 + .../roles/monitoring/tasks/exporters.yaml | 2 +- 5 files changed, 67 insertions(+), 63 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 7581a19ee..ff467f2b1 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -111,7 +111,7 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 3. When ready: set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 5. Set `gardener_logging_promtail_enabled: false` and `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release from the garden cluster and every shooted seed automatically. Remove `gardener_logging_promtail_migrate_cleanup` from your inventory afterwards. -6. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). +6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). ### Thanos Receive credentials diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 56382c3c8..c0b53440b 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -125,4 +125,4 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 2. When ready: set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 3. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 4. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. -5. Remove `event_exporter_name`/`event_exporter_tag` from your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). +5. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index c144975d8..a9fd1de82 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -18,64 +18,65 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| ------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| rethinkdb_exporter_name | yes | rethinkdb exporter image name | -| rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | -| event_exporter_name | | event exporter image name — only deployed when set; not needed when using Alloy for log collection | -| event_exporter_tag | | event exporter image tag — only deployed when set; not needed when using Alloy for log collection | -| event_exporter_migrate_cleanup | | Remove all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Set `true` after removing `event_exporter_tag` to let the role clean up the running resources automatically. Idempotent — safe to run even if the resources are already gone. | -| gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | -| gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | -| prometheus_chart_version | yes | version of the prometheus stack chart | -| prometheus_stack_repo | yes | chart of the prometheus stack | -| monitoring_namespace | | Name of the monitoring namespace | -| monitoring_ingress_grafana_tls | | If enabled, exposes Grafana through HTTPS on the ingress | -| monitoring_grafana_ingress_dns | | The dns name used for exposing Grafana via ingress | -| monitoring_prometheus_ingress_dns | | If enabled, exposes Prometheus through HTTPS on the ingress | -| monitoring_prometheus_image_tag | | Prometheus container image tag, defaults to chart's default | -| monitoring_prometheus_ingress_enabled | | Enables ingress for prometheus | -| monitoring_prometheus_storage_spec | | Prometheus storage spec, see [Storage Configuration](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) | -| monitoring_grafana_ingress_enabled | | Enables ingress for grafana | -| monitoring_prometheus_core_dns_enabled | | Enables CoreDns monitoring | -| monitoring_prometheus_kube_dns_enabled | | Enables KubeDns monitoring | -| monitoring_prometheus_kube_proxy_enabled | | Enables KubeProxy monitoring | -| monitoring_prometheus_kube_proxy_service_selector | | The service selector for kube-proxy | -| monitoring_prometheus_kube_scheduler_enabled | | Enables KubeScheduler monitoring | -| monitoring_prometheus_kube_etcd_enabled | | Enables KubeEtcd monitoring | -| monitoring_prometheus_kube_controller_manager_enabled | | Enables KubeControllerManager monitoring | -| monitoring_grafana_admin_password | | Sets the admin password for Grafana | -| monitoring_grafana_dashboard_timezone | | Sets the default's dashboard timezone for Grafana | -| monitoring_grafana_additional_datasources | | Configures additional datasources for Grafana | -| monitoring_grafana_github_oauth | | [Configure GitHub OAuth2 authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/github/) | -| monitoring_grafana_extra_secret_mounts | | Extra secret mounts | -| monitoring_slack_api_url | | The URL for the Slack API, required to send alerts. This must be a valid and accessible Slack webhook URL specific to the channel where alerts will be sent. | -| monitoring_slack_notification_channel | | The name or ID of the Slack channel where the alerts will be posted. This must correspond to an existing Slack channel within the workspace defined by the `monitoring_slack_api_url`. | -| monitoring_slack_notification_icon_url | | The URL of an image that will be used as the icon for notifications in Slack. This is optional, and if not provided, the default icon will be used. The URL must be accessible by Slack and should point to a valid image file. | -| monitoring_slack_notification_title | | The title of the Slack notification. This will be used as the headline or subject of the alert and can be a static text or include dynamic content based on the alert data. | -| monitoring_slack_notification_text | | The main text body of the Slack notification. This should include the detailed information about the alert and can support Slack's formatting and templating for more structured and readable notifications. | -| monitoring_alertmanager_additional_routes | | Additional [routes](https://prometheus.io/docs/alerting/latest/configuration/#route-related-settings) for the alertmanager. | -| monitoring_alertmanager_additional_receivers | | Additional [receivers](https://prometheus.io/docs/alerting/latest/configuration/#general-receiver-related-settings) for the alertmanager. | -| monitoring_alertmanager_webhook_logger_repeat_interval | | The alertmanager repeat interval for the webhook-logger | -| monitoring_alertmanager_webhook_slack_repeat_interval | | The alertmanager repeat interval for slack | -| monitoring_metal_api_url | | The URL where to reach metal-api | -| monitoring_metal_api_hmac | | The hmac to authenticate against metal-api | -| monitoring_thanos_object_store_config | | Object storage used by Thanos, see [Official Documentation](https://thanos.io/tip/thanos/storage.md/#supported-clients) | -| monitoring_thanos_object_store_resource_preset | | The resource preset to use for the object store (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | -| monitoring_thanos_object_store_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the storegateway pod (init may take quite long depending on the bucket size) | -| monitoring_thanos_receive_enabled | | Enable Thanos Receive component | -| monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | -| monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | -| monitoring_thanos_receive_ingress_basic_auth_user | | Basic auth username for the Thanos Receive ingress (default: `thanos-receive`). Used by nginx and automatically by `gardener-logging` Alloy for `prometheus.remote_write`. | -| monitoring_thanos_receive_ingress_basic_auth_password | | Basic auth password for the Thanos Receive ingress. When set, nginx basic auth is enabled and the htpasswd entry is generated automatically. Also used by `gardener-logging` Alloy for `prometheus.remote_write`. | -| monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | -| monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | -| monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | -| monitoring_thanos_receive_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the receiver pod (init may take quite long depending on the bucket size) | -| monitoring_thanos_receive_resource_preset | | The resource preset to use for the receiver (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | -| monitoring_thanos_compactor_size | | The PVC size of the Thanos Compactor component | -| monitoring_thanos_compactor_storage_class | | The storage class used for the compactor | -| monitoring_thanos_compactor_resource_preset | | The resource preset to use for the compactor (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| Name | Mandatory | Description | +| ------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| rethinkdb_exporter_name | yes | rethinkdb exporter image name | +| rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | +| event_exporter_enabled | | Deploy the event-exporter. Set `true` to opt in; not needed when using Alloy for log collection (events are collected natively via `loki.source.kubernetes_events`) | +| event_exporter_name | | event exporter image name | +| event_exporter_tag | | event exporter image tag | +| event_exporter_migrate_cleanup | | Remove all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Set `true` after setting `event_exporter_enabled: false` to let the role clean up the running resources automatically. Idempotent — safe to run even if the resources are already gone. | +| gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | +| gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | +| prometheus_chart_version | yes | version of the prometheus stack chart | +| prometheus_stack_repo | yes | chart of the prometheus stack | +| monitoring_namespace | | Name of the monitoring namespace | +| monitoring_ingress_grafana_tls | | If enabled, exposes Grafana through HTTPS on the ingress | +| monitoring_grafana_ingress_dns | | The dns name used for exposing Grafana via ingress | +| monitoring_prometheus_ingress_dns | | If enabled, exposes Prometheus through HTTPS on the ingress | +| monitoring_prometheus_image_tag | | Prometheus container image tag, defaults to chart's default | +| monitoring_prometheus_ingress_enabled | | Enables ingress for prometheus | +| monitoring_prometheus_storage_spec | | Prometheus storage spec, see [Storage Configuration](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) | +| monitoring_grafana_ingress_enabled | | Enables ingress for grafana | +| monitoring_prometheus_core_dns_enabled | | Enables CoreDns monitoring | +| monitoring_prometheus_kube_dns_enabled | | Enables KubeDns monitoring | +| monitoring_prometheus_kube_proxy_enabled | | Enables KubeProxy monitoring | +| monitoring_prometheus_kube_proxy_service_selector | | The service selector for kube-proxy | +| monitoring_prometheus_kube_scheduler_enabled | | Enables KubeScheduler monitoring | +| monitoring_prometheus_kube_etcd_enabled | | Enables KubeEtcd monitoring | +| monitoring_prometheus_kube_controller_manager_enabled | | Enables KubeControllerManager monitoring | +| monitoring_grafana_admin_password | | Sets the admin password for Grafana | +| monitoring_grafana_dashboard_timezone | | Sets the default's dashboard timezone for Grafana | +| monitoring_grafana_additional_datasources | | Configures additional datasources for Grafana | +| monitoring_grafana_github_oauth | | [Configure GitHub OAuth2 authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/github/) | +| monitoring_grafana_extra_secret_mounts | | Extra secret mounts | +| monitoring_slack_api_url | | The URL for the Slack API, required to send alerts. This must be a valid and accessible Slack webhook URL specific to the channel where alerts will be sent. | +| monitoring_slack_notification_channel | | The name or ID of the Slack channel where the alerts will be posted. This must correspond to an existing Slack channel within the workspace defined by the `monitoring_slack_api_url`. | +| monitoring_slack_notification_icon_url | | The URL of an image that will be used as the icon for notifications in Slack. This is optional, and if not provided, the default icon will be used. The URL must be accessible by Slack and should point to a valid image file. | +| monitoring_slack_notification_title | | The title of the Slack notification. This will be used as the headline or subject of the alert and can be a static text or include dynamic content based on the alert data. | +| monitoring_slack_notification_text | | The main text body of the Slack notification. This should include the detailed information about the alert and can support Slack's formatting and templating for more structured and readable notifications. | +| monitoring_alertmanager_additional_routes | | Additional [routes](https://prometheus.io/docs/alerting/latest/configuration/#route-related-settings) for the alertmanager. | +| monitoring_alertmanager_additional_receivers | | Additional [receivers](https://prometheus.io/docs/alerting/latest/configuration/#general-receiver-related-settings) for the alertmanager. | +| monitoring_alertmanager_webhook_logger_repeat_interval | | The alertmanager repeat interval for the webhook-logger | +| monitoring_alertmanager_webhook_slack_repeat_interval | | The alertmanager repeat interval for slack | +| monitoring_metal_api_url | | The URL where to reach metal-api | +| monitoring_metal_api_hmac | | The hmac to authenticate against metal-api | +| monitoring_thanos_object_store_config | | Object storage used by Thanos, see [Official Documentation](https://thanos.io/tip/thanos/storage.md/#supported-clients) | +| monitoring_thanos_object_store_resource_preset | | The resource preset to use for the object store (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| monitoring_thanos_object_store_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the storegateway pod (init may take quite long depending on the bucket size) | +| monitoring_thanos_receive_enabled | | Enable Thanos Receive component | +| monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | +| monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | +| monitoring_thanos_receive_ingress_basic_auth_user | | Basic auth username for the Thanos Receive ingress (default: `thanos-receive`). Used by nginx and automatically by `gardener-logging` Alloy for `prometheus.remote_write`. | +| monitoring_thanos_receive_ingress_basic_auth_password | | Basic auth password for the Thanos Receive ingress. When set, nginx basic auth is enabled and the htpasswd entry is generated automatically. Also used by `gardener-logging` Alloy for `prometheus.remote_write`. | +| monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | +| monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | +| monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | +| monitoring_thanos_receive_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the receiver pod (init may take quite long depending on the bucket size) | +| monitoring_thanos_receive_resource_preset | | The resource preset to use for the receiver (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| monitoring_thanos_compactor_size | | The PVC size of the Thanos Compactor component | +| monitoring_thanos_compactor_storage_class | | The storage class used for the compactor | +| monitoring_thanos_compactor_resource_preset | | The resource preset to use for the compactor (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | ### Gardener @@ -111,6 +112,6 @@ The `gardener-logging` role automatically picks up these credentials for Alloy's ### event-exporter is now opt-in -The event-exporter Deployment (which wrote Kubernetes events to stdout for Promtail to scrape) is now only deployed when `event_exporter_name` and `event_exporter_tag` are set. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. +The event-exporter Deployment (which wrote Kubernetes events to stdout for Promtail to scrape) is now only deployed when `event_exporter_enabled: true` is set. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. -Once you have verified that Alloy is collecting events correctly, remove `event_exporter_name` and `event_exporter_tag` from your config and set `event_exporter_migrate_cleanup: true`. On the next run the monitoring role will skip the Deployment and delete all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Remove `event_exporter_migrate_cleanup` from your inventory afterwards. +Once you have verified that Alloy is collecting events correctly, set `event_exporter_enabled: false` (or remove it — the default is `false`) and set `event_exporter_migrate_cleanup: true`. On the next run the monitoring role will skip the Deployment and delete all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Remove `event_exporter_migrate_cleanup` from your inventory afterwards. diff --git a/control-plane/roles/monitoring/defaults/main.yaml b/control-plane/roles/monitoring/defaults/main.yaml index 120818713..e790dc585 100644 --- a/control-plane/roles/monitoring/defaults/main.yaml +++ b/control-plane/roles/monitoring/defaults/main.yaml @@ -36,6 +36,9 @@ monitoring_alertmanager_ingress_basic_auth_password_salt: "{{ monitoring_alertma monitoring_alertmanager_webhook_logger_repeat_interval: 60m monitoring_alertmanager_webhook_slack_repeat_interval: 24h +# event exporter +event_exporter_enabled: false + # metal metrics exporter monitoring_metal_api_url: "http://metal-api.metal-control-plane.svc:8080/metal" monitoring_metal_api_hmac: "metal-view" diff --git a/control-plane/roles/monitoring/tasks/exporters.yaml b/control-plane/roles/monitoring/tasks/exporters.yaml index 05fdbb3d1..d658a5dbd 100644 --- a/control-plane/roles/monitoring/tasks/exporters.yaml +++ b/control-plane/roles/monitoring/tasks/exporters.yaml @@ -16,7 +16,7 @@ definition: "{{ lookup('template', 'metrics-exporters/event-exporter.yaml') }}" namespace: "{{ monitoring_namespace }}" apply: true - when: event_exporter_tag is defined + when: event_exporter_enabled | default(false) | bool - name: Remove event-exporter k8s: From 40d4a2d406e4e5c461850c6fc2ff525ba38f4a9f Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Wed, 20 May 2026 13:40:15 +0200 Subject: [PATCH 11/24] chore: backwards compatibility --- .../roles/gardener-logging/README.md | 42 ++++++++-------- .../roles/gardener-logging/defaults/main.yaml | 14 +++--- .../roles/gardener-logging/tasks/main.yaml | 25 ++++------ control-plane/roles/logging/README.md | 48 +++++++++---------- .../roles/logging/defaults/main.yaml | 14 +++--- control-plane/roles/logging/tasks/main.yaml | 24 ++++------ control-plane/roles/monitoring/README.md | 8 ++-- .../roles/monitoring/defaults/main.yaml | 2 +- 8 files changed, 80 insertions(+), 97 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index ff467f2b1..e55c9b222 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -4,7 +4,7 @@ Deploys [Grafana Alloy](https://grafana.com/docs/alloy/latest/) into Gardener sh Expects the [logging role](../logging/) to have been deployed first. -This role supports deploying Alloy and/or Promtail as log collectors. Both `gardener_logging_alloy_enabled` and `gardener_logging_promtail_enabled` **must be set explicitly** — there are no defaults. See [Migration from Promtail](#migration-from-promtail) for guidance. +This role supports deploying Alloy and/or Promtail as log collectors. Promtail is deployed by default for backward compatibility. See [Migration from Promtail](#migration-from-promtail) for guidance on switching to Alloy. ## Configuration @@ -18,21 +18,21 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Default | Description | -| ------------------------------------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| gardener_logging_alloy_enabled | yes | | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. | -| gardener_logging_promtail_enabled | yes | | Deploy Promtail. Set `true` to keep existing behavior (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | -| gardener_logging_alloy_chart_version | | | Helm chart version for alloy — required when `gardener_logging_alloy_enabled: true` | -| gardener_logging_alloy_chart_repo | | | Repository for alloy — required when `gardener_logging_alloy_enabled: true` | -| gardener_logging_promtail_chart_version | | | Helm chart version for promtail — required when `gardener_logging_promtail_enabled: true` | -| gardener_logging_promtail_chart_repo | | | Repository for promtail — required when `gardener_logging_promtail_enabled: true` | -| gardener_logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release from the garden cluster and all shooted seeds. Set `true` after cutover to let the role remove the releases automatically. Idempotent — safe to run even if the releases are already gone. | -| gardener_logging_namespace | | | The deployment's target namespace | -| gardener_logging_ingress_dns | | | DNS for loki ingress | -| gardener_logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | -| gardener_logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | -| gardener_logging_deploy_to_garden_cluster | | `true` | Deploys Alloy also into the garden cluster | -| gardener_logging_shooted_seeds | | | Shooted seed names on which to deploy Alloy that logs to loki | +| Name | Mandatory | Default | Description | +| ------------------------------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| gardener_logging_alloy_enabled | | `false` | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. | +| gardener_logging_promtail_enabled | | `true` | Deploy Promtail (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | +| gardener_logging_alloy_chart_version | | | Helm chart version for alloy — required when `gardener_logging_alloy_enabled: true` | +| gardener_logging_alloy_chart_repo | | | Repository for alloy — required when `gardener_logging_alloy_enabled: true` | +| gardener_logging_promtail_chart_version | | | Helm chart version for promtail — required when `gardener_logging_promtail_enabled: true` | +| gardener_logging_promtail_chart_repo | | | Repository for promtail — required when `gardener_logging_promtail_enabled: true` | +| gardener_logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release from the garden cluster and all shooted seeds. Set `true` after cutover to let the role remove the releases automatically. Idempotent — safe to run even if the releases are already gone. | +| gardener_logging_namespace | | | The deployment's target namespace | +| gardener_logging_ingress_dns | | | DNS for loki ingress | +| gardener_logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | +| gardener_logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | +| gardener_logging_deploy_to_garden_cluster | | `true` | Deploys Alloy also into the garden cluster | +| gardener_logging_shooted_seeds | | | Shooted seed names on which to deploy Alloy that logs to loki | ### Alloy @@ -87,7 +87,7 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. ## Migration from Promtail -Alloy is the recommended log collector. Both `gardener_logging_alloy_enabled` and `gardener_logging_promtail_enabled` must be **explicitly set in your inventory** — the role will fail immediately on upgrade if they are missing, forcing a conscious migration decision. +Alloy is the recommended log collector. Promtail is deployed by default — existing installations continue to work without changes after upgrading. > **Promtail is deprecated.** Setting `gardener_logging_promtail_enabled: true` emits a deprecation warning on every run. Promtail support will be removed in a future release. @@ -101,17 +101,17 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and | --------------------------------- | -------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Fresh deployment** | `true` | `false` | Alloy only. | | **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. Deprecated — emits a warning on every run. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | | **Cutover complete** | `true` | `false` | Remove Promtail from each seed: `helm uninstall promtail -n {{ gardener_logging_namespace }}`. | **To migrate an existing Promtail installation:** 1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see [Thanos Receive credentials](#thanos-receive-credentials) below. -2. Add to your inventory: `gardener_logging_promtail_enabled: true, gardener_logging_alloy_enabled: false`. The role will continue deploying Promtail and emit a deprecation warning each run. -3. When ready: set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. +2. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. +3. Set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 5. Set `gardener_logging_promtail_enabled: false` and `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release from the garden cluster and every shooted seed automatically. Remove `gardener_logging_promtail_migrate_cleanup` from your inventory afterwards. -6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). +6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). ### Thanos Receive credentials diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 35204d79a..4cc27ccb9 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -1,9 +1,9 @@ --- gardener_logging_namespace: monitoring -# Set to true to deploy Alloy. Set to false to skip Alloy deployment entirely -# (e.g. when keeping only Promtail before migrating). -# gardener_logging_alloy_enabled: true +# Alloy deployment (default false). Set true to deploy Alloy. +# Requires gardener_logging_alloy_chart_version and gardener_logging_alloy_chart_repo when enabled. +gardener_logging_alloy_enabled: false gardener_logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" @@ -46,11 +46,11 @@ gardener_logging_alloy_prometheus_wal_max_keepalive_time: "8h" # Full Alloy River config override. When set, bypasses the seed-alloy-config.alloy.j2 template. # gardener_logging_alloy_config_raw: | -# Set to true to also deploy Promtail alongside Alloy during a migration window. -# Both DaemonSets will ship logs simultaneously — Loki will receive duplicate entries. -# Requires gardener_logging_promtail_chart_version and gardener_logging_promtail_chart_repo to be set. +# Promtail deployment (default true, deprecated). Enabled by default for backward +# compatibility with existing installs. Set false once migrated to Alloy. +# Requires gardener_logging_promtail_chart_version and gardener_logging_promtail_chart_repo when enabled. # See the Migration from Promtail section in README.md. -# gardener_logging_promtail_enabled: false +gardener_logging_promtail_enabled: true gardener_logging_deploy_to_garden_cluster: true gardener_logging_shooted_seeds: [] diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 11bb91adf..7b8bdfaff 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -4,30 +4,21 @@ - name: Check mandatory variables for this role are set assert: - fail_msg: >- - gardener_logging_alloy_enabled and gardener_logging_promtail_enabled must be - explicitly set — there are no defaults, to force a conscious migration decision. - At least one must be true. - Fresh install: set gardener_logging_alloy_enabled: true, gardener_logging_promtail_enabled: false. - Existing Promtail install: set gardener_logging_promtail_enabled: true to keep current - behavior (deprecated), then migrate to Alloy at your own pace. - See README.md for the full migration guide. + fail_msg: "not all mandatory variables given, check role documentation" quiet: true that: - - gardener_logging_alloy_enabled is defined - - gardener_logging_promtail_enabled is defined - - gardener_logging_alloy_enabled | default(false) | bool or gardener_logging_promtail_enabled | default(false) | bool - - not gardener_logging_alloy_enabled | default(false) | bool or gardener_logging_alloy_chart_version is defined - - not gardener_logging_alloy_enabled | default(false) | bool or gardener_logging_alloy_chart_repo is defined - - not gardener_logging_promtail_enabled | default(false) | bool or gardener_logging_promtail_chart_version is defined - - not gardener_logging_promtail_enabled | default(false) | bool or gardener_logging_promtail_chart_repo is defined + - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_chart_version is defined + - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_chart_repo is defined + - not gardener_logging_promtail_enabled | bool or gardener_logging_promtail_chart_version is defined + - not gardener_logging_promtail_enabled | bool or gardener_logging_promtail_chart_repo is defined - name: Promtail deprecation warning - ansible.builtin.debug: + ansible.builtin.fail: msg: >- DEPRECATION: gardener_logging_promtail_enabled is true. Promtail support is deprecated and will be removed in a future release. Migrate to Alloy — see README.md. - when: gardener_logging_promtail_enabled | default(false) | bool + when: gardener_logging_promtail_enabled | bool + ignore_errors: true - name: Build Alloy config for garden cluster set_fact: diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index c0b53440b..ed2dc5c81 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -8,7 +8,7 @@ Components: - **Alloy** — log collector (DaemonSet), collects pod logs via the Kubernetes API (`loki.source.kubernetes`) and forwards them to Loki - Loki ingress with optional TLS and basic auth -This role supports deploying Alloy and/or Promtail as log collectors. Both `logging_alloy_enabled` and `logging_promtail_enabled` **must be set explicitly** — there are no defaults. See [Migration from Promtail](#migration-from-promtail) for guidance. +This role supports deploying Alloy and/or Promtail as log collectors. Promtail is deployed by default for backward compatibility. See [Migration from Promtail](#migration-from-promtail) for guidance on switching to Alloy. ## Configuration @@ -24,24 +24,24 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Default | Description | -| --------------------------------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| logging_chart_version | yes | | Helm chart version for loki (release vector) | -| logging_chart_repo | yes | | Repository for loki (release vector) | -| logging_alloy_enabled | yes | | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `logging_alloy_chart_version` and `logging_alloy_chart_repo`. | -| logging_promtail_enabled | yes | | Deploy Promtail. Set `true` to keep existing behavior (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | -| logging_alloy_chart_version | | | Helm chart version for alloy — required when `logging_alloy_enabled: true` | -| logging_alloy_chart_repo | | | Repository for alloy — required when `logging_alloy_enabled: true` | -| logging_promtail_chart_version | | | Helm chart version for promtail — required when `logging_promtail_enabled: true` | -| logging_promtail_chart_repo | | | Repository for promtail — required when `logging_promtail_enabled: true` | -| logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release. Set `true` after cutover to let the role remove the release automatically. Idempotent — safe to run even if the release is already gone. | -| logging_namespace | | | The deployment's target namespace | -| logging_loki_size | | | The size of the volume that loki will use for storing logs | -| logging_ingress_dns | | | DNS for loki ingress | -| logging_ingress_loki_tls | | | If enabled, exposes loki through HTTPS on the ingress | -| logging_ingress_loki_basic_auth_password_salt | | | The basic auth password salt used for stable password hashes | -| logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | -| logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | +| Name | Mandatory | Default | Description | +| --------------------------------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| logging_chart_version | yes | | Helm chart version for loki (release vector) | +| logging_chart_repo | yes | | Repository for loki (release vector) | +| logging_alloy_enabled | | `false` | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `logging_alloy_chart_version` and `logging_alloy_chart_repo`. | +| logging_promtail_enabled | | `true` | Deploy Promtail (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | +| logging_alloy_chart_version | | | Helm chart version for alloy — required when `logging_alloy_enabled: true` | +| logging_alloy_chart_repo | | | Repository for alloy — required when `logging_alloy_enabled: true` | +| logging_promtail_chart_version | | | Helm chart version for promtail — required when `logging_promtail_enabled: true` | +| logging_promtail_chart_repo | | | Repository for promtail — required when `logging_promtail_enabled: true` | +| logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release. Set `true` after cutover to let the role remove the release automatically. Idempotent — safe to run even if the release is already gone. | +| logging_namespace | | | The deployment's target namespace | +| logging_loki_size | | | The size of the volume that loki will use for storing logs | +| logging_ingress_dns | | | DNS for loki ingress | +| logging_ingress_loki_tls | | | If enabled, exposes loki through HTTPS on the ingress | +| logging_ingress_loki_basic_auth_password_salt | | | The basic auth password salt used for stable password hashes | +| logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | +| logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | ### Alloy @@ -102,7 +102,7 @@ Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki. ## Migration from Promtail -Alloy is the recommended log collector. Both `logging_alloy_enabled` and `logging_promtail_enabled` must be **explicitly set in your inventory** — the role will fail immediately on upgrade if they are missing, forcing a conscious migration decision. +Alloy is the recommended log collector. Promtail is deployed by default — existing installations continue to work without changes after upgrading. > **Promtail is deprecated.** Setting `logging_promtail_enabled: true` emits a deprecation warning on every run. Promtail support will be removed in a future release. @@ -116,13 +116,13 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and | --------------------------------- | ----------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Fresh deployment** | `true` | `false` | Alloy only. | | **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. Deprecated — emits a warning on every run. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | | **Cutover complete** | `true` | `false` | Remove Promtail: `helm uninstall promtail -n {{ logging_namespace }}`. | **To migrate an existing Promtail installation:** -1. Add to your inventory: `logging_promtail_enabled: true, logging_alloy_enabled: false`. The role will continue deploying Promtail and emit a deprecation warning each run. -2. When ready: set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. +1. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. +2. Set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 3. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 4. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. -5. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#event-exporter-is-now-opt-in). +5. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index c8e89218e..c3124cc2f 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -2,9 +2,9 @@ logging_namespace: monitoring logging_alloy_port: 12345 -# Set to true to deploy Alloy. Set to false to skip Alloy deployment entirely -# (e.g. when keeping only Promtail before migrating). -# logging_alloy_enabled: true +# Alloy deployment (default false). Set true to deploy Alloy. +# Requires logging_alloy_chart_version and logging_alloy_chart_repo when enabled. +logging_alloy_enabled: false # Loki push endpoints. logging_alloy_loki_write_endpoints: @@ -31,11 +31,11 @@ logging_alloy_prometheus_wal_max_keepalive_time: "8h" # Full Alloy River config override. When set, bypasses all structured vars above. # logging_alloy_config_raw: | -# Set to true to also deploy Promtail alongside Alloy during a migration window. -# Both DaemonSets will ship logs simultaneously — Loki will receive duplicate entries. -# Requires logging_promtail_chart_version and logging_promtail_chart_repo to be set. +# Promtail deployment (default true, deprecated). Enabled by default for backward +# compatibility with existing installs. Set false once migrated to Alloy. +# Requires logging_promtail_chart_version and logging_promtail_chart_repo when enabled. # See the Migration from Promtail section in README.md. -# logging_promtail_enabled: false +logging_promtail_enabled: true logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" logging_ingress_loki_tls: true diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index 5ea64ac75..3f211369c 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -4,31 +4,23 @@ - name: Check mandatory variables for this role are set assert: - fail_msg: >- - logging_alloy_enabled and logging_promtail_enabled must be explicitly set — there - are no defaults, to force a conscious migration decision. At least one must be true. - Fresh install: set logging_alloy_enabled: true, logging_promtail_enabled: false. - Existing Promtail install: set logging_promtail_enabled: true to keep current - behavior (deprecated), then migrate to Alloy at your own pace. - See README.md for the full migration guide. + fail_msg: "not all mandatory variables given, check role documentation" quiet: true that: - logging_chart_version is defined - logging_chart_repo is defined - - logging_alloy_enabled is defined - - logging_promtail_enabled is defined - - logging_alloy_enabled | default(false) | bool or logging_promtail_enabled | default(false) | bool - - not logging_alloy_enabled | default(false) | bool or logging_alloy_chart_version is defined - - not logging_alloy_enabled | default(false) | bool or logging_alloy_chart_repo is defined - - not logging_promtail_enabled | default(false) | bool or logging_promtail_chart_version is defined - - not logging_promtail_enabled | default(false) | bool or logging_promtail_chart_repo is defined + - not logging_alloy_enabled | bool or logging_alloy_chart_version is defined + - not logging_alloy_enabled | bool or logging_alloy_chart_repo is defined + - not logging_promtail_enabled | bool or logging_promtail_chart_version is defined + - not logging_promtail_enabled | bool or logging_promtail_chart_repo is defined - name: Promtail deprecation warning - ansible.builtin.debug: + ansible.builtin.fail: msg: >- DEPRECATION: logging_promtail_enabled is true. Promtail support is deprecated and will be removed in a future release. Migrate to Alloy — see README.md. - when: logging_promtail_enabled | default(false) | bool + when: logging_promtail_enabled | bool + ignore_errors: true - name: Create namespace {{ logging_namespace }} k8s: diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index a9fd1de82..aee45bfa5 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -22,7 +22,7 @@ The following variables can be set to configure the role: | ------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | rethinkdb_exporter_name | yes | rethinkdb exporter image name | | rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | -| event_exporter_enabled | | Deploy the event-exporter. Set `true` to opt in; not needed when using Alloy for log collection (events are collected natively via `loki.source.kubernetes_events`) | +| event_exporter_enabled | | Deploy the event-exporter. Enabled by default for Promtail compatibility — disable after migrating to Alloy, which collects events natively via `loki.source.kubernetes_events`. | | event_exporter_name | | event exporter image name | | event_exporter_tag | | event exporter image tag | | event_exporter_migrate_cleanup | | Remove all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Set `true` after setting `event_exporter_enabled: false` to let the role clean up the running resources automatically. Idempotent — safe to run even if the resources are already gone. | @@ -110,8 +110,8 @@ monitoring_thanos_receive_ingress_basic_auth_password: mysecret The `gardener-logging` role automatically picks up these credentials for Alloy's `prometheus.remote_write` when `monitoring_thanos_receive_ingress_enabled: true`. -### event-exporter is now opt-in +### Disabling the event-exporter after Alloy migration -The event-exporter Deployment (which wrote Kubernetes events to stdout for Promtail to scrape) is now only deployed when `event_exporter_enabled: true` is set. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. +The event-exporter Deployment (which writes Kubernetes events to stdout for Promtail to scrape) is deployed by default for backward compatibility. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. -Once you have verified that Alloy is collecting events correctly, set `event_exporter_enabled: false` (or remove it — the default is `false`) and set `event_exporter_migrate_cleanup: true`. On the next run the monitoring role will skip the Deployment and delete all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Remove `event_exporter_migrate_cleanup` from your inventory afterwards. +Once you have verified that Alloy is collecting events correctly, set `event_exporter_enabled: false` and `event_exporter_migrate_cleanup: true`. On the next run the monitoring role will skip the Deployment and delete all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Remove `event_exporter_migrate_cleanup` from your inventory afterwards. diff --git a/control-plane/roles/monitoring/defaults/main.yaml b/control-plane/roles/monitoring/defaults/main.yaml index e790dc585..82e9d4cf4 100644 --- a/control-plane/roles/monitoring/defaults/main.yaml +++ b/control-plane/roles/monitoring/defaults/main.yaml @@ -37,7 +37,7 @@ monitoring_alertmanager_webhook_logger_repeat_interval: 60m monitoring_alertmanager_webhook_slack_repeat_interval: 24h # event exporter -event_exporter_enabled: false +event_exporter_enabled: true # metal metrics exporter monitoring_metal_api_url: "http://metal-api.metal-control-plane.svc:8080/metal" From 39d0f4bc9b41e7b0fa2800cc5f46812652a972de Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Wed, 20 May 2026 13:50:38 +0200 Subject: [PATCH 12/24] fix: cutover docs --- control-plane/roles/gardener-logging/README.md | 12 ++++++------ control-plane/roles/logging/README.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index e55c9b222..57bd0f34a 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -97,12 +97,12 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and - **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to the control-plane Thanos Receive ingress, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_ingress_enabled: true`. - **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. -| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | -| --------------------------------- | -------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Fresh deployment** | `true` | `false` | Alloy only. | -| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | -| **Cutover complete** | `true` | `false` | Remove Promtail from each seed: `helm uninstall promtail -n {{ gardener_logging_namespace }}`. | +| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | +| --------------------------------- | -------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Fresh deployment** | `true` | `false` | Alloy only. | +| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | +| **Cutover complete** | `true` | `false` | Set `gardener_logging_promtail_migrate_cleanup: true`, `event_exporter_enabled: false`, and `event_exporter_migrate_cleanup: true` (in monitoring config) and re-run. Remove the cleanup variables afterwards. | **To migrate an existing Promtail installation:** diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index ed2dc5c81..9b41904ad 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -112,12 +112,12 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and - **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to in-cluster Thanos Receive, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_enabled: true`. - **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. -| Scenario | `logging_alloy_enabled` | `logging_promtail_enabled` | Notes | -| --------------------------------- | ----------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Fresh deployment** | `true` | `false` | Alloy only. | -| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | -| **Cutover complete** | `true` | `false` | Remove Promtail: `helm uninstall promtail -n {{ logging_namespace }}`. | +| Scenario | `logging_alloy_enabled` | `logging_promtail_enabled` | Notes | +| --------------------------------- | ----------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Fresh deployment** | `true` | `false` | Alloy only. | +| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | +| **Cutover complete** | `true` | `false` | Set `logging_promtail_migrate_cleanup: true`, `event_exporter_enabled: false`, and `event_exporter_migrate_cleanup: true` (in monitoring config) and re-run. Remove the cleanup variables afterwards. | **To migrate an existing Promtail installation:** From 6b3fe91f074a6420ed3be7020022f4b39ccbb7f8 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Tue, 2 Jun 2026 10:26:41 +0200 Subject: [PATCH 13/24] docs: credential rename --- control-plane/roles/gardener-logging/README.md | 2 ++ control-plane/roles/gardener-logging/defaults/main.yaml | 2 ++ control-plane/roles/logging/README.md | 2 ++ control-plane/roles/logging/defaults/main.yaml | 4 +++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 57bd0f34a..f0e486eb2 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -112,6 +112,8 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 5. Set `gardener_logging_promtail_enabled: false` and `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release from the garden cluster and every shooted seed automatically. Remove `gardener_logging_promtail_migrate_cleanup` from your inventory afterwards. 6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). +7. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. +8. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the logging role with updated variables replaces the secret automatically. If those credentials are also configured in `gardener_logging_ingress_loki_basic_auth_user` / `gardener_logging_ingress_loki_basic_auth_password` for Alloy/Promtail on the shooted seeds, update all of them in the same deployment to avoid auth failures. ### Thanos Receive credentials diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 4cc27ccb9..04f2f2335 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -8,6 +8,8 @@ gardener_logging_alloy_enabled: false gardener_logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" gardener_logging_garden_name: "{{ gardener_defaults_garden_name }}" +# is not renamed to alloy for backward compatibility with existing installs. +# if updated, the shooted seed`s alloy/promtail need their loki endpoints updated to match. gardener_logging_ingress_loki_basic_auth_user: promtail gardener_logging_ingress_loki_basic_auth_password: diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index 9b41904ad..dd5766631 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -126,3 +126,5 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 3. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 4. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. 5. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). +6. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. +7. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the role with updated variables replaces the secret automatically. If those credentials are also used by partition Promtail or Alloy to authenticate the `remote_write` to Loki, update both in the same deployment to avoid auth failures. diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index c3124cc2f..d2194d7b1 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -39,7 +39,9 @@ logging_promtail_enabled: true logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" logging_ingress_loki_tls: true -logging_ingress_loki_basic_auth_user: promtail # TODO rename to alloy or generic +# is not renamed to alloy for backward compatibility with existing installs. +# if updated, partition alloy/promtail need their loki endpoints updated to match. +logging_ingress_loki_basic_auth_user: promtail logging_ingress_loki_basic_auth_password: logging_ingress_loki_basic_auth_password_salt: "{{ logging_ingress_loki_basic_auth_password | hash('sha1') | truncate(22, True, '') }}" logging_ingress_annotations: {} From 634811d439a86d1d245cedfce6f0775adecc4860 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Tue, 2 Jun 2026 11:17:07 +0200 Subject: [PATCH 14/24] chore: ansible lint --- control-plane/roles/monitoring/tasks/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/roles/monitoring/tasks/main.yaml b/control-plane/roles/monitoring/tasks/main.yaml index a9441fdcc..0e7361689 100644 --- a/control-plane/roles/monitoring/tasks/main.yaml +++ b/control-plane/roles/monitoring/tasks/main.yaml @@ -19,7 +19,7 @@ Please migrate to 'monitoring_thanos_receive_ingress_basic_auth_user' and 'monitoring_thanos_receive_ingress_basic_auth_password' — the htpasswd entry is now generated automatically. See the Migration section in the role README. - quiet: yes + quiet: true that: - monitoring_thanos_receive_ingress_basic_auth is not defined or monitoring_thanos_receive_ingress_basic_auth == {} From be794f1e3bd9033d27ecbb2ca9364bfa67ce8c4a Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Wed, 3 Jun 2026 16:40:16 +0200 Subject: [PATCH 15/24] fix: remove loki and prometheus write endpoint defaults --- .../roles/gardener-logging/README.md | 37 +++++----------- .../roles/gardener-logging/defaults/main.yaml | 26 ++++++------ .../roles/gardener-logging/tasks/main.yaml | 3 +- control-plane/roles/logging/README.md | 42 ++++++++++--------- .../roles/logging/defaults/main.yaml | 18 +++++--- control-plane/roles/logging/tasks/main.yaml | 3 +- control-plane/roles/monitoring/README.md | 4 +- 7 files changed, 63 insertions(+), 70 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index f0e486eb2..e63101e38 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -36,15 +36,15 @@ The following variables can be set to configure the role: ### Alloy -| Name | Mandatory | Default | Description | -| -------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| gardener_logging_alloy_port | | `12345` | Alloy listen port | -| gardener_logging_alloy_loki_write_endpoints | | `https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push` with basic auth | List of Loki push endpoints. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_cluster_label | | `gardener_logging_shooted_seed.name` | Value for the `cluster=` label set on all log and metric streams via relabel rules | -| gardener_logging_alloy_prometheus_write_endpoints | | Thanos receive ingress (`{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive`) | List of Prometheus remote_write endpoints for Alloy self-metrics. Requires `monitoring_thanos_receive_ingress_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | -| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | -| gardener_logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Default | Description | +| -------------------------------------------------------- | --------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| gardener_logging_alloy_port | | `12345` | Alloy listen port | +| gardener_logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_cluster_label | | `gardener_logging_shooted_seed.name` | Value for the `cluster=` label set on all log and metric streams via relabel rules | +| gardener_logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | +| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | +| gardener_logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -79,7 +79,7 @@ Alloy watches events in all namespaces, which requires cluster-scope RBAC. The A ### Metrics -Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Seed clusters have no local Prometheus, so metrics are pushed to the control-plane Thanos Receive ingress. The endpoint and credentials are wired automatically when `monitoring_thanos_receive_ingress_enabled: true` — credentials are taken from `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password` in the monitoring role. Override `gardener_logging_alloy_prometheus_write_endpoints` only if you need custom credentials or a different URL. +Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Seed clusters have no local Prometheus. Self-metrics are disabled by default — set `gardener_logging_alloy_prometheus_write_endpoints` to push them to a remote endpoint such as the control-plane Thanos Receive ingress. Credentials can be taken from `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password` (monitoring role). ### Logs @@ -106,7 +106,7 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and **To migrate an existing Promtail installation:** -1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see [Thanos Receive credentials](#thanos-receive-credentials) below. +1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see the [monitoring role migration guide](../monitoring/README.md#thanos-receive-ingress-credentials). 2. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. 3. Set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. @@ -114,18 +114,3 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). 7. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. 8. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the logging role with updated variables replaces the secret automatically. If those credentials are also configured in `gardener_logging_ingress_loki_basic_auth_user` / `gardener_logging_ingress_loki_basic_auth_password` for Alloy/Promtail on the shooted seeds, update all of them in the same deployment to avoid auth failures. - -### Thanos Receive credentials - -If you push Alloy self-metrics to Thanos Receive (`monitoring_thanos_receive_ingress_enabled: true`), the monitoring role's basic auth configuration has changed. The old raw htpasswd string `monitoring_thanos_receive_ingress_basic_auth` has been replaced by two plaintext variables: - -```yaml -# Before -monitoring_thanos_receive_ingress_basic_auth: "myuser:$apr1$..." - -# After -monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-receive -monitoring_thanos_receive_ingress_basic_auth_password: mysecret -``` - -The htpasswd entry is now generated automatically. The monitoring role will fail immediately if the old variable is still set — see the [monitoring role migration guide](../monitoring/README.md#migration) for details. diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 04f2f2335..c3f016b19 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -14,11 +14,12 @@ gardener_logging_ingress_loki_basic_auth_user: promtail gardener_logging_ingress_loki_basic_auth_password: # Loki push endpoints. -gardener_logging_alloy_loki_write_endpoints: - - url: "https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push" - basic_auth: - username: "{{ gardener_logging_ingress_loki_basic_auth_user }}" - password: "{{ gardener_logging_ingress_loki_basic_auth_password }}" +# gardener_logging_alloy_loki_write_endpoints: + # - url: "https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push" + # remote_timeout: 60s + # basic_auth: + # username: "{{ gardener_logging_ingress_loki_basic_auth_user }}" + # password: "{{ gardener_logging_ingress_loki_basic_auth_password }}" # Value for the cluster= label attached to all log and metric streams via relabel rules gardener_logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" @@ -27,16 +28,13 @@ gardener_logging_alloy_port: 12345 # Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). # Alloy scrapes its own /metrics and pushes them via remote_write. -# Seed clusters have no local Prometheus, so metrics are pushed to the control-plane Thanos Receive. # Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -# Endpoint is included automatically when monitoring_thanos_receive_ingress_enabled: true. -# Credentials come from monitoring_thanos_receive_ingress_basic_auth_user/password (monitoring role). -gardener_logging_alloy_prometheus_write_endpoints: >- - {{ [{'url': 'https://' ~ monitoring_thanos_receive_ingress_dns ~ '/api/v1/receive'} | - combine({'basic_auth': {'username': monitoring_thanos_receive_ingress_basic_auth_user, - 'password': monitoring_thanos_receive_ingress_basic_auth_password}} - if monitoring_thanos_receive_ingress_basic_auth_password else {})] - if monitoring_thanos_receive_ingress_enabled | default(false) | bool else [] }} +# gardener_logging_alloy_prometheus_write_endpoints: +# - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" +# remote_timeout: 60s +# basic_auth: +# username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" +# password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" # WAL retention for prometheus.remote_write. Controls how long undelivered samples # are buffered on disk before being dropped during a remote endpoint outage. diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 7b8bdfaff..86b0d3249 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -11,12 +11,13 @@ - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_chart_repo is defined - not gardener_logging_promtail_enabled | bool or gardener_logging_promtail_chart_version is defined - not gardener_logging_promtail_enabled | bool or gardener_logging_promtail_chart_repo is defined + - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_loki_write_endpoints is defined or gardener_logging_alloy_config_raw is defined - name: Promtail deprecation warning ansible.builtin.fail: msg: >- DEPRECATION: gardener_logging_promtail_enabled is true. Promtail support is deprecated - and will be removed in a future release. Migrate to Alloy — see README.md. + and will be removed in a future release. Migrate to Alloy — see https://github.com/metal-stack/metal-roles/tree/main/control-plane/roles/gardener-logging#migration-from-promtail for instructions. when: gardener_logging_promtail_enabled | bool ignore_errors: true diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index dd5766631..d250aa7dd 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -45,15 +45,15 @@ The following variables can be set to configure the role: ### Alloy -| Name | Mandatory | Default | Description | -| ----------------------------------------------- | --------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| logging_alloy_port | | `12345` | Alloy listen port | -| logging_alloy_loki_write_endpoints | | `[{url: "http://loki:3100/loki/api/v1/push"}]` | List of Loki push endpoints (in-cluster Loki). Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_cluster_label | | `{{ metal_control_plane_stage_name }}` | Value for the `cluster=` label set on all log and metric streams via relabel rules | -| logging_alloy_prometheus_write_endpoints | | auto (Thanos Receive when `monitoring_thanos_receive_enabled: true`, else `[]`) | List of Prometheus remote_write endpoints for Alloy self-metrics. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | -| logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | -| logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Default | Description | +| ----------------------------------------------- | --------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| logging_alloy_port | | `12345` | Alloy listen port | +| logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_cluster_label | | `{{ metal_control_plane_stage_name }}` | Value for the `cluster=` label set on all log and metric streams via relabel rules | +| logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | +| logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | +| logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -88,12 +88,15 @@ Alloy watches events in all namespaces, which requires cluster-scope RBAC. The A ### Metrics -Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. When `monitoring_thanos_receive_enabled: true` in the monitoring role, Alloy automatically pushes metrics to the in-cluster Thanos Receive. Otherwise the list is empty and self-metrics are disabled. Override to customise: +Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. Self-metrics are disabled by default — set `logging_alloy_prometheus_write_endpoints` to enable them. Example for in-cluster Thanos Receive: ```yaml logging_alloy_prometheus_write_endpoints: - # automatically included when monitoring_thanos_receive_enabled: true - - url: "http://thanos-receive.{{ logging_namespace }}.svc.cluster.local:19291/api/v1/receive" + - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" + timeout: 60s + basic_auth: + username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" + password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" ``` ### Logs @@ -121,10 +124,11 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and **To migrate an existing Promtail installation:** -1. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. -2. Set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. -3. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. -4. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. -5. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). -6. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. -7. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the role with updated variables replaces the secret automatically. If those credentials are also used by partition Promtail or Alloy to authenticate the `remote_write` to Loki, update both in the same deployment to avoid auth failures. +1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see the [monitoring role migration guide](../monitoring/README.md#thanos-receive-ingress-credentials). +2. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. +3. Set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. +4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. +5. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. +6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). +7. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. +8. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the role with updated variables replaces the secret automatically. If those credentials are also used by partition Promtail or Alloy to authenticate the `remote_write` to Loki, update both in the same deployment to avoid auth failures. diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index d2194d7b1..7f46b7802 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -7,8 +7,12 @@ logging_alloy_port: 12345 logging_alloy_enabled: false # Loki push endpoints. -logging_alloy_loki_write_endpoints: - - url: "http://loki:3100/loki/api/v1/push" +# logging_alloy_loki_write_endpoints: + # - url: "https://{{ logging_ingress_dns }}/loki/api/v1/push" + # remote_timeout: 60s + # basic_auth: + # username: "{{ logging_ingress_loki_basic_auth_user }}" + # password: "{{ logging_ingress_loki_basic_auth_password }}" # Value for the cluster= label attached to all log and metric streams via relabel rules logging_alloy_cluster_label: "{{ metal_control_plane_stage_name }}" @@ -16,10 +20,12 @@ logging_alloy_cluster_label: "{{ metal_control_plane_stage_name }}" # Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). # Alloy scrapes its own /metrics and pushes them via remote_write. # Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -# Thanos Receive endpoint is included automatically when monitoring_thanos_receive_enabled: true. -logging_alloy_prometheus_write_endpoints: >- - {{ [{'url': 'http://thanos-receive.' ~ logging_namespace ~ '.svc.cluster.local:19291/api/v1/receive'}] - if monitoring_thanos_receive_enabled | default(false) | bool else [] }} +# logging_alloy_prometheus_write_endpoints: +# - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" +# remote_timeout: 60s +# basic_auth: +# username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" +# password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" # WAL retention for prometheus.remote_write. Controls how long undelivered samples # are buffered on disk before being dropped during a remote endpoint outage. diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index 3f211369c..384541ac5 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -13,12 +13,13 @@ - not logging_alloy_enabled | bool or logging_alloy_chart_repo is defined - not logging_promtail_enabled | bool or logging_promtail_chart_version is defined - not logging_promtail_enabled | bool or logging_promtail_chart_repo is defined + - not logging_alloy_enabled | bool or logging_alloy_loki_write_endpoints is defined or logging_alloy_config_raw is defined - name: Promtail deprecation warning ansible.builtin.fail: msg: >- DEPRECATION: logging_promtail_enabled is true. Promtail support is deprecated - and will be removed in a future release. Migrate to Alloy — see README.md. + and will be removed in a future release. Migrate to Alloy — see https://github.com/metal-stack/metal-roles/tree/main/control-plane/roles/logging#migration-from-promtail for instructions. when: logging_promtail_enabled | bool ignore_errors: true diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index aee45bfa5..0fee4f917 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -89,7 +89,7 @@ The following variables can be set to configure the role: ## Migration -### `monitoring_thanos_receive_ingress_basic_auth` removed +### Thanos Receive ingress credentials The old `monitoring_thanos_receive_ingress_basic_auth` variable (a raw htpasswd-format string) has been replaced by `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password`. The htpasswd entry for nginx is now generated automatically from these, following the same pattern as Alertmanager. @@ -108,8 +108,6 @@ monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-rece monitoring_thanos_receive_ingress_basic_auth_password: mysecret ``` -The `gardener-logging` role automatically picks up these credentials for Alloy's `prometheus.remote_write` when `monitoring_thanos_receive_ingress_enabled: true`. - ### Disabling the event-exporter after Alloy migration The event-exporter Deployment (which writes Kubernetes events to stdout for Promtail to scrape) is deployed by default for backward compatibility. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. From f4b4a80bd1cbb857540858939d1ccb9f1c8c85c9 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 8 Jun 2026 15:12:10 +0200 Subject: [PATCH 16/24] feat: flag for alloy helm chart service monitors --- control-plane/roles/gardener-logging/README.md | 18 +++++++++++++++++- control-plane/roles/logging/README.md | 16 +++++++++++++--- control-plane/roles/logging/defaults/main.yaml | 1 + .../roles/logging/templates/alloy-values.yaml | 3 +++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index e63101e38..65d72c5a5 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -79,7 +79,23 @@ Alloy watches events in all namespaces, which requires cluster-scope RBAC. The A ### Metrics -Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Seed clusters have no local Prometheus. Self-metrics are disabled by default — set `gardener_logging_alloy_prometheus_write_endpoints` to push them to a remote endpoint such as the control-plane Thanos Receive ingress. Credentials can be taken from `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password` (monitoring role). +Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Self-metrics are disabled by default. + +Alloy can scrape itself and push metrics to a remote_write endpoint. This works regardless of the Prometheus setup on the seed. The target just needs to support remote_write. +Set `gardener_logging_alloy_prometheus_write_endpoints` to push to a remote_write endpoint. + +Example for Thanos Receive ingress: + +```yaml +gardener_logging_alloy_prometheus_write_endpoints: + - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" + remote_timeout: 60s + basic_auth: + username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" + password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" +``` + +Gardener's Prometheus instances use annotation-based pod discovery restricted to extension namespaces (Seed Prometheus) or scrape their own known targets (Aggregate Prometheus). Neither reliably reaches the `monitoring` namespace where Alloy runs, so pull-based collection is not a supported option. ### Logs diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index d250aa7dd..bad4b345f 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -51,6 +51,7 @@ The following variables can be set to configure the role: | logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | | logging_alloy_cluster_label | | `{{ metal_control_plane_stage_name }}` | Value for the `cluster=` label set on all log and metric streams via relabel rules | | logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| logging_alloy_service_monitor_enabled | | `false` | Enable a Prometheus ServiceMonitor for Alloy self-metrics (pull model). Requires an in-cluster Prometheus. Mutually exclusive with `logging_alloy_prometheus_write_endpoints`. | | logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | | logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | | logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | @@ -88,17 +89,26 @@ Alloy watches events in all namespaces, which requires cluster-scope RBAC. The A ### Metrics -Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. Self-metrics are disabled by default — set `logging_alloy_prometheus_write_endpoints` to enable them. Example for in-cluster Thanos Receive: +Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. Self-metrics are disabled by default. Two collection approaches are supported: + +**Option A — Push (remote_write).** Alloy scrapes itself and pushes metrics to a remote_write endpoint. This is the primary option for clusters, which have no local Prometheus. +Set `logging_alloy_prometheus_write_endpoints` to push to the control-plane Thanos Receive ingress, for example: ```yaml logging_alloy_prometheus_write_endpoints: - - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" - timeout: 60s + url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" + remote_timeout: 60s basic_auth: username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" ``` +**Option B — Pull (ServiceMonitor).** Set `logging_alloy_service_monitor_enabled: true`. +This is an option if you have an in-cluster Prometheus that can scrape Alloy directly — for example, if you also deploy the monitoring role on the control-plane cluster. +The role then enables the Alloy ServiceMonitor, and the in-cluster Prometheus deployed by the [monitoring role](../monitoring/) will scrape the `/metrics` endpoint automatically. + +No `logging_alloy_prometheus_write_endpoints` is needed in this case. + ### Logs Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki.source.kubernetes` automatically — no additional configuration is needed. diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index 7f46b7802..c5a82b266 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -1,6 +1,7 @@ --- logging_namespace: monitoring logging_alloy_port: 12345 +logging_alloy_service_monitor_enabled: false # Alloy deployment (default false). Set true to deploy Alloy. # Requires logging_alloy_chart_version and logging_alloy_chart_repo when enabled. diff --git a/control-plane/roles/logging/templates/alloy-values.yaml b/control-plane/roles/logging/templates/alloy-values.yaml index 662b2ce97..812719eb9 100644 --- a/control-plane/roles/logging/templates/alloy-values.yaml +++ b/control-plane/roles/logging/templates/alloy-values.yaml @@ -23,6 +23,9 @@ alloy: # Alloy. enableReporting: false + serviceMonitor: + enabled: {{ logging_alloy_service_monitor_enabled | bool | lower }} + controller: volumes: extra: From b9c00926a76811fc508791486dbb6cf01987dba1 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 8 Jun 2026 15:36:57 +0200 Subject: [PATCH 17/24] chore: undo control-plane logging changes --- common/roles/defaults/defaults/main.yaml | 2 - control-plane/roles/logging/README.md | 151 ++-------------- .../roles/logging/defaults/main.yaml | 46 ----- control-plane/roles/logging/tasks/main.yaml | 42 +---- .../logging/templates/alloy-config.alloy.j2 | 168 ------------------ .../roles/logging/templates/alloy-values.yaml | 44 ----- control-plane/roles/monitoring/README.md | 142 ++++++--------- .../roles/monitoring/defaults/main.yaml | 7 +- .../roles/monitoring/tasks/exporters.yaml | 30 ---- .../roles/monitoring/tasks/main.yaml | 13 +- .../monitoring/templates/thanos-values.yaml | 10 +- 11 files changed, 86 insertions(+), 569 deletions(-) delete mode 100644 control-plane/roles/logging/templates/alloy-config.alloy.j2 delete mode 100644 control-plane/roles/logging/templates/alloy-values.yaml diff --git a/common/roles/defaults/defaults/main.yaml b/common/roles/defaults/defaults/main.yaml index f5c70fdb6..35a411551 100644 --- a/common/roles/defaults/defaults/main.yaml +++ b/common/roles/defaults/defaults/main.yaml @@ -163,8 +163,6 @@ metal_stack_release: metal_helm_chart_tag: "helm-charts.metal-stack.metal-control-plane.tag" logging_chart_version: "helm-charts.logging.loki.version" logging_chart_repo: "helm-charts.logging.loki.repository" - logging_alloy_chart_version: "helm-charts.logging.alloy.version" - logging_alloy_chart_repo: "helm-charts.logging.alloy.repository" gardener_logging_alloy_chart_version: "helm-charts.logging.alloy.version" gardener_logging_alloy_chart_repo: "helm-charts.logging.alloy.repository" logging_promtail_chart_version: "helm-charts.logging.promtail.version" diff --git a/control-plane/roles/logging/README.md b/control-plane/roles/logging/README.md index bad4b345f..b3b36c57f 100644 --- a/control-plane/roles/logging/README.md +++ b/control-plane/roles/logging/README.md @@ -1,144 +1,31 @@ # logging -Deploys the control-plane logging stack into the Kubernetes control-plane cluster. +This role is designed to set up logging using Ansible. +The role includes tasks to install and configure the following logging tools: -Components: - -- **Loki** — log storage and query backend -- **Alloy** — log collector (DaemonSet), collects pod logs via the Kubernetes API (`loki.source.kubernetes`) and forwards them to Loki -- Loki ingress with optional TLS and basic auth - -This role supports deploying Alloy and/or Promtail as log collectors. Promtail is deployed by default for backward compatibility. See [Migration from Promtail](#migration-from-promtail) for guidance on switching to Alloy. - -## Configuration - -The Alloy River config is generated from structured variables at deploy time. Override individual variables to customize behavior, or bypass the template entirely with `logging_alloy_config_raw`. +- Loki +- Logging ingress for Loki +- Promtail for monitoring the control plane cluster ## Variables -This role uses variables from [control-plane-defaults](/control-plane). Make sure you define them adequately as well. +This role uses variables from [control-plane-defaults](/control-plane). So, make sure you define them adequately as well. You can look up all the default values of this role [here](defaults/main.yaml). - The following variables can be set to configure the role: ### General -| Name | Mandatory | Default | Description | -| --------------------------------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| logging_chart_version | yes | | Helm chart version for loki (release vector) | -| logging_chart_repo | yes | | Repository for loki (release vector) | -| logging_alloy_enabled | | `false` | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `logging_alloy_chart_version` and `logging_alloy_chart_repo`. | -| logging_promtail_enabled | | `true` | Deploy Promtail (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | -| logging_alloy_chart_version | | | Helm chart version for alloy — required when `logging_alloy_enabled: true` | -| logging_alloy_chart_repo | | | Repository for alloy — required when `logging_alloy_enabled: true` | -| logging_promtail_chart_version | | | Helm chart version for promtail — required when `logging_promtail_enabled: true` | -| logging_promtail_chart_repo | | | Repository for promtail — required when `logging_promtail_enabled: true` | -| logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release. Set `true` after cutover to let the role remove the release automatically. Idempotent — safe to run even if the release is already gone. | -| logging_namespace | | | The deployment's target namespace | -| logging_loki_size | | | The size of the volume that loki will use for storing logs | -| logging_ingress_dns | | | DNS for loki ingress | -| logging_ingress_loki_tls | | | If enabled, exposes loki through HTTPS on the ingress | -| logging_ingress_loki_basic_auth_password_salt | | | The basic auth password salt used for stable password hashes | -| logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | -| logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | - -### Alloy - -| Name | Mandatory | Default | Description | -| ----------------------------------------------- | --------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| logging_alloy_port | | `12345` | Alloy listen port | -| logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_cluster_label | | `{{ metal_control_plane_stage_name }}` | Value for the `cluster=` label set on all log and metric streams via relabel rules | -| logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| logging_alloy_service_monitor_enabled | | `false` | Enable a Prometheus ServiceMonitor for Alloy self-metrics (pull model). Requires an in-cluster Prometheus. Mutually exclusive with `logging_alloy_prometheus_write_endpoints`. | -| logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | -| logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | -| logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | - -Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). - -## Labels - -### Pod logs (`loki.source.kubernetes`) - -| Label | Source | -| ----------- | --------------------------------------------------------------------------------------------------------------- | -| `cluster` | `logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | -| `namespace` | `__meta_kubernetes_namespace` | -| `pod` | `__meta_kubernetes_pod_name` | -| `container` | `__meta_kubernetes_pod_container_name` | -| `pod_uid` | `__meta_kubernetes_pod_uid` | -| `node_name` | `__meta_kubernetes_pod_node_name` | -| `app` | `app.kubernetes.io/name` pod label, falling back to `app` label, controller name (hash stripped), then pod name | -| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | -| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | -| `job` | `namespace/app` (using the computed `app` value above) | - -### Kubernetes events (`loki.source.kubernetes_events`) - -| Label | Value | -| ----------- | ------------------------------------------------------------------- | -| `cluster` | `logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | -| `job` | `monitoring/event-exporter` (relabelled for Promtail compatibility) | -| `namespace` | Namespace of the event | - -Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. - -## Meta-monitoring - -### Metrics - -Alloy exposes Prometheus metrics on port `{{ logging_alloy_port }}/metrics`. Self-metrics are disabled by default. Two collection approaches are supported: - -**Option A — Push (remote_write).** Alloy scrapes itself and pushes metrics to a remote_write endpoint. This is the primary option for clusters, which have no local Prometheus. -Set `logging_alloy_prometheus_write_endpoints` to push to the control-plane Thanos Receive ingress, for example: - -```yaml -logging_alloy_prometheus_write_endpoints: - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" - remote_timeout: 60s - basic_auth: - username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" - password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" -``` - -**Option B — Pull (ServiceMonitor).** Set `logging_alloy_service_monitor_enabled: true`. -This is an option if you have an in-cluster Prometheus that can scrape Alloy directly — for example, if you also deploy the monitoring role on the control-plane cluster. -The role then enables the Alloy ServiceMonitor, and the in-cluster Prometheus deployed by the [monitoring role](../monitoring/) will scrape the `/metrics` endpoint automatically. - -No `logging_alloy_prometheus_write_endpoints` is needed in this case. - -### Logs - -Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki.source.kubernetes` automatically — no additional configuration is needed. - -## Migration from Promtail - -Alloy is the recommended log collector. Promtail is deployed by default — existing installations continue to work without changes after upgrading. - -> **Promtail is deprecated.** Setting `logging_promtail_enabled: true` emits a deprecation warning on every run. Promtail support will be removed in a future release. - -Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and LogQL queries continue to work without changes. What has changed compared to Promtail: - -- **Kubernetes events are now built-in.** Promtail required a separate event-exporter Deployment. Alloy collects events natively via `loki.source.kubernetes_events` and labels them `job="monitoring/event-exporter"` for full backward compatibility. -- **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to in-cluster Thanos Receive, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_enabled: true`. -- **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. - -| Scenario | `logging_alloy_enabled` | `logging_promtail_enabled` | Notes | -| --------------------------------- | ----------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Fresh deployment** | `true` | `false` | Alloy only. | -| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `logging_promtail_chart_version` and `logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | -| **Cutover complete** | `true` | `false` | Set `logging_promtail_migrate_cleanup: true`, `event_exporter_enabled: false`, and `event_exporter_migrate_cleanup: true` (in monitoring config) and re-run. Remove the cleanup variables afterwards. | - -**To migrate an existing Promtail installation:** - -1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see the [monitoring role migration guide](../monitoring/README.md#thanos-receive-ingress-credentials). -2. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. -3. Set `logging_alloy_enabled: true` and add `logging_alloy_chart_version` and `logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. -4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. -5. Set `logging_promtail_enabled: false` and `logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release automatically. Remove `logging_promtail_migrate_cleanup` from your inventory afterwards. -6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). -7. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. -8. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the role with updated variables replaces the secret automatically. If those credentials are also used by partition Promtail or Alloy to authenticate the `remote_write` to Loki, update both in the same deployment to avoid auth failures. +| Name | Mandatory | Description | +| --------------------------------------------- | --------- | -------------------------------------------------------------- | +| logging_chart_version | yes | Helm chart version for loki specified under release vector | +| logging_chart_repo | yes | Repository for loki specified under release vector | +| logging_promtail_chart_version | yes | Helm chart version for promtail specified under release vector | +| logging_promtail_chart_repo | yes | Repository for promtail specified under release vector | +| logging_namespace | | The deployment's target namespace | +| logging_loki_size | | The size of the volume that loki will use for storing logs | +| logging_ingress_dns | | DNS for loki ingress | +| logging_ingress_loki_tls | | If enabled, exposes loki through HTTPS on the ingress | +| logging_ingress_loki_basic_auth_password_salt | | The basic auth password salt used for stable password hashes | +| logging_ingress_loki_basic_auth_password | | The basic auth password for the external loki ingress | +| logging_ingress_loki_basic_auth_user | | The basic auth user for the external loki ingress | diff --git a/control-plane/roles/logging/defaults/main.yaml b/control-plane/roles/logging/defaults/main.yaml index c5a82b266..114d4eaab 100644 --- a/control-plane/roles/logging/defaults/main.yaml +++ b/control-plane/roles/logging/defaults/main.yaml @@ -1,53 +1,7 @@ --- logging_namespace: monitoring -logging_alloy_port: 12345 -logging_alloy_service_monitor_enabled: false - -# Alloy deployment (default false). Set true to deploy Alloy. -# Requires logging_alloy_chart_version and logging_alloy_chart_repo when enabled. -logging_alloy_enabled: false - -# Loki push endpoints. -# logging_alloy_loki_write_endpoints: - # - url: "https://{{ logging_ingress_dns }}/loki/api/v1/push" - # remote_timeout: 60s - # basic_auth: - # username: "{{ logging_ingress_loki_basic_auth_user }}" - # password: "{{ logging_ingress_loki_basic_auth_password }}" - -# Value for the cluster= label attached to all log and metric streams via relabel rules -logging_alloy_cluster_label: "{{ metal_control_plane_stage_name }}" - -# Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). -# Alloy scrapes its own /metrics and pushes them via remote_write. -# Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -# logging_alloy_prometheus_write_endpoints: -# - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" -# remote_timeout: 60s -# basic_auth: -# username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" -# password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" - -# WAL retention for prometheus.remote_write. Controls how long undelivered samples -# are buffered on disk before being dropped during a remote endpoint outage. -# truncate_frequency: how often the WAL is compacted (default: Alloy built-in 2h) -# max_keepalive_time: samples older than this are dropped even if not yet delivered (default: Alloy built-in 8h) -logging_alloy_prometheus_wal_truncate_frequency: "2h" -logging_alloy_prometheus_wal_max_keepalive_time: "8h" - -# Full Alloy River config override. When set, bypasses all structured vars above. -# logging_alloy_config_raw: | - -# Promtail deployment (default true, deprecated). Enabled by default for backward -# compatibility with existing installs. Set false once migrated to Alloy. -# Requires logging_promtail_chart_version and logging_promtail_chart_repo when enabled. -# See the Migration from Promtail section in README.md. -logging_promtail_enabled: true - logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" logging_ingress_loki_tls: true -# is not renamed to alloy for backward compatibility with existing installs. -# if updated, partition alloy/promtail need their loki endpoints updated to match. logging_ingress_loki_basic_auth_user: promtail logging_ingress_loki_basic_auth_password: logging_ingress_loki_basic_auth_password_salt: "{{ logging_ingress_loki_basic_auth_password | hash('sha1') | truncate(22, True, '') }}" diff --git a/control-plane/roles/logging/tasks/main.yaml b/control-plane/roles/logging/tasks/main.yaml index 384541ac5..e761869a0 100644 --- a/control-plane/roles/logging/tasks/main.yaml +++ b/control-plane/roles/logging/tasks/main.yaml @@ -9,19 +9,8 @@ that: - logging_chart_version is defined - logging_chart_repo is defined - - not logging_alloy_enabled | bool or logging_alloy_chart_version is defined - - not logging_alloy_enabled | bool or logging_alloy_chart_repo is defined - - not logging_promtail_enabled | bool or logging_promtail_chart_version is defined - - not logging_promtail_enabled | bool or logging_promtail_chart_repo is defined - - not logging_alloy_enabled | bool or logging_alloy_loki_write_endpoints is defined or logging_alloy_config_raw is defined - -- name: Promtail deprecation warning - ansible.builtin.fail: - msg: >- - DEPRECATION: logging_promtail_enabled is true. Promtail support is deprecated - and will be removed in a future release. Migrate to Alloy — see https://github.com/metal-stack/metal-roles/tree/main/control-plane/roles/logging#migration-from-promtail for instructions. - when: logging_promtail_enabled | bool - ignore_errors: true + - logging_promtail_chart_version is defined + - logging_promtail_chart_repo is defined - name: Create namespace {{ logging_namespace }} k8s: @@ -44,24 +33,7 @@ helm_chart_version: "{{ logging_chart_version }}" helm_value_file_template: "loki-values.yaml" -- name: Build Alloy config - set_fact: - logging_alloy_config: "{{ lookup('template', 'alloy-config.alloy.j2') if (logging_alloy_config_raw | default('') | length == 0) else logging_alloy_config_raw }}" - when: logging_alloy_enabled - -- name: Deploy Alloy - include_role: - name: ansible-common/roles/helm-chart - vars: - helm_repo: "{{ logging_alloy_chart_repo }}" - helm_chart: "alloy" - helm_release_name: alloy - helm_target_namespace: "{{ logging_namespace }}" - helm_chart_version: "{{ logging_alloy_chart_version }}" - helm_value_file_template: "alloy-values.yaml" - when: logging_alloy_enabled - -- name: Deploy Promtail (parallel migration window) +- name: Deploy Promtail include_role: name: ansible-common/roles/helm-chart vars: @@ -71,11 +43,3 @@ helm_target_namespace: "{{ logging_namespace }}" helm_chart_version: "{{ logging_promtail_chart_version }}" helm_value_file_template: "promtail-values.yaml" - when: logging_promtail_enabled - -- name: Remove Promtail - kubernetes.core.helm: - name: promtail - namespace: "{{ logging_namespace }}" - state: absent - when: logging_promtail_migrate_cleanup | default(false) | bool diff --git a/control-plane/roles/logging/templates/alloy-config.alloy.j2 b/control-plane/roles/logging/templates/alloy-config.alloy.j2 deleted file mode 100644 index 07e4d33db..000000000 --- a/control-plane/roles/logging/templates/alloy-config.alloy.j2 +++ /dev/null @@ -1,168 +0,0 @@ -logging { - level = "info" - format = "logfmt" -} - -discovery.kubernetes "pods" { - role = "pod" -} - -discovery.relabel "pods" { - targets = discovery.kubernetes.pods.targets - - rule { - source_labels = ["__meta_kubernetes_namespace"] - target_label = "namespace" - } - - rule { - source_labels = ["__meta_kubernetes_pod_name"] - target_label = "pod" - } - - rule { - source_labels = ["__meta_kubernetes_pod_container_name"] - target_label = "container" - } - - rule { - source_labels = ["__meta_kubernetes_pod_uid"] - target_label = "pod_uid" - } - - rule { - source_labels = ["__meta_kubernetes_pod_node_name"] - target_label = "node_name" - } - - // Derive app: app.kubernetes.io/name → app label → controller name (hash stripped) → pod name - rule { - source_labels = ["__meta_kubernetes_pod_controller_name"] - regex = "([0-9a-z-.]+?)(-[0-9a-f]{8,10})?" - target_label = "__tmp_controller_name" - } - - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name", "__meta_kubernetes_pod_label_app", "__tmp_controller_name", "__meta_kubernetes_pod_name"] - regex = "^;*([^;]+)(;.*)?$" - replacement = "$1" - target_label = "app" - } - - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_instance", "__meta_kubernetes_pod_label_instance"] - regex = "^;*([^;]+)(;.*)?$" - replacement = "$1" - target_label = "instance" - } - - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_component", "__meta_kubernetes_pod_label_component"] - regex = "^;*([^;]+)(;.*)?$" - replacement = "$1" - target_label = "component" - } - - rule { - source_labels = ["namespace", "app"] - separator = "/" - target_label = "job" - } - - // Force cluster label to prevent pod kubernetes labels from overriding this value. - rule { - target_label = "cluster" - replacement = "{{ logging_alloy_cluster_label }}" - } -} - -loki.source.kubernetes "pods" { - targets = discovery.relabel.pods.output - forward_to = [loki.write.default.receiver] -} - -loki.source.kubernetes_events "events" { - job_name = "kubernetes-events" - log_format = "json" - forward_to = [loki.relabel.events.receiver] -} - -loki.relabel "events" { - forward_to = [loki.write.default.receiver] - - rule { - action = "labeldrop" - regex = "instance" - } - - rule { - target_label = "cluster" - replacement = "{{ logging_alloy_cluster_label }}" - } - - rule { - target_label = "job" - replacement = "monitoring/event-exporter" - } -} - -// default loki endpoints -loki.write "default" { -{% for endpoint in logging_alloy_loki_write_endpoints %} - endpoint { - url = "{{ endpoint.url }}" -{% if endpoint.remote_timeout is defined %} - remote_timeout = "{{ endpoint.remote_timeout }}" -{% endif %} -{% if endpoint.basic_auth is defined %} - basic_auth { - username = "{{ endpoint.basic_auth.username }}" - password = "{{ endpoint.basic_auth.password }}" - } -{% endif %} - } -{% endfor %} -} - -// write metrics to remote Prometheus endpoint -{% if logging_alloy_prometheus_write_endpoints %} -prometheus.exporter.self "alloy" {} - -prometheus.scrape "alloy_self" { - targets = prometheus.exporter.self.alloy.targets - forward_to = [prometheus.relabel.alloy_self.receiver] -} - -prometheus.relabel "alloy_self" { - forward_to = [prometheus.remote_write.default.receiver] - - rule { - target_label = "cluster" - replacement = "{{ logging_alloy_cluster_label }}" - } -} - -prometheus.remote_write "default" { -{% for endpoint in logging_alloy_prometheus_write_endpoints %} - endpoint { - url = "{{ endpoint.url }}" -{% if endpoint.remote_timeout is defined %} - remote_timeout = "{{ endpoint.remote_timeout }}" -{% endif %} -{% if endpoint.basic_auth is defined %} - basic_auth { - username = "{{ endpoint.basic_auth.username }}" - password = "{{ endpoint.basic_auth.password }}" - } -{% endif %} - } -{% endfor %} - wal { - // How often to truncate the WAL. Samples older than max_keepalive_time are dropped. - truncate_frequency = "{{ logging_alloy_prometheus_wal_truncate_frequency }}" - // Maximum time to keep undelivered samples in the WAL before dropping them. - // Increase if you expect control-plane outages longer than the default. - max_keepalive_time = "{{ logging_alloy_prometheus_wal_max_keepalive_time }}" - } -} -{% endif %} diff --git a/control-plane/roles/logging/templates/alloy-values.yaml b/control-plane/roles/logging/templates/alloy-values.yaml deleted file mode 100644 index 812719eb9..000000000 --- a/control-plane/roles/logging/templates/alloy-values.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# Source with all the defaults: https://raw.githubusercontent.com/grafana/alloy/main/operations/helm/charts/alloy/values.yaml -alloy: - configMap: - # -- Create a new ConfigMap for the config file. - create: true - # -- Content to assign to the new ConfigMap. This is passed into `tpl` allowing for templating from values. - content: |- - {{ logging_alloy_config | indent(6) }} - - # -- Port to listen for traffic on. - listenPort: {{ logging_alloy_port }} - - # -- Path to where Alloy stores its state (e.g. log positions file for loki.source.kubernetes). - # Backed by a hostPath volume so positions survive pod restarts on the same node. - storagePath: /var/lib/alloy/data - - mounts: - extra: - - name: alloy-data - mountPath: /var/lib/alloy/data - - # -- Enables sending Grafana Labs anonymous usage stats to help improve Grafana - # Alloy. - enableReporting: false - - serviceMonitor: - enabled: {{ logging_alloy_service_monitor_enabled | bool | lower }} - -controller: - volumes: - extra: - - name: alloy-data - hostPath: - path: /var/lib/alloy/data - type: DirectoryOrCreate - - # -- Tolerations to apply to Grafana Alloy pods. - tolerations: - - key: node-role.kubernetes.io/master - operator: Exists - effect: NoSchedule - - key: node-role.kubernetes.io/control-plane - operator: Exists - effect: NoSchedule diff --git a/control-plane/roles/monitoring/README.md b/control-plane/roles/monitoring/README.md index 0fee4f917..1be572ba3 100644 --- a/control-plane/roles/monitoring/README.md +++ b/control-plane/roles/monitoring/README.md @@ -18,65 +18,62 @@ The following variables can be set to configure the role: ### General -| Name | Mandatory | Description | -| ------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| rethinkdb_exporter_name | yes | rethinkdb exporter image name | -| rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | -| event_exporter_enabled | | Deploy the event-exporter. Enabled by default for Promtail compatibility — disable after migrating to Alloy, which collects events natively via `loki.source.kubernetes_events`. | -| event_exporter_name | | event exporter image name | -| event_exporter_tag | | event exporter image tag | -| event_exporter_migrate_cleanup | | Remove all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Set `true` after setting `event_exporter_enabled: false` to let the role clean up the running resources automatically. Idempotent — safe to run even if the resources are already gone. | -| gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | -| gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | -| prometheus_chart_version | yes | version of the prometheus stack chart | -| prometheus_stack_repo | yes | chart of the prometheus stack | -| monitoring_namespace | | Name of the monitoring namespace | -| monitoring_ingress_grafana_tls | | If enabled, exposes Grafana through HTTPS on the ingress | -| monitoring_grafana_ingress_dns | | The dns name used for exposing Grafana via ingress | -| monitoring_prometheus_ingress_dns | | If enabled, exposes Prometheus through HTTPS on the ingress | -| monitoring_prometheus_image_tag | | Prometheus container image tag, defaults to chart's default | -| monitoring_prometheus_ingress_enabled | | Enables ingress for prometheus | -| monitoring_prometheus_storage_spec | | Prometheus storage spec, see [Storage Configuration](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) | -| monitoring_grafana_ingress_enabled | | Enables ingress for grafana | -| monitoring_prometheus_core_dns_enabled | | Enables CoreDns monitoring | -| monitoring_prometheus_kube_dns_enabled | | Enables KubeDns monitoring | -| monitoring_prometheus_kube_proxy_enabled | | Enables KubeProxy monitoring | -| monitoring_prometheus_kube_proxy_service_selector | | The service selector for kube-proxy | -| monitoring_prometheus_kube_scheduler_enabled | | Enables KubeScheduler monitoring | -| monitoring_prometheus_kube_etcd_enabled | | Enables KubeEtcd monitoring | -| monitoring_prometheus_kube_controller_manager_enabled | | Enables KubeControllerManager monitoring | -| monitoring_grafana_admin_password | | Sets the admin password for Grafana | -| monitoring_grafana_dashboard_timezone | | Sets the default's dashboard timezone for Grafana | -| monitoring_grafana_additional_datasources | | Configures additional datasources for Grafana | -| monitoring_grafana_github_oauth | | [Configure GitHub OAuth2 authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/github/) | -| monitoring_grafana_extra_secret_mounts | | Extra secret mounts | -| monitoring_slack_api_url | | The URL for the Slack API, required to send alerts. This must be a valid and accessible Slack webhook URL specific to the channel where alerts will be sent. | -| monitoring_slack_notification_channel | | The name or ID of the Slack channel where the alerts will be posted. This must correspond to an existing Slack channel within the workspace defined by the `monitoring_slack_api_url`. | -| monitoring_slack_notification_icon_url | | The URL of an image that will be used as the icon for notifications in Slack. This is optional, and if not provided, the default icon will be used. The URL must be accessible by Slack and should point to a valid image file. | -| monitoring_slack_notification_title | | The title of the Slack notification. This will be used as the headline or subject of the alert and can be a static text or include dynamic content based on the alert data. | -| monitoring_slack_notification_text | | The main text body of the Slack notification. This should include the detailed information about the alert and can support Slack's formatting and templating for more structured and readable notifications. | -| monitoring_alertmanager_additional_routes | | Additional [routes](https://prometheus.io/docs/alerting/latest/configuration/#route-related-settings) for the alertmanager. | -| monitoring_alertmanager_additional_receivers | | Additional [receivers](https://prometheus.io/docs/alerting/latest/configuration/#general-receiver-related-settings) for the alertmanager. | -| monitoring_alertmanager_webhook_logger_repeat_interval | | The alertmanager repeat interval for the webhook-logger | -| monitoring_alertmanager_webhook_slack_repeat_interval | | The alertmanager repeat interval for slack | -| monitoring_metal_api_url | | The URL where to reach metal-api | -| monitoring_metal_api_hmac | | The hmac to authenticate against metal-api | -| monitoring_thanos_object_store_config | | Object storage used by Thanos, see [Official Documentation](https://thanos.io/tip/thanos/storage.md/#supported-clients) | -| monitoring_thanos_object_store_resource_preset | | The resource preset to use for the object store (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | -| monitoring_thanos_object_store_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the storegateway pod (init may take quite long depending on the bucket size) | -| monitoring_thanos_receive_enabled | | Enable Thanos Receive component | -| monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | -| monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | -| monitoring_thanos_receive_ingress_basic_auth_user | | Basic auth username for the Thanos Receive ingress (default: `thanos-receive`). Used by nginx and automatically by `gardener-logging` Alloy for `prometheus.remote_write`. | -| monitoring_thanos_receive_ingress_basic_auth_password | | Basic auth password for the Thanos Receive ingress. When set, nginx basic auth is enabled and the htpasswd entry is generated automatically. Also used by `gardener-logging` Alloy for `prometheus.remote_write`. | -| monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | -| monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | -| monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | -| monitoring_thanos_receive_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the receiver pod (init may take quite long depending on the bucket size) | -| monitoring_thanos_receive_resource_preset | | The resource preset to use for the receiver (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | -| monitoring_thanos_compactor_size | | The PVC size of the Thanos Compactor component | -| monitoring_thanos_compactor_storage_class | | The storage class used for the compactor | -| monitoring_thanos_compactor_resource_preset | | The resource preset to use for the compactor (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| Name | Mandatory | Description | +| ------------------------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| rethinkdb_exporter_name | yes | rethinkdb exporter image name | +| rethinkdb_exporter_tag | yes | rethinkdb exporter image tag | +| event_exporter_name | yes | event exporter image name | +| event_exporter_tag | yes | event exporter image tag | +| gardener_metrics_exporter_image_name | yes | gardener metrics exporter image name | +| gardener_metrics_exporter_image_tag | yes | gardener metrics exporter image tag | +| prometheus_chart_version | yes | version of the prometheus stack chart | +| prometheus_stack_repo | yes | chart of the prometheus stack | +| monitoring_namespace | | Name of the monitoring namespace | +| monitoring_ingress_grafana_tls | | If enabled, exposes Grafana through HTTPS on the ingress | +| monitoring_grafana_ingress_dns | | The dns name used for exposing Grafana via ingress | +| monitoring_prometheus_ingress_dns | | If enabled, exposes Prometheus through HTTPS on the ingress | +| monitoring_prometheus_image_tag | | Prometheus container image tag, defaults to chart's default | +| monitoring_prometheus_ingress_enabled | | Enables ingress for prometheus | +| monitoring_prometheus_storage_spec | | Prometheus storage spec, see [Storage Configuration](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/storage.md) | +| monitoring_grafana_ingress_enabled | | Enables ingress for grafana | +| monitoring_prometheus_core_dns_enabled | | Enables CoreDns monitoring | +| monitoring_prometheus_kube_dns_enabled | | Enables KubeDns monitoring | +| monitoring_prometheus_kube_proxy_enabled | | Enables KubeProxy monitoring | +| monitoring_prometheus_kube_proxy_service_selector | | The service selector for kube-proxy | +| monitoring_prometheus_kube_scheduler_enabled | | Enables KubeScheduler monitoring | +| monitoring_prometheus_kube_etcd_enabled | | Enables KubeEtcd monitoring | +| monitoring_prometheus_kube_controller_manager_enabled | | Enables KubeControllerManager monitoring | +| monitoring_grafana_admin_password | | Sets the admin password for Grafana | +| monitoring_grafana_dashboard_timezone | | Sets the default's dashboard timezone for Grafana | +| monitoring_grafana_additional_datasources | | Configures additional datasources for Grafana | +| monitoring_grafana_github_oauth | | [Configure GitHub OAuth2 authentication](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/github/) | +| monitoring_grafana_extra_secret_mounts | | Extra secret mounts | +| monitoring_slack_api_url | | The URL for the Slack API, required to send alerts. This must be a valid and accessible Slack webhook URL specific to the channel where alerts will be sent. | +| monitoring_slack_notification_channel | | The name or ID of the Slack channel where the alerts will be posted. This must correspond to an existing Slack channel within the workspace defined by the `monitoring_slack_api_url`. | +| monitoring_slack_notification_icon_url | | The URL of an image that will be used as the icon for notifications in Slack. This is optional, and if not provided, the default icon will be used. The URL must be accessible by Slack and should point to a valid image file. | +| monitoring_slack_notification_title | | The title of the Slack notification. This will be used as the headline or subject of the alert and can be a static text or include dynamic content based on the alert data. | +| monitoring_slack_notification_text | | The main text body of the Slack notification. This should include the detailed information about the alert and can support Slack's formatting and templating for more structured and readable notifications. | +| monitoring_alertmanager_additional_routes | | Additional [routes](https://prometheus.io/docs/alerting/latest/configuration/#route-related-settings) for the alertmanager. | +| monitoring_alertmanager_additional_receivers | | Additional [receivers](https://prometheus.io/docs/alerting/latest/configuration/#general-receiver-related-settings) for the alertmanager. | +| monitoring_alertmanager_webhook_logger_repeat_interval | | The alertmanager repeat interval for the webhook-logger | +| monitoring_alertmanager_webhook_slack_repeat_interval | | The alertmanager repeat interval for slack | +| monitoring_metal_api_url | | The URL where to reach metal-api | +| monitoring_metal_api_hmac | | The hmac to authenticate against metal-api | +| monitoring_thanos_object_store_config | | Object storage used by Thanos, see [Official Documentation](https://thanos.io/tip/thanos/storage.md/#supported-clients) | +| monitoring_thanos_object_store_resource_preset | | The resource preset to use for the object store (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| monitoring_thanos_object_store_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the storegateway pod (init may take quite long depending on the bucket size) | +| monitoring_thanos_receive_enabled | | Enable Thanos Receive component | +| monitoring_thanos_receive_ingress_enabled | | Enable Ingress for Thanos Receive | +| monitoring_thanos_receive_ingress_annotations | | Annotations that will be attached to the ingress resource for the Thanos Receive component | +| monitoring_thanos_receive_ingress_basic_auth | | Set basic authentication on the Ingress for Thanos Receive | +| monitoring_thanos_receive_ingress_dns | | The DNS name used for exposing Thanos Receive via Ingress | +| monitoring_thanos_receive_ingress_tls | | If enabled, exposes Thanos Receive through HTTPS on the Ingress | +| monitoring_thanos_receive_size | | The PVC size of the Thanos Receive component | +| monitoring_thanos_receive_probe_failure_threshold | | The failure threshold of liveness and readiness probe for the receiver pod (init may take quite long depending on the bucket size) | +| monitoring_thanos_receive_resource_preset | | The resource preset to use for the receiver (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | +| monitoring_thanos_compactor_size | | The PVC size of the Thanos Compactor component | +| monitoring_thanos_compactor_storage_class | | The storage class used for the compactor | +| monitoring_thanos_compactor_resource_preset | | The resource preset to use for the compactor (see [this table](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15)) | ### Gardener @@ -86,30 +83,3 @@ The following variables can be set to configure the role: | monitoring_gardener_metrics_exporter_image_name | | gardener-metrics-exporter image name | | monitoring_gardener_metrics_exporter_image_tag | | gardener-metrics-exporter image tag | | monitoring_gardener_virtual_garden_kubeconfig | | The kubeconfig for the kube-apiserver of the virtual garden | - -## Migration - -### Thanos Receive ingress credentials - -The old `monitoring_thanos_receive_ingress_basic_auth` variable (a raw htpasswd-format string) has been replaced by `monitoring_thanos_receive_ingress_basic_auth_user` and `monitoring_thanos_receive_ingress_basic_auth_password`. The htpasswd entry for nginx is now generated automatically from these, following the same pattern as Alertmanager. - -> **The role will fail immediately** with a clear error message if the old variable is still set. Nothing is deployed until you migrate. - -**Before:** - -```yaml -monitoring_thanos_receive_ingress_basic_auth: "myuser:$apr1$..." -``` - -**After:** - -```yaml -monitoring_thanos_receive_ingress_basic_auth_user: myuser # default: thanos-receive -monitoring_thanos_receive_ingress_basic_auth_password: mysecret -``` - -### Disabling the event-exporter after Alloy migration - -The event-exporter Deployment (which writes Kubernetes events to stdout for Promtail to scrape) is deployed by default for backward compatibility. When using Alloy, events are collected natively via `loki.source.kubernetes_events` and the event-exporter is no longer needed. - -Once you have verified that Alloy is collecting events correctly, set `event_exporter_enabled: false` and `event_exporter_migrate_cleanup: true`. On the next run the monitoring role will skip the Deployment and delete all event-exporter resources (Deployment, ServiceAccount, ConfigMap, ClusterRole, ClusterRoleBinding). Remove `event_exporter_migrate_cleanup` from your inventory afterwards. diff --git a/control-plane/roles/monitoring/defaults/main.yaml b/control-plane/roles/monitoring/defaults/main.yaml index 82e9d4cf4..fbd775dcb 100644 --- a/control-plane/roles/monitoring/defaults/main.yaml +++ b/control-plane/roles/monitoring/defaults/main.yaml @@ -36,9 +36,6 @@ monitoring_alertmanager_ingress_basic_auth_password_salt: "{{ monitoring_alertma monitoring_alertmanager_webhook_logger_repeat_interval: 60m monitoring_alertmanager_webhook_slack_repeat_interval: 24h -# event exporter -event_exporter_enabled: true - # metal metrics exporter monitoring_metal_api_url: "http://metal-api.metal-control-plane.svc:8080/metal" monitoring_metal_api_hmac: "metal-view" @@ -73,9 +70,7 @@ monitoring_thanos_receive_enabled: false monitoring_thanos_receive_ingress_enabled: false monitoring_thanos_receive_ingress_dns: "thanos-receive.{{ metal_control_plane_ingress_dns }}" monitoring_thanos_receive_ingress_annotations: [] -monitoring_thanos_receive_ingress_basic_auth_user: thanos-receive -monitoring_thanos_receive_ingress_basic_auth_password: -monitoring_thanos_receive_ingress_basic_auth_password_salt: "{{ monitoring_thanos_receive_ingress_basic_auth_password | hash('sha1') | truncate(22, True, '') }}" +monitoring_thanos_receive_ingress_basic_auth: {} monitoring_thanos_receive_ingress_tls: {} monitoring_thanos_receive_size: 50Gi monitoring_thanos_receive_resource_preset: "large" diff --git a/control-plane/roles/monitoring/tasks/exporters.yaml b/control-plane/roles/monitoring/tasks/exporters.yaml index d658a5dbd..0bf33da17 100644 --- a/control-plane/roles/monitoring/tasks/exporters.yaml +++ b/control-plane/roles/monitoring/tasks/exporters.yaml @@ -16,36 +16,6 @@ definition: "{{ lookup('template', 'metrics-exporters/event-exporter.yaml') }}" namespace: "{{ monitoring_namespace }}" apply: true - when: event_exporter_enabled | default(false) | bool - -- name: Remove event-exporter - k8s: - state: absent - definition: - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: event-exporter - namespace: "{{ monitoring_namespace }}" - - apiVersion: v1 - kind: ServiceAccount - metadata: - name: event-exporter - namespace: "{{ monitoring_namespace }}" - - apiVersion: v1 - kind: ConfigMap - metadata: - name: event-exporter-cfg - namespace: "{{ monitoring_namespace }}" - - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - name: event-exporter - - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding - metadata: - name: event-exporter - when: event_exporter_migrate_cleanup | default(false) | bool # TODO: Fix to make compatible with gardener-operator # - name: Deploy gardener-metrics-exporter diff --git a/control-plane/roles/monitoring/tasks/main.yaml b/control-plane/roles/monitoring/tasks/main.yaml index 0e7361689..46e59b483 100644 --- a/control-plane/roles/monitoring/tasks/main.yaml +++ b/control-plane/roles/monitoring/tasks/main.yaml @@ -9,20 +9,11 @@ that: - rethinkdb_exporter_name is defined - rethinkdb_exporter_tag is defined + - event_exporter_name is defined + - event_exporter_tag is defined - gardener_metrics_exporter_image_name is defined - gardener_metrics_exporter_image_tag is defined -- name: Check deprecated variables are not set - assert: - fail_msg: > - 'monitoring_thanos_receive_ingress_basic_auth' has been removed. - Please migrate to 'monitoring_thanos_receive_ingress_basic_auth_user' and - 'monitoring_thanos_receive_ingress_basic_auth_password' — the htpasswd entry - is now generated automatically. See the Migration section in the role README. - quiet: true - that: - - monitoring_thanos_receive_ingress_basic_auth is not defined or monitoring_thanos_receive_ingress_basic_auth == {} - - name: Create namespace {{ monitoring_namespace }} k8s: definition: diff --git a/control-plane/roles/monitoring/templates/thanos-values.yaml b/control-plane/roles/monitoring/templates/thanos-values.yaml index 12ea33f42..c2813031d 100644 --- a/control-plane/roles/monitoring/templates/thanos-values.yaml +++ b/control-plane/roles/monitoring/templates/thanos-values.yaml @@ -89,14 +89,14 @@ receive: enabled: {{ monitoring_thanos_receive_ingress_enabled }} hostname: {{ monitoring_thanos_receive_ingress_dns }} ingressClassName: nginx -{% if monitoring_thanos_receive_ingress_annotations or monitoring_thanos_receive_ingress_basic_auth_password or monitoring_thanos_receive_ingress_tls %} +{% if monitoring_thanos_receive_ingress_annotations or monitoring_thanos_receive_ingress_basic_auth or monitoring_thanos_receive_ingress_tls %} annotations: {% endif %} {% if monitoring_thanos_receive_ingress_tls %} nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/ssl-redirect: "true" {% endif %} -{% if monitoring_thanos_receive_ingress_basic_auth_password %} +{% if monitoring_thanos_receive_ingress_basic_auth %} nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: thanos-receive-basic-auth {% endif %} @@ -119,13 +119,13 @@ receive: replicaCount: 2 -{% if monitoring_thanos_receive_ingress_basic_auth_password %} +{% if monitoring_thanos_receive_ingress_basic_auth %} extraDeploy: - apiVersion: v1 kind: Secret metadata: name: thanos-receive-basic-auth type: Opaque - stringData: - auth: "{{ monitoring_thanos_receive_ingress_basic_auth_user ~ ':' ~ monitoring_thanos_receive_ingress_basic_auth_password | string | password_hash('bcrypt', salt=monitoring_thanos_receive_ingress_basic_auth_password_salt) }}" + data: + auth: "{{ monitoring_thanos_receive_ingress_basic_auth | b64encode }}" {% endif %} From 14060af9a2ae4d084eed59d9e0f06e1e6055f7b1 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Wed, 10 Jun 2026 08:46:41 +0200 Subject: [PATCH 18/24] docs: finalize --- .../roles/gardener-logging/README.md | 74 ++++++++++++------- .../roles/gardener-logging/defaults/main.yaml | 10 +-- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 65d72c5a5..f87ee496b 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -6,6 +6,31 @@ Expects the [logging role](../logging/) to have been deployed first. This role supports deploying Alloy and/or Promtail as log collectors. Promtail is deployed by default for backward compatibility. See [Migration from Promtail](#migration-from-promtail) for guidance on switching to Alloy. +## Background and Architecture + +### Why this role exists + +Gardener ships with a built-in logging stack — gardenlet can deploy [Vali](https://github.com/credativ/plutono) (a Loki fork) and fluent-bit into each seed cluster. The metal-stack deployment disables this stack intentionally. + +Instead, this role deploys Alloy as a DaemonSet into the Garden cluster and Shooted Seeds to ship **all** pod logs to the central Loki instance in the metal-stack control plane. This gives platform operators a single place to query infrastructure logs across all Gardener clusters — including the Garden cluster itself, seed system components, and extension controllers. + +This role targets the **Garden cluster** (`gardener_logging_deploy_to_garden_cluster: true`) and **Shooted Seeds** (`gardener_logging_shooted_seeds`). + +### Why there are two task files + +- **`tasks/main.yaml`** — handles the Garden cluster. The Garden cluster is a normal Kubernetes cluster reachable with the current kubeconfig. Alloy is deployed directly. +- **`tasks/gardener-shooted-seed.yaml`** — handles one Shooted Seed per loop iteration. Because Shooted Seeds are managed as Shoots, their kubeconfig is not directly available. The task fetches it via the virtual garden API (`virtual_garden_kubeconfig` + `shoot_admin_kubeconfig` filter), then deploys Alloy using that kubeconfig. + +### What gets collected + +Alloy runs as a Kubernetes DaemonSet in the `monitoring` namespace. It uses `loki.source.kubernetes` to collect logs from **every pod** in the cluster via the Kubernetes API — no annotations, opt-in labels, or per-service configuration is required. Kubernetes events are also collected natively via `loki.source.kubernetes_events`. + +### Prometheus metrics + +Alloy can optionally push its own self-metrics to a remote Prometheus endpoint (see [Meta-monitoring](#meta-monitoring)). It does **not** collect metrics from other workloads in the cluster. + +Seed workload metrics are Gardener's responsibility: gardenlet manages Cache, Seed, and Aggregate Prometheus instances on each seed. Those Prometheus instances use annotation-based discovery restricted to known namespaces and do not reach the `monitoring` namespace where Alloy runs — pull-based metric collection from Alloy is therefore not supported. + ## Configuration The Alloy River config is generated from structured variables at deploy time. Override individual variables to customize behavior, or bypass the template entirely with `gardener_logging_alloy_config_raw`. @@ -36,15 +61,15 @@ The following variables can be set to configure the role: ### Alloy -| Name | Mandatory | Default | Description | -| -------------------------------------------------------- | --------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| gardener_logging_alloy_port | | `12345` | Alloy listen port | -| gardener_logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_cluster_label | | `gardener_logging_shooted_seed.name` | Value for the `cluster=` label set on all log and metric streams via relabel rules | -| gardener_logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | -| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | -| gardener_logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | +| Name | Mandatory | Default | Description | +| -------------------------------------------------------- | --------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_port | | `12345` | Alloy listen port | +| gardener_logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_cluster_label | | `gardener_logging_shooted_seed.name` | Value for the `cluster=` label set on all log and metric streams via relabel rules. Defaults to the shooted seed name per loop iteration; the garden cluster deployment substitutes `gardener_logging_garden_name` via task-level vars before the template is rendered. | +| gardener_logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | +| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | +| gardener_logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). @@ -67,11 +92,11 @@ Alloy's positions file (tracking the read offset for each container log) is pers ### Kubernetes events (`loki.source.kubernetes_events`) -| Label | Value | -| ----------- | ----------------------------------------------------------------------- | -| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | -| `job` | `monitoring/event-exporter` (relabelled for Promtail compatibility) | -| `namespace` | Namespace of the event | +| Label | Value | +| ----------- | ------------------------------------------------------------------------------------ | +| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | +| `job` | `monitoring/event-exporter` — stream name used consistently across all logging roles | +| `namespace` | Namespace of the event | Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. @@ -109,16 +134,16 @@ Alloy is the recommended log collector. Promtail is deployed by default — exis Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and LogQL queries continue to work without changes. What has changed compared to Promtail: -- **Kubernetes events are now built-in.** Promtail required a separate event-exporter Deployment. Alloy collects events natively via `loki.source.kubernetes_events` and labels them `job="monitoring/event-exporter"` for full backward compatibility. -- **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write` to the control-plane Thanos Receive ingress, removing the ServiceMonitor ordering dependency. Wired automatically when `monitoring_thanos_receive_ingress_enabled: true`. +- **Kubernetes events are now built-in.** The previous Promtail deployment in this role only collected pod logs (`cri`/`docker` pipeline stages) — it had no event collection at all. Alloy adds native Kubernetes event collection via `loki.source.kubernetes_events`, labelled `job="monitoring/event-exporter"` for consistency with the control-plane logging role. +- **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write`, removing the ServiceMonitor ordering dependency. Configure `gardener_logging_alloy_prometheus_write_endpoints` to enable this — see [Meta-monitoring](#meta-monitoring). - **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. -| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | -| --------------------------------- | -------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Fresh deployment** | `true` | `false` | Alloy only. | -| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | -| **Cutover complete** | `true` | `false` | Set `gardener_logging_promtail_migrate_cleanup: true`, `event_exporter_enabled: false`, and `event_exporter_migrate_cleanup: true` (in monitoring config) and re-run. Remove the cleanup variables afterwards. | +| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | +| --------------------------------- | -------------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Fresh deployment** | `true` | `false` | Alloy only. | +| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | +| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | +| **Cutover complete** | `true` | `false` | Set `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role removes the Promtail Helm release from the garden cluster and all shooted seeds automatically. Remove the variable afterwards. | **To migrate an existing Promtail installation:** @@ -127,6 +152,5 @@ Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and 3. Set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. 4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. 5. Set `gardener_logging_promtail_enabled: false` and `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release from the garden cluster and every shooted seed automatically. Remove `gardener_logging_promtail_migrate_cleanup` from your inventory afterwards. -6. Set `event_exporter_enabled: false` in your monitoring config and set `event_exporter_migrate_cleanup: true` — only needed for Promtail's event pipeline. See the [monitoring role migration guide](../monitoring/README.md#disabling-the-event-exporter-after-alloy-migration). -7. Verify Promtail is removed: the DaemonSet and related resources are gone. Verify the event-exporter Deployment is removed if you also migrated that. -8. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the logging role with updated variables replaces the secret automatically. If those credentials are also configured in `gardener_logging_ingress_loki_basic_auth_user` / `gardener_logging_ingress_loki_basic_auth_password` for Alloy/Promtail on the shooted seeds, update all of them in the same deployment to avoid auth failures. +6. Verify Promtail is removed: the DaemonSet and related resources are gone. +7. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the logging role with updated variables replaces the secret automatically. If those credentials are also configured in `gardener_logging_ingress_loki_basic_auth_user` / `gardener_logging_ingress_loki_basic_auth_password` for Alloy/Promtail on the shooted seeds, update all of them in the same deployment to avoid auth failures. diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index c3f016b19..92c81a116 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -1,13 +1,15 @@ --- gardener_logging_namespace: monitoring +gardener_logging_garden_name: "{{ gardener_defaults_garden_name }}" +gardener_logging_deploy_to_garden_cluster: true +gardener_logging_shooted_seeds: [] + # - name: my-shooted-seed # Alloy deployment (default false). Set true to deploy Alloy. # Requires gardener_logging_alloy_chart_version and gardener_logging_alloy_chart_repo when enabled. gardener_logging_alloy_enabled: false gardener_logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" - -gardener_logging_garden_name: "{{ gardener_defaults_garden_name }}" # is not renamed to alloy for backward compatibility with existing installs. # if updated, the shooted seed`s alloy/promtail need their loki endpoints updated to match. gardener_logging_ingress_loki_basic_auth_user: promtail @@ -51,7 +53,3 @@ gardener_logging_alloy_prometheus_wal_max_keepalive_time: "8h" # Requires gardener_logging_promtail_chart_version and gardener_logging_promtail_chart_repo when enabled. # See the Migration from Promtail section in README.md. gardener_logging_promtail_enabled: true - -gardener_logging_deploy_to_garden_cluster: true -gardener_logging_shooted_seeds: [] - # - name: my-shooted-seed From a0c212de33d2396e84b8d8e7547d15954ac34c54 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Wed, 10 Jun 2026 12:22:50 +0200 Subject: [PATCH 19/24] fix: empty defaults --- control-plane/roles/gardener-logging/defaults/main.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 92c81a116..409490537 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -16,7 +16,7 @@ gardener_logging_ingress_loki_basic_auth_user: promtail gardener_logging_ingress_loki_basic_auth_password: # Loki push endpoints. -# gardener_logging_alloy_loki_write_endpoints: +gardener_logging_alloy_loki_write_endpoints: [] # - url: "https://{{ gardener_logging_ingress_dns }}/loki/api/v1/push" # remote_timeout: 60s # basic_auth: @@ -31,7 +31,7 @@ gardener_logging_alloy_port: 12345 # Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). # Alloy scrapes its own /metrics and pushes them via remote_write. # Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -# gardener_logging_alloy_prometheus_write_endpoints: +gardener_logging_alloy_prometheus_write_endpoints: [] # - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" # remote_timeout: 60s # basic_auth: From 78960e649fb9788ab49ea5956d4806a2ae41c701 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 15 Jun 2026 13:45:52 +0200 Subject: [PATCH 20/24] chore: use logging-common --- .../roles/gardener-logging/README.md | 147 ++++----------- .../roles/gardener-logging/defaults/main.yaml | 35 +--- .../roles/gardener-logging/meta/main.yaml | 1 + .../tasks/gardener-shooted-seed.yaml | 30 ++-- .../roles/gardener-logging/tasks/main.yaml | 35 ++-- .../templates/seed-alloy-config.alloy.j2 | 168 ------------------ .../templates/seed-alloy-values.yaml | 41 ----- 7 files changed, 66 insertions(+), 391 deletions(-) delete mode 100644 control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 delete mode 100644 control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index f87ee496b..9e65f14b0 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -1,10 +1,12 @@ # gardener-logging -Deploys [Grafana Alloy](https://grafana.com/docs/alloy/latest/) into Gardener shooted seeds and optionally into the garden cluster itself. Alloy collects pod logs via the Kubernetes API and forwards them to the Loki instance in the metal-stack control plane. +Deploys [Grafana Alloy](https://grafana.com/docs/alloy/latest/) into Gardener shooted seeds and optionally into the garden cluster itself. Alloy reads pod logs from the node filesystem and forwards them to the Loki instance in the metal-stack control plane. Expects the [logging role](../logging/) to have been deployed first. -This role supports deploying Alloy and/or Promtail as log collectors. Promtail is deployed by default for backward compatibility. See [Migration from Promtail](#migration-from-promtail) for guidance on switching to Alloy. +Alloy configuration, labels, meta-monitoring, and migration guidance are documented in [logging-common](../logging-common/). + +> **Promtail is deprecated.** Setting `gardener_logging_promtail_enabled: true` emits a deprecation warning on every run. See the [migration guide](../logging-common/README.md#migration-from-promtail). ## Background and Architecture @@ -23,134 +25,53 @@ This role targets the **Garden cluster** (`gardener_logging_deploy_to_garden_clu ### What gets collected -Alloy runs as a Kubernetes DaemonSet in the `monitoring` namespace. It uses `loki.source.kubernetes` to collect logs from **every pod** in the cluster via the Kubernetes API — no annotations, opt-in labels, or per-service configuration is required. Kubernetes events are also collected natively via `loki.source.kubernetes_events`. +Alloy runs as a Kubernetes DaemonSet in the `monitoring` namespace. It reads pod logs from the node filesystem (`/var/log/pods`, `loki.source.file`) — one DaemonSet pod per node, collecting only the logs for pods scheduled on that node. Kubernetes events are also collected natively via `loki.source.kubernetes_events` with clustering-based leader election to avoid duplication. ### Prometheus metrics -Alloy can optionally push its own self-metrics to a remote Prometheus endpoint (see [Meta-monitoring](#meta-monitoring)). It does **not** collect metrics from other workloads in the cluster. - -Seed workload metrics are Gardener's responsibility: gardenlet manages Cache, Seed, and Aggregate Prometheus instances on each seed. Those Prometheus instances use annotation-based discovery restricted to known namespaces and do not reach the `monitoring` namespace where Alloy runs — pull-based metric collection from Alloy is therefore not supported. - -## Configuration +Alloy can optionally push its own self-metrics to a remote Prometheus endpoint (see [logging-common meta-monitoring](../logging-common/README.md#meta-monitoring)). It does **not** collect metrics from other workloads in the cluster. -The Alloy River config is generated from structured variables at deploy time. Override individual variables to customize behavior, or bypass the template entirely with `gardener_logging_alloy_config_raw`. +Seed workload metrics are Gardener's responsibility: gardenlet manages Cache, Seed, and Aggregate Prometheus instances on each seed. Those Prometheus instances use annotation-based discovery restricted to known namespaces and do not reach the `monitoring` namespace where Alloy runs — pull-based metric collection from Alloy is not supported. Use push via `logging_alloy_prometheus_write_endpoints` instead. ## Variables You can look up all the default values of this role [here](defaults/main.yaml). -The following variables can be set to configure the role: - ### General -| Name | Mandatory | Default | Description | -| ------------------------------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| gardener_logging_alloy_enabled | | `false` | Deploy Alloy. Set `true` for new installs and Alloy-only setups. Requires `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. | -| gardener_logging_promtail_enabled | | `true` | Deploy Promtail (**deprecated** — see [Migration from Promtail](#migration-from-promtail)). Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | -| gardener_logging_alloy_chart_version | | | Helm chart version for alloy — required when `gardener_logging_alloy_enabled: true` | -| gardener_logging_alloy_chart_repo | | | Repository for alloy — required when `gardener_logging_alloy_enabled: true` | -| gardener_logging_promtail_chart_version | | | Helm chart version for promtail — required when `gardener_logging_promtail_enabled: true` | -| gardener_logging_promtail_chart_repo | | | Repository for promtail — required when `gardener_logging_promtail_enabled: true` | -| gardener_logging_promtail_migrate_cleanup | | `false` | Uninstall the Promtail Helm release from the garden cluster and all shooted seeds. Set `true` after cutover to let the role remove the releases automatically. Idempotent — safe to run even if the releases are already gone. | -| gardener_logging_namespace | | | The deployment's target namespace | -| gardener_logging_ingress_dns | | | DNS for loki ingress | -| gardener_logging_ingress_loki_basic_auth_password | | | The basic auth password for the external loki ingress | -| gardener_logging_ingress_loki_basic_auth_user | | | The basic auth user for the external loki ingress | -| gardener_logging_deploy_to_garden_cluster | | `true` | Deploys Alloy also into the garden cluster | -| gardener_logging_shooted_seeds | | | Shooted seed names on which to deploy Alloy that logs to loki | +| Name | Mandatory | Default | Description | +| ------------------------------------------------- | --------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| gardener_logging_alloy_enabled | | `true` | Deploy Alloy. Requires `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. | +| gardener_logging_alloy_chart_version | | | Helm chart version for alloy (release vector) | +| gardener_logging_alloy_chart_repo | | | Repository for alloy (release vector) | +| gardener_logging_promtail_enabled | | `false` | Deploy Promtail (**deprecated**). When `false`, any existing Promtail release is removed automatically from the garden cluster and all seeds. Requires chart vars when `true`. | +| gardener_logging_promtail_chart_version | | | Helm chart version for promtail (release vector) | +| gardener_logging_promtail_chart_repo | | | Repository for promtail (release vector) | +| gardener_logging_namespace | | `monitoring` | Target namespace | +| gardener_logging_garden_name | | `{{ gardener_defaults_garden_name }}` | Name of the garden cluster (used as `cluster=` label for the garden cluster Alloy deployment) | +| gardener_logging_deploy_to_garden_cluster | | `true` | Deploy Alloy also into the garden cluster | +| gardener_logging_shooted_seeds | | `[]` | List of shooted seeds to deploy Alloy into. Each entry: `{name: }` | +| gardener_logging_ingress_dns | | `loki.{{ metal_control_plane_ingress_dns }}` | DNS for the loki ingress (used in `gardener_logging_alloy_loki_write_endpoints` examples) | +| gardener_logging_ingress_loki_basic_auth_user | | `promtail` | Basic auth user for the external loki ingress | +| gardener_logging_ingress_loki_basic_auth_password | yes | | Basic auth password for the external loki ingress | ### Alloy -| Name | Mandatory | Default | Description | -| -------------------------------------------------------- | --------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| gardener_logging_alloy_port | | `12345` | Alloy listen port | -| gardener_logging_alloy_loki_write_endpoints | | | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_cluster_label | | `gardener_logging_shooted_seed.name` | Value for the `cluster=` label set on all log and metric streams via relabel rules. Defaults to the shooted seed name per loop iteration; the garden cluster deployment substitutes `gardener_logging_garden_name` via task-level vars before the template is rendered. | -| gardener_logging_alloy_prometheus_write_endpoints | | | List of Prometheus remote_write endpoints for Alloy self-metrics. When unset, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted. Samples older than `max_keepalive_time` are dropped | -| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped. Increase if you expect remote endpoint outages longer than this window | -| gardener_logging_alloy_config_raw | | | Full Alloy River config string override. When set, bypasses all structured vars above. | - -Alloy's positions file (tracking the read offset for each container log) is persisted via a `hostPath` volume at `/var/lib/alloy/data`. This ensures `loki.source.kubernetes` does not re-read already-shipped logs after a pod restart. The directory is created automatically on first run (`DirectoryOrCreate`). - -## Labels - -### Pod logs (`loki.source.kubernetes`) - -| Label | Source | -| ----------- | --------------------------------------------------------------------------------------------------------------- | -| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `discovery.relabel`) | -| `namespace` | `__meta_kubernetes_namespace` | -| `pod` | `__meta_kubernetes_pod_name` | -| `container` | `__meta_kubernetes_pod_container_name` | -| `pod_uid` | `__meta_kubernetes_pod_uid` | -| `node_name` | `__meta_kubernetes_pod_node_name` | -| `app` | `app.kubernetes.io/name` pod label, falling back to `app` label, controller name (hash stripped), then pod name | -| `instance` | `app.kubernetes.io/instance` pod label, falling back to `instance` label (empty if neither is set) | -| `component` | `app.kubernetes.io/component` pod label, falling back to `component` label (empty if neither is set) | -| `job` | `namespace/app` (using the computed `app` value above) | - -### Kubernetes events (`loki.source.kubernetes_events`) - -| Label | Value | -| ----------- | ------------------------------------------------------------------------------------ | -| `cluster` | `gardener_logging_alloy_cluster_label` (relabel rule in `loki.relabel`) | -| `job` | `monitoring/event-exporter` — stream name used consistently across all logging roles | -| `namespace` | Namespace of the event | - -Alloy watches events in all namespaces, which requires cluster-scope RBAC. The Alloy Helm chart includes the required `events` rule in its default `rbac.rules`, so no additional configuration is needed. - -## Meta-monitoring +| Name | Mandatory | Default | Description | +| ------------------------------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_loki_write_endpoints | yes\* | `[]` | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_config_raw | | | Full Alloy River config override for all targets. Bypasses all structured vars and the template. | -### Metrics - -Alloy exposes Prometheus metrics on port `{{ gardener_logging_alloy_port }}/metrics`. Self-metrics are disabled by default. - -Alloy can scrape itself and push metrics to a remote_write endpoint. This works regardless of the Prometheus setup on the seed. The target just needs to support remote_write. -Set `gardener_logging_alloy_prometheus_write_endpoints` to push to a remote_write endpoint. - -Example for Thanos Receive ingress: - -```yaml -gardener_logging_alloy_prometheus_write_endpoints: - - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" - remote_timeout: 60s - basic_auth: - username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" - password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" -``` - -Gardener's Prometheus instances use annotation-based pod discovery restricted to extension namespaces (Seed Prometheus) or scrape their own known targets (Aggregate Prometheus). Neither reliably reaches the `monitoring` namespace where Alloy runs, so pull-based collection is not a supported option. - -### Logs - -Alloy runs as a Kubernetes DaemonSet, so its own pod logs are captured by `loki.source.kubernetes` automatically — no additional configuration is needed. +For WAL settings, `logging_alloy_prometheus_write_endpoints`, and other Alloy options, set the `logging_alloy_*` variables from [logging-common](../logging-common/) directly in your inventory — they are used by the shared deployment task. ## Migration from Promtail -Alloy is the recommended log collector. Promtail is deployed by default — existing installations continue to work without changes after upgrading. - -> **Promtail is deprecated.** Setting `gardener_logging_promtail_enabled: true` emits a deprecation warning on every run. Promtail support will be removed in a future release. - -Alloy's label derivation is identical to Promtail's, so dashboards, alerts, and LogQL queries continue to work without changes. What has changed compared to Promtail: - -- **Kubernetes events are now built-in.** The previous Promtail deployment in this role only collected pod logs (`cri`/`docker` pipeline stages) — it had no event collection at all. Alloy adds native Kubernetes event collection via `loki.source.kubernetes_events`, labelled `job="monitoring/event-exporter"` for consistency with the control-plane logging role. -- **Metrics are now push-based.** Promtail exposed a `/metrics` endpoint and relied on Prometheus scraping it via a ServiceMonitor. Alloy instead scrapes itself and pushes metrics via `prometheus.remote_write`, removing the ServiceMonitor ordering dependency. Configure `gardener_logging_alloy_prometheus_write_endpoints` to enable this — see [Meta-monitoring](#meta-monitoring). -- **Metric WAL is new.** Alloy buffers undelivered self-metrics on disk (default: 8h). Promtail had no equivalent. - -| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | -| --------------------------------- | -------------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Fresh deployment** | `true` | `false` | Alloy only. | -| **Parallel run** | `true` | `true` | Both DaemonSets ship logs. Loki receives duplicate entries during this window. Requires `gardener_logging_promtail_chart_version` and `gardener_logging_promtail_chart_repo`. | -| **Promtail only** (keep existing) | `false` | `true` | Promtail only. **Default behavior** — existing installs work without changes. Deprecated — emits a warning on every run. | -| **Cutover complete** | `true` | `false` | Set `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role removes the Promtail Helm release from the garden cluster and all shooted seeds automatically. Remove the variable afterwards. | +See the [logging-common migration guide](../logging-common/README.md#migration-from-promtail) for general guidance. -**To migrate an existing Promtail installation:** +Alloy is deployed by default. Existing Promtail installations are automatically removed from the garden cluster and all shooted seeds when the role runs. -1. If you are pushing Alloy self-metrics to Thanos Receive, migrate the credentials first — see the [monitoring role migration guide](../monitoring/README.md#thanos-receive-ingress-credentials). -2. Promtail runs by default and a deprecation warning fires on every run as a reminder. Proceed when ready. -3. Set `gardener_logging_alloy_enabled: true` and add `gardener_logging_alloy_chart_version` and `gardener_logging_alloy_chart_repo`. Both DaemonSets will ship logs — Loki receives duplicate entries during this window. -4. Verify Alloy is working: logs arrive in Loki and existing dashboards, alerts, and LogQL queries return results as expected. -5. Set `gardener_logging_promtail_enabled: false` and `gardener_logging_promtail_migrate_cleanup: true` and re-run. The role will uninstall the Promtail Helm release from the garden cluster and every shooted seed automatically. Remove `gardener_logging_promtail_migrate_cleanup` from your inventory afterwards. -6. Verify Promtail is removed: the DaemonSet and related resources are gone. -7. **Optional:** Rotate the external Loki ingress credentials. The `loki-basic-auth` Kubernetes Secret is fully managed by Helm and holds a single entry derived from `logging_ingress_loki_basic_auth_user` and `logging_ingress_loki_basic_auth_password`. The default username remains `promtail` for backward compatibility — there is no need to change it. If you do want to rename the user (e.g. to `alloy`), re-running the logging role with updated variables replaces the secret automatically. If those credentials are also configured in `gardener_logging_ingress_loki_basic_auth_user` / `gardener_logging_ingress_loki_basic_auth_password` for Alloy/Promtail on the shooted seeds, update all of them in the same deployment to avoid auth failures. +| Scenario | `gardener_logging_alloy_enabled` | `gardener_logging_promtail_enabled` | Notes | +| -------------------------- | -------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ | +| **Default** | `true` | `false` | Alloy is deployed; existing Promtail releases are removed automatically from all targets. | +| **Parallel run** | `true` | `true` | Both collectors ship logs. Loki receives duplicate entries. Deprecated — emits a warning on every run. | +| **Promtail only** (legacy) | `false` | `true` | Deprecated — emits a warning on every run. | diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 409490537..4154310d4 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -5,9 +5,9 @@ gardener_logging_deploy_to_garden_cluster: true gardener_logging_shooted_seeds: [] # - name: my-shooted-seed -# Alloy deployment (default false). Set true to deploy Alloy. +# Alloy deployment. # Requires gardener_logging_alloy_chart_version and gardener_logging_alloy_chart_repo when enabled. -gardener_logging_alloy_enabled: false +gardener_logging_alloy_enabled: true gardener_logging_ingress_dns: "loki.{{ metal_control_plane_ingress_dns }}" # is not renamed to alloy for backward compatibility with existing installs. @@ -23,33 +23,8 @@ gardener_logging_alloy_loki_write_endpoints: [] # username: "{{ gardener_logging_ingress_loki_basic_auth_user }}" # password: "{{ gardener_logging_ingress_loki_basic_auth_password }}" -# Value for the cluster= label attached to all log and metric streams via relabel rules -gardener_logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" - -gardener_logging_alloy_port: 12345 - -# Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). -# Alloy scrapes its own /metrics and pushes them via remote_write. -# Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} -gardener_logging_alloy_prometheus_write_endpoints: [] -# - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" -# remote_timeout: 60s -# basic_auth: -# username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" -# password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" - -# WAL retention for prometheus.remote_write. Controls how long undelivered samples -# are buffered on disk before being dropped during a remote endpoint outage. -# truncate_frequency: how often the WAL is compacted (default: Alloy built-in 2h) -# max_keepalive_time: samples older than this are dropped even if not yet delivered (default: Alloy built-in 8h) -gardener_logging_alloy_prometheus_wal_truncate_frequency: "2h" -gardener_logging_alloy_prometheus_wal_max_keepalive_time: "8h" - -# Full Alloy River config override. When set, bypasses the seed-alloy-config.alloy.j2 template. -# gardener_logging_alloy_config_raw: | - -# Promtail deployment (default true, deprecated). Enabled by default for backward -# compatibility with existing installs. Set false once migrated to Alloy. +# Promtail deployment (deprecated). Set true only if still on Promtail. # Requires gardener_logging_promtail_chart_version and gardener_logging_promtail_chart_repo when enabled. +# When false, any existing Promtail release is automatically uninstalled. # See the Migration from Promtail section in README.md. -gardener_logging_promtail_enabled: true +gardener_logging_promtail_enabled: false diff --git a/control-plane/roles/gardener-logging/meta/main.yaml b/control-plane/roles/gardener-logging/meta/main.yaml index 6f2132b35..223386a71 100644 --- a/control-plane/roles/gardener-logging/meta/main.yaml +++ b/control-plane/roles/gardener-logging/meta/main.yaml @@ -14,5 +14,6 @@ galaxy_info: dependencies: - role: metal-roles/common/roles/defaults - role: metal-roles/control-plane/roles/defaults + - role: metal-roles/control-plane/roles/logging-common - role: metal-roles/control-plane/roles/gardener-defaults - role: ansible-common diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index af17adc37..ec0a27f4a 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -7,21 +7,19 @@ set_fact: _shoot_kubeconfig: "{{ virtual_garden_kubeconfig | string | shoot_admin_kubeconfig('garden', gardener_logging_shooted_seed.name) | from_yaml }}" -- name: Build alloy config - set_fact: - gardener_logging_alloy_config: "{{ lookup('template', 'seed-alloy-config.alloy.j2') if (gardener_logging_alloy_config_raw | default('') | length == 0) else gardener_logging_alloy_config_raw }}" - when: gardener_logging_alloy_enabled - -- name: Deploy alloy - kubernetes.core.helm: - name: alloy - chart_repo_url: "{{ gardener_logging_alloy_chart_repo }}" - chart_version: "{{ gardener_logging_alloy_chart_version }}" - chart_ref: alloy - namespace: "{{ gardener_logging_namespace }}" - values: "{{ lookup('template', 'seed-alloy-values.yaml') | from_yaml }}" - kubeconfig: "{{ _shoot_kubeconfig }}" - create_namespace: true +- name: Deploy Alloy to seed + include_role: + name: metal-roles/control-plane/roles/logging-common + tasks_from: deploy-alloy.yaml + vars: + logging_alloy_namespace: "{{ gardener_logging_namespace }}" + logging_alloy_chart_repo: "{{ gardener_logging_alloy_chart_repo }}" + logging_alloy_chart_version: "{{ gardener_logging_alloy_chart_version }}" + logging_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" + logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" + logging_alloy_service_monitor_enabled: false + logging_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" + logging_alloy_kubeconfig: "{{ _shoot_kubeconfig }}" when: gardener_logging_alloy_enabled - name: Deploy Promtail (parallel migration window) @@ -42,4 +40,4 @@ namespace: "{{ gardener_logging_namespace }}" kubeconfig: "{{ _shoot_kubeconfig }}" state: absent - when: gardener_logging_promtail_migrate_cleanup | default(false) | bool + when: not gardener_logging_promtail_enabled | bool diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 86b0d3249..8657490f6 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -7,11 +7,8 @@ fail_msg: "not all mandatory variables given, check role documentation" quiet: true that: - - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_chart_version is defined - - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_chart_repo is defined - not gardener_logging_promtail_enabled | bool or gardener_logging_promtail_chart_version is defined - not gardener_logging_promtail_enabled | bool or gardener_logging_promtail_chart_repo is defined - - not gardener_logging_alloy_enabled | bool or gardener_logging_alloy_loki_write_endpoints is defined or gardener_logging_alloy_config_raw is defined - name: Promtail deprecation warning ansible.builtin.fail: @@ -21,27 +18,19 @@ when: gardener_logging_promtail_enabled | bool ignore_errors: true -- name: Build Alloy config for garden cluster - set_fact: - gardener_logging_alloy_config: "{{ lookup('template', 'seed-alloy-config.alloy.j2') if (gardener_logging_alloy_config_raw | default('') | length == 0) else gardener_logging_alloy_config_raw }}" - when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled - vars: - gardener_logging_shooted_seed: - name: "{{ gardener_logging_garden_name }}" - - name: Deploy Alloy to garden cluster - kubernetes.core.helm: - name: alloy - chart_repo_url: "{{ gardener_logging_alloy_chart_repo }}" - chart_version: "{{ gardener_logging_alloy_chart_version }}" - chart_ref: alloy - namespace: "{{ gardener_logging_namespace }}" - values: "{{ lookup('template', 'seed-alloy-values.yaml') | from_yaml }}" - create_namespace: true - when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled + include_role: + name: metal-roles/control-plane/roles/logging-common + tasks_from: deploy-alloy.yaml vars: - gardener_logging_shooted_seed: - name: "{{ gardener_logging_garden_name }}" + logging_alloy_namespace: "{{ gardener_logging_namespace }}" + logging_alloy_chart_repo: "{{ gardener_logging_alloy_chart_repo }}" + logging_alloy_chart_version: "{{ gardener_logging_alloy_chart_version }}" + logging_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" + logging_alloy_cluster_label: "{{ gardener_logging_garden_name }}" + logging_alloy_service_monitor_enabled: false + logging_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" + when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled - name: Deploy Promtail to garden cluster (parallel migration window) kubernetes.core.helm: @@ -62,7 +51,7 @@ name: promtail namespace: "{{ gardener_logging_namespace }}" state: absent - when: gardener_logging_deploy_to_garden_cluster and gardener_logging_promtail_migrate_cleanup | default(false) | bool + when: gardener_logging_deploy_to_garden_cluster and not gardener_logging_promtail_enabled | bool - name: Loop over Gardener shooted seeds include_tasks: gardener-shooted-seed.yaml diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 b/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 deleted file mode 100644 index 41524f3d7..000000000 --- a/control-plane/roles/gardener-logging/templates/seed-alloy-config.alloy.j2 +++ /dev/null @@ -1,168 +0,0 @@ -logging { - level = "info" - format = "logfmt" -} - -discovery.kubernetes "pods" { - role = "pod" -} - -discovery.relabel "pods" { - targets = discovery.kubernetes.pods.targets - - rule { - source_labels = ["__meta_kubernetes_namespace"] - target_label = "namespace" - } - - rule { - source_labels = ["__meta_kubernetes_pod_name"] - target_label = "pod" - } - - rule { - source_labels = ["__meta_kubernetes_pod_container_name"] - target_label = "container" - } - - rule { - source_labels = ["__meta_kubernetes_pod_uid"] - target_label = "pod_uid" - } - - rule { - source_labels = ["__meta_kubernetes_pod_node_name"] - target_label = "node_name" - } - - // Derive app: app.kubernetes.io/name → app label → controller name (hash stripped) → pod name - rule { - source_labels = ["__meta_kubernetes_pod_controller_name"] - regex = "([0-9a-z-.]+?)(-[0-9a-f]{8,10})?" - target_label = "__tmp_controller_name" - } - - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name", "__meta_kubernetes_pod_label_app", "__tmp_controller_name", "__meta_kubernetes_pod_name"] - regex = "^;*([^;]+)(;.*)?$" - replacement = "$1" - target_label = "app" - } - - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_instance", "__meta_kubernetes_pod_label_instance"] - regex = "^;*([^;]+)(;.*)?$" - replacement = "$1" - target_label = "instance" - } - - rule { - source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_component", "__meta_kubernetes_pod_label_component"] - regex = "^;*([^;]+)(;.*)?$" - replacement = "$1" - target_label = "component" - } - - rule { - source_labels = ["namespace", "app"] - separator = "/" - target_label = "job" - } - - // Force cluster label to prevent pod kubernetes labels from overriding this value. - rule { - target_label = "cluster" - replacement = "{{ gardener_logging_alloy_cluster_label }}" - } -} - -loki.source.kubernetes "pods" { - targets = discovery.relabel.pods.output - forward_to = [loki.write.default.receiver] -} - -loki.source.kubernetes_events "events" { - job_name = "kubernetes-events" - log_format = "json" - forward_to = [loki.relabel.events.receiver] -} - -loki.relabel "events" { - forward_to = [loki.write.default.receiver] - - rule { - action = "labeldrop" - regex = "instance" - } - - rule { - target_label = "cluster" - replacement = "{{ gardener_logging_alloy_cluster_label }}" - } - - rule { - target_label = "job" - replacement = "monitoring/event-exporter" - } -} - -// default loki endpoints -loki.write "default" { -{% for endpoint in gardener_logging_alloy_loki_write_endpoints %} - endpoint { - url = "{{ endpoint.url }}" -{% if endpoint.remote_timeout is defined %} - remote_timeout = "{{ endpoint.remote_timeout }}" -{% endif %} -{% if endpoint.basic_auth is defined %} - basic_auth { - username = "{{ endpoint.basic_auth.username }}" - password = "{{ endpoint.basic_auth.password }}" - } -{% endif %} - } -{% endfor %} -} - -// write metrics to remote Prometheus endpoint -{% if gardener_logging_alloy_prometheus_write_endpoints %} -prometheus.exporter.self "alloy" {} - -prometheus.scrape "alloy_self" { - targets = prometheus.exporter.self.alloy.targets - forward_to = [prometheus.relabel.alloy_self.receiver] -} - -prometheus.relabel "alloy_self" { - forward_to = [prometheus.remote_write.default.receiver] - - rule { - target_label = "cluster" - replacement = "{{ gardener_logging_alloy_cluster_label }}" - } -} - -prometheus.remote_write "default" { -{% for endpoint in gardener_logging_alloy_prometheus_write_endpoints %} - endpoint { - url = "{{ endpoint.url }}" -{% if endpoint.remote_timeout is defined %} - remote_timeout = "{{ endpoint.remote_timeout }}" -{% endif %} -{% if endpoint.basic_auth is defined %} - basic_auth { - username = "{{ endpoint.basic_auth.username }}" - password = "{{ endpoint.basic_auth.password }}" - } -{% endif %} - } -{% endfor %} - wal { - // How often to truncate the WAL. Samples older than max_keepalive_time are dropped. - truncate_frequency = "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" - // Maximum time to keep undelivered samples in the WAL before dropping them. - // Increase if you expect control-plane outages longer than the default. - max_keepalive_time = "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" - } -} -{% endif %} diff --git a/control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml b/control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml deleted file mode 100644 index d2ce8b4ff..000000000 --- a/control-plane/roles/gardener-logging/templates/seed-alloy-values.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Source with all the defaults: https://raw.githubusercontent.com/grafana/alloy/main/operations/helm/charts/alloy/values.yaml -alloy: - configMap: - # -- Create a new ConfigMap for the config file. - create: true - # -- Content to assign to the new ConfigMap. This is passed into `tpl` allowing for templating from values. - content: |- - {{ gardener_logging_alloy_config | indent(6) }} - - # -- Port to listen for traffic on. - listenPort: {{ gardener_logging_alloy_port }} - - # -- Path to where Alloy stores its state (e.g. log positions file for loki.source.kubernetes). - # Backed by a hostPath volume so positions survive pod restarts on the same node. - storagePath: /var/lib/alloy/data - - mounts: - extra: - - name: alloy-data - mountPath: /var/lib/alloy/data - - # -- Enables sending Grafana Labs anonymous usage stats to help improve Grafana - # Alloy. - enableReporting: false - -controller: - volumes: - extra: - - name: alloy-data - hostPath: - path: /var/lib/alloy/data - type: DirectoryOrCreate - - # -- Tolerations to apply to Grafana Alloy pods. - tolerations: - - key: node-role.kubernetes.io/master - operator: Exists - effect: NoSchedule - - key: node-role.kubernetes.io/control-plane - operator: Exists - effect: NoSchedule From 460eb041583b8a41b2db13277039c8eef8f940c5 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Mon, 15 Jun 2026 16:37:10 +0200 Subject: [PATCH 21/24] chore: support overrides for all alloy vars --- .../roles/gardener-logging/README.md | 19 +++++++++++-------- .../roles/gardener-logging/defaults/main.yaml | 17 +++++++++++++++++ .../tasks/gardener-shooted-seed.yaml | 3 +++ .../roles/gardener-logging/tasks/main.yaml | 3 +++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index 9e65f14b0..d9cc992d3 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -51,18 +51,21 @@ You can look up all the default values of this role [here](defaults/main.yaml). | gardener_logging_garden_name | | `{{ gardener_defaults_garden_name }}` | Name of the garden cluster (used as `cluster=` label for the garden cluster Alloy deployment) | | gardener_logging_deploy_to_garden_cluster | | `true` | Deploy Alloy also into the garden cluster | | gardener_logging_shooted_seeds | | `[]` | List of shooted seeds to deploy Alloy into. Each entry: `{name: }` | -| gardener_logging_ingress_dns | | `loki.{{ metal_control_plane_ingress_dns }}` | DNS for the loki ingress (used in `gardener_logging_alloy_loki_write_endpoints` examples) | -| gardener_logging_ingress_loki_basic_auth_user | | `promtail` | Basic auth user for the external loki ingress | -| gardener_logging_ingress_loki_basic_auth_password | yes | | Basic auth password for the external loki ingress | +| gardener_logging_ingress_dns | | `loki.{{ metal_control_plane_ingress_dns }}` | DNS for the loki ingress (used in `gardener_logging_alloy_loki_write_endpoints` examples and in the deprecated Promtail template) | +| gardener_logging_ingress_loki_basic_auth_user | | `promtail` | Basic auth user for the external loki ingress (used in `gardener_logging_alloy_loki_write_endpoints` examples and in the deprecated Promtail template) | +| gardener_logging_ingress_loki_basic_auth_password | | | Basic auth password for the external loki ingress. Used by the deprecated Promtail template and for configuring `gardener_logging_alloy_loki_write_endpoints`. | ### Alloy -| Name | Mandatory | Default | Description | -| ------------------------------------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| gardener_logging_alloy_loki_write_endpoints | yes\* | `[]` | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | -| gardener_logging_alloy_config_raw | | | Full Alloy River config override for all targets. Bypasses all structured vars and the template. | +| Name | Mandatory | Default | Description | +| -------------------------------------------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| gardener_logging_alloy_loki_write_endpoints | yes\* | `[]` | List of Loki push endpoints. Required when `gardener_logging_alloy_enabled: true`. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_prometheus_write_endpoints | | `[]` | Prometheus remote_write endpoints for Alloy self-metrics. When empty, self-metrics are disabled. Each entry: `{url, remote_timeout?: duration, basic_auth?: {username, password}}` | +| gardener_logging_alloy_prometheus_wal_truncate_frequency | | `2h` | How often the WAL is compacted | +| gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped | +| gardener_logging_alloy_config_raw | | | Full Alloy River config override for all targets. Bypasses all structured vars and the template. | -For WAL settings, `logging_alloy_prometheus_write_endpoints`, and other Alloy options, set the `logging_alloy_*` variables from [logging-common](../logging-common/) directly in your inventory — they are used by the shared deployment task. +All `gardener_logging_alloy_*` variables are explicitly mapped to their `logging_alloy_*` counterparts when calling [logging-common](../logging-common/). This is necessary because the two roles use different variable prefixes — unlike the [logging role](../logging/) which shares the `logging_alloy_*` prefix with logging-common and therefore does not need an explicit mapping. ## Migration from Promtail diff --git a/control-plane/roles/gardener-logging/defaults/main.yaml b/control-plane/roles/gardener-logging/defaults/main.yaml index 4154310d4..525f5fb0c 100644 --- a/control-plane/roles/gardener-logging/defaults/main.yaml +++ b/control-plane/roles/gardener-logging/defaults/main.yaml @@ -23,6 +23,23 @@ gardener_logging_alloy_loki_write_endpoints: [] # username: "{{ gardener_logging_ingress_loki_basic_auth_user }}" # password: "{{ gardener_logging_ingress_loki_basic_auth_password }}" +# Prometheus remote_write endpoints for Alloy self-metrics (prometheus.exporter.self). +# Alloy scrapes its own /metrics and pushes them via remote_write. +# Each entry: {url, remote_timeout?: duration, basic_auth?: {username, password}} +gardener_logging_alloy_prometheus_write_endpoints: [] +# - url: "https://{{ monitoring_thanos_receive_ingress_dns }}/api/v1/receive" +# remote_timeout: 60s +# basic_auth: +# username: "{{ monitoring_thanos_receive_ingress_basic_auth_user }}" +# password: "{{ monitoring_thanos_receive_ingress_basic_auth_password }}" + +# WAL retention for prometheus.remote_write. +gardener_logging_alloy_prometheus_wal_truncate_frequency: "2h" +gardener_logging_alloy_prometheus_wal_max_keepalive_time: "8h" + +# Full Alloy River config override. When set, bypasses all structured vars and the template. +# gardener_logging_alloy_config_raw: | + # Promtail deployment (deprecated). Set true only if still on Promtail. # Requires gardener_logging_promtail_chart_version and gardener_logging_promtail_chart_repo when enabled. # When false, any existing Promtail release is automatically uninstalled. diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index ec0a27f4a..e82a82e62 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -18,6 +18,9 @@ logging_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" logging_alloy_service_monitor_enabled: false + logging_alloy_prometheus_write_endpoints: "{{ gardener_logging_alloy_prometheus_write_endpoints }}" + logging_alloy_prometheus_wal_truncate_frequency: "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" + logging_alloy_prometheus_wal_max_keepalive_time: "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" logging_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" logging_alloy_kubeconfig: "{{ _shoot_kubeconfig }}" when: gardener_logging_alloy_enabled diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 8657490f6..7dc819b65 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -29,6 +29,9 @@ logging_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" logging_alloy_cluster_label: "{{ gardener_logging_garden_name }}" logging_alloy_service_monitor_enabled: false + logging_alloy_prometheus_write_endpoints: "{{ gardener_logging_alloy_prometheus_write_endpoints }}" + logging_alloy_prometheus_wal_truncate_frequency: "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" + logging_alloy_prometheus_wal_max_keepalive_time: "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" logging_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled From 070bc623d0ec8e61524470eb1d77c364d4b27061 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Tue, 16 Jun 2026 14:40:47 +0200 Subject: [PATCH 22/24] chore: rename logging_ to logging_common_ for logging-common vars --- .../roles/gardener-logging/README.md | 2 +- .../tasks/gardener-shooted-seed.yaml | 22 +++++++++---------- .../roles/gardener-logging/tasks/main.yaml | 20 ++++++++--------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/control-plane/roles/gardener-logging/README.md b/control-plane/roles/gardener-logging/README.md index d9cc992d3..8d8b6f2e3 100644 --- a/control-plane/roles/gardener-logging/README.md +++ b/control-plane/roles/gardener-logging/README.md @@ -65,7 +65,7 @@ You can look up all the default values of this role [here](defaults/main.yaml). | gardener_logging_alloy_prometheus_wal_max_keepalive_time | | `8h` | Maximum time undelivered samples are kept in the WAL before being dropped | | gardener_logging_alloy_config_raw | | | Full Alloy River config override for all targets. Bypasses all structured vars and the template. | -All `gardener_logging_alloy_*` variables are explicitly mapped to their `logging_alloy_*` counterparts when calling [logging-common](../logging-common/). This is necessary because the two roles use different variable prefixes — unlike the [logging role](../logging/) which shares the `logging_alloy_*` prefix with logging-common and therefore does not need an explicit mapping. +All `gardener_logging_alloy_*` variables are explicitly mapped to their `logging_common_alloy_*` counterparts when calling [logging-common](../logging-common/). This is necessary because the two roles use different variable prefixes — the [logging role](../logging/) similarly maps its `logging_alloy_*` variables to `logging_common_alloy_*`. ## Migration from Promtail diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index e82a82e62..290241538 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -12,17 +12,17 @@ name: metal-roles/control-plane/roles/logging-common tasks_from: deploy-alloy.yaml vars: - logging_alloy_namespace: "{{ gardener_logging_namespace }}" - logging_alloy_chart_repo: "{{ gardener_logging_alloy_chart_repo }}" - logging_alloy_chart_version: "{{ gardener_logging_alloy_chart_version }}" - logging_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" - logging_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" - logging_alloy_service_monitor_enabled: false - logging_alloy_prometheus_write_endpoints: "{{ gardener_logging_alloy_prometheus_write_endpoints }}" - logging_alloy_prometheus_wal_truncate_frequency: "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" - logging_alloy_prometheus_wal_max_keepalive_time: "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" - logging_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" - logging_alloy_kubeconfig: "{{ _shoot_kubeconfig }}" + logging_common_alloy_namespace: "{{ gardener_logging_namespace }}" + logging_common_alloy_chart_repo: "{{ gardener_logging_alloy_chart_repo }}" + logging_common_alloy_chart_version: "{{ gardener_logging_alloy_chart_version }}" + logging_common_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" + logging_common_alloy_cluster_label: "{{ gardener_logging_shooted_seed.name }}" + logging_common_alloy_service_monitor_enabled: false + logging_common_alloy_prometheus_write_endpoints: "{{ gardener_logging_alloy_prometheus_write_endpoints }}" + logging_common_alloy_prometheus_wal_truncate_frequency: "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" + logging_common_alloy_prometheus_wal_max_keepalive_time: "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" + logging_common_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" + logging_common_alloy_kubeconfig: "{{ _shoot_kubeconfig }}" when: gardener_logging_alloy_enabled - name: Deploy Promtail (parallel migration window) diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 7dc819b65..9ed912900 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -23,16 +23,16 @@ name: metal-roles/control-plane/roles/logging-common tasks_from: deploy-alloy.yaml vars: - logging_alloy_namespace: "{{ gardener_logging_namespace }}" - logging_alloy_chart_repo: "{{ gardener_logging_alloy_chart_repo }}" - logging_alloy_chart_version: "{{ gardener_logging_alloy_chart_version }}" - logging_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" - logging_alloy_cluster_label: "{{ gardener_logging_garden_name }}" - logging_alloy_service_monitor_enabled: false - logging_alloy_prometheus_write_endpoints: "{{ gardener_logging_alloy_prometheus_write_endpoints }}" - logging_alloy_prometheus_wal_truncate_frequency: "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" - logging_alloy_prometheus_wal_max_keepalive_time: "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" - logging_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" + logging_common_alloy_namespace: "{{ gardener_logging_namespace }}" + logging_common_alloy_chart_repo: "{{ gardener_logging_alloy_chart_repo }}" + logging_common_alloy_chart_version: "{{ gardener_logging_alloy_chart_version }}" + logging_common_alloy_loki_write_endpoints: "{{ gardener_logging_alloy_loki_write_endpoints }}" + logging_common_alloy_cluster_label: "{{ gardener_logging_garden_name }}" + logging_common_alloy_service_monitor_enabled: false + logging_common_alloy_prometheus_write_endpoints: "{{ gardener_logging_alloy_prometheus_write_endpoints }}" + logging_common_alloy_prometheus_wal_truncate_frequency: "{{ gardener_logging_alloy_prometheus_wal_truncate_frequency }}" + logging_common_alloy_prometheus_wal_max_keepalive_time: "{{ gardener_logging_alloy_prometheus_wal_max_keepalive_time }}" + logging_common_alloy_config_raw: "{{ gardener_logging_alloy_config_raw | default('') }}" when: gardener_logging_deploy_to_garden_cluster and gardener_logging_alloy_enabled - name: Deploy Promtail to garden cluster (parallel migration window) From bbcbb74901e2446843694dbe1f68aa10af2acdc9 Mon Sep 17 00:00:00 2001 From: Matthias Hartmann Date: Thu, 25 Jun 2026 16:36:31 +0200 Subject: [PATCH 23/24] fix: migration docs link --- control-plane/roles/gardener-logging/tasks/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 84abf9bf7..9ec1ed878 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -14,7 +14,7 @@ ansible.builtin.fail: msg: >- DEPRECATION: gardener_logging_promtail_enabled is true. Promtail support is deprecated - and will be removed in a future release. Migrate to Alloy — see https://github.com/metal-stack/metal-roles/tree/main/control-plane/roles/gardener-logging#migration-from-promtail for instructions. + and will be removed in a future release. Migrate to Alloy — see https://github.com/metal-stack/metal-roles/tree/master/control-plane/roles/logging-common#migration-from-promtail for instructions. when: gardener_logging_promtail_enabled | bool ignore_errors: true From b93c37e811c6e5d1a3a6e62f2f75e2c73bb582a3 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Fri, 26 Jun 2026 13:00:30 +0200 Subject: [PATCH 24/24] Review. --- control-plane/roles/gardener-logging/meta/main.yaml | 1 - .../roles/gardener-logging/tasks/gardener-shooted-seed.yaml | 2 +- control-plane/roles/gardener-logging/tasks/main.yaml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/control-plane/roles/gardener-logging/meta/main.yaml b/control-plane/roles/gardener-logging/meta/main.yaml index 35f9fa39a..12f651d01 100644 --- a/control-plane/roles/gardener-logging/meta/main.yaml +++ b/control-plane/roles/gardener-logging/meta/main.yaml @@ -15,5 +15,4 @@ dependencies: - role: ansible-common - role: metal-roles/common/roles/defaults - role: metal-roles/control-plane/roles/defaults - - role: metal-roles/control-plane/roles/logging-common - role: metal-roles/control-plane/roles/gardener-defaults diff --git a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml index 3580cbbc4..e7778ce61 100644 --- a/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml +++ b/control-plane/roles/gardener-logging/tasks/gardener-shooted-seed.yaml @@ -8,7 +8,7 @@ _shoot_kubeconfig: "{{ virtual_garden_kubeconfig | string | shoot_admin_kubeconfig('garden', gardener_logging_shooted_seed.name) | from_yaml }}" - name: Deploy Alloy to seed - include_role: + ansible.builtin.include_role: name: metal-roles/control-plane/roles/logging-common tasks_from: deploy-alloy.yaml vars: diff --git a/control-plane/roles/gardener-logging/tasks/main.yaml b/control-plane/roles/gardener-logging/tasks/main.yaml index 9ec1ed878..e6cc8c0d5 100644 --- a/control-plane/roles/gardener-logging/tasks/main.yaml +++ b/control-plane/roles/gardener-logging/tasks/main.yaml @@ -19,7 +19,7 @@ ignore_errors: true - name: Deploy Alloy to garden cluster - include_role: + ansible.builtin.include_role: name: metal-roles/control-plane/roles/logging-common tasks_from: deploy-alloy.yaml vars: