Run GitHub Actions on Fedora CoreOS

United Artists, Public domain, via Wikimedia Commons

GitHub Actions is a service provided to quickly setup continuous integration and delivery (CI/CD) workflows . These workflows run on hosts called runners. GitHub provides hosted runners with a limited set of operating system choice (Windows Server, Ubuntu, MacOS).

Another option is to use self-hosted runners which gives the repository administrator more control on the runners. Self-hosted runners are dedicated to a repository or organization. The following article goes through the steps of configuring self-hosted runners using Fedora CoreOS.

Getting Started

Fedora CoreOS is a minimalist operating system designed to be easy to deploy and maintain at scale. The operating system will automaticaly update and provide, by default, the tools needed to run containers. For all of these reasons, Fedora CoreOS is a great choice to consider for running CI/CD workflows.

The first step to configure and provision a Fedora CoreOS machine is to generate an Ignition file. Butane allows you to generate Ignition’s file using a friendlier format (YAML).

Configure a Fedora CoreOS runner

To execute GitHub actions on Fedora CoreOS, the host needs the binaries and scripts used to register and run the runner. Download the binaries and scripts from the actions runner project and deploy under /usr/local/sbin/actions-runner.

version: "1.3.0"
variant: fcos
storage:
  directories:
    - path: /usr/local/sbin/actions-runner
      mode: 0755
      user:
        name: core
      group:
        name: core
  files:
    - path: /usr/local/sbin/actions-runner/actions-runner-linux.tar.gz
      overwrite: true
      contents:
        source: https://github.com/actions/runner/releases/download/v2.278.0/actions-runner-linux-x64-2.278.0.tar.gz
      mode: 0755
      user:
        name: core
      group:
        name: core

Registration and Removal token

Configuring runners for a project requires a “token”. This prevents registering or removing self-hosted runners from projects without the correct permissions. Tokens provided by Github have a one hour expiration time. If the runner restarts after this time it will require a new registration token.

The token can be problematic, in particular with Fedora CoreOS automatic updates. The update process expects that the host will restart at least once every couple weeks after receiving new data.

Luckily, it is possible to use GitHub REST API to obtain these tokens and automatically configure the runner every time the host restarts. The following manage-runner.sh script uses the APIs to retrieve a token, remove any runner already configured and register the runner with a new token.

#!/bin/bash
# Handles the Github Action runner configuration.
# Remove and Registration token expires after 1 hour, if we want our runner
# to work after a reboot (auto update) we need to refresh the tokens.

# First remove the runner with a fresh remove token
REMOVE_TOKEN=$(curl -u ${GITHUB_USER}:${GITHUB_TOKEN} -X POST -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/actions/runners/remove-token | jq -r '.token')
/usr/local/sbin/actions-runner/config.sh remove --token ${REMOVE_TOKEN}


# Then register the runner with a fresh registration token
REGISTRATION_TOKEN=$(curl -u ${GITHUB_USER}:${GITHUB_TOKEN} -X POST -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/actions/runners/registration-token | jq -r '.token')
/usr/local/sbin/actions-runner/config.sh --url https://github.com/cverna/fcos-actions-runner --token ${REGISTRATION_TOKEN} --labels fcos --unattended

The script above uses a few environment variables that contain a GitHub username and a Personal Access Token used to authenticate the REST API requests. The Personal Access Token requires the repo permissions in order to successfully retrieve the runner registration and removal tokens. The token is security sensitive so it is better to store it in a different file with stricter permissions. In this example that file is actions-runner.

GITHUB_USER=<user>
GITHUB_REPO=<repo>
GITHUB_TOKEN=<personal_access_token> 

Following is the Butane snippet that creates these two files – manage-runner.sh and actions-runner.

    - path: /usr/local/sbin/actions-runner/manage-runner.sh
      contents:
        local: manage-runner.sh
      mode: 0755
      user:
        name: core
      group:
        name: core
    - path: /etc/actions-runner
      contents:
        local: actions-runner
      mode: 0700
      user:
        name: core
      group:
        name: core

Running Actions on Fedora CoreOS

Finally, create the systemd services that will configure and start the runner. Define the services in the Butane configuration file.

systemd:
  units:
    - name: github-runner-configure.service
      enabled: true
      contents: |
        [Unit]
        Description=Configure the github action runner for a repository
        After=network-online.target boot-complete.target
        Requires=boot-complete.target
        [Service]
        EnvironmentFile=/etc/actions-runner
        Type=oneshot
        RemainAfterExit=yes
        User=core
        WorkingDirectory=/usr/local/sbin/actions-runner
        ExecStartPre=tar xvf actions-runner-linux.tar.gz --no-same-owner
        ExecStart=/usr/local/sbin/actions-runner/manage-runner.sh
        [Install]
        WantedBy=multi-user.target
    - name: github-runner.service
      enabled: true
      contents: |
        [Unit]
        Description=Run the github action runner
        After=github-runner-configure.service
        [Service]
        WorkingDirectory=/usr/local/sbin/actions-runner
        User=core
        ExecStart=/usr/local/sbin/actions-runner/run.sh
        [Install]
        WantedBy=multi-user.target

