Docker 基础技术之 Linux namespace 详解

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),大家可以关注我的公众号阅读,那里的阅读体验会更好一些。

PS:对云计算感兴趣的小伙伴可以关注我的微信公众号:aCloudDeveloper,专注云计算领域,坚持分享干货。

原文地址:https://www.cnblogs.com/bakari/p/8560437.html

时间: 2024-10-19 18:01:54

Docker 基础技术之 Linux namespace 详解的相关文章

Docker基础技术:Linux Namespace(上)

导读 时下最热的技术莫过于Docker了,很多人都觉得Docker是个新技术,其实不然,Docker除了其编程语言用go比较新外,其实它还真不是个新东西,也就是个新瓶装旧酒的东西,所谓的The New “Old Stuff”.Docker和Docker衍生的东西用到了很多很酷的技术,我会用几篇 文章来把这些技术给大家做个介绍,希望通过这些文章大家可以自己打造一个山寨版的docker.先从Linux Namespace开始. 简介 Linux Namespace是Linux提供的一种内核级别环境隔

Docker 基础技术:Linux Namespace(下)

导读 在Docker基础技术:Linux Namespace(上篇)中我们了解了,UTD.IPC.PID.Mount 四个namespace,我们模仿Docker做了一个相当相当山寨的镜像.在这一篇中,主要想向大家介绍Linux的User和Network的Namespace User Namespace User Namespace主要是用了CLONE_NEWUSER的参数,使用了这个参数后,内部看到的UID和GID已经与外部不同了.默认情况下容器没有的UID,系统自动设置上了最大的UID655

Docker基础技术:Linux Namespace(下)

在 Docker基础技术:Linux Namespace(上篇)中我们了解了,UTD.IPC.PID.Mount 四个namespace,我们模仿Docker做了一个相当相当山寨的镜像.在这一篇中,主要想向大家介绍Linux的User和Network的Namespace. 好,下面我们就介绍一下还剩下的这两个Namespace. User Namespace User Namespace主要是用了CLONE_NEWUSER的参数.使用了这个参数后,内部看到的UID和GID已经与外部不同了,默认显

Docker基础技术:Linux CGroup

前面,我们介绍了Linux Namespace,但是Namespace解决的问题主要是环境隔离的问题,这只是虚拟化中最最基础的一步,我们还需要解决对计算机资源使用上的隔离.也就是说,虽然你通过Namespace把我Jail到一个特定的环境中去了,但是我在其中的进程使用用CPU.内存.磁盘等这些计算资源其实还是可以随心所欲的.所以,我们希望对进程进行资源利用上的限制或控制.这就是Linux CGroup出来了的原因. Linux CGroup全称Linux Control Group, 是Linu

Docker虚拟化技术系列之-命令详解

虚拟化原理:虚拟化解决方案的底部是要进行虚拟化的物理机器.这台机器可能直接支持虚拟化,也可能不会直接支持虚拟化:那么就需要系统管理程序 层的支持.系统管理程序(Virtual machine monitor),或称为 VMM,可以看作是平台硬件和操作系统的抽象化.在某些情况中,这个系统管理程序就是一个操作系统:此时,它就称为主机操作系统. 随着docker不断的学习,我们要想进一步去维护docker,就需要掌握docker日常使用的命令,如下为docker常用命令: 一.容器基础命令 docke

高可用集群技术之corosync应用详解(一)

Corosync概述: Corosync是集群管理套件的一部分,它在传递信息的时候可以通过一个简单的配置文件来定义信息传递的方式和协议等.它是一个新兴的软件,2008年推出,但其实它并不是一个真正意义上的新软件,在2002年的时候有一个项目Openais , 它由于过大,分裂为两个子项目,其中可以实现HA心跳信息传输的功能就是Corosync ,它的代码60%左右来源于Openais. Corosync可以提供一个完整的HA功能,但是要实现更多,更复杂的功能,那就需要使用Openais了.Cor

Linux 目录详解 树状目录结构图

1.树状目录结构图 2./目录 目录 描述 / 第一层次结构的根.整个文件系统层次结构的根目录. /bin/ 需要在单用户模式可用的必要命令(可执行文件):面向所有用户,例如:cat.ls.cp,和/usr/bin类似. /boot/ 引导程序文件,例如:kernel.initrd:时常是一个单独的分区[6] /dev/ 必要设备, 例如:, /dev/null. /etc/ 特定主机,系统范围内的配置文件. 关于这个名称目前有争议.在贝尔实验室关于UNIX实现文档的早期版本中,/etc 被称为

linux awk详解与应用

文章来自于本人个人博客: linux awk详解与应用 1.awk awk是一个强大的文本分析工具,它可以通过分析文本来生成一个数据报告.它的原理就是读取每行的输入,然后按照分隔符切分(默认是空格),再进行定制计算. awk '{print $1}' /etc/passwd   #打印出passwd文件的所有行的第一列 这是awk的基础语法,在awk中$n代表列数,即$1--第一列,$2---第二列....,但是$0代表整行 接下来我们按照指定的分隔符打印数据: awk -F ':' '{pri

Linux权限详解,多用户多组各种权限配置原理

网上有太多关于Linux权限详解,这里不啰嗦那些.主要解释下这些权限是杂用的,否则知道了什么用户.组之类的权限也配不好. 一.权限分类 r:读取权限,数字代号为"4". w:写入权限,数字代号为"2". x:执行或切换权限,数字代号为"1". -:不具任何权限,数字代号为"0". linux内每一段为r+w+x的值,最大777,见下图 二.权限在何时使用 目录写权限:可以增删目录内所有内容,改文件和目录名. 目录读权限:可以l