当前位置: 首页 > news >正文

浅谈日常使用的 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/subuidcat /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 -aLinux 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/Linuxroot@ubuntu:/home# cat /boot/config-5.15.0-7config-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_NSCONFIG_USER_NS=yroot@ubuntu:/home#

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

    CONFIG_UTS_NS=yCONFIG_IPC_NS=yCONFIG_USER_NS=yCONFIG_PID_NS=yCONFIG_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 文件系统

相关文章:

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

适合的读者&#xff0c;对Docker有过简单了解的朋友&#xff0c;想要进一步了解Docker容器的朋友。 前言 回想我这两年&#xff0c;一直都是在使用 Docker&#xff0c;看过的视频、拜读过的博客&#xff0c;大都是在介绍 Docker 的由来、使用、优点和发展趋势&#xff0c;但对…...

前端面试:【DOM】编织网页的魔法

嘿&#xff0c;亲爱的代码魔法师&#xff01;在JavaScript的奇幻世界里&#xff0c;有一项强大的技能&#xff0c;那就是DOM操作。DOM&#xff08;文档对象模型&#xff09;操作允许你选择、修改和创建网页元素&#xff0c;就像是在编织一个魔法的网页。 1. 什么是DOM&#xff…...

基于MATLAB开发AUTOSAR软件应用层Code mapping专题-part 2 Inport和Outports 标签页介绍

上篇我们介绍了Function页的内容,这篇我们介绍Inports和Outports页的内容,这里我们再次强调一个概念,code mapping是以simulink的角度去看的,就是先要在模型中建立simulink模块,在code mapping里映射他要对应的autosar的元素,之后生成代码时的c语言的名字是以Autosar的元…...

第9步---MySQL的索引和存储引擎

第9步---MySQL的索引和存储引擎 1.索引 1.1分类 索引可以快速的找出具有特定值的行。不用从头开始进行寻找了。 类别 hash和btree hash 根据字段值生生成一个hash的值 快速的进行定位到对应的行的值 可能会出现相同的值&#xff0c;找到对应的空间会出现对应的值 btree树…...

Numpy入门(3)—线性代数

线性代数 线性代数&#xff08;如矩阵乘法、矩阵分解、行列式以及其他方阵数学等&#xff09;是任何数组库的重要组成部分&#xff0c;NumPy中实现了线性代数中常用的各种操作&#xff0c;并形成了numpy.linalg线性代数相关的模块。本节主要介绍如下函数&#xff1a; diag&am…...

php的openssl_encrypt是不是自动做了PKCS5Padding?

在PHP中&#xff0c;openssl_encrypt函数默认使用的是PKCS7填充&#xff08;不是PKCS5填充&#xff09;。PKCS7填充实际上是PKCS5填充的扩展&#xff0c;用于对不同块大小的数据进行填充。 当你使用openssl_encrypt函数进行加密时&#xff0c;如果你没有显式指定填充模式和填充…...

在本地创建repository及上传至github

文章目录 本地管理设定git的用户名与邮箱初始化添加修改提交修改设定分支问题一:error: insufficient permission for adding an object... 数据同步创建SSH keys创建并关联远程仓库上传改动至github问题二:Failed to connect to github.com port 443: Connection timed out问题…...

情人节特别定制:多种语言编写动态爱心网页(附完整代码)

写在前面案例1&#xff1a;HTML Three.js库案例2&#xff1a;HTML CSS JavaScript案例3&#xff1a;Python环境 Flask框架结语 写在前面 随着七夕节的临近&#xff0c;许多人都在寻找独特而令人难忘的方式来表达爱意。在这个数字时代&#xff0c;结合创意和技术&#xff0…...

Docker mysql主从同步安装

1. 构建master实例 docker run -p 3307:3306 --name mysql-master \ -v /mydata/mysql-master/log:/var/log/mysql \ -v /mydata/mysql-master/data:/var/lib/mysql \ -v /mydata/mysql-master/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORDroot \ -d mysql:5.7 2. 构建master配置…...

docker update 命令

docker update 更新一个或多个容器的配置。官方文档 用法 $ docker update [OPTIONS] CONTAINER [CONTAINER...]请参阅选项部分OPTIONS&#xff0c;了解此命令可用的概述。 描述 该docker update命令动态更新容器配置。您可以使用此命令来防止容器消耗 Docker 主机的过多资…...

阻塞和挂起的区别和联系

阻塞和挂起是进程两种不同的状态&#xff0c;其描述如下&#xff1a; 阻塞&#xff1a;正在执行的进程由于发生某时间&#xff08;如I/O请求、申请缓冲区失败等&#xff09;暂时无法继续执行。此时引起进程调度&#xff0c;OS把处理机分配给另一个就绪进程&#xff0c;而让受阻…...

水力发电厂测量装置配置选型及厂用电管理系统

《水力发电厂测量装置配置设计规范》对水电厂的测量装置配置做了详细要求和指导。测量装置是水力发电厂运行监测的重要环节&#xff0c;水电厂的测量主要分为电气量测量和非电量测量。电气测量指使用电的方式对电气实时参数进行测量&#xff0c;包括电流、电压、频率、功率因数…...

【RabbitMQ】RabbitMQ整合SpringBoot案例

文章目录 1、前情提要【RabbitMQ】2、RabbitMQ-SpringBoot案例 -fanout模式2.1 实现架构总览2.2 具体实现2.2.1生产者2.2.1消费者 1、前情提要【RabbitMQ】 【RabbitMQ】消息队列-RabbitMQ篇章 RabbitMQ实现流程 2、RabbitMQ-SpringBoot案例 -fanout模式 2.1 实现架构总览…...

如何在window下cmd窗口执行linux指令?

1.Git&#xff1a;https://git-scm.com/downloads(官网地址) 2.根据自己的实际路径,添加两个环境变量 3.重启电脑...

c++基础系列:字符串、向量和数组

字符串、向量和数组 命名空间的using声明 目前用到的库函数基本上都属于命名空间std&#xff1b;通过using声明&#xff08;using declaration&#xff09;实现更简单的途径使用到命名空间中的成员。 标准库类型string string表示可变长的字符序列&#xff0c;必须先包含st…...

docker 05(dockerfile)

一、docker镜像原理 镜像可以复用 二、容器转镜像 将容器保存为镜像[参考] docker commit -a -m 现有容器ID 保存后的名称&#xff1a;版本号 -a :提交的镜像作者&#xff1b; -c :使用Dockerfile指令来创建镜像&#xff1b; -m :提交时的说明文字&#xff1b; -p :…...

PostMan 测试项目是否支持跨域

使用PostMan可以方便快速的进行跨域测试。 只需要在请求头中手动添加一个Origin的标头&#xff0c;声明需要跨域跨到的域&#xff08;IP&#xff1a;端口&#xff09;就行&#xff0c;其余参数PostMan会自动生成。添加此标头后&#xff0c;请求会被做为一条跨域的请求来进行处…...

jsp 协同过滤 图书管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 协同过滤 图书管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境 为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为My…...

商城-学习整理-高级-商城业务-商品上架es(十)

目录 一、商品上架1、sku在ES中存储模型分析2、nested数据类型场景3、构造基本数据&#xff08;商品上架&#xff09; 二、首页1、项目介绍2、整合thymeleaf&#xff08;spring-boot下模板引擎&#xff09;渲染页面3、页面修改不重启服务器实时更新4、渲染二级三级数据 三、搭建…...

【水文学法总结】河道内生态流量计算方法(含MATLAB实现代码)

生态流量&#xff08;Ecological Flow, EF&#xff09; 是指维持河道内生态环境所需要的水流流量。生态流量计算方法众多&#xff0c;主要分为水文学方法、栖息地模拟法、水力学方法、整体法等&#xff0c;各方法多用于计算维持河道生态平衡的最小生态流量&#xff08;Minimum …...

特斯拉Model 3的七年狂飙

‍ 作者 | 张祥威 编辑 | 德新 发布一周拿下32万张订单&#xff0c;之后用时五年&#xff0c;交付量突破100万辆。粗略计算&#xff0c;自2016年发布至今&#xff0c;特斯拉Model 3已交付超150万辆。 放眼新能源赛道&#xff0c;如此战绩 别无二家。 Model 3踩中纯电动车的…...

物流签收异常,财务对账复杂,怎么解决?

电子商务行业的蓬勃发展为人们的购物体验带来了巨大的便利&#xff0c;然而&#xff0c;随之而来的物流签收异常和财务对账复杂问题却给电商企业的财务部门带来了一系列困扰。 每天大量的订单和货物流转&#xff0c;不可避免地导致了物流签收数据与财务记录之间的不一致和差异…...

docker之镜像与数据卷

镜像 简介 1.镜像是一种轻量级、可执行的独立软件包&#xff0c;用来打包软件运行环境和基于环境开发的软件&#xff0c;他包含运行某个软件所需的所有内容&#xff0c;包括代码、运行时库、环境变量、配置文件 2.将所有的应用和环境11&#xff0c;直接打包成docker镜像&…...

服务器为什么会被攻击?43.248.186.x

进入信息时代&#xff0c;各种行业对数据的安全和技术要求也越来越高,同时也遇到了各种各样的服务器故障问题,但是网络攻击才是最大的伤害&#xff0c;在这上面造成了企业不必要的损失,那么服务器为什么会被攻击&#xff0c;这里给大家讲解一下服务器被攻击有哪几种情况 一、服…...

