Docker 的運作方式——建置、傳送、執行

Docker 的運作方式,畫在一張互動畫布上:由 Dockerfile 到映像檔、由映像檔倉庫到容器,以及隔離一個程序的 namespaces 與 cgroups。

Docker 把一個應用程式連同它所需的一切打包成一個映像檔,然後把它當作容器執行——一個隔離的程序,在任何裝有 Docker 引擎的機器上,行為都完全一致。

Docker 的運作方式——建置、傳送、執行

The interactive FlowJam canvas for this explanation — every lane, row and arrow above is a real QueryChart diagram you can open and edit.

How to read this visual

  • 由左至右讀這三個直欄:建置、傳送、執行。
  • 最底那一條「主機作業系統」是地基:流程末端的那個容器就是在它之上執行,並共用它的核心。
  • 映像檔是其餘一切都在搬動的那件產物——由 Docker 引擎去到映像檔倉庫,再回到執行環境。

建置這個映像檔

「開發者撰寫一份描述應用程式的 Dockerfile」是一份食譜,不是一個結果:基礎映像檔、要安裝的依賴、要複製的檔案,以及要執行的指令。「docker build 把應用程式連同它的依賴一起打包」執行這份食譜,而「建置產生一個映像檔——一張唯讀的快照」就是輸出——一件存放在分層檔案系統裡、不可變的產物。可以重現才是重點:同一份 Dockerfile 在任何機器上都產生同一個映像檔。

把產物傳送出去

「映像檔推送到映像檔倉庫,或者由倉庫拉取」就是「傳送」這一幕。映像檔倉庫是映像檔的發布網絡——你的團隊用一個私有倉庫,基礎映像檔則來自公開的倉庫。映像檔在傳送途中永遠不會改變,而這正是部署可以是決定性的原因:你測試過的,就正正是你執行的那一個。

把容器執行起來

「docker run 由這個映像檔啟動一個容器」由 Docker 引擎跨進「容器執行環境」群組。「容器以 namespaces 與 cgroups 隔離」就是那套隔離機制——namespaces 令這個程序對檔案系統、網絡與程序表都有自己一套視野,cgroups 則為它的 CPU 與記憶體封頂。「容器共用主機核心,但擁有自己的檔案系統」放在「主機作業系統」那一條裡,是要把它與虛擬機器的對比明明白白畫出來,而「應用程式程序在任何地方都以同樣方式執行」就是令整疊技術站得住腳的回報。

Key relationships and takeaways

  • 映像檔是一張唯讀的快照;容器則是它執行起來的一個實例。
  • 容器共用主機核心,並以 namespaces 造出隔離的假象——這就是它們幾秒內就啟動,而虛擬機器要用幾分鐘的原因。
  • Cgroups 限制一個容器可以耗用多少,所以一個吵鬧的鄰居不會令整部主機挨餓。
  • 映像檔不可變這一點,正是「在任何地方都以同樣方式執行」成立的原因。
  • Docker 提供打包與執行環境;Kubernetes 這類編排工具則管理散佈在多部主機上的大量容器。

When to use this visual

  • 向一位開發者講解 Docker 為甚麼行得通,以及容器與虛擬機器有甚麼分別。
  • 在一支團隊寫下第一份 Dockerfile 之前,先帶他們熟習建置、傳送、執行這個模型。
  • 為一場關於環境漂移的討論定下基礎——為甚麼「在我的機器上是好的」不再說得通。

運作方式

  1. 標註你自己的 Dockerfile 步驟

    在 Dockerfile 那個方框上,列出你建置真實的分層——基礎映像檔、安裝依賴、複製原始碼、執行指令——讓這份食譜變得具體。

  2. 寫上你的映像檔倉庫

    把映像檔倉庫那一步改名為你實際使用的倉庫(一個私有的 ECR、一個公開的映像檔),並註明哪些映像檔是你推送的,哪些是你拉取的。

  3. 加上網絡與 volume 兩個方框

    把執行那一欄延伸下去:你的容器之間怎樣溝通——一個 bridge 網絡——以及狀態存放在哪裡——一個 volume——因為這兩部分正是初學者最常漏掉的。

  4. 連上編排

    如果你使用 Kubernetes,加一則備註,由執行中的容器指向 Kubernetes 那張圖解,並說明 kubelet 改變了容器在哪裡執行這件事。

常見問題

容器與虛擬機器有甚麼分別?

虛擬機器把硬件虛擬化:它自帶一套完整的作業系統,一切都經由 hypervisor 居中處理,所以它既笨重又啟動得慢。容器則把作業系統虛擬化:它共用主機核心,以 namespaces 達成隔離,以 cgroups 設下資源上限。這就是容器幾秒內就啟動、而且輕得多的原因——代價是同一部主機上的所有容器,都共用那部主機的核心。

映像檔與容器有甚麼不同?

映像檔是靜態的產物——由 Dockerfile 建置出來、關於應用程式及其依賴的一張唯讀快照。容器則是這個映像檔動起來的樣子:一個執行中的程序,上面多了一層可寫入的分層。你只建置一次映像檔,就可以由它啟動成千上萬個容器,而每一個都與其他容器隔離。

Docker 怎樣隔離容器?

靠兩項 Linux 核心功能。Namespaces 令每一個容器對系統都有自己一套視野——自己的檔案系統、網絡堆疊、程序表與用戶 id——所以它看起來就像一部獨立的機器。Cgroups(control groups)則限制並計量資源,為一個容器可以耗用多少 CPU、記憶體與 I/O 封頂,令一個容器無法拖垮它的鄰居。

Docker 為甚麼令應用程式可以到處搬?

因為映像檔把應用程式連同它的執行環境、程式庫與設定一併打包——除了核心以外的一切。只要有 Docker 引擎的地方,這個映像檔就以同樣的方式執行,於是開發者的手提電腦、CI runner 與生產環境伺服器之間的落差,就收窄到只剩下那些刻意留在映像檔外面的設定。

用 QueryChart(FlowJam)編輯這張圖解

把上面這張 Docker 畫布開啟為你自己的圖表——把群組改名為你自己的技術棧,並標註你的映像檔管線。

用 QueryChart(FlowJam)編輯這張圖解

互動圖解的更多內容