GitHub Runner as container (Docker) using Moonrepo task as launcher

GitHub Runner as container (Docker) using Moonrepo task as launcher

One command to set up a runner and handle jobs

ยท

3 min read

Prerequisites

  • Full understanding of Moonrepo tasks system (and a Moon project) (moonrepo.dev)
  • Full understanding of GitHub Actions (And a repository, with the Moon project inside; obvious? Just saying ๐Ÿ˜˜)
  • Full understanding of Docker (and Docker installed; did you guess it?)
  • Basic knowledge of bash/sh scripting

What is the goal?

The goal is to be able to launch a GitHub Runner as a container from any development computer (running Docker). Additionally, integrate it inside a Moonrepo monorepo repository (๐Ÿค•) and have a task to launch it (or them) locally.

And because it's fun to start playing with monorepos and see how helpful those tools are when running in CI.

At Alpsify, we work with a monorepo powered by Moonrepo. We launch self-hosted GitHub Runners on local machines because we can't afford big Kubernetes clusters to auto-scale a fleet of runners. So, we have a fleet of approximately 5 runners across our computers. It's like free CPU time, as our computers never hit the limit and are always on during working time. It's just our way of doing it to save costs.

Note: This approach is not a perfect fit for everyone. We're just sharing something that works for us and may work for others.

GitHub Runner as a Container

Create a folder inside your Moonrepo repository (๐Ÿ˜ฎโ€๐Ÿ’จ) and name it as you wish. I'll refer to it as <project-name> afterwards.

Register it in your .moon/workspace.yml if needed (depends on your config).

This part is inspired by a discussion on GitHub (https://github.com/actions/runner/issues/367#issuecomment-1507331413)

Create a Dockerfile inside <project-name>

# <project-name>/Dockerfile
FROM ghcr.io/actions/actions-runner:latest
RUN sudo apt update -y && sudo apt install build-essential git curl -y #(whatever you need)

COPY ./runner.sh .
RUN sudo chmod +x runner.sh

CMD ["./runner.sh"]

Create the runner.sh file inside

# <project-name>/runner.sh
#!/bin/sh

./config.sh --url https://github.com/<repo-name> --token $TOKEN --name $NAME --unattended
./run.sh

At this step, you can actually run your first GitHub Runner:

Inside your <project-name> folder, run docker build --no-cache -t docker/github-runner . to build your image. Then run docker run docker/github-runner.

Don't forget to replace $TOKEN or $NAME with the actual values. You can find your $TOKEN at this link: github.com//settings/actions/runners/new. Don't replace it inside the runner.sh as it's going to be versioned. And believe me, you don't want your private secret $TOKEN to be versioned.

Moonrepo Task Configuration

Create a moon.yml file inside <project-name> folder

# <project-name>/moon.yml
...

project:
    name: 'GitHub Runner in Docker'
    description: 'GitHub Runner (as container) launcher'
    ...

tasks:
    build:
        command: 'docker build --no-cache -t docker/github-runner .'
        platform: 'system'
        local: true

  run:
    #-- -n -t
    deps:
        - 'build'
    command: 'bash ./launch-container.sh'
    platform: 'system'
    local: true

Create the launch-container.sh file

# <project-name>/launch-container.sh
#!/bin/bash
while getopts n:t: flag
do
    case "${flag}" in
        n) name=${OPTARG};;
        t) token=${OPTARG};;
    esac
done

docker run -d -e NAME=$name -e TOKEN=$token --name $name docker/github-runner

The --name $name is to quickly identify which container it is (compared to GitHub Runner registration name). Sometimes you get errors, and you need to go deep into the details, so auto-generated names are not that useful in this kind of situation. ๐Ÿฅน

Enjoy

Run the command moon <project-name>:run -- -t $TOKEN -n my-awesome-runner-1 (still need $TOKEN to be replaced)

Run it again with another name -n my-lovely-runner-1 ! Voilร ! You got another one up and running, ready to take care of your CI jobs.

Don't forget to add runs-on: 'self-hosted' in your .github/workflows/<whatever>.yml file for your CI to use it.

Look at the page https://github.com/<your-repo-name>/settings/actions/runners and enjoy your new GitHub Runners! ๐Ÿฅณ

ย