feat(gitea): docker-mode support in add-host-runner.sh + capacity guidance
- add-host-runner.sh: optional [mode] arg (host|docker); docker mode sets dedicated 'docker' label, container.docker_host/force_pull/options, and appends host.docker.internal to NO_PROXY so containerized jobs reach the host Gitea through the corp proxy (avoids HTTP 504) - GITEA_VM_SETUP.md 11.5: docker-mode runner setup + proxy-bypass caveat; fleet now 3 host runners x capacity 3 + 1 docker runner Validated: runs-on: docker job runs in Ubuntu 24.04 container and reaches Gitea /api/v1/version.
This commit is contained in:
parent
0e89dafa43
commit
6381cabe68
@ -445,8 +445,36 @@ bash scripts/gitea/add-host-runner.sh 3 2
|
||||
- idempotent: re-running just reloads the service
|
||||
|
||||
The Homebrew `act_runner` service is runner #1; `add-host-runner.sh` adds
|
||||
#2, #3, … Three runners × capacity 2 ≈ **6 parallel job slots**. Verified:
|
||||
pushing a multi-job workflow lights up all three runners simultaneously.
|
||||
#2, #3, … Current fleet: 3 host runners × capacity 3 ≈ **9 parallel host
|
||||
slots**. Verified: pushing a multi-job workflow distributes jobs across all
|
||||
three runners simultaneously.
|
||||
|
||||
**Add a docker-mode runner (stronger isolation):**
|
||||
|
||||
```bash
|
||||
# Pull the act image once (≈2.3 GB; works through the corp proxy):
|
||||
docker pull catthehacker/ubuntu:act-latest
|
||||
|
||||
# Stand up runner #4 in docker mode (capacity 1):
|
||||
bash scripts/gitea/add-host-runner.sh 4 1 docker
|
||||
```
|
||||
|
||||
Docker mode advertises a dedicated **`docker`** label (not `ubuntu-latest`),
|
||||
so it does not hijack the host-mode `ubuntu-latest` jobs. Opt a job in with
|
||||
`runs-on: docker`. The generated config:
|
||||
|
||||
- sets `runner.labels` to `docker:docker://<image>` (act_runner reads labels
|
||||
from the config file, **not** `register --labels`)
|
||||
- `container.docker_host: "-"`, `force_pull: false` (use the locally-pulled
|
||||
image), `options: --add-host=host.docker.internal:host-gateway`
|
||||
- **adds `host.docker.internal` to `NO_PROXY`/`no_proxy`** — without this,
|
||||
containerized jobs inherit the corp proxy env and route
|
||||
`host.docker.internal:3300` through the proxy, getting an HTTP 504. Jobs must
|
||||
reach Gitea via `host.docker.internal:3300` (not `localhost`) from inside the
|
||||
container.
|
||||
|
||||
Validated end-to-end: a `runs-on: docker` job runs in an `Ubuntu 24.04`
|
||||
container and reaches Gitea (`GET /api/v1/version` → `{"version":"…"}`).
|
||||
|
||||
List + prune the fleet:
|
||||
|
||||
|
||||
@ -16,8 +16,13 @@
|
||||
# - its own launchd plist ~/Library/LaunchAgents/com.bytelyst.act_runner-<N>.plist
|
||||
#
|
||||
# Usage:
|
||||
# bash add-host-runner.sh <N> [capacity] # e.g. add-host-runner.sh 2 2
|
||||
# bash add-host-runner.sh 2 2 && bash add-host-runner.sh 3 2
|
||||
# bash add-host-runner.sh <N> [capacity] [mode] # mode: host (default) | docker
|
||||
# bash add-host-runner.sh 2 2 && bash add-host-runner.sh 3 2 # two host runners
|
||||
# bash add-host-runner.sh 4 1 docker # a docker-mode runner
|
||||
#
|
||||
# Docker mode advertises a dedicated `docker` label (not `ubuntu-latest`) so it
|
||||
# does not hijack existing host-mode jobs. Target it with `runs-on: docker` and
|
||||
# reach Gitea from inside the job container via host.docker.internal:3300.
|
||||
#
|
||||
# Requires: act_runner on PATH, a Gitea admin PAT at ~/.gitea_c5_pat,
|
||||
# and the canonical runner.env to already exist (created during runner hardening).
|
||||
@ -25,9 +30,11 @@
|
||||
# Idempotent: if runner <N> is already registered it reloads the service and exits.
|
||||
set -euo pipefail
|
||||
|
||||
N="${1:?usage: add-host-runner.sh <N> [capacity]}"
|
||||
N="${1:?usage: add-host-runner.sh <N> [capacity] [mode] (mode: host|docker)}"
|
||||
CAP="${2:-2}"
|
||||
MODE="${3:-host}" # host | docker
|
||||
INSTANCE="${GITEA_INSTANCE:-http://localhost:3300}"
|
||||
DOCKER_IMAGE="${RUNNER_DOCKER_IMAGE:-catthehacker/ubuntu:act-latest}"
|
||||
PAT_FILE="${GITEA_PAT_FILE:-$HOME/.gitea_c5_pat}"
|
||||
CANONICAL_CONFIG="${CANONICAL_CONFIG:-/opt/homebrew/etc/act_runner/config.yaml}"
|
||||
SHARED_ENV_FILE="${SHARED_ENV_FILE:-/opt/homebrew/etc/act_runner/runner.env}"
|
||||
@ -48,7 +55,18 @@ PAT="$(cat "$PAT_FILE")"
|
||||
|
||||
mkdir -p "$BASE" "$WORKDIR" "$LOG_DIR"
|
||||
|
||||
echo "── add host runner #$N (capacity $CAP) ──"
|
||||
echo "── add $MODE runner #$N (capacity $CAP) ──"
|
||||
[ "$MODE" = "docker" ] && echo " image : $DOCKER_IMAGE"
|
||||
|
||||
# ── labels per mode (act_runner uses config-file labels, not --labels) ──────
|
||||
if [ "$MODE" = "docker" ]; then
|
||||
# Dedicated `docker` label so existing `runs-on: ubuntu-latest` jobs (which
|
||||
# assume host mode + localhost Gitea) are NOT hijacked. Opt in with
|
||||
# `runs-on: docker` and reach Gitea via host.docker.internal.
|
||||
LABELS="docker:docker://$DOCKER_IMAGE,ubuntu-docker:docker://$DOCKER_IMAGE,self-hosted:host"
|
||||
else
|
||||
LABELS="ubuntu-latest:host,macos-latest:host,macos-15:host,self-hosted:host"
|
||||
fi
|
||||
|
||||
# ── already registered? just (re)load the service ───────────────────────────
|
||||
if [ -f "$RUNNER_FILE" ]; then
|
||||
@ -61,16 +79,32 @@ fi
|
||||
|
||||
# ── derive a per-runner config from the canonical one ───────────────────────
|
||||
# Preserve the proxy/env block + env_file; override file path, capacity, workdir.
|
||||
python3 - "$CANONICAL_CONFIG" "$CONFIG" "$RUNNER_FILE" "$CAP" "$WORKDIR" "$SHARED_ENV_FILE" <<'PY'
|
||||
python3 - "$CANONICAL_CONFIG" "$CONFIG" "$RUNNER_FILE" "$CAP" "$WORKDIR" "$SHARED_ENV_FILE" "$MODE" "$LABELS" <<'PY'
|
||||
import sys, yaml
|
||||
src, dst, runner_file, cap, workdir, env_file = sys.argv[1:7]
|
||||
src, dst, runner_file, cap, workdir, env_file, mode, labels = sys.argv[1:9]
|
||||
cfg = yaml.safe_load(open(src)) or {}
|
||||
cfg.setdefault("runner", {})
|
||||
cfg["runner"]["file"] = runner_file
|
||||
cfg["runner"]["capacity"] = int(cap)
|
||||
cfg["runner"]["env_file"] = env_file
|
||||
# act_runner reads labels from the config file (it ignores `register --labels`).
|
||||
cfg["runner"]["labels"] = labels.split(",")
|
||||
cfg.setdefault("host", {})
|
||||
cfg["host"]["workdir_parent"] = workdir
|
||||
if mode == "docker":
|
||||
c = cfg.setdefault("container", {})
|
||||
c["docker_host"] = "-" # auto-detect host docker daemon
|
||||
c["force_pull"] = False # use locally-pulled image (corp proxy)
|
||||
c["privileged"] = False
|
||||
# Let job containers reach the host's Gitea on Docker Desktop.
|
||||
c["options"] = "--add-host=host.docker.internal:host-gateway"
|
||||
# Containerized jobs inherit the corp proxy env; without this they route
|
||||
# host.docker.internal:3300 through the proxy and get a 504. Bypass it.
|
||||
envs = cfg["runner"].setdefault("envs", {})
|
||||
for k in ("NO_PROXY", "no_proxy", "NPM_CONFIG_NOPROXY"):
|
||||
v = envs.get(k, "")
|
||||
if "host.docker.internal" not in v:
|
||||
envs[k] = (v + "," if v else "") + "host.docker.internal"
|
||||
yaml.safe_dump(cfg, open(dst, "w"), default_flow_style=False, sort_keys=False)
|
||||
print(f" + wrote {dst}")
|
||||
PY
|
||||
@ -81,13 +115,13 @@ REG_TOKEN=$(curl -fsS -H "Authorization: token $PAT" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")
|
||||
[ -n "$REG_TOKEN" ] || { echo "✗ could not fetch registration token" >&2; exit 1; }
|
||||
|
||||
# ── register (host-mode labels) ─────────────────────────────────────────────
|
||||
# ── register (labels come from config file; --labels is informational) ──────
|
||||
act_runner register \
|
||||
--no-interactive \
|
||||
--instance "$INSTANCE" \
|
||||
--token "$REG_TOKEN" \
|
||||
--name "$RUNNER_NAME" \
|
||||
--labels "ubuntu-latest:host,macos-latest:host,macos-15:host,self-hosted:host" \
|
||||
--labels "$LABELS" \
|
||||
--config "$CONFIG"
|
||||
echo " ✓ registered as $RUNNER_NAME"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user