GitBook/GitHub/CircleCIでメンテ可能な仕様書を書く

# はじめに
あるプロジェクトで仕様書をそれなりのクオリティで書く必要が出てきました。Markdownで書きたいなーとか履歴管理したいなーと思っていたので、GitHubで管理してGitBookとしてドキュメント化することに決めました。

# 内容
## GitBookとは？
「Markdownで簡単に電子書籍が作れるよ」というものです。GitHubのレポジトリを指定すると勝手にホスティングしてくれる [gitbook.com](https://www.gitbook.com) というサービスの他に、[OSSでも公開されている](https://github.com/GitbookIO/gitbook-cli)ので自前の構築も可能です。

`gitbook.com` を使えばかなり楽が出来ますが、以下の2点が問題点としてありました。

* 確認した時点で日本語ディレクトリだと正常に動かなかった
* ほかの人(主にクライアント)に見せるためにprivateで構築してコラボレーターに追加するのが面倒

そこで今回はOSS版のGitBookを活用し、自前で構築しました。

## やったこと
* GitBookで出力されたHTMLをS3にホスティング
* リバースプロキシを設置してBasic認証をかける
* CIで自動更新する

の3つです。これで

* 誰でも簡単に更新ができ
* 更新履歴が完璧に残り
* ブラウザさえあればいつでもどこでも最新のドキュメントが確認できる

環境が整いました。

## 前提
GitBookとして動かせるレポジトリを構築している事。試しに動かしてみたい場合は、以下のような記事を参考にしてみると良いでしょう。
http://qiita.com/nekogeruge_987/items/46dfb552b33aac4ef9b0

## 手順
### S3にホスティングする

ホスティング先としてAmazon S3を選択しました。配置するHTMLを吐き出すには、gitbookのプロジェクトディレクトリで以下のコマンドを実行します。

```
$ gitbook build
```

正常に変換が完了すると、`_book`というディレクトリにファイル一式がExportされます。これを任意のS3バケットにアップロードします。

ファイルを置いただけではWebサイトとしてアクセスできないので、バケットの設定から「静的ウェブサイトのホスティング」 -> 「ウェブサイトのホスティングを有効にする」を選択してホスティングします(インデックスドキュメントの欄は `index.html` としています)。

デフォルトだと(たぶん)AWSのユーザにしかアクセス権限が与えられていないため、以下のようにポリシーを設定します。

```json:ポリシー
{
  "Version":"2012-10-17",
  "Statement":[{
      "Sid":"AddPerm",
      "Effect":"Allow",
        "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::<Bucket Name>/*"]
    }
  ]
}
```

これでエンドポイント`<bucket-name>.s3-website-ap-northeast-1.amazonaws.com`にアクセスするとGitBookのページが確認できるはずです。

### リバースプロキシを設置してBasic認証を行う

このままでは仕様書が全世界に公開されてしまうため、EC2でリバースプロキシを設置してBasic認証をかけます。

せっかくなので最近追加されたAmazon Linxuの t2.nanoインスタンスを使います。AWSコンソールからEC2の起動が完了したら、sshで接続して必要な設定をします。

今回はnginx使用するので、yumでインストールします。また、デフォルトではSecurityGroupがsshしか開いていないはずなので、HTTP:80を追加してください。

```
$ sudo yum install nginx
$ sudo nginx
$ sudo chkconfig nginx on
```

ここまでの手順で、EC2のグローバルIPにブラウザでアクセスするとnginxのデフォルトの画面が表示されるはずです。表示されない場合は「nginxが起動しているか？」「EC2のSecurityGroupは開いているか」などを確認すると良いでしょう。

次にリバースプロキシの設定です。EC2へのアクセスをS3に流します。nginxの設定ファイルを開き、

```
$ sudo vim /etc/nginx/nginx.conf
```

`location /`の部分にS3のエンドポイントを指定します。

```
location / {
  proxy_pass http://<bucket-name>.s3-website-ap-northeast-1.amazonaws.com;
}
```

以下のコマンドでnginxを再起動し、再度ブラウザでEC2にアクセスします。S3にホスティングされたGitBookのページが表示されたら成功です。

```
$ sudo nginx -s reload
```

次にBasic認証の設定をします。まずはhtpasswdファイルを生成し、

```
$ sudo yum install httpd-tools
$ cd /etc/nginx
$ sudo htpasswd -c .htpasswd <User>
```

nginxの設定ファイルでBasic認証を有効にします。

```
location / {
  auth_basic "Restricted";
  auth_basic_user_file /etc/nginx/.htpasswd;
  proxy_pass http://<bucket-name>.s3-website-ap-northeast-1.amazonaws.com;
}
```

ここまで完了したらもう一度nginxを再起動し、ブラウザでアクセスしてみてください。Basic認証を要求されるはずです。

### S3へのアクセスを制限する
このままではS3のエンドポイントからも普通にアクセスできてしまうため、S3のポリシーを以下のように書きかえます。 `aws:SourceIp` にEC2のIPアドレスを指定することで、リバースプロキシ経由でのアクセスのみ許可しています。

```
{
"Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AddPerm",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::miraimarche-gitbook-hosting/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "##.##.##.##"
          ]
        }
      }
    }
  ]
}
```

### CIの設定をする
毎度S3に手動でアップロードするのは面倒なので、CircleCIでS3へのデプロイを自動化しました。

デプロイスクリプトはnode.jsで記述しているので、そのセットアップをします。ファイルのアップロードは[s3](https://www.npmjs.com/package/s3)というnpmを使いました。

```
$ npm init
$ npm install s3 gitbook-cli --save
```

package.jsonにgitbookのビルドスクリプトを書きます。

```json:package.json
  "scripts": {
    "export": "./node_modules/gitbook-cli/bin/gitbook.js build"
  }
```

CircleCIの設定ファイルです。今回はmasterへコミットが発生したタイミングで更新をかけています。post scriptとして先ほどpackage.jsonに定義したコマンドを実行してHTMLを生成し、デプロイスクリプトを実行します。

```yaml:circle.yml
machine:
  node:
    version: 4.2

dependencies:
  override:
    - npm install
  post:
    - npm run export

test:
  override:
    - echo "No Test"
    
deployment:
  master:
    branch: master
    commands:
      - node deploy.js
```

以下がデプロイスクリプトです。簡単ですね。

```js:deploy.js
console.log("============= start =============");

var s3 = require('s3');

var client = s3.createClient({
  s3Options: {
    accessKeyId: process.env.AWS_ACCESS_KEY,
    secretAccessKey: process.env.AWS_SECRET_KEY,
    region: process.env_AWS_REGION || 'ap-northeast-1'
  }
});

var params = {
  localDir: '_book',
  s3Params: {
    Bucket: process.env.AWS_S3_BUCKET
  }
};

var uploader = client.uploadDir(params);
uploader.on('error', function(error) {
  console.error('unable to sync: ', error);
});
uploader.on('end', function() {
  console.log("done uploading");
});
```

最後にCircleCIで以下の環境変数を設定して終わりです。

```
AWS_ACCESS_KEY=xxxxxxxxxxxxx
AWS_SECRET_KEY=xxxxxxxxxxxxxxx
AWS_S3_BUCKET=xxxxxxxxxxxxxx
```

# 最後に
これ本当に便利です。