Webページの更新チェッカー on Zabbix@Docker

#概要
※初Qiita投稿です。至らない点があれば、コメントにて教えていただけると幸いです。

ボカロ好きな私が、ニコニコ動画上で特定のタグに更新があった場合に、Zabbixからお知らせ出来ないかと思い、試行錯誤してみました。

参考にさせて頂いたページ「[Zabbixで更新チェッカーを作ってみる](https://qiita.com/kosuge/items/61a37451c65fb15623ae
)」

#現在の状態
Ubuntu上にDocker-composerを利用してZabbixサーバーを構築しています。

``` docker-compose.yml_抜粋
version: '2'

services:
  zabbix_db:
    container_name: zabbix_db
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    restart: always
    image: mysql:5.7
    volumes:
      - "./data/db:/var/lib/mysql"
    environment:

  zabbix_server:
    container_name: zabbix_server
    image: zabbix/zabbix-server-mysql:alpine-4.2-latest
    depends_on:
      - zabbix_db
    links:
      - zabbix_db
    volumes:
      - ./scripts:/usr/lib/zabbix/externalscripts
      - ./data/zabbix:/var/lib/zabbix
    ports:
      - "30051:10051"
      - "30050:10050"
    environment:
      DB_SERVER_HOST: zabbix_db

  zabbix_web:
    container_name: zabbix_web
    #image: zabbix/zabbix-web-nginx-mysql:alpine-4.2-latest
    build: 
      context: ./
      dockerfile: zabbix_web
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime:ro
    links:
      - zabbix_db
    ports:
      - "7998:80"
    environment:
      DB_SERVER_HOST: zabbix_db
      ZBX_SERVER_HOST: zabbix_server
      PHP_TZ: Asia/Tokyo
    depends_on:
      - zabbix_db
      - zabbix_server
    
  zabbix_agent:
    container_name: zabbix_agent
    image: zabbix/zabbix-agent:alpine-latest
    restart: always
    environment:
      ZBX_SERVER_HOST: zabbix_server
      ZBX_ENABLEREMOTECOMMANDS: 1
    links:
      - zabbix_server
    depends_on:
      - zabbix_server

```

# 参考にしたページを元につくってみる

##スクリプトの用意

``` ./script/urlcheck.sh
#!/bin/bash

URL="$1"
# URLに含まれる文字でファイル名に使えない"/"は置換する
URL_PATH=${URL//\//-}
STORE_PATH="/tmp/urlcheck/url_${URL_PATH}"

mkdir -p /tmp/urlcheck

if [ -e "${STORE_PATH}_after" ]; then
  # afterが存在すればbeforeとしてコピー
  cp -f "${STORE_PATH}_after" "${STORE_PATH}_before"
else
  # afterが存在しなければbeforeとしてコンテンツ取得
  curl -s -g "$URL" -o "${STORE_PATH}_before"
fi

# コンテンツ取得
curl -s -g "$URL" -o "${STORE_PATH}_after"

# 差分確認
diff "${STORE_PATH}_before" "${STORE_PATH}_after" >/dev/null
echo $?
```

元のページのままだと取得時にエラーが出ることがあったため、一部改変しています。
実行権限を付与し、docker-composeに下記を追記します。

```docker-compose.yml
  zabbix_server:
    volumes:
      - ./scripts:/usr/lib/zabbix/externalscripts
```
docker内に入り、スクリプトをテストします。

```
# /usr/lib/zabbix/externalscripts/urlcheck.sh http://google.com/
0
```

正常終了なら、０が戻ります。
ページが更新された場合、１が戻る仕様になっているので、これをZabbixで監視します。

##Zabbixにアイテム登録

![ss1-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8424/5cea5fe2-4112-65e6-8a07-95226ee5f68a.png)


上記の通り登録します。

 - 名前：URL定期更新テスト
 - タイプ：外部チェック
 - キー：urlcheck.sh["https://google.com/"]
 - 更新間隔：60m

##Zabbixにアクション登録

![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8424/e4f900e3-6d55-c99d-fd32-00a1309256fb.png)

