自宅環境監視への入門

どうも自分は監視についての知識や技術が乏しい、ということに気付いた。 そもそも何かを監視するという習慣を持っていないのがまず良くないと反省し、まずは自宅の環境を監視する練習から始めてみることにした。

監視とは役割ではなくスキルであり、チーム内の全員がある程度のレベルに至っておくべきです。

入門 監視 ―モダンなモニタリングのためのデザインパターン


環境

国内のクラウドサービス でサーバを借りることにしたので、そこに環境を整えていく。

Ansible from macOS

サーバ上で設定を編集して再起動したりとかをしたくない。すべて手元のMacBookからAnsibleで設定していくことにした。

とはいえmacOSからのAnsible実行にはいくつかの罠があるようだった。以下は自分がハマったもの。

$ brew install gnu-tar
$ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

Ansible Galaxy

Ansibleにも不慣れで、できるだけ自分でYAMLを書かずに先人の知恵を借りていきたい。 極力Galaxyを使ってやっていくことにした。

galaxy.ansible.com

$ ansible-galaxy collection install prometheus.prometheus
$ ansible-galaxy collection install victoriametrics.cluster
$ ansible-galaxy collection install grafana.grafana
$ ansible-galaxy collection install community.general
$ ansible-galaxy collection install community.grafana

VictoriaMetricsによる監視

監視の定番は Prometheus かもしれないが、ここでは VictoriaMetrics を使ってみることにした。

victoriametrics.com

Prometheusとの互換性を持ちつつも 収集・保存・クエリ・アラートなどがそれぞれ独立したコンポーネントとして提供されていて、必要に応じて組み合わせて使うことができる。そしてそれらはクラスタ化してスケールアウトする運用もしやすくなっているようだ。

個人で1台のサーバで使うぶんには Single-node version で十分で、これは vminsert, vmselect, vmstorage を一つにまとめたもの、になっているようだ。 結局Prometheusを立てるのとそんなに変わらないのかもしれないが、データの長期保存に向いているなどの特徴もあるようなので、メリットは十分にありそう。

Metricsの取得、収集

VictoriaMetrics自体は何かのmetricsを取れるわけではないので(自身のプロセスのmetricsは取れるが)、それこそPrometheusコミュニティで使われているようなExporterを使っていくことになる。

Node Exporter

まずは定番のNode Exporter。 Galaxyで提供されているのでroleを参照するだけ。

- name: Install node_exporter
  hosts: servers
  roles:
    - prometheus.prometheus.node_exporter

これでport :9100node_exporter が起動し、metricsを取得できるようになる。

VictoriaMetrics (vmsingle, vmagent)

それからVictoriaMetricsを立ち上げ、Node Exporterからのmetricsを収集していく。 Single-node version の vmsingle にも収集機能はあるようだが、ここでは別プロセスで vmagent を動かすようにする。

- name: Install monitoring tools
  hosts: monitoring
  become: true
  roles:
    - role: victoriametrics.cluster.vmsingle
    - role: victoriametrics.cluster.vmagent
      vars:
        vmagent_scrape_config:
          scrape_configs:
            - job_name: node-exporter
              static_configs:
                - targets:
                    - 127.0.0.1:9100

これでport :8428victoriametrics が起動し、また vmagent--remoteWrite.url=http://localhost:8428/api/v1/write というオプションで起動するので、上記 scrape_configs に従って node_exporter のmetricsを定期的に収集し、 victoriametrics に書き込むようになる。

:8428/vmui/ にアクセスするとGUIでデータを確認できる。

SwitchBotによる温度/湿度/CO2濃度の取得

実際に自宅の環境を監視していくためのデータ取得。

センサ機器は Raspberry Pi とか使って自作すれば良いのかもしれないけど、手軽に始めたかったのでとりあえず SwitchBot のものを購入してみた。

