从零搭建机器人技术文档中心:GitLab + Antora + CI/CD + Nginx 实战教程

Table of Contents

本教程整理自“文档网站搭建建议”对话的完整实验记录。目标是搭建一套自己可控的局域网文档中心:用 GitLab 保存 AsciiDoc,用 Runner 执行 Antora 构建,用 Nginx 提供网页;ABB 与 FANUC 分别维护内容仓库,各版本内容更新后自动触发网站发布。

原实验先在 WSL 本地学习 Antora,之后才搭建 Ubuntu Server。为方便从零复现,本文将服务器环境准备放在前面,保留原实验的项目命名、地址和关键故障。命令经过整理,并非逐条聊天记录。门户首页在原对话中仅提出方案,本文放在可选扩展中;搜索、定制 UI、域名和 HTTPS 不属于已完成实验。

1. 最终目标、角色与参数

1.1. 系统各部分做什么

部分 职责

Ubuntu Server

提供长期运行的服务器环境、网络与持久化目录。

Docker / Compose

用镜像运行 GitLab、Runner、Nginx;Compose 用配置文件描述启动方式。

GitLab CE

保存 Git 仓库、管理用户权限、创建 Pipeline、保存 Artifact。

GitLab Runner

领取并执行 CI Job;Docker executor 为 Job 创建临时容器。

Antora

读取指定仓库和分支,将 .adoc、导航与图片汇总成静态 HTML。

Nginx

持续监听端口,将生成好的 HTML、CSS、JS、图片返回给浏览器。

多项目触发

内容仓库的 Pipeline 触发 Site 仓库的 Pipeline。

共享 CI 模板

把触发规则放在 Site 仓库维护,由各内容仓库引用。

WSL / 编辑电脑                         Ubuntu Server
修改 AsciiDoc                          192.168.179.129
     │ git push                             │
     └─────────────────────────────→ GitLab :8181
                                     robot-docs/
                                     ├─ robot-docs-abb  v1.0 / v1.1
                                     ├─ robot-docs-fanuc v1.0
                                     └─ robot-docs-site main
                                              ↑ 内容仓库触发
                                              │
                                           Runner
                                              │ npm ci + Antora
                                           build/site
                                              │ Artifact → deploy-docs
                                     /srv/robot-docs-site
                                              │ 只读挂载
                                           Nginx :8080
                                              │
                                            浏览器

CI(持续集成)负责自动验证和构建;本实验的 CD(持续部署)把成功构建的网页自动复制到网站目录。Pipeline 是一次流水线,Stage 是阶段,Job 是实际执行命令的任务。

1.2. 实验参数表

参数 本文示例 使用前核对

服务器地址

192.168.179.129

替换为你的虚拟机实际 IP;尽量设置 DHCP 地址保留。

服务器用户

vision3d

替换为安装 Ubuntu 时创建的用户。

GitLab Web

http://192.168.179.129:8181

GitLab、Runner、Job 容器和 WSL 都必须能访问。

Ubuntu SSH

22

用于登录服务器。

GitLab SSH

2222

映射到 GitLab 容器的 22,区别于服务器登录端口。

文档网站

http://192.168.179.129:8080

由 Nginx 提供。

Group

robot-docs

包含 Site、ABB、FANUC 三个项目。

工作目录

/robot_docs_demo、/robot_docs_site

位于 WSL;不等于服务器的 /srv/

Runner 配置

/srv/gitlab-runner/config/config.toml

位于 Ubuntu Server。

网站发布目录

/srv/robot-docs-site

位于 Ubuntu Server,Nginx 只读、CI 可写。

历史镜像代理

docker.1ms.run

原实验实际使用的第三方代理;现在是否可用需自己验证。

本文中 Bash 命令在 Ubuntu/WSL 执行。带 \ 的多行命令是一条命令,反斜杠必须是该行最后一个字符;改成一行时去掉反斜杠即可。命令中的示例邮箱、版本和 Token 占位符需替换,不要把提示符 $ 一起复制。

历史日志记录了 Node 22、Antora 3.2.0、Runner 19.3.3 等版本,这些是当时环境的信息,不代表本教程已在这些版本的新环境中重新部署。新建环境应固定实际可用版本,Runner 与 helper 必须匹配。GitLab 的 strategy: mirror 要求 GitLab 18.2 或以上。

2. 阶段一:准备 Windows、WSL 与 Ubuntu Server

2.1. 创建虚拟机

  1. 准备 Windows 主机、可正常启动虚拟机的 VMware Workstation,以及 Ubuntu Server 24.04 LTS 安装 ISO。

  2. 创建独立实验虚拟机,选择 Linux / Ubuntu 64 位,网络使用 NAT。可从 4 vCPU、8 GB 内存、100 GB 虚拟磁盘开始;这是实验起点,内存和磁盘应按实际负载调整。

  3. 启动安装程序,选择标准 Ubuntu Server,设置用户名与密码。

  4. 网络优先使用 DHCP;安装过程中显示的临时地址不一定是安装完成后的实际地址。

  5. 存储选择仅用于这台实验机的虚拟磁盘,可使用 LVM。确认格式化对象确实是新虚拟磁盘再继续。

  6. Ubuntu Pro 可选择暂时跳过;勾选安装 OpenSSH Server,实验初期允许密码登录。

  7. Featured Server Snaps 不必选择;完成安装后卸载 ISO 并重启。

原实验 100 GB 虚拟磁盘使用 LVM,根分区约 49 GB,其余空间留在卷组中。虚拟磁盘大小和根分区可用空间是两回事,Docker 镜像、GitLab 数据和 Artifact 主要消耗根分区空间。

hostname -I
ip -br addr
ip route
df -h /
sudo vgs
sudo lvs
sudo systemctl status ssh --no-pager

在 Windows PowerShell 或 WSL 登录服务器:

ssh vision3d@192.168.179.129

Ubuntu Server 更新系统与安装常用工具:

sudo apt update
sudo apt upgrade -y
sudo apt install -y curl ca-certificates git tree unzip
curl -I https://ubuntu.com

验收:Windows 能 SSH 登录;服务器有默认路由,软件源可访问;确认实际 IP 后,后续所有配置使用同一地址。

2.2. WSL 负责编辑和本地实验

Windows PowerShell 中可查看 WSL:

wsl --list --verbose

如果尚未安装,在有相应权限的 PowerShell 中执行 wsl --install -d Ubuntu,按系统提示完成重启和初始化。已有 WSL Ubuntu 可直接使用。以后文中的 WSL 表示编辑与本地构建环境,Ubuntu Server 表示运行 GitLab/Runner/Nginx 的虚拟机。

2.3. VMware 启动故障

原实验曾在 Windows 10 升级到 Windows 11 后出现:

无法打开内核设备 "\\.\VMCIDev\VMX"
模块 "DevicePowerOn" 启动失败

