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