Monitoring- und Alerting-Lösungen wie Prometheus informieren in erster Linie technische Teams über Probleme in Anwendungen und Infrastrukturen. In vielen Szenarien müssen jedoch auch Fachbereiche zeitnah auf Störungen reagieren können. Dieser Beitrag zeigt anhand eines Praxisbeispiels, wie Prometheus-Alerts über den Alertmanager aggregiert und direkt in eine Fachanwendung integriert werden können.

Ausgangslage: FTP-Störungen in einer skalierenden Microservice-Architektur

Im betrachteten Szenario übernimmt ein Microservice die Übertragung von Dateien an ein externes Drittsystem. Ein Microservice dient als Datei-Mover, um Daten von einem S3-Bucket via FTP an ein Drittsystem zu übertragen. Um Lastspitzen abzufangen, skaliert der Service horizontal auf bis zu zehn Instanzen.

In der Praxis erwies sich die FTP-Verbindung jedoch als störungsanfällig. Die Ursachen reichen von kurzzeitigen Netzwerkschwankungen bis hin zu vollständigen Ausfällen des Drittsystems.

Um das Operations-Team bzw. die Entwickler über anhaltende Probleme zu informieren, existierte ein Prometheus-Alert: Besteht die Verbindungsstörung länger als zehn Minuten, wird automatisch eine Benachrichtigung in Microsoft Teams abgesetzt. Diese dient primär der technischen Analyse.

Warum technische Prometheus-Alerts nicht ausreichen

Die technische Alarmierung allein reichte nicht aus. Auch der Fachbereich musste zeitnah über Störungen informiert werden, um deren Auswirkungen bewerten und gegebenenfalls Gegenmaßnahmen einleiten zu können.

Bei länger anhaltenden Störungen muss der Fachbereich die Verarbeitung selbstständig über ein Feature-Toggle stoppen können. Technische Alerts sollen deshalb direkt in der Fachanwendung sichtbar sein und dort konkrete Handlungen ermöglichen.

Außerdem soll nicht bei jeder Netzwerkschwankung eine Benachrichtigung erzeugt werden, sondern nur wenn der Fehler instanzübergreifend über einen längeren Zeitraum auftritt.

Umsetzung: Prometheus-Alerting für fachliche Benachrichtigungen

Die Grundlage bildet ein Prometheus-Alert in dem Microservice, der für den Dateitransfer zum FTP-Server zuständig ist.

Ausgelöst wird der Alert unter folgenden Bedingungen: Der Alarm schlägt an, wenn über einen Zeitraum von zehn Minuten hinweg FTP-Befehle fehlschlagen und in diesem Zeitfenster keine einzige erfolgreiche FTP-Aktion registriert wird. So werden kurzzeitige kurzzeitige Netzwerkstörungen ignoriert und nur echte, anhaltende Blockaden gemeldet.

Da unsere Container-Umgebung stark skaliert, setzen wir auf den Amazon Managed Service for Prometheus. Der serverlose Dienst ermöglicht die zuverlässige Überwachung unserer dynamisch skalierenden Infrastruktur – auch bei hoher Last und vielen Instanzen.

Weitere Informationen zur Definition von Alerting Rules finden sich in der offiziellen Prometheus-Dokumentation. Amazon Managed Service for Prometheus stellt einen vollständig verwalteten Alertmanager bereit, über den Alerts gruppiert und an verschiedene Zielsysteme weitergeleitet werden können.

Alert-Aggregation mit dem Prometheus Alertmanager

Ein zentraler Bestandteil dieser Architektur ist der Prometheus Alertmanager. Er empfängt die von Prometheus erzeugten Alerts, fasst sie zusammen und leitet sie an die definierten Zielsysteme weiter.

 

