mirror of
https://gitea.com/gitea/docs.git
synced 2026-11-04 17:19:52 +00:00
Simplify the runner docs and fix their outdated facts (#600)
The runner pages had drifted from the runner. They claimed Podman is unsupported, a registration retry limit of 10, a `container.env` proxy override that has no effect, and they used the deprecated `generate-config` command, while the actual instructions were buried under implementation details. The develop and 5.x pages are rewritten from the current runner sources (`main` is `v5.2.0`). They are shorter and aimed at operators, with a new page covering what jobs get. The reference generator now documents the `config` command, and the quickstart pages in every language use the `gitea-runner` binary name. Reviewed-on: https://gitea.com/gitea/docs/pulls/600 Reviewed-by: Lunny Xiao <[email protected]> Co-authored-by: silverwind <[email protected]>
This commit is contained in:
1 parent
a874918bb7
commit
be6aefc7e8
53 files changed
+1531
-1661
No files matched your search
@@ -4,62 +4,46 @@ sidebar_position: 1
|
||||
|
||||
# Install from a binary
|
||||
|
||||
The runner is a single static binary called `gitea-runner`. It has no dependencies apart from a Docker daemon for containerized jobs.
|
||||
The runner is a single binary, `gitea-runner`, built for Linux, macOS, Windows, and FreeBSD.
|
||||
|
||||
## Download
|
||||
|
||||
- released builds: [dl.gitea.com/gitea-runner](https://dl.gitea.com/gitea-runner/) or the [release page](https://gitea.com/gitea/runner/releases)
|
||||
- development builds of the `main` branch: [dl.gitea.com/gitea-runner/nightly](https://dl.gitea.com/gitea-runner/nightly/)
|
||||
|
||||
Each file is published next to a `.sha256` checksum and an `.xz` compressed variant:
|
||||
Download releases from [dl.gitea.com/gitea-runner](https://dl.gitea.com/gitea-runner/). Each binary has a matching `.sha256` checksum file:
|
||||
|
||||
```bash
|
||||
VERSION=5.1.1 # any 5.x release, see the release page
|
||||
VERSION=5.2.0
|
||||
curl -sSLO "https://dl.gitea.com/gitea-runner/$VERSION/gitea-runner-$VERSION-linux-amd64"
|
||||
curl -sSLO "https://dl.gitea.com/gitea-runner/$VERSION/gitea-runner-$VERSION-linux-amd64.sha256"
|
||||
sha256sum -c "gitea-runner-$VERSION-linux-amd64.sha256"
|
||||
install -m 0755 "gitea-runner-$VERSION-linux-amd64" /usr/local/bin/gitea-runner
|
||||
sudo install -m 0755 "gitea-runner-$VERSION-linux-amd64" /usr/local/bin/gitea-runner
|
||||
```
|
||||
|
||||
Check that the binary matches your platform:
|
||||
|
||||
```bash
|
||||
gitea-runner --version
|
||||
```
|
||||
|
||||
## Build from source
|
||||
|
||||
Building requires the Go version declared in the repository's `go.mod`:
|
||||
|
||||
```bash
|
||||
git clone https://gitea.com/gitea/runner.git
|
||||
cd runner
|
||||
make build
|
||||
```
|
||||
To build the runner yourself, run `make build` in a clone of [gitea/runner](https://gitea.com/gitea/runner).
|
||||
|
||||
## First run
|
||||
|
||||
```bash
|
||||
gitea-runner generate-config > config.yaml # optional, defaults are safe
|
||||
gitea-runner -c config.yaml register # see "Registering a runner"
|
||||
gitea-runner -c config.yaml daemon
|
||||
gitea-runner register --instance https://gitea.example.com --token <token>
|
||||
gitea-runner daemon
|
||||
```
|
||||
|
||||
The `daemon` command runs in the foreground. It reads the registration file (`runner.file`, `.runner` by default) relative to its working directory, so keep the working directory stable across restarts.
|
||||
`register` prompts for the remaining settings and writes the `.runner` registration file to the working directory. Run `daemon` from the same directory so it can read the file. See [Registration](../registration.md).
|
||||
|
||||
## Run as a systemd service
|
||||
## systemd
|
||||
|
||||
Create an unprivileged user, install the binary, and register the runner as that user so the `.runner` file ends up in the service's working directory:
|
||||
Create a user and a configuration file. Then register the runner as that user from its home directory:
|
||||
|
||||
```bash
|
||||
sudo useradd --system --home-dir /var/lib/gitea-runner --create-home gitea-runner
|
||||
sudo install -d /etc/gitea-runner
|
||||
sudo -u gitea-runner gitea-runner generate-config | sudo tee /etc/gitea-runner/config.yaml >/dev/null
|
||||
sudo mkdir -p /etc/gitea-runner
|
||||
sudo gitea-runner config init -c /etc/gitea-runner/config.yaml
|
||||
sudo chown gitea-runner /etc/gitea-runner/config.yaml
|
||||
cd /var/lib/gitea-runner
|
||||
sudo -u gitea-runner gitea-runner register -c /etc/gitea-runner/config.yaml
|
||||
sudo -u gitea-runner gitea-runner register -c /etc/gitea-runner/config.yaml \
|
||||
--instance https://gitea.example.com --token <token>
|
||||
```
|
||||
|
||||
Then install the unit as `/etc/systemd/system/gitea-runner.service`:
|
||||
Install the unit as `/etc/systemd/system/gitea-runner.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
@@ -79,8 +63,6 @@ User=gitea-runner
|
||||
Group=gitea-runner
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
# Allow running jobs to finish before the runner is stopped. Keep this in sync
|
||||
# with runner.shutdown_timeout in the config.
|
||||
TimeoutStopSec=3h
|
||||
|
||||
[Install]
|
||||
@@ -92,13 +74,17 @@ sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now gitea-runner
|
||||
```
|
||||
|
||||
If jobs use the host's Docker daemon, the `gitea-runner` user also needs access to the daemon socket. Adding it to the `docker` group grants that access and is [equivalent to root on the host](https://docs.docker.com/engine/security/#docker-daemon-attack-surface).
|
||||
Stopping the service cancels running jobs unless `runner.shutdown_timeout` gives them time to finish. Keep `runner.shutdown_timeout` below `TimeoutStopSec`:
|
||||
|
||||
Environment variables for the process — most importantly [proxy variables](../proxy.md) — belong in `Environment=` lines or a drop-in file, not in the runner config.
|
||||
```bash
|
||||
sudo -u gitea-runner gitea-runner config set -c /etc/gitea-runner/config.yaml runner.shutdown_timeout 2h50m
|
||||
```
|
||||
|
||||
## Run as a launchd daemon (macOS)
|
||||
For jobs in containers, give the `gitea-runner` user access to the Docker daemon's socket, for example through the `docker` group. That access is [equivalent to root](https://docs.docker.com/engine/security/#docker-daemon-attack-surface) on the host. Set the runner's environment variables, such as [proxy settings](../proxy.md), with `Environment=` lines in the unit.
|
||||
|
||||
macOS uses `launchd` instead of systemd. Daemons run as `root` by default; an unprivileged `_gitea-runner` user can be created with `dscl`. Install the following as `/Library/LaunchDaemons/com.gitea.runner.plist` and adjust the paths to your installation:
|
||||
## launchd (macOS)
|
||||
|
||||
Adjust the paths and user for your setup, then save this file as `/Library/LaunchDaemons/com.gitea.runner.plist`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@@ -114,16 +100,10 @@ macOS uses `launchd` instead of systemd. Daemons run as `root` by default; an un
|
||||
<string>--config</string>
|
||||
<string>/etc/gitea-runner/config.yaml</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>UserName</key>
|
||||
<string>_gitea-runner</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/var/lib/gitea-runner</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/lib/gitea-runner/runner.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/lib/gitea-runner/runner.err</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
@@ -131,23 +111,22 @@ macOS uses `launchd` instead of systemd. Daemons run as `root` by default; an un
|
||||
<key>HOME</key>
|
||||
<string>/var/lib/gitea-runner</string>
|
||||
</dict>
|
||||
<key>UserName</key>
|
||||
<string>_gitea-runner</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/lib/gitea-runner/runner.log</string>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo launchctl load /Library/LaunchDaemons/com.gitea.runner.plist
|
||||
sudo launchctl bootstrap system /Library/LaunchDaemons/com.gitea.runner.plist
|
||||
```
|
||||
|
||||
On macOS and Windows hosts, jobs usually run with [host labels](../labels.md) and the tools installed on the machine.
|
||||
|
||||
## Windows
|
||||
|
||||
Install the `windows-amd64` binary and register it as a service with any service wrapper (for example `sc.exe` plus a wrapper such as [WinSW](https://github.com/winsw/winsw), or a scheduled task at boot). The runner itself has no service-installer subcommand.
|
||||
Run `gitea-runner daemon` with a service wrapper such as [WinSW](https://github.com/winsw/winsw). The runner has no service installer of its own.
|
||||
|
||||
Keep in mind for Windows hosts:
|
||||
|
||||
- `runner.post_task_script` accepts `.exe`, `.bat` and `.cmd` paths; `.ps1` is not supported as the configured path.
|
||||
- host-mode jobs are terminated as a process tree, so tools that daemonize are not left behind.
|
||||
On macOS and Windows, jobs usually run directly on the runner's machine with [host labels](../labels.md).
|
||||
@@ -4,120 +4,74 @@ sidebar_position: 2
|
||||
|
||||
# Install with Docker
|
||||
|
||||
The official images are published on [Docker Hub](https://hub.docker.com/r/gitea/runner/tags) as `docker.io/gitea/runner`.
|
||||
`latest` is the newest release and the `5` tag follows the newest `5.x` release; `nightly` is built from the `main` branch, and every release is also tagged with its exact version.
|
||||
|
||||
In the container the registration and the daemon are combined: the entrypoint registers the runner on first start (when no registration file exists yet) and then execs `gitea-runner daemon`.
|
||||
|
||||
## Image flavours
|
||||
|
||||
All flavours contain the same `gitea-runner` binary and differ only in how a Docker daemon is made available to jobs.
|
||||
|
||||
| Tag | Base image | Docker daemon | Supervisor | Runs as |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `latest`, `5`, `5.1`, `<version>` | `alpine` | none, you provide one | `tini` | `root` |
|
||||
| `latest-dind`, `5-dind` | `docker:dind` | bundled, needs `--privileged` | `s6` | `root` |
|
||||
| `latest-dind-rootless`, `5-dind-rootless` | `docker:dind-rootless` | bundled, rootless | `s6` | `rootless` (UID 1000) |
|
||||
|
||||
The rootless flavour's UID is fixed at 1000 by the upstream base image, and its daemon always listens on `/run/user/1000/docker.sock`, so `--user 1001` does not work. To talk to a *host* rootless daemon under another UID, use the basic flavour and bind-mount that daemon's socket instead.
|
||||
|
||||
## Basic flavour
|
||||
|
||||
The default image ships no daemon of its own, so jobs that use `docker://` images need one from outside the container — usually the host's socket:
|
||||
Runner images are available on [Docker Hub](https://hub.docker.com/r/gitea/runner/tags) as `docker.io/gitea/runner`. On its first start, the container registers and starts the runner:
|
||||
|
||||
```bash
|
||||
docker run -d --name my_runner \
|
||||
-e GITEA_INSTANCE_URL=<instance_url> \
|
||||
-e GITEA_RUNNER_REGISTRATION_TOKEN=<registration_token> \
|
||||
-e GITEA_RUNNER_NAME=<runner_name> \
|
||||
-v $PWD/data:/data \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
docker.io/gitea/runner:5
|
||||
```
|
||||
|
||||
This flavour does not need `--privileged`. The trade-off is that jobs share the host's daemon and can therefore see its other containers and images. A job that can reach the socket can also read the reusable `GITEA_RUNNER_REGISTRATION_TOKEN` from the runner container's `docker inspect` output.
|
||||
|
||||
## Docker-in-Docker
|
||||
|
||||
The `dind` flavour bundles its own daemon, so no socket has to be mounted:
|
||||
|
||||
```bash
|
||||
docker run -d --name my_runner --privileged \
|
||||
-e GITEA_INSTANCE_URL=<instance_url> \
|
||||
-e GITEA_RUNNER_REGISTRATION_TOKEN=<registration_token> \
|
||||
-v $PWD/data:/data \
|
||||
docker run -d --name gitea-runner --restart always --privileged \
|
||||
-e GITEA_INSTANCE_URL=https://gitea.example.com \
|
||||
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
|
||||
-v ./data:/data \
|
||||
-v runner-docker:/var/lib/docker \
|
||||
docker.io/gitea/runner:5-dind
|
||||
```
|
||||
|
||||
`s6` starts `dockerd` first and the runner service waits for it before registering. Use `5-dind-rootless` to run both the daemon and the runner as an unprivileged user; rootless Docker's usual limitations around networking, cgroups and storage drivers apply.
|
||||
The `-dind` image runs its own Docker daemon for jobs. Jobs never get access to the host's Docker daemon socket, which grants the equivalent of root access on the host. The two volumes preserve registration and job images when you replace the container:
|
||||
|
||||
## Volumes
|
||||
- `/data` holds the `.runner` registration file. Without it, a new container registers as another runner.
|
||||
- `/var/lib/docker` holds the images pulled for jobs. Without it, a new container pulls them again.
|
||||
|
||||
Two different pieces of state are worth persisting, and neither implies the other:
|
||||
## Image flavours
|
||||
|
||||
- `/data` is the runner's working directory. It holds the `.runner` registration file and, optionally, the config file. Without it, a recreated container registers itself again as a new runner, leaving a stale entry in Gitea, and fails outright if the token has been reset in the meantime.
|
||||
- the Docker daemon's data root holds the images pulled for jobs. It is **not** under `/data`: for `dind` it is `/var/lib/docker` inside the container, for `dind-rootless` it is `/home/rootless/.local/share/docker`. Give it its own volume, or every new container re-pulls the job images.
|
||||
| Tag | Docker daemon for jobs |
|
||||
| --- | --- |
|
||||
| `5-dind` (recommended) | Bundled. Requires `--privileged`. |
|
||||
| `5-dind-rootless` | Bundled. Runs as UID 1000. Requires `--privileged` and [rootless Docker](https://docs.docker.com/engine/security/rootless/) support on the host. Stores its images in `/home/rootless/.local/share/docker`. |
|
||||
| `5` | None. Mount the host's Docker daemon socket with `-v /var/run/docker.sock:/var/run/docker.sock`, or set `DOCKER_HOST`. |
|
||||
|
||||
## Entrypoint environment variables
|
||||
The `5` tags follow the newest `5.x` release. The `latest` and exact version tags such as `5.2.0` use the same suffixes. The `-dind` flavours are available for amd64 and arm64. The basic image also supports riscv64 and s390x. The images are designed for Docker, not Podman.
|
||||
|
||||
The entrypoint ([`scripts/run.sh`](https://gitea.com/gitea/runner/src/branch/main/scripts/run.sh)) understands:
|
||||
When the basic image uses the host's Docker daemon, jobs share that daemon. Use this setup only for trusted repositories.
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Meaning |
|
||||
| --- | --- |
|
||||
| `GITEA_INSTANCE_URL` | instance to register against, e.g. `https://gitea.example.com/` |
|
||||
| `GITEA_RUNNER_REGISTRATION_TOKEN` | registration token; unset before the daemon starts |
|
||||
| `GITEA_RUNNER_REGISTRATION_TOKEN_FILE` | file to read the token from, for Docker/Kubernetes secrets |
|
||||
| `GITEA_RUNNER_NAME` | runner name, defaults to the container hostname |
|
||||
| `GITEA_RUNNER_LABELS` | labels, passed to both `register` and `daemon` |
|
||||
| `GITEA_RUNNER_EPHEMERAL` | any non-empty value registers the runner as [ephemeral](../registration.md#ephemeral-runners) |
|
||||
| `GITEA_RUNNER_ONCE` | any non-empty value runs a single job, then exits |
|
||||
| `GITEA_MAX_REG_ATTEMPTS` | registration attempts before giving up, default `10` |
|
||||
| `RUNNER_STATE_FILE` | registration file name inside `/data`, default `.runner` |
|
||||
| `CONFIG_FILE` | config file inside the container, passed as `--config` |
|
||||
| `GITEA_INSTANCE_URL` | Gitea instance to register with. |
|
||||
| `GITEA_RUNNER_REGISTRATION_TOKEN` | Registration token, needed only until registration completes. |
|
||||
| `GITEA_RUNNER_REGISTRATION_TOKEN_FILE` | File to read the registration token from, such as a Docker secret. |
|
||||
| `GITEA_RUNNER_NAME` | Runner name. Defaults to the container's hostname. |
|
||||
| `GITEA_RUNNER_LABELS` | Comma-separated [labels](../labels.md), applied on every start. |
|
||||
| `GITEA_RUNNER_EPHEMERAL` | Any nonempty value registers an [ephemeral runner](../registration.md#ephemeral-runners). |
|
||||
| `GITEA_RUNNER_ONCE` | Any nonempty value makes the runner exit after one job. |
|
||||
| `GITEA_MAX_REG_ATTEMPTS` | Maximum number of registration attempts. Unlimited by default, with 5 seconds between attempts. |
|
||||
| `CONFIG_FILE` | Path to a [configuration file](../configuration.md) inside the container. |
|
||||
|
||||
These are entrypoint variables, not runner settings: the runner process itself is configured only through the [config file](../configuration.md).
|
||||
|
||||
Mount the config file when you need one:
|
||||
To use a configuration file, mount it and set `CONFIG_FILE` to its path:
|
||||
|
||||
```bash
|
||||
docker run -v $PWD/config.yaml:/config.yaml -e CONFIG_FILE=/config.yaml ...
|
||||
docker run ... -v ./config.yaml:/config.yaml -e CONFIG_FILE=/config.yaml docker.io/gitea/runner:5-dind
|
||||
```
|
||||
|
||||
A config file can be generated with the image itself:
|
||||
|
||||
```bash
|
||||
docker run --rm --entrypoint="" docker.io/gitea/runner:5 gitea-runner generate-config > config.yaml
|
||||
```
|
||||
|
||||
## docker compose
|
||||
## Docker Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
runner:
|
||||
image: docker.io/gitea/runner:5
|
||||
image: docker.io/gitea/runner:5-dind
|
||||
restart: always
|
||||
privileged: true
|
||||
environment:
|
||||
CONFIG_FILE: /config.yaml
|
||||
GITEA_INSTANCE_URL: "${INSTANCE_URL}"
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: "${REGISTRATION_TOKEN}"
|
||||
GITEA_RUNNER_NAME: "${RUNNER_NAME}"
|
||||
GITEA_RUNNER_LABELS: "${RUNNER_LABELS}"
|
||||
GITEA_INSTANCE_URL: https://gitea.example.com
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: <token>
|
||||
volumes:
|
||||
- ./config.yaml:/config.yaml
|
||||
- ./data:/data
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- runner-docker:/var/lib/docker
|
||||
|
||||
volumes:
|
||||
runner-docker:
|
||||
```
|
||||
|
||||
When Gitea runs in the same compose project, depend on its health check so the runner does not try to register before the instance answers:
|
||||
|
||||
```yaml
|
||||
depends_on:
|
||||
gitea:
|
||||
condition: service_healthy
|
||||
restart: true
|
||||
```
|
||||
|
||||
The rootless Docker-in-Docker variant needs a few extra options:
|
||||
The rootless flavour needs these additional settings:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -126,22 +80,21 @@ services:
|
||||
restart: always
|
||||
privileged: true
|
||||
security_opt:
|
||||
# for hosts running AppArmor (Ubuntu, Debian), whose default profile blocks
|
||||
# the user namespace changes the bundled daemon needs
|
||||
- apparmor=rootlesskit
|
||||
volumes:
|
||||
- ./data/runner:/data
|
||||
environment:
|
||||
- GITEA_INSTANCE_URL=<instance_url>
|
||||
- GITEA_RUNNER_REGISTRATION_TOKEN=<registration_token>
|
||||
- DOCKER_HOST=unix:///var/run/user/1000/docker.sock
|
||||
# slirp4netns gives significantly better network throughput than vpnkit
|
||||
- DOCKERD_ROOTLESS_ROOTLESSKIT_NET=slirp4netns
|
||||
- DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=65520
|
||||
GITEA_INSTANCE_URL: https://gitea.example.com
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: <token>
|
||||
DOCKERD_ROOTLESS_ROOTLESSKIT_NET: slirp4netns
|
||||
DOCKERD_ROOTLESS_ROOTLESSKIT_MTU: 65520
|
||||
volumes:
|
||||
- runner-data:/data
|
||||
|
||||
volumes:
|
||||
runner-data:
|
||||
```
|
||||
|
||||
## Cache from a dockerized runner
|
||||
- Set `security_opt` on AppArmor hosts such as Ubuntu and Debian. Docker's `docker-ce-rootless-extras` package provides the `rootlesskit` profile.
|
||||
- Use `slirp4netns` for better job network throughput than the default.
|
||||
- Make `/data` writable by UID 1000. Use a named volume or a bind-mounted host directory owned by that UID.
|
||||
|
||||
A runner in a container creates a separate network per job by default, so the address it detects for its own cache server is often unreachable from job containers and `actions/cache` fails with a connection timeout. Set `cache.host` and `cache.port` explicitly and publish that port, or put the job containers on a shared network — see [Caching](../cache.md#dockerized-runners).
|
||||
|
||||
More deployment examples live in the [`examples`](https://gitea.com/gitea/runner/src/branch/main/examples) directory of the runner repository.
|
||||
See the runner repository's [examples](https://gitea.com/gitea/runner/src/branch/main/examples) for more deployments.
|
||||
@@ -4,46 +4,68 @@ sidebar_position: 3
|
||||
|
||||
# Install on Kubernetes
|
||||
|
||||
Ready-to-adapt manifests live in [`examples/kubernetes`](https://gitea.com/gitea/runner/src/branch/main/examples/kubernetes) of the runner repository, and a Helm chart is maintained at [gitea/helm-actions](https://gitea.com/gitea/helm-actions).
|
||||
A runner in Kubernetes can run jobs in two ways:
|
||||
|
||||
## Choosing a manifest
|
||||
- **Job pods**: Each job runs in its own pod under the cluster's quotas and policies, without privileged containers. Docker actions and `docker://` steps are not supported.
|
||||
- **Docker-in-Docker**: Jobs run in a Docker daemon alongside the runner. All workflow features are supported. The daemon is privileged, so a job can break out of its container and access the node.
|
||||
|
||||
| Example | Shape | Docker daemon |
|
||||
| --- | --- | --- |
|
||||
| `dind-docker.yaml` | `Deployment` with a native sidecar (`initContainer` with `restartPolicy: Always`, needs Kubernetes 1.29+) | privileged `docker:dind` sidecar, socket shared through an `emptyDir` |
|
||||
| `statefulset-dind.yaml` | `StatefulSet` with `volumeClaimTemplates` | same as above |
|
||||
| `rootless-docker.yaml` | `Deployment` with a single container | bundled rootless daemon of the `dind-rootless` image, reached over `tcp://localhost:2376` with TLS |
|
||||
The manifests below are in the runner repository's [`examples/kubernetes`](https://gitea.com/gitea/runner/src/branch/main/examples/kubernetes) directory. They read the registration token from the `runner-secret` Secret and store the `.runner` registration file in a volume at `/data`. This preserves registration when the pod restarts.
|
||||
|
||||
Prefer the `StatefulSet` variant when you scale past one replica: each pod then gets a stable identity and its own volume, so it keeps its `.runner` registration across restarts and reschedules instead of registering itself again as a new runner.
|
||||
## Job pods
|
||||
|
||||
## Two volumes, two purposes
|
||||
Deploy [`kubernetes-pods.yaml`](https://gitea.com/gitea/runner/src/branch/main/examples/kubernetes/kubernetes-pods.yaml). It registers the runner with a `kubernetes` label:
|
||||
|
||||
- `/data` — the runner's working directory, holding the `.runner` registration file and optionally the config file.
|
||||
- the daemon's data root — `/var/lib/docker` for the `dind` sidecar, `/home/rootless/.local/share/docker` for `dind-rootless`. It holds the images pulled for jobs and is *not* under `/data`. Dropping it still works, but every recreated pod re-pulls all job images.
|
||||
|
||||
With the rootless image, both volumes must be writable by UID/GID 1000, which is what `securityContext.fsGroup: 1000` in the example is for.
|
||||
|
||||
## Registration token
|
||||
|
||||
The examples read the token from a `Secret`:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
- name: GITEA_INSTANCE_URL
|
||||
value: http://gitea-http.gitea.svc.cluster.local:3000
|
||||
- name: GITEA_RUNNER_REGISTRATION_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: runner-secret
|
||||
key: token
|
||||
```text
|
||||
ubuntu-latest:kubernetes://docker.gitea.com/runner-images:ubuntu-latest
|
||||
```
|
||||
|
||||
The token stays valid for further registrations until it is reset, but each registration creates another runner entry. For pods that are recreated without their volume, use the instance-wide token configured on the Gitea side, and expect stale runner entries — or start an [ephemeral runner](../registration.md#ephemeral-runners) per job.
|
||||
The runner creates job pods in its own namespace using its service account. The manifest grants the permissions it needs:
|
||||
|
||||
## Privileges
|
||||
| Resource | Verbs |
|
||||
| --- | --- |
|
||||
| `pods` | `create`, `get`, `list`, `delete` |
|
||||
| `pods/exec` | `create`, `get` |
|
||||
| `pods/log` | `get` |
|
||||
| `secrets` | `create`, `delete` |
|
||||
|
||||
Docker-in-Docker needs `securityContext.privileged: true`, which lets a malicious job break out of the container. Weigh that against the alternatives:
|
||||
The cluster must run Kubernetes 1.30 or later. Job images need `sh`, `tar`, `tail`, and `env`. A runner outside the cluster uses the current context from the first file in `$KUBECONFIG`, or from `~/.kube/config` if `$KUBECONFIG` is unset.
|
||||
|
||||
- the rootless flavour, which reduces but does not remove the exposure;
|
||||
- pointing the basic flavour at a daemon outside the cluster;
|
||||
- keeping such runners on a dedicated node pool and only granting them to trusted repositories.
|
||||
Use pod templates to set resources, node selectors, image pull secrets, and other settings for job pods:
|
||||
|
||||
```yaml
|
||||
kubernetes:
|
||||
pod_template:
|
||||
spec:
|
||||
containers:
|
||||
- name: job # merged into the job container
|
||||
resources:
|
||||
limits:
|
||||
memory: 4Gi
|
||||
- name: $services # merged into every service container
|
||||
resources:
|
||||
limits:
|
||||
memory: 1Gi
|
||||
pod_templates:
|
||||
gpu: # merged on top for jobs with runs-on: gpu
|
||||
spec:
|
||||
nodeSelector:
|
||||
nvidia.com/gpu.present: "true"
|
||||
```
|
||||
|
||||
Services share the job pod's network. Steps reach them by their service ID or `localhost`. Two services cannot use the same port. Only `--health-*` is supported in a service's `options`. Job pods do not receive a service account token unless the template sets `automountServiceAccountToken: true`.
|
||||
|
||||
## Docker-in-Docker
|
||||
|
||||
| Manifest | Deployment type |
|
||||
| --- | --- |
|
||||
| [`dind-docker.yaml`](https://gitea.com/gitea/runner/src/branch/main/examples/kubernetes/dind-docker.yaml) | `Deployment` with a privileged `docker:dind` sidecar |
|
||||
| [`statefulset-dind.yaml`](https://gitea.com/gitea/runner/src/branch/main/examples/kubernetes/statefulset-dind.yaml) | `StatefulSet` with a privileged `docker:dind` sidecar, for multiple replicas |
|
||||
| [`rootless-docker.yaml`](https://gitea.com/gitea/runner/src/branch/main/examples/kubernetes/rootless-docker.yaml) | `Deployment` with the `dind-rootless` image |
|
||||
|
||||
Use the `StatefulSet` for multiple replicas so each has its own registration volume. A [Helm chart](https://gitea.com/gitea/helm-actions) is also available.
|
||||
|
||||
The Docker daemon stores images pulled for jobs in `/var/lib/docker`, or `/home/rootless/.local/share/docker` in the rootless image. The manifests mount this directory on a volume so a new pod does not pull every image again.
|
||||
|
||||
## Shutdown
|
||||
|
||||
The manifests set `terminationGracePeriodSeconds` to give a pod three hours to stop. To let running jobs finish, set `runner.shutdown_timeout` a little lower than that grace period. Otherwise, the runner cancels running jobs when the pod stops.
|
||||
Reference in new issue
Block a user