SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

エンジニアのためのCI/CD再入門

CircleCIで始めるCI/CD導入の基本のキ

エンジニアのためのCI/CD再入門 第2回

実際のアプリケーションの例

 CircleCIの設定のイメージはつかんでもらえたでしょうか? Hello Worldではもの足りないので、今度はもっと実践的な例で詳しく説明していきます。

 コードはhttps://github.com/kimh/codezine-circleci-demo-ruby-railsにあります。これはRuby On Railsで書かれた簡単なブログアプリです。このレポジトリを自分のGitHubアカウントにフォークして上記の手順に従いプロジェクトとして追加してください。

 以下の設定はこのブログアプリをCircleCI上でビルドするための最低限の設定です。すこし項目が多いですが順番に説明していきます。

version: 2
jobs:
  build:
    working_directory: ~/circleci-demo-ruby-rails
    docker:
      - image: circleci/ruby:2.4.1-node
        environment:
          RAILS_ENV: test
      - image: circleci/postgres:9.4.12-alpine
    steps:
      - checkout

      - restore_cache:
          keys:
            - v1-rails-demo-{{ checksum "Gemfile.lock" }}
            - v1-rails-demo

      - run: bundle install --path vendor/bundle

      - save_cache:
          key: v1-rails-demo-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle

      - run:
          command: |
            bundle exec rake db:create
            bundle exec rake db:schema:load

      - run: bundle exec rspec
version: 2
jobs:
  build:

 buildというジョブの定義しています。CircleCIではビルドの設定をジョブという単位に分けて管理します。ジョブに分けるメリットを体験するには次回解説するワークフローを理解しないといけないので、ここではbuildというジョブにビルドの設定を入れていくということだけ覚えておいてください。

working_directory: ~/circleci-demo-ruby-rails

 working_directorystepsに書かれているコマンドをどのディレクトリで実行するかを指定します。指定しない場合は~/projectが使われますが、明示的に~/<リポジトリ名>と指定するとよいでしょう。

docker:
  - image: circleci/ruby:2.4.1-node
    environment:
      RAILS_ENV: test
  - image: circleci/postgres:9.4.12-alpine

 docker:このジョブがDocker Executorを使うということを定義しています。Executorとはビルドの実行環境のことです。

その他の実行環境

 CircleCIではDockerの他にiOSやmacOSをビルドするために使われるmacosやVM環境でビルドするためのmachineを実行環境として指定できますが、有料プランのみの機能となるため今回は説明しません。詳しくは公式ドキュメントをご確認ください。

 image:使用するDockerのイメージを指定します。ここで指定したイメージ上でコマンドが実行されます。CircleCIではDocker Hubのパブリック/プライベートだけではなく、その他のレジストリもサポートしています。例えば、DockerHubのプライベートなイメージを使う場合認証情報はauthを使って以下のように設定します。

docker:
  - image: kimh/my-private-image
    auth:
      username: mydockerhub-user
      password: $DOCKERHUB_PASSWORD

 パスワードを直接書いてしまうのはまずいので、$DOCKERHUB_PASSWORDのように環境変数に設定しています。環境変数については後ほどrunステップのところで詳しく解説します。

environment:
  RAILS_ENV: test

 このイメージに環境変数を設定しています。ここで指定した環境変数はこのイメージで実行されるコマンドのすべてで共有されます。今回の場合だとRAILS_ENV: testと指定しているのでRailsのテストを実行する際にRAILS_ENV=test <test-command>のように毎回環境変数を指定する必要がありません。

- image: circleci/postgres:9.4.12-alpine

 2つ目のDockerイメージを指定しています。CircleCIではジョブで使うイメージを複数指定することができます。1つ目に指定したイメージ(ここではcircleci/ruby:2.4.1-node)はプライマリイメージと呼ばれ、この上で実際のテストやビルドのコマンドが実行されます。それ以降はサービスイメージと呼ばれ、アプリケーションが必要なデータベースなどのサービスを提供するために使われます。今回の例だと、ブログアプリを動かすためにPostgreSQLが必要なのでサービスイメージで指定しています。なお、サービスイメージは複数指定できるので、例えばビルドの中で複数のデーターベースを使ったりもできます。

steps:

 steps:ビルドの中で実行される各処理をステップと呼びます。これ以降の階層にステップを定義していきます。

- checkout

 checkout:ソースコードをVCS(GitHubやBitbucket)からダウンロードします。通常コードがないとビルドを始められないので、最初に書いておくとよいでしょう。

依存関係のキャッシュについて

 以下の設定では依存関係をインストールしています。毎回ジョブを実行するたびに依存関係を最初からインストールするのは無駄なので、CircleCIでは依存関係のキャッシュをサポートしています。

- restore_cache:
    keys:
      - v1-rails-demo-{{ checksum "Gemfile.lock" }}
      - v1-rails-demo

- run: bundle install --path vendor/bundle