これ一つで温度、湿度、CO2濃度を取得できる。が、後で知ったのだけど単体ではスマホアプリからBluetoothで繋いでデータを閲覧するくらいで、外部からAPIでデータを取得するようなことはできない。別途ハブを購入して そちらからインターネット接続することで実現できる。

赤外線操作の機能もあるらしいが、うちのテレビやエアコンに試してみた限りでは上手く動かず…。CO2濃度にこだわらなければ Nature Remo を買った方がよかったかも?

Exporter

ともかくセンサとハブが繋がっていれば、Open Token, Secret Keyを使って SwitchBot API でデータを取得できるようになる。

このAPIを使うための便利ライブラリが既に公開されていて、

github.com

さらにそれを使ってPrometheus向けのmetricsを出力してくれるExporterまで公開されている!

github.com

有り難くこれを使わせていただくことで、1行もコードを書かずにSwitchBotのセンサデータをmetricsとして収集することができるようになる。

とはいえ、CO2の値の取得に対応されたのは数ヶ月前で、その変更は最新のリリースには含まれていないし Linuxで動作するバイナリも配布はされていないので、main branchからLinux向けのバイナリを自分でビルドする必要がある。

#!/bin/bash

REPO_URL="https://github.com/nasa9084/switchbot-exporter.git"
OUTPUT_BINARY="switchbot-exporter_linux_amd64"

git clone --depth=1 --branch=master "${REPO_URL}"
GOOS=linux GOARCH=amd64 go build -o "${OUTPUT_BINARY}"

あとはこのバイナリを送り、systemdで起動するように設定する。

- name: Send switchbot-exporter binary
  ansible.builtin.copy:
    src: "./switchbot-exporter_linux_amd64"
    dest: "{{ switchbot_exporter_binary_path }}"
    mode: "0755"

- name: Create switchbot-exporter envfile
  ansible.builtin.template:
    src: "./switchbot-exporter.env.j2"
    dest: "{{ switchbot_exporter_envfile_path }}"
    mode: "0644"
  register: envfile

- name: Create switchbot-exporter service
  ansible.builtin.template:
    src: "./switchbot-exporter.service.j2"
    dest: "/etc/systemd/system/switchbot-exporter.service"
    mode: "0644"
  register: service

- name: Reload systemd if envfile or service changed
  when: envfile.changed or service.changed
  ansible.builtin.systemd:
    daemon_reload: true
  notify: Restart switchbot-exporter service

- name: Start switchbot-exporter service
  ansible.builtin.systemd:
    name: switchbot-exporter
    state: started
    enabled: true
  when: not ansible_check_mode | bool

-web.listen-address で指定したアドレスでmetricsが取れるようになるので、 vmagentscrape_configs に追加してやれば良い。

    - role: victoriametrics.cluster.vmagent
      vars:
        vmagent_scrape_config:
          scrape_configs:
            - job_name: node-exporter
              static_configs:
                - targets:
                    - 127.0.0.1:9100
            - job_name: switchbot
              metrics_path: /metrics
              static_configs:
                - targets:
                    - B0E9FE56AF0D
                    - D53535302D51
              relabel_configs:
                - source_labels: [__address__]
                  target_label: __param_target
                - source_labels: [__param_target]
                  target_label: instance
                - target_label: __address__
                  replacement: {{ switchbot_exporter_address }}

設定例も README に書いてあり、こうすることで targets に指定したDevice IDを instance として取得できるようになるらしい。

Grafanaによる可視化

無事にSwitchBotで取得した温度・湿度・CO2濃度のデータをVictoriaMetricsで収集、保存ができるようになったので、次はそれを可視化していく。

ここは定番のGrafanaで。

- name: Install grafana
  hosts: monitoring
  become: true
  roles:
    - role: grafana.grafana.grafana
      vars:
        grafana_ini:
          server:
            ...
          security:
            admin_user: admin
            admin_password: ...

最低限の設定で起動してWebUIにアクセスできるようになるので、そこでDashboardを作成していく。 互換性のおかげで、ちゃんと Prometheus datasource としてVictoriaMetricsを指定できる。

