理解容器技術
容器是一種沙盒(sandbox)技術。顧名思義,沙盒就像一個容器,可以將應用程式「打包」起來,確保應用程式之間因為有了邊界而不會互相干擾。而且,裝在「容器」裡的應用程式,還可以遷移到不同的系統環境中執行。在深入探討容器技術的原理之前,理解什麼是「程序」(process)非常重要。程序的靜態表現就是一個程式,通常靜靜地躺在磁碟上。一旦執行起來,它就變成電腦中的資料和狀態的總和——也就是它的動態表現。容器技術的核心功能,就是透過限制和修改程序的動態行為,為程序建立一個「邊界」。
Docker 容器技術
對於大多數 Linux 容器(包括 Docker)來說,Cgroups 技術主要用來施加限制,而 Namespace 技術則是修改程序視角的主要方法。想像你有一個 Docker 專案執行在 Linux 作業系統上,例如 Ubuntu 22.04。讓我們建立一個容器來實驗:
$ docker run -it busybox /bin/sh
這個 Docker 指令會基於 busybox 映像檔啟動一個容器,並在其中執行一個互動式的 shell 工作階段。
docker run:建立並啟動一個新的容器實例。-i:即使沒有連接終端機,也保持 STDIN 開啟,允許與容器互動。-t:分配一個虛擬終端機或終端,建立互動式的 shell 環境。/bin/sh:指定容器啟動時要執行的指令,在此範例中,會在容器內啟動一個 shell 工作階段。
容器內的程序
因此,Ubuntu 機器成為主機,而一個執行 /bin/sh 的容器在其中運作。這個範例及其背後的原則,對熟練的 Docker 使用者來說應該不陌生。如果你在容器內執行 ps 指令,你會發現一些有趣的事:
/ # ps
PID USER TIME COMMAND
1 root 0:00 /bin/sh
10 root 0:00 ps
在此,在 Docker 內最初執行的 /bin/sh 是容器內的程序編號 1(PID=1),且只有兩個程序在執行。這表示 /bin/sh 和我們剛剛執行的 ps 指令已經被隔離在與主機不同的環境中。
Namespace 機制
這是如何做到的呢?通常,當 /bin/sh 程式在主機上執行時,作業系統會為它分配一個程序 ID,例如 PID=100,用以唯一識別它。當在 Docker 容器中執行這個程式時,Docker 施加了一種「幻覺」,讓實際上 PID 為 100 的程序以為自己是第一個程序(PID=1)。這個機制操作了隔離應用程式的程序空間,讓它們看到重新計算後的程序 ID。
Linux 的 Namespace 技術
使用 Namespace 相當有趣:它只是 Linux 中建立新程序的一個可選參數。Linux 中建立程序的系統呼叫是 clone(),如下所示:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
這個呼叫會建立一個新程序,並傳回其程序 ID(pid)。當使用 clone() 系統呼叫建立新程序時,可以指定 CLONE_NEWPID 引數:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
這樣新建立的程序就會「看到」一個全新的程序空間,其中它的 PID 為 1。這個「幻覺」並不會改變主機程序空間中的實際 PID,該值仍然是真實的,例如 100。
多個 Namespace 的運作
重複執行上述的 clone() 呼叫會建立多個 PID Namespace。每個 Namespace 中的應用程序都認為自己是各自容器內的第一個程序,對主機的實際程序空間或其他 PID Namespace 的細節一無所知。除了 PID Namespace 之外,Linux 還提供了 Mount、UTS、IPC、Network 和 User Namespace,用來「遮蔽」程序各種不同的上下文。
Mount Namespace
- Mount Namespace:它讓被隔離的程序只能看到與目前 Namespace 相關的掛載點,這表示容器內的程序完全不知道主機上的其他掛載點。
Network Namespace
- Network Namespace:它為被隔離的程序提供目前 Namespace 特有的網路設備與設定的視圖。每個 Network Namespace 都有自己的網路設備、IP 位址、路由表及連接埠號碼,與主機及其他 Namespace 完全分開。
其他 Namespace
- UTS Namespace:隔離節點名稱(hostname)與網路主機名稱資訊。
- IPC Namespace:隔離程序間通訊的資源,例如訊息佇列與號誌。
- User Namespace:隔離使用者 ID 與群組 ID,允許將容器內的使用者 ID 對映到主機上不同的 ID,以增強安全性。
Docker 容器的實作原理
這就是 Linux 容器的基本實作原理。因此,看似複雜的 Docker 容器概念,實際上就是在建立容器程序時指定一組 Namespace 參數。這樣一來,容器只能「看到」受目前 Namespace 限制的資源、檔案、設備、狀態或設定,對主機和其他無關程式完全一無所知。
容器與虛擬機器的比較
因此,容器是一種特殊的程序。當考慮為程序分配獨立空間的想法時,自然會聯想到虛擬機器。虛擬機器透過 Hypervisor 軟體模擬硬體,執行一個完整的客戶端作業系統來實現應用程式隔離。相比之下,Docker 容器透過 Namespace 和 Cgroups 技術實現隔離,同時共用主機的作業系統核心。
Docker 的輕量級虛擬化
Docker 常被稱為「輕量級」虛擬化技術,因為它直接利用主機資源,無需模擬硬體或執行額外的作業系統。這使得它在啟動速度、資源利用率和效能方面,與傳統虛擬機器相比具有顯著優勢。
結論
Docker 容器技術透過 Namespace 和 Cgroups 技術,為應用程序提供了一個隔離且輕量的環境。這項技術使容器能快速啟動、高效執行,並與主機及其他容器保持隔離,使其成為現代雲端運算和微服務架構中不可或缺的技術。
Novita AI*,一站式無限創意平台,提供 100 多種 API。從影像生成、語言處理到音訊增強與影片編輯,按量計費價格低廉,讓您在建立自己產品的同時,免除 GPU 維護的困擾。立即免費試用。
