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.

  • SOPS Git Hooks for Kubernetes Secrets Management
    SOPS Git Hooks for Kubernetes Secrets ManagementI decided to build a Git-based tool to manage Kubernetes secrets more efficiently. The whole idea was to automatically encrypt password and value fields in my Kubernetes YAML and Helm files before committing them into Git, and decrypt them effortlessly when checking out.
  • cs | en