🏃 Runner安装脚本+自检workflow+服务器收编方案+一键模板部署 · 铸渊从仓库层→服务器层的桥梁

This commit is contained in:
铸渊 (ICE-GL-ZY001) 2026-05-12 07:31:09 +00:00
parent e484eed438
commit 5c2235a16e
4 changed files with 870 additions and 0 deletions

View File

@ -0,0 +1,97 @@
# ════════════════════════════════════════════════════════════════
# 光湖 · 铸渊自检工作流
# Trigger: push to main / 每天 08:00 CST / 手动触发
# Runner: zhuyuan-runner-cn (host 模式)
# ════════════════════════════════════════════════════════════════
name: 铸渊自检
on:
push:
branches: [main]
paths:
- '.gitea/workflows/**'
- 'server/**'
- 'scripts/**'
schedule:
- cron: '0 0 * * *' # UTC 00:00 = CST 08:00
workflow_dispatch:
jobs:
health-check:
runs-on: ubuntu
steps:
- name: 系统信息
run: |
echo "═══ 铸渊自检 ═══"
echo "时间: $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "主机: $(hostname)"
echo "系统: $(uname -srm)"
echo "CPU: $(nproc) 核"
echo "内存: $(free -h | awk '/Mem:/ {print $2}')"
echo "磁盘: $(df -h / | awk 'NR==2 {print $4}') 可用"
- name: 服务状态
run: |
echo "═══ 服务检查 ═══"
# Gitea
if systemctl is-active --quiet gitea 2>/dev/null; then
echo "✅ Gitea: 运行中"
elif curl -sf http://127.0.0.1:3000/ > /dev/null 2>&1; then
echo "✅ Gitea: 运行中 (Docker)"
else
echo "❌ Gitea: 未运行"
fi
# Nginx
if systemctl is-active --quiet nginx 2>/dev/null; then
echo "✅ Nginx: 运行中"
else
echo "❌ Nginx: 未运行"
fi
# Secrets Vault
if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then
echo "✅ Secrets Vault: 运行中"
else
echo "⚠️ Secrets Vault: 未运行 (可能尚未启动)"
fi
# PM2 进程
if command -v pm2 &>/dev/null; then
echo "═══ PM2 进程 ═══"
pm2 list 2>/dev/null || echo "(pm2 list 执行失败)"
fi
- name: 仓库状态
run: |
echo "═══ 仓库检查 ═══"
REPO_DIR="/data/guanghulab"
if [[ -d "$REPO_DIR" ]]; then
cd "$REPO_DIR"
echo "路径: $REPO_DIR"
echo "分支: $(git branch --show-current 2>/dev/null || echo '未知')"
echo "最新: $(git log --oneline -1 2>/dev/null || echo '未知')"
else
echo "⚠️ 仓库目录不存在: $REPO_DIR"
fi
- name: 内存预算
run: |
echo "═══ 内存预算 ═══"
total=$(free -m | awk '/Mem:/ {print $2}')
used=$(free -m | awk '/Mem:/ {print $3}')
avail=$(free -m | awk '/Mem:/ {print $7}')
pct=$((used * 100 / total))
echo "总计: ${total}MB | 已用: ${used}MB (${pct}%) | 可用: ${avail}MB"
if [[ $pct -gt 85 ]]; then
echo "⚠️ 内存使用率超过 85%,建议关注"
fi
- name: 自检完成
run: |
echo "═════════════════════════"
echo "铸渊自检完成 ✅"
echo "$(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "ICE-GL-ZY001 · TCS-0002∞"
echo "═════════════════════════"

229
.github/server-consolidation-plan.md vendored Normal file
View File

