# Kimina Lean Server 离线镜像构建与部署指南 本文档说明如何将 `kimina-lean-server` 打包为完全离线的 Docker 镜像,部署到无公网访问的内部服务器上。 --- ## 一、构建思路 ### 1.1 为什么要做离线镜像 kimina-lean-server 运行时依赖以下组件,它们在首次启动或运行过程中会尝试联网: | 组件 | 联网行为 | 离线处理方式 | |---|---|---| | **elan** | 安装时下载 Lean 工具链;运行时会检查更新 | 构建时预装工具链;运行时禁用更新检查 | | **lake** | `lake build` 时从 git 拉取依赖;`lake exe cache get` 下载编译缓存 | 构建时完成所有编译;修改 manifest 为本地 path 类型 | | **mathlib4** | 依赖众多 Lean 包,需从 GitHub 下载 | 构建时完整克隆并编译,预装入镜像 | | **Python 依赖** | `pip install` 从 PyPI 下载 | 构建时全部安装到镜像中 | ### 1.2 核心策略:构建时联网,运行时零依赖 整个方案遵循"有网构建、无网运行"的原则: 1. **构建阶段**(在有网络的机器上):安装 elan、下载 Lean 工具链、克隆 repl 和 mathlib4、编译所有依赖、安装 Python 包 2. **镜像封装**:将编译产物、缓存、依赖全部打入 Docker 镜像 3. **离线阶段**(内部服务器):仅通过 `docker load` 加载镜像,无需任何外部网络 ### 1.3 关键处理点 - **lake-manifest.json 改写**:构建完成后,将 mathlib4 的 `lake-manifest.json` 中所有包的 `type` 从 `"git"` 改为 `"path"`,并删除 `url` 字段。这样 lake 在运行时不会再尝试从 git 拉取依赖 - **elan 自更新禁用**:通过 `ELAN_NO_UPDATE_CHECK=1` 和 `elan config --disable-self-update` 双重保证 - **工具链预装**:构建时通过 `elan-init.sh --default-toolchain v4.26.0` 将工具链固定安装到镜像中 --- ## 二、文件说明 ### 2.1 `Dockerfile`(仓库根目录) 定义镜像的构建流程,关键步骤: ```dockerfile # 基础镜像:Python 3.13 slim FROM python:3.13-slim # 构建参数:Lean 版本、REPL 源、mathlib 源等 ARG LEAN_SERVER_LEAN_VERSION=v4.26.0 ARG REPL_REPO_URL=https://github.com/leanprover-community/repl.git ARG REPL_BRANCH=${LEAN_SERVER_LEAN_VERSION} ARG MATHLIB_REPO_URL=https://github.com/leanprover-community/mathlib4.git ARG MATHLIB_BRANCH=${LEAN_SERVER_LEAN_VERSION} # 运行时环境变量 ENV LEAN_SERVER_LEAN_VERSION=${LEAN_SERVER_LEAN_VERSION} \ LEAN_SERVER_REPL_PATH=/repl/.lake/build/bin/repl \ LEAN_SERVER_PROJECT_DIR=/mathlib4 \ LEAN_SERVER_MAX_REPL_MEM=12G \ # 关键:内存限制(见下文注意事项) LEAN_SERVER_INIT_REPLS='{"import Mathlib": 1}' \ ELAN_NO_UPDATE_CHECK=1 # 禁用 elan 更新检查 # 1. 安装系统依赖(curl, git, jq, build-essential 等) RUN apt-get update && apt-get install -y ... # 2. 复制并执行 setup.sh(安装 elan、lean、repl、mathlib4) COPY setup.sh /usr/local/bin/ RUN sed -i 's/\r$//' /usr/local/bin/setup.sh \ # 修复 Windows CRLF && chmod +x /usr/local/bin/setup.sh \ && /usr/local/bin/setup.sh # 3. 安装 Python 依赖 RUN pip install ... && prisma generate # 4. 启动服务 CMD ["python", "-m", "server"] ``` ### 2.2 `setup.sh`(仓库根目录) 在容器构建阶段执行,完成 Lean 生态的预装: ```bash #!/usr/bin/env bash set -euxo pipefail # 安装 elan(Lean 工具链管理器) curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf \ | sh -s -- --default-toolchain "${LEAN_SERVER_LEAN_VERSION}" -y # 禁用 elan 自动更新(离线环境必须) elan config --disable-self-update # 克隆并编译 REPL install_repo repl "$REPL_REPO_URL" "$REPL_BRANCH" false # 克隆并编译 mathlib4 install_repo mathlib4 "$MATHLIB_REPO_URL" "$MATHLIB_BRANCH" true ``` `install_repo` 函数的核心逻辑: 1. `git clone --single-branch --depth 1` 浅克隆仓库 2. `lake exe cache get` 下载 mathlib4 的预编译缓存(加速构建,失败则回退到源码编译) 3. `lake build` 编译项目 4. 对 mathlib4 执行 **manifest 改写**: ```bash jq '.packages |= map(.type="path"|del(.url)|.dir=".lake/packages/"+.name)' \ lake-manifest.json ``` 5. 构建完成后删除 `.git` 目录减小镜像体积 ### 2.3 `deploy/offline/build-and-save.sh` 一键构建脚本,执行以下流程: ``` 构建镜像 | v 启动容器(--network=none)进行离线冒烟测试 | v 等待 "Initialized REPLs" 日志(最多 10 分钟) | v 测试通过 → docker save | gzip 导出 tar.gz ``` 可覆盖的环境变量: | 变量 | 默认值 | 说明 | |---|---|---| | `LEAN_VERSION` | `v4.26.0` | Lean 版本 | | `IMAGE` | `kimina-lean-server:${LEAN_VERSION}-offline` | 输出镜像名 | | `PLATFORM` | `linux/amd64` | 目标平台架构 | | `APT_MIRROR` | "" | APT 源镜像(国内网络可设阿里云) | | `PIP_INDEX_URL` | "" | pip 源镜像 | | `SKIP_UV` | "" | 非空则跳过 uv 安装 | ### 2.4 `deploy/offline/compose.offline.yaml` 离线服务器上的 Docker Compose 配置: ```yaml services: server: image: kimina-lean-server:v4.26.0-offline ports: - "80:8000" environment: LEAN_SERVER_ENVIRONMENT: prod LEAN_SERVER_LEAN_VERSION: v4.26.0 LEAN_SERVER_MAX_REPL_MEM: 12G # 与 Dockerfile 保持一致 LEAN_SERVER_INIT_REPLS: '{"import Mathlib": 1}' restart: unless-stopped ``` --- ## 三、构建步骤 ### 3.1 前置要求 - 一台**有公网访问**的 Linux 机器(架构需与目标服务器一致,通常为 `linux/amd64`) - Docker 已安装并运行 - Bash、Git 可用 ### 3.2 执行构建 ```bash cd deploy/offline bash build-and-save.sh ``` 构建耗时约 10-30 分钟,取决于网络速度。成功后会输出: ``` ==> Offline smoke test PASSED ... ==> Done. image: kimina-lean-server:v4.26.0-offline image digest: sha256:... tar: .../deploy/offline/out/kimina-lean-server-v4.26.0.tar.gz tar sha256: 8e5d3edcc18ace92aa7252329e0a06a24d1b1506b3263042b0138e56de74761f ``` ### 3.3 使用国内镜像加速(可选) 如果构建机器在国内,可设置镜像源: ```bash export APT_MIRROR=https://mirrors.aliyun.com export PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ export SKIP_UV=1 bash build-and-save.sh ``` --- ## 四、部署步骤 ### 4.1 传输文件到内部服务器 将以下两个文件复制到离线服务器: ``` deploy/offline/out/kimina-lean-server-v4.26.0.tar.gz deploy/offline/compose.offline.yaml ``` ### 4.2 加载镜像 在内部服务器上执行: ```bash # 加载镜像(约 1-3 分钟,取决于磁盘速度) gunzip -c kimina-lean-server-v4.26.0.tar.gz | docker load # 验证镜像已加载 docker images | grep kimina-lean-server ``` ### 4.3 启动服务 ```bash docker compose -f compose.offline.yaml up -d ``` 服务将在后台运行,监听容器的 8000 端口,映射到宿主机的 80 端口。 ### 4.4 查看日志 ```bash docker compose -f compose.offline.yaml logs -f ``` 预期输出: ``` INFO Running Kimina Lean Server 'v2.0.0' in prod mode with Lean version: 'v4.26.0' INFO REPL manager initialized with: MAX_REPLS=20, MAX_REPL_USES=-1, MAX_REPL_MEM=12288 MB INFO Initialized REPLs with: {"import Mathlib": 1} INFO Application startup complete. INFO Uvicorn running on http://0.0.0.0:8000 ``` --- ## 五、测试方法 ### 5.1 快速功能测试 服务启动后,在离线服务器上执行: ```bash curl --request POST \ --url http://localhost:8000/api/check \ --header 'Content-Type: application/json' \ --data '{ "snippets": [ {"id": "check-nat-test", "code": "#check Nat"} ] }' ``` 预期返回(类似): ```json { "snippets": [ { "id": "check-nat-test", "response": { "messages": [ { "severity": "info", "pos": {"line": 1, "column": 0}, "endPos": {"line": 1, "column": 6}, "data": "Nat : Type" } ], "env": 0 } } ] } ``` ### 5.2 离线环境验证 确认容器确实在断网环境下运行: ```bash # 查看容器网络模式(应为默认 bridge,但无外网访问) docker inspect <容器名> | grep -i network # 进入容器内部测试网络连通性 docker exec -it <容器名> bash -c "curl -I https://github.com 2>&1" # 预期:连接超时或无法解析(证明确实离线) ``` ### 5.3 健康检查端点 ```bash curl http://localhost:8000/health ``` 预期返回: ```json {"status": "ok"} ``` --- ## 六、注意事项 ### 6.1 内存限制(重要) Lean 4.26.0 + Mathlib 在 REPL 启动时需要大量虚拟内存。测试发现: | 内存限制 | 结果 | |---|---| | 8G | REPL 启动失败(进程被内存限制阻塞) | | 9G+ | 正常工作 | | **12G**(推荐) | 稳定运行,留有余量 | 因此 Dockerfile 和 compose 文件中将默认值设为 `12G`。如果你的服务器内存紧张,可尝试降低到 9-10G,但不建议低于 9G。 ### 6.2 架构一致性 构建机器的 CPU 架构必须与目标服务器一致。如果不一致,需使用 Docker 的交叉编译: ```bash # 例如:在 Apple Silicon Mac 上构建 amd64 镜像 export PLATFORM=linux/amd64 bash build-and-save.sh ``` ### 6.3 镜像体积 离线镜像体积较大(约 2-3GB),主要来源于: - mathlib4 预编译产物和依赖包 - Lean 工具链 - REPL 二进制(约 200MB) ### 6.4 版本升级 如需升级 Lean 版本,修改以下位置后重新构建: 1. `Dockerfile` 中的 `LEAN_SERVER_LEAN_VERSION` 2. `compose.offline.yaml` 中的 `LEAN_SERVER_LEAN_VERSION` 3. 重新运行 `build-and-save.sh` --- ## 七、故障排查 | 现象 | 可能原因 | 解决方法 | |---|---|---| | 构建时 `env: 'bash\r': No such file or directory` | setup.sh 有 Windows CRLF 换行符 | Dockerfile 中已添加 `sed -i 's/\r$//'` 自动修复 | | 离线启动时 REPL 返回空响应 | 内存限制过低 | 提高 `LEAN_SERVER_MAX_REPL_MEM` 到 12G | | `lake exe cache get` 失败 | 网络问题或 lake 版本不兼容 | setup.sh 中已添加 `|| true` 容错,会回退到源码编译 | | 容器启动后立刻退出 | REPL 初始化失败 | 查看 `docker logs` 中的错误信息 | | elan 尝试联网更新 | 环境变量未生效 | 检查 `ELAN_NO_UPDATE_CHECK=1` 是否设置 |