This creates two services, github-runner-configure.service (running once when the host has finished booting) and github-runner.service (running the Actions runner binaries and waiting for new CI/CD jobs).

Now that the Butane configuration is complete, generate an Ignition file out of it and provision a Fedora CoreOS Actions runner.

$ podman run -i --rm -v $PWD:/code:z --workdir /code quay.io/coreos/butane:release --pretty --strict --files-dir /code config.yaml -o config.ignition

Once the Ignition file is generated, it can be used to provision a runner on the platforms where Fedora CoreOS is available.

Configure an Action to use a self-hosted runner

The following test Action workflow will test the FCOS self-hosted worker. Create the following file in your git repository .github/workflows/main.yml

# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run. 
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: fcos

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Runs a single command using the runners shell
      - name: Run a one-line script
        run: podman run --rm fedora-minimal:34 echo Hello World !

Note that the runs-on configuration is set up to use a runner with the label fcos.

The code presented in this article is available here.

For Developers For System Administrators Using Software

10 Comments

  1. GitLabber

    Very nice. One can also use docker images, though compute time limits and overhead still make a self configured runner useful.

  2. VladimirSlavik

    Thank you, that’s a nice concise write-up.

  3. Ralf Seidenschwang

    https://coreos.github.io/ignition/supported-platforms/

    I‘m not pretty sure what this kernel parameter does? This seems to be for installation without any disks, so the kernel pulls the configuration from a link and configures the machine according to the config-files that was downloaded, and I assume disk partitioning is one of the tasks that can be found here. So the disks are not in use here and the pulled configuration via this Kernel parameter can be applied to the disks without the disks being touched upfront.

    This sounds reasonable, but I would be very cautious with these automatic installation methods that are available for different products (like OpenShift), since they are pulling the same files from a internet source, ignoring my firewall settings completely.

    Is that the way these installer scripts work?

  4. Sergey

    Sorry to be off-topic, but I don’t think that’s what fcos is for. In my opinion, fkos is only supposed to ensure the launch of containers and no more.
    And everything else should already be in them. For example the same ubi, etc.

  5. I have to agree with Sergey; this really isn’t a use case for FCOS. FCOS is for having a clean, immutable environment for hosting containers. If you want to run your Github actions in a clean immutable image, then that is a great use case for running a container on FCOS.

    The benefit of using a container on top of FCOS is that you can have multiple images with multiple runners. Maybe you’re compiling and packaging an application? Well with containers you can have a Fedora, RHEL, Debian, and an Ubuntu image so now you’re application gets compiled and packaged for all of them while only requiring a single FCOS host.

    If you’re don’t have to use Github but want to pipeline your application, I’d recommend Gitlab instead. They have an RPM package for their runner. You can have the ignition file install the RPM and the podman-docker package, register the runner, and then set the executor to Docker. Then your pipelines can deploy images and containers on demand for running your jobs.

    • My interest in using FCOS in that case was to be able to very easily destroy and provision new runners. And also not having to care about updating the host since FCOS does that automatically for me.
      With that configuration I can have a couple of long running runners, and quickly (a minute or so) burst new runners if needed.

      Running GitLab CI runner on FCOS would be super interesting too, I believe that they provide a container image which could make it super easy.
      Would you be interested do write a Fedora Magazine article about it ?

  6. Stephen Snow

    Nice article Clement. Thanks for more useful tips, and another very suitable use for FCOS, as a Github Actions server.

  7. Ralf Seidenschwang

    For people relatively new to the topic:
    I’ve found that very helpful, the tutorial is half an hour long:
    https://www.youtube.com/watch?v=R8_veQiYBjI

  8. Jiri Konecny

    Hi, I just want to warn anyone using this guide. The guide is correct but GitHub self hosted runners are not recommended by GitHub because they are easy to attack.
    Problem starts when external contributor will create a PR with a change to GitHub workflow. One problem is that external contributor could attach your self-hosted runner to any existing workflow with a pull_request trigger by just changing the workflow. The second problem is that the self-hosted runner immediately executing any code written to PR. So it’s pretty easy to use your host to access internal resources or use your runner for crypto currency mining.

    To avoid such an issue you can do three things.
    – Use private repository.
    – Use only

    pull_request_target

    and not

    pull_request

    trigger. (beware, if you do it wrong, contributor has a write permissions and access to your secrets)
    – Manually gate all external contributors to verify that they don’t changing your workflow. (See the third option here: https://github.blog/changelog/2021-07-01-github-actions-new-settings-for-maintainers/ )

  9. Aricjoshua

    In that situation, my motivation for utilizing FCOS was to be able to quickly delete and provide new runners. Also, I don’t have to worry about upgrading the host because FCOS does it for me.
    With that setup, I can have a few long-distance runners and rapidly (a minute or two) blast in fresh runners if needed. https://cookieclicker2.io/
    Thank you!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

The opinions expressed on this website are those of each author, not of the author's employer or of Red Hat. Fedora Magazine aspires to publish all content under a Creative Commons license but may not be able to do so in all cases. You are responsible for ensuring that you have the necessary permission to reuse any work on this site. The Fedora logo is a trademark of Red Hat, Inc. Terms and Conditions

%d bloggers like this: