像Docker Engine这样的应用程序容器技术提供了底层应用程序组件的基于标准的打包和运行时的管理。
容器可以快速部署并有效利用系统资源。使用容器,开发人员可以获得应用程序的可移植性和可编程的图像管理。 运营团队可获得部署和管理的标准运行时间单位。
但是, 在所有已知的应用程序容器的好处中,有一个常见的误解,那就是人们普遍认为容器是短暂的,因此仅适用于无状态的微服务类型的应用程序,并且不可能容纳有状态的应用程序。然我们深入看看这是否成立。
应用程序状态只是应用程序组件执行其工作(即执行任务)所需的数据。所有应用程序都具有状态软件编程架构模式,范例和语言,从本质上描述如何管理应用程序行为(任务,操作等)和状态(数据)。
即使微服务式应用程序也有状态!在微服务式体系结构中,每个服务可以有多个实例,每个服务实例被设计为无状态。这意味着服务实例不会在操作中存储任何数据。因此,无状态就意味着任何服务实例都可以从其他地方获取执行行为所需的所有应用程序状态。这是微服务式应用程序的一个重要架构约束,因为它可以实现弹性,弹力,并允许任何可用的服务实例执行任何任务。
通常,应用程序状态存储在数据库,缓存,文件或其他形式的存储中。另外,任何需要在操作中记住的应用程序状态更改都必须写回存储。
所以,所有的应用程序都有状态,但是如果一个应用程序组件能将行为从数据中干净利落分离出来并且可以获取执行任何行为所需的数据,那么这个组件就可以是无状态的。这似乎只是简单地将问题传递给其他组件 - 另一个组件如何管理状态?这就取决于我们正在讨论的状态的类型。
为了回答这个问题,我们考虑应用程序可能具有的五种状态,以及我们能如何处理每种状态来容器化应用程序:
持续的应用程序状态需要在应用程序重新启动和中断之后继续。这种状态通常存储在冗余数据库层中,并对其执行定期备份。
虽然可以将应用程序和数据库放在同一个容器中,但最好将他们分开,因为应用程序组件的更改频率会更高。分离数据库还允许在多个应用程序实例之间共享。
如果您的应用程序已经使用外部数据库,既可以作为服务提供,也可以安装在不同的物理或虚拟服务器上,您可以保留该体系结构,并简单地通过容器化应用程序层来启动。大多数容器管理系统允许将数据库访问信息作为配置状态传递给应用层容器(参见下面的“配置状态”)。
或者,你可以选择容器化数据库!这会带来从容器到数据层的快速恢复和部署以及所有其他的好处。在这种情况下,需要考虑几个与您的数据库相关的问题:
要在容器终止时允许数据存在,您将需要使用管理容器外数据的存储机制。通过使用主机卷,这很容易完成并将其映射到容器来完成。
同样,为了在主机终止时允许数据存在,您将需要使用存储机制来管理主机之外的数据。大多数云平台支持共享(联网)文件系统或块存储(卷),可以独立管理和连接/分离到任何主机。因此,假设您的容器编排器提供生命周期事件来管理存储组件,这也会相当简单。
但是,如果你的数据需要保留在特定的容器上呢?这可能事出有因。为什么这可能是必要的 ?- 例如,我们的客户之一想要管理大量的无法复制的视频内容。如果他们的容器死机了而在另一个主机上被重新启动,他们希望相同的数据可用于该容器。
如果你有很多这样的应用程序,卷插件可以简化数据的编排。卷插件位于容器引擎下方,并协助存储编排。很多卷插件本质还是IaaS或CMP。但其他卷插件的目标是提供丰富的功能,如QoS和分层存储以及对企业存储的支持,可能值得一看。
让我们总结一下选项:
应用程序通常需要非域数据才能正确配置。这种配置状态可能是其他外部服务的IP地址,或是连接到数据库的证书。
由Heroku推广的大多数PaaS解决方案所采用的12因子应用指南规定将配置数据存储在环境中。在一个容器化的世界里,大部分配置数据都可以作为可以注入容器的环境变量来被管理。
但是,机密信息(如凭证,密码,密钥和其他秘密数据)最好通过其他安全机制处理,这些机制可以更好地避免在主机、网络或存储上显示和访问机密数据。对于这种类型的配置状态,像KeyWhiz和Vault这样的凭证管理工具可以在具有一次性访问令牌的容器中使用。其他选项结合使用卷插件和密钥存储来安全地提供秘密数据给容器化应用。
当用户登录时,会话数据可能由应用程序生成。这可能是用户的身份验证密钥或其他临时状态。在大多数现代应用程序中,会话状态被存储在分布式缓存或可由任何服务实例访问的数据库中。
但是,在传统的多页面Web应用程序中,每个Web页面都需要访问由服务器管理的会话状态。因此,该会话的所有用户请求必须定向到相同的后端服务器,否则用户将被强制重新登录。这些应用程序据说需要“粘性会话(sticky sessions)”,其中会话状态存储在特定的服务器中,而客户端会话的所有请求总是被路由到相同的服务。
这不是一个容器化的问题,因为当请求在虚拟或物理机器中部署的应用服务器之间负载平衡时,存在相同的问题。而且大多数负载均衡器都可以选择支持粘性会话。
在一个容器化的世界里,你的容器的IP地址可能和你主机的IP地址不一样。如果您将第4-7层负载平衡解决方案用于具有有状态会话数据的前端应用程序容器,那么负载平衡器也会需要处理粘滞会话。
容器本地解决方案Nirmata的服务网关提供对粘性会话的支持,并且在容器重新部署到主机之间时动态更新路由信息。
某些应用程序可能通过协议进行通信,如Websockets,因为通信实体可以通过连接来交换消息序列,所以这些应用程序被认为是有状态的。相比之下,像HTTP这样的协议被认为是无状态的,因为服务器不记得任何请求状态,允许其他服务器回答下一个请求。
如果您的应用程序使用有状态协议,则容器负载平衡解决方案还需要支持将客户端请求路由到有状态协议的容器。例如,如果您使用Websockets,负载平衡解决方案将需要支持持续跨请求的TCP连接。这个特性在传统的负载均衡器中很常见,而且可以在大多数容器——本地负载均衡器中找到。
某些应用程序作为群集中的多个实例运行,以实现可用性和扩展,并需要共享群集成员和状态的知识。此状态不是持久性的,但是如果群集成员资格更改,则可能需要更新。
在集群应用程序中,每个群成集员都需要了解其他成员及其角色。大多数现代群集应用程序都需要使用成员种子集(通常是其IP地址和端口)进行初始引导,然后才能动态管理成员资格和更改。但是,某些群集服务可能需要手动更新,并在需要传播成员信息的更改时重新启动。
容器本地编排系统应该能够处理这两种情况。例如,Kubernetes最近引入了一个名为PetSet的功能来管理一个有状态的群集。Nirmata支持对预先计算容器布局的群集系统进行预订编排,而且所有集群成员都被注入了独特的身份和集群状态。
当我们与客户合作来容纳他们的应用程序时,我们遇到了各种各样有趣的情况。例如,一个应用程序读取了本地MAC地址,并将其用作唯一标识自身的方法!很显然,如果容器重启并获得不同的MAC地址,这个方案就会崩溃。
幸运的是,Docker现在允许指定容器的MAC地址。对于这样的异常值,您将需要确保您的编排系统在运行容器时能够灵活地指定自定义设置。
在这篇文章中,我们讨论了什么应用程序状态,您可能遇到的不同类型的应用程序状态。我们还介绍了如何在容器环境中管理每种类型的状态。在大多数情况下,有几个选项可供选择。所以,尽管容器是短暂的,但是应用程序状态并不需要!
我的这篇文章显示了有状态的应用程序可以被容器化。我们很乐意听取您的反馈和经验,或者如果您有任何问题,我可以帮助解答。
在这免费探索Nirmata:https ://try.nirmata.io/
得到我们免费电子书的副本:容器化传统的应用程序