怎么用通俗易懂的话来解释Docker(容器)技术
官网上的介绍是这样的:Dockerisanopenplatformfordevelopersandsysadminstobuild,ship,andrundistributedapplications .其实看完这句话我还是不明白它是什么。我们慢慢解释吧。
但总而言之,将其想象为以新颖方式实现的超轻量级虚拟机通常是正确的。
当然,实现原理和应用与VM还是有很大不同的,专业名称是应用容器(ApplicationContainer)。
为什么要使用容器?那么应用程序容器是什么样的呢?一个完整的应用程序容器看起来就像一个安装了一组特定应用程序的虚拟机。
例如,如果我现在想使用MySQL,我会找到一个安装了MySQL的容器并运行它。
然后我就可以使用MySQL了。
然后直接安装MySQL就可以了。
为什么我需要这么奇怪的容器概念?话虽这么说,如果你真的想安装MySQL,你可能必须安装一堆依赖库,并根据你的操作系统平台和版本进行设置。
有时你必须从源代码编译并报告一堆莫名其妙的错误。
这不是真的。
很容易假装。
如果你的机器崩溃了,一切都必须重新启动,并且你可能必须重做配置。
但有了容器,你就相当于拥有了一个可以运行的虚拟机。
只要你可以运行容器,MySQL的配置就会被保存。
一旦您想更换一台机器,只需拿起容器并将其放入另一台机器即可。
不需要考虑硬件、操作系统、运行环境等,在公司一个很大的用处就是保证线下开发环境、测试环境和线上生产环境一致。
当年我在百度就经常遇到这种事。
当开发人员准备进行测试时,他们通常会收到一段代码和一张介绍上线步骤的部署表。
结果代码无法在测试机上运行,开发人员四处奔波寻找问题。
前一刻忘记提交配置文件,后一刻线上命令写错。
我发现了一个错误并报告了它。
开发组一看,为什么我又忘记在网上订单上写这个命令了?上线的时候也会出现类似的情况,而且你的软件版本和我机器上的不一样……我在亚马逊的时候,一个开发人员直接担任以上三个职位,还有自动化部署机制。
所以问题会少一些,但是上线大家还是会害怕。
如果使用容器,那么开发就直接在容器中完成。
测试时,对整个容器进行测试。
测试完毕后,只需在容器中进行修改即可上线。
通过容器,整个开发、测试和生产环境可以保持高度一致。
另外,容器也和VM一样具有一定程度的隔离性。
每个容器之间的数据和内存空间是相互隔离的,可以保证一定的安全性。
微服务架构之「容器技术」
如今,当我们谈论容器技术时,大家默认都会提到Docker。但实际上,在Docker出现之前,PaaS社区就已经有了以CloudFoundry和OpenShift为代表的容器技术,这在当时是主流。
那么为什么Docker最终会流行起来呢?传统的PaaS技术虽然可以将本地应用一键部署到云端,并部署在隔离的环境(容器)中,但其兼容性非常差。
因为主要原理是将本地应用程序和启停脚本打包在一起,然后上传到云服务器,然后通过云服务器中的脚本启动应用程序。
这种方法似乎很理想。
然而,在实际情况中,由于本地和云环境的差异,上传到云端的应用程序经常会报各种错误并无法运行,需要更改各种配置和参数来实现兼容性。
即使在设计迭代过程中,也必须重新调整多个版本的代码,这涉及大量的能源消耗。
不过,Docker通过一个小小的创新,完美解决了这个问题。
在Docker解决方案中,它不仅打包本地应用程序,还打包本地环境(操作系统的一部分),形成一个称为“Docker镜像”的文件包。
因此,这个“Docker镜像”包含应用程序运行所需的所有依赖项。
我们可以直接基于这个“Docker镜像”在本地进行开发和测试。
完成后,我们可以直接将这个“Docker镜像”上传到云端运行点击。
就这样。
Docker实现了本地和云环境完全一致,真正实现了一次开发、随处运行。
1.容器到底是什么?容器到底是什么?可能我们对容器了解不多,但对虚拟机却很熟悉,所以我们先看一下容器和虚拟机的对比和区别:上图左边是虚拟机的原理,右边是虚拟机的原理。
side就是Docker容器的原理。
虚拟机基于Hypervisor软件在宿主机上虚拟出一套操作系统所需的硬件设备,然后在这个虚拟硬件上安装GuestOS操作系统。
因此不同的应用程序可以在不同的GuestOS上运行。
它们相互独立,资源隔离,但由于创建虚拟机需要Hypervisor,并且每个虚拟机必须运行全套GuestOS操作系统,因此这种方法会产生大量的额外资源开销。
Docker容器中没有Hypervisor层。
虽然需要在主机上运行DockerEngine,但其原理与Hypervisor完全不同。
它不虚拟化硬件设备,也不独立部署一套完整的GuestOS操作系统。
Docker容器没有这么复杂的实现原理。
其实只是一个普通的流程,只不过是一个普通的流程经过了特殊的处理。
当我们启动容器(dockerrun )时,DockerEngine只是启动一个进程,该进程在我们的容器中运行应用程序。
但是DockerEngine对这个进程做了一些特殊的处理,经过这些特殊的处理之后,这个进程看到的外部环境就不再是宿主机的环境了(它看不到宿主机中的其他进程,并认为自己是唯一的)。
当前操作系统中的进程),并且DockerEngine还限制了该进程使用的资源,以防止其无限制地使用主机资源。
那么DockerEngine做了哪些特殊处理才能达到如此神奇的效果呢?2、容器如何实现隔离和资源节流?Docker容器主要使用两个技术点来隔离这个过程:理解这两个技术点对于理解容器的原理非常重要。
它们是容器技术的核心。
下面详细解释一下:3.什么是容器镜像?基础容器镜像实际上是一个rootfs,它包含操作系统文件系统(文件和目录),但不包含操作系统内核。
rootfs是挂载在容器根目录上的全新文件系统。
该文件系统与主机的文件系统无关。
它是一个完全独立的文件系统,用于为容器提供环境。
对于Docker容器来说,需要根据pivot_root语句将容器内的系统根目录更改为rootfs,使用rootfs,容器可以为进程创建完整的文件系统,并实现与主机的环境隔离。
正是有了rootfs,本地基于容器的应用程序和云应用程序的执行环境才能保持一致。
此外,为了方便镜像复用,Docker在镜像中引入了层(Layer)的概念,可以将多个镜像一层层堆叠起来。
这样,如果我们想要创建新的镜像,我们可以根据之前创建的镜像继续创建。
如上图所示,在这个例子中,最底层是OS启动,最顶层是基础镜像层(Linux文件系统),最顶层代表了Docker将与这些镜像一起挂载到的各种应用镜像。
编辑点时,这些图像层是只读的。
只有容器的顶层是可读可写的。
这种分层的解决方案实际上是基于UnionFS(UnionFileSystem)联合文件系统的技术。
它可以将不同的目录全部挂载在同一目录中。
例如,如果您有test1和test2文件夹,则这两个文件夹中的文件可能相同或不同。
然后我们可以使用联合挂载的方式将这两个文件夹挂载到test3上,那么test3目录下就会有test1和test2的所有文件(相同的文件会去重,不同的文件会保留)。
这个原则也适用于Docker镜像。
例如,有两个学生。
A同学已经创建了一个基于Linux的Java环境镜像。
S同学想要创建一个JavaWeb环境,所以他不需要创建Java环境的镜像。
可以直接根据A同学的镜像添加Tomcat,生成新的镜像。
以上是对微服务架构“容器技术”的一些思考。
编码并不容易。
如果您喜欢,可以转发给您的朋友或点击文章右下角“阅读”。
Docker&k8s(一)
容器技术的主要功能是通过约束和修改流程的动态性能来创建其“边界”。
对于大多数像Docker这样的Linux容器来说,Cgroups是用于创建约束的主要方法,而Namespace是用于修改进程视图的主要方法。
实际上,它只是Linux创建新进程的一个可选参数。
我们知道Linux中创建线程的系统调用是clone(),例如:
这个系统调用将为我们创建一个新的进程,并返回它的进程号。
当我们使用clone()系统调用创建新进程时,我们可以在参数中指定CLONE_NEWPID参数,如:
此时,新创建的进程将“看到”一个全新的进程process进程空间,在这个进程空间中,它的PID是1。
之所以说“看到”,是因为这只是宿主机真实进程空间中的一个“掩码”,这个进程的进程ID(PID)仍然是真实的。
值,例如100。
例如例如,MountNamespace允许隔离进程只能看到当前命名空间中的挂载点信息;NetworkNamespace用于允许隔离进程查看当前命名空间中的网络设备和配置。
这就是Linux容器的基本实现原理。
因此,容器实际上是一个私有进程。
命名空间技术实际上修改了整个计算机应用进程的“视图”,即它的“视图”受到操作系统的限制,只能“看到”某些特定的。
优点:重量更轻,不浪费资源。
缺点:不完全隔离
Cgroups暴露给用户的操作接口是文件系统
例如,向容器组中的cfs_quota文件写入20ms(20000us):
表示每100毫秒,受该控制组限制的进程只能使用20毫秒的CPU时间,也就是说该进程只能使用20%的CPU带宽中心性。
将受限进程ID写入容器组的任务文件中,以上设置将对进程生效:
除CPU子系统外,每个Cgroups子系统都有其自己的进程ID自己独特的能力来限制资源,比如:
对于Docker项目来说,它的基本原理其实就是创建用户进程:
第一部分,只读层:其中是这个容器根部的底部五层,对应ubuntu:latest镜像的五层。安装方式都是只读的(ro+wh,即只读+白化)
每一层都一步步包含了Ubuntu操作系统的一部分
第二部分是读写层。
(rw)
在写入文件之前该目录是空的。
一旦写入容器,编辑产生的就会逐渐出现在该层中。
如果删除AuFS,则会在读写层中创建一个洗白文件,并且只读层中的文件将被“阻止”。
专门用于存储rootfs修改后创建的增量。
原来只读层的不会改变。
有些文件原本属于Ubuntu只读部分。
镜像,但用户在启动容器时往往需要写入一些特定的值如hostname,因此需要在读写层进行修改。
然而,这些修改往往只对当前容器有效。
我们不想在执行dockercommit时通过读写层发送此信息。
因此,Docker所做的就是在修改这些文件后将它们作为单独的层挂载。
用户执行dockercommit时,只会提交可读可写的类,因此不包含这些。
可以参考gitignore的思路。
Dockerfile:
ENTRYPOINT:入口点传统上用于指定启动容器后的执行脚本。
其实我们从名字上也可以理解,这是“输入”。
CMD:cmd提供了容器默认的可执行主体,即容器启动后默认执行该命令。
如果dockerrun没有指定任何执行命令或者有dockerfile中没有入口点,就会使用cmd指定的默认执行命令,如果不进一步指定,就会执行cmd命令,否则。
只要指定了,cmd就不会被执行,即,cmd会被覆盖
Dockercommit实际上意味着容器运行后,上面的“可读写层”会被添加到原容器镜像的只读层中,形成新的镜像,当然下面的只读层在主机上是共享的,不会。
占用额外的空间
由于使用了Shared文件系统,你对容器中的镜像根所做的任何修改都会被操作系统复制到这个读写层,然后进行修改。
称为copy-on-write
进程的每个Linux命名空间都有一个对应的虚拟文件,对应的/proc/[进程号]/ns下对于它来说,它与一个空间文件相关联真实姓名。
创建容器进程时,即使MountNamespace正在运行,容器进程始终可以看到主机上的整个文件系统,直到执行chroot(或Pivot_root)为止。
所以,设置好rootfs后,在执行chroot之前,将volume指定的主机目录(即/home目录)挂载到主机上指定容器目录(即/test目录)的对应目录(即/var/lib/docker/aufs/mnt/[可读写层标识符]/test),该文件夹的安装工作完成。
因为执行本次挂载操作时创建了ContainerProcess,也就意味着此时MountNamespace被打开了。
因此,该安装事件仅在该容器中可见。
您无法在主机上看到容器内的挂载点。
这确保了容器的绝缘层不会因尺寸而被破坏。
这里将使用到的挂载技术是Linux绑定挂载机制(bindmount)。
它的主要功能是允许您将目录或文件而不是整个设备挂载到特定目录。
而且,此时你对挂载点进行的任何操作都只发生在已安装的目录或文件上,原来挂载点的将被隐藏,不受影响。
链接拼接实际上就是替换inode的过程。
在Linux操作系统中,inode可以理解为存储文件的“对象”,而dentry也称为目录项,是用来访问这个inode的“指针”。
浅谈Containerd、Docker和CRI-O三种容器运行时工作原理
运行时是指程序的生命周期阶段或使用特定语言执行程序的阶段。容器运行时的工作原理类似——它是容纳运行和管理容器所需组件的软件。
这些工具可以更轻松地高效实施和部署容器,并且是容器管理的重要组成部分。
在容器架构中,容器运行时负责从存储库加载容器镜像、监控本地系统资源、隔离容器使用的系统资源以及管理容器生命周期。
容器运行时的常见示例包括runC、containerrd和Docker。
容器运行时主要分为三种类型:低级运行时、高级运行时以及沙箱或虚拟运行时。
在容器技术中,容器运行时可以分为三种类型:低级运行时、高级运行时和沙箱或虚拟运行时。
总的来说,不同类型的容器运行时有各自的优点、缺点和适用场景。
在选择何时运行容器时,必须根据实际需求和限制进行权衡和选择。
LowlevelContainerRuntime一般是指根据OCI规范实现的应用程序,可以接收可启动文件系统(rootfs)和配置文件(config.json)并运行隔离的进程。
这类运行时只负责在相对隔离的资源空间中运行进程,不提供存储实现和网络实现。
但其他应用程序可以在系统中预先设置相关资源,并在低级容器运行时通过config.json声明来加载相应的资源。
低级运行时是低级的、轻量级的、灵活的,其局限性也很明显:解决这些限制中的一个或多个的容器运行时称为高级容器运行时。
运行高级容器时要做的第一件事是打开OCIimagespec和runtimespec。
老实说,这是一个有效处理到rootfs和config.json的图像转换的问题。
创建config.json比较简单,运行时结合imageconfig和请求者的需求直接生成即可;最复杂的部分是将图像转换为rootfs,这涉及到图像的拖动和存储、图像层的压缩和编辑。
文件系统压缩层(fslayer)用于存储fslayer并将其集成到rootfs中。
你得到团结首先将图像从imageregistry拖到清单文件中。
处理不仅要兼容OCIimage规范,还要兼容Dockerimage规范,同时兼顾Docker生态(幸运的是,两者差别并不大)。
。
运行时应用程序首先从清单中获取图层列表,首先检查本地是否存在相应的图层,如果不存在,则下载相应的图层。
下载的layer或者tar.gz一般直接存储在磁盘上,为了实现快速处理,需要创建索引,比如从reference:tag(如docker.io/library/redis..)进行映射。
.)到清晰的存储路径;当然,对图层的访问比图像更频繁,相应存储路径的Layersha256值也会被索引。
因此,运行时一般围绕图像索引和图像层存储组织独立的模块,为其他模块提供服务。
如果要将镜像层转换为rootfs文件,需要将各层逐层解压到文件系统层(fslayer),然后合并。
这会导致很多问题。
首先,fslayer还需要多次复用存储磁盘,所以必须有一种方法从镜像映射到相应的fslayer。
然后,与图像层一样,必须创建索引以保留原始。
尽可能复用内层文件,避免重复工作;最后,运行隔离过程后会出现由于Rootfs重用而导致的问题,必须以某种方式避免对共享fslayer的更改。
最后,高级运行时需要充当隔离的进程管理器,而低级运行时(例如runC)可以同时被多个高级运行时使用。
同时,想象一下,如果隔离进程被终止,如何以最快的方式恢复运行?高级运行时实现通常提供容器(或元容器)的抽象,其中存储标识符、图像信息、低级运行时描述、OCIspec(jsonconfig)、worklayerid和K-V结构的命名信息。
因此,只要创建了容器定义,后续所有与隔离进程相关的操作,如运行进程、获取进程信息、附加进程、获取进程日志等,都可以通过容器ID进行。
容器技术与沙盒技术在原理上有什么区别?
要讨论容器技术和沙盒技术的原理区别,关键是要了解它们各自提供的隔离环境和功能。容器技术在Docker、AppContainer等应用中特别流行,它基于操作系统级别的隔离,允许多个轻量级进程运行在同一台服务器上,每个进程封装在独立的环境中,但拥有自己的文件系统和文件系统。
工艺空间。
这种隔离方式极大地提高了资源利用率和应用部署效率。
沙箱技术更注重在操作系统层面提供安全的运行环境,旨在防止应用程序之间的直接交互,保护系统免受恶意代码的侵害。
沙箱通常不提供资源共享,而是限制应用程序的权限和访问,以确保它们只能与隔离环境中的资源进行交互。
这种隔离方式在一定程度上提高了系统的安全性和稳定性。
尽管容器和沙箱这两个术语在日常表达中有时可以互换使用,但原则上它们在设计目标和实现机制上有所不同。
容器技术注重资源的高效分配和快速部署,而沙盒技术更注重安全和隔离。
因此,开发者在选择使用容器技术还是沙箱技术时,必须根据具体的应用场景和需求,权衡资源使用的效率和安全性。
在实际应用中,不同的平台和工具可能会使用不同的术语和模型,例如Kubernetes中的Pod被称为沙箱,Docker的网络模型使用沙箱来描述网络隔离环境。
这反映了技术术语的灵活使用和上下文依赖性,但核心概念始终围绕提供高效且安全的隔离环境。