1
0
Fork 0
mirror of https://code.forgejo.org/forgejo/runner.git synced 2025-07-27 17:28:35 +00:00

Improve doc and add full example for Kubernetes (#657)

Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/657
Reviewed-by: earl-warren <earl-warren@noreply.code.forgejo.org>
Co-authored-by: Grégoire Bellon-Gervais <gregoire.bellon-gervais@docaposte.fr>
Co-committed-by: Grégoire Bellon-Gervais <gregoire.bellon-gervais@docaposte.fr>
This commit is contained in:
Grégoire Bellon-Gervais 2025-07-03 15:22:24 +00:00 committed by earl-warren
parent 2fbe500d36
commit d871b38c8d
No known key found for this signature in database
GPG key ID: F128CBE6AB3A7201
3 changed files with 181 additions and 44 deletions

View file

@ -1,7 +1,20 @@
## Kubernetes Docker in Docker Deployment
# Kubernetes Docker in Docker Deployment
Registers Kubernetes pod runners using [offline registration](https://forgejo.org/docs/v1.21/admin/actions/#offline-registration), allowing the scaling of runners as needed.
Registers Kubernetes Pod runners using [offline registration](https://forgejo.org/docs/latest/admin/runner-installation/#offline-registration), allowing the scaling of runners as needed.
NOTE: Docker in Docker (dind) requires elevated privileges on Kubernetes. The current way to achieve this is to set the pod `SecurityContext` to `privileged`. Keep in mind that this is a potential security issue that has the potential for a malicious application to break out of the container context.
[`dind-docker.yaml`](dind-docker.yaml) creates a deployment and secret for Kubernetes to act as a runner. The Docker credentials are re-generated each time the pod connects and does not need to be persisted.
[`dind-docker.yaml`](dind-docker.yaml) creates a Deployment and Secret for Kubernetes to act as a runner. The Docker credentials are re-generated each time the pod connects and does not need to be persisted.
Do not forget to update `FORGEJO_INSTANCE_URL` value.
# Build you first container image
First, you will need to generate an Applications Access token with the permission `write:package` (see [doc](https://forgejo.org/docs/latest/user/token-scope/)), usually from https://your-forgejo.fr/user/settings/applications.
Then, you will create 2 forgejo Actions [Secrets](https://forgejo.org/docs/latest/user/actions/#secrets):
- `USERNAME_WRITE_REPOSITORY` containing Token name
- `PASSWORD_WRITE_REPOSITORY` containing Token value
And you can then, use the [`build.yaml`](build.yaml) file provided as exemple.
This file must be created in your repository under: `.forgejo/workflows/build.yaml`

View file

@ -0,0 +1,48 @@
name: build
on:
push:
paths:
- airflow/Dockerfile # Trigger only if Dockerfile is changed
- airflow/requirements.txt # Trigger only if requirements.txt is changed
jobs:
build:
runs-on: docker
steps:
- name: Checkout the repo
uses: https://data.forgejo.org/actions/checkout@v4
- name: Extract the current airflow version from the Dockerfile and set it in a variable name airflow_version
id: extract_airflow_version
run: echo "::set-output name=airflow_version::$(grep -oP '(?<=FROM apache/airflow:)[0-9]+\.[0-9]+\.[0-9]+' airflow/Dockerfile)"
- name: Add variables
id: add-vars
run: |
echo "::set-output name=registry::${GITHUB_SERVER_URL#*//}" # built-in env variable
echo "::set-output name=repository::${GITHUB_REPOSITORY}" # built-in env variable
echo "::set-output name=app::airflow"
echo "::set-output name=context::airflow" # Dockerfile is in airflow folder, so context is airflow folder not .
echo "::set-output name=dockerfile::airflow/Dockerfile" # Dockerfile path
echo "::set-output name=tag::${{ steps.extract_airflow_version.outputs.airflow_version }}-${{ github.sha}}"
- name: Docker CLI installation
run: |
apt update
apt install -y ca-certificates curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ steps.add-vars.outputs.registry }}
username: ${{ secrets.USERNAME_WRITE_REPOSITORY }}
password: ${{ secrets.PASSWORD_WRITE_REPOSITORY }}
- name: Image build
run: docker build -f ${{ steps.add-vars.outputs.dockerfile }} -t ${{ steps.add-vars.outputs.registry }}/${{ steps.add-vars.outputs.repository }}:${{ steps.add-vars.outputs.app }}-${{ steps.add-vars.outputs.tag }} ${{ steps.add-vars.outputs.context }}
- name: Image push to registry
run: docker push ${{steps.add-vars.outputs.registry }}/${{steps.add-vars.outputs.repository }}:${{steps.add-vars.outputs.app }}-${{ steps.add-vars.outputs.tag }}

View file

@ -3,43 +3,58 @@
# Alternatively, create this with
# kubectl create secret generic runner-secret --from-literal=token=your_offline_token_here
apiVersion: v1
stringData:
token: your_offline_secret_here
kind: Secret
metadata:
name: runner-secret
stringData:
token: your_offline_secret_here # Replace with your Forgejo offline registration token
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: forgejo-runner
labels:
app: forgejo-runner
name: forgejo-runner
app.kubernetes.io/component: forgejo-runner
app.kubernetes.io/instance: forgejo-runner
app.kubernetes.io/managed-by: Kustomize
app.kubernetes.io/name: forgejo-runner
annotations:
argocd.argoproj.io/sync-wave: '1'
spec:
# Two replicas means that if one is busy, the other can pick up jobs.
replicas: 2
selector:
matchLabels:
app: forgejo-runner
strategy: {}
template:
metadata:
creationTimestamp: null
name: forgejo-runner
labels:
app: forgejo-runner
app.kubernetes.io/component: forgejo-runner
app.kubernetes.io/instance: forgejo-runner
app.kubernetes.io/managed-by: Kustomize
app.kubernetes.io/name: forgejo-runner
spec:
automountServiceAccountToken: false
restartPolicy: Always
volumes:
- name: docker-certs
emptyDir: {}
- name: runner-data
emptyDir: {}
# Initialise our configuration file using offline registration
# https://forgejo.org/docs/v1.21/admin/actions/#offline-registration
initContainers:
- name: runner-register
image: code.forgejo.org/forgejo/runner:6.0.1
command: ["forgejo-runner", "register", "--no-interactive", "--token", $(RUNNER_SECRET), "--name", $(RUNNER_NAME), "--instance", $(FORGEJO_INSTANCE_URL)]
image: code.forgejo.org/forgejo/runner:6.4.0
command:
- /bin/bash
- -c
args:
- |
while : ; do
forgejo-runner register --no-interactive --token $(RUNNER_SECRET) --name $(RUNNER_NAME) --instance $(FORGEJO_INSTANCE_URL) && break ;
sleep 1 ;
done ;
forgejo-runner generate-config > /data/config.yml ;
sed -i -e "s|network: .*|network: host|" config.yml ;
sed -i -e "s|^ envs:$$| envs:\n DOCKER_HOST: tcp://localhost:2376\n DOCKER_TLS_VERIFY: 1\n DOCKER_CERT_PATH: /certs/client|" config.yml ;
sed -i -e "s|^ options:| options: -v /certs/client:/certs/client|" config.yml ;
sed -i -e "s| valid_volumes: \[\]$$| valid_volumes:\n - /certs/client|" config.yml
env:
- name: RUNNER_NAME
valueFrom:
@ -51,37 +66,98 @@ spec:
name: runner-secret
key: token
- name: FORGEJO_INSTANCE_URL
value: http://forgejo-http.forgejo.svc.cluster.local:3000
value: https://my-forgejo.fr # Replace with your Forgejo instance URL
resources:
limits:
cpu: "0.50"
memory: "64Mi"
cpu: '0.5'
ephemeral-storage: 100Mi
memory: 64Mi
requests:
cpu: 100m
ephemeral-storage: '0'
memory: 64Mi
volumeMounts:
- name: runner-data
mountPath: /data
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: runner
image: code.forgejo.org/forgejo/runner:6.0.1
command: ["sh", "-c", "while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; forgejo-runner daemon"]
env:
- name: DOCKER_HOST
value: tcp://localhost:2376
- name: DOCKER_CERT_PATH
value: /certs/client
- name: DOCKER_TLS_VERIFY
value: "1"
volumeMounts:
- name: runner
image: code.forgejo.org/forgejo/runner:6.4.0
command:
- /bin/bash
- -c
args:
- |
while ! nc -z localhost 2376 </dev/null ; do
echo 'waiting for docker daemon...' ;
sleep 5 ;
done ;
forgejo-runner --config config.yml daemon
env:
- name: DOCKER_HOST
value: tcp://localhost:2376
- name: DOCKER_CERT_PATH
value: /certs/client
- name: DOCKER_TLS_VERIFY
value: '1'
resources:
limits:
cpu: '1'
ephemeral-storage: 3Gi
memory: 4Gi
requests:
cpu: 100m
ephemeral-storage: '0'
memory: 64Mi
volumeMounts:
- name: docker-certs
mountPath: /certs
- name: runner-data
mountPath: /data
- name: tmp
mountPath: /tmp
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
- name: daemon
image: docker.io/docker:28.3.0-dind
env:
- name: DOCKER_TLS_CERTDIR
value: /certs
resources:
limits:
cpu: '1'
ephemeral-storage: 3Gi
memory: 4Gi
requests:
cpu: 100m
ephemeral-storage: '0'
memory: 64Mi
securityContext:
privileged: true
volumeMounts:
- name: docker-certs
mountPath: /certs
volumes:
- name: docker-certs
mountPath: /certs
emptyDir: {}
- name: runner-data
mountPath: /data
- name: daemon
image: docker:27.4.1-dind
env:
- name: DOCKER_TLS_CERTDIR
value: /certs
securityContext:
privileged: true
volumeMounts:
- name: docker-certs
mountPath: /certs
emptyDir: {}
- name: tmp
emptyDir: {}