Запуск devpi (прокси-кэш PyPI) через Traefik с Docker Compose

devpi — свой индекс Python-пакетов: кэширует ответы upstream (например Aliyun) на диске стенда. При pip install miss уходит наружу один раз, дальше отдача локально. Это не полный клон PyPI, а прокси-кэш.

Нужны рабочий Traefik и сеть traefik_network. Порт 3141 наружу не открываем.

1. Каталог

mkdir -p devpi/data
cd devpi

Рядом положите Dockerfile и entrypoint.sh (образ с devpi-server + devpi-web + devpi-client) либо используйте готовый образ, если предпочитаете без сборки. Ниже — вариант со сборкой, как на типичном стенде.

2. .env

cat > .env <<'EOF'
DEVPI_PASSWORD=(youpassword)
DEVPI_CI_USER=ci
DEVPI_CI_PASSWORD=(youpassword)
DEVPI_MIRROR_URL=https://mirrors.aliyun.com/pypi/simple/
DEVPI_OUTSIDE_URL=https://pypi.(yourdomain)
DEVPI_THREADS=2
DEVPI_INDEXER_BACKEND=null
EOF

3. docker-compose.yml

services:
  devpi:
    build: .
    container_name: devpi
    restart: unless-stopped
    environment:
      DEVPI_PASSWORD: ${DEVPI_PASSWORD}
      DEVPI_CI_USER: ${DEVPI_CI_USER:-ci}
      DEVPI_CI_PASSWORD: ${DEVPI_CI_PASSWORD}
      DEVPI_MIRROR_URL: ${DEVPI_MIRROR_URL:-https://mirrors.aliyun.com/pypi/simple/}
      DEVPI_OUTSIDE_URL: ${DEVPI_OUTSIDE_URL:-https://pypi.(yourdomain)}
      DEVPI_THREADS: ${DEVPI_THREADS:-2}
      DEVPI_INDEXER_BACKEND: ${DEVPI_INDEXER_BACKEND:-null}
    volumes:
      - ./data:/data
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://127.0.0.1:3141/+api"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.devpi_https.redirectscheme.scheme=https
      - traefik.http.routers.devpi.entrypoints=web
      - traefik.http.routers.devpi.middlewares=devpi_https@docker
      - traefik.http.routers.devpi.rule=Host(`pypi.(yourdomain)`)
      - traefik.http.routers.devpi_https.entrypoints=websecure
      - traefik.http.routers.devpi_https.rule=Host(`pypi.(yourdomain)`)
      - traefik.http.routers.devpi_https.tls=true
      - traefik.http.routers.devpi_https.tls.certresolver=le
      - traefik.http.services.devpi.loadbalancer.server.port=3141
    networks:
      - traefik_network

networks:
  traefik_network:
    external: true

⚠️ Замените (yourdomain) и (youpassword). При желании добавьте middleware allowlist в Traefik, чтобы индекс был только из LAN/VPN.

Первый старт entrypoint инициализирует serverdir, поднимает devpi-server с --outside-url, настраивает mirror на DEVPI_MIRROR_URL и создаёт пользователей root / ci.

4. Запуск

docker compose up -d --build
curl -sf https://pypi.(yourdomain)/+api | head
curl -sfI https://pypi.(yourdomain)/root/pypi/+simple/pip/

Админ внутри контейнера:

docker exec -it devpi sh -c 'devpi use http://127.0.0.1:3141 && devpi login root --password="$DEVPI_PASSWORD"'

5. pip и CI

Чтение кэша без логина:

export PIP_INDEX_URL=https://pypi.(yourdomain)/root/pypi/+simple/
export PIP_TRUSTED_HOST=pypi.(yourdomain)
pip install -r requirements.txt

~/.config/pip/pip.conf:

[global]
index-url = https://pypi.(yourdomain)/root/pypi/+simple/
trusted-host = pypi.(yourdomain)

В GitLab CI:

variables:
  PIP_INDEX_URL: "https://pypi.(yourdomain)/root/pypi/+simple/"
  PIP_TRUSTED_HOST: "pypi.(yourdomain)"

Загрузка своих пакетов — под пользователем ci в индекс вроде ci/prod (пароль из .env).

6. Замечания

  • Кэш лежит в ./data — планируйте диск; du -sh data/ периодически полезен.
  • Смена паролей: обновите .env, затем devpi user -m … после логина root и docker compose up -d.
  • UI можно подправить своими CSS/шаблонами через bind-mount поверх devpi_web.

Итог: стабильный pip-index за HTTPS для стенда и CI без прямых походов на публичный PyPI при каждом билде.