Gatsby + CircleCI + AWS でサイトを構築した

サイトを作り直した。
それに伴い、デプロイのフローも整備した。
具体的には CircleCI でビルドを行い、S3 へのアップロードや CloudFront のキャッシュのクリアまで行う。

ソースは全部公開してあるので参考にどうぞ。
github.com

前提として、予めサイトは S3 で公開済みであり、CloudFront からの配信、独自ドメインHTTPSの設定も行ってあった。

なので今回行ったのは、コンテンツの作り直しと、手動で行われていた S3 へのアップロードやキャッシュのクリアの自動化。
CircleCI に入門する機会にしたいという目的もあった。

Gatsby

まずはコンテンツの作り直し。
Gatsbyで作成した。

Gatsby は、React を使ってスタティックなサイトをジェネレートするフレームワーク
このスライドの説明が分かりやすいと思う。

作ったサイトはこれ。
https://numb86.net

ほとんど中身はないのでHTMLを直打ちしたほうが速いだろうが、Gatsby に触ってみるというのも目的の一つだったので問題ない。
何より、マークアップより React を書いているほうが楽しい。個人プロジェクトなのだから楽しさが重要。

まずはインストール。

$ npm i -g gatsby-cli

その後、プロジェクトを作成。とにかくシンプルに始めたかったので、スターターはhello-worldにした。

$ gatsby new numb86.net https://github.com/gatsbyjs/gatsby-starter-hello-world
$ cd numb86.net

あとは、チュートリアルを参考にしてサイトを作っていく。

$ gatsby buildするとpublicディレクトリに成果物が出力される。
なので後はこのディレクトリの内容を S3 にアップロードすればいい。

サイト内容に問題がなければ次は、CircleCI を使ってビルドやデプロイを自動化する。
だがその前に、CircleCI が S3 へのアップロードを行えるよう、AWS の権限を設定する必要がある。

AWS IAM

AWS の認証や認可を操作するには、IAM という機能を使う。

まずは以下の内容のポリシーを作成。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": ["arn:aws:s3:::numb86.net", "arn:aws:s3:::numb86.net/*"]
    }
  ]
}

次に、このポリシーを付与されたユーザーを作成する。
するとアクセスキーが生成されるので、それをメモしておく。

CircleCI

GitHub のアカウントで CircleCI にサインアップ。
Add Projectsで自分のリポジトリの一覧が表示されるので、対象のリポジトリを選択する。
Start buildingをクリックするとCIが実行される。設定ファイルがないので失敗するが、CircleCI が実行されるのは確認できた。あとは設定を整えていけばいい。

まず、環境変数の設定。
CircleCI の設定画面で行えるので、AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYを登録する。値はそれぞれ、先程作成した IAM のアクセスキー。

次に、プロジェクト内に.circleci/config.ymlを作成する。これが設定ファイルになる。

version: 2
jobs:
  build:
    docker:
      - image: circleci/node:latest
    branches:
      only: master
    steps:
      - checkout
      - restore_cache:
          keys:
            - dependencies-
            # fallback to using the latest cache if no exact match is found
            - dependencies-
      - run:
          name: Install
          command: yarn install
      - save_cache:
          paths:
            - node_modules
          key: dependencies-
      - run:
          name: Gatsby build site
          command: yarn build
      - run: curl -L https://github.com/bep/s3deploy/releases/download/v2.2.0/s3deploy_2.2.0_Linux-64bit.tar.gz | tar xvz
      - run:
          name: deploy
          command: ./s3deploy -source=public/ -region=ap-northeast-1 -bucket=numb86.net

この状態でmasterにコミットしてpushすると、自動的に CircleCI が実行され、デプロイまで行われるはず。

デプロイにはs3deployというライブラリを使っている。

github.com

CloudFront Create Invalidation

コンテンツは CloudFront から配信しているので、デプロイ時にキャッシュのクリアも行うようにした。

まずは、IAM のポリシーを変更して、CloudFront も操作できるようにした。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::numb86.net",
                "arn:aws:s3:::numb86.net/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudfront:GetDistribution",
                "cloudfront:CreateInvalidation"
            ],
            "Resource": "*"
        }
    ]
}

最初はこのドキュメントを参考にしてarn:aws:cloudfront::AWS_ACCOUNT_ID:distribution/DISTRIBUTION_IDのようにリソースを指定しようとしたが、以下のメッセージが出てしまい断念。

ポリシーのアクションはリソースレベルのアクセス許可をサポートしておらず、 すべてのリソース を選択する必要があります

そのため"Resource": "*"とした。

次に、CircleCI にCLOUDFRONT_DISTRIBUTION_IDという環境変数を設定して、キャッシュのクリアを行うディストリビューションを指定する。

最後に、.circleci/config.ymlにキャッシュのクリアに関する記述を追加する。

       - run:
           name: deploy
-          command: ./s3deploy -source=public/ -region=ap-northeast-1 -bucket=numb86.net
+          command: ./s3deploy -source=public/ -region=ap-northeast-1
+                    -distribution-id=$CLOUDFRONT_DISTRIBUTION_ID -bucket=numb86.net

これでmasterにコミットしてpushすれば、ビルド、デプロイ、キャッシュのクリアを全て、CircleCI が自動的に行われるはず。

しかもs3deployは優秀で、キャッシュのクリアが必要なときだけ、行ってくる。つまり配信されるコンテンツに変更がない場合はクリアを行わないという判断を、自動的に行なってくれる。

参考資料