サーバが増えた時にインフラ担当者がやってきたこと

# はじめに

![IMG_00217.jpg](https://qiita-image-store.s3.amazonaws.com/0/14093/953366b7-6bbb-29b5-5aa9-31edd85c9bdc.jpeg)

これは [ドリコムAdventCalendar](http://www.adventar.org/calendars/518) の9日目の記事です。
8日目は[sazae657](https://twitter.com/sazae657)さんによる[ドリコムの俺を支えるUIツールキット](http://sazae657.hatenablog.com/entry/2014/12/08/012300)です。

# 自己紹介

[@hiracy](https://twitter.com/y05_net)　といいます。

[ドリコム](http://www.drecom.co.jp/)のインフラやってます。

* 最近発表したスライド
    * [ドリコムのInfrastructure as Code](http://www.slideshare.net/y05_net/infrastructure-as-code-35373108)
    * [インフラ自動化とテストについて](http://www.slideshare.net/y05_net/ss-39978680)

# この内容について
WEBサービス・ソーシャルゲームのインフラにてサーバが増加した時の管理について採用してきたツールとノウハウについて書かせて頂きました。
サーバ増加時の管理にお悩みのインフラ担当者は参考にしてみてはいかがでしょうか。

# プロビジョニング
業者又は自前でラッキングされたサーバやクラウド業者で契約し使えるようになったサーバからOS設定・ミドルウェアインストール等を1台1台コマンドで設定すると日が暮れてしまいます。**(たまにやってみるといい気付きがあります)**
また、構築した際の手順から別のサーバを構築すると再現性が不完全になってしまいますので、**プログラミング言語でサーバの状態を記述**できるツールを使うと良いと思います。

ドリコムでは以下のポイントでこのプロビジョニングツールを検討しました

1. ツール自体を含む機能の拡張が一般的なプログラミングを習得しているエンジニアであれば容易にできる
2. ツール自体の機能の学習・調整に時間を取られない
3. gitによるサーバの状態・構築手順の管理ができる
4. お金が(かからない|あまりかからない)

検討していた時期の2年前には上記に当てはまるツールがChef-Server/SoloとPuppetくらいしかなかったのですが、1の理由で[LWRP](https://docs.getchef.com/lwrp.html)による拡張のしやすさと、2の理由で当時Chef-Serverが色々なミドルウェアのごった煮状態で調整が難しく、得られるメリットが感じられなかったためChef-Soloを拡張することに決めました。
最近だと以下のような、Chefに引けを取らないツールがあるので初めてプロビジョニングツールを検討する方は参考にして頂けたらと思います。

 * [ansible](http://www.ansible.com/home)
    * pythonで書ける。構築するサーバ自身にエージェントが要らない。
 * [itamae](https://github.com/ryotarai/itamae)
    * ChefっぽいDSL、Chefより機能がシンプル(且つ十分)

# テスト
管理するサーバの[冪等性](http://ja.wikipedia.org/wiki/%E5%86%AA%E7%AD%89)を保証するために主に以下のケースで[Serverspec](http://serverspec.org/)を使っています。

 * プロビジョニングの後
 * セキュリティ対応等ミドルウェアの設定調整やDB・KVSの冗長構成変更の際に実施している、限定的な手動サーバオペレーションの後

現在このようなサーバの状態を一般的なプログラミング言語でテストするツールはServerspec一択のような気がします。(他は[shUnit2とか](https://sites.google.com/site/paclearner/shunit2-documentation))

# CI
インフラがコード化するとCI(継続的インテグレーション)環境を用意し、コードを管理からサーバ構築までの一連の作業を自動化できるようになります。
ドリコムでは以下のような環境と順番にてCIを実施しています。

![スライド07.jpg](https://qiita-image-store.s3.amazonaws.com/0/14093/8587de70-3cc1-f0c2-5918-42c919ab56fe.jpeg)

1. Chefのレシピを編集して社内のGitLabサーバにpushします。push時にJenkinsサーバにweb hookが発行されます。
2. 本番環境でのサーバ構築とテスト(Chef-Solo+Serverspec)と同等の処理を[Vagrant](https://www.vagrantup.com/)の仮想環境上で実行します。
3. 2の成否結果をインフラ担当者全員にメール送付します。
4. 3の成功結果を確認した後、担当者が本番環境にRundeck(後述)やsshを使ってサーバ構築(Chef-Solo+Serverspec)を実行します。

# オペレーション(ssh手動運用)
最近は[Immutable Infrastructure](http://ja.wikipedia.org/wiki/Immutable_Infrastructure)といった**一度サーバを構築したらいじらない、構成を変更する際は新たに作り直す**といった手法もあるようですが、採用しているクラウド業者によっては色々な事情で実現できないケースも多く、また手軽に扱えるため手動でのサーバオペレーションの機会は多いと思われます。

## [Rundeck](http://rundeck.org/)
Java/Groovy製のオーケストレーションツールで[Salesforce.comも使っているそうです](http://rundeck.org/stories/salesforce_gigantor.html)。
元々インフラ担当者のサーバオペレーションに[capistrano](https://github.com/capistrano/capistrano)を使っていたのですが対象サーバのフィルタリングや実行履歴の管理の機能に不足を感じていた際に、その要件を満たしていてしかもWEB UIその他機能も充実しているので採用となりました。
あまり採用している会社もないようなので下記ちょっとだけ紹介したいと思います。

#### ホスト管理画面
ホスト情報はxmlやyamlで管理するのですが、ドリコムではサーバ監視システムであるZabbixサーバのAPIを介して自動で生成しています。

![rundeck-01.png](https://qiita-image-store.s3.amazonaws.com/0/14093/f7a7797d-5094-a237-0e56-8dcbf4a2f11a.png)

#### コマンド実行
フィルタリングした結果のサーバのみコマンドを実行します。実行時のユーザはLDAPにて管理しています。

![rundeck-02.png](https://qiita-image-store.s3.amazonaws.com/0/14093/b80dc36a-8f79-b536-4ee3-42b1d71daa2e.png)

#### 実行履歴管理
過去と同じパターンを検索し再実行することも可能です。

![rundeck-03.png](https://qiita-image-store.s3.amazonaws.com/0/14093/be8b446d-9070-9a34-fbff-24b6ba29c1a5.png)

#### その他
JOBを登録してcronのように使えたり、WEB APIにて別のシステムと連携可能と機能が充実しているので複数サーバオペレーション管理のツールを検討している方にはオススメです。

## tmux + ssh + Zabbix API
Rundeckは主にインフラ担当者がサーバ運用に使う位置付けのツールですが、[とあるエンジニア](https://github.com/mitaku)から[tmux + ssh + Mackerel API](http://yuuki.hatenablog.com/entry/tmux-ssh-mackerel)というライフチェンジングなサーバオペレーションについての記事を紹介頂き、Mackerelを社内で監視に利用しているZabbixに置き換えたツールを勢いで作ってみました。

#### 仕組み
上記記事内に「タイトルに Mackerel と書いていますが、半分以上は Mackerel に依存しない話です。」と書かれておりますが、その通りMackerel APIをZabbix APIによるホスト情報取得に置き換えただけです。
ただMackerelには「Service」というサーバ登録時にグルーピング指定を明示的に指定する必要がありますが、ドリコムでは以下のようにホスト名の命名規則に、ある程度厳格な運用をしているためそちらをグルーピングに利用しています。

##### ホスト名命名規則

```
[servicename]-[role](-subrole)(-cluster_no)-[server_no]

servicename: サービス名
role:        そのサーバの役割
subrole:     roleの補助名(option)
cluster_no:  クラスタ番号(option)
server_no:   サーバ番号

例) anyservice-db-01-01
```

#### zabbix APIからホスト情報の取得
[tmux + ssh + Mackerel API](http://yuuki.hatenablog.com/entry/tmux-ssh-mackerel)と同様に以下の２つのコマンドで対象サーバのIPアドレスを取得できるgemツールを作りました。

```
app <サービス名>
> サービス名にマッチしたサーバ一覧が出力される

role <サービス名> <ロール名>
> サービス名とロール名にマッチしたサーバ一覧が出力される
```

#### 使用例

##### サービスに所属するホスト一覧

```
$app anyservice
anyservice-web-01-01
anyservice-web-01-02
anyservice-db-01-01
anyservice-kvs-01-01
```

##### サービスに所属した特定のロールのホスト一覧

```
$role anyservice web
anyservice-web-01-01
anyservice-web-01-02
```

##### tsshを使って特定のサービス・ロールのサーバをオペレーションする

```
$tssh `role anyservice web`
```

#### デモ
![tmux+tssh+zabbix api](https://dl.dropboxusercontent.com/u/26514847/qiita/klivp0.gif)

**※弊社のサーバ全てslコマンドが使えるようになっているわけではありません**

#### 実装について

このような仕組みを持つツールをgemで実現していますが、詳細は割愛させて頂きます。
代わりにZabbix APIをcurl,jqコマンドにてホスト情報のJSONを取得するシェルスクリプトを添付しますのでこのようなツールを作成する時に参考にして頂けたらと思います。

```
ZABBIX_APIUSER="user"
ZABBIX_APIPASSWORD="passwd"
ZABBIX_HOST="http://zabbix.any"
ZABBIX_API_PATH="api_jsonrpc.php"
ZABBIX_API_URL="${ZABBIX_HOST}/${ZABBIX_API_PATH}"
ZABBIX_JSONRPC="2.0"
CURL_OPTS="-s -d"

login_ans_json=`curl ${CURL_OPTS} \
                 '{"auth":null, \
                   "method":"user.login", \
                   "id":1, \
                   "params":{ \
                      "user":"'${ZABBIX_APIUSER}'", \
                      "password":"'${ZABBIX_APIPASSWORD}'"}, \
                      "jsonrpc":"'${ZABBIX_JSONRPC}'"}' \
                   -H "Content-Type:application/json-rpc" ${ZABBIX_API_URL}`

api_key=`echo ${login_ans_json} | jq ".result" | sed "s/\"//g"`

host_json=`curl ${CURL_OPTS} \
            '{ "jsonrpc":"'${ZABBIX_JSONRPC}'", \
               "method":"host.get", \
               "params":{ \
                 "output": ["extend"], \
                 "selectInterfaces": ["ip"]}, \
               "auth":"'${api_key}'","id":1}' \
            -H "Content-Type: application/json-rpc" ${ZABBIX_API_URL}`

echo $host_json | jq .
```

# 監視・モニタリング
ツールを選定する際には以下の点を意識してきました。

1. コマンド実行結果の値を監視・モニタリングにて状態取得でき、ある程度カスタマイズして表示できるか
2. 監視・モニタリング対象のサーバが増えた時にスケールアップ又はスケールアウトが容易か
3. 異なるネットワーク間の監視・モニタリングが自分たちのDCにマッチしているか(使用ポート・プロキシ機能有無等)
4. 最終的に自分達でツールに手を加えて面倒を見れるか(見れる自信があるか)
5. 結局運用してみないと機能不足や見辛さに気付かない事も多いので、とにかく色々試してみる

ドリコムでは以前は主に[Nagios](http://www.nagios.org/)と[cacti](http://www.cacti.net/)を使っていましたが、急激なサーバ増加の際に対応するための自動化がしづらいということもあり、現在は以下のツールを使いプロビジョニング時の情報からほぼ全自動で最適な監視が設定できるようにしています。

 * 監視: [Zabbix](http://www.zabbix.com/jp/)
 * モニタリング： [Mine](http://www.slideshare.net/KenichiMitsuki/ss-35379098)(社内オリジナル)

また、最近は以下のような自分達で監視・モニタリングサーバを持たずにWEBサービスを利用することも多くなってきていますので選択肢の中に入れておいて良いのかもしれません

 * [mackerel](https://mackerel.io/)
 * [datadog](https://www.datadoghq.com/)
 * [New Relic](http://newrelic.com/)

尚、よくあるのが「色々対応しているけど結局何をみたらいいのかわからない状態」になり、障害時や性能劣化が発生している際に誰も何もしない状況に陥るというケースです。
これを防ぐためには事前にエスカレーションフローを決める事や、サービスとして正しい状態とは何かをエンジニアで共有・決定した上でツールの選定や監視・モニタリングする項目の設計をしていくのが良いと思います。
 
# 10日目は
[tkeo](https://twitter.com/tkeo)さんが発表します！