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

文章目录

  • 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

结果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

修复vite中使用react提示Fast refresh only works when a file only exports components.

前言 我通过 vite 构建了一个 react 应用并使用 react.lazy 来懒加载组件,但是在使用过程中 一直提示 Fast refresh only works when a file only exports components. Move your component(s) to a separate file.eslint(react-refresh/only-export-components)。…

编译OpenWRT固件

前言 编译环境,我是使用Ubuntu16.04.07 LTS 64位版 1.安装Ubuntu16.04.07 LTS 64 作者写这篇文章的时候lede源码使用debian11编译,对于的就是Ubuntu 20,至于为什么要安装ub16是因为最开始我不清楚要使用ub20安装,用ub16安装的时…

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

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

RAG部署 | 使用TensorRT-LLM在Windows上部署检索增强生成聊天机器人RAG

项目应用场景 面向 Windows 平台部署 RAG 检索增强生成聊天机器人场景,项目采用 TensorRT-LLM 进行 GPU 加速推理,注意项目需要 RT4090 及以上的英伟达显卡支持。 项目效果 项目细节 > 具体参见项目 README.md (1) 下载构建好的 Llama2 TensorRT 模型…

Web开发:ASP.NET CORE的前端demo(纯前端)

目录 一、建立项目 二、删除无用文件 三、样式添加 四、写一个登录页面 五、登录主界面 一、建立项目 二、删除无用文件 三、样式添加 将你的图片资源添加在wwwroot下方,例如pics/logo.png 四、写一个登录页面 将Privacy.cshtml改为 Forget.cshtml &#xff0…

AJAX——图书管理案例

1.渲染列表 自己的图书数据:给自己起个外号,并告诉服务器,默认会有三本书,基于这三本书做数据的增删改查。 // 目标1:渲染图书列表 // 1.1 获取数据 // 1.2 渲染数据const creator 哈哈 // 封装-获取并渲染图书列表函…

设计模式学习笔记 - 开源实战三(中):剖析Google Guava中用到的设计模式

概述 上篇文章,我通过 Google Guava 这样一个优秀的开源类库,讲解了如何在业务开发中,发现跟业务无关、可以复用的通用功能模块,并将它们抽离出来,设计成独立的类库、框架或功能组件。 本章再来学习下,Go…

[Linux][进程信号][二][信号如何被保存][信号处理][可重入函数]详细解读

目录 1.信号如何被保存?1.信号其他相关常见概念2.信号在内核中的表示3.sigset_t -- 本质是个位图4.信号集操作函数sigset_t:sigprocmask()sigpending() 5.思考6.使用 2.信号处理0.内核态和用户态1.内核空间和用户空间2.信号何时被处理?3.信号…

PSA Group EDI 需求分析

PSA集团(以下简称PSA)中文名为标致雪铁龙集团,是一家法国私营汽车制造公司,致力于为全球消费者提供独具特色的汽车体验和自由愉悦的出行方案,旗下拥有标致、雪铁龙、DS、欧宝、沃克斯豪尔五大汽车品牌。 汽车制造企业对…

JavaWeb--前端--02JavaScript

JavaScript 1 JavaScript介绍2 引入方式3 基础语法3.1 书写语法3.2 变量3.3 数据类型和运算符 4 JS的函数4.1函数的第一种定义4.2 函数的第二中定义 5 JavaScript对象5.1 基本对象5.1.1 Array对象5.1.2 String对象5.1.3 Json对象 5.2 BOM5.2.1 BOM对象5.2.1 Windows对象5.2.2 L…

c++补充

构造函数、析构函数 #include <iostream> using namespace std;// 构造函数、析构函数 // --- "构造函数"类比生活中的"出厂设置" --- // --- "析构函数"类比生活中的"销毁设置" --- // 如果我们不写这两种函数&#xff0c;编译…

定制k8s域名解析------CoreDns配置实验

定制k8s域名解析------CoreDns配置实验 1. 需求 k8s集群内通过CoreDns互相解析service名. 同时pana.cn域为外部dns解析,需要通过指定dns服务器进行解析 再有3个服务器,需要使用A记录进行解析 2. K8s外DNS服务器 查看解析文件 tail -3 /var/named/pana.cn.zone 解析内容 ww…

STM32G431RBT6之时钟树配置与生成工程

默认大家都下载了蓝桥杯嵌入式资源包了哈. 首先,打开cubumx,修改RCC与SYS. 打开并观察原理图,发现晶振是24Mhz. 第一步,打开Clock Configuration. 第二步,修改晶振为原理图相对应的24Mhz. 第三步,切换到HSE. 第四步,切换到PLLCLK. 第五步,设置HCLK为80Mhz(15届真题要求为8…

【信号处理】基于EEG脑电信号的自闭症预测典型方法实现

理论 自闭者主要受到遗传和环境因素的共同影响。由于自闭症是一种谱系障碍&#xff0c;因此每个自闭症患者都有独特的优势和挑战。自闭症患者学习、思考和解决问题的方式可以是高技能的&#xff0c;也可以是严峻的挑战。研究表明&#xff0c;高质量的早期干预可以改善学习、沟…

Java web应用性能分析之【MySQL安装注意事项】

本文主要是针对以前LAMP&#xff0c;以及默认用apt安装的mysql。数据文件、日志文件都在一起&#xff1b;innodb_buffer_pool默认用128M。如果你排查问题&#xff0c;最后发现是因为mysql的安装配置不对&#xff0c;是否一口老血要喷出来。同时给MySQL数据库安装做参考。 关于M…

ZYNQ NVME高速存储之EXT4文件系统

前面文章分析了高速存储的各种方案&#xff0c;目前主流的三种存储方案是&#xff0c;pcie switch高速存储方案&#xff0c;zynq高速存储方案&#xff0c;fpga高速存储方案。虽然三种高速存储方案都可以实现高速存储&#xff0c;但是fpga高速存储方案是最烂的&#xff0c;fpga…

23.组件注册方式

组件注册方式 一个 Vue 组件在使用前需要先被“注册”&#xff0c;这样 Vue 才能在渲染模板时找到其对应的实现。组件注册有两种方式&#xff1a;全局注册和局部注册 全局注册 import { createApp } from vue import App from ./App.vue import GlobalComponent from ".…

C++三大特性之一:继承

文章目录 前言一、继承方式二、继承类型继承中构造和析构的顺序继承中的内存分配多继承语法(非重点)继承中同名静态成员的处理继承一般在哪里用到进阶&#xff1a;菱形继承和虚拟继承 总结 前言 C三大特性&#xff1a;继承、多态和封装。继承是面向对象编程的一个核心概念&…

实在IDP文档审阅产品导引

实在IDP文档审阅&#xff1a;智能文档处理的革新者 一、引言 在数字化转型的浪潮中&#xff0c;文档处理的智能化成为企业提效的关键。实在智能科技有限公司推出的实在IDP文档审阅&#xff0c;是一款利用AI技术快速理解、处理文档的智能平台&#xff0c;旨在为企业打造专属的…

在PostgreSQL中如何进行全文搜索,以及如何优化全文搜索性能?

文章目录 如何进行全文搜索1. 创建全文搜索向量2. 执行全文搜索查询 如何优化全文搜索性能1. 使用GIN索引2. 限制搜索范围3. 优化文本处理4. 使用并发搜索5. 监控和调整配置 在PostgreSQL中&#xff0c;全文搜索通常通过使用tsvector和tsquery类型&#xff0c;以及to_tsvector和…