オートスケールするインスタンスをzabbix監視対象として自動登録(削除)する


AWSのAuto-Scalingって、とってもクラウドらしくて聞こえもよく便利なんですが、
本気で導入しようとすると、結構細かい考慮が必要だったりします。

そのひとつが、監視対象をどうやってコントロールするか？　です。

本記事では、zabbixを使って監視を行う構成を例に、その構成方法を解説したいと思います。


# 1.そもそもAuto-Scalingとは？


ざっくりですが、Auto-Scaingのおさらい。

Auto-Scalingは、大きく3つのコンポーネントから構成されています。

| コンポーネント         | 設定内容（超ざっくり）     　　　　　　　   |
|:------------------- |:--------------------------------------|
|Launch-configuration | 起動するAMI-IDやインスタンスタイプを指定    |
|Auto-Scaling-group   | 起動するAZ指定、max、min台数の指定         |
|Auto-Scaling-policy  | スケール時に何台ずつ拡張・縮退するかの指定    |


この3つのコンポーネントを定義しておき、CloudwatchでAuto-Scaling-groupの負荷状態を監視します。
Cloudwathは、定義した敷居値を超えると"アラーム"が発生するので、これをトリガーにAuto-Scaling-actionを発動し、
定義した内容の通りにインスタンスを増減させる仕組みです。