如果旧虚拟机也无法启动,应先检查 VMware 主机驱动、服务与 Windows 版本兼容性。关闭虚拟机、备份虚拟机文件,使用具有管理员权限的安装程序修复或更新 VMware,重启 Windows 后再次测试。出现 “You must have administrative privileges…​” 时,说明当前修复程序没有提升权限。这个错误发生在 Ubuntu 启动前,不应通过修改 Docker 或 GitLab 配置处理。

3. 阶段二:安装 Docker 并处理镜像网络

3.1. 安装 Docker Engine 与 Compose

原实验访问 download.docker.com 出现连接重置,因此改用 Ubuntu 软件仓库。以下是一条完整的 Ubuntu Server 实验安装路线

sudo apt update
sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker --version
sudo docker compose version
sudo usermod -aG docker "$USER"
exit

重新 SSH 登录,使用户组生效,然后执行:

docker ps
docker run --rm hello-world

docker.io 是 Ubuntu 打包的 Engine;docker-compose-v2 提供 docker compose 子命令。另一条路线是使用 Docker 官方 APT 源安装 docker-cedocker-compose-plugin,不要混装两条路线。软件包不存在时检查 Ubuntu 版本、软件源和 universe 仓库配置。官方路线参见 Docker Ubuntu 安装说明

加入 docker 用户组等同于授予强大的主机管理权限。本文使用独立实验机,避免把不受信任用户加入该组。

3.2. 区分 DNS、HTTPS 与 Docker 自身问题

getent ahostsv4 registry-1.docker.io
resolvectl status
curl -4 -I --connect-timeout 10 https://registry-1.docker.io/v2/
curl -I --connect-timeout 10 https://registry.gitlab.com/v2/
sudo journalctl -u docker -n 80 --no-pager

Registry 返回 HTTP 401 Unauthorized 通常说明已经连通,只是尚未认证;连接超时、重置和无法解析是另一类故障。一次 DNS 查询不能单独证明根因,应结合解析、TCP/HTTPS 和 Docker 日志。原实验先发现 NAT DNS 可疑,切换 DNS 后 Docker Hub 的 HTTPS 仍失败,说明只换 DNS 没有解决全部问题。

临时修改 DNS(网卡名须取自 ip -br addr):

sudo resolvectl dns ens33 223.5.5.5 119.29.29.29
sudo resolvectl flush-caches
resolvectl status ens33

该设置可能在重启或网络重配后失效。需要持久化时先查看 /etc/netplan/,在原有网卡配置中修改 nameservers,避免覆盖原路由和地址;远程操作优先 sudo netplan try。不要将第三方公共 DNS 强行用于依赖公司内部域名的网络。

3.3. 镜像代理与离线导入

原实验成功使用了显式镜像代理路径:

docker pull docker.1ms.run/library/hello-world:latest
docker run --rm docker.1ms.run/library/hello-world:latest

同样的路径用于 library/node:22library/nginx:alpinegitlab/gitlab-cegitlab/gitlab-runner-helper。代理路径会直接出现在镜像名称里,与 Docker daemon 的 registry-mirrors 配置不同。Docker Hub 代理不会自动解决 registry.gitlab.com 的访问问题。

如果该历史代理不可用,可使用自己信任且可访问的镜像仓库、正确配置 Docker daemon 的网络代理,或者离线导入。以下在 能联网且架构匹配的电脑 执行:

docker pull node:22
docker save -o node22.tar node:22
scp node22.tar vision3d@192.168.179.129:~/

然后在 Ubuntu Server

docker load -i ~/node22.tar
docker image inspect node:22

如果导入镜像名是 node:22,CI 中也应使用 node:22,或先 docker tag 为 CI 配置使用的完整镜像名。预拉取与离线导入必须针对 Job 实际使用的 Docker daemon。

验收:Docker/Compose 能报告版本,至少一个测试镜像能成功运行。不要把拉取失败误认为 Docker 安装失败。

4. 阶段三:用 Compose 部署 GitLab CE

4.1. 准备持久化目录

Ubuntu Server

sudo mkdir -p /srv/gitlab/{config,logs,data}
mkdir -p ~/gitlab-docker
cd ~/gitlab-docker
nano compose.yaml

compose.yaml(保留原实验启动方式):

services:
  gitlab:
    image: docker.1ms.run/gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    hostname: gitlab-server
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.179.129:8181'
        gitlab_rails['gitlab_shell_ssh_port'] = 2222
    ports:
      - "8181:8181"
      - "2222:22"
    volumes:
      - "/srv/gitlab/config:/etc/gitlab"
      - "/srv/gitlab/logs:/var/log/gitlab"
      - "/srv/gitlab/data:/var/opt/gitlab"
    shm_size: "256m"

external_url 决定 GitLab 对外链接以及这里内部 Nginx 的监听端口,因此本例使用 8181:81812222:22 只用于 GitLab 的 Git SSH。三个卷分别保存配置、日志和数据,使容器重建不等于仓库消失。持久化不替代备份。

latest 是原实验使用方式;建立长期运行环境前,应将其改成已验证的具体版本标签并记录镜像摘要,升级按 GitLab 支持的升级路径执行。不能通过随意拉取新 latest 跳过版本升级要求。

4.2. 启动、等待初始化并登录

docker compose config
docker compose up -d
docker ps
docker logs --tail 100 -f gitlab

第一次下载与初始化可能较慢。查看日志时按 Ctrl+C 仅退出日志跟随,不会停止后台容器。网页短暂 502 或 health: starting 时继续检查容器初始化,不要反复删容器重建。

docker exec gitlab gitlab-ctl status
curl -I http://192.168.179.129:8181/users/sign_in
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

浏览器访问 http://192.168.179.129:8181,使用 root 和初始密码登录,随后修改密码。初始密码只用于第一次登录,不要放入文档、仓库或截图。资料初始化页面可跳过。

验收:GitLab 登录成功,可进入项目创建页面;8181 是 GitLab 页面,8080 暂时还没有正式文档服务。

5. 阶段四:在 WSL 建立最小 Antora 样例

5.1. 准备 Node 与站点依赖

WSL 使用已有 Node 22 环境。若没有 nvm,可从其官方 Git 仓库安装一个明确版本(下面使用 v0.40.3),再安装 Node 22:

sudo apt update
sudo apt install -y git curl ca-certificates
git clone --branch v0.40.3 --depth 1 https://github.com/nvm-sh/nvm.git "$HOME/.nvm"
export NVM_DIR="$HOME/.nvm"
. "$NVM_DIR/nvm.sh"
nvm install 22
nvm use 22
nvm alias default 22

如果 ~/.nvm 已存在,不要覆盖,加载其 nvm.sh 即可。在 ~/.bashrc 中添加以下两行,使新终端也能加载:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

nvm 是独立开源项目;GitHub 或 Node 下载受限时,先解决网络,或从 Node 官方下载页 取得对应 Linux 架构的 Node 22 并按其说明安装。不要把 Windows Node 误当作 WSL Linux Node。先检查:

node --version
npm --version
mkdir -p ~/robot_docs_demo
cd ~/robot_docs_demo
npm init -y
npm install --save-dev --save-exact @antora/cli@3.2.0 @antora/site-generator@3.2.0
npx antora --version

