什么是容器鏡像?
容器鏡像包含一個(gè)打包的應(yīng)用,以及它的依賴關(guān)系,還有它在啟動(dòng)時(shí)運(yùn)行的進(jìn)程信息。
容器是當(dāng)今 IT 運(yùn)維的一個(gè)關(guān)鍵部分。容器鏡像包含了一個(gè)打包的應(yīng)用,以及它的依賴關(guān)系,還有它在啟動(dòng)時(shí)運(yùn)行的進(jìn)程信息。
你可以通過提供一組特殊格式的指令來創(chuàng)建容器鏡像,可以是提交給注冊(cè)中心,或者是作為 Dockerfile 保存。例如,這個(gè) Dockerfile 為 PHP Web 應(yīng)用創(chuàng)建了一個(gè)容器:
FROM registry.access.redhat.com/ubi8/ubi:8.1
RUN yum --disableplugin=subscription-manager -y module enable php:7.3 \
&& yum --disableplugin=subscription-manager -y install httpd php \
&& yum --disableplugin=subscription-manager clean all
ADD index.php /var/www/html
RUN sed -i 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf \
&& sed -i 's/listen.acl_users = apache,nginx/listen.acl_users =/' /etc/php-fpm.d/www.conf \
&& mkdir /run/php-fpm \
&& chgrp -R 0 /var/log/httpd /var/run/httpd /run/php-fpm \
&& chmod -R g=u /var/log/httpd /var/run/httpd /run/php-fpm
EXPOSE 8080
USER 1001
CMD php-fpm & httpd -D FOREGROUND
這個(gè)文件中的每條指令都會(huì)在容器鏡像中增加一個(gè)層。每一層只增加與下面一層的區(qū)別,然后,所有這些堆疊在一起,形成一個(gè)只讀的容器鏡像。
它是如何工作的?
你需要知道一些關(guān)于容器鏡像的事情,按照這個(gè)順序理解這些概念很重要:
- 聯(lián)合文件系統(tǒng)
- 寫入時(shí)復(fù)制(COW)
- 疊加文件系統(tǒng)
- 快照器
聯(lián)合文件系統(tǒng)
聯(lián)合文件系統(tǒng)(UnionFS)內(nèi)置于 Linux 內(nèi)核中,它允許將一個(gè)文件系統(tǒng)的內(nèi)容與另一個(gè)文件系統(tǒng)的內(nèi)容合并,同時(shí)保持“物理”內(nèi)容的分離。其結(jié)果是一個(gè)統(tǒng)一的文件系統(tǒng),即使數(shù)據(jù)實(shí)際上是以分支形式組織。
這里的想法是,如果你有多個(gè)鏡像有一些相同的數(shù)據(jù),不是讓這些數(shù)據(jù)再次復(fù)制過來,而是通過使用一個(gè)叫做層的東西來共享。
UnionFS
每一層都是一個(gè)可以在多個(gè)容器中共享的文件系統(tǒng),例如,httpd 基礎(chǔ)層是 Apache 的官方鏡像,可以在任何數(shù)量的容器中使用。想象一下,由于我們?cè)谒械娜萜髦惺褂孟嗤幕A(chǔ)層,我們節(jié)省了多少磁盤空間。
這些鏡像層總是只讀的,但是當(dāng)我們用這個(gè)鏡像創(chuàng)建一個(gè)新的容器時(shí),我們會(huì)在它上面添加一個(gè)薄的可寫層。這個(gè)可寫層是你創(chuàng)建、修改、刪除或進(jìn)行每個(gè)容器所需的其他修改的地方。
寫時(shí)復(fù)制(COW)
當(dāng)你啟動(dòng)一個(gè)容器時(shí),看起來好像這個(gè)容器有自己的整個(gè)文件系統(tǒng)。這意味著你在系統(tǒng)中運(yùn)行的每個(gè)容器都需要自己的文件系統(tǒng)副本。這豈不是要占用大量的磁盤空間,而且還要花費(fèi)大量的時(shí)間讓容器啟動(dòng)?不是的,因?yàn)槊總€(gè)容器都不需要它自己的文件系統(tǒng)副本!
容器和鏡像使用寫時(shí)復(fù)制(COW)機(jī)制來實(shí)現(xiàn)這一點(diǎn)。寫時(shí)復(fù)制策略不是復(fù)制文件,而是將同一個(gè)數(shù)據(jù)實(shí)例分享給多個(gè)進(jìn)程,并且只在一個(gè)進(jìn)程需要修改或?qū)懭霐?shù)據(jù)時(shí)進(jìn)行復(fù)制。所有其他進(jìn)程將繼續(xù)使用原始數(shù)據(jù)。
Docker 對(duì)鏡像和容器都使用了寫時(shí)復(fù)制的機(jī)制。為了做到這一點(diǎn),在舊版本中,鏡像和運(yùn)行中的容器之間的變化是通過圖驅(qū)動(dòng)來跟蹤的,現(xiàn)在則是通過快照器來跟蹤。
在運(yùn)行中的容器中執(zhí)行任何寫操作之前,要修改的文件的副本被放在容器的可寫層上。這就是發(fā)生 寫 的地方?,F(xiàn)在你知道為什么它被稱為“寫時(shí)復(fù)制”了么。
這種策略既優(yōu)化了鏡像磁盤空間的使用,也優(yōu)化了容器啟動(dòng)時(shí)間的性能,并與 UnionFS 一起工作。
疊加文件系統(tǒng)
疊加文件系統(tǒng)位于現(xiàn)有文件系統(tǒng)的頂部,結(jié)合了上層和下層的目錄樹,并將它們作為一個(gè)單一的目錄來呈現(xiàn)。這些目錄被稱為層。下層保持不被修改。每一層只增加與下一層的差異(計(jì)算機(jī)術(shù)語為 “diff”),這種統(tǒng)一的過程被稱為聯(lián)合掛載。
最低的目錄或鏡像層被稱為下層目錄,上面的目錄被稱為 上層目錄。最后的覆蓋層或統(tǒng)一層被稱為合并層。
Layered file system
常見的術(shù)語包括這些層的定義:
- 基礎(chǔ)層:是你的文件系統(tǒng)的文件所在的地方。就容器鏡像而言,這個(gè)層就是你的基礎(chǔ)鏡像。
- 疊加層:通常被稱為容器層,因?yàn)閷?duì)運(yùn)行中的容器所做的所有改變,如添加、刪除或修改文件,都會(huì)寫到這個(gè)可寫層。對(duì)這一層所做的所有修改都存儲(chǔ)在下一層,是基礎(chǔ)層和差異層的聯(lián)合視圖。
- 差異層包含了在疊加層所作的所有修改。如果你寫的東西已經(jīng)在基礎(chǔ)層了,那么疊加文件系統(tǒng)就會(huì)把文件復(fù)制到差異層,并做出你想寫的修改。這被稱為寫時(shí)復(fù)制。
快照器
通過使用層和圖驅(qū)動(dòng),容器可以將其更改作為其容器文件系統(tǒng)的一部分來構(gòu)建、管理和分發(fā)。但是使用圖驅(qū)動(dòng)的工作真的很復(fù)雜,而且容易出錯(cuò)。快照器與圖驅(qū)動(dòng)不同,因?yàn)樗鼈儾挥昧私忡R像或容器。
快照器的工作方式與 Git 非常相似,比如有樹的概念,并跟蹤每次提交對(duì)樹的改變。一個(gè)快照代表一個(gè)文件系統(tǒng)狀態(tài)。快照有父子關(guān)系,使用一組目錄。可以在父級(jí)和其快照之間進(jìn)行差異比較(diff
),以創(chuàng)建一個(gè)層。
快照器提供了一個(gè)用于分配、快照和掛載抽象的分層文件系統(tǒng)的 API。
總結(jié)
你現(xiàn)在對(duì)什么是容器鏡像以及它們的分層方法如何使容器可移植有了很好的認(rèn)識(shí)。接下來,我將介紹容器的運(yùn)行機(jī)制和內(nèi)部結(jié)構(gòu)。