跳转至

PyPI Server 服务安装部署文档

本文档可直接用于 AI 部署,包含完整的服务安装步骤和配置

环境信息

项目 版本/说明
操作系统 CentOS 7
Python管理 Anaconda3
Python版本 3.11.5
反向代理 Nginx

部署架构

                    ┌─────────────┐
                    │    Nginx    │
                    │   :8080     │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │ pypiserver  │
                    │   :28081    │
                    │ (认证访问)   │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │  /app/pypi  │
                    │  /packages  │
                    └─────────────┘

    上传方式: pip download → Xftp → /app/pypi-server/packages/

一、基础环境准备

1.1 系统依赖安装

# 安装系统基础工具
yum install -y wget curl vim

# 安装 nginx
yum install -y epel-release
yum install -y nginx

# 安装 htpasswd 工具(用于生成密码文件)
yum install -y httpd-tools

1.2 Anaconda3 环境准备

# 假设 Anaconda3 已安装,创建独立的 Python 3.11.5 环境
conda create -n pypi-server python=3.11.5 -y

# 激活环境
conda activate pypi-server

二、PyPI Server 部署

2.1 创建目录结构

# 创建 pypiserver 工作目录
mkdir -p /app/pypi-server/packages
mkdir -p /app/pypi-server/logs

# 设置目录权限
chmod 755 /app/pypi-server
chmod 755 /app/pypi-server/packages
chmod 755 /app/pypi-server/logs

2.2 安装 pypiserver

# 激活 conda 环境
conda activate pypi-server

# 安装 pypiserver
pip install pypiserver[passlib]

2.3 配置认证账号

# 创建密码文件(添加第一个用户)
# 替换 YOUR_USERNAME 和 YOUR_PASSWORD 为实际值
htpasswd -bc /app/pypi-server/.htpasswd YOUR_USERNAME YOUR_PASSWORD

# 示例:创建 admin 用户
htpasswd -bc /app/pypi-server/.htpasswd admin Admin@2025

# 添加更多用户(不要带 -c 参数,会覆盖文件)
# htpasswd -b /app/pypi-server/.htpasswd ANOTHER_USER ANOTHER_PASSWORD

# 设置密码文件权限(只读)
chmod 644 /app/pypi-server/.htpasswd

2.4 创建 systemd 服务

cat > /etc/systemd/system/pypi-server.service << 'EOF'
[Unit]
Description=PyPI Server
After=network.target

[Service]
Type=simple
User=root
Environment="PATH=/root/anaconda3/envs/pypi-server/bin:/usr/local/bin:/usr/bin:/bin"
WorkingDirectory=/app/pypi-server
ExecStart=/root/anaconda3/envs/pypi-server/bin/pypi-server run \
    -p 28081 \
    -i 0.0.0.0 \
    -a download,list \
    -P /app/pypi-server/.htpasswd \
    --disable-fallback \
    --overwrite \
    /app/pypi-server/packages

Restart=always
RestartSec=10
StandardOutput=append:/app/pypi-server/logs/pypi-server.log
StandardError=append:/app/pypi-server/logs/pypi-server-error.log

[Install]
WantedBy=multi-user.target
EOF

# 重载 systemd
systemctl daemon-reload

# 启动服务
systemctl start pypi-server

# 设置开机自启
systemctl enable pypi-server

# 检查服务状态
systemctl status pypi-server

2.5 参数说明

参数 说明
run pypiserver 2.x 必须使用的子命令
-p 28081 监听端口 28081
-i 0.0.0.0 监听所有网络接口
-a download,list 需要认证的操作(download=下载, list=列表)
-P 指定 htpasswd 密码文件路径
--disable-fallback 禁止从 PyPI 官方代理下载
--overwrite 允许覆盖同名包

三、Nginx 反向代理配置

3.1 创建 nginx 配置

cat > /etc/nginx/conf.d/pypi-server.conf << 'EOF'
server {
    listen 8080;
    server_name _;

    # 日志配置
    access_log /var/log/nginx/pypi-access.log;
    error_log /var/log/nginx/pypi-error.log;

    # 禁止 POST/PUT/DELETE 请求(只允许下载,禁止上传)
    if ($request_method !~ ^(GET|HEAD)$) {
        return 403;
    }

    # 禁止访问首页(web 界面)
    location = / {
        return 403;
    }

    # 允许包列表和下载接口 /simple/
    location /simple/ {
        proxy_pass http://127.0.0.1:28081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header  WWW-Authenticate;
        proxy_pass_header  Authorization;
        proxy_connect_timeout 300;
        proxy_send_timeout 300;
        proxy_read_timeout 300;
    }

    # 允许包文件下载 /packages/
    location /packages/ {
        proxy_pass http://127.0.0.1:28081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header  WWW-Authenticate;
        proxy_pass_header  Authorization;
    }

    # 其他所有路径禁止访问
    location / {
        return 403;
    }
}
EOF

