概念總覽#

容器(Container)能夠「看起來像一台獨立的機器」,最關鍵的核心機制就是 Linux 命名空間(Namespaces)。它將原本全域的核心資源切成多個獨立的視圖(view),讓不同的行程(Process)在各自的命名空間中以為自己擁有整個系統。

  • 命名空間並不是「虛擬機」,所有行程仍共用同一個 Kernel
  • 每個命名空間只隔離特定一類資源(例如 PID、網路、掛載點)
  • 一個行程同時屬於每一類命名空間中的某一個

七種主要 Namespaces#

Linux Kernel 目前提供以下幾種主要的命名空間:

  • PID Namespace:隔離行程編號(Process ID),每個 PID Namespace 都有自己的 PID 1
  • Mount Namespace:隔離掛載點(mount points),讓不同容器看到不同的檔案系統樹
  • Network Namespace:隔離網路裝置、IP、路由表、iptables 規則
  • User Namespace:隔離使用者與群組 ID,可讓容器內的 root 對應到主機上的非特權使用者
  • UTS Namespace:隔離主機名稱(hostname)與 NIS domain name
  • IPC Namespace:隔離 System V IPC 與 POSIX message queue
  • Cgroup Namespace:隔離行程在 cgroup 階層中的視圖

Time Namespace 是較新加入的成員,用於隔離 CLOCK_MONOTONICCLOCK_BOOTTIME,但容器執行環境(runtime)目前較少使用。

三個關鍵的 Syscall#

操作命名空間主要透過三個系統呼叫:

  • clone(2):建立新行程的同時,可透過 flags(如 CLONE_NEWPIDCLONE_NEWNS)讓子行程進入新的命名空間
  • unshare(2):讓「目前」這個行程脫離原本的命名空間,進入新建立的命名空間
  • setns(2):讓行程加入「已經存在」的命名空間(例如另一個容器的 PID Namespace)

對照表:

動作Syscall典型使用場景
建立新行程並進入新 namespaceclone(2)容器執行環境啟動容器主行程
自己離開舊 namespace 進入新 namespaceunshare(2)unshare 指令、容器初始化前置
加入已存在 namespacesetns(2)nsenterdocker exec

從指令觀察#

可以用以下指令初步觀察命名空間:

# 列出目前 shell 屬於哪些 namespace
ls -l /proc/self/ns/

# 建立一個新的 UTS namespace 並修改 hostname
sudo unshare --uts /bin/bash
hostname demo

每個 /proc/<pid>/ns/<type> 都是一個 symbolic link,連結目標形如 pid:[<inode>],inode 相同代表屬於同一個命名空間。

命名空間與容器的關係#

容器(例如 Docker、containerd 啟動的容器)在啟動時,runtime 會:

  1. 透過 clone(2) 建立新行程,並一次帶入多個 CLONE_NEW* flag
  2. 在新行程中設定根目錄(pivot_root 或 chroot)、掛載 procfs
  3. 設定 cgroup 限制與網路裝置
  4. 執行容器內真正的入口程式(entrypoint)

理解 Namespaces 是後續理解容器隔離、容器除錯、以及 docker exec / nsenter 等工具的基礎。

延伸閱讀#