跳至主要内容

2.3 容器與虛擬機

在網路上搜尋什麼是容器,常常會看到網友們拿容器和虛擬機做比較,這兩項技術在達成的目的上是有相似之處,但在眾多相似之處,又有著些許的不同。

什麼是虛擬機?

理論上的說法是在電腦上透過一個叫做 Hypervisor 的軟體將作業系統和應用程式以及硬體分開來,這樣就可以將自己劃分為數個獨立的「虛擬機器」

白話一點的說法可以想像成一個管家 ( Hypervisor ) 幫你把一個房子分隔成好幾間套房,每一個套房都有自己的衛浴設備和供電設施,誰也不求誰,但還是佔掉了整個房子的容積,水龍頭的流水量可能也會因為多條水管牽線而變得不那麼順暢。

虛擬機所佔據的硬體比較多,啟動速度也比較慢,是因為每個虛擬機器都可以獨立執行自己的作業系統和應用程式,同時還可以分配到從 Hypervisor 所管理的原始資源。這些資源包括記憶體、RAM、儲存設備等等,可以看看下方圖片:

虛擬機示意圖

什麼是容器?

在經過前兩個章節使用 Docker 容器後,相信使用上是有一些概念了,但到底什麼是容器呢?可以看到下方的圖片:

容器示意圖

容器其實是一個抽象的應用層,把程式碼和相依套件打包再一起,多個容器可以在同一台機器上運作,並且和其他容器共享作業系統的核心 ( 這和虛擬機完全不相同 ),而虛擬機是每一個都有自己的作業系統的。

容器在 Linux 作業系統上作為獨立的執行程序,相比虛擬機器就能夠佔用更少的空間,又能夠處理更多的應用程式。

Docker 容器只是在 Linux 作業系統上的執行程序

Docker 容器只是在 Linux 作業系統上的執行程序,並不是什麼迷你的虛擬機,它能夠獲取的資源有限,在程序結束時,容器就會進入退出的狀態。

下方示範為 Ubuntu 20.04 的版本,為了驗證容器在 Linux 系統上只是執行程序。

這邊可以先來驗證為什麼 Docker 容器本身只是 Linux 作業系統上的執行程序,我們先啟動一個容器。

$ docker container run --detach --name pg --publish 5432:5432 -e POSTGRES_PASSWORD=password postgres # 不換行
654a8b38709bcb4abbbeea0947dcc50257b350324

接著我們使用 docker container top 這個指令來查看該容器的執行程序,docker container top 這個指令的用意在於列出指定容器中的所有執行程序,可以看到裡面有列出 PID 也就是執行程序本身的 ID。

$ docker container top pg
UID PID PPID C STIME TTY TIME CMD
lxd 50522 50501 0 16:12 ? 00:00:00 postgres
lxd 50604 50522 0 16:12 ? 00:00:00 postgres:checkpointer

至於要如何驗證容器本身只是一個 Linux 作業系統上的執行程序呢?

我們可以在 Linux 作業系統的終端機內輸入 ps aux ( 列出系統所有的執行程序資料 ),可以看到 pg 容器內的 PID 與 Linux 作業系統內的 PID 相符,且 COMMAND 還是 postgres。

這樣我們就可以驗證其實 Docker 容器本身也是跑在 Linux 的作業系統上,並非虛擬機的概念,如果是使用虛擬機,在當前的作業系統上是看不到執行程序的。

$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
lxd 50522 0.0 2.6 21.. 26.. ? Ss 16:12 0:00 postgres
lxd 50604 0.0 0.3 21.. 39.. ? Ss 16:12 0:00 postgres:checkpointer

為什麼只提 Linux 作業系統?

從上一小節就一直不斷地強調 Linux 系統,其實是因為 Windows 以及macOS 上的 Docker 是運行在迷你虛擬機上,所以當您在 Windows 或是 macOS 作業系統中輸入列出執行程序的指令是沒辦法看到容器的執行程序。

這就更進一步證明了 Docker 容器就是作業系統上的執行程序,所以你沒辦法透過本機的作業系統 ( macOS、Windows ) 看到虛擬機內的執行程序。

那要如何在 macOS 上看到容器的執行緒呢?我們需要連接上那迷你的虛擬機,只要按照下方範例的指令就一樣可以看到虛擬機內的執行程序了:

$ docker run -it --rm --privileged --pid=host justincormack/nsenter1 # 不換行
Unable to find image 'justincormack/nsenter1:latest' locally
latest: Pulling from justincormack/nsenter1
5bc638ae6f98: Pull complete
Digest: sha256:e876f694a4cb6ff9e6861197ea3680fe2e3c5ab773a1e37ca1f13171f7f5798e
Status: Downloaded newer image for justincormack/nsenter1:latest

/# ps aux
PID USER TIME COMMAND
1 root 0:02 /sbin/init
2 root 0:00 [kthreadd]
3 root 0:00 [rcu_gp]
....
32851 999 0:00 postgres
32938 999 0:00 postgres: checkpointer
32939 999 0:00 postgres: background writer
32940 999 0:00 postgres: walwriter
32941 999 0:00 postgres: autovacuum launcher
32942 999 0:00 postgres: stats collector
32943 999 0:00 postgres: logical replication launcher
.....

/#

至於如何在 Windows 上查看虛擬機內的執行程序,我個人就沒有涉略關於 Windows Container 的知識,我自己在 Windows 上也是使用 WSL 運行 Ubuntu 作業系統。

接著透過下方的圖片,我們可以看到安裝虛擬機導致 macOS 以及 Windows 沒辦法直接透過本機來看到容器的執行程序的原因。

虛擬機導致 macOS 及 Windows 無法在本機檢查作業程序

macOS 及 Windows 為什麼要安裝虛擬機?

如前面所提,Docker 容器是運作在 Linux 上的執行程序,道理就像打開一個 App 一樣,但這些容器的執行檔案必須建立在其作業系統核心之上。

舉例來說,一個為了 linux/x86_64 核心打造的 Ruby 映像檔是沒辦法在 Windows 上以 ruby.exe 的方式執行,這也是為什麼 Docker 桌面應用程式會需要在 macOS 以及 Windows 上面安裝一個迷你的虛擬機來改變運行容器的作業系統核心。

Docker 從 2013 年到 2016 年,為多種架構 ( amd64、arm/v6、arm/v7、i386等等 ) 建置映像檔,當時還沒辦法為 Windows 的核心系統建構,所以那時候的 Docker 只適用於 Linux。

到了 2016 年,Windows Containers 才問世,但我個人私心認為,如果是使用 Windows 的用戶,用 WSL 安裝 Ubuntu 作業系統並且安裝 Docker,使用上的體驗會更好。

時至今日,Docker 已經不再受限於 Linux,但當你想到映像檔時,要認知到它本身會針對您電腦的作業系統核心以及架構進行建置。

Docker 透過 manifest 在後台天衣無縫地完成這些作業,他可以偵測您所運作的作業系統並下載最適合的映像檔。

如果您使用 docker container run --platform,就能夠強制使用和您電腦作業系統不一樣的映像檔。

順道一提,本書仍會以 Linux x64 的映像檔為主,主要是因為本書是給新手學習基本 Docker 技能,所以更著重在基礎的概念和使用上,和 Windows Container 大概有 95% 的概念都是雷同的,除了一些作業系統上的基本指令之外。