OCI 與容器鏡像構(gòu)建
大家好,我是張晉濤。
這篇文章中我將介紹 OCI 及容器鏡像相關(guān)的內(nèi)容,歡迎留言討論。
OCI 的前世今生
2013 年 3 月 dotCloud 公司在 PyCon 上進(jìn)行了 Docker 的首次展示,隨后宣布開源。自此 Docker 開始被眾人知曉,隨后掀起了一股容器化的熱潮。
在 2014 年 6 月 Docker 1.0 正式發(fā)布,有近 460 位貢獻(xiàn)者和超過 8700 次提交,這也標(biāo)志著 Docker 達(dá)到了生產(chǎn)可用的狀態(tài)。
在當(dāng)時,提到容器化第一想法就是用 Docker 。而當(dāng)時 Docker 的實(shí)現(xiàn)或者說發(fā)展方向主要是由 Docker Inc. 公司控制的,并沒有一個統(tǒng)一的工業(yè)標(biāo)準(zhǔn)。這對于一些頭部公司而言,顯然是不能接受的,沒有統(tǒng)一的工業(yè)標(biāo)準(zhǔn)意味著如果選擇了使用 Docker 的容器化技術(shù),便會被 Docker Inc. 公司所綁定;加上隨著 Docker 軟件的升級,某些功能或者特性必然會進(jìn)行變動,沒人能保證不發(fā)生破壞性變更。
所以,為了推進(jìn)容器化技術(shù)的工業(yè)標(biāo)準(zhǔn)化,2015 年 6 月在 DockerCon 上 Linux 基金會與 Google,華為,惠普,IBM,Docker,Red Hat,VMware 等公司共同宣布成立開放容器項(xiàng)目(OCP),后更名為 OCI。它的主要目標(biāo)便是 建立容器格式和運(yùn)行時的工業(yè)開放通用標(biāo)準(zhǔn)。
發(fā)展至今, OCI 制定的主要標(biāo)準(zhǔn)有三個分別是 runtime-spec 、image-spec 和 distribution-spec 這三個標(biāo)準(zhǔn)分別定義了容器運(yùn)行時,容器鏡像還有分發(fā)的規(guī)范,后面會展開介紹。
為了支持 OCI 容器運(yùn)行時標(biāo)準(zhǔn)的推進(jìn),Docker 公司起草了鏡像格式和運(yùn)行時規(guī)范的草案,并將 Docker 項(xiàng)目的相關(guān)實(shí)現(xiàn)捐獻(xiàn)給了 OCI 作為容器運(yùn)行時的基礎(chǔ)實(shí)現(xiàn),現(xiàn)在項(xiàng)目名為 runc 。
后來 Docker 將其容器運(yùn)行時獨(dú)立成了一個項(xiàng)目,名為 containerd 并將此項(xiàng)目捐獻(xiàn)給了 CNCF ,現(xiàn)在已經(jīng)是 CNCF 畢業(yè)項(xiàng)目了。
OCI image vs Docker image
OCI 的建立推動了容器技術(shù)的工業(yè)標(biāo)準(zhǔn)化,但是否此標(biāo)準(zhǔn)就是唯一呢?其實(shí)不然。在成立 OCI 并制定 image-spec 標(biāo)準(zhǔn)的時候 Docker 已經(jīng)空前繁榮,并得到了廣泛的應(yīng)用。
由于標(biāo)準(zhǔn)只定義了最基本的內(nèi)容,想要將 Docker 的實(shí)現(xiàn)全部按照標(biāo)準(zhǔn)進(jìn)行改造的話,會對 Docker 造成破壞性變更,也不利于 Docker 功能的迭代。
所以,Docker 為了支持 OCI 標(biāo)準(zhǔn)的普及,已經(jīng)推進(jìn)了 registry 對 OCI 鏡像的支持,現(xiàn)在也正在給 Docker 自身增加適配中,目標(biāo)是讓 Docker 支持兩種鏡像格式,分別是符合 Docker 標(biāo)準(zhǔn)的鏡像和符合 OCI 標(biāo)準(zhǔn)的鏡像。
那這兩者有什么異同呢?我們來逐步看下。
Docker Image 和 OCI Image 的區(qū)別和聯(lián)系
在我以前的文章中我們已經(jīng)詳細(xì)的從根本上介紹了 Docker image 是什么,這里我們就快速的介紹下。
每個 Docker 鏡像都是由一系列的配置清單和相應(yīng)的層進(jìn)行組織的。每個層一般都是 tar 格式的歸檔,配置清單中描述了對應(yīng)的層應(yīng)該按何種順序進(jìn)行組織,以及鏡像的一些元屬性。比如鏡像所支持的架構(gòu),例如 amd64 之類的,還有 ENV 等提前配置好的一些參數(shù)等。
當(dāng)然,在 Docker Image 中也包含著構(gòu)建鏡像時候所用的 Docker 版本 docker_version 以及構(gòu)建鏡像的歷史記錄 history 等信息。所以你在 DockerHub 或者其他的鏡像倉庫上可以看到構(gòu)建鏡像所用的 Docker 版本, 或者可通過 docker history 的方式來查看構(gòu)建歷史。
那么 OCI Image 是什么呢?首先我們需要有一個 OCI Image 才好探究它到底是什么。
這里介紹一個工具 skopeo 可以很方便的從鏡像倉庫或者本地 Docker daemon 甚至是通過 docker save 保存的 Docker Image tar 文件轉(zhuǎn)換為 OCI Image 。
關(guān)于 skopeo 的安裝過程就不再贅述了,參考項(xiàng)目主頁的文檔說明即可。這里直接開始使用。
我們使用 debian 的鏡像為例。
(MoeLove)????skopeo?copy?docker://debian:stretch?oci:debian:stretch????
Getting?image?source?signatures
Copying?blob?a4d8138d0f6b?done
Copying?config?45f82268e3?done
Writing?manifest?to?image?destination
Storing?signatures
通過上面的命令便會得到一個 OCI Image 了, 我們看下它的目錄結(jié)構(gòu)。
(MoeLove)????tree?debian?
debian
├──?blobs
│???└──?sha256
│???????├──?0043cd2a654fe86258f43f5b1dbbb4e6c582cc4bb6e505e9c5171c124150d155
│???????├──?45f82268e32180cb1839f90467d9b8a8258953d68b7221199976653308d92ef5
│???????└──?a4d8138d0f6b5a441aaa533faf5fe0c3996a6ca42643c46f4402c7e8bda53742
├──?index.json
└──?oci-layout
2?directories,?5?files
是不是有種似曾相識的感覺?沒錯 OCI Image 的規(guī)范是在 Docker Image 的基礎(chǔ)上建立的,所以大致看起來差異不是特別大。我們看看其中具體的內(nèi)容。
oci-layout
這個文件是 OCI Image 的布局文件,也是用于說明它所使用或者遵循的鏡像規(guī)范。
(MoeLove)????debian?cat?oci-layout|?jq
{
??"imageLayoutVersion":?"1.0.0"
}
可以看到此處的內(nèi)容寫的是 1.0.0 這便說明該鏡像遵循 OCI 1.0.0 版本的布局規(guī)范。
index.json
index.json 文件中的 manifest 字段類似于 Docker Image 中的 manifest.json 作為 OCI Image 的頂級配置, 也是鏡像的一個入口配置。
(MoeLove)????debian?cat?index.json?|?jq
{
??"schemaVersion":?2,
??"manifests":?[
????{
??????"mediaType":?"application/vnd.oci.image.manifest.v1+json",
??????"digest":?"sha256:0043cd2a654fe86258f43f5b1dbbb4e6c582cc4bb6e505e9c5171c124150d155",
??????"size":?349,
??????"annotations":?{
????????"org.opencontainers.image.ref.name":?"stretch"
??????},
??????"platform":?{
????????"architecture":?"amd64",
????????"os":?"linux"
??????}
????}
??]
}
從它的內(nèi)容可以看到它其中的 mediaType 字段與 Docker Image 中的類型形式相同,但是將 docker 都換成了 oci。從這個配置文件,我們可以找到第一個 blob 是 sha256:0043cd2a654fe86258f43f5b1dbbb4e6c582cc4bb6e505e9c5171c124150d155 我們看看它的內(nèi)容。
(MoeLove)????debian?cat?blobs/sha256/0043cd2a654fe86258f43f5b1dbbb4e6c582cc4bb6e505e9c5171c124150d155?|?jq
{
??"schemaVersion":?2,
??"config":?{
????"mediaType":?"application/vnd.oci.image.config.v1+json",
????"digest":?"sha256:45f82268e32180cb1839f90467d9b8a8258953d68b7221199976653308d92ef5",
????"size":?579
??},
??"layers":?[
????{
??????"mediaType":?"application/vnd.oci.image.layer.v1.tar+gzip",
??????"digest":?"sha256:a4d8138d0f6b5a441aaa533faf5fe0c3996a6ca42643c46f4402c7e8bda53742",
??????"size":?45337510
????}
??]
}
這個入口文件描述了 OCI 鏡像的實(shí)際配置和其中的 Layer 配置。如果有多層那 layers 也會相應(yīng)增加。
注意:layers 中 mediaType 使用了 application/vnd.oci.image.layer.v1.tar+gzip 說明數(shù)據(jù)內(nèi)容是經(jīng)過 gzip 壓縮的 如果有興趣你可以將它用 tar 解壓一下,你會發(fā)現(xiàn)很有趣的內(nèi)容。
“這里先將結(jié)果說出來,解壓后你會得到一個
”rootfs這與 Docker Image 是類似的。
小結(jié)
我們通過 skopeo 工具,從本地的 Docker daemon 中由 debian 的 Docker Image 得到了 OCI Image,并分析了它其中的內(nèi)容。
最主要的區(qū)別在于它們的目錄結(jié)構(gòu)不完全相同,配置信息尤其是 mediaType 的規(guī)范是不相同的。
而它們的聯(lián)系也在于此,OCI Image 的規(guī)范是由 Docker Image 的規(guī)范修改而來的,所以類似它們的 blob 的組織形式大致是相同的,配置文件中很多的參數(shù)也相似。
另外,我們也可以很容易的得到另一個結(jié)論,那便是我們可以很方便的將 Docker Image 轉(zhuǎn)換為 OCI Image 。
OCI image 和 Docker image 的轉(zhuǎn)換
上面我們已經(jīng)看到,使用 skopeo 工具,可以將 Docker Image 轉(zhuǎn)換為 OCI Image ,當(dāng)然它也可以將 OCI Image 轉(zhuǎn)換為 Docker Image 。下面給出了方法:
#?從?DockerHub?將?debian?的?Docker?Image?拉取并轉(zhuǎn)換為?OCI?Image
(MoeLove)????skopeo?copy?docker://debian:stretch?oci:debian:stretch????
Getting?image?source?signatures
Copying?blob?a4d8138d0f6b?done
Copying?config?45f82268e3?done
Writing?manifest?to?image?destination
Storing?signatures
#?將當(dāng)前目錄下的?debian?的?OCI?Image?轉(zhuǎn)換為?Docker?Image?并存儲到本地?docker?daemon?中
(MoeLove)????skopeo?copy??oci:debian:stretch?docker-daemon:local/debian:oci
Getting?image?source?signatures
Copying?blob?0e350e141713?done
Copying?config?aae58a37cf?done
Writing?manifest?to?image?destination
Storing?signatures
#?驗(yàn)證
(MoeLove)????oci?docker?images?local/debian
REPOSITORY??????????TAG?????????????????IMAGE?ID????????????CREATED?????????????SIZE
local/debian????????oci?????????????????ac6bcf605d82????????6?months?ago????????101MB
鏡像構(gòu)建工具
在 CI/CD 環(huán)境中,雖然我們可以使用 DinD (Docker in Docker) 的方式啟動一個 docker daemon 或者使用掛載的方式,將外部的 /var/run/docker.sock 掛載進(jìn)容器內(nèi)部,亦或者將 Docker API 使用 HTTP 的方式暴露出來,直接使用該地址進(jìn)行構(gòu)建。
但這些方式你是否會覺得比較重?是否有考慮安全問題,或者壓力及負(fù)載的問題?
這里的壓力及負(fù)載主要是指當(dāng)所有的任務(wù)都共用同一個 docker daemon 提供服務(wù)的話,對該 docker daemon 造成的壓力。
這里我們來介紹一些其他的鏡像構(gòu)建工具,使用這些工具可以讓你在無 Docker 的環(huán)境下構(gòu)建出鏡像并上傳至 Docker 鏡像倉庫中。
到目前為止,我們可以有很多種選擇:
BuildKit img orca-build umoci buildah kaniko FTL Bazel rules_docker
這些工具側(cè)重點(diǎn)各有不同,當(dāng)然也不僅有上面列到的這些工具,只是這些工具比較典型罷了。
通常情況下,在網(wǎng)絡(luò)上比較容易見到宣傳為下一代鏡像構(gòu)建工具的是 buildah ,最主要原因是因?yàn)樗梢灾苯訕?gòu)建 OCI 標(biāo)準(zhǔn)的鏡像或 Docker 鏡像,也可以直接使用 Dockerfile 。并且它還可以 pull/push 鏡像,可以說在鏡像構(gòu)建方面與 Docker 是完全兼容,甚至可以說它在構(gòu)建鏡像方面可以作為 Docker 的替代品了。
并且 buildah 構(gòu)建鏡像的時候不需要任何 root 權(quán)限,也不依賴 Docker, 它使用了簡單的 fork-exec 模型,同時它也可以作為一個庫包含在其他的工具中。它的最終目標(biāo)便是提供一個更低層次的核心工具集,來完成構(gòu)建鏡像相關(guān)的事情。
說完這個典型的替代品,我們再來說下 BuildKit 和 img , img 這個工具是構(gòu)建在 BuildKit 之上的,所以有很多相似性。它們使用非 root 用戶來構(gòu)建鏡像。當(dāng)然 BuildKit 我在之前的文章中詳細(xì)介紹過了,它是 Docker 內(nèi)置的下一代構(gòu)建工具,獨(dú)立使用也是可以的。稱它為“下一代鏡像構(gòu)建工具” 也并不為過。
kaniko 是 Google 推出的,它主要的宣傳語為 “在 Kubernetes 中構(gòu)建容器鏡像” 實(shí)際上無論是在 K8S 集群中或者在容器中它都是可以工作的。它也可以使用 Dockerfile 構(gòu)建鏡像。當(dāng)然還有很重要的一點(diǎn),它所有的構(gòu)建命令都是運(yùn)行在用戶態(tài)的,并且也可以很好的與 Kubernetes 結(jié)合,在云原生時代下,它也占據(jù)了一定的優(yōu)勢。
以上工具都只是大致介紹了下,如果對它們感興趣,可直接進(jìn)入項(xiàng)目主頁查看 README.md 基礎(chǔ)使用都有比較詳細(xì)的說明,這里不再進(jìn)行贅述了。
總結(jié)
本篇為大家介紹了 OCI 的前世今生,以及 OCI Image 的規(guī)范和特點(diǎn),另外也介紹了一個可用于在 OCI Image 和 Docker Image 之間鏡像轉(zhuǎn)換的工具 skopeo 。另外介紹了一些可用于在 CI 環(huán)境或其他有特定場景環(huán)境下替代 Docker build 的工具,請大家按實(shí)際需求進(jìn)行選擇。
