docker 倉庫裏面python好多tag都代表什麼意思?我們該如何選擇

docker 倉庫裏面python好多tag都代表什麼意思?我們該如何選擇

作者:張首富
時間:2020-05-25
w x:y18163201

起因

今天讓我同事幫忙構建一個基於python代碼的docker包,然後他問我使用那個底層鏡像,我說你直接去docker hub上找一個,他打開之後問我這麼多我該使用那個,他們之間有什麼不一樣呢?

比較不同tag之間的區別

我們在docker hub上能看到很多python tag的標籤,我們這邊以3.6版本的來做介紹

3.6.10-buster, 3.6-buster
3.6.10-slim-buster, 3.6-slim-buster, 3.6.10-slim, 3.6-slim
3.6.10-stretch, 3.6-stretch
3.6.10-slim-stretch, 3.6-slim-stretch
3.6.10-alpine3.11, 3.6-alpine3.11, 3.6.10-alpine, 3.6-alpine
3.6.10-alpine3.10, 3.6-alpine3.10

我們查看他們構建的dockerfile其實就能看出來大概的區別

docker tag 基礎版本
3.6.10-apline3.10 apline:3.10
3.6.10-apline3.11 apline:3.11
3.6.10-slim-stretch debian:stretch-slim
3.6.10-slim-buster debian:buster-slim
3.6.10-buster buildpack-deps:buster
3.6.10-stretch buildpack-deps:stretch

其實我們從上面這個表格就可以看出來,他們之間的區別就是因爲底層鏡像不一樣而造成的差異。

debian版本號

debian發行版本號 含義
buster 當前的穩定版
stretch 舊的穩定版,包含了Debian官方最近一次發行的軟件包,優先推薦使用的版本
testing 測試版本,包含了哪些暫時未被收錄進”穩定版“的軟件包
ubstable 不穩定版,開發版本

buildpack-deps 基礎鏡像

該鏡像包含了通常開發所必須的頭文件和工具(比如源碼管理工具)。但是這個鏡像中添加了非必須的東西,有點違反docker最小構建的原理。

這裏推薦使用debian爲基礎鏡像

用Alpine 會讓python Docker 的構建慢50倍

一般我們選擇docker 鏡像爲基礎鏡像時,Apline Linux 就會被推薦,因爲他小,如果我們在使用go,這將是一個很好的選擇,因爲go不依賴於任何環境。

但是如果我們打包的是python代碼,那個Apline Linux會是你的構建:

  1. 讓你的構建更慢
  2. 讓你的鏡像更大
  3. 浪費我們寶貴的時間
  4. 偶爾引入一些令人費解的運行時bug

詳情請看這個:https://www.infoq.cn/article/VODLe9FsiBkQdlcxJZZj

Apline鏡像探祕

Alpine 是衆多Linux發行版中的一員,和Centos,Ubuntu,Debian 之類一樣,只是一個發行版的名字,號稱安全小巧,有自己的包管理工具apk

與Centos 和Ubuntu他們不同的是,Apline並沒有像Red Hat 之類的大公司爲其提供維護支持,軟件包數量也比這些發行版少很多(如果只看開箱即用的默認軟件倉庫,Apline只有10000個軟件包,而ubuntu,Debian的軟件包數量均大於50000).

容器崛起之前,Apline 還是個無名之輩,可能是因爲大家並不是很關心操作系統本身的大小,畢竟大家只關心業務數據和文檔,程序,庫文件和系統本身大小通常可以忽略不計。

容器技術席捲整個軟件產業之後,大家都注意到了一個問題,那就是容器的鏡像太大了,浪費磁盤空間,拉去鏡像時間長,不夠輕便。於是,人們開始尋求適用於容器的更小鏡像。對於那些耳熟能詳的發行版(例如 Ubuntu、Debian、Fedora)來說,只能通過刪除某些工具(例如 ifconfignetstat)將鏡像體積控制在 100M 以下。而對於 Alpine 而言,什麼都不用刪除,鏡像大小也就只有 5M 而已。

Alpine 鏡像的另一個優勢是包管理工具的執行速度非常快,安裝軟件體驗非常順滑。誠然,在傳統的虛擬機上不需要太關心軟件包的安裝速度,同一個包只需要裝一次即可,無需不停重複安裝。容器就不一樣了,你可能會定期構建新鏡像,也可能會在運行的容器中臨時安裝某些調試工具,如果軟件包的安裝速度很慢,會很快消磨掉我們的耐心。

爲了更直觀,我們來做個簡單的對比測試,看看不同的發行版安裝 tcpdump 需要多長時間,測試命令如下:

→ time docker run <image> <packagemanager> install tcpdump

測試結果如下:

Base image           Size      Time to install tcpdump
---------------------------------------------------------
alpine:3.11          5.6 MB      1-2s
archlinux:20200106   409 MB      7-9s
centos:8             237 MB      5-6s
debian:10            114 MB      5-7s
fedora:31            194 MB    35-60s
ubuntu:18.04          64 MB      6-8s

