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
|
- idempotent: re-running just reloads the service
|
||||||
|
|
||||||
The Homebrew `act_runner` service is runner #1; `add-host-runner.sh` adds
|
The Homebrew `act_runner` service is runner #1; `add-host-runner.sh` adds
|
||||||
#2, #3, … Three runners × capacity 2 ≈ **6 parallel job slots**. Verified:
|
#2, #3, … Current fleet: 3 host runners × capacity 3 ≈ **9 parallel host
|
||||||
pushing a multi-job workflow lights up all three runners simultaneously.
|
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:
|
List + prune the fleet:
|
||||||
|
|
||||||
|
|||||||
@ -16,8 +16,13 @@
|
|||||||
# - its own launchd plist ~/Library/LaunchAgents/com.bytelyst.act_runner-<N>.plist
|
# - its own launchd plist ~/Library/LaunchAgents/com.bytelyst.act_runner-<N>.plist
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash add-host-runner.sh <N> [capacity] # e.g. add-host-runner.sh 2 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
|
# 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,
|
# 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).
|
# 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.
|
# Idempotent: if runner <N> is already registered it reloads the service and exits.
|
||||||
set -euo pipefail
|
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}"
|
CAP="${2:-2}"
|
||||||
|
MODE="${3:-host}" # host | docker
|
||||||
INSTANCE="${GITEA_INSTANCE:-http://localhost:3300}"
|
INSTANCE="${GITEA_INSTANCE:-http://localhost:3300}"
|
||||||
|
DOCKER_IMAGE="${RUNNER_DOCKER_IMAGE:-catthehacker/ubuntu:act-latest}"
|
||||||
PAT_FILE="${GITEA_PAT_FILE:-$HOME/.gitea_c5_pat}"
|
PAT_FILE="${GITEA_PAT_FILE:-$HOME/.gitea_c5_pat}"
|
||||||
CANONICAL_CONFIG="${CANONICAL_CONFIG:-/opt/homebrew/etc/act_runner/config.yaml}"
|
CANONICAL_CONFIG="${CANONICAL_CONFIG:-/opt/homebrew/etc/act_runner/config.yaml}"
|
||||||
SHARED_ENV_FILE="${SHARED_ENV_FILE:-/opt/homebrew/etc/act_runner/runner.env}"
|
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"
|
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 ───────────────────────────
|
# ── already registered? just (re)load the service ───────────────────────────
|
||||||
if [ -f "$RUNNER_FILE" ]; then
|
if [ -f "$RUNNER_FILE" ]; then
|
||||||
@ -61,16 +79,32 @@ fi
|
|||||||
|
|
||||||
# ── derive a per-runner config from the canonical one ───────────────────────
|
# ── derive a per-runner config from the canonical one ───────────────────────
|
||||||
# Preserve the proxy/env block + env_file; override file path, capacity, workdir.
|
# 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
|
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 = yaml.safe_load(open(src)) or {}
|
||||||
cfg.setdefault("runner", {})
|
cfg.setdefault("runner", {})
|
||||||
cfg["runner"]["file"] = runner_file
|
cfg["runner"]["file"] = runner_file
|
||||||
cfg["runner"]["capacity"] = int(cap)
|
cfg["runner"]["capacity"] = int(cap)
|
||||||
cfg["runner"]["env_file"] = env_file
|
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.setdefault("host", {})
|
||||||
cfg["host"]["workdir_parent"] = workdir
|
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)
|
yaml.safe_dump(cfg, open(dst, "w"), default_flow_style=False, sort_keys=False)
|
||||||
print(f" + wrote {dst}")
|
print(f" + wrote {dst}")
|
||||||
PY
|
PY
|
||||||
@ -81,13 +115,13 @@ REG_TOKEN=$(curl -fsS -H "Authorization: token $PAT" \
|
|||||||
| python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")
|
| python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")
|
||||||
[ -n "$REG_TOKEN" ] || { echo "✗ could not fetch registration token" >&2; exit 1; }
|
[ -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 \
|
act_runner register \
|
||||||
--no-interactive \
|
--no-interactive \
|
||||||
--instance "$INSTANCE" \
|
--instance "$INSTANCE" \
|
||||||
--token "$REG_TOKEN" \
|
--token "$REG_TOKEN" \
|
||||||
--name "$RUNNER_NAME" \
|
--name "$RUNNER_NAME" \
|
||||||
--labels "ubuntu-latest:host,macos-latest:host,macos-15:host,self-hosted:host" \
|
--labels "$LABELS" \
|
||||||
--config "$CONFIG"
|
--config "$CONFIG"
|
||||||
echo " ✓ registered as $RUNNER_NAME"
|
echo " ✓ registered as $RUNNER_NAME"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user