![e6029a30472e56ecd078d5b0539f7597.png](https://i-blog.csdnimg.cn/blog_migrate/fb5358fcc435c7177bc3a4dd3d3a9722.jpeg)
第二十二期 启迪云解决方案架构师 林文炜
我们以创建一个web平台为例:
“Cloud Native”这一术语,指的是专门设计用于运行于云计算环境中的应用程序,Cloud-Native应用程序,组件之间通常采用松耦合的设计思路,并使用容器化和微服务的方式运行;这类应用程序通常需要能够应对底层环境随时可能发生的故障,并能够确保持续可靠的运行和快速故障恢复。
为了提供这种能力,支撑Cloud-Native应用程序的支撑运行平台必须具备一套约定和约束机制,这种约定确保应用程序遵循一定的约束,并能够自动化的管理容器化的应用程序。
今天,许多用户已经理解由传统应用架构向Cloud-Native方式转型的重要性,但并不知道从何开启Cloud-Native之旅。如果要确保Cloud-Native支撑运行平台和容器化应用之间能够很好的协调工作,则需要具备对底层基础架构发生故障的预测感知能力和自动恢复与自动扩展能力。
本文描述了一系列的关于应用容器化的必要原则,以帮助你的应用能够自动适配今天主流的Cloud-Native支撑运行平台(如Kubernetes)。
应用容器化通常的最佳实践:
- 容器镜像小型化
- 支持任意用户ID
- 标记出容器的重要通信端口
- 使用外部存储卷来储存应用所需要持久化的数据
- 设置容器镜像的元数据
- 同步主机和镜像
软件设计原则(PRINCIPLES OF SOFTWARE DESIGN)
Principles(原则)这个词,通常存在于日藏生活中的许多领域,它们通常指的是一些基本真理。 在本文所指的软件工程领域,Principles是一个相当抽象的指导原则,在我们进行软件设计的过程中,我们必须遵循一些基本原则,这些原则可以应用于任何编程语言,并使用不同的Patterns(模式)实现,且实现了不同的Practices(实践)。
通常,Patterns和Practices是用于实现Principles中所期望的结果的相关过程工具。
下面是几个编写高质量软件的一些基本原则:
- KISS – Keep it simple, stupid. 简单性原则
- DRY- Don’t repeat yourself. 少做重复性的工作
- YAGNI- You aren’t gonna need it. 尽可能不要出现无用的代码
- SoC—Separation of concerns. 关注点分离
虽然这些原则没有具体的规定,但他们依然代表了许多开发者理解的经常要参考的共同语言和普遍认知。
著名的Agile Software Development (《敏捷软件开发》)一书的作者Robert C. Martin就提出了SOLID原则组合:
SOLID原则组合包含了如下几个基本原则:
- Single Responsibility Principle(SRP) 单一职责原则
- Open Closed Principle(OCP)开放封闭原则
- Liskov Substitution Principle(LSP) 里氏替换原则
- Interface Segregation Principle(ISP)接口隔离原则
- Dependency Inversion Principle(DIP)依赖倒置原则
以上这些原则已经成为了编写良好的面向对象应用程序的基本原则。应用SOLID原则,我们将更有可能创建一套具备高质量属性和长期可维护性的应用系统。
这一原则组合代表了编写更好的面向对象软件的指导原则。它是一个由互补原则组成的框架,这些原则是通用的,开放的,可以进行解释,但仍然为创建更好的面向对象设计提供了足够的方向。SOLID原则的期望是:当采纳SOLID原则时,我们将更有可能创建一个具有更高质量属性并且从长远来看更易于维护的系统。
![bc90f7b1e6a38cc448e84b026259182e.png](https://i-blog.csdnimg.cn/blog_migrate/31a4c0bef6f6f475a03acfb605d2d4b7.jpeg)
Agile Software Development by Robert C. Martin
SOLID原则用于面向对象的原语和类似“类”、“接口”和继承等概念来推理面向对象的设计。
以类似的方式,以及设计云原生应用程序的原则,其中主原语是容器镜像而不是类。 遵循这些原则,我们更有可能创建更适合Cloud-Native平台(如Kubernetes)的容器化应用程序。
SOLID原则使用面向对象的Primitives(原语)、Classes(类),Interfaces(接口),inheritance(继承)等概念来推理面向对象的设计。 以类似的方式,还有设计Cloud-Native应用程序的原则,其中主原语(the main primitive)是容器镜像(Container Image)而不是Classes(类)。 遵循这些原则,我们更有可能创建更适合Cloud-Native环境(如Kubernetes)的容器化应用程序。
Red Hat关于实现CLOUD-NATIVE云原生容器应用的相关原则
今天,容器技术几乎可以支持将任何应用程序放到容器(Container)中运行。但是要将一个容器化的应用程序通过像Kubernetes这样的云原生平台自动化和协调编排,这还需要许多额外的工作才能做到良好的支持。
下面即将描述的一些原则,受到业内一些其他主流思维的启发,例如著名的“The Twelve-Factor App”(十二因素应用),其范围包括从源代码管理到应用程序的可伸缩性模型。 但是,我们这里的讨论,仅限于基于Cloud-Native平台(如Kubernetes)设计的容器化微服务的应用程序。
单一相关性原则(SINGLE CONCERN PRINCIPLE <SCP>)
在许多方面,这个原则类似于我们之前描述的“SOLID原则组合”中的Single ResponsibilityPrinciple(单一职责原则SRP),它建议一个class(类)应该只有一个责任。SRP背后的动机是每个“职责”都是一个变更轴心,一个class(类)应只能有一个变更因素。
SCP原则(SINGLE CONCERN PRINCIPLE)中的“CONCERN”一词则比SRP 中的“Responsibility”具有更高的抽象层次,它用于更好的描述Container(容器)所包含的范围,而不是用于class(类)的描述。
虽然SRP的主要动机指的是单一变更因素,但SCP原则的主要动机则是容器镜像(Container Image)的重用和可替换性。如果您创建一个用于解决单个问题的容器,并且它具备完备的功能实现,那么在不同的应用程序上下文中(application contexts)重用容器镜像的可能性会更高。
因此,SCP原则规定了每个容器应只解决一个问题,这要比在面向对象(object-oriented)的世界中实现SRP原则显得更加容易,因为一个容器内通常仅包含单个应用进程,并由这个进程解决单个问题。
![c3b1ea61b32126e2a409a6ef9f639340.png](https://i-blog.csdnimg.cn/blog_migrate/5a9b318d77cd64235eedc06475be7830.jpeg)
如果您期望你的容器化应用需要解决多个问题,则可以使用诸如sidecar和init-containers之类的组合模式,将多个单一功能的容器组合成一个部署单元中,这个部署单元指的是Kubernetes的容器组(Pod)概念,这样,实现了一次部署应用的多个功能,而其中每个容器仅负责处理单个问题。同时,您还可以基于Pod轻松实现对一组容器的快速变更,例如:将当前正在运行的一组web应用程序进行批量更新,或者对当前的容器组(Pod)的运行数量进行扩容。
可观察性原则(HIGH OBSERVABILITY PRINCIPLE <HOP>)
容器(Container)就像一个黑盒子,它提供了封装和运行应用程序的统一方式。 但任何旨在成为Cloud-Native的容器必须为运行时环境提供API接口(应用程序编程接口),以便在容器运行过程中,通过其所运行的Kubernetes平台可以观察到容器的实时运行状况,并相应地采取对应的操作。 这是容器自动化运维和生命周期管理的基本先决条件,这将有助于提高了系统的弹性和用户体验。
![64afdda2a0b227a0dd11c1b7f90f666d.png](https://i-blog.csdnimg.cn/blog_migrate/866a21949bc334fe3e3d917884ea4c2c.jpeg)
Figure 2. A container with multiple observability APIs (containers have multiple APIs to enable observability)
实际上,当我们在设计容器化应用程序时候,应当至少要提供liveness(活跃) 和readiness(就绪)等健康检查所需要的相应的API接口。
应用程序应将重要的运行事件记录到STDERR(标准错误)和STDOUT(标准输出)信息中,以便通过Fluentd和Logstash等日志收集工具对日志进行收集,并结合追踪(tracing)和计量(metrics-gathering libraries)系统(如OpenTracing,Prometheus等)集成。
所以,您仅需将您的应用程序视为黑盒子,而对这个黑盒子应用的监控和管理,则可以通过应用提供相关的API即可轻松实现。
生命周期一致性原则(LIFE-CYCLE CONFORMANCE PRINCIPLE <LCP>)
前一章节所述的“可观察性原则(HOP)”规定了您所设计的容器应用应提供相关API以供Kubernetes平台读取相关健康状态信息。本章节描述的“生命周期一致性原则(LCP)”则是规定您的应用程序也可以相应的读取来自Kubernetes平台的相关事件信息,并对这些事件作出相应的反应。这就是LCP原则的名称由来。
LCP使得您的应用程序可以和Kubernetes平台通过API进行交互。
![4a94c557f04ee039100361dc0ec1e684.png](https://i-blog.csdnimg.cn/blog_migrate/3c1153161b46e2a0053745ea17403b27.jpeg)
Kubernetes平台目前已经提供了多种事件(events),旨在帮助您管理容器的生命周期。具体要对哪些事件(events)进行处理并作出反应,这将由您的应用程序来决定和执行。
但有些事件比其他事件更重要。例如:如果应用程序本身要求当关闭时,必须是一个正常的关闭流程,基于自动化的需求,这个应用程序希望预先获得相关的关闭通知信号,如“终止信号(SIGTERM)”。应用程序在获得这个“终止信号(SIGTERM)”后,能够提前执行关闭应用程序的自动化操作,从而避免被系统强制关闭kill(SIGKILL)。
还有一些其他的重要事件,如PostStart和PreStop,可能对您的应用程序生命周期管理很重要。例如,某些应用程序需要在服务请求之前预热,而某些应用程序需要在关闭之前释放资源。
镜像不可变原则(IMAGE IMMUTABILITY PRINCIPLE <IIP>)
应用程序容器化后,一旦容器镜像构建完成,其镜像具有内容不可变的特性。这意味着,容器中的应用程序在运行过程中产生的数据需要使用外部存储设施来储存;此外,为了适应不同执行环境的差异化配置需求,容器应用程序的常用配置应当可以通过外部注入的方式来实现个性化配置;这样做,无需重复修改并产生大量繁杂的容器镜像。
对容器内的任何更改都必然会导致重构新的容器镜像,而多个使用了该镜像的应用运行环境也将相应的进行更新。这种理念目前在“不可变基础设施”的领域中很流行。
![8b67f3f84680422f81ae2b19f92a1441.png](https://i-blog.csdnimg.cn/blog_migrate/9411bd299d0ef83fe359c4d59e5a168e.jpeg)
遵循“镜像不可变原则(IIP)“应避免一个相同的应用在不同的环境中创建类似的,但某些配置有所不同的容器镜像,而应当坚持为每个环境统一配置一个相同的容器镜像。这个原则还允许应用程序更新期间,执行自动回滚等最佳实践,这是云原生应用自动化的一个重要方面。
用后可弃原则(PROCESS DISPOSABILITY PRINCIPLE <PDP>)
应用程序容器化的主要动机之一是支持短暂的执行任务,并且这种机制使得一个运行的容器随时可以被另一个容器替换。替换容器行为的原因有很多,例如容器运行中未通过运行状况检查,应用程序池缩容,或者需要将一组容器迁移到其他主机,以便对该主机进行服务器维护,或者底层平台资源不足导致必须关闭并启动新的容器实例等。
![d6e96f0a0dfb77326781d2ed0c0b78fe.png](https://i-blog.csdnimg.cn/blog_migrate/f1d9ab9dab05671e80e60f48563dca00.jpeg)
Figure 5. A containerized application with quick start up and shut down for easy replaceability
这意味着容器化应用程序必须保持其状态外部化、分布式和冗余。这也意味着应用程序应该能够实现快速启动和关闭,甚至为突然发生的硬件故障做好快速恢复的准备。
实现此原则的另一个最佳实践是容器镜像尽可能小型化。Cloud-Native环境中(如Kubernetes)运行的容器可以自动调度并在不同的主机上启动。当一个容器需要在新的主机上启动之前,Kubernetes需要先将该容器的镜像数据从容器镜像仓库(Container Registry)复制到该主机的本地文件系统之后才能启动,因此,较小的容器镜像,能够获得更快的启动时间和效率。
自我包含原则(SELF-CONTAINMENT PRINCIPLE <S-CP>)
这个原则要求容器镜像应该包含容器构建过程中所需的一切内容。Linux Container依赖于Linux®内核的存在,构建容器时,还会添加其他相关的依赖库、程序包;此外,它还应当包含应用所需的相关程序语言的运行时环境(the language runtime)。
这里,唯一的例外是应用程序的配置,应用程序配置往往需要根据不同的运行环境进行具体的调整和修改,往往需要在应用程序启动的时候提供; 当前,在Kubernetes平台上,提供了名为ConfigMap的功能,实现了应用配置的外部化管理。
![283c04c811736b96780c059513c0218f.png](https://i-blog.csdnimg.cn/blog_migrate/8978b5f37c959d93ab2e33a3117272bc.jpeg)
另外,有很多应用程序往往需要由多个容器完成所需的各项功能模块。例如,前端的Web应用程序,往往需要后端数据库应用。S-CP原则不建议合并这两个应用容器。相反,我们建议是:Web应用容器仅包含Web应用程序,而数据库容器则仅包含数据库应用;当应用启动后,Web应用程序容器将根据外部配置访问对应的数据库应用容器。
运行时限制原则(RUNTIME CONFINEMENT PRINCIPLE <RCP>)
前一章节描述的”自我包含原则(S-CP)”是从容器构建时间的角度来生成容器二进制文件的。
但是,容器不单只是文件系统上一个单维度的黑盒子,容器在它启动后的容器实例具有多个维度,例如CPU、内存以及其他资源消耗维度。
![5145260741e9aeb461fc6b22eeb67753.png](https://i-blog.csdnimg.cn/blog_migrate/31aadc903d1b13d248d9d199175fe2bb.jpeg)
Figure 7. A container that declares its runtime resource requirements and respects them at runtime
“运行时限制原则(RCP)“建议每个容器明确声明其对资源的需求,并将该信息传递给Kubernetes平台。
这通常需要声明根据CPU,内存,网络,磁盘等指标,传递给Kubernetes平台所需的执行调度的方式,自动扩展策略,容量管理以及SLA等方面的要求。
除了传递容器的资源需求之外,限定容器的资源需求也是很重要的。如果对容器配置了资源限制,则在平台资源不足时,其被平台终止和迁移的可能性就会降低。
总结
Cloud-Native不仅仅是今天应用程序的最终形态,它也是一种工作方法。本白皮书描述了许多应用容器化的基本原则,这些原则代表了对应用程序容器化通常需要遵守的基本准则,这样,您的应用程序才能更加匹配Cloud-Native运行环境,并获得其带来的最大化优势。
除了上述的几个原则之外,创建良好的容器化应用程序还需要熟悉其他与容器相关的最佳实践和技术。上述的几个基本原则适用于大多数情况,但我们下面所列的几个最佳实践,则需要判断具体的适用场景去采纳。以下这些最佳实践的描述:
- 容器镜像小型化:
通过清理临时文件并避免安装不必要的包来创建较小的容器镜像。这样可以减少复制容器镜像时的容器大小,构建时间和网络时间。
- 支持任意用户ID
避免使用sudo命令或要求特定的用户ID
- 标记重要端口
相比在容器启动时临时指定端口号的做法,使用EXPOSE命令指定端口号则可以使人和软件都更容易获取和便捷的配置容器端口
- 使用外部存储卷储存需要持久保存的数据
在容器被销毁前,必须将需要持久化的数据保存到外部存储卷中
- 配置好容器镜像的元数据
为容器镜像配置好Tags, Labels, Annotations等元数据参数,可以使您的容器镜像提供更好的使用体验
- 同步主机和容器属性
某些容器化应用程序要求容器启动后在某些属性(例如容器内的time和machine ID)上与主机同步。
以下是资源的链接,其中包含模式和最佳实践,以帮助您实现上述目标
• https://www. slideshare.net/luebken/ container-patterns
• https:// docs.docker.com/engine/ userguide/eng-image/dockerfile_best-practices
• http:// docs.projectatomic.io/c ontainer-best-practices
• https:// docs.openshift.com/ente rprise/3.0/creating_images/guidelines.html
• https://www. usenix.org/system/files /conference/hotcloud16/hotcloud16_burns.pdf
本文翻译源自Redhat Whitepaper 《PRINCIPLES OF CONTAINER-BASED APPLICATION DESIGN》,作者:Bilgin Ibryam;原文链接: https://www. redhat.com/cms/managed- files/cl-cloud-native-container-design-whitepaper-f8808kc-201710-v3-en.pdf