docs(us2): Soft Serve → Gitea 迁移事实与参考镜像 (Plane VPS-94)
- hosts/us2: Gitea 1.27.3-rootless 部署实况 (repo.windy.me SSH:2222/Web), 16 仓迁移核对, 备份/回滚; soft-serve 停用保留作回滚 - compose/gitea: 参考镜像 (rootless compose + 备份 sidecar + 一次性迁移脚本留档) - AGENTS/inventory/compose README: 服务表与索引同步
This commit is contained in:
+2
-1
@@ -11,7 +11,8 @@ files: every secret is a `${VAR}` reference resolved from the **server-local
|
||||
| `vaultwarden` | us2 (`/opt/vaultwarden`) | `compose/vaultwarden/compose.yml` | static file + `compose-deploy.yml` |
|
||||
| `pdns` | hk2 (`/opt/pdns`) | `compose/pdns/compose.yml` | static file + `compose-deploy.yml` |
|
||||
| `pgdb` | pgdb (`/opt/database`, 无 ansible) | `compose/pgdb/compose.yml` | static file(手动部署:scp → `docker compose config -q` → `up -d`;服务器文件名 `docker-compose.yml`) |
|
||||
| `soft-serve` | us2 (`/opt/soft-serve`) | `compose/soft-serve/compose.yml` (+ `Dockerfile.backup`, `scripts/`) | static file(参考镜像, 未接入 compose-deploy; 服务器文件为准) |
|
||||
| `soft-serve` | us2 (`/opt/soft-serve`, 已退役停用) | `compose/soft-serve/compose.yml` (+ `Dockerfile.backup`, `scripts/`) | static file(参考镜像; 2026-09-18 被 gitea 替换 VPS-94, 数据保留作回滚) |
|
||||
| `gitea` | us2 (`/opt/gitea`) | `compose/gitea/compose.yml` (+ `Dockerfile.backup`, `scripts/`) | static file(参考镜像, 未接入 compose-deploy; 服务器文件为准; 2026-09-18 替换 soft-serve, VPS-94) |
|
||||
| `adguardhome` | dns.windy.lan (`/opt/adguardhome`) | — (待从 LAN 提取) | static file (pending) |
|
||||
| `unifi` | ubnt (`/home/windy/unifi-9`) | — (待从 LAN 提取) | static file (pending) |
|
||||
| `wireguard` | us4 (`/opt/wireguard`) | `ansible/templates/wireguard-compose.yml.j2` | role-rendered (inventory vars) |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# compose/gitea — 秘密一律走服务器本地 .env, 不入库
|
||||
# 迁移期一次性: Gitea 管理员生成的 token (mirror-migrate.sh 读取, 用后撤销)
|
||||
GITEA_MIGRATE_USER=
|
||||
GITEA_MIGRATE_TOKEN=
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache sqlite rsync tzdata
|
||||
WORKDIR /scripts
|
||||
@@ -0,0 +1,59 @@
|
||||
# Gitea on us2 — reference compose (Plane VPS-94, 迁移完成 2026-09-18)
|
||||
# 参考镜像, 服务器 /opt/gitea 文件为准 (同 soft-serve 约定, 未接入 compose-deploy)
|
||||
# rootless 镜像: uid 1000 原生非 root; 数据 /var/lib/gitea (宿主 ./data), 配置 /etc/gitea (宿主 ./config)
|
||||
# SSH: 容器内监听 2322 (非特权, SSH_LISTEN_PORT), 对外 repo.windy.me:2222 经 Traefik TCP entrypoint `ssh`
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea@sha256:1c17ecaead42eb3b5391553d8708103a4beb0e86edf5b9ebc1eb269c318845f2 # 1.27.3-rootless
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
user: "1000:1000"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ./data:/var/lib/gitea
|
||||
- ./config:/etc/gitea
|
||||
- ./secrets:/secrets:ro # 复用的 soft-serve host key (SSH_SERVER_HOST_KEYS)
|
||||
networks:
|
||||
- traefik
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
# Web UI: repo.windy.me (2026-09-18 操作者决定复用现有域名, 免 DNS 变更)
|
||||
- traefik.http.routers.gitea-web.rule=Host(`repo.windy.me`)
|
||||
- traefik.http.routers.gitea-web.entrypoints=websecure
|
||||
- traefik.http.routers.gitea-web.tls.certresolver=letsencrypt
|
||||
- traefik.http.services.gitea-web.loadbalancer.server.port=3000
|
||||
# SSH: 接管 :2222 (entrypoint 已存在, router 动态生效, 无需重启 Traefik)
|
||||
- traefik.tcp.routers.gitea-ssh.entrypoints=ssh
|
||||
- traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)
|
||||
- traefik.tcp.routers.gitea-ssh.tls=false
|
||||
- traefik.tcp.services.gitea-ssh.loadbalancer.server.port=2322
|
||||
|
||||
gitea-backup:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.backup
|
||||
container_name: gitea-backup
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./data:/data:ro
|
||||
- ./config:/config:ro
|
||||
- ./backups:/backup
|
||||
- ./scripts:/scripts
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
BACKUP_UID: 1000
|
||||
BACKUP_GID: 1000
|
||||
entrypoint: >
|
||||
/bin/sh -ec "
|
||||
umask 077 &&
|
||||
touch /backup/backup.log &&
|
||||
crontab /scripts/crontab.txt &&
|
||||
echo '[INFO] gitea backup cron installed' &&
|
||||
crond -f -l 8
|
||||
"
|
||||
|
||||
networks:
|
||||
traefik:
|
||||
external: true
|
||||
name: vw-net
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
umask 077
|
||||
D() { date "+%Y-%m-%d %H:%M:%S"; }
|
||||
TS=$(date +%Y%m%d_%H%M%S)
|
||||
OUT="/backup/gitea_${TS}"
|
||||
mkdir -p "$OUT"
|
||||
echo "[$(D)] Starting gitea backup -> $OUT"
|
||||
# rootless 布局: app.ini=/etc/gitea(宿主 ./config), db+repos=/var/lib/gitea/data(宿主 ./data/data)
|
||||
# app.ini 含 SECRET_KEY/INTERNAL_TOKEN — 恢复 2FA/session/mirror 凭据必需
|
||||
tar czf "$OUT/app.ini.tar.gz" -C /config app.ini
|
||||
sqlite3 /data/data/gitea.db ".backup '$OUT/gitea.db'"
|
||||
rsync -a /data/data/git/repositories/ "$OUT/repos/"
|
||||
tar czf "$OUT/repos.tar.gz" -C "$OUT" repos
|
||||
rm -rf "$OUT/repos"
|
||||
chmod 600 "$OUT"/*
|
||||
if [ -n "${BACKUP_UID:-}" ] && [ -n "${BACKUP_GID:-}" ]; then
|
||||
chown -R "$BACKUP_UID:$BACKUP_GID" "$OUT" /backup/backup.log
|
||||
fi
|
||||
echo "[$(D)] Backup OK: $(du -sh "$OUT" | cut -f1)"
|
||||
@@ -0,0 +1,4 @@
|
||||
# Run gitea backup daily at 02:00
|
||||
0 2 * * * /bin/sh /scripts/backup.sh >> /backup/backup.log 2>&1
|
||||
# Prune backups older than 14 days daily at 03:00
|
||||
0 3 * * * /bin/sh /scripts/prune.sh >> /backup/backup.log 2>&1
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
# 一次性迁移辅助 (Plane VPS-94 Phase 2): 在 gitea 容器内执行。
|
||||
# 已于 2026-09-18 执行完成 (16 仓), 留档备查; 复用时按 VPS-94 流程重生成一次性 token。
|
||||
# 用法:
|
||||
# GITEA_MIGRATE_USER=<user> GITEA_MIGRATE_TOKEN=<token> \
|
||||
# docker exec -e GITEA_MIGRATE_USER -e GITEA_MIGRATE_TOKEN gitea \
|
||||
# /scripts/mirror-migrate.sh [public_repo ...]
|
||||
# 每仓: API 建仓 (默认 private, 参数中列出的为 public) -> push --mirror。
|
||||
# default_branch 按源仓 symbolic-ref HEAD 设置, 避免非 main 源仓在 Gitea 显示为空。
|
||||
# 结束后按 VPS-94 Phase 3 逐仓核对 git ls-remote ref 全集。
|
||||
set -eu
|
||||
MUSER="${GITEA_MIGRATE_USER:?need GITEA_MIGRATE_USER}"
|
||||
TOKEN="${GITEA_MIGRATE_TOKEN:?need GITEA_MIGRATE_TOKEN}"
|
||||
SRC="/migration-src"
|
||||
API="http://localhost:3000/api/v1"
|
||||
PUBLIC_REPOS=" $* "
|
||||
|
||||
migrate_one() {
|
||||
dir="$1"
|
||||
git -C "$dir" rev-parse --git-dir >/dev/null 2>&1 || { echo "[SKIP] $dir (not a git repo)"; return 0; }
|
||||
name=$(basename "$dir"); name=${name%.git}
|
||||
def_branch=$(git -C "$dir" symbolic-ref --short HEAD)
|
||||
case "$PUBLIC_REPOS" in *" $name "*) private=false ;; *) private=true ;; esac
|
||||
echo "[MIGRATE] $name (default=$def_branch private=$private)"
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "$API/user/repos" \
|
||||
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"$name\",\"private\":$private,\"default_branch\":\"$def_branch\",\"auto_init\":false}")
|
||||
case "$code" in
|
||||
201) : ;;
|
||||
409) echo " [WARN] $name 已存在, 直接补推" ;;
|
||||
*) echo " [FAIL] create HTTP $code"; return 1 ;;
|
||||
esac
|
||||
git -C "$dir" push --mirror "http://$MUSER:$TOKEN@localhost:3000/$MUSER/$name.git"
|
||||
echo " [OK] $name pushed"
|
||||
}
|
||||
|
||||
for dir in "$SRC"/*.git "$SRC"/cdia; do
|
||||
[ -d "$dir" ] || continue
|
||||
migrate_one "$dir"
|
||||
done
|
||||
echo "[DONE] 全部处理完毕; 迁移后记得撤销一次性 token"
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
D() { date "+%Y-%m-%d %H:%M:%S"; }
|
||||
ls -dt /backup/gitea_* 2>/dev/null | tail -n +15 | xargs -r rm -rf
|
||||
echo "[$(D)] Pruned. Kept $(ls -d /backup/gitea_* 2>/dev/null | wc -l) backups (max 14)"
|
||||
Reference in New Issue
Block a user