當前位置: 妍妍網 > 碼農

為Python套用選擇最好的Docker映像

2024-03-30碼農

在使用 Python 的早些年,為了解決 Python 包的隔離與管理 virtualenvwrapper 就成為我的工具箱中重要的一員。後來,隨著 Python 3 的普及,virtualenvwrapper 逐漸被 venv 所替換。畢竟 venv 是 Python 3 的標配,優點是顯而易見的。而這幾年,套用場景的的復雜性越來與高,無論是開發還是部署都需要設定復雜的環境。例如使用 redis 實作訊息佇列,用 Psycopg 完成對於 PostgreSQL 資料庫的存取等等。隨之而來 Docker 就變成了程式設計師必不可少的常備工具。為了掌握如何將我的 Python 套用與 Docker 結合起來,就要學習他人的經驗分享。於是一次又一次地看到了下面這樣的 Dockerfile 例子:

相比起來,我不熟悉 Alpine 這個 Linux 發行版本。我很困惑這個版本難道比其它哪些老字號的 Linux 發行版更適合 Docker 的環境嗎?至於我的 Python 套用,究竟選擇哪一個 Docker 基礎映像更好呢?

對於 Docker 基礎映像的要求

為我的 Python 套用構建一個 Docker 映像並不是要從零開始,而是從現有的 Linux 基礎映像開始構建。這些基礎映像除了提到過的 Alpine 以外 還有我更熟悉的 Ubuntu、Centos 、Debian 等等。在決定選擇哪一個之前,我們需要回答的一個問題就是:

  • 我們究竟對於這個 Docker 基礎映像有哪些要求?

  • 這些要求既要滿足通常意義上我們對作業系統的要求,也要滿足對於 Python 套用的特殊的需求。

  • 歸納起來,我們的需求應當包括這幾點:

  • 穩定性: 要求今天的構建與以後的構建有相同的基本庫、目錄結構和基礎結構。否則我們的 Python 應用程式將將有可能存在不確定的隱患。

  • 安全更新: 需要基礎映像得到良好維護,以便及時獲取基本作業系統的安全更新

  • 最新的依賴關系: 除非我們的套用僅僅是一個簡單的 Python 程式,否則就不得不依賴作業系統所提供 的庫和應用程式(例如:GCC 編譯器、OpenSSL 庫等)。我們需要這些套用和庫要足夠新,否則就會有各種安全性的問題或者功能性的不足。

  • 豐富的庫資源: 對於某些套用,可能需要安裝一些不太流行的庫(例如 lxml 等)。這就需要我們選擇的基本映像提供豐富的庫資源。

  • 最新的 Python 版本: 雖然可以透過自行安裝 Python 來解決,但是擁有最新的 Python 的版本無疑可以節省我們的時間、精力。

  • 小型的 Docker 映像: 在所有條件都相同的情況下,擁有尺寸較小的 Docker 映像無疑更勝一籌。無論是映像的構建還是占用儲存空間的開銷,更小的尺寸一定更有優勢。

  • 考慮到套用部署在產環境的需要,我們所選擇的 Docker 映像還應當具備長期支持(Long-term support, LTS) 的承諾。

    長期支持 (英語:Long-term support,縮寫:LTS)是一種軟體的產品生命周期政策,特別是開源軟體,它增加了軟體開發過程及軟體版本周期的可靠度。長期支持延長了軟體維護的周期;它也改變了軟體更新(修補程式)的型別及頻率以降低風險、費用及軟體部署的中斷時間,同時提升了軟體的可靠性。但這並不必然包含技術支持。 – 維基百科

    Linux 映像版本的選擇

    圍繞著上述的需求,我們很容易就會找到一批候選的版本。乍看起來,這些基礎映像應該能夠滿足我們的需要。接下來我們需要仔細甄別其具體的差異,以找出最適合我們的版本,尤其是適合我們的 Python 套用。

    選項一:傳統的 Linux 分發版本 – Ubuntu TLS、CentOS 以及 Debian

    這三個 Linux 分發版本歷史久遠(Debian 早在 1993 年就已出現),名氣很大,在 Linux 伺服器市場上擁有廣泛的使用者。

  • Ubuntu 18.04(Docker 映像的名字 ubuntu:18.04)釋出於 2018 年 4 月,由於這是 Canonical 公司的長期支持版本(LTS),意味著該版本的使用者在 2023 年之前都將獲得安全更新。

  • CentOS 8( Docker 映像的名字 centos:8 )於 2019 年釋出,將在 2024 年前進行全面更新,並在 2029 年前提供維護更新。

  • Debian 10(Docker 映像的名字 debian:buster)釋出於 2019 年 7 月,承諾支持到 2024 年。

  • 需要註意的是這些映像預安裝的 Python 有可能不是最新的版本。例如 Ubuntu 18.04 預安裝的是 Python 3.6.7,而 Python 3 的最新穩定版本已經升級為 Python 3.8.1。因此我們必須在構建 Docker 映像的時候去完成 Python 的安裝或者升級。在一些特定的 Linux 分發版本中,我們甚至需要自行透過編譯 Python 源碼的方式來獲得最新版本的 Python。例如在 CentOS 8 中,就需要用這個辦法來安裝 Python3.8。至於具體的辦法,可以參考在「Python 3.8 已經來了,你準備好了嗎?」一文中的介紹。

    https://amazonaws-china.com/cn/blogs/china/python-3-8-is-here-are-you-ready/

    選項二:Docker 官方的 Python 映像

    這個 Docker 映像由 Docker 官方提供。該版本的最大特點就是預裝了 Python,並且提供多個不同 Python 版本的選項,例如 Python 3.7、Python 3.8、Python 3.9。需要註意的是,這個版本提供了多個不同的變體,如果搞不清楚這一點很容易在使用中遇到難以預料的問題。這些差異很大的變體包括了:

  • Alpine Linux, 關於這個版本,後面會有專門的討論

  • Debian Buster, 這是基於 Debian 的一個分支版本,是基於 Debian 的標準映像的 Layer 來構建的。特點是基礎庫很完整,缺點是尺寸較大,磁盤的利用率較低。

  • Debian Buster slim,這個版本是針對 Debian Buster 的「瘦身」後的版本。尺寸小,磁盤利用率高是其優點。但是,它缺少通用的包,可能會導致對部份的套用支持不好。

  • 選項三:雲端運算上的 Linux 映像 – Amazon Linux 2

    今天當我們談到 Docker 在生產環境中的部署的時候,不能缺少的一個話題就是雲端運算。相比較起來,雲端運算上 Linux 以及 Docker 的使用與部署與我們熟悉的傳統方式有一些區別。例如安全性的要求、與雲端運算平台的整合以及管理工具等。於是雲端運算的平台 Linux 分發版本以及 Docker 映像也就應需而生。AWS 就提供了 Amazon Linux 的兩個版本:Amazon Linux 2 和 Amazon Linux。其中,Amazon Linux 2 是 Amazon Linux 的新一代的 Linux 伺服器作業系統版本,這也是 AWS 官方推薦使用的版本。至於選擇 Amazon Linux 2 的的理由,簡單來說這是一個由 Amazon 提供長期支持的(LTS)、進行了針對性效能最佳化的、強調安全的、免費的 Linux 分發版本以及 Docker 的映像。

    選項四:Alpine 映像

    很難想象,Alpine 這個 Linux 發行版已經有了 14 年的歷史。但廣為大家所熟知還是要拜 Docker 的流行所賜。最初,Alpine Linux 是 LEAF 計畫(Linux Embedded Appliance Framework Project)的一個分支。LEAF 計畫的設計的目標是開發出一個可以放在單個軟碟上的 Linux 發行版,而後繼的 Alpine 在此基礎之上附加了一些更常用的軟體包例如 Squid、Samba 等等。因此,Alpine 的典型特征就是「 尺寸小 」!當然為達成這個目標就要做出妥協。為減少尺寸就不得不放棄我們熟悉的 Linux 環境中最常見的 GNU C Library(glibc)

    https://en.wikipedia.org/wiki/GNU_C_Library

    取而代之的是一個迷你版的 C 標準庫 musl

    http://musl.libc.org/

    此外,我們常用的 Linux 工具(例如 grep、ls、cp 等等)則采用了 BusyBox 以求進一步壓縮空間。這種努力的結果是非常有成效的,alpine:latest 映像的尺寸只有區區的 5.59MB ,而與之相對的 ubuntu:18.04 的映像的尺寸卻高達 64.2MB。簡單計算下來,Alpine 的磁盤空間的需求只是 Ubuntu 18.04 的 8.7% !

    對比 – Docker 基礎映像的尺寸

    想象一下,在真實的生產環境中我們部署的 Docker 例項的數量可能成百、上千。考慮到數量的因素,Docker 映像的尺寸就應當是我們考量的一個重要依據。此外啟動一個 Docker 例項我們往往需要在盡可能短的時間內完成,Docker 映像的尺寸無疑也是一個關鍵因素。那麽,我們就將上面列舉的 Docker 映像在尺寸方面做一個對比 :

    測試日期:2020 年 2 月 28 日
    Linux 分發版本 映像名稱 拉取命令 尺寸
    Ubuntu ubuntu:18.04 docker pull ubuntu:18.04 64.2MB
    Alphine alpine:latest docker pull alpine:latest 5.59MB
    Debian debian:buster docker pull amd64/debian:buster 114MB
    CentOS centos:8 docker pull centos:8 237MB
    Amazon Linux 2 amazonlinux:latest docker pull amazonlinux:latest 163MB
    Debian python:3.7 docker pull python:3.7 919MB
    Alphine python:3.7-slim docker pull python:3.7-slim 179MB

    好了,在這一項的測試中名次如下 :

    python:3.7 > centos:8 > python:3.7-slim > amazonlinux:latest > debian:buster > ubuntu:18.04 > alpine:latest

    如果從這個排名來看 centos 8 無疑表現的差強人意,故被淘汰。從數位來看似乎 alpine 是最好的選擇。且慢,我們再來進行下一項測試- 構建時間。

    對比 – Docker 映像的構建時間

    在大多數的時間裏,我們所使用的 Docker 映像都需要從基礎映像開始構建。例如下面的這個 Dockerfile 就用來構建一個 Flask 的套用

    # Dockerfile-flask
    # Simply inherit the Python 3 image.
    FROM python:3
    # Set an environment variable
    ENV APP /app
    # Create the directory
    RUN mkdir $APP
    WORKDIR $APP
    # Expose the port uWSGI will listen on
    EXPOSE 5000
    # Copy the requirements file in order to install
    # Python dependencies
    COPY requirements.txt .
    # Install Python dependencies
    RUN pip install -r requirements.txt
    # We copy the rest of the codebase into the image
    COPY . .
    # Finally, we run uWSGI with the ini file





    這種模式帶來的問題就是我們不得不考慮構建帶來的額外的開銷。尤其在一個復雜的計畫中,我們需要構建的則不僅僅上面這樣簡單的場景,復雜的套用往往需要一個較長的構建時間。如果構建時間的開銷比較大或者比較復雜,則必然增加了額外的管理、部署以及監控的成本。我的這個測試場景比較簡單,只是安裝 Python3,以及比較常見的 python 包 numpy、matplotlib 和 pandas。看看每一種 Docker 基礎映像的構建所需的時間是多少。

    1、 ubuntu:18 , 構建時間 1 分 31.044 秒

    FROM ubuntu:18.04
    RUN apt-get update -y && \
    apt-get install -y python3.7 python3-pip python3.7-dev
    RUN pip3 install --upgrade pip
    RUN pip3 install --no-cache-dir numpy matplotlib pandas

    2、 amazonlinux:2 , 構建時間 30.898 秒

    FROM amazonlinux:latest
    RUN yum update -y && \
    yum install -y python3 python3-devel
    RUN pip3 install --no-cache-dir numpy matplotlib pandas

    3、 debian:latest , 構建時間 52.237 秒

    FROM debian:buster
    RUN apt-get update -y && \
    apt-get install -y python3 python3-pip python3-dev
    RUN pip3 install --no-cache-dir numpy matplotlib pandas

    4、 python:latest ,構建時間 35.752 秒

    FROM python:3.7
    RUN apt-get update -y && \
    apt-get install -y python3-pip
    RUN pip3 install pip --upgrade
    RUN pip3 install --no-cache-dir numpy matplotlib pandas

    5、 python:3.7-slim , 構建時間 53.475 秒

    FROM python:3.7
    RUN apt-get update -y && \
    apt-get install -y python3-pip
    RUN pip3 install pip --upgrade
    RUN pip3 install --no-cache-dir numpy matplotlib pandas

    6、alpine:latest ,構建時間 24 分 43.122 秒

    FROM alpine:latest
    RUN apk update && \
    apk add --no-cache --update \
    gcc make automake gcc g++ python3 python3-dev cython freetype-dev
    RUN pip3 install --upgrade pip
    RUN pip3 install --no-cache-dir numpy matplotlib pandas

    測試的結果出來了:

    alpine:latest > ubuntu:18.04 > > python:3.7-slim > debian:buster > python:last > amazonlinux:latest

    確實沒有看錯,被我們寄予厚望的 Alpine 映像的構建時間居然是 24 分鐘 以上。與構建速度最快的 Amazon Linux 2 比較起來足足慢了有 24 倍 的時間!!

    如果細心一些,你會發現這個 Dockerfile 與上面的幾個不同,多出了 gcc、make、automake、g++這些與編譯工具和幾個庫。事實上,在我第一次構建的時候遇到了這樣的錯誤資訊 :

    這真是未曾預料的問題啊!深究之下終於發現在 Appine 使用 pip 安裝 matplotlib 以及 pandas 的時候,並不是從 PyPi 的倉庫中下載 whl 包,而是需要下載原始碼然後編譯再進行安裝的。標準的預編譯的 Python 包居然無法直接安裝,這究竟是為什麽?

    答案原來出在 Alpine 使用的 musl 庫上。原來幾乎全部 Linux 發行版都使用 GNU 版本的 C 標準庫(glibc)。但是 Alpine Linux 為了減小儲存空間而使用了 musl 庫。而我們透過 pip 安裝的這些二進制 Python 包是基於 glibc 編譯而成的。因此 Alpine 無法安裝這些 python 庫,只能透過源碼編譯的方式來進行安裝。這也就是為什麽 Alpine 的 Docker file 會與其它的不同,以及花費如此之多的時間進行構建的秘密。

    我相信熟悉 Alpine 的使用者會與我爭論,Alpine 的 Edge 版本會解決這個問題。不幸的是,Edge 版本目前還不是穩定版本。如果用於生產環境,我們還是不要自尋煩惱為好。

    結論

    這個測試也許不能覆蓋我們需要的各個角度,但至少給我們提供了一個選擇的參考。而我在這個測試之後也得到了自己需要的結果,

  • Alpine 顯然不適合作為 Python 套用的基礎映像。盡管它提供了驚人儲存的空間上效率,但它對於 Python 包支持的不足的缺陷是難以彌補的。也許 Alpine 更適合於一些對於映像尺寸敏感的場合,還可以考慮將它用於你的 Go 套用。

  • 在 AWS 雲端運算的環境中,Amazon Linux 2 的效能表現是有目共睹的。如果你希望得到一個穩定、安全以及高效能的 Python 基礎映像,那就不要忘記 Amazon Linux 2 這個選擇。

  • Ubuntu 18.04 以及 Debian 10 表現的中規中矩,完全在我的意料之中。考慮到 Debian 10(Buster)較 Ubuntu 更新一些。這應該是一個好選擇。不過隨著 Ubuntu 20.04 LTS 即將釋出,在我的候選清單上也許要多出一個。

  • 至於 Docker 官方的 Python 映像,並沒有看出明顯的優點。考慮到安全性與維護性的問題,我不認為這是個好的選擇。

  • 關於 Docker 基礎映像的選擇,還需要考慮的一點就是 Linux 一致性的問題。雜亂的、多樣化的 Linux 版本會在大規模套用的時候帶來不可預估的風險,不可掉以輕心。

  • 連結:https://aws.amazon.com/cn/blogs/china/choose-the-best-docker-image-for-your-python-application/
    僅用於傳遞和分享更多資訊,並不代表本平台贊同其觀點和對其真實性負責,版權歸原作者所有,如有侵權請聯系我們刪除。