日記

日々のことと、Python/Django/PHP/Laravel/nodejs などソフトウェア開発のことを書き綴ります

CircleCI 2.0 入門 = その1 =

CircleCI 1.0 の終わり

CircleCI 2.0 が正式版になったことは知っていましたが、先日、CircleCI 1.0 の終了スケジュールが発表されました。終了は 2018 年 8 月 31 日です。CircleCI 1.0 は 2013 年頃から利用しているので、非常に感慨深い…。

circleci.com

こちらのブログにも書かれていますが、CircleCI 1.0 で運用している人たちは早めに移行の段取りを考えていきましょう。

CircleCI 2.0 の始まり

CircleCI 1.0 では OS が Ubuntu 14.04 か 12.04 のどちらかか、そして言語のバージョンは予めビルトインされているものから選ぶ形で CI 環境を構築していました。(もちろん CircleCI 1.0 でも自分でビルドするなりバイナリを用意すればその別の言語バージョンを利用することも可能)

CircleCI 2.0 では CI の実行環境が Docker コンテナになりました。CircleCI 2.0 の設定は .circleci/config.yml に定義し、次のように書きます。ちなみに circle.yml.circleci/config.yml が同居している場合は、 CircleCI 2.0 が優先的に利用されるようです。

version: 2

jobs:
  hello_world_job:
    docker:
      - image: circleci/python:3.6.5-stretch

    working_directory: ~/repo

    steps:
      - checkout

      - run:
          name: execute hello world
          command: |
            echo 'hello world'

この設定では hello_world_job という CircleCI が実行するジョブを定義し、ジョブを実行する環境として Docker イメージに circleci/python:3.6.5-strech を指定しています。この Docker イメージは CircleCI が公式に用意しているものです。内容としては Debian 9 (stretch) と buildpack-deps をベースに、Python 3.6.5 を追加したイメージです。

GitHub リポジトリの checkout 先として ~/repo を設定します。この working_directory がリポジトリのルートディレクトリとなります。steps の run に記述するコマンドは working_directory をカレントディレクトリとして実行されます。

CircleCI が用意している公式のイメージは、次の URL にまとまっています。

circleci.com

イメージ名の命名規則に基づいて、イメージ内にインストールされるパッケージの内容が変えられているようです。Image Variants のセクションに -node -browser -node-browser の説明があります。ジョブの内容に応じて、必要なパッケージがインストールされたイメージを選択すると CI の実行時間が短縮されて効率化できます。

カスタムイメージを使う

先ほどの設定では CircleCI が提供する Docker イメージを使用しましたが、Docker Hub に docker push することで自分で作成したイメージを CircleCI 上の CI に利用することもできます。

    docker:
      - image: circleci/python:3.6.5-stretch

設定ファイルで指定した circleci/python:3.6.5-stretch は、Docker Hub の CircleCI アカウントの Python リポジトリ、3.6.5-stretch のタグを利用する設定です。

では、実際に自前のカスタムイメージを作成して、CircleCI で利用してみます。使用する Dockerfile は GitHub リポジトリに置いてあります。

以下のようにして、docker push まで行っています。

$ git clone git@github.com:tnamao/dockerfiles.git
$ cd ./dockerfiles/circleci/awscli
$ docker build -t "tnamao/circleci-awscli:20180411" .
$ docker push tnamao/circleci-awscli:20180411

ちなみに dockerfile の内容は CircleCI の公式イメージに AWS CLI を追加したものです。

FROM circleci/python:3.6-stretch

RUN sudo apt-get update \
  && sudo apt-get install -y \
         groff-base \
         less \
         python \
         python-pip \
         python-dev \
  && sudo pip install awscli

ADD .aws/ /home/circleci/.aws

このイメージを CircleCI 上で利用するために設定を書き換えします。

version: 2

jobs:
  hello_world_job:
    docker:
      - image: tnamao/circleci-awscli:20180411

    working_directory: ~/repo

    steps:
      - checkout

      - run:
          name: execute hello world
          command: |
            echo 'hello world'

docker のイメージ名だけ変更しました。これで GitHub に変更を push すると自前のカスタムイメージで CI が実行されます。

ちなみに AWS CLI は CircleCI 1.0 の時に Project Settings からアクセスキーを設定しました。CircleCI 2.0 でもそれは変更ありません。CI 環境のコンテナが起動する際に、自動的に環境変数に設定がアサインされます。そのため、AWS CLI コマンドは自動的にアクセスキーを認識する状態になっています。

カスタムイメージ作成の推奨事項

先ほどの CircleCI の Python Docker Hub リポジトリ を見ると次のように CircleCI のイメージ作成のポリシーが書かれています。

However, we frequently found them lacking some tools making them appropriate for dev/CI use. CircleCI publishes this image to extend the images in the following ways:

  1. Common tools used in development and CI are installed e.g. git, ssh, tar, ca-certificates, curl, wget.
  2. Docker tools: latest docker, docker-compose, and dockerize are installed
  3. Variants for common use cases, e.g. images with common browsers installed and configured to run in a containized environment
  4. Use a non-root user (namely circleci) by default. Many applications refuse to run as run (e.g. chrome) or their behavior differs when run as root (e.g. tar file ownership behavior)

CircleCI も buildpack-deps のイメージをベースに作られていますが、OS を Ubuntu にしたイメージを作る時などは参考にできそうです。ユーザは root でも動作上問題なければ無視しても良さそう。

プライベートリポジトリのカスタムイメージの利用

作成したイメージをプライベートリポジトリにおいて、CircleCI から利用したいケースもあると思います。CircleCI 2.0 では Docker Hub のプライベートリポジトリの他、ECR, GCR, Quay にも対応しています。

