You have two options to follow along with this guide:
-
Build the project from scratch by manually setting up the structure and copy-pasting the provided code base (src and tests folders).
-
Clone the repository, install dependencies using the command
uv sync
, and run the commands explained below directly to:- Execute the test suite
- Build the Docker image
- Modify and test GitHub Actions
Ship it like it's hot! π’π₯
MLOps (Machine Learning Operations) is all about bringing DevOps principles into machine learning, making model deployment, versioning, and monitoring more efficient. However, managing dependencies, ensuring reproducibility, and streamlining deployments can be a major headache for ML/DS teams.
That's where UV comes inβa fast, modern package manager that simplifies dependency management, build processes, and CI/CD for Python projects.
In this article, we'll explore how UV can enhance MLOps workflows through AceBet, a mock-up FastAPI app that predicts the winner of an ATP match (for demonstration purposes onlyβdonβt bet your savings on it!). We'll cover:
- Setting up a UV-based MLOps project
- Managing dependencies and lockfiles
- Automating CI/CD with GitHub Actions
- Building and deploying with Docker
Make sure to read:
When working on an MLOps project, structuring your codebase properly is crucial. We'll start by setting up a packaged application using UV:
uv init --package acebet
A packaged application follows the src-based structure, where the source code is contained within a dedicated package directory (src/acebet
). This approach is beneficial for:
β
Large applications with multiple modules
β
Projects that need to be distributed (e.g., PyPI packages, CLI tools)
β
Better namespace isolation, preventing import conflicts
β
Improved testability and modularity
acebet/
βββ src/
β βββ acebet/
β β βββ __init__.py
β β βββ data_prep.py
β β βββ model.py
β β βββ predict.py
β β βββ api.py
β β βββ utils.py
βββ tests/
β βββ test_model.py
βββ pyproject.toml
βββ README.md
This structure ensures:
β Encapsulation: The application is a proper Python package, avoiding accidental name conflicts.
β Reusability: Can be installed via pip install .
or published to PyPI.
β Cleaner Imports: Enforces absolute imports (from acebet.utils import foo
) instead of relative imports.
β Better CI/CD Support: Easier to package and distribute in Docker, PyPI, or GitHub Actions.
π For quick scripts or internal projects? Use a regular application.
π For scalable, maintainable, and deployable projects? Use a packaged application.
Since AceBet is a full MLOps project, weβll use a packaged application.
Once your project is initialized, install the necessary dependencies for developing AceBet, including FastAPI and machine learning libraries like Scikit-learn:
uv add fastapi scikit-learn pandas lightgbm
UV will automatically resolve versions and install the required packages.
One of UV's key advantages is ensuring dependency reproducibility with a lockfile. This guarantees that all environments (local, staging, production) use the same dependency versions.
Once you're satisfied with the initial codebase, generate a lockfile:
uv lock
Or, if you want to sync all dependencies in one go:
uv sync
This process ensures version consistency across environments, which is an essential practice in MLOps.
Testing is just as important as model accuracy in MLOps. UV provides three different ways to manage testing tools (e.g., pytest
):
Method | Command | Use Case |
---|---|---|
Adding as Dev Dependency | uv add --dev pytest |
When pytest is part of your Python project |
Running Temporarily | uvx pytest tests |
When you only need to run it occasionally |
Installing Persistently | uv tool install pytest |
When you need pytest in Docker or as a global CLI |
For MLOps best practices, we add testing dependencies:
uv add --dev pytest
To run tests:
uv run pytest tests
π Best Practice: Explicitly list all required dependencies in pyproject.toml
for consistent test environments.
A CI/CD pipeline ensures your models and applications remain production-ready. UV makes GitHub Actions setup seamless.
A simple workflow to run tests on every commit to main
:
name: Testing
on:
push:
branches:
- "main"
jobs:
uv-example:
name: Python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install UV
uses: astral-sh/setup-uv@v5
- name: Install the project
run: uv sync --all-extras --dev
- name: Run tests
run: uv run pytest tests
β
Installs UV
β
Syncs dependencies
β
Runs unit tests using Pytest
A well-built Docker image ensures consistent deployment across environments. UV simplifies containerization.
FROM python:3.12-slim
# Install UV
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Copy the application into the container
COPY . /app
# Set working directory
WORKDIR /app
# Install dependencies
RUN uv sync --frozen --no-cache
# Run the FastAPI app
CMD ["/app/.venv/bin/fastapi", "run", "src/acebet/api.py", "--port", "80", "--host", "0.0.0.0"]
For production-ready builds, use a multi-stage Docker build to keep the image lightweight.
Feature | UV π (Rust-based) | Poetry π οΈ (Python-based) |
---|---|---|
β© Performance & Speed | β‘ 8-10x faster than pip & pip-tools (80-115x with cache). Ideal for CI/CD. | π’ Slower dependency resolution and package installation. Can be a bottleneck in large projects. |
π§ Dependency Management | Uses a global cache to avoid redundant installations. Faster and more efficient. | Uses a custom resolver, but can be slow for large projects. |
π¦ Environment Handling | Manages Python versions natively (no need for pyenv). Creates fast, lightweight virtual environments. | Supports virtual environments but requires external tools for Python version management. |
π³ Docker Efficiency | β Smaller images & faster builds. Simplifies deployment by combining Python & dependency management. | β Larger image footprint due to reliance on multiple tools. Longer build times. |
π CI/CD Pipelines | β Faster builds due to Rust-based optimizations. Reduces install time in GitHub Actions, Docker, and cloud environments. | β Slower CI/CD performance due to Python-based dependency resolution. |
π Migration & Ecosystem | β Follows PEP standards closely, making migration easier. Less tightly integrated, offering flexibility. | β More opinionated ecosystem, making migration or integration with existing tools more complex. |
π Authentication & Config | β Simplifies authentication using environment variables. Ensures cross-platform consistency. | β Configuration can be complex, requiring additional setup for cross-platform consistency. |
π Unified Tooling | β All-in-one tool: Handles package management, virtual environments, and Python versions. No need for extra tools. | β Depends on multiple tools (e.g., pyenv for Python versioning), increasing setup complexity. |
ποΈ Build & Deployment | β Optimized for modern workflows. Generates smaller wheels and installs faster in Docker, Kubernetes, and cloud deployments. | β Traditional package builds, not as optimized for modern DevOps/MLOps pipelines. |
- If you need speed, lightweight builds, and a streamlined DevOps workflow, UV is the better choice. π
- If you prefer a well-established but slower tool with more integrated features, Poetry remains viable. π οΈ
- For MLOps & CI/CD, UV's speed and efficiency make it the preferred option. π‘
By integrating UV into your MLOps workflow, you get a fast, reproducible, and efficient setup for managing dependencies, testing, and deployment.
With AceBet, we demonstrated how to:
βοΈ Initialize a UV-based project
βοΈ Manage dependencies & lockfiles
βοΈ Automate testing with GitHub Actions
βοΈ Build Docker images for deployment
Give UV a tryβit might just replace Pip and Poetry in your workflow! π
Happy Coding!