3.2.0 是原对话安装输出记录的版本。若所用 npm 源不能提供该版本,先 npm view @antora/cli versions --jsonnpm view @antora/site-generator versions --json,选择二者共同支持的版本,再将两个版本同步替换。不要只替换 CLI。--save-exactpackage-lock.json 让依赖可重现;CI 后续使用 npm ci 严格按 lock 文件安装。

5.2. 建立 ABB 内容组件

mkdir -p abb/modules/ROOT/{pages,images}

创建 abb/antora.yml

name: abb
title: ABB机器人
version: '1.0'
nav:
  - modules/ROOT/nav.adoc

创建 abb/modules/ROOT/pages/index.adoc

= ABB 机器人文档

欢迎使用 ABB 集成实验文档。

== 文档入口

* xref:network.adoc[网络配置]
* xref:faq.adoc[常见问题]

创建 abb/modules/ROOT/pages/network.adoc

= 网络配置

== 准备工作

NOTE: 以下是文档站点演示内容,机器人参数须依据现场实际配置。

== 操作步骤

. 核对电脑与控制器的地址。
. 检查网络连通性。
. 保存配置并记录变更。

WARNING: 修改现场设备地址前应确认停机与操作授权。

创建 abb/modules/ROOT/pages/faq.adoc

= 常见问题

== 页面无法访问

先区分网页服务是否运行,以及页面文件是否生成。

创建 abb/modules/ROOT/nav.adoc

* xref:index.adoc[ABB简介]
* xref:network.adoc[网络配置]
* xref:faq.adoc[常见问题]

name 是组件标识,title 是显示名称,version 是文档版本;ROOT 是默认模块,pages 放页面,images 放图片,nav.adoc 决定左侧导航。右侧页内目录由正文标题生成。

5.3. 将内容纳入 Git

cd ~/robot_docs_demo/abb
git init -b v1.0
git config user.name "Your Name"
git config user.email "you@example.com"
git add antora.yml modules
git commit -m "Add ABB 1.0 documentation"
cd ~/robot_docs_demo

本地内容源也需要 Git 仓库和至少一次提交;Antora 通过 Git 识别版本。这里仅初始化 abb,不要把包含其他子仓库、node_modules 与构建结果的整个 Demo 目录当成内容仓库。

5.4. 配置本地 playbook

创建 ~/robot_docs_demo/antora-playbook.yml

site:
  title: 机器人技术文档中心
  start_page: abb::index.adoc
content:
  sources:
    - url: ./abb
      branches: [v1.0]
ui:
  bundle:
    url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/master/raw/build/ui-bundle.zip?job=bundle-stable
    snapshot: true
output:
  dir: ./build/site

sitecontentuioutput 是平级配置。ui.bundle.url 必须是字符串。UI 下载受限时,在可联网环境取得默认 UI bundle,放到 ui/ui-bundle.zip,将 URL 改为 ./ui/ui-bundle.zip,并把文件纳入 Site 仓库以供 CI 使用。

npx antora --clean antora-playbook.yml
find build/site -name index.html
python3 -m http.server 8080 --directory build/site

打开 Windows 浏览器 http://localhost:8080/abb/1.0/index.html。该服务运行于 WSL,不能假定 VM 的 192.168.179.129:8080 也可访问。终端关闭或按 Ctrl+C 后,临时服务会停止。

验收:能看到 ABB 首页、左侧导航、代码和提示框;日志出现 Site generation complete!。如果只显示目录列表,检查是否错误地从 ~/robot_docs_demo 而非 build/site 提供服务。

6. 阶段五:练习图片、正文结构与跨页引用

将图片复制到 abb/modules/ROOT/images/,页面中使用:

image::network-example.png[网络配置示例,width=900]

== 网络检查

=== 地址核对

NOTE: 图片资源和页面一起纳入 Git,才能在 CI 中构建。

参见 xref:faq.adoc[常见问题]。

image:: 是块图片;不要用文件系统相对路径 ../images/ 代替 Antora 资源引用。本教程自身的图片位于 images/tutorial/,可直接验证图片资源解析:

ABB 文档页面原始实验截图
Figure 1. 原对话截图:ABB 页面由 Nginx 提供,正文与导航正常显示

截图直接复制自原对话资源,保持原样;其内容是当时状态,不能代替新环境的验收。本文代码块中的 AsciiDoc 图片示例属于示例文本,不会被当成实际资源加载。

7. 阶段六:增加 FANUC 与 ABB 多版本

7.1. 创建 FANUC 组件

WSL

cd ~/robot_docs_demo
mkdir -p fanuc/modules/ROOT/{pages,images}

fanuc/antora.yml

name: fanuc
title: FANUC机器人
version: '1.0'
nav:
  - modules/ROOT/nav.adoc

fanuc/modules/ROOT/pages/index.adoc

= FANUC 机器人文档

本组件用于演示 FANUC R-30iB 文档的独立管理。

== 入门

内容按控制器和程序版本逐步完善。

fanuc/modules/ROOT/nav.adoc

* xref:index.adoc[FANUC简介]
cd ~/robot_docs_demo/fanuc
git init -b v1.0
git config user.name "Your Name"
git config user.email "you@example.com"
git add antora.yml modules
git commit -m "Add FANUC 1.0 documentation"

7.2. 创建 ABB 1.1 分支

cd ~/robot_docs_demo/abb
git switch -c v1.1
nano antora.yml

version 改为 '1.1',编辑该版本页面,再提交:

git add antora.yml modules
git commit -m "Add ABB documentation version 1.1"
git show v1.0:antora.yml
git show v1.1:antora.yml

两个分支分别必须声明 1.0、1.1;仅创建分支但不修改 version,可能导致组件版本重复。分支名 v1.1 和文档版本 '1.1' 不必相同;网站路径由组件版本决定,本文预期 /abb/1.1/,不带 v

将 playbook 的 content 部分改为:

content:
  sources:
    - url: ./abb
      branches: [v1.0, v1.1]
    - url: ./fanuc
      branches: [v1.0]
cd ~/robot_docs_demo
npx antora --clean antora-playbook.yml
find build/site -maxdepth 3 -name index.html

验收:组件选择器中有 ABB 1.0、ABB 1.1、FANUC 1.0,版本切换后 URL 与正文对应。改版本名后使用 --clean 清理 构建输出,避免旧 /abb/v1.0/ 路径残留。

8. 阶段七:在 GitLab 创建 Group 与内容仓库

8.1. 创建空项目

浏览器进入 GitLab:

  1. 创建 Group robot-docs

  2. 在该 Group 下创建 Private 空项目 robot-docs-abb

  3. 同样创建 robot-docs-fanuc

  4. 暂不初始化 README,避免与已有本地仓库产生独立历史。

  5. 检查 Project name 和 Project slug 都与目标名称一致;只修改显示名不一定同步修改 slug。

8.2. 配置 GitLab SSH 密钥并推送

WSL 创建专用于本实验的密钥,已有合适密钥可复用:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_robot_docs -C "robot-docs-lab"
cat ~/.ssh/id_ed25519_robot_docs.pub

