最近のCYBIRDゲームインフラ環境について

# 初めに
[CYBIRDエンジニア Advent Calendar 2015](http://qiita.com/advent-calendar/2015/cybird) 1日目担当の @gotyoooo です。

CYBIRDでゲームインフラ部という部署に所属しています。

今年もアドベントカレンダーをやることになり、カレンダーを事前に埋めるためにとっておいたらそのまま1日目担当になることになり、非常に焦りながらこの記事を書いてます。
# 最初に
* 弊社のモバイルゲームにおけるインフラ環境について赤裸々に語ります
* 浅く広く環境の紹介がメイン

# 前提
* ここ1年ぐらいで新規リリースしたゲームの**商用環境**のインフラ周りの構成や構築情報を紹介
* もちろん1人でやっているわけではないので、私以外の担当領域も含まれます

# 構成

## オンプレミス？クラウド？
* どちらもあります。以下の様なメリット/デメリットを見て選択

|        | オンプレミス | クラウド |
|:-------|:----------:|:-------:|
|構築(Bootstrap) |☓ ラッキングや配線・OSインストールなどのスケジュールも考慮する必要がある|◯ インスタンスを立てるだけ|
|運用     |☓ 機器自体の故障なども踏まえた運用が必要になる|△ 自動化が進めば楽| 
|コスト   |◯ クラウドに比べて高性能なサーバが安い|☓ リザーブドインスタンス等を使いコストを抑える方法もあるが、転送量が結構高い|
|スケール性|☓ 物理サーバを購入するには時間がかかる|◯ 足りなければすぐ作れる|

### オンプレミスを選択するとき
* トラフィックをある程度事前に予測できるゲーム
 * (例)量産型ゲームや、シリーズ物

### クラウドを選択するとき
* トラフィックが読みにくいゲーム
 * (例)完全新規開発ゲーム
* 日本以外へリリースするゲーム

## LAMP+αの環境
* Linux
 * CentOS6系がメイン
* [Apache HTTP Server](https://httpd.apache.org/)
* [nginx](http://nginx.org/)
* [MySQL](https://www-jp.mysql.com/)
* [Redis](http://redis.io/)
* [Memcached](http://memcached.org/)
* 一部では[MongoDB](https://www.mongodb.org/)、[Couchbase](http://www.couchbase.com/jp)も利用している

## 監視
* [Zabbix](http://www.zabbix.com/jp/)
 * デフォルトの監視項目はもちろんのこと、自作した監視スクリプトやローレベルディスカバリ等を駆使し、わりと細かく数値を取って閾値を決めている
 * アラート発生時はメール送信、[CahtWork](http://www.chatwork.com/ja/)でメッセージ通知で知らせるようにしている
* [MONyog](https://www.webyog.com/product/monyog)
 * ボトルネックがMySQLであることがほとんどなので、MySQLに対する監視を深くするため利用
 * また障害発生時のプロファイリングもしやすく、困ったときに利用
* [NewRelic](https://newrelic.com/)
 * サーバサイドのパフォーマンス監視用途
 * こちらも困ったときや負荷試験時に重宝することが多い

## 他の特徴
### [GlusterFS](http://www.gluster.org/)
* 複数サーバでの画像共有
 * あるサービスで画像合成をして、その画像を保存するような処理が存在
 * 「この画像が既に存在するか?」を確認後、なければ合成処理が行われる
 * 処理にある程度CPUやメモリリソースが必要なため、複数台のサーバが必要
 * 複数のサーバで同じ画像を合成するのを防ぐために保存先にGlusterFSで作成したVolumeを利用
 * NASストレージが利用できない環境のためのプラン
 * 1年半ほど運用しているが、大きな障害は発生していない
 * ファイルがなくなっても処理でなんとかなるので「Distributed Volume」を利用

> ![Distributed_Volume.png](https://qiita-image-store.s3.amazonaws.com/0/59261/9f70e3fa-3182-f143-a19e-4aad465b04df.png)

* 容量が大きいストレージを持ったサーバのリソースを効率よく利用
 * オンプレ環境の各コンテンツに1台は3TB超えのストレージを持ったサーバが存在
 * MySQLのダンプファイルや、ログの置き場として利用するがバックアップさせつつ、さらに大きなストレージとするために「Distributed Striped Replicated Volume」を利用

> ![Distributed_Striped_Replicated_Volume.png](https://qiita-image-store.s3.amazonaws.com/0/59261/ad5b5b47-f375-d654-83fa-6da17c1c2110.png)


### [Fluentd(td-agent)](http://www.fluentd.org/)
* ログ系のリアルタイムバックアップが主な用途
* ZabbixにApache/nginxのログのレスポンスタイムを渡していい感じに見えるようにもしている
![zabbix-httpresponce.png](https://qiita-image-store.s3.amazonaws.com/0/59261/95b14519-81eb-fc49-eecd-3396854dd996.png)

### [Docker](https://www.docker.com/)
* FluentdでログをAmazon S3に転送しているが、各サーバ単体でそれぞれ別でやらせるとポートが枯渇する問題があり中継サーバとして利用
* 受信ポートを分けるために複数コンテナを起動し、1サーバで複数のtd-agentを起動させてコンテンツごとの中継先にしている
* Chefレシピ等のTDD用途にも利用(後述)

### [Serf](https://www.serfdom.io/)
* 各サーバのServerspecテストのためにロールを持ったり、色々情報もたせたりしてる(後述)
* ~~内部DNS設定がめんどくさいから~~host名で名前解決するために/etc/hostsファイルにSerfクラスタに入っているサーバ情報を記述

### LVS
* オンプレミス環境のロードバランサー
 * アプライアンス製品は使ってません
* Heartbeat + Pacemaker + ldirectord
* 構築後SplitBrainで問題になったことがありPacemakerのSTONITH機能も利用

# 構築

## Chef/Serverspec

### Cookbook/Roleの方針
* Cookbookはほとんど内製開発
 * [Chef Supermarket](https://supermarket.chef.io/dashboard)のものもあるが、ラップしたようなCookbookも作っている
* Recipeにロジックを入れない
 * 管理が煩雑になってしまうのを防ぐ
 * 広く使えるようなものではなく、わかりやすいことを優先している(多種OSに対応するようなものにはしていない)
* インストールするソフトウェアのバージョンや各種パラメータはRoleで管理
 * data_bagを利用し暗号化
 * **Roleを見ればどのような環境であるか一目瞭然になる**
 * 以下はRoleのイメージ

```TEST_ROLE.json
{
  "name": "TEST_ROLE",
  "description": "とある開発用WEBサーバのRole",
  "json_class": "Chef::Role",
  "default_attributes": {
  },
  "override_attributes": {
    "user": {
      "user_list": {
        "test": {
          "password": "暗号化したパスワード"
        }
      }
    },
    "visudo": {
      "sudoers_d": {
        "test": "test    ALL=(ALL)    ALL",
      }
    },
    "httpd" : {
      "httpd_conf": {
        "prefork" : {
          "start_servers"          :    8,
          "min_spare_servers"      :    5,
          "max_spare_servers"      :   20,
          "server_limit"           :  256,
          "max_clients"            :  256,
          "max_requests_per_child" : 1000
        }
      },
      "application" : {
        "hoge.jp.conf": {
          "listen_port": 80,
          "contents_user": "test",
          "document_root": "/home/test/app/current/public",
          "server_name": "hoge.jp",
          "server_alias": ["hoge.jp", "www.hoge.jp"]
        },
      }
    },
    "php" : {
      "version" : "php55",
      "packages" : ["php", "php-cli", "php-common", "php-devel", "php-gd", "php-mbstring", "php-pear", "php-pdo", "php-opcache", "php-mysqlnd"],
      "php_ini": {
        "date_timezone": "Asia/Tokyo"
      }
    },
    "serf": {
      "tags": {
        "chef_role": "TEST"
      }
    },
    "zabbix_agent": {
      "version" : "2.2.10-1.el6",
      "zabbix_server_ip": "ZabbixサーバのIP"
    }
  },
  "chef_type": "role",
  "run_list": [
    "user",
    "sshd",
    "visudo",
    "php",
    "httpd",
    "zabbix_agent",
    "serf"
  ],
  "env_run_lists": {
  }
}
```

### 構築はknife-soloで
* Jenkinsでknife-solo用のJOBを作り実行している
* Serverspecでテストを行うようにしている

### Chef開発環境
* GitHub上でPull Request開発
* Pull Request時にServerspecテストが走る
 * テストが通った時点でレビュー依頼->OKならマージ
* Serverspecテストが実行される流れ
 1.  Jenkinsの「GitHub pull request builder plugin」でhook
 2.  新規Dockerコンテナ起動(空のCentOS)
 3.  2に対してknife-solo実行(roleはbranch名をつけたテスト用roleを作成している)
 4.  3に対してServerspec実施(3のロールのAttributeを読み込みテスト)
 5.  テスト結果をGitHubに通知

### 毎日Serverspecテスト
* 運用中のサーバにも毎日Serverspecテストが走る
* 適用するChefのロール名をSerfのタグに持たせている
* そのロールを元にテスト実行、失敗したらChatWorkへメッセージ通知

## Kickstart
* オンプレミスの物理サーバに対して、OSインストールからknife-soloを実行できるようにするまでを担当
* PXEネットワークブートさせて実行

# 未来へ向けて考えていること

## HTTP/2への移行
* APIでJSON返すだけならそこまでいらないが、いわゆるWEBサイトなら効果絶大なはず
* [nginxもHTTP/2サポート](https://www.nginx.com/blog/nginx-1-9-5/)したので今こそ!という気持ち

## chef-soloからchef-zeroへ
* まぁ・・・ね。
* 今Chef開発環境はChefZeroでやっていたりするが、それを商用環境でも適用したい。というかやりましょう。

## Chefによる単体のサーバごとの構成管理だけではなく、全体構成管理も
* [Terraform](https://terraform.io/)かな。

## 負荷試験方法の進化
* 現状は[JMeter](http://jmeter.apache.org/)を利用
* テストシナリオを作るのは大変
 * JMeterProxyを利用する手もあるが、それでは事足りない
* リリース前の本番環境に負荷をかけているので、運用中に実施は実質不可能
* **ここはまだまだ模索中**

# ここまでのまとめ
* 変わったことはあんまりしていない
* **導入・運用に対するコストを踏まえて適切に選ぶことを重要視**している

# 最後に

投稿日前日に書くと辛いのでみんな計画的に書きましょう・・・

[CYBIRDエンジニア Advent Calendar 2015](http://qiita.com/advent-calendar/2015/cybird) 明日は、 @keitarou の「JavaScriptにおける非同期処理の歴史」です。

@keitarou は後輩でありながら、プログラムに関しては私の先生になってくれてるすごい奴です。
乞うご期待！