PyPI很慢。如何搭建自己的服务器?

44 投票
5 回答
22905 浏览
提问于 2025-04-17 19:55

当一个新开发者加入团队,或者Jenkins进行完整构建时,我需要创建一个新的虚拟环境(virtualenv)。我发现,用Pip设置一个虚拟环境并安装很多(超过10个)依赖包时,从PyPI下载所有内容的速度非常慢。很多时候,安装过程会完全失败,出现如下错误:

Downloading/unpacking Django==1.4.5 (from -r requirements.pip (line 1))
Exception:
Traceback (most recent call last):
  File "/var/lib/jenkins/jobs/hermes-web/workspace/web/.venv/lib/python2.6/site-packages/pip-1.2.1-py2.6.egg/pip/basecommand.py", line 107, in main
    status = self.run(options, args)
  File "/var/lib/jenkins/jobs/hermes-web/workspace/web/.venv/lib/python2.6/site-packages/pip-1.2.1-py2.6.egg/pip/commands/install.py", line 256, in run
    requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
  File "/var/lib/jenkins/jobs/hermes-web/workspace/web/.venv/lib/python2.6/site-packages/pip-1.2.1-py2.6.egg/pip/req.py", line 1018, in prepare_files
    self.unpack_url(url, location, self.is_download)
  File "/var/lib/jenkins/jobs/hermes-web/workspace/web/.venv/lib/python2.6/site-packages/pip-1.2.1-py2.6.egg/pip/req.py", line 1142, in unpack_url
    retval = unpack_http_url(link, location, self.download_cache, self.download_dir)
  File "/var/lib/jenkins/jobs/hermes-web/workspace/web/.venv/lib/python2.6/site-packages/pip-1.2.1-py2.6.egg/pip/download.py", line 463, in unpack_http_url
    download_hash = _download_url(resp, link, temp_location)
  File "/var/lib/jenkins/jobs/hermes-web/workspace/web/.venv/lib/python2.6/site-packages/pip-1.2.1-py2.6.egg/pip/download.py", line 380, in _download_url
    chunk = resp.read(4096)
  File "/usr/lib64/python2.6/socket.py", line 353, in read
    data = self._sock.recv(left)
  File "/usr/lib64/python2.6/httplib.py", line 538, in read
    s = self.fp.read(amt)
  File "/usr/lib64/python2.6/socket.py", line 353, in read
    data = self._sock.recv(left)
timeout: timed out

我知道Pip有一个--use-mirrors的选项,有时我的团队成员会通过使用--index-url http://f.pypi.python.org/simple(或者其他镜像)来解决这个问题,直到他们找到一个响应速度快的镜像。我们在英国,但德国有一个PyPI镜像,我们从其他网站下载数据没有问题。

所以,我在考虑为我们的团队在内部搭建一个PyPI镜像。

我考虑过的选项有:

  1. 运行我自己的PyPI实例。官方的PyPI实现是:CheeseShop,还有一些第三方实现,比如:djangopypipypiserver(见脚注)

    这个方法的问题是,我并不需要完整的PyPI功能,比如文件上传,我只想镜像它提供的内容。

  2. 使用pep381clientpypi-mirror来运行一个PyPI镜像。

    这个方法看起来可行,但需要我的镜像先从PyPI下载所有内容。我已经设置了一个pep381client的测试实例,但我的下载速度在5 Kb/s到200 Kb/s之间波动(是比特,不是字节)。除非有完整的PyPI存档的副本,否则我需要几周才能有一个有用的镜像。

  3. 使用像yopypi这样的PyPI轮询代理。

    这个方法现在不相关,因为http://pypi.python.org本身就由几个地理位置不同的服务器组成。

  4. 在开发者之间复制虚拟环境,或者托管一个当前项目依赖的文件夹

    这个方法不太可行:我们有几个不同的Python项目,它们的依赖会随着时间慢慢变化。一旦任何项目的依赖发生变化,这个中央文件夹就必须更新以添加新的依赖。不过,复制虚拟环境比复制包更麻烦,因为任何带有C模块的Python包都需要为目标系统编译。我们的团队有Linux和OS X的用户。

    (不过,这看起来还是一堆糟糕选项中最好的。)

  5. 使用智能的PyPI缓存代理:collective.eggproxy

    这似乎是一个很好的解决方案,但PyPI上的最后一个版本是2009年的,而且讨论的是mod_python。

其他大型Python团队是怎么做的?有什么好的解决方案可以快速安装相同的Python包?

脚注:

5 个回答

8

我最近在我们开发团队的Vagrant配置中安装了devpi,让它的包缓存存放在主机的文件系统上。这样,每个虚拟机(VM)都可以有自己的devpi-server守护进程,作为virtualenv/pip的索引网址。当这些虚拟机被销毁并重新配置时,之前下载的包就不需要一次又一次地下载了。每个开发者只需下载一次,建立自己的本地缓存,只要这些缓存还在主机的文件系统上,就可以一直使用。

我们还有一个内部的PyPi索引,用于存放我们的私有包,目前只是一个由Apache提供服务的目录。最终,我打算把它转换成一个devpi代理服务器,这样我们的构建服务器不仅可以维护Python依赖的包缓存,还能托管我们的私有库。这将为我们的开发环境、生产部署和公共PyPi之间增加一个缓冲层。

到目前为止,这似乎是我找到的最稳健的解决方案。

9

虽然这并不能解决你在PyPI上的问题,但可以通过Terrarium将构建好的虚拟环境交给开发者或用于部署。

使用terrarium可以打包、压缩并保存虚拟环境。你可以把它们保存在本地,甚至可以存储到S3上。根据GitHub上的文档:

$ pip install terrarium
$ terrarium --target testenv --storage-dir /mnt/storage install requirements.txt

在创建一个新的环境后,terrarium会将这个环境打包并压缩,然后把它复制到你指定的存储位置。

在后续安装相同需求集时,如果指定了相同的存储位置,terrarium会从/mnt/storage复制并解压缩之前的压缩包。

如果你想查看terrarium将如何命名这个压缩包,可以运行以下命令:

$ terrarium key requirements.txt more_requirements.txt
x86_64-2.6-c33a239222ddb1f47fcff08f3ea1b5e1
35

你们有共享的文件系统吗?

如果有的话,我建议使用pip的缓存设置。这非常简单。比如在/mnt下创建一个叫做pip-cache的文件夹。

mkdir /mnt/pip-cache

然后每个开发者都需要在他们的pip配置文件里加上一行代码(在Unix系统中是$HOME/.pip/pip.conf,在Windows系统中是%HOME%\pip\pip.ini)。

[global]
download-cache = /mnt/pip-cache

这样做之后,pip还是会去检查PyPi,看看有没有最新版本。然后它会查看这个版本是否已经在缓存里。如果在缓存里,就直接从那里安装;如果不在,就会下载这个版本。下载后会把它存到缓存里,然后再安装。所以每个包在新版本发布时只会下载一次。

撰写回答