.pub 公钥添加到 GitLab 用户的 SSH Keys。私钥不要上传。可在 ~/.ssh/config 追加:

Host robot-docs-gitlab
  HostName 192.168.179.129
  User git
  Port 2222
  IdentityFile ~/.ssh/id_ed25519_robot_docs
  IdentitiesOnly yes
ssh -T robot-docs-gitlab
cd ~/robot_docs_demo/abb
git remote add origin robot-docs-gitlab:robot-docs/robot-docs-abb.git
git push -u origin v1.0
git push -u origin v1.1
cd ~/robot_docs_demo/fanuc
git remote add origin robot-docs-gitlab:robot-docs/robot-docs-fanuc.git
git push -u origin v1.0

若已有 origin,先 git remote -v 核对,再使用 git remote set-url origin …​。首推后检查 GitLab 默认分支、分支保护状态与用户权限;它们可能影响后续变量和 CI。

SSH 的标准完整 URL 形式是 ssh://git@192.168.179.129:2222/robot-docs/robot-docs-abb.git。此时 2222 是端口;不要在不带 ssh:// 的普通 SCP 形式中把它误写成目录。

验收:GitLab 中能浏览 ABB 两个版本分支及 FANUC 的 v1.0;WSL push 成功。

9. 阶段八:让 Antora 读取私有 GitLab 仓库

9.1. 项目 Deploy Token 与 Group Deploy Token

先理解三类凭据:

凭据 用途

用户 SSH Key

编辑者 clone / push 内容仓库。

Deploy Token

Antora 只读获取私有仓库;项目级覆盖一个项目,Group 级覆盖该组的项目。

Runner authentication token

Runner 向 GitLab 认证,通常是 glrt-…​;不是 Git 仓库读取 Token。

原实验先为 ABB 使用项目 Deploy Token,加入 FANUC 后改为 Group Deploy Token。在 Group 的 Settings → Repository → Deploy tokens 创建只带 read_repository 权限的 Token,记录实际用户名和 Token。只有需要读取镜像仓库时才增加对应 registry 权限。

删除或撤销旧项目 Token 前,应先验证 Group Token 能读取所有目标仓库。Group Token 的读取范围更广,应限制使用者与有效期。

9.2. 修改远程内容源

将 playbook 中 content 替换为:

content:
  sources:
    - url: http://192.168.179.129:8181/robot-docs/robot-docs-abb.git
      branches: [v1.0, v1.1]
    - url: http://192.168.179.129:8181/robot-docs/robot-docs-fanuc.git
      branches: [v1.0]

Token 不应写入 URL 并提交。原实验通过 GIT_CREDENTIALS 提供认证,下面用交互输入避免真实 Token 留在命令历史中:

cd ~/robot_docs_demo
read -r -p 'Deploy token username: ' ANTORA_GIT_USERNAME
read -r -s -p 'Deploy token: ' ANTORA_GIT_TOKEN
printf '\n'
GIT_CREDENTIALS="http://${ANTORA_GIT_USERNAME}:${ANTORA_GIT_TOKEN}@192.168.179.129:8181" \
  npx antora --fetch --clean antora-playbook.yml
unset ANTORA_GIT_USERNAME ANTORA_GIT_TOKEN

用户名和 Token 如包含 URL 保留字符,应先按 URL userinfo 规则编码。默认 GitLab 生成的 Deploy Token 通常适用于该形式。若改用凭据文件,限制权限并放在仓库外,通过 GIT_CREDENTIALS_PATH 指定,避免提交到 Git。

Antora 自己负责仓库获取,不要假定命令行 Git 已登录就等于 Antora 已认证。参见 Antora 私有仓库认证

验收:本地构建能生成 ABB 与 FANUC。若有疑问,可临时改名本地 ABB 目录,验证仍从远端构建,之后恢复目录。--fetch 刷新远端内容,--clean 清理输出,两者作用不同。

10. 阶段九:独立网站总控仓库 robot-docs-site

在 GitLab Group 中创建 Private *空*项目 robot-docs-site,检查 slug 正确。

WSL

mkdir -p ~/robot_docs_site
cd ~/robot_docs_site
cp ~/robot_docs_demo/antora-playbook.yml .
cp ~/robot_docs_demo/package.json .
cp ~/robot_docs_demo/package-lock.json .

如果使用本地 UI bundle,也复制 ui/。创建 .gitignore

node_modules/
build/
.cache/
*.log
.env
.git-credentials

此时总控仓库只保存构建配置与依赖锁定,不复制 ABB/FANUC 子仓库。node_modules/ 能从 lock 文件还原,build/ 是生成结果,二者不进入 Git。

npm ci
git init -b main
git config user.name "Your Name"
git config user.email "you@example.com"
git add .gitignore antora-playbook.yml package.json package-lock.json
git commit -m "Initial Antora site configuration"
git remote add origin robot-docs-gitlab:robot-docs/robot-docs-site.git
git push -u origin main

验收:GitLab 总控仓库只有站点配置、package 文件等,没有 node_modulesbuild。内容仓库和总控仓库各自拥有独立 Git 历史。

11. 阶段十:启动并注册 GitLab Runner

11.1. 在 GitLab 创建 Runner

进入 Site 项目的 Settings → CI/CD → Runners,创建 Project Runner(界面名称随版本可能变化):

  • Description:antora-docker-runner

  • Tags:antora,docker,用逗号分隔,不能写成一个 antora docker 标签。

  • 原实验勾选 Run untagged jobs;本文最终 CI 也显式写两个 tags。

  • 实验初期不暂停;若启用 Protected,任务分支必须满足保护条件。

记录创建后显示的 glrt-…​ authentication token。新注册流程是在 GitLab 创建 Runner 后使用该 Token,不应把旧 registration token 和 --registration-token 流程混用。

11.2. 在 Ubuntu Server 启动 Runner 容器

sudo mkdir -p /srv/gitlab-runner/config
docker run -d \
  --name gitlab-runner \
  --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  docker.1ms.run/gitlab/gitlab-runner:latest
docker exec -it gitlab-runner gitlab-runner register

交互输入:

提示 填写

GitLab instance URL

http://192.168.179.129:8181

Token(若询问)

GitLab 刚创建的 authentication token

Runner name

antora-docker-runner

Executor

docker

Default Docker image

docker.1ms.run/library/node:22

按照实际版本提示填写;也可使用 GitLab 创建页给出的 register --url …​ --token …​ 命令,但不要把实际 Token 保存到脚本、聊天或教程里。

docker exec gitlab-runner gitlab-runner --version
docker exec gitlab-runner gitlab-runner verify
docker logs --tail 80 gitlab-runner

docker.sock 让 Runner 能通过宿主机 Docker 创建 Job 容器,不是 Docker-in-Docker。本实验不要求 Job 挂载这个 socket,也不要求 privileged = true。Runner 本身拥有强大的主机权限,应只用于可信项目。

config.toml 中保存实际认证 Token,不要把完整配置输出到公开日志。参见 Runner 注册文档

验收:GitLab 显示 Online / Idle;Idle 表示在线等待任务。Project Runner 分配给 Site 即可,内容仓库仅使用 trigger job 时不需要执行脚本的 Runner。

12. 阶段十一:修复 helper image 与首次镜像下载

12.1. helper image 为什么必须能下载

Docker executor 启动业务镜像之前,使用 helper image 完成源码获取、缓存和 Artifact 上传下载。原实验 Runner 已在线,但 Job 卡在:

Using helper image:
registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:x86_64-v19.3.3
Pulling docker image registry.gitlab.com/...

这是 GitLab Registry 拉取问题,不是 Node、Antora 或 CI script 的问题。

Ubuntu Server 查版本与架构,并预拉取 *匹配*的 helper:

docker exec gitlab-runner gitlab-runner --version
uname -m
docker pull docker.1ms.run/gitlab/gitlab-runner-helper:x86_64-v19.3.3
docker pull docker.1ms.run/library/node:22

x86_64-v19.3.3 是原实验值。若 Runner 为其他版本或 ARM,按实际 helper 标签替换,不要复制这个标签搭配不同版本 Runner。

12.2. 修改 config.toml

sudo nano /srv/gitlab-runner/config/config.toml

在现有 Runner 的 [runners.docker] 段内修改或添加,保留已生成的 URL、Token 等字段,不重复创建第二个段:

[runners.docker]
  image = "docker.1ms.run/library/node:22"
  helper_image = "docker.1ms.run/gitlab/gitlab-runner-helper:x86_64-v19.3.3"
  pull_policy = "if-not-present"
  privileged = false
  volumes = ["/cache"]
docker restart gitlab-runner

if-not-present 优先使用本地存在的镜像,不会自动更新浮动标签。它适合此独立实验机的网络情况,长期运行应采用明确版本/摘要及可控更新流程。升级 Runner 时同步更新 helper;很多 Runner 配置能自动重载,此处重启是便于实验确认。

验收:Job 日志显示已使用配置的 helper,之后开始拉取 Node。如果卡点转移到 Node 的下载,说明 helper 这一层已经通过。首次 Node 下载曾耗时较长,应观察日志和拉取进度,不能仅凭时间长判定挂死。

13. 阶段十二:第一个 CI Job

WSL 的 Site 仓库 创建 .gitlab-ci.yml

stages: [test]
test-ci:
  stage: test
  image: docker.1ms.run/library/node:22
  tags: [antora, docker]
  script:
    - echo "GitLab CI Runner is working."
    - node --version
    - npm --version
cd ~/robot_docs_site
git add .gitlab-ci.yml
git commit -m "Add initial GitLab CI test"
git push

进入 Build → Pipelines,打开这次提交的 Pipeline,再打开 test-ci 看日志。正常顺序是 Runner 领取任务、准备 Docker executor、helper、Node 镜像、获取代码、执行 script。

验收:日志打印 Node/npm 版本,出现 Job succeeded,Pipeline Passed。历史失败 Pipeline 不必删除;以本次提交为准。

14. 阶段十三:配置 CI 变量与 Antora 构建 Artifact

14.1. 保存只读认证变量

Site 项目 Settings → CI/CD → Variables:

Key 设置

ANTORA_GIT_USERNAME

Group Deploy Token 实际用户名

可 Visible;关闭 Expand variable reference。

ANTORA_GIT_TOKEN

Group Deploy Token

Masked,支持时 Hidden;关闭 Expand variable reference。

实验中的分支若未保护,Protected 变量不会提供给相应任务。应使变量与分支保护策略一致:初期可按原实验不勾 Protected,稳定后保护 main 与需要发布的分支,再启用对应保护。不要为了解决缺变量,把 Token 写进 playbook。

14.2. 构建 Job

将测试配置替换成:

stages: [build]
build-docs:
  stage: build
  image: docker.1ms.run/library/node:22
  tags: [antora, docker]
  script:
    - test -n "$ANTORA_GIT_USERNAME"
    - test -n "$ANTORA_GIT_TOKEN"
    - npm ci
    - GIT_CREDENTIALS="http://${ANTORA_GIT_USERNAME}:${ANTORA_GIT_TOKEN}@192.168.179.129:8181" npx antora --fetch --clean antora-playbook.yml
    - test -f build/site/index.html
    - ls -la build/site
  artifacts:
    name: "robot-docs-site-$CI_COMMIT_SHORT_SHA"
    paths:
      - build/site/
    expire_in: 7 days

提交后观察构建日志:

git add .gitlab-ci.yml
git commit -m "Build Antora site in GitLab CI"
git push
Antora 构建与 Artifact 成功日志
Figure 2. 原对话截图:Antora 构建成功,网页作为 Artifact 上传

14.3. Artifact、仓库与网站的区别

仓库存的是 .adoc 和配置;Artifact 是 Job 执行结束前上传给 GitLab 的 build/site/ 成品包。Job 容器是临时的,没有 Artifact,构建结果会随着临时环境清理而失去。

build-docs 的 Job 页面,右侧 Job artifacts 下可以 Browse / Download;这些按钮不在项目仓库首页。Browse 便于核对生成文件,Download 下载成品包,Keep 可保留该次产物。expire_in 是过期配置,具体保留还受 GitLab “保留最近成功产物”等设置影响。

Artifact 不等于已经发布的网站,也不是 cache:Artifact 用于交付本次结果,cache 通常用于复用依赖、减少下载。

验收:构建成功,Artifact 中有 ABB 1.0、ABB 1.1、FANUC 1.0 和入口 index.html;此时还不能仅凭构建成功就认定 VM 的 8080 网站已更新。

15. 阶段十四:部署 Nginx 并验证目录挂载

Ubuntu Server

sudo mkdir -p /srv/robot-docs-site
printf '<h1>Hello Robot Documentation!</h1>\n' | sudo tee /srv/robot-docs-site/index.html
docker pull docker.1ms.run/library/nginx:alpine
docker run -d \
  --name robot-docs-nginx \
  --restart always \
  -p 8080:80 \
  -v /srv/robot-docs-site:/usr/share/nginx/html:ro \
  docker.1ms.run/library/nginx:alpine
docker ps
curl -I http://127.0.0.1:8080/

浏览器访问 http://192.168.179.129:8080,应看到测试标题。

Nginx 初次目录挂载测试
Figure 3. 原对话截图:首次 Nginx 测试页面

8080:80 表示主机 8080 转发到容器 80;卷把主机目录作为 Nginx 的网页根目录;:ro 表示 Nginx 只能读取这个挂载。CI 或宿主机仍可写入同一目录。

Nginx 只处理请求并读取文件,不解析 AsciiDoc、不替代 Antora,也不执行 CI。更新静态网页通常不需要重启;修改 Nginx 配置才需要测试并 reload。GitLab 容器内部也有 Nginx,但它与这里的 robot-docs-nginx 是两个独立用途。

验收:VM 上长期运行 gitlab、gitlab-runner、robot-docs-nginx;Windows 能看到 VM 8080 测试页,关闭 WSL 预览服务不影响它。

