對應到容器技術,為了隔離不同類型的資源,Linux 內核裏面實現了以下幾種不同類型的 namespace。
- UTS,對應的宏為 CLONE_NEWUTS,表示不同的 namespace 可以配置不同的 hostname。
- User,對應的宏為 CLONE_NEWUSER,表示不同的 namespace 可以配置不同的用户和組。
- Mount,對應的宏為 CLONE_NEWNS,表示不同的 namespace 的文件系統掛載點是隔離的
- PID,對應的宏為 CLONE_NEWPID,表示不同的 namespace 有完全獨立的 pid,也即一個 namespace 的進程和另一個 namespace 的進程,pid 可以是一樣的,但是代表不同的進程。
- Network,對應的宏為 CLONE_NEWNET,表示不同的 namespace 有獨立的網絡協議棧。
操作 namespace 的常用指令 nsenter,可以用來運行一個進程,進入指定的 namespace。例如,通過下面的命令,我們可以運行 /bin/bash,並且進入 nginx 所在容器的 namespace。
# nsenter --target 58212 --mount --uts --ipc --net --pid -- env --ignore-environment -- /bin/bash
root@f604f0e34bc2:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
23: eth0@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
另一個命令是 unshare,它會離開當前的 namespace,創建且加入新的 namespace,然後執行參數中指定的命令。
例如,運行下面這行命令之後,pid 和 net 都進入了新的 namespace。
unshare --mount --ipc --pid --net --mount-proc=/proc --fork /bin/bash
clone 和 unshare 的區別是,unshare 是使當前進程加入新的 namespace;clone 是創建一個新的子進程,然後讓子進程加入新的 namespace,而當前進程保持不變。
在內核裏面,對於任何一個進程 task_struct 來講,裏面都會有一個成員 struct nsproxy,用於保存 namespace 相關信息,裏面有 struct uts_namespace、struct ipc_namespace、struct mnt_namespace、struct pid_namespace、struct net *net_ns 和 struct cgroup_namespace *cgroup_ns。
創建 namespace 的時候,我們在內核中會調用 copy_namespaces,調用順序依次是 copy_mnt_ns、copy_utsname、copy_ipcs、copy_pid_ns、copy_cgroup_ns 和 copy_net_ns,來複制 namespace。