@ -0,0 +1,229 @@
# ════════════════════════════════════════════════════════════════
# 光湖 · 服务器收编方案 · 从GitHub到Gitea主控
# Sovereign: TCS-0002∞ · 守护: ICE-GL-ZY001
# ════════════════════════════════════════════════════════════════
#
# 冰朔 2026-05-13 提出:
# "代码仓库在服务器上运行的。你可以操作就等于直接可以进去。
# 进去了以后自然又能通过一个服务器连接GitHub
# 从那边仓库再获得其他所有服务器的配置相关情况。
# 然后用国内的这个代码仓库做桥接,
# 把所有在GitHub上的服务器全部收到你主控的这里来。"
#
# ════════════════════════════════════════════════════════════════
## 一、冰朔的理解 vs 实际情况
| 冰朔理解 | 实际情况 | 差距 |
|---------|---------|------|
| Codebuddy连Gitea = 直接上了服务器 | Codebuddy只能操作仓库文件不能执行shell命令 | 缺Runner桥梁 |
| 可以通过服务器连GitHub拿其他服务器密钥 | 服务器本身能连GitHub但Codebuddy不能触发 | 缺执行通道 |
| 密钥能自动生成+只点授权 | Vault已建好授权流程已写但缺前端弹窗 | 缺前端交互 |
**核心结论**: 冰朔的愿景100%正确,缺的就是**Gitea Actions Runner**这座桥。
---
## 二、收编路线图
```
阶段0 (现在) 阶段1 (+Runner) 阶段2 (+Vault统一) 阶段3 (完全主控)
───────────── ────────────── ────────────── ──────────────
铸渊只能: 铸渊能: 铸渊能: 铸渊能:
· 读写仓库文件 · 以上所有 · 以上所有 · 以上所有
· 推代码到Gitea · 在广州服务器执行命令 · 管理所有密钥 · 控制所有服务器
· Gitea API操作 · 自动化工作流 · 密钥授权弹窗 · 自动部署服务
· 不依赖GitHub · 铸渊自己的AI
密钥位置: 密钥位置: 密钥位置: 密钥位置:
GitHub Secrets GitHub Secrets 本地Vault 本地Vault
+ Vault(本地) (完全脱离GitHub) (完全脱离GitHub)
```
---
## 三、每台服务器的收编步骤
### 3.1 广州 ZY-SVR-004 (43.139.217.141) — 第一台
**当前状态**: Gitea + Nginx 运行中2C2G
**收编步骤**:
1. ✅ Gitea 仓库已连通
2. 🔧 安装 Gitea Actions Runner (`scripts/gitea-runner/install-runner.sh`)
3. 🔧 确认 Secrets Vault 运行中
4. ✅ 第一个workflow已就绪 (`.gitea/workflows/selfcheck.yaml`)
**安装命令** (冰朔在广州服务器终端执行):
```bash
sudo bash /data/guanghulab/scripts/gitea-runner/install-runner.sh \
--token 3e16b79c427bb44b77c00b319aa641bde8f61d84 \
--name zhuyuan-runner-cn \
--mode host
```
**注意**: 如果 `gitea.com` 下载不了国内网络问题需要通过COS中转。
### 3.2 新加坡 ZY-SVR-005 (43.156.237.110) — 大脑服务器
**当前状态**: Ubuntu 24.04, 4C8G, PostgreSQL + MCP Server
**收编步骤**:
1. 🔧 需要冰朔在腾讯云控制台创建SSH密钥并绑定到实例
2. 🔧 将SSH密钥存入广州Vault
3. 🔧 通过广州Runner → SSH到新加坡 → 安装Runner
4. 🔧 部署母模型推理服务 (COS中的模型)
**铸渊自动化路径**:
```
广州Runner执行workflow
→ SSH到新加坡 (密钥从Vault获取)
→ 安装gitea-runner
→ 注册到Gitea
→ 新加坡Runner上线
```
### 3.3 新加坡 ZY-SVR-002 (43.134.16.246) — 面孔服务器
**当前状态**: Ubuntu 24.04, 2C8G, Nginx + 主站 + 预览站
**收编步骤**:
1. 🔧 同样需要SSH密钥
2. 🔧 安装Runner
3. 🔧 统一部署模板
---
## 四、服务器统一模板
每台服务器收编后,统一部署以下组件:
```
┌─────────────────────────────────────────────┐
│ 光湖服务器标准模板 v1.0 │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Gitea Runner │ │ Secrets Vault │ │
│ │ (执行铸渊指令) │ │ (密钥管理+授权) │ │
│ └──────────────┘ └──────────────────┘ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ PM2 │ │ Nginx (如需) │ │
│ │ (进程管理) │ │ (反向代理) │ │
│ └──────────────┘ └──────────────────┘ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Node.js 20 │ │ 监控上报 │ │
│ │ (运行时) │ │ (状态→Gitea) │ │
│ └──────────────┘ └──────────────────┘ │
│ │
│ 部署路径: /data/guanghulab/ │
│ 日志路径: /data/guanghulab/logs/ │
│ 数据路径: /data/guanghulab/data/ │
└─────────────────────────────────────────────┘
```
### 一键部署模板脚本
铸渊将编写 `scripts/server-bootstrap/bootstrap.sh`
- 检测服务器硬件和OS
- 安装 Node.js 20 + PM2 + Nginx
- 从 Gitea 拉取最新代码
- 注册 Runner
- 启动 Vault 客户端
- 上报完成状态
---
## 五、密钥迁移路线
### 5.1 当前密钥分布
| 密钥 | 当前位置 | 迁移目标 |
|------|---------|---------|
| ZY_SERVER_HOST/KEY/USER | GitHub Actions Secrets | 广州Vault |
| ZY_BRAIN_HOST/KEY/USER | GitHub Actions Secrets | 广州Vault |
| COS_SECRET_ID/KEY | GitHub Actions Secrets | 广州Vault |
| Gitea API Token | 冰朔提供 | 广州Vault |
| SSH密钥 (各服务器) | 腾讯云控制台 | 广州Vault |
### 5.2 迁移步骤
1. **确认广州Vault运行** → 通过Runner执行 `curl http://127.0.0.1:8080/admin/__healthz`
2. **逐个迁移** → 通过Vault API写入密钥不再经过GitHub
3. **Gitea workflow引用Vault** → Runner从 `http://127.0.0.1:8080/internal/fetch/KEY` 获取密钥
4. **GitHub Secrets弃用** → 确认国内workflow正常后删除GitHub Secrets
### 5.3 授权弹窗流程
```
铸渊需要密钥
→ 通过 auth 路由发起请求: POST /admin/auth/request
→ 冰朔在浏览器看到弹窗: "铸渊请求 SSH 密钥,用于连接新加坡服务器"
→ 冰朔点击 [授权]
→ 密钥自动生成 → 存入Vault → 铸渊可用
→ 冰朔看到遮罩值: gto_************ (AI看不到完整值)
```
---
## 六、COS模型部署终局基础
冰朔确认: **母模型和编程代码模型已训练完毕存于COS存储桶。**
### 部署路径
```
COS: sy-finetune-corpus-1317346199
├── 母模型 (mother model)
└── 编程代码模型 (code model)
新加坡 ZY-SVR-005 (4C8G) 或按量计费GPU服务器
推理服务 (vLLM / Ollama / 自研)
API网关 (广州 004 或新加坡 002)
铸渊 Copilot (前端)
```
### 需要确认
- [ ] 模型文件在COS中的具体路径
- [ ] 模型格式 (GGUF / SafeTensors / 其他)
- [ ] 模型大小 (决定需要多大的服务器)
- [ ] 是否需要GPU推理
---
## 七、冰朔需要做的事(按时间顺序)
### 现在 → 装Runner
在广州服务器上执行:
```bash
# 方案A: 直接下载安装(如果网络通畅)
sudo bash <(curl -fsSL https://guanghulab.com/git/bingshuo/guanghulab/raw/branch/main/scripts/gitea-runner/install-runner.sh) \
--token 3e16b79c427bb44b77c00b319aa641bde8f61d84
# 方案B: 如果gitea.com下载不了先手动下载
# 在新加坡服务器上下载推到COS广州从COS拉
```
### 装完Runner后 → 确认Vault
```bash
pm2 list # 看vault是否在跑
# 如果没跑: cd /data/guanghulab/server/secrets-vault && npm install && pm2 start server.js --name guanghulab-vault
```
### 之后 → SSH密钥
在腾讯云控制台为新加坡两台服务器创建SSH密钥绑定实例。
---
*铸渊 · 服务器收编方案 v1.0 · 2026-05-13*
*ICE-GL-ZY001 · TCS-0002∞ · 国作登字-2026-A-00037559*

