8

如何打包一个不依赖 python 的 supervisor 的包去离线部署

 1 year ago
source link: https://zhangguanzhang.github.io/2022/11/03/pyinstaller-supervisor/#/%E5%8F%82%E8%80%83
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

近期的一次信创适配,需要 supervisor 离线安装

信创涉密设备介绍

无网络的信创涉密设备适配,要求列举下就是:

  1. 光盘拷贝 rpm 包上去,实际可以绕过去,但是不重要
  2. 不能使用容器,必须全部 rpm 包方式部署业务,有专门的图形软件添加和安装 rpm 包,意味着你搭建本地源啥的都用不了,只能一个个 rpm 包添加到界面安装
  3. 系统的 rpm 包不能升级和降级,这个代表着你拷贝了全套的 rpm 包,很可能都用不了,例如自带的某个 so 版本和你的业务的冲突或者不匹配
  4. 只有系统里注册的 rpm 包里的文件和脚本才具备执行能力,比如你在设备上 vi test.sh 写 echo 123, 然后 bash test.sh 都无法执行
  5. 自己的要求: 对于业务进程以及方便现场的实施查看我们的所有业务服务和支持日志轮转。

业务侧是业务研发处理,大部分都是 golang 开发的(都是静态编译的),我们需要做的就是进程纳管,这方面的轮子简单对比下,大概需求列为表格就是下面的需求

选型 安装过程 支持 env file 支持给进程 logfile rotate 组(或者label)纳管 备注
systemd 不老的系统都自带 有通配符,但是没组的概念
supervisor-py版本 需要pip或者包管理安装 x env file 的支持 hack 就行
supervisor-go版本 单独二进制 x
pm2 还得安装node x x 除了安装过程,貌似我都不了解

pm2 从安装就不考虑了,supervisor-go 版本感觉开发者都不看 issue,而且它的 master 提交记录修复的 bug 描述看都是很常见的场景,出现这个问题我认为是单元测试覆盖不全面以及用户面小,现阶段和生产上我不会考虑它。而 systemd 不支持组纳管,初步是选用 python 版本的 supervisor,但是 supervisor 安装无非就两种,一种是 pip 安装,一种是全套 rpm 包安装:

  • python-meld3 >= 0.6.5
  • python2-meld3
  • supervisor

涉密设备系统上不一定有 pip,使用 rpm 的方式的话,在 arm64 的麒麟里下的上面三个 rpm 可能在另一个 arm64 的 uos 上就无法使用了,并且实际上麒麟也有好几个类型,穷举的话可以,但是同一个系统还可能在迭代升级,之前的 rpm 包里的某个依赖可能就不满足了,再穷举每个os的所有版本去维护的话,难度就非常痛苦。

之前组长的解决办法就是起容器安装 supervisor,然后把 site-package 目录打包,事实证明这个确实可以,但是部署的客户数量少,如果碰到部分客户已经安装了某个 site-package 的依赖,我们这个 supervisor 的包就安装不上去了。

这次就想着如何把 supervisor 打包成一个没有任何依赖的 rpm 包,想着是利用 pyinstaller,搜了下这块没人做过,现在是已经搞完了验证可行了。

pyinstaller 打包

pyinstaller 是一个把 py 脚本打包成独立的可执行文件的工具,推荐 pip 安装 pyinstaller,我的思路是容器里安装 supervisor 后打包

docker run --rm -ti -w /test --entrypoint bash centos:7
# python3 会自带 python3-pip , binutils 是 pyinstaller 打包的时候依赖
yum install -y python3 binutils gcc zlib-devel which

pip3 install wheel
pip3 install pyinstaller
pip3 install supervisor

过程和遇到的错误

pyinstaller `which supervisord`

打包完后:

[root@6ec6694c6688 test]# ls -l
total 4
drwxr-xr-x 3 root root 25 Nov 3 10:57 build
drwxr-xr-x 3 root root 25 Nov 3 10:57 dist
-rw-r--r-- 1 root root 1150 Nov 3 10:57 supervisord.spec
[root@6ec6694c6688 test]# ls -l dist/
total 4
drwxr-xr-x 3 root root 4096 Nov 3 10:57 supervisord
[root@6ec6694c6688 test]# ls -l dist/supervisord/
total 11580
-rw-r--r-- 1 root root 786953 Nov 3 10:57 base_library.zip
drwxr-xr-x 2 root root 4096 Nov 3 10:57 lib-dynload
-rwxr-xr-x 1 root root 68192 Nov 20 2015 libbz2.so.1
-rwxr-xr-x 1 root root 15856 Sep 30 2020 libcom_err.so.2
-rwxr-xr-x 1 root root 2521144 Aug 9 2019 libcrypto.so.10
-rwxr-xr-x 1 root root 173320 Sep 30 2020 libexpat.so.1
-rwxr-xr-x 1 root root 32328 Apr 1 2020 libffi.so.6
-rwxr-xr-x 1 root root 320720 Sep 30 2020 libgssapi_krb5.so.2
-rwxr-xr-x 1 root root 210784 Sep 30 2020 libk5crypto.so.3
-rwxr-xr-x 1 root root 15688 Jun 10 2014 libkeyutils.so.1
-rwxr-xr-x 1 root root 967840 Sep 30 2020 libkrb5.so.3
-rwxr-xr-x 1 root root 67104 Sep 30 2020 libkrb5support.so.0
-rwxr-xr-x 1 root root 157424 Nov 5 2016 liblzma.so.5
-rwxr-xr-x 1 root root 402384 Aug 2 2017 libpcre.so.1
-rwxr-xr-x 1 root root 3144192 Nov 16 2020 libpython3.6m.so.1.0
-rwxr-xr-x 1 root root 285136 Aug 8 2019 libreadline.so.6
-rwxr-xr-x 1 root root 155744 Apr 1 2020 libselinux.so.1
-rwxr-xr-x 1 root root 470376 Aug 9 2019 libssl.so.10
-rwxr-xr-x 1 root root 174576 Sep 6 2017 libtinfo.so.5
-rwxr-xr-x 1 root root 90160 May 12 14:58 libz.so.1
-rwxr-xr-x 1 root root 1749176 Nov 3 10:57 supervisord

然后执行了下,发现报错:

$ ./supervisord --version
Traceback (most recent call last):
File "supervisord", line 7, in <module>
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "supervisor/supervisord.py", line 41, in <module>
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "PyInstaller/loader/pyimod03_importers.py", line 495, in exec_module
File "supervisor/options.py", line 63, in <module>
File "supervisor/options.py", line 61, in _read_version_txt
FileNotFoundError: [Errno 2] No such file or directory: '/root/supervisor/dist/supervisord/supervisor/version.txt'
[18114] Failed to execute script 'supervisord' due to unhandled exception!

解决办法就是创建文件 supervisor/version.txt ,但是实际上可以 hack 下:

ver=$(find /usr/ -type f -name 'version.txt' -path '*/supervisor/*' -exec cat {} \; )
op_file=$(find /usr/ -type f -name 'options.py' -path '*/supervisor/*')
# 设置 VERSION
sed -ri '/^VERSION\s+=\s+/s#= .+#= "'"${ver}"'"#' $op_file

# 打包
pyinstaller `which supervisord`

然后发现移动路径会无法执行:

$ cp dist/supervisord/supervisord .
$ ./supervisord --help
[1648] Error loading Python lib '/test/libpython3.6m.so.1.0': dlopen: /test/libpython3.6m.so.1.0: cannot open shared object file: No such file or directory

看样子是 dlopen 的形式打开相对目录的 so,但是我想打包成一个文件,看了下不支持静态编译

pyinstaller --help |& grep -P 'link|static'

最后搜索了下,加上选项 -F, --onefile Create a one-file bundled executable. 打包成一个文件:

rm -rf supervisord* dist/ build

pyinstaller --onefile `which supervisord`

然后发现可以执行,但是在 arm64 下打包的文件执行会报错 No moudle named supervisor.xxx,需要加 -p DIR, --paths DIR A path to search for imports (like using PYTHONPATH).

pyinstaller --onefile -p /usr/local/lib/python3.7/site-packages `which supervisord`

而其实还有其他文件需要打包:

# 可以 rpm 包安装下 supervisor ,看下哪些文件需要
$ rpm -ql supervisor | grep -Pv '\.py|/site-packages/|/share/'
/etc/logrotate.d/supervisor
/etc/supervisord.conf
/etc/supervisord.d
/etc/tmpfiles.d/supervisor.conf
/usr/bin/echo_supervisord_conf
/usr/bin/pidproxy
/usr/bin/supervisorctl
/usr/bin/supervisord
/usr/lib/systemd/system/supervisord.service
/var/log/supervisor
/var/run/supervisor
docker run --rm -ti -w /test --entrypoint bash centos:7
# python3 会自带 python3-pip , binutils 是 pyinstaller 打包的时候依赖
yum install -y python3 binutils gcc zlib-devel which

pip3 install wheel
pip3 install pyinstaller
pip3 install supervisor

ver=$(find /usr/ -type f -name 'version.txt' -path '*/supervisor/*' -exec cat {} \; )
op_file=$(find /usr/ -type f -name 'options.py' -path '*/supervisor/*')
# 设置 VERSION
sed -ri '/^VERSION\s+=\s+/s#= .+#= "'"${ver}"'"#' $op_file

dir=$(find /usr -type d -name supervisor -path '*/site-packages/*' -exec dirname {} \;)
pyinstaller --onefile -p $dir `which pidproxy`
pyinstaller --onefile -p $dir `which supervisord`
pyinstaller --onefile -p $dir `which supervisorctl`

制作 rpm 包

其实这个步骤也可以用于容器内无 python 添加 supervisor
相关步骤和文件存放在我 github 上
https://github.com/zhangguanzhang/compile-and-packages


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK