从零自制docker-11-【pivotRoot切换实现文件系统隔离】

news2024/12/25 9:27:22

文章目录

  • busybox
  • `docker run -d busybox top`
  • `containerId=(docker ps --filter "ancestor=busybox:latest"|grep -v IMAGE|awk '{print $1}')`
  • `docker export -o busybox.tar $containerId` or `sudo docker export 09bbf421d93f > ./busybox.tar `
  • `tar -xvf busybox.tar -C busybox/`
  • mount namespace的共享性
  • 自我挂载
  • `syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")`
  • bind mount
  • 挂载点的私有和mount namespace的私有
  • pivotroot
  • 自己理解
  • 代码
  • 结果

busybox

一个简单的UNIX文件系统,里面很多文件,可以将busybox理解一个文件夹
使用它来作为第一个容器内运行的文件系统。

docker run -d busybox top

docker run -d busybox top 这个命令是用来在Docker中启动一个新的容器并执行特定操作的。下面是对命令各部分的详细解释:

  • docker run: 这是Docker的主要命令之一,用于从指定的镜像启动一个新的容器。

  • -d: 这是一个标志(flag),它告诉Docker以守护进程(detached)模式运行容器。这意味着容器将在后台运行,而不是直接连接到用户的终端。当容器以这种方式运行时,Docker会立即返回容器ID,用户可以继续在命令行中执行其他命令而不影响容器的运行。

  • busybox: 这是镜像的名称,BusyBox是一个轻量级的类Unix工具集合,包含了大量常用命令行工具在一个小巧的镜像中。

  • top: 这是将要在容器内部执行的命令。在Linux系统中,top 命令提供了一个实时的系统状态报告,显示了当前系统中各进程的资源占用情况,比如CPU和内存使用率等。

综上所述,当你执行 docker run -d busybox top 这个命令时,Docker会从BusyBox镜像创建一个新的容器并在该容器内部启动 top 命令,使其成为一个长期运行的进程,从而使得容器持续在后台运行。这种做法常用于测试或者监控目的,让容器内的 top 命令持续展示容器内部的系统状态。

containerId=(docker ps --filter "ancestor=busybox:latest"|grep -v IMAGE|awk '{print $1}')

containerId=(docker ps --filter "ancestor=busybox:latest"|grep -v IMAGE|awk '{print $1}') 这行Shell命令的目的是为了获取基于busybox:latest镜像启动的最新容器ID,并将其赋值给变量containerId。下面逐一分解说明:

  • docker ps: 这是Docker的命令,用于列出所有正在运行的容器及其相关信息。

  • --filter "ancestor=busybox:latest": 这是一个过滤器参数,它会筛选出那些从busybox:latest镜像派生出来的容器。

  • | (管道符号):用于将前面命令的输出作为后面命令的输入。

  • grep -v IMAGE: grep命令用于在文本流中查找匹配项,-v参数表示反向选择,即排除包含指定模式的行。这里过滤掉了输出中描述镜像的那一行,因为我们只需要容器ID。

  • awk '{print $1}': awk是一个强大的文本分析工具,这里使用它提取每行的第一列数据,即容器ID列。

awk '{print $1}' 是一种基于 awk 工具的命令行表达式,用于处理文本数据流并对数据进行操作。在这条命令中:

  • 它读取文件或者输入流(在此案例中是通过管道传递过来的 docker ps 的输出),并按照指定的模式对每一行数据进行处理。

  • {} 中括号内包含了awk命令要执行的动作。

  • print 是awk内置的一个命令,用于输出指定的字段或变量。

  • $1 是awk中的特殊变量,表示每一行记录的第一个字段(字段之间默认由空格或制表符等空白字符分隔)。在许多UNIX/Linux命令的输出格式中,第一列常常包含关键信息,比如在 docker ps 输出中,第一列通常是容器ID。

综合以上命令,它实际上是从正在运行的容器列表中找出最近基于busybox:latest镜像启动的容器,并取出它的ID。然后将这个ID值赋给shell脚本中的变量containerId

最后的 echo "containerId" $containerId 是用来输出变量containerId的值,可以看到实际获得的容器ID。

docker export -o busybox.tar $containerId or sudo docker export 09bbf421d93f > ./busybox.tar