AWS ECR とそれ以外で認証情報の設定方法が異なりますが、あまり大差なく利用できるようです。上記の URL から設定の部分だけを抜粋すると

  • AWS の場合
    docker:
      - image: account-id.dkr.ecr.us-east-1.amazonaws.com/org/repo:0.1
        aws_auth:
          aws_access_key_id: $AWS_ACCESS_KEY_ID_STAGING
          aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_STAGING
    docker:
      - image: acme-private/private-image:321
        auth:
          username: mydockerhub-user  # can specify string literal values
          password: $DOCKERHUB_PASSWORD  # or project environment variable reference

GCR と Quay の場合は quay.io/project/image:tagドメイン名から書かないといけないようです。

ジョブとワークフローの利用

CircleCI 1.0 では、ジョブの流れが dependency test deployment と予め決まった流れしか利用できませんでした。 CircleCI 2.0 になり、ワークフローとジョブを定義するときの自由度が圧倒的に高まりました。

ワークフローでは次のような定義ができます。

  • 複数のジョブを羅列して一つのワークフローにまとめる
    • ワークフローにまとまったジョブは、並列に実行される
  • テストの後にデプロイを実行などの依存関係も定義できる
  • ジョブを実行するブランチを指定できる
    • 1つのワークフローを定義して、テストは全てのブランチ、デプロイは master ブランチだけというのも可能
    • 実行対象のブランチ指定は、jobs 側でも指定可能
  • ジョブの途中にユーザ承認の追加
    • CircleCI dashboard から承認ボタンを押して次のジョブに進めると言ったことが可能
    • 逆にキャンセルして、それ以降のジョブを実行させないこともできる

では設定例を見ていきます。まずは単純なワークフローで、ワークフローの記述は、jobs とは別の定義になります。

version:2

jobs:
  test:
    ...
  deploy:
    ...

workflows:
  version: 2
  app_ci_workflow:
    jobs:
      - test
      - deploy

この設定では、 app_ci_workflow というワークフローに testdeploy というジョブを設定しました。何も指定が無い場合、CircleCI 2.0 では可能であれば複数コンテナが起動して並列にジョブが実行されます。

このため、test, deploy の順で実行されることを期待してもテストに通る前に deploy ジョブが実行されてしまいます。テストの後にデプロイを実行するには次のようにします。

version:2

jobs:
  test:
    ...
  deploy:
    ...

workflows:
  version: 2
  app_ci_workflow:
    jobs:
      - test
      - deploy:
          requires:
            - test

requires という設定を追加し、test を指定することで test が終わってから deploy ジョブが実行となります。 requires は複数のジョブが指定可能なので、CircleCI 2.0 ではタスクを上手く分割して並列に処理させることで、CI の処理時間の短縮ができそうです。

続いて deploy が全てのブランチで実行されてしまうと困るので、 master ブランチだけで実行されるように設定をします。

version:2

jobs:
  test:
    ...
  deploy:
    ...

workflows:
  version: 2
  app_ci_workflow:
    jobs:
      - test
      - deploy:
          requires:
            - test
          filters:
            branches:
              only:
                - master

階層が深くなりましたが、 filters の属性を使って実行対象を設定します。only の他は ignore で対象外ブランチの指定も可能です。また、branches の替わりに tags を使うことで対象をタグにできます。branches と tags は排他では無く同時に定義できます。

続いて、ワークフローに承認フローを入れてみます。

version:2

jobs:
  test:
    ...
  deploy:
    ...

workflows:
  version: 2
  app_ci_workflow:
    jobs:
      - test
      - director_confirmation:
          type: approval
          requires:
            - test
      - deploy:
          requires:
            - director_confirmation
          filters:
            branches:
              only:
                - master
  app_ci_workflow:
    jobs:
      - test
      - deploy:

ディレクターチェックを入れてみました。ワークフローに定義した director_confirmation がそれに当たります。 これで test ジョブが完了後に deploy には進まず、CircleCI dashboard からの承認が必要なワークフローになりました。deploy ジョブの requires に指定するジョブも director_confirmation と変わっていることに注意してください。

スケジュールされたワークフロー

CircleCI 2.0 になって新たな機能として、ワークフロー起動のスケジューリングがあります。指定した時間にワークフローを起動できるもので、思いっきり cron のそれです。

設定方法は workflows 以下に次のように定義します。

workflows:
  version: 2
  nightly:
    triggers:
      - schedule:
          cron: "0 0 * * *"
          filters:
            branches:
              only:
                - master
                - develop
    jobs:
      - coverage
      - packaging
          requires:
            - coverage

jobs の替わりに triggers を指定してます。こうすると、nightly のワークフローは git push されたタイミングでは起動せず、 schedule に設定した cron 定義により、ワークフローが起動します。これで、Nightly Build も手軽に行えるようになりました。

ただ cron の設定は */2 などのシンタックスに対応してないようです。とはいえ、必要十分な感じもしますが…。

まとめ

  • Docker ベースになった
    • 起動が速い
    • Docker イメージは公式が色々用意してるし、カスタムイメージも手軽に使える
    • カスタムイメージは推奨事項があるけど、ほどほどにそれを守り自由に作れる
  • CI のワークフローは 1.0 と比較して自由度がかなり向上した
    • ジョブ単位で並列実行
    • 依存関係を付けて直列実行もできる
    • approval でユーザによる承認機能も付いた
    • スケジュールされたワークフロー起動ができる (cron)

次回は jobs で定義できることを掘り下げてやっていきます。