浅谈日常使用的 Docker 底层原理-三大底座

适合的读者,对Docker有过简单了解的朋友,想要进一步了解Docker容器的朋友。

前言

回想我这两年,一直都是在使用 Docker,看过的视频、拜读过的博客,大都是在介绍 Docker 的由来、使用、优点和发展趋势,但对于 Docker 底层到底是如何实现,却是没有提起太多,当然也是我太菜啦,哈哈哈~

便想借本次技术专题的机会,一方面希望能满足自己心底的那份好奇心,另外也想编写一篇关于 Docker 实现原理的文章来让更多的小伙伴知道和了解自己所使用 Docker 底层到底是怎么样的

本文更偏向科普,能不能写好,坦白说,我心里也没底,希望大佬们给点建议,我加油改改

本文大纲:
在这里插入图片描述

那么接下来就进入正文吧。

一、Docker 基本架构图

在这里插入图片描述

图片来源:Docker 官方文档

Docker 采用了 C/S 架构,包括客户端和服务端。Docker 守护进程 (Daemon)作为服务端接受来自客户端的请求,并处理这些请求(创建、运行、分发容器)。

客户端和服务端既可以运行在一个机器上,也可通过 socket 或者 RESTful API 来进行通信。

Docker 守护进程一般在宿主主机后台运行,等待接收来自客户端的消息。

Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker 守护进程交互。

另外这一点,也可以在执行 docker info 时看出来。

关于这个更详细的内容,官网说的更加详细,我就没多写啦。

二、Docker Client 和 Docker Server 如何连接?

docker 底层是通过套接字的方式去连接的。

Docker 守护进程可以通过三种不同类型的 Socket 侦听Docker Engine API :unix, tcp, and fd.

默认情况下,unix域套接字(或 IPC 套接字)在 处创建 /var/run/docker.sock,需要root权限或docker组成员身份。

在这里插入图片描述

  • 补充:linux sock文件是指通过 shell 编程后形成的套接口文件;socket 是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口;在设计模式中,Socket 其实就是一个门面模式,它把复杂的TCP/IP 协议族隐藏在 Socket 接口后面。

如果需要远程访问Docker守护进程,则需要启用tcp Socket。这也是我们实现远程操作Docker的实现方式。

下面我们借助 socat 来直观的感受一下,本机中的 client 和 server 如何连接的吧

sudo apt install socat

补充:什么是socat

socat是一个用于数据转发的命令行工具,它可以在两个端口之间建立虚拟通道,将数据从一个端口转发到另一个端口,同时支持很多网络协议,如常见的 TCP、UDP、HTTP、HTTPS等等。

在此我们就是借助它来进行tcp报文的转发。

socat -v UNIX-LISTEN:/tmp/dockerapi.sock UNIX-CONNECT:/var/run/docker.sock &

1)-v 参数的作用是表示在执行过程中输出详细信息。 -v 选项会让 socat 输出更多的信息,方便调试和监控。

2)UNIX-LISTEN:/tmp/dockerapi.sock: 这部分指定了 socat 要监听的 Unix 域套接字路径。它告诉 socat/tmp/dockerapi.sock 这个路径上监听传入的连接请求。

3)UNIX-CONNECT:/var/run/docker.sock: 这部分指定了 socat 要连接的目标 Unix 域套接字路径。它告诉 socat 要将传入的数据流连接到 /var/run/docker.sock 这个路径上。

4) & : 这个符号将命令放入后台运行.

简单点说就是将从 /tmp/dockerapi.sock 接收到的数据流连接到 /var/run/docker.sock 上。

我们来查看一下是否成功启动监听啦。

    ps -e | grep socat

在这里插入图片描述

docker client 可以通过指定 -H 来指定需要连接的服务端。下面是我们指定为本机的 Server 来进行测试。上面我们已经使用 socat 监听来自unix://tmp/dockerapi.sock 数据输入啦,它会帮我们连接到 /var/run/docker.sock 上去。看结果吧

    docker -H unix://tmp/dockerapi.sock ps

在这里插入图片描述
在这里插入图片描述

