首頁 > 軟體

Python venv虛擬環境跨裝置遷移的實現

2022-06-13 14:02:35

背景

我們通常會遇到想簡易搬遷一臺裝置的python開發環境到另外一臺裝置的情況,但可能我們另一臺裝置本身的python環境包括系統環境是不可控的,這裡我遇到的是從centos7搬遷python開發環境到centos6,centos7自帶的python環境為2.7.5版本且一些依賴的檔案庫也是適配2.7.5版本及以上的,導致我們把環境搬遷到centos6的預設python2.6.6環境下時,產生了非常多的報錯,以下檔案內容記錄我遇到的報錯及解決方式,並提供一種較為完美的方式輕量級的解決環境移植問題。

python載入lib庫的順序

環境
製作python 虛擬環境裝置:

  • 系統版本:centos 7
  • python版本:python2.7.5

移植的目標裝置:

  • 系統版本:centos 6
  • python版本:python2.6.6

詳細操作

安裝virtualenv

[centos 7] # pip install virtualenv

建立python venv環境

[centos 7] # mkdir -p /opt/python_venv_test
[centos 7] # virtualenv -p /usr/bin/python2.7 --copies /opt/python_venv_test

--copies的引數意思為儘量不要為/opt/python_venv_test的檔案建立軟連結,如果不指定該引數,我們可以看到/opt/python_venv_test目錄有些檔案就是這樣的:

[centos 7] # ll /opt/python_venv_test/lib64/python2.7/*
lrwxrwxrwx 1 root root   32 Apr 26 20:24 /opt/python_venv_test/lib64/python2.7/lib-dynload -> /usr/lib64/python2.7/lib-dynload
lrwxrwxrwx 1 root root   26 Apr 26 20:24 /opt/python_venv_test/lib64/python2.7/os.py -> /usr/lib64/python2.7/os.py
lrwxrwxrwx 1 root root   27 Apr 26 20:24 /opt/python_venv_test/lib64/python2.7/os.pyc -> /usr/lib64/python2.7/os.pyc
-rw-r--r-- 1 root root 6978 Apr 26 20:24 /opt/python_venv_test/lib64/python2.7/site.py

/opt/python_venv_test/lib64/python2.7/config:
total 0
lrwxrwxrwx 1 root root 36 Apr 26 20:24 Makefile -> /usr/lib64/python2.7/config/Makefile

/opt/python_venv_test/lib64/python2.7/site-packages:
total 0

可以看到很多檔案直接是做了軟連結到原python 環境中的lib庫中的檔案,如果這個時候咱們把他打包移植到另外的裝置,這些檔案就全部都會被清空,所以一定要加--copies這個引數。加了--copies引數之後是這樣的:

[centos 7] # ll -d /opt/python_venv_test/lib64/python2.7/*
drwxr-xr-x 2 root root  4096 Apr 26 20:31 /opt/python_venv_test/lib64/python2.7/config
drwxr-xr-x 2 root root  4096 Apr 26 20:31 /opt/python_venv_test/lib64/python2.7/lib-dynload
-rw-r--r-- 1 root root 25769 Apr 26 20:31 /opt/python_venv_test/lib64/python2.7/os.py
-rw-r--r-- 1 root root 25557 Apr 26 20:31 /opt/python_venv_test/lib64/python2.7/os.pyc
drwxr-xr-x 2 root root  4096 Apr 26 20:31 /opt/python_venv_test/lib64/python2.7/site-packages
-rw-r--r-- 1 root root  6978 Apr 26 20:31 /opt/python_venv_test/lib64/python2.7/site.py

可以看到沒有軟連結了,那麼我們這個環境就是完全獨立的,這時候我們的python venv環境就已經生成了。

打包依賴的glibc庫

我們的python環境為python2.7.5版本,比較推薦的是使用glibc-2.17版本,下載地址為:glibc下載地址 ,裡面有各個版本的glibc檔案。

為什麼這裡要打包依賴的glibc庫呢?

我把虛擬環境目錄移植到目標的centos6機器上測試了一下,如果上面的這些庫檔案在新的裝置上沒有,那麼就會報錯,這裡我遇到的報錯就是:

/bin/python: error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file: No such file or directory

./bin/python: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /usr/lib64/libpython2.7.so.1.0)

這兩個報錯都是很有可能會命中的,提示我們缺少庫檔案,沒有辦法執行python binary。

那麼到底缺少哪些庫檔案呢?我們可以通過readelf -d /opt/python_venv_test/bin/python 命令檢查python binary的依賴檔案:

[centos 7] # readelf -d /opt/python_venv_test/bin/python

