让我们看一下如何获取有关Python生态系统中安装软件包的出处的信息。这个想法是截至目前处于草案状态的PEP-710的一部分。
教程使用github.com/fridex/pip-provenance的文件。
让我们使用Chainguard's Python image创建一个简单的Python应用程序。此应用程序将是一个简单的烧瓶Hello World应用程序。 app.py
脚本将具有以下内容:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello, world!'
app.run(host='0.0.0.0', port=8080)
此外,我们将创建一个带有以下内容的requirements.in
文件:
flask
我们将使用pip-tools锁定对特定版本的依赖性,以获得可重复性。此外,我们将保留安装的Python分布的哈希:
pip-compile --generate-hashes
上面的命令将创建一个requirements.txt
文件。这样的文件的一个示例可以是found here。
接下来,让我们使用应用程序创建一个容器化的环境。
使用上游PIP
首先,我们将使用上游PIP,该PIP也在Chainguard的图像中发货。我们可以直接采用最小更改的Dockerfile as written by Chainguard,以确保我们有一个容器化的应用程序:
FROM cgr.dev/chainguard/python:latest-dev as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt --user
FROM cgr.dev/chainguard/python:latest
WORKDIR /app
# Make sure you update Python version in path
COPY --from=builder /home/nonroot/.local/lib/python3.11/site-packages /home/nonroot/.local/lib/python3.11/site-packages
COPY app.py .
ENTRYPOINT ["python", "/app/app.py"]
可以构建容器化的应用程序:
podman build -f raw/Dockerfile -t pip-provenance:raw .
随后,可以在locahost:8080:
上运行并访问构建的应用程序
podman run -p 8080:8080 pip-provenance:raw
现在,让我们想象有人将此图像发布给注册表,我们想获取有关已安装软件包的信息。我们可以拉动pip-provenance:raw
图像并运行pip freeze
。不幸的是,pip freeze
仅显示安装Python包及其版本:
$ pip freeze
click==8.1.3
Flask==2.2.3
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
Werkzeug==2.2.3
我们没有任何实际安装这些软件包的信息。另外,我们没有有关这些软件包摘要的任何信息。例外是使用PEP-610之后使用直接URL安装的软件包,但在我们的示例中并非如此。
使用修补的PIP
PEP-710中有一个建议,用于存储有关使用其名称识别的安装软件包的出处信息,并可以选择地将其版本(这是我们的示例)。让我们看一下存储了哪些信息以及如何访问它的信息。
首先,让我们调整我们的Dockerfile以使用遵循PEP-710的a patched version of pip:
FROM cgr.dev/chainguard/python:latest-dev as builder
WORKDIR /app
COPY requirements.txt .
# ----->%------
USER root
RUN pip install --force-reinstall pip install git+https://github.com/fridex/pip.git@provenance-url
USER nonroot
# -----%<------
RUN pip install -r requirements.txt --user
FROM cgr.dev/chainguard/python:latest
WORKDIR /app
# Make sure you update Python version in path
COPY --from=builder /home/nonroot/.local/lib/python3.11/site-packages /home/nonroot/.local/lib/python3.11/site-packages
COPY app.py .
ENTRYPOINT ["python", "/app/app.py"]
让我们构建此应用程序:
podman build -f patched/Dockerfile -t pip-provenance:patched .
我们可以运行该应用程序并在localhost:8080上访问它,PIP中引入的更改对此没有影响:
podman run -p 8080:8080 pip-provenance:patched
PEP-710之后,PIP存储了位于site-packages
中的*.dist-info
目录中的出处的信息。让我们从容器化的环境中复制site-packages
目录,以便我们可以检查那里安装的内容(用上一个示例中运行的容器化环境的哈希替换[CONTAINER_HASH]
):
podman cp [CONTAINER_HASH]:/home/nonroot/.local/lib/python3.11/site-packages site-packages
我们可以查看flask
的provenance_url.json
文件*:
$ cat ./site-packages/Flask-2.2.3.dist-info/provenance_url.json | jq
{
"archive_info": {
"hash": "sha256=c0bec9477df1cb867e5a67c9e1ab758de9cb4a3e52dd70681f59fa40a62b3f2d",
"hashes": {
"sha256": "c0bec9477df1cb867e5a67c9e1ab758de9cb4a3e52dd70681f59fa40a62b3f2d"
}
},
"url": "https://files.pythonhosted.org/packages/95/9c/a3542594ce4973786236a1b7b702b8ca81dbf40ea270f0f96284f0c27348/Flask-2.2.3-py3-none-any.whl"
}
此文件由修补程序创建,在PEP-710中进行了更详细的描述。
一种称为pip-preserve的小工具,可以读取site-packages
目录的内容,并了解安装每个Python软件包的provenance_url.json
。此外,如果使用直接URL安装软件包,则该工具还可以如PEP-610中所述读取direct_url.json
,以完全重建环境。让我们从容器化的环境中使用site-packages
目录上的工具:
$ pip install pip-preserve
...
$ pip-preserve --ignore-errors --site-packages ./site-packages
#
# This file is autogenerated by pip-preserve version 0.0.2.post1 with Python 3.10.6.
#
click==8.1.3 \
--hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
flask==2.2.3 \
--hash=sha256:c0bec9477df1cb867e5a67c9e1ab758de9cb4a3e52dd70681f59fa40a62b3f2d
itsdangerous==2.1.2 \
--hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44
jinja2==3.1.2 \
--hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
markupsafe==2.1.2 \
--hash=sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6
werkzeug==2.2.3 \
--hash=sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612
您可以看到,该工具重建了requirements.txt
文件,列出了所有安装的软件包及其版本和哈希。
读者可以注意到,重建的文件每个软件包只有一个哈希。原因是PIP仅安装一个软件包。我们原始的requirements.txt
文件lists multiple hashes that correspond to Python distributions as published on PyPI在运行pip-compile
命令时。在安装时间内,PIP采用与安装Python分布的环境相匹配的PIP。例如,PIP获取了为flask==2.2.3发布的轮毂文件,而不是PYPI上可用的源分布(您可以通过检查伪影哈希进行验证)。使用PIP的修补版本,我们可以指出已安装的确切工件。
如果我们将--direct-url
选项传递到pip-preserve
工具,则可以从安装Python软件包的地方获得精确的URL:
$ pip-preserve --ignore-errors --direct-url --site-packages ./site-packages
#
# This file is autogenerated by pip-preserve version 0.0.2.post1 with Python 3.10.6.
#
https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl \
--hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
https://files.pythonhosted.org/packages/95/9c/a3542594ce4973786236a1b7b702b8ca81dbf40ea270f0f96284f0c27348/Flask-2.2.3-py3-none-any.whl \
--hash=sha256:c0bec9477df1cb867e5a67c9e1ab758de9cb4a3e52dd70681f59fa40a62b3f2d
https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl \
--hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44
https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl \
--hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
https://files.pythonhosted.org/packages/5a/94/d056bf5dbadf7f4b193ee2a132b3d49ffa1602371e3847518b2982045425/MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl \
--hash=sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6
https://files.pythonhosted.org/packages/f6/f8/9da63c1617ae2a1dec2fbf6412f3a0cfe9d4ce029eccbda6e1e4258ca45f/Werkzeug-2.2.3-py3-none-any.whl \
--hash=sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612
为什么这有用?
好吧,现在我们知道我们可以使用PEP-710获得有关安装软件包的出处的信息。如果我们查看其他软件包,例如TensorFlow,我们可以看到已发布的multiple wheel files-每个包裹与特定环境相对应。如果我们只是pip install tensorflow
,实际上使用了哪个轮子文件(假设我们无需访问安装日志)?
另外,请注意,可以在私有Python软件包索引上托管的Python软件包的特定构建。这些轮子可以使用wheel tags表示无法表达的选项构建。如果您使用的是Python环境(不一定是容器化的环境),您如何知道已安装Python软件包的出处(无需访问安装日志,或最终任何构建配置)?
本文中使用的构建集装箱环境可在docker.io/fridex/pip-provenance上获得:
podman pull fridex/pip-provenance:raw
podman pull fridex/pip-provenance:patched
您可以在discuss.python.org上进行有关PEP-710的相关讨论。
*,即使修补程序生成的provenance_url.json
文件保留了hash
键,pep-710也不能定义它。修补的PIP实现使用PEP-610(直接URL)定义的代码。现在在PEP-610引入的direct_url.json
文件中删除了hash
密钥。