LoginSignup
2
2

More than 3 years have passed since last update.

GitHub Actions/Cache : Dockerコンテナ上でbundle installしたgemをキャッシュする

Posted at

GitHub AcitonsでRailsプロジェクトのrspec/rubocopを実行するような場合に、bundle install したgemのキャッシュを行いたいのだが、Permission deniedエラーが出てうまくキャッシュが作れず、キャッシュキーが汚染1されることがあった。

error例
Post job cleanup.
/bin/tar --posix --use-compress-program zstd -T0 -cf cache.tzst -P -C /home/runner/work/project/project --files-from manifest.txt
/bin/tar: ../../../../../tmp/cache/bundle/gems/puma-5.1.1/ext/puma_http11/.gem.20201227-1-wtkvl5: Cannot open: Permission denied
/bin/tar: ../../../../../tmp/cache/bundle/gems/mini_racer-0.3.1/ext/mini_racer_extension/.gem.
[中略]
/bin/tar: ../../../../../tmp/cache/bundle/gems/sassc-2.4.0/ext/.gem.20201227-1-1ompol5: Cannot open: Permission denied
/bin/tar: Exiting with failure status due to previous errors
Warning: Tar failed with error: The process '/bin/tar' failed with exit code 2

上記のエラーはローカルの /var/cache/bundle ディレクトリを、コンテナの /bundle にマウントした状態で BUNDLE_PATH=/bundle bundle install を実行し、/var/cache/bundleActions/Cache でキャッシュさせる場合に発生した

docker-compose と workflow のyamlは下記のような感じ

docker-compose.yaml
version: '3.7'

services:
  app:
    ...

    volumes:
      - ./:/app:cached
      - /var/cache/bundle:/bundle
.github/workflows/ci.yml
jobs:
  ...
  rspec:
    ...
    env:
      GEMS_CACHE_DIR: /tmp/cache/bundle

    steps:
    ...

    - name: Cache bundle gems
      id: cache-bundle-gems
      uses: actions/cache@v2
      with:
        path: ${{env.GEMS_CACHE_DIR}}
        key: ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-${{ hashFiles('Gemfile.lock') }}
        restore-keys: |
          ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-

解決策 1 アクセス権限を書き換える

アクセス権限の問題なので、雑にアクセス権限を書き換えてしまう方法。

cache作成時のtarコマンドでsudoがつけられたらいいのだが、そういうオプションはないので、下記のようなアクションstepsの最後に差し込む

.github/workflows/ci.yml
  - name: Fix permissions of bundle
    id: fix-permissions-of-bundle
    run: sudo chmod -R a+rwx ${{env.GEMS_CACHE_DIR}}

この方法で多分大丈夫。

ですが、アクセス権限書き換えることになるので、その辺りの動作確認は取れません(そこが問題になることなんてほぼないと思いますが…

解決策 2 コンテナ上でtarを作成してcacheさせる

アクセス権限の問題が発生するのは、コンテナ上で bundler install しているのにローカルでtarを作成しているため。

コンテナ上で /bundle のtarを作成し、そのtarを Actions/Cache でキャッシュして貰えば問題ありません。

下記の設定では必要最低限の軽量コンテナのみを立ち上げて(今回は standalone という名前のコンテナを使用している)、tarを作っています。

.github/workflows/ci.yml
  env:
    GEMS_ARCHIVE_DIR: /tmp/cache/bundle-gem-archive
  ...
  # $GEMS_ARCHIVE_DIR ディレクトリをキャッシュ
  - name: Cache bundle gems
    id: cache-bundle-gems
    uses: actions/cache@v2
    with:
      path: ${{ env.GEMS_ARCHIVE_DIR }}
      key: ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-${{ hashFiles('Gemfile.lock') }}
      restore-keys: |
        ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-

  # $GEMS_ARCHIVE_DIR をマウントした軽量コンテナ上で /archive/bundle.tar からファイルを抽出
  - name: Unarchive bundle gems cache
    id: unarchive-bundle-gems-cache
    run: test -f ${{env.GEMS_ARCHIVE_DIR}}/bundle.tar
      && docker-compose run --rm -v ${{ env.GEMS_ARCHIVE_DIR }}:/archive:cached standalone
        tar -xf /archive/bundle.tar -C /
      || echo "bundle gem cache does not exist."

  ...

  # 軽量コンテナ上で $GEM_HOME の内容をtarで固め、/archive/bundle.tar の名前で保存
  - name: Create bundle gems archive
    id: create-bundle-gems-archive
    if: steps.cache-bundle-gems.outputs.cache-hit != 'true'
    run: docker-compose run --rm -v ${{ env.GEMS_ARCHIVE_DIR }}:/archive:cached
        standalone tar -cf /archive/bundle.tar -C / bundle

この方法の場合、コンテナ上でtarを実行するので多少遅くなるようだが自分の環境の場合はほぼ誤差(数秒程度)しか変わらなかった(gemの量などにもよると思う)。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2