ラベル alertmanager の投稿を表示しています。 すべての投稿を表示
ラベル alertmanager の投稿を表示しています。 すべての投稿を表示

2024年7月8日月曜日

Alertmanager を nginx 配下で動作させる方法

Alertmanager を nginx 配下で動作させる方法

概要

前回 Prometheus を nginx 配下で動作させました
今回は Alertmanager を動作させます

環境

  • Ubuntu 22.04
  • Alertmanager 0.26.0
  • nginx 1.27.0

Alertmanager の起動オプション変更

--web.external-url=http://localhost:9093/alertmanager/ を追加します
localhost の部分は listen したいホスト名や IP に変更してください

nginx.conf

/prometheus で受けれるようにします

upstream alertmanager {
    server 192.168.1.100:9093;
}

server {
    listen 8080;
    server_name host01;

    location = /alertmanager {
        return 302 /alertmanager/;
    }

    location /alertmanager/ {
        proxy_set_header Host $host;
        proxy_pass http://alertmanager;
    }
}

prometheus.yml の変更

Prometheus から Alertmanager を使っている場合は alerting 部分を変更します
具体的に path_prefix を追加し targets を nginx のアドレスに変更します

alerting:
  alertmanagers:
    - scheme: http
      path_prefix: "/alertmanager/"
      static_configs:
        - targets:
            - "192.168.1.200:8080"

動作確認

:8080/alertmanager にアクセスして問題なく動作することを確認しましょう
またアラートも届くことを確認しましょう

また Alertmanager のエンドポイントにアクセスしているツールが他にある場合はそちらのエンドポイントも修正する必要があります

最後に

Prometheus も Alertmanager も同じ起動オプションを変更することで nginx 配下で動作させることができます

2023年12月13日水曜日

Alertmanager でノードが再起動したときにアラートを上げる方法とその効果

Alertmanager でノードが再起動したときにアラートを上げる方法とその効果

概要

ノードが再起動した際にアラートを上げるルールを紹介します
またその効果を紹介します

環境

  • Gitlab 16.3.6
  • Alertmanager 2.30.4

ルール

groups:
- name: Custom
  rules:
  - alert: NodeHasRebooted
    annotations:
      description: Node has rebooted
      summary: Node {{ (or $labels.node $labels.instance) }} has rebooted {{ $value }} seconds ago.
    expr: (time() - node_boot_time_seconds < 600) and (time() - 600 - (node_boot_time_seconds offset 10m) > 600)
    labels:
      severity: critical

効果

  • ノードが再起動した際に必ずアラートします
  • それにより再起動時に RESOLVED メールが届かないという問題が解消します
  • 再起動アラートは10分間なので10分間ノードが起動すると解消します
  • もし他のアラートが上がっている状態でノードが再起動しそのアラートがノード再起動時に解消していると RESOLVED メールが届かないことがあります
  • それをノード再起動アラートを必ず起こすことで解消メールが送信させることができます

最後に

600sec 以下だと time との差が短すぎてアラートが上がらないことがあるので少し長めに設定しましょう

参考サイト

2023年12月12日火曜日

omnibus-gitlab の AlertManager でログレベルをデバッグにする方法

omnibus-gitlab の AlertManager でログレベルをデバッグにする方法

概要

機能として提供されていないので直接ファイルを編集する必要があります

環境

  • Gitlab 16.3.6

runit ファイルの編集

--log.level=debug を追記します

  • docker cp root_gitlab_1:/opt/gitlab/service/alertmanager/run .
  • vim run
#!/bin/sh
exec 2>&1

umask 077
exec chpst -P -e /opt/gitlab/etc/alertmanager/env \
  -U gitlab-prometheus:gitlab-prometheus \
  -u gitlab-prometheus:gitlab-prometheus \
  /opt/gitlab/embedded/bin/alertmanager --web.listen-address=0.0.0.0:9093 --storage.path=/var/opt/gitlab/alertmanager/data --config.file=/var/opt/gitlab/alertmanager/alertmanager.yml --log.level=debug

あとは再度コンテナに配置し再起動します

  • docker cp run root_gitlab_1:/opt/gitlab/service/alertmanager/run
  • docker-compose exec gitlab gitlab-ctl restart alertmanager

再起動後ログが debug になっていることを確認します

  • tail -f /path/to/gitlab_mnt/log/alertmanager/current

prometheus の場合は

root_gitlab_1:/opt/gitlab/service/prometheus/run ファイルを同様に編集して配置してあげましょう

コンテナが再起動すると元に戻ってしまう

omnibus-gitlab はコンテナが再起動すると reconfigure が走ってしまいます
reconfigure が走ると内部で chef が実行されるため手動で変更した runit のファイルが上書きされログレベルの設定がなくなります

