为什么建议一个容器中只运行一个进程
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">在云原生与容器化时代浪潮下,大多数新手的普遍认识是“容器=虚拟机”,既然容器等同于虚拟机,那么在容器中想运行多少个进程就运行多少个进程。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">作为从新手村走过来的人,笔者想为这个想法纠偏,避免大家和我走一样的弯路。有两个概念我们要理清:第一,容器不等同于虚拟机;第二,容器中不建议运行多个进程。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">本文以Docker容器为主要讨论展开。</span></p>
<h1 style="text-align: left; margin-bottom: 10px;">为什么说容器不等同于虚拟机呢?#</h1>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">我们来看一个较为学术的定义:</span><span style="color: green;"><span style="color: green;">A docker container is not a full virtual machine to run a complete stack of application instances and services. Docker container is the application; or more accurately a service that helps to make up the application.</span></span><span style="color: green;">即,Docker容器不是一个运行完整的应用程序实例和服务堆栈的完整虚拟机。Docker容器是一种应用程序,或更准确地说是一种有助于构建应用程序的服务。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">虽然容器不等同于虚拟机,但它们也有相似之处,容器与虚拟机拥有着类似的使命:对应用程序及其关联性进行隔离,从而构建起一套能够随处运行的自容纳单元。此外,容器与虚拟机还摆脱了对物理硬件的直接控制,允许我们更为高效地使用计算资源,从而提升能源效率与成本效益。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">那么,容器和虚拟机的不同点究竟在哪呢?笔者在这里做了一个通俗形象的类比。首先,对于物理机、虚拟机和容器在基础架构上的不同,可以类比为独栋别墅、小区楼盘和胶囊式公寓的区别。</span></p>
<div style="text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/pgc-image/ea2968ddc41f4303bcdc98427e9e30a1~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1704567371&x-signature=%2Ffv%2BjWahlVp48z7MOy7nfIoUljU%3D" style="width: 100%; margin-bottom: 20px;"></div>
<h1 style="text-align: left; margin-bottom: 10px;">物理机#</h1>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">物理机就像独栋别墅,拥有独立的基础设施,水管、电线、地基等都由自己独享,也就是说,网卡、内存、CPU等硬件都由自身操作系统独占。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">物理机上的硬件可以看作是基础设施(Infrastructure),没有基础设施一切都无从谈起,硬件之上则是主操作系统(Host Operating System)。除此之外,还有各类基础软件,如各类硬件配套的驱动软件。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">这里还要再重点介绍的是</span><span style="color: green;"><span style="color: green;">Hypervisor</span></span><span style="color: green;">,它(也称为虚拟机监视器或VMM)是创建和运行虚拟机(VM)的软件。虚拟机管理程序通过虚拟共享其资源(例如内存和CPU),使一台主机可以支持多个</span><span style="color: green;"><span style="color: green;">Guest OS</span></span><span style="color: green;">。</span></p>
<h1 style="text-align: left; margin-bottom: 10px;">虚拟机#</h1>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">虚拟机就像小区楼盘,所有房子共享地基,但房子内部有自己独立了电线、水管,虽然最终也是走的统一水电管网出小区。也就是说,多个虚拟机之间,网卡、内存、CPU等硬件虽然最终是共用一套,但在虚拟机内部是由独立的一套虚拟硬件进行运转的。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">前面我们介绍了</span><span style="color: green;"><span style="color: green;">hypervisor</span></span><span style="color: green;">,</span><span style="color: green;"><span style="color: green;">hypervisor</span></span><span style="color: green;">是一种虚拟化服务器的软件,这是在物理机及宿主机操作系统上运行虚拟机VM的基础,它帮助我们对硬件进行虚拟化。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">虚拟机里的操作系统我们称为</span><span style="color: green;"><span style="color: green;">Guest OS</span></span><span style="color: green;">,这是一个完整的操作系统,只要安装应用程序运行所必需的二进制文件和库,在其之上便可以运行应用程序,且能确保各个虚拟机之间、虚拟机与宿主机之间的环境完全独立、资源相互隔离。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">由于虚拟机里运行的是一个完整的操作系统,且需要虚拟出</span><span style="color: green;"><span style="color: green;">Guest OS</span></span><span style="color: green;">运行时所需的所有硬件的虚拟副本,这意味着虚拟机会占用大量系统资源,特别是CPU和内存的资源占用率会比较高,且虚拟机的空间占用可以高达数GB。不过在合理范围内,虚拟机可以利用固有的硬件资源虚拟出多台完整VM,其存在仍然有价值,与单独运行的物理机相比仍然是经济的。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">虚拟机是伟大的,它通过抽象来增加并行,服务于多操作系统的使用,并提供业界最好的安全性。但对于隔离,它们相当昂贵。</span></p>
<div style="text-align: left; margin-bottom: 10px;"><img src="https://p26-sign.toutiaoimg.com/pgc-image/a9214184fa264179b7b79698dda70c55~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1704567371&x-signature=KHAmxFzhjG1TDie8FzD30i5YTkI%3D" style="width: 100%; margin-bottom: 20px;"></div>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">图1. 虚拟机层级图</span></p>
<h1 style="text-align: left; margin-bottom: 10px;">容器#</h1>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">容器就像胶囊式公寓,所有隔间不仅共享地基,连电线、水管也都是共享的,且每个隔间的空间有限,彼此之间也相互隔离。当然,他们是共用大房间里的基础设施,如公共空间。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">也就是说,容器是共用物理网卡、内存、CPU的。只有当他们之间需要通信时,才会采用容器层面的网桥</span><span style="color: green;"><span style="color: green;">docker0</span></span><span style="color: green;">,而对于其他硬件,可以简单认为每个容器占有了限定范围内的资源(如RAM、CPU等)。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">容器只是运行在宿主机上的一种特殊进程,多个容器之间使用的还是同一个宿主机的操作系统内核。可以认为,容器是一个不依赖于操作系统,运行应用程序的环境。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">容器通过Linux的</span><span style="color: green;"><span style="color: green;">Namespace</span></span><span style="color: green;">和</span><span style="color: green;"><span style="color: green;">Cgroups</span></span><span style="color: green;">技术对应用程序进程进行隔离和资源限制。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;"><span style="color: green;">Namespace</span></span><span style="color: green;">的作用是环境隔离,它让应用程序只看到该</span><span style="color: green;"><span style="color: green;">Namespace</span></span><span style="color: green;">内的世界。而Cgroups 的作用是限制分配给进程的宿主机资源。不过,对于宿主机来说,这些被“隔离”了的进程跟其他进程并没有太大区别。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">对于</span><span style="color: green;"><span style="color: green;">Namespace</span></span><span style="color: green;">技术,这里做一个稍微深入一点的解读。通过</span><span style="color: green;"><span style="color: green;">Mount Namespace</span></span><span style="color: green;">,容器可以修改进程对自己的文件系统“挂载点”的认知。在容器进程启动之前重新挂载它的整个根目录"/",这个挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的“容器镜像”。它还有一个更为专业的名字,叫作:rootfs(根文件系统)。rootfs只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。同一台机器上的所有容器,都共享宿主机操作系统的内核。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;"><span style="color: green;">Linux CGroup</span></span><span style="color: green;">全称</span><span style="color: green;"><span style="color: green;">Linux Control Group</span></span><span style="color: green;">,是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。</span><span style="color: green;"><span style="color: green;">Cgroup</span></span><span style="color: green;">可让您为系统中所运行任务(进程)的用户定义组群分配资源—比如CPU时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的</span><span style="color: green;"><span style="color: green;">Cgroup</span></span><span style="color: green;">,拒绝</span><span style="color: green;"><span style="color: green;">Cgroup</span></span><span style="color: green;">访问某些资源,甚至在运行的系统中动态配置您的</span><span style="color: green;"><span style="color: green;">Cgroup</span></span><span style="color: green;">。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">回过头来谈谈操作系统内核,由于同一台宿主机上的所有容器都共享宿主机的操作系统内核,那么如果有一个容器里的应用程序需要配置内核参数,跟内核进行直接交互,则这些参数对所有容器来说就像一个“全局变量”,牵一发而动全身。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">这也是容器劣势的主要原因,正是因为容器共享宿主机操作系统内核,因此不能像虚拟机一样模拟出完整的硬件机器充当沙盒,从而实现完全隔离。也就是说,容器是进程级的隔离,它可以通过影响宿主机操作系统内核来影响其他容器。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">但容器还是有很多优势。首先,容器的空间占用比虚拟机小很多,甚至可以小到10MB,和虚拟机动则数GB相比十分小巧;其次,容器能轻松限制内存和CPU使用率,相比虚拟机采用</span><span style="color: green;"><span style="color: green;">hypervisor</span></span><span style="color: green;">来实现虚拟化更加轻量;同时,正是由于容器体积小、采用的技术(Containerzation Engine)更轻量,使得它启动十分迅速,这十分有利于快速扩展。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">值得一提的是,在DevOps理念日益流行的时代,容器对于持续集成和持续部署(CI/CD)实施也是极好的选择,它使得开发人员更容易构建、分发以及快速部署他们的应用程序。</span></p>
<div style="text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/pgc-image/d1b5fa75e04445bda55c230e7aebbeaa~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1704567371&x-signature=LpYe2UUXpbpkHZhuEROo8y3q9yU%3D" style="width: 100%; margin-bottom: 20px;"></div>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">图2. 容器层级图</span></p>
<h1 style="text-align: left; margin-bottom: 10px;">为什么说容器中不建议运行多个进程呢?#</h1>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">聊过虚拟机与容器的区别之后,让我们暂时忘记架构和软件工程哲学。在前面的讨论中我们已经知道,容器其实是应用程序抽象出来的可相互隔离的线程。尽管单个容器中确实可以运行多个应用程序,但出于实际原因,您可能需要考虑遵循“每个容器一个应用程序”的经验法则。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">1、每个容器中只运行一个应用程序,则水平伸缩将变得十分容易。例如,当你需要一个Tomcate容器,可以从现有的容器再扩展出一个,但如果你的这个容器中不仅有Tomcate,还有MySQL等其他应用程序,事情就会变得复杂起来。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">2、每个容器中只运行一个应用程序,可以轻松地将其重新用于其他项目或目的,极大增加复用度。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">3、每个容器中只运行一个应用程序,出现故障时开发人员能方便地对该故障容器进行问题排查,而不必对整个系统的各个部分进行排查,这也使得其更具有可移植性和可预测性。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">4、每个容器中只运行一个应用程序,升级程序时能够将影响范围控制在更小的粒度,极大增加应用程序生命周期管理的灵活性,避免在升级某个服务时中断相同容器中的其他进程。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">5、每个容器中只运行一个应用程序,从安全性和隔离性角度来看,能够提供更安全的服务和应用程序间的隔离,以保持强大的安全状态或遵守PCI之类的规定。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">话说回来,容器本身的设计,就是希望容器和服务/应用能够具备相同的生命周期。即:一个容器对应一个进程。这样,才能够最好地应用容器编排来管理好容器和服务。</span></p>
<p style="font-size: 18px; line-height: 40px; text-align: left; margin-bottom: 30px;"><span style="color: green;">综上,建议单个容器中只运行一个独立的进程。</span></p>
页:
[1]