從 container 反推 image#

啟動 container 之前,必須先有可以「執行」的東西。Docker Image 就是這個東西:一份打包好的檔案系統快照,附帶啟動程序所需的 metadata(環境變數、預設指令、暴露的 port 等)。它本身不是執行中的程序,更像一個唯讀的模板。Container 啟動時,runtime 會以 image 為基底,在上面疊一層可寫的工作區,然後跑起裡面指定的 process。

簡單記憶:Image 是「靜態的檔案系統 + 設定」;Container 是「以 image 為基底跑起來的程序 + 一層可寫空間」。

docker pull:把 image 拉到本地#

Docker Image 預設存在 registry(如 Docker Hub、私有 registry)。要在本機使用,必須先拉下來:

docker pull nginx:1.27

實際發生的事:

  • Client 向 registry 詢問 nginx:1.27 對應的 Manifest
  • Manifest 描述了這個 image 由哪些 layer 組成,每個 layer 各自有一個 SHA256 摘要
  • Docker 比對本機已有的 layer,只下載缺少的那幾層
  • 下載完成後,layer 被解壓存放到本機的 image store,並透過 SHA256 索引

這帶出一個關鍵性質:image 內容是以 Content-Addressable Storage 的方式儲存的。同樣內容的 layer,無論被多少 image 引用,本機只會留一份。

docker inspect:看 image 的設定#

Image 不只是檔案,也帶設定。docker inspect 可以印出該 image 的 JSON 描述:

docker inspect nginx:1.27

可以觀察的欄位包括:

  • Id:image 的 SHA256 摘要
  • RepoTags / RepoDigests:人類可讀的 tag 與 digest
  • Config:預設 CmdEntrypointEnvWorkingDirExposedPorts
  • RootFS.Layers:組成 image 的 layer SHA256 清單
  • GraphDriver.Data:本機儲存路徑(OverlayFS 的 LowerDir、UpperDir 等)

Config 區段是 container 啟動時的預設值。實際 docker run 時可以再被覆蓋。

docker history:image 是怎麼長出來的#

Image 通常由 Dockerfile 一行一行建出來。docker history 可以倒著看每一層的成因:

docker history nginx:1.27

預期會看到一份表格,欄位大致是:

  • IMAGE:該 layer 對應的 image id(多數中間層為 <missing>,因為 layer 本身不一定有獨立 tag)
  • CREATED / CREATED BY:什麼時間、由哪一行 Dockerfile 指令產生
  • SIZE:這一層帶來的檔案大小變化
  • COMMENT:附註

從 history 可以反推 Dockerfile 的大致樣貌,這也是為什麼把祕密直接 COPY 進 image 是危險的——history 會記住,即使後續 layer 把檔案刪除,原本那一層仍然存在於 image 中。

image 與 container 的關係#

  • 一個 image 可以被啟動成多個 container;container 之間共享 image 的唯讀 layer
  • container 啟動時會在 image layer 上疊一層可寫 layer;container 內的所有寫入都進這一層
  • 當 container 被刪除時,這層可寫 layer 也跟著消失;image 本身不受影響
  • 如果想把 container 的當下狀態保存成新 image,可以用 docker commit(後續章節會用到)

可以把這個關係想像成「一份模板可以蓋出很多臨時房子」:模板本身不變,每間房子各自有一塊可以亂塗鴉的牆,房子拆了模板還在。

一個簡單的觀察流程#

下面這串命令是熟悉 image 的最短路徑:

docker pull alpine:3.19
docker images
docker inspect alpine:3.19
docker history alpine:3.19
docker run --rm alpine:3.19 echo hello

預期會看到:alpine 只有一兩層、體積很小、history 對應到 base image 的建構步驟、docker run 之後 container 跑完 echo hello 立刻退出。

docker images --digests 可以直接看到每個 image 的 SHA256 digest,這是跨機器唯一識別 image 的方式,比 tag 可靠(tag 可以被覆蓋)。

延伸閱讀#