温湿度計だけのものも併用して別の部屋に置いているので、2つの部屋の状態の違いなども一目でわかるようになっている。

Alertmanagerを使ったアラート通知

あとはアラート、通知送信。

CO2濃度の値が一定以上になったらアラートを出すようにする。 その対応は簡単に自動化できるものでもないので、とりあえずSlackの適当なChannelに通知を送信するようにする。

Grafanaにもアラート機能があるようだけど、ここでは Prometheusの Alertmanager を使うことにする。 (機能の違いなどをまだ把握していない)

Alertmanagerのインストール

- name: Install monitoring tools
  hosts: monitoring
  become: true
  roles:
    - role: prometheus.prometheus.alertmanager

デフォルトで :9093 で起動してWebUIにもアクセスできるようになる。

vmalert によるアラート

VictoriaMetricsではアラートを出すためのコンポーネントvmalert として独立したものが提供されている。

    - role: victoriametrics.cluster.vmalert
      vars:
        vic_vm_alert_rules:
          - name: SwitchBot
            rules:
              - alert: HighCO2LevelWarning
                expr: switchbot_meter_CO2 > 1200
                for: 5m
                labels:
                  severity: warning
                annotations:
                  summary: "CO2 level exceeds 1200 ppm (Warning)"
                  description: "{% raw %}The current CO2 level is {{ $value }} ppm.{% endraw %}"
              - alert: HighCO2LevelCritical
                expr: switchbot_meter_CO2 > 1800
                for: 5m
                labels:
                  severity: critical
                annotations:
                  summary: "CO2 level exceeds 1800 ppm (Critical)"
                  description: "{% raw %}The current CO2 level is {{ $value }} ppm.{% endraw %}"

これもデフォルトで --datasource.url として VictoriaMetrics (vmsingle) の http://localhost:8428 を、--notifier.url として Alertmanager の http://localhost:9093 を指定して起動するようになっているので、ruleだけ設定すればアラートが発報されるようになる。

とりあえず2つの閾値を設定し、 warningcritical の2種類の severity をlabelにつけている。 CO2濃度は 1,000 ppm 以下が基準となっているようだけど、閾値が低すぎると我が家の環境では結構頻繁にアラート出てしまうのでちょっと調整している…。

Alerting rulesなどは {{ $value }} といったテンプレートを使用でき、それをAnsibleから指定しようとするとJinja2のテンプレートと混ざらないよう気をつける必要があり、まぁまぁ面倒…。

Slackへの通知設定

改めてAlertmanagerの設定。

receivers:
  - name: noop
  - name: slack_critical
    slack_configs:
      - api_url: https://hooks.slack.com/services/...
        color: danger
        text: "{{ .CommonAnnotations.description }}"
        title: "{{ .CommonAnnotations.summary }}"
  - name: slack_warning
    slack_configs:
      - api_url: https://hooks.slack.com/services/...
        color: '{{ if eq .Status "firing" }}warning{{ else }}good{{ end }}'
        send_resolved: true
        text: '{{ if eq .Status "firing" }}{{ .CommonAnnotations.description }}{{ else }}Resolved.{{ end }}'
        title: "{{ .CommonAnnotations.summary }}"

inhibit_rules:
  - equal:
      - instance
    source_match:
      severity: critical
    target_match:
      severity: warning

route:
  receiver: noop
  routes:
    - match:
        severity: critical
      receiver: slack_critical
    - match:
        severity: warning
      receiver: slack_warning

送信先api_url は同一だが warning 用と critical 用で設定を変えて、 warning の方は解決したときにもその旨を通知するようにしている。

これで、常にGrafana Dashboardを眺めていなくても自宅の環境が異常になっていることを通知によって気付くことができるようになった。

まとめ

監視に馴染みがなく不慣れながらも、どうにか自宅の環境を監視する環境を整えることができた。

これからも継続して運用・改善していきつつ、他にも監視対象にできるものがあれば追加したりしていきたい。