[k8s理论知识]3.docker基础(二)隔离技术

devtools/2024/10/18 13:16:03/

容器其实是一种沙盒技术,其核心是通过约束和修改进程的动态表现,为其创建一个边界。这个边界确保了应用与应用之间不会相互干扰,同时可以方便在不同的环境中迁移,这是PaaS最理想的状态。

程序是代码的可执行镜像,通常以二进制文件的形式存储在磁盘上。但是当程序被执行时,操作系统会将程序的数据加载到内存中,读取计算指令并指示CPU执行。CPU与内存协作进行计算,使用寄存器存放数值,内存堆栈保存执行的命令和变量,此外,程序还可能打开文件和调用IO设备。所有这些状态信息和数据信息的集合,构成了进程的动态表现。

进程可以理解为是 动态的程序,但更准确地说,进程是程序在计算机系统中的一个 执行实例。程序本身只是静态的代码文件(通常指由源代码编译得到的可执行文件),而进程则是该程序的一个活动状态,它包含了程序的执行上下文和资源。

那么,如果给进程之间确立边界,也就是给程序创建了边界。而容器技术的核心,就是通过约束和修改进程的动态表现,从而为其创造一个边界。

Nampespace技术

启动一个busybox容器,通过以下操作让我们更好的理解进程隔离。

[root@master ~]# docker run -it busybox:v1 /bin/sh
/ # pid
/bin/sh: pid: not found
/ # ps
PID   USER     TIME  COMMAND1 root      0:00 /bin/sh6 root      0:00 ps

可以看到,我们在 Docker 里最开始执行的 /bin/sh,就是这个容器内部的第 1 号进程(PID=1),而这个容器里一共只有两个进程在运行。这就意味着,前面执行的 /bin/sh,以及我们刚刚执行的 ps,已经被 Docker 隔离在了一个跟宿主机完全不同的世界当中。

当我们不要关闭这个命令行(否则容器会销毁),而打开另一个窗口,查看这个容器的pid,可以看到这个容器的PID实际为16043。

[root@master ~]# docker ps | grep busybox
e45240f24ffb   busybox:v1                                          "/bin/sh"                 12 seconds ago   Up 11 seconds             epic_moser
[root@master ~]# docker inspect --format '{{.State.Pid}}' e45240f24ffb 
16043

以上表述实际上是非常不严谨的,因为容器本身没有PID,容器只是为程序提供一个隔离的视图,而运行在容器里的程序(在我们的例子中是/bin/bash)才拥有PID。也就是说在查看容器的PID的时候,查看的实际上是它内部程序的PID。

所以我们可以理解,这个bin/bash本身的PID是16043,而容器实施了一个障眼法,让前面的所有PID都看不见了,这个程序的PID也就成了1。

事实上,以上只是将进程空间进行了Namespace隔离,容器还需要进行文件系统、IO设备等的隔离。这些在后面会一一演示。

在理解了 Namespace 的工作方式之后,你就会明白,跟真实存在的虚拟机不同,在使用 Docker 的时候,并没有一个真正的“Docker 容器”运行在宿主机里面。Docker 项目帮助用户启动的,还是原来的应用进程,只不过在创建这些进程时,Docker 为它们加上了各种各样的 Namespace 参数。

Cgroups

从上面例子可以看出,Namespace技术改变了进程看待计算机的视图,但是计算机并没有改变看待进程的视图,对于底层操作系统而言,容器里的进程和直接运行的进程没有区别。也就是说,虽然上面例子中的bin/bash进程表面上被隔离了起来,但是实际上它能得到的内存和cpu资源,却可以随意的被其他进程占用。而这个进程本身也可以吃掉别的进程的资源,这不符合一个沙盒的行为表现。而且,在linux内核中,有很多的资源是不能namespace化的,比如时间。

Linux Cgroups 的全称是 Linux Control Group。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。通过以下命令可以看到cgroup在系统中挂载的位置。

[root@master ~]# mount -t cgroup 
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)

可以看到,在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。这些都是我这台机器当前可以被 Cgroups 进行限制的资源种类。我们进入CPU目录,可以看到有很多cpu的配置文件。在这些文件中,文件夹container是我们后来创建的。

当我们进入container文件夹,会看到里面已经创建了完整的cgoups文件系统。 这是因为当你在cgroups的某个子系统(比如cpu或者sys等),你实际上在创建一个新的控制组,控制组允许你为该组内的进程设置资源限制或监控资源使用情况。因此Linux内核会在这个子目录下生成一系列与该子系统相关的文件。

 但是这些文件实际上是虚拟的,并不是存储载物理磁盘上的文件,只是为用户提供的与内核交互的接口。

 随后打开另一个终端,执行以下命令。这条命令是一个死循环,会吃光所有cpu资源。

