LoginSignup
1
1

GitHub Actions、キャッシュで時間短縮(とLambda更新で少し苦労)した話

Last updated at Posted at 2023-12-11

取り組んだこと

本記事は、GitHub Actions Advent Calendar 2023の 20日目の記事です。

以前投稿したこちらの記事で、workflowを作成しました。
このときのworkflowは、とりあえず動くものでした。

作業前のworkflow

今回は、キャッシュを利用して、実行時間短縮を図ろう
ということで作業を行っていました。

キャッシュは以下の2カ所に設定していきます。

  • python関係のライブラリをインストール
  • DockerイメージのPushまでの過程

python関係のライブラリをインストール

github actions pip cacheと検索すると、以下リポジトリが見つかりました。

関連する実装を色々確認して、以下の実装を使わせていただきました。
Pythonとpipのそれぞれのレイヤーでキャッシュを活用する方法です。

I have one more suggestion that I decided to use, because I want to avoid downloading all pip dependencies everytime any of them changes - to introduce a second cache just for pip cache.

(訳)pip依存関係が変更されるたびに、すべての再ダウンロードするのは非効率的だと考えたためです、すべてのpip依存関係を毎回ダウンロードすることを避けるため、pipのキャッシュ専用の2つ目のキャッシュを導入することにしました。

  - name: "Cache: Cache Python"
    id: python-cache
    uses: actions/cache@v3.3.1
    with:
      path: ${{env.pythonLocation}}
      key: ${{env.pythonLocation}}-${{hashFiles('requirements.txt')}}
  - name: "Shell: Get pip cache dir"
    id: pip-cache-dir
    if: steps.python-cache.outputs.cache-hit != 'true'
    run: |
        python -m pip install -U pip
        pip install -U wheel
        echo "pip-cache-dir=$(pip cache dir)" >> ${GITHUB_OUTPUT}
  -   name: "Cache: Cache pip"
      if: steps.python-cache.outputs.cache-hit != 'true'
      uses: actions/cache@v3.3.1
      with:
          path: ${{steps.pip-cache-dir.outputs.pip-cache-dir}}
          key: 3.12-${{hashFiles('requirements.txt')}}
          restore-keys: |
              3.12-${{hashFiles('requirements.txt')}}-
              3.12-
  -   name: "Shell: Install pip dependencies"
      if: steps.python-cache.outputs.cache-hit != 'true'
      run: pip install -r requirements.txt

続けて、Dockerイメージのキャッシュに関する作業に入ります。

DockerイメージのPushまでの過程

続いて、DockerイメージをPushするまでにキャッシュを使って、時間短縮を図ります。

build-push-actionの利用

GitHub Actionsエコシステムにある便利なアクションです。Dockerイメージのビルド、プッシュ、キャッシュを管理してくれます。
build-push-actionのオプションを追加します。

build-push-actionのExamplesにあった、Cache managementから確認しました。

大きく4種類ありそうです。

  • Inline cache
  • Registry cache
  • GitHub cache(experimental)
  • Local cache