- save_cache:
    key: v1-rails-demo-{{ checksum "Gemfile.lock" }}
    paths:
      - vendor/bundle

 save_cacheでキャッシュをアップロードしてrestore_cacheでダウンロードしてします。restore_cacheの直後にbundle installで依存関係をインストールしていることに注目してください。Ruby On Railの標準パッケージマネジャーであるBundlerはすでにダウンロードされている依存関係はスキップして、新しい依存関係だけをダウンロードしてくれます。つまり、restore_cacheでダウンロードしたキャッシュに含まれなかった依存関係だけをインストールします。

 最後にsave_cacheでインストールした依存関係をアップロードします。こうすることで最新の依存関係をキャッシュできます。Bundlerに限らず最近のパッケージマネジャーであれば同じような挙動をするので、restore_cache⇒依存関係のインストールコマンド⇒save_cacheはCircleCIでもっともよく使われるパターンの一つです。

 save_cacherestore_cacheについて詳しく見ていきましょう。どちらのステップにもv1-rails-demo-{{ checksum "Gemfile.lock" }}と書かれています。これはキャッシュする依存関係に対して一意なキーを設定しています。もう少し噛み砕いて言うと、CircleCIではキャッシュは単に依存関係をtarでまとめた1つのファイルで、restore_cacheでどのファイルをダウンロードするか指定するために、一意なキーで名前をつけています。

 v1-rails-demo-は文字列リテラルです。{{ checksum "Gemfile.lock" }}はCircleCIが特別に用意してあるテンプレートで、指定したファイルのチェックサムに展開されます。具体的にはファイルのSHA256ハッシュを取って、それをBase64でエンコードした値になります。

 以下は、v1-rails-demo-{{ checksum "Gemfile.lock" }}で指定したキャッシュが実際に使われているところです。bundle installUsing...となっていることに注目してください。依存関係を新たにダウンロードせずにキャッシュからインストールしています。

 もう一つ重要なポイントはCircleCIは依存関係をダウンロードする時に最もマッチするキーを優先的に使おうとします。分かりやすいように具体例で説明します。

  1. Gemfile.lockのチェックサムがabc123だったとします(実際はBase64なのでもっと長いです)。
  2. CircleCIはv1-rails-demo-abc123というキーのキャッシュがすでに保存されているかチェックします。
  3. もしあればその依存関係をビルドにダウンロードします。
  4. なければ次のキーであるv1-rails-demoに前方マッチするキャッシュがあればダウンロードします。

 ここまで理解できれば、save_cacheの動きは簡単に理解できるのではないでしょうか? keyでアップロードするキャッシュに対してキーを決めます。pathsはキャッシュする依存関係があるディレクトリを指定しています。

 以上がキャッシュに関する基本的な説明です。実はCircleCIにはchecksum以外にも使えるテンプレートが用意されています。紙面の関係ですべてを紹介することはできませんが、コミットのSHAに展開される{{ .Revision }}や任意の環境変数に展開される{{ .Environment.variableName }}などもあります。詳しくは公式ドキュメントをご覧ください。

Runステップでコマンドを実行する

 テストの実行などのコマンドはrunステップで実行します。

- run:
    command: |
      bundle exec rake db:create
      bundle exec rake db:schema:load

- run: bundle exec rspec

YAMLでの改行

 command:の直後の|は複数行を書く時に便利です。こうすると、改行されていても、1つの文字列として扱われます。YAMLの標準機能の一つです。

 上の例ではテストデータベースをセットアップするコマンドとテストコマンドをrunで実行しています。runステップで指定したコマンドは新しいシェルを介して実行されます。使うイメージにインストールされていれば、/bin/bashが、なければ/bin/shが使われます。

 ここで注意したい点は、各runステップは新しいシェル上で実行されるということです。つまり、前とその後のrunステップでは環境が引き継がれません。例えば以下のようにしてもうまく動きません。

- run: export Foo=foo
- run: echo $FOO #$FOOには何も入っていない

 このようなことを実現するためにCircleCIでは環境変数がサポートされています。使い方は2通りあって、.circleci/config.ymlに書く方法とプロジェクトの設定画面を使う方法があります。

 .circleci/config.yml内で設定する場合はenvironmentを使います。3つの設定方法があります。

1:ジョブのトップレベルキーとして指定する
version: 2
jobs:
  build:
    environment:
      FOO: bar

 この方法だとbuildジョブの中のすべてのステップからFOOが参照できるようになります。

2:イメージの中で指定する
- image: postgres:9.4.1
  environment:
    POSTGRES_USER: root

 ある特定のイメージだけに環境変数を指定したい場合にはimageで指定することもできます。

3:runで指定する
- run:
    environment:
      RAILS_ENV: "test"
    command: |
      bundle exec rspec # RAILS_ENV=test bundle exec rspecと同じ

 runの中だけで有効な環境変数も設定できます。

環境変数の展開は行えない

 CircleCIではenvironment内での変数展開はサポートされていません。

environment:
  PATH: $PATH:/sbin

 つまり、上記のようにすると値は文字列として解釈され、結果、PATHに$PATH:/sbinという文字列がセットされてしまいます。CircleCIで最もよく陥る間違いの一つなので気をつけてください。

 .circleci/config.ymlはソースコードと一緒にレポジトリで管理されるので、パスワードのような内容を隠したい環境変数は書くことができません。そのような場合はプロジェクト設定画面から追加しましょう。

 右上にあるギアのアイコンをクリックするとプロジェクトの設定ページへ行くことができます。そこから、Environment Variablesをクリックすると、ここで追加した環境変数を使うことができます。

 例えばパスワードで認証が必要なサイトをcurlで取得していた場合、$SITE_PASSWORDをプロジェクト設定から追加すれば以下のようにすることができます。

- run: curl -u my-user:$SITE_PASSWORD http://hoge.com

 本格的にCI/CDの設定をする場合、環境変数の利用は避けれないのでぜひマスターしてください。もっと詳しくしりたい場合は公式ドキュメント読むことをおすすめします。

次のページ
Ver. 2.1

この記事は参考になりましたか?

エンジニアのためのCI/CD再入門連載記事一覧

もっと読む

この記事の著者

金 洋国(CircleCI Japan)(キム ヒロクニ)

CircleCIで2.0などのプロダクト開発に携わった後、CircleCI Japanを立ち上げてからはTech Leadとして技術全般を担当。趣味は電動キックボードで日本で普及するように様々な活動をしています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/11208 2018/11/29 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー