# 铁路无人机智能巡检平台离线 Kubernetes 一键部署项目方案

> 文档状态：规划稿
> 编制日期：2026-07-25
> 适用仓库：AItrackwalker
> 目标：将当前 Docker Compose 部署演进为同时支持单机测试、多机生产、无公网环境的一键部署项目。

## 1. 结论摘要

建议把部署能力作为仓库内一个独立子项目建设，技术基线如下：

- Kubernetes 发行版采用 **RKE2**。单机与多机使用同一发行版、同一套自动化，仅通过 inventory 和 profile 切换。
- Linux 初始化和集群安装采用 **Ansible + 少量幂等 Shell 脚本**，业务和中间件采用 **Helm Chart** 管理。
- 交付物不是源码压缩包，而是一个经过校验和签名的 **离线发布包**。目标服务器不执行在线下载，也不现场编译前端、Java 或 Python 依赖。
- 提供两个正式部署档案：
  - `single`：一台服务器，面向测试、演示、功能验收，可选 CPU 或单 GPU。
  - `ha`：多台服务器，面向生产，至少 3 个 RKE2 server 节点，业务、存储和 GPU 工作负载按角色调度。
- 一键入口统一为 `deploy.sh`，内部依次完成预检、操作系统配置、Kubernetes 安装、离线镜像导入、基础组件安装、平台发布和验收。
- 初次部署采用 **RKE2 air-gap image tar + 节点预导入**；多节点启用 RKE2 内置分布式 OCI 镜像分发服务（Spegel），在集群节点之间共享已导入镜像。必须保留“所有节点预导入”作为兼容回退方案。
- 多节点不等于应用已经高可用。当前后端包含本机内存 SSE 订阅者、进程内异步任务和定时任务，`artifact-installer` 也存在单写者约束。第一阶段生产档案应保持后端和安装代理单副本；完成分布式任务和事件改造后，才能把后端扩展到 2 个以上副本。
- 当前模型安装代理写模型目录、推理服务只读共享模型目录。首期在多节点使用 RWX PVC 兼容现状；中期改造为“模型保存在 MinIO，推理 Pod 使用 initContainer/sidecar 下载到本地缓存”，解除共享文件系统对 GPU 横向扩展的限制。

建议先交付“单机离线可重复部署”，再交付“多机生产基线”，最后完成“应用级高可用与灾备演练”，不要在首个迭代中一次性叠加所有复杂度。

## 2. 范围、边界与术语

### 2.1 “无网络环境”的定义

本方案中的无网络是指 **目标环境无法访问互联网或公共软件仓库**，但仍需具备：

- 多节点之间的私有网络互通；
- 部署控制机到各节点的 SSH 连通；
- 内部 DNS，或由部署项目维护固定主机名解析；
- 内部 NTP/Chrony 时间源，或明确指定一台内网时间服务器；
- 用户访问平台所需的内网入口；
- 离线包通过移动介质、堡垒机、内网文件服务器或云硬盘快照进入目标环境。

如果服务器之间完全不能通信，则无法构成多节点 Kubernetes 集群。单机档案仍然可以工作。

### 2.2 “从 0 开始”的边界

默认起点是：

- 已创建 Linux 云主机或物理机；
- 已安装并启动项目锁定版本的 Linux；
- 已配置管理网 IP；
- 已提供具有 `sudo` 权限的部署账号；
- 数据盘已被操作系统识别，但可以尚未分区和挂载；
- 离线部署包已放到部署控制机。

可选扩展是把操作系统 ISO、云镜像、Kickstart/Autoinstall 和固件纳入离线包，实现裸机装机。云厂商 VM 创建、VPC、安全组、云硬盘和负载均衡器通常依赖厂商控制面，不纳入第一版通用一键部署。

### 2.3 “一键部署”的定义

一键部署不是一个不可维护的超大 Shell 文件，而是一个统一入口驱动可测试的多个幂等阶段：

```bash
sudo ./deploy.sh \
  --profile single \
  --inventory inventories/single/hosts.yml \
  --config config/site.yml \
  --secrets config/secrets.sops.yaml
```

一键入口必须支持：

- `--preflight-only`：只检查，不修改；
- `--dry-run`：输出计划，不执行发布；
- `--resume-from <phase>`：失败后从指定阶段继续；
- `--skip-app`：只部署 Kubernetes 基础设施；
- `--verify-only`：仅执行验收；
- `--upgrade <release>`：升级；
- `--rollback <release>`：回滚应用版本；
- 全程生成带时间戳的日志、节点检查报告和最终验收报告；
- 重复执行不破坏已有集群和数据。

### 2.4 安装期离线与运行期联网

安装期离线和业务运行期是否允许访问外部服务必须分别定义：

- `runtime_network_mode: isolated`
  - 完全内网运行；
  - 使用模拟无人机适配器或内网私有接口；
  - 外部 DJI 云接口不可用时必须显式降级或禁用，不能持续超时重试。
- `runtime_network_mode: restricted-egress`
  - 安装仍完全离线；
  - 运行期只允许通过内网代理或防火墙白名单访问已批准的 DJI/第三方域名和端口；
  - 所有外连必须可审计。

## 3. 当前项目部署基线

### 3.1 当前组件映射

| 当前组件 | 当前形态 | Kubernetes 目标形态 | 主要状态或约束 |
| --- | --- | --- | --- |
| `frontend` | Nginx 容器，端口 8088 | Deployment + Service + Ingress | Nginx 内部代理名 `backend` 可在同 namespace 继续使用；需要保留大文件和 SSE 超时配置 |
| `backend` | Spring Boot 3.3 / Java 17 | Deployment + Service | Flyway 自动迁移；包含本机 SSE、进程内异步任务和定时任务，当前不宜直接多副本 |
| `vision-inference` | FastAPI / ONNX Runtime CPU 或 CUDA | Deployment + Service | GPU 模式应申请明确的 `nvidia.com/gpu` 资源，不能照搬 Compose 的 `gpus: all` |
| `pointcloud-analysis` | FastAPI / Open3D / ONNX Runtime | Deployment + Service | CPU、内存和共享内存需求较高；点云大文件对网络和存储吞吐敏感 |
| `artifact-installer` | FastAPI，写模型目录 | Deployment + Service | 单写者；需要 RWX 模型卷，或后续改造为对象存储发布 |
| PostGIS 16-3.4 | 单容器 + 本地卷 | 测试用 StatefulSet；生产用 PostGIS HA 方案 | 保存核心业务数据，必须有独立备份和恢复演练 |
| Redis 7 | 单容器 | Deployment/StatefulSet | 当前代码未发现明显 Redis 数据访问；发布前确认是否仍为必需依赖 |
| Kafka 3.8.1 KRaft | 单 broker | 测试 1 broker；生产 3 broker | 当前事件在 Kafka 不可用时以数据库为权威，但生产仍需监控和持久化 |
| MinIO | 单容器 + 本地卷 | 测试单实例；生产分布式 Tenant | 保存样本、附件和模型制品，不能只依赖卷快照 |
| Prometheus | 单容器，仅抓取后端 | kube-prometheus-stack 或精简监控栈 | 需要补齐节点、K8s、GPU、数据库、Kafka、MinIO 告警 |
| 模型目录 | 主机目录共享 | 单机本地 PVC；多机 RWX PVC | 推理只读，安装代理可写；需版本化、摘要校验和回滚 |

### 3.2 不能直接把 Compose 翻译成 YAML 的问题

1. Compose 中的默认口令和 Token 不能进入生产 Kubernetes Secret。
2. 当前镜像 Dockerfile 会访问 Maven、npm、PyPI、APT 和公共镜像仓库，不能在离线目标机现场构建。
3. 后端的 SSE 订阅者保存在单进程内存中，异步模型任务也由当前进程执行。直接增加副本会出现事件丢失、重复任务或任务状态不一致。
4. 模型文件目前依赖共享文件系统路径，多 GPU 节点需要 RWX 存储或模型分发改造。
5. 当前 PostGIS、Kafka、MinIO 均为单实例，不具备节点故障后的服务连续性。
6. 当前前端和后端支持 2 GB 级上传，多一层 Ingress 后必须同步配置请求体、缓冲、空闲超时和 SSE。
7. Compose 的容器资源上限不能原样视为 Kubernetes 的合理 requests/limits，需要基于压测重定标。
8. 当前 Prometheus 只抓取后端，不能支撑生产告警、容量预测和故障定位。

## 4. 目标架构

```mermaid
flowchart TB
    subgraph Build["联网构建与发布区"]
        SRC["源码与模型制品"]
        CI["构建、测试、SBOM、漏洞扫描"]
        BUNDLE["离线发布包<br/>镜像/Chart/RKE2/OS 包/模型/校验清单"]
        SRC --> CI --> BUNDLE
    end

    BUNDLE -->|"移动介质或内网传输"| CTRL["离线部署控制机<br/>deploy.sh + Ansible + Helm"]

    subgraph Target["无公网目标环境"]
        CTRL --> CP["RKE2 server 节点<br/>1 台或 3 台"]
        CTRL --> WK["CPU/GPU worker 节点"]
        CP <-->|"内置镜像分发/预导入"| WK

        subgraph K8s["Kubernetes 集群"]
            INGRESS["内网 VIP / Ingress"]
            APP["Frontend + Backend"]
            AI["Vision + Pointcloud + Artifact Installer"]
            DATA["PostGIS + Kafka + Redis + MinIO"]
            OBS["Prometheus + Grafana + 日志 + GPU 监控"]
            STORAGE["本地盘 / Longhorn / 云 CSI"]
            INGRESS --> APP --> AI
            APP --> DATA
            AI --> DATA
            DATA --> STORAGE
            OBS -.监控.-> APP
            OBS -.监控.-> AI
            OBS -.监控.-> DATA
        end
    end
```

### 4.1 单机测试档案

单机部署一台 RKE2 server，控制面和业务工作负载共用节点。

建议初始规格：

| 场景 | CPU | 内存 | 数据盘 | GPU |
| --- | ---: | ---: | ---: | --- |
| CPU 功能测试 | 16 vCPU | 32 GB | 300 GB SSD | 无 |
| GPU 集成测试 | 24 vCPU | 64 GB | 500 GB SSD | 1 张、显存建议不低于 16 GB |

说明：

- 规格是当前 Compose 资源配置推导出的起始值，不是容量承诺，必须在真实模型和数据上压测。
- PostGIS、Kafka、MinIO、Redis 均为单副本。
- 存储使用 `local-path` 或独立本地 StorageClass。
- 入口使用节点 IP + Ingress 80/443，也可配置单个内网 VIP。
- 允许节点重启后自动恢复，但不承诺节点故障期间可用。

### 4.2 多机生产档案

生产控制面使用奇数个 RKE2 server，推荐 3 个，并提供固定 API 注册地址。

最小可用拓扑：

- 3 台混合节点，同时承担 control-plane、etcd、worker；
- 至少 2 台具有 NVIDIA GPU，避免单 GPU 节点成为唯一故障点；
- 每台至少两块盘：系统/容器盘与业务数据盘分离；
- 适合早期生产或资源受限场景，但 AI、存储 I/O 与 etcd 会争用资源。

推荐拓扑：

- 3 台独立 control-plane/etcd 节点；
- 3 台以上 worker/storage 节点；
- 2 台以上 GPU worker；
- 3 个独立存储故障域；
- 10 GbE 或经过实际影像、点云吞吐验证的等效网络；
- Kubernetes API 使用云内网负载均衡或 kube-vip；
- 业务入口使用云内网负载均衡，或 MetalLB + Ingress。

| 节点角色 | 推荐数量 | 主要工作负载 | 关键约束 |
| --- | ---: | --- | --- |
| `control-plane` | 3 | RKE2 server、etcd | 独立快速 SSD；禁止普通业务调度；低延迟互联 |
| `cpu-worker` | 2+ | 前端、后端、安装代理、运维组件 | 使用拓扑分散和反亲和 |
| `gpu-worker` | 2+ | 视觉、点云推理 | 相同 OS/kernel/驱动基线；标签和污点隔离 |
| `storage-worker` | 3+ | Longhorn、PostGIS、Kafka、MinIO | 独立原始数据盘；避免与高波动临时目录共盘 |

同一物理机可以有多个角色，但生产验收报告必须明确故障域和资源争用风险。

## 5. 技术选型

### 5.1 Kubernetes 发行版

| 方案 | 离线复杂度 | 单机/多机一致性 | 生产运维 | 结论 |
| --- | --- | --- | --- | --- |
| kubeadm + 自建 Ansible | 高 | 高 | 自由度最高，但要自行集成全部组件 | 不作为第一版 |
| Kubespray | 中高 | 高 | 功能全面，变量和依赖较多 | 可作为备选 |
| K3s | 低 | 高 | 更偏边缘和轻量场景 | 测试合适，生产基线不统一 |
| RKE2 | 中低 | 高 | 内置 containerd、etcd、CNI 和 air-gap 能力 | **选用** |

选择 RKE2 的原因：

- 官方提供 air-gap image tar、离线安装和升级路径；
- 单 server 与 3 server HA 使用同一套发行版；
- 内置 containerd，减少目标机额外安装 Docker 的依赖；
- 内置 Canal CNI、Ingress 等基础组件，减少离线镜像和兼容组合；
- 可启用内置分布式 OCI 镜像分发服务，降低多节点首次导入成本；
- 自带 etcd 快照工具。

所有版本必须写入 `versions.lock.yaml`，严禁使用 `latest`。第一阶段技术验证完成后冻结以下精确版本：

- Linux 发行版、补丁版本和内核；
- RKE2/Kubernetes；
- Helm、Ansible Core 和离线 collection；
- Longhorn、Ingress、监控栈、GPU Operator/Device Plugin；
- PostGIS、Kafka、Redis、MinIO；
- 平台所有镜像和模型版本。

### 5.2 操作系统基线

第一版只支持一种经过验证的 OS，建议：

- Ubuntu Server 22.04 LTS x86_64；
- 固定已验证的内核小版本；
- GPU 节点保持相同 OS 和内核；
- 禁止在未验证的系统自动升级内核；
- Rocky Linux、openEuler、ARM64 作为后续独立适配，不与第一版混装。

最终 OS 版本应在 GPU 驱动、云厂商镜像和安全维护周期三者之间确认后锁定。

### 5.3 CNI、入口和存储

- CNI：第一版使用 RKE2 默认 Canal，避免额外组合；后续确有可观测性或零信任需求再评估 Cilium。
- Kubernetes API：
  - 单机使用节点固定 IP；
  - 多机使用云内网 LB 或 kube-vip 的固定注册地址。
- 应用入口：
  - 云环境优先使用云内网 LB；
  - 无云 LB 时使用 MetalLB L2/BGP + RKE2 Ingress。
- 单机存储：本地 StorageClass。
- 多机通用存储：Longhorn 作为默认基线，也允许通过 profile 切换为经过验证的云 CSI 或外部存储。
- 模型共享卷：第一版使用 Longhorn RWX。所有可能挂载 RWX 卷的节点必须离线安装 NFSv4 客户端。

### 5.4 内部镜像仓库策略

第一版不强制引入 Harbor，避免为了部署平台先部署一套复杂的状态服务：

1. RKE2 自身镜像 tar 分发到所有节点。
2. 平台和基础组件镜像至少导入一个种子节点。
3. 多机启用 RKE2 内置分布式 OCI 镜像分发服务。
4. 核心状态组件镜像可以预导入所有可能调度的节点，作为确定性保障。
5. 如果内置镜像分发服务在现场网络或安全策略下验证不通过，切换为“全节点导入”。

后续需要长期管理多个集群、频繁增量发布或统一镜像审计时，再部署独立 Harbor。Harbor 本身必须位于独立故障域或先于业务集群可用，不能形成启动环依赖。

## 6. 部署子项目建议结构

建议在仓库新增 `deploy/`，大文件离线制品只进入发布产物，不进入 Git：

```text
deploy/
├── README.md
├── Makefile
├── versions.lock.yaml
├── config/
│   ├── site.example.yaml
│   ├── secrets.example.sops.yaml
│   └── schemas/
├── inventories/
│   ├── single/
│   │   └── hosts.example.yml
│   └── ha/
│       └── hosts.example.yml
├── ansible/
│   ├── ansible.cfg
│   ├── site.yml
│   ├── collections/
│   └── roles/
│       ├── preflight/
│       ├── os_baseline/
│       ├── disks/
│       ├── nvidia/
│       ├── rke2_server/
│       ├── rke2_agent/
│       ├── airgap_images/
│       └── postcheck/
├── charts/
│   └── trackwalker/
│       ├── Chart.yaml
│       ├── values-single.yaml
│       ├── values-ha.yaml
│       └── templates/
├── addons/
│   ├── longhorn/
│   ├── gpu/
│   ├── observability/
│   ├── postgis/
│   ├── kafka/
│   ├── minio/
│   └── redis/
├── scripts/
│   ├── build-offline-bundle.sh
│   ├── verify-bundle.sh
│   ├── deploy.sh
│   ├── verify.sh
│   ├── upgrade.sh
│   ├── rollback.sh
│   ├── backup.sh
│   └── restore.sh
├── tests/
│   ├── molecule/
│   ├── helm/
│   └── e2e/
└── docs/
    ├── install-single.md
    ├── install-ha.md
    ├── operations.md
    ├── backup-restore.md
    └── troubleshooting.md
```

`deploy/artifacts/`、生成后的 bundle、真实 inventory 和秘密文件必须加入 `.gitignore`。

## 7. 离线发布包设计

### 7.1 发布区与目标区

离线方案至少包含两个流程：

1. **联网发布工厂**
   - 拉取锁定版本依赖；
   - 构建并测试平台镜像；
   - 下载 RKE2、Chart、OS 软件包和安全扫描数据库；
   - 收集模型；
   - 生成清单、摘要、SBOM 和签名；
   - 在隔离测试环境完成一次无网安装。
2. **离线目标安装**
   - 验证发布包完整性和签名；
   - 不访问公网；
   - 不执行源码构建；
   - 只消费离线包内已批准制品。

### 7.2 离线包内容

```text
trackwalker-offline-<release>-linux-amd64/
├── bundle.manifest.yaml
├── versions.lock.yaml
├── SHA256SUMS
├── SHA256SUMS.sig
├── RELEASE-NOTES.md
├── tools/
│   ├── deploy.sh
│   ├── ansible/
│   ├── helm
│   ├── kubectl
│   ├── yq
│   ├── sops
│   └── cosign
├── rke2/
│   ├── install.sh
│   ├── rke2.linux-amd64.tar.gz
│   ├── rke2-images.linux-amd64.tar.zst
│   └── sha256sum-amd64.txt
├── os-packages/
│   ├── common/
│   ├── nfs/
│   └── nvidia/
├── images/
│   ├── platform-images.oci.tar.zst
│   ├── addon-images.oci.tar.zst
│   └── image-index.yaml
├── charts/
│   ├── trackwalker-<version>.tgz
│   └── third-party/
├── models/
│   ├── model-manifest.yaml
│   └── artifacts/
├── security/
│   ├── sbom/
│   ├── scan-reports/
│   └── trivy-db/
├── config/
│   └── examples/
└── docs/
```

