Poetry와 CI/CD, Docker 통합

GitHub Actions, GitLab CI, Docker에서 Poetry 활용하기

Poetry를 CI/CD 파이프라인과 Docker 환경에서 효과적으로 사용하는 방법을 다룬다. GitHub Actions, GitLab CI 설정, Docker 멀티스테이지 빌드, 캐싱 전략, 자동 배포 파이프라인 구성까지 실무 패턴을 정리한다.

Engineering
Python
DevOps
저자

Kwangmin Kim

공개

2025년 10월 09일

1 CI/CD에서 Poetry 사용 원칙

CI/CD 환경에서 Poetry를 사용할 때 핵심 원칙:

  1. poetry.lock을 기준으로 설치 — 재현 가능한 빌드 보장
  2. 캐시 활용 — 빌드 시간 단축
  3. 가상환경 생성 여부 제어 — Docker에서는 불필요할 수 있음
  4. 환경변수로 설정 — 설정 파일 대신 환경변수 사용

2 GitHub Actions

2.1 기본 설정

# .github/workflows/ci.yml
name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install Poetry
        uses: snok/install-poetry@v1
        with:
          version: latest
          virtualenvs-create: true
          virtualenvs-in-project: true

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }}
          restore-keys: |
            venv-${{ runner.os }}-${{ matrix.python-version }}-

      - name: Install dependencies
        run: poetry install --no-interaction

      - name: Run tests
        run: poetry run pytest --cov

      - name: Run linter
        run: poetry run ruff check .

2.2 캐싱 전략

Poetry 캐시의 핵심은 poetry.lock의 해시를 캐시 키로 사용하는 것이다.

# 방법 1: .venv 디렉토리 캐시 (virtualenvs.in-project=true 필요)
- uses: actions/cache@v4
  with:
    path: .venv
    key: venv-${{ runner.os }}-${{ hashFiles('poetry.lock') }}

# 방법 2: Poetry 캐시 디렉토리 캐시
- uses: actions/cache@v4
  with:
    path: ~/.cache/pypoetry
    key: poetry-${{ runner.os }}-${{ hashFiles('poetry.lock') }}

2.3 자동 PyPI 배포

# .github/workflows/publish.yml
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install Poetry
        uses: snok/install-poetry@v1

      - name: Build and publish
        env:
          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
        run: |
          poetry build
          poetry publish

3 GitLab CI

3.1 기본 설정

# .gitlab-ci.yml
image: python:3.11-slim

variables:
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  POETRY_VIRTUALENVS_IN_PROJECT: "true"

cache:
  key:
    files:
      - poetry.lock
  paths:
    - .venv/
    - .cache/pip/

stages:
  - test
  - build
  - deploy

before_script:
  - pip install poetry
  - poetry install --no-interaction

test:
  stage: test
  script:
    - poetry run pytest --cov
    - poetry run ruff check .

build:
  stage: build
  script:
    - poetry build
  artifacts:
    paths:
      - dist/

deploy:
  stage: deploy
  only:
    - tags
  script:
    - poetry publish --username __token__ --password $PYPI_TOKEN

4 Docker 통합

4.1 기본 Dockerfile

FROM python:3.11-slim

# Poetry 설치
ENV POETRY_VERSION=1.8.0
RUN pip install "poetry==$POETRY_VERSION"

# 가상환경 생성 비활성화 (Docker 자체가 격리 환경)
ENV POETRY_VIRTUALENVS_CREATE=false

WORKDIR /app

# 의존성 파일만 먼저 복사 (Docker 레이어 캐시 활용)
COPY pyproject.toml poetry.lock ./

# 의존성 설치 (개발 의존성 제외)
RUN poetry install --only main --no-interaction --no-ansi

# 소스코드 복사
COPY src/ src/

CMD ["python", "-m", "my_project"]

4.2 멀티스테이지 빌드 (권장)

빌드 도구를 최종 이미지에서 제거하여 이미지 크기를 줄인다.

# === Build stage ===
FROM python:3.11-slim AS builder

