共计 8289 个字符,预计需要花费 21 分钟才能阅读完成。
pex(https://github.com/pantsbuild/pex)代表 Python EXecutable,是一种生成易于分发的 python 包的方法。需要注意的一件重要事情是 pex 没有可靠的 Windows 支持。因此,您需要在 *NIX 系统上运行 pex。本文将展示您可以使用 pex 执行的一些操作,以分发不同类型的 python 项目。
基本用法
鉴于用于 pex 打包的 python 解释器很重要,强烈建议使用虚拟环境。作为示例,我将使用 python 3.11 环境:
$ virtualenv --python=python3.11 venv
$ source venv/bin/activate
$ python -m pip install pex
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting pex
Using cached https://www.piwheels.org/simple/pex/pex-2.1.144-py2.py3-none-any.whl (2.9 MB)
Installing collected packages: pex
Successfully installed pex-2.1.144
pex CLI 执行的一般格式为:
pex [MODULES] [OPTIONS]
其中 [MODULES] 是 pip 样式依赖项声明字符串中以空格分隔的模块列表:
$ pex "requests" "setproctitle==1.3.2" "uvicorn[standard]"
Python 3.11.4 (main, Aug 17 2023, 03:18:09) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
如果没有任何其他选项,pex 将进入交互式 shell,并且提供的模块将在以下位置可用:
>>> import requests
>>> import setproctitle
>>> import uvicorn
>>>
关闭控制台后我们可以看到虚拟环境包完全不受影响:
$ pip list
Package Version
---------- -------
pex 2.1.144
pip 23.2.1
setuptools 65.5.0
$
需求管理
由于列出每个模块通常并不理想,因此可以使用两种替代方法来传递需求。第一个解决方案是使用 requirements.txt 文件:
requirements.txt
requests
setproctitle==1.3.2
uvicorn[standard]
然后可以使用传入的 - r 选项和 requirements.txt 文件运行 pex:
$ pex -r requirements.txt
Python 3.11.4 (main, Aug 17 2023, 03:18:09) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
- r 如果您捆绑了多个项目,也可以多次传递参数。如果您已经设置了虚拟环境,那么您可以传递 pip freeze 到 pex:
$ pex $(pip freeze)
Python 3.11.4 (main, Aug 17 2023, 03:18:09) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)
>>>
requirements.txt 如果您有很多模块需要使用,那么该方法会很好。pip freeze 对于已经设置了 virtualenv 的情况很有用。
Python 项目结构化模块
pex 还支持 python 包作为模块,其结构类似于 python 打包文档中的基本结构。对于此示例,我将使用此 git 存储库中的项目布局(https://github.com/cwgem/pex_simple)。它包括一个带有自述文件、许可证、简单模块和 pyproject.toml 的基本布局。这足以让它被 pex 像开发模式 pip install 一样识别:
$ pex .
Python 3.11.4 (main, Aug 17 2023, 03:18:09) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from simple_pex import simple_math
>>> simple_math(3,4)
7
>>>
这一切都是在无需构建项目本身的情况下实现的。
资源目录
pex 还可以添加重要项目的目录,例如测试数据和配置。在应用程序存储库中有一个 resources 目录,其中包含一个 test_data.json 如下所示的文件:
{
"a": 1,
"b": 2
}
我们可以使用 pex 参数 - D 来添加用于捆绑的特定目录。然后可以在脚本 / 交互式提示中使用它,如下所示:
$ pex . -D resources
Python 3.11.4 (main, Aug 17 2023, 03:18:09) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)>>> from simple_pex import simple_math
>>> import json
>>> fp = open('resources/test_data.json', 'r')
>>> data = json.load(fp)
>>> fp.close()>>> simple_math(data['a'], data['b'])
3
>>>
正如您所看到的,JSON 数据被加载,然后传递给 simple_math 返回正确结果的函数。
切入点
python 脚本的一项功能是能够像运行基本程序一样设置入口点。对于此示例,我将使用此存储库中托管的代码(https://github.com/cwgem/pex_simple_cli)。使这项工作有效的是控制台脚本的声明,如下所示:
[project.scripts]adder = "cli_pex:run"
这将生成一个名为“adder”的脚本,该脚本将从包 run 中执行 cli_pex:
import argparse
def run():
parser = argparse.ArgumentParser()
parser.add_argument("--integer1", type=int, help="First Integer")
parser.add_argument("--integer2", type=int, help="Second Integer")
args = parser.parse_args()
print(args.integer1 + args.integer2)
虽然不是一个非常实用的程序,但它可以完成展示 pex 如何与控制台脚本一起工作的工作。为了展示这一点:
$ pex . -o adder.pex -c adder
$ ./adder.pex --integer1 3 --integer2 4
7
using- c 告诉 pex 我们要 adder 使用 pyproject.toml. 现在,当我们打包所有内容时,它就像一个基本程序一样。还有一个使用固定参数的选项,因此只.pex 需要执行文件:
$ pex . -o adder.pex -c adder --inject-args "--integer1 3 --integer2 4"
$ ./adder.pex
7
这对于轻松部署采用绑定端口和主机名等参数的服务器脚本非常有用。
Docker 部署
为了将这一切放在一起,我将对 pex Web 应用程序进行 Docker 部署。它将把 Gunicorn 与 Flask 应用程序捆绑在一起,该应用程序将充当容器的入口点。可以在此处找到此示例中使用的代码(https://github.com/cwgem/pex_web_example)。在此设置中,有一个简单的 Flask 应用程序、一个 Gunicorn 配置文件和一个用于启用部署的 Dockerfile。这次 pyproject.toml 声明了一些依赖项:
dependencies = [
"flask",
"gunicorn",
"setproctitle",
]
另一件需要考虑的事情是,pex 需要将其打包的系统设置与目标系统相当接近。这意味着我将在 Unbuntu 盒子上构建,而我的容器将基于 Debian(更精简,并且系统足够接近)。其他一些需要完成的事情:
-
pex 可执行文件需要指向 gunicorn 控制台脚本才能运行服务器
-
Gunicorn 配置文件需要复制到系统中
-
–inject-args 需要将 –config 参数设置为 gunicorn 配置
-
生成的.pex 文件需要设置为入口点
查看需求,结果 pex 调用将是:
pex . -o web_pex.pex -c gunicorn --inject-args "--config /home/gunicorn/app/gunicorn.config.py"
虽然 Dockerfile 看起来像:
FROM python:3.11.4-bullseye
USER root
RUN useradd -d /home/gunicorn -r -m -U -s /bin/bash gunicorn
USER gunicorn
RUN mkdir /home/gunicorn/app
COPY config/gunicorn.config.py /home/gunicorn/app
COPY web_pex.pex /home/gunicorn/app
ENTRYPOINT /home/gunicorn/app/web_pex.pex
EXPOSE 8000
鉴于构建.pex 捆绑包的解释器是 python 3.11,我将其设置为基础映像。现在剩下的就是构建 Dockerfile,然后运行生成的映像:
$ docker buildx build -f Dockerfile -t flask/web-pex:latest .
$ docker run -it -p 8000:8000 flask/web-pex:latest
[2023-08-25 00:13:11 +0000] [7] [INFO] Starting gunicorn 21.2.0
[2023-08-25 00:13:11 +0000] [7] [INFO] Listening at: http://0.0.0.0:8000 (7)
[2023-08-25 00:13:11 +0000] [7] [INFO] Using worker: sync
[2023-08-25 00:13:11 +0000] [8] [INFO] Booting worker with pid: 8
[2023-08-25 00:13:11 +0000] [9] [INFO] Booting worker with pid: 9
这将运行新创建的 flask/web-pex:latest 映像并公开端口 8000。现在使用 curl 进行测试:
$ curl http://127.0.0.1:8000
Hello World
感谢 setproctitle 进程列表也变得更清晰:
$ ps auxUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
gunicorn 1 0.0 0.0 2480 512 pts/0 Ss+ 00:13 0:00 /bin/sh -c /home/gunicorn/app/web_pex.pex
gunicorn 7 4.5 0.2 53904 48244 pts/0 S+ 00:13 0:00 gunicorn: master [gunicorn]
gunicorn 8 1.1 0.3 63244 52084 pts/0 S+ 00:13 0:00 gunicorn: worker [gunicorn]
gunicorn 9 0.6 0.3 62024 51644 pts/0 S+ 00:13 0:00 gunicorn: worker [gunicorn]
gunicorn 10 0.5 0.0 6052 3784 pts/1 Ss 00:13 0:00 /bin/bash
gunicorn 17 0.0 0.0 8648 3276 pts/1 R+ 00:13 0:00 ps aux
这使得更容易辨别容器上的各种 gunicorn 进程。
工具
另一个有趣的功能是 pex 还提供了一些可用的工具,可以让我们创建性能更高的 docker 镜像。为了使这项工作正常进行,我们需要添加 –include-tools 到 pex 构建命令中:
$ pex . -o web_pex.pex -c gunicorn --inject-args "--config /home/gunicorn/app/gunicorn.config.py"
--include-tools
Dockerfile 还将更新为多阶段构建以生成最终的映像:
FROM python:3.11.4-bullseye as deps
RUN mkdir -p /home/gunicorn/app
COPY web_pex.pex /home/gunicorn/
RUN PEX_TOOLS=1 /usr/local/bin/python3.11 /home/gunicorn/web_pex.pex venv --scope=deps --compile /home/gunicorn/app
FROM python:3.11.4-bullseye as srcs
RUN mkdir -p /home/gunicorn/app
COPY web_pex.pex /home/gunicorn
COPY config/gunicorn.config.py /home/gunicorn/app
RUN PEX_TOOLS=1 /usr/local/bin/python3.11 /home/gunicorn/web_pex.pex venv --scope=srcs --compile /home/gunicorn/app
FROM python:3.11.4-bullseyeRUN useradd -d /home/gunicorn -r -m -U -s /bin/bash gunicorn
COPY --from=deps --chown=gunicorn:gunicorn /home/gunicorn/app /home/gunicorn/app
COPY --from=srcs --chown=gunicorn:gunicorn /home/gunicorn/app /home/gunicorn/app
USER gunicorn
ENTRYPOINT /home/gunicorn/app/pex
EXPOSE 8000
这将分离依赖项和源编译。当 python 进行编译时,它将创建一组特定于解释器的字节码,因此不必在运行时完成。这使得事情运行得更快。docker 构建的唯一变化是 Dockerfile 不同,而运行命令保持不变:
$ docker buildx build -f Dockerfile_pex_tools -t flask/web-pex:latest .
$ docker run -it -p 8000:8000 flask/web-pex:latest
[2023-08-25 01:25:47 +0000] [7] [INFO] Starting gunicorn 21.2.0
[2023-08-25 01:25:47 +0000] [7] [INFO] Listening at: http://0.0.0.0:8000 (7)
[2023-08-25 01:25:47 +0000] [7] [INFO] Using worker: sync
[2023-08-25 01:25:47 +0000] [8] [INFO] Booting worker with pid: 8
[2023-08-25 01:25:47 +0000] [9] [INFO] Booting worker with pid: 9
查看容器内部,您可以看到用户目录 pex 中的布局:~/appgunicorn
$ cd ~/app
$ ls
PEX-INFO __main__.py __pycache__ bin gunicorn.config.py include lib lib64 pex pyvenv.cfg
并且缓存文件也显示在 gunicorn 工人生成之前的时间,以表明它们确实是编译输出,而不仅仅是 Python 自然生成它们:
$ ls -lah lib/python3.11/site-packages/flask/__pycache__/
total 388K
drwxr-xr-x 2 gunicorn gunicorn 4.0K Aug 25 01:03 .
drwxr-xr-x 4 gunicorn gunicorn 4.0K Aug 25 01:03 ..
-rw-r--r-- 1 gunicorn gunicorn 4.0K Aug 25 01:03 __init__.cpython-311.pyc
-rw-r--r-- 1 gunicorn gunicorn 249 Aug 25 01:03 __main__.cpython-311.pyc
-rw-r--r-- 1 gunicorn gunicorn 86K Aug 25 01:03 app.cpython-311.pyc
-rw-r--r-- 1 gunicorn gunicorn 32K Aug 25 01:03 blueprints.cpython-311.pyc
结论
pex 对用于打包 Python 代码的了解到此结束。(https://github.com/pantsbuild/pex/issues/716)这是一个有趣的系统,从 GitHub 问题来看也具有可重复构建的潜力。启用工具可以轻松地使用单个包部署,同时通过多阶段编译启用更高性能的选项。我鼓励您看看它如何增强您的 Python 项目。文章来源:https://www.toymoban.com/diary/python/301.html
文章来源地址 https://www.toymoban.com/diary/python/301.html
到此这篇关于使用 PEX 打包 Python 可执行文件的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!