【Docker 内核详解】namespace 资源隔离(一):进行 namespace API 操作的 4 种方式

这篇具有很好参考价值的文章主要介绍了【Docker 内核详解】namespace 资源隔离(一):进行 namespace API 操作的 4 种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Docker 内核详解 - namespace 资源隔离】系列包含:

  • namespace 资源隔离(一):进行 namespace API 操作的 4 种方式
  • namespace 资源隔离(二):UTS namespace & IPC namespace
  • namespace 资源隔离(三):PID namespace
  • namespace 资源隔离(四):Mount namespace & Network namespace
  • namespace 资源隔离(五):User namespaces


当谈论 Docker 时,常常会聊到 Docker 的实现方式。很多开发者都知道,Docker 容器本质上是宿主机上的进程(容器所在的运行环境统一称为宿主机)。Docker 通过 namespace 实现了 资源隔离,通过 cgroups 实现了 资源限制,通过写时复制机制( copy-on-write)实现了 高效的文件操作。但当更进一步深入 namespacecgroups 等技术细节时,大部分开发者都会感到茫然无措。所以在这里,希望先带领大家走进 Linux 内核,了解 namespacecgroups 的技术细节。

Docker 大热之后,热衷技术的开发者就会思考,想要实现一个资源隔离的容器,应该从哪些方面下手?也许第一反应就是 chroot 命令,这条命令给用户最直观的感受就是在使用后根目录 / 的挂载点切换了,即 文件系统 被隔离了。接着,为了在分布式的环境下进行通信和定位,容器必然要有独立的 IP、端口、路由等,自然就联想到了 网络 的隔离。同时,容器还需要一个独立的 主机名 以便在网络中标识自己。有了网络,自然离不开通信,也就想到了 进程间通信 需要隔离。开发者可能也已经想到了权限的问题,对用户和用户组的隔离就实现了 用户权限 的隔离。最后,运行在容器中的应用需要有进程号(PID),自然也需要与宿主机中的 PID 进行隔离。

由此,基本上完成了一个容器所需要做的 6 项隔离,Linux 内核中提供了这 6 种 namespace 隔离的系统调用,如下表所示。当然,真正的容器还需要处理许多其他工作。

namespace 系统调用参数 隔离内容
UTS CLONE_ NEWUTS 主机名与域名
IPC CLONE_NEWIPC 信号量、消息队列和共享内存
PID CLONE_NEWPID 进程编号
Network CLONE_NEWNET 网络设备、网络栈、端口等
Mount CLONE_NEWNS 挂载点(文件系统)
User CLONE_NEWUSER 用户和用户组

实际上,Linux 内核实现 namespace 的一个主要目的,就是实现轻量级虚拟化(容器)服务。在同一个 namespace 下的进程可以感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,仿佛自己置身于一个独立的系统环境中,以达到独立和隔离的目的。

需要说明的是,本文所讨论的 namespace 实现针对的均是 Linux 内核 3.8 3.8 3.8 及以后的版本(user namespace 在内核 3.8 3.8 3.8 版本以后才支持)。接下来,将首先介绍使用 namespace 的 API,然后对这 6 种 namespace 进行逐一讲解,并通过程序让读者切身感受隔离效果。

namespace 的 API 包括 clone()setns() 以及unshare(),还有 /proc 下的部分文件。为了确定隔离的到底是哪 6 项 namespace,在使用这些 API 时,通常需要指定以下 6 个参数中的一个或多个,通过 |(位或)操作来实现。从上表可知,这 6 个参数分别是 CLONE_NEWIPCCLONE_NEWNSCLONE_NEWNETCLONE_NEWPIDCLONE_NEWUSERCLONE_NEWUTS

1.通过 clone() 在创建新进程的同时创建 namespace

使用 clone() 来创建一个独立 namespace 的进程,是最常见的做法,也是 Docker 使用 namespace 最基本的方法,它的调用方式如下。

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

clone() 实际上是 Linux 系统调用 fork() 的一种更通用的实现方式,它可以通过 flags 来控制使用多少功能。一共有 20 多种 CLONE_*flag(标志位)参数用来控制 clone 进程的方方面面(如是否与父进程共享虚拟内存等),下面挑选与 namespace 相关的 4 个参数进行说明。

  • child func 传入子进程运行的程序主函数。
  • child stack 传入子进程使用的栈空间。
  • flags 表示使用哪些 CLONE_* 标志位,与 namespace 相关的主要包括 CLONE_NEWIPCCLONE_NEWNSCLONE_NEWNETCLONE_NEWPIDCLONE_NEWUSERCLONE_NEWUTS
  • args 则可用于传入用户参数。

2.查看 /proc/[pid]/ns 文件

3.8 3.8 3.8 版本的内核开始,用户就可以在 /proc/[pid]/ns 文件下看到指向不同 namespace 号的文件,效果如下所示,形如 [4026531839] 者即为 namespace 号。