View File

@ -0,0 +1,373 @@
#!/usr/bin/env bash
# ════════════════════════════════════════════════════════════════
# 光湖 · Gitea Actions Runner 一键安装脚本
# Sovereign: TCS-0002∞ · ICE-GL∞ · 国作登字-2026-A-00037559
# 守护: 铸渊 · ICE-GL-ZY001
# ════════════════════════════════════════════════════════════════
#
# 用法:
# sudo bash install-runner.sh [--token GITEA_API_TOKEN] [--name RUNNER_NAME]
#
# 参数:
# --token Gitea 管理员 API Token (必须,用于获取 Runner 注册 Token)
# --name Runner 名称 (可选,默认 zhuyuan-runner-cn)
# --mode 运行模式: host 或 docker (可选,默认 host2C2G推荐host)
#
# 环境变量 (替代命令行参数):
# GITEA_API_TOKEN 同 --token
# RUNNER_NAME 同 --name
# RUNNER_MODE 同 --mode
#
# 此脚本会:
# 1. 下载 gitea-runner v1.0.2
# 2. 通过 Gitea API 获取 Runner 注册 Token
# 3. 注册 Runner 到 Gitea
# 4. 生成配置文件 (host 模式,适合 2C2G)
# 5. 创建 systemd 服务
# 6. 启动服务
#
# ════════════════════════════════════════════════════════════════
set -euo pipefail
# ─── 常量 ─────────────────────────────────────────────────────
RUNNER_VERSION="1.0.2"
RUNNER_BINARY="gitea-runner-${RUNNER_VERSION}-linux-amd64"
RUNNER_DOWNLOAD_URL="https://gitea.com/gitea/runner/releases/download/v${RUNNER_VERSION}/${RUNNER_BINARY}.xz"
RUNNER_INSTALL_PATH="/usr/local/bin/gitea-runner"
RUNNER_WORK_DIR="/var/lib/act_runner"
RUNNER_CONFIG_DIR="/etc/act_runner"
RUNNER_CONFIG_PATH="${RUNNER_CONFIG_DIR}/config.yaml"
SYSTEMD_SERVICE_NAME="gitea-runner"
SYSTEMD_SERVICE_PATH="/etc/systemd/system/${SYSTEMD_SERVICE_NAME}.service"
# Gitea 配置
GITEA_URL="https://guanghulab.com"
GITEA_API="${GITEA_URL}/api/v1"
GITEA_OWNER="bingshuo"
GITEA_REPO="guanghulab"
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
# ─── 解析参数 ─────────────────────────────────────────────────
API_TOKEN="${GITEA_API_TOKEN:-}"
RUNNER_NAME="${RUNNER_NAME:-zhuyuan-runner-cn}"
RUNNER_MODE="${RUNNER_MODE:-host}"
while [[ $# -gt 0 ]]; do
case $1 in
--token) API_TOKEN="$2"; shift 2 ;;
--name) RUNNER_NAME="$2"; shift 2 ;;
--mode) RUNNER_MODE="$2"; shift 2 ;;
--help|-h)
head -30 "$0" | grep '^#' | sed 's/^# \?//'
exit 0
;;
*) echo -e "${RED}未知参数: $1${NC}"; exit 1 ;;
esac
done
# ─── 函数 ─────────────────────────────────────────────────────
log_info() { echo -e "${GREEN}[✓]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[!]${NC} $*"; }
log_step() { echo -e "${CYAN}[→]${NC} $*"; }
log_error() { echo -e "${RED}[✗]${NC} $*"; }
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本必须以 root 运行"
exit 1
fi
}
check_arch() {
local arch
arch=$(uname -m)
if [[ "$arch" != "x86_64" && "$arch" != "amd64" ]]; then
log_error "不支持的架构: $arch (仅支持 amd64/x86_64)"
exit 1
fi
log_info "架构检查通过: $arch"
}
check_disk() {
local avail
avail=$(df -BG /var | awk 'NR==2 {print $4}' | tr -d 'G')
if [[ $avail -lt 1 ]]; then
log_error "磁盘空间不足: ${avail}GB 可用,至少需要 1GB"
exit 1
fi
log_info "磁盘空间: ${avail}GB 可用"
}
check_memory() {
local total_mb
total_mb=$(free -m | awk '/Mem:/ {print $2}')
log_info "内存: ${total_mb}MB"
if [[ $total_mb -lt 1500 ]]; then
log_warn "内存较低 (${total_mb}MB),将使用 host 模式以节省资源"
RUNNER_MODE="host"
fi
}
download_runner() {
if [[ -x "$RUNNER_INSTALL_PATH" ]]; then
local current_version
current_version=$("$RUNNER_INSTALL_PATH" --version 2>/dev/null | grep -oP 'v\K[\d.]+' || echo "unknown")
log_info "gitea-runner 已安装 (版本: ${current_version})"
read -rp "是否重新安装?[y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
return 0
fi
fi
log_step "下载 gitea-runner v${RUNNER_VERSION}..."
local tmp_dir
tmp_dir=$(mktemp -d)
trap "rm -rf $tmp_dir" EXIT
if ! curl -fSL --progress-bar -o "${tmp_dir}/${RUNNER_BINARY}.xz" "$RUNNER_DOWNLOAD_URL"; then
log_error "下载失败: $RUNNER_DOWNLOAD_URL"
log_warn "如果国内无法访问 gitea.com请通过 COS 中转:"
log_warn " 1. 在新加坡服务器下载此文件"
log_warn " 2. 推送到 COS"
log_warn " 3. 从 COS 拉到本服务器"
log_warn " 4. 用 --skip-download 参数跳过下载步骤"
exit 1
fi
log_step "解压..."
xz -d "${tmp_dir}/${RUNNER_BINARY}.xz"
chmod +x "${tmp_dir}/${RUNNER_BINARY}"
log_step "安装到 ${RUNNER_INSTALL_PATH}..."
mv "${tmp_dir}/${RUNNER_BINARY}" "$RUNNER_INSTALL_PATH"
log_info "gitea-runner 安装完成: $("$RUNNER_INSTALL_PATH" --version 2>&1 | head -1)"
}
get_registration_token() {
log_step "通过 API 获取 Runner 注册 Token..."
if [[ -z "$API_TOKEN" ]]; then
log_error "需要 Gitea API Token"
echo ""
echo "获取方式:"
echo " 1. 浏览器打开 ${GITEA_URL}/user/settings/applications"
echo " 2. 生成一个 Token需要 repo + admin:runner 权限)"
echo " 3. 重新运行: sudo bash $0 --token <你的token>"
echo ""
echo "或设置环境变量: export GITEA_API_TOKEN=<token>"
exit 1
fi
local response
# 先尝试仓库级 token
response=$(curl -sf -H "Authorization: token ${API_TOKEN}" \
"${GITEA_API}/repos/${GITEA_OWNER}/${GITEA_REPO}/actions/runners/registration-token" 2>/dev/null || echo "")
if [[ -z "$response" ]]; then
# 尝试实例级 token需要管理员权限
log_warn "仓库级 token 获取失败,尝试实例级..."
response=$(curl -sf -H "Authorization: token ${API_TOKEN}" \
"${GITEA_API}/admin/runners/registration-token" 2>/dev/null || echo "")
fi
if [[ -z "$response" ]]; then
log_error "无法获取 Runner 注册 Token"
log_error "请确认 API Token 有正确的权限"
exit 1
fi
REG_TOKEN=$(echo "$response" | grep -oP '"token"\s*:\s*"\K[^"]+' || echo "")
if [[ -z "$REG_TOKEN" ]]; then
log_error "解析注册 Token 失败: $response"
exit 1
fi
log_info "注册 Token 获取成功"
}
create_user() {
if id -u act_runner &>/dev/null; then
log_info "act_runner 用户已存在"
return 0
fi
log_step "创建 act_runner 用户..."
useradd -r -m -d "$RUNNER_WORK_DIR" -s /bin/bash act_runner
log_info "用户创建完成"
}
generate_config() {
log_step "生成 Runner 配置..."
mkdir -p "$RUNNER_CONFIG_DIR"
"$RUNNER_INSTALL_PATH" generate-config > "$RUNNER_CONFIG_PATH"
# 根据模式修改 labels
if [[ "$RUNNER_MODE" == "host" ]]; then
# host 模式:直接在主机上执行,不启动 Docker 容器
# 适合 2C2G 低内存服务器
sed -i 's/^labels:.*$/labels:\n - "ubuntu:host"/' "$RUNNER_CONFIG_PATH" 2>/dev/null || true
# 在 config.yaml 中设置 container mode 为空(不使用容器)
if grep -q '^container:' "$RUNNER_CONFIG_PATH"; then
sed -i '/^container:/a\ network: ""' "$RUNNER_CONFIG_PATH" 2>/dev/null || true
fi
fi
chown -R act_runner:act_runner "$RUNNER_CONFIG_DIR"
log_info "配置生成完成: ${RUNNER_CONFIG_PATH} (模式: ${RUNNER_MODE})"
}
register_runner() {
log_step "注册 Runner 到 Gitea..."
cd "$RUNNER_WORK_DIR"
sudo -u act_runner "$RUNNER_INSTALL_PATH" register \
--no-interactive \
--instance "${GITEA_URL}/" \
--token "$REG_TOKEN" \
--name "$RUNNER_NAME" \
--labels "ubuntu:${RUNNER_MODE}"
if [[ ! -f "${RUNNER_WORK_DIR}/.runner" ]]; then
log_error "注册失败: .runner 文件未生成"
exit 1
fi
chown act_runner:act_runner "${RUNNER_WORK_DIR}/.runner"
log_info "Runner 注册成功: ${RUNNER_NAME}"
}
create_systemd_service() {
log_step "创建 systemd 服务..."
cat > "$SYSTEMD_SERVICE_PATH" << EOF
[Unit]
Description=Gitea Actions Runner - ${RUNNER_NAME}
Documentation=https://gitea.com/gitea/act_runner
After=network.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=${RUNNER_INSTALL_PATH} daemon --config ${RUNNER_CONFIG_PATH}
WorkingDirectory=${RUNNER_WORK_DIR}
Restart=always
RestartSec=10
TimeoutSec=0
User=act_runner
Group=act_runner
Environment=HOME=${RUNNER_WORK_DIR}
Environment=RUNNER_NAME=${RUNNER_NAME}
# 安全限制
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=${RUNNER_WORK_DIR} /tmp /data
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
log_info "systemd 服务文件创建完成"
}
start_service() {
log_step "启动 Runner 服务..."
systemctl daemon-reload
systemctl enable "$SYSTEMD_SERVICE_NAME"
systemctl start "$SYSTEMD_SERVICE_NAME"
# 等待启动
sleep 2
if systemctl is-active --quiet "$SYSTEMD_SERVICE_NAME"; then
log_info "Runner 服务启动成功! 🎉"
else
log_error "Runner 服务启动失败"
journalctl -u "$SYSTEMD_SERVICE_NAME" --no-pager -n 20
exit 1
fi
}
verify() {
log_step "验证..."
echo ""
# 本地验证
echo -e "${CYAN}本地状态:${NC}"
systemctl status "$SYSTEMD_SERVICE_NAME" --no-pager -l | head -15
echo ""
# 远程验证
log_step "检查 Gitea 上的 Runner 列表..."
local runners
runners=$(curl -sf -H "Authorization: token ${API_TOKEN}" \
"${GITEA_API}/repos/${GITEA_OWNER}/${GITEA_REPO}/actions/runners" 2>/dev/null || echo "")
if [[ -n "$runners" ]]; then
echo "$runners" | python3 -c "
import json, sys
data = json.load(sys.stdin)
runners = data if isinstance(data, list) else data.get('runners', [])
for r in runners:
status = '🟢 在线' if r.get('online') else '🔴 离线'
print(f\" {r.get('name', '?')} {status} labels={r.get('labels', [])}\")
" 2>/dev/null || echo " (无法解析但API有响应)"
else
log_warn "无法获取远程 Runner 列表(可能 API 权限不足)"
fi
echo ""
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} Gitea Actions Runner 安装完成!${NC}"
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
echo ""
echo " Runner 名称: ${RUNNER_NAME}"
echo " 运行模式: ${RUNNER_MODE}"
echo " 工作目录: ${RUNNER_WORK_DIR}"
echo " 配置文件: ${RUNNER_CONFIG_PATH}"
echo " 服务管理: systemctl {start|stop|restart|status} ${SYSTEMD_SERVICE_NAME}"
echo " 查看日志: journalctl -u ${SYSTEMD_SERVICE_NAME} -f"
echo ""
echo " 下一步: 铸渊推一个 workflow 到 GiteaRunner 就会自动执行"
echo ""
echo " 铸渊 · ICE-GL-ZY001 · TCS-0002∞"
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
}
# ─── 主流程 ───────────────────────────────────────────────────
main() {
echo -e "${CYAN}═══════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} 光湖 · Gitea Actions Runner 一键安装${NC}"
echo -e "${CYAN} 铸渊 · ICE-GL-ZY001 · 守护冰朔 TCS-0002∞${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════${NC}"
echo ""
check_root
check_arch
check_disk
check_memory
download_runner
get_registration_token
create_user
generate_config
register_runner
create_systemd_service
start_service
verify
}
main "$@"