### 7.3 发布包规则

- 每个文件都有 SHA-256；
- 整个摘要清单使用离线信任的发布密钥签名；
- OCI 镜像使用 digest 锁定，Helm values 中不允许浮动 tag；
- 每个镜像生成 CycloneDX 或 SPDX SBOM；
- 漏洞扫描使用打包日期对应的离线数据库，报告必须记录数据库时间；
- 模型单独记录模型组、版本、文件名、大小、SHA-256、标签摘要和兼容运行时；
- 记录第三方许可证和可再分发性，尤其是 NVIDIA 驱动、CUDA 和商业模型；
- 大模型允许作为单独的 `model-pack`，平台包与模型包分别签名；
- 发布包支持分卷，且合并后再次校验；
- 生成机器、时间、Git commit、构建流水线编号和制品来源必须可追溯。

### 7.4 镜像构建要求

- 前端、后端和 3 个 AI 服务在联网 CI 中完成多阶段构建和测试；
- 目标环境禁止运行 `npm ci`、`mvn package`、`pip install` 或 `apt-get`；
- 生产运行镜像与构建镜像分离，后端运行镜像不应继续使用 Maven 基础镜像；
- 运行容器尽量使用非 root 用户、只读根文件系统和最小 Linux capability；
- Dockerfile 中的镜像加速地址只影响联网构建区，不应成为运行期依赖；
- CPU 和 GPU 镜像分别标记并锁定；
- 在 CI 中对镜像执行架构、CUDA、ONNX Runtime、Open3D 和健康接口测试。

## 8. 服务器初始化设计

### 8.1 部署前输入

现场只需要填写并审核：

- 节点管理 IP、主机名、SSH 用户和角色；
- Kubernetes API 固定地址；
- Pod CIDR、Service CIDR，且不得与 VPC、VPN、铁路内网和现场设备网段冲突；
- Ingress VIP、域名和 TLS 策略；
- 数据盘设备名和挂载规划；
- GPU 节点及设备策略；
- 内部 DNS、NTP、代理和运行期网络模式；
- 存储类型；
- 资源配额、数据保留周期、备份地址；
- 加密后的秘密文件。

### 8.2 自动预检

预检至少覆盖：

- OS、架构、内核和软件包版本；
- CPU、内存、磁盘容量、磁盘类型、inode 和挂载点；
- 唯一主机名、machine-id、MAC 和节点 IP；
- 节点互通、MTU、时延、丢包和所需端口；
- 是否存在默认路由。即使没有互联网，RKE2 也需要可识别主 IP 的默认路由；必要时由自动化配置持久化 dummy route；
- DNS 正反向解析和时间同步偏差；
- Pod/Service CIDR 冲突；
- 防火墙、AppArmor/SELinux、swap、内核模块和 sysctl；
- GPU 型号、显存、驱动、`nvidia-smi`、IOMMU/虚拟机透传状态；
- 离线包剩余空间，解压空间和最终镜像空间；
- 所有摘要和签名；
- SSH sudo、文件权限和 umask；
- 云主机安全组是否放行集群内部必需端口。

任何硬性检查失败必须停止，不得继续“带病安装”。允许风险豁免时需要在验收报告中记录批准人和原因。

### 8.3 OS 配置

自动化负责：

- 配置主机名、hosts/DNS、NTP；
- 安装 bundle 内的 `nfs-common`、iSCSI、chrony、审计和诊断工具；
- 加载 `overlay`、`br_netfilter` 等所需模块；
- 配置转发、bridge netfilter、文件句柄、inotify 等 sysctl；
- 按 RKE2 要求处理 swap；
- 配置防火墙精确规则，不采用生产环境永久关闭防火墙的方式；
- 创建并挂载：
  - `/var/lib/rancher/rke2`
  - `/var/lib/longhorn`
  - `/opt/trackwalker`
  - `/var/log/trackwalker-deploy`
- 设置日志轮转和磁盘告警；
- 固定已验证内核，升级通过新的离线发布包执行；
- 所有变更由 Ansible role 管理并可审计。

### 8.4 网络端口

项目需要维护一份按 profile 生成的端口矩阵，至少包含：

- RKE2/Kubernetes API、节点注册、etcd、kubelet；
- Canal CNI 数据面；
- RKE2 内置镜像分发服务；
- Ingress 80/443；
- kube-vip/MetalLB 所需协议；
- Longhorn、NFSv4/iSCSI；
- 监控采集；
- 部署控制机 SSH；
- 内部 DNS、NTP、备份端点；
- 运行期批准的第三方接口。

etcd、数据库、Kafka、Redis、MinIO 管理口、AI 内部接口不得直接暴露到公网。

## 9. Kubernetes 安装设计

### 9.1 单机

1. 将 RKE2 air-gap image tar 放入 RKE2 agent image 目录。
2. 使用离线 `install.sh` 和本地 artifact path 安装 RKE2。
3. 配置节点 IP、cluster/service CIDR、TLS SAN、审计和快照。
4. 启动 `rke2-server` 并等待 `Ready`。
5. 生成权限受控的 kubeconfig。
6. 安装本地存储、Ingress 配置、监控和平台 Chart。

### 9.2 多机

1. 配置 Kubernetes API 固定注册地址。
2. 初始化第一个 RKE2 server。
3. 顺序加入另外两个 server，确认 etcd quorum。
4. 加入 CPU/GPU worker。
5. 启用分布式镜像分发服务并验证跨节点拉取。
6. 安装 Longhorn、GPU、监控、日志和入口组件。
7. 给节点增加角色标签、污点、拓扑域和存储标签。
8. 部署状态服务，再部署平台服务。
9. 执行节点重启、镜像拉取、调度和网络验收。

控制面扩容和升级必须一次只操作一个节点，并在每步确认 etcd、API 和节点健康。

### 9.3 RKE2 备份

