過去のブログでは、CircleCI で React Native Camera のビルドを最適化する方法について説明しました。この取り組みで得た教訓は、メンテナンスの担当者が最適化できることも認識していなかったことです。私のすべての説明は、CircleCI ドキュメントで参照できますが、何を知らないかを知っていないことには、何も始めることはできません。このブログでは、CircleCI のあらゆるプロジェクトで利用できる多くの最適化機能について説明します。

ビルドの時間は貴重です。特に、予算が限られているオープンソースプロジェクトであれば尚更です。CircleCI のドキュメントでも最適化について詳しく説明していますが、競合他社とは一線を画する CircleCI プラットフォームのさまざまな機能を知っていただくために、ブログを書くことにしました。

次のほぼすべての機能は、プライベートプロジェクトでもパブリックプロジェクトでも、すべてのユーザーが無料で利用いただけます。有償プランで利用できる機能はマークされています。できるだけ多くのプロジェクトに対して PR(プルリク)を開きたいと思っていますが、それは実用的ではないかもしれません。次善の策は、皆さんが自分で実行できるようにする知識とリソースを身に付けてもらうことだと思います。

最適化機能

CircleCI には、開発者がビルドとデプロイを高速化するために使用できる多くの機能があります。

依存関係のキャッシュ

Diagram-v3-Cache.png

CircleCI システムのすべてのジョブは、新しいフレッシュな環境でスピンアップされます。これは、過去のジョブや予期しない動作による影響を避けるためです。しかし、このため、同じジョブをその後で実行する場合でも、同じ依存関係を再度ダウンロードする必要があります。

この問題を解決するため、依存関係のキャッシュ機能が提供されています。プロジェクトの依存関係をキャッシュして、ビルドプロセスを劇的に高速化できます。

たとえば、Node.js アプリケーションをビルドする場合、node_modules をキャッシュします。キャッシュをキーとして保存すると、キーに使用できるテンプレートをいくつも利用できます。

次の例では、package_json の SHA256 を含むキーに node_modules フォルダをキャッシュしています。このように、依存関係が変更されるとキャッシュキーも変更され、その後、新しい依存関係には新しいキャッシュが作成されます。package.lock または yarn.lock ファイルのチェックサムを使用することを選択する場合があります。

version: 2.1
jobs:
  my-job:
    executor: my-executor
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "package.json" }}
            - v1-dependencies- # Fallback cache
      - run: yarn
      - save_cache:
          key: v1-dependencies-{{ checksum "package.json" }}
          paths:
            - node_modules

詳細と例については、CircleCI のドキュメントを参照してください。

ワークスペース

Diagram-v3-Workspaces.png

上記のセクションで述べたように、システム内のすべてのジョブは、新しいフレッシュな環境でスピンアップされます。ワークフローのジョブ間でファイルとアーティファクトを共有する場合、ワークスペースを使用できます。ワークスペースを使用すると、作業を 1 つのジョブから次のジョブに簡単に転送して、重複作業を回避できます。

次の例では、Go バイナリビルドと Docker イメージビルドを別のジョブに分けています。リポジトリのクローンを再作成したり、ビルドをやり直したりするのを避けるために、ワークスペースから必要なファイルを転送しています。

version: 2.1
jobs:
  my-build-job:
    executor: my-executor
    steps:
      - checkout
      - run: go build -o APP_NAME cmd/**/*.go
      - persist_to_workspace:
          root: .
          paths:
            - Dockerfile
            - cmd
            - internal
            - go.mod
            - go.sum
  my-docker-job:
    executor: my-other-executor
    steps:
      - attach_workspace:
          at: .
      - run: docker build -t TAG_NAME .

詳細と例については、CircleCI のドキュメントを参照してください。

以前に作成した Docker イメージを再利用する

とりわけ、ワークスペースは、ビルドした Docker イメージをジョブ間で転送するときに役立ちます。多くのお客様が CircleCI プラットフォームで Docker のビルドを行っており、Docker イメージのビルドとテストを別々のジョブに分けている場合もあります。

