『WEB+DB PRESS Vol.107』の「実践CircleCI」を読んだ

職場にあったので何となく目を通してみたら、思ったより初歩から説明してあったのでちゃんと読むことにした。

gihyo.jp

CircleCI、というかCIについてはほとんど何も知らないレベルだったが、ちゃんと入っていけた。
最後のほうは流し読みだから大した知識は身についていないが、取っ掛かりを持てたという意味で、意義はあった。

以前の記事に書いたように自分のサイトを構築し直してCircleCIでビルドやデプロイを行えるようにしたのだが、そのキッカケもこの特集を読んだことだった。
すごく難しい印象があったけど、「複雑なことをやろうとしなければ結構いけるかもしれない」と思えた。実際、いけた。

numb86-tech.hatenablog.com

すごく便利だし、強いプログラマたちに少し近づけた気がして楽しかった。

敷居が高そうな技術でも、興味があったりいずれ習得する必要があると思っているのなら、積極的に触ってみるのが大事だなと学んだ。
初歩的な知識でも、それがあるのとないのとでは大きく違うし、関連する記事を見つけたときも「取り敢えず読んでみよう」と積極的になれる。

以下、メモ。

第1章 はじめてのCircleCI

  • XP(エクストリーム・プログラミング)のプラクティスの一つとして、CI(継続的インテグレーション)がある
    • CIとは、コードを常にテスト、ビルドすることで、プロダクトの品質を保つことを目的としている
    • CD(継続的デリバリ)というものもある
      • デプロイを自動化することで、デプロイに伴う作業負担の減少や、スピーディーなリリースを実現する
  • コードを常にテスト、ビルドする、という部分を自動化するためのツールとしてCIツールがある
  • CircleCIはCIツールのひとつで、SaaS(Software as a Service)として提供されている
    • SaaSなので、自分でサーバーを用意する必要がない
  • CircleCIは、Docker環境の中でCIを実行する
  • CircleCIはワークフローという仕組みにより、並列処理などの複雑な実行が可能になっている
  • CircleCIの利用目的は、ビルド、テスト、デプロイの3つに大別できる
    • ビルドできる状態の維持、テストにパスした状態の維持、CD、を実現する

第2章 環境構築

  • CircleCIはcircleciコマンドでローカルでジョブを実行できる
  • circleciはDocker上で実行するので、まずDockerをインストールしておく必要がある
  • 次にCircleCIのCLIツールをダウンロードする
  • $ circleci update checkでアップデートがないかチェックできる
  • 設定ファイルは.circleci/config.yml
  • $ circleci config validateで、設定ファイルの構文チェックが出来る
  • $ circleci local executeでローカルでジョブを実行できる
    • 以下の.circleci/config.ymlが、シンプルな例
version: 2
jobs:
  build:
    docker:
      - image: circleci/node:8.11.3
    steps:
      - run: echo "hello world"
  • デフォルトではbuildジョブを実行するが--jobオプションでジョブを指定できる
  • -eオプションで環境変数を設定できる
  • $ circleci local executeを以下のようにして試してみる
    • $ circleci local execute --job build2だとnopeが出力される
    • $ circleci local execute --job build2 -e HOGE=fooだとfooが出力される
version: 2
jobs:
  build:
    docker:
      - image: circleci/node:8.11.3
    steps:
      - run: echo "hello world"
  build2:
    docker:
      - image: circleci/node:8.11.3
    steps:
      - run: echo ${HOGE-nope}

第3章 基本設定

  • 設定は全て.circleci/config.ymlに書かれる
  • 主要なキー
    • version
      • CircleCIのバージョンを指定する
    • jobs
      • ジョブを1つ以上定義する。1つしか定義しない場合は必ずbuildという名前にしないといけない。
    • docker
      • ジョブを実行する環境を記述。docker以外にもmachineなども使える。
    • steps
      • 実行したいコマンドを1つ以上定義する
      • ユーザーが自由に記述できるrunと、checkoutのようにCircleCIによって用意されているステップがある
        • runnamecommandの組み合わせという形でも記述できる
version: 2
jobs:
  build:
    docker:
      - image: circleci/node:8.11.3
    steps:
      - run:
        name: Run install
        command: yarn install
      - run: yarn build
  • CircleCIが予め用意しているステップ(の一部)

第4章 ビルド

  • ビルドするためにいつも行っていたコマンドを設定ファイルに記述することで、CircleCIでビルドを行えるようになる
  • CircleCIには、予め用意されている「ビルトイン環境変数」というものがある
    • CircleCIの実行時に最初に表示される
    • アプリケーションにビルド情報を埋め込むのに使ったりする
    • ここで一覧を見れる
  • CircleCIでは各種言語やミドルウェアのプリビルドイメージをDockerHubに用意しているので、それを利用できる
    • 言語イメージはrubynode、サービスイメージはmysqlなどがある
  • アプリケーションのビルドだけでなく、Dockerイメージのビルドも可能

第5章 テスト

  • ビルドと同じようにテストもCircleCIで自動化出来る
  • store_test_resultsというステップを使うことでレポートファイルを保存できる
  • CircleCIの並列実行には、パラレルジョブとマルチコンテナの2種類がある
    • パラレルジョブは、同じジョブを2つ以上のコンテナを使って並列に実行する方法
      • 例えばテストファイルを分割して並列実行することなどが出来る
    • マルチコンテナは、複数のコンテナを使ってジョブを並列実行する方法
      • 依存関係のない処理を並列で走らせることで、実行時間の短縮が期待できる

第6章 デプロイ

  • デプロイには、コードを直接デプロイするケースや、DockerイメージのDockerレジストリへのデプロイ、コンテナのコンテナサービスへのデプロイなど、いくつかの種類がある
  • デプロイの有無やデプロイ先をリポジトリのブランチタグの有無によって切り替えることが出来る
  • triggersを使うことで、タスクの定期実行も設定できる
  • Contextsという機能を使うことで、環境変数の値を切り替えることが出来る
    • 複数のAWSアカウントを使っているときなどに便利

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

参考資料