👏

GitHub ActionsでDockerイメージを作成し、そのイメージを使ってローカルのDockerでコンテナを起動する

2024/02/19に公開

最初に

ローカルでdocker buildするとマシンのメモリが食われるて他の作業ができなくなったり、時間もかかるし環境変数とかたくさん渡してると設定も大変なので GitHub Actions でできるといいなと思ったのでやってみました。

Dockerファイルとワークフローファイルを用意する

テスト用に簡単なDockerfileを用意します。

Dockerfile
FROM ubuntu

CMD ["echo", "hello"]

続いてワークフローのファイルです。

.github/workflows/generate-docker-image.yml
name: GENERATE-DOCKER-IMAGE

on:
  workflow_dispatch:

jobs:
  generate-docker-image:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: BuildImage
        uses: docker/build-push-action@v5
        with:
          context: ./
          file: ./Dockerfile
          outputs: type=tar,dest=docker-image.tar
          push: false
          tags: "my-docker-image"

      - name: UploadArtifact
        uses: actions/upload-artifact@v4
        with:
          name: docker-build-output
          path: ./docker-image.tar

ワークフローでは以下のステップを実行します。

  • ブランチのチェックアウト
  • DockerfileからDockerイメージを作成してカレントディレクトリに配置
  • ビルドしたDockerイメージをGitHubのストレージにアップロード

イメージ作成には docker/build-push-action を使用します。
また、actions/upload-artifact を使うとワークフローで作成した成果物をGitHubのストレージにアップロードして保存することができます。(参考: ワークフロー データを成果物として保存する - GitHub Docs

イメージのビルド部分について

属性 説明
context ビルドコンテキストにカレントディレクトリを指定
file ビルドで使うDockerfileのパスを指定
outputs tar形式で、"docker-image.tar"というファイル名で出力する
push pushはしないのでfalseを指定
tags イメージ名を"my-docker-image"と指定

これでカレントディレクトリにdocker-image.tarというファイルが生成されます。

- name: BuildImage
        uses: docker/build-push-action@v5
        with:
          context: ./
          file: ./Dockerfile
          outputs: type=tar,dest=docker-image.tar
          push: false
          tags: "my-docker-image"

ワークフローを実行する

ワークフローのトリガーをworkflow_dispatchにしてあるので画面上からブランチを選択して実行できます。

on:
  workflow_dispatch:

「Run workflow」を押して実行します。

成功すると、Artifactsにdocker-imageが作成されます。

生成したDockerイメージをダウンロードする

docker-imageをクリックするとzipファイルに圧縮されてダウンロードされます。
ダウンロードフォルダに移動して確認します。

Downloads $ ls
docker-image.zip

現状はzip形式以外でダウンロードする方法はないようでした。
Zipped Artifact Downloads

現在のところ、ワークフロー実行終了後にアーティファクトをZIP以外の形式でダウンロードしたり、アーティファクトのコンテンツを個別にダウンロードしたりする方法はない。この制限の結果として、ワークフロー実行中にzipがアップロードされ、その後UIからダウンロードされた場合、二重のzipが作成されることになる。

解凍する

unzipコマンドで解凍します。
ダブルクリックで解凍するとtarファイルまで解凍されてしまのでコマンドでやります。

Downloads $ unzip docker-image.zip
Archive:  docker-image.zip
  inflating: docker-image.tar

ローカルのDockerにインポートする

actionsの画面でdocker/build-push-action@v5のログを見ると#6 exporting to client tarballとあります。

saveではなくexportされたものなのでloadではなくimportでローカルのDockerにイメージを取り込みます。注意が必要なのは、exportはDockerのメタ情報などは無視され、ファイルシステムのみが保存されるため、CMDの指定が無くなります。そのためimportする際に--change 'CMD ["echo", "test"]'で再度付与してやる必要があります。

Downloads $ docker import --change 'CMD ["echo", "test"]' docker-image.tar my-docker-image:latest
sha256:199e1cd44c7a2e4334084ecbc8b161e1a9346f3bd515ad1315dc4463cd672451

イメージが登録されているか確認します。

Downloads $ docker images                                        
REPOSITORY                                                                   TAG                                                     IMAGE ID       CREATED         SIZE
my-docker-image                                                              latest                                                  b05b852df1b8   8 seconds ago   77.9MB

ローカルでコンテナを起動する

docker runでコンテナを作成して起動します。
CMD ["echo", "test"]も実行されていることがわかります。

Downloads $ docker run my-docker-image
test

ちなみに、importする際に--change 'CMD ["echo", "test"]'を付けずにやった場合、docker runでイメージ名の後ろに実行コマンドを渡してやらないと下記のようにエラーになります。

Downloads $ docker run my-docker-image 
docker: Error response from daemon: no command specified.
See 'docker run --help'.

この場合は、イメージにecho "test"を渡して実行すればうまくいきます。

Downloads $ docker run my-docker-image echo "test"
test

参考

Discussion