docker save および docker load コマンドを使用して、保存したイメージを転送できます。

この例では、最初のジョブで Docker イメージをビルドし、ワークスペースに保存する前に docker-cache/image.tar に書き込んでいます。次のジョブはそのワークスペースをアタッチし、イメージとテストスイートの両方を実行する前にイメージをロードします。

version: 2.1
jobs:
  my-build:
    executor: my-executor
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build and Save Docker Image
          command: |
            docker build -t MY_APP:MY_TAG .
            mkdir docker-cache
            docker save -o docker-cache/image.tar MY_APP:MY_TAG
      - persist_to_workspace:
          root: .
          paths:
            - docker-cache
  my-test:
    machine: true
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Load, Run Docker Image and Tests
          command: |
            docker load < docker-cache/image.tar
            docker run -d -p 8080:80 MY_APP:MY_TAG
            mvn test -Dhost=http://localhost:8080

カスタム Docker イメージの Executor

Custom Dockerfile

CircleCI のプラットフォームはさまざまな実行環境を提供していますが、そのうちの 1 つが Docker です。さまざまなフレームワークと言語に対応し、すぐに使えるイメージを CircleCI は提供しています。多くの場合、これらのイメージをスタートポイントとしてご利用いただけます。ただし、非常に高度で複雑なユースケースの場合、Executor として使用する独自のカスタム Docker イメージを簡単に作成できます。

カスタムイメージを使用することにより、必要なすべての依存関係と必要なその他の設定を組み込んで環境を事前に構築できます。デフォルトでは、Docker Hub からイメージを取得しますが、他のソースから(認証して)取得する必要がある場合は、コンフィグで簡単に変更できます。完全修飾の URL を必ず指定してください。

version: 2.1
jobs:
  build:
    docker:
      - image: quay.io/project/IMAGE:TAG
        auth:
          username: $QUAY_USER
          password: $QUAY_PASS

詳細と例については、CircleCI のドキュメントを参照してください。

テストの分割と並列処理(Performance プランとオープンソース Linux)

Test Parallelization

また、CircleCI のプラットフォームでは、テストを分割して並列処理することもできます。テストを分割し、異なるワークロードを並行実行することにより、テスト全体に要する時間を劇的に削減できます。組織で並列処理できるワークロードが 1 倍以上であれば、並列処理する意味があります。Free プランのオープンソース組織は、Linux/Docker ビルドで最大 4 倍の並行処理を実行でき、Performance プランのお客様は、多くのリソースを使用するため、すべての Executor の並行実行をスケーリングできます。チームで使用できるリソースを増強することを検討されている場合には、CircleCI の担当者にお問い合わせください

詳細と例については、CircleCI のドキュメントを参照してください。

構成可能なリソース(Performance プラン)

Docker Executor Sizes

デフォルトでは、すべての実行環境は、ミディアムサイズで実行されますが、Executor のタイプによってこれは異なります。ただし、CircleCI プラットフォームを使用すると、ユーザーは自分のビルドにさまざまなリソース(vCPU と RAM)を柔軟に構成できます。

多くのリソースを必要としないジョブもあるため、小規模な Docker コンテナを選択し、コンピュートとクレジットを節約できます。しかし、2xlarge 以上のコンテナを使用する利点のあるジョブもあります。どのような場合であっても、ワークロードの需要を満たすように CircleCI を構成できます。

この例では、ビルドで xlarge リソースクラスを使用するように指定しており、8 つの vCPU と 16 GB の RAM を自由に使用できます。コンフィグを 1 行変更するだけでこの指定は完了します。

version: 2.1
jobs:
  build:
    docker:
      - image: circleci/node:10.15.1-browsers
    resource_class: xlarge
    steps:
      # ... steps here

Docker レイヤーキャッシュ(Performance プラン)

Docker Layer Caching Output

Docker イメージをビルドされる場合、Docker レイヤーキャッシュと呼ばれる優れた機能を利用できます。アプリケーションの依存関係をキャッシュするのと同じように、Docker イメージレイヤーをキャッシュして、その後の Docker イメージのビルドを高速化できます。