16. 阶段十五:让 CI 自动部署到 Nginx

16.1. 让 Job 可写发布目录

Ubuntu Server 修改 Runner 的 config.toml 中原有 volumes:

volumes = [
  "/cache",
  "/srv/robot-docs-site:/srv/robot-docs-site"
]
docker restart gitlab-runner

该设置决定 Job 容器 的挂载,而不是只给 Runner 管理容器增加卷。目录映射最终是:

宿主机 /srv/robot-docs-site
  ├─ Nginx /usr/share/nginx/html(只读)
  └─ Job   /srv/robot-docs-site(可写)

本实验应将有发布目录权限的 Runner 限制给可信 Site 项目;否则使用这个 Runner 的其他 Job 也能改网站目录。

16.2. 完整 Site CI 配置

将 Site 的 .gitlab-ci.yml 改为以下完整版本。沿用原实验的直接复制部署,并增加入口检查与部署互斥:

stages:
  - build
  - deploy

default:
  image: docker.1ms.run/library/node:22
  tags: [antora, docker]

build-docs:
  stage: build
  script:
    - test -n "$ANTORA_GIT_USERNAME"
    - test -n "$ANTORA_GIT_TOKEN"
    - npm ci
    - GIT_CREDENTIALS="http://${ANTORA_GIT_USERNAME}:${ANTORA_GIT_TOKEN}@192.168.179.129:8181" npx antora --fetch --clean antora-playbook.yml
    - test -f build/site/index.html
  artifacts:
    name: "robot-docs-site-$CI_COMMIT_SHORT_SHA"
    paths:
      - build/site/
    expire_in: 7 days

deploy-docs:
  stage: deploy
  resource_group: robot-docs-production
  needs:
    - job: build-docs
      artifacts: true
  script:
    - test -f build/site/index.html
    - test -d /srv/robot-docs-site
    - mountpoint -q /srv/robot-docs-site
    - test -w /srv/robot-docs-site
    - find /srv/robot-docs-site -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
    - cp -a build/site/. /srv/robot-docs-site/
    - test -f /srv/robot-docs-site/index.html
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  environment:
    name: production
    url: http://192.168.179.129:8080

needs: artifacts: true 将 build-docs 的产物下载到部署 Job,两个 Job 不共享临时工作目录。rules 限制只有 main 部署;environment 登记部署环境,不会自动启动服务器。resource_group 防止本项目的部署 Job 同时复制目录,但不会自动保证所有排队 Pipeline 按内容新旧顺序发布。

清理命令会删除指定发布目录中的旧网站,必须确认固定路径是专用发布目录。mountpoint 检查用于阻止未正确挂载时向 Job 内部普通目录误部署。该方式不是原子发布:复制期间可能短暂缺文件,复制失败也可能留下不完整网站。构建失败时部署不会运行,旧网站保留;不能把这一性质扩展为“所有部署失败都能保留旧网站”。正式环境可进一步采用 release 目录、原子切换和回滚方案。