不过这个数据没有格式化,有点难看,但还是可以看出它将我本地在运行的容器显示出来啦。

我也尝试了使用 docker client 来管理其他机器的 docker 服务。(需要修改其他机器上的docker服务配置文件,并重启)

在这里插入图片描述

可参考:Docker开放2375端口,实现远程访问

注意:这并不是一个安全的操作,因为并没有加密和验证之类,云服务器请谨慎操作或及时关闭,我是本地虚拟机测试。

开胃小菜结束了,下面的才是有意思的,但是我想通过上面两节小小案例的演示,大家对于 Docker 的客户端和服务端之间的交互应该了解了一些了吧~

三、Docker 核心原理的三大底座

  1. 在容器进程启动之前重新挂载它的整个根目录“/”,用来为容器提供隔离后的执行环境文件系统(rootfs)。
  2. 通过 Liunx Namespace 创建隔离,决定进程能够看到和使用哪些东西。
  3. 通过 control groups 技术来约束进程对资源的使用。

四、Rootfs

详细的请阅读这篇文章:Docker 原理剖析(三)rootfs,这一小节主要内容均来自于此篇文章,作者写的太好啦。

这是我在看这方面博客写的最好的一篇啦,真心建议阅读,文章深度和内容都足够好。


4.1、rootfs 介绍

rootfs 是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。在 Linux 操作系统中,这两部分是分开存放的,操作系统只有在开机启动时才会加载指定版本的内核镜像。

实际上,同一台机器上的所有容器,都共享宿主机操作系统的内核。所以宿主机操作系统的内核,它对于该机器上的所有容器来说是一个全局变量,牵一发而动全身。

由于 rootfs 的存在,容器才有了一个被反复宣传至今的重要特性:一致性。由于 rootfs 里打包的不只是应用,而是整个操作系统的文件和目录,也就意味着,应用以及它运行所需要的所有依赖,都被封装在了一起。

有了容器镜像“打包操作系统”的能力,这个最基础的依赖环境也终于变成了应用沙盒的一部分。这就赋予了容器所谓的一致性:无论在本地、云端,还是在一台任何地方的机器上,用户只需要解压打包好的容器镜像,那么这个应用运行所需要的完整执行环境就被重现出来了。

在 Docker 架构中,当 Docker daemon 为 Docker 容器挂载 rootfs 时,沿用的 liunx 内核启动时的方法,即将 rootfs 设为只读模式。在挂载完毕之后,利用联合挂载(union mount )技术在已有的只读 rootfs 上再挂载一个读写层。这样,可读写层处于Docker容器文件系统的最顶层,其下可能联合挂载了多个只读层,只有在Docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并且隐藏只读层的老版本文件

我们可以看一个 Ubuntu 镜像,实际上它是 Ubuntu 操作系统的 rootfs,包含了 Ubuntu 操作系统的所有文件和目录。不过这个 rootfs,由多个层组成,每一个层都是一个增量 rootfs,每一层都是 Ubuntu 操作系统文件与目录的一部分。在使用镜像时,Docker 会把这些增量联合挂载在一个统一的挂载点上,这个挂载点就是 /var/lib/docker/aufs/mnt/。(镜像的层都放置在 /var/lib/docker/aufs/diff 目录下)

4.2、rootfs 组成

rootfs 由三部分组成,由上往下分别是:可读写层,init 层,只读层。

我们以之前使用的 Ubuntu 镜像为例。

在这里插入图片描述

只读层是容器的 rootfs 的下五层,它们的挂载方式都是只读的,可见这些层都以增量的方式分别包含了 Ubuntu 操作系统的一部分。

可读写层是容器的 rootfs 的最上面一层,在没有写入文件之前,这个目录是空的。而一旦在容器里做了写操作,你修改产生的内容就会以增量的方式出现在这个层中。

但是,如果我现在要做的,是删除只读层里的一个文件呢?为了实现这样的删除操作,会在可读写层创建一个 whiteout 文件,把只读层里的文件遮挡起来。比如,你要删除只读层里一个名叫 foo 的文件,那么这个删除操作实际上是在可读写层创建了一个名叫.wh.foo 的文件。

这样,当这两个层被联合挂载之后,foo 文件就会被.wh.foo 文件遮挡起来,消失了。综上所述,最上面这个可读写层的作用,就是专门用来存放你修改 rootfs 后产生的增量,无论是增、删、改,都发生在这里,而原先的只读层里的内容则不会有任何变化。

相当于你做的所有操作都只会影响到读写层,并不会影响到在此之前的只读层,这一层的可读写层也就是我们的容器啦。

Init 层在只读层与可读写层的中间,是 Docker 项目单独生成的一个内部层,专门用来存放 /etc/hosts、/etc/resolv.conf 等信息。

需要这样一层的原因是,这些文件本来属于只读的 Ubuntu 镜像的一部分,但是用户往往需要在启动容器时写入一些指定的值比如 hostname,所以就需要在可读写层对它们进行修改。可是,这些修改往往只对当前的容器有效,我们并不希望执行 docker commit 时,把这些信息连同可读写层一起提交掉。所以,Docker 做法是,在修改了这些文件之后,以一个单独的层挂载了出来。而用户执行 docker commit 只会提交可读写层,所以是不包含这些内容的。

4.3、文件联合系统(UnionFS)

UnionFS 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

在这里插入图片描述

Union文件系统是Docker镜像的基础

Unios FS 在 Docker 中的使用大致如下图:
在这里插入图片描述

镜像可以通过分层来进行继承,基于基础镜像。可以制作各种具体的应用镜像。 分层最大的一个优点是共享资源;多个镜像都从相同的base镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像即可;同时内存中也只需要加载一份base镜像,就可以为所有容器服务,而且镜像的每一层都可以被共享。

4.4、Docker 镜像原理

在这里插入图片描述

所以当我们使用用docker run命令启动某个容器时,实际上在镜像的顶部添加了一个新的可写层,而这个新的可写层,被我们称为了容器

容器启动后,其内的应用所有对容器的改动,文件的增删改操作都只会发生在容器层中,对容器层下面的所有只读镜像层没有影响。

这也就是写时复制

五、Linux Namespace

5.1、namespace 是什么?

Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。

5.2、namespace 解决了什么问题?

Linux 内核出现 namespace 的一个主要目的就是实现轻量级虚拟化(容器)服务。在同一个 namespace 下的进程可以感知彼此的变化,而对外界的进程一无所知,从而达到隔离的目的。

其实换个说法,Linux 内核所提供的 namespace 技术为 docker 等容器技术的出现和发展提供了基础条件。没有 linux 内核中的 namespace 的出现,可能 docker 容器化技术还不会那么快出现。

5.3、namespace 具体有哪些呢?

1.Mount Namespace 文件系统隔离。隔离了一组进程所看到的文件系统挂载点的集合,在不同的Mount Namespace 的进程所看到的文件是不同的。

2.UTS Namespace 隔离主机和域名信息。隔离了 uname() 系统调用返回的两个系统标识符 nodename 和 domainname. 在容器的上下文中,UTS namespace 允许每个容器拥有自己的hostname 和 UNIX domainname,这对于初始化和配置脚本是十分有用的,这些脚本根据这些名称来定制他们的操作。

3.IPC Namespace 隔离进程间通信。隔离了某些IPC资源(interprocess community,进程间通信)使划分到不同IPC Namespace 的进程组通信上隔离,无法通过消息队列、共享内存、信号量方式通信,这样,只有在同一个Namespace下的进程才能相互通信。如果你熟悉IPC的原理的话,你会知道,IPC需要有一个全局的ID,即然是全局的,那么就意味着我们的Namespace需要对这个ID隔离,不能让别的Namespace的进程看到。 要启动IPC隔离,我们只需要在调用clone时加上CLONE_NEWIPC参数就可以了。 int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);

4.PID Namespace 进程隔离。隔离了进程ID空间,不同的 PID Namespace 中的进程可以拥有相同的 PID。PID Namespace 的好处之一是,容器可以在主机之间迁移,同时容器内的进程保持相同的进程ID。PID Namespace 空间还允许每个容器拥有自己的 init (PID 1),它是“所有进程的祖先”,负责管理各种系统初始化任务,并在子进程终止时收割孤儿进程。