上記通り登録します。

 - 名前：定期更新アクション
 - 条件式：{localhost:urlcheck.sh["https://google.com/"].last()}<>0
 - 説明：{ACTION.NAME} is changed.

# 問題点：単一ページでは動くが、RSSなどの動的ページでは想定通りに動作しない

##原因を調べてみる

https://www.nicovideo.jp/tag/%E9%9A%A0%E3%82%8C%E3%81%9F%E3%83%9C%E3%82%AB%E3%83%AD%E5%90%8D%E6%9B%B2?rss=2.0

上記のニコニコ動画のRSSをよーく眺めてみると、/rss/channel/lastBuildDateのところに更新日時が入ってしまっている。
この文字列が毎回変更されるため、取得しに行くたびに変更した扱いになるため、想定の動作しませんでした。

なので、XMLを簡易解析し、/rss/channel/item[n]/titleをテキスト出力し、それを比較することにします。

##Dockerfileの作成とdocker-composeの修正
現在の設定ではZabbixServer内でXMLの解析が行えないため、Dockerイメージを作り直すことにしました。

```Dockerfile
FROM zabbix/zabbix-server-mysql:alpine-4.2-latest

RUN apk add --update curl xmlstarlet && \
    rm -rf /var/cache/apk/*
```

- curl
- xmlstarlet

を有効にしたDockerfileを作成し、docker-composeに追記します。

``` cocker-compose.yml
  zabbix_server:
    #image: zabbix/zabbix-server-mysql:alpine-4.2-latest
    build:
      context: ./zabbix_server
      dockerfile: Dockerfile
```

## シェルスクリプトの追加

``` rsscheck.sh
#!/bin/bash

URL="$1"
# URLに含まれる文字でファイル名に使えない"/"は置換する
URL_PATH=${URL//\//-}
STORE_PATH="/tmp/urlcheck/rss_${URL_PATH}"

mkdir -p /tmp/urlcheck

if [ -e "${STORE_PATH}_after" ]; then
  # afterが存在すればbeforeとしてコピー
  cp -f "${STORE_PATH}_after" "${STORE_PATH}_before"
else
  # afterが存在しなければbeforeとしてコンテンツ取得
  curl -s -g "$URL" | xmlstarlet sel -t -m "/rss/channel/item" -v "title " -o " / " -v "pubDate" -n > "${STORE_PATH}_before"
fi

# コンテンツ取得
curl -s -g "$URL" | xmlstarlet sel -t -m "/rss/channel/item" -v "title " -o " / " -v "pubDate" -n > "${STORE_PATH}_after"

# 差分確認
diff "${STORE_PATH}_before" "${STORE_PATH}_after" >/dev/null
echo $?
```

RSS用のシェルスクリプトを作成し、同様に実行権限を付与し、テストします。
やってることは単純で、CURLで取得したデータをXmlstarletを通して、下記の通り整形しています。

- タイトル / 動画の更新日
- タイトル / 動画の更新日
- ・・・

```
# /usr/lib/zabbix/externalscripts/rsscheck.sh https://www.nicovideo.jp/tag/%E9%9A%A0%E3%82%8C%E3%81%9F%E3%83%9C%E3%82%AB%E3%83%AD%E5%90%8D%E6%9B%B2?rss=2.0
0
```

あとは、既に記載したように、Zabbixにアイテム登録すれば動きます。
お盆休みで田舎に帰って暇だったので、思いつきで作ってみました。

# 2019/08/14 追記
RSSチェックが、時々更新判定を失敗しているので、よくよく確認してたところ、オプションフラグをつけないと毎回最新順にソートされているとは限らないようでした。
現在は下記の通りの設定でうごいています。

- キー：rsscheck.sh["https://www.nicovideo.jp/tag/%E6%A9%9F%E5%8B%95%E6%AD%8C%E5%A7%AB%E3%83%B4%E3%82%A9%E3%83%BC%E3%82%AB%E3%83%AA%E3%82%AA%E3%83%B3?sort=f&order=d&rss=2.0"]