Python项目打包可执行文件

1.简介

DolphinScheduler调度平台使用时候一般都是Java直接打包成Jar以后执行,而Python原先使用的是git拉取代码到执行调度机,然后激活python虚拟环境,最后通过Python命令执行,使用和迁移起来代价比较大,不是特别方便,为了简单易用,使用Pyinstaller将整个Python项目打包成一个可执行文件或者压缩包,上传到调度平台后可以随时执行。

2.使用方法

Pyinstaller可以将Python脚本打包成独立的可执行程序,支持多平台Windows,Linux和Mac OS X等。

Python 默认并不包含 PyInstaller 模块,因此需要自行安装 PyInstaller 模块。

pip install pyinstaller
表 1 PyInstaller 支持的常用选项
-h,--help
查看该模块的帮助信息
-F,-onefile 产生单个的可执行文件
-D,--onedir 产生一个目录(包含多个文件)作为可执行程序
-a,--ascii 不包含 Unicode 字符集支持
-d,--debug 产生 debug 版本的可执行文件
-o DIR,--out=DIR 指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
-p DIR,--path=DIR 设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径
-n NAME,--name=NAME 指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字

-y, --noconfirm

不询问

-n NAME, --name NAME

打包后文件名

--distpath DIR

指定打包位置

--add-data <SRC;DEST or SRC:DEST>

添加数据文件

--add-binary <SRC;DEST or SRC:DEST>

添加二进制文件

--hidden-import MODULENAME, --hiddenimport MODULENAME

打包隐藏的模块

--version-file FILE

加入版本文件

在表 1 中列出的只是 PyInstaller 模块所支持的常用选项,如果需要了解 PyInstaller 选项的详细信息,则可通过 pyinstaller -h 来查看。

3.使用实例

项目目录:

├── Makefile
├── README.md
├── __pycache__
│   └── main.cpython-36.pyc
├── autotest
│   ├── dev_config.sh
│   ├── local_config.sh
│   ├── pylint.sh
│   └── pylintrc
├── conf
│   └── data_source
│   ├── mongo.conf
│   ├── mysql.conf
│   └── redis.conf
├── main.py
├── requirements
│   ├── base.txt
│   ├── internal.txt
│   └── monitor.sql
├── tasks
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   ├── adan_day_sample_diff_task.cpython-36.pyc
│   │   ├── baseTask.cpython-36.pyc
│   │   ├── check_live_task.cpython-36.pyc
│   │   ├── check_timeout_task.cpython-36.pyc
│   │   ├── diff_sync_table_task.cpython-36.pyc
│   │   ├── kafka_monitor_task.cpython-36.pyc
│   │   ├── statistics_task.cpython-36.pyc
│   │   └── test_task.cpython-36.pyc
│   ├── adan_day_sample_diff_task.py
│   ├── adan_global_sample_diff_task.py
│   ├── adan_repair_useraccountbinding.py
│   ├── anubis_status_monitor_task.py
│   ├── back_server_log_to_hdfs_task.py
│   ├── baseTask.py
│   ├── check_live_task.py
│   ├── check_timeout_task.py
│   ├── check_timeout_task.pyc
│   ├── check_warn.py
│   ├── diff_sync_table_task.py
│   ├── error_log_increase_monitor_task.py
│   ├── kafka_monitor_task.py
│   ├── statistics_task.py
│   ├── statistics_task.pyc
│   └── test_task.py
├── test.py
└── utils
├── __init__.py
├── __pycache__
│   ├── __init__.cpython-36.pyc
│   ├── adan_diff.cpython-36.pyc
│   ├── aes_util.cpython-36.pyc
│   ├── country_code.cpython-36.pyc
│   └── send_email.cpython-36.pyc
├── adan_diff.py
├── aes_util.py
├── country_code.py
├── country_code.txt
└── send_email.py

 

对于我们的监控项目,Task,我们想要在调度平台执行,那么我们需要在项目根目录下执行:

pyinstaller ---clean --hidden-import tasks --add-data conf:conf main.py  # 打包
tar -zcvf task.tar.gz dist/main    
hadoop fs -put -task.tar.gz  /dolphinscheduler/work/resources/   # 上传到调度平台
rm -rf build       # 删除生成的build文件
rm -rf main.spec   # 删除生成的main.spec
rm -rf dist        # 删除打包后的文件夹
rm -rf task.tar.gz # 删除压缩后的文件

 

4.注意事项

使用中碰到了一些问题

1. --hidden-import,我们最开始直接打包后发现,通过命令:

./main --module tasks.test_task --task_class=TestTask 去进行执行发现并没有导入过来task的各个监控脚本,报错:

(.env) guochengdeMacBook-Pro:main guocheng$ ./main --module tasks.test_task --task_class=TestTask
Traceback (most recent call last):
File "main.py", line 52, in <module>
File "importlib/__init__.py", line 126, in import_module
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 941, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'tasks'
[57485] Failed to execute script main

,因为我们使用的是动态导入,所以并没有被识别到task被导入了,如果main.py中使用了from tasks import test_task,则不会报错,所以必须使用--hidden-import进行隐式导入,这个时候,这个时候需要添加

--hidden-import tasks,需要在task下面的__init__.py文件进行修改,将每一个脚本都导入后放到__all__里面。
#!usr/bin/env python
# -*- coding: utf-8 -*-
# Version:
# Last modified: 2020-07-03 14:51
from tasks import adan_day_sample_diff_task, adan_global_sample_diff_task, test_task
 
__all__ = [
    'adan_day_sample_diff_task',
    'adan_global_sample_diff_task',
    'test_task'
]

 

2.--add-data,我们的项目要用到conf下面的conf文件,所以需要将conf文件也打包,所以通过

--add-data conf:conf,将项目目录下的conf文件命名为conf打在包文件夹下面

 

3.我们打包是可以通过-F打包成单个可执行文件,也可以通过默认的-D打包成一个文件夹,目录为disk/xxx,但是因为utils4py导入配置时候的路径拼接问题,

使得单个文件不能读取到conf配置,我们打包为文件夹后,进入到文件夹执行,则可以正常的拼接conf目录然后读取。

posted @ 2020-09-09 15:24  G先生  阅读(216)  评论(0)    收藏  举报

:guocheng