- 开启压缩的定时 etcd 快照；
- 多 server 快照策略和保留数量由配置统一管理；
- 快照复制到集群外部的 S3 兼容存储、NAS 或备份服务器；
- 同时备份 RKE2 server token；没有原 token，快照不能完整恢复 bootstrap 数据；
- 每个正式版本必须完成一次“恢复到新节点”的演练。

## 10. 平台 Kubernetes 化

### 10.1 Helm Chart 原则

- 一个顶层 `trackwalker` Chart 管理本平台服务；
- PostGIS、Kafka、MinIO、Longhorn 和监控等第三方组件使用锁定版本的独立 Chart；
- `values-common.yaml` 保存公共配置；
- `values-single.yaml` 与 `values-ha.yaml` 只保存差异；
- Secret 与普通 values 分离；
- 所有模板通过 `helm lint`、schema 校验和渲染测试；
- Deployment 使用 checksum annotation 触发配置变更滚动更新；
- 所有工作负载设置 startup/readiness/liveness probe；
- 所有服务设置 requests，生产设置经过压测的 limits；
- 使用 PDB、反亲和和 topology spread 管理生产副本；
- 使用 NetworkPolicy 限制组件间访问。

### 10.2 建议工作负载配置

| 工作负载 | `single` | `ha` 第一阶段 | 完整 HA 目标 |
| --- | ---: | ---: | ---: |
| frontend | 1 | 2 | 2+ |
| backend | 1 | **1** | 2+，完成分布式任务/SSE 改造后 |
| vision-inference | 1 | 按 GPU 容量 1+ | 2+，跨 GPU 节点分散 |
| pointcloud-analysis | 1 | 按 GPU/CPU 容量 1+ | 2+，跨节点分散 |
| artifact-installer | 1 | **1** | 1，或完成分布式锁和幂等发布后扩展 |
| PostGIS | 1 | 1 + 备份 | 3 实例 HA |
| Redis | 1 | 1 | 3 节点 Sentinel/等效方案，前提是确认仍需使用 |
| Kafka | 1 | 3 broker/controller | 3+ |
| MinIO | 1 | 4 个 server/drive | 4+，跨故障域 |

### 10.3 后端多副本改造前置项

在把后端副本数从 1 提升到 2 之前，至少完成：

- SSE 事件由 Kafka/Redis/数据库事件表驱动，任意后端实例均可恢复事件；
- 模型测试任务和制品安装任务使用数据库租约、消息队列 consumer group 或专用 worker；
- 任务状态迁移幂等，支持实例崩溃后重新领取；
- `DemoRunService` 的进程内执行器改为持久化任务；
- 定时清理任务使用 leader election 或分布式锁；
- 明确前端 SSE 是否需要 sticky session；推荐消除依赖，而不是长期依赖粘性会话；
- 完成并发、重复消费、实例滚动和实例故障测试。

### 10.4 数据库迁移

- Flyway migration 在应用启动前以独立 Kubernetes Job 执行；
- Job 成功后才允许新版本后端滚动；
- 禁止多个后端 Pod 同时承担不可逆数据库升级；
- 数据库 schema 变更采用 expand/contract，确保应用版本可回滚；
- 每次迁移前自动执行数据库备份并验证备份可读；
- 不支持自动回滚的 migration 必须在发布说明中明确人工恢复步骤。

### 10.5 Ingress 与大文件

需要把当前 Nginx 行为迁移到 Ingress：

- 请求体至少 2 GB，后端 Spring 请求上限保持一致；
- 禁用需要完整缓冲的大文件代理路径；
- 上传和模型安装接口使用足够长的超时；
- SSE 禁用代理缓冲，保持长连接；
- 配置连接数、空闲超时和优雅终止；
- 平台入口只暴露前端/网关，`/api` 和 `/actuator` 按安全策略路由；
- `/actuator` 只允许监控 namespace 或管理网访问。

## 11. 状态服务和存储

### 11.1 单机

- PostGIS、MinIO、Kafka 使用独立 PVC；
- 模型卷、数据库卷和对象存储卷尽量位于独立数据盘；
- 使用 `Retain` reclaim policy；
- 重装应用不能自动删除 PVC；
- 一键卸载默认只卸载无状态工作负载，删除数据必须使用独立命令并二次确认。

### 11.2 多机生产

推荐逐步达到：

- PostGIS：使用经验证的 PostgreSQL Operator 和 PostGIS 兼容镜像，3 实例，主从跨故障域；
- Kafka：3 broker/controller 的 KRaft 集群，副本因子和最小同步副本按容错目标配置；
- MinIO：至少 4 个 server/drive 的分布式 Tenant，Pod 跨节点分散；
- Redis：先确认真实使用场景；需要时部署 Sentinel/HA；
- Longhorn：RWO 卷 3 副本，存储节点和磁盘显式选择；
- 模型卷：首期 RWX，安装代理写、推理 Pod 只读；
- 所有状态服务设置 PDB、反亲和、资源保障和磁盘水位告警。

Longhorn 的块复制不能替代数据库、Kafka 和对象存储自身的备份或应用级复制。

### 11.3 模型存储演进

第一阶段兼容方案：

```text
MinIO 原始制品
      |
      v
artifact-installer（RWX 可写）
      |
      v
模型 RWX PVC
      |
      +--> vision-inference（只读）
      +--> pointcloud-analysis（只读）
```

第二阶段目标方案：

```text
MinIO 版本化制品 + manifest + SHA-256
      |
      v
推理 Pod initContainer/sidecar
      |
      v
Pod 本地只读模型缓存
      |
      v
健康检查通过后原子切换版本
```

第二阶段需要补充模型缓存回收、并发下载、断点续传、签名验证、灰度加载和 GPU 内存回收策略。

## 12. GPU 离线部署

建议采用“主机驱动预安装 + Kubernetes 设备管理组件”的确定性方案：

1. 在版本锁中固定 OS、内核、NVIDIA 驱动、CUDA 容器运行时和 GPU 组件版本。
2. 驱动和内核头文件进入离线 OS 包仓库，或由现场按许可证单独提供。
3. Ansible 安装并验证 `nvidia-smi`。
4. 配置 RKE2/containerd 的 NVIDIA runtime。
5. 部署锁定版本的 GPU Operator 或 NVIDIA Device Plugin/DCGM Exporter。
6. 如果 GPU Operator 不管理主机驱动，显式设置 `driver.enabled=false`。
7. 使用 CUDA 验证 Pod 检查设备可见性和基础计算。
8. 为 GPU 节点增加标签和污点，例如 `workload.trackwalker.io/gpu=true`。
9. AI Pod 显式请求 `nvidia.com/gpu: 1`，并通过压测决定一个 GPU 是否允许时间切片或 MIG。
10. 内核、驱动、RKE2 任一升级都必须执行 GPU 兼容矩阵测试。