5.Network Namespace 网络资源隔离。每个 Network Namespace 都有自己的网络设备、IP地址、IP路由表、/proc/net目录、端口号等。

6.User Namespace 用户和用户组隔离。一个进程的用户和组ID在 User Namespace 空间外可以是不同的,一个进程可以在用户命名空间外拥有一个正常的无权限用户 ID,同时在命名空间内拥有一个(root 权限) 的用户ID。

上面说到了一句,要启动IPC隔离,我们只需要在调用clone时加上CLONE_NEWIPC参数就可以了

这主要是因为 Linux 的 namespace 主要是利用下面三个系统调用:

  • clone() – 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述参数达到隔离。
  • unshare() – 使某进程脱离某个namespace
  • setns() – 把某进程加入到某个namespace

深处我也不会~

参考文章来自于:https://coolshell.cn/articles/17010.html

也是在这里,我才知道 Docker 流行的真的好早好早,而我真的知道 Docker 的时候已经在2020年啦

5.4、通过实践来证明

理论结合实践才能更好的理解这些到底在说什么,我用一个简单的例子来简单阐述一下吧。

    docker run -it busybox /bin/sh

1、我们以交互式的方式进入容器,执行 ls 命令,可以看到常规的 unix 系统目录结构,但这并非是宿主机的文件系统。
在这里插入图片描述

有了文件系统隔离,我们在当前容器内对文件所做的操作并不会影响到外部宿主机的文件,另外我们启动不同的容器,我们所看到的文件系统也是隔离的。

2、隔离主机和域名信息。这点其实很好验证,当执行 hostname 命令时,控制台会返回当前主机名称给我们。

在这里插入图片描述

前者是在容器内部执行,返回的是容器ID,后者是在宿主机的控制台执行,输出的是ubuntu。

3、当我们执行 ifconfig 命令,我们会看到和我们宿主机不同的网卡和网关信息等

在这里插入图片描述

除了上面的方式,我们也可以通过 docker inspect <容器名|容器ID> 来看容器的相关信息,其中也包含了容器网络相关信息。

在这里插入图片描述

在这里插入图片描述

4、进程隔离也是同样如此,我们在宿主机和容器中分别执行 ps -ef | grep sh 命令,看看结果就知道啦

在这里插入图片描述

在容器内明显看不到宿主机其他进程情况,并且容器内的 /bin/sh PID 为1,PID =1 的进程是系统启动时的第一个进程,也称 init 进程。其他的进程,都是由它管理产生的。而此时,PID=1 却是 /bin/sh 进程。

而它在宿主机上所展示时,它的PID变成了 2456。

这一点也可以换成下面这条命令来准确定位

    docker inspect -f '{{.State.Pid}}' <容器名称或ID> # 查看容器的PID

这个结论就非常好说了,在容器中,它确确实实是完成了 PID 的隔离,明明宿主机上是 2456 的进程,变成了容器内的 1 号进程,同时在容器中还看不到宿主机其他进程。

5.5、Docker 中的 User Namespace 详解

Docker 中引入的 user namespace 可以让容器中有一个 “假”的 root 用户,它在容器内是 root,在容器外是一个非 root 用户。也就是 user namespace 实现了 host user 和 container user 之间的映射。

我们拿一个简单的例子来说明:

    docker run -it -v /bin:/host/bin busybox /bin/sh

先来解释下这条命令,现在是 dj(uid=1000,gid=1000)的用户,将本机上的 /bin 目录,挂载到容器中 /host/bin 目录下啦,并以交互式的方式启动了容器 busybox ,进入到容器内部。

那么现在有个问题:我在容器中的 /host/bin 目录下可以修改文件或者增加文件吗? 见下图。

在这里插入图片描述

答案是可以的。为什么呢?我一个非root用户,为什么可以操作root权限的文件呢?

原因:Docker容器运行的时候,如果没有专门指定user, 默认以root用户运行。它并不是说按照你现在的登录的用户去分配权限的,而是没有指定就默认使用root用户运行。