docker export -o busybox.tar $containerId 是一个Docker命令,用于将指定容器的文件系统内容导出为一个tar归档文件。

  • docker export: 这是Docker命令,用于导出容器的文件系统快照。这个命令只会导出容器内的文件系统层次结构,不包括容器的元数据、配置信息或运行时状态。

  • -o busybox.tar: -o 选项后跟的是输出文件的名称,这里是指定将导出的内容保存到名为 busybox.tar 的tar文件中。

  • $containerId: 这是一个变量,代表之前通过命令获取的容器ID。在这个命令中,它指定了要导出的容器的具体实例。

所以,整个命令的意思是:从之前通过 docker run 命令创建并运行的BusyBox容器中,以tar文件的形式导出其文件系统内容,并将导出的文件命名为 busybox.tar。这个tar文件包含了容器内部的所有文件和目录结构,可以用于备份或者在另一个环境中重新导入这个文件系统。

tar -xvf busybox.tar -C busybox/

tar -xvf busybox.tar -C busybox/ 这条命令是用来解压缩并提取 busybox.tar 文件中的内容到指定的目录下的。命令各部分的含义如下:

  • tar: 这是一个在Unix/Linux系统中用于处理档案文件(通常是*.tar文件)的工具,可以用来打包、压缩、解压和列出档案内的文件。

  • -x: 这个选项表示执行解压操作(extract)。它告诉tar工具从指定的tar档案中提取文件。

  • -v: 这是verbose模式,解压过程中会显示详细信息,包括正在解压的文件名,方便用户了解解压进度。

  • -f: 这个选项后面跟着的是要操作的tar文件名,即 busybox.tar。这个参数是必需的,用来指定你想要解压的tar归档文件。

  • -C: 这个选项允许指定解压后文件存放的目录,其后面跟的是目标目录路径 busybox/。这意味着所有从 busybox.tar 中解压出来的文件和目录都将被放在名为 busybox 的目录下。

综上所述,这条命令的整体效果是从 busybox.tar 中提取所有文件,并将它们解压到当前目录下的 busybox/ 子目录中。如果 busybox/ 目录尚不存在,tar 会在解压前创建这个目录。
在这里插入图片描述

mount namespace的共享性

在Linux的mount namespace中挂载一个文件系统,是否会影响宿主机的文件系统视具体情况而定。

  • 默认情况下(在systemd引入之前或设置了MS_SHARED传播类型时)
    如果在新的mount namespace中挂载文件系统,且挂载点是共享的(即挂载传播类型为MS_SHARED),那么在新的mount namespace中的挂载操作将会传播回宿主机以及其他共享该挂载点的mount namespace。这意味着宿主机的文件系统视图会发生相应的变化。

  • 设置为私有挂载(MS_PRIVATE)
    若在创建mount namespace时,将挂载点设为私有(通过syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")命令),那么在这个新的mount namespace中挂载的任何文件系统,将不会影响宿主机或其他未设置为私有的mount namespace。这意味着在新的mount namespace中对文件系统的挂载、卸载操作,只会对该命名空间产生影响,宿主机的文件系统视图将维持不变。

在容器技术如Docker中,通常会创建一个新的mount namespace并将其内部的挂载点设为私有,以确保容器内部的文件系统操作不会影响到宿主机或其他容器。这就实现了容器级别的文件系统隔离。

自我挂载

想象一下,你有一棵大树,树干代表宿主机的根文件系统,树枝代表挂载在其上的各个目录。当你想要创建一个独立的分支(比如 /newroot)作为容器的新根目录时,如果不采取措施,这个分支仍会受到整棵树(宿主机)上其他变动的影响。

例子
假设在宿主机上有一个目录结构如下:

/
├── bin
├── etc
├── home
├── newroot
│   ├── bin
│   ├── etc
│   └── var
└── var

你想让 /newroot 成为容器的独立根目录,但如果不对 /newroot 进行自我绑定挂载,那么在 /newroot 下做的挂载操作会影响到宿主机的其他部分。例如,如果在 /newroot/var 下挂载了一个临时文件系统,这个挂载也将传播到宿主机的 /var 目录。

然而,通过执行自我绑定挂载(mount --bind /newroot /newrootsyscall.Mount("/newroot", "/newroot", "", syscall.MS_BIND|syscall.MS_REC, "")),你实际上创建了一个“镜像”分支,这个分支与宿主机的其余部分在挂载行为上是相互独立的。此后,在 /newroot 下做的任何挂载操作,比如挂载临时文件系统到 /newroot/var,将只影响 /newroot 这个“镜像”分支,而不会影响到宿主机原有的 /var 目录。

这样一来,在执行 pivot_root/newroot 切换为容器的新根目录时,新的根目录 (/newroot) 就是一个清洁且独立的文件系统视图,它与宿主机的其余部分互不影响,从而实现了容器所需的隔离性。

syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "")

syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "") 是一个在Go编程语言中调用Linux内核 mount() 系统调用的代码片段,目的是改变当前进程的根目录挂载点的挂载标志。具体来说:

  • 第一个参数 "" 表示当前挂载点,这里为空字符串表示当前进程的根目录 /
  • 第二个参数也是 "/",指的是目标挂载点,这里也是根目录。
  • 第三个参数 "" 表示文件系统类型,由于这不是挂载一个新的文件系统,而是修改现有挂载点的属性,所以这里也留空。
  • 第四个参数是挂载标志,syscall.MS_PRIVATE|syscall.MS_REC
    • syscall.MS_PRIVATE:设置当前挂载点及其所有子挂载点为私有挂载。这意味着在这个挂载点上进行的任何挂载、卸载或重新挂载操作,都不会传播到其他挂载命名空间,从而实现了挂载事件的隔离,这对于创建独立的容器环境或其他需要文件系统隔离的场景非常关键。
    • syscall.MS_REC:这是一个递归标志,意味着这个私有挂载属性不仅应用到根目录 / 上,还将应用到根目录下的所有子挂载点,确保整个挂载树都变为私有挂载。
  • 第五个参数 "" 表示额外的挂载选项参数,这里为空字符串,表明没有额外的挂载选项需要设置。

综上所述,这条命令的作用是将当前进程的整个文件系统挂载树(包括根目录和所有子目录)设置为私有挂载,确保在当前进程及其后代进程的挂载命名空间内进行的挂载操作不会影响到宿主机或其他进程的命名空间。

bind mount

这是因为 “bind mount” 操作会在宿主机上创建一个新的文件系统挂载点,该挂载点与原来的 /container_root 目录具有相同的内容,但位于一个不同的文件系统中。

具体来说:

  1. 在容器中,/container_root 是容器的根文件系统。
  2. 当我们执行 mount --bind /container_root /container_root 时,会在宿主机上创建一个新的文件系统挂载点,该挂载点指向 /container_root 目录。
  3. 这个新的文件系统挂载点与宿主机的根文件系统 /host_root 是不同的文件系统。

也就是说,从宿主机的角度来看,/container_root 现在是一个独立的文件系统,与宿主机的根文件系统 /host_root 不在同一个文件系统中。

这个过程可以用一个例子来解释:

假设在宿主机上,/container_root 原本是宿主机根文件系统 /host_root 的一个子目录。但是执行 mount --bind 后,/container_root 就变成了一个独立的文件系统挂载点,与 /host_root 不在同一个文件系统中了。

这样做的目的就是为了满足 syscall.PivotRoot 函数的要求,即新的根文件系统和旧的根文件系统不能在同一个文件系统下。通过 “bind mount” 操作,我们可以实现这个要求。

挂载点的私有和mount namespace的私有

Mount Namespace的属性主要指的是挂载点在Namespace中的传播属性(Mount Propagation)。下面通过一个例子来解释:

例子场景
假设在宿主机上有两个目录/mnt/shared/mnt/private,并且宿主机的根目录/的挂载传播属性为默认值。

  1. 创建新的Mount Namespace并设置挂载点传播属性
    我们可以使用unshare命令创建一个新的Mount Namespace,并将/mnt/shared设置为共享挂载(shared)属性,将/mnt/private设置为私有挂载(private)属性。

    sudo unshare -m bash            # 创建一个新的Mount Namespace
    sudo mount --make-shared /mnt/shared    # 设置/mnt/shared为共享挂载
    sudo mount --make-private /mnt/private  # 设置/mnt/private为私有挂载
    
  2. Namespace内的操作

    • 共享挂载(/mnt/shared):在新的Namespace中,如果挂载了一个新的文件系统到/mnt/shared/subdir,由于/mnt/shared是共享挂载,所以在宿主机或其他Mount Namespace中也可以看到这个新增的挂载点。
    • 私有挂载(/mnt/private):如果在新的Namespace中对/mnt/private做任何挂载或卸载操作,这些操作不会影响到宿主机或其他Mount Namespace。也就是说,在新的Namespace中对/mnt/private的任何更改对外部Namespace都是透明的。
  3. 效果

    • 新Namespace内的进程只能看到在该Namespace内部定义的挂载点和它们的传播属性。
    • 对于/mnt/shared,在Namespace内外的挂载操作会相互影响,而对于/mnt/private,则完全隔离。

