工作记录¶
2026-07-27 —— 本次工作的记录:搭建这套文档平台、改变 edge 配置的归属模型、 补上运维上的薄弱环节,并把全部文档翻译成七种语言。
本页的目的不是记录做了什么,而是记录为什么这样决定。理由是最不容易过时的 信息——代码会变,但决策的依据依然成立。
范围
主机地址、路径、机密配置值等运维细节不包含在这个公开站点中——它们存放在 相应仓库内的封闭 runbook 里。
五条主线¶
| # | 工作 | 结果 |
|---|---|---|
| 1 | 搭建文档平台 | docs.gerege.mn 上线运行 |
| 2 | 拆分 edge 配置的归属 | 每个域名迁入各自的仓库 |
| 3 | 补上运维薄弱环节 | 消除了误删与监控盲区 |
| 4 | 把文档翻译成七种语言 | 蒙古语 + 联合国六种官方语言,完整覆盖 |
| 5 | 记录向 Nexus 的迁移 | 生态系统的新模型已用七种语言记录 |
1. 文档平台¶
做了什么¶
一个把 Gerege 生态系统层面文档汇聚一处的 MkDocs Material 门户:25 个页面、 以蒙古语为默认、Mermaid 图示、品牌 CSS。语言覆盖后来扩展到七种——见第 4 节。
内容取自生态系统各仓库的 README、docs/ 目录以及架构文档,并整理为四个部分:
分层 · 平台 · 标准 · 运维。
关键决策¶
静态站点由单独的容器提供。 给 edge nginx 容器新增 mount 就必须重建它—— 那一刻所有域名都会短暂中断。改由自己的小容器来服务,edge 侧只需追加配置 并 reload 即可。
用 rsync 原地部署。 站点目录以 bind mount 挂进容器。若把目录整体替换
(mv),容器会继续盯着旧的 inode,新内容根本不会出现。rsync 是原地更新
文件,因此 mount 始终有效。
西里尔字母标题的锚点。 Python-Markdown 的 toc 默认 slugify 会删除
非 ASCII 字符——## Танилт 这样的标题拿到空 id,页面内链接就悄然失效。
已改用保留 unicode 的 pymdownx.slugs.slugify。
开启了 validation.anchors。 MkDocs 的锚点校验默认是关闭的。不开启,
指向 #某节 的失效链接就会通过构建并进入生产。现在它会让 --strict 失败。
凡进入 docs/ 的内容都会公开。 因此运维细节被单独放到不进入站点的目录中。
2. edge 配置的归属¶
这是最大的一项架构变更。
此前的状况¶
所有域名的 vhost 都放在一个集中式文件里。后果是:要改 docs.gerege.mn
的一个小配置,得向别的仓库提 PR,还要等别的团队部署。归属含混,变更缓慢。
现在的状况¶
edge nginx 的配置目录
├──(共享文件) ← 由统一 stack 的仓库持有
│ sso · dan · gsign · xyp
├── docs.gerege.mn.conf ← docs-gerege-mn
├── developer.gerege.mn.conf ← developer-gerege-mn
└── template.gerege.mn.conf ← template-gerege-mn
现在每个域名的 vhost 都放在该服务自己的仓库中,由它自己的部署流程安装。 变更在一个仓库内即可闭环。
为什么这样可行¶
配置目录在物理上位于另一个仓库的工作副本内,而那个仓库的部署会执行
git reset --hard。但git reset --hard 只恢复被跟踪的文件——不会碰
untracked 文件。因此从外部仓库安装进来的文件能够存活。
完全独立的三个条件¶
vhost 不得在任何方面依赖那些共享文件:
| 条件 | 原因 |
|---|---|
自带 limit_req_zone |
一旦引用共享的 zone 文件,就形成了依赖 |
自带 listen 80 块(ACME + 跳转) |
这样证书续期才能独立运作 |
| 每次部署都重新安装 | 文件一旦丢失,可自行恢复 |
零中断的迁移顺序¶
- 先安装新文件。此时同一个域名会在两处被定义,但 nginx 只会把它当作
conflicting server name警告来处理——行为不变,两者都指向同一 upstream。 - 再从集中式文件中移除。重复消失,新文件随之生效。
若顺序颠倒,在这两步之间域名就会中断。
开源仓库中不写主机路径¶
template-gerege-mn 是开源的,而既有约定是服务器信息只保存在 CI 的 secrets
中。因此安装脚本自行从 edge 容器的 mount 中找出配置目录的路径:
docker inspect <edge> --format \
'{{range .Mounts}}{{if eq .Destination "/etc/nginx/conf.d"}}{{.Source}}{{end}}{{end}}'
这比把路径硬编码更可靠,因此后来在三个仓库中都采用了——主机更换、路径变动 时它都能继续工作。
安全上的前置条件¶
先证书,后 vhost。 在还没有证书时就添加 HTTPS vhost,会让 nginx -t 失败,
那一刻所有域名都处于风险之中。ACME challenge 走的是 80 端口上通用的
default server,因此申请证书无需改动配置。
推送之前,最终配置已在临时容器中、用真实网络与真实证书通过 nginx -t
校验过。
3. 运维加固¶
防止误删¶
有些支撑着生产的文件,在任何仓库里都不是被跟踪的。git reset --hard 不会碰
它们,但git clean -fd 会删掉。
| 文件 | 一旦丢失 | 解法 |
|---|---|---|
| 三个域名的 vhost | 3 个域名同时中断 | .gitignore |
| 主机的 compose override | 容器脱离 edge 网络 → 502 | .gitignore + 示例文件 |
为什么 .gitignore 能解决: 不带 -x 时,git clean 会跳过被忽略的
文件。这样无需把它们纳入跟踪即可加以保护。
compose override 不能纳入跟踪:compose 会自动读取它,那样生产配置就会强行
作用于每位开发者的本地环境。因此实际生效的文件在主机上保持 untracked,仓库中
只保留一份用于恢复的示例。示例与实际配置产出一致,这一点通过比对
docker compose config 的输出得到了确认。
健康监控¶
此前主机上已有监控脚本,但两处缺陷叠加:它根本没有登记到 cron;而且脚本中 列出的部分容器已改名、实际不存在。脚本对不存在的容器是静默跳过的,因此监控 其实早已完全停摆,却无人知晓。
新版本的关键决策:
优先采用容器自带的 healthcheck。 它的 interval 与 retries 早已针对该服务 调校过。
HTTP 探测从容器内部发起。 若一律经 edge 探测,那么 edge 一挂就会显得 所有服务都挂了,从而触发批量重启,把真正的故障掩盖掉。现在每项检查都是 独立的。
连续失败阈值 + cooldown。 短暂的卡顿(部署、GC、负载)不会触发重启; 真正坏掉的服务也不会被反复关停重启。
有状态基础设施不自动重启。 数据库与缓存只做监控与记录。重启并不能解决 磁盘写满这类真实原因,反而会切断多个 stack 的事务,只会加重损失。 这种情况交由人来判断。
容器不存在按错误记录——不再重复旧版本的核心缺陷。
阈值 · cooldown · 有状态策略 · 恢复 · 容器缺失——这五种行为都在隔离的测试容器上 做了真实验证。
证书续期¶
看到 cron 里有条目并不够——续期是否真的在运行,是用 --dry-run 实测过的,
并确认所有域名都能成功续期。这类风险的特点是:直到证书到期之前一直悄无声息。
4. 七种语言的覆盖¶
做了什么¶
站点的全部页面都翻译成了蒙古语加联合国六种官方语言:العربية · 中文 · English · Français · Русский · Español。此前英文翻译只是部分完成(首页、简介、 分层、平台清单、认证),现已补全,并另加五种语言。
关键决策¶
蒙古语仍是源头。 其余六种都是译文——原文以蒙古语写成,再由此转译。 一旦存在两个「源」语言,内容就会悄悄分岔。
按页翻译,而不是按语言翻译。 把一份文档同时译成六种语言,术语、表格行、 结构都能保持一致。反过来「先把所有页面译成英文」,那么后译的语言就得去追赶 一份已经变过的原文。
技术性记法未作翻译。 域名、仓库名、代码、YAML、endpoint 路径、标准名称 (OIDC · PKCE · RFC 3161)在所有语言中都保持原样。译了就无法照抄执行。
文件名不翻译。 是 platforms/sso.ru.md,而不是 платформы/sso.md。
这样切换语言时 URL 路径保持一致,来自其他仓库的深层链接也能继续生效。
阿拉伯语的 RTL 不是手工做的。 Material 能识别 ar locale,自动设置
<html dir="rtl"> 并翻转菜单与内容排布。代码块与 ASCII 图保持 LTR——这是对的,
因为命令与 URL 一旦颠倒方向,含义就被破坏了。
unicode slugify 的重要性如今翻了三倍。 当初为西里尔标题引入的
pymdownx.slugs.slugify,现在同样在支撑阿拉伯语、中文、俄语标题的锚点。
若用的是标准 slugify,六个 locale 的全部页面内链接都会悄然失效。
fallback_to_default 仍保持开启。 目前所有页面均已翻译,回退不会触发;
但它仍是一道保障:当新增页面而翻译滞后时,站点依然完整。
适用范围¶
本政策只适用于生态系统层面的文档,也就是本站。各平台深层的技术文档 (endpoint 结构、SDK 参考)仍在各自仓库内保持 MN + EN;它们的读者是正在该仓库 中工作的工程师,因此扩大语言覆盖收益不大。
5. 记录向 Nexus 的迁移(2026-08-07)¶
发生了什么¶
open-gerege-nexus 仓库于 2026-08-05 创建,08-07 平台更名为 Gerege Nexus
并迁至 nexus.gerege.mn。随后出现两个分叉:sso-gerege-nexus(Gerege SSO)与
eduge-mn-nexus(eduge.mn)。由于这改变了生态系统的分发模型,文档在七种
语言上一并更新对齐。
关键决策¶
没有删除任何既有平台页面。 Template、Gerege Platform、SSO 与 Kiosk 都仍在 production 运行。做法是新增一个页面,并在既有页面上标注「当前以哪种状态为准」。 删除页面等于抹掉一个仍在运行的系统的文档。
把第 3 层拆成两代。 Nexus 并不取代 Template——二者同时位于第 3 层。若新增一 层,分层规则(「不得跨层直达」)本身就失去了意义。
Nexus 自带的 OIDC 提供方并不是第 2 层。 Nexus 内含 OAuth2/OIDC 提供方,但它 服务的是该部署自己的租户与第三方客户端。生态系统识别公民的路径仍然是 Gerege SSO。不点明这一点,读者就会得出「Nexus 取代了 SSO」的错误结论。
每个域名都逐一实测。 通过 DNS、HTTP 与 TLS 证书确认 nexus.gerege.mn、
eduge.mn、geregekiosk.mn 均在线。由此发现两件事:geregekiosk.mn 现在
提供的是 Nexus;而 open.gerege.mn 已从该主机的证书中移除,因此 HTTPS 会
因名称不匹配而失败。
订正了一条过时的承诺。 Template 页面称 autosync 每天把变更送往下游;该自动化 已于 2026-08-06 在全机队范围停用。虚假的承诺比缺失的事实更糟:工程师会一直等着 修复自己流过去。
补上了两处翻译缺口。 域名映射中的「独立品牌域名」一节在六种译文里完全缺失; 现已七种语言齐平。
决策摘要¶
| 决策 | 依据 |
|---|---|
| 静态站点用独立容器 | 重建 edge 会让所有域名中断 |
用 rsync,不用 mv |
bind mount 会停留在旧 inode 上 |
| unicode slugify | 标准 slugify 会毁掉西里尔标题的锚点 |
开启 validation.anchors |
否则失效链接会流入生产 |
| vhost 放在服务自己的仓库 | 变更在一个仓库内闭环 |
| vhost 完全自包含 | 若依赖共享文件,拆分就失去意义 |
| 先安装,后移除 | 顺序颠倒会导致域名中断 |
| 自动探测路径 | 比硬编码可靠;开源仓库中不留下路径 |
用 .gitignore 保护 |
git clean 会跳过被忽略的文件 |
| 不把 override 纳入跟踪 | compose 会自动读取——生产配置会作用于本地 |
| 探测从容器内部发起 | 避免 edge 一挂就批量重启 |
| 有状态服务不重启 | 重启不解决根因,只会加重损失 |
| 蒙古语是唯一源语言 | 有两个「源」,内容就会悄悄分岔 |
| 按页翻译,不按语言 | 术语与结构在六种语言中保持一致 |
| 不翻译代码、域名、仓库名 | 译了就无法照抄执行 |
| 不翻译文件名 | 切换语言时 URL 路径不变,深层链接可用 |
各语言置于子路径(/ar/) |
若改用子域名,SAN · vhost · hreflang 会同时膨胀 |
有意未做的事¶
没有把 sso · dan · gsign · xyp 域名拆出去。 它们的代码就在统一
stack 的仓库内,因此那个集中式文件本来就是它们自己的仓库。没有拆分的理由。
没有给 edge 容器新增单独的 mount。 那不仅要改统一 stack 的 compose 文件, 还得重建容器,从而让所有域名短暂中断。
没有强行做蒙古语的搜索索引。 lunr.js 不支持蒙古语,因此默认 locale 的
搜索按标准分词工作。自行编写 stemmer 的代价高于当前收益。
没有为每种语言单独开域名或子域名。 /ar/、/zh/ 这类路径用一张证书、
一个 vhost、一次部署即可解决。改用子域名会让证书 SAN、edge 配置与 hreflang
同时膨胀。
残留风险¶
三个域名的 vhost 与主机上的 override 文件虽有 .gitignore 保护,但若有人执行
git clean -fdx(连被忽略的文件也一并清除),它们仍会被删除。修复办法是
重新运行相应仓库的安装脚本——这一点已记入三个仓库各自的 runbook。