基于Docker和Kubernetes的最佳架构——神话仍是现实?

软件开发领域在Docker和Kubernetes时代是如何变化的? 是否有可能使用这些技术搭建一劳永逸的架构? 当全部东西都被“打包”进容器中时,是否有可能统一开发及集成的流程? 这些决策的需求是什么? 它们会带来什么限制? 它们会让开发人员更轻松,或者相反,反而增长没必要要的复杂性吗?php

如今是时候以文本和原始插图方式阐明这些以及其余问题了!前端

这篇文章将带您踏上从现实生活到开发流程再到架构最后回到现实生活的旅程,并一路为您解答在这些停靠站点上遇到的最重要问题。 咱们将试图肯定一些应该成为架构一部分的组件和原则,并演示一些示例,但不会进入其实现领域。python

文章的结论可能会让你心烦意乱,或者无比开心,这一切都取决于你的经验、你对这三章故事的见解,甚至在阅读本文时你的心情。 在下面能够发表评论或提出问题,让我知道您的想法!git

从现实生活到开发工做流

在大多数状况下,我所见过的或者很荣幸搭建的全部开发流程都只是为了一个简单的目标——缩短从概念产生到交付生产环境之间的时间间隔,同时保持必定程度的代码质量。redis

想法的好坏可有可无。 由于糟糕的想法来也匆匆,去也匆匆——你只要尝试一下,就能够把它们丢 进故纸堆里。 这里值得一提的是,从一个糟糕的想法回滚是能够落在自动化设施的肩膀上的,这能够自动化您的工做流程。算法

持续集成和交付看起来像是软件开发领域的救命稻草。 究竟还有什么比这更简单呢? 若是你有一个想法,你有代码,那么就去作吧! 若是不是轻微的问题,这将是完美无瑕的——集成和交付过程相对而言难以独立于公司特有的技术和业务流程以外。docker

然而,尽管任务看起来很复杂,但在生活中不时会出现一些优秀的想法,这些想法可让咱们(固然,我本身是肯定的)更接近构建一个无瑕疵的而且几乎能够在任何场合使用的机制。 对我来讲,离这样的机制最近的步骤是Docker和Kubernetes,他们的抽象层次和思想方法使我认为如今能够用几乎相同的方法解决80%的问题。数据库

其他的20%的问题显然还在原地踏步,但这正是如此你才能够将你发自心里的创意天赋聚焦在有趣的工做上,而不是处理重复的例行公事。 只要照料“架构框架”仅仅一次,就可让您忘掉已经解决的80%问题。编程

这一切意味着什么?以及Docker如何解决开发工做流程的问题的? 让咱们看一个简单的过程,这对于大多数工做环境来讲也足够了:json

经过适当的方法,您能够自动化并整合上面序列图中的全部内容,并在将来几个月内将其抛之脑后。

设置开发环境

一个项目应该包含一个docker-compose.yml文件,这可让你省去考虑在本地机器上运行应用程序/服务须要作些什么以及如何操做的问题。 一个简单的命令docker-compose up应该启动您的应用程序及其全部依赖项,使用fixtures填充数据库,上传容器内的本地代码,启用代码跟踪以便即时编译,并最终在指望的端口开始响应请求。 即便在设置新服务时,您也没必要担忧如何启动、在哪里提交更改或使用哪一个框架。 全部这些都应该提早在标准说明中描述,并由针对不一样设置的服务模板指定:前端、后端和worker。

自动化测试

全部你想知道的关于“黑匣子”(至于为何我把容器称之为如此的更多信息,将在文章中的稍后部分阐明)的状况是,它里面的一切都无缺无损,是或否,1或0。您能够在容器内部执行有限数量的命令,而docker-compose.yml

描述了它的全部依赖关系,您能够轻松自动化和整合这些测试,而没必要过度关注实现细节。

好比,像这样!在这里,测试不只意味着单元测试,还包括功能测试、集成测试、(代码样式)测试和副本、检查过期的依赖关系以及已使用软件包的许可证正常与否等等。 关键是全部这些都应该封装在Docker镜像中。

系统交付

不管在什么时候何地想安装您的项目都无所谓。 结果就像安装进程同样,应该始终如一。 至于您要安装的是整个生态系统的哪一个部分或者您将从哪一个git仓库得到代码也没有区别。 这里最重要的组件是幂等性。 惟一应该指定的是控制安装过程的变量。