通过这个例子,我们可以看出Mount Namespace的属性是如何影响挂载点在不同Namespace间的可见性和操作效果的。在容器技术中,Mount Namespace的这些属性为容器提供了独立的文件系统视图,有助于实现资源隔离和安全性。

pivotroot

会切换根系统目录,但如果不会自动切换当前工作目录到跟系统去。如果不切换,那么会依然停留到执行该程序的工作目录
如下图
在这里插入图片描述
加个syscall.Chdir("/");
在这里插入图片描述
不加syscall.Chdir("/");,但加个command.Dir = "/home/llk/Desktop/docker/src/pivotroot_docker/busybox"
那么当前的工作目录在command.start会自动切换
在这里插入图片描述

自己理解

  1. 不同mount namspace有各种的符合当前的namespace的挂载点列表会,就是挂载后会显示的文件系统不同,但文件视图依然和从宿主机继承的一样。而是否挂载后会影响到宿主机和其他mount namespace的文件就看这个挂载点的属性 。如果是私有的就会修改到宿主机和其他mount namespace的文件,否则就不会影响到

下图是将当前进程根目录挂载点设置为私有和在mount namespace中直接启动后,挂载proc到当前容器内的/proc,发现此时并没有覆盖原主机上的/proc
在这里插入图片描述
而如果不将当前进程根目录挂载点设置为私有。挂载后的内容会影响到宿主机的/proc。
在这里插入图片描述
2. 将当前进程的根目录挂载点下都设置为私有,那么当该进程操作操作该挂载点时,不会影响宿主机和其他mount namespace的根目录挂载点
3. 将新的根目录挂载点绑定挂载,是因为pivotroot需要新的根目录和原根目录所在不同的挂载点。由于新根目录肯定在原根目录的挂载点中,为了使得二者不在同一个挂载点中,于是重复绑定,那么会使得当前进程认为新根目录所在的挂载点在新根目录。这样就使得二者不在同一个挂载点中了

代码

https://github.com/FULLK/llkdocker/tree/main/pivotroot_docker

结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1611748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python | Leetcode Python题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; class Solution:def firstMissingPositive(self, nums: List[int]) -> int:n len(nums)for i in range(n):while 1 < nums[i] < n and nums[nums[i] - 1] ! nums[i]:nums[nums[i] - 1], nums[i] nums[i], nums[nums[i] - 1]for …

域名信息查询同款WHOIS源码

域名查询一般是指查询域名的whois注册信息&#xff0c;域名WHOIS是当前域名系统中不可或缺的一项信息服务。在使用域名进行Internet冲浪时&#xff0c;很多用户希望进一步了解域名、名字服务器详细信息&#xff0c;这就会用到WHOIS。 域名信息查询同款WHOIS源码

在Postgres中,如何有效地管理大型数据库的大小和增长

文章目录 一、定期清理和维护1. VACUUM和ANALYZE2. 删除旧数据和归档 二、分区表三、压缩数据四、配置优化1. 调整维护工作负载2. 监控和日志 五、使用外部存储和扩展1. 外部表和FDW2. 扩展和插件 六、定期备份和恢复测试结论 管理大型数据库的大小和增长是数据库管理员&#x…

EA包图上嵌套的包位置不对

Extreme 2024-4-11 11:36 我从工具栏把一个包拖在另一个包里面&#xff0c;可是项目树上两个包的位置并列&#xff0c;拖了几次结果都一样。我的目的是做一个多层级的包图&#xff0c;是不是&#xff08;EA&#xff09;不能在图上做&#xff1f; UMLChina潘加宇 确实是这样&a…

m个人的成绩存放在score数组中,请编写函数fun,它的功能是:将低于平均分的人数作为函数值返回,将低于平均分的分数放在below所指的数组中。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法和详细的解析。 题干 m个人的成绩…

强固型工业电脑在轮胎成型机设备行业应用

轮胎成型机设备行业应用 项目背景 我国是一个轮胎生产与出口大国&#xff0c;在轮胎的生产过程中&#xff0c;成型工序是生产的关键工序&#xff0c;将半成品的部件按照轮胎的技术规范在成型机上组装成胎胚&#xff0c;工艺较为复杂&#xff0c;对设备加工和定位精度要求较高。…

【Linux学习】gcc与g++的使用与程序翻译的过程