好吧,既然 Alpine 這麼棒,爲什麼不用它作爲所有鏡像的基礎鏡像呢?別急,先一步一步來,爲了趟平所有的坑,需要分兩種情況來考慮:

  1. 使用 Alpine 作爲第二構建階段(run 階段)的基礎鏡像
  2. 使用 ALpine 作爲所有構建階段(run 階段和 build 階段)的基礎鏡像

run 階段使用 Alpine

帶着激動的心情,將 Alpine 鏡像加入了 Dockerfile:

FROM gcc AS mybuildstage
COPY hello.c .
RUN gcc -o hello hello.c

FROM alpine
COPY --from=mybuildstage hello .
CMD ["./hello"]

第一個坑來了,啓動容器出現了錯誤:

standard_init_linux.go:211: exec user process caused "no such file or directory"

這個報錯在上篇文章已經見識過了,上篇文章的場景是使用 scratch 鏡像作爲 C 語言程序的基礎鏡像,錯誤的原因是 scratch 鏡像中缺少動態庫文件。可是爲什麼使用 Alpine 鏡像也有報錯,難道它也缺少動態庫文件?

也不完全是,Alpine 使用的也是動態庫,畢竟它的設計目標之一就是佔用更少的空間。但 Alpine 使用的標準庫與大多數發行版不同,它使用的是 musl libc,這個庫相比於 glibc 更小、更簡單、更安全,但是與大家常用的標準庫 glibc 並不兼容。

你可能又要問了:‘既然 musl libc 更小、更簡單,還特麼更安全,爲啥其他發行版還在用 glibc?’

mmm。。。因爲 glibc 有很多額外的擴展,並且很多程序都用到了這些擴展,而 musl libc 是不包含這些擴展的。詳情可以參考 musl 的文檔

也就是說,如果想讓程序跑在 Alpine 鏡像中,必須在編譯時使用 musl libc 作爲動態庫。

所有階段使用 Alpine

爲了生成一個與 musl libc 鏈接的二進制文件,有兩條路:

  • 某些官方鏡像提供了 Alpine 版本,可以直接拿來用。
  • 還有些官方鏡像沒有提供 Alpine 版本,我們需要自己構建。

golang 鏡像就屬於第一種情況,golang:alpine 提供了基於 Alpine 構建的 Go 工具鏈。

構建 Go 程序可以使用下面的 Dockerfile

FROM golang:alpine
COPY hello.go .
RUN go build hello.go

FROM alpine
COPY --from=0 /go/hello .
CMD ["./hello"]

生成的鏡像大小爲 7.5M,對於一個只打印 ‘hello world’的程序來說確實有點大了,但我們可以換個角度:

  • 即使程序很複雜,生成的鏡像也不會很大。
  • 包含了很多有用的調試工具。
  • 即使運行時缺少某些特殊的調試工具,也可以迅速安裝。

Go 語言搞定了,C 語言呢?並沒有 gcc:alpine 這樣的鏡像啊。只能以 Alpine 鏡像作爲基礎鏡像,自己安裝 C 編譯器了,Dockerfile 如下:

FROM alpine
RUN apk add build-base
COPY hello.c .
RUN gcc -o hello hello.c

FROM alpine
COPY --from=0 hello .
CMD ["./hello"]

必須安裝 build-base,如果安裝 gcc,就只有編譯器,沒有標準庫。build-base 相當於 Ubuntu 的 build-essentials,引入了編譯器、標準庫和 make 之類的工具。

最後來對比一下不同構建方法得到的 ‘hello world’鏡像大小:

  • 使用基礎鏡像 golang 構建:805MB
  • 多階段構建,build 階段使用基礎鏡像 golang,run 階段使用基礎鏡像 ubuntu:66.2MB
  • 多階段構建,build 階段使用基礎鏡像 golang:alpine,run 階段使用基礎鏡像 alpine:7.6MB
  • 多階段構建,build 階段使用基礎鏡像 golang,run 階段使用基礎鏡像 scratch:2MB

最終鏡像體積減少了 99.75%,相當驚人了。再來看一個更實際的例子,上一節提到的使用 net 的程序,最終的鏡像大小對比:

  • 使用基礎鏡像 golang 構建:810MB
  • 多階段構建,build 階段使用基礎鏡像 golang,run 階段使用基礎鏡像 ubuntu:71.2MB
  • 多階段構建,build 階段使用基礎鏡像 golang:alpine,run 階段使用基礎鏡像 alpine:12.6MB
  • 多階段構建,build 階段使用基礎鏡像 golang,run 階段使用基礎鏡像 busybox:glibc:12.2MB
  • 多階段構建,build 階段使用基礎鏡像 golang 並使用參數 CGO_ENABLED=0,run 階段使用基礎鏡像 ubuntu:7MB

鏡像體積仍然減少了 99%

囉裏囉嗦了這麼多,你大概知道要使用那個鏡像去構建 docker 包了吧!

參考:

用 Alpine 會讓 Python Docker 的構建慢 50 倍

Docker 鏡像製作教程:針對不同語言的精簡策略

python-images

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章