一般情況下,當你想為你的Python開發環境選擇一個基礎鏡像時,大多數人都會選擇Alpine,為什麼?因為它太小了,僅僅隻有 5 MB 左右(對比 Ubuntu 系列鏡像接近 100 MB),但事實的真相是,我們選擇基礎鏡像并不是為了體驗一下Python文法而已,在此基礎上,我們需要調試和安裝各種擴充,可能會安裝很多三方依賴,甚至預設更多服務,在這種環境下,Alpine就并非是一個很好的選擇了,本次我們就來分别在Alpine和Ubuntu上來體驗一下安裝和編譯Python的差別。
首先分别拉取Alpine和Ubuntu的鏡像:
docker pull ubuntu:18.04
docker pull alpine
拉取完畢後,可以看到,體積上确實差距明顯:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 18.04 6526a1858e5d 2 weeks ago 64.2MB
alpine latest a24bb4013296 3 months ago 5.57MB
ubuntu占用64mb,而alpine僅僅5.57mb。
但是先别着急,假設我們的python應用需要做一些科學計算,并且将資料以圖形的方式展示出來,這時候就需要matplotlib和pandas這兩個庫的幫助了,先用ubuntu來安裝這倆個庫,編寫Dockerfile.ubuntu
FROM python:3.7-slim
RUN pip install --no-cache-dir matplotlib pandas
然後運作鏡像腳本:
docker build -f Dockerfile.ubuntu -t 'ubuntu-mat' .
可以看到,編譯好的鏡像從原先的60mb暴漲到了263mb。
liuyue:blog liuyue$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu-mat latest 401f0425ce63 About a minute ago 263MB
使用起來沒有什麼問題。
現在,我們來試試Alpine,看看速度和體積上有沒有比Ubuntu更具優勢
編寫Dockerfile.alpine:
FROM python:3.7-alpine
RUN pip install --no-cache-dir matplotlib pandas
編譯鏡像腳本
docker build -f Dockerfile.alpine -t 'alpine-mat' .
在編譯過程中,我們會發現報錯了:
liuyue:blog liuyue$ docker build -f Dockerfile.alpine -t 'alpine-mat' .
Sending build context to Docker daemon 112.1kB
Step 1/2 : FROM python:3.7-alpine
3.7-alpine: Pulling from library/python
df20fa9351a1: Pull complete
36b3adc4ff6f: Pull complete
4db9de03f499: Pull complete
cd38a04a61f4: Pull complete
6bbb0c43b470: Pull complete
Digest: sha256:d1375bf0b889822c603622dc137b24fb7064e6c1863de8cc4262b61901ce4390
Status: Downloaded newer image for python:3.7-alpine
---> 078114edb6be
Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas
---> Running in 6d3c44420e5c
Collecting matplotlib
Downloading matplotlib-3.3.1.tar.gz (38.8 MB)
ERROR: Command errored out with exit status 1:
command: /usr/local/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-40p0g06u/matplotlib/setup.py'"'"'; __file__='"'"'/tmp/pip-install-40p0g06u/matplotlib/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-pip-egg-info-zk64hzam
cwd: /tmp/pip-install-40p0g06u/matplotlib/
這是怎麼搞的?如果你仔細看上面基于Ubuntu的建構,你會發現它下載下傳三方庫的安裝包是matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl,這是一個預編譯的二進制安裝包。而Alpine則隻能下載下傳源代碼(matplotlib-3.1.2.tar.gz)的壓縮包,這就是Alpine的緻命問題:标準的Linux安裝包在Alpine Linux上根本無法使用。
大多數Linux發行版都使用GNU版本的标準C庫(glibc),幾乎所有基于C語言的腳本語言都需要這個庫,包括Python。但Alpine Linux使用的是musl,那些二進制安裝包是針對glibc編譯的,是以Alpine禁用了Linux安裝包支援。現在大多數Python包都在PyPI上包含了二進制安裝包,大大加快了安裝時間。但是如果你使用的是Alpine Linux,你需要編譯你使用的每一個Python包中的所有C源碼。
這也就意味着你需要自己弄清楚每一個系統庫的依賴性。事先編譯好需要的依賴,重新改寫Dockerfile.alpine:
FROM python:3.7-alpine
RUN apk --update add gcc build-base freetype-dev libpng-dev openblas-dev
RUN pip install --no-cache-dir matplotlib pandas
再次編譯:
docker build -f Dockerfile.alpine -t 'alpine-mat' .
經過了漫長的編譯安裝,大約半個小時左右,因為我們都知道從源碼編譯安裝要遠遠慢于通過安裝包安裝,此時檢視編譯好的鏡像:
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine-mat latest 601f0425ce63 About a minute ago 873MB
可以看到體積已經變成873mb了,Alpine最引以為傲的體積小輕便等特性也已經蕩然無存。
雖然從理論上講,Alpine使用的musl 核心與其他Linux發行版使用的glibc大多是相容的,但在實際操作中,這種差異可能會造成各種問題。而當這些問題真的發生時,想解決它們就沒那麼簡單了,比如說Alpine的線程預設堆棧容量較小,這會導緻Python崩潰,同時也會影響python應用的運作速度。
結語:在本地環境,如果你隻是想“玩一玩”,那麼基礎鏡像選擇Alpine無可厚非,但是如果你想要将你的python應用部署到生産環境時,特别是部署分布式系統需要多次編譯的場景下,選擇老牌的Ubuntu顯然更加的明智。