文章目录 修改sudo用户名单gcc与g指令gcc用法g用法 程序翻译的过程函数库动态库与静态库什么是动静态库&#xff1f;动静态库的优缺点ldd指令 这里补充一个前面遗漏的知识&#xff1a; 修改sudo用户名单 必须使用root超级用户进行修改&#xff0c;普通用户是不能访问修改。 …

Spring 事务实现方式:

Spring 事务实现方式&#xff1a; Spring并不直接支持事务&#xff0c;只有当数据库支持事务的时候&#xff0c;Spring才支持事务&#xff0c;Spring只不过简化了开发人员实现事务的开发步骤 Spring事务的实现方式有两种&#xff1a; 一、基于申明式事务&#xff1a; Service…

CCF-CSP真题《202312-2 因子化简》思路+python,c++满分题解

想查看其他题的真题及题解的同学可以前往查看&#xff1a;CCF-CSP真题附题解大全 试题编号&#xff1a;202312-2试题名称&#xff1a;因子化简时间限制&#xff1a;2.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 题目背景 质数&#xff08;又称“素数”&#xff09;是指…

安装AngusTester节点代理

一、介绍 节点代理程序(AngusAgent)提供三方面作用&#xff1a; 通过启动"执行器(AngusRunner)"来执行脚本任务。在代理节点上运行和管理Mock服务。收集、监控和报告关于节点的各种指标和性能数据&#xff0c;该数据用于在稳定性测试中可分析节点资源使用率。 二、…

Java高阶私房菜:高并发之线程池底层原理学习

以往我们需要获取外部资源&#xff08;数据源、Http请求等&#xff09;时&#xff0c;需要对目标源创建链接对象&#xff0c;三次握手成功后方可正常使用&#xff0c;为避免持续的资源占用和可能的内存泄漏&#xff0c;还需要调用目标对象close方法释放资源销毁对象。这一建一销…

【PCL】教程conditional_euclidean_clustering 对输入的点云数据进行条件欧式聚类分析...

[done, 3349.09 ms : 19553780 points] Available dimensions: x y z intensity 源点云 Statues_4.pcd 不同条件函数output.pcd 【按5切换到强度通道可视化】 终端输出&#xff1a; Loading... >> Done: 1200.46 ms, 19553780 points Downsampling... >> Done: 411…

ssm064农产品仓库管理系统系统+jsp

农产品仓库管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本农产品仓库管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者…

Llama 3“智商”测试:英文提示表现亮眼,中文不完美但差强人意!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

C++从入门到精通——C++动态内存管理

C动态内存管理 前言一、C/C内存分布分类1分类2题目选择题sizeof 和 strlen 区别示例sizeofstrlen 二、C语言中动态内存管理方式malloc/calloc/realloc/free示例例题malloc/calloc/realloc的区别malloc的实现原理 三、C内存管理方式new/delete操作内置类型new和delete操作自定义…

zabbix自定义监控、自动发现和注册以及代理设置

前言 监控项的定制和新设备的注册往往需要大量手动操作&#xff0c;这会导致维护成本的增加和监控效率的降低。本文将介绍如何利用 Zabbix 的自定义功能&#xff0c;实现监控项的动态发布和新设备的自动注册以及代理设置、从而简化运维工作并实现更高效的监控管理。 Zabbix 监…

LearnOpenGL(二)之三角形

一、重要概念 顶点数组对象&#xff1a;Vertex Array Object&#xff0c;VAO顶点缓冲对象&#xff1a;Vertex Buffer Object&#xff0c;VBO元素缓冲对象&#xff1a;Element Buffer Object&#xff0c;EBO 或 索引缓冲对象 Index Buffer Object&#xff0c;IBO 以数组的形式…

【简单介绍下R-Tree】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

单链表的实现(单链表的增删查改)

在顺序表中实现数据的增删的操作时&#xff0c;都要把操作位置之后的数据全部移动一遍&#xff0c;操作效率低下。其次是容量固定&#xff08;静态顺序表&#xff09;&#xff0c;虽然在动态顺序表中容量可变&#xff0c;但也会造成空间上的浪费。 单链表就完美解决了上述缺点…

.net8系列-02图文并茂手把手教你编写增删改查接口

前情提要 接上篇文章&#xff0c;我们的应用已经创建完毕了&#xff0c;接下来我们编写几个自己的接口 快速开始 新增Controller 复制一份WeatherForecastController.cs,改名为CommonInfoController 设置Class名 将CommonInfoController中的复制过来的class名改成新名 …