Carpe Diem

備忘録

CircleCI 2.0 でworkflowを使ったtagからのデプロイ

概要

CircleCI 2.0でtagからのビルド&デプロイをできるようにします。

主に使う機能としては

  • workflow
  • cache

です。workflowはビルドパイプラインのようなもので、実行ジョブを細かく分けて順に実行させることができます。

f:id:quoll00:20180215234304p:plain

上の例ではmasterブランチにマージ後、testジョブが実行されてからdeploy_devというdev環境にデプロイするジョブが実行されています。

ジョブ毎に分かれているお陰で、以下のように途中から(失敗したジョブから)実行することも可能です。

f:id:quoll00:20180215234536p:plain

基本的な使い方

直列実行のworkflow

f:id:quoll00:20180216004635p:plain

ref: Using Workflows to Schedule Jobs - CircleCI

この図のように幾つかのジョブを直列で実行したい場合は

workflows:
  version: 2
  build-test-and-deploy:
    jobs:
      - build
      - test1:
          requires:
            - build
      - test2:
          requires:
            - test1
      - deploy:
          requires:
            - test2

のように書きます。deployを実行したい場合、requiresという依存関係を見ると

  • deployはtest2を必要とする
  • test2はtest1を
  • test1はbuildを

となり、結果として

build -> test1 -> test2 -> deploy

の順に実行されます。

deployの発火条件はこの例では書かれていませんので注意

並列実行のworkflow

f:id:quoll00:20180216005057p:plain

ref: Using Workflows to Schedule Jobs - CircleCI

並列に実行したい場合は

workflows:
  version: 2
  build_accept_deploy:
    jobs:
      - build
      - acceptance_test_1:
          requires:
            - build
      - acceptance_test_2:
          requires:
            - build
      - acceptance_test_3:
          requires:
            - build
      - acceptance_test_4:
          requires:
            - build
      - deploy:
          requires:
            - acceptance_test_1
            - acceptance_test_2
            - acceptance_test_3
            - acceptance_test_4

こんな感じになります。
buildジョブ後にacceptance_test_1~acceptance_test_4が並行に実行され、それらがすべて完了するとdeployジョブが実行されます。

deployの発火条件はこの例では書かれていませんので注意

特定のブランチで発火させる

ブランチ毎に発火条件を変えたい要件があると思います。その場合

workflows:
  version: 2
  dev_stage_pre-prod:
    jobs:
      - test_dev:
          filters:
            branches:
              only:
                - dev
                - /user-.*/
      - test_stage:
          filters:
            branches:
              only: stage
      - test_pre-prod:
          filters:
            branches:
              only: /pre-prod(?:-.+)?$/

このようにfilters:branches:の組み合わせで特定のジョブを実行できます。
この例だと

  • devブランチ、user-xxxブランチに変化があったらtest_devを実行
  • stageブランチに変化があったらtest_stageを実行
  • pre-prodブランチに変化があったらtest_pre-prodを実行

と言った感じです。

gitのtagに連動させる

gitのtagを発行したらデプロイしたいという要件は多いと思います。この場合

workflows:
  version: 2
  build-n-deploy:
    jobs:
      - deploy:
          filters:
            tags:
              only: /^release-v.*/
            branches:
              ignore: /.*/

のようにfilters:tags:セクションを設定すれば発火します。この例だとrelease-v1.0.0といったtagで発火します。
またbranches:セクションでignore: /.*/としていることでブランチの変化(マージやプルリク)に反応しないようにしています。

ただtagの場合注意が必要で、require:に設定しているジョブも同じtags:セクションの設定が必要という点です。

例えば

build -> test -> deploy

と実行するworkflowの場合、

workflows:
  version: 2
  build-n-deploy:
    jobs:
      - build:
          filters:
            tags:
              only: /^config-test.*/
      - test:
          requires:
            - build
          filters:
            tags:
              only: /^config-test.*/
      - deploy:
          requires:
            - test
          filters:
            tags:
              only: /^config-test.*/
            branches:
              ignore: /.*/

