In my last post, I explained a deployment difference between ARM templates and Terraform. Then I suggested a few possible workarounds for the Terraform PaaS networking challenge. One of which was deploying a GitHub-runner in a network you control. This post will outline the steps required for deploying your own self-hosted GitHub-runner.
Since I do not have access to a GitHub organization or Enterprise, this post will focus on repository-level runners. I will show you how to use a GitHub-runner in a container on your local machine. Future posts might add examples for Azure Container Instance, Azure VM or Azure Kubernetes Service, but I assume it is similar to this.
If you want to follow the steps outlined in this post, you will need:
There are several different roads to a containerized self-hosted GitHub-runner.
I have chosen the last option, simply because this requires the least amount of container image build experience. Which I have way too little of.. Choosing the custom build option can result in a slimmer container image, and will most likely reduce execution time.
You can find a nice install guide here for Docker Desktop on Windows with WSL2 or Hyper-V backend.
If you are using Ubuntu, you can find a guide that suits you here. More distros are listed in the left side navigation.
Make sure you can reach the internet from containers running on your machine. This is required for downloading container images, and contacting GitHub Workflows for reporting status and fetching jobs.
You also need to be able to run Linux containers, and make sure this is chosen in your docker engine. You can find instructions for Docker Desktop here.
Docker Desktop service, or some other Docker engine, must be running. I use Docker Desktop personally, but mostly because it is easy to use for my test cases. When it is running, you can see an icon in the taskbar.
You should already have a suitable repository, but if you don’t just create one with a descriptive name (docker-test, test-docker, gh-runner-testing, etc.).
Remember to record your token somewhere safe! You will not be able to retrieve the token later.
Make sure you do not check this in to any repository anywhere. Private or public, does not matter. Secrets should never be checked in to any repository, ever. You can always create a new token, or regenerate it if it is lost or compromised.
Starting the docker container is as simple as running a docker command in your shell of choice. Here is the command I will be running for this demo, slightly modified from the source (repo name - tf-dockerpoc):
docker run -d –restart always –name github-runner \ -e REPO_URL=“https://github.com/torivara/tf-dockerpoc" \ -e RUNNER_NAME=“github-runner” \ -e ACCESS_TOKEN=“myGitHubPersonalAccessToken” \ -e RUNNER_WORKDIR="/tmp/github-runner-tf-dockerpoc” \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/github-runner-tf-dockerpoc:/tmp/github-runner-tf-dockerpoc \ myoung34/github-runner:latest
I will try to describe the different parts of this command below.
If you are running on Windows, the line endings need to be changed according to your shell. Use the backtic ( ` ) for PowerShell. The command above is running in Ubuntu on WSL2 backend, and is therefore using the backslash ( \ ). The WSL2 backend automatically “speaks” to Docker Desktop for running containers. More on this backend here. Also the path mounts might need modification for running on Windows.
This GitHub repository contains relevant information on running the container differently, for instance how other environment variables can be set.
When the container is started successfully, you can see the runner as “idle” in your GitHub repository Settings -> Actions -> Runners.
When you have this runner in an idle state, it is ready to accept jobs. There are several different ways to use this in your workflow, but this is the easiest if you have only one self-hosted runner:
name: Package
on: release: types: [created]
jobs: build: runs-on: self-hosted steps: - uses: actions/checkout@v1 - name: build packages run: make all
Note the runs-on: self-hosted property. More info on usage here.
Use actions as you would on a GitHub hosted runner.
You should now be able to run your own Self-Hosted GitHub-runner, and make sure your public IP is added to the PaaS firewall exceptions. Of course, this is not ideal for a production scenario, but it is a nice proof of concept. The runner will stop running when your machine shuts down.
A better solution would be to host it in Azure VMs (e.g. Azure VM or Azure VM Scale Set), cloud container service (e.g. ACI or GCR), or cloud K8s service (e.g. AKS or GKE). I would prefer a managed service, to reduce administrative effort. This would make scaling and high availability easier to accomplish.
In my next blog post I will go a step further and actually provision Azure resources with Terraform from this runner. Most likely an Azure Key Vault and an Azure Storage Account, both with network restrictions enabled.
As always, don’t hesitate to comment if you see room for improvement!