Martin Koníček

Blog

Self Hosted GitHub Action Runner on Kubernetes

Showcase image

Introduction

I recently set up a self-hosted GitHub Action Runner on Kubernetes using the Action Runner Controller, aiming for more control over my CI/CD pipeline. This involved configuring Kubernetes with DNS, ingress, and microk8s, and securing my private Docker registry with TLS and a custom Certificate Authority.

Create GitHub app and secrets

On repository click on Developer settings and create and install an app. You should get all details below for creating a secret

kubectl create secret generic controller-manager -n actions \
--from-literal=github_app_id=YOUR_GITHUB_APP_ID \
--from-literal=github_app_installation_id=YOUR_INSTALLATION_ID \
--from-literal=github_app_private_key=YOUR_PRIVATE_KEY

Helm Chart Values

githubWebhookServer:
  enabled: true
  replicaCount: 1
  secret:
    enabled: true
    create: true
    name: "github-webhook-server"
    ### GitHub Webhook Configuration
    github_webhook_secret_token: "GITHUB_WEBHOOK_TOKEN"
    ...
  ingress:
    enabled: true
    ingressClassName: ""
    annotations:
      kubernetes.io/ingress.class: traefik-ingress-class
      kubernetes.io/tls-acme: "true"
    hosts:
      - host: github-runner.yourdomain.com
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: github-runner-cert
        hosts:
          - github-runner.yourdomain.com

When setting up your GitHub Action Runner on Kubernetes, focus on two crucial steps: First, update the ingress section in your YAML with your actual domain name, ensuring GitHub can reach your webhook server. Second, use tls-acme: "true" to integrate cert-manager.

GitHub Webhook

One would input github-runner.yourdomain.com as the Payload URL, corresponding to yours Kubernetes-hosted GitHub Action Runner's address. The webhook settings are found under the repository's "Settings" tab, and choosing "Webhooks" from the sidebar will navigate to this page.

image:
  repository: "summerwind/actions-runner-controller"
  actionsRunnerRepositoryAndTag: "summerwind/actions-runner:latest"
  dindSidecarRepositoryAndTag: "docker:dind"
  pullPolicy: IfNotPresent

certManagerEnabled: true

The certManagerEnabled: true parameter activates Cert-Manager within the Kubernetes cluster, which automates the issuance and renewal of TLS certificates for secure communications. The image section outlines the Docker images used by the self-hosted GitHub Actions runner, including the controller, runner, and Docker-in-Docker sidecar images, to manage and execute CI/CD jobs.

Install Helm Chart

helm repo add actions-runner-controller https://actions-runner-controller.github.io/actions-runner-controller
helm repo update

This will add a helm repo

helm fetch actions-runner-controller/actions-runner-controller --untar --untardir ./mychart
cp ./mychart/actions-runner-controller/values.yaml ./custom-values.yaml

Now you will get helm chart values and you can modify it with values above

helm install actions-runner-controller actions-runner-controller/actions-runner-controller -f custom-values.yaml

Create runner deployment

kubectl create secret generic ca-cert --from-file=ca.crt -n actions

If you use private repository with SSL (I use ingress for SSL) you need to import SSL certificate into secret above.

apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  annotations:
    karpenter.sh/do-not-evict: "true"
  name: self-hosted-runner-deployment
  namespace: actions
spec:
  template:
    spec:
      repository: githubuser/repository
      labels:
        - self-hosted-linux
      resources:
        requests:
          cpu: 1500m
          memory: 2000Mi
      volumes:
      - name: certs
        secret:
          secretName: ca-cert
          items:
          - key: ca.crt
            path: ca.crt
      dockerVolumeMounts:
      - mountPath: /etc/docker/certs.d/yourprivaterepo.intranet
        name: certs
        readOnly: true

Create a file runnerdeployment.yml above with modifying yourprivaterepo.intranet and apply it.

kubectl apply -f runnerdeployment.yml

Scaling deployments

apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
  name: self-hosted-runner-deployment-autoscaler
  namespace: actions
spec:
  maxReplicas: 30
  minReplicas: 0
  scaleTargetRef:
    kind: RunnerDeployment
    name: self-hosted-runner-deployment
  scaleUpTriggers:
  - duration: 30m
    githubEvent:
      workflowJob: {}

This YAML configuration defines a HorizontalRunnerAutoscaler in Kubernetes, which dynamically adjusts the number of self-hosted GitHub Actions runners between 0 and 30. It scales up in response to workflow job events.

Testing with GitHub runners

After installing you should see when run CI/CD pipeline GitHub runners Online.

  • MicroK8S failing with Kustomize
    MicroK8S failing with KustomizeWhen you get an error message error: failed to run '/snap/microk8s/5250/usr/bin/git fetch --depth=1 https://... main': fatal: couldn't find remote ref main this might be a solution
  • cs | en