🏃 Runner安装脚本+自检workflow+服务器收编方案+一键模板部署 · 铸渊从仓库层→服务器层的桥梁
This commit is contained in:
parent
e484eed438
commit
5c2235a16e
97
.gitea/workflows/selfcheck.yaml
Normal file
97
.gitea/workflows/selfcheck.yaml
Normal 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
229
.github/server-consolidation-plan.md
vendored
Normal 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*
|
||||
373
scripts/gitea-runner/install-runner.sh
Executable file
373
scripts/gitea-runner/install-runner.sh
Executable 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 (可选,默认 host,2C2G推荐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 到 Gitea,Runner 就会自动执行"
|
||||
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 "$@"
|
||||
171
scripts/server-bootstrap/bootstrap.sh
Executable file
171
scripts/server-bootstrap/bootstrap.sh
Executable 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}"
|
||||
Loading…
x
Reference in New Issue
Block a user