Skip to content
个人作品推荐
栾媛爱动物
播放动物叫声趣味微信小程序
栾媛爱动物微信小程序
微信扫码体验

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
11New-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-23if ($env:DOCKER_REGISTRY) { ... }若设置了环境变量,则传入对应 --build-arg,用于国内镜像
24Push-Location $RepoRoot切换到仓库根目录,Docker 构建上下文需在根目录
26$ErrorActionPreference = "Continue"临时允许继续执行,避免 Docker 的 stderr 导致脚本中断
27& docker @argList执行 docker build@argList 展开为参数
29Pop-Location恢复原工作目录
33-37if (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

逐行解析

行号代码解析
9ARG DOCKER_REGISTRY=docker.1ms.run/library/构建参数,默认使用国内 Docker 镜像;直连时可传空
10ARG PIP_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple默认 PyPI 镜像,国内加速
13FROM ${DOCKER_REGISTRY}python:3.10-slim-bullseye AS base基础镜像:Python 3.10,Debian Bullseye,GLIBC 2.31,兼容 CentOS 8、Ubuntu 20.04 等
15ARG PIP_INDEX在 base 阶段再次声明,以便 RUN 使用
17WORKDIR /build工作目录设为 /build
20COPY lyedu-api-python/requirements.txt .仅复制依赖文件,便于依赖未变时复用缓存
23-24RUN pip install ...安装依赖:${PIP_INDEX:+-i "$PIP_INDEX"} 表示 PIP_INDEX 非空时加 -i 参数;安装 requirements 和 pyinstaller
27FROM base AS builder第二阶段,继承 base
30RUN apt-get update && apt-get install -y --no-install-recommends binutils && rm -rf /var/lib/apt/lists/*安装 binutils(提供 objdump),PyInstaller 在 Linux 下需要;删除 apt 缓存减小镜像
33COPY lyedu-api-python /build复制源码到 /build.dockerignore 会排除 build/dist/.venv 等
35WORKDIR /build工作目录设为 /build
38RUN python -m PyInstaller lyedu_backend.spec --clean --distpath /out执行 PyInstaller:--clean 清理缓存,--distpath /out 输出到 /out
41FROM scratch AS export导出阶段:空镜像,无文件系统,避免符号链接和多余依赖
42COPY --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.ps1

2. 产物

  • 路径: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 等)

七、常见问题

  1. GLIBC 版本不足:若目标系统 GLIBC < 2.31,需改用更老的基础镜像(如 python:3.10-slim-buster),或升级目标系统。
  2. Docker 构建失败:国内网络可优先使用 Dockerfile 中默认的 1ms 镜像;若需更换,可设置 DOCKER_REGISTRY