如下是我在解决这个问题时至关有效的算法:

1. 从全部Dockerfiles收集镜像(例如像这样)

2. 使用元项目,经过Kube API将这些镜像交付给Kubernetes。 启动交付一般须要几个输入参数:

● Kube API端点

● 一个“机密”对象,因不一样的环境而异(本地/测试/预发布/生产)

● 要展现的系统名称以及针对这些系统的Docker镜像的标签(在上一步中获取)

做为一个涵盖全部系统和服务的元项目的例子(换句话说,是一个描述生态系统如何编排以及如何交付更新的项目),我更愿意使用Ansibleplaybooks,经过这个模块来与Kube API集成。 然而,复杂的自动化能够参考其余选项,我稍后将详细讨论本身的选择。 可是,您必须考虑中心化/统一的管理架构的方式。 这样一个方式可让您方便、统一地管理全部服务/系统,并消除即将到来的执行相似功能的技术和系统丛林可能带来的任何复杂状况。

一般,须要以下的安装环境:

● “测试” - 用于对系统进行一些手动检查或调试

● “预发布” - 用于近乎实时的环境以及与外部系统的集成(一般位于DMZ而不是测试环境)

● “生产” - 最终用户的实际环境

集成和交付的连续性

若是你有一个统一的方式来测试Docker镜像——或者“黑盒子”——你能够假设这些测试结果可让你无缝地(而且心安理得)将功能分支集成到你的git仓库的的上游或主分支中。也许,这里惟一的交易断路器是集成和交付的顺序。若是没有发行版,那么如何经过一组并行的功能分支阻止一个系统上的“竞争条件”?所以,只有在没有竞争的状况下才能开始这个过程,不然“竞争条件”会萦绕脑海:

1. 尝试将功能分支更新到上游(git rebase/ merge)

2. 从Dockerfiles构建镜像

3. 测试全部构建的镜像

4. 开始并等待,直到系统交付了构建自步骤2的镜像

5. 若是上一步失败,则将生态系统回滚到以前的状态

6. 在上游合并功能分支并将其发送到存储库

在任何步骤中的任何失败都应终止交付过程,并将任务返回给开发人员以解决错误,不管是失败的测试仍是合并冲突。

您可使用此过程来操做多个存储库。只需一次为全部存储库执行每一个步骤(步骤1用于代码库A和B,步骤2用于代码库A和B等),而不是对每一个单独的存储库重复执行整个流程(步骤1-6用于代码库A ,步骤1-6用于代码库B,等等)。

此外,Kubernetes容许您分批次地推出更新以进行各类AB测试和风险分析。 Kubernetes是经过分离服务(接入点)和应用程序在内部实现的。您能够始终以所需的比例平衡组件的新旧版本,以促进问题的分析并为潜在的回滚提供途径。

系统回滚

架构框架的强制性要求之一是可以回滚任何部署。反过来,这又须要一些显式和隐式的细微差异。如下是其中最重要的一些事项:

● 服务应该可以设置其环境以及回滚更改。例如,数据库迁移、RabbitMQ schema等等。

● 若是没法回滚环境,则该服务应该是多态的,并支持旧版本和新版本的代码。例如:数据库迁移不该该中断旧版本的服务(一般是2或3个之前的版本)

● 向后兼容任何服务更新。一般,这是API兼容性,消息格式等。

在Kubernetes集群中回滚状态至关简单(运行kubectl rollout undo deployment/some-deployment,Kubernetes将恢复先前的“快照”),可是为了让此功能生效,您的元项目应包含有关此快照的信息。可是更为复杂的交付回滚算法让人望而生畏,尽管它们有时是必需的。

如下是能够触发回滚机制的内容:

● 发布后应用程序错误的高比例

● 来自关键监控点的信号

