从零自制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

结果

在这里插入图片描述

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

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

相关文章

nvm下载的node没有npm

nvm下载的node没有npm 相信大家最近可能发现自己使用的nvm下载nodejs没有npm了。 会出现这种情况: C:\Users\89121>nvm install 15 Downloading node.js version 15.14.0 (64-bit)... Complete Downloading npm version 7.7.6... Download failed. Rolling Bac…

如何查找一篇英文文献的源代码?(论文中没有源代码链接时)如何查找一篇论文的实现代码从而复现论文?

有两个网址,从这两个网址里面能找到论文相关代码,但不确定是不是人家论文里的源代码,但是根据论文实在找不到的情况下,只能试试这两个网址了 1. https://paperswithcode.com/ 2. https://www.catalyzex.com/

【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView(图文并茂超详细版本)

往期回顾 【QT进阶】Qt Web混合编程之CEF、QCefView简单介绍-CSDN博客 【QT进阶】Qt Web混合编程之VS2019 CEF的编译与使用(图文并茂超详细介绍)-CSDN博客【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客【QT进阶】Qt Web混合编程之VS2019 C…

Geoserver的RESTful接口使用

概述 GeoServer提供了一个RESTful接口,客户端可以通过该接口获取有关实例的信息并进行配置更改。REST接口使用简单的HTTP调用,通过客户端就可以配置GeoServer,而无需使用Web管理接口。 Geoserver中的关系 工作区、数据源、图层、图层组以及…

随身WiFi真实测评推荐!格行vs新讯随身wifi对比,公认最好的随身WiFi格行随身wifi有什么优势?

在当前移动网络高度发达的时代,随身 WiFi 已成为人们出差、旅行等场景中不可或缺的工具。格行和新讯是目前比较受欢迎的无线随身wifi。本次评测将对比分析这两款产品的区别,做为随身WiFi推荐第一名的格行随身wifi到底有什么优势呢? 品牌对比&…

手写Java设计模式之工厂模式,附源码解读

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 工厂模式提供了一种创建对象的方式,而无需指定要创建的具体类。 工厂模式属于创建型…

网络流问题详解

1. 网络最大流 1.1 容量网络和网络最大流 1.1.1 容量网络 设 G(V, E)是一个有向网络,在 V 中指定了一个顶点,称为源点(记为 Vs),以及另一个顶点,称为汇点(记为 Vt);对…

基于java+springboot+vue实现的校园一卡通系统(文末源码+Lw+ppt)23-26

摘 要 近些年来,随着科技的飞速发展,互联网的普及逐渐延伸到各行各业中,给人们生活带来了十分的便利,校园一卡通利用计算机网络实现信息化管理,使整个校园一卡通管理的发展和服务水平有显著提升。 本文拟采用java技…

Aws Nat Gateway

要点 NAT网关要能访问外网,所以需要部署在有互联网网关的Public子网中。 关键: NAT网关创建是选择子网,一定要选择公有子网(有互联网网关子网) 特别注意: 新建nat网关的时候,选择的子网一定…

【C++】哈希结构

目录 一,哈希结构的认识 1-1,哈希思想 1-2,哈希函数 1-3,哈希冲突 1-3-1,闭散列 1-3-2,开散列 二,哈希结构的封装实现 2-1,闭散列封装实现 ​编辑 2-2,开散列封…

genetic algorithm

genetic algorithm 遗传算法

C++入门5.内联函数,auto关键字,基于范围的for循环(C++11),指针空值nullptr(C++11)

本篇是C过度C初始的最后一篇,快快对入门须知的知识有个印象后,就可以顺顺利利的学习C的类了。 目录 内联函数: 内联函数的特性: auto关键字(C11): auto简介: 使用细则: auto不能推导的场…

基于java+springboot+vue实现的物业管理系统(文末源码+Lw+ppt)23-23

摘 要 快速发展的社会中,人们的生活水平都在提高,生活节奏也在逐渐加快。为了节省时间和提高工作效率,越来越多的人选择利用互联网进行线上打理各种事务,通过线上物业管理系统也就相继涌现。与此同时,人们开始接受方…

K8S基础概念

一、MASTER Kubernetes里的Master指的是集群控制节点,在每个Kubernetes集群里都需要有一个Master来负责整个集 群的管理和控制,基本上 Kubernetes的所有控制命令都发给它,它负责具体的执行过程,我们后 面执行的所有命 令基本都…

idea2024.1发布,lambda多语句的内联断点,增强spring图标等新特性,你没玩过的全新版本

这里是weihubeats,觉得文章不错可以关注公众号小奏技术 简述 2024-04-04 idea官方宣布发布了 一些重大更新 随后我便下载了你没玩过的全新版本IntelliJ IDEA Ultimeate版本试玩 然后脑子里面想到这个 开玩笑 实际下载完是这样 更新内容 更新的内容比较多 关键亮点主要有如下…

Redis入门到通关之数据结构解析-RedisObject

文章目录 ☃️概述☃️源码 ☃️概述 RedisObject 是 Redis 中表示数据对象的结构体,它是 Redis 数据库中的基本数据类型的抽象。在 Redis 中,所有的数据都被存储为 RedisObject 类型的对象。 RedisObject 结构体定义如下(简化版本&#xf…

MDC搭配ttl

1.MDC 1.简介 MDC 介绍​ MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可…

CSS实现广告自动轮播

实现原理 该广告轮播功能的实现主要依靠HTML和CSS。HTML负责搭建轮播框架,而CSS则控制样式和动画效果。通过CSS中的关键帧动画(Keyframes),我们可以定义图片在容器内的滚动效果,从而实现轮播功能。 HTML结构 首先&am…

如何搭建线下陪玩系统(本地伴游、多玩圈子)APP小程序H5多端前后端源码交付,支持二开!

一、卡顿的优化方法 1、对陪玩系统源码中流媒体传输的上行进行优化,通过提升推流端的设备性能配置、推流边缘CDN节点就近选择等方式解决音视频数据源流的卡顿。 2、对陪玩系统源码中音视频数据的下载链路进行优化,通过选择更近更优质的CDN边缘节点来减少…

Navicat导入sql文件图文教程

本文使用的MySQL工具为:Navicat.默认已经连接数据库!! 步骤: 1.右键自己的数据库,选择新建数据库. 2.输入数据库名称,字符集选择“utf8”,排序规则选择“ utf8_general_ci”,确定. 3.双击新建好的“数据库”。右键点击“运行SQL文件”。 4.选择本地的s…