株式会社ホクソエムのブログ

R, Python, データ分析, 機械学習

GitHub Actions実行時に依存するRパッケージのインストールをキャッシュ化する

ホクソエムの u_ribo です。漫画「ブリーチ」の石田雨竜に親近感を感じます。仕事はシュッと終わらせて趣味の時間を増やしたいですよね。

要約

  • GitHub Actionsに対してrenvを使ったキャッシュ機能を活用。依存するRパッケージのインストール時間を短縮する
    • パッケージのインストールに要する時間を1/25に短縮
    • renvのキャッシュはOSによりパスが異なるため、GitHub Actionsを実行するOSに応じて変更が必要になる
    • キャッシュ機能はpipでも使えるため、Pythonによる処理を適用するときも便利
  • GitHub Actionsでrenvのキャッシュを利用するサンプル

はじめに

GitHub上でビルド、テスト、デプロイ等の作業を自動的に行えるGitHub Actionsが便利ですね。RやPythonも実行可能なため、データ分析の作業を補助する機能を持たせることもできるかと思います。例えば、リポジトリ上のデータが更新されたタイミングで分析コードを走らせてレポートを作成するといった具合です。このブログでも id:shinichi-takayanagi さんが記事を書かれています。

blog.hoxo-m.com

そんなGitHub Actionsですが、RやPythonのコードを実行する際にパッケージのインストールが必要になる場合があります。パッケージの追加もコマンドで行えるため、それ自体は問題になりません。しかし処理時間に関してはどうでしょう。パッケージのインストールはGitHub Actionsが動作するたびに実行されます。依存パッケージが多い・頻繁に実行されるジョブでは、ここでの作業がジョブ全体に要する時間の大部分を占める恐れがあります。

そこで、ジョブの過程で取得した依存パッケージを次回の実行時に再利用できるようにする、キャッシュ機能を活用します。これにより実行時間の短縮が期待できます。

公式のExampleを見ると、Pythonであればpip、Nodeはnpmやyarnを利用したパッケージ管理ツールを利用する方法が書かれています。ではRの場合はどうするの?が本記事の話題です。ここではRパッケージのインストール結果をキャッシュ化する例として、Rのパッケージ管理にrenvを利用して、pkgdownでのウェブサイトの構築を行うワークフローに導入することとします。

pkgdownでのウェブサイトの構築を行うワークフローについては id:yutannihilation さんの下記の記事をご覧ください。

notchained.hatenablog.com

この記事を参考に、まずはキャッシュ機能を使わないGitHub Actionsの設定を済ませます。本記事では、ここで用意したyamlファイルを編集します。

renvでパッケージ管理

renvはRStudioにより開発されているパッケージ管理のためのパッケージです。プロジェクトで利用されるパッケージの依存関係を明らかにし、再現可能な形で環境を構築します。具体的にはプロジェクトで使われるRパッケージとそのバージョン、インストール元の情報等を renv.lock ファイルに記録します。

speakerdeck.com

用意したプロジェクトに対してrenvによる管理を有効化しましょう。renv::init() を実行すると renv/フォルダ、renv.lockファイルが生成されます(.Rprofileがない場合はこれも)。

この時、すでにRコードが存在する場合、利用するパッケージおよびその依存パッケージがrenvによりインストールされ、その情報がrenv.lockに記録されます。パッケージのインストール先は従来Rが利用する環境(/usr/local/lib/R/site-library//Library/Frameworks/R.framework/Versions/{Rバージョン}/Resources/library)とは異なる環境となります。それはホームディレクトリに近い場所とrenvを有効化したプロジェクトの中です。

最初の「ホームディレクトリに近い場所」はOSごとに異なります。具体的には以下のとおりです。

プラットフォーム 場所
Linix ~/.local/share/renv
macOS ~/Library/Application Support/renv
Windows %LOCALAPPDATA%/renv

renvを使ったプロジェクトでパッケージをインストールするとこのディレクトリにファイルが保存されます(renvのキャッシュを無効化した場合はプロジェクトの中に直接保存されます)。そのため、他のRプロジェクトでパッケージのアップデートを行ってもその影響を受けません。また、依存関係も記述されているので再インストールも安全に行えます。

renv.lockに書かれたパッケージを復元するにはrenv::restore()を実行します。一度インストールされたパッケージであればキャッシュからインストールが行われるため、ファイルのダウンロード、ビルドの作業が省略されます。

またプロジェクトで利用するパッケージに変更(追加や更新、削除)があった際は renv::status() で確認、必要に応じて renv::snapshot()renv::lock を更新しましょう。

詳しい利用方法はここでは省略します。興味のある方は

qiita.com

speakerdeck.com

をご覧ください。

GitHub Actionsにrenvを導入する

続いてGitHub Actionsにrenvを導入する方法です。pkgdownによるウェブサイトのビルドを行うActionsではmacOS上で動作します。そこでrenvのキャッシュもmacOS仕様にする必要があります。

.github/workflows/ にある pkgdownのウェブページをビルドするYAMLファイルにある以下の箇所を変更します。

      - name: Install dependencies
        run: |
          Rscript -e 'install.packages("remotes")' \
                  -e 'remotes::install_deps(dependencies = TRUE)' \
                  -e 'remotes::install_github("jimhester/pkgdown@github-actions-deploy")'
  1. renvをインストール
  2. renv::restore()で必要なパッケージを復元する
    - name: Install dependencies
      run: |-
        Rscript -e "install.packages('renv')" \
                -e "renv::restore(confirm = FALSE)"

YAMLを書き換えたら、手元のRコンソールでrenv::install("jimhester/pkgdown@github-actions-deploy")renv::snapshot()を実行してrenv.lockを更新します。これは元のYAMLに書かれているremotes::install_github("jimhester/pkgdown@github-actions-deploy")の代わりに必要な処理です。

続いてキャッシュの指定です。今回はmacOSで動作させているので、キャッシュのpathもmacOSの~/Library/Application Support/renvとします。

    - uses: actions/cache@v1
      if: startsWith(runner.os, 'macOS')
      with:
        path: ~/Library/Application Support/renv
        key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }}
        restore-keys: |
          ${{ runner.os }}-renv-

こちらが編集後のyamlファイルです。

それではキャッシュ化の効果を見てみましょう。依存パッケージのインストールにかかった時間 (Install Package Dependenciesの部分)を見ます。最初の処理で4分38秒だったのに対し、キャッシュが働く2回目はわずか11秒で完了しています。1/25の差は大きいですね。出力を見ても、きちんとキャッシュを利用しているのがわかります。

f:id:u_ribo:20200123174147p:plain

任意のOS、Rコードの実行に必要な依存パッケージを扱う例

renvのキャッシュはOSごとに異なることは述べたとおりです。OSごとのキャッシュ先の指定方法は、PRを出してマージされたのでGitHub Actionsのcacheリポジトリに記載されています。

https://github.com/actions/cache/blob/master/examples.md#r---renv

また、今回解説したウェブサイト構築以外の用途で利用する際のサンプルとして、以下のリポジトリへのリンクを示しておきます。

GitHub - uribo/renv_ghaction: for demonstration

投げていたジョブが完了しました。お先に失礼します! Enjoy!