LyEdu Linux 可执行文件构建流程
本文档描述在 Windows 环境下,通过 Docker 生成 Linux 平台可执行文件 lyedu_backend 的完整流程,包含每行代码的解析。
一、流程概览
[Windows 主机]
│
├─ 1. 执行 build.ps1
│
├─ 2. Docker 拉取 python:3.10-slim-bullseye 镜像
│
├─ 3. 安装 requirements.txt + PyInstaller
│
├─ 4. 复制源码,执行 PyInstaller 打包
│
├─ 5. 导出单文件 lyedu_backend 到 dist/
│
└─ 产出: lyedu-api-python/dist/lyedu_backend(Linux ELF 可执行文件)前置条件:已安装并启动 Docker Desktop。
二、build.ps1 构建脚本
路径:lyedu-api-python/build.ps1
作用:在仓库根目录执行,调用 Docker 构建并导出产物到 lyedu-api-python/dist/。
powershell
# LyEdu build.ps1 - Docker 构建 Linux 可执行文件
# 产出: lyedu-api-python/dist/lyedu_backend
# 使用: .\lyedu-api-python\build.ps1
# 国内网络默认使用清华镜像,直连 Docker Hub 可设: $env:DOCKER_REGISTRY=""
$ErrorActionPreference = "Stop"
$RepoRoot = if ($PSScriptRoot) { Split-Path $PSScriptRoot -Parent } else { Split-Path (Get-Location) -Parent }
$DistDir = Join-Path $RepoRoot "lyedu-api-python\dist"
$DistFile = Join-Path $DistDir "lyedu_backend"
New-Item -ItemType Directory -Force -Path $DistDir | Out-Null
# Dockerfile 已内置国内镜像默认值,直连 Docker Hub 可传: --build-arg DOCKER_REGISTRY= --build-arg PIP_INDEX=
Write-Host "[LyEdu] Building in Docker (Linux target) ..." -ForegroundColor Cyan
$env:DOCKER_BUILDKIT = "1"
$dest = (Resolve-Path $DistDir).Path
$argList = @(
"build", "-f", "lyedu-api-python/Dockerfile.build",
"--target", "export", "--progress=plain",
"--output", "type=local,dest=$dest", "."
)
if ($env:DOCKER_REGISTRY) { $argList += "--build-arg", "DOCKER_REGISTRY=$env:DOCKER_REGISTRY" }
if ($env:PIP_INDEX) { $argList += "--build-arg", "PIP_INDEX=$env:PIP_INDEX" }
Push-Location $RepoRoot
try {
$ErrorActionPreference = "Continue"
& docker @argList
$ErrorActionPreference = "Stop"
} finally {
Pop-Location
}
if (Test-Path $DistFile) {
Write-Host "[LyEdu] Build done: lyedu-api-python\dist\lyedu_backend" -ForegroundColor Green
} else {
Write-Host "[LyEdu] Output not found, check build log above." -ForegroundColor Yellow
exit 1
}逐行解析
| 行号 | 代码 | 解析 |
|---|---|---|
| 6 | $ErrorActionPreference = "Stop" | 遇到错误时停止执行,避免静默失败 |
| 7 | $RepoRoot = if ($PSScriptRoot) { Split-Path $PSScriptRoot -Parent } else { Split-Path (Get-Location) -Parent } | 获取仓库根目录:脚本在 lyedu-api-python/ 下,$PSScriptRoot 为其目录,-Parent 得到 LyEdu 根目录 |
| 8 | $DistDir = Join-Path $RepoRoot "lyedu-api-python\dist" | 构建产物目录:<仓库根>/lyedu-api-python/dist |
| 9 | $DistFile = Join-Path $DistDir "lyedu_backend" | 最终可执行文件路径:<仓库根>/lyedu-api-python/dist/lyedu_backend |
| 11 | New-Item -ItemType Directory -Force -Path $DistDir | Out-Null | 强制创建目录,不存在则创建,Out-Null 丢弃输出 |
| 15 | $env:DOCKER_BUILDKIT = "1" | 启用 BuildKit,支持 --output type=local 导出到本地 |
| 16 | $dest = (Resolve-Path $DistDir).Path | 获取 dist 的绝对路径,供 Docker 使用 |
| 17-21 | $argList = @(...) | 构建 docker build 参数列表:• -f lyedu-api-python/Dockerfile.build 指定 Dockerfile• --target export 只构建到 export 阶段• --progress=plain 普通进度输出• --output type=local,dest=$dest 导出到本地 |
| 22-23 | if ($env:DOCKER_REGISTRY) { ... } | 若设置了环境变量,则传入对应 --build-arg,用于国内镜像 |
| 24 | Push-Location $RepoRoot | 切换到仓库根目录,Docker 构建上下文需在根目录 |
| 26 | $ErrorActionPreference = "Continue" | 临时允许继续执行,避免 Docker 的 stderr 导致脚本中断 |
| 27 | & docker @argList | 执行 docker build,@argList 展开为参数 |
| 29 | Pop-Location | 恢复原工作目录 |
| 33-37 | if (Test-Path $DistFile) { ... } else { ... } | 检查产物是否存在,成功则提示路径,失败则退出码 1 |
三、Dockerfile.build
路径:lyedu-api-python/Dockerfile.build
作用:多阶段构建,在 Linux 容器内完成 PyInstaller 打包并导出单文件。
dockerfile
# LyEdu 后端 PyInstaller 打包镜像(分层构建,基础环境缓存复用)
# 使用: 见 lyedu-api-python/build.ps1
#
# 分层说明:
# Stage base - Python + requirements + PyInstaller(仅在 requirements 变更时重建)
# Stage builder - 复制源码并打包(源码变更时重建,复用 base 缓存)
#
# 国内网络: 默认 1ms 镜像,直连可传 --build-arg DOCKER_REGISTRY=
ARG DOCKER_REGISTRY=docker.1ms.run/library/
ARG PIP_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple
# ========== Stage 1: 基础环境(显式 -bullseye 锁定 GLIBC 2.31,兼容 CentOS 8、Ubuntu 20.04 等) ==========
FROM ${DOCKER_REGISTRY}python:3.10-slim-bullseye AS base
ARG PIP_INDEX
WORKDIR /build
# 先只复制依赖文件,利用 Docker 层缓存
COPY lyedu-api-python/requirements.txt .
# 安装运行时依赖 + PyInstaller,此层会被缓存(PIP_INDEX 为空时使用默认源)
RUN pip install --no-cache-dir ${PIP_INDEX:+-i "$PIP_INDEX"} -r requirements.txt \
&& pip install --no-cache-dir ${PIP_INDEX:+-i "$PIP_INDEX"} pyinstaller
# ========== Stage 2: 打包(源码变更时重建,基础环境复用) ==========
FROM base AS builder
# PyInstaller 在 Linux 下需要 objdump(来自 binutils)
RUN apt-get update && apt-get install -y --no-install-recommends binutils && rm -rf /var/lib/apt/lists/*
# 复制完整源码,.dockerignore 排除 build/dist/.venv 等
COPY lyedu-api-python /build
WORKDIR /build
# 执行打包
RUN python -m PyInstaller lyedu_backend.spec --clean --distpath /out
# ========== Stage 3: 导出层(scratch 仅含可执行文件,避免 Alpine 符号链接在 Windows 导出失败) ==========
FROM scratch AS export
COPY --from=builder /out/lyedu_backend /lyedu_backend逐行解析
| 行号 | 代码 | 解析 |
|---|---|---|
| 9 | ARG DOCKER_REGISTRY=docker.1ms.run/library/ | 构建参数,默认使用国内 Docker 镜像;直连时可传空 |
| 10 | ARG PIP_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple | 默认 PyPI 镜像,国内加速 |
| 13 | FROM ${DOCKER_REGISTRY}python:3.10-slim-bullseye AS base | 基础镜像:Python 3.10,Debian Bullseye,GLIBC 2.31,兼容 CentOS 8、Ubuntu 20.04 等 |
| 15 | ARG PIP_INDEX | 在 base 阶段再次声明,以便 RUN 使用 |
| 17 | WORKDIR /build | 工作目录设为 /build |
| 20 | COPY lyedu-api-python/requirements.txt . | 仅复制依赖文件,便于依赖未变时复用缓存 |
| 23-24 | RUN pip install ... | 安装依赖:${PIP_INDEX:+-i "$PIP_INDEX"} 表示 PIP_INDEX 非空时加 -i 参数;安装 requirements 和 pyinstaller |
| 27 | FROM base AS builder | 第二阶段,继承 base |
| 30 | RUN apt-get update && apt-get install -y --no-install-recommends binutils && rm -rf /var/lib/apt/lists/* | 安装 binutils(提供 objdump),PyInstaller 在 Linux 下需要;删除 apt 缓存减小镜像 |
| 33 | COPY lyedu-api-python /build | 复制源码到 /build,.dockerignore 会排除 build/dist/.venv 等 |
| 35 | WORKDIR /build | 工作目录设为 /build |
| 38 | RUN python -m PyInstaller lyedu_backend.spec --clean --distpath /out | 执行 PyInstaller:--clean 清理缓存,--distpath /out 输出到 /out |
| 41 | FROM scratch AS export | 导出阶段:空镜像,无文件系统,避免符号链接和多余依赖 |
| 42 | COPY --from=builder /out/lyedu_backend /lyedu_backend | 从 builder 阶段复制 /out/lyedu_backend 到 /lyedu_backend,供 BuildKit 导出 |
四、lyedu_backend.spec(PyInstaller 配置)
路径:lyedu-api-python/lyedu_backend.spec
作用:与 Windows exe 共用,定义 PyInstaller 打包规则。
python
# -*- mode: python ; coding: utf-8 -*-
# 打包 alembic.ini 和 alembic 目录,首次运行 exe 时复制到 ~/.lyedu/(与 config.ini 同层级)
a = Analysis(
['main.py'], # 入口脚本
pathex=[],
binaries=[],
datas=[
('alembic.ini', '.'), # 打包数据库迁移配置
('alembic', 'alembic'), # 打包迁移脚本目录
],
...
)
pyz = PYZ(a.pure) # 纯 Python 模块
exe = EXE(
pyz, a.scripts, a.binaries, a.datas,
[],
name='lyedu_backend', # 输出文件名(Linux 无 .exe)
icon='favicon.ico', # 仅 Windows/macOS 生效
...
)关键配置
| 配置 | 说明 |
|---|---|
main.py | 程序入口 |
datas | 将 alembic.ini 和 alembic 目录打包进可执行文件 |
name='lyedu_backend' | Linux 下输出为 lyedu_backend,Windows 下为 lyedu_backend.exe |
五、运行方式
1. 构建
在仓库根目录或 lyedu-api-python 目录执行:
powershell
.\lyedu-api-python\build.ps12. 产物
- 路径:
lyedu-api-python/dist/lyedu_backend - 类型:Linux ELF 可执行文件,单文件,无需系统 Python
3. 在 Linux 上运行
bash
chmod +x lyedu_backend
./lyedu_backend配置与 exe 相同:优先使用 ~/.lyedu/conf/config.ini,首次运行会从 alembic 生成迁移目录。
4. 直连 Docker Hub 时
powershell
$env:DOCKER_REGISTRY = ""
$env:PIP_INDEX = ""
.\lyedu-api-python\build.ps1六、依赖关系说明
| 依赖 | 说明 |
|---|---|
| 系统 Python | 不需要,PyInstaller 已打包解释器 |
| 系统 GLIBC | 需要,目标系统 GLIBC ≥ 2.31(Bullseye 对应版本) |
| 配置文件 | 使用 ~/.lyedu/conf/config.ini(MySQL、Redis 等) |
七、常见问题
- GLIBC 版本不足:若目标系统 GLIBC < 2.31,需改用更老的基础镜像(如
python:3.10-slim-buster),或升级目标系统。 - Docker 构建失败:国内网络可优先使用 Dockerfile 中默认的 1ms 镜像;若需更换,可设置
DOCKER_REGISTRY。
