Mackerel で Gitlab のバージョンを監視する

## 動機

弊社では一部の開発基盤に Gitlab および Gitlab Runner を利用しています。

Gitlab CE は定期的にアップデートされており、今のところウチの Gitlab は最新版に追従している。実際、公開 Web に開いているので、重要なセキュリティアップデートはすぐにアップデートしたいところです。

しかし Gitlab 君、アップデートを確認するには Admin 権限を持ったユーザーで管理画面にアクセスしてバージョンを確認しなければなりません。

<img width="136" alt="2018-12-15_10h19_45.png" src="https://qiita-image-store.s3.amazonaws.com/0/83984/2c724d0b-a0c1-d87b-baec-ccb589cf9539.png">


だるい。


## Mackerelで監視しよう

それ、Mackerel で監視すればええやん！

## Gitlab のバージョンを監視

さっそく作っていきます。
完成品は=> https://gist.github.com/watahani/526a6cdb98dbc27a4c961cea4cbde2ba

### Check Plugin の追加

まずは Mackerel公式の Check Plugin を追加します。これにより、あらかじめ定義されたサービスごとのプラグイン(ex. postgress, apache, linux, ....) などを利用してサービスを監視したり、自分で定義したチェックプログラムをもとにメトリクスを追加したりできます。

追加するには

```sh
sudo apt-get install mackerel-check-plugins
```

おー、簡単。

### 自作 Plugin の作成

Mackerel では自作のコマンドを登録して、その実行結果を監視することができます。

- [チェック監視項目を追加する - Mackerel ヘルプ](https://mackerel.io/ja/docs/entry/custom-checks)


公式の例

```toml
[plugin.checks.ssh]
command = "ruby /path/to/check-ssh.rb"
notification_interval = 60
max_check_attempts = 1
check_interval = 5
timeout_seconds = 45
prevent_alert_auto_close = true
env = { HOST = "hostname", PORT = "port" }
action = { command = "ruby /path/to/notify_something.rb", env = { NOTIFY_API_KEY = "API_KEY" } }
memo = "This check monitor is ..."
```

`plugin.checks.xxxx` という名前で監視項目を追加すると、定期的に command を実行し、その実行結果（終了コード）をもとにノーマル(0)・アラート(1)・クリティカル(2) ・不明(3) のアラートを上げてくれます。

今回は Gitlab のバージョンを取得したうえで、そのバージョンをバージョンチェックサーバー（https://version.gitlab.com) に投げて、その結果返ってくる SVG ファイルを解析するプログラムを書くことにしました。

まずは Chrome のデバッグモードでネットワーク監視をして、SVG ファイルをどのようにしてとってきているかをチェックします。

![2018-11-17_16h34_54.png](https://qiita-image-store.s3.amazonaws.com/0/83984/039e9581-f537-f9c3-aab8-6ff2bcd567ad.png)

Builderscon で curl に変換できることを聞いていたので、 curl に変換します。

```sh
curl 'https://version.gitlab.com/check.svg?gitlab_info=eyJ2ZXJzaW9uIjoiMTEuMy40In0=' -H 'Referer: https://gitlab.example.com/' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36' --compressed
```

注意点として Header の Referer は必須らしく、なしでは情報が取れませんでした。ただし、URL自体はなんでもいいっぽいです。一応存在する URL にしておきます。

>何か統計とかとって分析してるだろうしね。頑張れ Gitlab。

```sh
curl 'https://version.gitlab.com/check.svg?gitlab_info=eyJ2ZXJzaW9uIjoiMTEuMy40In0=' -H 'Referer: https://gitlab.example.com/'
```

バージョンはどう見ても Base64 でエンコードされているので、デコードして中身を見てみると

```sh
> echo eyJ2ZXJzaW9uIjoiMTEuMy40In0= | base64 -d
{"version":"11.3.4"}
```

ということで、

- バージョンとってきて 
- json にして 
- base64 エンコードしたうえで
- HTTP GET リクエストを version.gitlab.com に投げて
- 返ってきた svg を結果を解析する

ということをすれよさそうです。

### コーディング

大したことないしシェル芸でいく（と思っていた時期が私にもありました

gitlab のバージョンは [stack overflow](https://stackoverflow.com/questions/21068773/how-to-check-the-version-of-gitlab) に `sudo gitlab-rake gitlab:env:info` で見れるって書いてあったけど、クッソ遅いので manifest ファイルから取得することにします。

```sh
cat /opt/gitlab/version-manifest.txt | grep gitlab-ce | awk '{printf "{\"version\":\"%s\"}", $2}'
{"version":"11.3.4"}
```

base64 で base64 に

```sh
cat /opt/gitlab/version-manifest.txt | grep gitlab-ce | awk '{printf "{\"version\":\"%s\"}", $2}' | base64
eyJ2ZXJzaW9uIjoiMTEuMy40In0K
```

curl で情報を取得

```sh
curl $(cat /opt/gitlab/version-manifest.txt | grep gitlab-ce | awk '{printf "{\"version\":\"%s\"}", $2}' | base64 | sed -e 's/^/https:\/\/version.gitlab.com\/check.svg?gitlab_info=/') -H 'Referer: https://gitlab.example.com/'

<!-- Generated automatically via `rake generate_svg` -->
<svg xmlns="http://www.w3.org/2000/svg" width="134" height="25">
  <g shape-rendering="crispEdges">
    <path fill="#D0021B" d="M0 0h134v25H0z"/>
  </g>
  <text x="67" y="16" fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="13">update asap</text>
</svg>


```

なんとなくワンライナーでやってるけど魔境になってきた。

後はこの SVG から、`<text ..>update asap</text> 部分を抜き出して判定すればよさそうなのですが、 Linux の sed には、最小一致での検索がなく抜き出し方法が分かりません。

検索すると、クラスを使って[sedで最短一致をする - Qiita](https://qiita.com/d-ebi/items/c26de9f0c464e5d456c6) という記事が見つかりました。

...いみがわからないよ

30分ほど sed と格闘したすえに python スクリプトにすることにします(笑)

無駄な時間をかけるより簡単なツールで作成したほうが良いです。

ということで完成品はこちらです。： https://gist.github.com/watahani/526a6cdb98dbc27a4c961cea4cbde2ba

ポイントとしては単に exit ではなく、 sys.exit で終了することぐらいです。

あとは設定ファイル `/etc/mackerel-agent/mackerel-agent.conf` に

```toml
[plugin.checks.gitlab_version]
command = "python /etc/mackerel-agent/check-gitlab-version.py"
env = { HOST = "https://gitlab.example.com" }
max_check_attempts = 3
check_interval = 240
memo = "gitlab ce version check"
```

を追加して、agent を再起動します。

```sh
sudo systemctl restart mackerel-agent.service
```

アップデートが必要だと Slack にメンションが飛びます

<img width="372" alt="2018-12-15_10h18_31.png" src="https://qiita-image-store.s3.amazonaws.com/0/83984/cfc239f8-0983-c813-8d72-cda9a6cb0703.png">


メンションが飛んだら、本文に含まれる https://about.gitlab.com/update/#ubuntu を参考に gitlab のアップデートをします。

それでは良いサーバー監視ライフを。