【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

雨點的名字發表於2021-10-12

映象是 Docker 容器的基石,容器是映象的執行例項,有了映象才能啟動容器。為什麼我們要討論映象的內部結構?

如果只是使用映象,當然不需要了解,直接通過 docker 命令下載和執行就可以了。

但如果我們想建立自己的映象,或者想理解 Docker 為什麼是輕量級的,就非常有必要學習這部分知識了。

一、最小的映象

1、執行hello-world映象

hello-world 是 Docker 官方提供的一個映象,通常用來驗證 Docker 是否安裝成功。

我們先通過 docker pull 從 Docker Hub 下載它。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

docker images 命令檢視映象的資訊。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

hello-world 映象竟然還不到 14KB! 通過 docker run 執行。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

其實我們更關心 hello-world 映象包含哪些內容。

2. hello-world映象內容

Dockerfile 是映象的描述檔案,定義瞭如何構建Docker映象。Dockerfile的語法簡潔且可讀性強,後面我們會專門討論如何編寫Dockerfile。

hello-world 的 Dockerfile 內容如下:

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

只有短短三條指令。

#1、此映象是從白手起家,從 0 開始構建。
FROM scratch
#2、將檔案“hello”複製到映象的根目錄。
COPY hello /
#3、容器啟動時,執行 /hello
CMD ["/hello"]

映象 hello-world 中就只有一個可執行檔案 “hello”,其功能就是列印出 “Hello from Docker ......” 等資訊。

/hello 就是檔案系統的全部內容,連最基本的 /bin,/usr, /lib, /dev 都沒有。

hello-world 雖然是一個完整的映象,但它並沒有什麼實際用途。通常來說,我們希望映象能提供一個基本的作業系統環境,使用者可以根據

需要安裝和配置軟體。這樣的映象我們稱作 base 映象。我們下一節討論 base 映象。


二、base 映象

1.base映象含義

base 映象有兩層含義:

1、不依賴其他映象,從 scratch 構建。
2、其他映象可以之為基礎進行擴充套件。

所以,能稱作 base 映象的通常都是各種 Linux 發行版的 Docker 映象,比如 Ubuntu, Debian, CentOS 等。

2.base映象內容

我們以 CentOS 為例考察 base 映象包含哪些內容。

下載映象:docker pull centos

檢視映象資訊

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

映象大小 231MB。等一下!一個 CentOS 才 200MB ?平時我們安裝一個 CentOS 至少都有幾個 GB,怎麼可能才 200MB !

相信這是幾乎所有 Docker 初學者都會有的疑問,包括我自己。下面我們來解釋這個問題。

Linux 作業系統由核心空間使用者空間組成。如下圖所示:

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

核心空間是kernel,Linux 剛啟動時會載入 bootfs 檔案系統,之後 bootfs 會被解除安裝掉。使用者空間的檔案系統是 rootfs,包含我們熟悉的

/dev, /proc, /bin 等目錄。對於 base 映象來說,底層直接用 Host 的 kernel,自己只需要提供 rootfs 就行了。

而對於一個精簡的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程式庫就可以了。相比其他 Linux 發行版,CentOS 的 rootfs

已經算臃腫的了,alpine 還不到 10MB。我們平時安裝的 CentOS 除了 rootfs 還會選裝很多軟體、服務、圖形桌面等,需要好幾個 GB 就

不足為奇了。下面是 CentOS 映象的 Dockerfile 的內容:

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

第二行 ADD 指令新增到映象的 tar 包就是 CentOS 7 的 rootfs。在製作映象時,tar包會自動解壓到 / 目錄下,生成 /dev, /porc, /bin 等目錄。

不同 Linux 發行版的區別主要就是 rootfs。

比如 Ubuntu 14.04 使用 upstart 管理服務,apt 管理軟體包;而 CentOS 7 使用 systemd 和 yum。這些都是使用者空間上的區別,

Linux kernel 差別不大。所以 Docker 可以同時支援多種 Linux 映象,模擬出多種作業系統環境。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

上圖 Debian 和 BusyBox(一種嵌入式 Linux)上層提供各自的 rootfs,底層共用 Docker Host 的 kernel。

這裡需要說明的是:

容器只能使用 Host 的 kernel,並且不能修改。
所有容器都共用 host 的 kernel,在容器中沒辦法對 kernel 升級。如果容器對 kernel 版本有要求(比如應用只能在某個 kernel 版本下執行),則不建議用容器,這種場景虛擬機器可能更合適。

下一節我們討論映象的分層結構。


三、映象的分層結構

Docker 支援通過擴充套件現有映象,建立新的映象。

1、映象分層示例

實際上,Docker Hub 中 99% 的映象都是通過在 base 映象中安裝和配置需要的軟體構建出來的。比如我們現在構建一個新的映象,Dockerfile 如下:

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

① 新映象不再是從 scratch 開始,而是直接在 Debian base 映象上構建。
② 安裝 emacs 編輯器。
③ 安裝 apache2。
④ 容器啟動時執行 bash。

構建過程如下圖所示:

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

可以看到,新映象是從 base 映象一層一層疊加生成的。每安裝一個軟體,就在現有映象的基礎上增加一層

2、映象分層好處

問什麼 Docker 映象要採用這種分層結構呢?

最大的一個好處就是 - 共享資源

比如:有多個映象都從相同的 base 映象構建而來,那麼 Docker Host 只需在磁碟上儲存一份 base 映象;同時記憶體中也只需載入一份 base

映象,就可以為所有容器服務了。而且映象的每一層都可以被共享,我們將在後面更深入地討論這個特性。

這時可能就有人會問了:如果多個容器共享一份基礎映象,當某個容器修改了基礎映象的內容,比如 /etc 下的檔案,這時其他容器的 /etc

