不只有Docker:可選擇的容器化工具還有很多……
本文轉(zhuǎn)載自公眾號(hào)“讀芯術(shù)”(ID:AI_Discovery)。
在過(guò)去的容器時(shí)代(更確切地說(shuō)是四年前),Docker是容器角逐賽中的唯一參與者。今非昔比,現(xiàn)在的Docker已經(jīng)不再是唯一,只是業(yè)界全景圖中的容器引擎之一。
Docker允許構(gòu)建、運(yùn)行、拉取、推送或檢查容器鏡像,但是對(duì)于每個(gè)任務(wù)來(lái)說(shuō),其他的可替代工具可能比Docker做得更好。因此,我們必須得探討一下現(xiàn)狀,這也許會(huì)讓你卸載并完全忘記Docker。
為什么不使用Docker?
對(duì)于已經(jīng)用了很久docker的人,可能需要花費(fèi)些功夫去說(shuō)服自己去考慮轉(zhuǎn)換使用不同的工具。
Docker是一個(gè)龐大單一的工具,它嘗試做任何事,但通常沒(méi)有用最好的方法去做。我們最好選擇只做一件事,但確實(shí)做得很好的專門(mén)工具。如果害怕切換不同的工具集,擔(dān)心不得不學(xué)習(xí)使用不同的命令行界面(CLI)、不同的API或通常使用不同的概念,那么現(xiàn)在這將不再是問(wèn)題。
選擇本文中顯示的任何工具都是完全無(wú)縫銜接的,因?yàn)樗鼈?包括Docker)都遵循開(kāi)放容器計(jì)劃(OCI)下的相同規(guī)范。該計(jì)劃包含有關(guān)容器運(yùn)行時(shí)、容器分發(fā)和容器鏡像的規(guī)范,涵蓋了使用容器所需的所有功能。借助OCI,你可以選擇最符合需求的一組工具,與此同時(shí)仍然可以使用與Docker相同的API和CLI命令。
因此,如果你愿意嘗試新工具,那么請(qǐng)比較一下Docker及其競(jìng)爭(zhēng)對(duì)手的優(yōu)勢(shì)、劣勢(shì)和功能,看看是否有必要考慮放棄Docker,試試某些新工具。
容器引擎
在將Docker與其他任何工具進(jìn)行比較時(shí),我們需要按組件對(duì)其進(jìn)行分類,并且首先要談的是容器引擎。
容器引擎是一種工具,提供了用于處理圖像和容器的用戶界面,因此不必?fù)?dān)心擾亂SECCOMP規(guī)則或SELinux策略。它的工作還包括從遠(yuǎn)程存儲(chǔ)庫(kù)中提取圖像并將其擴(kuò)展到磁盤(pán),似乎也在運(yùn)行容器,但它實(shí)際上的工作是創(chuàng)建帶有圖像層的容器清單和目錄,然后將它們傳遞到容器運(yùn)行時(shí),如runc或crun。
目前有許多可用的容器引擎,Docker最主要的競(jìng)爭(zhēng)對(duì)手是Red Hat開(kāi)發(fā)的Podman。與Docker不同,Podman不需要運(yùn)行守護(hù)進(jìn)程,也不需要root特權(quán),這是Docker長(zhǎng)期以來(lái)一直關(guān)注的問(wèn)題。
顧名思義,Podman不僅可以運(yùn)行容器,還可以運(yùn)行pods。pod是Kubernetes的最小計(jì)算單元。它由一個(gè)或多個(gè)容器組成,執(zhí)行支持任務(wù)。這使Podman用戶以后可以更輕松地將其工作負(fù)載遷移到Kubernetes。以下是如何在單個(gè)pod中運(yùn)行2個(gè)容器的方法:
- ~ $ podman podcreate --name mypod
- ~ $ podman podlist
- POD ID NAME STATUS CREATED # OFCONTAINERS INFRA ID
- 211eaecd307b mypod Running 2 minutes ago 1 a901868616a5
- ~ $ podman run -d--pod mypod nginx # Firstcontainer
- ~ $ podman run -d--pod mypod nginx # Secondcontainer
- ~ $ podman ps -a--pod
- CONTAINERID IMAGE COMMAND CREATED STATUS PORTS NAMES POD POD NAME
- 3b27d9eaa35c docker.io/library/nginx:latest nginx -g daemon o... 2 seconds ago Up 1 second ago brave_ritchie 211eaecd307b mypod
- d638ac011412 docker.io/library/nginx:latest nginx -g daemon o... 5 minutes ago Up 5 minutes ago cool_albattani 211eaecd307b mypod
- a901868616a5 k8s.gcr.io/pause:3.2
最后,Podman提供了與Docker完全相同的CLI命令,只需執(zhí)行alias Docker=Podman并假裝沒(méi)有任何更改。
除了Docker和Podman,還有其他的容器引擎,但是筆者認(rèn)為它們都沒(méi)有出路,或者都不適合本地開(kāi)發(fā)和使用。具體原因如下:
- LXD——LXD是用于LXC(Linux容器)的容器管理器(守護(hù)進(jìn)程)。這個(gè)工具提供了運(yùn)行系統(tǒng)容器的能力,這些容器提供了更類似于VMs的容器環(huán)境。它位于非常狹窄的空間中,用戶不多,所以除非有非常具體的用例,否則最好使用Docker或Podman。
- CRI-O——當(dāng)搜索什么是CRI-O時(shí),可能會(huì)發(fā)現(xiàn)它被描述為容器引擎。不過(guò),它實(shí)際上是容器運(yùn)行時(shí)。此外,它也不適合“正常”使用。筆者的意思是,它是專門(mén)為Kubernetes運(yùn)行時(shí)(CRI)而構(gòu)建的,而不是供最終用戶使用。
- rkt——rkt(“火箭”)是CoreOS開(kāi)發(fā)的容器引擎。這里提到這個(gè)項(xiàng)目只是為了文章的完整性,因?yàn)轫?xiàng)目結(jié)束了,它的開(kāi)發(fā)也停止了,所以趁早別用它。
構(gòu)建鏡像
容器引擎中,Docker只有一個(gè)替換項(xiàng),但當(dāng)談到構(gòu)建鏡像,我們有更多的選擇。
首先來(lái)介紹Buildah。Buildah是redhat開(kāi)發(fā)的另一個(gè)工具,它可以很好地與Podman配合使用。如果已經(jīng)安裝了Podman,可能會(huì)注意到Podman build子命令,它實(shí)際上只是偽裝的Buildah,其二進(jìn)制文件包含在Podman中。
它的功能遵循與Podman相同的路線,是無(wú)守護(hù)程序和無(wú)根的,并且可以生成OCI兼容的鏡像,可以確保你的鏡像與Docker構(gòu)建鏡像的運(yùn)行方式相同。除此之外,Buildah還提供了對(duì)圖像層的更精細(xì)的控制,允許將許多更改提交到單個(gè)層中。與Docker相比,Buildah構(gòu)建的鏡像是特定于用戶的,因此只能列出自己構(gòu)建的鏡像。
那么,既然Buildah已經(jīng)包含在podman CLI中,為什么還要使用單獨(dú)的Buildah CLI?原因在于,buildahcli是podman build中包含的命令的超集,可能不需要接觸buildah CLI,但是通過(guò)使用它,可能還會(huì)發(fā)現(xiàn)一些額外有用的特性。
可以看一個(gè)小型過(guò)程展示:
- ~ $ buildah bud-f Dockerfile .
- ~ $ buildah from alpine:latest # Create starting container - equivalentto "FROM alpine:latest"
- Getting image source signatures
- Copying blobdf20fa9351a1 done
- Copying configa24bb40132 done
- Writing manifest toimage destination
- Storing signatures
- alpine-working-container # Name of the temporary container
- ~ $ buildah runalpine-working-container -- apk add --update --no-cache python3 # equivalent to "RUN apk add--update --no-cache python3"
- fetchhttp://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz
- fetchhttp://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/APKINDEX.tar.gz
- ...
- ~ $ buildahcommit alpine-working-container my-final-image # Create final image
- Getting image source signatures
- Copying blob50644c29ef5a skipped: already exists
- Copying blob362b9ae56246 done
- Copying config1ff90ec2e2 done
- Writing manifest toimage destination
- Storing signatures
- 1ff90ec2e26e7c0a6b45b2c62901956d0eda138fa6093d8cbb29a88f6b95124c
- ~# buildah images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- localhost/my-final-imagelatest 1ff90ec2e26e 22 seconds ago 51.4 MB
從上面的腳本可知,可以僅使用buildah bud來(lái)構(gòu)建鏡像,其中bud代表使用Dockerfile進(jìn)行構(gòu)建,但是還可以使用更多的腳本化方法,通過(guò)Buildahs的from、run和copy,這與Dockerfile中的命令等效。
接下來(lái)是Google的Kaniko。Kanik也與Dockerfile構(gòu)建容器鏡像,類似于Buildah,它也不需要守護(hù)進(jìn)程。其與Buildah的主要區(qū)別在于,Kaniko更專注于在Kubernetes中構(gòu)建鏡像。
Kanik使用gcr.io/kaniko-project/executor作為鏡像運(yùn)行,這對(duì)于Kubernetes有意義,但對(duì)于本地構(gòu)建而言并不方便,且無(wú)法達(dá)到目的,因?yàn)樾枰褂肈ocker運(yùn)行Kaniko鏡像來(lái)構(gòu)建新鏡像。
話雖如此,如果正在尋找用于在Kubernetes集群中構(gòu)建鏡像的工具(例如在CI/CD管道中),無(wú)守護(hù)進(jìn)程并且(也許)更安全,Kaniko可能是一個(gè)不錯(cuò)的選擇。
不過(guò),根據(jù)筆者的個(gè)人經(jīng)驗(yàn),同時(shí)使用Kaniko和Buildah在Kubernetes/OpenShift集群中構(gòu)建鏡像,筆者認(rèn)為兩者都可以很好地完成工作,但是使用Kaniko時(shí),筆者看到了一些隨機(jī)的構(gòu)建崩潰,并且在將鏡像推送到注冊(cè)表時(shí)失敗了。
Docker的第三個(gè)競(jìng)爭(zhēng)者是buildkit,也可以稱為下一代docker build。它是Moby項(xiàng)目的一部分(與Docker一樣),可以使用DOCKER_BUILDKIT = 1 dockerbuild作為實(shí)驗(yàn)特性啟用Docker。
它引入了許多改進(jìn)和功能,包括并行構(gòu)建步驟、跳過(guò)未使用的階段、更好的增量構(gòu)建和無(wú)根構(gòu)建。但另一方面,它仍然需要運(yùn)行守護(hù)程序(buildkitd)。因此,如果不想擺脫Docker,但是想要一些新功能和不錯(cuò)的改進(jìn),那么使用buildkit可能是理想選擇。
除此之外,還有一些值得一提的內(nèi)容,但不是筆者的最佳選擇:
- Source-To-Image(S2I)是一個(gè)工具包,可直接從源代碼構(gòu)建鏡像,而無(wú)需Dockerfile。該工具非常適合簡(jiǎn)單的預(yù)期場(chǎng)景和工作流程,但如果不需要太多自定義或項(xiàng)目的布局不理想,那么它很快就會(huì)變得笨拙。如果對(duì)Docker不太有把握,或者在OpenShift集群上構(gòu)建鏡像,則可以考慮使用S2I,因?yàn)槭褂肧2I進(jìn)行構(gòu)建是內(nèi)置功能。
- Jib是Google的另一種工具,專門(mén)用于構(gòu)建Java鏡像。它包括Maven和Gradle插件,可以輕松構(gòu)建鏡像而不會(huì)擾亂Dockerfile。
- 最后是Bazel,它是Google的另一種工具,不僅用于構(gòu)建容器鏡像,而且是一個(gè)完整的構(gòu)建系統(tǒng)。如果只想構(gòu)建鏡像,那么鉆研Bazel可能會(huì)有些過(guò)頭,但絕對(duì)會(huì)是一種不錯(cuò)的學(xué)習(xí)體驗(yàn)。
容器運(yùn)行時(shí)
最后一個(gè)難題是容器運(yùn)行時(shí),它負(fù)責(zé)運(yùn)行容器。容器運(yùn)行時(shí)是整個(gè)容器生命周期/堆棧的一部分,除非對(duì)速度、安全性等有非常特定的要求,否則它不會(huì)被輕易擾亂。有以下可選工具:
runc是基于OCI容器運(yùn)行時(shí)規(guī)范創(chuàng)建的最受歡迎的容器運(yùn)行時(shí)。Docker(通過(guò)容器)、Podman和CRI-O使用了它,所以幾乎所有東西都希望使用LXD(它使用LXC)。幾乎所有內(nèi)容都是默認(rèn)設(shè)置,即使你在閱讀本文后放棄使用Docker,也很可能仍會(huì)使用runc。
還有一種類似runc但令人困惑替代方法,名為crun。這是Red Hat開(kāi)發(fā)的工具,完全用C編寫(xiě)(runc用Go編寫(xiě))。這使其比runc更快、更高效。它也是OCI兼容的運(yùn)行時(shí),如果想自己檢查一下,可以輕松切換到它。雖然目前不太流行,但它將作為RHEL8.3版本中的替代OCI運(yùn)行時(shí)出現(xiàn)在技術(shù)預(yù)覽中,最終可能會(huì)被Podman或CRI-O視為默認(rèn)的Red Hat產(chǎn)品。
說(shuō)到CRI-O,之前筆者說(shuō)過(guò)CRI-O并不是容器引擎,而是容器運(yùn)行時(shí)。這是因?yàn)镃RI-O不包含推送鏡像之類的功能,而這正是你所期望的容器引擎的特性。
作為運(yùn)行時(shí)的CRI-O在內(nèi)部使用runc來(lái)運(yùn)行容器。該運(yùn)行時(shí)不是應(yīng)該在計(jì)算機(jī)上嘗試使用的運(yùn)行時(shí),因?yàn)樗菫樵贙ubernetes節(jié)點(diǎn)上用作運(yùn)行時(shí)而構(gòu)建的,被描述為“所有Kubernetes所需的運(yùn)行時(shí),僅此而已”。
因此,除非要設(shè)置Kubernetes集群(或OpenShift集群——CRI-O已經(jīng)是默認(rèn)值),否則別接觸此集群。
最后要講的是容器化,這是云原生計(jì)算基金會(huì)(CNCF)即將畢業(yè)的項(xiàng)目。這是一個(gè)守護(hù)程序,可充當(dāng)各種容器運(yùn)行時(shí)和操作系統(tǒng)的API外觀。在后臺(tái),它依賴于runc,是Docker引擎的默認(rèn)運(yùn)行時(shí)。
GoogleKubernetes Engine(GKE)和IBM Kubernetes Service(IKS)也使用它。它是Kubernetes容器運(yùn)行時(shí)界面(與CRI-O相同)的實(shí)現(xiàn),是Kubernetes集群運(yùn)行時(shí)的理想選擇。
鏡像檢查和分發(fā)
容器堆棧的最后一部分是鏡像檢查和分發(fā)。這有效地代替了docker inspect,并且(可選地)增加了在遠(yuǎn)程注冊(cè)表之間復(fù)制/鏡像的功能。
唯一可以完成這些任務(wù)的工具是Skopeo。它是由Red Hat制造的,并且是Buildah、Podman和CRI-O的隨附工具。除了從Docker知道的基本skopeo inspect外,Skopeo還能夠使用skopeo copy來(lái)復(fù)制鏡像,這使得可以在遠(yuǎn)程注冊(cè)表之間制作鏡像,無(wú)需先將它們拉到本地注冊(cè)表。如果使用本地注冊(cè)表,此功能也可以用作拉取/推送。
另外,筆者還想提到一下Dive,它是一種檢查、瀏覽和分析圖像的工具,更加人性化,提供了更具可讀性的輸出,并且可以更深入地挖掘(或潛水,我想)鏡像,并分析和衡量其效率。它也適用于CI管道,可以在其中測(cè)量鏡像是否“足夠有效”。
圖源:unsplash
筆者并非想說(shuō)服你完全放棄使用Docker,而是希望展示所有工具的全景圖以及構(gòu)建、運(yùn)行、管理和分發(fā)容器及其鏡像的所有選項(xiàng)。包括Docker在內(nèi)的每種工具都有其優(yōu)缺點(diǎn),我們必須評(píng)估哪種工具最適合工作流程和用例,這一點(diǎn)很重要。






























