Linux容器实现原理简单探究(一)

由 liuchangfgv 发布

Linux容器在实际使用中非常常见,docker、LXC都是使用了容器技术,本系列目的就是探究Linux容器的实现。
一般情况下,Linux容器使用到了以下几个内核的功能:

  • namespace
  • cgroup
  • seccomp

各个功能用处如下

namespace

它是Linux内核的对资源进行分区的技术,使得一组进程只能看到相应组的资源,现有以下几个namespace:

Mount(mnt)

mnt namespace可以实现挂载点的隔离,在创建mnt namespace后新的名字空间会复制先前名字空间的挂载点,之后便可以挂载一些挂载点使其在原来的名字空间中不可见。
这里说的是可以,因为Linux上的挂载点具有不同的传播类型,现有如下几个:

  • MS_SHARED
    共享挂载,通过这种方式挂载会影响其他名字空间。
  • MS_PRIVATE
    私有挂载,通过这种方式挂载不会对其他名字空间有任何影响。
  • MS_SLAVE
    重属挂载,父名字空间内改变会影响到子名字空间,但子名字空间无法直接修改
  • MS_UNBINDABLE
    不可解除挂载,通过这种方式,即使父名字空间解除了挂载,子名字空间中仍然存在

我们可以使用cat /proc/self/mountinfo来查看当前进程(猜猜当前进程指的是那个进程)所在名字空间的挂载点信息,可以看到,基本每行都能看到sharer:[n](其中[n]为数字),因此在我们新创建名字空间的时候,这些挂载点便会共享到新的名字空间中。
如果我们在docker容器中运行这条命令呢?这样你会发现,没有了刚刚说的那种特征,这以为着这些挂载点是私有的,因此在上级的名字空间中你看不到这些挂载点,相反,如果这个挂载点是共享的,那么我们在父名字空间中看到这个挂载点,在之后代码我们可以试试。

Process ID(pid)

pid namespace,可以让进程的pid在新的名字空间中重新编号,但实际上这就是一种重新映射,我们可以开一个debian的docker容器,通过以下指令来查看这一映射:
1.在容器中执行sleep 10000
2.利用docker exec来为此容器创建一个新的shell,输入ps -ef | grep "sleep 10000"
3.在主机上,执行同样的指令
在容器中执行,可以看到类似的结果:

root         216       1  0 06:54 pts/0    00:00:00 sleep 10000
root         218       8  0 06:54 pts/1    00:00:00 grep sleep 10000

但在主机上运行,结果是这样的:

root       60683   59675  0 14:54 pts/0    00:00:00 sleep 10000
lc         60689   59430  0 14:54 pts/2    00:00:00 grep sleep 10000

可以看出,容器中的进程上层名字空间内不仅可见,而且pid也和上级不同。
我们在主机上可以继续执行以下指令,来看出这个进程在另一个名字空间中:

sudo ls -la /proc/60683/ns/

输出结果如下:

dr-x--x--x 2 root root 0  2月20日 14:57 .
dr-xr-xr-x 9 root root 0  2月20日 14:54 ..
lrwxrwxrwx 1 root root 0  2月20日 14:57 cgroup -> 'cgroup:[4026533635]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 ipc -> 'ipc:[4026533573]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 mnt -> 'mnt:[4026533188]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 net -> 'net:[4026533576]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 pid -> 'pid:[4026533575]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 pid_for_children -> 'pid:[4026533575]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 uts -> 'uts:[4026533570]'

我们再查看当前进程所处的名字空间:

sudo ls -la /proc/self/ns/
总计 0
dr-x--x--x 2 root root 0  2月20日 14:57 .
dr-xr-xr-x 9 root root 0  2月20日 14:57 ..
lrwxrwxrwx 1 root root 0  2月20日 14:57 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 mnt -> 'mnt:[4026531841]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 net -> 'net:[4026531840]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 uts -> 'uts:[4026531838]'

也可以继续查看其他进程:

sudo ls -la /proc/1/ns/
总计 0
dr-x--x--x 2 root root 0  2月20日 10:36 .
dr-xr-xr-x 9 root root 0  2月20日 10:36 ..
lrwxrwxrwx 1 root root 0  2月20日 10:36 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 mnt -> 'mnt:[4026531841]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 net -> 'net:[4026531840]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0  2月20日 14:57 uts -> 'uts:[4026531838]'

可以看到,处于统一名字空间的self(当前进程)和1目录下的链接目标相同,但在容器中的却不同,你也可以拿其他的进程做测试。
除此之外,pid namespace还有一特征,进入容器的第一个进程pid为1(当然是在子名字空间中),这一点可以在docker进入容器最初始的shell上看到:

root@7cd6bdd0d303:/# echo $$
1

这一特征也能帮我们在容器中运行systemd等(systemd要求自身pid必须为1,即对应实体机开机后的第一个用户态进程或由其exec产生的进程)服务管理工具。

Network(net)

Inter-process Communication(ipc)

UTS

User ID(user)

cgroup

Time


仅有一条评论

  1. fish
    fish · 2024-05-12 22:36

    二呢?

发表评论