CHENSEE's picture
Upload folder using huggingface_hub
985a50e verified
|
Raw
History Blame Contribute Delete
10.5 kB
# 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` 是否设置 |