使用 uv 构建隔离的数据管道环境:Data Engineering Zoomcamp 虚拟环境实战
2026/9/12 12:06:57 网站建设 项目流程

使用 uv 构建隔离的数据管道环境:Data Engineering Zoomcamp 虚拟环境实战

【免费下载链接】data-engineering-zoomcampData Engineering Zoomcamp is a free 9-week course on building production-ready data pipelines. Join the course here 👇🏼项目地址: https://gitcode.com/GitHub_Trending/da/data-engineering-zoomcamp

在数据工程实践中,一个典型的数据管道(data pipeline)会经历"读取数据 → 转换处理 → 落地存储"的完整生命周期。本篇指南以 Data Engineering Zoomcamp 的cohorts/2027/01-docker-terraform模块为背景,讲解如何使用uv这一现代化 Python 包管理器创建隔离的虚拟环境、管理依赖并运行数据管道脚本。学完本篇,你将掌握虚拟环境的原理与必要性、uv的核心命令工作流,以及如何将本地环境无缝迁移到 Docker 容器中的完整路径。

数据管道:接收数据、输出数据的服务

数据管道是一种以数据为输入、以数据为输出的服务。以最简单的形式为例:读取一个 CSV 文件,对数据进行某种转换,然后将其存储为 PostgreSQL 数据库中的一张表。

在本模块的 workshop 中,我们将构建具备以下能力的管道:

  • 从网络下载 CSV 数据
  • 使用 pandas 转换和清洗数据
  • 将数据加载到 PostgreSQL 中以供查询
  • 分块(chunk)处理数据以应对大文件

仓库中完整的落地实现位于 pipeline 目录,包含摄取脚本、Dockerfile、docker-compose 配置与辅助脚本,后续章节会逐步对照说明。

创建第一个简单管道

首先创建一个pipeline目录,并在其中创建文件pipeline.py

import sys print("arguments", sys.argv) day = int(sys.argv[1]) print(f"Running pipeline for day {day}")

这个脚本演示了管道最基础的形态:通过命令行参数接收输入(某一天的编号),打印出运行信息。接下来引入 pandas,让管道具备真正的数据处理能力:

import pandas as pd df = pd.DataFrame({"A": [1, 2], "B": [3, 4]}) print(df.head()) df.to_parquet(f"output_day_{sys.argv[1]}.parquet")

此时管道会构造一个 DataFrame、打印其内容,并把结果输出为按日期命名的 Parquet 文件——这就是"数据输入 → 转换 → 输出"的雏形。

为什么需要虚拟环境

现在管道依赖 pandas,但系统中并没有安装它。在正式进入容器(container)之前,我们希望先在本地测试脚本。最直接的方式是用pip安装:

pip install pandas pyarrow

但这样做会把包全局安装到系统 Python 中。如果不同项目需要同一个包的不同版本,全局安装就会引发版本冲突。例如项目 A 需要pandas 1.x,而项目 B 需要pandas 2.x,全局共存几乎不可能。

解决方案是使用虚拟环境(virtual environment):一个相互隔离的 Python 环境,将当前项目的依赖与其他项目、以及系统 Python 彻底分隔开。虚拟环境保证:

  • 依赖隔离:每个项目拥有独立的包集合与版本;
  • 环境可复现:配合锁文件可以精确重建相同的运行环境;
  • 系统安全:不会污染系统级 Python,也不会被系统升级破坏。

uv:更现代的 Python 包管理器

本模块采用uv——一个用 Rust 编写的高性能 Python 包与项目管理器。它比pip快得多,并且自动管理虚拟环境,省去了手动创建与激活环境的繁琐步骤。

安装 uv:

pip install uv

然后初始化一个 Python 项目:

uv init --python=3.13

该命令会生成两个关键文件:

  • pyproject.toml:项目的依赖声明文件(项目元数据 + 依赖列表);
  • .python-version:锁定项目使用的 Python 版本。

同时 uv 会自动创建虚拟环境(.venv目录)。仓库中真实的 pyproject.toml 展示了项目化后的完整形态:

[project] name = "pipeline" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.13" dependencies = [ "click>=8.3.1", "pandas>=2.3.3", "psycopg2-binary>=2.9.11", "pyarrow>=22.0.0", "sqlalchemy>=2.0.44", "tqdm>=4.67.1", ] [dependency-groups] dev = [ "jupyter>=1.1.1", "pgcli>=4.3.0", ]

可以看到requires-python = ">=3.13"uv init --python=3.13一一对应,而dev依赖组则专门放 Jupyter、pgcli 这类仅用于开发调试的工具。

对比虚拟环境与系统 Python

执行下面的命令,可以直观看到两种 Python 的差异:

uv run which python # Python in the virtual environment uv run python -V which python # System Python python -V

uv run会优先使用虚拟环境中的解释器,因此两条命令输出的路径与版本号通常不同——这正是环境隔离的直观证明:uv run运行在独立环境中,不依赖也不影响系统 Python。

添加依赖并运行管道

向项目添加 pandas 与 pyarrow:

uv add pandas pyarrow

该命令会把依赖写入pyproject.toml,并同步安装到虚拟环境中。随后即可运行管道:

uv run python pipeline.py 10