なのでそれの対処として直接 cookbooks を編集する方法も紹介します

sv-alertmanager-run.erb の編集

  • docker cp root_gitlab_1:/opt/gitlab/embedded/cookbooks/monitoring/templates/sv-alertmanager-run.erb .
  • vim sv-alertmanager-run.erb
#!/bin/sh
exec 2>&1
<%= render("mount_point_check.erb") %>
umask 077
exec chpst -P -e <%= @options[:env_dir] %> \
  -U <%= node['monitoring']['prometheus']['username'] %>:<%= node['monitoring']['prometheus']['group'] %> \
  -u <%= node['monitoring']['prometheus']['username'] %>:<%= node['monitoring']['prometheus']['group'] %> \
  /opt/gitlab/embedded/bin/alertmanager <%= @options[:flags] %> --log.level=debug
  • docker cp sv-alertmanager-run.erb root_gitlab_1:/opt/gitlab/embedded/cookbooks/monitoring/templates/sv-alertmanager-run.erb

これでコンテナが再作成 (down -> up) されない限り runit の実行ファイルに debug オプションが付与され続けます

Prometheus の場合は /opt/gitlab/embedded/cookbooks/monitoring/templates/sv-prometheus-run.erb を編集してください

最後に

omnibus-gitlab に MR して docker-compose から変更できるようにしてもいいのかもしれません

参考サイト

2023年12月1日金曜日

Promethues + Alertmanager + node_exporter の最小構成 docker compose

Promethues + Alertmanager + node_exporter の最小構成 docker compose

概要

メモがてら残しておきます

環境

  • Ubuntu 22.04
  • docker 24.0.4

compose.yml

  • vim compose.yml
services:
  prometheus:
    image: prom/prometheus
    container_name: prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
    ports:
      - 9090:9090
    restart: unless-stopped
    volumes:
      - ./prometheus:/etc/prometheus
      - prom_data:/prometheus
  node_exporter:
    image: quay.io/prometheus/node-exporter:latest
    container_name: node_exporter
    command:
      - '--path.rootfs=/host'
    network_mode: host
    pid: host
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'
  alertmanager:
    image: prom/alertmanager
    container_name: alertmanager
    volumes:
      - ./alertmanager:/etc/alertmanager
    command: "--config.file=/etc/alertmanager/alertmanager.yml"
    ports:
      - 9093:9093
    restart: unless-stopped
volumes:
  prom_data:

Promethues

  • vim promethues/prometheus.yml
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - alertmanager:9093

rule_files:
- '/etc/prometheus/*.rules'

scrape_configs:
- job_name: 'node_exporter'
  static_configs:
  - targets:
    - '192.168.100.1:9100'
  • vim prometheus/node.rules
groups:
- name: Node
  rules:
  - record: instance:node_cpus:count
    expr: count without(cpu, mode) (node_cpu{mode="idle"})

Alertmanager

  • vim alertmanager/alertmanager.yml
oute:
  receiver: default-receiver
  routes:
  - receiver: email
    matchers:
    - alertname=""
    group_wait: 30s
    group_interval: 5m
    repeat_interval: 4h
receivers:
- name: default-receiver
- name: email
  email_configs:
  - send_resolved: true
    to: your_mail_address@here
    from: your_from_address@here
    hello: localhost
    smarthost: smtp.ess.nifcloud.com:465
    auth_username: xxx
    auth_password: xxx
    headers:
      Subject: '{{ template "my.email.text.subject" . }}'
    text: '{{ template "my.email.text.body" . }}'
    require_tls: false
