什么是容器鏡像?
容器鏡像包含一個打包的應(yīng)用,以及它的依賴關(guān)系,還有它在啟動時運行的進程信息。
容器是當(dāng)今 IT 運維的一個關(guān)鍵部分。容器鏡像包含了一個打包的應(yīng)用,以及它的依賴關(guān)系,還有它在啟動時運行的進程信息。
你可以通過提供一組特殊格式的指令來創(chuàng)建容器鏡像,可以是提交給注冊中心,或者是作為 Dockerfile 保存。例如,這個 Dockerfile 為 PHP Web 應(yīng)用創(chuàng)建了一個容器:
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
這個文件中的每條指令都會在容器鏡像中增加一個層。每一層只增加與下面一層的區(qū)別,然后,所有這些堆疊在一起,形成一個只讀的容器鏡像。
它是如何工作的?
你需要知道一些關(guān)于容器鏡像的事情,按照這個順序理解這些概念很重要:
- 聯(lián)合文件系統(tǒng)
- 寫入時復(fù)制(COW)
- 疊加文件系統(tǒng)
- 快照器
聯(lián)合文件系統(tǒng)
聯(lián)合文件系統(tǒng)(UnionFS)內(nèi)置于 Linux 內(nèi)核中,它允許將一個文件系統(tǒng)的內(nèi)容與另一個文件系統(tǒng)的內(nèi)容合并,同時保持“物理”內(nèi)容的分離。其結(jié)果是一個統(tǒng)一的文件系統(tǒng),即使數(shù)據(jù)實際上是以分支形式組織。
這里的想法是,如果你有多個鏡像有一些相同的數(shù)據(jù),不是讓這些數(shù)據(jù)再次復(fù)制過來,而是通過使用一個叫做層的東西來共享。
UnionFS
每一層都是一個可以在多個容器中共享的文件系統(tǒng),例如,httpd 基礎(chǔ)層是 Apache 的官方鏡像,可以在任何數(shù)量的容器中使用。想象一下,由于我們在所有的容器中使用相同的基礎(chǔ)層,我們節(jié)省了多少磁盤空間。
這些鏡像層總是只讀的,但是當(dāng)我們用這個鏡像創(chuàng)建一個新的容器時,我們會在它上面添加一個薄的可寫層。這個可寫層是你創(chuàng)建、修改、刪除或進行每個容器所需的其他修改的地方。
寫時復(fù)制(COW)
當(dāng)你啟動一個容器時,看起來好像這個容器有自己的整個文件系統(tǒng)。這意味著你在系統(tǒng)中運行的每個容器都需要自己的文件系統(tǒng)副本。這豈不是要占用大量的磁盤空間,而且還要花費大量的時間讓容器啟動?不是的,因為每個容器都不需要它自己的文件系統(tǒng)副本!
容器和鏡像使用寫時復(fù)制(COW)機制來實現(xiàn)這一點。寫時復(fù)制策略不是復(fù)制文件,而是將同一個數(shù)據(jù)實例分享給多個進程,并且只在一個進程需要修改或?qū)懭霐?shù)據(jù)時進行復(fù)制。所有其他進程將繼續(xù)使用原始數(shù)據(jù)。
Docker 對鏡像和容器都使用了寫時復(fù)制的機制。為了做到這一點,在舊版本中,鏡像和運行中的容器之間的變化是通過圖驅(qū)動來跟蹤的,現(xiàn)在則是通過快照器來跟蹤。
在運行中的容器中執(zhí)行任何寫操作之前,要修改的文件的副本被放在容器的可寫層上。這就是發(fā)生 寫 的地方。現(xiàn)在你知道為什么它被稱為“寫時復(fù)制”了么。
這種策略既優(yōu)化了鏡像磁盤空間的使用,也優(yōu)化了容器啟動時間的性能,并與 UnionFS 一起工作。
疊加文件系統(tǒng)
疊加文件系統(tǒng)位于現(xiàn)有文件系統(tǒng)的頂部,結(jié)合了上層和下層的目錄樹,并將它們作為一個單一的目錄來呈現(xiàn)。這些目錄被稱為層。下層保持不被修改。每一層只增加與下一層的差異(計算機術(shù)語為 “diff”),這種統(tǒng)一的過程被稱為聯(lián)合掛載。
最低的目錄或鏡像層被稱為下層目錄,上面的目錄被稱為 上層目錄。最后的覆蓋層或統(tǒng)一層被稱為合并層。
Layered file system
常見的術(shù)語包括這些層的定義:
- 基礎(chǔ)層:是你的文件系統(tǒng)的文件所在的地方。就容器鏡像而言,這個層就是你的基礎(chǔ)鏡像。
- 疊加層:通常被稱為容器層,因為對運行中的容器所做的所有改變,如添加、刪除或修改文件,都會寫到這個可寫層。對這一層所做的所有修改都存儲在下一層,是基礎(chǔ)層和差異層的聯(lián)合視圖。
- 差異層包含了在疊加層所作的所有修改。如果你寫的東西已經(jīng)在基礎(chǔ)層了,那么疊加文件系統(tǒng)就會把文件復(fù)制到差異層,并做出你想寫的修改。這被稱為寫時復(fù)制。
快照器
通過使用層和圖驅(qū)動,容器可以將其更改作為其容器文件系統(tǒng)的一部分來構(gòu)建、管理和分發(fā)。但是使用圖驅(qū)動的工作真的很復(fù)雜,而且容易出錯。快照器與圖驅(qū)動不同,因為它們不用了解鏡像或容器。
快照器的工作方式與 Git 非常相似,比如有樹的概念,并跟蹤每次提交對樹的改變。一個快照代表一個文件系統(tǒng)狀態(tài)。快照有父子關(guān)系,使用一組目錄。可以在父級和其快照之間進行差異比較(diff
),以創(chuàng)建一個層。
快照器提供了一個用于分配、快照和掛載抽象的分層文件系統(tǒng)的 API。
總結(jié)
你現(xiàn)在對什么是容器鏡像以及它們的分層方法如何使容器可移植有了很好的認識。接下來,我將介紹容器的運行機制和內(nèi)部結(jié)構(gòu)。