inline推し、みたいです。しかしinlineは、min キャッシュモードのみサポートのようです。
レジストリキャッシュエクスポーターを cache-to オプションに設定することで、max キャッシュモードを使用可能なようです。
min , maxについては以下の通りです。(参考

  • mix:最終レイヤーのみキャッシュ
  • max:中間レイヤー含めてキャッシュ

今回はせっかくなので、GitHub cache(experimental)を使います。

余談:キャッシュ保存先はs3も選択可能

Cache managementのページにあったリンクから、詳細を確認してみました。
s3とazblobがunreleasedとなっています。

https://docs.docker.com/build/cache/backends/

リリース前か、と思いましたが、
以下ページではS3 cache (experimental)でした。
動作確認してみたので、気になる方はご覧ください。(記事の最後で)

https://github.com/moby/buildkit#s3-cache-experimental

GitHub cache(experimental)

基本的にはドキュメント通りで、Scopeは確認しました。

Scope is a key used to identify the cache object. By default, it is set to buildkit. If you build multiple images, each build will overwrite the cache of the previous, leaving only the final cache.
(訳)複数のイメージに対してキャッシュを利用したい場合に、どのイメージを何というキー名で設定するか、明記してください。scopeに何も設定しないと、最後のイメージしかキャッシュされませんよ~

今回私はイメージを一つしか利用しませんが、使ってみます。
一旦まとめると、build-push-action部分は以下の通りです。

  - name: Docker build and push to ECR         
    uses: docker/build-push-action@v5
    with:
      context: .
      file: ./Dockerfile
      tags: ${{ secrets.AWS_REGISTRY_URL }}:latest
      push: true
      cache-from: type=gha,scope=FastAPI-backend-dev
      cache-to: type=gha,scope=FastAPI-backend-dev,mode=max

続いて、参考にしていたyamlファイルに、setup-buildx-actionとあるので、これを確認します。

setup-buildx-actionの利用

GitHub Action to set up Docker Buildx.

Buildxとは?

buildx is a Docker CLI plugin for extended build capabilities with BuildKit.

BuildKitとは?

BuildKit is a toolkit for converting source code to build artifacts in an efficient, expressive and repeatable manner.

BuildKitを使うために

  • GitHub ActionでBuildxを使うためには、setup-buildx-actionが必要
  • Buildxが「BuildKitを使った拡張ビルド機能を提供するためのプラグイン」

setup-buildx-actionをyamlファイルに記述すれば、BuildKitが使えそう。

docker-buildxとmulti-platform build周りについてまとめ」も参考になりました!

コンテナイメージを作成する際にamd64アーキテクチャ向けのイメージだけじゃなくて、arm向けのイメージとかも作りたいときに使うものだと思えばOK

ちなみに、setup-buildx-actionを定義せずworkflowを実行すると、エラーとなりました。

buildx failed with: ERROR: Cache export feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")
image.png

ここまでを踏まえて、setup-buildx-action部分は以下の通りです。(参考

- name: Set up Docker Buildx      
  uses: docker/setup-buildx-action@v3
 with:
    version: latest
   driver-opts: image=moby/buildkit:latest
# build-push-actionをこれ以降に実行

AWSのLambda関数更新に失敗

以下2つについて、実装が完了しました。

  • build-push-action
  • setup-buildx-action
    あと、Lambda関数更新で終わり!と思って動作確認すると、

InvalidParameterValueException: The image manifest or layer media type for the source image ***:latest is not supported.
image.png

マネジメントコンソールからECRの画面を確認すると、おかしいな。。。

想定していた状態

イメージタグがlatest、アーティファクト・タイプがImageの状態

image.png

実際の状態 ...

アーティファクト・タイプがImage Index
アーティファクト・タイプがImageが2つ?サイズも異なる?
image.png

犯人探し

今回追加した以下2つを調べます。setup-buildx-actionはエラーないので、まずはbuild-push-actionから確認しました。

  • setup-buildx-action
  • build-push-action

build-push-action内の容疑者

build-push-action ECRと検索すると、以下の記事が見つかりました。
今回とは異なる部分もありますが、ECRにPushしたら謎な状態に、という質問でした。

以下、回答のコメントです。別の回答にもprovenanceとあり、少し調べました。

Just add provenance: false in docker/build-push-action@v4

provenanceというオプション

私は全然知らない領域の話がでした。SLSAというものが関係しているようです。

ソフトウェアのサプライチェーン、つまり開発からビルドを経てデプロイされるまでの一連の過程において、外部の攻撃からその完全性を守るためのフレームワークがSLSAである。

ここは掘り下げしないで、進みます!
provenance: falseとすることで、Lambda関数の更新に成功しました。

setup-buildx-action内の容疑者

setup-buildx-action側には問題がないのか確認します。

マネジメントコンソールからECRの画面を確認すると、おかしい。。。

この文章を記載した箇所で載せた画像にある通り、
アーティファクトタイプがImage Index、エラー文にはimage Manifestとあります。両者は関係ありそうなので、調べました。

  • Image Manifest:特定のアーキテクチャとオペレーティングシステム向けの単一のコンテナイメージの設定とレイヤーのセットを提供
  • Image Index:様々なアーキテクチャとオペレーティングシステムにまたがるイメージセットに関する情報を含んでおり、(上位マニフェスト)コンシューマはインデックスを処理できるようにしておくべき
mediaTypeの確認

workflow失敗時に出力されていたmetaデータを見ました。
エラー文にmedia typeという単語もあったので、調べます。

{
  "containerimage.descriptor": {
    "mediaType": "application/vnd.oci.image.index.v1+json",
    "digest": "sha256:40ec8fca07e81d832bae55168d7c5d8ec4e359f3d1dd886238a8ccd8eb623548",
    "size": 856
  },
  "containerimage.digest": "sha256:40ec8fca07e81d832bae55168d7c5d8ec4e359f3d1dd886238a8ccd8eb623548",
  "image.name": "***:latest"
}

以下を確認しました。

image.png

あれ、ociのv1以上だよな???なんで動かないんだ?
調べる中で、、まさに起きている事象通りのissueがありました。

v0.11.0: unsupported manifest media type and no default available: application/vnd.oci.image.manifest.v1+json moby/buildkit#3491
(訳)バージョン0.11.0では、デフォルトで対応できるフォーマットはありません。

バージョンの問題が悪さをしている模様です。

記載の通り、moby/buildkitのバージョンを下げました。このときは
provenance: falseを削除しても、Lambda関数の更新に成功しました。

まとめると・・・

  • moby/buildkitのバージョンを最新で進めるなら、provenance: falseを記述
  • moby/buildkitのバージョンを下げるなら、provenance: falseは不要

実行時間は短縮できた?

詳しく話していない部分もありますが、最終的に以下のworkflowを実行しました。

  • Linterツール(ruff)によるチェック
  • アプリケーションテスト
  • DockerイメージのBuild、ECRへPush、Lambda関数の更新

キャッシュ利用なし:1m30s-1m50s
image.png

キャッシュ利用あり:40-50s
image.png

実行時間を半分近く短くできました!

キャッシュを使いたくない場合

キャッシュを使った話が中心でしたが、workflow_dispatchを用意すれば、手動実行でキャッシュを使わない形のworkflowを実行できます。

yamlファイルの内容

ここでは、pipの方は省略します。

on: 
  push:
  workflow_dispatch:
    inputs:
      docker-no-cache:
        description: "Build docker images with no cache" # ブラウザでチェックボックス横に表示
        default: false
        required: false
        type: boolean
        # 略
      - name: Docker build and push to ECR         
        uses: docker/build-push-action@v5
        with:
       # 略
          push: true
          no-cache: ${{ inputs.docker-no-cache == true }} # チェックありなら、キャッシュを利用しない
          cache-from: type=gha,scope=${{ github.workflow }}
          cache-to: type=gha,mode=max,scope=${{ github.workflow }}

ブラウザの表示
image.png

感想と今後

最終的なworkflow

workflowで自分自身でキャッシュまで導入したのは初めてだったので、勉強になりました。公式ドキュメントの漁り方も少しは理解できた気がします。

Github Actionsについて、理解すべきことはたーくさんありそうなので、少しずつやっていきたいと思いました。記事を書いた時点では、以下のようなことが気になっています。

  • AWSの認証情報に関して、OIDCを使ってセキュアなワークフローに(参考
  • AWS Lambdaはマルチアーキテクチャ対応のイメージはビルドできないようなので、そのあたりの追加確認(参考
  • Lambda関数更新で利用したリポジトリなどを参考に、Custom Action作成方法やOSSライセンスに関する理解
余談続:s3へのキャッシュアップロード

余談:s3とazblobはもう選択可能?

以下のように実装を変更して実施してみました。そしてうまくいきましたね。

  - name: Docker build and push to ECR         
    uses: docker/build-push-action@v5
    with:
      context: .
      file: ./Dockerfile
      tags: ${{ secrets.AWS_REGISTRY_URL }}:latest
      push: true
      cache-from: type=s3,region=ap-northeast-1,bucket=github-cache-bucket,name=${{ github.workflow }}
      cache-to: type=s3,region=ap-northeast-1,bucket=github-cache-bucket,name=${{ github.workflow }},mode=max

実施後、s3にファイルがアップロードされていました。
image.png

1回目のworkflow実行(キャッシュ設定前)
image.png

2回目のworkflow実行(キャッシュ設定後
image.png

1
1
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
1
1