3.2 启动 nginx

# 测试配置
nginx -t

# 启动 nginx
systemctl start nginx

# 设置开机自启
systemctl enable nginx

# 检查状态
systemctl status nginx

3.3 安全配置说明

pypiserver 路径说明

/simple/          # 包列表接口(pip 访问获取包信息)
/simple/{pkg}/    # 单个包的信息页面
/packages/        # 包文件实际存储路径(pip 下载 .whl 文件)

安全限制表

限制项 配置方式 目的
禁止 Web 首页 location = / { return 403; } 防止访问 web 界面
允许包列表 代理 /simple/ pip 获取包列表
允许包下载 代理 /packages/ pip 下载 .whl 文件
禁止上传 if ($request_method !~ ^(GET|HEAD)$) 禁止 POST/PUT/DELETE
禁止其他路径 location / { return 403; } 阻止未授权访问

允许的访问

✅ GET  http://SERVER:8080/simple/              # 包列表(需认证)
✅ GET  http://SERVER:8080/simple/{pkg}/        # 包信息页(需认证)
✅ GET  http://SERVER:8080/packages/*/*.whl     # 包文件下载(需认证)
✅ HEAD http://SERVER:8080/simple/*             # HEAD 请求

禁止的访问

❌ GET  http://SERVER:8080/                     # 首页(403 Forbidden)
❌ POST/PUT/DELETE 任何路径                     # 上传操作(403 Forbidden)
❌ GET  http://SERVER:8080/favicon.ico          # 静态资源(403 Forbidden)
❌ 其他未定义路径                               # (403 Forbidden)

四、防火墙配置

# 开放 nginx 端口
firewall-cmd --permanent --add-port=8080/tcp

# 重载防火墙
firewall-cmd --reload

五、包上传方式

5.1 在有网络的机器上下载包

# 下载单个包(包含依赖)
pip download -d ./packages package-name

# 下载多个包
pip download -d ./packages package1 package2 package3

# 根据 requirements.txt 下载
pip download -d ./packages -r requirements.txt

# 指定 Python 版本
pip download -d ./packages --python-version 3.11 package-name

# 指定平台
pip download -d ./packages --only-binary=:all: package-name

5.2 上传包到服务器

使用 Xftp 或其他 SFTP 工具上传:

本地目录: ./packages/*.whl
上传目标: /app/pypi-server/packages/

5.3 验证包是否生效

# 查看已上传的包
ls -lh /app/pypi-server/packages/

# 测试访问
curl -u admin:Admin@2025 http://SERVER_IP:8080/simple/

六、验证部署

# 1. 检查 pypiserver 服务(本地端口 28081)
curl -I http://127.0.0.1:28081/

# 2. 检查 nginx 代理(外部端口 8080)
curl -I http://127.0.0.1:8080/

# 3. 测试带认证访问
curl -u admin:Admin@2025 http://127.0.0.1:8080/simple/

# 4. 测试无认证访问(应该返回 401)
curl -I http://127.0.0.1:8080/simple/

七、服务管理命令

# pypiserver 管理
systemctl start pypi-server      # 启动
systemctl stop pypi-server       # 停止
systemctl restart pypi-server    # 重启
systemctl status pypi-server     # 状态
journalctl -u pypi-server -f     # 查看日志

# nginx 管理
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl status nginx

八、故障排查

8.1 服务无法启动

# 查看详细日志
journalctl -u pypi-server -n 50
tail -f /app/pypi-server/logs/pypi-server-error.log

8.2 认证失败

# 检查密码文件
cat /app/pypi-server/.htpasswd

# 重新生成密码
htpasswd -bc /app/pypi-server/.htpasswd username newpassword

8.3 下载 401 错误

  • 检查 -a download,list 参数是否正确配置
  • 确认 nginx 正确传递了 Authorization 头
  • 验证密码文件权限正确(644)

九、目录结构

/app/pypi-server/
├── packages/           # Python 包存储目录
├── logs/               # 日志目录
│   ├── pypi-server.log
│   └── pypi-server-error.log
└── .htpasswd           # 认证密码文件

/etc/nginx/conf.d/
└── pypi-server.conf    # Nginx 配置

/etc/systemd/system/
└── pypi-server.service # systemd 服务文件