Namespace是怎麼回事?
Linux namespace,可以理解爲將全局的操作系統資源,邏輯上劃分成功能獨立的單元。如進程號PID,這些個單元有各自獨立的PID空間,還比如網絡棧,這些個獨立的單元有各自獨立的路由表,網絡接口。linux實現了mount、UTS、IPC、network、pid、user這六種namespace。
下面先用unshare來測試實驗梳理一下linux namespace。
[root@centos ~]# unshare --help
Usage:
unshare [options] <program> [<argument>...]
Run a program with some namespaces unshared from the parent.
Options:
-m, --mount unshare mounts namespace
-u, --uts unshare UTS namespace (hostname etc)
-i, --ipc unshare System V IPC namespace
-n, --net unshare network namespace
-p, --pid unshare pid namespace
-U, --user unshare user namespace
-f, --fork fork before launching <program>
--mount-proc[=<dir>] mount proc filesystem first (implies --mount)
-r, --map-root-user map current user to root (implies --user)
[root@centos ~]# unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
unshare: unshare failed: Invalid argument // 說明是哪個參數不支持
逐個減少參數後,反覆執行後,確認是-U這個參數的問題。
只指定-U參數
[root@centos ~]# unshare -U /bin/bash
unshare: unshare failed: Invalid argument // 問題確認, user namespace不支持嗎?
查詢了網上資料,確認了docker版本、內核都已經支持user ns的前提下,centos7默認沒有啓用user namespace。
執行下面的命令啓用user namespace,然後reboot
[root@centos ~]# grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
資料來源:
https://github.com/procszoo/procszoo/wiki/How-to-enable-%22user%22-namespace-in-RHEL7-and-CentOS7%3F
Reboot後,執行下面的命令:
[root@centos ~]# unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
[root@centos ~]# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
在新的network namespace,只存在loop這個設備
[root@centos ~]# ip route
[root@centos ~]
在新的network namespace,路由表是空的
[root@centos ~]# ip link add type veth // 添加veth設備
[root@centos ~]# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 52:e9:ba:d6:23:4e brd ff:ff:ff:ff:ff:ff
3: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether ba:6f:7a:dc:9f:4b brd ff:ff:ff:ff:ff:ff
在新的network namespace,創建了veth設備
[root@centos ~]# hostname onecloud
[root@centos ~]# hostname
onecloud
在宿主機上執行hostname
[root@centos ~]# hostname
centos
在新的UTS namespace,可以設置屬於自己的hostname, domain
[root@centos ~]#mkdir /tmp1 && mount -t tmpfs -o size=20m tmpfs /tmp1
[root@centos ~]# df -h | grep tmp1
tmpfs 20M 0 20M 0% /tmp1
在宿主機上執行
[root@centos ~]# df -h | grep tmp1
[root@centos ~]#
在新的mount namespace,執行mount動作後,宿主機被屏蔽了。這也是容器volume工作的基礎(volume相關的知識會另外講到)。
[root@centos ~]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:08 pts/3 00:00:00 /bin/bash
root 12 1 0 11:09 pts/3 00:00:00 ps -ef
在宿主機執行
[root@centos ~]# ps -ef
root 14069 14052 0 11:08 pts/3 00:00:00 unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
root 14071 14069 0 11:08 pts/3 00:00:00 /bin/bash
root 14100 967 0 11:09 ? 00:00:00 sleep 60
root 14106 13000 0 11:10 pts/2 00:00:00 ps -ef
在新的pid namespace,bash是第一號進程
接下來,我們來看看有點模糊的user namespace。
執行下面的命令:
[test@centos ~]$ id
uid=1000(test) gid=1000(test) groups=1000(test)
[test@centos ~]$ hostname test
hostname: you must be root to change the host name
[test@centos ~]$ unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
unshare: unshare failed: Operation not permitted
[test@centos ~]$ unshare -u -i -n -p -U -r -f /bin/bash //去掉了參數-m --mount-proc
[root@centos ~]# id
uid=0(root) gid=0(root) groups=0(root)
[root@centos ~]# hostname test
[root@centos ~]# mount -t tmpfs -o size=20m tmpfs /tmp1
mount: permission denied
[root@centos ~]# ip link add type veth
[root@centos ~]# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 3e:93:49:1a:0b:bc brd ff:ff:ff:ff:ff:ff
3: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 82:f2:8d:1e:18:e3 brd ff:ff:ff:ff:ff:ff
[root@centos ~]# useradd jacky
bash: /usr/sbin/useradd: Permission denied
可以得出幾點:
- test這個普通用戶,在linux系統裏是沒有權限執行與mount有關的命令的,因此執行unshare這個命令也不能帶-m參數。當然,如果是宿主機的管理員root執行unshare,是沒有這個限制的。
- user namespace的默認管理員root,是“map test user to root”而來,有權限設置主機名,管理網絡,沒有權限mount,也沒有權限管理用戶
那麼如果是管理員root的情況呢?
先準備一個centos 的rootfs
mkdir /opt/rootfs && cd /opt
docker export $(docker create centos) | tar -C ./rootfs -xf -
[root@centos opt]# unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
[root@centos opt]# chroot ./rootfs/
[root@centos /]# useradd peter //創建新的用戶
[root@centos /]# passwd
Changing password for user root. // 重新設置root管理員的密碼
New password:
BAD PASSWORD: The password is a palindrome
Retype new password:
passwd: all authentication tokens updated successfully.
我們用命令chroot將rootfs切換到了/opt/rootfs,因此ueradd、passwd命令的結果就不會影響宿主機系統的配置。如果不切換rootfs,那useradd、passwd改變的就是宿主機系統的配置,這將是很危險的。
我們再從用戶態看看,linux系統的namespace是怎麼樣的形式:
[root@centos opt]# unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
[root@centos opt]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:08 pts/2 00:00:00 /bin/bash
root 12 1 0 14:08 pts/2 00:00:00 ps -ef
[root@centos opt]# ll /proc/1/ns/ // 1號進程創建了新的namespace
total 0
lrwxrwxrwx 1 root root 0 Apr 26 14:08 ipc -> ipc:[4026532455]
lrwxrwxrwx 1 root root 0 Apr 26 14:08 mnt -> mnt:[4026532452]
lrwxrwxrwx 1 root root 0 Apr 26 14:08 net -> net:[4026532458]
lrwxrwxrwx 1 root root 0 Apr 26 14:08 pid -> pid:[4026532456]
lrwxrwxrwx 1 root root 0 Apr 26 14:08 user -> user:[4026532450]
lrwxrwxrwx 1 root root 0 Apr 26 14:08 uts -> uts:[4026532454]
[root@centos opt]# exit
exit
[root@centos opt]# ll /proc/1/ns // 宿主機1號進程的namespace
total 0
lrwxrwxrwx 1 root root 0 Apr 26 14:11 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Apr 26 14:11 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Apr 26 14:11 net -> net:[4026531962]
lrwxrwxrwx 1 root root 0 Apr 26 14:11 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Apr 26 14:11 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 26 14:11 uts -> uts:[4026531838]
很顯然,這些個ns的id表示它們是屬於不同的namespace。
好了,通過上面的過程,我們基本上明白了namespace在linux上是怎麼回事。