# vps.git Docker 迁移方案 v1 ## 目标 把 `vps.git` 上当前裸机部署的: - Gitea - Keycloak - MySQL 迁移为 Docker 版本,并满足以下约束: - `gitea`、`keycloak`、`mysql` 位于同一个 Docker 网络 - MySQL 只保留一个容器实例,但实例内分两个独立库: - `` - `` - 宿主机 `nginx` 架构不变,继续保留在宿主机上 - Gitea 直接挂载现有数据目录 - Keycloak 从 H2 迁移到 MySQL - 敏感信息统一放在 `.env` - Docker 部署根目录放在 `~/git-kc` - 本次迁移不顺手升级大版本,优先保持现有版本基线 - 本次迁移默认先不启用 Git over SSH,仅保证 HTTPS Git 正常 --- ## 当前现状摘要 ### 服务现状 宿主机当前运行: - `nginx` - `gitea.service` - `keycloak.service` - `mysql.service` ### 监听关系 - `nginx`:对外 `80/443` - `gitea`:宿主机监听 `3000` - `keycloak`:宿主机监听 `8443` - `mysql`:宿主机监听 `localhost:3306` ### 当前数据位置 - Gitea 配置:`/etc/gitea/app.ini` - Gitea 数据:`/var/lib/gitea` - Keycloak 配置:`/opt/keycloak/conf/keycloak.conf` - Keycloak H2 数据:`/opt/keycloak/data/h2` - MySQL 数据:`/var/lib/mysql` ### 当前版本基线 - Gitea:`1.22.4` - Keycloak:`26.0.6` - MySQL:`8.0.x` 迁移时保持同代版本,不使用 `latest`。 --- ## 目标架构 ### 宿主机保留 - `nginx` - 当前证书与域名 - 宿主机 SSH 运维入口 ### Docker 内运行 - `mysql` - `gitea` - `keycloak` 三者放在同一个 Docker 网络中,例如: - `git-kc-net` ### 端口策略 - `mysql` - 不对宿主机发布端口 - 仅允许 Docker 内部网络访问 - `gitea` - 发布到 `localhost:3000` - `keycloak` - 发布到 `localhost:8080` ### nginx 反代目标 - `git.hangman-lab.top` → `http://localhost:3000` - `login.hangman-lab.top` → `http://localhost:8080` 说明: - Gitea 迁移后,对外域名不变 - Keycloak 迁移后,对外域名不变 - Keycloak 容器内不再自行持有 HTTPS,TLS 由宿主机 nginx 负责终止 --- ## 部署目录设计 Docker 部署根目录: - `~/git-kc` 建议结构: - `~/git-kc/compose.yaml` - `~/git-kc/.env` - `~/git-kc/backups/` - `~/git-kc/mysql/` - `~/git-kc/gitea/` - `~/git-kc/keycloak/` - `~/git-kc/keycloak/import/` - `~/git-kc/docs/` 用途说明: - `compose.yaml`:Compose 主文件 - `.env`:敏感配置 - `backups/`:迁移专用备份 - `gitea/`:Gitea 容器使用的配置副本 - `keycloak/import/`:Keycloak realm 导入文件 - `docs/`:迁移记录与回滚说明 --- ## 数据与配置保留策略 ### Gitea Gitea 采用“保留原数据目录,容器挂载使用”的方式: - 直接挂载现有数据目录:`/var/lib/gitea` 配置文件策略: - 不直接原地修改宿主机当前使用的 `/etc/gitea/app.ini` - 先复制一份到:`~/git-kc/gitea/app.ini` - Docker Gitea 使用这份配置副本 - 原始 `/etc/gitea/app.ini` 保留不动,用于快速回滚 ### Keycloak Keycloak 不直接复用当前运行目录作为 Docker 运行目录,而是: 1. 从旧 Keycloak 执行 realm export 2. 在 Docker Keycloak 中导入 realm 3. 新容器改用 MySQL 存储 旧目录保留: - `/opt/keycloak/conf` - `/opt/keycloak/data/h2` ### MySQL MySQL 不直接接管旧宿主机 datadir,而采用逻辑导出 / 导入迁移: - 导出旧 `` - 在 Docker MySQL 内创建新实例环境 - 导入 `` - 新建 `` --- ## 数据库设计 Docker MySQL 容器内使用一个实例,分为两个独立数据库: - `` - `` 分别创建独立账户: - `gitea` - `keycloak` 权限原则: - `gitea` 仅访问 `` - `keycloak` 仅访问 `` 字符集与排序规则保持与当前环境一致: - `utf8mb4` - `utf8mb4_0900_ai_ci` 说明: - 是“共用一个 MySQL 容器实例” - 不是“共用同一个 database/schema” --- ## Gitea 容器设计 ### 运行目标 迁移后保持: - 域名不变 - 数据目录不变 - 仓库、用户、OIDC、package 等数据保留 - 本次先不启用 Git over SSH ### 配置原则 基于现有 `app.ini` 的副本进行调整,重点改动: - `[database] HOST` - 从 `localhost:3306` - 改为 `mysql:3306` 其余核心配置尽量保持不变,例如: - `ROOT_URL` - `APP_DATA_PATH` - `repository ROOT` - `oauth2/openid` - `security` - `service` - `package/lfs/queue` ### 端口 - 宿主机:`localhost:3000` - 容器内:`3000` ### SSH 策略 本次迁移默认: - 不启用 Git over SSH - 不抢占宿主机 `22` - 所有 Git 操作先走 HTTPS - Gitea 配置中应显式关闭 SSH(例如 `DISABLE_SSH = true`;若存在 `START_SSH_SERVER`,则设为 `false`) 后续如需恢复 Git over SSH,再单独设计第二阶段方案。 --- ## Keycloak 容器设计 ### 迁移原则 Keycloak 从: - 裸机 - H2 - 自身提供 HTTPS 8443 迁移为: - Docker - MySQL - 容器内部 HTTP - 宿主机 nginx 负责 TLS 终止 ### 数据库连接 Keycloak 容器连接: - Host:`mysql` - Port:`3306` - Database:`` ### 对外行为保持不变 - 域名:`login.hangman-lab.top` - Realm:`Hangman-Lab` - OIDC issuer 保持不变 - 尽量保持原有 client 与集成关系不变 ### 端口 - 宿主机:`localhost:8080` - 容器内:`8080` ### 容器配置原则 通过 `.env` 注入: - `KC_DB=mysql` - `KC_DB_URL_HOST=mysql` - `KC_DB_URL_DATABASE=` - `KC_DB_USERNAME` - `KC_DB_PASSWORD` - `KC_HOSTNAME=login.hangman-lab.top` - `KC_HTTP_ENABLED=true` - `KC_PROXY_HEADERS=xforwarded` 说明: - Docker Keycloak 不再自行持有 TLS 证书 - nginx 负责 HTTPS --- ## nginx 变更原则 宿主机 nginx 架构保持不变。 ### Gitea 继续反代到: - `http://localhost:3000` 如容器沿用相同宿主端口,Gitea 对应 nginx 配置可能无需变更。 ### Keycloak 需要把 upstream 从: - `https://localhost:8443` 改为: - `http://localhost:8080` 并保留以下头: - `Host` - `X-Real-IP` - `X-Forwarded-For` - `X-Forwarded-Proto` --- ## 迁移实施步骤 ### Phase A:准备阶段 1. 在 `vps.git` 上安装 Docker Engine 与 Docker Compose Plugin 2. 创建目录: - `~/git-kc` - `~/git-kc/backups` - `~/git-kc/mysql` - `~/git-kc/gitea` - `~/git-kc/keycloak/import` - `~/git-kc/docs` 3. 编写 `compose.yaml` 与 `.env` 4. 复制 Gitea 当前配置: - `/etc/gitea/app.ini` → `~/git-kc/gitea/app.ini` 5. 调整 Gitea 容器配置副本中的数据库地址 6. 准备 Keycloak 容器参数 7. 预拉取固定版本镜像 ### Phase B:备份阶段 正式切换前执行迁移专用备份: 1. 备份 `/etc/gitea/app.ini` 2. 记录并确认 `/var/lib/gitea` 当前状态 3. 导出 Gitea 数据库:`mysqldump ` 4. 从旧 Keycloak 导出 realm / users 5. 备份 nginx 相关站点配置 说明: - 即使整机已有备份,仍建议保留迁移专用备份,以便快速回滚 ### Phase C:切换阶段 建议顺序: 1. 进入维护窗口 2. 停止旧 Gitea:`systemctl stop gitea` 3. 停止旧 Keycloak:`systemctl stop keycloak` 4. 执行 Gitea 最终 SQL 导出 5. 执行 Keycloak 最终 export 6. 停止旧 MySQL:`systemctl stop mysql` 7. 启动 Docker MySQL 8. 初始化 Docker MySQL: - 创建 `` - 创建 `` - 创建 `gitea` 用户 - 创建 `keycloak` 用户 9. 导入 Gitea SQL 到 Docker MySQL 10. 启动 Docker Keycloak,并导入 realm 11. 启动 Docker Gitea 12. 修改 nginx 的 Keycloak upstream: - 从 `https://localhost:8443` - 改为 `http://localhost:8080` 13. 执行 `nginx -t` 14. 执行 `systemctl reload nginx` 15. 执行完整验证 --- ## 验证清单 ### Gitea - `https://git.hangman-lab.top/` 可访问 - 首页正常 - 用户可登录 - 仓库列表正常 - 既有仓库可见 - Package 功能正常 - HTTPS clone 正常 - HTTPS push 正常 - OIDC 登录可正常跳转到 Keycloak 并返回 ### Keycloak - `https://login.hangman-lab.top/` 可访问 - Realm `Hangman-Lab` 正常 - OIDC discovery 正常 - 管理后台可登录 - 普通用户可登录 - 原有 client 保留 - issuer 与原环境一致 ### MySQL - `` 表完整 - `` 表完整 - Gitea 可正常连接数据库 - Keycloak 可正常连接数据库 - 容器重启后数据不丢失 ### nginx - HTTPS 证书正常 - 无重定向循环 - 无 mixed content - 代理头工作正常 --- ## 回滚方案 如果迁移后验证失败,按以下顺序回滚: 1. 停止 Docker 容器: - `gitea` - `keycloak` - `mysql` 2. 恢复 nginx 的 Keycloak upstream: - 改回 `https://localhost:8443` 3. 启动旧宿主机服务: - `systemctl start mysql` - `systemctl start keycloak` - `systemctl start gitea` 4. 执行 `nginx -t` 5. 执行 `systemctl reload nginx` 回滚前提: - 不删除旧数据 - 不覆盖旧宿主配置 - Gitea 使用配置副本而不是原地覆盖 - MySQL 使用逻辑迁移而不是直接接管 datadir --- ## 主要风险点 ### 1. Keycloak H2 → MySQL 这是整个迁移中最高风险点。 控制策略: - 必须走 export / import - 不采用“直接改 DB 配置试运行”的方式 ### 2. Gitea 与 Keycloak 的 OIDC 一致性 控制策略: - 不改域名 - 不改 realm 名称 - 不改 issuer - 尽量不重建 client ### 3. Gitea 直接挂载旧目录 控制策略: - 切换时必须先彻底停止旧 Gitea - 避免新旧环境同时访问同一数据目录 ### 4. Keycloak 容器改为 HTTP 控制策略: - 确保 nginx 传递正确代理头 - 确保 Keycloak hostname 与 proxy 配置正确 - 仅绑定到 `localhost` --- ## 已确定的关键决策 - 宿主机 nginx 架构不动 - Docker 部署根目录使用 `~/git-kc` - Gitea 直接挂载现有数据目录 - MySQL 采用逻辑迁移,不直接接管旧 datadir - Keycloak 改为 MySQL,走 export / import 迁移 - 敏感信息统一放 `.env` - 版本先保持现状,不升级大版本 - Git over SSH 本次先不启用;必要时后续单独设计第二阶段方案 --- ## 结论 本方案的核心原则是: - 先完成 Docker 化迁移 - 保持外部域名与功能语义不变 - 优先控制风险与保留回滚能力 - 不把“迁移”与“升级”混在同一轮进行 在此基础上,下一步可进一步补充: - `compose.yaml` 草案 - `.env` 模板 - 精确到命令级的实施 checklist