无公网环境下由 GPU Operator 在线编译或下载驱动依赖风险较高。若采用该模式，必须同时提供本地软件仓库或预编译驱动容器，且要验证云厂商内核。

## 13. 配置、凭据与安全

### 13.1 配置分层

- `versions.lock.yaml`：不可变软件和镜像版本；
- `site.yml`：站点级非秘密配置；
- `hosts.yml`：节点和角色；
- `values-<profile>.yaml`：Kubernetes profile；
- `secrets.sops.yaml`：使用 SOPS + age 加密的秘密；
- ConfigMap 和 Secret 名称包含配置摘要，确保发布可追踪。

### 13.2 秘密规则

- 禁止使用当前 Compose 中的 `rail`、`minioadmin`、`local-artifact-token` 等默认值；
- 部署入口发现默认值、短口令或空 Token 时直接失败；
- 生产 Secret 不进入 Git、不进入普通日志、不进入最终验收报告；
- 自动部署使用 age 私钥文件或受控的现场输入解密，不把私钥放进离线包；
- kubeconfig、RKE2 token、数据库口令、MinIO 密钥、TLS 私钥权限设为 `0600`；
- 支持凭据轮换，轮换不会强制重装集群；
- 后续可接入 Vault 等内网秘密管理系统，但不作为第一版硬依赖。

### 13.3 安全基线

- 默认拒绝公网暴露；
- namespace 级 NetworkPolicy；
- 最小 RBAC；
- Pod Security Admission 基线，逐步达到 restricted；
- 非 root、只读根文件系统、禁止 privilege escalation；
- 仅 GPU/存储系统组件按必要范围使用特权；
- Kubernetes API 审计日志；
- 镜像 digest、签名、SBOM 和离线漏洞报告；
- TLS 使用企业内部 CA 或部署专用 CA，证书到期进入监控；
- 离线漏洞库需要定期随维护包更新，不能把“无网”等同于“无漏洞”；
- 变更、部署、升级、回滚和备份恢复全部留痕。

## 14. 可观测性与运维

第一版至少包含：

- Prometheus；
- Grafana；
- Alertmanager；
- kube-state-metrics；
- node-exporter；
- RKE2/Kubernetes 控制面指标；
- NVIDIA DCGM Exporter；
- 后端 `/actuator/prometheus`；
- AI 服务请求数、耗时、错误率、模型版本、执行 provider、显存/内存；
- PostGIS、Kafka、MinIO、Redis exporter；
- 日志使用 Loki 或对接现场现有日志平台；
- 时钟偏差、证书到期、磁盘水位、PVC、Pod 重启、镜像拉取失败告警。

关键 SLI：

- 平台入口可用率；
- API p95/p99；
- SSE 连接失败率；
- 模型推理成功率和 p95；
- 任务积压和最长等待时间；
- Kafka ISR、消费积压；
- PostGIS 连接和复制延迟；
- MinIO 容量、错误和修复状态；
- GPU 利用率、显存、温度和 Xid；
- 备份最后成功时间和恢复演练时间。

## 15. 备份与灾难恢复

必须分别备份，不能只做虚拟机快照：

| 对象 | 建议方式 | 目标位置 |
| --- | --- | --- |
| RKE2 etcd | RKE2 定时快照 + server token | 集群外部 |
| PostGIS | 逻辑备份 + WAL/物理备份 | 集群外部 |
| MinIO | bucket replication 或 `mc mirror` | 独立对象存储/NAS |
| Kafka | 以业务数据库为权威时明确可重放边界；必要主题做跨集群复制/归档 | 独立存储 |
| 模型制品 | manifest、文件、标签、SHA-256 | 独立制品库 |
| Kubernetes 配置 | Git 中的非秘密配置 + 加密秘密备份 | 发布库和安全保管区 |
| Grafana/告警 | 声明式配置 | Git/离线包 |

在业务方确认前，可暂定：

- 单机测试：RPO 24 小时，RTO 4 小时；
- 多机生产：RPO 1 小时，RTO 2 小时；
- 模型和发布配置：每次发布均产生不可变备份。

正式值必须由业务、数据量、备份链路和预算共同确认。

每季度至少执行一次：

1. 新节点恢复 RKE2；
2. 恢复 PostGIS；
3. 恢复 MinIO 样本和模型；
4. 重新部署应用；
5. 执行全链路验收；
6. 记录实际 RPO/RTO。

## 16. 升级、回滚与卸载

### 16.1 应用升级

1. 校验增量离线包；
2. 执行发布前备份；
3. 预导入新镜像；
4. 执行数据库 migration Job；
5. 按 frontend/backend/AI 的兼容顺序滚动；
6. 执行 smoke test；
7. 失败时回滚 Helm release；
8. 数据库不可逆变更按发布说明恢复，不伪装为自动回滚。

### 16.2 Kubernetes 升级

- 每次只跨一个受支持 minor；
- 先升级一个测试集群；
- 多机依次升级 server，再升级 agent；
- 每个节点升级前检查 etcd 快照、quorum、PodDisruptionBudget 和工作负载迁移；
- GPU 节点单独验证驱动和容器运行时；
- 保留上一个完整离线包。

### 16.3 卸载

默认卸载只删除应用 Deployment、Service、Ingress 和无状态 ConfigMap，不删除：

- PVC；
- Longhorn volume；
- PostGIS 数据；
- MinIO bucket；
- 模型；
- etcd 快照；
- 备份。

数据销毁必须使用独立的 `destroy-data.sh`，列出精确对象，要求二次确认并生成销毁记录。

## 17. 一键部署阶段

`deploy.sh` 应按以下状态机执行：