templates:
- /etc/alertmanager/*.tmpl
  • vim alertmanager/my_email_text.tmpl
{{ define "my.email.text.body" }}
{{ range .Alerts.Firing }}
{{ range .Annotations.SortedPairs }}
- {{ .Name }} = {{ .Value }}
{{ end }}
{{ end }}
{{ end }}

{{ define "my.email.text.subject" }}
{{ $alerts := "" }}
{{ $instance_id := (index .Alerts 0).Labels.instance_id }}
{{ range .Alerts }}
{{ $alerts = (printf "%v %v" $alerts .Labels.alertname) }}
{{ end }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] ({{ $instance_id }} ->{{ $alerts }})
{{ end }}

動作確認

  • docker compose up -d
  • docker compose ps
NAME                IMAGE                                     COMMAND                  SERVICE             CREATED             STATUS                    PORTS
alertmanager        prom/alertmanager                         "/bin/alertmanager -…"   alertmanager        13 minutes ago      Up 13 minutes             0.0.0.0:9093->9093/tcp, :::9093->9093/tcp
node_exporter       quay.io/prometheus/node-exporter:latest   "/bin/node_exporter …"   node_exporter       13 minutes ago      Up 13 minutes
prometheus          prom/prometheus                           "/bin/prometheus --c…"   prometheus          13 minutes ago      Up 13 minutes             0.0.0.0:9090->9090/tcp, :::9090->9090/tcp

最後に

あとは必要に応じて node.rules ファイルや prometheus.yml でメトリックスの取得先を変更すれば OK です

my_email_text.tmpl を変更すればテキストメールのテンプレートも変更できます

2023年11月30日木曜日

Gitlab の alertmanager でテンプレートファイルを使用する方法

Gitlab の alertmanager でテンプレートファイルを使用する方法

概要

前回メール本文のテンプレートをカスタマイズする方法を紹介しました
今回は直接テンプレートを docker-compose に記載せずテンプレートファイルを使ってカスタムしてみたいと思います

環境

  • Gitlab 16.3.6
  • Alertmanager 0.25.0

my_email_html.tmpl

まずは html メールのカスタマイズからしていきます
html テンプレートのベースはこれを使います
リンクの部分を削除するのとテンプレート名を「my.email.html」という名前に変更しています

  • vim my_email_html.tmpl
{{ define "email.default.subject" }}{{ template "__subject" . }}{{ end }}
{{ define "my.email.html" }}
<!--
Style and HTML derived from https://github.com/mailgun/transactional-email-templates


The MIT License (MIT)

Copyright (c) 2014 Mailgun

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{ template "__subject" . }}</title>
<style>
@media only screen and (max-width: 640px) {
  body {
    padding: 0 !important;
  }

  h1,
h2,
h3,
h4 {
    font-weight: 800 !important;
    margin: 20px 0 5px !important;
  }

  h1 {
    font-size: 22px !important;
  }

  h2 {
    font-size: 18px !important;
  }

  h3 {
    font-size: 16px !important;
  }

  .container {
    padding: 0 !important;
    width: 100% !important;
  }

  .content {
    padding: 0 !important;
  }

  .content-wrap {
    padding: 10px !important;
  }

  .invoice {
    width: 100% !important;
  }
}
</style>
</head>

<body itemscope itemtype="https://schema.org/EmailMessage" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 1.6em; background-color: #f6f6f6; width: 100%;">

<table class="body-wrap" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; background-color: #f6f6f6; width: 100%;" width="100%" bgcolor="#f6f6f6">
  <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
    <td style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top;" valign="top"></td>
    <td class="container" width="600" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block; max-width: 600px; margin: 0 auto; clear: both;" valign="top">
      <div class="content" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; margin: 0 auto; display: block; padding: 20px;">
        <table class="main" width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; background-color: #fff; border: 1px solid #e9e9e9; border-radius: 3px;" bgcolor="#fff">
          <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
            {{ if gt (len .Alerts.Firing) 0 }}
            <td class="alert alert-warning" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; vertical-align: top; font-size: 16px; color: #fff; font-weight: 500; padding: 20px; text-align: center; border-radius: 3px 3px 0 0; background-color: #E6522C;" valign="top" align="center" bgcolor="#E6522C">
              {{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
                {{ .Name }}={{ .Value }}
              {{ end }}
            </td>
            {{ else }}
            <td class="alert alert-good" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; vertical-align: top; font-size: 16px; color: #fff; font-weight: 500; padding: 20px; text-align: center; border-radius: 3px 3px 0 0; background-color: #68B90F;" valign="top" align="center" bgcolor="#68B90F">
              {{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
                {{ .Name }}={{ .Value }} 
              {{ end }}
            </td>
            {{ end }}
          </tr>
          <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
            <td class="content-wrap" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 30px;" valign="top">
              <table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  <td class="content-block" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 0 0 20px;" valign="top">
                  </td>
                </tr>
                {{ if gt (len .Alerts.Firing) 0 }}
                <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  <td class="content-block" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 0 0 20px;" valign="top">
                    <strong style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">[{{ .Alerts.Firing | len }}] Firing</strong>
                  </td>
                </tr>
                {{ end }}
                {{ range .Alerts.Firing }}
                <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  <td class="content-block" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 0 0 20px;" valign="top">
                    <strong style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">Labels</strong><br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                    {{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">{{ end }}
                    {{ if gt (len .Annotations) 0 }}<strong style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">Annotations</strong><br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">{{ end }}
                    {{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">{{ end }}
                  </td>
                </tr>
                {{ end }}

                {{ if gt (len .Alerts.Resolved) 0 }}
                  {{ if gt (len .Alerts.Firing) 0 }}
                <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  <td class="content-block" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 0 0 20px;" valign="top">
                    <br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                    <hr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                    <br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  </td>
                </tr>
                  {{ end }}
                <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  <td class="content-block" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 0 0 20px;" valign="top">
                    <strong style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">[{{ .Alerts.Resolved | len }}] Resolved</strong>
                  </td>
                </tr>
                {{ end }}
                {{ range .Alerts.Resolved }}
                <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                  <td class="content-block" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; padding: 0 0 20px;" valign="top">
                    <strong style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">Labels</strong><br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
                    {{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">{{ end }}
                    {{ if gt (len .Annotations) 0 }}<strong style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">Annotations</strong><br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">{{ end }}
                    {{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">{{ end }}
                  </td>
                </tr>
                {{ end }}
              </table>
            </td>
          </tr>
        </table>

        <div class="footer" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; padding: 20px;">
          <table width="100%" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
            <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px;">
            </tr>
          </table>
        </div></div>
    </td>
    <td style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top;" valign="top"></td>
  </tr>
</table>

</body>
</html>

{{ end }}

テンプレートファイルの配置

コンテナで動作する Gitlab が参照できるようにします
今回はホストマシンの一部のパスをマウントしてコンテナが参照できるようにしています

  • mkdir /path/to/opt/alertmanager/template
  • cp my_email_html.tmpl /path/to/opt/alertmanager/template

gitlab.rb

あとはテンプレートを参照するように alertmanager の設定を書き換えます

html の部分を定義した template を使うように変更します
また配置したテンプレートファイルを読み込むように alertmanager['templates'] を定義します

  • vim .gitlab.rb
alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      headers: {
        subject: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] ({{ $$alerts := "" }}{{ $$instance_id := (index .Alerts 0).Labels.instance_id }}{{ range .Alerts }}{{ $$alerts = (printf "%v %v" $$alerts .Labels.alertname) }}{{ end }}{{ $$instance_id }} ->{{ $$alerts }})'
      },
      html: '{{ template "my.email.html" . }}',
      send_resolved: true
    ]
  }
]
alertmanager['routes'] = [
  {
    receiver: 'email',
    group_wait: '30s',
    group_interval: '5m',
    repeat_interval: '4h',
    matchers: [ 'instance_id = hoge' ]
  }
]
alertmanager['templates'] = ['/var/opt/gitlab/alertmanager/template/*.tmpl']
# alertmanager['default_receiver'] = 'email'

動作確認

あとは redis などを停止すればアラートがきます

  • docker-compose exec gitlab gitlab-ctl stop redis

こんな感じで alertmanager へのリンクが削除されているのが確認できると思います

テキストメールのテンプレート化

次にテキストメールでもテンプレート化してみます
テンプレート化する内容は前回の本文とタイトルの内容をテンプレート化してみます

今回は1つのテンプレートファイルに2つの define を使って本文とタイトル用のテンプレートを定義します

  • vim my_email_text.tmpl
{{ define "my.email.text.body" }}
{{ range .Alerts.Firing }}
{{ range .Annotations.SortedPairs }}
- {{ .Name }} = {{ .Value }}
{{ end }}
{{ end }}
{{ end }}

{{ define "my.email.text.subject" }}
{{ $alerts := "" }}
{{ $instance_id := (index .Alerts 0).Labels.instance_id }}
{{ range .Alerts }}
{{ $alerts = (printf "%v %v" $alerts .Labels.alertname) }}
{{ end }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] ({{ $instance_id }} ->{{ $alerts }})
{{ end }}

コンテナが参照できるパスにコピーします

  • cp my_email_text.tmpl /path/to/opt/alertmanager/template

gitlab.rb でテキストメールの本文とタイトルにテンプレートを使うように変更します
html メールは無効化するのでブランクを設定します

  • vim gitlab.rb
alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      headers: {
        subject: '{{ template "my.email.text.subject" . }}'
      html: '',
      text: '{{ template "my.email.text.body" . }}',
      send_resolved: true
    ]
  }
]
alertmanager['routes'] = [
  {
    receiver: 'email',
    group_wait: '30s',
    group_interval: '5m',
    repeat_interval: '4h',
    matchers: [ 'instance_id = hoge' ]
  }
]
alertmanager['templates'] = ['/var/opt/gitlab/alertmanager/template/*.tmpl']
# alertmanager['default_receiver'] = 'email'

あとは再起動して再度 redis を停止すればテンプレートファイル化された内容でテキストメールのアラートが届くことが確認できると思います

最後に

docker-compose に直接テンプレート構文を使うこともできますがテンプレートの内容が長くなり一行では管理しづらい場合などはテンプレートファイル化してあげるといいのかなと思います

参考サイト

2023年11月29日水曜日

Gitlab の alertmanager でメール本文をカスタマイズする方法

Gitlab の alertmanager でメール本文をカスタマイズする方法

概要

前回 routes を設定する方法を紹介しました
今回はメールの本文やタイトルをカスタムする方法を紹介します

環境

  • Gitlab 16.3.6
  • Alertmanager 0.25.0

gitlab.rb

alertmanager['receivers'] の email_configs 内にある headers.subject, text を編集します
デフォルトの html メールは無効にするためブランクを設定しておきます

メールの内容には golang のテンプレート構文が使えます
注意するのは変数の定義で golang ではダラー1つで定義しますが今回は docker-compose 内でテンプレートを定義するためダラーを2つにする必要があります

使用できるテンプレート文字列やテンプレート構文に関しては各種公式を参考にしてください

alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      headers: {
        subject: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] ({{ $$alerts := "" }}{{ $$instance_id := (index .Alerts 0).Labels.instance_id }}{{ range .Alerts }}{{ $$alerts = (printf "%v %v" $$alerts .Labels.alertname) }}{{ end }}{{ $$instance_id }} ->{{ $$alerts }})'
      },
      html: '',
      text: '{{ range .Alerts.Firing }}{{ range .Annotations.SortedPairs }}  - {{ .Name }} = {{ .Value }}{{ "\n" }}{{ end }}{{ end }}',

      send_resolved: true
    ]
  }
]
alertmanager['routes'] = [
  {
    receiver: 'email',
    group_wait: '30s',
    group_interval: '5m',
    repeat_interval: '4h',
    matchers: [ 'instance_id = hoge' ]
  }
]

custom.rules

groups:
- name: Custom
  rules:
  - alert: RedisDown
    expr: avg_over_time(redis_up[5m]) * 100 < 50
    labels:
      severity: critical
      instance_id: hoge
    annotations:
      description: The Redis service is not responding for more than 50% of the time for 5 minutes.
      summary: The Redis service is not responding

動作確認

実際に redis をダウンさせるとカスタムした本文とタイトルでテキストメールが届くと思います

最後に

Gitlab 内の alertmanager のメール本文をカスタムする方法を紹介しました
golang の知識が必要になると alertmanager に送信させる json の内容も把握する必要があります

alertmanager に送信される json 情報はどこかで確認することができるのだろうか

参考サイト

2023年11月28日火曜日

amtool を使って alertmanager のテンプレートの確認をする方法

amtool を使って alertmanager のテンプレートの確認をする方法

概要

メールなどを送信する際に事前にテンプレートの内容がちゃんと展開できるか確認することができます

環境

  • Ubuntu 22.04
  • amtool 0.26.0

インストール

  • go install github.com/prometheus/alertmanager/cmd/amtool@latest

data.json

{
  "Status": "firing",
  "Alerts": [
    {
      "Status": "firing",
      "Labels": {
        "alertname": "alert1",
        "instance_id": "hoge"
      },
      "Annotations": {
        "runbook": "https://1.example.com/",
        "description": "desc1"
      }
    },
    {
      "Status": "firing",
      "Labels": {
        "alertname": "alert2",
        "instance_id": "hoge"
      },
      "Annotations": {
        "runbook": "https://2.example.com/",
        "description": "desc2"
      }
    }
  ],
  "GroupLabels": {
    "cluster": "cluster-A"
  }
}

試す

template.text に好きなテンプレート文字列を渡してテストできます
テスト用のデータは template.data で渡します

  • ~/go/bin/amtool template render --template.glob=/dev/null --template.text='{{ $alerts := "" }}{{ $instance_id := (index .Alerts 1).Labels.instance_id }}{{ range .Alerts }}{{ $alerts = (printf "%v %v" $alerts .Labels.alertname) }}{{ end }}{{ $instance_id }} ->{{ $alerts }}' --template.data=data.json

結果は以下のようになります

hoge -> alert1 alert2

最後に

サンプルは golang のテンプレートの記述に慣れていればもっと簡潔に書けるかなと思います

書いているときに一行だと大変なので複数行で記載してから一行にするといいかなと思います

参考サイト

2023年11月23日木曜日

Gitlab の Alertmanager で特定のアラートだけメールで送信する方法

Gitlab の Alertmanager で特定のアラートだけメールで送信する方法

概要

前回はとりあえず出たアラートをすべてメールで送信しました
今回は route 機能を使って特定のアラートだけをメールで飛ばすようにしてみます

環境

  • Gitlab 16.3.6
  • Alertmanager 0.25.0

gitlab.rb

alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      send_resolved: true
    ]
  }
]
alertmanager['routes'] = [
  {
    receiver: 'email',
    group_wait: '30s',
    group_interval: '5m',
    repeat_interval: '4h',
    matchers: [ 'alertname = RedisDown' ]
  }
]
# alertmanager['default_receiver'] = 'email'

ポイント

  • alertname は必ず付与されるのでこれを使ってアラートを特定します
    • マッチには正規表現が使える match_re などもあります
    • ルールをカスタムすれば severity などのラベルも付与できますが面倒なので alertname を使います
    • デフォルトの Gitlab のルールだと他に instance, job あたりがマッチルールに使えます
  • default_receiver を設定してしまうと route にマッチしない場合に defualt_receiver の設定を使ってしまうのでコメントする

参考サイト

2023年11月20日月曜日

Gitlab の Alertmanager を使ってメール通知する方法

Gitlab の Alertmanager を使ってメール通知する方法

概要

過去に slack 通知する方法を紹介しました
今回はメール通知する方法を紹介します

環境

  • Gitlab 16.3.6

gitlab.rb の設定

alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [
  {
    name: 'email',
    email_configs: [
      to: 'your_to@mail',
      from: 'your_from@mail',
      smarthost: 'smtp.ess.nifcloud.com:465',
      auth_username: 'your_access_key',
      auth_password: 'your_secret_key',
      require_tls: false,
      send_resolved: true
    ]
  }
]
alertmanager['default_receiver'] = 'email'

docker-compose.yml の設定

9093 を外部から見れるようにするには ports を設定してホスト側にもバインドしてあげましょう

ports:
  - 9093:9093

動作確認

  • docker-compose down
  • docker-compose up -d

で適用し

  • docker-compose exec gitlab gitlab-ctl stop redis

すると以下のようなメールが来ることが確認できると思います

Alertmanager でもアラートを確認できると思います

トラブルシューティング

2023-11-17_04:52:18.74549 ts=2023-11-17T04:52:18.745Z caller=notify.go:732 level=warn component=dispatcher receiver=email integration=email[0] msg="Notify attempt failed, will retry later" attempts=1 err="'require_tls' is true (default) but \"smtp.ess.nifcloud.com:465\" does not advertise the STARTTLS extension"

こんな感じのエラーが Alertmangaer から出ている場合は require_tls を false にしてください


An error has occurred while serving metrics:

collected metric pg_replication_is_replica label:{name:"server"  value:"/var/opt/gitlab/postgresql:5432"}  gauge:{value:0} has help "Indicates if this host is a slave" but should have "Indicates if the server is a replica"

という感じで postgres_exporter がエラーになっている場合は postgres_exporter を無効にすればとりあえずアラートは解消されます

postgres_exporter['enable'] = false

参考サイト

2021年11月15日月曜日

Alertmanager のサイレンス API を curl でコールするサンプル

Alertmanager のサイレンス API を curl でコールするサンプル

概要

過去に少し紹介しました
今回は一通りコールしてみたのでサンプルリクエストを紹介します

なお今回使用する API のバージョンは v2 になります

環境

  • Alertmanager 0.23.0

サイレンスとの登録

サンプルリクエスト

curl http://localhost:9093/api/v2/silences \
-H "content-type: application/json" \
-d @- << EOF
{
  "matchers": [
    {
      "name": "severity",
      "value": "critical",
      "isRegex": false
    }
  ],
  "startsAt": "`date -u +%Y-%m-%dT%H:%M:%S`",
  "endsAt": "`date -u -d '2 hours' +%Y-%m-%dT%H:%M:%S`",
  "createdBy": "api",
  "comment": "test silence"
}
EOF

サンプルレスポンス

{"silenceID":"274f9549-967a-4df6-8342-e82d57513237"}

date コマンドを使うことで実行した時間から 2 時間分のサイレンスを登録しています

2hours を 2days などにするとサイレンスの時間を延長することができます

サイレンス情報の取得

サンプルリクエスト

curl http://localhost:9093/api/v2/silence/274f9549-967a-4df6-8342-e82d57513237

サンプルレスポンス

{
  "id": "274f9549-967a-4df6-8342-e82d57513237",
  "status": {
    "state": "active"
  },
  "updatedAt": "2021-11-12T01:16:43.965Z",
  "comment": "test silence",
  "createdBy": "api",
  "endsAt": "2021-11-12T03:16:43.000Z",
  "matchers": [
    {
      "isEqual": true,
      "isRegex": false,
      "name": "severity",
      "value": "critical"
    }
  ],
  "startsAt": "2021-11-12T01:16:43.965Z"
}

単独のサイレンスを取得する場合は /silence/id を使います
すべてのサイレンス情報を取得したい場合は /silences を使います

curl http://localhost:9093/api/v2/silences

expire にする

サンプルリクエスト

curl -XDELETE http://localhost:9093/api/v2/silence/274f9549-967a-4df6-8342-e82d57513237

レスポンスボディはありません

最後に

ドキュメントが少ないので Github にある openapi のリファレンスを見るしかなさそうです

参考サイト

2021年10月14日木曜日

Omnibus Gitlab に付属している Prometheus と Alertmanager を使って Gitlab がダウンした際のアラートを受け取る方法

Omnibus Gitlab に付属している Prometheus と Alertmanager を使って Gitlab がダウンした際のアラートを受け取る方法

概要

Omnibus Gitlab には Prometheus と Alertmanager が付属しています
デフォルトだとどちらも有効にはなっていますが Alertmanager に関してはレシバーが登録されていないので通知はされません

今回は Slack レシバーを追加して Gitlab がダウンした際のアラートを受け取る設定を紹介します

環境

  • Omnibus Gitlab 14.2.3-ee

AlertManager の設定を gitlab.rb にする

gitlab.rb に以下を設定します

docker の場合は GITLAB_OMNIBUS_CONFIG に設定します

紹介しているのは Alertmanager と Prometheus に関する部分だけなので他の設定がある場合はその設定に追記してください

prometheus['listen_address'] = '0.0.0.0:9090'
alertmanager['listen_address'] = '0.0.0.0:9093'
alertmanager['receivers'] = [{'name' => 'slack','slack_configs' => ['api_url' => 'https://hooks.slack.com/services/xxx/xxx/xxx', 'channel' => '#private', 'text' => "{{ .CommonAnnotations.summary }}", 'send_resolved' => true]}]
alertmanager['default_receiver'] = 'slack'

ポイントは receivers と default_receiver の部分です

recievers には Slack レシーバを登録します
この設定は alertmanager.yml に記載する receivers の設定と同じなのでそのまま使えます

default_receiver は使用するレシーバを設定します
今回は「slack」レシーバを設定したのでそのレシーバを使うように default_receiver を設定します

あとは Gitlab を再起動しましょう

  • docker-compose down
  • docker-compose up -d

動作確認

gitlab プロセスを停止してみましょう

  • docker-compose exec gitlab gitlab-ctl stop puma
ok: down: puma: 0s, normally up

これでしばらくすると Prometheus と Alertmanager でアラートが発生しているのが確認できます

Slack にもちゃんと通知が来ることを確認しましょう

なぜか .CommonAnnotations.summary が埋め込まれていませんがこれは default-receiver が残ってしまっているせいです

default-receiver と別のグループとして slack レシーバを登録できればちゃんと表示されるはずです

最後に

メリットとして Omnibus Gitlab の監視設定をそのまま使えるので別途監視ツールを導入する必要はありません

デメリットとしては Prometheus 側のメトリックとアラートの設定は Omnibus Gitlab が用意しているものをそのまま使うので本来監視したくないルールがある場合に無視できません

ただ自分が試してみた感じだとこのあたりのドキュメントが少なくどこをどうやって設定するかは Omnibus gitlab の cookbooks などを眺める必要があるので少し面倒かもしれません

参考サイト

2021年10月13日水曜日

Alertmanager でアラート時にシェルスクリプトを実行する方法

Alertmanager でアラート時にシェルスクリプトを実行する方法

概要

AlertManager はアラートの情報を Slack やメールで通知します
アラート発生時に特定のコマンドを実行したい場合があります
その場合は webhook と組み合わせてコマンドを実行しましょう

Promethues や AlertManager の基本的な設定は前回の記事を確認してください

環境

  • Ubuntu 18.04
  • docker 20.10.7
  • Prometheus 2.30.3
  • Alertmanager 0.23.0
  • Node export 1.2.2
  • webhook 2.5.0

webhook の準備

インストールはこちらを参照してください
webhook を受け取った際に実行するシェルと AlertManager から webhook を受け取りペイロードを変数に格納する設定を準備します

  • vim /tmp/alertmanager_test.sh
#!/bin/bash
echo $STATUS > result_alertmanager_test_shell.log
echo $ANNOTATIONS >> result_alertmanager_test_shell.log
  • chmod +x /tmp/alertmanager_test.sh

AlertManager から送信されるペイロード情報はこちらを参考にしてください

  • vim /tmp/alertmanager_hooks.json
[
  {
    "id": "alertmanager_test",
    "execute-command": "/tmp/alertmanager_test.sh",
    "command-working-directory": "/tmp",
    "include-command-output-in-response": true,
    "pass-environment-to-command":
    [
      {
        "envname": "STATUS",
        "source": "payload",
        "name": "status"
      },
      {
        "envname": "ANNOTATIONS",
        "source": "payload",
        "name": "alerts.0.annotations.summary"
      }
    ]
  }
]

今回はアラート情報の status と annotations.summary を取得してシェルスクリプト側で使うような設定を記載しています

スクリプトと設定ファイルが準備できたら webhook を起動しましょう

  • webhook -hooks /tmp/alertmanager_hooks.json -verbose

alertmanager.yml

アラート発生時に webhook を投げるように alertmanager.yml に記載します
同時に Slack にも通知したい場合など複数のレシバーを定義する場合には routes で発生したアラートに応じてどのレシバーを使用するのかを定義する必要があります

continue: true にすることで次のレシバーも評価してくれます

  • vim alertmanager.yml
route:
  receiver: 'slack'
  routes:
    - receiver: 'slack'
      group_wait: 10s
      match_re:
        job: node1
      continue: true
    - receiver: 'webhook'
      group_wait: 10s
      match_re:
        job: node1
      continue: true

receivers:
  - name: 'slack'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/xxx/xxx/xxx'
        channel: '#private'
        text: "{{ .CommonAnnotations.summary }}"
        send_resolved: true
  - name: 'webhook'
    webhook_configs:
      - url: 'http://192.168.100.10:9000/hooks/alertmanager_test'

webhook_configs に起動した webhook の URL 情報などを記載します

動作確認

  • stress-ng -c 2 -l 100

などで CPU の使用率を上げてみましょう アラートが上がった際に webhook のログを確認して以下のようなログが出ていれば正常に webhook が投げられ届いていることになります

[webhook] 2021/10/08 13:32:10 executing /tmp/alertmanager_test.sh (/tmp/alertmanager_test.sh) with arguments ["/tmp/alertmanager_test.sh"] and environment [STATUS=firing ANNOTATIONS=Node1 cpu usage over 40%] using /tmp as cwd

ちなみに webhook_configs はデフォルトだと resolved 時にもリクエストして来ます

  • cat result_alertmanager_test_shell.log
resolved
Node1 cpu usage over 40%

コマンドを複数回実行されても問題ないようにするか send_resolved: false を設定してアラート発生時のみ webhook が投げられるようにしましょう

最後に

AlertManager でシェルスクリプトを実行する方法を紹介しました

webhook のプロセスを一つ管理する必要があるので面倒な感じもしますが AlertManager 自体にシェルをキックするような機能は現状ないのでこの方法がベストプラクティスになるかなと思います

Jenkins などがあればそこに webhook を投げてジョブを実行する感じでもいいのかもしれません

参考サイト

2021年10月11日月曜日

Prometheus で CPU 使用率が高くなったら Slack に通知するサンプル

Prometheus で CPU 使用率が高くなったら Slack に通知するサンプル

概要

Prometheus + Alertmanager + Node Export でノードの CPU 使用率を見て一定以上になったら Slack に通知するサンプルです

よく使う形式なので雛形として残しておきます

環境

  • Ubuntu 18.04
  • docker 20.10.7
  • Prometheus 2.30.3
  • Alertmanager 0.23.0
  • Node export 1.2.2

Node export 起動

  • docker run -d --net="host" --pid="host" -v "/:/host:ro,rslave" quay.io/prometheus/node-exporter:latest --path.rootfs=/host

:9100 ポートで起動します

Alertmanager 起動

  • mkdir data
  • vim alertmanager.yml
route:
  receiver: 'slack'
receivers:
- name: 'slack'
  slack_configs:
    - api_url: 'https://hooks.slack.com/services/xxx/xxx/xxx'
      channel: '#private'
      text: "{{ .CommonAnnotations.summary }}"
      send_resolved: true
  • docker run -d -p 9093:9093 --name alertmanager -v $(pwd):/alertmanager prom/alertmanager --config.file=/alertmanager/alertmanager.yml

:9093 ポートで起動します

Prometheus 起動

  • vim prometheus.yml
global:
  scrape_interval:     15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - 192.168.100.10:9093

rule_files:
  - 'alerts.rule'

scrape_configs:
  - job_name: 'node1'
    static_configs:
    - targets: ['192.168.100.10:9100']
  • vim alerts.rule
groups:
  - name: 'Node1 cpu monitoring'
    rules:
    - alert: 'Node1 cpu usage alert'
      expr: avg without(cpu) (rate(node_cpu_seconds_total{ job="node1",mode!="idle"}[1m])) >= 0.4
      for: 5s
      annotations:
        summary: 'Node1 cpu usage over 40%'
  • docker run -d -p 9090:9090 --name prometheus -v $(pwd):/prometheus-data prom/prometheus --config.file=/prometheus-data/prometheus.yml

:9090 ポートで起動します

動作確認

CPU に負荷テストします

  • stress-ng -c 2 -l 100

-c オプションは CPU のコア数です
各自の環境に合わせて変更してください

これで Slack に通知が来れば OK です

最後に

必要最低限な設定としてセットで覚えちゃってもいいかもしれません

stress-ng で 100% 負荷なのに Prometheus 的には 40% ほぼの負荷にしかなっていなかったのは CPU のモードごとに Prometheus が管理しているだと思います

stress-ng 実行後に top コマンドでコアごとの使用率を見ると 100% だったので top と同じ値を監視したい場合は監視ルールを少し変更する必要がありそうです