この機能を最大限に活用するには、常に変化する項目(たとえば、ソースコードの COPY や ADD など)を Dockerfile の下部に配置し、最も変化しない項目を上部に配置します。

このキャッシュ機能は、YAML にキーを追加するのと同じくらい簡単に使用できます。リモート Docker で Docker Executor を使用する場合には、以下のようになります。

jobs:
  build:
    executor: my-executor
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: true
      # ... other steps here

また、Machine Executorを使用する場合には、以下のようになります。

jobs:
  build:
    machine: true
      docker_layer_caching: true
    steps:
      # ... steps here

詳細と例については、CircleCI のドキュメントを参照してください。

便利な機能

ビルド時間を最適化する機能に加えて、生産性を高め、開発者の全体的なエクスペリエンスを向上させるのに役立つ多くの便利な機能も利用できます。

再利用可能なコンフィグと Orbs(パラメーター付き)

1 年前に、CircleCI では Orbs と再利用可能なコンフィグをリリースしました。新しい 2.1 のコンフィグを使用すると、再利用可能なジョブ、Executor、コマンドをパラメーターにして、プロジェクト内だけでなくプロジェクト間や組織全体で定義して利用できます。これには、いくつか利点があります。

  • 一度定義したコンフィグを再利用できます。 これは、Executor、ジョブ、およびコマンドを定義または確立して、共有し、信頼できる唯一のソースとして Orbs で管理するチームにとって最適です。パラメーターを使用すると、さまざまな入力を同じジョブまたはコマンドロジックで利用して、再利用性を最大限に高めることができます。
  • 車輪を再発明が不要になります。 たとえば、AWS は広く使用されているクラウドサービスプロバイダーです。たとえば、AWS ECSにデプロイしているのであえば、他のユーザーも同じような操作を実行しているのです。同じコンフィグであるのに再度記述するのは、合理的ではなく、不要であることに気が付くでしょう。.
  • Orbs はコードと同じように維持できます。 Orbs は YAML にパッケージングされているため、他のコードと同様にバージョン管理、テスト、およびリリースできます。つまり、開発者はこのプロセス全体を自動化して追跡でき、Orbs を利用するために新しい言語やフレームワークを習得する必要はありません。

次の例では、ファイルを AWS S3 にデプロイしています。AWS CLI を更新するため、コンフィグで多くのコード行を記述し、これを構成し実行する代わりに、5 行だけで済むようになりました。Orbs を「インポート」するために 2 行を記述し、次に、Orbs を使用するために 3 行(引数付き)を記述するだけです。

orbs: # "Import" the orb
  aws-s3: circleci/aws-s3@1.0.8

jobs:
  deploy:
    docker:
      - image: circleci/python:2.7
    steps:
      - attach_workspace:
          at: .
      - aws-s3/sync: # Using said orb
          from: .
          to: my_awesome_bucket

詳細と例については、CircleCI のドキュメントを参照してください。

データベースとサービス

自分のテストでデータベースをスピンアップできることを知っていますか?また、他のサービスイメージもスピンアップできることを知っていますか?Docker の下にある最初のイメージの後で指定される任意のイメージは、サービスイメージとみなされます。プライマリ実行コンテナと同じネットワークでスピンアップされます。

次の例では、プライマリコンテナでアプリケーションをテストするために Postgres データベースをスピンアップしています。デフォルトでは、名前を指定しなければ、localhost からコンテナにアクセスできますが、ホスト名前を指定して、使用することもできます。

jobs:
  build:
    docker:
      # Primary Executor
      - image: circleci/openjdk:11.0.3-jdk-stretch

      # Dependency Service(s)
      - image: postgres:10.8
        name: postgres # Can access via postgres:5432
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres

リソースにフルアクセスできる Machine Executor を使用している場合は、他の仮想マシンと同様に単純に扱い、Docker コンテナのネットワークをスピンアップできます。