| 阶段 | 内容 | 失败处理 |
| --- | --- | --- |
| `00-bundle-verify` | 校验签名、摘要、版本和架构 | 立即停止 |
| `10-preflight` | 节点、网络、磁盘、GPU、CIDR、时间检查 | 立即停止并出报告 |
| `20-os-baseline` | OS 包、内核模块、sysctl、磁盘、NTP、防火墙 | 可幂等重试 |
| `30-rke2-bootstrap` | 首 server 初始化 | 保留日志，允许 resume |
| `40-rke2-join` | 其余 server/agent 加入 | 单节点失败不继续发布应用 |
| `50-images` | 导入并验证 RKE2、addon、平台镜像 | 缺少任一 digest 即停止 |
| `60-addons` | 存储、GPU、监控、入口 | 等待健康，失败停止 |
| `70-data-services` | PostGIS、Kafka、Redis、MinIO | 等待健康和初始化 Job |
| `80-application` | 平台 Helm 发布、Flyway、模型 manifest | 发布失败自动回滚无状态部分 |
| `90-verify` | 基础、功能、GPU、存储和全链路验收 | 输出失败项，不删除现场证据 |
| `99-report` | 生成安装报告、版本清单和运维入口 | 保存到控制机和集群内 |

部署锁防止两名运维人员同时执行。日志中每个秘密必须脱敏。

## 18. 项目工作分解

### Epic A：技术基线与验证

- 确认 OS、RKE2、GPU、Longhorn 和中间件版本；
- 建立 1 台和 3 台离线实验环境；
- 验证 RKE2 air-gap、固定 API 地址和内置镜像分发服务；
- 验证无默认互联网路由场景；
- 验证 NVIDIA、CUDA 12.4、ONNX Runtime GPU 和 Open3D；
- 输出版本锁和兼容矩阵。

完成标准：在断开公网的环境中，用手工最小步骤建立单机和 3 server 集群，并运行 CUDA 测试 Pod。

### Epic B：离线发布工厂

- 改造生产运行镜像；
- CI 构建、测试和 digest 锁定；
- 自动收集 Helm Chart 和全部间接镜像；
- 构建 OS 离线包仓库；
- 生成 SBOM、漏洞报告、许可证清单；
- 生成签名 bundle 和 model-pack；
- 增加 bundle 完整性测试。

完成标准：一条联网流水线命令产出可审计离线包，包内不存在运行期缺失镜像。

### Epic C：服务器与 RKE2 自动化

- inventories 和配置 schema；
- 预检 role；
- OS、磁盘、NTP、防火墙 role；
- RKE2 server/agent role；
- 镜像导入和镜像分发服务 role；
- 单机、3 server、worker 加入；
- 幂等、resume 和日志。

完成标准：从干净 OS 到所有节点 `Ready` 全程无需联网，第二次执行无破坏性变更。

### Epic D：Kubernetes 基础组件

- StorageClass 和 Longhorn；
- API/Ingress VIP；
- GPU 组件；
- Prometheus/Grafana/Alertmanager；
- 日志；
- NetworkPolicy、RBAC 和 Pod 安全；
- etcd 快照。

完成标准：节点、存储、入口、GPU 和监控均通过自动验收。

### Epic E：平台 Helm 化

- 5 个平台工作负载；
- ConfigMap、Secret、Service、Ingress；
- probe、资源、优雅终止；
- Flyway migration Job；
- 模型 RWX PVC；
- CPU/GPU values；
- 单机/多机 profile；
- Compose 环境变量逐项映射测试。

完成标准：当前 Compose 功能在 K8s 单机档案下全部通过。

### Epic F：生产状态服务

- PostGIS 备份版和 HA 版；
- Kafka 3 节点；
- MinIO 分布式；
- Redis 必要性确认和对应部署；
- 存储故障域、PDB、反亲和；
- 备份和恢复脚本。

完成标准：逐项完成 Pod、节点、磁盘故障和恢复测试。

### Epic G：应用高可用改造

- SSE 分布式事件；
- 持久化任务队列；
- worker 租约和幂等；
- 定时任务 leader election；
- 安装代理分布式锁；
- 后端多副本压测；
- 模型本地缓存方案。

完成标准：任意一个后端 Pod 或一个 GPU worker 故障时，正在执行的任务可恢复，入口持续可用。

### Epic H：验收、运维与移交

- 单机和多机 E2E；
- 无公网流量证明；
- 升级/回滚；
- 备份/恢复；
- 节点替换；
- 运维手册和故障树；
- 现场培训。

完成标准：非开发人员仅依赖发布包和文档完成一次部署及一次恢复。

## 19. 里程碑与估算

以下为 1 名基础设施工程师主导、应用开发和测试配合的初步估算：

| 里程碑 | 主要产出 | 预计 |
| --- | --- | ---: |
| M0 技术验证 | 版本锁、网络/存储/GPU PoC | 3～5 个工作日 |
| M1 离线工厂 | 镜像、bundle、签名、SBOM | 5～8 个工作日 |
| M2 单机一键部署 | 干净 OS 到完整平台 | 7～10 个工作日 |
| M3 多机生产基线 | 3 server、worker、存储、GPU、入口 | 8～12 个工作日 |
| M4 状态服务与灾备 | PostGIS/Kafka/MinIO、备份恢复 | 7～10 个工作日 |
| M5 应用 HA | 后端任务/SSE/模型分发改造 | 8～15 个工作日 |
| M6 验收移交 | 故障、升级、恢复、文档和培训 | 5～8 个工作日 |

总体约 6～10 周。节点、GPU、存储、云厂商限制和现有应用改造量会显著影响周期。

## 20. 验收标准

### 20.1 离线与可重复性

- 目标环境公共 DNS 和互联网路由不可用时仍能完成部署；
- 部署过程没有访问公共镜像仓库、APT、PyPI、Maven、npm、GitHub；
- bundle 中每个文件摘要通过；
- 目标节点所有运行镜像 digest 与版本锁一致；
- 同一命令第二次执行成功且不重建数据库、不清空卷；
- 中途断开 SSH 后可以从失败阶段继续。

### 20.2 单机

- 从锁定 OS 到平台健康，除 bundle 传输时间外目标不超过 60 分钟；
- 所有 Pod Ready；
- 平台入口、API、Flyway、PostGIS、Kafka、MinIO、模型安装和推理健康；
- CPU profile 能完成全链路演示；
- GPU profile 能识别 GPU，并返回正确执行 provider；
- 重启服务器后自动恢复；
- 执行一次应用升级和回滚；
- 执行一次 PostGIS、MinIO 和 etcd 恢复。