cp -a build/site/. 包含隐藏文件,避免 build/site/* 漏复制。Node 镜像默认以 root 运行时通常能写入;若自定义非 root 用户,需按实际 UID/GID 配置目录权限,不建议以 chmod 777 掩盖问题。

提交与验证:

cd ~/robot_docs_site
git add .gitlab-ci.yml
git commit -m "Deploy Antora site to Nginx"
git push
Build Deploy 两阶段成功的 Pipeline
Figure 4. 原对话截图:Pipeline 的 Build 与 Deploy 均通过

验收:Build 和 Deploy Passed;VM 8080 的测试标题变成真正 Antora 页面;/abb/1.1/network.html/fanuc/1.0/index.html 可访问。无需重启 Nginx。

17. 阶段十六:内容仓库更新触发 Site Pipeline

17.1. 在 ABB v1.1 添加触发 Job

WSL

cd ~/robot_docs_demo/abb
git switch v1.1
nano .gitlab-ci.yml

先用独立配置理解触发机制:

stages: [trigger]
trigger-site-build:
  stage: trigger
  trigger:
    project: robot-docs/robot-docs-site
    branch: main
    strategy: mirror
  rules:
    - if: '$CI_COMMIT_BRANCH =~ /^v[0-9]+\.[0-9]+$/'

trigger 是 GitLab 调度的多项目触发任务,不是运行 curl 的脚本 Job,因此不需要 Docker 镜像或 Runner tags。它以触发 Pipeline 用户的权限创建下游 Pipeline;该用户必须有权限在 Site 的 main 上运行 Pipeline。Deploy Token 只解决 Antora 仓库读取,不能替代这个用户权限。

strategy: mirror 让上游任务等待下游并镜像成功/失败状态,要求 GitLab 18.2+;较旧实例可按其版本使用 depend,语义并不完全相同,需参考对应版本文档。本规则只匹配 v1.0、v1.1 等分支,不匹配 main、v1.0.1 或 Tag。

git add .gitlab-ci.yml
git commit -m "Trigger site pipeline from ABB version branch"
nano modules/ROOT/pages/network.adoc

在 network.adoc 增加:

== 自动发布测试

本段内容通过 GitLab CI/CD 自动构建并部署。
git add modules/ROOT/pages/network.adoc
git commit -m "Verify ABB automatic publication"
git push
ABB 内容更新自动触发发布的验证
Figure 5. 原对话截图:只更新 ABB 内容后,网站出现自动发布测试文字

验收:ABB Pipeline 的 trigger job 指向 Site 下游 Pipeline,Site Build/Deploy 成功,刷新 Nginx 页面可见新段落。检查 Site playbook 确实包含被更新的分支,否则触发成功也不会收录该分支。

18. 阶段十七:将触发配置覆盖 ABB 1.0 和 FANUC

18.1. ABB 分支间同步与 cherry-pick

先确定待同步提交只包含目标 CI 配置,避免把 ABB 1.1 的正文和 version 一并带到 1.0:

cd ~/robot_docs_demo/abb
git status
git log --oneline -- .gitlab-ci.yml
git show --stat <待同步提交SHA>
git switch v1.0
git cherry-pick <待同步提交SHA>

尖括号处替换为实际提交 SHA;不要原样执行占位符。

原实验出现 modify/delete 冲突:

CONFLICT (modify/delete): .gitlab-ci.yml deleted in HEAD
and modified in 94b84e6

当前 v1.0 没有文件,但引入提交修改了文件,Git 无法替你决定保留还是删除。目标是给 v1.0 增加 CI,因此检查工作区已有文件并选择保留:

git status
cat .gitlab-ci.yml
git add .gitlab-ci.yml
git cherry-pick --continue
git push

如目标确实是删除该文件,使用 git rm .gitlab-ci.yml 后 continue;如果选错提交或想撤销本次操作,使用 git cherry-pick --abort。普通内容冲突需要手工编辑,删除 <<<<<<<=======>>>>>>> 标记后再 add/continue。不要在未解决冲突时继续创建其他提交。

cherry-pick 删除与修改冲突的实际终端截图
Figure 6. 原对话截图:跨分支同步 CI 时的 modify/delete 冲突

只同步单文件时也可在确认没有未保存修改后,使用 git restore --source=v1.1 — .gitlab-ci.yml,再创建一个明确的新提交;这样不必引入混合提交。

18.2. FANUC 加入触发

FANUC 的 v1.0 增加同样的触发配置,提交后在其首页增加自动发布验证段落并 push。

cd ~/robot_docs_demo/fanuc
git switch v1.0
nano .gitlab-ci.yml
git add .gitlab-ci.yml
git commit -m "Trigger documentation site from FANUC"
git push
FANUC 自动发布成功的实际页面
Figure 7. 原对话截图:FANUC 页面出现自动发布验证内容

验收:ABB v1.0、ABB v1.1、FANUC v1.0 各自更新能触发 Site。发布三个版本依赖 playbook 明确列出三个来源,不是给任何分支 push 都会自动加入站点。

19. 阶段十八:统一共享 CI 模板

19.1. Site 创建共享模板

WSL 的 Site 仓库

cd ~/robot_docs_site
mkdir -p ci
nano ci/trigger-site.yml

ci/trigger-site.yml

stages:
  - trigger
trigger-site-build:
  stage: trigger
  trigger:
    project: robot-docs/robot-docs-site
    branch: main
    strategy: mirror
  rules:
    - if: '$CI_COMMIT_BRANCH =~ /^v[0-9]+\.[0-9]+$/'
git add ci/trigger-site.yml
git commit -m "Add shared documentation trigger CI template"
git push

Site 自己的根 .gitlab-ci.yml 仍是 Build/Deploy 配置,不引用这个触发模板,避免触发自身形成循环。

19.2. 内容仓库仅保留 include

将 ABB 的 v1.0、v1.1 和 FANUC v1.0 中 .gitlab-ci.yml 改为:

include:
  - project: robot-docs/robot-docs-site
    ref: main
    file: /ci/trigger-site.yml

逐个分支提交 push。各内容仓库无需再复制 trigger 的完整逻辑。

include:project 在创建 Pipeline 时从指定项目读取 YAML,用户必须能访问私有模板项目。ref: main 会使用当时 main 的模板;模板修改不会主动重跑所有内容 Pipeline,下一次内容更新才使用新模板。长期稳定使用可固定模板 Tag 或 SHA,升级时有计划地更新引用。

本模板声明 stages: [trigger] 适用于原实验的纯文档内容仓库;若以后加入其他 build/test job,需统一规划 stages,不能忽略 include 合并后阶段覆盖的问题。可在 GitLab CI Editor 的 Validate / 合并配置视图检查实际展开结果。

验收:三个版本分支都使用 include,内容修改仍能触发 Site Build/Deploy;Site Pipeline 不反向触发内容仓库。共享模板成功是原对话已经完成的实验终点。

共享模板阶段的 ABB 页面原始截图
Figure 8. 原对话截图:共享模板更新后,ABB 自动发布页面仍正常

20. 可选扩展:无版本门户首页

原对话提出了实验 19 首页方案,但没有完成后的验收截图。以下可选操作与已完成的自动发布链路分开。

Site 仓库新增 antora.yml

name: home
title: 机器人技术文档中心
version: ~
nav:
  - modules/ROOT/nav.adoc

新增 modules/ROOT/pages/index.adoc

= 机器人技术文档中心

== 选择机器人品牌

* xref:abb::index.adoc[ABB 机器人文档]
* xref:fanuc::index.adoc[FANUC 机器人文档]

新增 modules/ROOT/nav.adoc

* xref:index.adoc[文档中心首页]
* xref:gitlab-antora-ci-nginx-guide.adoc[从零搭建机器人技术文档中心]

playbook 改为 start_page: home::index.adoc,content.sources 增加:

    - url: .
      branches: HEAD

此本地来源读取当前 Site checkout(仓库必须已有提交),适用于本地与 CI;也可按原对话使用 Site 的远端 Git URL 和 main。保留 ABB/FANUC 两个来源。version: ~ 是无版本组件,页面路径不包含版本段;跨组件 xref 由 Antora 解析实际 URL。

先本地构建验收,再提交 push。首页方案、全文搜索、产品导航、UI 定制与 HTTPS 可逐项扩展,不能假定它们已经由当前模板自动实现。

21. 把本教程加入当前服务并验证功能

21.1. 最短接入方法:加入已存在内容组件

不必先完成 home 组件,可把本包放到 已被 playbook 读取的 ABB v1.1 中:

  1. 在内容仓库切到 v1.1,并确认工作区无未提交冲突。

  2. 复制包内 modules/ROOT/pages/gitlab-antora-ci-nginx-guide.adoc 到同名目录。

  3. 复制包内 modules/ROOT/images/tutorial/ 整个目录到同名目录。

  4. 在现有 modules/ROOT/nav.adoc 末尾增加下面这一行,不要覆盖原导航。

* xref:gitlab-antora-ci-nginx-guide.adoc[从零搭建机器人技术文档中心]
  1. 提交页面、图片和导航,push 后观察内容 Pipeline → Site Pipeline → Nginx。

git add modules/ROOT/pages/gitlab-antora-ci-nginx-guide.adoc modules/ROOT/images/tutorial modules/ROOT/nav.adoc
git commit -m "Add GitLab Antora CI Nginx tutorial"
git push

包内 README 和 examples 是接入说明及配置参考,不要求复制到页面目录。不要为了导入本文覆盖自己的 .gitlab-ci.yml、playbook 或 antora.yml

21.2. 验收表

检查 预期

上游 Pipeline

include 成功解析,trigger job 创建 Site 下游 Pipeline。

Site build-docs

npm ci、Antora 成功,日志无本文图片缺失错误。

Artifact

包含教程 HTML 及 _images/tutorial/ 资源。

Site deploy-docs

下载同一 build-docs 产物并完成部署。

浏览器

左侧出现教程入口,正文、表格、代码块、提示框和图片正常。

直接访问

ABB v1.1 接入时路径为 /abb/1.1/gitlab-antora-ci-nginx-guide.html

版本隔离

仅上传到 v1.1 时,v1.0 不会凭空拥有该页;需要另行同步。

服务持续性

关闭 WSL 临时预览后,VM 的 Nginx 网站仍可访问。

22. 典型故障与逐层排查

22.1. Docker 源或镜像仓库连接失败

curl: (35) Connection reset by peer 后又出现 chmod: cannot access docker.asc,先处理密钥下载失败;后面的文件不存在是连锁错误。不要继续配置一个引用不存在密钥的 APT 源。切换 Ubuntu 仓库安装路线前检查 /etc/apt/sources.list.d/,禁用自己刚添加且无效的 Docker 源,不要误删系统 Ubuntu 源。

Docker Hub 拉取失败:先检查 DNS、HTTPS,再检查 daemon 日志。ping 成功不代表 HTTPS Registry 可达。shell 的代理环境变量也不自动等于 Docker daemon 已使用代理;必要时按 Docker 服务的代理配置方式设置并验证。

GitLab Registry 拉取失败:观察是否是 helper image;Docker Hub 镜像代理可用不代表 GitLab Registry 可达。使用匹配 Runner 版本和架构的 helper 镜像,确认本地镜像名与配置完全一致。

22.2. Runner 注册失败或任务 Pending

docker ps --filter name=gitlab-runner
docker logs --tail 100 gitlab-runner
docker exec gitlab-runner gitlab-runner verify
curl -I http://192.168.179.129:8181/users/sign_in

403 或 Token 验证失败:确认使用新 authentication token、Runner 未删除、地址指向正确实例。不要重复创建多个 Runner 掩盖问题。

Pending:检查 Runner Online、是否分配给项目、是否暂停、Job tags 是否都匹配、是否允许 untagged,以及 Protected Runner 是否允许当前分支。antora docker 单标签与 antora,docker 两标签不同。

Job 已运行但长期卡在 Preparing:查看 helper 或业务镜像拉取日志;这不是 Pending。Runner 的 verify 仅证明认证通信,不证明整个 Docker executor 能完成 Job。

22.3. 私有仓库认证、变量与多项目权限

HTTP Basic: Access denied:检查 Deploy Token 用户名、过期状态、read_repository、Group 范围和 URL。CI 中变量为空:检查变量拼写、Environment scope、Protected 与分支保护条件。避免打开 set -x 或打印 GIT_CREDENTIALS

downstream pipeline cannot be created:确认 project 路径、main 存在,触发用户有权限,目标分支保护允许,且 Site 的 workflow/rules 没有排除 CI_PIPELINE_SOURCE == "pipeline"。跨项目 include 失败还要核对模板 file/ref 和读取权限。API Job Token allowlist 与用户权限是不同机制;本实验的原生 trigger 不需要先添加一个 curl API Token。

网站内容未更新:确认内容 commit 已 push 到 playbook 列出的分支;Site Job 使用 --fetch;检查 Site Pipeline 是否真的 deploy;最后才处理浏览器缓存。不要只看上游绿色就推断最终 HTML 一定包含目标内容。

22.4. YAML、组件与版本错误

site.content.sources not declared:content 错误缩进到了 site 下;它应是顶层键。ui.bundle.url must be String:检查 bundle/url 的层级和 URL 是否变成映射。

重复组件版本:检查每个分支 antora.yml 的 name/version 组合。分支名带 v 不代表输出 URL 一定带 v。原 /abb/v1.0/ 残留属于旧输出时,用 --clean;若部署时仅追加复制未删除旧文件,发布目录也可能残留。

22.5. Artifact 不存在或无法传递

No files to upload:检查 output.dir 是否为 build/site、Job 工作目录、Antora 是否成功,artifacts.paths 必须相对项目工作目录。不要把 output 改为 public 后还继续上传 build/site。

部署 Job 找不到 build/site:检查 needs 的 job 名和 artifacts: true,确认产物未过期。不同 Job 的临时目录独立存在,cache 不替代 Artifact 交付。

仓库首页没有 build/site 是预期行为,去 build-docs Job 的 Job artifacts 查看。Artifact 被删除不等于 Nginx 中已部署文件也自动删除;两者是不同存储。

22.6. Nginx 拒绝连接、403、404 与权限

Ubuntu Server

docker ps --filter name=robot-docs-nginx
docker port robot-docs-nginx
docker logs --tail 80 robot-docs-nginx
docker exec robot-docs-nginx nginx -t
docker exec robot-docs-nginx ls -la /usr/share/nginx/html
ls -la /srv/robot-docs-site
curl -I http://127.0.0.1:8080/
sudo ss -lntp
sudo ufw status

ERR_CONNECTION_REFUSED:检查容器与端口映射是否存在,网址是否指向正确机器;Windows localhost、WSL localhost 与 VM IP 不应混淆。如果 VM 本机可访问而 Windows 不可访问,检查 VMware 网络、地址变化与防火墙。若有 UFW,按可信局域网范围开放 8080/8181/2222,并保留管理 SSH 的规则。

403:检查网页入口文件、目录遍历权限与文件读取权限;不要仅确认目录存在。404:检查请求版本路径、实际 HTML 文件、部署是否成功。临时 Python 服务显示目录列表时,通常是提供服务的根目录错误。

Deploy Passed 但网站仍旧:先确认 Job 挂载和 Nginx 挂载指向同一个主机路径,检查服务器上文件时间与内容;最后使用强制刷新排除客户端缓存。写入失败时应核对 UID/GID、挂载的 rw/ro 和磁盘空间。

22.7. 磁盘空间与重启

df -h
docker system df
sudo du -sh /srv/gitlab/data /srv/gitlab/logs /var/lib/docker

根分区不足会导致镜像解压、GitLab 数据写入、Artifact 上传与部署失败。先判断数据类型和保留策略,再清理;不要执行带 volumes 的全局 prune 删除持久化数据。LVM 有空闲空间并不代表根文件系统已经扩容,扩容前核对实际逻辑卷名并备份。

服务器重启后检查三个容器是否恢复、IP 是否变化。若 IP 变化,external_url、Runner URL、playbook、凭据匹配地址和 environment URL 都需要一致调整。

23. 最终目录与日常工作流

WSL
~/robot_docs_demo/
├── abb/                     独立 Git 内容仓库
│   ├── antora.yml
│   ├── .gitlab-ci.yml        include 共享模板
│   └── modules/ROOT/
│       ├── nav.adoc
│       ├── pages/
│       └── images/
└── fanuc/                   独立 Git 内容仓库

~/robot_docs_site/            独立 Git 网站总控仓库
├── .gitignore
├── .gitlab-ci.yml            build + deploy
├── antora-playbook.yml
├── package.json
├── package-lock.json
├── ci/trigger-site.yml
├── node_modules/             不提交
└── build/site/               不提交,保存 Artifact

Ubuntu Server
/srv/gitlab/{config,logs,data}
/srv/gitlab-runner/config/config.toml
/srv/robot-docs-site/          Nginx 读取的发布目录

日常内容更新:切换正确版本分支 → 编辑 .adoc 与图片 → 检查差异 → commit/push → 观察上游/下游 Pipeline → 浏览器验收。增加新品牌时,创建新组件和内容仓库,配置版本与 include,并在 Site playbook 增加来源;仅复制 CI 模板不会自动把新品牌收录进网站。

修改站点依赖时同时更新 package.json 与 package-lock.json;修改共享模板时先验证展开配置;跨分支同步时确保不误改组件版本。失败构建先查看首个错误,不要被后续缺文件日志带偏。

24. 配套资源与参考

本文配套图片来自原对话 6aa79b5f-1388-83ee-b3e7-fe1d51fea405 的真实附件,位于 modules/ROOT/images/tutorial/。图片反映原实验,不包含重新部署结果。配置样例是在原实验基础上整理的参考文件,替换参数后再使用。

官方参考主要用于核对版本相关行为;本文完整实验顺序、操作说明和截图基于原对话重新编写。