评估本地PyPI服务

为什么需要本地PyPI服务

既然已经有了pypi.python.org这个官方源,为什么还需要一个本地源、私有源、企业源?

对于一个开发团队,如果有不止一个闭源的Python项目,并且它们之间还有相互依赖关系, 那么最好的做法,就是使用一个本地的PyPI服务,用来发布私有包。 一个Python开发团队是否有技术积累,往往取决于此。

对于一家公司,尤其是中国公司,如果深度依赖于Python的官方源, 那么也可以自建一个镜像或代理,避免一些偶然的网络可达性问题。 另外,提高访问速度、降低公网带宽压力,也是很重要的优势。

否则,Python项目只能基于代码托管平台进行发布,或者为了移植方便,只能做单文件项目。 这固然也是Python支持的方式,但毕竟太业余。 PyPI的核心优势,不仅仅在于文档与发布,还在于版本与依赖。 只要是专业的Python开发者,都明白PyPI这个平台的意义。

原理

所谓本地源、私有源、企业源,其实都是一个可以通过网络访问的目录。 其中,像pypi.python.org那样,在/simple/下按固定结构,放置了Python包。 并且,还提供了一些网络接口,供pip这样的包管理程序访问。

一些服务与对比

服务 版本 更新 镜像 代理 发布 UI Docker
pypi-legacy 2005-08-01 2005-08-01
warehouse master ~
bandersnatch 2.0.0 2017-04-05
pypiserver 1.2.0 2016-06-25
devpi-server 4.3.0 2017-04-23
Flask-Pypi-Proxy 0.5.1 2014-01-08
localshop 0.9.3 2015-05-15
pyshop 1.3.0 2017-06-29

上表中的概念解释:

官方服务

官方源的界面,可以访问:

孤没有对pypi-legacy进行深入研究。 它的原名似乎叫Cheese Shop,最新发布版为2005-08-01。 它应该是在2005年左右就已停止开发,虽然近期一直有提交,但应该是维护性质的。

warehouse的最新tag是v14.2.1,时间是2014年2月8日,但是至今一直有大量稳定提交。 之前预计warehouse将在2017年(今年)取代pypi-legacy, 不过至今(2017年8月)仍然只能在pypi.org访问,处于pre-production状态。

Warehouse is the next generation Python Package Repository, designed to replace the legacy code base that currently powers PyPI. Warehouse is due for public release in 2017.

现在看来,pypi-legacy的界面非常简陋,是一种本世纪初的设计风格。 而warehouse的界面就好多了,处处体现了近期的设计理念。 不过,除了界面,暂时并没看到实质性的改动。

作为私有源,warehouse可能太重了。 默认master分支上的Docker跑起来共9个容器,还是调试状态的。 需要等到它正式发布了,才能尝试使用。

bandersnatch

在上表中可以看出,bandersnatch是唯一一个提供全量同步的服务。 所以,现在国内知名的PyPI镜像,基本都是实用它,比如ustcaliyun等。

全量同步的优点突出,就是下载方便。 缺点也很明显,对服务器的硬盘、带宽的占用率非常高,有效利用率却非常低。 如果不是一家做大型Python项目的公司,比如豆瓣,这样做明显不划算。

此外,同步类方案,都有频率问题。 比如,aliyun的镜像每天一次,而ustc的每4小时一次。 在同步前,新发布的包是无法访问的。

pypiserver

pypiserver是最简单的代理、发布服务。

它支持发布私有包,并且有一个非常简陋的网页,可以看到已上传的私有包。 下载时,先查找、下载私有包; 如果没发现,则查找、下载指定源中的包。

pypiserver会存储私有包,但不会缓存其它源上的包。

devpi

devpi是以devpi-server为核心的一组Python包。

devpi-server可以创建多个用户、仓库,并且通过一个URL对外提供。 比如,基于一个PyPI的代理(type=mirror),建立一个私有库(type=stage),只需要对外提供私有库即可。

它最大的优势是,在代理、缓存方面做得非常出色。

localshop

这是一个基于Django的Web应用。 利用XMLRPC接口,localshoppypi.python.org或其它镜像进行查找、下载。 同时,也提供一个私有发布库,并且有良好的界面展示,包括README.RST文件的预览。

同类项目有pyshop,这是一个基于Pyramid的Web应用。 相比localshop,它不提供代理服务。

相关的项目还有djangopypi,这是一个Django插件。 它是从chishop项目fork而来,可以在一个Django项目中,添加一个URL,在/pypi/下提供PyPI的私有源服务。

这类项目中,目前还是localshop做得比较完善,开箱即用。 缺点是,有代理没有缓存。 (文档中说是有缓存功能的,但是孤在测试使用中没有配置出来。)

总结

对于资源丰富,想要全量同步、建立镜像,为公司内部、甚至外部提供高速访问服务的人来说,bandersnatch是唯一的选择。 否则,还是得从其它服务上考虑。

代理这种方式,其实还有很多小细节。 比如,是真的代理,还是假的,仅仅只是URL重定向。 这两种方式的区别是,当客户端所在的机器无法访问外部源时(比如隔离外网的工作站),前者可以正常工作,而后者不行。 当然,严格地说,重定向并不能称为代理,只是对服务端来说形式类似,前面是为了制表方便才未区分。

在真的代理中,还有是否缓存的区别。 如果没有缓存,则每次都是从被代理的源直接下载,这需要服务端对该源的访问速度够快。 如果有缓存,那么只有首次下载某个包时,才会直接下载。

服务 重定向 代理 缓存
pypiserver
devpi-server
Flask-Pypi-Proxy
localshop
pyshop

(注:Flask-Pypi-Proxy在孤试用过程中,没有调通,所以不推荐。)

如果要有良好的前端界面,并且支持代理或重定向,那么localshop是较好的选择。 但是,没有缓存是较大的问题。 而且,即使有缓存,首次下载速度慢,也是个硬伤。 所以,多个服务进行组合,可以得到一个各方面都还行的方案。

如果只部署一个服务,那么devpi-server是首选。 如果可以再部署一个,那么可以配上localshop。 前者专门负责代理其它源,后者负责管理私有包。 假如对首次访问速度有很高要求,那么可以换成bandersnatch来与localshop搭配。

如果有闲置的开发资源,可以基于localshop,结合devpi-server的优点,开发一款新的Web App。 对大型Python开发团队来说,这应该是最好的选择。

One more thing

前面说了那么多,都是Python系的。 本来,Python生态的核心部分,由Python自己来支持,思路是没错。 但评估下来发现,除了warehouse,其它的都是各有特点的小项目,而warehouse又处于开发阶段。

有没有开源的、大团队开发维护的、满足复杂需求的PyPI服务?

有,Nexus Repository Manager的OSS版本。

The world’s first and only universal repository solution that’s FREE to use.

它可以管理以下形式的仓库:

其中,PyPI类的服务,支持三种:

可以说,这一个服务,就相当于前面总结的组合服务,所有特性基本上都能打钩。 此外,OSS版内还有很多额外功能,和Pro版、以及Sonatype其它服务有很多联系。 什么安全、缺陷、开源协议等等杂七杂八的东西,都可以看到接口。

当然,它并不是没有缺点。 功能繁多,导致UI复杂而丑陋。 还有一些细节也没做好,比如代理下载时,要等它先下载完才开始提供客户端的下载。 这一点,devpi-server就做得很好,它是一边缓存、一边提供下载。

对于真正有大量多种需求的公司,它可能是目前唯一免费的选择。 企业级的应用,目前果然还是Java系的比较靠谱。


相关笔记