python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python项目打包

从源码到Docker全方位解析Python项目打包完整指南

作者:大头an

在实际开发中,将Python项目打包成可部署的格式是一个至关重要的环节,本文将全面介绍Python项目的各种打包方式,从基础的分发打包到现代化的Docker容器化部署,希望对大家有所帮助

一、Python打包工具全景图

在开始具体打包之前,我们先了解下主流的Python打包工具及其适用场景:

工具适用场景特点输出格式
setuptools传统Python库Python生态标准,历史悠久wheel, source distribution
Poetry现代Python项目依赖管理优秀,配置简洁wheel, source distribution
PDM新式Python项目快速,PEP 582支持wheel, source distribution
PyInstaller桌面应用程序跨平台,单文件打包EXE, APP, 可执行文件
Docker微服务部署环境隔离,持续交付Docker Image
Nuitka高性能需求编译为C,性能提升可执行文件,扩展模块

二、传统setuptools打包详解

2.1 项目结构准备

一个标准的Python项目结构如下:

MyProject/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── main.py
│       └── utils.py
├── tests/
├── docs/
├── requirements.txt
└── setup.py

2.2 基础setup.py配置

# setup.py - 基础配置
from setuptools import setup, find_packages

setup(
    name="myproject",
    version="1.0.0",
    author="Your Name",
    author_email="your.email@example.com",
    description="A sample Python project",
    long_description=open("README.md").read(),
    long_description_content_type="text/markdown",
    packages=find_packages(where="src"),
    package_dir={"": "src"},
    classifiers=[
        "Development Status :: 3 - Alpha",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
    ],
    python_requires=">=3.8",
    install_requires=[
        "requests>=2.25.0",
        "click>=8.0.0",
    ],
    entry_points={
        "console_scripts": [
            "myproject=mypackage.main:main",
        ],
    },
)

2.3 进阶配置(setup.cfg)

# setup.cfg - 现代配置方式
[metadata]
name = myproject
version = 1.0.0
author = Your Name
author_email = your.email@example.com
description = A sample Python project
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/username/myproject
classifiers =
    Development Status :: 3 - Alpha
    Intended Audience :: Developers
    License :: OSI Approved :: MIT License
    Programming Language :: Python :: 3
    Programming Language :: Python :: 3.8
    Programming Language :: Python :: 3.9
    Programming Language :: Python :: 3.10

[options]
packages = find:
package_dir =
    = src
python_requires = >=3.8
install_requires =
    requests>=2.25.0
    click>=8.0.0

[options.packages.find]
where = src

[options.entry_points]
console_scripts =
    myproject = mypackage.main:main

2.4 打包命令

# 安装构建工具
pip install build

# 构建源码包和wheel包
python -m build

# 或者使用传统方式
python setup.py sdist bdist_wheel

# 安装到当前环境(开发模式)
pip install -e .

# 清理构建文件
python setup.py clean --all
rm -rf build dist *.egg-info

三、Poetry现代化打包

Poetry是当前最流行的Python打包和依赖管理工具之一。

3.1 项目初始化

# 创建新项目
poetry new myproject
cd myproject

# 或在现有项目初始化
poetry init

3.2 pyproject.toml配置

# pyproject.toml - Poetry配置
[tool.poetry]
name = "myproject"
version = "1.0.0"
description = "A sample Python project"
authors = ["Your Name <your.email@example.com>"]
readme = "README.md"
license = "MIT"

[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.25.0"
click = "^8.0.0"

[tool.poetry.dev-dependencies]
pytest = "^7.0.0"
black = "^22.0.0"
flake8 = "^4.0.0"

[tool.poetry.scripts]
myproject = "myproject.main:main"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

# 可选:工具配置
[tool.black]
line-length = 88
target-version = ['py38']

[tool.pytest.ini_options]
testpaths = ["tests"]

3.3 Poetry命令

# 安装依赖
poetry install

# 添加依赖
poetry add requests
poetry add --dev pytest

# 构建项目
poetry build

# 发布到PyPI
poetry publish

# 运行脚本
poetry run myproject

# 进入虚拟环境
poetry shell

四、PDM快速打包

PDM是一个现代的Python包管理器,具有快速的依赖解析。

4.1 PDM项目配置

# 初始化项目
pdm init

# 添加依赖
pdm add requests click
pdm add -d pytest black

4.2 pyproject.toml (PDM版本)

[project]
name = "myproject"
version = "1.0.0"
description = "A sample Python project"
authors = [
    {name = "Your Name", email = "your.email@example.com"}
]
dependencies = [
    "requests>=2.25.0",
    "click>=8.0.0"
]
requires-python = ">=3.8"

[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "black>=22.0.0"
]

[project.scripts]
myproject = "myproject.main:main"

[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"

4.3 PDM命令

# 安装依赖
pdm install

# 构建项目
pdm build

# 运行脚本
pdm run myproject

# 锁定依赖
pdm lock

五、可执行文件打包

5.1 使用PyInstaller

# build_spec.py - PyInstaller构建配置
import PyInstaller.__main__

PyInstaller.__main__.run([
    'src/mypackage/main.py',
    '--name=myproject',
    '--onefile',  # 单文件打包
    '--windowed',  # 无控制台窗口(GUI应用)
    '--add-data=src/mypackage/data;mypackage/data',  # 包含数据文件
    '--icon=assets/icon.ico',  # 应用图标
    '--hidden-import=some_hidden.module',  # 隐藏导入
    '--clean',  # 清理临时文件
])
# 命令行方式
pyinstaller src/mypackage/main.py --onefile --name myproject

# 使用spec文件
pyinstaller myproject.spec

5.2 使用cx_Freeze

# setup_cxfreeze.py
import sys
from cx_Freeze import setup, Executable

build_exe_options = {
    "packages": ["os", "sys", "requests"],
    "excludes": ["tkinter"],
    "include_files": ["data/", "config.ini"],
}

base = None
if sys.platform == "win32":
    base = "Win32GUI"  # 用于GUI应用

setup(
    name="myproject",
    version="1.0.0",
    description="My Python Application",
    options={"build_exe": build_exe_options},
    executables=[Executable("src/mypackage/main.py", base=base)],
)

六、Docker容器化部署

6.1 多阶段构建Dockerfile

# 第一阶段:构建阶段
FROM python:3.9-slim as builder

WORKDIR /app

# 安装构建依赖
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 创建虚拟环境
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 第二阶段:运行阶段
FROM python:3.9-slim

WORKDIR /app

# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 从构建阶段拷贝虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 拷贝应用代码
COPY --chown=appuser:appuser . .

# 切换到非root用户
USER appuser

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["python", "-m", "mypackage.main"]

6.2 优化版Dockerfile(Poetry)

# 使用多阶段构建优化镜像大小
FROM python:3.9-slim as builder

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 安装Poetry
RUN pip install poetry

# 配置Poetry不创建虚拟环境
RUN poetry config virtualenvs.create false

# 拷贝依赖文件
COPY pyproject.toml poetry.lock* ./

# 安装依赖(仅运行时)
RUN poetry install --no-dev --no-interaction --no-ansi

# 运行阶段
FROM python:3.9-slim

WORKDIR /app

# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 从构建阶段拷贝已安装的包
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin

# 拷贝应用代码
COPY --chown=appuser:appuser . .

USER appuser

# 设置环境变量
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1

EXPOSE 8000

CMD ["python", "-m", "mypackage.main"]

6.3 Docker Compose编排

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - app-network

  redis:
    image: redis:6-alpine
    volumes:
      - redis_data:/data
    networks:
      - app-network

volumes:
  db_data:
  redis_data:

networks:
  app-network:
    driver: bridge

七、高级打包技巧

7.1 使用Nuitka编译优化

# 基础编译
nuitka3 --standalone --onefile src/mypackage/main.py

# 优化版本
nuitka3 \
    --standalone \
    --onefile \
    --enable-plugin=pyqt5 \
    --include-package-data=mypackage \
    --output-filename=myapp \
    src/mypackage/main.py

# Windows特定优化
nuitka3 --windows-icon-from-ico=assets/icon.ico src/mypackage/main.py

7.2 自动化构建脚本

#!/bin/bash
# build.sh - Python项目自动构建脚本

set -e

echo "🚀 开始构建Python项目..."

# 清理构建目录
echo "🧹 清理构建文件..."
rm -rf build/ dist/ *.egg-info/ .pytest_cache/ __pycache__/
find . -name "*.pyc" -delete

# 代码检查
echo "🔍 运行代码检查..."
python -m flake8 src/
python -m black --check src/
python -m isort --check-only src/

# 运行测试
echo "🧪 运行测试..."
python -m pytest tests/ -v

# 构建包
echo "📦 构建分发包..."
python -m build

# 构建Docker镜像
echo "🐳 构建Docker镜像..."
docker build -t myproject:latest .

echo "✅ 构建完成!"
echo "📦 分发包位置: dist/"
echo "🐳 Docker镜像: myproject:latest"

7.3 GitHub Actions自动化

# .github/workflows/ci-cd.yml
name: Python CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.8, 3.9, 3.10]

    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v3
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
        
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e .[dev]
        
    - name: Lint with flake8
      run: |
        flake8 src/ tests/ --count --show-source --statistics
        
    - name: Test with pytest
      run: |
        pytest tests/ -v
        
  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build and push Docker image
      run: |
        echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
        docker build -t myproject:${{ github.sha }} .
        docker push myproject:${{ github.sha }}

八、最佳实践总结

8.1 项目结构标准化

myproject/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── main.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_main.py
├── docs/
├── scripts/
│   └── build.sh
├── .github/
│   └── workflows/
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
├── README.md
└── .gitignore

8.2 依赖管理最佳实践

# pyproject.toml - 依赖管理示例
[project]
dependencies = [
    "requests>=2.25.0,<3.0.0",  # 版本范围
    "click>=8.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "black>=22.0.0",
    "flake8>=4.0.0",
]
test = [
    "pytest>=7.0.0",
    "pytest-cov>=3.0.0",
]
docs = [
    "sphinx>=4.0.0",
    "sphinx-rtd-theme>=1.0.0",
]

8.3 安全考虑

# 安全加固的Dockerfile
FROM python:3.9-slim

# 安全更新
RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*

# 创建非特权用户
RUN groupadd -r appuser && useradd -r -g appuser -s /bin/false appuser

# 应用适当的文件权限
RUN chown -R appuser:appuser /app
USER appuser

# 使用可信的基础镜像
# 定期更新依赖
# 扫描安全漏洞

8.4 性能优化

# 优化版Dockerfile
FROM python:3.9-slim

# 使用国内镜像源加速
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# 禁用缓存和pip版本检查
ENV PIP_NO_CACHE_DIR=1
ENV PIP_DISABLE_PIP_VERSION_CHECK=1

# 优化Python设置
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

结语

Python项目打包已经从简单的源码分发发展到现代化的容器化部署。掌握这些打包技术对于Python开发者至关重要。无论是传统的库分发还是现代的微服务,选择合适的打包方式都能大大提高开发和部署效率。

以上就是从源码到Docker全方位解析Python项目打包完整指南的详细内容,更多关于Python项目打包的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文