可以看到这个进程PID是47975。

接下来进入container目录, 看到 container 控制组里的 CPU quota 还没有任何限制(即:-1),CPU period 则是默认的 100  ms(100000  us)

[root@master container]# cat cpu.cfs_period_us 
100000
[root@master container]# cat cpu.cfs_quota_us 
-1

接下来我们进行控制组的限制操作,向container组里的cfs_quota和cfs_period文件写入参数进行限制。

以下的操作意思是100000us(100ms)里面有20000us(20ms)可以给这个控制组使用。

接下来要将这个PID(47975)放入这个控制组。

重新用top命令查看,发现 它虽然是一个死循环,但是只占用了20%的cpu资源。

控制组 

不是每一个Linux进程都有自己的控制组,但是每个进程都会属于某一个资源控制组。在没有配置自定义 cgroups 的情况下,所有进程都会被分配到系统的默认控制组,这些默认的 cgroups 通常是由 init 系统(如 systemd)或直接由内核管理的。多个进程也可以被放置在同一个控制组中。

比如说我们查看cgroup/cpu里的user.slice文件夹,可以看到里面有一个procs文件,打开可以看到这个资源控制组的所有进程。

假设有一个进程PID是12345,我们可以将它加入控制组中。

echo 12345 > /sys/fs/cgroup/cpu/user.slice/cgroup.procs

创建一个docker,并规定了他的cpu资源限制。可以看到docker ID是b47efc8e7d29,cgroup目录是system.slice下的以b47efc8e7d29开头的文件夹。

[root@master ~]# docker run  -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash
root@b47efc8e7d29:/# cat /proc/self/cgroup
11:pids:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
10:memory:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
9:perf_event:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
8:cpuset:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
7:net_prio,net_cls:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
6:devices:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
5:blkio:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
4:cpuacct,cpu:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
3:hugetlb:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
2:freezer:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope
1:name=systemd:/system.slice/docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope

进入文件夹,可以看到他的cpu资源限制。 也就是说docker可以在创建容器的时候进行资源限制。

[root@master system.slice]# cd docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope/
[root@master docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope]# ls
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
[root@master docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope]# cat cpu.rt_period_us 
1000000
[root@master docker-b47efc8e7d291be37292fdf8edb160947e2bfa413595f90d6d4960f7b22a3edf.scope]# cat cpu.cfs_quota_us 
20000
echo操作 

 /sys/fs/cgroup/cpu/container/cgroup.procs 和 /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us 这两个文件的行为是不同的,所以当我们进行echo操作的时候,写入cpu.cfs_quota_us 文件的行为是覆盖式的。当你使用 echo 写入一个新的值时,它会替换掉之前的值。cgroup.procs 是一个特殊的文件,它的目的是让用户通过写入进程 ID 来动态管理进程所属的控制组。因此它的行为不是像普通文件那样追加或覆盖内容,而是执行一个动作(将进程加入到该控制组)。

文件系统

如果不进行单的文件系统挂载,容器内的进程的文件系统视图就是整个物理主机的视图。所以在容器创建之前,docker通过重新挂载根目录,从而让容器进程看到的是一个独立的隔离环境。为了能够让容器的这个根目录看起来更“真实”,Docker会在这个容器的根目录下挂载一个完整操作系统的文件系统,比如 Ubuntu16.04 的 ISO。这样,在容器启动之后,我们在容器里通过执行 "ls /" 查看根目录下的内容,就是 Ubuntu 16.04 的所有目录和文件。而挂载在容器根目录上,用来为进程提供隔离后环境的文件的操作系统,就是所谓的容器镜像。它还有一个更为专业的名字,叫作:rootfs(根文件系统)。

值得注意的是,rootfs只是一个操作系统所包含的文件、配置和目录,并不包含操作系统的内核。Linux操作系统分为两部分,内核和用户态。

内核:这是操作系统的核心,负责与硬件交互、调度进程、管理内存、网络、文件系统等。无论是ubuntu、centos还是其他linux发行版的内核,他们的核心功能是相同的。

用户态:这是操作系统中用户可以直接接触的部分,比如程序、库、配置文件等等。不同的linux发行版(linux/ubuntu/debian)在用户态可能有很大不同,包括包管理、配置文件等。

