diff --git a/examples/kubernetes/README.md b/examples/kubernetes/README.md index d00cf1a..8e23524 100644 --- a/examples/kubernetes/README.md +++ b/examples/kubernetes/README.md @@ -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` diff --git a/examples/kubernetes/build.yaml b/examples/kubernetes/build.yaml new file mode 100644 index 0000000..12ffaea --- /dev/null +++ b/examples/kubernetes/build.yaml @@ -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 }} diff --git a/examples/kubernetes/dind-docker.yaml b/examples/kubernetes/dind-docker.yaml index 4f978eb..bd15d13 100644 --- a/examples/kubernetes/dind-docker.yaml +++ b/examples/kubernetes/dind-docker.yaml @@ -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