Da unser File Mover bei hoher Last auf bis zu zehn Instanzen hochskaliert, würden ohne den Alertmanager im Fehlerfall zehn separate Alarme ausgelöst werden. Der Alertmanager erkennt diese Redundanz, fasst die Meldungen der einzelnen Instanzen zu einem einzigen logischen Alarm zusammen und leitet diesen gebündelt weiter – bisher primär an Microsoft Teams und nun zusätzlich an unsere Fachanwendung.

Alert im Microservice

changes(aws_lambda_successful_ftp_command_exists_sum[5m]) == 0 and max_over_time(aws_lambda_failed_ftp_command_exists_sum[10m]) > 0

Integration der Alerts in AWS SNS und SQS

Prometheus und der Alertmanager wurden in Terraform definiert, um die Ressourcen auf den verschiedenen Umgebungen identisch zu erstellen.

Die Routing-Logik des Alertmanagers wird ebenfalls über Terraform abgebildet. Dafür werden zunächst die möglichen Empfänger sowie die Regeln für die Weiterleitung der Alerts definiert.

Da es verschiedene Empfänger des Alerts vom Alertmanager gibt, wurden Variablen definiert:

variable "alert_routes" {
  type = list(object({
    receiver              = string
    matchers              = list(string)
    active_time_intervals = list(string)
  }))
  description = "Describes an AlertManager route. The receiver must match a key in the alert_receivers map. The mute_time_intervals is ignored when empty."
}

variable "alert_receivers" {
  type = map(object({
    hook_parameter = optional(string)
    send_resolved  = bool
  }))
  description = "Describes an AlertManager receiver. The map key is used as receiver name."
}

Anschließend folgt die Definition der Alert-Routen. Dafür werden die Variablen werden wie folgt befüllt:

 alert_receivers = {
    teams_technical = {
      hook_parameter = "/amp/alertmanager/ms/teams/support/hook/url"
      send_resolved  = false
    }
    sqs_receiver = {
      send_resolved = false
    }
 } 
 alert_routes = [
    {
      receiver : "sqs_receiver"
      matchers : [
        "architecture = application",
        "error_type = business"
      ]
    },
    {
      receiver : "teams_technical"
      matchers : [
        "architecture = application",
        "error_type = technical"
      ]
    }
 ]   

Über das Label error_type wird zwischen rein technischen Benachrichtigungen und fachlich relevanten Störungen unterschieden. Dadurch können Alerts gezielt an unterschiedliche Empfängergruppen weitergeleitet werden.

Bereitstellung der SNS-Topics

Für jeden definierten Empfänger wird anschließend ein eigenes SNS-Topic erzeugt:

module "sns_topic" {
  for_each = var.alert_receivers
  
  source                    = "./modules/sns_topic"
  name                      = "${module.label.id}-${each.key}-notification"
  topic_policy_statements   = {
        amp = {
          actions = [
            "sns:Publish",
            "sns:GetTopicAttributes"
          ]
        }
        effect = "Allow"

        principals = [{
          type        = "Service"
          identifiers = ["aps.amazonaws.com"]
        }]  
  }
}

Das SNS-Topic des SQS-Empfängers wird anschließend über eine Subscription mit der Ziel-Queue verbunden:

resource "aws_sns_topic_subscription" "aws_sqs_notification_input_queue_name_sns_subscription" {
  for_each = {
    for k, v in var.alert_receivers : k => v
    if startswith(k, "sqs_receiver")
  }
  topic_arn = module.sns_topic[each.key].aws_sns_topic_arn
  protocol  = "sqs"
  endpoint  = module.sqs.aws_sqs_topic_arn
}

Nachrichtentemplates für Fachanwendungen

Damit die Fachanwendung die Alertinformationen verarbeiten kann, wird die Nachrichtenstruktur über ein Template standardisiert.

{{ define "sns.sqs.notification.message" }}{ "messageHeader": "{{ .CommonLabels.MessageHeader }}", "type": "ERROR", "title": "{{ .CommonLabels.Title }}", "message": "{{ .CommonLabels.Message }}" }{{ end }}

