├── kustomize ├── kustomization.yaml ├── imagePullPolicyAlways.yaml ├── imagePullPolicyIfNotPresent.yaml └── deployment.yaml ├── test.sh ├── entrypoint.sh ├── Dockerfile ├── kustomization.yaml ├── .github └── workflows │ ├── master.yaml │ ├── dev.yaml │ └── release.yaml ├── LICENSE ├── install.sh └── README.md /kustomize/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - deployment.yaml -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A tiny pre-flight check to confirm that this image is suitable for use 3 | set -ex 4 | 5 | which docker 6 | which git 7 | which ping 8 | whoami 9 | sudo whoami -------------------------------------------------------------------------------- /kustomize/imagePullPolicyAlways.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: actions-runner 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: runner 10 | imagePullPolicy: Always 11 | -------------------------------------------------------------------------------- /kustomize/imagePullPolicyIfNotPresent.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: actions-runner 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: runner 10 | imagePullPolicy: IfNotPresent 11 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Use /work as both PWD and the work directory configured for the runner 5 | cd /work 6 | 7 | # Configure the runner 8 | /opt/actions-runner/config.sh \ 9 | --replace \ 10 | --token "${TOKEN}" \ 11 | --unattended \ 12 | --url "${REPOSITORY}" \ 13 | --work /work 14 | 15 | # Off to the races 16 | cd /opt/actions-runner 17 | /opt/actions-runner/bin/runsvc.sh 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # GitHub's Linux runners default to the latest Ubunutu 2 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources 3 | FROM ubuntu:bionic 4 | 5 | # Add our installation script 6 | COPY install.sh /root/ 7 | 8 | # Install and update the system in one tidy layer 9 | ARG ACTIONS_RUNNER_VERSION="2.169.1" 10 | ENV ACTIONS_RUNNER_VERSION=$ACTIONS_RUNNER_VERSION 11 | RUN /bin/bash /root/install.sh 12 | 13 | # Run as the runner user instead of root 14 | USER runner 15 | WORKDIR /home/runner 16 | COPY *.sh ./ 17 | RUN /bin/bash ./test.sh 18 | ENTRYPOINT ["/bin/bash", "./entrypoint.sh"] 19 | -------------------------------------------------------------------------------- /kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization is expected to be valid on long-lived refs. 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | 5 | bases: 6 | - ./kustomize 7 | 8 | images: 9 | - name: docker.pkg.github.com/urcomputeringpal/actions-runner-kubernetes/actions-runner-kubernetes-dev 10 | newName: docker.pkg.github.com/urcomputeringpal/actions-runner-kubernetes/actions-runner-kubernetes 11 | # newTag: v2.169.1-ucp3 12 | newTag: master 13 | 14 | patchesStrategicMerge: 15 | # Useful for refs that change frequently, like master 16 | - kustomize/imagePullPolicyAlways.yaml 17 | # Useful for refs that are not expected to change, like tags 18 | # - kustomize/imagePullPolicyIfNotPresent.yaml 19 | -------------------------------------------------------------------------------- /.github/workflows/master.yaml: -------------------------------------------------------------------------------- 1 | name: master 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: checkout 11 | uses: actions/checkout@v2.0.0 12 | 13 | # A utility step used to mutate variables with bash 14 | - name: vars 15 | id: vars 16 | env: 17 | REPO: ${{ github.repository }} 18 | run: | 19 | echo "::set-output name=repo-without-owner::$(echo ${REPO} | cut -f 2 -d /)" 20 | 21 | - name: docker 22 | uses: machine-learning-apps/gpr-docker-publish@v1.4 23 | id: docker 24 | with: 25 | DOCKERFILE_PATH: 'Dockerfile' 26 | BUILD_CONTEXT: './' 27 | # Image names are organization/user scoped! 28 | # Resist the urge to create a 'dev' or 'test' image for a specific repo, for example. 29 | IMAGE_NAME: ${{ steps.vars.outputs.repo-without-owner }} 30 | TAG: master 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/dev.yaml: -------------------------------------------------------------------------------- 1 | name: dev 2 | on: 3 | push: 4 | branches-ignore: 5 | - 'master' 6 | tags-ignore: 7 | - '*' 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@v2.0.0 14 | 15 | # A utility step used to mutate variables with bash 16 | - name: vars 17 | id: vars 18 | env: 19 | REPO: ${{ github.repository }} 20 | run: | 21 | echo "::set-output name=repo-without-owner::$(echo ${REPO} | cut -f 2 -d /)" 22 | 23 | - name: docker 24 | uses: machine-learning-apps/gpr-docker-publish@v1.4 25 | id: docker 26 | with: 27 | DOCKERFILE_PATH: 'Dockerfile' 28 | BUILD_CONTEXT: './' 29 | # Image names are organization/user scoped! 30 | # Resist the urge to create a 'dev' or 'test' image for a specific repo, for example. 31 | # Instead, prefix it like so! 32 | IMAGE_NAME: ${{ steps.vars.outputs.repo-without-owner }}-dev 33 | CACHE: true 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jesse Newland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: checkout 11 | uses: actions/checkout@v2.0.0 12 | 13 | # A utility step used to mutate variables with bash 14 | - name: vars 15 | id: vars 16 | env: 17 | REF: ${{ github.ref }} 18 | REPO: ${{ github.repository }} 19 | run: | 20 | echo "::set-output name=short-ref::$(echo ${REF} | cut -f 3 -d /)" 21 | echo "::set-output name=repo-without-owner::$(echo ${REPO} | cut -f 2 -d /)" 22 | 23 | - name: docker 24 | uses: machine-learning-apps/gpr-docker-publish@v1.4 25 | id: docker 26 | with: 27 | DOCKERFILE_PATH: 'Dockerfile' 28 | BUILD_CONTEXT: './' 29 | # Image names are organization/user scoped! 30 | # Resist the urge to create a 'dev' or 'test' image for a specific repo, for example. 31 | IMAGE_NAME: ${{ steps.vars.outputs.repo-without-owner }} 32 | # Tag by the Git tag in this case 33 | TAG: ${{ steps.vars.outputs.short-ref }} 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /kustomize/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: actions-runner 5 | labels: 6 | app: actions-runner 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: actions-runner 12 | template: 13 | metadata: 14 | labels: 15 | app: actions-runner 16 | spec: 17 | imagePullSecrets: 18 | - name: github-package-registry 19 | containers: 20 | - name: runner 21 | image: docker.pkg.github.com/urcomputeringpal/actions-runner-kubernetes/actions-runner-kubernetes-dev:latest 22 | imagePullPolicy: Always 23 | envFrom: 24 | - secretRef: 25 | name: actions-runner 26 | env: 27 | - name: DOCKER_TLS_CERTDIR 28 | value: /certs 29 | - name: DOCKER_HOST 30 | value: tcp://localhost:2376 31 | - name: DOCKER_TLS_VERIFY 32 | value: "1" 33 | - name: DOCKER_CERT_PATH 34 | value: /certs/client 35 | volumeMounts: 36 | - name: docker-certs 37 | mountPath: /certs 38 | - name: work 39 | mountPath: /work 40 | - name: docker 41 | image: docker:stable-dind 42 | env: 43 | - name: DOCKER_TLS_CERTDIR 44 | value: /certs 45 | # TODO investigate rootless 46 | securityContext: 47 | privileged: true 48 | volumeMounts: 49 | - name: docker-storage 50 | mountPath: /var/lib/docker 51 | - name: docker-certs 52 | mountPath: /certs 53 | - name: work 54 | mountPath: /work 55 | volumes: 56 | - name: docker-storage 57 | emptyDir: {} 58 | - name: docker-certs 59 | emptyDir: {} 60 | - name: work 61 | emptyDir: {} -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Fresh 5 | apt-get update 6 | 7 | # Upgrade base 8 | apt-get -y upgrade 9 | 10 | # Install setup dependencies 11 | apt-get install -y \ 12 | apt-transport-https \ 13 | ca-certificates \ 14 | curl \ 15 | gnupg-agent \ 16 | lsb-release \ 17 | software-properties-common \ 18 | ; 19 | 20 | # Install docker repo 21 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - 22 | add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 23 | 24 | # Add Git PPA 25 | add-apt-repository -y ppa:git-core/ppa 26 | 27 | apt-get update 28 | 29 | # Create a runner user 30 | useradd -ms /bin/bash runner 31 | 32 | # Install the runner and its dependencies 33 | mkdir -p /opt/actions-runner && cd /opt/actions-runner 34 | curl -sLO https://github.com/actions/runner/releases/download/v${ACTIONS_RUNNER_VERSION}/actions-runner-linux-x64-${ACTIONS_RUNNER_VERSION}.tar.gz 35 | tar xzf ./actions-runner-linux-x64-${ACTIONS_RUNNER_VERSION}.tar.gz 36 | rm ./actions-runner-linux-x64-${ACTIONS_RUNNER_VERSION}.tar.gz 37 | /opt/actions-runner/bin/installdependencies.sh 38 | 39 | # Begrudgingly allow the runner to modify itself as it writes logs to ./_diag and credentials to ./.* 40 | chown -R runner /opt/actions-runner 41 | 42 | # Install utilities 43 | # TODO grab this from https://help.github.com/en/actions/automating-your-workflow-with-github-actions/software-installed-on-github-hosted-runners somehow? 44 | apt-get install -y \ 45 | docker-ce \ 46 | git \ 47 | inetutils-ping \ 48 | sudo \ 49 | ; 50 | 51 | # Hook up the runner user with passwordless sudo 52 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#administrative-privileges-of-github-hosted-runners 53 | echo "runner ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/runner 54 | chmod 0440 /etc/sudoers.d/runner 55 | 56 | # Cleanup 57 | rm -rf /var/lib/apt/lists/* 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # GitHub Actions self-hosted runners for Kubernetes 3 | 4 | Unofficial support for running [GitHub Actions](https://github.com/features/actions) [self-hosted runners](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/hosting-your-own-runners) (https://github.com/actions/runner) on Kubernetes. 5 | 6 | - [Caveats](#caveats) 7 | - [Installation](#installation) 8 | - [Upgrading](#upgrading) 9 | - [Hacking](#hacking) 10 | - [Running the most recently built image from a local checkout](#running-the-most-recently-built-image-from-a-local-checkout) 11 | 12 | ## Caveats 13 | 14 | * Not associated with, provided by, or supported by GitHub. 15 | * Runs a Docker-in-Docker container in your cluster **in priviledged mode** to facilitate running Docker-based Actions. **Priviledge escalatations are almost certainly possible as a result**. 16 | * Credentials are not persisted in any manner outside of the container filesystem. This, combined with the fact that the token provided during the setup process has a 1h TTL, means that **service will be interrupted if runner Pods are deleted or evicted**. 17 | * A limited set of development utilities are provided. Work to keep the set of installed utilities in sync with upstream is TBD. 18 | * **[Not reccommended for use on open source repositories](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#self-hosted-runner-security-with-public-repositories).** 19 | 20 | ## Installation 21 | 22 | 23 | ### Configure your cluster with permisions to pull Docker images from GitHub Packages 24 | 25 | * [Create a GitHub Personal Access Token](https://github.com/settings/tokens/new) with `repo` and `packages:read` scope. 26 | * Copy the token provided. 27 | * Create a secret named `github-package-registry` with this token in the Kubernetes namespace in which you wish to install the runner: 28 | 29 | ``` 30 | kubectl --namespace create secret docker-registry github-package-registry \ 31 | --docker-server=docker.pkg.github.com --docker-username= --docker-password= 32 | ``` 33 | 34 | 35 | ### Create a secret with a fresh runner token 36 | 37 | * Visit the Repository **Settings** page for your repository. 38 | * Click **Actions** in the sidebar. 39 | * In the **Self-hosted runners** section, click **Add runner**. 40 | * Copy the token provided. 41 | * Create a secret named `actions-runner` with this token in the Kubernetes namespace in which you wish to install the runner: 42 | 43 | ``` 44 | kubectl --namespace create secret generic actions-runner \ 45 | --from-literal=REPOSITORY= \ 46 | --from-literal=TOKEN= 47 | ``` 48 | 49 | * Install the actions runner in your namespace: 50 | 51 | ``` 52 | kubectl --namespace apply -k https://github.com/urcomputeringpal/actions-runner-kubernetes/releases/ 53 | ``` 54 | 55 | ## Upgrading 56 | 57 | Upgrading currently requires updating the token used to register your runners. 58 | 59 | * Delete the existing secret: 60 | 61 | ``` 62 | kubectl -n kube-system delete secret actions-runner 63 | ``` 64 | 65 | * Create a [new secret](#create-a-secret-with-a-fresh-runner-token) 66 | * Upgrade to the [latest release](https://github.com/urcomputeringpal/actions-runner-kubernetes/releases) 67 | 68 | ``` 69 | kubectl --namespace apply -k https://github.com/urcomputeringpal/actions-runner-kubernetes?ref=v2.169.1-ucp3 70 | ``` 71 | 72 | * Cleanup any stale runners listed in your repository's Settings. 73 | 74 | ## Hacking 75 | 76 | ### Running the most recently built image from a local checkout 77 | 78 | ``` 79 | kubectl -n apply -k kustomize 80 | ``` 81 | --------------------------------------------------------------------------------