预期的输出为:

  • ['pipeline.py', '10']
  • job finished successfully for day = 10

sys.argv列表中的两个元素分别对应脚本名和传入的参数10,说明管道已正确接收并处理了命令行输入。

从示例到真实工程:仓库中的完整摄取管道

示例管道演示了概念,而仓库 pipeline/ingest_data.py 给出了生产级的完整实现——将 NY 出租车数据摄取到 PostgreSQL 的脚本。它与示例管道一脉相承,但补充了大量工程细节:

  • 命令行参数解析:使用click声明式定义参数,核心参数包括--pg-user--pg-pass--pg-host--pg-port(默认 5432)、--pg-db(默认ny_taxi)、--year--month--target-table--chunksize(默认 100000);
  • 显式数据类型:通过dtype字典为 16 个字段指定类型(如VendorID"Int64"、金额字段用"float64"store_and_fwd_flag"string"),避免 pandas 在读取大文件时因列类型混合产生DtypeWarning
  • 日期解析:通过parse_dates列表将tpep_pickup_datetimetpep_dropoff_datetime解析为时间戳;
  • 分块摄取pd.read_csv(..., iterator=True, chunksize=100000)按 10 万行一批读取数据,配合tqdm进度条逐块写入 PostgreSQL——这正是原文档强调的"处理大文件"能力的工程化体现。

分块写入的完整循环逻辑如下(与 05-data-ingestion.md 中的 Jupyter 版本一致):第一块数据用df_chunk.head(0).to_sql(..., if_exists='replace')仅创建表结构,后续每块数据用if_exists='append'追加写入。执行摄取脚本的命令为:

uv run python ingest_data.py \ --pg-user=root \ --pg-pass=root \ --pg-host=localhost \ --pg-port=5432 \ --pg-db=ny_taxi \ --target-table=yellow_taxi_trips

从虚拟环境到容器:uv 保证环境一致性

虚拟环境解决的是本地开发的一致性,而生产运行还需要容器。uv 的优势在于:同一套依赖声明(pyproject.toml+uv.lock)既可以驱动本地虚拟环境,也可以驱动 Docker 镜像构建,实现"本地与容器完全一致"。

仓库 pipeline/Dockerfile 展示了这一实践:

FROM python:3.13.11-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/ WORKDIR /code ENV PATH="/code/.venv/bin:$PATH" COPY pyproject.toml .python-version uv.lock ./ RUN uv sync --locked COPY ingest_data.py . ENTRYPOINT ["python", "ingest_data.py"]

逐行解读:

  • FROM python:3.13.11-slim:使用精简版 Python 3.13 镜像,缩小镜像体积;
  • COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/:从官方 uv 镜像复制 uv 二进制(多阶段构建模式);
  • WORKDIR /code:设置容器内工作目录;
  • ENV PATH="/code/.venv/bin:$PATH":把虚拟环境加入 PATH,使容器内可直接使用已安装的包;
  • COPY pyproject.toml .python-version uv.lock ./:先复制依赖清单文件,充分利用 Docker 层缓存;
  • RUN uv sync --locked:基于锁文件安装依赖,--locked确保安装版本与锁定版本完全一致,构建可复现;
  • COPY ingest_data.py .:复制应用代码;
  • ENTRYPOINT ["python", "ingest_data.py"]:容器启动时执行摄取脚本。

值得一提的是,uv init时生成的uv.lock锁文件是环境可复现的关键:它精确记录了每个依赖及其传递依赖的版本哈希,无论本地还是容器,uv sync --locked都会还原出完全相同的依赖树。在 03-dockerizing-pipeline.md 中,还给出了两种 Dockerfile 写法(pip install版与 uv 版)的对比,uv 版在构建速度与可复现性上更优。

Git 配置:防止提交生成文件

管道运行后会产生 Parquet 等二进制产物,这些文件不应进入版本控制。在.gitignore中添加:

*.parquet

这样 git 会忽略所有 Parquet 文件,避免把大型二进制文件误提交到仓库中。同理,虚拟环境目录.venv/也应纳入忽略列表,因为它是可随时重建的本地产物。

小结

通过本篇实战,你完成了从零构建数据管道的完整链路:

  1. 理解数据管道"输入 → 转换 → 输出"的基本形态;
  2. 理解虚拟环境解决全局依赖冲突的核心价值;
  3. 掌握uv的完整工作流:uv inituv adduv run
  4. 通过which python对比验证环境隔离效果;
  5. 对照 pipeline 目录了解生产级摄取脚本的分块处理与参数化设计;
  6. 理解 uv 锁文件如何让 Docker 镜像与本地环境保持一致;
  7. 通过.gitignore管理生成文件。

这套"虚拟环境 + uv 锁文件 + Dockerfile"的组合,正是后续模块中所有数据管道项目标准化环境管理的基石。接下来可以继续阅读 Dockerizing the Pipeline 与 Running PostgreSQL with Docker,将管道真正容器化并连接数据库。

【免费下载链接】data-engineering-zoomcampData Engineering Zoomcamp is a free 9-week course on building production-ready data pipelines. Join the course here 👇🏼项目地址: https://gitcode.com/GitHub_Trending/da/data-engineering-zoomcamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询