建立第一個容器#

從這一章開始進入實作。本章使用最小的 Linux 映像 Alpine 啟動一個容器(Container),並逐步拆解 docker run 背後到底發生了什麼。

為什麼選 Alpine#

Alpine 是基於 musl libc 與 BusyBox 的極小 Linux 發行版:

  • 映像體積約 5–8 MB
  • 啟動極快,適合教學與實驗
  • 預設 shell 是 ash(BusyBox 提供)而非 bash
  • 套件管理使用 apk

對於觀察容器生命週期與行程(Process)行為而言,Alpine 是非常理想的對象。

第一條指令:docker run#

最簡單的容器啟動指令:

docker run -it --name mybox alpine

參數解析:

  • -i:保持 STDIN 開啟(interactive)
  • -t:分配虛擬終端機(TTY)
  • --name mybox:給這個容器一個名字,方便後續操作
  • alpine:使用的映像名稱,預設從 Docker Hub 拉取 alpine:latest

預期輸出(具體值依環境而異):第一次執行會看到 Docker 從 Docker Hub pull alpine:latest 的訊息(包含每個 layer 的下載進度),完成後直接進入容器內的 shell prompt(通常是 / #)。

docker run 的三個階段#

docker run 並不是一個原子指令,而是依序執行三件事。理解這個流程對後續除錯很重要:

docker run = docker pull (if needed) → docker create → docker start

階段 1:pull#

Docker daemon 會檢查 host 上是否已經有該映像。如果沒有:

  1. 解析 image 名稱(例如 alpinedocker.io/library/alpine:latest
  2. 與 registry(預設 Docker Hub)建立 HTTPS 連線
  3. 下載映像的 manifest(描述 layer 結構)
  4. 平行下載每個 layer(壓縮 tarball)
  5. 解壓縮並寫入聯合檔案系統(Union File System,多半是 OverlayFS)

可以單獨執行:

docker pull alpine

階段 2:create#

create 階段會:

  1. 為新容器產生唯一 ID 與 metadata
  2. 設定 cgroups(CPU、記憶體限額)
  3. 準備命名空間(Namespaces):PID、Mount、Network、UTS、IPC、User
  4. 透過 OverlayFS 建立可寫層(容器的 rootfs)
  5. 設定網路介面(連到預設的 bridge 網路)

此時容器尚未執行任何行程。可以單獨執行:

docker create --name mybox alpine

階段 3:start#

start 階段才是真正執行容器的進入點(entrypoint):

  1. fork / exec 出新行程,並將該行程放入前面準備好的命名空間與 cgroups
  2. 該行程在容器內成為 PID 1(init process)
  3. STDIO 視 -it 等參數決定是否連回 host terminal

可以單獨執行:

docker start -ai mybox

-ai--attach --interactive 的縮寫。

容器內看到什麼#

進入容器後執行:

ps -ef
ls /
cat /etc/os-release
hostname

預期輸出(具體值依環境而異):

  • ps -ef 只會看到極少的行程,且第一個行程的 PID 是 1(也就是進入容器時啟動的 ash
  • ls / 看到典型的 Linux 根目錄結構(binetcprocsysusr 等)
  • /etc/os-release 顯示 NAME="Alpine Linux" 與版本號
  • hostname 顯示一段隨機英數字串(容器 ID 的前綴)

這些觀察驗證了容器是「被隔離的 Linux 行程」這個事實:它有自己的根目錄、自己的 PID 樹、自己的主機名稱。

從 host 看容器#

開另一個 terminal,執行:

docker ps

預期輸出(具體值依環境而異):會看到一行紀錄,包含容器 ID、image(alpine)、command(/bin/shash)、status(Up xx seconds)、name(mybox)。

接著查看實際的行程:

ps -ef | grep ash

在 Linux host 上會直接看到 ash 行程,且該行程的 parent 多半是 containerd-shim 或類似的 runtime 元件。在 macOS 上要先進入 LinuxKit VM 才看得到(參見上一章)。

容器內的 PID 1 與 host 上看到的 PID 是同一個行程,只是在不同 PID Namespace 中有不同的編號。這就是「行程隔離」的核心。

退出容器與容器狀態#

在容器內輸入 exit 或按 Ctrl-D

  • 容器內的 PID 1(ash)會結束
  • 容器隨之停止(不是刪除)
  • docker ps 看不到,但 docker ps -a 仍會列出

這個「停止 ≠ 刪除」的差別下一章會詳細說明。

一次性容器:–rm#

如果只想做一次性實驗,加上 --rm 讓容器結束時自動刪除:

docker run -it --rm alpine

退出後,連 docker ps -a 都不會留下紀錄。對教學情境特別實用。

延伸閱讀#