另外有没有觉得这是非常恐怖的一件事情,我明明没有 root 权限,却突然之间通过 docker 给容器挂载一个文件目录,虽然我一下没想起来可以做什么,但还是有点恐怖的哈。


你看到这里也许会觉得有些疑惑,为什么和此小节说的第一段话是自相矛盾的呢?

因为在 Docker中默认并没有开启 user namespace,这并不是说 Linux 机器没有支持 user namespace ,而是 docker 中没有开启。

在说如何让 Docker 启用 user namespace 之前,我们先用另一种方式来曲线救国一下。

我之前使用 id 命令, 看到了我当前用户 dj 的(uid=1001),我们在执行 docker run 记得指定一下即可。

上面的命令改为:

    docker run -it  -u 1001:1001 -v /bin:/host/bin busybox /bin/sh

我们使用了 -u 1001:1001 来指定了容器内所使用的用户。我们再重复一下上面的操作。

在这里插入图片描述

显示权限不足啦。

5.6、如何让 Docker启用 User namespace 呢?

1、备份 docker 配置文件

在这里插入图片描述

因为我之前没有配置文件,所以我直接新建了一个文件

2、修改 docker 配置文件

添加 User Namespace 配置: 在配置文件中添加以下内容以启用 User Namespace。这将告诉 Docker 守护进程使用 User Namespace 功能。

    {
      "userns-remap": "default"
    }

3、保存文件,重启启动 Docker 服务,以使配置文件生效

    sudo systemctl restart docker

4、验证配置

    cat /etc/subuid
    cat /etc/subgid

在这里插入图片描述

dockermap 是默认的映射名称。

补充/etc/subuid 是一个系统配置文件,用于管理用户命名空间中用户的子用户标识(sub-IDs)。不多占篇幅介绍了,/etc/subgid 相应就是用户组的标识。具体感兴趣可以去了解。

现在我们再执行上面之前测试的命令。

    docker run -it -v /bin:/host/bin busybox /bin/sh

在这里插入图片描述

我们还是使用 dj 这个用户,但是它已经没有权限修改从宿主机 /bin 映射到容器中的/host/bin

的目录了。

并且使用 id 命令,可以看到容器内部是 root 用户,但实际上它在容器外并不是root 用户。

另外还可以查找容器的PID(进程号),通过容器的进程号,来查看它的命名空间。

    docker inspect -f '{{.State.Pid}}' <容器名称或ID> # 查看容器的PID

在这里插入图片描述

查看进程命名空间:

    cat /proc/${容器PID}/uid_map

在这里插入图片描述

/proc/5517/uid_map 它表示了容器内外用户的映射关系即将host 上的 231072 用户映射为容器内的 0 即 root 用户。

这说明通过使用 user namespace 使得容器内的进程运行在非 root 用户上,我们成功地限制了容器内进程的权限

5.7、Docker 为什么不默认启用 User namespace呢?

编写这一小章节的时候,我原打算去找资料啦,因为我已经猜测到一些原因,但需要验证一下,但是看到还没关闭的 GPT窗口,就打算拿它试一下。答案如下:

在这里插入图片描述

结论也很容易得出,Docker 希望降低复杂性,获取更强的兼容性,降低故障排查难度,也希望降低普通开发人员的使用门槛,更好的推广。

5.8、Namespace 存在的问题

我们都知道 Namespace 的隔离是轻量化的,比起虚拟化的隔离,它的缺陷也很明显,就是无法进行彻底的隔离

因为不管如何隔离,它都是依赖于同一个内核的,那么此时内核就成了所有容器的共享变量,改动了一次,就会影响到全部。

5.9、检查 linux 操作系统是否启用了 namespace

运行下面的命令即可检查是否启用了:

    root@ubuntu:/home# uname -a
    Linux ubuntu 5.15.0-78-generic #85~20.04.1-Ubuntu SMP Mon Jul 17 09:42:39 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
    root@ubuntu:/home# cat /boot/config-5.15.0-7
    config-5.15.0-76-generic  config-5.15.0-78-generic  
    root@ubuntu:/home# cat /boot/config-5.15.0-78-generic | grep CONFIG_USER_NS
    CONFIG_USER_NS=y
    root@ubuntu:/home#

