当前位置: 首页 > Linux

深入解析Container--系统架构

时间:2023-04-06 19:34:09 Linux

什么是Container?随着Kubernetes、DockerCompose、MesosOS、Consul等的出现,容器成为了这些云计算时代的头条新闻。要真正理解容器的架构组成,首先需要了解以下内容:LinuxKernelUser&SystemSpaceSyscallsandCapabilitiesCgroupsNamespacesEIAF(EverythingIsAFile),对基于Unix的文件系统的描述虽然Mac内核是POSIX兼容的并且基于OpenBSD,这5件事对于真正理解容器的工作原理以及为什么需要LinuxVM在Windows和Mac上运行容器很重要。为什么我要这样开始?我将深入研究有关容器的文章,重点是Docker、Dockerfile和dockerbuild-t等命令。相当小的部分仍将在后面的部分中介绍,但我今天介绍的是它们工作原理的基础。所以我采取了不同的方法,直接研究容器的工作原理、外观和功能。让我们从了解Linux内核系统和用户空间开始,以深入了解容器的实际作用。Linux内核空间在Linux中,我们通常有两个空间用于运行应用程序,内核系统空间和用户空间。通常,在默认内核配置下,用户空间占用0-3GB,而内核空间占用3-4GB。内核空间是我们为运行内核的低级应用程序提供系统内存的地方。用户空间是我们的用户进程运行和执行的环境。这两个内存空间被称为“环”的微调权限层分开。这些环定义了在授予某些操作之前应用程序需要满足的特权或特权级别。这些环并不是Linux特有的,而是操作系统内部定义明确的布局,尽管每个级别的功能区域都是根据操作系统运行的CPU架构分配的。为了在用户空间和内核空间之间切换,我们通过系统调用(简称syscalls)应用操作。这使用可从用户空间应用程序访问的已定义内核函数来请求访问内核级函数。下图完美地说明了这个顺序是如何定义的。每当应用程序向内核级函数发出请求时,都会发送一个中断,通知处理器停止正在执行的操作并处理该特定请求,如果简化理解,可以将其视为上下文切换。如果用户空间应用程序拥有相关权限,则可以在内核空间进行上下文切换,上下文切换发起后,用户空间应用程序将等待响应并通过适当的中断在内核空间执行需要的程序处理程序/函数。tmp\_buf=mmap(文件,len);#这里的mmap来自C库\#这称为内存映射,它是一个C函数\#它为任务、文件等分配一定数量的内存。\#由于内存是内核空间资源,因此系统调用是对linux内核中的mmap系统调用进行调用以使此请求成为可能SysCalls和Capabilities系统调用akasyscall是一种API,它允许将一小部分内核功能公开给用户级应用程序。真正强调的一小部分是告诉任何阅读的人,系统调用是有限的,并且对于特定目的是通用的。它们在每个操作系统上都不同,访问内容和访问方式也不同。回到前面的mmap示例,接口没有列出,因为它是一个小子集,这里提供了linux中系统调用的完整列表。有时,我们有一组系统调用想要组合在一起,我们使用称为Capabilities的Linux内核功能来实现。这些是运行中的程序可以访问或受其限制的预定义权限集。功能通过将相关调用分组为可立即授予或拒绝的已定义权限,进一步增强了系统调用。这甚至可以防止根级应用程序利用保留权限利用受限的内核空间。有几个linux功能,稍后将在大多数文章中访问这些功能,以了解它们如何使用SecComp等配置文件与容器集成,更具体地说是AppArmor、SELinux等LSM(Linux安全模块),但您可以在此处参考手册页中的列表.Cgroups控制组(通常称为cgroups)是Linux内核的一项功能,它允许将进程组织成层次结构,然后可以限制和监视其对各种类型资源的使用的组。内核的cgroup接口是通过一个名为cgroupfs的伪文件系统实现的。内核代码分组在核心cgroup中,而资源跟踪和限制分组在每种资源类型(内存、CPU等)的子系统中。简单来说,cgroups控制着我们可用的功能。它们的功能列表如下:资源限制:组可以配置为不超过指定的内存限制或使用比预期更多的处理器,或限制为特定外围设备。优先级:一个或多个组可以配置为使用更少或更多的CPU或磁盘I/O吞吐量。会计:按组监控和测量资源使用情况。控制:进程组可以被冻结或停止并重新启动。Cgroups通过使用可以修改进程运行时环境的子系统/控制器来工作。v1和v2版本均提供多个控制器。在v1控制器领域,我们有以下优势:blkio-该子系统对块设备(如物理驱动器(磁盘、固态或USB))的输入/输出访问设置限制。cpu-该子系统使用调度程序为cgroup任务提供对CPU的访问。cpuacct—此子系统生成有关cgroup中任务使用的CPU资源的自动报告。cpuset-此子系统将各个CPU(在多核系统上)和内存节点分配给cgroups中的任务。devices-该子系统允许或拒绝cgroup中的任务访问设备。freezer-该子系统暂停或恢复cgroup中的任务。memory-该子系统对cgroup中任务的内存使用设置限制,并自动生成有关这些任务使用的内存资源的报告。net_cls—该子系统使用类标识符(classid)标记网络数据包,允许Linux流量控制器(tc)识别源自特定cgroup任务的数据包。net_prio-此子系统提供了一种动态设置每个网络接口的网络流量优先级的方法。ns—命名空间子系统。perf_event—该子系统识别任务的cgroup成员资格,可用于性能分析。hugetlb-支持限制cgroups使用大页面。pids-此控制器允许限制可以在cgroup(及其后代)中创建的进程数。rdma—RDMA(远程DMA)控制器允许限制每个cgroup对RDMA/IB专用资源的使用。对于v2控制器空间,我们具有v1的一些功能,因为某些控制组未实现,linux系统可以同时使用两者,但v2系统更紧凑且cgroup更少。io—这是版本1blkio控制器的继承者。内存——这是版本1内存控制器的后续版本。pids—与版本1pids控制器相同。perf_event—与版本1perf_event控制器相同。rdma—与版本1rdma控制器相同。cpu—这是版本1cpu和cpuacct控制器的继承者。您会注意到它在功能上与版本1控制器相同。每个cgroup都提供限制一个或多个资源的能力。用于此问题的库和工具将在后面的部分中重新介绍。命名空间让容器相信它们存在于一个完全隔离的环境中,而不是在主要的主机系统中。更具体地说,容器内的进程将自己视为系统上的唯一进程。你可以认为它在盒子里,在盒子里,你认为你是盒子的主人,但你只是在另一个人的盒子里做梦,而这个人实际上拥有两个盒子。由于此功能,可以在容器内运行容器,尽管我们稍后会谈到这一点。对于命名空间功能,它嵌入在Linux组件中。这些名称空间提供不同的功能。IPC—IsolationInter-ProcessCommunication,这个大词的意思是进程之间可以沿着通道或管道共享消息,就像流水通过管道一样。没有命名空间,就像我们的主管道一样,一个容器(进程)可以使用同一个管道向其他进程提供数据。管道对于命名空间是唯一的,并且仅限于命名空间内的某些进程。在linux中,这是使用/dev/shm(共享内存)或/dev/mqueue(消息队列)块文件从主机共享的。Network——负责隔离IP地址、接口、网络请求、端口等。Mount——限制在主机上使用卷和外部数据挂载。命名空间中的进程在其自己的本机文件系统中运行。PID——在进程运行时隔离宿主机上的进程和命名空间中的进程之间的纯粹限制。所以宿主机上的bash实例和容器里的是不一样的。这是一项独特的功能,使我们能够在主机本身之外的容器中运行应用程序。User——限制容器用户的UID(UserID)和GID(GroupID)分配。这有效地保护了主机,因为容器无法从主机读取信息。UTS—用于设置或获取主机名,非常简单。所有这些命名空间都是使用unshare系统调用来隔离资源来实现的。Linux文件系统在Linux中首先要注意的是一切皆文件。我没有骗你,从存储设备、串行设备等/dev/\*一直到/proc/fileystems中的文件系统列表,一直到主机上运行的cgroup。不同文件系统之间的大多数交互都由虚拟文件系统驱动程序(VFS)处理,但这是另一天的话题。因为一切都是文件,所以我可以直接cat查看支持的配置。如前所述,容器是进程。我们都理解容器和VM之间的区别的方式是容器共享主机的内核和它的一些资源。这里的主要提示是资源,容器使用不同的根文件系统开始自己的操作,容器(提示:它们是进程)启动的实际文件系统是图像。该映像是一个Linux文件系统,大部分是压缩的,然后使用一些COW(写时复制)文件系统(例如AUFS、设备映射器、Btrfs、XFS等)从中执行。对于安装了docker的用户,可以运行以下命令查看docker镜像的内部结构(不是容器,容器是进程,运行镜像等)mkdirrootfs&&\dockerexport$(dockercreateubuntu:18.04)|tar-Crootfs-xvf-主linux文件系统非常特别,因为您会注意到vmlinuz和initrd.img,稍后我会讲到。这里我们注意到我们在主文件系统上看不到的initrd和vmlinuz文件,这是因为这两个文件是内核文件。InitRD—InitRamDisk初始RAM磁盘(initrd)是在实际根文件系统可用之前安装的初始根文件系统。initrd绑定到内核并作为内核引导过程的一部分加载。然后内核将这个initrd挂载为两阶段引导过程的一部分,以加载模块以使真正的文件系统可用并获得真正的根文件系统。VMLinuz—虚拟内存LINUxgZipvmlinuz是Linux内核可执行文件的名称。vmlinuz是压缩的Linux内核,能够将操作系统加载到内存中,使计算机可用并准备好运行应用程序。在Linux上,您可能会遇到vmlinux或vmlinuz。它们是相同的,但其中一个是压缩的。vmlinuz=VirtualMemoryLINUxgZip=压缩的Linux内核可执行文件vmlinux=VirtualMemoryLINUX=非压缩的Linux内核可执行文件vmlinuz和initrd文件在启动时使用。现在,这是主要原因,容器文件系统或镜像中没有这两个文件。容器使用主机内核!不需要启动序列来获取内核,容器中应用程序的所有可能请求都是通过主机调用发出的,这些请求通过环、功能、seccomp、LSM等强制执行,一路返回到所有正常的linux程序.这里的主要思想是容器只是使用完全不同的文件系统,但它们共享相同的Linux内核。对于那些对linux有一点了解的人,我们知道我们可以chroot进入外部linux文件系统并在其中运行,就好像文件系统已启动一样,前提是主机上的所有必需文件都通过bind安装在该文件夹中。如果您阅读了那篇文章的某些部分,您会发现容器就像chroots,只是与名称空间、cgroup和许多其他很酷的东西打包在一起,以尽可能确保同一主机上的应用程序沙箱安全。Summary容器是一个运行时进程,它在由cgroups和各种其他LSM和安全功能管理的命名空间内执行,以确保在运行时完全隔离进程。容器中的这些进程,尤其是像Docker这样的容器运行时,自动化了很多讨论的事情,但我正在解释的主要底层仍然是相同的。