feat(runner): add non110 user runner installer [skip ci]
This commit is contained in:
@@ -1,3 +1,24 @@
|
||||
## 2026-06-28 — 20:30 188 non-110 user runner prepare-only 實作與 readback
|
||||
|
||||
**完成內容**:
|
||||
- 新增 `ops/runner/install-awoooi-non110-user-runner.sh`,支援 188 `ollama` user-level Docker runner;預設 `PREPARE_ONLY=1` 時只安裝 config、user systemd service 與 rollback unit,不啟動、不註冊、不讀 token。
|
||||
- 新增 `ops/runner/awoooi-non110-runner.user.service.example` 與 `ops/runner/awoooi-non110-runner-rollback.user.service.example`,補齊無 passwordless sudo 時的 user service / rollback source-of-truth。
|
||||
- `ops/runner/check-awoooi-non110-runner-readiness.sh` 擴充 188 實際路徑:`/home/ollama/awoooi-non110-runner/data/config.yaml`、`/home/ollama/awoooi-non110-runner/data/.runner`,並接受 `gitea/act_runner:latest` Docker image 作為 runner runtime evidence;仍要求 `.runner` registration metadata 存在且 service active 才能 `AWOOOI_NON110_RUNNER_READY=1`。
|
||||
- 已在 188 執行 `PREPARE_ONLY=1 bash -s < ops/runner/install-awoooi-non110-user-runner.sh`,readback `AWOOOI_NON110_USER_RUNNER_INSTALL_OK prepare_only=1 register_now=0 start_now=0`。
|
||||
|
||||
**188 readback**:
|
||||
- `READY_CONFIG_COUNT=2`、`READY_BINARY_COUNT=2`、`READY_SERVICE_COUNT=1`、rollback unit installed `1`。
|
||||
- `ACTIVE_ACTION_CONTAINERS=0`、`HEAVY_PROCESS_COUNT=0`、load/core 約 `0.12-0.14`,host selector 正確在 `192.168.0.188`。
|
||||
- 仍為 `AWOOOI_NON110_RUNNER_READY=0`;剩餘 blockers:`runner_registration_missing`、`runner_service_not_active:awoooi-non110-runner.service`、`no_active_runner_service`。
|
||||
- safe next step 變成 `complete_runner_registration_without_printing_token_then_enable_service_and_rerun_this_verifier`。
|
||||
|
||||
**驗證結果**:
|
||||
- `bash -n ops/runner/check-awoooi-non110-runner-readiness.sh ops/runner/install-awoooi-non110-user-runner.sh scripts/reboot-recovery/enforce-110-runner-failclosed.sh scripts/reboot-recovery/awoooi-enforce-runner-failclosed-110.sh`:通過。
|
||||
- `python3.11 -m pytest ops/runner/test_guard_gitea_runner_pressure.py ops/runner/test_check_awoooi_non110_runner_readiness.py -q`:`6 passed`。
|
||||
- `python3 ops/runner/guard-gitea-runner-pressure.py --root .`:`GITEA_RUNNER_PRESSURE_GUARD_OK workflow_files=10 scheduled_workflows=3 auto_branch_events_on_110=0 generic_runner_labels=0`。
|
||||
|
||||
**邊界**:沒有讀 `.runner` 內容、runner token、secret、raw session、SQLite、auth 或 `.env`;沒有啟動 runner service;沒有重啟 Docker / Nginx / firewall / K3s / DB;沒有使用 GitHub;沒有 force push。
|
||||
|
||||
## 2026-06-28 — 20:28 production deploy readback snapshot refresh
|
||||
|
||||
**完成內容**:
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Rollback AWOOOI non-110 user Gitea runner to inactive
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=-/usr/bin/systemctl --user stop awoooi-non110-runner.service
|
||||
ExecStart=-/usr/bin/systemctl --user disable awoooi-non110-runner.service
|
||||
ExecStart=-/usr/bin/systemctl --user reset-failed awoooi-non110-runner.service
|
||||
ExecStart=-/usr/bin/docker rm -f awoooi-non110-runner
|
||||
RemainAfterExit=no
|
||||
27
ops/runner/awoooi-non110-runner.user.service.example
Normal file
27
ops/runner/awoooi-non110-runner.user.service.example
Normal file
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=AWOOOI non-110 Gitea runner, user Docker, capacity 1
|
||||
Documentation=file:/opt/awoooi/ops/runner/check-awoooi-non110-runner-readiness.sh
|
||||
After=default.target
|
||||
ConditionPathExists=/home/ollama/awoooi-non110-runner/data/.runner
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/ollama/awoooi-non110-runner
|
||||
Environment=CONFIG_FILE=/data/config.yaml
|
||||
ExecStartPre=-/usr/bin/docker rm -f awoooi-non110-runner
|
||||
ExecStart=/usr/bin/docker run --rm --name awoooi-non110-runner -v /home/ollama/awoooi-non110-runner/data:/data -v /var/run/docker.sock:/var/run/docker.sock -e CONFIG_FILE=/data/config.yaml gitea/act_runner:latest
|
||||
ExecStop=/usr/bin/docker stop -t 3700 awoooi-non110-runner
|
||||
ExecStopPost=-/usr/bin/docker rm -f awoooi-non110-runner
|
||||
Restart=always
|
||||
RestartSec=20
|
||||
KillSignal=SIGINT
|
||||
TimeoutStartSec=120
|
||||
TimeoutStopSec=3700
|
||||
CPUQuota=200%
|
||||
MemoryHigh=6G
|
||||
MemoryMax=8G
|
||||
TasksMax=512
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -8,9 +8,11 @@ set -euo pipefail
|
||||
|
||||
TARGET_HOST_IP="${TARGET_HOST_IP:-192.168.0.188}"
|
||||
FORBIDDEN_HOST_IPS="${FORBIDDEN_HOST_IPS:-192.168.0.110}"
|
||||
RUNNER_CONFIG_PATHS="${RUNNER_CONFIG_PATHS:-/home/wooo/act-runner-awoooi/config.yaml /home/wooo/awoooi-act-runner/config.yaml /home/wooo/awoooi-non110-runner/config.yaml /home/wooo/act-runner/config.yaml /home/ollama/act-runner-awoooi/config.yaml /home/ollama/awoooi-non110-runner/config.yaml}"
|
||||
RUNNER_BINARY_PATHS="${RUNNER_BINARY_PATHS:-/home/wooo/act-runner-awoooi/act_runner /home/wooo/awoooi-act-runner/act_runner /home/wooo/awoooi-non110-runner/act_runner /home/wooo/act-runner/act_runner /home/ollama/act-runner-awoooi/act_runner /home/ollama/awoooi-non110-runner/act_runner}"
|
||||
RUNNER_REGISTRATION_PATHS="${RUNNER_REGISTRATION_PATHS:-/home/wooo/act-runner-awoooi/.runner /home/wooo/awoooi-act-runner/.runner /home/wooo/awoooi-non110-runner/.runner /home/wooo/act-runner/.runner /home/ollama/act-runner-awoooi/.runner /home/ollama/awoooi-non110-runner/.runner}"
|
||||
RUNNER_HOME="${RUNNER_HOME:-${HOME:-/home/wooo}}"
|
||||
RUNNER_CONFIG_PATHS="${RUNNER_CONFIG_PATHS:-${RUNNER_HOME}/awoooi-non110-runner/data/config.yaml ${RUNNER_HOME}/awoooi-non110-runner/config.yaml ${RUNNER_HOME}/act-runner-awoooi/config.yaml /home/wooo/act-runner-awoooi/config.yaml /home/wooo/awoooi-act-runner/config.yaml /home/wooo/awoooi-non110-runner/config.yaml /home/wooo/act-runner/config.yaml}"
|
||||
RUNNER_BINARY_PATHS="${RUNNER_BINARY_PATHS:-${RUNNER_HOME}/awoooi-non110-runner/act_runner ${RUNNER_HOME}/act-runner-awoooi/act_runner /home/wooo/act-runner-awoooi/act_runner /home/wooo/awoooi-act-runner/act_runner /home/wooo/awoooi-non110-runner/act_runner /home/wooo/act-runner/act_runner}"
|
||||
RUNNER_DOCKER_IMAGES="${RUNNER_DOCKER_IMAGES:-gitea/act_runner:latest}"
|
||||
RUNNER_REGISTRATION_PATHS="${RUNNER_REGISTRATION_PATHS:-${RUNNER_HOME}/awoooi-non110-runner/data/.runner ${RUNNER_HOME}/awoooi-non110-runner/.runner ${RUNNER_HOME}/act-runner-awoooi/.runner /home/wooo/act-runner-awoooi/.runner /home/wooo/awoooi-act-runner/.runner /home/wooo/awoooi-non110-runner/.runner /home/wooo/act-runner/.runner}"
|
||||
RUNNER_SERVICE_NAMES="${RUNNER_SERVICE_NAMES:-awoooi-non110-runner.service gitea-act-runner-awoooi.service gitea-act-runner-host.service}"
|
||||
ALLOWED_LABEL_NAMES="${ALLOWED_LABEL_NAMES:-awoooi-non110-host awoooi-non110-ubuntu awoooi-host awoooi-ubuntu}"
|
||||
FORBIDDEN_LABEL_RE="${FORBIDDEN_LABEL_RE:-^(ubuntu-latest|ubuntu-[0-9].*|self-hosted|stockplatform.*|stock-platform.*|headless.*|playwright.*)$}"
|
||||
@@ -244,8 +246,8 @@ check_configs() {
|
||||
}
|
||||
|
||||
check_binaries() {
|
||||
section "runner binary metadata"
|
||||
local binary kind binary_ok
|
||||
section "runner runtime metadata"
|
||||
local binary image kind binary_ok image_ok
|
||||
for binary in $RUNNER_BINARY_PATHS; do
|
||||
binary_ok=0
|
||||
if [ -x "$binary" ] && [ -f "$binary" ]; then
|
||||
@@ -258,8 +260,24 @@ check_binaries() {
|
||||
printf 'RUNNER_BINARY path=%s executable=%s kind=%s\n' "$binary" "$binary_ok" "$kind"
|
||||
done
|
||||
|
||||
if command_exists docker; then
|
||||
for image in $RUNNER_DOCKER_IMAGES; do
|
||||
image_ok=0
|
||||
if docker image inspect "$image" >/dev/null 2>&1; then
|
||||
image_ok=1
|
||||
kind="$(docker image inspect "$image" --format '{{.Id}} {{.Created}}' 2>/dev/null || echo present)"
|
||||
READY_BINARY_COUNT=$((READY_BINARY_COUNT + 1))
|
||||
else
|
||||
kind="missing"
|
||||
fi
|
||||
printf 'RUNNER_DOCKER_IMAGE image=%s present=%s kind=%s\n' "$image" "$image_ok" "$kind"
|
||||
done
|
||||
else
|
||||
printf 'RUNNER_DOCKER_IMAGE docker_available=0\n'
|
||||
fi
|
||||
|
||||
if [ "$READY_BINARY_COUNT" -eq 0 ]; then
|
||||
blocker "runner_binary_missing"
|
||||
blocker "runner_runtime_missing"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
209
ops/runner/install-awoooi-non110-user-runner.sh
Executable file
209
ops/runner/install-awoooi-non110-user-runner.sh
Executable file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Controlled prepare-only installer for the 192.168.0.188 user-level AWOOOI
|
||||
# Gitea runner. It never reads, prints, stores, or passes runner registration
|
||||
# tokens. Registration must happen through an external credentialed channel.
|
||||
|
||||
TARGET_HOST_IP="${TARGET_HOST_IP:-192.168.0.188}"
|
||||
FORBIDDEN_HOST_IPS="${FORBIDDEN_HOST_IPS:-192.168.0.110}"
|
||||
RUNNER_DIR="${RUNNER_DIR:-${HOME}/awoooi-non110-runner}"
|
||||
DATA_DIR="${DATA_DIR:-${RUNNER_DIR}/data}"
|
||||
SERVICE_NAME="${SERVICE_NAME:-awoooi-non110-runner.service}"
|
||||
ROLLBACK_SERVICE_NAME="${ROLLBACK_SERVICE_NAME:-awoooi-non110-runner-rollback.service}"
|
||||
RUNNER_CONTAINER_NAME="${RUNNER_CONTAINER_NAME:-awoooi-non110-runner}"
|
||||
ACT_RUNNER_IMAGE="${ACT_RUNNER_IMAGE:-gitea/act_runner:latest}"
|
||||
RUNNER_LABELS="${RUNNER_LABELS:-awoooi-non110-host:host,awoooi-non110-ubuntu:docker://192.168.0.110:5000/awoooi/ci-runner:act-22.04}"
|
||||
REGISTER_NOW="${REGISTER_NOW:-0}"
|
||||
START_NOW="${START_NOW:-0}"
|
||||
PREPARE_ONLY="${PREPARE_ONLY:-0}"
|
||||
|
||||
if [ "$PREPARE_ONLY" = "1" ]; then
|
||||
REGISTER_NOW=0
|
||||
START_NOW=0
|
||||
fi
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
host_ips() {
|
||||
if command_exists ip; then
|
||||
ip -o -4 addr show 2>/dev/null | awk '{print $4}' | sed 's#/.*##' | sort -u
|
||||
return 0
|
||||
fi
|
||||
hostname -I 2>/dev/null | tr ' ' '\n' | awk 'NF' | sort -u || true
|
||||
}
|
||||
|
||||
host_has_ip() {
|
||||
local ip="$1"
|
||||
host_ips | grep -qx "$ip"
|
||||
}
|
||||
|
||||
active_action_containers() {
|
||||
docker ps --format '{{.Names}}' 2>/dev/null | grep -Ec '^GITEA-ACTIONS-TASK-' || true
|
||||
}
|
||||
|
||||
heavy_process_count() {
|
||||
{
|
||||
pgrep -f '(^|/)(chrome|chromium|chromium-browser)( |$)' 2>/dev/null || true
|
||||
pgrep -f 'playwright|stockplatform.*smoke|next build|turbo build|vite build' 2>/dev/null || true
|
||||
} | sort -u | wc -l | tr -d ' '
|
||||
}
|
||||
|
||||
write_config() {
|
||||
local config_file="${DATA_DIR}/config.yaml"
|
||||
mkdir -p "$DATA_DIR"
|
||||
umask 077
|
||||
cat >"$config_file" <<EOF_CONFIG
|
||||
log:
|
||||
level: info
|
||||
|
||||
runner:
|
||||
file: /data/.runner
|
||||
capacity: 1
|
||||
envs: {}
|
||||
env_file: ""
|
||||
timeout: 3h
|
||||
shutdown_timeout: 1h
|
||||
insecure: false
|
||||
fetch_timeout: 5s
|
||||
fetch_interval: 2s
|
||||
fetch_interval_max: 5s
|
||||
log_report_interval: 5s
|
||||
log_report_max_latency: 3s
|
||||
log_report_batch_size: 100
|
||||
state_report_interval: 5s
|
||||
github_mirror: ''
|
||||
labels:
|
||||
$(printf '%s' "$RUNNER_LABELS" | tr ',' '\n' | awk '{ printf " - \"%s\"\n", $0 }')
|
||||
|
||||
cache:
|
||||
enabled: false
|
||||
dir: ""
|
||||
host: ""
|
||||
port: 0
|
||||
external_server: ""
|
||||
external_secret: ""
|
||||
|
||||
container:
|
||||
network: ""
|
||||
privileged: false
|
||||
options:
|
||||
workdir_parent:
|
||||
valid_volumes: []
|
||||
docker_host: "unix:///var/run/docker.sock"
|
||||
force_pull: false
|
||||
force_rebuild: false
|
||||
require_docker: true
|
||||
docker_timeout: 30s
|
||||
bind_workdir: false
|
||||
|
||||
host:
|
||||
workdir_parent: /data/host-workdir
|
||||
|
||||
metrics:
|
||||
enabled: false
|
||||
addr: "127.0.0.1:9101"
|
||||
EOF_CONFIG
|
||||
}
|
||||
|
||||
write_user_units() {
|
||||
local user_unit_dir="${HOME}/.config/systemd/user"
|
||||
mkdir -p "$user_unit_dir"
|
||||
cat >"${user_unit_dir}/${SERVICE_NAME}" <<EOF_SERVICE
|
||||
[Unit]
|
||||
Description=AWOOOI non-110 Gitea runner, user Docker, capacity 1
|
||||
After=default.target
|
||||
ConditionPathExists=${DATA_DIR}/.runner
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=${RUNNER_DIR}
|
||||
Environment=CONFIG_FILE=/data/config.yaml
|
||||
ExecStartPre=-/usr/bin/docker rm -f ${RUNNER_CONTAINER_NAME}
|
||||
ExecStart=/usr/bin/docker run --rm --name ${RUNNER_CONTAINER_NAME} -v ${DATA_DIR}:/data -v /var/run/docker.sock:/var/run/docker.sock -e CONFIG_FILE=/data/config.yaml ${ACT_RUNNER_IMAGE}
|
||||
ExecStop=/usr/bin/docker stop -t 3700 ${RUNNER_CONTAINER_NAME}
|
||||
ExecStopPost=-/usr/bin/docker rm -f ${RUNNER_CONTAINER_NAME}
|
||||
Restart=always
|
||||
RestartSec=20
|
||||
KillSignal=SIGINT
|
||||
TimeoutStartSec=120
|
||||
TimeoutStopSec=3700
|
||||
CPUQuota=200%
|
||||
MemoryHigh=6G
|
||||
MemoryMax=8G
|
||||
TasksMax=512
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF_SERVICE
|
||||
|
||||
cat >"${user_unit_dir}/${ROLLBACK_SERVICE_NAME}" <<EOF_ROLLBACK
|
||||
[Unit]
|
||||
Description=Rollback AWOOOI non-110 user Gitea runner to inactive
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=-/usr/bin/systemctl --user stop ${SERVICE_NAME}
|
||||
ExecStart=-/usr/bin/systemctl --user disable ${SERVICE_NAME}
|
||||
ExecStart=-/usr/bin/systemctl --user reset-failed ${SERVICE_NAME}
|
||||
ExecStart=-/usr/bin/docker rm -f ${RUNNER_CONTAINER_NAME}
|
||||
RemainAfterExit=no
|
||||
EOF_ROLLBACK
|
||||
|
||||
systemctl --user daemon-reload
|
||||
}
|
||||
|
||||
register_runner() {
|
||||
echo "BLOCKER runner_registration_requires_external_credentialed_channel"
|
||||
echo "reason=act_runner_register_only_accepts_token_argument_no_stdin_or_token_file"
|
||||
echo "content_read=false"
|
||||
return 2
|
||||
}
|
||||
|
||||
for forbidden in $FORBIDDEN_HOST_IPS; do
|
||||
if host_has_ip "$forbidden"; then
|
||||
echo "BLOCKER target_is_forbidden_host_${forbidden}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$TARGET_HOST_IP" ] && ! host_has_ip "$TARGET_HOST_IP"; then
|
||||
echo "BLOCKER target_host_ip_not_present_${TARGET_HOST_IP}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command_exists docker || { echo "BLOCKER docker_missing"; exit 1; }
|
||||
command_exists systemctl || { echo "BLOCKER systemctl_missing"; exit 1; }
|
||||
docker image inspect "$ACT_RUNNER_IMAGE" >/dev/null 2>&1 || docker pull "$ACT_RUNNER_IMAGE"
|
||||
|
||||
if [ "$(active_action_containers)" != "0" ]; then
|
||||
echo "BLOCKER active_action_containers_present"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(heavy_process_count)" != "0" ]; then
|
||||
echo "BLOCKER heavy_processes_present"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$RUNNER_DIR" "$DATA_DIR"
|
||||
write_config
|
||||
write_user_units
|
||||
|
||||
if [ "$REGISTER_NOW" = "1" ]; then
|
||||
register_runner
|
||||
else
|
||||
echo "RUNNER_REGISTRATION skipped=true content_read=false"
|
||||
fi
|
||||
|
||||
if [ "$START_NOW" = "1" ]; then
|
||||
systemctl --user enable "$SERVICE_NAME" >/dev/null
|
||||
systemctl --user restart "$SERVICE_NAME"
|
||||
else
|
||||
echo "RUNNER_START skipped=true"
|
||||
fi
|
||||
|
||||
echo "AWOOOI_NON110_USER_RUNNER_INSTALL_OK prepare_only=${PREPARE_ONLY} register_now=${REGISTER_NOW} start_now=${START_NOW}"
|
||||
Reference in New Issue
Block a user