kbigwheelのプログラミング・ソフトウェア技術系ブログ

プログラミング・ソフトウェア技術系のことを書きます

DockerベースのGithub Actionsの憂鬱

tl;dr

  • DockerベースのGithub Actionsは僕自身がGithub Actions 8ヶ月使ってみてわかったことまとめ - Qiitaの記事で絶賛した通りポータビリティ・独立性などの点で画期的でした
  • しかしリリースからほぼ1年たった現実ではNodejsベースのGithub Actionsのほうが好まれています
  • 原因はDockerベースのActionの重さにあります
  • CIの開始時に毎回docker pull / buildが走るため1つに付き10 ~ 数十秒かかるため、同じ機能のActionならDockerベースのActionよりセットアップが1~3秒で終わるnodejsベースのActionがよく選ばれます

長めの説明

Dockerベースの再利用可能なCIパーツ、という思想はGithub Actions 8ヶ月使ってみてわかったことまとめ - Qiitaに書いたとおり画期的でした。 僕はこの時点ではDockerが世を席巻したようにDockerベースのCIパーツという思想がCIサービスで多用されるのではないかと想像していました。 しかし、現実にはGithub Actionsサービス内ですらDockerベースのGithub ActionsよりNodejsベースのGithub Actionsの方が好まれており、Terraformの公式ActionsのようにDockerベースだったものをNodejsベースで置き換えるようなケースもちらほら出ています。

なぜDockerベースのGithub Actionsはだめなのか

だめな点1. Action実行の遅さ

NodejsベースのActionと比べてDockerベースのActionは明らかに遅いです。 Docker内での処理実行が遅いというわけではありません。そこは普通のDockerコンテナ実行と同じくほとんどノーペナルティと言って良いです。 遅いのはActionを開始するまでの準備です。 NodejsベースのActionがたかだか数十KBのzipのダウンロードと解凍なのに比べてDockerは数十 ~ 数百MBのDockerイメージをDockerhubその他からダウンロード・解凍する必要があります。 経験にこれは1つに付き数十秒かかります。DockerベースのActionが複数あればその分だけかかることになります。 またこの処理はワークフロー全体の一番最初に必ず実行するため、一連のワークフローの一番最初のAction実行でエラーが発生して以降のステップがまったく実行されない場合でもすべてのDockerベースアクションをダウンロード・解凍する必要があります。

だめな点2. 柔軟性の欠如

DockerベースのActionはActionへの入力(jobs.<job_id>.steps.with)を元に処理を行うことが基本ですが、柔軟性に乏しいと感じることが多いです。また $GITHUB_WORKSPACE のディレクトリもまたdockerコンテナ実行時にカレントディレクトリとしてマウントされるのですが、マウントポイントを $GITHUB_WORKSPACE ではなくそのサブディレクトリにすることなどもできません。 例えばpip installしてくれるDockerベースのGithub Actionsを作ったとすると、requirements.txtがリポジトリルートにあればよいですがサブディレクトリにある場合だと途端にそのGithub Actionsは使えないことになります。

考えてみるとCIを組む側としてほしかったのは相互に影響しあわない処理環境の独立性がメインであり、実行する処理内容が細かく規定されているのは余計かもしれません。

だめな点3. GitHub公式が別にDockerベースActionを押していない

DockerベースのActionが主流になれば、各種言語インタープリタコンパイラなどは個別のGithub Actionsになり、CIの実行ホストはさながらalpine linuxのようにminimumなOSになると思っていました。 しかし現実にはGithubvirtual-environments/Ubuntu2004-README.md at main · actions/virtual-environmentsを見ればわかるように古今東西あらゆる言語のあらゆるバージョンをインストールしたホストでGithub Actionsを実行しています。 これは最初からインストールされていれば新たにバイナリなどをダウンロードする必要がないからという理屈でこうしているのだと思われますが、この場合インストールされているバージョンや言語・ライブラリはすべてGithub Actions運用側の選択次第ということになります。

もしDockerベースのGithub Actionsに力を入れるのであれば、もっともネックとなっているイメージのダウンロード時間を改善するべくダウンロードしたイメージのデータを一定時間キャッシュできるようにする、直接Dockerhubからダウンロードするのではなくプロキシを通すことでキャッシングするなどの選択肢はあるはずです。そのような改善がいまだ入らず、結果的に全部入りのVMで実行する形が速度を考えると最善になっている今には正直失望を感じます。

ではNodejsベースのActionでいいんじゃないの?

と思われるかもしれませんが、NodejsベースのActionにも欠点はあります。 1つはjavascriptという1つの言語に実装が縛られることです。これはこの言語に慣れ親しんでいない人にとってはかなりのハードルになります。もう1つがnodejsが外部コマンドの呼び出しやファイル操作などいわゆるシェルスクリプト的な処理に向いていないことです。CIの処理というのは基本的にシェルスクリプトがベースになりますが、Nodejsの場合はそれをjavascriptの同等コードに置き換える必要が出てきます。置き換えられる場合はまだましで、現実的にはできないケースも多々あります。

それに対してDockerベースのActionはシェルスクリプトが基本であり、柔軟性も高いです。

まとめ

というわけで、現状DockerベースのGithub ActionsとNodejsベースのGithub Actionsはそれぞれ一長一短あります。ただしよく使われるものはCIの実行時間を短くして使い勝手を良くするためにNodejsベースで実装されるものが多いです。

自分でカスタムアクションを作成する場合は上記の使い勝手と実装のしやすさの間のトレードオフを踏まえてどちらを採用するか決めましょう。