前言
Docker 是“新瓶装旧酒”的产物,依赖于 Linux 内核技术 chroot 、namespace 和 cgroup。本篇先来看 namespace 技术。
Docker 和虚拟机技术一样,从操作系统级上实现了资源的隔离,它本质上是宿主机上的进程(容器进程),所以资源隔离主要就是指进程资源的隔离。实现资源隔离的核心技术就是 Linux namespace。这技术和很多语言的命名空间的设计思想是一致的(如 C++ 的 namespace)。
隔离意味着可以抽象出多个轻量级的内核(容器进程),这些进程可以充分利用宿主机的资源,宿主机有的资源容器进程都可以享有,但彼此之间是隔离的,同样,不同容器进程之间使用资源也是隔离的,这样,彼此之间进行相同的操作,都不会互相干扰,安全性得到保障。
为了支持这些特性,Linux namespace 实现了 6 项资源隔离,基本上涵盖了一个小型操作系统的运行要素,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。
这 6 项资源隔离分别对应 6 种系统调用,通过传入上表中的参数,调用 clone()
函数来完成。
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
clone()
函数相信大家都不陌生了,它是 fork()
函数更通用的实现方式,通过调用 clone()
,并传入需要隔离资源对应的参数,就可以建立一个容器了(隔离什么我们自己控制)。
一个容器进程也可以再 clone()
出一个容器进程,这是容器的嵌套。
如果想要查看当前进程下有哪些 namespace 隔离,可以查看文件 /proc/[pid]/ns (注:该方法仅限于 3.8 版本以后的内核)。
可以看到,每一项 namespace 都附带一个编号,这是唯一标识 namespace 的,如果两个进程指向的 namespace 编号相同,则表示它们同在该 namespace 下。同时也注意到,多了一个 cgroup,这个 namespace 是 4.6 版本的内核才支持的。Docker 目前对它的支持普及度还不高。所以我们暂时先不考虑它。
下面通过简单的代码来实现 6 种 namespace 的隔离效果,让大家有个直观的印象。
UTS namespace
UTS namespace 提供了主机名和域名的隔离,这样每个容器就拥有独立的主机名和域名了,在网络上就可以被视为一个独立的节点,在容器中对 hostname 的命名不会对宿主机造成任何影响。
首先,先看总体的代码骨架:
#define _GNU_SOURCE #include <sys/types.h> #include <sys/wait.h> #include <stdio.h> #include <sched.h> #include <signal.h> #include <unistd.h> #define STACK_SIZE (1024 * 1024) static char container_stack[STACK_SIZE]; char* const container_args[] = { "/bin/bash", NULL }; // 容器进程运行的程序主函数 int container_main(void *args) { printf("在容器进程中!\n"); execv(container_args[0], container_args); // 执行/bin/bash return 1; } int main(int args, char *argv[]) { printf("程序开始\n"); // clone 容器进程 int container_pid = clone(container_main, container_stack + STACK_SIZE, SIGCHLD, NULL); // 等待容器进程结束 waitpid(container_pid, NULL, 0); return 0; }
该程序骨架调用 clone()
函数实现了子进程的创建工作,并定义子进程的执行函数,clone()
第二个参数指定了子进程运行的栈空间大小,第三个参数即为创建不同 namespace 隔离的关键。
对于 UTS namespace,传入 CLONE_NEWUTS,如下:
int container_pid = clone(container_main, container_stack + STACK_SIZE, SIGCHLD | CLONE_NEWUTS, NULL);
为了能够看出容器内和容器外主机名的变化,我们子进程执行函数中加入:
sethostname("container", 9);
最终运行可以看到效果如下:
IPC namespace
IPC namespace 实现了进程间通信的隔离,包括常见的几种进程间通信机制,如信号量,消息队列和共享内存。我们知道,要完成 IPC,需要申请一个全局唯一的标识符,即 IPC 标识符,所以 IPC 资源隔离主要完成的就是隔离 IPC 标识符。
同样,代码修改仅需要加入参数 CLONE_NEWIPC 即可,如下:
int container_pid = clone(container_main, container_stack + STACK_SIZE, SIGCHLD | CLONE_NEWUTS | CLONE_NEWIPC, NULL);
为了看出变化,首先在宿主机上建立一个消息队列:
然后运行程序,进入容器查看 IPC,没有找到原先建立的 IPC 标识,达到了 IPC 隔离。
PID namespace
PID namespace 完成的是进程号的隔离,同样在 clone()
中加入 CLONE_NEWPID 参数,如:
int container_pid = clone(container_main, container_stack + STACK_SIZE, SIGCHLD | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID, NULL);
效果如下,echo $$
输出 shell 的 PID 号,发生了变化。
但是对于 ps/top 之类命令却没有改变:
具体的原因和接下来的内容(包括 mount namespace,network namespace 和 user namespace),大家可以关注我的公众号阅读,那里的阅读体验会更好一些。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]