如果是 「y」,则启用了,否则未启用。同样地,可以查看其它 namespace:

    CONFIG_UTS_NS=y
    CONFIG_IPC_NS=y
    CONFIG_USER_NS=y
    CONFIG_PID_NS=y
    CONFIG_NET_NS=y

六、Linux Control Cgroups

无论 Docker 如何进行隔离,无法否认的是我们在当前宿主机中运行的所有容器,它依赖的硬件资源都只是当前机器。

另外在上文中我们也谈到,其实启动的每一个容器进程,它本身其实就是当前宿主机的进程之一,那么本质上来说,它也会和宿主机中的其他进程进行资源的竞争。

6.1、Control Cgroups

所以我们就要针对Docker运行的容器进行资源的限制,Cgroups 就是 Linux 内核中用来为进程设置资源的一个技术。

Linux Cgroups 的全称是 Linux Control Group。它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。

还可以对进程进行优先级设置,审计,挂起和恢复等操作。

Linxu中为了方便用户使用cgroups,已经把其实现成了文件系统,其目录在/sys/fs/cgroup下,在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。这些都是我这台机器当前可以被 Cgroups 进行限制的资源种类。Cgroups 的每一个子系统都有其独有的资源限制能力,比如:

cpu:限制进程在一段时间内能够分配到的 CPU 时间 blkio:为块设备设定 I/O 限制,一般用于磁盘等设备 cpuset:为进程分配单独的 CPU 核和对应的内存节点 memory:为进程设定内存使用的限制

完整子系统如下图:

在这里插入图片描述

补充:当然这里面还牵扯到许多其他的问题,比如是如何实现的,资源如何分配等等,我也不会啦。

我们可以把 Linux Cgroups 理解成一个子系统目录加上一组资源限制文件的组合。而对于 Docker 等 Linux 容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的 PID 填写到对应控制组的 tasks 文件中就可以了。而至于在这些控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的参数指定了。

6.2、Cgroups 存在的问题

跟 Namespace 的情况类似,Cgroups 对资源的限制能力也有很多不完善的地方,被提及最多的自然是 /proc 文件系统的问题。

我们知道,Linux 下的 /proc 目录存储的是记录当前内核运行状态的一系列特殊文件,用户可以通过访问这些文件,查看系统以及当前正在运行的进程的信息,比如 CPU 使用情况、内存占用率等,这些文件也是 top 指令查看系统信息的主要数据来源。

在这里插入图片描述

但是,你如果在容器里执行 top 指令,就会发现,它显示的信息居然是宿主机的 CPU 和内存数据,而不是当前容器的数据。

在这里插入图片描述

我在容器中执行 free 命令,展示的是我宿主机的相关信息。

造成这个问题的原因就是,/proc 文件系统并不知道用户通过 Cgroups 给这个容器做了什么样的资源限制,即: /proc 文件系统不了解 Cgroups 限制的存在

最后

写到这里的时候,看着好像是写完了,但其实还只是浅浅的开了个端罢了,很多想写,能力上知识面上都有所欠缺,还在加油学,希望能一起交流和学习~

在写相关内容的时候,拜读了许多大佬写的文章,同时也借鉴和整合多位大佬的文章内容,真的是知道的越多,也就是知道的越少。

以往认为对 Linux 已经有所了解,但到要一探究竟的时候,就纯纯一小白了,也是因为这次偶然的对 Docker 的好奇,让我对 Linux 有了更深刻的了解。

好奇永远都是探究式学习的动力之一

~

参考文章

https://moelove.info/2021/11/17/一篇搞懂容器技术的基石-cgroup/#contents:cgroup-的核心文件—张晋涛

https://www.cnblogs.com/rexcheny/p/11017146.html 重点参考

https://developer.aliyun.com/article/377862 Linux user namespace 重点参考

https://developer.aliyun.com/article/377862 非常优秀,给了很多思路。

https://www.cnblogs.com/michael9/p/13039700.html#容器的文件系统容器镜像—rootfs

https://zhuanlan.zhihu.com/p/445258335

https://www.liaoxuefeng.com/article/1481991528644643

https://blog.csdn.net/Yosigo_/article/details/119124013

https://blog.csdn.net/qq_43380180/article/details/125953218

https://www.bilibili.com/video/BV163411G7vb/?spm_id_from=333.999.0.0&vd_source=f9d1f15d0ed8efc261af664b960ef668

https://zhuanlan.zhihu.com/p/392508816 rootfs 文件系统

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

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

相关文章

QT学习笔记-开发环境编译Qt MySql数据库驱动与交叉编译Qt MySql数据库驱动

QT学习笔记-开发环境编译Qt MySql数据库驱动与交叉编译Qt MySql数据库驱动 0、背景1、基本环境2、开发环境编译Qt MySql数据库驱动2.1 依赖说明2.2 MySQL驱动编译过程 3、交叉编译Qt MySql数据库驱动3.1 依赖说明3.3.1 如何在交叉编译服务器上找到mysql.h及相关头文件3.3.2 如果…

【PHP】基础语法变量常量

文章目录 PHP简介前置知识了解静态网站的特点动态网站特点 PHP基础语法代码标记注释语句分隔(结束)符变量变量的基本概念变量的使用变量命名规则预定义变量可变变量变量传值内存分区 常量基本概念常量定义形式命名规则使用形式系统常量魔术常量 PHP简介 PHP定义&#xff1a;一…

【服务器】Strace显示后台进程输出

今天有小朋友遇到一个问题 她想把2331509和2854637这两个进程调到前台来&#xff0c;以便于在当前shell查看这两个python进程的实时输出 我第一反应是用jobs -l然后fg &#xff08;参考这里&#xff09; 但是发现jobs -l根本没有输出&#xff1a; 原因是jobs看的是当前ses…

Oracle Database12c数据库官网下载和安装教程

文章目录 下载安装Oracle自带的客户端工具使用 下载 进入oracle官网 点击下载连接之后右上角会有一个下载 我们只需要数据库本体就够了 运行这个下载器 等待下好之后即可 出现 Complete 之后代表下载成功&#xff0c;然后我们解压即可 安装 双击 双击setup.exe 根据…

NLP | 基于LLMs的文本分类任务

比赛链接&#xff1a;讯飞开放平台 来源&#xff1a;DataWhale AI夏令营3&#xff08;NLP&#xff09; Roberta-base&#xff08;BERT的改进&#xff09; ①Roberta在预训练的阶段中没有对下一句话进行预测&#xff08;NSP&#xff09; ②采用了动态掩码 ③使用字符级和词级…

引领行业高质量发展|云畅科技参编《低代码开发平台创新发展路线图(2023)》

8月8日-9日&#xff0c;中国电子技术标准化研究院于北京顺利召开《低代码开发平台创新发展路线图&#xff08;2023&#xff09;》封闭编制会。云畅科技、浪潮、百度、广域铭岛等来自低代码开发平台解决方案供应商、用户方、科研院所等近30家相关单位的40余位专家参与了现场编制…

android studio gradle build running慢 卡住不动 失败 原因与解决方式