是否也會被修改?

答案是不會!

修改會被限制在單個容器內

這就是我們接下來要學習的容器 Copy-on-Write 特性。

3、Copy-on-Write 特性

可寫的容器層

當容器啟動時,一個新的可寫層被載入到映象的頂部。這一層通常被稱作“容器層”,“容器層”之下的都叫“映象層”。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

所有對容器的改動 - 無論新增、刪除、還是修改檔案都只會發生在容器層中。

只有容器層是可寫的,容器層下面的所有映象層都是隻讀的

下面我們深入討論容器層的細節。

映象層數量可能會很多,所有映象層會聯合在一起組成一個統一的檔案系統。如果不同層中有一個相同路徑的檔案,比如 /a,上層的 /a 會

覆蓋下層的 /a,也就是說使用者只能訪問到上層中的檔案 /a。在容器層中,使用者看到的是一個疊加之後的檔案系統。

新增檔案

在容器中建立檔案時,新檔案被新增到容器層中。

讀取檔案

在容器中讀取某個檔案時,Docker 會從上往下依次在各映象層中查詢此檔案。一旦找到,開啟並讀入記憶體。

修改檔案

在容器中修改已存在的檔案時,Docker 會從上往下依次在各映象層中查詢此檔案。一旦找到,立即將其複製到容器層,然後修改之。

刪除檔案

在容器中刪除檔案時,Docker 也是從上往下依次在映象層中查詢此檔案。找到後,會在容器層中記錄下此刪除操作。

只有當需要修改時才複製一份資料,這種特性被稱作 Copy-on-Write。可見容器層儲存的是映象變化的部分,不會對映象本身進行任何修改。

這樣就解釋了我們前面提出的問題:*容器層記錄對映象的修改,所有映象層都是隻讀的,不會被容器修改,所以映象可以被多個容器共享

理解了映象的原理和結構,下一節我們學習如何構建映象。


四、構建映象

1、為何要構建映象

對於 Docker 使用者來說,最好的情況是不需要自己建立映象。幾乎所有常用的資料庫、中介軟體、應用軟體等都有現成的 Docker 官方映象或

其他人和組織建立的映象,我們只需要稍作配置就可以直接使用。

使用現成映象的好處除了省去自己做映象的工作量外,更重要的是可以利用前人的經驗。特別是使用那些官方映象,因為 Docker 的工程師

知道如何更好的在容器中執行軟體。

當然,某些情況下我們也不得不自己構建映象,比如:

1. 找不到現成的映象,比如自己開發的應用程式。
2. 需要在映象中加入特定的功能,比如官方映象幾乎都不提供 ssh。

所以本節我們將介紹構建映象的方法。同時分析構建的過程也能夠加深我們對前面映象分層結構的理解。

2、構建映象方法

Docker 提供了兩種構建映象的方法:

1. docker commit 命令
2. Dockerfile 構建檔案

3、docker commit構建映象

docker commit 命令是建立新映象最直觀的方法,其過程包含三個步驟:

1. 執行容器
2. 修改容器
3. 將容器儲存為新的映象

舉個例子:在 ubuntu base 映象中安裝 vi 並儲存為新映象。

1)、第一步:執行容器

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

-it 引數的作用是以互動模式進入容器,並開啟終端。6e2d389d4576 是容器的內部 ID。

2)、第二步:安裝 vi

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

確認 vi 沒有安裝。開始安裝 apt-get install -y vim

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

3)、第三步:儲存新映象

在新視窗中檢視當前執行的容器。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

gifted_stallman 是 Docker 為我們的容器隨機分配的名字。

執行 docker commit 命令將容器儲存為映象。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

新映象命名為 ubuntu-with-vi

檢視新映象的屬性。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

從 size 上看到映象因為安裝了軟體而變大了。從新映象啟動容器,驗證 vi 已經可以使用。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

以上演示瞭如何用 docker commit 建立新映象。然而,Docker 並不建議使用者通過這種方式構建映象。原因如下:

1. 這是一種手工建立映象的方式,容易出錯,效率低且可重複性弱。比如要在 debian base 映象中也加入 vi,還得重複前面的所有步驟。
2. 更重要的:使用者並不知道映象是如何建立出來的,裡面是否有惡意程式。也就是說無法對映象進行審計,存在安全隱患。

既然 docker commit 不是推薦的方法,我們幹嘛還要花時間學習呢?

原因是:即便是用 Dockerfile(推薦方法)構建映象,底層也 docker commit 一層一層構建新映象的。學習 docker commit 能夠幫助我們

更加深入地理解構建過程和映象的分層結構。

下一節我們學習如何通過 Dockerfile 構建映象。


五、Dockerfile 構建映象

Dockerfile 是一個文字檔案,記錄了映象構建的所有步驟。

1、Dockerfile 構建映象

1)建立Dockerfile檔案

touch Dockerfile

2)用 Dockerfile 建立上節的 ubuntu-with-vi,其內容則為:

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

3)構建映象

docker build -t ubuntu-with-vi-dockerfile . 

ubuntu-with-vi-dockerfile是構建映象所取的名字

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

執行 docker build 命令,-t 將新映象命名為 ubuntu-with-vi-dockerfile,命令末尾的 . 指明 build context 為當前目錄。

Docker 預設會從 build context 中查詢 Dockerfile 檔案,我們也可以通過 -f 引數指定 Dockerfile 的位置。

4)映象構建成功

通過 docker images 檢視映象資訊。

【Docker】(9)---每天5分鐘玩轉 Docker 容器技術之映象

可以看到新映象已經構建成功,而且大小跟之前docker commit 構建的大小是一樣大的。


參考

本部落格所有內容均來自 《每天5分鐘玩轉 Docker 容器技術》書籍



相關文章