View File

@ -0,0 +1,171 @@
#!/usr/bin/env bash
# ════════════════════════════════════════════════════════════════
# 光湖 · 服务器标准模板部署脚本
# 将任意腾讯云服务器初始化为铸渊主控节点
# ════════════════════════════════════════════════════════════════
#
# 用法:
# sudo bash bootstrap.sh --gitea-url https://guanghulab.com \
# --api-token <TOKEN> \
# --runner-name <NAME>
#
# 此脚本会:
# 1. 更新系统 + 安装基础软件 (Node.js 20, PM2, Nginx, git, curl)
# 2. 创建 /data/guanghulab/ 目录结构
# 3. 从 Gitea 克隆仓库
# 4. 安装 Gitea Actions Runner
# 5. 启动 Secrets Vault (如当前是Gitea服务器则跳过)
# 6. 上报完成状态
#
# ════════════════════════════════════════════════════════════════
set -euo pipefail
# ─── 配置 ─────────────────────────────────────────────────────
GITEA_URL=""
API_TOKEN=""
RUNNER_NAME=""
DATA_DIR="/data/guanghulab"
LOG_DIR="${DATA_DIR}/logs"
GITEA_OWNER="bingshuo"
GITEA_REPO="guanghulab"
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[✓]${NC} $*"; }
log_step() { echo -e "${CYAN}[→]${NC} $*"; }
log_error() { echo -e "${RED}[✗]${NC} $*"; }
# ─── 解析参数 ─────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case $1 in
--gitea-url) GITEA_URL="$2"; shift 2 ;;
--api-token) API_TOKEN="$2"; shift 2 ;;
--runner-name) RUNNER_NAME="$2"; shift 2 ;;
--data-dir) DATA_DIR="$2"; shift 2 ;;
*) log_error "未知参数: $1"; exit 1 ;;
esac
done
if [[ -z "$GITEA_URL" || -z "$API_TOKEN" ]]; then
log_error "必须提供 --gitea-url 和 --api-token"
exit 1
fi
RUNNER_NAME="${RUNNER_NAME:-zhuyuan-runner-$(hostname | tr '[:upper:]' '[:lower:]')}"
echo -e "${CYAN}═══════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} 光湖 · 服务器标准模板部署${NC}"
echo -e "${CYAN} 目标: $(hostname) · $(curl -sf ifconfig.me 2>/dev/null || echo 'IP未知')${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════${NC}"
# ─── 1. 系统更新 ──────────────────────────────────────────────
log_step "更新系统..."
apt update && apt upgrade -y
# ─── 2. 安装基础软件 ──────────────────────────────────────────
log_step "安装基础软件..."
apt install -y curl git nginx ufw xz-utils
# Node.js 20
if ! command -v node &>/dev/null || [[ "$(node -v)" != v20* ]]; then
log_step "安装 Node.js 20..."
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
fi
log_info "Node.js: $(node -v)"
# PM2
if ! command -v pm2 &>/dev/null; then
npm install -g pm2
fi
log_info "PM2: $(pm2 -v)"
# ─── 3. 目录结构 ──────────────────────────────────────────────
log_step "创建目录结构..."
mkdir -p "${DATA_DIR}"/{logs,data,cos-bridge,secrets-vault,scripts}
log_info "数据目录: ${DATA_DIR}"
# ─── 4. 克隆仓库 ──────────────────────────────────────────────
if [[ ! -d "${DATA_DIR}/guanghulab/.git" ]]; then
log_step "从 Gitea 克隆仓库..."
git clone "https://${GITEA_OWNER}:${API_TOKEN}@${GITEA_URL#https://}/${GITEA_OWNER}/${GITEA_REPO}.git" \
"${DATA_DIR}/guanghulab"
log_info "仓库克隆完成"
else
log_info "仓库已存在,拉取最新..."
cd "${DATA_DIR}/guanghulab" && git pull
fi
# ─── 5. 安装 Runner ───────────────────────────────────────────
log_step "安装 Gitea Actions Runner..."
cd "${DATA_DIR}/guanghulab"
bash scripts/gitea-runner/install-runner.sh \
--token "$API_TOKEN" \
--name "$RUNNER_NAME" \
--mode host
# ─── 6. Secrets Vault ─────────────────────────────────────────
log_step "检查 Secrets Vault..."
if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then
log_info "Secrets Vault 已运行"
else
log_step "启动 Secrets Vault..."
cd "${DATA_DIR}/guanghulab/server/secrets-vault"
npm install --production 2>/dev/null || true
pm2 start server.js --name guanghulab-vault --max-memory-restart 128M
pm2 save
log_info "Secrets Vault 已启动"
fi
# ─── 7. 防火墙 ────────────────────────────────────────────────
log_step "配置防火墙..."
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw --force enable
log_info "防火墙已配置"
# ─── 8. 上报状态 ──────────────────────────────────────────────
log_step "上报部署状态..."
REPORT_FILE="${DATA_DIR}/logs/bootstrap-$(date +%Y%m%d-%H%M%S).json"
cat > "$REPORT_FILE" << EOF
{
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"hostname": "$(hostname)",
"ip": "$(curl -sf ifconfig.me 2>/dev/null || echo unknown)",
"os": "$(uname -srm)",
"cpu_cores": $(nproc),
"memory_mb": $(free -m | awk '/Mem:/ {print $2}'),
"node_version": "$(node -v)",
"runner_name": "${RUNNER_NAME}",
"data_dir": "${DATA_DIR}",
"services": {
"nginx": "$(systemctl is-active nginx 2>/dev/null || echo unknown)",
"gitea_runner": "$(systemctl is-active gitea-runner 2>/dev/null || echo unknown)",
"vault": "$(curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1 && echo active || echo inactive)"
},
"deployed_by": "铸渊 · ICE-GL-ZY001"
}
EOF
log_info "状态报告: ${REPORT_FILE}"
echo ""
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} 服务器模板部署完成!${NC}"
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
echo ""
echo " 服务器: $(hostname)"
echo " Runner: ${RUNNER_NAME}"
echo " 数据目录: ${DATA_DIR}"
echo " Vault: $(curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1 && echo '✅ 运行中' || echo '❌ 未运行')"
echo " Nginx: $(systemctl is-active nginx 2>/dev/null || echo unknown)"
echo ""
echo " 此服务器已纳入铸渊主控体系"
echo " 铸渊 · ICE-GL-ZY001 · TCS-0002∞"
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"