残念ながら、2024/1/27 現在 GitHub-hosted runner では BSD系のOSはサポートされていない。
ので、表題の通りself-hosted runnerを利用しようと思うのだが、これまた残念なことに、self-hosted runnerも公式にはWindows,Linux,macOSのサポートのみでBSD系OSはサポートされていないしする予定も今のところ無いようだ。
というのも、GitHub Actions runnerはC#で実装されているのだが、処理系の.NETが公式にはBSD系OSをサポートしていないので、同様にサポートをするつもりはないらしい。
それでも、やっぱりBSD系OSのCI環境が欲しい。
そういえば、FreeBSDってLinux互換レイヤーがあったような...
What will you do
方針
- FreeBSDでLinux互換レイヤーを有効化
- その上で、Linux版のself-hosted runnerを動作させる
筆者環境
macOS: 11.7.10
shell: zsh
Vagrant: 2.3.7
Docker: 24.0.6
OSやソフト等が若干古めだが、それぞれの仮想化ツールが実行できれば、バージョンはさほど問題にならないだろう。
なお、以降特別の記載がない限りは、ホストOS上のディレクトリはVagrantfileが置いてあるディレクトリを起点として記す。
Setup FreeBSD
お手軽にFree BSDの環境が欲しかったので、Vagrant+VirtualBoxで用意することにした。
Vagrantfile
に以下の内容を書き込んでvagrant up
, vagrant ssh
で接続
Vagrant.configure("2") do |config|
config.vm.box = "generic/freebsd14"
# これだと上手いこと共有できなかったので、nfsとした
# config.vm.synced_folder ".", "/home/vagrant/shared", type: "virtualbox"
config.vm.network "private_network", ip: "192.168.33.10"
config.vm.synced_folder ".", "/home/vagrant/shared", type: "nfs"
end
のちに必要になるコマンドをインストールしておく
sudo pkg install -y git
Enable Linux Compat Layer
こちらを参考にLinux互換レイヤーを有効化。
# In BSD
sudo kldload linux
sudo kldload linux64
sudo pkg install -y emulators/linux_base-c7
echo 'linux_enable="YES"' | sudo tee -a /etc/rc.conf
Install .NET
actions/runner
にLinux版 .NETを簡単にインストールできるスクリプトがあるので、流用させていただく。
(わざわざ、ホストOSとの共有ディレクトリにcloneしているのは、のちの事情のためである)
# In BSD
cd /home/vagrant/shared
git clone https://github.com/actions/runner.git
cd runner/src
./Misc/dotnet-install.sh --version "6.0.418" --install-dir "/home/vagrant/_dotnetsdk/6.0.418" --os linux
パスも通しておく
export PATH="/home/vagrant/_dotnetsdk/6.0.418:$PATH"
# In BSD
dotnet --version
Process terminated. Couldn't find a valid ICU package installed on the system. Please install libicu using your package manager and try again. Alternatively you can set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. Please see https://aka.ms/dotnet-missing-libicu for more information.
at System.Environment.FailFast(System.String)
at System.Globalization.GlobalizationMode+Settings..cctor()
at System.Globalization.CultureData.CreateCultureWithInvariantData()
at System.Globalization.CultureData.get_Invariant()
at System.Globalization.CultureInfo..cctor()
at System.Globalization.CultureInfo.get_CurrentUICulture()
at System.TimeZoneInfo.GetUtcStandardDisplayName()
at System.TimeZoneInfo.CreateUtcTimeZone()
at System.TimeZoneInfo..cctor()
at System.DateTime.get_Now()
at Microsoft.DotNet.Cli.Program.Main(System.String[])
Abort trap
libicuがないため怒られた。
ないものは入れるのが適切なのだろうが、環境変数を設定することでlibicuを要求しないようにできるため今回はそれとする。
# In BSD
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
dotnet --version
6.0.418
無事怒られずに済んだ。
Install GitHub Actions self-hosted runner
適当なレポジトリを作成して
Settings > Actions > Runners > New self-hosted runner
から手順に沿ってインストールを実施
# In BSD
cd ~
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.312.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.312.0/actions-runner-linux-x64-2.312.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.312.0.tar.gz
Let's try self-hosted runner on FreeBSD
Set up self-hosted runner
それぞれのレポジトリによってURL等は変わってくるので注意
# In BSD
./config.sh --url https://github.com/<USER>/<REPO> --token <TOKEN>
--------------------------------------------------------------------------------
| ____ _ _ _ _ _ _ _ _ |
| / ___(_) |_| | | |_ _| |__ / \ ___| |_(_) ___ _ __ ___ |
| | | _| | __| |_| | | | | '_ \ / _ \ / __| __| |/ _ \| '_ \/ __| |
| | |_| | | |_| _ | |_| | |_) | / ___ \ (__| |_| | (_) | | | \__ \ |
| \____|_|\__|_| |_|\__,_|_.__/ /_/ \_\___|\__|_|\___/|_| |_|___/ |
| |
| Self-hosted runner registration |
| |
--------------------------------------------------------------------------------
# Authentication
√ Connected to GitHub
# Runner Registration
Enter the name of the runner group to add this runner to: [press Enter for Default]
Enter the name of runner: [press Enter for freebsd14]
This runner will have the following labels: 'self-hosted', 'Linux', 'X64'
Enter any additional labels (ex. label-1,label-2): [press Enter to skip] freebsd-latest
√ Runner successfully added
√ Runner connection is good
# Runner settings
Enter name of work folder: [press Enter for _work]
√ Settings Saved.
freebsd-latest
というラベルをつけたのみで、あとはデフォルトの設定のままだ。
# In BSD
./run.sh
√ Connected to GitHub
Current runner version: '2.312.0'
2024-01-27 02:49:48Z: Listening for Jobs
ここまでは、目論見通りLinux互換レイヤーで.NETを動かすことに成功している!
あとは、実際にジョブを実行できるか試してみる。
Run job
以下のような、ワークフローを用意して、レポジトリの.github/workflows/test.yml
に置いてコミット&プッシュ
name: test
on:
push:
jobs:
test:
runs-on: freebsd-latest
steps:
- uses: actions/checkout@v4
- name: run freebsd-version
run: freebsd-version
が、ここで問題が発生...
あるはずの、tarコマンドが存在しない旨のエラーが、表示される...
Current runner version: '2.312.0'
Runner name: 'freebsd13'
Runner group name: 'Default'
Machine name: 'freebsd13'
GITHUB_TOKEN Permissions
Secret source: Actions
Prepare workflow directory
Prepare all required actions
Getting action download info
Download action repository 'actions/checkout@v4' (SHA:b4ffde65f46336ab88eb53be808477a3936bae11)
Error: tar: command not found
が、BSD環境でtarコマンドの存在を確認してみても存在している...
# In BSD
which tar
/usr/bin/tar
紆余曲折、あったが苦労して調べてみると原因はWhichUtil.Which
(whichコマンドと同じような動作をする)関数が内部で呼んでいる、System.IO.Directory.GetFiles
から返ってくる値が、空の配列、つまり、ディレクトリ内のファイルの一覧が取得できないことに起因するtarコマンドの存在確認ができなかったことだった。
(当然だが、互換レイヤーだけで完璧に動くわけではないようだ...)
ので、最終的に要求されている、tarコマンドのフルパスを直接プログラムにベタ書きすることで、無理矢理動かすこととした。
ハードコーディングは本来すべきではないが動かないのだからいたしかたない...
(.NET 7以降 であれば、標準ライブラリで tar を展開する機能が使えるのでtarコマンドがなかったらそちらにフォールバックするようにすれば良いのだろうが、残念ながら今時点ではactions/runner
で利用しているのは.NET 6系である)
これらのファイルに次のようなパッチを当てた。
diff --git a/src/Runner.Listener/SelfUpdater.cs b/src/Runner.Listener/SelfUpdater.cs
index 419a14e..bdbf506 100644
--- a/src/Runner.Listener/SelfUpdater.cs
+++ b/src/Runner.Listener/SelfUpdater.cs
@@ -411,7 +411,7 @@ namespace GitHub.Runner.Listener
}
else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
{
- string tar = WhichUtil.Which("tar", trace: Trace);
+ string tar = WhichUtil.Which("/usr/bin/tar", trace: Trace);
if (string.IsNullOrEmpty(tar))
{
diff --git a/src/Runner.Listener/SelfUpdaterV2.cs b/src/Runner.Listener/SelfUpdaterV2.cs
index b64619b..fc91e0a 100644
--- a/src/Runner.Listener/SelfUpdaterV2.cs
+++ b/src/Runner.Listener/SelfUpdaterV2.cs
@@ -371,7 +371,7 @@ namespace GitHub.Runner.Listener
}
else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
{
- string tar = WhichUtil.Which("tar", trace: Trace);
+ string tar = WhichUtil.Which("/usr/bin/tar", trace: Trace);
if (string.IsNullOrEmpty(tar))
{
diff --git a/src/Runner.Worker/ActionManager.cs b/src/Runner.Worker/ActionManager.cs
index bf7838c..c08f2f3 100644
--- a/src/Runner.Worker/ActionManager.cs
+++ b/src/Runner.Worker/ActionManager.cs
@@ -851,7 +851,7 @@ namespace GitHub.Runner.Worker
throw new InvalidActionArchiveException($"Can't un-zip archive file: {archiveFile}. action being checked out: {downloadInfo.NameWithOwner}@{downloadInfo.Ref}. error: {e}.");
}
#else
- string tar = WhichUtil.Which("tar", require: true, trace: Trace);
+ string tar = WhichUtil.Which("/usr/bin/tar", require: true, trace: Trace);
// tar -xzf
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
そしてこれを、ビルドする。
これのために環境を汚したく無いので、ホストOS上で、dockerを利用してサクッとビルドしてしまう。
BSD上でビルドを実行しないのは、単純にビルドができないからだ。
actions/runner
レポジトリに用意されている開発用のビルドスクリプトがBSD系をサポートしていない(当然だ)上に、動くように多少パッチを当てたところで、dotnet build
コマンドをはじめとするビルド系のコマンドが上手く動作しない(途中でうんともすんとも言わなくなる...)
どうやら、互換レイヤーの上では.NETのマルチスレッド系の機能が一部上手く動作しないようで、途中で止まってしまっている様子である。
(ビルド成果物をBSD環境にコピーするのが面倒だったので、わざわざ、BSDとホストOSの共有ディレクトリ上にactions/runner
レポジトリをcloneしていた。これがのちの事情というやつである)
# Host OS
cd shared/runner
docker run --rm -it -v `pwd`:/runner/ -w /runner/src ghcr.io/catthehacker/ubuntu:act-latest bash
イメージ自体はなんでも良いはずだが、必要そうな開発ツールが入っているイメージを選んだ。
# In docker container
./dev.sh layout Release linux-x64
./dev.sh package Release
ビルドが終わったら、これを動かす。
インストール手順は基本的に先ほどと一緒だが、DLしてくる代わりに自前でビルドしたものを使う。
cd ~
# 古い方を削除
rm -rf actions-runner
mkdir actions-runner && cd actions-runner
cp ~/shared/runner/_package/actions-runner-linux-x64-2.312.0.tar.gz .
tar xzf ./actions-runner-linux-x64-2.312.0.tar.gz
再度登録
# In BSD
./config.sh --url https://github.com/<USER>/<REPO> --token <TOKEN>
Retry self-hosted runner on FreeBSD
# In BSD
./run.sh
ジョブの再実行をするか、レポジトリに適当な内容でコミット&プッシュ
Run actions/checkout@v4
/usr/home/vagrant/actions-runner/externals/node20/bin/node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by /usr/home/vagrant/actions-runner/externals/node20/bin/node)
/usr/home/vagrant/actions-runner/externals/node20/bin/node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /usr/home/vagrant/actions-runner/externals/node20/bin/node)
/usr/home/vagrant/actions-runner/externals/node20/bin/node: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /usr/home/vagrant/actions-runner/externals/node20/bin/node)
/usr/home/vagrant/actions-runner/externals/node20/bin/node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /usr/home/vagrant/actions-runner/externals/node20/bin/node)
/usr/home/vagrant/actions-runner/externals/node20/bin/node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by /usr/home/vagrant/actions-runner/externals/node20/bin/node)
/usr/home/vagrant/actions-runner/externals/node20/bin/node: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by /usr/home/vagrant/actions-runner/externals/node20/bin/node)
今度は、tarコマンドが無いとは言われなかったが、nodeを実行しようとしたら、複数の共有ライブラリがないと言われた。
これは、Linuxバイナリ用の共有ライブラリが無いのが原因のようなのだが、バージョンを揃えてそれぞれを用意するのが面倒だった&互換レイヤー周りの影響を受けたくなかったので、BSD版が提供されているなら、そっちを使えばいいじゃないということで、BSD版のnodeをインストールしてリンクをはることとした。
sudo pkg install -y node
node --verison
# v20.10.0
which node
# /usr/local/bin/node
ln -s /usr/local/bin/node /usr/home/vagrant/actions-runner/externals/node20/bin/node
(今回は、要求するランタイムがnode20だけだったので、node20だけ置き換えたが、node16の方も同様にリンクを張っておくとnode16をランタイムとして動くアクションも動作するようになる)
Re Retry self-hosted runner on FreeBSD
Run freebsd-version
freebsd-version
shell: /bin/bash -e {0}
14.0-RELEASE
よし!無事に完走した!!!
End
なんとかしてFreeBSD上で、GitHub Actions self-hosted runner を動かすことに成功したわけだが、当然全ての機能が正常に動く保証はないので、試される方は自己責任でお願いしたい。
今回紹介したのは、FreeBSD 14だが、他の環境でもおそらく似たような手順を踏めば動くはずだ。
チャレンジした人がいたら、ここに記録を残していただければ、他のチャレンジャーの手助けになるだろう。
また、今回未解決だった。System.IO.Directory.GetFiles
が空の配列を返してくることについて、わかったことがあればコメントしていただけると、とても嬉しい。