对于容器来讲,操作系统就是宿主机的操作系统内核。所以说,当你拉取一个 CentOS 容器镜像时,实际上你只是拉取了 CentOS 的用户态部分(包括系统工具、库、配置文件等),而不包含 CentOS 的内核。容器中的进程依然使用的是宿主机的内核来运行。因此,无论宿主机运行的是 Ubuntu 还是其他发行版的内核,只要它是 Linux 内核,容器中的 CentOS 文件系统都可以正常工作。

Docker和VM对比

但是事实上,宿主机不可能光有一个内核态,他也是有用户态的。当你在一个 Ubuntu 宿主机上运行一个 CentOS 容器时,容器中的进程会使用 CentOS 的用户态工具和库。比如,容器中的 yum(CentOS 的包管理器)可以正常运行,而不会使用 Ubuntu 的 apt(Ubuntu 的包管理器)。因此,容器就像是一个独立的系统,拥有自己的文件系统、程序、配置等,不依赖宿主机的用户态。你可以把它想象为容器本身有一个“虚拟的用户态”,但它并不运行在宿主机的用户态环境中。

当你在一个 Ubuntu 宿主机上运行了一个 CentOS 虚拟机,虚拟机内的 CentOS 系统会像在物理机上一样启动,它将加载自己的 CentOS 内核,并启动 CentOS 的用户态环境。CentOS 虚拟机使用自己的内核来处理系统调用、管理进程、分配内存等。这个内核与宿主机的内核完全独立,因此 CentOS 虚拟机可以运行与宿主机不同版本的内核。

对 Docker 项目来说,它最核心的原理实际上就是为待创建的用户进程:启用 Linux Namespace 配置;设置指定的 Cgroups 参数;切换进程的根目录(Change Root)。


http://www.ppmy.cn/devtools/126732.html

相关文章

极简版Java敏感词检测SDK

敏感词工具 sensitive-word 基于 DFA 算法实现的高性能敏感词工具,开源在GitHub:https://github.com/houbb/sensitive-word。用于敏感词/违禁词/违法词/脏词等的识别和阻拦,是基于 DFA 算法实现的高性能 java 敏感词过滤工具框架。 使用场景…

滚雪球学Redis[5.3讲]:Redis持久化优化深度解析:RDB与AOF的策略选择与实践

全文目录: 🎉前言🚦Redis的监控与报警🔄1. 使用Redis自带工具进行监控🎯1.1 Redis常用监控命令🎯1.2 Redis配置文件中的监控相关参数 ⚙️2. 使用第三方工具进行Redis监控💡2.1 Prometheus与Red…

【Mac苹果电脑安装】DBeaverEE for Mac 数据库管理工具软件教程【保姆级教程】

Mac分享吧 文章目录 DBeaverEE 数据库管理工具 软件安装完成,打开效果图片Mac电脑 DBeaverEE 数据库管理工具 软件安装——v24.21️⃣:下载软件2️⃣:安装JDK,根据下图操作步骤提示完成安装3️⃣:安装DBeaverEE&#…

linux之rm使用技巧

对于包含乱码的文件或目录名,在Linux中删除它们可能会有些棘手,但还是可以通过一些方法来实现。下面是一些处理这种情况的方法: 方法1: 使用通配符 如果这些乱码文件或目录的名字有共同的特征(例如都是乱码)&#xf…

LeetCode 面试经典150题 151.反转字符串中的单词

题目: 给你一个字符串 s ,请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意:输入字符串 s中可能会…

Java中的异步编程:使用CompletableFuture提升并发性能

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 引言 在现代应用程序中,响应速度和并发性能变得越来越重要。随着处理任务变得复杂,应用程序常常需要同时处理多个任务,这对系统资源和性能提出了严峻的挑战。在传统的阻塞式编程模型中,线程等待任务完成往往…

Java笔试03

线性结构是指数据元素之间存在一对一的线性关系的数据结构。 树 不属于线性结构。树是由节点组成的层次结构,每个节点可以有多于两个的子节点。 队列 属于线性结构。队列是一种先进先出(FIFO)的数据结构,元素之间存在一对一的…

【arcgis】ArcGIS中如何避免标注压盖要素

ArcGIS中如何避免标注压盖要素 在制图工作中,标注(Label)是传达地理信息的重要方式。然而,在复杂的地图上,标注容易出现压盖要素的情况,影响地图的美观性和信息的准确传达。ArcGIS提供了Maplex标注引擎&am…