快速导航 分析原因解决办法 分析原因 主要原因是 gradle 构建时无法从网络获取需要的包或库。 解决办法 将国外库替换为阿里云镜像库。 例如 google 对应的库是 maven { url ‘https://maven.aliyun.com/repository/google’ }

基于决策树(Decision Tree)的乳腺癌诊断

决策树(DecisionTree)学习是以实例为基础的归纳学习算法。算法从--组无序、无规则的事例中推理出决策树表示形式的分类规则,决策树也能表示为多个If-Then规则。一般在决策树中采用“自顶向下、分而治之”的递归方式,将搜索空间分为若千个互不相交的子集,在决策树的内部节点(非叶…

容灾双活方案,异地容灾备份与双活

数据信息的安全性和完整性面临着硬件问题、病毒入侵、自然灾害等各种威胁。为了应对这些威胁&#xff0c;公司需要采取有效的数据保护措施&#xff0c;其中特别重要的是外部容灾备份和双活技术。  让我们来看看其他地方的容灾备份。这是一种可以将数据复制到避免初始区域的设…

IO day 7

1、使用消息队列完成两个进程间相互通信 msgsnd #include <myhead.h>typedef struct {long msgtype;char data[1024]; }Msg_ds;#define SIZE sizeof(Msg_ds)-sizeof(long)int main(int argc, const char *argv[]) {//创建key值key_t key;if((key ftok("/",k…

物通博联嵌入式数据采集网关采集传感器的数据上传到云端

在当今的物联网&#xff08;IoT&#xff09;时代&#xff0c;各种传感器广泛应用于各种工业领域。传感器数据采集是实现自动化生产的基础&#xff0c;可以为企业决策提供科学的数据支持&#xff0c;通过各类智能传感器采集传输终端&#xff0c;将采集的传感器数据实时传输到设备…

如何将应用程序发布到 App Store

憧憬blog主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是移动开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索iOS开发的无限可能&#xff01; &#x1f525;我们与您分享最新的技术洞察和实战经验&#xff0…

电工-学习电工有哪些好处

学习电工有哪些好处&#xff1f;在哪学习电工&#xff1f; 学习电工有哪些好处&#xff1f;在哪学习电工&#xff1f;学习电工可以做什么&#xff1f;优势有哪些&#xff1f; 学习电工可以做什么&#xff1f;学习电工有哪些好处&#xff1f; 就业去向&#xff1a;可在企业单位…

docker 03(docker 容器的数据卷)

一、数据卷的概念和作用 删除后&#xff0c;数据也没了。 不能 数据卷 是宿主机中的一个目录或文件当容器目录和数据卷目录绑定后&#xff0c;对方的修改会立即同步一个数据卷可以被多个容器同时挂载 作用&#xff1a; 容器数据持久化 外部机器和容器间接通信 容器之间数据交换…

【运维】linkis1.3.2添加jdbc引擎(添加mysql、greenplum、starrocks、doris数据源查询)与配合多数据源管理提交任务初探

文章目录 一. 引擎的安装1. 前置工作2. 获取引擎插件3. 上传和加载4. 引擎刷新4.1. 重启刷新4.2. 检查引擎是否刷新成功 二. 测试mysql、starrocks与doris数据库1. 通过shell提交任务2. 通过(IDE)shell进行提交3. 通过接口提交 三. 添加greenplum四. 通过linkis的数据源管理提交…

【算法刷题之数组篇(1)】

目录 1.leetcode-59. 螺旋矩阵 II&#xff08;题2.题3相当于二分变形&#xff09;2.leetcode-33. 搜索旋转排序数组3.leetcode-81. 搜索旋转排序数组 II(与题目2对比理解)&#xff08;题4和题5都是排序双指针&#xff09;4.leetcode-15. 三数之和5.leetcode-18. 四数之和6.leet…

无涯教程-Perl - undef函数

描述 此函数未定义EXPR的值。用于标量,列表,哈希,函数或类型范围。在带有诸如undef $hash {$key}之类的语句的哈希上使用&#xff1b;实际上将指定键的值设置为未定义的值。 如果要从哈希中删除元素,请使用delete函数。 语法 以下是此函数的简单语法- undef EXPRundef返回…

学会Mybatis框架:让你的代码更具灵活性、可维护性、安全性和高效性【二.动态SQL】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Mybatis的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Mybatis动态SQL如何应用 1.需求 2.…

resultType和paramstype的区别

文章目录 1. resultType&#xff1a;2. parameterType&#xff1a;3. 总结看这里就够啦&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;举例 1. resultType&#xff1a; 返回值类型&#xff0c;类型即为对象类型&#xff0c;返回结果字段与对象属性…

【洁洁送书第五期】为什么我们要了解可观测性工程

导读 可观测性已成为一个热门话题&#xff0c;并广受关注。随着它的普及&#xff0c;“可观测性”不幸被误作“监控”或“系统遥测”的同义词。可观测性是软件系统的一个特征。而且&#xff0c;只有当团队采用新的实践进行持续开发时&#xff0c;才能在生产软件系统中有效利用这…