用 Docker + pyinstaller 建置執行檔

Chih-Cheng Liang
6 min readMar 7, 2016

--

build 完就像這個清朝電話一樣,驚呆了!

前情提要:幫公司開發 flask 程式,需要佈署到有其他系統在運行的主機。

佈署的問題

這台主機是 RHEL(Red Hat Enterprise Linux 6.6)作業系統,並且有其他系統在運行,無法連外網。

程式是用 python 2.7 開發,用 pyenv 讓同伴們開發環境一致。

在開始佈署前創立了一個新使用者,以免使用 root 做出奇怪的事情。

怎麼讓寫好的程式順利在主機運行呢?以下列出嘗試過的思路:

用 .py 檔直接在佈署機上執行原始碼:

  • 是否主機有 python? 我們用 python 2.7 開發,是否和主機 python 版本相符? → 主機是 python 2.6, python 27要另外 build,沒嘗試
  • 怎麼安裝 python 相依套件? → 主機無法連外網,把套件 scp 到主機好像也不通,因為套件可能要 build。感覺工程很大,最後沒嘗試。

使用 docker 在佈署機上起一個 container:

用 binary 檔形式在佈署機上執行:

  • 在自己筆電上輕易用 pyinstaller build 出 binary 檔 →然而在執行時佈署機抱怨底下 glibc 缺乏某個版本
  • 經過一段痛苦的過程,嘗試在佈署機上 build 出 glibc → 執行時佈署機抱怨底下 glibc 缺乏另一個版本
  • 棄用 bing 和百度, google 查到某個 stackoverflow 的指示:別試圖在佈署機上 build 出 glibc ,因為 build 完之後必須手動指定哪些底層函式要相依哪個 glibc﹍。文中建議的做法是要在和佈署機有一樣 glibc 的環境build 出 binary 檔。(頭髮掉了很多根﹍)
  • 怎樣取得和佈署機一樣環境?可以使用近年成熟的虛擬化機器技術。由於 RHEL 是商業版,不能免費使用和下載,所以找相對應的 centos 映像檔。最後使用 Docker 建置出符合佈署機 glibc 環境的 binary 檔,才終於成功佈署。

這是最後的 Dockerfile:

FROM centos:6.6RUN yum -y update; yum clean all
RUN yum -y install epel-release centos-release-SCL; yum clean all
RUN yum -y groupinstall “Development tools”
RUN yum -y install tar wget python-devel gcc libxml2 libxml2-devel libxslt libxslt-devel \
zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel \
tk-devel gdbm-devel db4-devel libpcap-devel xz-devel; yum clean all
RUN wget http://python.org/ftp/python/2.7.6/Python-2.7.6.tar.xz; \
tar xf Python-2.7.6.tar.xz;
RUN cd Python-2.7.6; \
./configure — prefix=/usr/local — enable-unicode=ucs4 — enable-shared LDFLAGS=”-Wl,-rpath /usr/local/lib”; \
make && make altinstall; cd
RUN wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py; \
python2.7 ez_setup.py; \
easy_install-2.7 pip ; \
pip2.7 install pyinstaller packaging lxml
ADD /web/requirements.txt /RUN pip2.7 install -r /requirements.txtCMD cd /root/web/; \
pyinstaller -F -p /usr/local/lib/python2.7/site-packages main.spec

這篇 Dockerfile 如此複雜是因為在 centos:6.6 裡面又 build 了 python (感謝這篇文章),另外很多系統的 dependency 是 python lxml 套件需要的(感謝這篇 SO)。

執行 docker build 時會產生一個 image ,上面有系統相依的套件與 python 需要的套件、 pyinstaller需要的套件。 接著執行 docker run ,volumn /root/web/ 即可產生執行檔。

docker build -t sap .docker run — rm -v $PWD/web:/root/web/ sap

我都會在自己一年份免費的 AWS ec2 instance 上建置 docker 映像檔,在下載系統套件與 python 套件時會特別快。建置完的執行檔再 scp 複製回筆電裡。

最後 pyinstaller 可能會抱怨沒有 six, packaging, packaging.version, packaging.specifiers ,灌好一個才抱怨下一個,而且最後兩個還沒辦法裝。別去動 hidden import ,解法如 這篇SO 指引,在主程式裡把 pyinstaller 抱怨的套件全引了重 build。

import six, packaging, packaging.version, packaging.specifiers

參考資料

佈署的問題:

上一篇:

--

--