1. Intro
기존에 Poetry를 활용하여 대부분의 프로젝트와 패키지를 관리해왔었는데요. 이번에 uv를 활용해보면서 여러 장점을 느꼈고, 나아가 production 환경에서 테스트, 배포까지 uv로 변경해보고 싶은 욕심이 들었습니다. 그중 기존의 작업 중 하나인 merge 전에 테스트 통과 여부를 확인하는 Github Action의 workflows도 변경했었는데, 이번 포스트에서는 테스트를 위한 workflow를 어떻게 작성하면 좋을지 공유해보려고 합니다.
workflow를 공유하기 앞서, 먼저 굳이 poetry로 잘 돌아가던 프로젝트를 uv로 변경하는 이유에 대해 공유해보고자 합니다. uv로 변경하는 가장 큰 이유는 빌드와 배포 과정에서 유의미한 숫자의 변화를 확인했습니다. 배포의 경우 image를 실행하는 것이기 때문에 속도 면에서는 크게 차이가 나지는 않았지만, image 용량은 차이가 났었습니다.
Build time
빌드 속도는 poetry를 활용했을 때와 uv를 활용했을때의 차이를 비교해보았습니다. 비교하는 환경은 같은 프로젝트에서 각각 poetry, uv를 활용하여 docker build를 실행했고 가장 유의미한 변화를 주기 위해 build 코드는 다음과 같이 차이가 났습니다.
# Install poetry and set up virtual environment.
RUN pip install --upgrade pip
RUN pip install poetry setuptools
RUN poetry install
# Install uv and set up virtual environment.
#
RUN pip install --upgrade pip
RUN pip install uv
RUN uv sync
Docker
복사
각각 다음과 같이 차이가 나타났습니다.
•
poetry : 27.6 seconds
•
uv : 20.7 seconds
빌드 과정에서 Download 시간과 다른 미미한 차이가 있었지만 대체적으로 가상환경 세팅(poetry install과 uv sync)에서 약 5초 정도의 차이가 발생했었습니다. 전체 빌드 시간과 비교하면 20% ~ 25%정도 빌드 시간을 절약할 수 있었습니다.
Image size
빌드 후 이미지 크기를 비교해보았습니다. 빌드 후 이미지 크기를 비교해보니 다음과 같이 차이가 발생했습니다.
•
poetry : 959.18MB
•
uv : 848.82MB
두 용량을 비교하면 약 18% 정도 이미지 크기가 차이가 나는 것을 알 수 있었습니다.
2. ci.yaml
.github/workflows/ci.yaml 파일은 아래와 같습니다.
name: CI
on:
pull_request:
push:
branches:
- main
- develop
- 'release/**'
jobs:
test:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
services:
postgresql:
image: postgres:latest
env:
POSTGRES_USER: SOMETHINGUSER
POSTGRES_PASSWORD: SOMETHINGPASSWORD
POSTGRES_DATABASE: SOMETHINGDATABASE
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
# github action에서 사용할 `uv` 설치
# 캐싱 여부 활성화(Optional) - 옵션 활성화 시 uv.lock 필수
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
# project와 호환되는 python 설치
- name: Set up Python
run: uv python install 3.12
# uv.lock과 pyproject.toml에 정의된 모듈을 .venv를 만들어 싱크를 맞춰줌
- name: Setup virtual environment
run: uv sync
# 테스트 코드 실행
- name: Run Django test
run: |
uv run python manage.py test
env:
DJANGO_SETTINGS_MODULE: "<PROJECT SETTINGS VALUE>"
# 기타 원하는 작업 추가...
YAML
복사
간단히 ci.yaml 에 대한 내용을 주석과 함께 작성해보았습니다. uv와 python 설치 같은 설명은 넘어가고, setup과 caching, 그리고 테스트 코드 실행 부분만 간단히 정리해보겠습니다.
Setup virtual environment
github action에서 실행할 uv와 uv를 활용하여 원하는 버전의 python을 설치했으니, 이제 uv run을 할 준비를 해야 합니다. uv의 경우 실행하는 host(혹은 docker 내)에서 싱크를 맞춰주는 작업을 해줘야 합니다. 만약 poetry로 이러한 설정을 해줘야 한다면 다음과 같이 비교할 수 있을 것 같습니다.
# uv를 활용한 가상환경 세팅
uv sync
# poetry를 활용한 가상환경 세팅
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
Bash
복사
Enable caching
Workflow 실행 전반에 걸쳐 uv 캐시를 저장하면 CI 시간을 절약할 수 있습니다. 이를 위해 캐싱을 적용하고자 한다면 with: enable-cache 옵션만 넣어주면 쉽게 활성화가 가능합니다. 이 역시 poetry와 비교하면 다음과 같습니다.
# uv를 활용한 캐싱 여부 활성화
- name: Enable caching
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
# poetry를 활용한 캐싱 여부 활성화
- name: Cache virtualenv
uses: actions/cache@v4
with:
path: ./.venv
key: poetry-venv-${{ hashFiles('poetry.lock') }}
YAML
복사
uv sync의 경우 pyproject.toml 파일만 있어도 가능하지만, 만약 캐싱을 옵션으로 선택하여 사용할 경우 uv.lock이 필요합니다. 성공한 케이스를 살펴보면 아래와 같은 옵션이 기본으로 포함돼 있음을 알 수 있습니다. ( 관련하여 공식 문서를 참고하셔도 좋습니다. )
만약 uv.lock이 없는 상태로 ci.yaml 이 실행되면 아래의 예시처럼 기본적으로 캐싱 옵션 활성화에 이슈가 발생함을 알 수 있습니다.
Run test
테스트 코드를 실행하는 건 poetry와 uv 크게 차이가 없습니다.
3. Conclusion
빌드 시간과 빌드된 이미지의 유의미한 변화를 확인하고, poetry로 잘 돌아가던 프로젝트를 uv로 변경해보는 작업은 그렇게 오래 걸리지 않은 작업이었습니다. 물론 잘 돌아가던 프로젝트를 다른 패키지 매니저로 변경하는 것이기 때문에 약간의 두려움은 있었습니다. 두려움을 해소해보고자 docker 빌드와 docker-compose 등 여러 방면으로 테스트 하면서 문제없이 돌아가는지, 테스트 코드는 문제 없는지, 빌드해서 구동해보았을 때 큰 차이는 없는지 등을 확인해보았고 문제 없다는 걸 알고 약간의 자신감이 들었습니다.
uv로 변경하는 작업을 마치고… 스스로 간단히 회고를 해보았습니다. 다시 생각해보면, 프로젝트 및 패키지 매니징을 하는 것이다보니, 라이브러리 버전만 잘 맞춰준다면 사실상 큰 차이는 없다는 생각이 들었습니다. 그렇게 두려울 필요는 없었던 작업이었다는 생각이 들었고 심심한 코웃음을 쳤더랬죠.