Cane's Blog

Cane

【Python】搭建私有pypi仓库

90
2022-02-25

一、介绍

利用 pypi-server 搭建私有的 pypi 仓库

二、搭建

1. 拉取镜像

docker pull pypiserver/pypiserver

2. 创建一个用来保存私有库包文件的文件夹

mkdir /root/pypi && mkdir /root/pypi/packages

3. 创建不带密码和用户验证的 pypi 私有仓库

docker run --name 'pypi' --restart always -v /root/pypi/packages:/data/packages -p 8080:8080 -d pypiserver/pypiserver -P . -a . packages

4. 创建带密码和用户验证的 pypi 私有仓库

  1. 创建 .htpasswd 文件,格式为“用户名:密码”

    Window:通过文本文档创建,另存为 ".htpasswd"(带双引号)
    Linux:用 htpasswd -sc .htpasswd <username> 来创建

  2. 创建容器

    docker run --name 'pypi' --restart always -v /root/pypi/packages:/data/packages -v /root/pypi/.htpasswd:/data/.htpasswd -p 8080:8080 -d pypiserver/pypiserver -P .htpasswd packages
  3. 设置权限

    docker exec -it pypi bash
    
    cd /data
    
    chmod 777 -R packages

三、上传

1. 编辑 .pypirc 文件

Linux 位于:~/.pypirc

Windows 位于:%USERNAME%/.pypirc

[distutils]
index-servers =
    pypi
    pypitest

[pypi]  # 官方PYPI源信息
repository: https://pypi.python.org/pypi
username: 
password: 

[pypitest]  # 自己搭建的PYPI源信息
repository: http://127.0.0.1:8080/
username: 
password:

2. 上传

twine upload -r test-pypi dist/* (如果有账号密码,按提示输入即可)

twine upload --repository-url http://127.0.01:8080 dist/* (不设置.pypirc的情况下)

四、拉取

pip install [package-name] -i http://127.0.0.1:8080 --trusted-host 127.0.0.1

也可以通过设置配置文件省去每次输入源地址的麻烦

Linux: ~/.pip/pip.conf

Windows: %APPDATA%/pip/pip.ini

[global]
timeout = 10
index-url=http://service.xxx.com:8081/simple
extra-index-url=https://pypi.douban.com/simple
[install]
trusted-host =
    service.xxx.com

五、补充

1. docker-compose 结合 nginx 配置

pypi.yaml

version: "3.5"
services:
  pypi:
    image: pypiserver/pypiserver
    container_name: pypi
    restart: always
    networks:
      - nginx
    volumes:
      - /home/pypi/packages:/data/packages
      - /home/pypi/.htpasswd:/data/.htpasswd
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8081:8080"
    command: -P /data/.htpasswd /data/packages --fallback-url https://pypi.tuna.tsinghua.edu.cn/simple
networks:
  nginx:
    external: true

nginx.conf

client_max_body_size 4096m;
client_body_buffer_size 30M;

server {
    listen 80;
    server_name pypi.xxxx.com;
    location / {
        proxy_pass http://pypi:8080;  # 因为 pypi 加入了 nginx 的网络,这里要写容器内的端口8080(本机访问通过 localhost:8081)
    }
}

.pypirc

[distutils]
index-servers =
    pypi-private

[pypi-private]
repository: http://pypi.xxxx.com
username:test
password:test

2. pip 的拉包逻辑

index-urlextra-index-url 查找,拉取「版本号最高」的包(并不具有真正意义上的优先级,从源码的角度来讲可能是首先检查第一个,但是不应该依赖这个来区分优先度,具体的介绍可以看「这篇文章

3. pypi-server 的推包逻辑(以 demo 为例)

  1. 推送本地仓库(pypi-server)中的 demo 包

  2. 若本地仓库当中没有找到 demo 包,则去 pypi.org 中查找

    基于这个流程,会导致假如我在 pip.ini 中同时设置了多个源地址(公有源地址和私有源地址),我在私有源上传了一个公有源同名的包,例:`request-0.01.whl`, 我使用 pip install request 的时候会导致我拉不到私有源中的 requests 包(版本号低于公有源中的 requests包版本号)

    注:只要在 pip.ini 设置了一个公有源地址

    pip install requests -i http://localhost:8080/simple --trusted-host localhost

    pip install requests --extra-index-url http://localhost:8080/simple --trusted-host localhost

    pip install -r requirements.txt -i http://localhost:8080/simple --trusted-host localhost

    上述三种方式都是拉取不到 requests-0.01.whl 包的

4. 解决私有包和公有包冲突:

  1. 私有库中的包名 以 $ORG_NAME 开头

  2. 检查 pypi.org 的中的包名,选择上面不存在的包名作为包名

  3. pip.ini 不设置任何公有源地址,公有源的包放在 requirements.txt,私有源的包放在 private-requirements.txt,拉取的时候使用 pip3 install -i https://pypi.douban.com/simple -r requirements.txtpip3 install -r private-requirements.txt --trusted-host localhost -i http://localhost:8080/simple

    关于这方面的讨论:「How are people avoiding name conflicts?

    Dockerfile 配置

    FROM python:3
    ADD . /code
    WORKDIR /code
    RUN pip3 install -r requirements.txt -i https://pypi.douban.com/simple
    RUN pip3 install -r private-requirements.txt --trusted-host localhost -i http://localhost:8080/simple
    RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
    CMD python -u main.py

  1. pip.ini 只设置私有源地址,在搭建 pypi 的时候,设置 --fallback-url [url] url 填写公有源地址(可以填写国内源来加速)所有包都通过私有源拉取,私有源不存在的包它会自动去公有源追溯。

    其他关于 pypi-server 配置选项

    --disable-fallback 禁止在找不到包的时候跳转到 pypi 官网链接

    --fallback-url [url] 指明找不到包需要跳转的链接