ver 0.1
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# Architecture
|
||||
|
||||
## Design summary
|
||||
|
||||
The runner is a Docker image that wraps the official `act_runner` binary in a controlled, predictable environment. All dependencies are installed at image build time. The running container is long-lived and operator-managed.
|
||||
|
||||
---
|
||||
|
||||
## Layers
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Gitea instance (git.alwisp.com) │ ← dispatches jobs via act protocol
|
||||
└───────────────────┬─────────────────────────┘
|
||||
│ HTTP / WebSocket
|
||||
┌───────────────────▼─────────────────────────┐
|
||||
│ custom-gitea-runner container │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ tini (PID 1) │ │
|
||||
│ │ └── docker-entrypoint.sh │ │
|
||||
│ │ ├── validate binaries │ │
|
||||
│ │ ├── install custom CAs │ │
|
||||
│ │ ├── register (first boot only) │ │
|
||||
│ │ └── act_runner daemon │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Bundled tools (installed at build time): │
|
||||
│ bash, curl, git, jq, node, npm, │
|
||||
│ docker CLI, ca-certificates, tini │
|
||||
└───────────────────┬─────────────────────────┘
|
||||
│ /var/run/docker.sock mount
|
||||
┌───────────────────▼─────────────────────────┐
|
||||
│ Host Docker daemon │ ← runs job containers
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key design decisions
|
||||
|
||||
See `docs/decisions/` for ADR-style records of each major choice.
|
||||
|
||||
| Decision | Choice | Doc |
|
||||
|---|---|---|
|
||||
| Base image | Ubuntu 22.04 | `decisions/0001-base-image.md` |
|
||||
| act_runner strategy | Wrap official binary | — |
|
||||
| Docker access | Socket mount (`/var/run/docker.sock`) | — |
|
||||
| PID 1 | `tini` | — |
|
||||
| Node.js source | NodeSource (Node 20 LTS) | — |
|
||||
| v1 user model | root (Docker socket requires it) | — |
|
||||
|
||||
---
|
||||
|
||||
## Registration modes
|
||||
|
||||
**Mode A — Pre-generated config (preferred for stable production)**
|
||||
|
||||
Operator mounts a pre-generated `runner.yaml` at `RUNNER_CONFIG_PATH`. The entrypoint detects the file and skips registration, going straight to daemon start.
|
||||
|
||||
**Mode B — Bootstrap registration (first boot)**
|
||||
|
||||
If `RUNNER_CONFIG_PATH` does not exist, the entrypoint reads `GITEA_INSTANCE_URL`, `GITEA_RUNNER_TOKEN`, and `GITEA_RUNNER_NAME` from the environment, generates a default config, and calls `act_runner register`. Subsequent starts use the persisted config (Mode A path).
|
||||
|
||||
This flow is idempotent — reboots with an existing config will not re-register.
|
||||
|
||||
---
|
||||
|
||||
## Docker socket model
|
||||
|
||||
The runner container mounts the host Docker socket (`/var/run/docker.sock`). This allows:
|
||||
- `docker build` and `docker push` in workflow steps
|
||||
- `docker/login-action` against external registries
|
||||
|
||||
**Trust implication:** a workflow running on this runner has full access to the host Docker daemon. This is acceptable for a trusted, self-hosted environment. Future hardening options (DinD, rootless, network isolation) are tracked in `ROADMAP.md` Phase 6.
|
||||
|
||||
---
|
||||
|
||||
## Future hardening tracks
|
||||
|
||||
- Rootless container execution
|
||||
- Docker-in-Docker (DinD) variant for true isolation
|
||||
- Dedicated BuildKit daemon
|
||||
- Ephemeral single-job runners
|
||||
- Network policy enforcement
|
||||
@@ -0,0 +1,49 @@
|
||||
# ADR 0001 — Base Image Selection
|
||||
|
||||
**Status:** Accepted
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The runner needs a base OS image that provides:
|
||||
- A working apt package ecosystem for installing `git`, `curl`, `jq`, `tini`, `ca-certificates`, `tzdata`.
|
||||
- Compatibility with Docker's official apt repository (for Docker CLI).
|
||||
- Compatibility with NodeSource (for Node.js 20 LTS).
|
||||
- A label environment the existing workflows reference: `ubuntu-latest`.
|
||||
|
||||
Candidates considered:
|
||||
1. `ubuntu:22.04` — LTS, wide ecosystem support, good compatibility.
|
||||
2. `ubuntu:24.04` — Newer LTS, some tooling still stabilizing.
|
||||
3. `debian:bookworm-slim` — Smaller footprint, good compatibility, but `ubuntu-latest` label mismatch.
|
||||
4. `debian:bullseye-slim` — Older, stable, but further from `ubuntu-latest` expectations.
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
Use `ubuntu:22.04` as the base image, pinned by exact tag.
|
||||
|
||||
---
|
||||
|
||||
## Rationale
|
||||
|
||||
- Existing Gitea workflows use `runs-on: ubuntu-latest`. Basing the runner image on Ubuntu 22.04 maximizes compatibility with GitHub Actions steps written for that environment.
|
||||
- Ubuntu 22.04 LTS has long-term support through 2027, reducing forced base image churn.
|
||||
- Docker's apt repository and NodeSource both officially support Ubuntu 22.04.
|
||||
- Ubuntu 22.04 is the most common self-hosted runner base image in community examples, so troubleshooting resources are abundant.
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
- Final image size will be larger than a minimal Alpine or Debian slim base, which is acceptable given the goal of bundling all runtime dependencies.
|
||||
- When the workflow ecosystem moves to `ubuntu-24.04`, the base image can be bumped with a version argument change (`ARG UBUNTU_VERSION=24.04`).
|
||||
|
||||
---
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Debian bookworm-slim:** Smaller image, but the `ubuntu-latest` label in existing workflows creates an expectation of Ubuntu compatibility. Switching to Debian may cause subtle incompatibilities with actions that check the OS ID.
|
||||
|
||||
**Ubuntu 24.04:** Not yet the default for `ubuntu-latest` as of the initial build date; 22.04 remains the more tested choice for self-hosted runners.
|
||||
@@ -0,0 +1,134 @@
|
||||
# Operations
|
||||
|
||||
## Normal startup sequence
|
||||
|
||||
When the container starts, expect to see:
|
||||
|
||||
```
|
||||
======================================================
|
||||
custom-gitea-runner
|
||||
Image version : v0.1.0
|
||||
Git revision : abc1234
|
||||
======================================================
|
||||
Runtime:
|
||||
act_runner : v0.2.11
|
||||
node : v20.x.x
|
||||
npm : 10.x.x
|
||||
git : 2.x.x
|
||||
docker : 24.x.x
|
||||
Config:
|
||||
Config path : /config/runner.yaml
|
||||
Work dir : /work
|
||||
Gitea URL : https://git.example.com
|
||||
Runner name : unraid-runner-01
|
||||
Labels : ubuntu-latest:docker://node:20-bullseye
|
||||
======================================================
|
||||
[OK] All required binaries present.
|
||||
[INFO] Existing config found at /config/runner.yaml. Skipping registration.
|
||||
[START] Starting act_runner daemon ...
|
||||
```
|
||||
|
||||
On first boot with no config:
|
||||
|
||||
```
|
||||
[BOOTSTRAP] No config found at /config/runner.yaml. Initiating registration.
|
||||
[BOOTSTRAP] Registration complete. Config at /config/runner.yaml.
|
||||
[START] Starting act_runner daemon ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Health check
|
||||
|
||||
The container runs a health check every 30s. Check status with:
|
||||
|
||||
```bash
|
||||
docker inspect --format='{{.State.Health.Status}}' gitea-runner
|
||||
```
|
||||
|
||||
Or view health check output:
|
||||
|
||||
```bash
|
||||
docker inspect --format='{{json .State.Health}}' gitea-runner | jq .
|
||||
```
|
||||
|
||||
Expected healthy output from `healthcheck.sh`:
|
||||
|
||||
```
|
||||
[HEALTH] OK : act_runner process is running
|
||||
[HEALTH] OK : Config file present at /config/runner.yaml
|
||||
[HEALTH] OK : Binary present: act_runner
|
||||
[HEALTH] OK : Binary present: git
|
||||
[HEALTH] OK : Binary present: node
|
||||
[HEALTH] OK : Binary present: npm
|
||||
[HEALTH] OK : Docker socket accessible
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Smoke test
|
||||
|
||||
Before production use, run the smoke test against the image:
|
||||
|
||||
```bash
|
||||
./scripts/verify-runtime.sh custom-gitea-runner:v0.1.0
|
||||
```
|
||||
|
||||
Or directly:
|
||||
|
||||
```bash
|
||||
docker run --rm custom-gitea-runner:v0.1.0 /usr/local/bin/smoke-test.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checking runner registration in Gitea
|
||||
|
||||
1. Go to your Gitea instance.
|
||||
2. Navigate to **Site Administration → Runners** (admin panel) or **Repository Settings → Runners**.
|
||||
3. The runner should appear with status **Online**.
|
||||
|
||||
---
|
||||
|
||||
## Upgrade procedure
|
||||
|
||||
1. Build or pull the new pinned image tag.
|
||||
2. Review `CHANGELOG.md` for breaking changes.
|
||||
3. Stop the existing container.
|
||||
4. Update the image tag in your compose file or Unraid template.
|
||||
5. Start the container.
|
||||
6. Verify startup logs and health check status.
|
||||
7. Run a smoke workflow to confirm job execution works.
|
||||
|
||||
---
|
||||
|
||||
## Rollback procedure
|
||||
|
||||
1. Stop the container.
|
||||
2. Revert the image tag to the prior known-good version.
|
||||
3. Start the container.
|
||||
4. Confirm the registration config is still valid (it should be — it's persisted in the config volume).
|
||||
5. Run a smoke workflow.
|
||||
|
||||
---
|
||||
|
||||
## Re-registration
|
||||
|
||||
If you need to re-register the runner (e.g. after a token reset or renaming):
|
||||
|
||||
1. Stop the container.
|
||||
2. Delete `config/runner.yaml`.
|
||||
3. Set the new `GITEA_RUNNER_TOKEN` in your environment.
|
||||
4. Start the container — it will register fresh on boot.
|
||||
|
||||
---
|
||||
|
||||
## Log access
|
||||
|
||||
```bash
|
||||
# Tail live logs
|
||||
docker logs -f gitea-runner
|
||||
|
||||
# Last 100 lines
|
||||
docker logs --tail 100 gitea-runner
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
# Troubleshooting
|
||||
|
||||
Symptom-driven guide for common runner failures.
|
||||
|
||||
---
|
||||
|
||||
## Runner not appearing in Gitea admin panel
|
||||
|
||||
**Check:** Container logs for registration errors.
|
||||
|
||||
```bash
|
||||
docker logs gitea-runner 2>&1 | grep -E "BOOTSTRAP|FATAL|ERROR"
|
||||
```
|
||||
|
||||
**Common causes:**
|
||||
|
||||
| Cause | Fix |
|
||||
|---|---|
|
||||
| `GITEA_RUNNER_TOKEN` is invalid or expired | Generate a fresh token in Gitea admin → Runners |
|
||||
| `GITEA_INSTANCE_URL` wrong or unreachable | Verify URL is reachable from inside the container: `docker exec gitea-runner curl -I "$GITEA_INSTANCE_URL"` |
|
||||
| TLS cert error (self-signed Gitea) | Mount your CA cert to `/config/trusted-ca/` and restart |
|
||||
| Runner name already registered | Use a unique `GITEA_RUNNER_NAME` or deregister the old one |
|
||||
|
||||
---
|
||||
|
||||
## `node: not found` in job logs
|
||||
|
||||
The runner image bundles Node.js 20 LTS. If you see this error it usually means:
|
||||
|
||||
- The job is running inside a container image that does not have Node.js (e.g. `alpine`).
|
||||
- The `ubuntu-latest` label is mapped to a minimal image.
|
||||
|
||||
**Fix:** Ensure the label mapping uses a full Node image:
|
||||
|
||||
```
|
||||
ubuntu-latest:docker://node:20-bullseye
|
||||
```
|
||||
|
||||
Or use a host-mode label:
|
||||
|
||||
```
|
||||
ubuntu-latest:host
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker socket permission error
|
||||
|
||||
**Symptom:**
|
||||
|
||||
```
|
||||
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
|
||||
```
|
||||
|
||||
**Causes and fixes:**
|
||||
|
||||
| Cause | Fix |
|
||||
|---|---|
|
||||
| Socket not mounted | Add `-v /var/run/docker.sock:/var/run/docker.sock` |
|
||||
| Socket GID mismatch | Container runs as root in v1 — this should not occur. If using a non-root custom image, add the runner user to the `docker` group or set `--group-add $(stat -c '%g' /var/run/docker.sock)` |
|
||||
|
||||
---
|
||||
|
||||
## Workflow checkout failures
|
||||
|
||||
**Symptom:** `actions/checkout` fails with an auth or host error.
|
||||
|
||||
**Check:**
|
||||
1. Gitea URL is accessible from inside the container.
|
||||
2. If using self-signed TLS, the CA cert is trusted inside the container.
|
||||
|
||||
```bash
|
||||
docker exec gitea-runner curl -v https://git.example.com
|
||||
```
|
||||
|
||||
If you see certificate errors, add your CA cert to `/config/trusted-ca/` and restart.
|
||||
|
||||
---
|
||||
|
||||
## Registry login failures (`docker/login-action`)
|
||||
|
||||
**Symptom:** Login to `registry.alwisp.com` fails.
|
||||
|
||||
**Check:**
|
||||
1. Registry URL is correct.
|
||||
2. Credentials are valid.
|
||||
3. If using self-signed TLS on the registry, the CA cert needs to be trusted inside the container AND the Docker daemon config must also trust the registry.
|
||||
|
||||
For Docker daemon CA trust on the Unraid host:
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/docker/certs.d/registry.alwisp.com
|
||||
cp my-ca.crt /etc/docker/certs.d/registry.alwisp.com/ca.crt
|
||||
```
|
||||
|
||||
Then restart the Docker daemon and the runner container.
|
||||
|
||||
---
|
||||
|
||||
## Container job image issues
|
||||
|
||||
**Symptom:** Jobs that run inside container images fail to pull or execute.
|
||||
|
||||
**Check:**
|
||||
- The container image referenced in the label mapping is pullable from inside the runner container.
|
||||
- If using a self-hosted registry image, the runner container can authenticate to it.
|
||||
|
||||
---
|
||||
|
||||
## CA / certificate trust issues
|
||||
|
||||
**Symptom:** TLS errors when connecting to Gitea or the registry.
|
||||
|
||||
**Fix:**
|
||||
1. Copy your CA cert (`.crt` format, PEM encoded) to `config/trusted-ca/`.
|
||||
2. Restart the container — the entrypoint installs certs from that directory on startup.
|
||||
3. Verify: `docker exec gitea-runner curl -v https://git.example.com` should show cert chain OK.
|
||||
|
||||
---
|
||||
|
||||
## Unraid-specific: container exits immediately
|
||||
|
||||
**Check:** Container logs before exit:
|
||||
|
||||
```bash
|
||||
docker logs gitea-runner
|
||||
```
|
||||
|
||||
Look for `[FATAL]` lines. Common cause is a missing required env var (`GITEA_INSTANCE_URL`, `GITEA_RUNNER_TOKEN`, `GITEA_RUNNER_NAME`) when no config file exists.
|
||||
|
||||
**Fix:** Set required env vars in the Unraid container template.
|
||||
|
||||
---
|
||||
|
||||
## Startup validation failures
|
||||
|
||||
**Symptom:** Container exits with `[FATAL] Missing required binaries`.
|
||||
|
||||
This is an image build problem — the binary should always be present if the image was built correctly.
|
||||
|
||||
**Fix:** Rebuild the image from the `docker/runner/Dockerfile` and verify:
|
||||
|
||||
```bash
|
||||
./scripts/verify-runtime.sh custom-gitea-runner:latest
|
||||
```
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
# Unraid Deployment Guide
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers deploying the custom-gitea-runner as a Docker container on an Unraid host. The recommended approach is Docker Compose via Unraid's compose manager, or manual template entry via the Unraid Docker UI.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Unraid with Docker enabled.
|
||||
- Access to your Gitea instance admin panel to generate a runner token.
|
||||
- The runner image pushed to your registry (e.g. `registry.alwisp.com`), or built locally on the Unraid host.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Get a runner token
|
||||
|
||||
1. Log into your Gitea instance as an admin.
|
||||
2. Go to **Site Administration → Runners → Create new runner**.
|
||||
3. Copy the registration token. It will be used as `GITEA_RUNNER_TOKEN`.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Set up appdata directories
|
||||
|
||||
```bash
|
||||
mkdir -p /mnt/user/appdata/gitea-runner/config
|
||||
mkdir -p /mnt/user/appdata/gitea-runner/work
|
||||
```
|
||||
|
||||
If using custom CA certificates:
|
||||
|
||||
```bash
|
||||
mkdir -p /mnt/user/appdata/gitea-runner/config/trusted-ca
|
||||
# Copy your CA cert:
|
||||
cp my-ca.crt /mnt/user/appdata/gitea-runner/config/trusted-ca/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Docker UI container settings
|
||||
|
||||
In Unraid's Docker tab, click **Add Container** and fill in:
|
||||
|
||||
**Basic**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `gitea-runner` |
|
||||
| Repository | `registry.alwisp.com/custom-gitea-runner:v0.1.0` |
|
||||
| Network Type | `Bridge` |
|
||||
| Privileged | `No` |
|
||||
| Restart | `Unless Stopped` |
|
||||
|
||||
**Environment variables**
|
||||
|
||||
| Key | Value |
|
||||
|---|---|
|
||||
| `GITEA_INSTANCE_URL` | `https://git.example.com` |
|
||||
| `GITEA_RUNNER_TOKEN` | *(your registration token)* |
|
||||
| `GITEA_RUNNER_NAME` | `unraid-runner-01` |
|
||||
| `GITEA_RUNNER_LABELS` | `ubuntu-latest:docker://node:20-bullseye` |
|
||||
| `TZ` | `America/Chicago` |
|
||||
|
||||
**Volumes**
|
||||
|
||||
| Container path | Host path | Mode |
|
||||
|---|---|---|
|
||||
| `/config` | `/mnt/user/appdata/gitea-runner/config` | Read/Write |
|
||||
| `/work` | `/mnt/user/appdata/gitea-runner/work` | Read/Write |
|
||||
| `/var/run/docker.sock` | `/var/run/docker.sock` | Read/Write |
|
||||
|
||||
Click **Apply**.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Verify startup
|
||||
|
||||
View container logs in the Unraid Docker UI or via terminal:
|
||||
|
||||
```bash
|
||||
docker logs gitea-runner
|
||||
```
|
||||
|
||||
First boot should show `[BOOTSTRAP] Registration complete`.
|
||||
Subsequent starts show `[INFO] Existing config found`.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Verify in Gitea
|
||||
|
||||
Go to Gitea **Site Administration → Runners**. The runner should appear as **Online**.
|
||||
|
||||
---
|
||||
|
||||
## Update procedure
|
||||
|
||||
1. Pull the new image tag to the Unraid host (or use the Community Applications plugin if listed).
|
||||
2. In the Docker UI, edit the container and update the Repository to the new tag.
|
||||
3. Click **Apply** — Unraid stops and recreates the container from the new image.
|
||||
4. Check logs to confirm startup, then test a workflow.
|
||||
|
||||
## Rollback procedure
|
||||
|
||||
Repeat the update procedure but specify the previous known-good tag.
|
||||
|
||||
---
|
||||
|
||||
## Appdata backup
|
||||
|
||||
The `/config` volume (at `/mnt/user/appdata/gitea-runner/config/`) contains the runner registration config. Include it in Unraid's Appdata Backup plugin to preserve registration between host moves.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- The Docker socket mount (`/var/run/docker.sock`) is intentional and required for build/push workflows. On a single-tenant, self-owned Unraid host this is the standard approach.
|
||||
- The `/work` directory grows as jobs run. You can safely clear it when no jobs are active to reclaim space.
|
||||
Reference in New Issue
Block a user