### 20.3 多机生产基线

- 3 个 server 的 etcd quorum 正常；
- worker 可重新加入和替换；
- 关闭任意一个 control-plane 后 API 仍可用；
- 关闭任意一个存储节点后，符合副本策略的数据卷仍可用或可恢复；
- 平台 Pod 按角色、污点和拓扑正确分散；
- GPU 任务不会调度到 CPU 节点；
- Kafka、MinIO 和 PostGIS 故障测试符合已批准的 RPO/RTO；
- 入口 TLS、NetworkPolicy、RBAC、审计和告警生效；
- 注意：在 Epic G 完成前，后端单副本故障仍会造成业务短时中断，验收报告必须明确这一限制。

### 20.4 性能

使用真实数据集测量：

- 2 GB 级上传和断点/分片行为；
- 视觉、热成像、变化检测和点云推理 p50/p95/p99；
- 模型加载时间、显存和常驻模型数量；
- Kafka 峰值事件；
- PostGIS 查询和 Flyway 时间；
- MinIO 上传/下载吞吐；
- RWX 模型卷读取；
- 节点重启和 Pod 漂移恢复时间。

容量结论必须基于压测，不直接沿用 Compose 的资源限制。

## 21. 风险清单

| 风险 | 影响 | 缓解措施 |
| --- | --- | --- |
| OS 内核与 NVIDIA 驱动不匹配 | GPU 节点不可用 | 固定 OS/kernel/driver；升级前矩阵测试；准备回退内核 |
| 漏收某个间接镜像或 OS 包 | 现场安装中断 | 对 Helm 渲染结果自动收集镜像；在真断网环境做发布门禁 |
| 离线包和模型过大 | 传输和导入时间过长 | zstd、分卷、增量包、模型独立 pack、预估临时空间 |
| 后端直接扩多副本 | SSE 丢失、重复任务 | Epic G 前锁定单副本并在 UI/告警中说明 |
| 模型 RWX 性能或锁异常 | 模型加载慢、发布失败 | 首期压测 Longhorn RWX；中期改本地缓存 |
| 单机同时运行全部状态服务和 AI | 内存/OOM、I/O 抖动 | 明确最低规格；资源 requests；限制测试并发 |
| Longhorn 与数据库双重复制放大 I/O | 延迟和容量膨胀 | 分层压测；关键数据库使用应用级复制并调整卷副本 |
| 云厂商不支持二层 VIP/MetalLB | 入口不可达 | profile 支持云 LB、kube-vip、MetalLB 三种模式 |
| CIDR 与铁路内网/VPN 冲突 | 服务网络不可用 | 部署前自动路由冲突检查 |
| 无内部 NTP | TLS、日志、etcd 异常 | 把内部时间源列为硬性前置，监控偏差 |
| 默认口令泄漏 | 数据和控制面风险 | 部署入口拒绝默认口令；加密秘密和轮换 |
| 只备份 PVC/VM | 无法一致恢复 | 分组件备份；季度恢复演练 |
| 运行期外部 DJI 接口与隔离策略冲突 | 外部业务功能不可用 | 明确 isolated/restricted-egress 档案和降级行为 |
| 漏洞库长期不更新 | 已知漏洞无法发现 | 每个维护包携带新离线漏洞库和扫描报告 |
| 第三方制品不可再分发 | 交付合规风险 | 发布前审查 NVIDIA、模型和第三方许可证 |

## 22. 开工前必须确认的输入

1. 第一版支持的云厂商和 Linux 发行版。
2. 云服务器是 x86_64 还是同时需要 ARM64。
3. GPU 厂商、型号、数量、显存、驱动来源和云厂商透传方式。
4. 单机测试是否需要真实 GPU 推理，还是 CPU 基线即可。
5. 生产首期节点数量，以及能否提供 3 个 control-plane。
6. 数据盘数量、容量、类型和预期影像/点云日增量。
7. 是否有可用的云 CSI、云内网 LB、内部 DNS、NTP 和外部备份存储。
8. 内网域名、证书签发方式和用户入口。
9. 安装期完全离线后，运行期是否允许访问 DJI 或其他外部接口。
10. 期望 RPO、RTO、数据保留周期和维护窗口。
11. 是否必须达到等保或企业安全基线，以及审计日志对接要求。
12. 模型文件能否随平台包分发及其许可证限制。

这些输入不会阻塞单机 PoC，但会影响生产拓扑、制品内容和最终验收标准。

## 23. 推荐首个迭代范围

首个迭代建议严格收敛为：

- 1 台 Ubuntu x86_64 测试服务器；
- RKE2 单 server；
- CPU profile，GPU 作为可选增量；
- 当前全部平台和中间件功能；
- 本地持久卷；
- 完整离线包；
- Ansible + Helm + `deploy.sh`；
- bundle 校验、日志、验收、重启恢复；
- 应用升级/回滚；
- PostGIS、MinIO、etcd 的备份恢复；
- 在断开公网的隔离环境完成两次全新安装。

该迭代通过后，再复用同一代码进入 3 server + worker 的多机验证。这样最早可以获得一个真实可用、可重复、可审计的离线部署闭环。

## 24. 官方参考

- [RKE2 Air-Gap Install](https://docs.rke2.io/install/airgap)
- [RKE2 High Availability](https://docs.rke2.io/install/ha)
- [RKE2 Embedded Registry Mirror](https://docs.rke2.io/install/registry_mirror)
- [RKE2 Backup and Restore](https://docs.rke2.io/datastore/backup_restore)
- [Kubernetes Ports and Protocols](https://kubernetes.io/docs/reference/networking/ports-and-protocols/)
- [NVIDIA GPU Operator Air-Gapped Installation](https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/install-gpu-operator-air-gapped.html)
- [Longhorn RWX Volumes](https://longhorn.io/docs/1.12.0/nodes-and-volumes/volumes/rwx-volumes/)

## 25. 下一步

方案批准后，建议立即完成：

1. 在 `deploy/` 建立项目骨架、配置 schema 和版本锁；
2. 确认第一版 OS、RKE2 和 GPU 版本；
3. 建立一台断网 Linux VM 做 M0；
4. 把现有 5 个应用服务制作为不依赖现场构建的 OCI 镜像；
5. 产出第一个可校验 bundle；
6. 以“从干净 OS 到平台全链路成功”为第一个端到端门禁。
