障害復旧にSiriOpsは役立つか？

初めましての方もご存知の方もこんにちは。
リブセンスで **アルコール駆動開発** を提唱し続けているインフラエンジニア、@Etsです。

さっきまでビールを3パイントほど飲んでいたところからこの記事の執筆をスタートしています。
現在の時刻は2016/12/07 02:16。アルコールの力で生産効率が倍増！！
・・・を通り越して **超眠い** です。頑張ります。


なお、これは[『Livesense Advent Calendar 2016』](http://qiita.com/advent-calendar/2016/livesense_1)の8日目の記事です。
昨日は@xorphitusさんの[『Lispってどう書くの?』](http://qiita.com/xorphitus/items/64f739e2594caba6eb4d)でした。

ちなみに弊社は3本同時アドベントカレンダーという凶行を実施しています。

* [Livesense Advent Calendar 2016 その1](http://qiita.com/advent-calendar/2016/livesense_1)
* [Livesense Advent Calendar 2016 その2](http://qiita.com/advent-calendar/2016/livesense_2)
* [Livesense Advent Calendar 2016 その3](http://qiita.com/advent-calendar/2016/livesense_3)

他2本のカレンダーもよろしくお願いします。

# はじめに
タイトルがもう全てなのですが、書かせてください。

## この記事の背景とゴール
誰かが言ったわけです。

> サーバーにログインしてオペレーションするのは今やイケてないよね。

インフラエンジニアたるもの、問題が起きたらサーバーにログインして調査ってのは基本です。ですが、気持ちはわからなくないですよね。

例えば、 **深夜のアラート対応** とか。

アラートの中にはどう考えても **「プロセスを再起動しておけばOK」** なものはあります。
でも、ベッドから起きて、VPN繋いで、SSHして、再起動して・・・なんてことをしていたら、目もしっかり覚めて寝不足になってしまいます。

完全に自動復旧をさせるのは怖い・・・でも、明らかにどうでもいいアラートだったら、
**サーバーにログインすることなく、ささっとプロセス再起動程度のオペレーションがしたい** ものです。

手軽と言えば、やっぱイマドキなのは **ChatOps** ですよね。

本記事は、サーバーにログインすることなく **「チャットのような手軽な方法でApacheプロセスの再起動」** を実現することをゴールにしようと思います。


## 技術選定

[Neptune.io](https://www.neptune.io/)流行ってますよね。[Qiitaでもこんなにも投稿があります。](http://qiita.com/tags/neptune.io)

・・・嘘です、将来に期待して先にリンクを張りました。

このサービスの最大のメリットは **「オンプレを含む各種環境のサーバーに対して、Webhookなどのインタフェースを通じて、PUSHで処理を送ることができる」** 点にあります。

![neptune_overview.png](https://qiita-image-store.s3.amazonaws.com/0/82212/3cd7e66b-1691-eebb-9731-13e2df013c48.png "neptune_overview.png")


監視サービスと組み合わせて、障害発生時のファーストアクションを自動化したり、自動復旧を実現しようという趣旨ですね。

Webhookなんて簡単なインタフェースがあれば、色々できそうな気がしますね。
これを実装の核に採用します。

# 準備: Neptune.ioとサーバーの連携

ここではVagrantで構築したCentOS6.8を検証環境として手順をまとめています。
僕が前回書いた記事を繰り返すような内容ですが、約1年ぶりということで改めて記述していきます。

この作業によってNeptune.ioと繋ぎこんでChatOps可能にするサーバーを、本記事では **『hostA』** と呼ぶことにします。

## neptune-agentdのセットアップ

### インストール
Neptune.ioのサイトにログインして、[SEVERS]->[Add agent to your server]と辿って、手順に従ってインストールしましょう。
![0001.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/7e366112-a590-4c06-d819-5acc5db8440d.png "0001.PNG")

・・・というか掲示された手順を対象のサーバーへコピペすれば良いです。
![0002.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/bb228a22-6257-d91c-1b85-bde52d037fa4.png "0002.PNG")


```bash
$ AGENT_USER="neptune" API_KEY="xxxxxx" bash -c "$(curl -sS -L https://raw.githubusercontent.com/neptuneio/neptune-agent/prod/scripts/linux/install_neptune_agent_linux.sh)"

（中略）

Agent is running with pid 1332
-------------------------------------
To check agent status   : sudo service neptune-agentd status
To stop agent           : sudo service neptune-agentd stop
To start agent          : sudo service neptune-agentd start
To restart agent        : sudo service neptune-agentd restart
To uninstall agent      : sudo service neptune-agentd uninstall
Agent log available at     : /home/neptune/agent/neptune-agent.log
-------------------------------------

```
※以前と変わって、ユーザー名が「neptune」とシンプルになってますね。

インストールすると、エージェントは自動的に起動します。しばらくすると、Neptune.ioのサイトの[SERVERS]のページに対象のホストが並ぶのが確認できるはずです。
![0003.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/9fe32400-0dac-b6d6-b56b-762182363921.png "0003.PNG")


### sudoers設定
neptune-agentdの実行ユーザー「neptune」が特権でコマンドを実行できるように、sudoersを作っておきます。

```bash
$ vim /etc/sudoers.d/11_neptuneio

Defaults:neptune !requiretty
neptune ALL=(ALL) NOPASSWD:ALL
```

**本当は必要なコマンドだけ許可しておけば良い** のですが、ここでは雑に設定しています。

## WebhookによるRuleを作る

### Ruleを作成する

Neptune.ioのサイトにて、[Rules]->[Create Rules]と辿って新規Ruleを作成しましょう。
![0004.png](https://qiita-image-store.s3.amazonaws.com/0/82212/6f99c273-757f-3ce3-e800-3c8de0ec1076.png "0004.png")



今回は話をシンプルにするために **1Host:1Rule** の関係でRuleを定義します。
その気になれば複数のホストに同時にアクションを実行させることもできるようですが、ここでは割愛します。

- Trigger source: webhook
- Target type: SPECIFIC_HOST
- Host name: ["neptune-agentdを入れたホスト"]
- Runbook: 以下参照

```bash:Runbook
echo "$(date) sudo ${command}" >> /tmp/neptune.log.$(date +%Y-%m-%d)
sudo ${command}
```

いやぁ・・・ **説明不要なくらいリスクを抱えたコード** ですね。
本番系でこんなコードが動いていたらゾッとしますが、このまま突き進みます。

### curlでの動作確認

ここまで来たらcurlでまずは動作確認しましょう。

```bash:動作確認
$ curl -H "Content-type: application/json" -X POST "https://www.neptune.io/api/v1/trigger/channel/webhook/xxxx......" -d '{ "MetaData" : { "command" : "tail /var/log/messages"}}'
```

うまく動いたでしょうか？
うまく動けばメール等で通知も来るはずです。

正常動作すれば、本来閲覧には特権が必要なはずの`/var/log/messages`の内容が、Neptune.ioのGUIの[Rule]上で見れるかと思います。
![0005.png](https://qiita-image-store.s3.amazonaws.com/0/82212/13aad7cf-fff2-b8a1-ef26-651119f16c24.png "0005.png")
Ruleで定義した`sudo`経由のコマンドが実行されたわけですね。

見て分かる通り、 **`MetaData`で与えた`command`変数の内容が、Runbookのスクリプトに渡されて実行** されています。
commandに渡したものが問答無用に実行されるのです。 **クレイジーだぜ。**

ここまでの作業で **HTTPでリクエストを送ればなんでもできる** 準備が整いました。

大事なことなので書いておきますが、 **このRuleはセキュリティホール以外の何物でもないので、このまま本番利用したときの責任は取れません** ので真似しないでくださいね。



# Siriにオペレーションしてもらう
今、ChatOpsどこ行った？って思いましたよね。
・・・ここまで書いて力尽きて寝てしまったので時間がなくなったのです。

仕事もしていたので **2016/12/08 00:00まで、あと3時間しかありません。**

**ChatOpsなんて前座は書いている暇がありません。ヤバイです。**

## 技術選定
Siriに命令を出す方法は幾つかありますが、今回は比較的自然言語を理解してくれるHomekitを経由して命令を送り込もうと思います。

[Homebridge](https://github.com/nfarina/homebridge)は、Homekitに対応した仮想デバイスとして振る舞うサービス兼フレームワークです。
様々なプラグインが公開されており、そのうちの1つに[cmdSwitch2](https://www.npmjs.com/package/homebridge-cmdswitch2)があります。
cmdSwitch2は、Homebridgeを経由して特定のコマンドを実行するためのプラグインです。

・・・もう見えてきましたね。

## Homebridge + cmdSwitch2を準備
[Homebridge界隈は色んな人が記事を書いている](http://qiita.com/search?q=homebridge)ので、ざっくりと手順を書いていきます。

ここでは、MacにHomebridgeとcmdSwitch2を入れる手順を書いていきます。

### nodebrewをインストール
HomebridgeはNode.jsで動いています。nodebrewを利用してNode.js環境を整えましょう。

```bash:nodebrewのインストール
### インストール 
$ curl -L git.io/nodebrew | perl - setup

### パスを追加する
$ vim ~/.bash_profile

# nodebrew
export PATH=$HOME/.nodebrew/current/bin:$PATH

### ロードする
$ . ~/.bash_profile
```

### Homebridgeをインストール
ここも駆け足で書いていきます。躓いた時は、丁寧な人の説明を探して読んで下さい。

```bash:Homebridgeのインストール
### stableバージョンのNode.jsを入れる
$ nodebrew install-binary stable
$ nodebrew use stable

### Homebridgeとhomebridge-cmdswitch2を入れる
$ sudo npm install -g homebridge
$ sudo npm install -g homebridge-cmdswitch2
```



### Homebridge自動起動用plistファイルを記載する
Macで`launchctl`を使用してHomebridgeをdaemon化するために、以下のような設定ファイルを作っておきます。

```bash:Homebridgeのplistファイル例
$ vim ~/Library/LaunchAgents/com.bridge.server.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>com.homebridge.server</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/YOURNAME/.nodebrew/current/bin/homebridge</string>
        <string>-U</string>
        <string>/Users/YOURNAME/.homebridge</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
            <key>PATH</key>
            <string>/Users/YOURNAME/.nodebrew/current/bin:/usr/local/bin/:$PATH</string>
    </dict>
</dict>
</plist>
```
※ `YOURNAME`部分にはあなたのユーザー名を入れて、ちゃんとパスを通しましょう。

これで、以下のようなコマンドでdaemon化することが出来ます。

```bash:Homebridge起動
$ launchctl load ~/Library/LaunchAgents/com.bridge.server.plist
```

### Homebridgeの設定ファイルを記載する
Homebridgeの設定ファイルを作りましょう。

```bash
$ vim ~/.homebridge/config.json
```

JSONでこのような設定を作ります。
`pin`はHomekitで使用しますので覚えておきましょう。

```json:~/.homebridge/config.json
{
  "bridge": {
    "name": "Homebridge",
    "username": "PCのMACアドレス",
    "port": 51826,
    "pin": "123-45-678"
  },
  "platforms": [
    ### この部分は後述します
  ]
}
```

この時点で、Homebridgeを起動して、iPhoneなどのiOSデバイスでHomekitを起動すれば、「Homebridge」という名前のデバイスが見つかるはずです。
登録時にはPINコードを求められますが、これは先程の設定ファイルの`pin`を手動で打てばOKです。

しかし、まだ何もデバイス（プラグイン）が設定されていませんので、起動しても意味はありません。

## curlコマンドをスクリプト化

後述しますが、JSON中に長いcurlコマンドを埋め込みたくないので、単純なBASHスクリプト『hostA_apache.sh』を予め用意しておきます。

```bash:hostA_apache.sh
#!/bin/bash -
ACTION=${1}
curl -H "Content-type: application/json" -X POST "https://www.neptune.io/api/v1/trigger/channel/webhook/xxxx..." -d '{ "MetaData" : { "command" : "service httpd '"${ACTION}"'"}}'
```

**実に酷いスクリプトですね。**

動作確認しておきましょう。

```bash
$ bash hostA_apache.sh stop
$ bash hostA_apache.sh start
```

**hostAでApacheの再起動が走ったでしょうか？**
先程Neptune.ioに設定した酷いRule、酷いRunbookを経由して、hostAで`service httpd xxxx`コマンドが実行されたわけです。


## Homebridgeにスクリプトを連動させる
Homebridgeの設定ファイルの`platforms`部分に以下の設定を追加しましょう。


```json:~/.homebridge/config.json
"platforms": [
  {
    "platform": "cmdSwitch2",
    "name": "CMD Switch",
    "switches": [
      {
        "name" : "Neptune.io hostA Apache",
        "on_cmd": "bash /path/to/hostA_apache.sh start",
        "off_cmd": "bash /path/to/hostA_apache.sh stop",
        "state_cmd": "curl -s -I http://hostAのIPアドレス/",
        "polling": true,
        "interval": 30
      }
    ]
  }
]
```
先程のスクリプトは、このように設定の中で使用されています。
※ `/path/to`部分にはスクリプトを配置したディレクトリへのパスを書きましょう。


ポイントは、cmdSwitch2の`switches`の設定に

- `on_cmd`として先程のBASHスクリプトを`start`オプションで指定していること
- `off_cmd`として先程のBASHスクリプトを`stop`オプションで指定していること
- `state_cmd`としてcurlでApacheの動作確認を入れていること

です。

名前から推測できるように、それぞれデバイスの「ON」「OFF」「状態確認」に対応しています。

なお、状態確認コマンドは省略しても良いようです。
もっと高度に、監視サービスなどを突いても良いでしょう。

## Homebridgeを起動する
Daemon化する場合は以下です。`load`の代わりに`unload`すれば停止できます。

```bash:Homebridge起動
$ launchctl load ~/Library/LaunchAgents/com.bridge.server.plist
```

初回はフォアグラウンドで動かしたほうがデバッグしやすいでしょう。
フォアグラウンドで動かすときは、以下でOKです。

```bash
$ homebridge
```

明示的に設定ファイルを読み込ませたければ以下のようにオプションを付けます。（plistの記載と同じ指定になりますね）

```bash
$ homebridge -U ~/.homebridge
```


## Homekitに認識させる
iOSデバイスでHomekitアプリを起動しましょう。
HomebridgeをセットアップしたMacと同じネットワークに繋がっていれば、[アクセサリの追加]からHomebridgeを見つけることができるはずです。

![IMG_3661.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/390e935f-9713-dba0-fb4c-249f557e7ca6.png "IMG_3661.PNG")

更にその中を開けば、『Neptune.io hostA Apache』というデバイスが表示されているはずです。

![IMG_3676.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/eaff799f-18d4-2849-dc92-e99560b7db44.png "IMG_3676.PNG")

`state_cmd`がちゃんと機能して、今Apacheが動いていることも状態として取れていますね。


お疲れさまでした。 **これで準備は整いました。**

# さぁ試そう
## まずはGUIで動かす
Homekitのアプリ上で『Neptune.io hostA Apache』のスイッチをオン・オフしてみましょう。
hostAのApacheが起動・停止するのが確認できたでしょうか。

これが上手くいかないようであれば、何かHomebridgeの設定にミスがあります。
Homebridgeの設定を見直しましょう。

うまく動いた人は、 **いよいよSiriOpsをする時です。**

## SiriOpsを試す(1)
ホームボタンを長押ししてSiriを呼び出しましょう。

落ち着いて、声をかけましょう。

僕「Neptune.io hostA Apacheの電源を切って」

![IMG_3675.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/14ff5c21-e59c-df17-a10b-dac63836b8d6.png "IMG_3675.PNG")

Siri『すみません、よくわかりません』

**まだ頑張ったほうだよ、Siri・・・！！**

分かる分かる。無理だよね。
期待してはいなかったよ。
むしろ、 **Apacheと認識できる** のにびっくりしたよ。


## 名前を変えましょう
デバイス名をHomekitアプリから変更することが出来ます。
**なるべく発音が困難じゃない名前** にするのがSiriに優しい設定です。
![IMG_3677.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/c000edbd-464b-67e6-e858-d0376dd8f086.png "IMG_3677.PNG")

僕は『テストサーバーのApache』というデバイス名に変更しました。

## SiriOpsを試す(2)
再度SiriOpsを試しましょう。

### 状態確認
ホームボタンを長押ししてSiriを呼び出しましょう。

落ち着いて、声をかけましょう。

「テストサーバーのApacheは点いている？」

![IMG_3678.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/b831282a-3623-2013-6307-1d9b0d4bc5d0.png "IMG_3678.PNG")

**おぉぉ！動いた！**

ちなみに「点いている？」と質問したのは、SiriはcmdSwitch2デバイスを **家電か何かのスイッチ** として認識しているからです。家電に対しての質問のように話しかけるとSiriはスムーズに応答してくれます。

### プロセスを停止->起動する
再びSiriに話しかけます。

「テストサーバーのApacheを切って」

![IMG_3679.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/9d5360ca-55b8-51e2-4c31-f8945d244535.png "IMG_3679.PNG")

hostAのApacheが停止することを確認できました。

「テストサーバーのApacheをオンにして」

![IMG_3680.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/5d118915-881e-33f4-cfce-4f0efa9a87b2.png "IMG_3680.PNG")

hostAのApacheが起動することを確認できました。

**完璧です。**

### 連続で命令してみる
もう失敗することはないですね。
Siriに話しかければ、hostAのApacheは起動したり殺されたりされたい放題です。

![IMG_3681.PNG](https://qiita-image-store.s3.amazonaws.com/0/82212/df0b0d22-6138-63e6-b53d-6e6a34e683ca.png "IMG_3681.PNG")

深夜に起こされても、Siriに再起動が依頼できちゃうわけです。
**もう起こされてがっかりするアラートも怖くないですね。**


# あとがき
## 現在時刻
**現在時刻は、2016/12/07 23:04です。間に合った！！**
雑な部分、タイポや間違いがあったら後で直しておこうと思います。が、最初のリリースが間に合うことが大切ですよね。

## Apache?
本記事でApache httpdを『Apache』と呼んでいるのは嫌な感じを受ける人もいるかもしれません。
すみません、全てSiriのためです。

『httpd』という呼称を使っていたら、きっと
「テストサーバーのApache」は、
**「テストサーバーのエイチティーティーピーディー」** という名前にしておかなければならなかったでしょう。

**正確に発音する自信が僕にはありません。**

## テキストだらけでごめんなさい
Siriのくだりは動画を撮ればわかりやすかったですね。
時間がなかったのです、文章と画像で許してください。

## 明日は
[t_horiさん](http://qiita.com/t_hori)のアドベントカレンダー9日目の記事です。