Home > Article > Backend Development > pex: Packaging Python executable files
pex stands for Python EXecutable and is a way to generate python packages that are easy to distribute. One important thing to note is that pex does not have solid Windows support. Therefore, you need to run pex on a *NIX system. This article will show you some of the things you can do with pex to distribute different types of python projects.
Given the importance of a python interpreter for pex packaging, it is highly recommended to use a virtual environment. As an example I will use the python 3.11 environment:
$ 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
The general format of pex CLI execution is:
pex [模块] [选项]
where [MODULES]
is a space-separated list of modules in a pip-style dependency declaration string:
$ 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) >>>
Without any other options, pex will enter an interactive shell and the provided modules will be available at:
>>> import requests >>> import setproctitle >>> import uvicorn >>>
After closing the console we can see that the virtual environment package is completely unaffected:
$ pip list Package Version ---------- ------- pex 2.1.144 pip 23.2.1 setuptools 65.5.0 $
Since listing every module is usually not ideal, there are two alternative ways to communicate requirements. The first solution is to use the requirements.txt
file:
requirements.txt
requests setproctitle==1.3.2 uvicorn[standard]
pex can then be run using the -r
option and the requirements.txt
file passed in:
$ 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 的情况很有用。
pex 还支持 python 包作为模块,其结构类似于 python 打包文档中的基本结构。对于此示例,我将使用此 git 存储库中的项目布局。它包括一个带有自述文件、许可证、简单模块和 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 脚本的一个功能是能够像运行基本程序一样设置入口点。对于此示例,我将使用此存储库中托管的代码。使这项工作有效的是控制台脚本的声明,如下所示:
[project.scripts] adder = "cli_pex:run"
这将生成一个名为“adder”的脚本,该脚本将从 cli_pex
包中执行 run
:
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
使用 -c
告诉 pex 我们要使用 pyproject.toml
中定义的 adder
脚本。现在,当我们打包所有内容时,它就像一个基本程序一样。还有一个使用固定参数的选项,因此只需要执行 .pex
文件:
$ pex . -o adder.pex -c adder --inject-args "--integer1 3 --integer2 4" $ ./adder.pex 7
这对于轻松部署采用绑定端口和主机名等参数的服务器脚本非常有用。
为了将这一切放在一起,我将对 pex Web 应用程序进行 Docker 部署。它将把 Gunicorn 与 Flask 应用程序捆绑在一起,该应用程序将充当容器的入口点。可以在此处找到此示例中使用的代码。在此设置中,有一个简单的 Flask 应用程序、一个 Gunicorn 配置文件和一个用于启用部署的 Dockerfile。这次 pyproject.toml
声明了一些依赖项:
dependencies = [ "flask", "gunicorn", "setproctitle", ]
另一件需要考虑的事情是,pex 需要将其打包的系统设置与目标系统相当接近。这意味着我将在 Unbuntu 盒子上构建,而我的容器将基于 Debian(更精简,并且系统足够接近)。其他一些需要完成的事情:
--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 aux USER 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-to ols
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-bullseye RUN 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
查看容器内部,可以看到 gunicorn
用户的 ~/app
目录中 pex
的布局:
$ 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 代码的介绍到此结束。这是一个有趣的系统,从 GitHub 问题来看也具有可重复构建的潜力。启用工具可以轻松地使用单个包部署,同时通过多阶段编译启用更高性能的选项。我鼓励您看看它如何增强您的 Python 项目。
The above is the detailed content of pex: Packaging Python executable files. For more information, please follow other related articles on the PHP Chinese website!