jobs:
  build:
    machine: true
    steps:
      - run: docker-compose up
      # OR
      - run: docker run -d -p 5432:5432 -e POSTGRES_USER=postgres postgres
      - run: docker run -d -p 8080:80 MY_APP:MY_TAG
      # ...etc.

詳細と例については、CircleCI のドキュメントを参照してください。

インサイトとテストサマリー

大規模なテスト群を実行していますか?JUnit XML 形式のファイルを解析してデータを保存するテストサマリー機能を CircleCI は提供しています。これにより、以下を確認できます。

  • テストサマリー: Job Details ページの上部に、実行したテストの数とすべてのテストにパスしたかどうかの概要が表示されます。いずれかのテストに失敗すると場合、失敗したテストが表示され、すぐに出力を参照できます。重要な情報に迅速にアクセスでき、便利性が高くなっています。
  • インサイト: CircleCI ではこれらのテストサマリーを収集しており、各テストの平均所要時間を追跡しています。最も時間を要しているテスト、最も頻繁に失敗するテスト、およびプロジェクトとブランチ全体における平均ビルド時間の長さを表示できます。この情報は、CircleCIダッシュボードの別の Insights タブで確認できます。

この機能を実装するには、結果を JUnit XML 形式でファイルに出力するようにテストを構成する必要があります。次に、そのファイルをフォルダに保存し、store_test_results YAML キーを使用します。

jobs:
  build:
    executor: my-executor
    steps:
      # Other build steps
      - run:
          name: Run TestCafe tests
          command: |
            yarn runTests -- ${TESTFILES} # Outputs > /tmp/test-results/results.xml
      - store_test_results:
          path: /tmp/test-results
      # ...etc.

ビルドを表示し自分のコンフィグを表示します

詳細と例については、CircleCI のドキュメントを参照してください。

アーティファクト

Artifacts Tab

CircleCI のジョブは、毎回新しい、クリーンで分離されたビルド環境で実行されます。その後、ファイルやビルドアーティファクトを外部からアクセスできるようにする場合は、アーティファクト機能. を使用できます。ファイルをアーティファクトにすると、Job Details page 詳細ページの Artifacts タブで利用できるようになります。AWS S3 を使用しているため、参照パスが正しい限り、静的にホストされているようにファイルをプレビューできます。

この例は上記と同じです。テストスイートを実行し、結果を /tmp/test-results/results.xml に出力しました。次に、store_artifacts キーを使用してこれらのファイルをアーティファクトにし、Artifacts タブで表示できるようになりました。

ビルドを表示し自分のコンフィグを表示します

jobs:
  build:
    executor: my-executor
    steps:
      # Other build steps
      - run:
          name: Run TestCafe tests
          command: |
            yarn runTests -- ${TESTFILES}
      - store_artifacts:
          path: /tmp/test-results
          destination: .
      # ...etc.

詳細と例については、CircleCI のドキュメントを参照してください。

ビルドの SSH 接続

テスト出力は明解ですか?ビルドが失敗し、その理由を特定できない場合がありますか?CircleCI プラットフォームでは、SSH を使用してテストを再実行できます。この場合、同じビルドが再実行されますが、SSH ポートが開きます。GitHub または Bitbucket アカウントと同じ SSH キーを使用して、実行中のビルド環境にログインして認証し、ビルドを実行中に、デバッグできます。環境に直接アクセスすることで、実際に環境を調査したり、ログを追跡したり、他の項目を確認して、ビルドが失敗する原因を調査できます。

環境の外部から原因究明に試行錯誤するのではなく、デバッグを生産的な実行できる非常に便利な機能です。

詳細と例については、CircleCI のドキュメントを参照してください。

結論

上記のすべての機能のうち、有償または一部が有償となっている機能は 3 つだけです。開発者は CircleCI の多くの機能を利用して、ビルド速度を向上させ、生産性を高めることができます。多くのユーザーの方が CircleCI 製品とその機能を愛用し、活用されています。CircleCI では皆様からのご意見を元に、今後も製品の改善を行っていきます。