$ ls -l /proc/$$/ns         <<--$$是shell中表示当前运行的进程ID号
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 user->user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 uts -> uts:[4026531838]

如果两个进程指向的 namespace 编号相同,就说明它们在同一个 namespace 下,否则便在不同 namespace 里面。/proc/[pid]/ns 里设置这些 link 的另外一个作用是,一旦上述 link 文件被打开,只要打开的 文件描述符fd)存在,那么就算该 namespace 下的所有进程都已经结束,这个 namespace 也会一直存在,后续进程也可以再加入进来。在 Docker 中,通过文件描述符定位和加入一个存在的 namespace 是最基本的方式。

另外,把 /proc/[pid]/ns 目录文件使用 --bind 方式挂载起来可以起到同样的作用,命令如下:

# touch ~/uts
# mount --bind /proc/27514/ns/uts ~/uts

为了方便起见,后面的讲解中会使用这个 ~/uts 文件来代替 /proc/27514/ns/uts

注意:如果大家看到 ns 下的内容与本节所述不符,那可能是因为使用了 3.8 3.8 3.8 以前版本的内核。如在内核版本 2.6 2.6 2.6 中,该目录下存在的只有 ipcnetuts,并且以硬链接方式存在。

3.通过 setns() 加入一个已经存在的 namespace

上文提到,在进程都结束的情况下,也可以通过挂载的形式把 namespace 保留下来,保留 namespace 的目的是为以后有进程加入做准备。在 Docker 中,使用 docker exec 命令在已经运行着的容器中执行一个新的命令,就需要用到该方法。通过 setns() 系统调用,进程从原先的 namespace 加入某个已经存在的 namespace,使用方法如下。通常为了不影响进程的调用者,也为了使新加入的 pid namespace 生效,会在 setns() 函数执行后使用 clone() 创建子进程继续执行命令,让原先的进程结束运行。

int setns(int fd, int nstype);
  • 参数 fd 表示要加入 namespace 的文件描述符。上文提到,它是一个指向 /proc/[pid]/ns 目录的文件描述符,可以通过直接打开该目录下的链接或者打开一个挂载了该目录下链接的文件得到。
  • 参数 nstype 让调用者可以检查 fd 指向的 namespace 类型是否符合实际要求。该参数为 0 表示不检查。

为了把新加入的 namespace 利用起来,需要引入 execve() 系列函数,该函数可以执行用户命令,最常用的就是调用 /bin/bash 并接受参数,运行起一个 shell,用法如下。

fd = open(argv[1], O_RDONLY);    /* 获取 namespace 文件描述符 */
setns(fd, 0);                    /* 加入新的 namespace */
execvp(argv[2], &argv[2]);       /* 执行程序 */

假设编译后的程序名称为 setns-test

# ./setns-test ~/uts /bin/bash      # ~/uts 是绑定的 /proc/27514/ns/uts

至此,就可以在新加入的 namespace 中执行 shell 命令了,下文会多次使用这种方式来演示隔离的效果。

4.通过 unshare() 在原先进程上进行 namespace 隔离

最后要说明的系统调用是 unshare(),它与 clone() 很像,不同的是,unshare() 运行在原先的进程上,不需要启动一个新进程。

int unshare(int flags);

调用 unshare() 的主要作用就是,不启动新进程就可以起到隔离的效果,相当于跳出原先的 namespace 进行操作。这样,就可以在原进程进行一些需要隔离的操作。Linux 中自带的 unshare 命令,就是通过 unshare() 系统调用实现的。Docker 目前并没有使用这个系统调用,这里不做展开,读者可以自行查阅资料学习该命令的知识。

5.fork() 系统调用(拓展)

系统调用函数 fork() 并不属于 namespace 的 API,这部分内容属于延伸阅读,如果读者已经对 fork() 有足够多的了解,可以忽略该部分。

当程序调用 fork() 函数时,系统会创建新的进程,为其分配资源,例如存储数据和代码的空间,然后把原来进程的所有值都复制到新进程中,只有少量数值与原来的进程值不同,相当于复制了本身。那么程序的后续代码逻辑要如何区分自己是新进程还是父进程呢?

fork() 的神奇之处在于它仅仅被调用一次,却能够返回两次(父进程与子进程各返回一次),通过返回值的不同就可以区分父进程与子进程。它可能有以下 3 种不同的返回值:

  • 在父进程中,fork() 返回新创建子进程的进程 ID;
  • 在子进程中,fork() 返回 0;
  • 如果出现错误,fork() 返回一个负值。

下面给出一段实例代码,命名为 fork_example.c