● 失败的[冒烟](https://en.wikipedia.org/wiki/Smoke_testing_%28software%29)

● 手动模式——人为因素

确保信息安全和审计

没有一个工做流程能够奇迹般地“搭建”刀枪不入的安全性并保护您的生态系统免受外部和内部威胁,所以您须要确保您的架构框架是在每一个级别和全部子系统里按照公司的标准和安全策略执行的。

我将在后面的关于监控和告警的章节讨论有关解决方案的全部三个级别,它们自己也是系统完整性的关键。

Kubernetes拥有一套良好的针对访问控制、网络策略、事件审计以及其余与信息安全相关的强大工具的内置机制,可用于构建一个良好的防御边界,以抵御和阻止攻击及数据泄露。

从开发流程到架构

应该认真考虑将开发流程与生态系统紧密集成的想法。将这种集成的需求添加到架构的传统需求集(弹性、可伸缩性、可用性、可靠性、抵御威胁等)中,能够大大提升架构框架的价值。 这是相当重要的一个方面,由此致使出现了一个名为“DevOps”(开发运维)的概念,这是实现基础设施全面自动化并优化的合理步骤。 可是,若是有一个设计良好的架构和可靠的子系统,DevOps任务能够被最小化。

微服务架构

没有必要详细讨论面向服务的架构——SOA的好处,包括为何服务应该是“微”的。 我只会说,若是你决定使用Docker和Kubernetes,那么你极可能理解(并接受)单体应用架构是很困难甚至根子上就是错误的。 Docker旨在运行一个进程并持久化,Docker让咱们聚焦于DDD框架(领域驱动开发)内进行思考。 在Docker中,打包后的代码被视为具备一些公开端口的黑盒子。

生态系统的关键组件和解决方案

根据我在设计具备更高可用性和可靠性的系统方面的经验,有几个组件对于微服务的运维是相当重要的,稍后我会列出并讨论这些组件,我将在Kubernetes环境中引用它们,也能够参考个人清单做为其它任何平台的检查单。

若是你(像我同样)会得出这样的结论,即将这些组件做为常规的Kubernetes服务来管理,那么我建议你在除“生产环境”以外的单独集群中运行它们。 好比“预发布”集群,由于它能够在生产环境不稳定而且你迫切须要其镜像、代码或监控工具的来源时节省你的时间。 能够说,这解决了鸡和鸡蛋的问题。

身份认证

像往常同样,它始于访问——服务器、虚拟机、应用程序、办公室邮件等。 若是您是或想成为主要的企业平台(IBM、Google、Microsoft)之一的客户,则访问问题将由供应商的某个服务处理。 可是,若是您想拥有本身的解决方案,难道只能由您并在您的预算以内进行管理?

此列表可帮助您肯定适当的解决方案并估算设置和维护所需的工做量。 固然,您的选择必须符合公司的安全政策并经信息安所有门批准。

自动化服务配置

尽管Kubernetes在物理机器/云虚拟机(docker、kubelet、kube proxy、etcd集群)上只须要少许组件,但对于新机器的添加和集群管理仍然须要自动化。 如下是一些简单的方法:

● KOPS——此工具容许您在两个云供应商商(AWS或GCE)之一上安装集群

● Teraform——这可让您管理任何环境的基础设施,并遵循IAC(基础架设施即代码)的思想,

● Ansible——用于任何类型的通用自动化工具

就我的而言,我更喜欢第三个选项(带有一个Kubernetes的集成模块),由于它容许我使用服务器和k8s对象并执行任何类型的自动化。 可是,没有什么能阻止您使用Teraform及其Kubernetes模块。 KOPS在“裸机”方面效果不佳,但它仍然是与AWS/GCE一块儿使用的绝佳工具!

Git代码库和任务跟踪器

对于任何Docker容器,使其日志可访问的惟一方法是将它们写入正在容器中运行的根进程的STDOUT或STDERR,服务开发人员并不关心日志数据接下来的变化,而主要是它们应该在必要时可用,而且最好包含过去某个点的记录。知足这些期许的全部责任在于Kubernetes以及支持生态系统的工程师。

在官方文档中,您能够找到关于处理日志的基本(和好的)策略的说明,这将有助于您选择用于聚合和存储大量文本数据的服务。

在针对日志系统的推荐服务中,同一文档提到fluentd用于收集数据(在集群的每一个节点上做为代理启动时)以及用于存储和索引数据的Elasticsearch。即便你可能不赞同这个解决方案的效率,但鉴于它的可靠性和易用性,我认为这至少是一个好的开始。

Elasticsearch是一个资源密集型的解决方案,但它能够很好地扩展并有现成的Docker镜像,能够运行在单个节点以及所需大小的集群上。

跟踪系统

即便代码很是完美,然而仍是会确实发生故障,接着你想在生产环境中很是仔细地研究它们,并试图了解“若是在个人本地机器上一切工做正常,那么在生产环境上究竟发生了什么错误?”。好比缓慢的数据库查询、不正确的缓存、较慢的磁盘或与外部资源的链接、生态系统中的交易,瓶颈以及规模不足的计算服务都是您不得不跟踪和估算在实际负载下代码执行时间的一些缘由。

Opentracing和Zipkin足以应付大多数现代编程语言的这一任务,而且在封装代码以后不会增长额外的负担。固然,收集到的全部数据应该存储在适当的地方,并做为一个组件使用。

经过上述的开发标准和服务模板能够解决在封装代码以及经过服务、消息队列、数据库等转发“Trace ID”时出现的复杂状况。后者也考虑到了方法的一致性。

监控和告警

Prometheus已经成为现代监控系统中事实上的标准,更重要的是,它在Kubernetes上得到了开箱即用的的支持。您能够参考官方Kubernetes文档来了解更多关于监控和警报的信息。

监控是必须安装在集群内的少数几个辅助系统之一,集群是一个受监控的实体。可是对于监控系统的监控(抱歉有些啰嗦)只能从外部进行(例如,从相同的“预发布”环境)。在这种状况下,交叉检查可做为一个针对任何分布式环境的便捷解决方案,这不会使高度统一的生态系统架构复杂化。

整个监控范围能够分为三个彻底逻辑隔离的层级。如下是我认为的在每一个层级最重要的跟踪点例子:

● 物理层:——网络资源及其可用性——磁盘(I/O,可用空间)——单个节点(CPU、RAM、LA)的基本资源

● 集群层:——每一个节点上主集群系统的可用性(kubelet、kubeAPI、DNS、etcd等)——可用资源数量及其均匀分布——容许的可用资源相对于服务消耗的实际资源的监控——pod的从新加载

● 服务层:——任何类型的应用程序监控——从数据库内容到API调用频率——API网关上的HTTP错误数量——队列大小和worker的利用率——数据库的多个度量标准(复制延迟、事务的时间和数量、缓慢的请求等)——对非HTTP进程的错误分析——发送到日志系统请求的监控(能够将任何请求转换为度量标准)

至于在每一个层级的告警通知,我想推荐使用了无数次的其中一个外部服务,能够发送通知电子邮件,短信或打电话给手机号码。我还会提到另外一个系统——OpsGenie——它与Prometheus的alertmanaer是紧密集成的。

OpsGenie是一种弹性的告警工具,可帮助处理升级、全天候工做、通知渠道选择等等。在团队之间分发告警也很容易。例如,不一样级别的监控应向不一样的团队/部门发送通知:物理——Infra + Devops,集群——Devops,应用程序——每个相关的团队。

API Gateway和单点登陆

要处理诸如受权、认证、用户注册(外部用户——公司客户)和其余类型的访问控制等任务,您须要高度可靠的服务,以保持与API Gateway的弹性集成。使用与“身份服务”相同的解决方案没有什么坏处,可是您可能须要分离这两种资源以实现不一样级别的可用性和可靠性。

内部服务的集成不该该很复杂,您的服务不该该担忧用户和对方的受权和身份验证。相反,架构和生态系统应该有一个处理全部通讯和HTTP流量的代理服务。

让咱们考虑一下最适合与API Gateway集成的方式,即整个生态系统——令牌。此方法适用于全部三种访问方案:从UI、从服务到服务以及从外部系统。接着,接收令牌(基于登陆名和密码)的任务由用户界面自己或服务开发人员完成。区分UI中使用的令牌的生命周期(较短的TTL)和其余状况(较长的和自定义的TTL)也是有意义的。

如下是API Gateway解决的一些问题:

● 从外部和内部访问生态系统服务(服务不直接相互通讯)

● 与单点登陆服务集成:——令牌转换和附加HTTPS请求,头部包含所请求服务的用户标识数据(ID、角色和其余详细信息)——根据从单点登陆服务接收到的角色启用/禁用对所请求服务的访问控制

● 针对HTTP流量的单点监控

● 复合不一样服务的API文档(例如,复合Swagger的json/yml文件)

● 可以根据域和请求的URI管理整个生态系统的路由

● 用于外部流量的单一接入点,以及与接入供应商的集成

事件总线和企业集成/服务总线

若是您的生态系统包含数百个可在一个宏域中工做的服务,则您将不得不处理服务通讯的数千种可能方式。为了简化数据流,您应该具有在发生特定事件时将信息分发到大量收件人的能力,而无论事件的上下文如何。换句话说,您须要一个事件总线来发布基于标准协议的事件并订阅它们。

做为事件总线,您可使用任何能够操做所谓Broker的系统:RabbitMQ、Kafka、ActiveMQ等。通常来讲,数据的高可用性和一致性对于微服务是相当重要的,可是因为CAP定理,您仍然不得不牺牲某些东西来实现总线的正确分布和集群化。

天然,事件总线应该可以解决各类服务间通讯问题,但随着服务数量从几百个增长到几千个甚至几万个,即便是最好的基于事件总线的架构也会望而却步,您将须要寻找另外一种解决方案。一个很好的例子就是集成总线方法,它能够扩展上述“Dumb管——智能消费”策略的功能。

有几十个使用“企业集成/服务总线”方法的理由,其目的是减小面向服务架构的复杂性。如下是其中几个理由:

● 聚合多个消息

● 将一个事件拆分为几个事件

● 对于事件的系统响应的同步/事务分析

● 接口的协调,这对于与外部系统的集成特别重要

● 事件路由的高级逻辑

● 与相同服务的多重集成(从外部和内部)

● 数据总线的不可扩展中心化

做为企业集成总线的一款开源软件,您可能须要考虑Apache ServiceMix,其中包含几个对于此类SOA的设计和开发相当重要的组件。

数据库和其余有状态的服务

和Kubernetes同样,Docker一次又一次地改变了全部用于须要数据持久化以及与磁盘紧密相关的服务的游戏规则。有人说服务应该在物理服务器或虚拟机上以旧的方式“生存”。我尊重这一观点,而且不会谈论它的优势和缺点,但我至关确定这种说法的存在仅仅是由于在Docker环境中暂时缺少管理有状态服务的知识、解决方案和经验。

我还应该提到数据库常常占据存储世界的中心位置,所以您选择的解决方案应该彻底准备好在Kubernetes环境中工做。

根据个人经验以及市场状况,我能够区分如下几组有状态的服务以及每一个服务最适合的Docker解决方案的示例:

● 数据库管理系统——PostDock是在任何Docker环境中PostgreSQL简单可靠的解决方案

● 队列/消息代理——RabbitMQ是构建消息队列系统和路由消息的经典软件。 RabbitMQ配置中的cluster_formation参数对于集群设置是必不可少的

● 高速缓存服务——redis被认为是最可靠和弹性的数据高速缓存解决方案之一

● 全文搜索——我上面已经提到过的Elasticsearch技术栈,最初用于全文搜索,但一样擅长存储日志和任何具备大量文本数据的工做

● 文件存储服务——用于任何类型的文件存储和交付(ftp,sftp等)的通常化服务组

依赖镜像

若是您还没有遇到您须要的软件包或依赖项已被删除或暂时不可用的状况,请不要认为这种状况永远不会发生。 为避免没必要要的不可用性并为内部系统提供安全保护,请确保构建和交付服务都不须要Internet链接。 配置镜像和复制全部的依赖项到内部网络:Docker镜像、rpm包、源代码库、python/go /js/php模块。

这些以及其余任何类型的依赖关系都有本身的解决方案。 最多见的能够经过查询“private dependency mirror for ...”来Google搜索。

从架构到真实生活

无论你喜不喜欢,你的整个架构命中注定早晚会难觉得继。它老是会发生:技术过期很快(1 - 5年),方法和方法论——有点慢(5 - 10年),设计原则和基础——偶尔(10 - 20年),但终归是不可避免的。

考虑到技术的过期,须要老是试图让本身的生态系统处于技术创新的高峰期,计划并推出新的服务以知足开发人员、业务和最终用户的需求,向您的利益相关者推广新的实用程序,交付知识来推进您的团队和公司前进。

经过融入专业社区、阅读相关文献并与同事交流,保持在整个生态的顶端。注意项目中的新机会以及正确使用新趋势。试验并应用科学方法来分析研究结果,或依靠您信任和尊重的其余人的结论。

除非你是本领域的专家,不然很难为根本性的变化作好准备。咱们全部人只会在咱们的整个生涯中见证一些重大的技术变化,但并非咱们头脑中的知识数量使得咱们成为专业人士并让咱们攀登到顶峰的,而是咱们思惟的开放性以及接受蜕变的能力。

回到标题的问题,“是否有可能搭建一个更好的架构?”不,不是“一劳永逸”,但必定要争取它,并在某个时刻,“很短的时间”,你必定会成功!

推荐一个交流学习群:685167672 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多: