6
社区成员
发帖
与我相关
我的任务
分享在某些情况下,您希望通过 pip 灵活地安装 python 包,但又不想将其公开。本文将重点介绍使用devpi提供一个自托管的 pip 兼容的 python 包服务器。Ubuntu 将用于操作系统,因为它是相当常见的 Linux 发行版,并且可以在 Windows Linux 子系统上轻松使用。
在开始之前,出于安全目的,我们要确保 devpi 将作为实际用户而不是 root 进程运行:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ sudo useradd -m -s /bin/bash devpi
</code></span></span>
现在我将切换到该用户
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ sudo su - devpi
</code></span></span>
接下来是创建一个虚拟环境,以便 devpi 及其包不会扰乱全局命名空间(您可能需要这样做才能apt-get install python3-virtualenv工作):
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ virtualenv --python=python3.8 venv
created virtual environment CPython3.8.10.final.0-64 in 123ms
creator CPython3Posix(dest=/home/devpi/venv, clear=False, global=False)
seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, pkg_resources=latest, via=copy, app_data_dir=/home/devpi/.local/share/virtualenv/seed-app-data/v1.0.1.debian.1)
activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
$ src venv/bin/activate
</code></span></span>
最后安装devpi服务器:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ pip install devpi-server devpi-web
</code></span></span>
虽然devpi-web不是硬性要求,但它确实可以更好地可视化 python 包并允许pip search查询工作。现在我们将安装验证并初始化服务器:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code># devpi-server --version
6.9.0
# devpi-init
INFO NOCTX Loading node info from /root/.devpi/server/.nodeinfo
INFO NOCTX generated uuid: b7b5b93272124964b10bdb37cce942ad
INFO NOCTX wrote nodeinfo to: /root/.devpi/server/.nodeinfo
INFO NOCTX DB: Creating schema
INFO [Wtx-1] setting password for user 'root'
INFO [Wtx-1] created user 'root'
INFO [Wtx-1] created root user
INFO [Wtx-1] created root/pypi index
INFO [Wtx-1] fswriter0: committed at 0
</code></span></span>
这是为 devpi 服务器工作设置基本后端。它还为以后的管理任务创建 root 用户。接下来我们需要创建提供多个选项来运行 devpi 服务器的文件:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code># devpi-gen-config
It is highly recommended to use a configuration file for devpi-server, see --configfile option.
wrote gen-config/crontab
wrote gen-config/net.devpi.plist
wrote gen-config/launchd-macos.txt
wrote gen-config/nginx-devpi.conf
wrote gen-config/nginx-devpi-caching.conf
wrote gen-config/supervisor-devpi.conf
wrote gen-config/supervisord.conf
wrote gen-config/devpi.service
wrote gen-config/windows-service.txt
</code></span></span>
如果默认的 3141 不适合您,也可以在执行期间提供端口:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code># devpi-gen-config --port 4040
</code></span></span>
如您所见,有多种运行 devpi 服务器的方法,包括 cron、launchd(OSX 服务)、nginx、Windows 服务和supervisord。它还有一个systemd服务文件,我们可以使用它来轻松管理服务,因为 Ubuntu 使用它来进行主要服务管理。首先,我们需要一个代理脚本来确保 devpi 在虚拟环境中运行:
/home/devpi/start-devpi.sh
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">#!/bin/bash</span>
<span style="color:var(--syntax-text-color)">cd</span> <span style="color:var(--syntax-text-color)">$HOME</span>
<span style="color:var(--syntax-text-color)">source </span>venv/bin/activate
devpi-server <span style="color:var(--syntax-error-color)">--restrict-modify</span><span style="color:var(--syntax-error-color)">=</span>root
</code></span></span>
--restrict-modify=root使得需要 root 用户来进行管理更改,例如索引和用户创建。然后修改为systemd可执行:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ chmod u+x start-devpi.sh
</code></span></span>
最后我们调整systemd服务文件:
gen-config/devpi.service
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>[Unit]
Description=Devpi Server
Requires=network-online.target
After=network-online.target
[Service]
Restart=on-success
# ExecStart:
# - shall point to existing devpi-server executable
# - shall not use the deprecated `--start`. We want the devpi-server to start in foreground
ExecStart=/home/devpi/start-devpi.sh
# set User according to user which is able to run the devpi-server
User=devpi
[Install]
WantedBy=multi-user.target
</code></span></span>
现在退出 devpi 用户以返回到您在 Ubuntu 上使用的普通用户。然后继续:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ sudo cp /home/devpi/gen-config/devpi.service /etc/systemd/system/
$ sudo systemctl enable devpi.service
Created symlink /etc/systemd/system/multi-user.target.wants/devpi.service → /etc/systemd/system/devpi.service
$ sudo systemctl start devpi.service
</code></span></span>
现在,除非更改端口,否则该服务默认在http://localhost:3141上可用:
在开始之前,我们需要安装管理一切的客户端:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ pip install devpi-client
</code></span></span>
由于这是本地 pip 安装,它最终会出现在$HOME/.local/bin通常不在路径中的位置。我将修改~/.profile以添加它:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code># set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$HOME/.local/bin:$PATH"
fi
</code></span></span>
然后刷新会话:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ source ~/.profile
</code></span></span>
现在 devpi 需要知道如何与服务器通信。这是通过以下方式完成的:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ devpi use http://localhost:3141
using server: http://localhost:3141/ (not logged in)
no current index: type 'devpi use -l' to discover indices
/home/cwprogram/.config/pip/pip.conf: no config file exists
/home/cwprogram/.pydistutils.cfg: no config file exists
/home/cwprogram/.buildout/default.cfg: no config file exists
</code></span></span>
其中 3141 是您在设置过程中选择的端口(如果不同)。它还会尝试更新相关的 pip 配置文件(如果存在),这是我们稍后会讨论的内容。现在设置 root 密码并添加新用户:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ devpi login root --password ''
$ devpi user -m root password=[something cool here]
$ devpi user -c cwprogram password=[redacted] email=[redacted]
user created: cwprogram
</code></span></span>
最后我们需要创建一个用户可用于上传和下载的索引:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ devpi index -c cwprogram/stable bases=root/pypi volatile=True
http://localhost:3141/cwprogram/stable?no_projects=:
type=stage
bases=root/pypi
volatile=True
acl_upload=cwprogram
acl_toxresult_upload=:ANONYMOUS:
mirror_whitelist=
mirror_whitelist_inheritance=intersection
</code></span></span>
所以这里发生的事情表明cwprogram/stable这是一个stable归因于cwprogram用户的命名索引。bases=root/pypi让系统知道如果未找到包则尝试使用 PyPi。volatile=True将允许上传。现在管理工作已经完成,我们以 root 身份注销:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ devpi logoff
login information deleted
</code></span></span>
下一部分将从最终用户的角度进行讨论。考虑到这一点,我将在下一部分中切换到 Windows 系统。为了方便不需要密码的登录,我将利用 python密钥环项目以及devpi 的客户端扩展。这使得它可以与钥匙圈集成。由于钥匙圈更多地是多个钥匙串管理器的前端,因此必须有一个可用的后端。文档将其列出为:
如果计划在命令行 Linux 界面上使用它,则可以使用通过加密文本文件的keyrings.cryptfile或通过 ssh-agent 的sagecipher 。我将继续安装 devpi 客户端以及扩展和密钥环以开始:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> pip install devpi-client devpi-client-extensions[keyring] keyring
> keyring set http://localhost:3141/ cwprogram
Password for 'cwprogram' in 'http://localhost:3141':
</code></span></span>
注意: URL 中的最后一个/是必需的,否则钥匙串将不会被拾取
现在,如果我以 cwprogram 用户身份登录:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> devpi login cwprogram
Using cwprogram credentials from keyring
logged in 'cwprogram', credentials valid for 10.00 hours
</code></span></span>
现在我还可以使用pip该服务器进行身份验证。首先,我将有一个使用密钥环提供程序的全局设置:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> pip config set --global global.keyring-provider subprocess
</code></span></span>
请注意,pip config set --site如果您希望在每个虚拟环境项目的基础上处理此问题,也可以使用相同的选项。pip由于需要不同的主机格式,我们还需要再次设置密钥环:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> keyring set localhost cwprogram
</code></span></span>
现在来展示即使在虚拟环境中也能正常工作:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> virtualenv.exe --python=python3.9 venv_devpi
> .\venv_devpi\Scripts\activate
> devpi use cwprogram/stable
> pip install requests --index-url=http://localhost:3141/cwprogram/stable
Looking in indexes: http://localhost:3141/cwprogram/stable
Collecting requests
Downloading http://localhost:3141/root/pypi/%2Bf/58c/d2187c01e70e6/requests-2.31.0-py3-none-any.whl (62 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.6/62.6 kB 3.3 MB/s eta 0:00:00
Collecting charset-normalizer<4,>=2 (from requests)
Downloading http://localhost:3141/root/pypi/%2Bf/830/d2948a5ec37c3/charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl (97 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 97.1/97.1 kB ? eta 0:00:00
Collecting idna<4,>=2.5 (from requests)
Downloading http://localhost:3141/root/pypi/%2Bf/90b/77e79eaa3eba6/idna-3.4-py3-none-any.whl (61 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.5/61.5 kB ? eta 0:00:00
Collecting urllib3<3,>=1.21.1 (from requests)
Downloading http://localhost:3141/root/pypi/%2Bf/48e/7fafa40319d35/urllib3-2.0.3-py3-none-any.whl (123 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 123.6/123.6 kB ? eta 0:00:00
Collecting certifi>=2017.4.17 (from requests)
Downloading http://localhost:3141/root/pypi/%2Bf/c6c/2e98f5c7869ef/certifi-2023.5.7-py3-none-any.whl (156 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 157.0/157.0 kB ? eta 0:00:00
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests
Successfully installed certifi-2023.5.7 charset-normalizer-3.1.0 idna-3.4 requests-2.31.0 urllib3-2.0.3
</code></span></span>
如图所示,包安装成功。另一个有趣的功能是 pypi 下载被缓存:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>~/.devpi/server/+files/root/pypi/+f/48e/7fafa40319d35$ ls
urllib3-2.0.3-py3-none-any.whl
</code></span></span>
虽然文件结构可能不同,但我们尚未进行任何上传,但 urllib 位于 devpi 服务器文件系统上。由于不断使用--index-url指向服务器的选项并不理想,因此devpi use可以使用一个--set-cfg选项来运行,以写出pip默认情况下使用的自定义索引的本地配置选项:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> devpi use --set-cfg cwprogram/stable
> pip install moto
Looking in indexes: http://localhost:3141/cwprogram/stable/+simple/
Collecting moto
Downloading http://localhost:3141/root/pypi/%2Bf/6f4/0141ff2f3a309/moto-4.1.12-py2.py3-none-any.whl (3.0 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 48.2 MB/s eta 0:00:00
Collecting boto3>=1.9.201 (from moto)
...
</code></span></span>
它再次出现在 devpi 的本地文件系统上:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>$ find . -iname '*moto*'
./+files/root/pypi/+f/6f4/0141ff2f3a309/moto-4.1.12-py2.py3-none-any.whl
</code></span></span>
现在是时候进行实际的上传部分了,因为这就是大多数人想要私有 PyPi 索引的原因,对吗?首先,我将克隆tomchen 的示例 PyPi 存储库模板:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> git clone https://github.com/tomchen/example_pypi_package.git
</code></span></span>
然后我将安装 python build(版本指定在那里,否则我会收到 python 版本错误):
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> pip3.9 install build
> python -m build --sdist --wheel .
> dir dist\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2023/06/30 22:45 8317 example_pypi_package-0.1.0-py3-none-any.whl
-a--- 2023/06/30 22:45 9189 example_pypi_package-0.1.0.tar.gz
</code></span></span>
因此,上述命令会输出 sdist (tar.gz) 和wheel (.whl) 文件。devpi客户端提供了简单的上传方式:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> devpi upload --sdist --wheel --from-dir dist\
file_upload of example_pypi_package-0.1.0-py3-none-any.whl to http://localhost:3141/cwprogram/stable/
file_upload of example_pypi_package-0.1.0.tar.gz to http://localhost:3141/cwprogram/stable/
> pip install example_pypi_package
Looking in indexes: http://localhost:3141/cwprogram/stable/+simple/
Collecting example_pypi_package
Downloading http://localhost:3141/cwprogram/stable/%2Bf/318/c1017f3670e4c/example_pypi_package-0.1.0-py3-none-any.whl (8.3 kB)
Installing collected packages: example_pypi_package
Successfully installed example_pypi_package-0.1.0
</code></span></span>
pip从私人索引安装有效!现在进行测试以确保包本身可以正常工作。我也会在非源代码目录中运行它:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> python
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from examplepy import Number
>>> n1 = Number(2)
>>> n2 = Number(3)
>>> n1.add(n2)
>>> n1.val()
5
</code></span></span>
如果您想坚持使用传统的 PyPi 上传系统,Twine也是一个选择。它确实需要额外复制密钥环条目并添加配置文件。首先,我将创建一个包含以下内容的$HOME\.pypirc文件(确保这是 Powershell,以便正确扩展):$HOME
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>[distutils]
index-servers =
devpi-stable
[devpi-stable]
repository = http://localhost:3141/cwprogram/stable/
username = cwprogram
</code></span></span>
密钥环需要设置为以下值repository:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> keyring set http://localhost:3141/cwprogram/stable/ cwprogram
</code></span></span>
从那里只需像往常一样进行twine上传,并指定devpi-stable存储库:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>> twine upload -r devpi-stable dist\*
Uploading distributions to http://localhost:3141/cwprogram/stable/
Uploading example_pypi_package-0.1.0-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 28.4/28.4 kB • 00:02 • ?
Uploading example_pypi_package-0.1.0.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 29.1/29.1 kB • 00:00 • ?
</code></span></span>
这些软件包还通过 Web 界面提供了很好的概述:
devpi这就结束了对基本独立 python 包服务器的使用。这对于本地开发人员或小型团队来说非常有用,因为手动用户输入并不是什么大问题。也就是说,可以通过devpi-ldap将 LDAP 作为身份验证后端,尽管它需要相当多的技术技能来设置(以及运行 LDAP 服务器)。