容器映像(container image)
一個執行中的容器是行程加上隔離;一個容器映像則是那些行程從之啟動的、被凍結好、可運送的套件。把它想成一份完整、可即刻執行的檔案系統快照——應用程式、它的函式庫、它的設定、它所需的一切(核心除外)——被打包起來,使得完全相同的位元組能在你的筆電、同事的機器、以及雲端裡的一台伺服器上一模一樣地執行。映像是那個「建構一次、處處執行」的產物;容器則是你啟動一個映像時所得到的東西。
巧妙之處在於映像如何被建構與儲存:作為一疊「層(layer)」。每一層是一份檔案系統變動的 tar 封存——第一層也許是一個極簡的 Linux 使用者空間,下一層加上你的語言執行環境,再下一層複製進你的應用程式。各層用聯合(union)或覆蓋(overlay)檔案系統疊起來,使它們呈現為一個合併後的檔案系統,而關鍵是它們是內容定址的:每一層都以其內容的密碼學雜湊來命名。這帶來兩大勝利。其一是去重與快取——若十個映像共用同一個基底層,它只被儲存與下載一次。其二是不可變性與完整性——雜湊既為該層命名、也驗證它未被竄改。隨附的一份清單(manifest)列出各層,並指向一份描述「如何執行該映像」的設定(進入點、環境等等)。當一個容器啟動時,最上面會加上一層薄薄的可寫層,好讓執行中的容器能做改動,而不更動那個唯讀的映像。
它為何重要:容器映像把整棵相依樹、而不只是應用程式,打包成一個可重現、可驗證、可快取的單位,從而解決了「在我機器上明明能跑」的問題——這是現代軟體交付與 CI/CD 的基礎。誠實的澄清:一個映像含有一個使用者空間檔案系統,「但不含核心」——它在執行期借用主機的核心,這正是為何一個 Linux 容器需要一個 Linux 核心才能跑。而映像是一個被動的資料套件;在某個執行時把它解開、從它啟動一個容器之前,它什麼都不做。這些映像的標準、廠商中立的格式,由 OCI 映像規範所定義。
一個以 FROM debian:12 開頭、再複製進一個執行檔的 Dockerfile,會產生一個兩層的映像:共享的 Debian 基底層(在許多映像間被快取與重用)加上一個含有你執行檔的小小頂層。在任何地方拉取它,相同的雜湊保證了相同的位元組。
映像是內容定址的各層——共享的基底只儲存一次;雜湊為每一層命名並加以驗證。
容器映像運送的是一個使用者空間檔案系統,「不含」核心——它在執行期借用主機核心,所以 Linux 映像需要一個 Linux 主機。映像是惰性的資料;在某個執行時從它啟動一個容器之前,它什麼都不做。