GitHub ActionsにはLinuxホストランナーとしてUbuntuがありますが、Ubuntu 20ランナーイメージは2025/02/01-04/15まででサポート終了し、以降はubuntu-20.04
ホストランナーを使えません。
とはいえUbuntu 20.04環境をCIとして利用したいケースもあるので、2025/4/16以降もUbuntu 20.04環境を使う方法を紹介します。
なんでUbuntu 20.04環境を使いたいの?
Ubuntu 20.04は2025年4月でEOLを迎え、GitHub Actionsでのランナーイメージ廃止もこれに合わせたようです。EOLを迎える以上、Ubuntu 20.04を動作環境として使うのはやめて22.04や24.04に移行しましょう。しかし、CI環境としてのUbuntu 20.04にはEOLを迎えても使用するメリットがあります。
例えば古いバージョンのglibcやglibc++のバージョンが安定動作するので、多くのLinux環境で動作するネイティブアプリケーションをビルドする環境にはうってつけです。glibcでビルドしたアプリケーションはビルドした環境より新しいglibc環境では互換性があり動作すると期待できる一方、ビルドした環境より古いglibc環境ではまず動作しません。となると、幅広い環境で動作するアプリケーションをビルドするには、古すぎず安定して使える環境のglibcを用いてビルドするのは理にかなっています。
さて、glibcはLinuxディストリビューションごとにバージョンが異なるため、ビルドした環境より新しいglibc環境で動作するかは各ディストリビューションのglibcバージョンに依存します。各ディストリビューションのglibcバージョンは次のサイトでまとまっているので見てみましょう。
- Ubuntu 20.04: glibc 2.31
- Ubuntu 22.04: glibc 2.35
- CentOS Stream 9: glibc 2.34
- CentOS Stream 10: glibc 2.39
Ubuntu 22.04のglibcは2.35
と、結構多くのディストリビューションよりも高いバージョンだと分かります。もちろんUbuntu 22.04のglibcのバージョンを下げることも検討できますが、glibcのバージョンを下げた時に各種ツールチェインが素直に動くかというとそうでもありません。翻ってUbuntu 20.04環境はglibc 2.31
と他ディストリビューションよりわずかに古く、また依存が解決された状態です。
Ubuntu 20.04はglibc互換性を考えると最適なビルド環境に見えてきませんか?
これまでのUbuntu 20.04環境の使い方
2025/4/15までは、runs-on
にubuntu-20.04
を指定するだけでUbuntu 20.04をホスト環境として使えました。
例えばこの環境に.NET SDKとRustをインストールするワークフローは次の通りです。
name: ubuntu-20.04 on: pull_request: branches: [ main ] workflow_dispatch: jobs: build: runs-on: ubuntu-20.04 timeout-minutes: 30 steps: - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 with: dotnet-version: '9.0.x' - name: check dotnet version run: dotnet --list-sdks - uses: actions-rust-lang/setup-rust-toolchain@9399c7bb15d4c7d47b27263d024f0a4978346ba4 # v1.11.0 - name: check rust version run: rustc --version - name: check cargo version run: cargo --version
2025/4/16以降は、ubuntu-20.04
を指定してもUbuntu 20.04環境は使えません。
Requested labels: ubuntu-20.04 Job defined at: guitarrapc/ubuntu-glibc/.github/workflows/ubuntu20-old.yaml@refs/pull/2/merge Waiting for a runner to pick up this job... Job is waiting for a hosted runner to come online. Job is completed before starting.
2025/4/16以降のUbuntu 20環境の使い方
GitHub ActionsでUbuntu 20.04を使う方法をいくつか考えてみましょう。この中だとBが現実的に手間が小さく、シンプル、何か変更あっても保守しやすそうです。今回はこの方法を考えます。
- A: GitHub Actionsのジョブ中でDocker ComposeなどでUbuntu 20.04コンテナを起動して実行
- B: ジョブをUbuntu 20.04コンテナで実行する
- C: 適当なUbuntu 20.04マシンを用意してセルフホストランナーで実行
- D: そのほか素晴らしいアイデア
Ubuntuコンテナイメージを選択する
GitHub Actionsのジョブをコンテナで実行する方法1を使うと、GitHub Actionsジョブをホストランナーではなく指定したコンテナで実行できます。
では、どのコンテナイメージを使うと良いでしょうか?ビルドで使うので、git
やcurl、build-essential、ca-certificatesなどは入っていて欲しいです。ホストランナーなら.NET SDKなど各種ランタイムも入っていますが、そこはあきらめるとしてもある程度のセットアップは避けられるなら避けたいものです。ぱっと思いつくのは次の3つです。
- A: Docker Hubの公式Ubuntuイメージを利用
- B: 自分でビルドしたイメージをghcrにホストして利用
- C: 他のイメージを利用
AのDocker Hubの公式Ubuntuイメージには、curlやbuild-essentialを始めとしたツールがほとんど入っていません。ただ、18.04イメージがまだ残っているところを見ると、公式Ubuntuが数年以内に過去のイメージを消す確率は低そうです。
BはAの発展形です。ビルドごとのツールセットアップに時間がかかるなら、事前にイメージをビルドしてghcrにホストしておくと効率的です。ただしDockerfileを用意してイメージを作成し、ghcrにホストするなどのメンテナンスが必要です。
Cは、A/Bの手間を避けたい気持ちが強い選択です。始めから各種aptが入っている3rdパーティのUbuntuイメージがあれば手間を大きく削減出来そうです。3rdパーティということは、Ubuntu 20.04のコンテナイメージがEOLで消されてもしょうがないリスクがあります。
条件をいろいろ検討すると、Aの公式Ubuntuイメージを使ってビルドごとにツールをセットアップで始めれば良さそうです。ツールのセットアップ時間が耐えきれないぐらいに長いなら、Bにしましょう。
方針が決まったのでワークフローを用意します。
ジョブをUbuntu 20.04コンテナで実行する
GitHub Actionsのジョブをubuntu:20.04
コンテナで実行するには、runs-on: ubuntu-24.04
2を指定しcontainer
にimage:ubuntu:20.04
を指定します。後は必要なツールをセットアップして、アプリケーションビルドすればOKです。
name: ubuntu-20.04 on: pull_request: branches: [ main ] workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 container: # Needs to lock glibc version to 2.31, ubuntu 20.04 has glibc 2.31 image: ubuntu:20.04 timeout-minutes: 30 steps: - name: apt-get env: DEBIAN_FRONTEND: noninteractive run: | apt-get update apt-get install -y unzip curl git autoconf build-essential ca-certificates tzdata # follow https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install?tabs=dotnet8&pivots=os-linux-ubuntu-2004#register-the-ubuntu-net-backports-package-repository - name: apt-get (.NET) run: | apt-get install -y libicu66 - name: check glibc version run: ldd --version - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 with: dotnet-version: '9.0.x' - name: check dotnet version run: dotnet --list-sdks - uses: actions-rust-lang/setup-rust-toolchain@9399c7bb15d4c7d47b27263d024f0a4978346ba4 # v1.11.0 - name: check rust version run: rustc --version - name: check cargo version run: cargo --version
ワークフローを実行すると.NET SDKやRustツールチェインがセットアップできているのがわかります。
コンテナ実行ジョブの注意点
コンテナ実行ならではの注意点があります。公式ドキュメントでは記載がないので参考にどうぞ。各セクションは次のような意味を持ちます。
- ✔️ DO: 推奨
- ⚠️ CONSIDER: 検討/考慮する
- ❌ AVOID: 避ける
✔️ DO: 公式のイメージセットアップを参考にする
Ubuntu 20.04など指定した環境を再現する場合、公式はどうやっているのか、どうやるのを推奨しているのか参考にするのが良いでしょう。
- GitHub ActionsのUbuntu 20.04ホストランナーOSはactions/runner-imagesにコンテナイメージセットアップがある
- .NET SDKのコンテナはdotnet/dotnet-dockerにイメージセットアップがある
- .NET SDKのUbuntuセットアップはMicrosoft Learn3にUbuntu 20.04向けaptセットアップがある
今回.NET SDK向けにICUだけ導入しましたが、公式ドキュメントは他ツールのセットアップも推奨しています。必要に応じて追加してください。
- ca-certificates
- libc6
- libgcc-s1
- libgssapi-krb5-2
- libicu66
- libssl1.1
- libstdc++6
- zlib1g
⚠️ CONSIDER: defaults.run.working-directoryはコンテナに存在するパスか注意する
指定したパスでrun
のコマンドを実行するのにdefaults.run.working-directory
を指定できます。コンテナ実行の場合、必ずコンテナに存在するパスを指定してください。
ホストマシンのruns-on
の場合、ホストに存在しないパスを指定していても特にエラーが出ることはありませんでしたが、コンテナにおいてはコンテナにないパスを指定するとOCI runtime exec failed
エラーが出ます。
なお、nodejs
アクションのようにrun:
を使っていない場合は、ホストに存在しないパスで実行されてもエラーになりません。
jobs: build: runs-on: ubuntu-24.04 container: image: ubuntu:20.04 # コンテナジョブ実行ならなんでもいい timeout-minutes: 30 defaults: run: working-directory: /foo # コンテナに存在しないパスを指定するのは避ける! steps: - name: apt-get run: | echo foo
OCI runtime exec failed: exec failed: unable to start container process: chdir to cwd ("/foo") set in config.json failed: no such file or directory: unknown Error: Process completed with exit code 126.
もしリポジトリにあるパスを指定したい場合は、run
が実行される前にactions/checkout
を実行しておくとエラーを回避できます。
jobs: build: runs-on: ubuntu-24.04 container: image: ubuntu:20.04 timeout-minutes: 30 defaults: run: working-directory: /foo # リポジトリに/fooが含まれる steps: # 先にチェックアウトする - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: apt-get run: | echo foo
❌ AVOID: 大きなサイズのパッケージは利用を避ける
GitHub Actionsのコンテナ実行に限った話ではありませんが、サイズの小さいパッケージを利用するようにしましょう。パッケージサイズを小さくするだけで、ビルドの安定性が上がります。
毎ビルド時にセットアップするので、パッケージサイズが大きいと失敗する確率が飛躍的に上がります。残念ながらaptはそこまで安定していませんし、.NETインストールスクリプトのホスト先も不安定です。
# AVOID: サイズの大きなパッケージは避ける apt-get install -y libicu-dev # DO: サイズの小さなパッケージを利用する apt-get install -y libicu66
まとめ
glibcのバージョンに由来してUbuntu 20.04環境を使う、そんなニッチなニーズも世の中にはあります。GitHub Actionsは任意のコンテナイメージを利用できるので、小さな努力でやりたいことが実現できるのは素晴らしいですね。
GitHub Actionsのアクションを利用してセットアップを簡略化できるのも魅力的です。
参考
- Ubuntu 20.04 LTSの提供終了 | Ubuntu
- glibc package versions | Repology
- GitHub Actions: Ubuntu 20 runner image brownout dates and other breaking changes | GitHub Changelog
- The Ubuntu 20.04 Actions runner image will begin deprecation on 2025-02-01 and will be fully unsupported by 2025-04-15 · Issue #11101 | actions/runner-images
- Running jobs in a container | GitHub Docs