![](https://s3-ap-northeast-1.amazonaws.com/kaojitario7/qiita-image/20141202/auto-scaling-image.png)


例えば、こんな具合です。
>CPU使用率90超が2分間続いたらアラームを発生させ、インスタンスを1台ずつ増やす。
>CPU使用率10%以下が10分間続いたらアラームを発生させ、1台ずつインスタンスを削除する。




ここで大切なのは、
Auto-Scalingで起動してくるインスタンスは、`ど新規インスタンスである`ということと、
Auto-Scalingで削除されるインスタンスは、`stopではなく、terminateされる`ということです。

つまり、予め準備しておいたインスタンスを起動・停止するわけではないので、
IPアドレスやホスト名は都度変わってしまうことになります（AWSの仕様）。







#2.監視対象へ自動登録・削除するには？


ではどうするか？
`スケールアウト時には自動で監視対象に登録する`処理と、
`スケールイン時は、自動で監視対象から削除する`処理が必要になってきます。

zabbixはAPIが公開されているので、上記処理をスクリプトで書いておき、
インスタンス起動時(削除時)に自動的に実行するように仕込んでおけばOKです。



#3.ホスト登録・削除スクリプト


zabbixには色々なAPIが存在していますが、ここでは純粋な[zabbix-API](https://www.zabbix.com/documentation/2.2/manual/api)を利用して登録します。
zabbix-APIは、JSONを引数にしてメソッドやパラメーターを指定してhttpリクエストを投げる方式で処理を行っていきます。
イチからJSONをパースする処理を書くのは面倒なので、今回はpythonを使用してスクリプトを書いています。

...さて、やっと本題です。
zabbixのホストに登録するには以下の値が必要になります。
>auth-id : 認証情報です
>hostgroup-id : どのホストグループに登録するか?
>template-id : どんな監視項目（Template）を割り当てるか?

なので、まずはこれらの値を取得してから登録処理を行います。




```python:create_host.py
#coding=utf-8
import json
import urllib2
import socket

# based url and required header
url = "http://xxx.xxx.xxx.xxx/zabbix/api_jsonrpc.php"
header = {"Content-Type":"application/json"}


############### まずは認証し、認証idを取得します(以降の処理で必要) ###############

data = json.dumps(
{
   "jsonrpc": "2.0",
   "method": "user.login",
   "params": {
   "user": "Admin",        # initial-param
   "password": "zabbix"    # initial-param
},
"id": 0
})

# create request object
request = urllib2.Request(url,data)
for key in header:
   request.add_header(key,header[key])

# auth and get authid
result = urllib2.urlopen(request)
response = json.loads(result.read())
result.close()
authid = response['result']


############### host登録に必要な値①：gropu-idを取得します ###############

data = json.dumps(
{
   "jsonrpc": "2.0",
   "method": "hostgroup.get",
   "params": {
      "output": "extend",
      "filter": {
         "name":[ "XXXXXXXXXXX" ]
      }
   },
    "auth": authid,
    "id": 1
})

# create request object
request = urllib2.Request(url,data)
for key in header:
   request.add_header(key,header[key])

# get hostgroup-id
result = urllib2.urlopen(request)
response = json.loads(result.read())
result.close()
for gid in response['result']:
   # set hostgrup-id 
   targetGroupid = gid['groupid']



############### host登録に必要な値②：template-idを取得します ###############

data = json.dumps(
{
   "jsonrpc": "2.0",
   "method": "template.get",
   "params": {
      "output": "extend",
      "filter": {
         "host":[ "XXXXXXXXXXX" ]
      }
   },
    "auth": authid,
    "id": 1
})

# create request object
request = urllib2.Request(url,data)
for key in header:
   request.add_header(key,header[key])

# get template-id
result = urllib2.urlopen(request)
response = json.loads(result.read())
result.close()
for gid in response['result']:
   # set template-id
   targetTemplateId = gid['templateid']



###############  取得した値を基にhost登録 ###############
 
data = json.dumps(
{
   "jsonrpc": "2.0",
   "method": "host.create",
   "params": {
      "host": "XXXXXXXXXXX",
      "interfaces": [{
         "type": 1,
         "main": 1,
         "useip": 1,
         "ip": "xxx.xxx.xxx.xxx",
         "dns": "",
         "port": "999"}],
      "groups": [{
         "groupid": targetGroupid
      }],
      "templates":[{
         "templateid": targetTemplateId
      }]
   },
    "auth": authid,
    "id": 1
})

# create request object
request = urllib2.Request(url,data)
for key in header:
   request.add_header(key,header[key])

# create host
result = urllib2.urlopen(request)
response = json.loads(result.read())
result.close()
for hid in response['result']['hostids']:
   print "create host successful. host-id is " + hid
   
```






また、削除時には以下の値が必要になります。
登録時と同じように、まずはhost-idを取得し、取得したhost-idをキーに削除処理を行います。
>host-id : 削除対象のホストキー


ソースコードは省略しますが、
登録時と同じように、`認証 → host-id取得 → delete`という流れになります。



#4.自動起動・削除の設定（Linux）

最後に、作ったスクリプトをサービスとして登録し、
インスタンスの起動時・シャットダウン時に自動実行するように設定してあげます。
これは普通にLinuxのService登録をしてあげるだけす。

`etc/init.d`配下にserviceコマンド用のスクリプトを配置し、`chkconfig --add xxxx`でサービス登録します。
サービス登録用のスクリプトはこんな感じです。
このスクリプトから、先ほどのホスト登録スクリプトや削除スクリプトを呼んでいます。

```Bash:service用コマンド
#!/bin/sh

# chkconfig: 2345 99 06
# description: regist or delete zabbix-managed-host-list.

servicename='manage-zabbix-host'
lock_file="/var/lock/subsys/${servicename}"

start() {
    python /path/01_regist_host.py
    touch ${lock_file}
    echo "manage-zabbix-host-list start:[OK]"
    return 0
}
stop() {
    python /path/02_delete_host.py
    rm -f ${lock_file}
    echo "manage-zabbix-host-list stop:[OK]"
    return 0
}


case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;; 
  *) break ;;
esac

```



#5.おわりに

基本的な考え方は上記の通りです。
なお、例外処理は色々あると思うのでそこは適宜追加してください。

また、今回は一連の流れをどかーっと書きましたが、認証処理などは共通化できると思うので、
そこは適宜作り替えてもらえればと思います。
※こうやってるよ、というのがあれば教えてください！！！


後日、これの動作確認くらいの情報は追記したいと考えています。。。