GitHub Actions で Artifacts を使ってdockerのbuildとpushのステップを分離させたら色々とハマった
こんにちは、ツクリンクでSREエンジニアをやってるida.です。
弊社ではプレビュー環境と呼んでる一時的な検証環境をGitHub Actionsを使って構築して運用しているのですが、インフラ構築の裏でDockerイメージのbuildをしたら早くなるんじゃね?と思い対応しました。
その過程で少しハマったので同じような人がいたら参考になればと思い記事を書いてます。
やろうとしたこと
元々は以下のようなフローで構築しておりました。
で、今回はdeploy_infrastructureの裏でbuildをしたら早くなりそうだったので以下のように変更することにしました。
今回の変更を実装するにあたり検討した記録はこちらにあるので興味があれば見てください🙇
やることとしては大きく以下を対応していきました。
- buildとpushを別々のジョブに分離する
- ジョブが分離されるのでArtifactsを用いてジョブ間でデータを連携する
- 上記2つに伴いdocker関連のエクスポート処理やインポート処理を実装
Artifactsは以下を参照ください。
最終的なリソース
最終的なリソースだけ見たいという方もいると思うので、先にリソースを貼っておきます。
後述の問題や細かな対応は全て対応しているものになります。
※※※関連する部分だけに限定してます※※※
build_image:
runs-on: ubuntu-22.04
defaults:
run:
working-directory: ./foo
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Build Docker Image
run: |
docker build -t test:latest
docker save -o build-image.tar test:latest
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: build-image.tar
path: ./foo/build-image.tar
retention-days: 1
push_image:
needs: [build_container]
runs-on: ubuntu-22.04
defaults:
run:
working-directory: ./deploy/preview
permissions:
id-token: write
contents: read
actions: write
steps:
- uses: actions/checkout@v4
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
name: build-image.tar
path: ./foo
- name: Load Docker Image
run: docker load -i build-image.tar
- name: push
env:
REPO: xxxxxxxxxx
run: |
docker tag test:latest $(eval echo $REPO):latest
docker push $(eval echo $REPO):latest
- name: Delete Artifacts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ARTIFACTS_ID=$(gh api repos/${{ github.repository }}/actions/artifacts --jq '.artifacts[] | select(.name=="pre-build-image.tar") | .id')
if [ -n "$ARTIFACTS_ID" ]; then
gh api repos/${{ github.repository }}/actions/artifacts/$ARTIFACTS_ID -X DELETE
else
echo "No existing Artifacts found."
fi
問題 1: Artifacts のアクションは working-directory が無視される
Artifactsを利用する際はdownload-artifactやupload-artifactを利用すると思うのですが、これらはなんとworking-directory
を設定していても無視してルートから対象ファイルを探します。
大体どのアクションもworking-directoryを重んじてくれるので思いこみで気づくのが遅くなりました。。
解消方法としてはpathでworking-directlyと同じパス(もしくはファイルの格納先)を指定することでちゃんとファイルを見つけてくれました。
jobs:
build_image:
runs-on: ubuntu-22.04
defaults:
run:
working-directory: ./foo
~~~~
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: build-image.tar
path: ./foo/pre-build-image.tar
download-artifactも同じくpathで出力したいパスを指定したら大丈夫です!
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
name: build-image.tar
path: ./foo
問題 2: Docker を tar 形式で保存して Load するとエラーが発生
これは私の不注意ではあるのですが、docker buildでのoutputをdocker形式もしくはdocker save
で出力したらよかったものをtar形式で出力してました。
ただ、そのtar形式で出力したイメージをdocker load -i
で読み込むと以下エラーが発生します。
open /var/lib/docker/tmp/docker-import-xxxxxxxxx/app/json: no such file or directory
いや、これわからなくないですかー???
ファイルがないって言ってますよね。。ファイルはあるのになんで見えてないんだろうとハマってました。そんな絶望の中ネットをうろうろしてたら以下のサイトを見つけて抜け出せました。
かなり助かりました🙇🙇🙇
実際にはtar形式ではなくdocker形式またはsaveで出力しましょうねということですね。
- name: Build
run: docker build -t test:latest --output type=tar,dest=build-image.tar
- name: Build
run: docker build -t test:latest --output type=docker,dest=build-image.tar
または
- name: Build
run: |
docker build -t test:latest
docker save -o build-image.tar test:latest
インポートするときは普通にdocker load -i
でインポートできます。
- name: Load Docker Image
run: docker load -i build-image.tar
その他の細かい対応について
-
pushするジョブでイメージをダウンロードした後にArtifactのイメージを削除
残しておいてもそんなに問題なさそうでしたが、ストレージの無料枠が消費されるのがちょっと気になってダウンロードとプッシュが完了したらArtifactから削除するステップを実装しました。
これでストレージコストはほとんど消費しないようになりました。- name: Delete Artifact env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | ARTIFACT_ID=$(gh api repos/${{ github.repository }}/actions/artifacts --jq '.artifacts[] | select(.name=="build-image.tar") | .id') if [ -n "$ARTIFACT_ID" ]; then gh api repos/${{ github.repository }}/actions/artifacts/$ARTIFACT_ID -X DELETE else echo "No existing artifact found." fi
ちなみにこの対応には
actions: write
が必要なので忘れず付与してください。
-
Artifactへのアップロード時に保存期間を最低の1日に設定
1.の対応をしてるので不要っちゃ不要なんですが、もし途中でワークフローが止まったりするとデフォルトの90日間ストレージに残ってストレージ容量を消費してしまいます。
そのため最低の1日を保持期間に設定してArtifactにアップロードします。- name: Upload Artifact uses: actions/upload-artifact@v4 with: name: build-image.tar path: ./foo/build-image.tar retention-days: 1
さいごに
サクッとできるかなと思ってやってみましたが思ったよりハマったなと思いました。
あと、肝心な起動時間は5~7分短縮できてました🎉
これでエンジニアの生産性も5~7分/回は向上できたのでより早くユーザに価値を提供していけるようになるのかなと思ってます。
Discussion