译自:https:///@dpaunin/the-best-architecture-with-docker-and-kubernetes-myth-or-reality-77b4f8f3804d 作者/来源: Dmitriy Paunin 译者: 云技术社区/老刀IBM 在 Docker 和 Kubernetes 时代,软件开发的世界发生了怎样的变化?有可能使用这些技术一劳永逸地构建一个放之四海而皆准的架构吗?当所有东西都“打包”在容器中时,有可能统一开发和集成的过程吗?这些决策有什么要求?它们会带来什么限制?它们会让开发人员的生活变得更轻松,还是会增加不必要的复杂性? 是时候讨论和阐明这些(以及其它一些)问题了!(在本文和原创插图中) 本文将带你踏上从现实到开发过程到架构再回到现实的旅程,在沿途的每一站回答最重要的问题。我们将尝试确定一些应该成为体系架构一部分的组件和原则,并在不过多深入实现细节的情况下展示一些示例。 这篇文章的结论可能会让你不高兴或高兴。这完全取决于你的个人经验,你对这个分为三章的故事的看法,甚至可能取决于你阅读时的心情。请在文末发表评论或提出问题,让我知道你的想法! 一、从现实到开发流程在很大程度上,我所见过或有幸参与建立的所有开发过程都服务于一个简单的目标 —— 降低将一个想法变成现实、并交付生产的时间,同时保持一定程度的代码质量。 这个想法是好是坏并不重要。坏主意来来去去,你可能只是尝试一下,然后拒绝它们,让它们自生自灭。值得一提的是,从一个坏主意中 回退(rolling back)的责任落在将你的开发流程自动化的机器人肩上。 持续集成和交付(CI/CD)似乎是软件开发世界中的一根救命稻草。毕竟,还有什么比这更简单的呢?你有想法,你有代码,所以去做吧!如果不是存在一个小问题的话,这将会是完美无缺的 —— 如果与贵公司特有的技术和业务流程相分离,集成和交付流程将很难正规化。 然而,尽管这个任务看起来很复杂,生活还是在不断引入优秀的想法,让我们(当然是我自己)更接近于建立一个几乎在任何场合都有用的完美机制。对我来说,实现这种机制的最近一步是 Docker 和 Kubernetes,它们的抽象层次和意识形态方法让我认为现有问题中的 80% 都可以通过几乎相同的方法来解决。
所有这些意味着什么,Docker 是如何解决开发流程中的问题的?让我们来看一个简单的流程,这对于大多数工作环境来说也是足够的: 通过适当的方法,你可以自动化和统一以下序列中的所有内容,并在未来几个月内忘掉它。 建立开发环境一个项目应该包含一个 docker-compose.yml 文件,这样你就不用考虑在本地机器上运行应用程序/服务需要做什么和怎么做了。一个简单的 docker-compose up 命令就能启动你的应用程序及其所有依赖项、用预制数据填充数据库、上传本地代码到容器内、启用代码跟踪以便动态编译,并最终在预期的端口开始接收和响应请求。即使是在设置新服务时,你也不必担心如何启动、向哪里提交代码更改或使用哪些框架。所有这些都应该在标准说明中预先描述,并由不同配置的服务模板决定: frontend、backend 和 worker。 自动化测试关于“黑盒”你只需要知道,所有该有的东西都已经打包到里面了。是或否,1 或 0。更多关于我为什么将容器称为黑盒的信息,将在本文的后面介绍。有了数量有限的可以在容器内执行的命令,以及描述其所有依赖关系的 docker-compose.yml,你就可以轻松地自动化和统一测试工作,而无需过多关注实现细节。 例如, 像这样 !
系统交付你想在何时何地安装项目并不重要。结果,就像安装过程一样,应该总是相同的。至于你将安装整个生态系统中的哪个部分,或者从哪个 git 代码库中获取代码,也没有任何区别。这里最重要的组成部分是 幂等性(idempotence)。唯一应该指定的是控制安装的变量。 以下是在我看来解决这个问题非常有效的算法:
通常,在以下情况下需要安装环境:
集成和交付的连续性如果你有一个统一的测试方法对 Docker 镜像(或“黑盒”)进行测试,你就可以假设这样的测试结果将允许你无缝地(并且问心无愧地)将 特性分支(feature-branch)集成到 git 存储库的 上游(upstream)或 主分支(master)中。 或许,这里唯一的障碍是集成和交付的顺序。当没有发布时,你如何在一个拥有一组并行 功能分支(feature-branches)的系统上防止“竞争条件”的发生? 因此,只有在没有竞争的情况下,这个过程才应该开始,否则“竞争条件”会一直萦绕在你的脑海中:
任何步骤中发生的任何失败都应该终止交付过程,并将任务返回给开发人员来修复错误,不管是测试失败还是代码合并冲突。 你可以使用此流程来在多个代码库上工作。只需一次对所有代码库执行每个步骤(在 A 库和 B 库上执行步骤 1,在 A 库和 B 库上执行步骤 2,如此类推),而不是在每个单独的代码库上重复执行整个过程(在 A 库上执行步骤 1-6,在 B 库上执行步骤 1-6,如此类推)。
回滚系统架构框架的强制性要求之一是回滚任何部署过程的能力。这反过来又会带来一些显而易见和隐含的细微差别。以下是其中一些最重要的:
以下是触发回滚机制的因素:
确保信息安全和审计没有一个工作流可以神奇地“构建”防弹车级别的安全并保护你的生态系统免受外部和内部威胁,因此你需要确保你的架构框架在执行时着眼于公司在每个级别和所有子系统中的标准和安全策略。 稍后,我将在关于监控和告警的章节中讨论建议的所有三个级别解决方案,它们对系统完整性也至关重要。 Kubernetes 有一套良好的内置 访问控制机制 、 网络策略 、 事件审计 和其它与信息安全相关的强大工具,可用于构建出色的 周界保护(perimeter of protection)、抵御和防止攻击以及数据泄漏。 二、从开发工作流到架构应该认真对待在开发流程和生态系统之间建立紧密集成的尝试。将这种集成的需求添加到传统架构需求集(灵活性、可伸缩性、可用性、可靠性、防威胁等)中,可以极大地增加架构框架的价值。这是非常关键的一个方面,它导致了DevOps(Development Operation)这个概念的出现。DevOps 是实现基础设施完全自动化和优化的一个合乎逻辑的步骤。然而,一个设计良好的架构架构和可靠的子系统,可以让 DevOps 任务最小化。 微服务架构没有必要赘述面向服务的架构(SOA)的好处,包括为什么服务应该是 “微”(micro)粒度的。我只想说,如果你已经决定使用 Docker 和 Kubernetes,那么你很可能会理解(并接受)这样的观点:维护一个 单体(monolithic)架构是困难的,甚至在意识形态上是错误的。Docker 被设计成运行 单进程(single process)应用,并和持久层一起工作。它迫使我们在 DDD( 领域驱动开发(Domain-Driven Development))框架内思考。在 Docker 中,被打包的代码被视为一个暴露部分访问端口的黑盒。 生态系统的关键组成部分和解决方案根据我设计可用性和可靠性更高的系统的经验,有几个组件对微服务的运行至关重要。稍后我将列出并讨论这些组件,即使以下我只是在 Kubernetes 环境中引用它们,你也可以将我的这个列表作为任何其他平台的检查表使用。
身份服务和往常一样,一切开始于对服务器、虚拟机、应用程序、办公室邮件的访问。如果你现在就是或想要成为主要企业平台之一(IBM、谷歌、微软)的客户,访问问题将由服务提供商提供的服务来处理。然而,如果你想有自己的解决方案,那么这一切就只能由你自己搞定,并且不能超出你的预算? 这个关于单点登录的 列表 将帮助你决定合适的解决方案,并估计安装配置和维护该解决方案所需的工作量。当然,你的选择必须符合公司的安全政策并得到信息安全部门的批准。 自动化的服务开通虽然 Kubernetes 只要求在物理机器/云虚拟机上的少量组件(docker、kubelet、kube proxy、etcd 集群),但你仍然需要能够自动化完成添加新机器和对集群进行管理。以下是几个简单的方法:
Git 代码库和任务跟踪器不用说,要为开发人员和其他相关角色提供全面的工作环境,你需要有一个团队协作和代码存储的地方。我很难确定哪种服务是最合适的,但我个人最喜欢的任务跟踪工具是 redmine (免费)或 Jira (付费)。对代码库而言,有比较老牌的 gerrit (免费)或 bitbucket (付费)。
要充分利用任务跟踪器和代码库的组合,请考虑它们的集成策略。例如,以下是一些确保代码和相关任务关联性的提示(当然,你也可以选择自己的方法):
Docker 注册表应特别注意 Docker 镜像管理系统,因为它对于存储和交付服务至关重要。此外,该系统应该支持用户和用户组的访问,能够删除旧的和不必要的镜像,提供图形用户界面和 RESTful 应用编程接口。 你可以使用云解决方案(例如, hub.docker.com )或私有托管服务,甚至可以安装在你的 Kubernetes 集群中。作为 Docker 注册表的企业解决方案, Vmware Harbor 就是一个很好的例子。最坏的情况是,如果你只想存储镜像,而不需要复杂的系统,你就直接使用 Docker Registry 好了。 CI/CD 和服务交付系统我们之前讨论过的组件(git 存储库、任务跟踪器、带有 Ansible 剧本的元项目、外部依赖项)都不能像悬浮在真空中一样彼此分开运行。将它们连接起来的是持续集成和交付服务。
服务应该足够简单,并且没有任何与系统交付或配置相关的逻辑。CI/CD 服务应该做的就是对外部世界的事件(git 存储库中的变化,任务跟踪器中任务的移动)做出反应,并启动元项目中描述的操作。此外,CI/CD 服务是管理所有代码存储库的控制点和管理它们的工具(代码分支合并、来自上游/主分支的更新)。
在我们上面描述的方案中,集成服务主要负责启动四个主要流程和一个辅助流程,如下所示:
日志收集和分析系统任何 Docker 容器使其日志可访问的唯一方法是将它们写入容器中运行的根进程的标准输出或标准错误设备(STDOUT 或 STDERR)。微服务的开发人员并不真正关心日志数据接下来会发生什么,只需要保证它们应该在必要时可用,并且最好包含过去某个时刻的记录。Kubernetes 和支持这个生态的工程师负责这一切的实现。 在 官方文档 中,你可以找到处理日志的基本(也是一个好的)策略的描述,这将有助于你选择用于聚合和存储大量文本数据的服务。 在日志记录系统的推荐服务中,相同的文档提到了用于收集数据的 fluentd (作为代理在集群的每个节点上启动)和存储和索引数据的 Elasticsearch 。即使你可能不同意这种解决方案的效率,但它是可靠且易于使用的,所以我认为这至少是一个好的开始。
追踪系统尽管你的代码尽可能完美,但失败确实会发生,然后你想在生产环境中仔细研究它们,并尝试理解“在我的本地机器上一切工作都是正常的呀,现在到底是哪里出了什么问题呢?”。数据库查询速度慢、缓存不当、磁盘速度慢或与外部资源的连接速度慢、生态系统中的事务、瓶颈和规模不足的计算服务,这些都是你必须跟踪和估计在实际负载下执行代码所花费的时间的原因。 Opentracing 和 Zipkin 可以支持在大多数现代编程语言中完成这一任务,并且在 埋点(instrumenting)代码之后不会增加任何额外的负担。当然,所有收集的数据都应该存储在一个合适的地方,被 组件 之一所使用。
监控和报警Prometheus 已经成为现代系统中事实上的监控和报警标准,更重要的是,它在 Kubernetes 中几乎 开箱即用 。你可以参考 官方 Kubernetes 文档 ,了解更多有关监控和报警的信息。
整个监控范围分为三个完全逻辑隔离的级别。以下是我认为在每个级别跟踪点的最重要的例子:
至于每个级别的报警通知,我建议你使用无数外部服务之一,这些服务可以通过电子邮件、短信发送通知或拨打手机号码。我还要提到另一个系统 — OpsGenie ,它可以 与 Prometheus 的报警管理器紧密集成 。
API 网关和单点登录为了处理授权、身份验证、用户注册(外部用户——公司的客户)和其他类型的访问控制等任务,你需要一个高度可靠的服务,它可以与您的 API 网关保持灵活的集成。使用与“身份服务”相同的解决方案没有害处,但是您可能希望将这两种资源分开,以实现不同级别的可用性和可靠性。 服务间集成不应该很复杂,你的服务不应该担心用户的授权和认证。相反,架构和生态系统应该有一个代理服务来处理所有的通信和 HTTP 流量。
以下是 API 网关解决的一些问题:
事件总线和企业集成/服务总线如果你的生态系统包含在一个宏域中工作的数百个服务,你将不得不处理数以千计的服务通信方式。为了简化数据流,你应该考虑在某些事件发生时将消息分发到大量收件人的能力,而不管事件的上下文如何。换句话说,你需要一个事件总线来发布基于标准协议的事件并订阅它们。
很自然,事件总线应该能够解决各种服务间通信的问题,但是随着服务数量从成百上千增加到数万,即使是最好的基于事件总线的架构也可能会失败,因此你需要寻找另一种解决方案。一个很好的例子是集成总线方法,它可以扩展上述 “愚蠢的管道 —智能的消费者”(Dumb pipe — Smart consumer)策略的能力。 有很多原因决定了需要使用“ 企业集成/服务总线 ”方法,该方法旨在降低面向服务架构的复杂性。下面列出了部分原因:
作为企业集成总线的开源软件,你可能需要考虑 Apache ServiceMix ,它包含了设计和开发这种 SOA 所必需的几个组件。 数据库和其他有状态服务和 Kubernetes 一样,对于需要数据持久性和与磁盘紧密合作的服务,Docker 彻底改变了游戏规则。有人说,服务应该在物理服务器或虚拟机上以旧的方式“生存”。我尊重这一观点,我不会就其利弊展开争论,但我相当肯定,这种说法的存在只是因为在 Docker 环境中暂时缺乏管理有状态服务的知识、解决方案和经验。 我还应该提到,数据库通常占据存储世界的中心位置,因此你选择的解决方案应该为在 Kubernetes 环境中工作做好充分准备。 根据我的经验和市场情况,我可以区分以下几组有状态服务,并为每一组服务提供最合适的面向 Docker 的解决方案示例:
镜像依赖如果你还没有遇到你需要的包或依赖项已经从公共服务器上删除或暂时不可用的情况,请不要认为这种情况永远不会发生。为了避免任何不必要的不可用性并为内部系统提供安全性,请确保你的服务的构建和交付都不需要互联网连接。在内部网络中配置所有镜像和拷贝:Docker 镜像、rpm 包、源代码库、python/go/js/php 模块。 这些和任何其它类型的依赖都有自己的解决方案。最常见的方法可以通过在搜索引擎查询“ X 的私有依赖镜像 ”找到。 三、从架构回归现实不管你喜不喜欢,你的整个架构迟早都会失败。这样的情况一直在发生:技术很快就过时了(1-5 年),方法论变化慢一点(5-10 年),设计原则和基础理论偶尔变化(10-20 年),但不管怎么样这个趋势是不可阻挡的。 考虑到技术的过时,请始终努力将你的生态系统保持在技术创新的巅峰,规划和推出新的服务来满足开发人员、业务部门和最终用户的需求,向利益相关方推广新的实用程序,提供知识来推动你的团队和公司向前发展。 通过融入专业社区、阅读相关文献和与同事交流,可以让你保持领先地位。意识到你的机会,并在项目中正确使用新趋势。试验并应用科学的方法来分析你的研究结果,或者依靠你信任和尊重的其他人的结论。 现在,很难为根本性的变化做好准备。但如果你是这个领域的专家,这也是可能的。我们所有人一生中只会见证几个重大的技术变革,但不是头脑中的知识数量让我们成为专业人士,让我们达到顶峰,而是我们对于新想法的开放性和接受蜕变的能力。 回到标题中的问题,“有可能构建一个完美的架构吗”?不,当然不可能轻易“一劳永逸”,但一定要为之奋斗,在某个时刻,“在一个阶段内”,你一定会成功! PS: 原文 是以俄文写的,非常感谢我在 Lazada 的同事 Sergey Rodin 把它翻译成了英文。 点击“了解更多”可访问文内链接 |
|