chore: 砍COS改造(deploy.yml改artifact传递+gitea-backup可移植部署包+改造步骤文档)

This commit is contained in:
zqlit committed 2026-10-04 09:52:50 +08:00
1 parent a74b3c7127
commit 2388694e8a
8 files changed
+729 -147

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
# Gitea 部署包 —— 忽略规则
# 环境变量(含密钥)
.env
# 数据卷(迁移时单独打包,不进 git)
data/
# write-server 运行时产物(若纳入)
*.log
+130
View File
@@ -0,0 +1,130 @@
# Gitea 可移植部署包
把博客相关的**代码托管(Gitea)+ CI 执行器(act_runner)+ 又拍云同步**打包成一套 docker-compose,
目标:**任何一台机器上,复制文件 + 一条命令就能拉起整套环境**,彻底告别「迁移要半天手工操作」。
> 背景:境外那台 2C2G 服务器(23.254.236.47)十一月到期,且后续可能没时间慢慢迁移。
> 这个部署包就是把「迁移成本」从「半天手工」压到「copy + up」。
---
## 包含哪些服务
| 服务 | 镜像 | 说明 |
|---|---|---|
| **gitea** | `gitea/gitea:1.28.0-rootless` | 代码托管 + CI 调度。SQLite 单文件,数据全在 `data/gitea/` |
| **runner** | `gitea/act_runner:0.2.11` | Gitea Actions 执行器,跑博客构建/部署 |
| **upyun-sync** | `alpine:3.20` | 又拍云同步(原 1Panel 计划任务「又拍云同步(国内中转)」,容器化后不再依赖面板) |
| write-server | (可选,默认注释) | 写作后台,源码在仓库根 `write-server/` |
**不在本部署包里的**(它们有自己的形态,不适合塞进 compose):
| 服务 | 形态 | 为什么不在 |
|---|---|---|
| 博客站点 | 静态文件 + CDN | 构建产物由 runner 推给 EdgeOne / 又拍云,不是常驻服务 |
| 评论后端 artalk-cf | Cloudflare Workers | 无服务器,`wrangler deploy` 部署,见 `blog-admin/` |
| write-server | 独立 Docker | 可选纳入,见 compose 注释 |
---
## 目录结构
```
gitea-backup/
├── docker-compose.yml # 核心:三个服务 + 网络
├── .env.example # 变量模板(复制成 .env)
├── .gitignore # 忽略 .env 和 data/
├── README.md # 本文档
├── scripts/
│ ├── backup.sh # 一键备份:gitea dump + 打包 data 卷
│ └── restore.sh # 一键恢复
└── data/ # 数据卷(git 忽略,迁移时打包)
├── gitea/ # Gitea 数据(gitea.db + app.ini + 仓库)
├── runner/ # act_runner 配置
└── upyun-sync/ # 同步脚本 + upx + cos_sign.py
```
---
## 首次部署(新机器)
### 1. 准备代码和数据
```bash
# 克隆仓库(含 gitea-backup)
git clone <你的仓库> && cd <仓库>/gitea-backup
# 迁移数据:从旧机器把数据卷 rsync 过来
# (旧机器 23.254.236.47 上)
rsync -av --progress /opt/gitea/data/ 新机器:/path/to/gitea-backup/data/gitea/
# 又拍云同步目录同理:
rsync -av --progress /opt/upyun-sync/ 新机器:/path/to/gitea-backup/data/upyun-sync/
```
### 2. 配置
```bash
cp .env.example .env
# 编辑 .env:填域名、端口、又拍云密码
```
### 3. 启动
```bash
docker compose up -d
docker compose ps # 确认三个服务都 healthy/running
```
### 4. 注册 runner(首次必做)
```bash
# 到 Gitea 后台:站点管理 → Actions → Runners → 创建 runner → 复制注册 token
# 把 token 填进 .env 的 RUNNER_REGISTRATION_TOKEN,然后:
docker compose up -d --force-recreate runner
# 回到后台确认 runner 已在线(绿色)
```
---
## 备份
```bash
bash scripts/backup.sh
# 产物:backups/gitea-dump-<时间戳>.zip(含 gitea dump + app.ini)
# backups/data-<时间戳>.tar.gz(含完整 data 卷)
```
`gitea dump` 会导出仓库 + 用户 + 配置 + secrets(加密),是最完整的一站式备份。
## 恢复(灾难场景)
```bash
bash scripts/restore.sh backups/gitea-dump-<时间戳>.zip
# 或手动:把 data 卷解回 data/,然后 docker compose up -d
```
---
## 从旧机器迁移的完整清单(十一月到期前照着做)
1. 新机器装 docker + docker compose
2. `rsync` 旧机器 `/opt/gitea/data` → 新机器 `data/gitea/`
3. `rsync` 旧机器 `/opt/upyun-sync` → 新机器 `data/upyun-sync/`
4. `cp .env.example .env` 并填好
5. `docker compose up -d`
6. 后台重新注册 runner(token 会变)
7. DNS 把 `gitea.usj.cc` 指向新机器 IP
8. 验证:`git push` 一次,看 Actions 是否正常构建部署
> ⚠️ 第 6 步 runner token 每次重新注册都会变,旧 token 作废,必须在后台重新拿。
---
## 关于「又拍云同步」的容器化说明
原 1Panel 计划任务是 `bash /opt/upyun-sync/sync.sh`,每分钟跑一次,负责:
从 COS 拉构建产物 → 解压 → `upx sync` 到又拍云 → purge → 刷多吉云。
容器化后逻辑不变,只是**用 `while sleep 60` 循环替代了 cron**(不再依赖 1Panel)。
注意:`sync.sh` 里如果还引用 COS(`cos_sign.py` 签 COS 地址),说明 COS 还没砍;
如果已改为「拉 Gitea artifact」,则 `cos_sign.py` 可以删掉。
+157
View File
@@ -0,0 +1,157 @@
# =============================================================================
# Gitea 可移植部署包 —— 把所有「博客相关服务」打包成 docker-compose
#
# 目标:任何一台机器上,只要:
# 1. git clone 本仓库(含 gitea-backup/ 目录)
# 2. 准备 data/ 数据卷(迁移时从旧机器打包过来)
# 3. 复制 .env.example 为 .env 并填好
# 4. docker compose up -d
# 就能拉起整套环境。
#
# 包含的服务:
# - gitea 代码托管(境外已停用 GitHub Actions,构建全走这里)
# - runner Gitea Actions 的执行器(act_runner,跑博客构建/部署)
# - upyun-sync 又拍云同步(原 1Panel 计划任务,容器化后不再依赖面板)
#
# write-server(写作后台)源码仍在仓库根 write-server/,这里用相对路径引用,
# 不搬源码。如需一起管理,取消下面 write-server 段的注释。
# =============================================================================
name: gitea-backup
services:
# ---------------------------------------------------------------------------
# Gitea —— 代码托管 + CI 调度中心
# 数据:SQLite 单文件(/data/gitea/gitea.db)+ app.ini,全部在挂载卷里
# 迁移:把旧机器 /opt/gitea/data 整个 rsync 到本机 ./data/gitea 即可
# ---------------------------------------------------------------------------
gitea:
image: gitea/gitea:1.28.0-rootless
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- TZ=Asia/Shanghai
# 数据库用内置 SQLite,无需额外容器
- GITEA__database__DB_TYPE=sqlite3
# 域名与基础 URL(必须与你备案域名一致)
- GITEA__server__DOMAIN=${GITEA_DOMAIN}
- GITEA__server__ROOT_URL=${GITEA_ROOT_URL}
- GITEA__server__SSH_DOMAIN=${GITEA_SSH_DOMAIN}
- GITEA__server__SSH_PORT=${GITEA_SSH_PORT}
# 关闭注册,只有你一个人用
- GITEA__service__DISABLE_REGISTRATION=true
ports:
- "${GITEA_HTTP_PORT}:3000"
- "${GITEA_SSH_PORT}:2222"
volumes:
- ./data/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
- gitea-net
# ---------------------------------------------------------------------------
# act_runner —— Gitea Actions 执行器(跑博客的构建 + 部署流水线)
# 首次启动后需到 Gitea 后台「站点管理 → Actions → Runners」拿注册 token,
# 然后 exec 进容器执行一次注册(详见 README「首次初始化」)
# ---------------------------------------------------------------------------
runner:
image: gitea/act_runner:0.2.11
container_name: gitea-runner
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
- GITEA_INSTANCE_URL=${GITEA_ROOT_URL}
# 注册 token:首次跑前手动填进 .env(见 README)
- GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_REGISTRATION_TOKEN}
volumes:
- ./data/runner:/data
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
gitea:
condition: service_healthy
networks:
- gitea-net
# ---------------------------------------------------------------------------
# upyun-sync —— 又拍云同步(原 1Panel 计划任务「又拍云同步(国内中转)」)
# 容器化后:不再依赖 1Panel,随 compose 一起迁移。
# 工作目录 ./data/upyun-sync 里放 sync.sh / upx / cos_sign.py 等。
# 注意:此服务用 host 网络(要访问宿主机已配好的证书/代理时更省事)。
# ---------------------------------------------------------------------------
upyun-sync:
image: alpine:3.20
container_name: upyun-sync
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
# 又拍云凭据(从 .env 注入,容器内脚本读取)
- UPYUN_SERVICE=${UPYUN_BUCKET}
- UPYUN_OPERATOR=${UPYUN_OPERATOR}
- UPYUN_PASSWORD=${UPYUN_PASSWORD}
volumes:
- ./data/upyun-sync:/opt/upyun-sync
entrypoint: ["/bin/sh", "-c"]
command:
- |
apk add --no-cache curl bash python3 py3-pip tar zstd openssl >/dev/null 2>&1 || true
# 引导 upx(若 data 里没有)
if [ ! -x /opt/upyun-sync/upx ]; then
echo "引导 upx..."
cd /tmp && curl -fsSL -o upx.tgz \
"https://collection.b0.upaiyun.com/softwares/upx/upx_0.4.9_linux_amd64.tar.gz" \
&& tar -xzf upx.tgz upx && mv upx /opt/upyun-sync/upx && chmod 755 /opt/upyun-sync/upx || \
curl -fsSL -o upx.tgz \
"https://github.com/upyun/upx/releases/download/v0.4.9/upx_0.4.9_linux_amd64.tar.gz" \
&& tar -xzf upx.tgz upx && mv upx /opt/upyun-sync/upx && chmod 755 /opt/upyun-sync/upx
fi
# 每分钟跑一次 sync.sh(等价原 1Panel 的 * * * * * 计划任务)
while true; do
if [ -x /opt/upyun-sync/sync.sh ]; then
bash /opt/upyun-sync/sync.sh || echo "[$(date -Is)] sync.sh 退出码 $?"
else
echo "[$(date -Is)] 缺少 sync.sh,跳过"
fi
sleep 60
done
networks:
- gitea-net
# ---------------------------------------------------------------------------
# write-server(可选,默认注释)—— 写作后台
# 源码在仓库根 write-server/,这里用相对路径引用。
# 若要把写作后台也纳入本部署包,取消下面整段注释,
# 并确保 write-server/ 目录与 gitea-backup/ 同级。
# ---------------------------------------------------------------------------
# write-server:
# build:
# context: ../write-server
# dockerfile: Dockerfile
# container_name: write-server
# restart: unless-stopped
# ports:
# - "8016:8016"
# volumes:
# - ../..:/blog # 博客根目录(与 write-server 原 compose 一致)
# - ./data/write-server/recycle:/app/recycle
# - ./data/write-server/logs:/app/logs
# - ./data/write-server/backups:/app/backups
# environment:
# - NODE_ENV=production
# - PORT=8016
# - BLOG_ROOT=/blog
# - TZ=Asia/Shanghai
# networks:
# - gitea-net
networks:
gitea-net:
driver: bridge
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# =============================================================================
# Gitea 部署包 —— 一键备份
# 备份内容:
# 1. gitea dump(仓库 + 用户 + 配置 + secrets 加密导出)
# 2. 完整 data 卷(gitea.db / app.ini / runner 配置 / upyun-sync 目录)
# 产物输出到 backups/ 目录
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
BACKUP_DIR="$ROOT_DIR/backups"
STAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo "==> 1/2 导出 Gitea dump(含 secrets)..."
docker compose -f "$ROOT_DIR/docker-compose.yml" exec -T gitea \
gitea dump --config /data/gitea/conf/app.ini \
--file /data/gitea-dump.zip \
-R -S 2>/dev/null || {
echo "⚠️ gitea dump 失败(容器可能未运行),跳过,仅备份 data 卷"
}
# 把 dump 产物从容器拷出来
docker compose -f "$ROOT_DIR/docker-compose.yml" cp \
gitea:/data/gitea-dump.zip "$BACKUP_DIR/gitea-dump-$STAMP.zip" 2>/dev/null || true
echo "==> 2/2 打包 data 卷..."
tar -czf "$BACKUP_DIR/data-$STAMP.tar.gz" -C "$ROOT_DIR" data/
echo ""
echo "✅ 备份完成:"
ls -lh "$BACKUP_DIR" | tail -5
echo ""
echo "⚠️ 产物含密钥,请勿上传网盘/公开仓库。"
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# =============================================================================
# Gitea 部署包 —— 一键恢复
# 用法:bash restore.sh <backups/data-<时间戳>.tar.gz>
# 或 bash restore.sh <backups/gitea-dump-<时间戳>.zip>(仅恢复 dump)
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
if [ $# -lt 1 ]; then
echo "用法:bash restore.sh <备份文件>"
echo " - data-*.tar.gz 恢复完整 data 卷"
echo " - gitea-dump-*.zip 仅恢复 gitea dump"
exit 1
fi
SRC="$1"
if [ ! -f "$SRC" ]; then
echo "❌ 找不到备份文件:$SRC"
exit 1
fi
echo "==> 停止服务..."
docker compose -f "$ROOT_DIR/docker-compose.yml" down 2>/dev/null || true
case "$SRC" in
*data-*.tar.gz)
echo "==> 恢复 data 卷..."
tar -xzf "$SRC" -C "$ROOT_DIR"
echo "✅ data 卷已恢复到 $ROOT_DIR/data/"
;;
*gitea-dump-*.zip)
echo "==> 恢复 gitea dump..."
docker compose -f "$ROOT_DIR/docker-compose.yml" up -d gitea
sleep 5
docker compose -f "$ROOT_DIR/docker-compose.yml" cp \
"$SRC" gitea:/data/restore.zip
docker compose -f "$ROOT_DIR/docker-compose.yml" exec -T gitea \
gitea restore --config /data/gitea/conf/app.ini --file /data/restore.zip
echo "✅ gitea dump 已恢复"
;;
*)
echo "❌ 无法识别的备份文件类型:$SRC"
exit 1
;;
esac
echo "==> 启动服务..."
docker compose -f "$ROOT_DIR/docker-compose.yml" up -d
echo "✅ 恢复完成,检查状态:"
docker compose -f "$ROOT_DIR/docker-compose.yml" ps