rootfs 與容器技術
回到我們在 Linux 的環境,執行我們的“容器”:
/root/test/container
我們是不是可以像虛擬機器中的作業系統一樣,在容器中“為非作歹”了呢?
那!當!然!不!行!啦!
1. 為什麼需要 rootfs
先忽略 Cgroup 機制。在容器中探索時,不知大家是否發現,/bin,/etc,/var ...
等等目錄下的內容,它們與宿主機的目錄完全一致。你在這些目錄裡的更改一樣會影響到宿主機。聰明的讀者一定發現了,我們建立程序的時候不是指定開啟 Mount Namespace 了嗎,為什麼不生效呢?
即使我們啟用了 Mount Namespace,但它需要手動指定掛載時才能生效,我們在程式碼中指定容器程序中的 /tmp
/tmp
目錄是空的,與宿主機不一致的。
至於其他的目錄,因為我們也沒有指定掛載點變更,它們會直接使用宿主機的目錄。但理想狀態下,我們會希望,進入在一個新的容器後,它的所有檔案都應該儘可能與宿主機隔離,我們才能放心使用它。
實現起來也不算複雜,我們知道 Linux 的目錄結構都是從 /
開始的,如果我們製作一份 /
目錄,然後在容器程序啟動的時候將它切換掛載到容器的 /
,就可以解決這個問題。而像這種完整包含根目錄的拷貝,我們稱呼它:rootfs。
2. 構建 rootfs
構建一個真正可用的 rootfs 的過程其實非常繁瑣,我們不需要了解它的細節,我們僅僅需要使用它基本的功能理解 rootfs 即可。
這裡我們使用的構建命令如下:
cd /root/test/
mkdir rootfs
# 為rootfs提供初始目錄結構和核心依賴庫
sudo rpm -ivh --nodeps --root /home/root/test/rootfs https://mirrors.aliyun.com/centos/8/BaseOS/x86_64/os/Packages/centos-release-8.1-1.1911.0.9.el8.x86_64.rpm
# 為rootfs安裝vim 工具和gcc編譯環境
sudo dnf --installroot=/root/test/rootfs install vim gcc --nogpgcheck
3. 使用 rootfs
在 Linux 環境的 /root/test/
目錄下,已經有一份打包好的 rootfs,我們直接使用它即可。
我們來探查一下它的目錄結構:
tree -L 1
發現它與宿主機的/
目錄的結構一致, 因為它就是我們容器將要使用的/
。
下面我們讓我們的容器使用這個 rootfs,編寫一個 helloworld 程式
Tip: 下面我們會使用到 chroot, chroot 是在 unix 系統的一個操作,針對正在運作的軟體行程和它的子程序,改變它外顯的根目錄。一個執行在這個環境下,經由 chroot 設定根目錄的程式,它不能夠對這個指定根目錄之外的檔案進行訪問動作,不能讀取,也不能更改它的內容。Namespace Mount 事實上就是受到 chroot 的啟發改進而來的。
cd /root/test/
## 執行容器環境
./container
## chroot切換到rootfs
chroot /root/rootfs /bin/bash
# 進入容器中的home目錄,編寫程式碼 helloworld.c並編譯執行
cd /home
vim helloworld.c
#include <stdio.h>
int main()
{
// printf() 中字串需要引號
printf("Hello, World!");
return 0;
}
儲存後編譯執行
cc helloworld.c -o helloworld
./helloworld
我們將看到終端中顯示:
hello, world!
這樣我們獲得了一個檔案系統目錄與宿主機隔離的環境,在這裡,我們可以放心大膽地操作,不用擔心影響到宿主機的環境。
至此,我們獲取了一個可以使用 C 語言編譯執行應用的容器。
4. 拓展思考
如果 多個專案同時使用這個容器,他們之間如何隔離檔案系統呢?
直接拷貝或者重新構建多個 rootfs 似乎可行,但這浪費了空間
如果 容器使用過程中,不小心誤刪了某個 rootfs 中的關鍵檔案,導致容器被損壞, 怎麼辦?
讓 rootfs 的關鍵檔案設定為只讀的狀態,似乎可以解決這個問題。
如果我們想與其他人分享我們的容器和容器內的應用,該怎麼辦呢?
平臺相同的情況下,我們把 rootfs 和容器程序檔案拷貝一份副本給他們就行了。傳輸方式?U 盤,網盤,通訊軟體好像都可以。
如果…?
5. 小結
本節我們學習了容器場景下的 rootfs 需求的認知和構建使用。需要注意的是,rootfs只是一個作業系統所包含的檔案、配置和目錄,並不包括作業系統核心,因此在容器中執行的核心與宿主機一致,與核心相關的模組是無法隔離的。
結合前兩節中 Namespace 和 CGroup 的內容,我們對容器技術的核心概念和原理應該具有了充分的理解。但技術在產品化的過程中,比如拓展思考的部分,僅僅腦補就會遇到無數的問題,我們自己的解決方案似乎問題也很大,所以閉門造車是不行的,容器技術的核心部分將講到這裡,下面的章節就讓主角 Docker 正式登場。