devpi — свой индекс Python-пакетов: кэширует ответы upstream (например Aliyun) на диске стенда. При pip install miss уходит наружу один раз, дальше отдача локально. Это не полный клон PyPI, а прокси-кэш.
Нужны рабочий Traefik и сеть traefik_network. Порт 3141 наружу не открываем.
mkdir -p devpi/data
cd devpi
Рядом положите Dockerfile и entrypoint.sh (образ с devpi-server + devpi-web + devpi-client) либо используйте готовый образ, если предпочитаете без сборки. Ниже — вариант со сборкой, как на типичном стенде.
.envcat > .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
docker-compose.ymlservices:
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.
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"'
Чтение кэша без логина:
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).
./data — планируйте диск; du -sh data/ периодически полезен..env, затем devpi user -m … после логина root и docker compose up -d.devpi_web.Итог: стабильный pip-index за HTTPS для стенда и CI без прямых походов на публичный PyPI при каждом билде.