Featured image of post Namespace资源隔离原理浅析(概述)

Namespace资源隔离原理浅析(概述)

容器引擎通过Linux内核技术(Namespace)对容器的资源进行隔离,本文通过阅读Linux进程内核源码,浅析Namespace隔离资源的原理

Namespace资源隔离原理浅析

单个容器本身,其本质就是Linux系统中的一个进程,但是在容器进程环境中,我们所使用的环境和宿主机本身其实是两套不同的环境。这主要是因为容器技术实现了资源层面上的限制和隔离,这两个功能分别依赖于Linux系统内核中所提供的cgroup和Namespace技术。本文主要讨论Namespace隔离Linux进程资源的原理,cgroup限制Linux资源的原理将在另一篇博文中做介绍。

总体来说,Namespace是Linux内核的一项特性,它可以对内核资源进行区分,使得一个进程或同一组进程只能看到一组资源,而另一组进程看到的是不同的资源。从版本号为3.8的内核开始,目录/proc/[pid]/ns目录下会展示进程所属的所有Namespace信息,使用ls -al /proc/$$/ns查看当前进程的所属Namespace信息,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[root@localhost ~]# ls -al /proc/$$/ns
总用量 0
dr-x--x--x. 2 root root 0 5月   4 17:29 .
dr-xr-xr-x. 9 root root 0 5月   4 15:06 ..
lrwxrwxrwx. 1 root root 0 5月   4 17:29 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 5月   4 17:29 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 5月   4 17:29 net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0 5月   4 17:29 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 5月   4 17:29 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 5月   4 17:29 uts -> uts:[4026531838]

需要注意的是,这些Namespace文件都是链接文件,文件的内容格式为type:[inode number],其中type为Namespace的类型,inode number则是用来标识唯一一个Namespace,也可以将这个inode number理解为Namespace的ID。如果两个进程的某个Namespace同时指向了同一个链接文件,说明这两个进程相关的资源在同一个Namespace中。

而在Linux内核源码中,进程结构体task_struct很好的体现了进程和Namespace的关联关系。我们以linux-3.10.0-1160.el7版本的内核源码作为示例代码,task_struct的具体定义如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// file:include/linux/sched.h,只贴出本文关注的属性
struct task_struct {
    // 进程状态
    volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */

    ...

    // 进程线程的pid
    pid_t pid;
	pid_t tgid;

    ...

    // 进程树关系描述:父进程、子进程、兄弟进程
    struct task_struct __rcu *real_parent; /* real parent process */
    struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
    /*
     * children/sibling forms the list of my natural children
     */
    struct list_head children;	/* list of my children */
    struct list_head sibling;	/* linkage in my parent's children list */
    struct task_struct *group_leader;	/* threadgroup leader */

    ...

    // 进程调度优先级
    int prio, static_prio, normal_prio;
    unsigned int rt_priority;

    ...

    // 进程地址空间
    struct mm_struct *mm, *active_mm;

    ...

    // 进程文件系统信息(当前目录)
    struct fs_struct *fs;
    // 进程打开的文件信息
    struct files_struct *files;

    ...

    // 进程所属的Namespace
    struct nsproxy *nsproxy;
}

我们进一步关注和Namespace直接相关的最后一个属性nsproxy,其具体定义如下所示:

1
2
3
4
5
6
7
8
9
// file:inclue/linux/nsproxy.h
struct nsproxy {
    atomic_t count;
    struct uts_namespace *uts_ns; // 提供主机名隔离能力
    struct ipc_namespace *ipc_ns; // 提供进程间通信的隔离能力
    struct mnt_namespace *mnt_ns; // 提供磁盘挂载点和文件系统的隔离能力
    struct pid_namespace *pid_ns; // 提供进程隔离能力
    struct net 	     *net_ns;     // 提供网络隔离能力
};

从内核源码可以看出,每当一个进程创建完成后,各类Namespace都会作为这个进程的属性伴随整个进程的生命周期,直到进程被杀死,对应的Namespace也就会被释放。容器化技术中,通过Namespace技术实现容器运行环境隔离的本质,其实是Linux中,对进程运行环境的隔离。到此为止,已经大概明白了容器隔离的本质,但各类Namespace实现各类资源的隔离本质还需要继续深挖。

Built with Hugo
主题 StackJimmy 设计