diff --git a/docs/_images/user/actions/actions-cleanup.png b/docs/_images/user/actions/actions-cleanup.png deleted file mode 100644 index 0a344b3d..00000000 Binary files a/docs/_images/user/actions/actions-cleanup.png and /dev/null differ diff --git a/docs/_images/user/actions/runners-add.png b/docs/_images/user/actions/runners-add.png index 3cbb841d..c289a0ba 100644 Binary files a/docs/_images/user/actions/runners-add.png and b/docs/_images/user/actions/runners-add.png differ diff --git a/docs/admin/actions.md b/docs/admin/actions.md index da78cb3d..a24e340a 100644 --- a/docs/admin/actions.md +++ b/docs/admin/actions.md @@ -71,10 +71,8 @@ the `storage.artifacts` section of `app.ini` as [explained in in the storage documentation](../storage/). The `admin/monitor/cron` administration web interface can be used to -manually trigger the expiration of artifacts instead of waiting for -the scheduled task to happen. - -![cleanup logs and artifacts](../_images/user/actions/actions-cleanup.png) +manually trigger the `Cleanup actions expired logs and artifacts` task +instead of waiting for the scheduled task to happen. ## Forgejo runner @@ -82,548 +80,33 @@ The `Forgejo runner` is a daemon that fetches workflows to run from a Forgejo instance, executes them, sends back with the logs and ultimately reports its success or failure. -### Installation +Installation and setup instructions can be found in the [Forgejo Runner installation guide](../runner-installation/). -Each `Forgejo runner` release is published for all supported architectures as: +## Choosing labels -- [binaries](https://code.forgejo.org/forgejo/runner/releases) -- [OCI images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions) - -#### Installation of the binary - -Download the latest [binary release](https://code.forgejo.org/forgejo/runner/releases) and verify its signature: - -```shell -$ wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v3.4.1/forgejo-runner-3.4.1-linux-amd64 -$ chmod +x forgejo-runner -$ wget -O forgejo-runner.asc https://code.forgejo.org/forgejo/runner/releases/download/v3.4.1/forgejo-runner-3.4.1-linux-amd64.asc -$ gpg --keyserver keys.openpgp.org --recv EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 -$ gpg --verify forgejo-runner.asc forgejo-runner -Good signature from "Forgejo " - aka "Forgejo Releases " -``` - -#### Installation of the OCI image - -The [OCI -images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions) -are built from the Dockerfile which is [found in the source -directory](https://code.forgejo.org/forgejo/runner/src/branch/main/Dockerfile). It contains the `forgejo-runner` binary. - -```shell -$ docker run --rm code.forgejo.org/forgejo/runner:3.4.1 forgejo-runner --version -forgejo-runner version v3.4.1 -``` - -It does not run as root: - -```shell -$ docker run --rm code.forgejo.org/forgejo/runner:3.4.1 id -uid=1000 gid=1000 groups=1000 -``` - -One way to run the Docker image is via Docker Compose. To do so, -first prepare a `data` directory with non-root permissions -(in this case, we pick `1001:1001`): - -```shell -#!/usr/bin/env bash - -set -e - -mkdir -p data -touch data/.runner -mkdir -p data/.cache - -chown -R 1001:1001 data/.runner -chown -R 1001:1001 data/.cache -chmod 775 data/.runner -chmod 775 data/.cache -chmod g+s data/.runner -chmod g+s data/.cache -``` - -After running this script with `bash setup.sh`, define the following -`docker-compose.yml`: - -```yaml -version: '3.8' - -services: - docker-in-docker: - image: docker:dind - container_name: 'docker_dind' - privileged: 'true' - command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false'] - restart: 'unless-stopped' - - gitea: - image: 'code.forgejo.org/forgejo/runner:3.4.1' - links: - - docker-in-docker - depends_on: - docker-in-docker: - condition: service_started - container_name: 'runner' - environment: - DOCKER_HOST: tcp://docker-in-docker:2375 - # User without root privileges, but with access to `./data`. - user: 1001:1001 - volumes: - - ./data:/data - restart: 'unless-stopped' - - command: '/bin/sh -c "while : ; do sleep 1 ; done ;"' -``` - -Here, we're not running the `forgejo-runner daemon` yet because we -need to register it first. Please note that in a recent install of -docker `docker-compose` is not a separate command but should be run as -`docker compose`. -Follow the registration instructions below -by starting the `runner` service with `docker-compose up -d` and -entering it via: - -```shell -docker exec -it runner /bin/sh -``` - -In this shell, run the `forgejo-runner register` command as described -below. After that is done, take the service down again with -`docker-compose down` and modify the `command` to: - -```yaml -command: '/bin/sh -c "sleep 5; forgejo-runner daemon"' -``` - -Here, the sleep allows the `docker-in-docker` service to start up -before the `forgejo-runner daemon` is started. - -More [docker compose](https://docs.docker.com/compose/) examples [are -provided](https://codeberg.org/forgejo/runner/src/branch/main/examples/docker-compose) -to demonstrate how to install that OCI image to successfully run a workflow. - -### Execution of the workflows - -The `Forgejo runner` relies on application containers (Docker, Podman, -etc) or system containers (LXC) to execute a workflow in an isolated -environment. They need to be installed and configured independently. - -- **Docker:** - See [the Docker installation](https://docs.docker.com/engine/install/) documentation for more information. - -- **Podman:** - While Podman is generally compatible with Docker, - it does not create a socket for managing containers by default - (because it doesn't usually need one). - - If the Forgejo runner complains about "daemon Docker Engine socket not found", or "cannot ping the docker daemon", - you can use Podman to provide a Docker compatible socket from an unprivileged user - and pass that socket on to the runner, - e.g. by executing: - - ```shell - $ podman system service -t 0 & - $ DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/podman/podman.sock ./forgejo-runner daemon - ``` - -- **LXC:** - For jobs to run in LXC containers, the `Forgejo runner` needs passwordless sudo access for all `lxc-*` commands on a Debian GNU/Linux `bookworm` system where [LXC](https://linuxcontainers.org/lxc/) is installed. The [LXC helpers](https://code.forgejo.org/forgejo/lxc-helpers/) can be used as follows to create a suitable container: - - ```shell - $ git clone https://code.forgejo.org/forgejo/lxc-helpers - $ sudo cp -a lxc-helpers/lxc-helpers{,-lib}.sh /usr/local/bin - $ lxc-helpers.sh lxc_container_create myrunner - $ lxc-helpers.sh lxc_container_start myrunner - $ lxc-helpers.sh lxc_container_user_install myrunner 1000 debian - ``` - - > **NOTE:** Multiarch [Go](https://go.dev/) builds and [binfmt](https://github.com/tonistiigi/binfmt) need `bookworm` to produce and test binaries on a single machine for people who do not have access to dedicated hardware. If this is not needed, installing the `Forgejo runner` on `bullseye` will also work. - - The `Forgejo runner` can then be installed and run within the `myrunner` container. - - ```shell - $ lxc-helpers.sh lxc_container_run forgejo-runners -- sudo --user debian bash - $ sudo apt-get install docker.io wget gnupg2 - $ wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v3.4.1/forgejo-runner-amd64 - ... - ``` - - > **Warning:** LXC containers do not provide a level of security that makes them safe for potentially malicious users to run jobs. They provide an excellent isolation for jobs that may accidentally damage the system they run on. - -- **self-hosted:** - There is no requirement for jobs that run directly on the host. - - > **Warning:** there is no isolation at all and a single job can permanently destroy the host. - -### Registration - -The `Forgejo runner` needs to connect to a `Forgejo` instance and must be registered before doing so. It will give it permission to read the repositories and send back information to `Forgejo` such as the logs or its status. - -- Online registration - - A special kind of token is needed and can be obtained from the `Create new runner` button: - - - in `/admin/actions/runners` to accept workflows from all repositories. - - in `/org/{org}/settings/actions/runners` to accept workflows from all repositories within the organization. - - in `/user/settings/actions/runners` to accept workflows from all repositories of the logged in user - - in `/{owner}/{repository}/settings/actions/runners` to accept workflows from a single repository. - - ![add a runner](../_images/user/actions/runners-add.png) - - For instance, using a token obtained for a test repository from `next.forgejo.org`: - - ```shell - forgejo-runner register --no-interactive --token {TOKEN} --name runner --instance https://next.forgejo.org - INFO Registering runner, arch=amd64, os=linux, version=3.4.1. - INFO Runner registered successfully. - ``` - - It will create a `.runner` file that looks like: - - ```json - { - "WARNING": "This file is automatically generated. Do not edit.", - "id": 6, - "uuid": "fcd0095a-291c-420c-9de7-965e2ebaa3e8", - "name": "runner", - "address": "https://next.forgejo.org" - } - ``` - - The same token can be used multiple times to register any number of - runners, independent of each other. - - When using the `forgejo-runner register` command, it will ask for a - label too. To get a runner that is close to GitHub's runners, use - - ``` - ubuntu-22.04:docker://node:20-bullseye - ``` - - or - - ``` - ubuntu-22.04:docker://ghcr.io/catthehacker/ubuntu:act-22.04 - ``` - - as the label. The `act` container image is much bigger than the - `node` image, but also more similar to the GitHub runners. See - the [labels and `runs-on`](#labels-and-runs-on) section for - more information. - -- Offline registration - - When Infrastructure as Code (Ansible, kubernetes, etc.) is used to - deploy and configure both Forgejo and the Forgejo runner, it may be - more convenient for it to generate a secret and share it with both. - - The `forgejo forgejo-cli actions register --secret ` subcommand can be - used to register the runner with the Forgejo instance and the - `forgejo-runner create-runner-file --secret ` subcommand can - be used to configure the Forgejo runner with the credentials that will - allow it to start picking up tasks from the Forgejo instances as soon - as it comes online. - - For instance, on the machine running Forgejo: - - ```sh - $ forgejo forgejo-cli actions register --name runner-name --scope myorganization \ - --secret 7c31591e8b67225a116d4a4519ea8e507e08f71f - ``` - - and on the machine on which the Forgejo runner is installed: - - ```sh - $ forgejo-runner create-runner-file --instance https://example.conf \ - --secret 7c31591e8b67225a116d4a4519ea8e507e08f71f - ``` - - The secret must be a 40-character long string of hexadecimal numbers. - The first 16 characters will be used as an identifier for the runner, while - the rest is the actual secret. It is possible to update the secret of an - existing runner by running the command again on the Forgejo machine, with - the last 24 characters updated. - - For instance, the command below would change the secret set by the previous - command: - - ```sh - $ forgejo forgejo-cli actions register --name runner-name --scope myorganization \ - --secret 7c31591e8b67225a84e8e06633b9578e793664c3 - # ^^^^^^^^^^^^^^^^ This part is identical - ``` - - The registration command on the Forgejo side is mostly idempotent, with the - exception of the runner labels. If the command is run without `--labels`, they - will be reset, and the runner won't set them back until it is restarted. The - `--keep-labels` option can be used to preserve the existing labels. - -### Configuration - -The default configuration for the runner can be -displayed with `forgejo-runner generate-config`, stored in a -`config.yml` file, modified and used instead of the default with the -`--config` flag. - -```yaml -$ forgejo-runner generate-config > config.yml -# Example configuration file, it's safe to copy this as the default config file without any modification. - -log: - # The level of logging, can be trace, debug, info, warn, error, fatal - level: info - -runner: - # Where to store the registration result. - file: .runner - # Execute how many tasks concurrently at the same time. - capacity: 1 - # Extra environment variables to run jobs. - envs: - A_TEST_ENV_NAME_1: a_test_env_value_1 - A_TEST_ENV_NAME_2: a_test_env_value_2 - # Extra environment variables to run jobs from a file. - # It will be ignored if it's empty or the file doesn't exist. - env_file: .env - # The timeout for a job to be finished. - # Please note that the Forgejo instance also has a timeout (3h by default) for the job. - # So the job could be stopped by the Forgejo instance if it's timeout is shorter than this. - timeout: 3h - # Whether skip verifying the TLS certificate of the Forgejo instance. - insecure: false - # The timeout for fetching the job from the Forgejo instance. - fetch_timeout: 5s - # The interval for fetching the job from the Forgejo instance. - fetch_interval: 2s - # The labels of a runner are used to determine which jobs the runner can run, and how to run them. - # Like: ["macos-arm64:host", "ubuntu-latest:docker://node:16-bullseye", "ubuntu-22.04:docker://node:16-bullseye"] - # If it's empty when registering, it will ask for inputting labels. - # If it's empty when execute `daemon`, will use labels in `.runner` file. - labels: [] - -cache: - # Enable cache server to use actions/cache. - enabled: true - # The directory to store the cache data. - # If it's empty, the cache data will be stored in $HOME/.cache/actcache. - dir: "" - # The host of the cache server. - # It's not for the address to listen, but the address to connect from job containers. - # So 0.0.0.0 is a bad choice, leave it empty to detect automatically. - host: "" - # The port of the cache server. - # 0 means to use a random available port. - port: 0 - -container: - # Specifies the network to which the container will connect. - # Could be host, bridge or the name of a custom network. - # If it's empty, create a network automatically. - network: "" - # Whether to create networks with IPv6 enabled. Requires the Docker daemon to be set up accordingly. - # Only takes effect if "network" is set to "". - enable_ipv6: false - # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker). - privileged: false - # And other options to be used when the container is started (eg, --add-host=my.forgejo.url:host-gateway). - options: - # The parent directory of a job's working directory. - # If it's empty, /workspace will be used. - workdir_parent: - # Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob - # You can specify multiple volumes. If the sequence is empty, no volumes can be mounted. - # For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to: - # valid_volumes: - # - data - # - /src/*.json - # If you want to allow any volume, please use the following configuration: - # valid_volumes: - # - '**' - valid_volumes: [] - # overrides the docker client host with the specified one. - # If it's empty, act_runner will find an available docker host automatically. - # If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers. - # If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work. - docker_host: "" - -host: - # The parent directory of a job's working directory. - # If it's empty, $HOME/.cache/act/ will be used. - workdir_parent: -``` - -### Cache configuration - -Some actions such as https://code.forgejo.org/actions/cache or -https://code.forgejo.org/actions/setup-go can communicate with the -`Forgejo runner` to save and restore commonly used files such as -compilation dependencies. They are stored as compressed tar archives, -fetched when a job starts and saved when it completes. - -If the machine has a fast disk, uploading the cache when the job -starts may significantly reduce the bandwidth required to download -and rebuild dependencies. - -If the machine on which the `Forgejo runner` is running has a slow -disk and plenty of CPU and bandwidth, it may be better to not activate -the cache as it can slow down the execution time. - -### Running the daemon - -Once the `Forgejo runner` is successfully registered, it can be run from the directory in which the `.runner` file is found with: - -```shell -$ forgejo-runner daemon -INFO[0000] Starting runner daemon -``` - -To verify it is actually available for the targeted repository, go to `/{owner}/{repository}/settings/actions/runners`. It will show the runners: - -- dedicated to the repository with the **repo** type -- available to all repositories within an organization or a user -- available to all repositories, with the **Global** type - -![list the runners](../_images/user/actions/list-of-runners.png) - -Adding the `.forgejo/workflows/demo.yaml` file to the test repository: - -```yaml -on: [push] -jobs: - test: - runs-on: docker - steps: - - run: echo All Good -``` - -Will send a job request to the `Forgejo runner` that will display logs such as: - -```shell -... -INFO[2023-05-28T18:54:53+02:00] task 29 repo is earl-warren/test https://code.forgejo.org https://next.forgejo.org -... -[/test] [DEBUG] Working directory '/workspace/earl-warren/test' -| All Good -[/test] ✅ Success - Main echo All Good -``` - -It will also show a similar output in the `Actions` tab of the repository. - -If no `Forgejo runner` is available, `Forgejo` will wait for one to connect and submit the job as soon as it is available. - -### Enable IPv6 in Docker & Podman Networks - -When a `Forgejo runner` creates its own Docker or Podman networks, IPv6 is not enabled by default, and must be enabled explicitly in the `Forgejo runner` configuration. - -**Docker only**: The Docker daemon requires additional configuration to enable IPv6. To make use of IPv6 with Docker, you need to provide an `/etc/docker/daemon.json` configuration file with at least the following keys: - -```json -{ - "ipv6": true, - "experimental": true, - "ip6tables": true, - "fixed-cidr-v6": "fd00:d0ca:1::/64", - "default-address-pools": [ - { "base": "172.17.0.0/16", "size": 24 }, - { "base": "fd00:d0ca:2::/104", "size": 112 } - ] -} -``` - -Afterwards restart the Docker daemon with `systemctl restart docker.service`. - -> **NOTE**: These are example values. While this setup should work out of the box, it may not meet your requirements. Please refer to the Docker documentation regarding [enabling IPv6](https://docs.docker.com/config/daemon/ipv6/#use-ipv6-for-the-default-bridge-network) and [allocating IPv6 addresses to subnets dynamically](https://docs.docker.com/config/daemon/ipv6/#dynamic-ipv6-subnet-allocation). - -**Docker & Podman**: -To test IPv6 connectivity in `Forgejo runner`-created networks, create a small workflow such as the following: - -```yaml ---- -on: push -jobs: - ipv6: - runs-on: docker - steps: - - run: | - apt update; apt install --yes curl - curl -s -o /dev/null http://ipv6.google.com -``` - -If you run this action with `forgejo-runner exec`, you should expect this job fail: - -```shellsession -$ forgejo-runner exec -... -| curl: (7) Couldn't connect to server -[ipv6.yml/ipv6] ❌ Failure - apt update; apt install --yes curl -curl -s -o /dev/null http://ipv6.google.com -[ipv6.yml/ipv6] exitcode '7': failure -[ipv6.yml/ipv6] Cleaning up services for job ipv6 -[ipv6.yml/ipv6] Cleaning up container for job ipv6 -[ipv6.yml/ipv6] Cleaning up network for job ipv6, and network name is: FORGEJO-ACTIONS-TASK-push_WORKFLOW-ipv6-yml_JOB-ipv6-network -[ipv6.yml/ipv6] 🏁 Job failed -``` - -To actually enable IPv6 with `forgejo-runner exec`, the flag `--enable-ipv6` must be provided. If you run this again with `forgejo-runner exec --enable-ipv6`, the job should succeed: - -```shell-session -$ forgejo-runner exec --enable-ipv6 -... -[ipv6.yml/ipv6] ✅ Success - Main apt update; apt install --yes curl -curl -s -o /dev/null http://ipv6.google.com -[ipv6.yml/ipv6] Cleaning up services for job ipv6 -[ipv6.yml/ipv6] Cleaning up container for job ipv6 -[ipv6.yml/ipv6] Cleaning up network for job ipv6, and network name is: FORGEJO-ACTIONS-TASK-push_WORKFLOW-ipv6-yml_JOB-ipv6-network -[ipv6.yml/ipv6] 🏁 Job succeeded -``` - -Finally, if this test was successful, enable IPv6 in the `config.yml` file of the `Forgejo runner` daemon and restart the daemon: - -```yaml -container: - enable_ipv6: true -``` - -Now, `Forgejo runner` will create networks with IPv6 enabled, and workflow containers will be assigned addresses from the pools defined in the Docker daemon configuration. - -## Labels and `runs-on` - -The workflows / tasks defined in the files found in `.forgejo/workflows` must specify the environment they need to run with `runs-on`. Each `Forgejo runner` declares, when they connect to the `Forgejo` instance the list of labels they support so `Forgejo` sends them tasks accordingly. For instance if a job within a workflow has: +Runner labels are used by workflows to define what type of environment they need to be executed in. Each runner declares a set of labels, and the `Forgejo` server will send it tasks accordingly. For example, a workflow with: ```yaml runs-on: docker ``` -it will be submitted to a runner that declared supporting this label. +will be run on a runner which has declared a `docker` label. -When the `Forgejo runner` starts, it reads the list of labels from the -configuration file specified with `--config`. For instance: +A label has the following structure: -```yaml -runner: - labels: - - 'docker:docker://node:20-bookworm' - - 'node20:docker://node:20-bookworm' - - 'lxc:lxc://debian:bullseye' - - 'bullseye:lxc://debian:bullseye' - - 'self-hosted:host://-self-hosted' +``` +::// ``` -will have the `Forgejo runner` declare that it supports the `node20` and `bullseye` labels. +The `label name` is a unique string that identifies the label. It is the part that is specified in the `runs-on` field of workflows to choose which runners the workflow can be excecuted on. -If the list of labels is empty, it defaults to `docker:docker://node:16-bullseye` and will declare the label `docker`. -Declaring a label means that the `Forgejo runner` will accept tasks that specify this label in the `runs-on` field. - -So, to mimic the GitHub runners, the `runs-on` field can be set to `ubuntu-22.04:docker://node:20-bullseye` for instance. -With this, the Forgejo runner will respond to `runs-on: ubuntu-22.04` and will use the `node:20-bullseye` image from hub.docker.com. -This image is quite capable of running many of the workflows that are designed for the GitHub runners. -For a slightly bigger image, use `ghcr.io/catthehacker/ubuntu:act-22.04` instead of `node:20-bullseye` which should be compatible with most actions while remaining relatively small. -There exist larger images used that can go up to 20GB compressed with more software installed if needed. +The `label type` determines what containerization system will be used to run the workflow. There are three options: ### Docker or Podman -If `runs-on` is matched to a label mapped to `docker://`, the rest of it is interpreted as the default container image to use if no other is specified. The runner will execute all the steps, as root, within a container created from that image. The default container image can be overridden by a workflow to use `alpine:3.18` as follows. +If a label specifies `docker` as its `label type`, the rest of it is interpreted as the default container image to use if no other is specified. The runner will execute all the steps, as root, within a container created from that image. + +The default container container image can be overridden by a workflow: ```yaml runs-on: docker @@ -633,18 +116,18 @@ container: See the user documentation for `jobs..container` for more information. -Labels examples: +Label examples: - `node20:docker://node:20-bookworm` == `node20:docker://docker.io/node:20-bookworm` defines `node20` to be the `node:20-bookworm` image from hub.docker.com - `docker:docker://code.forgejo.org/oci/alpine:3.18` defines `docker` to be the `alpine:3.18` image from https://code.forgejo.org/oci/-/packages/container/alpine/3.18 ### LXC -If `runs-on` is matched to a label mapped to `lxc://`, the rest of it is interpreted as the default [template and release](https://images.linuxcontainers.org/) to use if no other is specified. The runner will execute all the steps, as root, within a [LXC container](https://linuxcontainers.org/) created from that template and release. The default template is `debian` and the default release is `bullseye`. +If a label specifies `lxc` as its `label type`, the rest of it is interpreted as the default [template and release](https://images.linuxcontainers.org/) to use if no other is specified. The runner will execute all the steps, as root, within a [LXC container](https://linuxcontainers.org/) created from that template and release. The default template is `debian` and the default release is `bullseye`. [nodejs](https://nodejs.org/en/download/) version 20 is installed. -They can be overridden by a workflow to use `debian` and `bookworm` as follows. +The default template and release can be overridden by a workflow: ```yaml runs-on: lxc @@ -654,42 +137,31 @@ container: See the user documentation for `jobs..container` for more information. -Labels examples: +Label example: -- `bookworm:lxc://debian:bookworm` defines bookworm to be an LXC container running Debian GNU/Linux bookworm. +- `bookworm:lxc://debian:bookworm` defines `bookworm` to be an LXC container running Debian GNU/Linux bookworm. -### shell +### Host -If `runs-on` is matched to a label mapped to `host://-self-hosted``, the runner will execute all the steps in a shell forked from the runner, directly on the host. +If a label specifies `host` as its `label type`, the runner will execute all the steps in a shell forked from the runner, directly on the host. + +> **Warning:** There is no isolation at all and a single job can permanently destroy the host. Label example: - `self-hosted:host://-self-hosted` defines `self-hosted` to be a shell -## Packaging +### Special labels -### NixOS +Runner labels can also be used to define other special features a runner has. For example, you could use `gpu:docker://node:20-bullseye` to define a runner that has a GPU installed. Workflows which need a GPU could then specify `runs-on: gpu` to be excecuted on this runner. -The [`forgejo-actions-runner`](https://github.com/NixOS/nixpkgs/blob/ac6977498b1246f21af08f3cf25ea7b602d94b99/pkgs/development/tools/continuous-integration/forgejo-actions-runner/default.nix) recipe is released in NixOS. +### Mimicking GitHub runners -Please note that the `services.forgejo-actions-runner.instances..labels` key may be set to `[]` (an empty list) to use the packaged Forgejo instance list. One of `virtualisation.docker.enable` or `virtualisation.podman.enable` will need to be set. The default Forgejo image list is populated with docker images. - -IPv6 support is not enabled by default for docker. The following snippet enables this. - -```nix -virtualisation.docker = { - daemon.settings = { - fixed-cidr-v6 = "fd00::/80"; - ipv6 = true; - }; -}; -``` - -If you would like to use docker runners in combination with [cache actions](#cache-configuration), be sure to add docker bridge interfaces "br-\*" to the firewalls' trusted interfaces: - -```nix -networking.firewall.trustedInterfaces = [ "br-+" ]; -``` +To mimic the GitHub runners, the `runs-on` field can be set to `ubuntu-22.04:docker://node:20-bullseye` for instance. +With this, the Forgejo runner will respond to `runs-on: ubuntu-22.04` and will use the `node:20-bullseye` image from hub.docker.com. +This image is quite capable of running many of the workflows that are designed for the GitHub runners. +For a slightly bigger image, use `ghcr.io/catthehacker/ubuntu:act-22.04` instead of `node:20-bullseye` which should be compatible with most actions while remaining relatively small. +There exist larger images used that can go up to 20GB compressed with more software installed if needed. ## Other runners diff --git a/docs/admin/index.md b/docs/admin/index.md index 916e8560..799bbca5 100644 --- a/docs/admin/index.md +++ b/docs/admin/index.md @@ -18,6 +18,7 @@ These documents are targeted to people who run Forgejo on their machines. - [Incoming Email](./incoming-email/) - [Logging Configuration](./logging-documentation/) - [Actions](./actions/) +- [Forgejo Runner installation guide](./runner-installation/) - [Forgejo CLI](./command-line/) - [Search Engines and robots.txt](./search-engines-indexation/) - [Recommended Settings and Tips](./recommendations/) diff --git a/docs/admin/runner-installation.md b/docs/admin/runner-installation.md new file mode 100644 index 00000000..4c8a91c3 --- /dev/null +++ b/docs/admin/runner-installation.md @@ -0,0 +1,520 @@ +--- +title: 'Forgejo Runner installation guide' +license: 'CC-BY-SA-4.0' +--- + +The `Forgejo Runner` is a daemon that fetches workflows to run from a Forgejo instance, executes them, sends back with the logs and ultimately reports its success or failure. + +It needs to be installed separately from the main Forgejo instance. For security reasons it is **not recommended** to install the runner on the same machine as the main instance. + +Each `Forgejo Runner` release is published for all supported architectures as: + +- [binaries](https://code.forgejo.org/forgejo/runner/releases) +- [OCI images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions) + +## Binary installation + +### Downloading and installing the binary + +Download the latest [binary release](https://code.forgejo.org/forgejo/runner/releases) and verify its signature: + +```shell +$ wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/vx.y.z/forgejo-runner-x.y.z-linux-amd64 +$ chmod +x forgejo-runner +$ wget -O forgejo-runner.asc https://code.forgejo.org/forgejo/runner/releases/download/vx.y.z/forgejo-runner-x.y.z-linux-amd64.asc +$ gpg --keyserver keys.openpgp.org --recv EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 +$ gpg --verify forgejo-runner.asc forgejo-runner +Good signature from "Forgejo " + aka "Forgejo Releases " +``` + +Next, copy the downloaded binary to `/usr/local/bin` and make it executable: + +```shell +$ cp forgejo-runner /usr/local/bin/forgejo-runner +$ chmod +x /usr/local/bin/forgejo-runner +``` + +You should now be able to test the runner by running `forgejo-runner -v`: + +``` +$ forgejo-runner -v +forgejo-runner version v3.5.1 +``` + +### Setting up the runner user + +Set up the user to run the daemon: + +```shell +$ useradd --create-home runner +``` + +If the runner will be using Docker or Podman, ensure the `runner` user had access to the docker/podman socket. +If you are using Docker, run: + +```shell +$ usermod -aG docker runner +``` + +### Setting up the container environment + +The `Forgejo runner` relies on application containers (Docker, Podman, etc.) or system containers (LXC) to execute a workflow in an isolated environment. They need to be installed and configured independently. + +- **Docker:** + See the [Docker installation](https://docs.docker.com/engine/install/) documentation for more information. + +- **Podman:** + While Podman is generally compatible with Docker, it does not create a socket for managing containers by default (because it doesn't usually need one). + + If the Forgejo runner complains about "daemon Docker Engine socket not found", or "cannot ping the docker daemon", you can use Podman to provide a Docker compatible socket from an unprivileged user and pass that socket on to the runner by executing: + + ```shell + $ podman system service -t 0 & + $ DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/podman/podman.sock ./forgejo-runner daemon + ``` + +- **LXC:** + For jobs to run in LXC containers, the `Forgejo runner` needs passwordless sudo access for all `lxc-*` commands on a Debian GNU/Linux `bookworm` system where [LXC](https://linuxcontainers.org/lxc/) is installed. The [LXC helpers](https://code.forgejo.org/forgejo/lxc-helpers/) can be used as follows to create a suitable container: + + ```shell + $ git clone https://code.forgejo.org/forgejo/lxc-helpers + $ sudo cp -a lxc-helpers/lxc-helpers{,-lib}.sh /usr/local/bin + $ lxc-helpers.sh lxc_container_create myrunner + $ lxc-helpers.sh lxc_container_start myrunner + $ lxc-helpers.sh lxc_container_user_install myrunner 1000 debian + ``` + + > **NOTE:** Multiarch [Go](https://go.dev/) builds and [binfmt](https://github.com/tonistiigi/binfmt) need `bookworm` to produce and test binaries on a single machine for people who do not have access to dedicated hardware. If this is not needed, installing the `Forgejo runner` on `bullseye` will also work. + + The `Forgejo runner` can then be installed and run within the `myrunner` container. + + ```shell + $ lxc-helpers.sh lxc_container_run forgejo-runners -- sudo --user debian bash + $ sudo apt-get install docker.io wget gnupg2 + $ wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v3.4.1/forgejo-runner-amd64 + ... + ``` + + > **Warning:** LXC containers do not provide a level of security that makes them safe for potentially malicious users to run jobs. They provide an excellent isolation for jobs that may accidentally damage the system they run on. + +- **Host:** + There is no requirement for jobs that run directly on the host. + + > **Warning:** there is no isolation at all and a single job can permanently destroy the host. + +### Registering the runner + +To receive tasks from the Forgejo instance, the runner needs to be registered. + +To register the runner, switch user to the `runner` user account, and return to the home directory: + +```shell +$ sudo su runner +$ whoami +runner +$ cd ~ +$ pwd +/home/runner +``` + +From here, follow the [registration instructions](#standard-registration). + +### Configuration + +The default configuration for the runner can be +displayed with `forgejo-runner generate-config`, stored in a +`config.yml` file, modified and used instead of the default with the +`--config` flag. + +```yaml +$ forgejo-runner generate-config > config.yml +# Example configuration file, it's safe to copy this as the default config file without any modification. + +log: + # The level of logging, can be trace, debug, info, warn, error, fatal + level: info + +runner: + # Where to store the registration result. + file: .runner + # Execute how many tasks concurrently at the same time. + capacity: 1 + # Extra environment variables to run jobs. + envs: + A_TEST_ENV_NAME_1: a_test_env_value_1 + A_TEST_ENV_NAME_2: a_test_env_value_2 + # Extra environment variables to run jobs from a file. + # It will be ignored if it's empty or the file doesn't exist. + env_file: .env + # The timeout for a job to be finished. + # Please note that the Forgejo instance also has a timeout (3h by default) for the job. + # So the job could be stopped by the Forgejo instance if it's timeout is shorter than this. + timeout: 3h + # Whether skip verifying the TLS certificate of the Forgejo instance. + insecure: false + # The timeout for fetching the job from the Forgejo instance. + fetch_timeout: 5s + # The interval for fetching the job from the Forgejo instance. + fetch_interval: 2s + # The labels of a runner are used to determine which jobs the runner can run, and how to run them. + # Like: ["macos-arm64:host", "ubuntu-latest:docker://node:16-bullseye", "ubuntu-22.04:docker://node:16-bullseye"] + # If it's empty when registering, it will ask for inputting labels. + # If it's empty when execute `daemon`, will use labels in `.runner` file. + labels: [] + +cache: + # Enable cache server to use actions/cache. + enabled: true + # The directory to store the cache data. + # If it's empty, the cache data will be stored in $HOME/.cache/actcache. + dir: "" + # The host of the cache server. + # It's not for the address to listen, but the address to connect from job containers. + # So 0.0.0.0 is a bad choice, leave it empty to detect automatically. + host: "" + # The port of the cache server. + # 0 means to use a random available port. + port: 0 + +container: + # Specifies the network to which the container will connect. + # Could be host, bridge or the name of a custom network. + # If it's empty, create a network automatically. + network: "" + # Whether to create networks with IPv6 enabled. Requires the Docker daemon to be set up accordingly. + # Only takes effect if "network" is set to "". + enable_ipv6: false + # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker). + privileged: false + # And other options to be used when the container is started (eg, --add-host=my.forgejo.url:host-gateway). + options: + # The parent directory of a job's working directory. + # If it's empty, /workspace will be used. + workdir_parent: + # Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob + # You can specify multiple volumes. If the sequence is empty, no volumes can be mounted. + # For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to: + # valid_volumes: + # - data + # - /src/*.json + # If you want to allow any volume, please use the following configuration: + # valid_volumes: + # - '**' + valid_volumes: [] + # overrides the docker client host with the specified one. + # If it's empty, act_runner will find an available docker host automatically. + # If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers. + # If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work. + docker_host: "" + +host: + # The parent directory of a job's working directory. + # If it's empty, $HOME/.cache/act/ will be used. + workdir_parent: +``` + +#### Cache configuration + +Some actions such as https://code.forgejo.org/actions/cache or +https://code.forgejo.org/actions/setup-go can communicate with the +`Forgejo runner` to save and restore commonly used files such as +compilation dependencies. They are stored as compressed tar archives, +fetched when a job starts and saved when it completes. + +If the machine has a fast disk, uploading the cache when the job +starts may significantly reduce the bandwidth required to download +and rebuild dependencies. + +If the machine on which the `Forgejo runner` is running has a slow +disk and plenty of CPU and bandwidth, it may be better to not activate +the cache as it can slow down the execution time. + +### Starting the runner + +After the runner has been registered, it can be started by running `forgejo-runner daemon` as the `runner` user, in the home directory: + +```shell +$ whoami +runner +$ pwd +/home/runner +$ forgejo-runner daemon +INFO[2024-09-14T19:19:14+02:00] Starting runner daemon +``` + +### Running as a systemd service + +To automatically start the runner when the system starts, copy [this file](https://code.forgejo.org/forgejo/runner/src/branch/main/contrib/forgejo-runner.service) to `/etc/systemd/system/forgejo-runner.service`. + +Then run `systemctl daemon-reload` to reload the unit files. Run `systemctl start forgejo-runner.service` to test the new service. If everything works, run `systemctl enable forgejo-runner.service` to enable auto-starting the service on boot. + +Use `journalctl -u forgejo-runner.service` to read the runner logs. + +## OCI image installation + +The [OCI images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions) +are built from the Dockerfile which is [found in the source directory](https://code.forgejo.org/forgejo/runner/src/branch/main/Dockerfile). It contains the `forgejo-runner` binary. + +```shell +$ docker run --rm code.forgejo.org/forgejo/runner:3.4.1 forgejo-runner --version +forgejo-runner version v3.4.1 +``` + +It does not run as root: + +```shell +$ docker run --rm code.forgejo.org/forgejo/runner:3.4.1 id +uid=1000 gid=1000 groups=1000 +``` + +One way to run the Docker image is via Docker Compose. To do so, first prepare a `data` directory with non-root permissions (in this case, we pick `1001:1001`): + +```shell +#!/usr/bin/env bash + +set -e + +mkdir -p data +touch data/.runner +mkdir -p data/.cache + +chown -R 1001:1001 data/.runner +chown -R 1001:1001 data/.cache +chmod 775 data/.runner +chmod 775 data/.cache +chmod g+s data/.runner +chmod g+s data/.cache +``` + +After running this script with `bash setup.sh`, define the following `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + docker-in-docker: + image: docker:dind + container_name: 'docker_dind' + privileged: 'true' + command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false'] + restart: 'unless-stopped' + + gitea: + image: 'code.forgejo.org/forgejo/runner:3.4.1' + links: + - docker-in-docker + depends_on: + docker-in-docker: + condition: service_started + container_name: 'runner' + environment: + DOCKER_HOST: tcp://docker-in-docker:2375 + # User without root privileges, but with access to `./data`. + user: 1001:1001 + volumes: + - ./data:/data + restart: 'unless-stopped' + + command: '/bin/sh -c "while : ; do sleep 1 ; done ;"' +``` + +Here, we're not running the `forgejo-runner daemon` yet because we need to register it first. Please note that in a recent install of docker `docker-compose` is not a separate command but should be run as `docker compose`. + +Follow the [registration instructions](#standard-registration) below by starting the `runner` service with `docker-compose up -d` and entering it via: + +```shell +docker exec -it runner /bin/sh +``` + +In this shell, run the `forgejo-runner register` command as described below. After that is done, take the service down again with `docker-compose down` and modify the `command` to: + +```yaml +command: '/bin/sh -c "sleep 5; forgejo-runner daemon"' +``` + +Here, the sleep allows the `docker-in-docker` service to start up before the `forgejo-runner daemon` is started. + +More [docker compose](https://docs.docker.com/compose/) examples [are provided](https://codeberg.org/forgejo/runner/src/branch/main/examples/docker-compose) to demonstrate how to install the OCI image to successfully run a workflow. + +## Standard registration + +The `Forgejo runner` needs to connect to a `Forgejo` instance and must be registered before doing so. It will give it permission to read the repositories and send back information to `Forgejo` such as the logs or its status. + +A special kind of token is needed and can be obtained from the `Create new runner` button: + +- in `/admin/actions/runners` to accept workflows from all repositories. +- in `/org/{org}/settings/actions/runners` to accept workflows from all repositories within the organization. +- in `/user/settings/actions/runners` to accept workflows from all repositories of the logged in user +- in `/{owner}/{repository}/settings/actions/runners` to accept workflows from a single repository. + +![Screenshot showing runner registration popup](../_images/user/actions/runners-add.png) + +To register the runner, excecute `forgejo-runner register` and fill in the prompts. For example: + +```shell +$ forgejo-runner register +INFO Registering runner, arch=arm64, os=linux, version=v3.5.1. +WARN Runner in user-mode. +INFO Enter the Forgejo instance URL (for example, https://next.forgejo.org/): +https://code.forgejo.org/ +INFO Enter the runner token: +6om01axzegBu98YCpsFtda4Go2DuJe7BEepzz2F3HY +INFO Enter the runner name (if set empty, use hostname: runner-host): +my-forgejo-runner +INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker://node:20-bookworm,ubuntu-18.04:docker://node:20-bookworm): + +INFO Registering runner, name=my-forgejo-runner, instance=https://code.forgejo.org/, labels=[docker:docker://node:20-bullseye]. +DEBU Successfully pinged the Forgejo instance server +INFO Runner registered successfully. +``` + +This will create a `.runner` file in the current directory that looks like: + +```json +{ + "WARNING": "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner.", + "id": 42, + "uuid": "d2ax6368-9c20-4dy0-9a5a-e09c53854zb5", + "name": "my-forgejo-runner", + "token": "864e6019009e1635d98adf3935b305d32494d42a", + "address": "https://code.forgejo.org/", + "labels": ["docker:docker://node:20-bullseye"] +} +``` + +To decide which labels to use, see [Choosing labels](../actions/#choosing-labels). + +The same token can be used multiple times to register any number of runners, independent of each other. + +## Offline registration + +When Infrastructure as Code (Ansible, kubernetes, etc.) is used to deploy and configure both Forgejo and the Forgejo runner, it may be more convenient for it to generate a secret and share it with both. + +The `forgejo forgejo-cli actions register --secret ` subcommand can be used to register the runner with the Forgejo instance and the `forgejo-runner create-runner-file --secret ` subcommand can be used to configure the Forgejo runner with the credentials that will allow it to start picking up tasks from the Forgejo instances as soon as it comes online. + +For instance, on the machine running Forgejo: + +```sh +$ forgejo forgejo-cli actions register --name runner-name --scope myorganization \ + --secret 7c31591e8b67225a116d4a4519ea8e507e08f71f +``` + +and on the machine on which the Forgejo runner is installed: + +```sh +$ forgejo-runner create-runner-file --instance https://example.conf \ + --secret 7c31591e8b67225a116d4a4519ea8e507e08f71f +``` + +The secret must be a 40-character long string of hexadecimal numbers. The first 16 characters will be used as an identifier for the runner, while the rest is the actual secret. It is possible to update the secret of an existing runner by running the command again on the Forgejo machine, with the last 24 characters updated. + +For instance, the command below would change the secret set by the previous command: + +```sh +$ forgejo forgejo-cli actions register --name runner-name --scope myorganization \ + --secret 7c31591e8b67225a84e8e06633b9578e793664c3 +# ^^^^^^^^^^^^^^^^ This part is identical +``` + +The registration command on the Forgejo side is mostly idempotent, with the exception of the runner labels. If the command is run without `--labels`, they will be reset, and the runner won't set them back until it is restarted. The `--keep-labels` option can be used to preserve the existing labels. + +## Enabling IPv6 in Docker & Podman networks + +When a `Forgejo runner` creates its own Docker or Podman networks, IPv6 is not enabled by default, and must be enabled explicitly in the `Forgejo runner` configuration. + +**Docker only**: The Docker daemon requires additional configuration to enable IPv6. To make use of IPv6 with Docker, you need to provide a `/etc/docker/daemon.json` configuration file with at least the following keys: + +```json +{ + "ipv6": true, + "experimental": true, + "ip6tables": true, + "fixed-cidr-v6": "fd00:d0ca:1::/64", + "default-address-pools": [ + { "base": "172.17.0.0/16", "size": 24 }, + { "base": "fd00:d0ca:2::/104", "size": 112 } + ] +} +``` + +Afterwards restart the Docker daemon with `systemctl restart docker.service`. + +> **NOTE**: These are example values. While this setup should work out of the box, it may not meet your requirements. Please refer to the Docker documentation regarding [enabling IPv6](https://docs.docker.com/config/daemon/ipv6/#use-ipv6-for-the-default-bridge-network) and [allocating IPv6 addresses to subnets dynamically](https://docs.docker.com/config/daemon/ipv6/#dynamic-ipv6-subnet-allocation). + +**Docker & Podman**: +To test IPv6 connectivity in `Forgejo runner`-created networks, create a small workflow such as the following: + +```yaml +--- +on: push +jobs: + ipv6: + runs-on: docker + steps: + - run: | + apt update; apt install --yes curl + curl -s -o /dev/null http://ipv6.google.com +``` + +If you run this action with `forgejo-runner exec`, you should expect this job fail: + +```shellsession +$ forgejo-runner exec +... +| curl: (7) Couldn't connect to server +[ipv6.yml/ipv6] ❌ Failure - apt update; apt install --yes curl +curl -s -o /dev/null http://ipv6.google.com +[ipv6.yml/ipv6] exitcode '7': failure +[ipv6.yml/ipv6] Cleaning up services for job ipv6 +[ipv6.yml/ipv6] Cleaning up container for job ipv6 +[ipv6.yml/ipv6] Cleaning up network for job ipv6, and network name is: FORGEJO-ACTIONS-TASK-push_WORKFLOW-ipv6-yml_JOB-ipv6-network +[ipv6.yml/ipv6] 🏁 Job failed +``` + +To actually enable IPv6 with `forgejo-runner exec`, the flag `--enable-ipv6` must be provided. If you run this again with `forgejo-runner exec --enable-ipv6`, the job should succeed: + +```shell-session +$ forgejo-runner exec --enable-ipv6 +... +[ipv6.yml/ipv6] ✅ Success - Main apt update; apt install --yes curl +curl -s -o /dev/null http://ipv6.google.com +[ipv6.yml/ipv6] Cleaning up services for job ipv6 +[ipv6.yml/ipv6] Cleaning up container for job ipv6 +[ipv6.yml/ipv6] Cleaning up network for job ipv6, and network name is: FORGEJO-ACTIONS-TASK-push_WORKFLOW-ipv6-yml_JOB-ipv6-network +[ipv6.yml/ipv6] 🏁 Job succeeded +``` + +Finally, if this test was successful, enable IPv6 in the `config.yml` file of the `Forgejo runner` daemon and restart the daemon: + +```yaml +container: + enable_ipv6: true +``` + +Now, `Forgejo runner` will create networks with IPv6 enabled, and workflow containers will be assigned addresses from the pools defined in the Docker daemon configuration. + +## Packaging + +### NixOS + +The [`forgejo-actions-runner`](https://github.com/NixOS/nixpkgs/blob/ac6977498b1246f21af08f3cf25ea7b602d94b99/pkgs/development/tools/continuous-integration/forgejo-actions-runner/default.nix) recipe is released in NixOS. + +Please note that the `services.forgejo-actions-runner.instances..labels` key may be set to `[]` (an empty list) to use the packaged Forgejo instance list. One of `virtualisation.docker.enable` or `virtualisation.podman.enable` will need to be set. The default Forgejo image list is populated with docker images. + +IPv6 support is not enabled by default for docker. The following snippet enables this. + +```nix +virtualisation.docker = { + daemon.settings = { + fixed-cidr-v6 = "fd00::/80"; + ipv6 = true; + }; +}; +``` + +If you would like to use docker runners in combination with [cache actions](#cache-configuration), be sure to add docker bridge interfaces "br-\*" to the firewalls' trusted interfaces: + +```nix +networking.firewall.trustedInterfaces = [ "br-+" ]; +```