#include <unistd.h>
#include <stdio.h>
int main (){
	pid_t fpid; // fpid 表示 fork 函数返回的值
	int count=0;
	fpid=fork();
	if (fpid < 0) printf("error in fork!");
	else if (fpid == 0) {
		printf("I am child. Process id is %d\n",getpid());
	}
	else {
		printf("i am parent. Process id is %d\n",getpid());
	}	
	return 0;
}

编译并执行,结果如下。

[root@local:~#] gcc -Wall fork_example.c && ./a.out
I am parent. Process id is 28365
I am child. Process id is 28366

代码执行过程中,在语句 fpid=fork() 之前,只有一个进程在执行这段代码,在这条语句之后,就变成父进程和子进程同时执行了。这两个进程几乎完全相同,将要执行的下一条语句都是 if (fpid < 0),同时 fpid=fork() 的返回值会依据所属进程返回不同的值。

使用 fork() 后,父进程有义务监控子进程的运行状态,并在子进程退出后自己才能正常退出,否则子进程就会成为 “孤儿” 进程。


后续将根据 Docker 内部对 namespace 资源隔离使用的方式分别对 6 种 namespace 进行详细的解析。文章来源地址https://www.toymoban.com/news/detail-739806.html

到了这里,关于【Docker 内核详解】namespace 资源隔离(一):进行 namespace API 操作的 4 种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Docker】0.空间资源隔离

    NameSpace Actual Conbat 打开shell窗口A,执行df -h 查看主机默认命名空间的磁盘挂载情况 打开一个新的shell窗口,执行mount隔离命令 添加新的磁盘挂载,并添加文件 分别在两个窗口中查看挂载信息,并查看文件。可以观察到两者观察到的目录内文件并不相同,说明我们实现了文件系

    2024年02月08日
    浏览(35)
  • docker的底层原理四: 资源隔离

    概述 :Docker 使用 Linux 内核的 Namespace 和 Cgroups 技术来实现资源隔离。这些技术保证了在宿主机上运行的每个 Docker 容器都是相互独立的,它们各自拥有自己的资源,并且不会互相干扰。以下是 Docker 资源隔离机制的一些具体细节: CPU 隔离 :通过 Cgroups 技术,Docker 可以对 C

    2024年02月20日
    浏览(41)
  • k8s资源管理命令与Namespace使用详解

    目录 一、前言 二、k8s概述 三、k8s常用操作管理命令 3.1 kubectl 命令用法

    2023年04月16日
    浏览(53)
  • 07-Docker 安全:基于内核的弱隔离系统如何保障安全性?

    在讨论容器的安全性之前,我们先了解下容器与虚拟机的区别,这样可以帮助我们更好地了解容器的安全隐患以及如何加固容器安全。 Docker 与虚拟机区别 Docker 是基于 Linux 内核的 Namespace 技术实现资源隔离的,所有的容器都共享主机的内核。其实这与以虚拟机为代表的云计算

    2024年01月17日
    浏览(62)
  • Linux Cgroups进程资源限制管理 之 资源子系统限制/控制、Docker资源隔离与限制原理解读

    Linux cgroups(控制组)最初由Google工程师Paul Menage在2006年提出,并在Linux内核的2.6.24版本中首次引入。自那时以来,cgroups一直是Linux内核的一部分,并在容器化技术等领域中发挥着至关重要的作用。随着时间的推移,cgroups功能不断得到改进和扩展,以满足对资源管理和隔离性能

    2024年02月21日
    浏览(43)
  • Docker 基础知识解析:容器与传统虚拟化对比:资源利用、启动时间、隔离性和部署效率

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月16日
    浏览(68)
  • linux 隔离内核

    1、 编辑grub 2、修改 or 3、update 4、查看更改 最终还是要重启系统。

    2024年02月14日
    浏览(36)
  • k8s资源对象(1) namespace&&pod

    Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下,kubernetes集群中的所有的Pod都是可以相互访问的。但是在实际中,可能不想让两个Pod之间进行互相的访问,那此时就可以将两个Pod划分到不同的

    2024年01月18日
    浏览(52)
  • 解决华为手机驱动与Win11的内核隔离冲突问题

    将华为手机通过USB数据线连接电脑时,会安装相应的驱动,这部分驱动会引起Win11提示内存隔离冲突,可能会影响系统更新或一些软件更新安装。 通过Win11系统自带的 pnputil 工具写在相应的区间,指令范例: 1、通过 win+R 运行cmd 2、以管理员身份运行命令提示符 执行下述指令

    2024年04月26日
    浏览(56)
  • 电脑重装系统Win11内核隔离无法打开怎么解决?

    内核隔离是通过计算机进程与您的操作系统和设备隔离在一起,为恶意软件和其他攻击提供了增强的保护。近期有部分Win11用户反映自己的电脑无法打开内核隔离,这是怎么回事呢?导致出现这一问题的原因有很多,可能是硬件设备不兼容,又或者是驱动程序不兼容,对此我

    2024年02月11日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包