文章简介:如何配置一个跨 linux 发行版解压即用的 python 运行环境
场景分析
我们所处的工作环境偏 ToB 离线安装升级。python 在运维工具上有成熟的 ansible 等运维工具。而在实际生产使用过程中,需要适配不同的 linux 发行版。在这过程中,我们需要一个比较
- 稳定开箱即用的 python 运行环境
- 安装简单 (最好解压即可运行)
- 第三方包安装尽可能简单
我们所面向的问题:
- linux 发行版众多,一些为 python2,一些为 python3;代码可以兼容 python2 和 python3,但部分依赖不兼容 python2 和 python3;并且 python2 已经 EOL,不期望为它花费力气做适配
- linux 发行版 glibc 版本各不相同,高版本 glibc 下编译 python 放到低版本 glibc 运行环境下会 glibc 版本检查失败
解决办法
docker
第一个想到的是 docker. 基于 docker 的容器 python 运行环境,docker image 中包含所有的 python 运行时依赖,包括 python 代码及动态库(包括 glibc)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
FROM python:3.9-slim as base
FROM base as builder
RUN mkdir /install
WORKDIR /install
COPY requirements.txt /requirements.txt
RUN sed -E -i -e 's/deb.debian.org/mirrors.aliyun.com/g' -e 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
RUN apt update --allow-insecure-repositories \
&& apt install -y gcc python3.9-dev libffi-dev -y
RUN pip install --no-cache-dir --trusted-host mirrors.aliyun.com --prefix=/install -r /requirements.txt
# FROM gcr.io/distroless/python3
FROM base
COPY --from=builder /install /usr/local
WORKDIR /app
COPY hello.py /app/
CMD ["hello.py", "/etc"]
|
但这种方式也有缺陷,docker 采用 namespace 的方式将 / 与 宿主机的 / 隔离开,容器如果期望操作宿主机的 systemd/crond 会比较复杂,可能会需要 docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
。另外一种方式是使用 ssh 免密操作宿主机 (ssh root@hostip sh
), 同样操作复杂。
portable python
想法来自于 golang, 所有的依赖都以静态编译的方式编译到一个文件中,单文件复制到其他机器即可运行,之前在静态编译 chrony, 基于这个想法,调研下 python 有没有类似的能力.
将如下命令放到 Dockerfile 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# Dockerfile
FROM ubuntu:22.04
RUN sed -E -i -e 's/(archive|ports).ubuntu.com/mirrors.aliyun.com/g' -e '/security.ubuntu.com/d' /etc/apt/sources.list
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates zstd wget && \
rm -rf /var/lib/apt/lists/* && \
update-ca-certificates
WORKDIR /opt/
ARG filename=cpython-3.10.13+20230826-x86_64-unknown-linux-gnu-debug-full
RUN wget https://github.com/indygreg/python-build-standalone/releases/download/20230826/${filename}.tar.zst \
&& unzstd ${filename}.tar.zst \
&& mkdir -p portable && tar -xvf ${filename}.tar -C portable \
&& rm -rf ${filename}.tar.zst ${filename}.tar
ENV PYTHON_HOME=/opt/portable/python/
RUN ${PYTHON_HOME}/install/bin/pip3 install --debug --verbose requests psycopg2-binary ansible -i https://pypi.tuna.tsinghua.edu.cn/simple
|
生成的 python 包的命令
1
2
3
|
docker build -t portable-python:dev --build-arg HTTPS_PROXY="http://host.docker.internal:7890" .
docker run --rm -it -v "$(pwd):/host" --workdir=/opt/portable portable-python:dev tar -cvf /host/portable-python-3.10.13.tar python
|
当前已经测试过的操作系统:
- centos:7
- ubuntu:20.04
- ubuntu:22.04
- rockylinux:8-minimal
- rockylinux:9-minimal
- openeuler:20.03-lts
- openeuler:22.03-lts
测试过的 pip 包:
- requests
- psycopg2-binary
- ansible
总结
TODO: 这里缺一个总结
处理问题的思路决定了做事的效率.
参考