避免不完全的云原生(四):技术和基础设施角度

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"本文最初发布于The Startup博客,经原作者授权由InfoQ中文站翻译并分享。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"注意:这是该系列文章的第4部分。你可以点击这里阅读"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/LmUVC2HHNPD5lHSzETUa","title":"xxx","type":null},"content":[{"type":"text","text":"上一部分"}]},{"type":"text","marks":[{"type":"italic"}],"text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虽然我们在前两篇文章中讨论的人员、流程、架构和设计问题都是云原生解决方案的关键推动因素,但云原生解决方案最终要落在技术和基础设施上,这也是我们在本文中要讨论的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/57\/2d\/5731748bacaa59c5bf382bfe99e6ef2d.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"云原生要素:技术和基础设施"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"云基础设施就是将底层硬件抽象出来,使解决方案能够快速地自助配置和扩展。它应该能够使用相同的操作技能管理不同的语言和产品运行时。此外,它应该促进操作自动化,并提供一个可观测性框架。让我们仔细看下,在云原生方法中使用的基础设施有什么关键特征。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/4e\/7c\/4ec1dc3d3db92ea173994c26f3545f7c.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"云原生的技术和基础设施"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"弹性、不可知且安全的平台"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了实现云原生,我们必须向部署组件的平台提很多要求。平台应该提供一种通用的机制,不管我们部署什么,它都能提供帮助,让我们不用考虑非功能性问题,同样,还应该“内置”安全性。因此,我们对平台的主要要求有:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弹性资源能力"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可知部署和操作"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"默认安全设置"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果开发人员要提高他们的生产力,就需要能够专注于编写创造业务价值的代码。这意味着平台应该解决负载平衡、高可用性、可伸缩性、弹性,甚至灾难恢复等问题。在部署时,我们应该能够指定高级的非功能性需求,而由平台完成剩下的工作。在这方面,我们将使用Kubernetes容器编排作为一个强大(实际上无处不在)的例子,但是云原生绝对不限于Kubernetes平台。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes集群可根据所部署的组件的需求,提供CPU、内存、存储、网络等资源的自动化、弹性配置。资源池可以分布在许多物理机上,也可以分布在许多独立地区的多个可用区域上。它负责找到你所需要的资源,并将组件部署到这些资源中。你只需要指定需求——需要什么资源,它们应该或不应该扩展,以及它们应该如何扩展和升级。对于基于虚拟机的平台,我们也可以这样说,但正如我们将要看到的,容器带来了更多的东西。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假设我们遵循了上一节的架构原则,在容器中交付应用程序组件,让Kubernetes以标准化的方式执行部署和后续操作,而不考虑任何特定容器的内容。如果组件基本上是无状态的、可抛弃的、细粒度的、良好解耦的,那么平台就很容易以通用的方式部署、扩展、监控和升级它们,而不需要了解它们的内部结构。对于每一种软件,现在都有一种逐步抛弃专有安装和拓扑配置的趋势,像Kubernetes这样的标准就是其中的一部分。现在,它们都以相同的方式工作,我们可以受益于操作的一致性、学习曲线的降低以及附加功能(如监视和性能管理)更广泛的适用性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后,我们希望平台内置了安全,这样,我们从第一天起就可以相信它是安全的应用程序运行环境。我们不应该每次设计一个新组件时都需要重新设计安全内核;相反,我们应该能够从平台继承一个模型。在理想情况下,这应该包括身份管理、基于角色的访问管理、保护外部访问和内部通信。我们注意到,在这个例子中,Kubernetes本身只是部分解决方案;对于一个完整的安全解决方案,还需要添加一些元素,比如用于内部通信的服务网格,以及用于入站流量的入站控制器。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"轻量级运行时"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要想实现操作敏捷性,就要保证组件尽可能简单和轻量级。这可以归结为三个主要特性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"快速启动\/关闭"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于文件系统的安装和配置"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于文件系统的代码部署"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了实现可用性和可伸缩性,组件必须能够快速创建和销毁。也就是说,容器内的运行时必须可以优雅地启动和关闭。它们还必须能够处理不优雅地关闭。这意味着有许多优化可能可以做:从删除依赖项,减少初始化过程中的内存占用,到在镜像构建中启用编译“左移”。至少,运行时应该能够在几秒内启动,这个期望值会不断降低(如"},{"type":"link","attrs":{"href":"https:\/\/quarkus.io\/blog\/runtime-performance\/","title":"","type":null},"content":[{"type":"text","text":"Quarkus"}]},{"type":"text","text":")。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果采用持续集成,我们还希望构建尽可能简单、快速。大多数现代运行时已经不再需要单独安装软件,而只需将文件放到文件系统上。类似地,根据定义,不可变镜像不应该在运行时更改,它们通常从属性文件读取配置,而不是通过自定义运行时命令接收配置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同样,实际的应用程序代码也可以放在文件系统中,而不是在运行时部署。这些特性组合在一起,使得构建能够通过简单的文件复制快速完成,非常适合容器镜像的分层文件系统。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"操作自动化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们能够作为发布的一部分,交付在运行时配置解决方案的整个行动方案(包括基础设施和拓扑结构的所有方面),会怎么样呢?如果我们可以将行动方案存储在代码存储库中,并可以像处理应用程序代码那样触发更新,情况会怎样?如果我们让操作人员这个角色聚焦于实现环境自治和自愈,会怎样?这些问题引出了一些关键的方法,我们应该以不同的方式处理基础设施:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基础设施即代码"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存储库触发的操作(GitOps)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"站点可靠性工程"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如前面所讨论的,基于镜像的部署已经使部署的一致性提高了很多。然而,这只交付了代码及其运行时。我们还需要考虑如何部署、扩展和维护解决方案。在理想情况下,我们希望能够将所有这些都作为“代码”和组件的源代码一起提供,以确保在不同环境中构建时的一致性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"术语“基础设施即代码”最初关注的是编写底层基础设施(比如虚拟机、网络、存储等)的脚本。编写基础设施脚本并不是什么新鲜事,但有越来越多的专用工具,如Chef、Puppet、Ansible和Terraform,大大推动了这方面的发展。这些工具首先假定有可用的硬件,然后在硬件上准备并配置虚拟机。有趣的是,当我们迁移到容器平台时,这个过程会有什么变化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在理想情况下,我们的应用程序应该可以假设,已经有一个Kubernetes集群可用。集群本身可能是使用Terraform构建的,但这与我们的应用程序无关;我们只要假设集群是可用的。那么,现在我们需要提供什么样的基础设施即代码元素,来完整地描述我们的应用程序呢?可以认为,就是那些打包在helm charts或Kubernetes Operator中的各种Kubernetes部署定义文件以及相关的客户资源定义(CRD)文件。结果是一样的——将一组文件与我们的不可变镜像一起交付,它们完整地描述了应该如何部署、运行、扩展和维护。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,如果我们的基础设施现在成了代码,那么为什么不用与应用程序相同的方式构建和维护基础设施呢?每次我们提交一个基础设施的重大更改时,都可以触发一次“构建”,它会自动地部署到环境中。这种日益流行的方法被称为GitOps。值得注意的是,这种方法特别偏爱采用“声明式”而非“命令式”方法的基础设施工具。你提供了一个描述“将来”目标状态的属性文件,然后该工具将解决如何让你达到目标状态的问题。上面提到的许多工具都可以在这种方式下工作,这是Kubernetes的基本操作方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"真正的系统是复杂的、不断变化的,所以认为它们永远不会崩溃是不合理的。不管像Kubernetes这样的平台在自动化动态扩展和可用性方面有多好,问题还是会出现,至少最初还是需要人工干预来诊断和解决。然而,在实现了细粒度、弹性伸缩组件自动化部署的世界中,反复的人工干预将变得越来越不可能,只要可能,就要实现自动化。为了做到这一点,操作人员需要重新接受培训,成为“站点可靠性工程师”(SRE),编写代码执行必要的操作工作。事实上,一些组织已在明确地招聘,或将开发人员移到运营团队中,以确保工程文化。人们越来越多地保证解决方案是可自愈的,这意味着当系统扩展时,我们不再需要增加相应的操作人员。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"可观测性和监控"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"随着组织向更细粒度的容器化工作负载发展,将监控作为事后加上去的东西是不可持续的。为了在云原生环境中取得成功,这又是一个我们需要“左移”的例子。解决这个问题取决于我们是否能够回答下面三个关于组件的问题:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它是健康的吗?你的应用程序有可以轻松访问的状态吗?"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它里面发生了什么?你的应用是否有效地使用了平台无关的日志记录和跟踪?"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它是如何与其他组件交互的?你利用了跨组件相关性吗?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可观测性并不是一个新术语,尽管它在IT领域,尤其是在云原生解决方案方面得到了新的应用。它的定义来自于非常古老的控制理论。它是一种度量,是指基于从外部看到的内容,可以在多大程度上理解系统内部的状态。如果你希望响应性地控制某个东西,你就需要能够准确地观测它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有些定义还做了区分,监控是针对已知的未知因素(如组件状态),而可观测性是针对未知的未知因素——为之前没有想过的问题寻找答案。在一个高度分布式的系统中,你两者都需要。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"平台必须能够轻松、迅速地评估已部署组件的运行状况,以便快速做出生命周期决策。例如,Kubernetes要求组件实现"},{"type":"link","attrs":{"href":"https:\/\/kubernetes.io\/docs\/tasks\/configure-pod-container\/configure-liveness-readiness-startup-probes\/","title":"","type":null},"content":[{"type":"text","text":"简单的探测"}]},{"type":"text","text":",以报告容器是否已启动、是否已准备好工作以及是否运行良好。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,组件应该基于其活动,以标准的形式提供易于访问的日志记录和跟踪结果,用于监控和诊断。通常,在容器中,我们只简单地记录到标准输出。然后,平台可以分类整理和聚合这些日志,并提供查看和分析服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于细粒度组件,涉及多个组件的交互可能会增加。为了能够理解这些交互并诊断问题,我们需要能够可视化跨组件请求。"},{"type":"link","attrs":{"href":"https:\/\/opentracing.io\/","title":"","type":null},"content":[{"type":"text","text":"OpenTracing"}]},{"type":"text","text":"是一个非常适合容器世界的、越来越流行的现代化分布式跟踪框架。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"回顾"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下图是前面提到的让云原生采用取得成功的所有关键要素:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/03\/e9\/03aec106876e8f46e7c05f56b0a8e0e9.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/2d\/20\/2d4f615de16028914ddd0e7714c06220.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","text":"云原生的要素"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常,它们是相互联系,而且相互加强的。你需要全部都做吗?这样的问题表述方式可能是错误的,就好像是说上面的任何一项都是简单的二元问题,“是的,你需要”或“不,你不需要”。问题应该是你要做的多深入。无疑,你需要考虑每一个方面的现状,并评估是否需要更进一步。在"},{"type":"link","attrs":{"href":"https:\/\/kylegenebrown.medium.com\/the-why-of-cloud-native-goals-and-benefits-5c559a4e73a5","title":"","type":null},"content":[{"type":"text","text":"下一篇文章"}]},{"type":"text","text":"中,我们将回到“为什么”人们被云原生所吸引,并看看这是否可以帮我们理清,我们如何、何时以及在多大程度上采纳上述要素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查看英文原文:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/medium.com\/swlh\/the-how-of-cloud-native-technology-and-infrastructure-perspective-765be1606840","title":"","type":null},"content":[{"type":"text","text":"The “How” of Cloud-Native: Technology and Infrastructure Perspective"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"延伸阅读:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/LmUVC2HHNPD5lHSzETUa","title":"xxx","type":null},"content":[{"type":"text","text":"避免不完全的云原生(三):架构和设计角度"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/Qj0SqAEDZZyVOPyhyTXY","title":"","type":null},"content":[{"type":"text","text":"避免不完全的云原生(二):人员和流程要素"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/PfHdeTgybyC47SpAB2fo","title":"","type":null},"content":[{"type":"text","text":"避免不完全的云原生(一):云原生到底意味着什么?"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/article\/LIRjAI0mhsClCooPtzLw","title":"","type":null},"content":[{"type":"text","text":"避免不完全的云原生"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章