ENV POETRY_VERSION=1.8.0
RUN pip install "poetry==$POETRY_VERSION"
ENV POETRY_VIRTUALENVS_IN_PROJECT=true

WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN poetry install --only main --no-interaction --no-ansi

# === Runtime stage ===
FROM python:3.11-slim AS runtime

WORKDIR /app

# 빌드 스테이지에서 가상환경만 복사
COPY --from=builder /app/.venv /app/.venv

# PATH에 가상환경 추가
ENV PATH="/app/.venv/bin:$PATH"

COPY src/ src/

# Poetry 없이 직접 실행
CMD ["python", "-m", "my_project"]

4.3 poetry export 방식 (Poetry 없이 배포)

Docker 이미지에 Poetry를 설치하지 않고, requirements.txt로 변환하여 pip으로 설치한다.

# 로컬에서 미리 실행:
# poetry export -f requirements.txt --without-hashes -o requirements.txt

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY src/ src/

CMD ["python", "-m", "my_project"]
힌트

poetry export 방식은 이미지가 가장 가볍고 빌드가 빠르다. 단, requirements.txt를 최신 상태로 유지해야 하는 부담이 있다. CI에서 자동 생성하면 이 문제를 해결할 수 있다.

4.4 Docker 레이어 캐시 최적화

핵심은 의존성 파일을 소스코드보다 먼저 복사하는 것이다.

# 좋은 예: 의존성 변경 없으면 캐시 활용
COPY pyproject.toml poetry.lock ./    # ← 변경 적음
RUN poetry install                     # ← 캐시됨
COPY src/ src/                         # ← 변경 잦음

# 나쁜 예: 소스 변경마다 의존성 재설치
COPY . .                               # ← 전체 복사
RUN poetry install                     # ← 매번 재실행

5 Docker Compose 예시

# docker-compose.yml
version: "3.8"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    volumes:
      - ./src:/app/src  # 개발 시 코드 변경 즉시 반영
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb

6 개발용 vs 프로덕션용 Docker

# === 공통 베이스 ===
FROM python:3.11-slim AS base
ENV POETRY_VERSION=1.8.0
RUN pip install "poetry==$POETRY_VERSION"
ENV POETRY_VIRTUALENVS_CREATE=false
WORKDIR /app
COPY pyproject.toml poetry.lock ./

# === 프로덕션 ===
FROM base AS production
RUN poetry install --only main --no-interaction
COPY src/ src/
CMD ["python", "-m", "my_project"]

# === 개발 ===
FROM base AS development
RUN poetry install --no-interaction  # dev 의존성 포함
COPY . .
CMD ["poetry", "run", "pytest", "--watch"]
# docker-compose.yml
services:
  app:
    build:
      target: development  # 또는 production

7 실무 체크리스트

CI/CD 설정 시:
[ ] poetry.lock을 Git에 커밋했는가?
[ ] 캐시 키에 poetry.lock 해시를 사용하는가?
[ ] --no-interaction 플래그를 사용하는가?
[ ] 비밀값(토큰 등)은 환경변수/시크릿으로 관리하는가?

Docker 설정 시:
[ ] 의존성 파일을 소스보다 먼저 COPY하는가? (레이어 캐시)
[ ] 프로덕션에서 dev 의존성을 제외하는가? (--only main)
[ ] Poetry 버전을 고정하는가? (ENV POETRY_VERSION=...)
[ ] 멀티스테이지 빌드로 이미지 크기를 줄이는가?

8 요약

환경 핵심 설정
GitHub Actions snok/install-poetry + actions/cache + poetry.lock 해시 키
GitLab CI POETRY_VIRTUALENVS_IN_PROJECT=true + cache.key.files
Docker (기본) POETRY_VIRTUALENVS_CREATE=false + 레이어 캐시
Docker (멀티스테이지) builder에서 .venv 생성 → runtime에 복사
Docker (export) poetry exportpip install -r requirements.txt

Subscribe

Enjoy this blog? Get notified of new posts by email: