たまにさわることがあるが、基礎的な概念を思い出すのに毎回けっこうな時間がかかるため、自分用にまとめた。
(CircleCI の version は 2.1 前提)
CircleCI とは
CI/CD パイプラインを実現する CI/CD ツールの一つ。
CI (continuous integration 継続的インテグレーション)
コードの変更を自動的にテストするプロセス。
CD (continuous delivery 継続的デリバリー)
CIのテストを通過したコードを自動的にデプロイするプロセス。
CircleCI では、Git ホスティングサービス(GitHub 等)のリポジトリへの push をトリガーに、設定された工程にしたがってリポジトリのcloneしプロジェクトのビルド、テスト、デプロイを実行する。
CircleCI での CI/CD パイプラインの設定
pipeline
pipeline
は複数のworkflow
で構成されるものである。
pipeline
の設定はプロジェクトルート直下の.circleci/config.yml
に記述する。
pipeline
は CircleCI の API によって実行される。
API のトリガーパターンは以下の通り。
- CircleCIと連携するGitホスティングサービス上のリポジトリのブランチにPUSHされたとき(最も基本的なトリガー)
- スケジュールした日時に達したとき
- CircleCI の Web アプリの UI で手動実行したとき
- 直接 CircleCI の API を実行したとき
workflow
workflow
は複数の job
の並びで構成されるものである。
workflow
に含まれる各 job
の実行トリガーは、並列実行/順次実行/手動承認から設定可能である。
CircleCI は、API がトリガーされると全ての workflow
を並列に実行しようとする。
job
job
は複数のstep
の並びで構成されるものである。
step
具体的な処理1つ1つ。
.circleci/config.yml
概要
CircleCIのpilelineは.circleci/config.yml
で設定し、主に以下のようなブロックで構成される。
具体的な記述の簡単なサンプルは以下のような形になる。
# CircleCI のバージョン
version: 2.1
# ジョブ群を列挙
jobs:
# job1の設定
job1:
# 実行環境の設定(下記はDockerコンテナで実行する場合を直接記述したもの。)
docker:
# ベースイメージを指定
- image: <ベースイメージ>
# job1内で実行する一連のstepを記述
steps:
- <step1>
- <step2>
...
# job2の設定
job2:
...
# workflow群を列挙
workflows:
# workflow1の設定
workflow1:
# workflow1内で実行するジョブのリストと実行トリガーを列挙
jobs:
- job1:
<job1の実行条件>
- job2:
<job2の実行条件>
workflow2:
...
参考
job の実行トリガー設定
並列実行
実行トリガーに何も設定しない場合、それらのジョブは同時実行される
workflows:
sample_workflow:
# job1とjob2を同時実行
jobs:
- job1:
- job2:
順次実行
- <ジョブ2名>
requires:
- <ジョブ1名>
と設定すると、ジョブ2の実行トリガーを「ジョブ1の正常完了後」とできる。
これを応用して、以下のようにファンイン/ファンアウトも実現できる。
workflows:
sample_workflow:
# job1実行後にjob2, job3, job4を同時実行し、すべて完了したらjob5を実行する
jobs:
- job1:
- job2:
requires:
- job1
- job3:
requires:
- job1
- job4:
requires:
- job1
- job5:
requires:
- job2
- job3
- job4
手動承認での実行
- <ジョブ名>
type: approval
とすると、CircleCI のワークフローのページで「Approval」ボタンを押下するまでジョブの実行を保留できる。
以下のように requires とも併用可能。
workflows:
sample_workflow:
# job1実行後の手動承認後にjob2を実行
jobs:
- job1:
- job2:
type: approval
requires:
- job1
参考
executors
jobの実行環境の事前定義である。
executors:
<executor名>:
<実行環境の設定>
定義した executor は以下の箇所で参照できる。
jobs:
<job名>:
executor: <executor名>
例えば以下のように記述し、この場合 sample-job の step は、Docker イメージ cimg/base を元に起動されるコンテナ上で実行される。
executors:
sample-executor:
docker:
- image: cimg/base
...
jobs:
sample-job:
executor: sample-executor
steps:
...
参考: Executor
commands
一連のstepの事前定義である。
commands:
<command名>:
<一連のstep>
定義したcommandは以下の箇所で参照できる。
jobs:
<job名>:
executor: <executor名>
steps:
- <command名>
parameters
型とデフォルト値含めて定義可能な引数である。デフォルト値を定義できることから、定数を定義する用途でも使える。
parameters は、特定の箇所でしか定義できない。
定義の記述は、以下のように行う。
paramters:
<parameter名>:
type: <型>
default: <デフォルト値>
parameter の値を渡せる箇所も限られている。
parameter を定義できる箇所、値を渡せる箇所を一覧化すると以下の通り。
定義できる箇所 | 値を渡せる箇所 |
---|---|
pipeline(.circleci/config.yml 内の第一階層) |
なし。CircleCIのAPI実行時に渡す。具体的にはCircleCIのUI上や直接APIを叩くときに渡すことができる。ブランチへのPUSH時は渡せず、確定でデフォルト値が適用されることになる(模様)。 |
executors.<executor名> |
jobs.<job名>.executor |
commands.<command名> |
jobs.<job名>.steps.<command名> |
jobs.<job名> |
workflows.<workflow名>.<job名> |
pipeline レベルの parameter は、.circleci/config.yml
内ならどこでも<< pipeline_parameters.<parameter名> >>
で参照できる。
executor, command, job レベルの parameter は、それぞれの定義内限定で<< parameters.<parameter名> >>
で参照できる。
なお、executor を parameter 値を渡しつつ呼び出す場合、以下のような記述になる。
jobs:
<job名>:
executor:
name: <executor名>
<parameter名>: <parameter値>
その他詳細メモ
.circleci/config.yml 内の階層を意識しつつまとめておく
-
parameters: パイプラインパラメータ。config.yml内でどこからでも呼び出せる変数を定義できるグローバル変数っぽいやつ。
- <パラメータ名>
- type: パラメータの型
- default: デフォルト値。ここを書き換えられるのは、APIでパイプラインを起動したときだけっぽい。というわけでAPIで起動しない場合は、parametersは変数っていうか定数というイメージでよさそう。
- <パラメータ名>
-
jobs
- <ジョブ名>
- parameters: ジョブレベルのparameterを定義する。イメージとしては、ジョブ実行時に渡せる引数の定義。引数はworkflowで渡す。
- <パラメータ名>: パラメータ名
- default: workflowでのジョブ呼び出し時に何も指定されなかった場合に採用されるパラメータの値。
- type: パラメータの型。
- <パラメータ名>: パラメータ名
- docker
- image: cimg/baseというのをよく指定するが、これはCircleCIが作成したイメージである。CircleCI コンビニエンスイメージという意味らしい。ビルドを実行する際に便利なツールが含まれている。
- working_directory: stepsを実行するディレクトリを指定。mkdir & cdに相当。
- steps
- checkout: SSHでGitリポジトリからプロジェクトソースをチェックアウトする。
- run: シェルコマンドを実行する。
- name: CircleCIのUI上で表示される「実行工程のタイトル」である。runに限らず、大体のステップで指定できる。何も設定しないと、各ステップのデフォルト名が表示される。
- command: 実行するコマンドを記述。
command: |
として複数行書いていくのが主流か。長くなリすぎる場合はシェルスクリプト化してそれを実行するようにした方が可読性が高そう(拡張子によっていい感じに識別子を色分け化してくれるエディタを使っている場合は特に)。 - working_directory: mkdir & cdに相当。以降のステップには影響しない。以降のステップに影響するやつがほしいんだけど、そういうのはないみたい・・・残念。
- save_cache: キャッシュキーを指定して、任意のファイルをキャッシュとして保存できる。ただし、一度使ったキャッシュキーを指定しても、キャッシュを上書きはしてくれず、工程がスキップされる。保存したい場合は、一意のキャッシュキーになるよう工夫する必要がある(checksum を使ったり)。
- restore_cache: キャッシュキーを指定して、キャッシュを復元できる。指定したキャッシュキーは前方一致で検索される。複数のキャッシュがヒットする場合、最新のキャッシュが採用される。
- store_artifacts: 指定したファイルを保存する。保存したファイルはCircleCIのUI上からDLできる。
- parameters: ジョブレベルのparameterを定義する。イメージとしては、ジョブ実行時に渡せる引数の定義。引数はworkflowで渡す。
- <ジョブ名>
-
workflows
- <ワークフロー名>
- jobs
- <ジョブ名>: ワークフローで実行するジョブ名
- name: この実行ジョブの別名。requiresでもこの別名で指定できるようになる。CircleCIのUI上で表示されるジョブのタイトルでもあり、指定していない場合は<ジョブ名>が表示される。
- <パラメータ名>: ジョブに渡すパラメータ値を指定。
- requires: 先に実行完了していなければならないジョブ群を指定
- <ジョブ名>: 先に指定していなければならないジョブ名を指定
- filters: ワークフロー実行対象のGitブランチ or タグを限定する場合に設定
- branches: ワークフロー実行対象のブランチを限定する場合に設定
- only: ワークフロー実行対象のブランチ名を正規表現で設定
- branches: ワークフロー実行対象のブランチを限定する場合に設定
- <ジョブ名>: ワークフローで実行するジョブ名
- jobs
- <ワークフロー名>
環境変数と parameter
環境変数 environment は以下に対して設定できる固定値である。
設定する階層 | 展開可能箇所 | 補足 |
---|---|---|
organization | job以下 | CircleCIのコンソール上で、organizationに紐づくcontextで複数の環境変数をcontext名と共に設定。workflowの定義でjobを列挙する際、contextキーでcontext名を指定してjobに渡す。環境変数というよりシークレットであり、一度値を設定するとその値は目視確認できなくなる |
project | .circleci/config.yml内のどこでも | CircleCIのコンソールで、projectに対して環境変数を設定する。 |
job | job内の全step | |
docker | コンテナ内(.circleci/config.yml上に展開するものではない) | circleci.yml内でなく、パイプライン実行時に作成されるコンテナ内に渡される環境変数の模様 |
step | 各step内 | stepをまたいでは設定できない。stepそれぞれでシェルが起動するため。環境変数を引き継ぐには、BASH_ENVに書き込んで、次回以降のstepでシェルが起動したときに自動で読み込ませるというような形にする。 |
.circleci/config上での値の展開は、$環境変数名
でできる(dockerキー以外)。
一方、parameterは以下に対して設定できるデフォルト値を持つ変数である。
設定する階層 | 展開可能箇所 | 補足 |
---|---|---|
pipeline | .circleci/config.yml内のどこでも | .circleci/config.yml内でトップレベルのparametersで設定する。デフォルト値からの変更チャンスはCircleCIパイプライン実行時のみ。APIの直接実行や、CircleCIのコンソール上で手動入力する形。したがって、通常利用(pushをトリガーとするパイプライン実行)ではブランチ名によって渡す値を変更するといったことはできない。 |
job | job直下の階層 | |
command | command直下の階層 |
.circleci/config.yml上でのparameterの展開は<< pipeline.<parameter名> >>
で。
所感
- シークレット情報(パスワード、他クラウドサービスを操作するためのサービスアカウントのキー等)はorganization, projectの環境変数で設定
- command, jobは適宜parametersを定義して、.cirleci/config.yml内で条件に応じた再利用をしやすく記述
- workflowに列挙するjobで, 条件に応じてjobにわたすparameterをベタ書きで切り替える
とするのが色々とすっきりしそう。
stepの成否判定
CircleCI では、workflow, steps, run, save_cacheといった特定の場面でwhenを指定することで条件分岐できる。
このうち、run と save_cache では、「前のstep群がすべて成功on_success
」か「どこかで失敗on_fail
」か「常時always
」のいずれかに該当したときにのみ処理を実行するという指定をする。これにより、処理結果に応じた分岐をさせることができる。(何も指定されていない場合はon_success扱い)
処理の成否判定は終了コードで判断されており、終了コード0: 成功、0以外:失敗と判断される。
command を定義する際にこれを意識しておかないと、「本当は失敗してるのに以降のstepを続行してしまう、成功しているのにstepが続行されない」といった期待しない動作となってしまう。(デプロイに絡むようなjobの場合、致命的な障害につながる可能性もあると思われる)
参考