しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

CircleCI を使った .NET Core アプリケーションのビルドを高速化する

CI の SaaS はビルドの度にコンテナが生成されて、完了したら破棄されるといったように揮発する性質があるので、キャッシュが効かずに時間がかかりがちです。.NET Core の場合は NuGet パッケージのインストール処理で影響が出やすいです。

特に dotnet コマンドを実行したタイミングで、ローカルにパッケージのキャッシュを展開するので時間がかかります。しかしコンテナは 1 回使えば破棄されるので、この処理は完全に無駄になります。

f:id:shiba-yan:20161209234919p:plain

別に CircleCI に限ったことではないですが、AppVeyor は .NET Core が最初からインストールされていて影響を受けないので、あえてこういった書き方にしました。

そこでローカルにパッケージキャッシュを展開しない設定を追加すると、dotnet コマンドの実行時間の短縮になります。以下の 2 つを環境変数に追加すれば OK です。

  • DOTNET_SKIP_FIRST_TIME_EXPERIENCE = true
  • NUGET_XMLDOC_MODE = skip

こういった設定はあまりドキュメント化されてないですが、Azure Web Apps にはデフォルトで設定されているので、いい感じに参考にすれば良いと思います。NuGet の方はおまけ的な感じで。

環境変数は circle.yml に書けば反映されるので、非常にお手軽です。

machine:
  environment:
    DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
    NUGET_XMLDOC_MODE: skip

dependencies:
  pre:
    - sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'
    - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893
    - sudo apt-get update
    - sudo apt-get install dotnet-dev-1.0.0-preview2.1-003177
  override:
    - dotnet restore

machine: の部分だけが重要なので、他は適当に流しといてください。

実際に CircleCI でビルドを行って試してみました。これがデフォルトの状態での結果です。

f:id:shiba-yan:20161210001408p:plain

Welcome メッセージが表示された後にローカルにパッケージキャッシュを展開しているので、合計で 50 秒ほどかかっています。半分以上がパッケージキャッシュの展開にかかった時間となります。

そしてパッケージキャッシュを展開しない設定を入れてビルドした結果です。

f:id:shiba-yan:20161210001803p:plain

パッケージキャッシュの展開にかかっていた時間を丸ごと短縮できました。

そして、これは色々とトレードオフになりますが NuGet でインストールしたパッケージが実際に格納されているディレクトリを、CircleCI のキャッシュディレクトリとして指定する方法もあります。

dependencies:
  cache_directories:
    - "~/.nuget"

インストールされたパッケージは、ホームに作られた .nuget というディレクトリに入ってます。これのキャッシュを利用することで dotnet restore の処理を大幅に減らせますが、キャッシュのサイズが大きくなると保存と復元に時間がかかります。

実際に circle.yml に設定を追加すると、Save cache で .nuget が対象になっていることが確認できます。

f:id:shiba-yan:20161210002908p:plain

そして次のビルドから 1 つ前のキャッシュが使われるので、何か変更してビルドさせます。

実際にビルドした結果ですが、dotnet restore が高速になりました。tools に入っているパッケージが対象外になっているのはちょっとよくわかりません。

f:id:shiba-yan:20161210002833p:plain

今回はサンプルのアプリケーションとして用意しやすかったので ASP.NET Core を使いましたが、.NET Core のアプリケーションなら何でも同じ方法が使えるので、前に書いた C# Lambda Function のデプロイなどでも同じ結果が得られます。

CircleCI が環境として Docker Image を指定できるようになれば、さらに改善することが出来そうなのでコンテナ方面の対応も少し期待しています。