Skip to main content

GitHub Actions で External Docker Engine を利用してイメージビルドを高速化する

先日、GitHub Actions で self-hosted Runner を利用することにより Docker Image Build を高速化する方法を検証しました。

この方法では、ビルドの並列数が1であるためビルドが増えると待ち時間が線形に増えるというデメリットがありました。

今回、 External Docker Host を利用して、並列数のデメリットを解消する方法を検証したので紹介します。

ssh 先の Docker Engine を利用できる

全然知らなかったんですが、ssh できるホストの dockerd を Engine として利用できるらしいです。

https://www.docker.com/blog/how-to-deploy-on-remote-docker-hosts-with-docker-compose/

クライアントで DOCKER_HOST=ssh://user@hostname とすると、クライアントで dockerd が動作しているかのように Docker を利用可能です。

そこで、複数の Runner から ssh で単一の Docker Engine を利用することにより、Runner をスケールさせることができるのでは?と考えました。

図で表すとこんな感じです。

検証

この構成が有効かどうか、検証します。

検証の前提条件

  • 大規模プロジェクトのサンプルとして Mastodon のビルドを実施します
  • 簡略化のため、イメージの Push 時間は考慮せず、ビルドの時間のみを検証します
  • ビルドの実行時間は、同条件で複数回(2~3回)実行して、10%以上の誤差がないことを確認しています
  • 前回の検証では、キャッシュなしクリーンビルドが 6m で終了しました。今回も同じマシンを利用します。

単一タスクで検証

まずこの構成でビルド速度に大きな影響が出ないか、単一のタスクで検証してみます。

後で複数 Runner で検証するため、Docker を利用して Runner を用意します。

こちらを利用させてもらいました。非常に便利。

https://github.com/myoung34/docker-github-actions-runner

スクリプトを用意して Runner を起動します。

env に External Docker Host を指定します。今回は Docker の Host に ssh することにします。

docker run -d --restart always \
  -e REPO_URL="https://github.com/takutakahashi/mastodon" \
  -e RUNNER_TOKEN="token" \
  -e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
  -e DOCKER_HOST="ssh://[email protected]" \
  -v $PWD/actions_rsa:/root/.ssh/id_rsa \
  myoung34/github-runner:latest

workflow を書き換えます。Host Verification を通過してくれないので無視します。

Runner が env を持っているため DOCKER_HOST の設定は必要ありません。

逆を言えば、workflow で env を指定すれば、Manage-Runner でもこの構成を取ることが可能です。

jobs:
  build-and-push:
    runs-on: self-hosted
    timeout-minutes: 300
    steps:
    - uses: actions/checkout@v1
    - name: Build
      run: |
        echo "StrictHostKeyChecking no" > /root/.ssh/config
        docker build -t test:${GITHUB_SHA} .

実行結果はこちらです。 COPY までの行でキャッシュが利用され、2m で終了しました。

前回の検証では、このマシンではキャッシュなしビルドが6mであったため、70% 以上の高速化ができます。

External Docker Engine によるスループットの低下はなさそうです。

並列タスクで検証

Runner を10個起動しました。圧巻ですね。

10 並列動かしてみました。

すべてのタスクで、COPY 以前までのレイヤキャッシュを利用できていることが確認できました。

CPU がサチってしまったため、ビルドの速度自体は参考値になります。

すべて正常に終了しました。

なお、CPU に考慮して2並列で動かした際は、どちらも 2m ほどで終了し、

レイヤキャッシュを利用して並列ビルドを高速化することができました。

セキュリティ上の考察

  • 通信経路

Docker Engine を動かすマシンへの疎通は ssh で実施するため、経路上のセキュリティリスクは存在しません。

Managed-Runner を利用する場合、ssh ポートを外部に公開する必要があるので、そちらは適切に防御する必要がありますが、ssh の防御手法は体系が確立されているのでそれほど難しいものではないでしょう。

  • 秘密鍵の保管

self-hosted Runner の場合なら、内部に閉じた通信を行うことで更にセキュアに運用できます。

あとは公開鍵認証で利用する秘密鍵を GitHub Actions の Secret に登録する必要があります。

Secret は Write Only であるため、GitHub 自体がハックされない限り登録した鍵が漏れることはありません。

そのため、セキュリティリスクは考慮する必要はないかな?と個人的には思います。

まとめ

レイヤキャッシュを効かせた状態で、複数並列のビルドを実行することができました。

この構成は便利そうです。