AWSでドキュメントサイトを最速で公開する方法。自動デプロイもあるよ！

Global Mobility Service株式会社でインフラエンジニアをしているodaです。
[Global Mobility Serviceのアドベントカレンダー](https://qiita.com/advent-calendar/2019/global-mobility-service)の1日目です。

当社のインフラ環境では、AWSを全面的に採用しています。
先日、ユーザ向けにドキュメントサイトを公開しました。
このドキュメントサイトをどのように構築したかを紹介したいと思います。

# 概要
- ドキュメント作成にはMkDocsを使い、Markdownで静的サイトを作成する
- S3とCloudFrontで静的サイトを公開する
- ドキュメントのリポジトリにはGitHubを利用する
- ドキュメントの変更のたびにCodeBuildで最新のドキュメントをデプロイする

# MkDocsを使う
MkDocsはMarkdownでドキュメントサイトを作成可能な静的サイトジェネレーターです。
https://www.mkdocs.org/

## インストール

pipでインストールします。 
 
```sh
$ pip install mkdocs
```

またはbrewでもインストール可能です。

```sh
$ brew install mkdocs
```

## プロジェクト作成

MkDocsをインストールすると、`mkdocs`コマンドが使えます。
プロジェクトを作成します。

```sh
$ mkdocs new my-project
$ cd my-project
```

`docs`ディレクトリにMarkdownファイルを作成して、ドキュメントを作ることができます。

## ローカルで動作確認

ローカルで開発用のサーバを立ち上げて、動作を確認することができます。

```sh
$ mkdocs serve
```

## 静的サイトのエクスポート

Markdownで書いた内容をhtml等の静的コンテンツにして、エクスポートできます。

```sh
$ mkdocs build
```

デフォルトだと、`site`ディレクトリに静的コンテンツが作成されます。

# S3に静的コンテンツを配置する

S3のバケットを作成します。

```sh
$ BUCKET_NAME=hi1280-project
$ aws s3api create-bucket --bucket ${BUCKET_NAME} --region us-east-1
```

静的コンテンツをS3に配置します。
`site`ディレクトリに静的コンテンツがあるので、S3のバケットとローカルのディレクトを同期するコマンドでコンテンツを配置しています。

```sh
$ aws s3 sync site s3://${BUCKET_NAME} 
```

ルートディレクトリ、サブディレクトリに配置されたindex.htmlを表示するために、静的ウェブサイトホスティングを有効にします。

```
$ aws s3 website s3://${BUCKET_NAME}  --index-document index.html
```

S3のバケットをパブリックでアクセス可能にします。

```sh
$ cat << EOF > public.json
{
    "Version": "2012-10-17",
    "Id": "PublicRead",
    "Statement": [
        {
            "Sid": "ReadAccess",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::${BUCKET_NAME}/*"
        }
    ]
}
EOF
$ aws s3api put-bucket-policy --bucket ${BUCKET_NAME} --policy file://public.json
```

# CloudFrontで公開する

## CloudFrontのリソースを作成する

CloudFrontからS3による静的ウェブサイトホスティングのURLを参照します。
S3のバケットを直接参照するとルート指定でindex.htmlが表示できないので注意が必要です。

```sh
$ aws cloudfront create-distribution --origin-domain-name ${BUCKET_NAME}.s3-website-us-east-1.amazonaws.com
```

## サイトを表示する

CloudFrontのドメイン名にアクセスします。
![スクリーンショット 2019-11-29 10.25.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80244/742dad26-a438-2bfd-1f81-14ea57abf6c9.png)

ちゃんと表示されてます！

この時点のソースをGitHubのリポジトリにPushしておきます。

# CodeBuildでデプロイする

## CodeBuildのプロジェクト作成
設定が必要な箇所のみを示します。その他の項目はデフォルトの設定で作成しています。

GitHubアカウントとの接続を行います。
詳細は以下にあります。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-access-tokens.html

![スクリーンショット 2019-11-29 12.07.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80244/16bb1935-a4c7-f223-6b35-baaaf25b5447.png)

リポジトリが変更されるたびにビルドを実行するように設定します。

![スクリーンショット 2019-11-29 12.08.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80244/53608152-4cc7-40f3-f071-8f46a3e6b6c0.png)

この後のbuildspecでPythonのランタイムを使いたいので、Amazon Linux 2 standard image 1.0を指定します。

![スクリーンショット 2019-11-29 12.08.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80244/758d1e65-5496-954b-f24d-6f6e77e74ba4.png)

## buiidspecファイルの用意

buildspecファイルはCodeBuildの実行内容を決めるファイルです。
プロジェクトの直下にこのファイルを作成して、リポジトリにPushします。

```yaml:buildspec.yml
version: 0.2

phases:
  install:
    runtime-versions:
      python: 3.7
  pre_build:
    commands:
      - pip install --upgrade pip
      - pip install mkdocs
  build:
    commands:
      - mkdocs build
      - aws s3 sync --delete site s3://hi1280-project
      - aws cloudfront create-invalidation --distribution-id E2XXXXXX --paths '/*'
```
`aws s3 sync`に`--delete`オプションをつけることで、同期元にないファイルは同期先でも削除されます。
`aws cloudfront create-invaidation`で、CloudFrontのキャッシュを削除して、最新のドキュメントが直ぐに反映されるようにしています。

## CodebuildのIAMロールに権限を設定

CodeBuildのプロジェクト作成時に、CodeBuild向けのIAMロールが作られています。
CodeBuildのビルドではS3とCloudFrontにアクセスするので、IAM権限を設定します。

![スクリーンショット 2019-11-29 12.18.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80244/7e172bea-1820-dc61-2f97-23da98449b65.png)

# 完成

これ以降はリポジトリの変更のたびにドキュメントがデプロイされます。

# この方法の良いところ
AWSを使うことでAWS上の様々なサービスとの連携が可能になります。

## Lambda@Edge
CloudFrontにはLambda@Edgeがあります。
これはCloudFrontから静的コンテンツへのHTTPリクエストやレスポンスの間にLambda関数を実行できる機能です。

今回のドキュメントサイトを構築するにあたり、限られたユーザのみからアクセスを可能にするために認証機能が必要でした。
Lambda@Edgeを使用することで静的サイトのコンテンツを変更することなく、認証機能を追加できました。

以下の例にあるようにLambda@Edgeは様々な利用が可能です。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html

# まとめ
最速かどうかは正直よく分からないですが、気軽にドキュメントサイトを公開できたと思います。
AWSをメインのインフラとして使用している場合には参考にしてもらえればと思います。
今回はMkDocsについてあまり触れませんでしたが、簡単に使えて機能も豊富な静的サイトジェネレーターなのでこちらもオススメです。

# おわりに
Global Mobility Serviceでは[AWSを使い倒したいエンジニア](https://www.global-mobility-service.com/recruit/tech.html#tech-requirements)を募集しています。
明日の[アドベントカレンダー](https://qiita.com/advent-calendar/2019/global-mobility-service)の担当は期待の新星[Shiruba](https://qiita.com/Shiruba)さんです。