Linux namespace
是 Linux
提供的一种内核级别环境隔离的方法。本文结合 unshare
、介绍 Linux namespaces
,该技术是容器(如Docker等)技术的基础。
namespace 机制
-------------------------------------------
| Process1 | Process2 | Process1 | Process2 |
-------------------------------------------
| Namespace1 | Namespace2 |
-------------------------------------------
| Linux Kernel |
-------------------------------------------
| Hardware |
-------------------------------------------
Linux namespace 机制提供一种资源隔离和虚拟化技术。如上图中 Namespace1
和 Namespace2
中经常需要有单独的 pid 命名空间,他们分别有自己的 init 进程(PID 为1)。
namespace 分类
Linux 从 Kernel 2.6 版本开始,逐步添加 Namespace
技术支持,Namespace 的 6 大类型如下:
namespace |
系统调用参数 |
Kernel Version |
作用 |
MNT(Mount) Namespaces |
CLONE_NEWNS |
Linux 2.4.19 |
提供磁盘挂载点和文件系统的隔离能力 |
UTS(Unix Timesharing System) Namespaces |
CLONE_NEWUTS |
Linux 2.6.19 |
提供独立的 hostname 和 domainname |
IPC(Inter Process Communication) Namespaces |
CLONE_NEWIPC |
Linux 2.6.19 |
提供进程间通信的隔离能力,包括消息队列,共享内存和信号量 |
PID(Process ID) Namespaces |
CLONE_NEWPID |
Linux 2.6.19 |
提供进程隔离能力 |
Net(Network) Namespaces |
CLONE_NEWNET |
Linux 2.6.24 ~ 2.6.29 |
网络隔离能力,提供独立的网络栈 |
User Namespaces |
CLONE_NEWUSER |
Linux 2.6.23 ~ 3.8 |
提供用户和用户组隔离能力 |
cgroup(control group)
为进程有一个独立的 cgroup 控制组
示例
# 宿主机
$ uname -a
Linux ubuntu 4.4.0-31-generic #50-Ubuntu SMP Wed Jul 13 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
# 获取当前进程PID
$ echo $$
2363
# 当前进程 namespace 信息
$ ls -lh /proc/$$/ns/
total 0
lrwxrwxrwx 1 root root 0 Jun 3 19:54 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jun 3 19:54 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Jun 3 19:54 mnt -> 'mnt:[4026531840]' # 不同 ns 的编号不同
lrwxrwxrwx 1 root root 0 Jun 3 19:54 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Jun 3 19:54 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 19:54 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 19:54 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jun 3 19:54 uts -> 'uts:[4026531838]'
使用 lsns
命令查看当前系统有哪些 namespace
,下面展示了系统默认的命令空间:
$ lsns
NS TYPE NPROCS PID USER COMMAND
4026531835 cgroup 330 1 root /sbin/init splash
4026531836 pid 330 1 root /sbin/init splash
4026531837 user 329 1 root /sbin/init splash
4026531838 uts 327 1 root /sbin/init splash
4026531839 ipc 330 1 root /sbin/init splash
4026531840 mnt 317 1 root /sbin/init splash
4026532000 net 329 1 root /sbin/init splash
MNT Namespace
# create tmp dir
mkdir /tmp/testdir
# mount tmpfs
mount -t tmpfs tmpfs /tmp/testdir
# touch file
cd /tmp/testdir
touch 1 2 3
$ unshare --mount /bin/bash
$ echo $$
27290 # 新的进程
$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jun 3 20:00 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jun 3 20:00 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Jun 3 20:00 mnt -> 'mnt:[4026532695]' # 与没有隔离的 4026531840 不同
lrwxrwxrwx 1 root root 0 Jun 3 20:00 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Jun 3 20:00 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:00 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:00 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jun 3 20:00 uts -> 'uts:[4026531838]'
# 进程隶属关系 2363 是 27290 的父进程
$ ps -ef |grep 27290 |grep -v grep
root 27290 2363 0 20:00 pts/0 00:00:00 /bin/bash
root 27336 27290 0 20:02 pts/0 00:00:00 ps -ef
$ pstree 2363
bash───bash───pstree
$ pstree 27290
bash───pstree
# mount tmpfs
mount -t tmpfs tmpfs /tmp/testdir
# 查看文件
$ ls -lh
total 0
结论:使用 unshare -m
或者 unshare –mnt
创建了隔离的 namespace。
IPC namespace
$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
# 创建隔离的ipc namespace
unshare --ipc /bin/bash
# 查看
$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jun 3 20:11 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jun 3 20:11 ipc -> 'ipc:[4026532695]' # id 变化
lrwxrwxrwx 1 root root 0 Jun 3 20:11 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Jun 3 20:11 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Jun 3 20:11 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:11 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:11 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jun 3 20:11 uts -> 'uts:[4026531838]'
# 创建消息队列
$ ipcmk --queue
# 查看
$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
0x22b527a3 0 root 644 0 0 # 新建
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
退出,在原有中查看不到新建的 queue
$ exit
$ echo $$
2363
$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
UTS namespace
$ hostname
vm
$ echo $$
27458
$ ls -lh /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jun 3 20:15 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jun 3 20:15 uts -> 'uts:[4026532695]' # 与之前 id 不同
# 修改 hostname
$ hostname
vm
$ hostname abc
$ hostname
abc
退出,在原有中查看不到新建的 queue
$ exit
$ echo $$
2363
$ hostname
PID namespace
# 隔离
$ unshare --fork --pid /bin/bash
# 进程信息为 1,证明时新的 pid
$ echo $$
1
$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jun 3 20:03 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jun 3 20:03 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Jun 3 20:03 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Jun 3 20:03 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 Jun 3 20:03 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:24 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jun 3 20:03 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jun 3 20:03 uts -> 'uts:[4026531838]'
USER namespace
$ id
uid=0(root) gid=0(root) groups=0(root)
$ unshare --user /bin/bash
$ echo $$
27684
$ ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 net -> 'net:[4026531992]'
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 user -> 'user:[4026532695]' # 与之前 id 不同
lrwxrwxrwx 1 nobody nogroup 0 Jun 3 20:21 uts -> 'uts:[4026531838]'
# 通过 id 证明在不同的 user ns 中
$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)