Abschließend wird der Prometheus Alertmanager definiert:

resource "aws_prometheus_alert_manager_definition" "alerts_definition" {
  workspace_id = aws_prometheus_workspace.this.id
  definition = templatefile("${path.module}/alertmanager_config.yml",
    {
      sns_subject_template = file("${path.module}/templates/sns_subject_template.yml"),
      sns_teams_template   = file("${path.module}/templates/sns_ms_teams_template.yml"),
      sns_sqs_template     = file("${path.module}/templates/sns_sqs_notification_template.yml"),
      topics               = module.sns_topic,
      alert_routes         = var.alert_routes,
      alert_receivers      = var.alert_receivers
    }
  )
}

Hier die alertmanager_config.yml:

---
template_files:
  sns_subject_template: |
    ${sns_subject_template}
  sns_teams_template: |
    ${sns_teams_template}
  sns_sqs_template: |
    ${sns_sqs_template}:

alertmanager_config: |
  global:
  templates:
    - 'sns_subject_template'
    - 'sns_teams_template'
    - 'sns_sqs_template'
  route:
    group_by: ['alertname', 'severity', 'cluster', 'one_shelf_cloud_environment', 'namespace', 'exported_namespace', 'app_kubernetes_io_instance']
    receiver: default_receiver
    routes:
%{ for alert_route in alert_routes }
      - receiver: ${alert_route.receiver}
        matchers:
  %{ for matcher in alert_route.matchers }
         - ${matcher}
  %{ endfor ~}
  %{ if length(alert_route.active_time_intervals) > 0 }
        active_time_intervals:
    %{ for active_time_interval in alert_route.active_time_intervals }
          - ${active_time_interval}
    %{ endfor ~}
  %{ endif }
%{ endfor ~}
  receivers:
%{ for alert_receiver_key, alert_receiver in alert_receivers }
    - name: ${alert_receiver_key}
      sns_configs:
        - topic_arn: ${topics[alert_receiver_key].aws_sns_topic_arn}
          send_resolved: ${alert_receiver.send_resolved}
          message: |
%{ if startswith(alert_receiver_key, "sqs_receiver") ~}
            {{ template "sns.sqs.notification.message" . }}
%{ else ~}
            {{ template "sns.ms.teams.subject" . }}
%{ endif ~}
          sigv4:
            region: eu-central-1
          attributes:
            key: severity
            value: info
%{ endfor ~}

Für die Microsoft-Teams-Benachrichtigungen müssen noch die Hook-Parameter aus dem SSM-Parameter-Store gesetzt werden und die SNS-Topic-Subscription definiert werden. Als Endpoint dient eine AWS-Lambda-Funktion, die die eingehenden Nachrichten an den entsprechenden Microsoft-Teams-Kanal weiterleitet. Wie diese Ressourcen genau definiert sind, wird in diesen Fall außer acht gelassen.

Fazit und Ausblick

Die direkte Integration von Prometheus-Alerts in die Fachanwendung schließt die Lücke zwischen technischem Monitoring und operativen Geschäftsprozessen. Fachbereiche erhalten relevante Informationen direkt dort, wo sie arbeiten, und können ohne Umwege auf Störungen reagieren. Stattdessen erhält das Business nun in Echtzeit handlungsrelevante Informationen direkt in seiner gewohnten Arbeitsumgebung.

Durch die intelligente Aggregation im Alertmanager verhindern wir ein Überfluten der Anwendung mit redundanten Meldungen, selbst wenn unser Microservice voll skaliert ist. Der Fachbereich wird befähigt, über die integrierten Feature-Toggles autonom und ohne Verzögerung auf anhaltende Drittanbieter-Störungen zu reagieren. Das entlastet nicht nur die Bereitschaftsdienste der Softwareentwickler, sondern minimiert auch Ausfallzeiten und inkonsistente Datenzustände im Gesamtsystem.

Alle Beiträge von Mark Hansen

Schreibe einen Kommentar