文心一言 VS 讯飞星火 VS chatgpt (79)-- 算法导论7.4 4题

四、请用go语言&#xff0c;证明:RANDOMIZED-QUICKSORT 期望运行时间是 Ω(nlgn)。 文心一言&#xff1a; 首先&#xff0c;为了证明RANDOMIZED-QUICKSORT的期望运行时间是Ω(nlg n)&#xff0c;我们需要证明在最坏的情况下&#xff0c;该算法的运行时间是O(nlg n)。然后&…...

整理mongodb文档:索引

个人博客 整理mongodb文档:索引 个人博客&#xff0c;求关注&#xff0c;有问题的地方欢迎指出&#xff0c;觉得讲解的繁琐的也请指出 文章概叙 本文主要还是在shell下的操作。让大家了解下mongodb中index的用途&#xff0c;基本的索引的新增、查看、删除等&#xff0c;最后…...

EPICS libCom库(4) -- epicsTimer

epicsTimer.h描述了一个C和C timer功能。 1 C接口 1.1 epicsTimerNotify和epicsTimer /* 使用一个timer的代码必须实现epicsTimerNotify */ class LIBCOM_API epicsTimerNotify { public:enum restart_t { noRestart, restart };class expireStatus {public:LIBCOM_API expir…...

成都大运会,保障大型活动无线电安全需要…

成都大运会 7月28日&#xff0c;备受关注的第31届世界大学生夏季运动会在成都正式开幕。据悉&#xff0c;这是全球首个5G加持的智慧大运会&#xff0c;也是众多成熟信息技术的综合“应用场”。使用基于5G三千兆、云网、8K超高清视频等技术&#xff0c;在比赛现场搭建多路8K摄像…...

【Spring Boot】构建RESTful服务 — 实战:实现Web API版本控制

实战&#xff1a;实现Web API版本控制 前面介绍了Spring Boot如何构建RESTful风格的Web应用接口以及使用Swagger生成API的接口文档。如果业务需求变更&#xff0c;Web API功能发生变化时应该如何处理呢&#xff1f;可以通过Web API的版本控制来处理。 1.为什么进行版本控制 …...

6.Web后端开发【SpringBoot入门】

文章目录 1 SpringBoot快速入门1.1 Web分析 2. HTTP协议2.1 HTTP-概述2.1.1 介绍2.2.2 特点 2.2 HTTP-请求协议2.3 HTTP-响应协议2.3.1 格式介绍2.3.2 响应状态码 常见的相应状态码 3 WEB服务器3.1 服务器概述 1 SpringBoot快速入门 Spring的官网Spring Boot 可以帮助我们非常…...

越秀网站建设设计/河北关键词排名推广

初始化 1、从list初始化一个ndarray arr np.array([1, 2, 3]) #list转ndarry li arr.tolist() #ndarry转list print(type(arr),type(li))<class numpy.ndarray> <class list>2、通过内置函数初始化 arr np.zeros((2, 3)) #初始化一个2*3的元素…...

厦门做企业网站找谁/百度网盘下载电脑版官方下载

Windows&#xff1a; netstat -ano | find "8081" Linux&#xff1a; netstat -ano | grep 8081 Mac sudo lsof -i :8081...

网站开发实用技术2.8.5/人民日报今日新闻

由于oracle 的高维护费用 &#xff0c;公司决定 将Oracle 数据库迁移到mysql。下面是 对数据库迁移中 表和view视图迁移的 总结。 使用 navicat 工具 问题&#xff1a; MySQL 存在关键字和列名 或表名重复的现象 如&#xff1a;select from from 表名 解决方式&#xff1a; …...

口碑好的句容网站建设/免费做做网站

在Android项目中使用到了org.apache.commons.codec.jar包下的Hex.encodeHexString(byte[] input)方法进行加密。结果编译通过&#xff0c;运行时总是报java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString([B)Ljava/lang/String;错误。 原因&am…...

义马网站开发/浙江百度推广开户

vue2 npm run dev 卡住 今天遇到了很惊奇的一件事&#xff0c;就是我在改了我的代码之后&#xff0c;发现我的页面卡住了&#xff0c;然后去看了一下发现卡在了98% 这里&#xff0c;关闭重新来过也没有用。 后来仔细检查了代码之后发现&#xff0c;是因为我自己在 import 的时…...

越秀区网站建设公司/广州关键词seo

1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具&#xff0c;拥有自己独特的语法以及一个独立的处理引擎&#xff0c;效率上可能不如str自带的方法&#xff0c;但功能十分强大。得益于这一点&#xff0c;在提供了正则表…...