このように、どのジョブにもtags:セクションにonly: /^config-test.*/という設定をしなくてはいけません。

実際の業務でのyaml

以下の要件を満たすyamlを用意します。

  • プルリク時はtestを実行
  • マージされたらtestを実行、その後dev環境へビルド
  • tagが発行されたらtestを実行し、その後stg環境へビルド

それでは設定を紹介します。

defaults: &defaults
  working_directory: /go/src/github.com/jun06t/sample-api
  environment:
    TZ: "/usr/share/zoneinfo/Asia/Tokyo"

version: 2
jobs:
  test:
    <<: *defaults
    docker:
      - image: circleci/golang:1.9
    steps:
      - checkout
      - restore_cache:
          key: vendor-{{ checksum "Gopkg.lock" }}
      - run:
          name: Setup For Test
          command: |
            if [ ! -e vendor ]; then
              dep ensure
            fi
      - run:
          name: Run Tests
          command: |
            make test
      - save_cache:
          key: vendor-{{ checksum "Gopkg.lock" }}
          paths:
            - vendor

  deploy_dev:
    <<: *defaults
    docker:
      - image: circleci/golang:1.9
    steps:
      - checkout
      - restore_cache:
          key: vendor-{{ checksum "Gopkg.lock" }}
      - setup_remote_docker
      - run:
          name: Build docker image
          command: |
            make build
      - deploy:
          name: Deploy container
          command: |
            ./scripts/deploy.sh dev latest

  deploy_stg:
    <<: *defaults
    docker:
      - image: circleci/golang:1.9
    steps:
      - checkout
      - restore_cache:
          key: vendor-{{ checksum "Gopkg.lock" }}
      - setup_remote_docker
      - run:
          name: Build docker image
          command: |
            make build
      - deploy:
          name: Deploy container
          command: |
            ./scripts/deploy.sh stg ${CIRCLE_TAG}


workflows:
  version: 2
  test_and_deploy:
    jobs:
      - test:
          filters:
            branches:
              only: /.*/
            tags:
              only: /.*/
      - deploy_dev:
          requires:
            - test
          filters:
            branches:
              only: master
      - deploy_stg:
          requires:
            - test
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^v[0-9](\.[0-9]){2}$/

workflowの説明

testジョブ

      - test:
          filters:
            branches:
              only: /.*/
            tags:
              only: /.*/

テストはプルリクやマージで発火するようにしています。またtagに連動する際にrequire:しているので、tags:セクションも書いています。

deploy_devジョブ

      - deploy_dev:
          requires:
            - test
          filters:
            branches:
              only: master

masterブランチにマージされたら発火するようにしています。testジョブをrequire:しているので、テスト後に実行されます。

deploy_stgジョブ

      - deploy_stg:
          requires:
            - test
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^v[0-9](\.[0-9]){2}$/

v1.0.0といったgit tagが発行されたら発火するようにしています。testジョブをrequire:しているので、テスト後に実行されます。

save_cache

golangvendorディレクトリに依存ライブラリを保持します。test時は良いのですが、その後のdeployフローでは再取得するのは無駄です。なので

      - save_cache:
          key: vendor-{{ checksum "Gopkg.lock" }}
          paths:
            - vendor

でキャッシュします。key名をlockファイルのハッシュベースで管理することで、lockファイルが更新されたら新しいキャッシュを作成&利用できます。

利用する際は

      - restore_cache:
          key: vendor-{{ checksum "Gopkg.lock" }}

でできます。
またvendorディレクトリの有無でdep ensureを実行するか判断します。

      - run:
          name: Setup For Test
          command: |
            if [ ! -e vendor ]; then
              dep ensure
            fi

tag名を利用

      - deploy:
          name: Deploy container
          command: |
            ./scripts/deploy.sh stg ${CIRCLE_TAG}

${CIRCLE_TAG}という環境変数でtag名を利用できます。workflowでジョブが分けられていてもちゃんと参照できます。

環境変数の一覧は

Using Environment Variables - CircleCI

で確認できます。

参考