Dynamic section at offset 0xdd8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libpython2.7.so.1.0]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libutil.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x4005e0
 0x000000000000000d (FINI)               0x4007a4
 0x0000000000000019 (INIT_ARRAY)         0x600dc0
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600dc8
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x400298
 0x0000000000000005 (STRTAB)             0x400478
 0x0000000000000006 (SYMTAB)             0x4002f8
 0x000000000000000a (STRSZ)              218 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x601000
 0x0000000000000002 (PLTRELSZ)           48 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x4005b0
 0x0000000000000007 (RELA)               0x400598
 0x0000000000000008 (RELASZ)             24 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x400578
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x400552
 0x0000000000000000 (NULL)               0x0

我們可以在兩臺機器上檢視一下到底有沒有庫檔案,以及庫檔案產出自哪個rpm包:

目標 centos 6機器沒有找到libpython2.7.so.1.0庫檔案, 而/lib64/libc.so.6檔案版本較低,沒有達到glibc-2.14版本:

[centos 6] #  whereis libpython2.7.so.1.0
libpython2.7.so.1:

[centos 6] # whereis /lib64/libc.so.6
libc.so: /lib/libc.so.6 /lib64/libc.so.6 /usr/lib64/libc.so

[centos 6] # rpm -qf /lib64/libc.so.6
glibc-2.12-1.80.el6.x86_64

我們回到centos 7機器看看,可以看到有libpython2.7.so.1.0這個檔案,而且/usr/lib64/libc.so.6的版本也大於glibc-2.14版本。

[centos 7] # whereis libpython2.7.so.1.0
libpython2.7.so.1: /usr/lib64/libpython2.7.so.1.0

[centos 7] #  rpm -qf /usr/lib64/libpython2.7.so.1.0
python-libs-2.7.5-68.el7.x86_64

[centos 7] #  whereis /lib64/libc.so.6
libc.so: /usr/lib/libc.so.6 /usr/lib64/libc.so /usr/lib64/libc.so.6
[centos 7] #  rpm -qf /usr/lib64/libc.so.6
glibc-devel-2.17-260.el7_6.3.x86_64

好了, 我們迴歸正題,我們現在要解決上面這些問題。

首先,在centos 7機器上,下載及安裝glibc-2.17:

[centos 7] # wget http://ftp.gnu.org/gnu/glibc/glibc-2.17.tar.xz -O /tmp/
[centos 7] # cd /tmp/; tar xf glibc-2.17.tar.xz; cd /tmp/glibc-2.17
[centos 7] # mkdir build; cd build
[centos 7] # ../configure --prefix=/opt/python_venv_test/glibc-217
[centos 7] # make -j4
[centos 7] # make install

以上操作完成後,我們就可以在/opt/python_venv_test/glibc-217目錄下看到glibc-2.17的所有檔案都在裡面了。然後我們把發現的不屬於glibc但又需要的庫檔案libpython2.7.so.1.0移植進來glibc-2.17的lib目錄裡:

[centos 7] # cp -ar /usr/lib64/libpython2.7.so.1.0 /opt/python_venv_test/glibc-217/lib/

除了glibc這個基礎庫,還需要python本身的基礎庫,是執行binary python時需要載入的模組,比如os等,這個庫一般是在系統的/usr/lib64/python2.7/目錄,我們把它移植到我們的虛擬環境目錄下(這個地方如果我們原生的/usr/lib64/python2.7 過大,可以考慮起一個可執行乾淨的python環境centos 7虛擬機器器,把它的/usr/lib64/python2.7拷貝過來,這樣就能保證它是最小的包量):

[centos 7] # rm -rf /opt/python_venv_test/lib64/python2.7

[centos 7] # cp -ar /usr/lib64/python2.7 /opt/python_venv_test/lib64/python2.7

如果上面這個操作你沒有做,可能就會遇到這樣的報錯:

[centos 7] # ./bin/python
Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
ImportError: No module named site

當你把$PYTHONHOME設定完成後,或者會遇到這樣的報錯(總之就是找不到基礎庫檔案):

[centos 7] # ./bin/python
Traceback (most recent call last):
  File "/home/zhaoqiang09/bsc_python_venv.b/lib64/python2.7/site.py", line 190, in <module>
    main()
  File "/home/zhaoqiang09/bsc_python_venv.b/lib64/python2.7/site.py", line 18, in main
    rewrite_standard_library_sys_path()
  File "/home/zhaoqiang09/bsc_python_venv.b/lib64/python2.7/site.py", line 97, in rewrite_standard_library_sys_path
    import os
ImportError: No module named os

改寫環境載入檔案/opt/python_venv_test/pyvenv.cfg,這樣我們就不需要依賴移植後目標centos 6系統本身的lib庫了,以免造成版本衝突:

home = /opt/python_venv_test/
implementation = CPython
version_info = 2.7.5.final.0
virtualenv = 20.13.0
include-system-site-packages = false
base-prefix = /opt/python_venv_test/
base-exec-prefix = /usr
base-executable = /opt/python_venv_test/bin/python

我們還得改寫一下/opt/python_venv_test/bin/activate 檔案,這個檔案是我們在移植後目標centos 6系統載入python虛擬環境的入口,我們在這裡面加一條對LD_LIBRARY_PATH環境變數的區域性重寫(紅色字型部分,第一條是為了退出環境變數時,下掉alias的繫結,第二條是開始載入activate環境時對LD_LIBRARY_PATH環境變數的區域性重寫):·

··········

deactivate () {
    unalias python >/dev/null 2>&1
    unset -f pydoc >/dev/null 2>&1 || true

·············

# Make sure to unalias pydoc if it's already there
alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true
alias python="${VIRTUAL_ENV}/glibc-217/lib/ld-2.17.so --library-path ${VIRTUAL_ENV}/glibc-217/lib:/lib64 ${VIRTUAL_ENV}/bin/python"

·················

然後,我們就可以打包了:

[centos 7] # cd /opt/

[centos 7] # tar -czf python_venv_test.tar.gz python_venv_test

移植包到目標Centos 6系統上執行

首先在centos 7 系統上我們scp壓縮包到目標機器

[centos 7] # cd /opt/

[centos 7] # scp python_venv_test.tar.gz root@centos_6:/opt/python_venv_test.tar.gz

然後我們在centos 6上解壓壓縮包:

[centos 6] # cd /opt/

[centos 6] # tar xf python_venv_test.tar.gz

這時候就可以虛擬環境目錄執行python了

[centos 6] # cd /opt/python_venv_test

[centos 6] # souce bin/bin/activate

[centos 6] # python
Python 2.7.5 (default, Apr 11 2018, 07:36:10)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

我們要擴充一些第三方擴充套件包

回到centos7機器

搬遷新增的flask、requests、psutil等第三方庫

我們還是先回到我們的虛擬環境目錄/opt/python_venv_test下面,先使用bin目錄下的pip安裝flask

# ./bin/pip install flask requests psutil

打壓縮包,並拷貝壓縮包到centos 6目標系統上

cd /opt/

tar -czf python_venv_test.tar.gz python_venv_test/

[centos 7] # scp python_venv_test.tar.gz root@centos_6:/opt/python_venv_test.tar.gz

回到centos6 機器上,解壓縮包,並執行python看看有沒有什麼問題

cd /opt/python_venv_test/

# source bin/activate

# python
Python 2.7.5 (default, Apr 11 2018, 07:36:10)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import flask
bash: /opt/bsc_python_venv/glibc-217/lib/libssl.so.10: No such file or directory

報錯顯示缺少libssl.so.10檔案,我們看看這個檔案分別在兩臺測試機的哪裡:

centos 6機器情況:

# whereis libssl.so.10
ld-2.17:

centos 7機器情況,並且要確定的是它不是一個軟連結:

# whereis libssl.so.10
libssl.so: /usr/lib64/libssl.so.10 /usr/lib64/libssl.so

# ll -d /usr/lib64/libssl.so.10
lrwxrwxrwx 1 root root 16 Jan 21  2021 /usr/lib64/libssl.so.10 -> libssl.so.1.0.2k

# ll -d /usr/lib64/libssl.so.1.0.2k
-rwxr-xr-x 1 root root 470360 Mar 12  2019 /usr/lib64/libssl.so.1.0.2k

我們再把centos7 的 /usr/lib64/libssl.so.1.0.2k 這個檔案給放到虛擬環境glibc-217目錄,然後做成壓縮包,傳到centos6機器上,再來試試

# cp -ar /usr/lib64/libssl.so.1.0.2k /opt/python_venv_test/glibc-217/lib/libssl.so.10

# cd /opt/

# tar -czf python_venv_test.tar.gz python_venv_test/

[centos 7] # scp python_venv_test.tar.gz root@centos_6:/opt/python_venv_test.tar.gz

我們再在centos 6的機器上把原來的檔案刪除,解壓縮,再試試import flask、requests、psutil這三個庫檔案,就不會再報剛才那個錯了:

cd /opt/python_venv_test/

# source bin/activate

# python
Python 2.7.5 (default, Apr 11 2018, 07:36:10)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import flask
>>> import requests
>>> import psutil

結束! 

本文設計的內容較多,如果有一些細節不太懂的地方,建議自行搜尋,再回來反覆檢視本檔案。

到此這篇關於Python venv虛擬環境跨裝置遷移的實現的文章就介紹到這了,更多相關Python venv遷移內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com