白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

GitHub Actionsのself-hosted runnersをDockerで作ってみる

こんばんは。
ずっと暑いですね。外に出ると溶けそうになります。

さて、最近はCI/CDといえばGitHub Actionsが結構有力な選択肢ですが、そういえば自分で構築したことがなかったなと思って試してみます。
前半はポチポチ触っていますが、後半に最終的なDockerfileの設定もあるので、場合によっては読み飛ばしてください。

self-hosted runnersってなんぞや

GitHub Actionsではpushやpull requestといったイベントをトリガーに任意のワークフローを実行することができます。
GitHubが用意してくれている環境を「github-hosted runners」、自分で用意する環境を「self-hosted runner」といいます。

github-hosted runnersはAzureの仮想マシンで動いているらしいですね。
パブリックリポジトリなら無制限、プライベートリポジトリだと無料枠があるようです。(Freeだと2000分/月)
GitHub Actions の課金について - GitHub Docs

self-hosted runnerはHTTPSのロングポーリングを使ってランナー側からGitHub側にアクセスしているので、ポート開放やIPアドレスの割当等が不要なあたりがとても便利です。

作るならDockerがいい

というわけで自宅のLinuxサーバにDockerを使ってself-hosted runnerを建ててみます。

手順を確認

まずは対象としたいリポジトリのSettings>Actions>Runnersから「New self-hosted runner」ボタンを押します。

自身の環境を選択します。今回の私の場合はRunner imageがLinuxでArchitectureがx64です。
LinuxでArchitecutureがよくわからない場合は「uname -a」コマンドとかを叩いてみるとよいかと思います。間違えると後で面倒なので先に確認しましょう。(ハマった人)

コンテナの準備

最初からDockerfileを書きたい気持ちもあるのですが、なんだかつまづきそうな気がするのでひとまずプレーンにコンテナを建ててコマンドを叩いていくことにします。

Dockerfile

FROM ubuntu:24.04

docker-compose.yml

version: '3.8'
services:
  self_hosted_runner:
    build: .
    container_name: self_hosted_runner
    tty: true
    stdin_open: true

上記の2ファイルを適当なフォルダに作ったら以下のコマンドを叩いて、コンテナの起動&コンテナの中に入ります。

docker compose up -d
docker exec -it self_hosted_runner /bin/bash
手順に沿って作業

基本的には画面に記載のコマンドのとおり実行していくのですが、なんだかんだあれが足りないとかこれが違うとか言われます。
インストールが必要になるのはsudoとcurlなので、先にapt-getを更新したうえでインストールしておきます。

apt-get update
apt-get install -y sudo curl

次にGitHubのページに従ってダウンロードまで済ませます。ハッシュのチェックはお好みらしいので一旦省略。(インストールするもの増えるので)

mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.317.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.317.0.tar.gz

あとは設定のシェルを実行すればOKと思いきや、

./config.sh --url https://github.com/{user}/{repository} --token {token}

rootユーザじゃダメだよ的なことを言われます。

Must not run with sudo

仕方がないのでユーザを作ります。このユーザをsudoグループに入れて最初からこのユーザを使うのもありですが、今回はつけないでおきます。

useradd -m runner -s /bin/bash
su runner

で、改めて実行すると

./config.sh --url https://github.com/{user}/{repository} --token {token}

依存関係が不足しているから、インストール用のシェルを叩いてと言われます。
が、何故かこれを実行しても問題が解決しません。(rootで実行とrunnerユーザにsudo権限つけて実行の両方試したけどダメでした…何故…)

Libicu's dependencies is missing for Dotnet Core 6.0
Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies.

仕方がないので足りないと言われているLibicuを自分でインストールします。

exit   # rootに戻る
apt-get install -y libicu-dev
su runner

今度こそ実行できるようになります。

./config.sh --url https://github.com/{user}/{repository} --token {token}

なんかこんな感じにいろいろなことを聞かれます。(Enterポチポチで良きに計らってくれます)

--------------------------------------------------------------------------------
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___(_) |_| | | |_   _| |__      / \   ___| |_(_) ___  _ __  ___      |
|      | |  _| | __| |_| | | | | '_ \    / _ \ / __| __| |/ _ \| '_ \/ __|     |
|      | |_| | | |_|  _  | |_| | |_) |  / ___ \ (__| |_| | (_) | | | \__ \     |
|       \____|_|\__|_| |_|\__,_|_.__/  /_/   \_\___|\__|_|\___/|_| |_|___/     |
|                                                                              |
|                       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 f6e1c843bd72]

This runner will have the following labels: 'self-hosted', 'Linux', 'X64'
Enter any additional labels (ex. label-1,label-2): [press Enter to skip]

√ Runner successfully added
√ Runner connection is good

# Runner settings

Enter name of work folder: [press Enter for _work]

√ Settings Saved.

これで設定が完了したので、あとは起動のシェルを叩けば待受状態になります。

./run.sh

√ Connected to GitHub

Runnerが作られて状態がIdleになっています。

Dockerfileにまとめよう

せっかくDockerを使っているのでまとめてみます。
RUNをどこまで繋げるものなのかよくわからない…繋げられる場合は全部繋げてしまっていいのだろうか。

Dockerfile

FROM ubuntu:24.04

RUN apt-get update \
 && apt-get install -y sudo curl libicu-dev

RUN useradd -m runner -s /bin/bash

RUN mkdir actions-runner && cd actions-runner \
 && curl -o actions-runner-linux-x64-2.317.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz \
 && tar xzf ./actions-runner-linux-x64-2.317.0.tar.gz 

USER runner
WORKDIR /actions-runner

RUN ./config.sh \
      --url https://github.com/{user}/{repository} \
      --token {token} \
      --unattended \
      --name sample-runner

CMD ["./run.sh"]

docker-compose.yml

version: '3.8'
services:
  self_hosted_runner:
    build: .
    container_name: self_hosted_runner

これで「docker compose up -d」一発でランナーの立ち上げまでできるようになりました。
中身ほとんどないのでdocker composeじゃなくてもいいかもしれないです。

試しにWorkflowを動かしてみる

とりあえず動作確認がしたいので、すごくシンプルなワークフローを作ります。
今回は「workflow_dispatch」を指定しているので、手動でトリガーする形です。
./github/workflows/sample.yml

name: Sample
on: workflow_dispatch
jobs:
  echo:
    runs-on: self-hosted
    steps:
      - name: Hello world
        run: echo "hello, world" && whoami && date

こんな感じ。

Actionsタブにワークフローが表示されるようになります。

Run workflowで実行すると、無事に実行されたようです。

ランナー側にはこんな感じでコンソール出力されていました。

2024-07-21 14:30:52Z: Running job: echo
2024-07-21 14:31:03Z: Job echo completed with result: Succeeded

わりとすんなり感はあったけれど

環境構築はなんだかんだでハマりますね…。
でも、設定のシェルが叩けるようになったあとは「もうつながったの?」という感じのすんなりさでした。

今回はちょっとした処理を試しただけですが、実際に利用するのであればDinDを使ってコンテナを綺麗に保ったり、DooDを使って別のリソースにアクセスしたりみたいな工夫が必要になってきそうですね。
(DooDについては以前の記事があるので良ければ参考に)

正直現状だと家でCI/CDちゃんとしたいような使い方をしていないので何ともですが、外からアクセスできることを生かした面白いことができたりしないもんかなと思っています。
外出先からVPNを有効にするとかできたら便利そうかなくらい…?何かないかな。

それでは。