<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>microservice | 伪架构师</title>
    <link>/tags/microservice/</link>
      <atom:link href="/tags/microservice/index.xml" rel="self" type="application/rss+xml" />
    <description>microservice</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Sat, 11 Jan 2020 20:59:19 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>microservice</title>
      <link>/tags/microservice/</link>
    </image>
    
    <item>
      <title>Istiod——回到单体的理由</title>
      <link>/post/istio-when-not-microservice/</link>
      <pubDate>Sat, 11 Jan 2020 20:59:19 +0800</pubDate>
      <guid>/post/istio-when-not-microservice/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://blog.christianposta.com/microservices/istio-as-an-example-of-when-not-to-do-microservices/&#34; target=&#34;_blank&#34;&gt;Istio as an Example of When Not to Do Microservices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;http://twitter.com/christianposta&#34; target=&#34;_blank&#34;&gt;Christian Posta&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;近五年来，我尽心尽力帮助各种组织踏入云原生之路。要让团队变得现代化并增强基于软件的产品的交付能力，人、过程以及技术决策都很重要。当应用架构的上限已经成为应对变化和加速发展的瓶颈时，微服务方法可能是合适的，但这并不是唯一的方法。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;微服务并非应用架构的乌托邦。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;过去，我在这方面发表了一些看法，例如我认为很多团队无法将其落地，实现过程中的困难之处，还提出了一些长远来看会对这项工作有益处的技术。甚至还写了一本书来讲述这一主题。&lt;/p&gt;

&lt;p&gt;尽管很多组织已经踏上微服务之旅，&lt;a href=&#34;https://blog.christianposta.com/microservices/when-not-to-do-microservices/&#34; target=&#34;_blank&#34;&gt;远离微服务&lt;/a&gt;一文仍然可能是个好起点。&lt;/p&gt;

&lt;h2 id=&#34;如果你已经踏上了微服务旅程&#34;&gt;如果你已经踏上了微服务旅程&lt;/h2&gt;

&lt;p&gt;如果发现微服务不灵，就该正视现实。拨乱反正是做出成功产品的正确举措。&lt;/p&gt;

&lt;p&gt;尽管出发点是好的，但开始使用微服务之后，开倒车还是有可能的。如果之前的假设或周遭环境已经发生了变化，重回单体架构也是可以理解的。&lt;/p&gt;

&lt;p&gt;为微服务通信构建服务网格的 Istio 社区，控制平面的实现将&lt;a href=&#34;https://docs.google.com/document/d/1v8BxI07u-mby5f5rCruwF7odSXgb9G8-C9W5hQtSIAg/edit#&#34; target=&#34;_blank&#34;&gt;最终从微服务架构转向更为单体的方式&lt;/a&gt;。Google API 基础设施的首席工程师和架构师 &lt;a href=&#34;https://twitter.com/louiscryan?lang=en&#34; target=&#34;_blank&#34;&gt;Louis Ryan&lt;/a&gt;，在 2019 年 KubeConNA 上讲述了这一变化的动机，并在&lt;a href=&#34;https://docs.google.com/document/d/1v8BxI07u-mby5f5rCruwF7odSXgb9G8-C9W5hQtSIAg/edit#&#34; target=&#34;_blank&#34;&gt;设计文档&lt;/a&gt;中进行了阐述。从 Istio 1.5 开始（可能会在 2020 年 2 月中旬），我们可能就会看到 &lt;code&gt;istiod&lt;/code&gt; 了，这个组件把前作中多个组件集成为单一进程。&lt;/p&gt;

&lt;p&gt;Istio 用于解决因为微服务、云原生架构引入的复杂的应用网络问题，所以为什么 Istio 自身却反其道而行之？最直接答案是：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;事实证明，微服务的复杂性无法实现其预期的价值或目标。相反，它违背了这些目标。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;对于 Istio 项目来说，似乎单体方式能更好的为目标服务。&lt;/p&gt;

&lt;h2 id=&#34;微服务模式的-istio&#34;&gt;微服务模式的 Istio&lt;/h2&gt;

&lt;p&gt;Istio 是一个开源的服务网格产品，其实现和其它同类产品大同小异，由控制平面和数据平面组成。数据平面由反向代理服务器组成，这些反向代理和各个应用实例伴行，并替代应用行使通信职责。控制平面在请求路径之外，用于对数据平面的行为进行管控。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/istio-cp-jan2020.png&#34; alt=&#34;istio-overview&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Istio 的控制平面分为几个组成部分，其职责如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Pilot&lt;/code&gt;：核心的数据平面配置（xDS）服务器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Galley&lt;/code&gt;：配置监听、验证和转发。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Injector&lt;/code&gt;：负责数据平面的注册和初始化。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Citadel&lt;/code&gt;：证书签发、Secret 生成、CA 集成等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Telemetry&lt;/code&gt;：&lt;code&gt;Mixer&lt;/code&gt; 组件之一，负责聚合监控信息到多种后端。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Policy&lt;/code&gt;：&lt;code&gt;Mixer&lt;/code&gt; 组件之二，在请求路径之中负责实现策略支持。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;运维人员通过一组配置指令来借由这些部件为数据平面提供服务并对其进行控制。&lt;/p&gt;

&lt;h2 id=&#34;微服务的好处&#34;&gt;微服务的好处&lt;/h2&gt;

&lt;p&gt;微服务能够降低变更过程中因为耦合产生的冲突，因此能加快组织的调整速度。有了微服务架构的帮助，每个服务都能可以有自己的团队，独立进行运维，有各自的变更频率和生命周期。这使得开发和运维能够轻装上阵，不会因为变更过程中的锁定、同步、协作等问题拖慢部署和变更的进度。&lt;/p&gt;

&lt;p&gt;拆分成微服务的另一个原因就是它的用法和扩展方式。例如一个需要大量读写的服务，能从读写分离上受益，这是因为读取过程需要更多内存（缓存），而写入需要更多的存储或者网络资源。拆分之后就可以放心的给读取服务分配大量内存，而写入服务则可以运行在 SSD 或者 EBS/SAN 等设施加持的服务器上。&lt;/p&gt;

&lt;p&gt;拆分微服务的另外几个理由：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;安全考虑&lt;/li&gt;
&lt;li&gt;领域分隔&lt;/li&gt;
&lt;li&gt;针对不同语言的优化&lt;/li&gt;
&lt;li&gt;安全分级&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;微服务架构的复杂性是第一号问题。当单体应用拆分为一些互相通信的小玩意之后，架构的复杂性以及对应的基础设施的复杂性都显而易见地提高了。&lt;/p&gt;

&lt;p&gt;除非已经清楚的意识到，这是为了获得更多好处，而做出的一种必要的妥协；否则就应该对假设进行评估，并及时做出反应——这就是 Istio 现在的举措。&lt;/p&gt;

&lt;h2 id=&#34;回头草&#34;&gt;回头草&lt;/h2&gt;

&lt;p&gt;首先要清楚，你的服务是谁开发谁运维的。在 Istio 社区，项目里不同的&lt;a href=&#34;https://github.com/istio/community/blob/master/WORKING-GROUPS.md&#34; target=&#34;_blank&#34;&gt;工作组&lt;/a&gt;维持着不同的组件。另一方面，下载、安装和运维 Istio 的用户就不那么清楚了。目前看来，都是由单一的工作组（甚至一个人）在操作 Istio 的控制平面。某种程度上，一组微服务构建的 Istio 控制平面更适合被当做一个更大规模的 SaaS 看待，但是目前的情况看来并非如此。&lt;/p&gt;

&lt;p&gt;第二个需要注意的就是部署问题。这些微服务能独自部署么？Istio 的回答是：理论上可以，但实际上可能并非如此。当新版本 Istio 发布时，需要更新/部署所有控制平面的组件。&lt;/p&gt;

&lt;p&gt;最后一个问题：”Istio 的各个组件，有各自不同的安全考量和伸缩需求吗？“，答案也并不肯定。来自 &lt;code&gt;istiod&lt;/code&gt; 的一段陈述：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;目前看来，对于多数组件来说并非如此。然而——控制平面的成本由单一的功能（xDS）决定。相对而言，其它所有组件的消耗微不足道，因此分离并无必要。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;为了安全起见，所有的控制平面都处于相同的特权级别：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;当前的情况下，Mutating Webhook、Envoy Bootstrap 以及 Pilot，这几个组件的特权级别和 Citadel 基本持平，对它们的滥用所引发的损失几乎相同。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Istio 设计文档中的潜台词就是——“复杂性是万恶之源，或者换个说法：停止焦虑，爱上单体”。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;istiod&lt;/code&gt; 是一个单体应用，它用较低的复杂性提供了和之前版本一致的功能。组成旧版控制平面的服务都还以子模块的方式存在于项目之中，但提供了更好的运维体验。操作者只需关注单一二进制文件的运行和升级了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/istiod.png&#34; alt=&#34;istiod&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Istio 一旦转向单体的控制平面，会大幅降低复杂性，从而：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;只需要对一个单独的服务进行部署和升级。&lt;/li&gt;
&lt;li&gt;因为无需关注编排服务自身的配置，因此配置复杂度也降低了。&lt;/li&gt;
&lt;li&gt;更容易除错。&lt;/li&gt;
&lt;li&gt;提高分发、共享和缓存的效率，降低开销。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外你可以看一下 &lt;a href=&#34;https://www.youtube.com/watch?v=QD115XiBXwY&#34; target=&#34;_blank&#34;&gt;Istiod 的 Demo 视频&lt;/a&gt;。这个视频基于一个早期版本，因此并不完善。&lt;/p&gt;

&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;

&lt;p&gt;很高兴看到 Istio 社区在持续提高其易用性和可运维性。转向单体应用的 Istio 带来了很多好处。这个过程会对你的项目产生什么启发么？如果有的话，你会采取什么措施么？&lt;/p&gt;

&lt;h2 id=&#34;相关链接&#34;&gt;相关链接&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Istio设计文档：&lt;code&gt;https://docs.google.com/document/d/1v8BxI07u-mby5f5rCruwF7odSXgb9G8-C9W5hQtSIAg/edit#&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Istiod 的 Demo 视频：&lt;code&gt;https://www.youtube.com/watch?v=QD115XiBXwY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;远离微服务：&lt;code&gt;https://blog.christianposta.com/microservices/when-not-to-do-microservices/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>为什么微服务适合我们</title>
      <link>/post/segment-why-microservices/</link>
      <pubDate>Tue, 11 Jun 2019 21:59:30 +0800</pubDate>
      <guid>/post/segment-why-microservices/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;最近的爆款文&lt;a href=&#34;https://www.infoq.cn/article/v3VjP*BYtY3qhD3438ay&#34; target=&#34;_blank&#34;&gt;《Segment 放弃了微服务》&lt;/a&gt;流传很广，让我想到了 2015 年底，Segment 的一篇关于微服务的文章。似乎目前没有中译文，因此翻译出来，便于对照。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;原文：&lt;a href=&#34;https://segment.com/blog/why-microservices/&#34; target=&#34;_blank&#34;&gt;Why Microservices Work For Us&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://segment.com/blog/authors/calvin-french-owen&#34; target=&#34;_blank&#34;&gt;Calvin French-Owen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在 Segment，我们全心拥护微服务理念，但个中原因，可能并不是读者所以为的那样。&lt;/p&gt;

&lt;p&gt;微服务和单体的争论已经够多了，我不想再次复盘。微服务的拥趸们说，微服务有更好的伸缩能力，是清晰软件工程师团队责任的的最佳方式。然而单体应用的拥护者则认为，微服务的运维太复杂，难于启动。&lt;/p&gt;

&lt;p&gt;但是微服务的主流益处和今天我们要讨论的好处不太相关，我们要讨论的是：&lt;strong&gt;可观察&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;当周二凌晨 3 点钟收到传呼的时候，如果看到特定的工作单元出了问题，而无需在单体应用的每个函数调用中加入追踪，那这个过程就轻松百倍了。&lt;/p&gt;

&lt;p&gt;并不是说紧耦合代码中无法实现良好的可观察性——只是极少有案例能够从第一天开始就具备完善的可观察性支持。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;可观察性从何而来？稍作思考，就能列出 &lt;code&gt;htop&lt;/code&gt;、&lt;code&gt;sysdig&lt;/code&gt;、&lt;code&gt;iftop&lt;/code&gt;、&lt;code&gt;ps&lt;/code&gt; 等运维工具。&lt;/p&gt;

&lt;p&gt;但是这些&lt;strong&gt;都不是&lt;/strong&gt;用来监控单一程序的执行情况的：热门执行路径、堆栈尺寸等。过去 20 年中，我们打磨出来的工具都是围绕主机、进程或设备进行的。&lt;/p&gt;

&lt;p&gt;在分布式系统中，我们可以在我们的监控指标中加入请求和网络吞吐，但是多数工具还是会尝试聚合到主机或者服务的级别。&lt;/p&gt;

&lt;p&gt;单体应用中，以进程为中心的监控工具很难感知到一个程序的耗时情况。在单体应用中，我们最好的调试方式，要么是用 Profiler 运行程序，或者实现自己的计时指标。&lt;/p&gt;

&lt;p&gt;还有更头痛的事情，正因为我们没有能够在函数级别完成监控，才成全了火焰图，令其成为流行工具。&lt;/p&gt;

&lt;p&gt;所以在 Segment，我们不再尝试往单体应用中塞进大量功能，而是在微服务方向花大力气。我们打赌，容器调度和编排会变得更简单也更强大，而多数指标和监控会持续以主机和服务为中心。&lt;/p&gt;

&lt;p&gt;这里做个提醒，微服务仅在容易创建新服务的情况下才奏效。否则我们只是把可观察性问题替换成了交付问题了。&lt;/p&gt;

&lt;p&gt;在另外一篇文章中，我们讲了一下我们&lt;a href=&#34;https://segment.com/blog/rebuilding-our-infrastructure/&#34; target=&#34;_blank&#34;&gt;服务的大体情况&lt;/a&gt;，以及我们&lt;a href=&#34;https://segment.com/blog/automating-our-infrastructure/&#34; target=&#34;_blank&#34;&gt;是如何使用 Terraform 的&lt;/a&gt;。如今我们开始把每个服务拆为模块，所以我们可以在预备和生产环境中复用同样的配置了。&lt;/p&gt;

&lt;p&gt;这里有个认证服务的例子，使用 &lt;a href=&#34;https://terraform.io/&#34; target=&#34;_blank&#34;&gt;Terraform&lt;/a&gt; 来进行资源分配：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;/**
 * Task definition.
 */

module &amp;quot;task&amp;quot; {
  source = &amp;quot;../task&amp;quot; # sets up an ECS task

  name = &amp;quot;auth&amp;quot;
  port = 5027
  image = &amp;quot;segment/auth
  image_version = &amp;quot;latest&amp;quot;
}

/**
 * Service implementation.
 */

module &amp;quot;service&amp;quot; {
  source = &amp;quot;../service&amp;quot; # sets up our shared service resources

  # Module variables
  task = &amp;quot;${module.task.arn}&amp;quot;
  name = &amp;quot;${module.task.name}&amp;quot;
  port = &amp;quot;${module.task.port}&amp;quot;

  # Input variables
  iam_role = &amp;quot;${var.iam_role}&amp;quot;
  zone_id = &amp;quot;${var.zone_id}&amp;quot;
  elb_subnets = &amp;quot;${var.elb_subnets}&amp;quot;
  elb_security_group = &amp;quot;${var.elb_security_group}&amp;quot;
  cluster = &amp;quot;${var.cluster}&amp;quot;
  desired_count = &amp;quot;${var.desired_count}&amp;quot;
  environment = &amp;quot;${var.environment}&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果感到好奇，可以看看&lt;a href=&#34;https://gist.github.com/calvinfo/0647044900d2b3a51163&#34; target=&#34;_blank&#34;&gt;完整的模块定义的例子&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这里有着显而易见的好处（监控指标）和很低的成本（少量的 Terraform 脚本），因此我们就不再需要将不同的功能挤到一个现存服务之中了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;目前为止，这套方法很有用。&lt;/p&gt;

&lt;p&gt;Segment 有点不同寻常：并非微服务们协调工作，我们有大量称为 &lt;code&gt;microworker&lt;/code&gt; 的单元。基本上这是同样的概念，不过 Worker 不为客户请求提供服务，而是从队列中读取数据，进行处理之后，然后给消息做一个 ACK。&lt;/p&gt;

&lt;p&gt;Worker 没有依赖，因此它比服务简单很多。没有耦合，因此不用担心一个 Worker 中的问题会影响到系统中的其它成员。&lt;/p&gt;

&lt;p&gt;实际工作中，有很多因素迫使我们做出了 microworker 的决定。其中最大的因素是我们的团队规模以及我们要尝试构建的产品的规模。&lt;/p&gt;

&lt;p&gt;微服务经常的宣传就是，当团队规模变大时，太多人在编写同一套代码。这种情况下，以团队为单位分配代码库的权限是个自然的举措。但是我们认为，对小团队来说，微服务也一样有好处。&lt;/p&gt;

&lt;p&gt;很多和我交流的人都吃惊于我们团队的规模之小，列几个数字来证明一下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;400 个私有仓库&lt;/li&gt;
&lt;li&gt;70 个不同的服务（Worker）&lt;/li&gt;
&lt;li&gt;10 个工程师&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们的产品规模和团队规模形成了鲜明的对比。所以如果我接到告警，告警可能是我编写的代码导致的，这段代码我半年前开发出来之后就再没朋友。&lt;/p&gt;

&lt;p&gt;如此情况下，小型的、定义清晰的服务就很有吸引力了。&lt;/p&gt;

&lt;p&gt;下面是一个典型场景：因为队列深度导致的告警。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/asset_iFAdhfpj.png&#34; alt=&#34;Alert&#34; /&gt;&lt;/p&gt;

&lt;p&gt;我们可以在队列的监控中检查一下是否是这个原因。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/asset_PmudaMWa.png&#34; alt=&#34;queue&#34; /&gt;&lt;/p&gt;

&lt;p&gt;我们可以清楚的知道是哪个 Worker 出了问题（因为每个 Worker 都会订阅单独的队列），也知道去哪里看日志。每个服务日志都有自己的标签，所以我们无需担忧同一应用中不同请求生成不相关的日志造成的干扰。&lt;/p&gt;

&lt;p&gt;我们可以在 Datadog 的独立的 Dashboard 上查看这个 Worker 的 CPU、内存，以及 ELB 报告的延迟。一旦我们识别了问题，只要阅读 50-100 行文件就可以获知问题的确切位置（例如内存泄漏）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/asset_3NazRMBR.png&#34; alt=&#34;datadog&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在单体应用中，我们也可以为每个端点加入特定的监控。然而如果每个端点都运行在各自的进程中，我们完全可以自由的进行修改，无需担心影响其它部分。&lt;/p&gt;

&lt;p&gt;还没有提到的是，微服务让我们有了隔离 CPU、内存和延时（如果是 ELB 代理的服务）的能力。同样是查找一处内存泄漏，在有上百个端点的单体应用的代码中查找，和在一个 Worker 的百多行代码中进行查找，其难度是不可同日而语的。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;我理解，这种方式不会适用于所有场合。为了在创建新服务时能够获得所有支持，需要很大的投入。这种投入会受到团队、工作负载以及产品形态等多种因素的影响，可能不那么理想。&lt;/p&gt;

&lt;p&gt;但是对任何产品来说，一旦其运行过程存在复杂的运维和负载，我会选择微服务架构。这种架构让基础设施有弹性、可伸缩，并易于监控，无需牺牲开发人员的生产力。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Segment 微服务反水案的一点思考</title>
      <link>/post/about-segment-and-microservice/</link>
      <pubDate>Sun, 09 Jun 2019 23:58:26 +0800</pubDate>
      <guid>/post/about-segment-and-microservice/</guid>
      <description>

&lt;p&gt;2015 年底，Segment 博客刊登了一篇文章，&lt;a href=&#34;https://segment.com/blog/why-microservices/&#34; target=&#34;_blank&#34;&gt;Why Microservices Work For Us（下文简称为《Work》）&lt;/a&gt;，三年多，又来了一篇更具话题性的新作：&lt;a href=&#34;https://segment.com/blog/goodbye-microservices/&#34; target=&#34;_blank&#34;&gt;Goodbye Microservices: From 100s of problem children to 1 superstar（下文简称为《Bye》）&lt;/a&gt;，两相比较，感觉还是有一定的代表性的，这里做一点整理和记录。&lt;/p&gt;

&lt;h2 id=&#34;微服务的动机&#34;&gt;微服务的动机&lt;/h2&gt;

&lt;p&gt;《Work》中提到，主要是因为原有架构中，故障处理不力，需要有更快的诊断和处理方法，这里提到了两个途径：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;问题的快速定位：借助更细致方便的监控指标，为突发事件提供明确指导。&lt;/li&gt;
&lt;li&gt;问题的快速解决：在定位问题之后，能够更好的从源码中获得支持并解决问题。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;要针对某个功能加入监控指标，在原有单体架构中可能会造成不必要的影响，范围不易控制；而源码方面，越小的服务，通常也代表着相对易读的代码；这两个主要需求都指向了同样的解决方案：微服务。&lt;/p&gt;

&lt;h2 id=&#34;微服务带来的好处&#34;&gt;微服务带来的好处&lt;/h2&gt;

&lt;p&gt;将原有单体应用拆分为微服务之后，不但解决了监控和排错的问题，还带来了一些额外的好处：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;隔离的消息队列，不同订阅互不干涉&lt;/li&gt;
&lt;li&gt;隔离系统资源，如 CPU 和内存。&lt;/li&gt;
&lt;li&gt;隔离网络，如 ELB。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里只是原文中提到的好处，更多的照本宣科内容这里就不赘述了。&lt;/p&gt;

&lt;h2 id=&#34;改造的隐忧&#34;&gt;改造的隐忧&lt;/h2&gt;

&lt;p&gt;《Work》一文中提到了两次，新建微服务应该如何如何。作者似乎认为，微服务有必要更快的创建起来。但按照我的理解，微服务仅是通过进程隔离的强制手段使得模块之间的边界更加清晰，事实上，因为缺乏单体应用强大的上下文支持，同一系统内的不同微服务，往往会因为上下文问题，导致更加复杂的开发过程。&lt;/p&gt;

&lt;h2 id=&#34;bye-一文中补充的拆分过程&#34;&gt;《Bye》一文中补充的拆分过程&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;单代码库阶段&lt;/p&gt;

&lt;p&gt;因为缺乏有效的时间说明，这一操作让人很迷惑，不知道是不是在《Work》发表的时候，各个服务还是在共用同一套代码。也在共用同一套测试方案。《Bye》中提到，一个失败的提交会导致整体测试失败，因此我们大致可以说，这一阶段里，CI/CD 过程也没能完成分割，个人认为，这种情况不太应该算作微服务。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;多代码库阶段&lt;/p&gt;

&lt;p&gt;为微服务独立创建各自的代码库，并享用各自的测试组件。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;共享库阶段&lt;/p&gt;

&lt;p&gt;我认为这一阶段呼应了前面的隐忧，在微服务落地之处，为了更快的建立微服务，开始出现了跨服务的共享代码库。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;败局的开始&#34;&gt;败局的开始&lt;/h2&gt;

&lt;p&gt;《Bye》的共享代码库一节，罗列了不少遇到的问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;共享库版本出现碎片：因为工程需要，并不敢冒险同时更新的多个服务的共享代码，造成部分服务的共享代码滞后。&lt;/li&gt;
&lt;li&gt;伸缩能力：微服务架构经常鼓吹的能力，在这里似乎被狠狠抽了一巴掌。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;个人的一点分析&#34;&gt;个人的一点分析&lt;/h2&gt;

&lt;p&gt;我眼中的微服务，务虚的角度上来说，有两个关键字：妥协，怀疑。&lt;/p&gt;

&lt;p&gt;这里所说的妥协指的是，我们的系统是存活于一个非“理想状态”中的，不管是“拥抱变化”，还是“面向故障”，都是对不完美世界的具体应对方式。&lt;/p&gt;

&lt;p&gt;而怀疑的设计态度，最简单的证据就是对隔离的强调，系统资源、数据的隔离都如此强调，我想，代码的隔离是不言自明的。&lt;/p&gt;

&lt;p&gt;反观 Segement 的重构之路，（可能）在宣称微服务的时候，各个服务还躺在同一个仓库里，还在共享同样的测试过程。&lt;/p&gt;

&lt;p&gt;在进行代码分离之后，又出现了两个不太容易理解的纰漏：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;暧昧的代码：前面提到，怀疑是微服务的基本态度，从《Bye》文中可以看到，他们在几十个服务之间共享了需要进行频繁重构的代码，抛开微服务和单体之争不谈，这个行为实在是无法理喻。&lt;/li&gt;
&lt;li&gt;暧昧的负载：在拆分之后，按照《Work》的说法，每个微服务都有了独立的队列和监控，由此推论，各个微服务的负载模型应该是比较容易做个描述的，很可惜的，这里称之为一种艺术。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;回到单体之后&#34;&gt;回到单体之后&lt;/h2&gt;

&lt;p&gt;此处内容给人一种感觉：“今天是个好日子”。给我的感觉是，一些原本应该在微服务阶段完成的工作，甚至是在微服务阶段之前的单体阶段完成的工作，终于完成了——例如改进的测试方案，这能算是半渡而击么？&lt;/p&gt;

&lt;h2 id=&#34;牢骚&#34;&gt;牢骚&lt;/h2&gt;

&lt;p&gt;流水账写到这里，有两个深刻的印象就是，这个团队从来没想过这些微服务是彼此独立的，也从来没把每个服务作为一个单独的交付物进行完善。从这个印象出发，回到单体形态，可以说是——得其所哉。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>土味微服务</title>
      <link>/post/some-comments-on-microservice/</link>
      <pubDate>Fri, 10 May 2019 13:06:50 +0800</pubDate>
      <guid>/post/some-comments-on-microservice/</guid>
      <description>

&lt;blockquote&gt;
&lt;p&gt;前段时间有大佬邀请，参加了一个微服务方面的交流，期间大放厥词，回味起来意犹未尽，整理一稿出来，博诸君一哂。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;扯一扯定义&#34;&gt;扯一扯定义&lt;/h2&gt;

&lt;p&gt;单体应用（monolithic application）这个名词，是经常被拿出来和微服务做比较（吊打）的。知己知彼百战不殆，在说微服务之前，似乎蛮有必要了解一下这个老对手的情况。&lt;/p&gt;

&lt;p&gt;按照 Google 程序员的常用抄袭手段，我在 Google 搜索 &lt;code&gt;monolithic application&lt;/code&gt;，选一点结果摘录如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Monolithic_application&#34; target=&#34;_blank&#34;&gt;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;In software engineering, a monolithic application describes a single-tiered software application in which the user interface and data access code are combined into a single program from a single platform.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://medium.com/koderlabs&#34; target=&#34;_blank&#34;&gt;Kodelabs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Monolith means composed all in one piece. The Monolithic application describes a single-tiered software application in which different components combined into a single program from a single platform.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;根据上面的陈述，会发现很难对单体应用做出一个精准的判断，&lt;code&gt;single program&lt;/code&gt;、&lt;code&gt;single-tiered&lt;/code&gt; 放到真实世界中，除了单服务器、单进程的小网站之外，这种情况似乎很难看到。在面对几十几百台服务器的时候，我想大概没有架构师会有兴趣让几百台服务器都跑同一个 WAR 吧。&lt;/p&gt;

&lt;p&gt;对手身份不太明确，那么会不会是个兔死狗烹呢？微服务的定义又是什么样的呢？下面是 &lt;a href=&#34;https://martinfowler.com/articles/microservices.html&#34; target=&#34;_blank&#34;&gt;Martin Folwer&lt;/a&gt; 给出的定义：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;小有多小，轻有多轻，全自动的范围有多大，去中心化到底有怎样的自治，似乎也都让人摸不到头脑。&lt;/p&gt;

&lt;p&gt;虽说概念没有一清二楚，然而从上述的定义来看，微服务是对一些既有的观点进行了强调的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;以业务为中心&lt;/li&gt;
&lt;li&gt;多技术栈共存&lt;/li&gt;
&lt;li&gt;轻量协议互访&lt;/li&gt;
&lt;li&gt;去中心化&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;据此，我对微服务这个风向的理解，是在传统软件的设计开发体系基础之上的一种变化，相对于翻天覆地的变革，我更愿称之为一种改良。&lt;/p&gt;

&lt;h2 id=&#34;是谁坑了瀑布流程&#34;&gt;是谁坑了瀑布流程&lt;/h2&gt;

&lt;p&gt;人人喊打的瀑布流程，在我看来，面对的是一种类似读书时候经常提到的&lt;code&gt;理想状况&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/perfect.png&#34; alt=&#34;perfect&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在理想状况中：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;需求是清晰而持久的&lt;/li&gt;
&lt;li&gt;基础设施都是稳定的&lt;/li&gt;
&lt;li&gt;依赖服务都是靠得住的&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;过往的开发过程在这种理想状况中，是具备非常多的好处的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;单一的技术栈有利于人才招聘、培养以及技术积累。&lt;/li&gt;
&lt;li&gt;少量的业务进程对发布、运维都有较小的压力，并且内部调用效率更高。&lt;/li&gt;
&lt;li&gt;集中数据的好处更是车载斗量，安全、事务等方面有极大的优势。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而世界发生了变化，廉价服务器包打天下，IT 人满坑满谷，业务日新月异，新技术层出不穷，用户漫山遍野，这一切因素叠加起来，都让以前重如千钧的需求变得相当轻浮。&lt;/p&gt;

&lt;p&gt;结果现在越来越多的开发过程，变成了这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/real.png&#34; alt=&#34;real&#34; /&gt;&lt;/p&gt;

&lt;p&gt;所有的环节，都随时面临变化的冲击：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;既有需求尚未完成，已经产生变更。&lt;/li&gt;
&lt;li&gt;设计开发过程受到新技术新方法的冲击。&lt;/li&gt;
&lt;li&gt;测试标准、交付标准的变化。&lt;/li&gt;
&lt;li&gt;运行环境升级和故障响应要求。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;种种变化归结起来：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;硬件和人力都变得廉价，变更频繁并且范围更大。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;快速的变化除了产生需求之外，还让黏连在一起的大进程难于适应：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;不同业务之间的信任关系会发生变化。&lt;/li&gt;
&lt;li&gt;共享数据容易污染。&lt;/li&gt;
&lt;li&gt;多小的代码变更，都可能引发大量的测试和上线动作。&lt;/li&gt;
&lt;li&gt;特定业务场景无法使用最优技术栈进行实现。&lt;/li&gt;
&lt;li&gt;必须同步进行缩放。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除了上述的外部因素之外，开发团队和交付物自身其实也同样的不理想：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;牵一发而动全身的共享代码库。&lt;/li&gt;
&lt;li&gt;不严格的组件边界，可能导致不同业务互相干扰。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;应对思路也顺理成章：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;越长的流程越容易被变化击中，换句话说，越短的流程越容易完成。&lt;/li&gt;
&lt;li&gt;缩短的流程，决定了交付物体积缩小，但是并行开发的需求大增。&lt;/li&gt;
&lt;li&gt;并行开发的过程，要求有多团队协同工作。&lt;/li&gt;
&lt;li&gt;多团队的结果就会导致扩展技术栈、去中心化的要求。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;更小的交付物，和更便宜的服务器勾搭起来，产生了大量运行小进程的廉价服务器，自然也就催生了“民用”的分布式计算。&lt;/p&gt;

&lt;p&gt;如此一来，各种集群、各层协议、各种分布式框架就成了刚需，产生了空前发展。&lt;/p&gt;

&lt;p&gt;从“集中力量干大事”，到“各村有各村的高招”，这种变化自然是&lt;strong&gt;为了满足客户需要&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&#34;没有-silver-bullshit&#34;&gt;没有 Silver Bullshit&lt;/h2&gt;

&lt;p&gt;按照前面提到的思路，我们：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;把进程内的组件拆成独立服务。&lt;/li&gt;
&lt;li&gt;独立的服务运行各自的进程。&lt;/li&gt;
&lt;li&gt;原有的进程内调用变成了网络调用。&lt;/li&gt;
&lt;li&gt;各个服务可能采用不同的技术栈来实现。&lt;/li&gt;
&lt;li&gt;每个服务独占存储自己的存储。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;大的有点不好，小的就一定好么？各自放飞的微服务，有了各自的实现、发布、伸缩的弹性，是不是就一定好了？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;更多的服务器，和相关设施，代表着更多的故障。所以微服务经常强调的是面向故障进行设计。&lt;/li&gt;
&lt;li&gt;分离的数据，相对于原有的单一数据库来说，事务保障变得复杂无比。&lt;/li&gt;
&lt;li&gt;大量的进程会给运维工作带来很大压力，急需监控和自动化的大力支持。&lt;/li&gt;
&lt;li&gt;单一业务的调用序列大幅延长，调试优化难度也一定是大大增加了。&lt;/li&gt;
&lt;li&gt;散乱的技术栈，会大幅提高技术管理的难度，降低知识的复用性。&lt;/li&gt;
&lt;li&gt;独立的代码库，很明显会降低代码复用的可能性。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由此看来，这种拆分并非只赚不赔的好买卖。是一个需要权衡的事情。不论微服务的数量有多大，个头有多小，作为一个独立的交付物，都还是需要遵循既有的软件开发技能和规律的。那么问题来了——对于给定的系统，到底应该有多少个微服务，每个服务有多大呢？&lt;/p&gt;

&lt;p&gt;前文说道，我更愿意称微服务为一种改良，他是一种应对变化的手段，而非特征。在如今的情况下，不管是对新业务实现的设计，还是对既有系统的重构，任何架构师都不会把明明应该拆分的东西硬性的合并到一起。所以这个分离的标准就比较重要了。&lt;/p&gt;

&lt;h2 id=&#34;拆还是不拆-这是个问题&#34;&gt;拆还是不拆，这是个问题&lt;/h2&gt;

&lt;p&gt;很明显的，微服务的设计不是一个靠数量取胜的事情，所以重点不是能拆成多少块，而是什么东西需要拆，下面列出我所知的几个影响因素。&lt;/p&gt;

&lt;h3 id=&#34;组织机构&#34;&gt;组织机构&lt;/h3&gt;

&lt;p&gt;利益人的识别，在各种软件开发过程中都是很重要的一个环节，在微服务中也是如此，而且尤其重要。当一份交付物存在多个利益人，并且互相之间不存在管辖关系的时候，就表明当前交付物的边界出现了问题，面临着令出多头、需求干扰的风险。&lt;/p&gt;

&lt;h3 id=&#34;伸缩需求&#34;&gt;伸缩需求&lt;/h3&gt;

&lt;p&gt;同一个进程之内的不同业务功能，有时在业务量方面会出现较大的差异，具体要求的进程数量会有较大差别，这样的模块锁定在同一进程之内，势必会造成资源的浪费。&lt;/p&gt;

&lt;h3 id=&#34;部署频率&#34;&gt;部署频率&lt;/h3&gt;

&lt;p&gt;这个很容易理解，同一个交付物内不同的组件有着不同的上线频率，会大大的提高上线流程的发生频率，会造成较大的人员浪费。&lt;/p&gt;

&lt;h3 id=&#34;修改的相关性&#34;&gt;修改的相关性&lt;/h3&gt;

&lt;p&gt;如果同一交付物内的不同组件，经常会被同步修改，这可能说明，如果发生拆分，这两个模块应该是”在一起“的。&lt;/p&gt;

&lt;h3 id=&#34;技术栈&#34;&gt;技术栈&lt;/h3&gt;

&lt;p&gt;新技术的爆发，让很多不同的场景有了更好的实现方式，如果有业务急需使用新的技术栈来（重新）实现。这种新技术除了新的语言、新的数据库之外，可能还包括容器、Service Mesh、API 网关等新晋 PaaS 能力。毫无疑问，这种业务也同样有拆分的需要。&lt;/p&gt;

&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;

&lt;p&gt;微服务虽说不是一个全新的东西，但也绝非是简单的新瓶装旧酒。总体说来，我认为这是在尊重现实，适当妥协的基础上，一种应对变化的好办法。&lt;/p&gt;

&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;/post/microservice/&#34;&gt;《微服务》&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href=&#34;/post/extract-from-monolith/&#34;&gt;《如何从单体应用中拆分富数据服务》&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>混合微服务模式</title>
      <link>/post/microservice-hybrid-model/</link>
      <pubDate>Thu, 04 Apr 2019 01:23:56 +0800</pubDate>
      <guid>/post/microservice-hybrid-model/</guid>
      <description>

&lt;p&gt;原作者：&lt;a href=&#34;https://thenewstack.io/author/sonya-koptyev/&#34; target=&#34;_blank&#34;&gt;Sonya Koptyev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原标题：&lt;a href=&#34;https://thenewstack.io/why-a-microservices-hybrid-model-is-what-you-probably-need-instead/&#34; target=&#34;_blank&#34;&gt;Why a Microservices Hybrid Model Is What You Probably Need Instead&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在听到别人谈及&lt;a href=&#34;https://www.twistlock.com/2017/03/20/microsecurity-for-microservices-2/&#34; target=&#34;_blank&#34;&gt;微服务&lt;/a&gt;的时候，你会注意到他们讨论的是一个非此即彼的问题：重构成微服务？还是固守单体形态？这种想法很合情理：微服务是个复杂的话题，把这个复杂性简化成一个二选一问题，就轻松了。&lt;/p&gt;

&lt;p&gt;然而微服务实际上并不是一个非黑即白的概念。在应用中部分使用微服务，其它组件仍然保持单体应用的状态，这是一种完全有可能的情况。&lt;/p&gt;

&lt;p&gt;这就成了一种混合模式的微服务。本文中我希望能解释一下混合微服务的具体含义，为什么会用到这种模式以及如何构建混合模式的微服务。&lt;/p&gt;

&lt;h2 id=&#34;混合微服务的概念和来由&#34;&gt;混合微服务的概念和来由&lt;/h2&gt;

&lt;p&gt;简单来说，混合&lt;a href=&#34;https://www.twistlock.com/resources/guide-cloud-native-microservices/&#34; target=&#34;_blank&#34;&gt;微服务应用&lt;/a&gt;意味着应用的一部分是用微服务的方式来进行开发和部署，其它部分则还是单体形式。&lt;/p&gt;

&lt;p&gt;可以将其与混合云相类比，混合云中包含了公有云、私有云，可能还有其它的自有基础设施。目前来看，混合云是一种流行的实践方式；实际上，可能很难找到一个完全单一云模式的组织。&lt;/p&gt;

&lt;p&gt;如果我们能和混合云共处，那么也应该能够应对混合微服务架构。这种做法能够比较简单的获得微服务在弹性、容量和安全方面的好处，又无需使用微服务架构将传统应用完全重构。&lt;/p&gt;

&lt;p&gt;对多数组织来说，将一个单体应用完全重构为微服务的过程中，对开发资源的调动是一个很严峻的问题；采用混合微服务策略是一个较好的方式，对开发团队来说，这种方式让微服务架构触手可及；否则的话，开发团队可能会因为时间、经验等方面的欠缺，无法接受对单体应用的重构工作。&lt;/p&gt;

&lt;h2 id=&#34;构建混合微服务架构的最佳实践&#34;&gt;构建混合微服务架构的最佳实践&lt;/h2&gt;

&lt;p&gt;混合微服务架构的基本概念很容易理解，但是实现起来就颇值得玩味了。首先面临的问题就是，需要对你的应用进行鉴别——哪些部分需要微服务改造，哪些部分需要保持单体形态。这里给出一些提示。&lt;/p&gt;

&lt;h3 id=&#34;最大化收益的部分优先重构&#34;&gt;最大化收益的部分优先重构&lt;/h3&gt;

&lt;p&gt;最醒目也是最重要的事情就是，应用的哪些部分重构为微服务会获得最大收益。&lt;/p&gt;

&lt;p&gt;哪些部分需要重构？要解答这个问题很明显根据实际情况来进行判断，但是还是存在一些指导性的微服务改造原则的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.twistlock.com/2019/01/28/security-strategy-needs-scale-heres/&#34; target=&#34;_blank&#34;&gt;伸缩性&lt;/a&gt;：你的应用中，可能有些部分需要比其它部分更多的实例数量。例如一个系统的首次访问可能需要登录，这就可能使得认证模块在某一时间出现峰值，而其它模块则不存在这种波动。这种情况下，将认证模块拆分出来形成独立的微服务，就能让你用更少资源来满足认证的峰值需要。&lt;/li&gt;
&lt;li&gt;安全性：应用中哪一部分最多暴露在安全威胁之下？哪一部分最有可能被入侵从而影响到其它部分的安全？这些组件应该优先转换为微服务，这样有利于进行有效的隔离，降低安全风险。&lt;/li&gt;
&lt;li&gt;更新：微服务的一个重要优势就是能够在不打扰其它微服务的情况下进行独立更新。有时一个应用中的不同组件会有不同的更新频率。有时候你会在很少更新后端的情况下，频繁的变更接口。这种情况下，可以把前端重构为一个或多个微服务，从而能够更容易的进行这部分的更新工作，让原有的后端继续以单体应用的形式进行。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;正视微服务的复杂性&#34;&gt;正视微服务的复杂性&lt;/h3&gt;

&lt;p&gt;微服务有很多优势，但是也有其固有的缺陷：把系统变得更复杂。这种额外的复杂性，直接提升了开发和运维工作的难度。&lt;/p&gt;

&lt;p&gt;正因如此，混合微服务的方式就很有意义了，可以尽可能的降低微服务引入的复杂性。例如哪一部分的 API 调用更少，或者哪一部分可以打包为独立的容器。如果优先对这些组件进行重构，就能够有效的减少 API 调用，降低容器化的难度。&lt;/p&gt;

&lt;h3 id=&#34;面向未来&#34;&gt;面向未来&lt;/h3&gt;

&lt;p&gt;和混合云一样，混合微服务策略不是一蹴而就的。这是个持续的过程。可能起初只有一小部分的组件被独立出来成为了微服务，从而形成了混合微服务架构，但是不必止步于此，随着时间的推移和环境的变化，可能会有更多的重构需求。&lt;/p&gt;

&lt;p&gt;所以在踏上混合微服务之路的时候，应该设计一个微服务的路线图。就算不具体到时间，至少也应该对你的混合微服务应用有一个基本的演进设想。&lt;/p&gt;

&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;

&lt;p&gt;我们都希望改善我们的单体应用，但实际情况是，多数情况下我们不会有足够的开发资源把单体应用迅速的重构为微服务架构。与其全盘放弃，不如考虑一下这种混合微服务架构。可以参考上面提出的标准，来决定需要进行微服务改造的模块，需求不够迫切的其它部分，继续以单体应用的模式提供服务。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Medium 的微服务架构</title>
      <link>/post/microservice-at-medium/</link>
      <pubDate>Sat, 10 Nov 2018 12:09:56 +0800</pubDate>
      <guid>/post/microservice-at-medium/</guid>
      <description>

&lt;p&gt;作者：&lt;a href=&#34;https://medium.engineering/@xiaoma&#34; target=&#34;_blank&#34;&gt;Xiao Ma&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文：&lt;a href=&#34;https://medium.engineering/microservice-architecture-at-medium-9c33805eb74f&#34; target=&#34;_blank&#34;&gt;Microservice Architecture at Medium&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;微服务架构的目标是帮助工程团队安全快速地完成高质量的产品交付。良好解耦的服务能够在最小化对其它系统的影响的条件下进行快速迭代。&lt;/p&gt;

&lt;p&gt;2012 年构建的 Node.js 单体应用构成了 Medium 的最初技术栈。我们构建了各种卫星服务，但是并没有提出系统化采用微服务架构的策略。2018 年初，随着系统复杂性的提高和团队规模的扩大，我们开始转向了微服务架构，工作中总结出一些如何高效完成这一过程并避免微服务症候群的经验，本文将分享这一经验。&lt;/p&gt;

&lt;h2 id=&#34;什么是微服务架构&#34;&gt;什么是微服务架构&lt;/h2&gt;

&lt;p&gt;首先我们花费一点时间来思考一下，微服务是什么/不是什么的问题。目前的软件工程有一些滥用和混乱的趋势，微服务就是其中一个。Medium 认为微服务是：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;微服务架构中，多个松耦合的服务协同工作，每个服务聚焦于一个单一的目的，而这一目的相关的数据和行为则在单一服务内高度聚合。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这一定义中包含了微服务的三个设计原则：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;单一目的：每个服务应该专注于做好一件事。&lt;/li&gt;
&lt;li&gt;低耦合：服务之间极少互相了解。对一个服务的变更不应该要求其它服务也进行变更。服务之间的通信金应该发生在公开的服务接口之中。&lt;/li&gt;
&lt;li&gt;高内聚：每个服务应该对所有相关的行为和数据进行封装。如果需要构建新的功能，所有的变更只应该落地到一个服务之中。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;images/1*f5yQlyPApGNPfauFBe0pTA.png&#34; alt=&#34;微服务建模三要素&#34; /&gt;&lt;/p&gt;

&lt;p&gt;进行微服务建模的过程中使用应遵循这三条原则。这是释放微服务能量的唯一途径，违反任何一条，都会违反微服务模式的理念。&lt;/p&gt;

&lt;p&gt;如果违反了目的单一性原则，每个微服务最终都会负担太多任务，成为多个单体服务。这无法获得微服务应有的好处，并且要付出更多的运维成本。&lt;/p&gt;

&lt;p&gt;不遵守松耦合原则的后果是，一个服务的变更会影响到其它服务，所以也就无法快速安全的进行发布——这是微服务的核心优势。紧密耦合还可能造成数据冲突甚至丢失。&lt;/p&gt;

&lt;p&gt;而如果缺乏内聚性，就会变成分布式的单体系统——为了完成一个功能修改，必须同时变更和部署一堆服务。分布式的单体系统经常比中心化的单体系统更加糟糕，多个服务甚至多个团队的协调过程造成更高的复杂度和成本消耗。&lt;/p&gt;

&lt;p&gt;同时，微服务不是什么，也是个同样重要的问题。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;微服务不一定是一个行数较少、或者完成细小任务的服务。显然，这一个误解是来自于微服务这个名字的。微服务架构的目标不是创建尽可能多的服务。符合上述三个原则的服务，可能结构复杂，可能规模庞大，也一样可以称之为微服务。&lt;/li&gt;
&lt;li&gt;微服务不一定需要用最新的技术进行构建。虽说微服务架构让团队能够更方面的试水新技术，但这不是主要目标。用同样的技术构建所有新服务完全没有问题，团队同样能从服务解耦中获益。&lt;/li&gt;
&lt;li&gt;微服务并不一定需要从头做起。如果已经有了一个架构良好的单体应用，应该避免从头构建所有新服务。可能会有机会对单体服务进行分拆。再一次提醒，上面的三个原则是应当坚守的。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;为什么是现在&#34;&gt;为什么是现在&lt;/h2&gt;

&lt;p&gt;在 Medium 需要在产品或者工程方面作出重大决策时，经常会提出一个问题：为什么是现在？“为什么”是个很明显需要解决的问题，但这个问题的答案在假设我们有无穷的人力、时间和资源，很明显这是一个危险的假设。把这个问题改为“为什么是现在”，就突然出现了很多的制约——对现有工作的影响、机会成本、并行的开销等。这一问帮助我们更好的确定优先级。&lt;/p&gt;

&lt;p&gt;为什么&lt;strong&gt;现在&lt;/strong&gt;要使用微服务架构的原因就是——我们的 Node.js 单体应用在很多方面都成为了瓶颈。&lt;/p&gt;

&lt;p&gt;首先最紧急和重要的瓶颈就是它的性能。有一些重度计算和重度 I/O 的任务并不适合 Node.js。我们一直在持续这一单体应用的改进工作，然而并无起色。低下的性能妨碍了产品改进工作——功能的改进可能让这个已经很慢的应用更加缓慢。&lt;/p&gt;

&lt;p&gt;第二，一个重要且有点紧急的瓶颈就是单体应用拖慢了应用开发过程。所有的工程师都在单一的应用中构建功能，耦合在一起。因为可能存在的互相影响，我们没法轻松的对系统的某一方面做出变更。整个应用作为一个整体进行部署，所以如果一个不良提交导致了问题，那么这次部署中涉及的所有其它变更不论工作状况有多完美，都会受到拖累。而在微服务架构中，团队可以更快的交付、研究和迭代。正在构建的新功能会从其它复杂系统中解耦出来，让开发团队可以专注于此。所以新的变更能够更快的推向生产环境，从而具备了安全尝试变更的机会。&lt;/p&gt;

&lt;p&gt;在我们新的微服务架构中，变更可以在一小时内发布到生产环境上，工程师不必担忧这些变更会对系统其它部分造成多大影响。团队还在探索在开发环境中安全使用生产数据的方法——这可是做了多年的白日梦。随着我们工程师团队的发展壮大，这些因素的重要性也日益提升。&lt;/p&gt;

&lt;p&gt;第三，单体应用中，很难针对部分任务作出性能扩展，也很难针对不同类型的任务进行资源隔离。单体应用只能为整个系统进行伸缩，为了某个资源大户进行整体扩展之后，对系统中其它的相对简单任务来说，系统资源就处于超配状态了。为了缓解这一情况，我们将不同类型的请求发送给不同的 Node.js 进程。这种做法在某种程度上来说是有效的，但是伸缩一样受到限制，这还是因为单体应用的紧耦合特性造成的负担。&lt;/p&gt;

&lt;p&gt;最后一点，也是同样重要的一点。一个即将浮上水面的问题就是单体应用阻止了对新技术的尝试。微服务架构的一个主要优势就在于每个服务可以使用不同的技术栈进行构建，将不同的技术进行整合。微服务架构让我们可以为特定工作选择最优方案，更重要的是，可以更快更安全的完成任务。&lt;/p&gt;

&lt;h2 id=&#34;微服务策略&#34;&gt;微服务策略&lt;/h2&gt;

&lt;p&gt;微服务之路并非一帆风顺，一旦误入歧途就会对生产力造成损害。本节中我们会分享七条策略，这些策略在我们进入微服务架构的初期给了我们很大帮助。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;构建具备清晰价值的新服务。&lt;/li&gt;
&lt;li&gt;单一的持久存储是有害的。&lt;/li&gt;
&lt;li&gt;服务的构建和运行应该解耦。&lt;/li&gt;
&lt;li&gt;详尽、一致的可观察性。&lt;/li&gt;
&lt;li&gt;无需从头构建每个新服务。&lt;/li&gt;
&lt;li&gt;故障总会发生，必须正视。&lt;/li&gt;
&lt;li&gt;从第一天开始预防微服务综合症。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;构建具备清晰价值的新服务&#34;&gt;构建具备清晰价值的新服务&lt;/h3&gt;

&lt;p&gt;可能有人认为采用新的服务架构意味着产品开发的一个暂停，并且需要重写所有东西。这是一种错误方法。我们决不应该为了构建新服务而构建新服务。每次我们构建新服务或者采纳新技术，都必须有明确的业务价值或工程价值。&lt;/p&gt;

&lt;p&gt;我们能交给用户的益处，就是业务价值的体现。相对我们的单体 Node.js 应用而言，新服务应该能够提供更多价值，或者更快的提供价值。工程价值就是让工程团队能够更好更快的工作。&lt;/p&gt;

&lt;p&gt;如果一个新服务并不具备清晰的价值，我们就会把它留在单体应用中。十年之后 Medium 仍然有单体的 Nodejs 应用在服役也并不是什么大不了的事情。从一个单体应用开始，实际上让我们能够更有策略的进行微服务建模。&lt;/p&gt;

&lt;h3 id=&#34;单一的持久存储是有害的&#34;&gt;单一的持久存储是有害的&lt;/h3&gt;

&lt;p&gt;建模微服务的一大部分工作就是对持久化数据存储进行建模。要把微服务整合在一起，共享数据存储看起来是个最方便的做法。事实上这种做法是有害的，我们应该不惜一切代价来避免这种情况。下面解释一下原因。&lt;/p&gt;

&lt;p&gt;首先持久化数据存储事关实现细节。在服务之间共享数据存储就相当于将服务实现的细节暴露给整个系统。如果这个服务修改了数据的格式，或者加入缓存层，又或者更换了不同的数据库，那么很多服务都必须跟进。这违反了松耦合原则。&lt;/p&gt;

&lt;p&gt;其次，持久化数据存储的过程，例如修改、处理和使用数据的方法，并不是服务行为。如果我们在不同服务之间共享数据存储，就意味着其它服务也要复制服务行为。这样就违反了高内聚原则。特定领域的行为被泄漏到多个服务之中。修改了一个行为，就必须修改所有相关其它服务。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;微服务架构中，特定类型的数据只应该由单一的服务负责。所有其它服务要使用这些数据只有两个选择，一是通过 API 向负责的服务发出请求；二是持有一个只读的非权威拷贝。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这听起来可能有点抽象，下面提供一个具体的例子。假设我们正构建一个新的推荐服务，这一服务需要一些来自发帖数据表中的数据，这些数据目前保存在 AWS DynamoDB 中。我们可以用两种方式把发帖数据提供给新的推荐服务。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/1*PRdrp_MZ3C2KwKHRr83XbA.png&#34; alt=&#34;Sharing data&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在单体存储模型中，推荐服务能够直接访问单体应用的持久化数据。这办法不太好：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;缓存是个麻烦。如果推荐服务共享单体应用的缓存，我们就必须在推荐服务中复制缓存的实现细节；如果推荐服务使用自己的缓存，又无法在单体应用更新发帖数据时进行缓存失效。&lt;/li&gt;
&lt;li&gt;如果单体应用决定用 RDS 替代 DynamoDB 进行发帖数据的存储，我们就必须重新在推荐服务中实现一次，当然所有其它共享这一数据的服务都要重来一次。&lt;/li&gt;
&lt;li&gt;单体应用中处理发帖数据的逻辑比较复杂，例如如何决定一篇帖子是否可以显示给特定用户。我们必须在推荐服务中重新实现这些逻辑。一旦单体应用修改或者添加了新的逻辑，我们也必须在所有相关服务中重新实现。&lt;/li&gt;
&lt;li&gt;不管推荐服务的访问模型如何，它也只能使用 DynamoDB。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在解耦存储模型中，推荐服务和其它新服务都没有直接访问发帖数据的能力。发帖数据的实现细节只存在于单一的服务中，有不同的方式可以实现这一目标。&lt;/p&gt;

&lt;p&gt;理想情况下，应该又一个发帖服务作为发帖数据的所有者，其它服务职能通过这一服务的 API 来访问发帖数据。然而为所有的核心数据模型构建新服务可能需要高昂的成本。&lt;/p&gt;

&lt;p&gt;在无法采用最佳方案的情况下，还有有两种行之有效的替代方案。视乎不同的数据访问模式，这可能是更实际的方法。选项 B 中，单体应用在相关帖子的数据更新时会通知推荐服务。通常这种操作的时效性要求不高，可以交给队列系统来完成。选项 C 中用 ETL 管道为推荐服务生成了一个帖子数据的只读拷贝，还加入了一些其它潜在可能对推荐服务有用的数据。两种方案里，推荐服务都完全控制了数据，所以它也就具备了正确处理缓存的能力、以及选择合适数据库的自由。&lt;/p&gt;

&lt;h3 id=&#34;服务的构建和运行应该解耦&#34;&gt;服务的构建和运行应该解耦&lt;/h3&gt;

&lt;p&gt;如果说服务构建困难重重的话，服务运行更是难上加难。如果服务的构建和运行耦合在一起的话，会拖慢工程团队，团队必须进行重复实现运行机制。我们希望每个服务能够专注于自身工作，无需担忧运行服务的复杂性，其中包括网络、通信协议、部署、监控等。服务管理应该完全从每个独立服务的实现中解耦出来。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;服务构建和运行的解耦策略，应该是独立的，并且不依赖于特定的服务实现技术，这样应用工程师能够完全投入到各自服务的业务实现之中。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;感谢近年来出现的容器化、容器编排、服务网格、APM 等进步技术，服务运行的解耦比过去任何时候都更加容易实现。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;网络&lt;/strong&gt;：网络（例如服务发现、路由、负载均衡、流量路由等）是服务运行的一个重要因素。传统的方式是为每个平台或者语言都提供库。这是有效的方法，但是不够理想，服务还是需要进行大量工作来完成这些库的集成和管理。有时候应用还要单独实现一些逻辑。现代的解决方案就是在服务网格中运行服务，Medium 使用了 &lt;a href=&#34;https://istio.io/&#34; target=&#34;_blank&#34;&gt;Istio&lt;/a&gt; 和 &lt;a href=&#34;https://www.envoyproxy.io/&#34; target=&#34;_blank&#34;&gt;Envoy&lt;/a&gt; 的 Sidecar 方案。构建服务的工程师完全不需要担心网络问题了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;通讯协议&lt;/strong&gt;：不管用什么语言或者技术栈来构建微服务，选择一个成熟、高效、典型、跨平台并无需大量开发投入的 RPC 方案都是至关重要的。支持向后兼容的 RPC 方案让部署更加安全。Medium 选择了 &lt;a href=&#34;https://grpc.io/&#34; target=&#34;_blank&#34;&gt;gRPC&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;一个常见的替代方案就是通过 HTTP 实现的 REST+JSON，这个方案在服务通讯领域流行已久。然而虽然这一方案对浏览器和服务器之间的通信很有效，但是对于服务间通信却有些不足，尤其是在发生大量请求的情况下。如果没有自动生成的 Stub 和模板代码，就只能手工实现服务端和客户端代码了。可靠的 RPC 实现不只是封装一个网络客户端。另外虽然 REST 很完备，但是有些细节并不能获得所有人的一致认同。例如一次调用，到底是 REST 还是 RPC？一条消息到底是一个资源还是一个操作？等等。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;部署&lt;/strong&gt;：有稳定的方式来构建、测试、打包、部署和管理服务是非常重要的。Medium 的微服务都运行在容器中。目前我们的系统是 &lt;a href=&#34;https://aws.amazon.com/ecs/&#34; target=&#34;_blank&#34;&gt;AWS ECS&lt;/a&gt; 和 Kubernetes 的混合架构，正在向全 Kubernetes 迁移。&lt;/p&gt;

&lt;p&gt;我们构建了我们自己的系统，称为 BBFD，用来完成构建、测试、打包和部署服务。在保证对不同服务的一视同仁和让各个服务可以使用各自的技术栈的弹性之间取得了一个平衡。每个服务只要提供一些基础信息，例如监听端口、用用构建、测试和运行服务的命令等。BBFD 会完成其它工作。&lt;/p&gt;

&lt;h3 id=&#34;详尽-一致的可观察性&#34;&gt;详尽、一致的可观察性&lt;/h3&gt;

&lt;p&gt;可观察性中包含了过程、方法和工具，让我们能够了解系统的工作状况，并在故障期间对问题进行鉴别。日志、性能跟踪、指标、Dashboard、告警都是可观察性的组成部分，它是能够左右微服务架构成败的关键因素。&lt;/p&gt;

&lt;p&gt;我们从单体服务向多服务构成的分布式系统迁移的过程中，会发生两件事情：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;丢失了可观察性——因为它的难度提高了，且更容易被忽视。&lt;/li&gt;
&lt;li&gt;不同团队都在重新发明轮子，最终导致可观察性呈现为一种碎片状态，这实际上就降低了可观测性，很难用这些碎片数据来对问题进行关联或鉴别。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;从一开始就应该建立良好的持续的可观察性，因此我们的 DevOps 团队提出了一致观察性的策略，并构建工具达成这一目标。每个服务都有详细的 &lt;a href=&#34;https://www.datadoghq.com/&#34; target=&#34;_blank&#34;&gt;DataDog&lt;/a&gt; Dashboard、告警和统一的日志搜索支持。我们还深度使用 &lt;a href=&#34;https://lightstep.com/&#34; target=&#34;_blank&#34;&gt;LightStep&lt;/a&gt; 来进行系统性能方面的分析。&lt;/p&gt;

&lt;h3 id=&#34;无需从头构建每个新服务&#34;&gt;无需从头构建每个新服务。&lt;/h3&gt;

&lt;p&gt;微服务架构中，每个服务的任务都是专注做好自己的事情。这里对如何服务的构建方式并无要求。如果正在从单体应用向微服务架构进行迁移，一个需要注意的问题就是，如果能够从单体应用中剥出目标服务，那么可能并不一定需要从头进行构建。&lt;/p&gt;

&lt;p&gt;针对是否需要从头构建新服务的问题，我们从务实的角度触发，主要考虑了两方面的问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node.js 是否适合新服务的需求。&lt;/li&gt;
&lt;li&gt;用新的技术栈重新实现的成本如何。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果 Node.js 是一个合适的选型，并且已有的实现并无不妥，我们就会从单体应用中抽取相关代码，以此作为基础来构建新服务。这样一来，虽然用的是既有实现，同样能够享受到微服务架构的益处。&lt;/p&gt;

&lt;p&gt;我们的 Node.js 单体应用的架构让我们能够比较方便的抽取代码进行新服务的构建。后面我们还会讨论一下如何正确的构建一个单体应用的问题。&lt;/p&gt;

&lt;h3 id=&#34;故障总会发生-必须正视&#34;&gt;故障总会发生，必须正视&lt;/h3&gt;

&lt;p&gt;分布式环境中可能包含更多的故障机会。关键服务的故障如果不能及时处理，可能演变成一场灾难。我们应该随时思考如何完成故障的检测和处理。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;第一也是最重要的，应该意识到所有一切都会在某个点上发生故障。&lt;/li&gt;
&lt;li&gt;RPC 调用尤其需要对故障案例进行着重处理。&lt;/li&gt;
&lt;li&gt;确保系统故障情况下的可观测性（上面刚有提及）。&lt;/li&gt;
&lt;li&gt;新服务上线之前必须针对故障进行测试，故障应对是新服务的 Check List 的一部分。&lt;/li&gt;
&lt;li&gt;尽可能构建自动恢复功能。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;从第一天开始预防微服务综合症&#34;&gt;从第一天开始预防微服务综合症。&lt;/h3&gt;

&lt;p&gt;微服务不是万金油，在解决一些问题的同时，也产生了被称为微服务综合症的新问题。如果我们不从一开始就考虑这个问题，事情有可能变得很麻烦，处理起来也要付出更多代价。下面列出一些常见症状。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;建模粗糙的微服务危害巨大，尤其是在数量较多的情况下。&lt;/li&gt;
&lt;li&gt;引入太多不同的语言和技术，会导致运维成本升高，分化工程团队。&lt;/li&gt;
&lt;li&gt;服务构建和运行相耦合，会导致复杂度的急剧升高并拖慢工程进度。&lt;/li&gt;
&lt;li&gt;忽略数据建模，导致单体数据存储形式的微服务。&lt;/li&gt;
&lt;li&gt;缺乏可观测性，致使性能问题和故障难于定位。&lt;/li&gt;
&lt;li&gt;面对问题时，团队可能选择创建新服务，而不是解决问题——即使解决问题是一个更好的选择。&lt;/li&gt;
&lt;li&gt;缺乏整体视图的松耦合服务群本身就是一个问题。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;是否不该构建单体服务&#34;&gt;是否不该构建单体服务&lt;/h2&gt;

&lt;p&gt;受益于近期的技术革新，使用微服务架构变得容易了很多。这是否意味着我们应该完全杜绝单体服务的构建了？&lt;/p&gt;

&lt;p&gt;不是的。虽说新技术给了微服务更好的支持，但是微服务架构始终是更难更复杂的。对于刚起步的小团队，单体应用依旧是一个更好的选项。然而可以尝试对单体应用架构进行更好的设计，从而降低未来转向微服务架构时所需的投入。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;从单体架构起步没什么问题，但是要保证系统的模块化，并按照上面的三个要素（单一目标、松耦合、高内聚）进行架构设计，最终构建成为一组技术栈一致、同步部署并且运行在同一个进程内的服务。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我们在 Medium 就在早期为单体应用做出了一些好的架构决策。&lt;/p&gt;

&lt;p&gt;我们的单体应用由高度模块化的组件构成，最终成长为包含 Web Server、后端服务和离线时间处理器的庞然大物。离线处理器单独运行，但是试用了同样的代码。这样就能够相对简单的将业务逻辑抽取出来成为新的服务，新服务能够和之前的实现提供同样的（高级）接口。&lt;/p&gt;

&lt;p&gt;我们的单体应用在较低层次对数据存储进行了封装。每个数据类型（一个数据库表）都有两层实现：数据层和服务层。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;数据层为特定类型的数据提供 CRUD 操作的实现。&lt;/li&gt;
&lt;li&gt;服务层处理特定类型数据的高级逻辑，并为系统的其余部分提供共用 API。服务之间不进行数据存储的共享。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;数据的实现细节对其它部分的代码是完全隐藏的，这种做法帮助我们更好的进行微服务架构的迁移。创建新服务来处理某种数据相对来说是比较轻松和安全的。&lt;/p&gt;

&lt;p&gt;单体应用还帮助我们对微服务进行建模，并让我们可以无需从头做起，而是专注在系统的最重要部分上进行微服务的迁移。&lt;/p&gt;

&lt;h2 id=&#34;结语&#34;&gt;结语&lt;/h2&gt;

&lt;p&gt;多年以来，我们的单体 Node.js 应用发挥了巨大作用，然而对于大项目的交付和快速迭代的要求，开始力不从心，所以我们开始系统地、策略地转向微服务架构。我们的探索才刚刚开始，但是已经看到了它的好处和潜力——它戏剧性的提高了开发生产力、让我们可以从大处着眼并对产品做出大规模的改进，同时工程团队得到解放，能够安全的测试新的技术。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>如何从单体应用中拆分富数据服务</title>
      <link>/post/extract-from-monolith/</link>
      <pubDate>Tue, 04 Sep 2018 15:48:42 +0800</pubDate>
      <guid>/post/extract-from-monolith/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://martinfowler.com/articles/extract-data-rich-service.html&#34; target=&#34;_blank&#34;&gt;How to extract a data-rich service from a monolith&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://computellect.com/&#34; target=&#34;_blank&#34;&gt;Praful Todkar&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;关于作者：Praful Todkar 是 ThoughtWorks 的首席顾问，专门构建大型分布式商业软件系统。软件的构建过程中，他乐于在尽快实现商业价值的目标下，为复杂问题创建简单、优雅的解决方案。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
&lt;p&gt;在将单体应用拆分为较小服务的过程中，最难的部分就是单体服务数据库中的数据拆分。要进行这样的拆分，保证数据有一个全程唯一的写拷贝，并且遵循一系列步骤是很有帮助的。拆分步骤从对现有单体应用的逻辑分割开始：将服务行为拆分为一个单独的模块，然后把数据拆分到单独的数据表中。一系列动作之后，这些元素最终成为一个自治的新服务。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;从单体应用向较小服务的迁移是目前的主流趋势。投资进行这样的迁移，其动力在于，围绕业务能力构建较小服务，能够提高开发者的生产力。团队一旦成为服务的主人，同时也就成为自身命运的主人，这就意味着可以不受系统中其他服务的限制，自由的对自有服务进行改善和升级。&lt;/p&gt;

&lt;p&gt;这个转换过程之中最难的部分，就是从单体应用所持有的数据库中把新服务所属的数据拆分出来。如果从单体应用中拆分出来的逻辑部分仍然连接到同一个数据库，这种拆分无疑是比较简单的。但是这样一来，数据库就成为跨应用共享数据库，整个系统所呈现出的各自独立的分布式形态仅是徒有其表，在数据库层面，这依旧是一个紧耦合系统。真正独立的服务需要有独立的数据库——格式和数据都专属于服务。&lt;/p&gt;

&lt;p&gt;本文中要讲述一系列步骤组成的一个解构模式，用来在最小化业务中断的前提下，从单体应用中拆出富数据服务。&lt;/p&gt;

&lt;h2 id=&#34;服务拆分过程的指导原则&#34;&gt;服务拆分过程的指导原则&lt;/h2&gt;

&lt;p&gt;深入探讨之前，我想首先介绍两个对于服务拆分具有重要指导意义的基本原则。这两条原则能把从单体应用到多服务的拆分过程变得更加平滑，也更加安全。&lt;/p&gt;

&lt;h3 id=&#34;整个迁移过程中-数据保持有单一的写拷贝&#34;&gt;整个迁移过程中，数据保持有单一的写拷贝&lt;/h3&gt;

&lt;p&gt;在转移过程中，我们应该保证待迁出服务的数据始终有一个单独的写拷贝。如果出现了多个可写拷贝，就会出现写冲突的风险。当多个客户端同时写入同一块数据的时候，写冲突就会出现。写冲突的应对是比较复杂的——需要选择一个处理模式，并进行相应的处理。例如以最后写入为准的话，会给部分客户端带来意料之外的后果，同时还要通知写入失败的客户端进行相应的纠错处理。这样的逻辑无疑是比较复杂的，应该尽量避免。&lt;/p&gt;

&lt;p&gt;这里的服务拆分模式会保证在服务拆分过程中的任意时间点上，都保持唯一的可写副本，从而避免写冲突造成的复杂性。&lt;/p&gt;

&lt;h3 id=&#34;遵循-架构演进原子化-的原则&#34;&gt;遵循“架构演进原子化”的原则&lt;/h3&gt;

&lt;p&gt;我的同事 Zhamak Dehghani 提出一个原则，叫架构演进原子化，代表在架构迁移过程中使用一系列原子化步骤。这些步骤完成以后，架构就完成了承诺的升级。如果这些步骤没有完全执行，那么这一架构的状态会比初始状态更加糟糕。例如决定分拆一个服务，结果最后只拆分了逻辑，没能拆分数据，这样收获的是一个数据库层耦合的状态，这一状态依然会导致开发和运行时的紧密耦合。实际上对比开始分拆之前，系统变得更加复杂，开发和除错的难度也随之增加了。&lt;/p&gt;

&lt;p&gt;下面讲到的模式中，我们建议完成其中的所有步骤来完成拆分工作。服务分拆过程之中的最大障碍并非来自技术，而是如何让既有的单体应用客户迁移到新的服务之中去。我们将在第五步讨论这一话题。&lt;/p&gt;

&lt;h2 id=&#34;服务拆分的步骤&#34;&gt;服务拆分的步骤&lt;/h2&gt;

&lt;p&gt;现在让我们进入实际的服务拆分模式之中。为了方便讲述和操作，我们会做一个例子，来解释服务拆分的具体过程。&lt;/p&gt;

&lt;p&gt;假设有个单体形态的商品信息系统，用来给我们的电商平台提供商品信息。经过一段时间的发展和演进，这一系统的服务范围不仅包含了商品名称、类目名称等核心信息和相关逻辑，同时还包含了商品定价方面的逻辑和数据。商品核心信息和价格信息之间并没有清晰的边界。&lt;/p&gt;

&lt;p&gt;另外系统的变动频率来看，价格方面的系统变更频率要远高于核心信息部分。数据访问的模式也是各有千秋。商品的价格信息变动比核心商品属性同样要快得多。这样一来，把价格部分分拆到单体应用之外，形成一个单独的服务就是个非常有吸引力的想法了。&lt;/p&gt;

&lt;p&gt;同商品核心信息相比，价格信息更加适合分拆，这是因为在原有应用的依赖体系中，定价功能是一个叶子节点。核心商品信息被很多其他功能所依赖，例如商品库存、市场等很多功能。如果将核心信息功能分拆出来，就意味着同时要触动很多相关系统，这会产生很大的风险。因此应该在功能依赖图中选择一个有业务价值的叶子节点作为开始。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/monolith-diagram.png&#34; alt=&#34;monolith-diagram&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 1：类目系统中包含了核心信息和价格信息两部分的逻辑和数据。有两个客户端——Web 应用和 iOS app。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;代码的初始状态&#34;&gt;代码的初始状态&lt;/h3&gt;

&lt;p&gt;下面是商品系统的现有代码。很明显的，这些代码不具备真实的复杂度。然而用来演示拆分富数据服务的重构过程，其复杂度还是足够的。下面的内容中会看到每个步骤中代码的变化过程。&lt;/p&gt;

&lt;p&gt;这段代码中包含了一个 &lt;code&gt;CatalogService&lt;/code&gt; 接口，用于给客户端提供服务。它使用一个 &lt;code&gt;productRepository&lt;/code&gt; 类和数据库进行交互，用于数据的获取和存储。&lt;code&gt;Product&lt;/code&gt; 是一个（Dumb data class）哑类，包含了商品信息。哑类设计是一种反模式设计，不过这不是本文的重点。&lt;code&gt;Sku&lt;/code&gt;、&lt;code&gt;Price&lt;/code&gt; 以及 &lt;code&gt;CategoryPriceRange&lt;/code&gt; 几个类都是比较边缘的小类。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class CatalogService…

  public Sku searchProduct(String searchString) {
      return productRepository.searchProduct(searchString);
  }

  public Price getPriceFor(Sku sku) {
      Product product = productRepository.queryProduct(sku);
      return calculatePriceFor(product);
  }

  private Price calculatePriceFor(Product product) {
      if(product.isOnSale()) return product.getSalePrice();
      return product.getOriginalPrice();
  }

  public CategoryPriceRange getPriceRangeFor(Category category) {
      List&amp;lt;Product&amp;gt; products = productRepository.findProductsFor(category);
      Price maxPrice = null;
      Price minPrice = null;
      for (Product product : products) {
          if (product.isActive()) {
              Price productPrice = calculatePriceFor(product);
              if (maxPrice == null || productPrice.isGreaterThan(maxPrice)) {
                  maxPrice = productPrice;
              }
              if (minPrice == null || productPrice.isLesserThan(minPrice)) {
                  minPrice = productPrice;
              }
          }
      }
      return new CategoryPriceRange(category, minPrice, maxPrice);
  }

  public void updateIsOnSaleFor(Sku sku) {
      final Product product = productRepository.queryProduct(sku);
      product.setOnSale(true);
      productRepository.save(product);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面就开始从商品单体应用中抽取 &amp;ldquo;Product pricing&amp;rdquo; 服务的第一步。&lt;/p&gt;

&lt;h3 id=&#34;步骤-1-识别新服务涉及到的逻辑和数据&#34;&gt;步骤 1：识别新服务涉及到的逻辑和数据&lt;/h3&gt;

&lt;p&gt;第一个步骤中，需要对商品应用中的商品定价服务所包含的逻辑和数据进行识别。我们的商品应用中包含了一个 &lt;code&gt;Products&lt;/code&gt; 数据表，其中包含了 &lt;code&gt;name&lt;/code&gt;、&lt;code&gt;SKU&lt;/code&gt;、&lt;code&gt;category_name&lt;/code&gt; 以及 &lt;code&gt;is_active&lt;/code&gt; 标志（用于表示该商品是否有效或者已经弃用）等核心数据。每个商品都属于一个商品类目，类目就是一组商品。例如“男式衬衫”类目包含了花衬衫和晚礼服衬衫等商品。同时这个应用中还有一些核心的商品逻辑，例如根据名称进行商品搜索等。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Products&lt;/code&gt; 数据表还有一些定价相关的字段，例如 &lt;code&gt;original_price&lt;/code&gt;、&lt;code&gt;sale_price&lt;/code&gt; 以及 &lt;code&gt;is_on_sale&lt;/code&gt; 标志（用于标识该商品是否在售）。商品应用中包含了一些定价方面的功能，比如计算商品价格、更新 &lt;code&gt;is_on_sale&lt;/code&gt; 标志。另外还有一个纠结的功能，就是获取一个类目的价格区间，它主要属于定价范畴，但是也要涉及一些商品的核心功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step1.png&#34; alt=&#34;step 1&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 2：绿色部分是商品的核心逻辑和数据，蓝色部分是定价方面的逻辑和数据。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;接下来重新贴一下上面的代码，代码没有发生变化，核心部分加入了注释（原文用蓝绿标识，MD 格式限制，只能用注释代替）。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;  // 搜索商品
  public Sku searchProduct(String searchString) {
      return productRepository.searchProduct(searchString);
  }

  public Price getPriceFor(Sku sku) {
      Product product = productRepository.queryProduct(sku);
      return calculatePriceFor(product);
  }

  private Price calculatePriceFor(Product product) {
      if(product.isOnSale()) return product.getSalePrice();
      return product.getOriginalPrice();
  }

  public CategoryPriceRange getPriceRangeFor(Category category) {
      // 搜索指定类目中的商品
      List&amp;lt;Product&amp;gt; products = productRepository.findProductsFor(category);
      Price maxPrice = null;
      Price minPrice = null;
      for (Product product : products) {
          // 商品是否可用
          if (product.isActive()) {
              Price productPrice = calculatePriceFor(product);
              if (maxPrice == null || productPrice.isGreaterThan(maxPrice)) {
                  maxPrice = productPrice;
              }
              if (minPrice == null || productPrice.isLesserThan(minPrice)) {
                  minPrice = productPrice;
              }
          }
      }
      return new CategoryPriceRange(category, minPrice, maxPrice);
  }

  public void updateIsOnSaleFor(Sku sku) {
      final Product product = productRepository.queryProduct(sku);
      product.setOnSale(true);
      productRepository.save(product);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;步骤-2-在单体应用中对新服务进行逻辑上的拆分&#34;&gt;步骤 2：在单体应用中对新服务进行逻辑上的拆分&lt;/h3&gt;

&lt;p&gt;第二三步是关于逻辑拆分的，要在商品应用&lt;strong&gt;持续运作&lt;/strong&gt;的情况下，对商品定价服务的逻辑和数据进行逻辑上的分割。简而言之就是在创建新服务之前，首先在单体应用中，对产品的定价数据和逻辑进行隔离。这样做的好处在于，由于还在同一个代码库中，如果定错了产品定价服务所包含的逻辑或者数据边界，纠正这一错误所需的重构工作，相对于拆分为新服务之后，会比较简单一些。&lt;/p&gt;

&lt;p&gt;第二步的一部分，我们要创建两个服务类，用来封装不同对象的逻辑：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;商品核心类：&lt;code&gt;CoreProductService&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;商品定价类：&lt;code&gt;ProductPricingService&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些服务类会和我们的“物理”类一一对应，也就是后面看到的商品定价和商品核心。我们还会创建独立的存储类—— &lt;code&gt;ProductPriceRepository&lt;/code&gt; 用来访问产品定价数据，以及 &lt;code&gt;CoreProductRepository&lt;/code&gt; 用来访问核心商品数据，这两种数据目前都存储在 &lt;code&gt;Products&lt;/code&gt; 数据表中。&lt;/p&gt;

&lt;p&gt;这部分工作中，需要时刻注意的关键一点是 &lt;code&gt;ProductPricingService&lt;/code&gt; 或者 &lt;code&gt;ProductPriceRepository&lt;/code&gt; 不应该访问 &lt;code&gt;Products&lt;/code&gt; 表中的商品核心信息，而应该通过 &lt;code&gt;CoreProductService&lt;/code&gt; 类来进行所有商品核心信息相关的访问。下文中会看到对 &lt;code&gt;getPriceRangeFor&lt;/code&gt; 方法进行重构的例子。&lt;/p&gt;

&lt;p&gt;不允许存在商品核心信息和商品定价信息之间的表关联。类似的，数据库中也不该有核心商品信息和商品定价信息之间的硬约束。所有的 JOIN 和 约束都应该从数据库层转移到逻辑层。不过知易行难是个普遍规律，数据库的拆分过程中，这一点是个难度和必要性都很高的任务。&lt;/p&gt;

&lt;p&gt;不难看出，商品核心和商品定价之间是有一个共享的标识符的——两个系统中，SKU 都能能够作为商品的唯一标识。这种跨系统标识符会用在跨系统的通信过程之中，它的重要性显而易见，因此必须慎重选择。只能够有一个系统独立掌握这一数据。所有其它服务都只能对其进行只读的引用——在这些服务的视角中，标识符是固定不变的。标识符所属的实体，其生命周期的管理者，是最适合作为该标识符的属主的。例如我们的案例中，商品核心信息服务掌控着商品的生命周期，因此 SKU 标识符也应该由这一部分进行管理。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step2.png&#34; alt=&#34;Step 2&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 3：商品核心信息逻辑和商品定价逻辑的拆分，此时商品定价逻辑仍然连接在同一个商品数据表上。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;下面是重构的代码。你会看到新建了 &lt;code&gt;ProductPricingService&lt;/code&gt; 用来负责定价相关的逻辑。另外还定义了 &lt;code&gt;productPriceRepository&lt;/code&gt; 用来访问 &lt;code&gt;Products&lt;/code&gt; 表中的定价相关数据。现在的 &lt;code&gt;Product&lt;/code&gt; 数据类被分为了 &lt;code&gt;CoreProduct&lt;/code&gt; 和 &lt;code&gt;ProductPrice&lt;/code&gt; 两个类，分别用户处理商品核心信息和商品定价信息。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class ProductPricingService…

  public Price getPriceFor(Sku sku) {
      ProductPrice productPrice = productPriceRepository.getPriceFor(sku);
      return calculatePriceFor(productPrice);
  }

  private Price calculatePriceFor(ProductPrice productPrice) {
      if(productPrice.isOnSale()) return productPrice.getSalePrice();
      return productPrice.getOriginalPrice();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;获取指定类目的价格范围，这一功能相对复杂。这是因为它需要获取类目中的商品列表，而这一操作是属于商品核心部分的。&lt;code&gt;getPriceRangeFor&lt;/code&gt; 方法首先要调用 &lt;code&gt;coreProductService&lt;/code&gt; 的 &lt;code&gt;getActiveProductsFor&lt;/code&gt; 方法来获取类目中的有效商品列表。前面提到过 &lt;code&gt;is_active&lt;/code&gt; 是商品核心的属性，因此将 &lt;code&gt;isActive&lt;/code&gt; 检查也放到 &lt;code&gt;coreProductService&lt;/code&gt; 之中。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class ProductPricingService…

  public CategoryPriceRange getPriceRangeFor(Category category) {
      // 获取商品列表
      List&amp;lt;CoreProduct&amp;gt; products = coreProductService.getActiveProductsFor(category);

      // 根据商品列表获取价格
      List&amp;lt;ProductPrice&amp;gt; productPrices = productPriceRepository.getProductPricesFor(mapCoreProductToSku(products));

      Price maxPrice = null;
      Price minPrice = null;
      for (ProductPrice productPrice : productPrices) {
              Price currentProductPrice = calculatePriceFor(productPrice);
              if (maxPrice == null || currentProductPrice.isGreaterThan(maxPrice)) {
                  maxPrice = currentProductPrice;
              }
              if (minPrice == null || currentProductPrice.isLesserThan(minPrice)) {
                  minPrice = currentProductPrice;
              }
      }
      return new CategoryPriceRange(category, minPrice, maxPrice);
  }

  private List&amp;lt;Sku&amp;gt; mapCoreProductToSku(List&amp;lt;CoreProduct&amp;gt; coreProducts) {
      return coreProducts.stream().map(p -&amp;gt; p.getSku()).collect(Collectors.toList());
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;getActiveProductsFor&lt;/code&gt; 方法获取指定类目商品列表的代码大致如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class CoreProductService…

  public List&amp;lt;CoreProduct&amp;gt; getActiveProductsFor(Category category) {
      // 获取类目下的商品列表
      List&amp;lt;CoreProduct&amp;gt; productsForCategory = coreProductRepository.getProductsFor(category);
      // 只返回 is_active 的商品列表
      return filterActiveProducts(productsForCategory);
  }

  // 根据 is_active 进行过滤
  private List&amp;lt;CoreProduct&amp;gt; filterActiveProducts(List&amp;lt;CoreProduct&amp;gt; products) {
      return products.stream().filter(p -&amp;gt; p.isActive()).collect(Collectors.toList());
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在本例中，我们把 &lt;code&gt;isActive&lt;/code&gt; 的检查保留在了服务中，但是把它转移到数据库查询之中也是很容易的。实际上将功能拆分为多个服务之后，很容易发现这些将逻辑下放到查询层从而提高运行效率的机会。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;updateIsOnSale&lt;/code&gt; 功能非常直接，可以进行如下重构：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class ProductPricingService…

  public void updateIsOnSaleFor(Sku sku) {
      final ProductPrice productPrice = productPriceRepository.getPriceFor(sku);
      productPrice.setOnSale(true);
      productPriceRepository.save(productPrice);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;searchProduct&lt;/code&gt; 方法指向新建的 &lt;code&gt;coreProductRepository&lt;/code&gt;，用于商品搜索。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class CoreProductService…

  public Sku searchProduct(String searchString) {
      return coreProductRepository.searchProduct(searchString);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;原单体应用的顶层接口是 &lt;code&gt;CatalogService&lt;/code&gt;，这里也需要进行重构，对不同的功能调用，要委托给不同的服务——&lt;code&gt;CoreProductService&lt;/code&gt; 和 &lt;code&gt;ProductPricingService&lt;/code&gt;。这个过程很重要，它保障了现有的客户端和服务端之间的契约。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;searchProduct&lt;/code&gt; 方法委托给了 &lt;code&gt;CoreProductService&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class CatalogService…

  public Sku searchProduct(String searchString) {
      return coreProductService.searchProduct(searchString);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;定价相关的方法则委托给了 &lt;code&gt;productPricingService&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class CatalogService…

  public Price getPriceFor(Sku sku) {
      return productPricingService.getPriceFor(sku);
  }

  public CategoryPriceRange getPriceRangeFor(Category category) {
      return productPricingService.getPriceRangeFor(category);
  }

  public void updateIsOnSaleFor(Sku sku) {
      productPricingService.updateIsOnSaleFor(sku);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;步骤-3-为身处单体服务当中的新服务创建数据表&#34;&gt;步骤 3：为身处单体服务当中的新服务创建数据表&lt;/h3&gt;

&lt;p&gt;这个步骤中，我们要把定价相关的数据拆分到一个新的数据表中——&lt;code&gt;Productprices&lt;/code&gt;。这一步骤的最后，商品定价逻辑应该访问 &lt;code&gt;ProductPrices&lt;/code&gt; 数据表，而不再是 &lt;code&gt;Products&lt;/code&gt; 表。对任何 &lt;code&gt;Products&lt;/code&gt; 数据表中关于商品核心信息的请求，都应该从商品核心逻辑层中获取。这个步骤中，除了 &lt;code&gt;productPricingRepository&lt;/code&gt; 类之外，所有其他类，尤其是服务类的代码都不应被触及。&lt;/p&gt;

&lt;p&gt;这个步骤中要把一个数据表一分为二，因此很重要的一项工作就是 &lt;code&gt;Products&lt;/code&gt; 表到 &lt;code&gt;ProductPrices&lt;/code&gt; 表的数据迁移。我的同事 Pramod Sadalage 写了一本关于&lt;a href=&#34;https://www.martinfowler.com/books/refactoringDatabases.html&#34; target=&#34;_blank&#34;&gt;数据库重构&lt;/a&gt;的书，如果希望认真的学习这方面的知识，这本书非常值得一读。要做个快速入门，可以看看 Pramod 和 Martin Fowler 的文章：&lt;a href=&#34;https://www.martinfowler.com/articles/evodb.html&#34; target=&#34;_blank&#34;&gt;Evolutionary Database Desgin&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;在本步骤最后，可能会觉察到新服务可能会对整体系统造成一些影响，尤其是性能方面。逻辑层中的内存内数据 Join 的性能影响是显而易见的。在我们的例子中，&lt;code&gt;getPriceRangeFor&lt;/code&gt; 方法就在商品核心信息和商品定价信息之间进行了一次连接。在业务代码中完成数据连接相对于数据库来说，始终是一种更大开销的操作，这也是数据解耦的代价之一。如果在这一阶段中的性能损失非常严重，那么把数据迁回的话，情况会变得更糟糕，更不要提将服务进行物理拆分之后了。如果性能需求（以及可能存在的受这次重构影响的其他需求）无法满足，那么很可能需要重新思考一下服务边界的问题。至少在目前阶段里，Web 应用和 iOS 还都保持良好，这是因为我们没有修改任何和客户端发生交互的部分。这一步骤的另一个功能，就是进行了一次物美价廉的测试。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step3.png&#34; alt=&#34;Step 3&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 4：拆分成两块：商品核心的数据和逻辑，商品定价的数据和逻辑。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;步骤-4-创建新的服务-并且访问单体应用的原有数据库&#34;&gt;步骤 4：创建新的服务，并且访问单体应用的原有数据库&lt;/h3&gt;

&lt;p&gt;这个步骤中就要开始给商品定价的逻辑建立新的“物理”服务了，新的服务以 &lt;code&gt;ProductPricingService&lt;/code&gt; 为基础，但是数据库依旧沿用单体应用所持有的 &lt;code&gt;ProductPrice&lt;/code&gt; 数据表。注意到了这一步，&lt;code&gt;ProductPricingService&lt;/code&gt; 调用 &lt;code&gt;CoreProductService&lt;/code&gt; 就会变为网络调用了，这种变化不仅会对性能造成影响，还需要增加对超时等网络调用特有问题的处理。&lt;/p&gt;

&lt;p&gt;这也是一个业务抽象验证的好机会，这里可以看到新的商品定价服务的建模到底针对的是技术方案还是业务需求。例如当业务用户在执行 &lt;code&gt;updateIsOnSale&lt;/code&gt; 时，他的真正需要是在系统中给特定商品创建一个“促销”。下面的代码就是重构以后的 &lt;code&gt;updateIsOnSaleFor&lt;/code&gt;。我们响应业务需求，对功能进行了改进，在参数中加入了促销价格，这在以前是没有的。这还是一个将从前流落到客户端的逻辑重新归纳到服务级别的好机会。在客户端的角度来看，这明显是一个有利的变更。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;class ProductPricingService…

  public void createPromotion(Promotion promotion) {
      final ProductPrice productPrice = productPriceRepository.getPriceFor(promotion.getSku());
      productPrice.setOnSale(true);
      productPrice.setSalePrice(promotion.getPrice());
      productPriceRepository.save(productPrice);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当然这些重构并非天马行空任意施为的，其中一个重要限制就是不能修改表结构以及表数据的语义，否则可能会破坏掉单体应用中的已有功能。服务拆分大功告成之后（步骤 9），就可以对自己的数据库以及代码为所欲为了。&lt;/p&gt;

&lt;p&gt;你可能想要在进行客户端迁移之前进行这一变更，尤其是在大型组织机构中，要让大量不同的服务消费者在限定时间内进行迁移，这一过程需要消耗大量的时间和金钱。下一步中会详细讨论这一问题。新的定价服务可以安全的部署到生产环境中进行测试——反正没有客户端在使用这一服务。同样这里对客户端没有任何变更，例如本例中的 Web 应用和 iOS App 都没有受到任何影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step4.png&#34; alt=&#34;Step 4&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 5：商品定价服务分拆成了新的“物理”服务，新服务在数据方面要依赖单体应用中的 &lt;code&gt;ProductPrices&lt;/code&gt; 表，而功能上则要依赖于单体应用中的核心产品功能。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;步骤-5-让客户端使用新的服务&#34;&gt;步骤 5：让客户端使用新的服务&lt;/h3&gt;

&lt;p&gt;这个步骤里，单体服务的客户端中涉及商品定价的部分就需要转移到新的服务上了。这一阶段的工作有两个依赖项：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;新旧结构中有多少接口发生了变更。&lt;/li&gt;
&lt;li&gt;在组织视角，有多少客户端团队需要及时进行迁移。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这一步骤启动以后，整体架构可能会处于一个中间状态：新旧服务都有客户端在访问。这种状况实际上是比初始状态更加糟糕的。这就是前面讨论过的原子化的架构演进原则的必要性。在迁移开始之前，要获得组织的确认，所有新功能的相关客户端能够按时完成迁移。在架构处于半完成状态期间，是非常容易受到其他的所谓高优先级问题的干扰的。&lt;/p&gt;

&lt;p&gt;好消息是，并非所有客户端都需要同时进行迁移。然而在进行下一步之前，完成所有的客户端改造是必要的。可以设置一些服务级的监控机制来监视定价相关的方法，来识别没有完成相关变更的客户端。&lt;/p&gt;

&lt;p&gt;理论上可以在客户端完成改造之前开始一些下一步的工作，尤其是下一步中包含了创建定价数据库的操作，但是为了工作的简化，我还是建议尽量按照顺序完成步骤。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step5.png&#34; alt=&#34;Step 5&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 6：要使用定价功能的客户端已经迁移到新的定价服务。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;步骤-6-为新服务创建数据库&#34;&gt;步骤 6：为新服务创建数据库&lt;/h3&gt;

&lt;p&gt;这一步相对简单，从单体应用的数据表中进行镜像，创建新的定价数据库。这一过程中有个很大的诱惑就是：既然代码已经进行了重构，干脆也对定价数据库进行一次重构吧。但是数据结构的变化会提高后面将要进行的数据迁移过程的难度。这还意味着新的新的定价服务同时要支持两套不同的结构。我还是建议让事情简单一点：首先分离定价服务（终结所有本文中提到的步骤），然后单独对定价服务进行重构。定价数据库的隔离一旦完成，对数据库的修改就很容易了，毕竟没有客户端会直接访问数据库。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step6.png&#34; alt=&#34;Step 6&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 7：独立的定价数据库已经建立。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;步骤-7-同步数据到新数据库&#34;&gt;步骤 7：同步数据到新数据库&lt;/h3&gt;

&lt;p&gt;这一步需要从单体应用的数据库中把定价表的数据同步给新的定价数据库。如果结构没有发生变化，那么这个同步过程是非常简单明了的。基本上相当于把定价数据库设置为原有数据库的只读副本过程（仅涉及到定价相关的数据表）。这样也能保障新的定价数据库的及时性。&lt;/p&gt;

&lt;p&gt;迁移完成后，就可以准备在下个步骤中，让独立的定价服务来访问新的定价数据库了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step7.png&#34; alt=&#34;Step 7&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 8：从旧数据库中同步定价数据表给新建的定价数据库。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;步骤-8-让新服务使用新数据库&#34;&gt;步骤 8：让新服务使用新数据库&lt;/h3&gt;

&lt;p&gt;开始之前，必须要保证所有使用定价功能的客户端迁移到新的服务上去。如果没有，就面临着写入冲突的风险，这也是前面强调“唯一写拷贝”原则的理由。所有客户端都迁移到新的服务端之后，就需要将定价服务指向新的数据库了。简单说来就是把数据库连接进行一次切换。&lt;/p&gt;

&lt;p&gt;这样做的一个好处就是在出现问题的时候，还有机会轻松的迁移回到原数据库。有一种常见问题就是，新数据库中缺乏一些新服务所必须的数据表或者字段。这是步骤一中的失误所产生的后果。有可能是缺少了一些必要的引用数据，比如货币代码。成功解决这些问题之后，就可以进入下一步了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://martinfowler.com/articles/extract-data-rich-service/step8.png&#34; alt=&#34;Step 8&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 9：指向定价数据库的定价服务。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;步骤-9-从单体应用中删除新服务相关的逻辑和数据&#34;&gt;步骤 9：从单体应用中删除新服务相关的逻辑和数据&lt;/h3&gt;

&lt;p&gt;这里就要从原有应用中删除定价功能相关的逻辑和数据库了。很多团队会在数据库中留着旧数据，仅仅是因为担心“万一有用呢？”。进行一次全库备份可能有助于缓解这种恐惧。&lt;/p&gt;

&lt;p&gt;现在 &lt;code&gt;CatalogService&lt;/code&gt; 的所有功能都委托给了对 &lt;code&gt;CoreProductService&lt;/code&gt; 服务的调用，顺理成章地，我们就可以移除这一中间层，让客户直接调用 &lt;code&gt;CoreProductService&lt;/code&gt; 服务了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/step9.png&#34; alt=&#34;Step 9&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 10：商品核心只有商品核心的相关逻辑和数据；商品定价服务持有定价的逻辑和数据，二者仅在逻辑层面进行交互。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;

&lt;p&gt;大功告成！我们成功的把一个富数据的服务从单体应用中解放了出来。&lt;/p&gt;

&lt;p&gt;第一次进行这项工作时，会有很多痛苦，也会受到很多教训，这都会让你的下一次拆分更加顺利。初次拆分过程中，不管面对多大诱惑，都最好不要尝试合并这些步骤。一次只进行一步，让整个工作流程更少悬念，更多的安全感和可预测性。在成功掌握这一模式之后，就可以根据自身所学对这些步骤进行优化了。&lt;/p&gt;

&lt;p&gt;祝你好运！&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>服务网格——模式还是技术？</title>
      <link>/post/service-mesh-pattern-not-tech/</link>
      <pubDate>Thu, 16 Aug 2018 10:27:43 +0800</pubDate>
      <guid>/post/service-mesh-pattern-not-tech/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://konghq.com/blog/service-mesh-new-pattern-not-new-technology&#34; target=&#34;_blank&#34;&gt;Service Mesh – A New Pattern, Not A New Technology?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原作者：&lt;a href=&#34;https://konghq.com/blog/author/marco/&#34; target=&#34;_blank&#34;&gt;Marco Palladino&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;service-mesh-是什么-从何而来&#34;&gt;Service Mesh 是什么？从何而来？&lt;/h2&gt;

&lt;p&gt;近几个月，你会注意到围绕服务网格以及未来软件架构的业内讨论如火如荼。这些讨论围绕着几个供应商，呈现出一种部落战争的态势。这种党争虽然司空见惯，但是其中一些共同话题还是很有意义的——例如企业中 API 应用的快速转型，以及服务网格对流量拓扑的意义。&lt;/p&gt;

&lt;p&gt;服务 API 最初用于在组织边界提供访问内部系统的接口，供外部开发人员访问；这种情况并未持续很久，很快，服务 API 就变成将内部系统连接在一起的粘合剂。而微服务架构的发展，带来了一个无法回避的后果：数据中心内的内部通信持续增长。服务网格适时出现，提供了一种面向现有技术的部署框架，可能成为应对东西向流量增长问题的一个解决方案。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/diagram-04-1.png&#34; alt=&#34;img&#34; /&gt;&lt;/p&gt;

&lt;p&gt;作为 Kong 的 CTO，我也很热衷于参加这些对话，我注意到对服务网格的认知有一个普遍误解。为了消除误会，升级对话，首先我想要明确提出：服务网格是一种模式，而非技术。&lt;/p&gt;

&lt;h2 id=&#34;服务网格是一种模式-而非技术&#34;&gt;服务网格是一种模式，而非技术&lt;/h2&gt;

&lt;p&gt;微服务是模式而非技术，服务网格也是一样。这两种概念的区别貌似很难理解。如果我们从 OOP 的角度来看，模式描述的是接口，不是实现。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/diagram-01.png&#34; alt=&#34;img2&#34; /&gt;&lt;/p&gt;

&lt;p&gt;服务网格这样的部署模式能够利用 Sidecar 增强东西向的流量管理，能给微服务提供强大支援。既然已经对单体应用进行了解耦，并用微服务构建了新的应用，我们的流量模型中，内部流量就会与日俱增。数据中心内，东西向的流量增长原因是我们将单体应用中的函数调用换成了网络调用，这意味着我们的微服务必须融入网络，互相消费。然而地球人都知道——网络靠不住。&lt;/p&gt;

&lt;p&gt;面对日益增长的东西向流量，服务网格通过新的部署架构进行应对。传统的南北向通信中，100 毫秒的延迟虽不够理想，但也在可接受范围之内；但在东西向通信中，这就无法忍受了。这是因为服务间的相互调用会使延迟倍增，跨越多个服务的 API 调用和返回，会造成延迟时间的大幅增加。&lt;/p&gt;

&lt;p&gt;为了降低延迟，引入了独立运行的 Sidecar，这种代理服务器会伴随微服务进程一起运行，用来移除多余的网络跳跃。Sidecar 在请求的执行路径上担任数据平面的角色，并且避免了单点失败，从而提供了更好的应用弹性。然而，每个微服务进程都配合一个代理进程，势必会造成资源的损耗，但同时他也降低了资源耗尽的肯能性。&lt;/p&gt;

&lt;p&gt;经过细致观察不难发现，服务网格中的很多功能，都已经在多年前的 API 管理产品中实现了。比如可观测、网络故障处理和健康检查等功能都是 API 管理的基本配置。这些特性并不新鲜，但服务网格作为一种新的模式，用新思路来完成了这些功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/diagram-02.png&#34; alt=&#34;img&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;传统-api-管理方案瞠目其后&#34;&gt;传统 API 管理方案瞠目其后&lt;/h2&gt;

&lt;p&gt;微服务和容器大潮迫使人们更多的关注轻量级进程，服务网格提供的轻量级进程，同时承担了代理和反向代理的角色，伴随微服务进程一同运行。为什么传统的 API 管理方案不采用这种新的部署方案？这是因为他们生于单体时代。这些早于 Docker 和 Kubernetes 面世的 API 管理系统本身就是单体应用，并不适用于新兴的容器生态圈。传统 API 管理方案往往具有重量级的运行时和低下的性能，这些问题在过去的边缘 API 用例中尚可承受，但是对于微服务体系来说，东西向调用量越多，中间环节的延迟就越大。究其根本，传统 API 管理方案的重量大、难度高以及速度慢的缺点是难以满足微服务世界的要求的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/diagram-05.png&#34; alt=&#34;img&#34; /&gt;&lt;/p&gt;

&lt;p&gt;开发人员了解了这些，传统的 API 管理方案引入了称之为 &amp;ldquo;microgateway&amp;rdquo;（微型网关）的技术，用来处理东西向的流量，并且避免重写现存的臃肿的单体网关方案。问题是这些所谓的微型网关虽然自身变轻了，但还是要依赖传统方案伴行，用于提供策略实施等能力。这不仅是留下陈旧组件的问题，还意味着增加了延迟。如此种种，服务网格就令人耳目一新了——对手太旧了。&lt;/p&gt;

&lt;h2 id=&#34;结语&#34;&gt;结语&lt;/h2&gt;

&lt;p&gt;传统的 API 管理方案在南北向流量管理中耕耘多年，服务网格的功能相对来说并不新鲜。多数网络方面以及观测方面的能力对东西向和南北向的流量都是通用的，服务网格让我们有机会运行轻量快速的 Sidecar 代理来完成这些通用功能，改变的是部署模式，不是底层功能。&lt;/p&gt;

&lt;p&gt;传统 API 管理方案的功能是服务网格功能的超级，某种意义上来说，普及了可靠性、服务发现和观测能力。服务网格是一种部署模式，用轻量级的方式获得同样的功能。业界经常混淆——有时还加重了这种混淆——很多服务网格方面的讨论，都混淆了特定部署模板和底层技术的界限。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Service Mesh 安全：用 Istio 应对攻击</title>
      <link>/post/service-mesh-security-addressing-attack-vectors-with-istio/</link>
      <pubDate>Thu, 07 Jun 2018 10:10:21 +0800</pubDate>
      <guid>/post/service-mesh-security-addressing-attack-vectors-with-istio/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://aspenmesh.io/2018/06/service-mesh-security-addressing-attack-vectors-with-istio/&#34; target=&#34;_blank&#34;&gt;Service Mesh Security: Addressing Attack Vectors with Istio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者： &lt;strong&gt;Zach Jory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;把单体应用拆分为微服务之后，会得到不少好处，例如稳定性的提高、持续运行时间的增长以及更好的故障隔离等。然而把大应用拆分为小服务的过程中，也会引入一个风险就是——可能的受攻击面积变大了。从前单体应用中通过函数调用完成的通信，现在都要通过网络完成。提高安全性从而避免这个问题带来的安全影响，是微服务之路上必须要着重考虑的问题。&lt;/p&gt;

&lt;p&gt;Aspen Mesh 的基础是一个开源软件：&lt;a href=&#34;https://istio.io/&#34; target=&#34;_blank&#34;&gt;Istio&lt;/a&gt;，他的关键能力之一就是为微服务提供安全性和策略控制方面的支持。Istio 为 Service Mesh 增加了很多安全特性，但是这并不是说微服务的安全工作就结束了。网络安全策略也是需要着重考虑的问题（推荐阅读：&lt;a href=&#34;https://medium.com/lightspeed-venture-partners/in-the-land-of-microservices-the-network-is-the-king-maker-37de7ec4119a&#34; target=&#34;_blank&#34;&gt;In the land of microservices, the network is the king(maker)&lt;/a&gt;），结合网络策略，可以检测和应对针对服务网格基础设施的攻击，从而解决各种安全威胁。&lt;/p&gt;

&lt;p&gt;后面的内容将会看看 Istio 所能够解决的问题，其中包含边缘通信的流量控制、网格内通信加密以及 7 层策略控制等。&lt;/p&gt;

&lt;h2 id=&#34;边缘通信安全&#34;&gt;边缘通信安全&lt;/h2&gt;

&lt;p&gt;针对不当进入网格的流量，Istio 加入了一个用来进行监控和防范的安全层。Istio 以 Ingress Controller 的形式和 Kubernetes 进行了集成，并完成了 Ingress 的负载均衡任务。用户可以用 Ingress Rule 的方式加入安全控制。可以通过监控来了解进入网格的流量，并通过路由规则来管理非法的边缘通信。&lt;/p&gt;

&lt;p&gt;要保证只有认证用户通过，Istio 的 RBAC（基于角色的访问控制）提供了有弹性的、可定制的访问控制，这种能力在网格内提供了 namespace、service 以及服务方法一级的控制能力。RBAC 引擎监控和跟进 RBAC 策略的变更，在运行时根据 RBAC 策略，根据请求的上下文对请求进行鉴权，最后返回鉴权结果。&lt;/p&gt;

&lt;h2 id=&#34;通信加密&#34;&gt;通信加密&lt;/h2&gt;

&lt;p&gt;边缘通信的安全是个好的开始，但是如果有恶意份子突破了边缘之后，Istio 还为服务之间的通信提供了双向 TLS 认证能力。网格能够对请求和响应进行自动的加密和解密，开发人员就无需在此投入精力了。这个功能还通过对连接的优先复用，减少了连接过程中的运算消耗。&lt;/p&gt;

&lt;p&gt;除了客户端和服务器之间的认证和鉴权能力之外，还让用户能够理解和管理服务间的通信和加密。Istio 把证书和密钥自动分发给服务，代理使用这些输入来给流量进行加密（提供双向 TLS），并周期性的进行证书轮转，从而降低证书暴露造成的威胁。可以利用 TLS 来确认 Istio 中的通信双方的服务实例都是合法的，从而防止中间人攻击。&lt;/p&gt;

&lt;p&gt;Istio 使用 Citadel 来进行密钥管理和认证控制，简化了 TLS 过程。他让用户能够保护流量，同时给每个服务提供基于身份的验证和授权功能。&lt;/p&gt;

&lt;h2 id=&#34;策略控制和执行&#34;&gt;策略控制和执行&lt;/h2&gt;

&lt;p&gt;Istio 给用户在应用级执行策略的能力。对于服务路由、重试、断路以及安全来说，在这一层进行控制是非常恰当的。Istio 为用户提供了黑白名单功能来来对服务进行准入的控制。&lt;/p&gt;

&lt;p&gt;Istio Mixer 可以把扩展集成进系统，用户用标准化的表达式语言来来声明网络以及服务行为方面的约束策略。这样做的好处是，可以用通用 API 在服务边缘来缓存策略的决策结果，如果下游的策略系统出现故障，网络还能保持运行。&lt;/p&gt;

&lt;p&gt;Istio 解决了一些微服务特定的关键问题。例如只允许被批准的服务间通信，加密通信防止通信过程中的入侵，执行应用范围内的策略等。当然还有很多其他方式可以实现这些能力，Mesh 的好处在于将这些能力融会贯通，让用户使用一致的稳定的方式来完成这些任务。&lt;/p&gt;

&lt;p&gt;Aspen Mesh 中正在做一些新的功能，在 Istio 中为用户提供更好的安全能力。近期我们会在博客上发点东西，所以请关注 &lt;a href=&#34;https://aspenmesh.io/blog/&#34; target=&#34;_blank&#34;&gt;Aspen Mesh 博客&lt;/a&gt;。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>如何在企业中尝试无服务器技术</title>
      <link>/post/serverless-101-how-to-get-serverless-started-in-the-enterprise/</link>
      <pubDate>Thu, 07 Jun 2018 02:06:09 +0800</pubDate>
      <guid>/post/serverless-101-how-to-get-serverless-started-in-the-enterprise/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://thenewstack.io/serverless-101-how-to-get-serverless-started-in-the-enterprise/&#34; target=&#34;_blank&#34;&gt;Serverless 101: How to Get Serverless Started in the Enterprise&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://thenewstack.io/author/michelle_gienow/&#34; target=&#34;_blank&#34;&gt;Michelle Gienow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;起初，世上有了&lt;a href=&#34;https://en.wikipedia.org/wiki/Bare-metal_server&#34; target=&#34;_blank&#34;&gt;裸机&lt;/a&gt;，这很好。&lt;/p&gt;

&lt;p&gt;独占的服务器很快、很可靠，也很安全——毕竟是独占。但是平心而论，这时候的配置和扩展非常麻烦。对于敏捷和弹性的渴望，催生了&lt;a href=&#34;https://en.wikipedia.org/wiki/Virtual_machine&#34; target=&#34;_blank&#34;&gt;虚拟机&lt;/a&gt;，云供应商们适时推出了&lt;a href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_a_service&#34; target=&#34;_blank&#34;&gt;IaaS（基础设施即服务）&lt;/a&gt;，云端+自助的模式也就自然而然的随之面世了。IaaS 的沃土中，产生了 &lt;a href=&#34;https://aws.amazon.com/&#34; target=&#34;_blank&#34;&gt;Amazon Web Services&lt;/a&gt;、编排技术、以及&lt;a href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_Code&#34; target=&#34;_blank&#34;&gt;基础设置即代码 (IaC: infrastructure as code)&lt;/a&gt;；容器化初露端倪、&lt;a href=&#34;https://en.wikipedia.org/wiki/Platform_as_a_service&#34; target=&#34;_blank&#34;&gt;PaaS（平台即服务）&lt;/a&gt;也应运而生。当然了，一切都很好，更好了。只不过，开发者们大声疾呼，他们需要语言无关的服务端点、水平伸缩以及实时的服务计费。&lt;/p&gt;

&lt;p&gt;祈祷有时候是有效的，这世界得到了一份大礼：无服务器计算，也被称为 &lt;a href=&#34;https://en.wikipedia.org/wiki/Function_as_a_service&#34; target=&#34;_blank&#34;&gt;Function as a Service (FaaS)&lt;/a&gt;。运行时部分只做执行，不保存数据。换个说法就是，AWS、&lt;a href=&#34;https://cloud.google.com/&#34; target=&#34;_blank&#34;&gt;Google Cloud&lt;/a&gt; 或者 &lt;a href=&#34;https://azure.microsoft.com/en-us/&#34; target=&#34;_blank&#34;&gt;Microsoft Azure&lt;/a&gt; 负责动态的管理资源的分配和分布。&lt;/p&gt;

&lt;p&gt;从前使用的基于预测的预付费服务方式已经不适合无服务器计算，取而代之的是根据实际用量进行的即时计费方案。2018 年开始，我们开始将其视为基础设施。&lt;/p&gt;

&lt;h2 id=&#34;并非魔法&#34;&gt;并非魔法&lt;/h2&gt;

&lt;p&gt;首先，这个名字很有误导性：无服务器计算不是魔法，并不是由神秘的月光驱动的——服务器还是需要的。&lt;/p&gt;

&lt;p&gt;这个说法大行其道的原因是，这一架构隐藏了服务器管理以及容量规划和决策等工作。无服务器计算中所谓的“无服务器”，是针对用户和开发者来说的，对他们来说，再也不用花力气、甚至不用知道基础设施了——服务器已经成为一个抽象概念了。无服务器架构中的代码可以和微服务这样的传统的代码部署一起使用——或者可以完全使用无服务器架构来构建应用，就完全不需要关注服务器的问题了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;无服务器架构的真正价值在于时间效率&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&#34;https://bitnami.com/&#34; target=&#34;_blank&#34;&gt;Bitnami&lt;/a&gt; 的云计算高级总监 &lt;a href=&#34;https://ch.linkedin.com/in/sebastien-goasguen-382b7b21&#34; target=&#34;_blank&#34;&gt;Sebastien Goasguen&lt;/a&gt; 说：“我认为这是一种最小化的 PaaS，一种类似胶水的软件”。“无服务器架构中的关键动作就是——从云中发生了事件，触发了函数的执行。”，Goasguen 描述了 AWS 中的一个场景：把图片保存到存储设施中，接下来调用一个函数来进行图片的缩放。无服务器系统中会处理把这些代码自动的注入到运行时环境中（服务器或者容器），然后进行公开，让函数可以被外部调用。&lt;/p&gt;

&lt;h2 id=&#34;不是魔法是什么&#34;&gt;不是魔法是什么？&lt;/h2&gt;

&lt;p&gt;传统云计算和无服务器计算的主要区别来自于客户：没人愿意为自己没使用的资源买单。从前我们需要对容量和资源需求进行预测，然后根据预测来采购基础设施——可能是自有也可能是公有云。我们前面的例子中，就是要在 AWS 启动服务器，令其保持就绪，这样才能随时使用这一服务来完成图片的缩放任务——只有在这时候才真正的调用了这一函数。&lt;/p&gt;

&lt;p&gt;无服务器计算服务把你的函数作为输入，执行其中的逻辑，然后返回函数的输出，最后关掉。用户只会收到函数执行期间的消费账单。&lt;/p&gt;

&lt;p&gt;随用随付，只为实际消费买单，这是个伟大创新。然而 Goasguen 和其他一些 Cloud Native 专家强调，无服务器技术的真正价值不在于成本效率，而在于时间效率 。&lt;/p&gt;

&lt;h2 id=&#34;好像时光机&#34;&gt;好像时光机？&lt;/h2&gt;

&lt;p&gt;是的有点像。或者更像一个通向未来的 Portal，无服务器技术让客户专注的用各种 AI、机器学习等神奇的新技术构建应用，而不是为了跟随时代步伐，永远的建设和翻新基础设施。&lt;/p&gt;

&lt;p&gt;无服务器技术的另外一个时光机的特性就是缩短从代码开发到生产运行的时间。简单到了一句话：“这是代码，拿去运行吧”，几乎没有基础设施方面的拖拖拉拉。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.oracle.com/index.html&#34; target=&#34;_blank&#34;&gt;Oracle&lt;/a&gt; 的无服务器技术副总裁 &lt;a href=&#34;https://www.linkedin.com/in/chadarimura&#34; target=&#34;_blank&#34;&gt;Chad Arimura&lt;/a&gt; 说：“基本思路，开发人员把编写好的代码推送给无服务器服务提供商，其余的部分交给服务商完成。”，另外他还补充说，常见的依赖内容，例如服务器和存储，也会在无服务器计算的环境中提供无缝支持。&lt;/p&gt;

&lt;p&gt;“幕后有专家团队和大量的自动化工具来运维这些系统，从而把开发者从这些问题中解脱出来”，Arimura 接下来说，“无服务器的魔术一般的体验，让这一概念的热度持续上升。“&lt;/p&gt;

&lt;h2 id=&#34;感受-faas-平台的力量&#34;&gt;感受 FaaS 平台的力量&lt;/h2&gt;

&lt;p&gt;Docker 简化了分布式应用的打包和依赖管理，Kubernetes 让企业能够在生产环境中运行这些应用，但是他们的易用性还是不够。Bitnami 的 Goasguen 说：”Dockerfile、基础设施的细节、Kubernetes 的清单——这些对开发思维的受众来说还是稍显复杂的。“&lt;/p&gt;

&lt;p&gt;无服务器计算的核心，就是 FaaS 平台。具体点说就是 &lt;a href=&#34;https://aws.amazon.com/lambda/&#34; target=&#34;_blank&#34;&gt;AWS Lambda&lt;/a&gt; 或者 &lt;a href=&#34;https://cloud.google.com/functions/&#34; target=&#34;_blank&#34;&gt;Google Cloud Function&lt;/a&gt;，所做的事情：资源管理、负载均衡以及多线程处理；而开发者只需要为雇主专注完成代码即可。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.iguazio.com/&#34; target=&#34;_blank&#34;&gt;iguaz.io&lt;/a&gt;  是一个致力于优化云端性能的持续数据平台，其创始人和 CTO &lt;a href=&#34;https://il.linkedin.com/in/yaronh&#34; target=&#34;_blank&#34;&gt;Yaron Haviv&lt;/a&gt; 提出：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;无服务器平台接收函数代码（FaaS 中的 &amp;ldquo;F&amp;rdquo;），为这些代码提供所有的依赖支持（库、内存、配置等），然后构建一个容器化的应用包（通常是 Docker 格式）。&lt;/li&gt;
&lt;li&gt;函数的触发方，可以是其他的平台服务，例如对象存储或者数据库；也可以是一个外部的 HTTP 请求。无服务器平台会把请求转发给一个可用的函数微服务上去。如果没有可用的实例，就部署一个——也就是冷启动。&lt;/li&gt;
&lt;li&gt;无服务器平台需要处理好微服务的故障恢复、按需伸缩、日志和监控，并在代码变更后执行滚动更新。开发者除了函数本身，所有其他问题都无需关注。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;无服务器架构的不足&#34;&gt;无服务器架构的不足&lt;/h2&gt;

&lt;p&gt;无服务器并非银弹，还是有一些不足的。有专家说，云服务商经常会缩减一些使用率不高的环境的资源，他们还会限制用户的资源总量，这样就产生一个矛盾——高性能和高延迟同时出现。另外云服务商的监控、排错和安全都会有自己的思路，可能让用户觉得很古怪。这些问题的根源就是：公有云是不受客户控制的。&lt;/p&gt;

&lt;p&gt;Amazon Lambda 跟无服务器计算是同时进步的，提供了一个多数人都可以接受的模型。 因为 Lambda 是无服务器技术的开创者，所以他的不足也自然而然的被视作无服务器技术的不足：缓慢的冷启动、低性能、短命的函数以及千篇一律的触发器。目前认为这些限制是所有无服务器平台的通病，然而实际上，这是实现过程的一些选择问题。我们应该注意一些新的无服务器平台，例如 &lt;a href=&#34;https://nuclio.io/&#34; target=&#34;_blank&#34;&gt;Nuclio&lt;/a&gt;，他提供了更丰富的用例。这些平台的限制更少，提供更好的性能，同时能够运行在多种云甚至是私有云上。&lt;/p&gt;

&lt;p&gt;”极客们喜欢这个，但是企业还在试水——他们连 Docker/K8S 还没用上呢，就来到无服务器时代了，“，Haviv 说。”和任何新技术一样，我们需要首先争取到早期试用者——他们通常更敏捷、更能承受风险，然后才能接近大众。而要获得早期试用者，需要建立信任、看到证据、解决性能、安全等方面的疑难等等。“&lt;/p&gt;

&lt;p&gt;显然无服务器技术正在日新月异的向前发展，肯定无法十全十美。虽然下面的话不一定是什么缺点，但却是能让董事们无法安坐在 Aeron 椅子的一个问题：”数字化转型总在被新技术打断，因此企业必须更加敏捷和并正视风险，“Haviv 指出。&lt;/p&gt;

&lt;p&gt;”有趣之处在于，Docker/K8S 的抽象是非常复杂的，而无服务器技术虽然更加新潮，却更加简单、更易接受。“&lt;/p&gt;

&lt;h2 id=&#34;我的公司是否适合无服务器技术&#34;&gt;我的公司是否适合无服务器技术？&lt;/h2&gt;

&lt;p&gt;暂时不提企业适合无服务器技术是好是坏，我们先来看看领导厂商。&lt;/p&gt;

&lt;p&gt;Oracle 的 Arimura 认为：”字面上来说，所有组织和公司，只要需要编写软件的，就都适合使用无服务器技术“，”目前的文化和云原生之间的距离越远，就越难应用无服务器技术“，换句话说，如果一个公司没有使用公有云、也没有在内部试用 Kubernetes 或者 Docker 这样的新技术，那么无服务器可能就不是一个合适的尝试。&lt;/p&gt;

&lt;p&gt;”这是一个新架构，需要不同的思维方式。最简单的例子：如果一个单体应用拆分为十个微服务，再分为一百个函数，这些单元都具有自己的发布周期和复杂的依赖关系。这很明显需要成熟稳定的持续构建、交付以及自动化系统的支撑。“，Arimura 说，”敏捷创新是无服务器技术的必要支撑，否则造成的害处可能要多余带来的益处。“&lt;/p&gt;

&lt;p&gt;”DevOps 的目标不是 NoOps，这完全是个错误思路，无服务器技术更加需要 DevOps 的支持。“&lt;/p&gt;

&lt;p&gt;来自 Bitnami 的 Goasguen 补充，采用无服务器技术（尤其是 AWS Lambda）的大多数公司都是以开发者为中心的，他们在此之前已经在应用 AWS，现在使用无服务器计算把服务连接在一起。所以机会就是，如果现在还没有使用 AWS，那么你不需要无服务器技术，然而你仍然应该保持跟踪，开始评估，识别企业中可能存在的事件源，看看如何使用这些事件构建完整的应用管线。&lt;/p&gt;

&lt;h2 id=&#34;企业如何试水无服务器技术&#34;&gt;企业如何试水无服务器技术？&lt;/h2&gt;

&lt;p&gt;”不要把一个单体应用整体转化为微服务或者函数“，Arimura 建议，”使用公司的最重要项目来学习新架构是不明智的，尤其是在公司的文化还在 DevOps 的尝试阶段。“&lt;/p&gt;

&lt;p&gt;从小处入手，例如一点自动化任务，或者一些市场活动以及一些事件驱动的用例等。&lt;/p&gt;

&lt;p&gt;New Stack 的无服务器技术系列，将协助你的公司探索无服务器的新领域。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Conduit 0.4.2 发布</title>
      <link>/post/conduit-0.4.2-release/</link>
      <pubDate>Tue, 05 Jun 2018 10:04:16 +0800</pubDate>
      <guid>/post/conduit-0.4.2-release/</guid>
      <description>

&lt;p&gt;Conduit 0.4.2 是生产就绪之路上的重要一步。这个版本为代理服务器的长期运行做出了很多修复和提高，并提供了更多的遥测功能。同时也为未来的双向 TLS 版本打好了基础。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;生产就绪

&lt;ul&gt;
&lt;li&gt;代理服务器会丢弃 10 分钟未更新的指标，以防长期运行过程中造成的额外的内存增长。&lt;/li&gt;
&lt;li&gt;新增了一个约束，限制一个节点能够路由的服务数量，缺省为 100。这样在代理服务器长期运行的时候，可以在容量到达上限的时候，丢弃闲置最久的客户端，从而达到节约资源的目的。&lt;/li&gt;
&lt;li&gt;代理服务器现在能够正确的处理 HTTP/2 请求的取消。&lt;/li&gt;
&lt;li&gt;修复连接错误时候的请求处理。&lt;/li&gt;
&lt;li&gt;正确处理 DNS 的 TTL。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conduit inject&lt;/code&gt; 现在可以处理 &lt;code&gt;statefulset&lt;/code&gt; 对象了。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;遥测

&lt;ul&gt;
&lt;li&gt;新版本的 &lt;code&gt;conduit stat&lt;/code&gt; 现在支持所有的 Kubernetes 资源，能够展示一个命名空间内所有对象的流量统计。&lt;/li&gt;
&lt;li&gt;Conduit Web UI 提供了命名空间概述的展示。&lt;/li&gt;
&lt;li&gt;修复了 Tap 功能的一个 Bug，这个 Bug 让代理服务器无法同时响应多个 Tap 请求。&lt;/li&gt;
&lt;li&gt;修复了故障状态下，无法报告某些 TCP 流的问题。&lt;/li&gt;
&lt;li&gt;加入了首字节响应时间的指标。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;内部

&lt;ul&gt;
&lt;li&gt;环境配置加入了对友好的时间格式的支持（例如 10s）。&lt;/li&gt;
&lt;li&gt;控制面使用 Kubernetes 1.10.2 客户端。&lt;/li&gt;
&lt;li&gt;更丰富的 Debug 日志，包含了 Socket 和 Stream 的元数据。&lt;/li&gt;
&lt;li&gt;为 TLS 支持对代理做出大量改进。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;非常感谢 &lt;a href=&#34;https://github.com/carllhw&#34; target=&#34;_blank&#34;&gt;@carllhw&lt;/a&gt;、&lt;a href=&#34;https://github.com/kichristensen&#34; target=&#34;_blank&#34;&gt;@kichristensen&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/sfroment&#34; target=&#34;_blank&#34;&gt;@sfroment&lt;/a&gt; 做出的贡献。&lt;/p&gt;

&lt;h2 id=&#34;从-0-4-1-升级&#34;&gt;从 0.4.1 升级&lt;/h2&gt;

&lt;p&gt;从 0.4.1 升级时，建议首先升级控制面，然后再升级注入。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Istio 0.8 的 Helm Chart 解析</title>
      <link>/post/istio-0.8.0-helm/</link>
      <pubDate>Mon, 04 Jun 2018 09:39:10 +0800</pubDate>
      <guid>/post/istio-0.8.0-helm/</guid>
      <description>

&lt;p&gt;儿童节期间，拖拉了一个多月的 Istio 0.8 正式发布了，这可能是 Istio 1.0 之前的最后一个 LTS 版本，意义重大。&lt;/p&gt;

&lt;p&gt;新版本中，原来的 Kubernetes 安装文件 &lt;code&gt;install/kubernetes/istio.yaml&lt;/code&gt;，变成了 &lt;code&gt;install/kubernetes/istio-demo.yaml&lt;/code&gt;，是的，你没看错，这个 LTS 的安装文件名字叫 demo。查看了一下文档，大概察觉到不靠谱的 Istio 发布组的意图了：这个项目的组件相对比较复杂，原有的一些选项是靠 ConfigMap 以及 istioctl 分别调整的，现在通过重新设计的 Helm Chart，安装选项用 values.yml 或者 helm 命令行的方式来进行集中管理了。下面就由看看 Istio 的 Helm Chart 的安装部署及其结构。&lt;/p&gt;

&lt;h2 id=&#34;使用-helm-安装-istio&#34;&gt;使用 Helm 安装 Istio&lt;/h2&gt;

&lt;p&gt;安装包内的 Helm 目录中包含了 Istio 的 Chart，官方提供了两种方法：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;用 Helm 生成 istio.yaml，然后自行安装。&lt;/li&gt;
&lt;li&gt;用 Tiller 直接安装。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;很明显，两种方法并没有什么本质区别。例如第一个命令：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;helm template install/kubernetes/helm/istio \
    --name istio --namespace  \
    istio-system &amp;gt; $HOME/istio.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里说的是使用 &lt;code&gt;install/kubernetes/helm/istio&lt;/code&gt; 目录中的 Chart 进行渲染，生成的内容保存到 &lt;code&gt;$HOME/istio.yaml&lt;/code&gt; 文件之中。而第二个命令&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;helm template install/kubernetes/helm/istio \
    --name istio --namespace istio-system \
    --set sidecarInjectorWebhook.enabled=false &amp;gt; $HOME/istio.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;只是设置了 Chart 中的一个变量 &lt;code&gt;sidecarInjectorWebhook.enabled&lt;/code&gt; 为 False。从而禁止自动注入属性生效。&lt;/p&gt;

&lt;p&gt;我们照猫画虎，看看命令二的结果提交到 Kubernetes 中会发生什么事情。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;helm template install/kubernetes/helm/istio --name istio \
--namespace istio-system --set sidecarInjectorWebhook.enabled=false &amp;gt; $HOME/istio.yaml

kubectl create namespace istio-system
kubectl create -f $HOME/istio.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;根据不同的网络情况，可能需要几分钟的等待，最后会看到这些 Pod 在运行：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;istio-citadel-ff5696f6f-h4rdz
istio-cleanup-old-ca-rp5p6
istio-egressgateway-58d98d898c-5jnpz
istio-ingress-6fb78f687f-wsl5q
istio-ingressgateway-6bc7c7c4bc-hhrh7
istio-mixer-post-install-d2kl5
istio-pilot-6c5c6b586c-dqv2w
istio-policy-5c7fbb4b9f-xmv6f
istio-statsd-prom-bridge-6dbb7dcc7f-27tx7
istio-telemetry-54b5bf4847-9gpr7
prometheus-586d95b8d9-gb846
&lt;/code&gt;&lt;/pre&gt;

&lt;ol&gt;
&lt;li&gt;过去的 istio-ca 现已更名 istio-citadel。&lt;/li&gt;
&lt;li&gt;istio-cleanup-old-ca 是一个 job，用于清理过去的 Istio 遗留下来的 CA 部署（包括 sa、deploy 以及 svc 三个对象）。&lt;/li&gt;
&lt;li&gt;istio-mixer-post-install 同样也是一个 job，和上面的 Job 一样，简单的调用 kubectl 创建第三方资源，从而避免了之前的 CDR 需要重复创建的尴尬状况。&lt;/li&gt;
&lt;li&gt;egressgateway、ingress 以及 ingressgateway，可以看出边缘部分的变动很大，以后会另行发文。&lt;/li&gt;
&lt;li&gt;和从前不同，缺省已经打开了监控界面。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;helm-chart-的安装配置&#34;&gt;Helm Chart 的安装配置&lt;/h3&gt;

&lt;p&gt;下面的配置项目，都可以使用 helm 的 &lt;code&gt;--set key=value&lt;/code&gt; 来设置，可以重复使用，用来设置多个值。&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;选项&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;缺省值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;global.hub&lt;/td&gt;
&lt;td&gt;绝大部分镜像所在的镜像库地址&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker.io/istionightly&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.tag&lt;/td&gt;
&lt;td&gt;Istio 使用的绝大部分镜像的 Tag&lt;/td&gt;
&lt;td&gt;&lt;code&gt;circleci-nightly&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.proxy.image&lt;/td&gt;
&lt;td&gt;指定 Proxy 的镜像名称&lt;/td&gt;
&lt;td&gt;&lt;code&gt;proxyv2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.imagePullPolicy&lt;/td&gt;
&lt;td&gt;镜像拉取策略&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IfNotPresent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.controlPlaneSecurityEnabled&lt;/td&gt;
&lt;td&gt;控制面是否启动 mTLS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.mtls.enabled&lt;/td&gt;
&lt;td&gt;服务间是否缺省启用 mTLS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.mtls.mtlsExcludedServices&lt;/td&gt;
&lt;td&gt;禁用 mTLS 的 FQDN 列表&lt;/td&gt;
&lt;td&gt;&lt;code&gt;- kubernetes.default.svc.cluster.local&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.rbacEnabled&lt;/td&gt;
&lt;td&gt;是否创建 RBAC 规则&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.refreshInterval&lt;/td&gt;
&lt;td&gt;Mesh 发现间隔&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.arch.amd64&lt;/td&gt;
&lt;td&gt;amd64 架构中的调度策略，0：never；1: least preferred；2：no preference；3：most preferred&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.arch.s390x&lt;/td&gt;
&lt;td&gt;amd64 架构中的调度策略，0：never；1: least preferred；2：no preference；3：most preferred&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;global.arch.ppc64le&lt;/td&gt;
&lt;td&gt;amd64 架构中的调度策略，0：never；1: least preferred；2：no preference；3：most preferred&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;galley.enabled&lt;/td&gt;
&lt;td&gt;是否安装 Galley 用于进行服务端的配置验证，需要 1.9 版本以上的 Kubernetes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;上面的内容来自&lt;a href=&#34;https://istio.io/docs/setup/kubernetes/helm-install/&#34; target=&#34;_blank&#34;&gt;官方文档&lt;/a&gt;，其实这是不符合实际情况的（Istio 用户的日常）。在 &lt;code&gt;install/kubernetes/helm/istio/values.yaml&lt;/code&gt; 中，包含这一发行版本中的所有的缺省值。可以直接修改或者新建 values.yaml，并在 helm 命令行使用 &lt;code&gt;-f my-values.yaml&lt;/code&gt; 参数来生成自行定制的 &lt;code&gt;istio.yaml&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&#34;解读-istio-helm-chart-中的模块&#34;&gt;解读 Istio Helm Chart 中的模块&lt;/h2&gt;

&lt;p&gt;打开 Istio 的 Chart 之后，发现其中并没有任何组件的内容，只有两个 Configmap 对象的模板。其安装主体再次很非主流的通过依赖 Chart 的方式实现了完全的模块化。因此这个 Chart 的主体，实际上是 &lt;code&gt;requirements.yaml&lt;/code&gt;，打开这个文件，会看到规规矩矩的列出很多模块，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  - name: sidecarInjectorWebhook
    version: 0.8.0
    # repository: file://../sidecarInjectorWebhook
    condition: sidecarInjectorWebhook.enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这表明在 &lt;code&gt;sidecarInjectorWebhook&lt;/code&gt; 取值为 &lt;code&gt;enabled&lt;/code&gt; 的时候，就渲染这一模板。因此这里可以看做是模块的启用停用的控制点。这里列出的模块包括：&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模块&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;egressgateway&lt;/td&gt;
&lt;td&gt;外发流量网关&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;galley&lt;/td&gt;
&lt;td&gt;在 K8S 服务端验证 Istio 的 CRD 资源的合法性&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;grafana&lt;/td&gt;
&lt;td&gt;Dashboard&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;ingress&lt;/td&gt;
&lt;td&gt;Ingress Controller&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;ingressgateway&lt;/td&gt;
&lt;td&gt;Ingress Gateway&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;mixer&lt;/td&gt;
&lt;td&gt;Mixer&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;pilot&lt;/td&gt;
&lt;td&gt;Pilot&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;prometheus&lt;/td&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;security&lt;/td&gt;
&lt;td&gt;安全相关内容&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;servicegraph&lt;/td&gt;
&lt;td&gt;调用关系图&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;sidecarInjectorWebhook&lt;/td&gt;
&lt;td&gt;自动注入&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;tracing&lt;/td&gt;
&lt;td&gt;Zipkin Jeager 的跟踪服务&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;下面逐一做一下简要说明&lt;/p&gt;

&lt;h3 id=&#34;egressgateway&#34;&gt;egressgateway&lt;/h3&gt;

&lt;p&gt;外发通信的网关。&lt;/p&gt;

&lt;p&gt;他的设置保存在 &lt;code&gt;values.yaml&lt;/code&gt; 的 &lt;code&gt;egressgateway&lt;/code&gt; 一节中（都是保存在同名分支下，后面不再重复）。部署内容包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment 和 Service：一个 proxy。&lt;/li&gt;
&lt;li&gt;HPA&lt;/li&gt;
&lt;li&gt;RBAC 相关内容&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可定制项目包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;服务端口和类型&lt;/li&gt;
&lt;li&gt;HPA 实例数量上下限&lt;/li&gt;
&lt;li&gt;资源限制&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;galley&#34;&gt;galley&lt;/h3&gt;

&lt;p&gt;之前的 istio 版本中，只能通过 istioctl 验证 Istio 相关 CRD 的有效性，这个模块提供一个在服务端验证 CRD 的方法，他的部署内容包含：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployement 和 Service。&lt;/li&gt;
&lt;li&gt;RBAC 相关&lt;/li&gt;
&lt;li&gt;用于 CRD 校验的 ValidatingWebhookConfiguration 对象。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;校验目标包含 Pilot（例如 destinationpolicies 和 routerules） 和 Mixer（例如 memquotas 和 prometheuses） 两类 CRD。&lt;/p&gt;

&lt;h3 id=&#34;grafana&#34;&gt;grafana&lt;/h3&gt;

&lt;p&gt;一个带有 Istio 定制 Dashboard 的 Grafana 封装。&lt;/p&gt;

&lt;p&gt;实际上将其镜像中的 Dashboard 复制出来就可以在其他 Grafana 实例上运行了。&lt;/p&gt;

&lt;p&gt;定制内容的 &lt;code&gt;grafana.ingress.*&lt;/code&gt; 中包含 Ingress 的配置，用于外网访问。&lt;/p&gt;

&lt;h3 id=&#34;ingress&#34;&gt;ingress&lt;/h3&gt;

&lt;p&gt;Istio 的 Ingress Controller&lt;/p&gt;

&lt;p&gt;具体部署内容和 egresscontroller 基本一致。&lt;/p&gt;

&lt;h3 id=&#34;ingressgateway&#34;&gt;ingressgateway&lt;/h3&gt;

&lt;p&gt;0.8.0 新增功能，为 Ingress 通信提供 Istio rules/destination 等特性。&lt;/p&gt;

&lt;p&gt;部署内容和 ingress 类似。&lt;/p&gt;

&lt;h3 id=&#34;mixer&#34;&gt;mixer&lt;/h3&gt;

&lt;p&gt;Mixer 负责的是遥测和前置检查，他的 Chart 相对比较复杂，部署内容包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;和前面的版本不同，Mixer 的部署分成了两个部分，分别是 Policy 和 Telemetry 两个 Deployment 对象。&lt;/li&gt;
&lt;li&gt;Service 也同样分成两个，其中 telemetry service 多了一个 prometheus 端口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;crds.yaml&lt;/code&gt; 中包含了 mixer 所支持的所有 crd 定义（例如 memquotas 和 prometheuses）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create-custom-resources-job.yaml&lt;/code&gt; 中包含了用于创建 crd 的 Job 对象。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;pilot&#34;&gt;pilot&lt;/h3&gt;

&lt;p&gt;Pilot 承上启下，负责服务发现和向 Proxy 下发配置。除了常规的 Deployment 和 Service 之外，还包含了 &lt;code&gt;crds.yaml&lt;/code&gt;，用于声明 CRD 资源类型（例如 destinationpolicies 和 routerules）。&lt;/p&gt;

&lt;h3 id=&#34;prometheus&#34;&gt;prometheus&lt;/h3&gt;

&lt;p&gt;这个组件跟前面的 Grafana 类似，也是一个预定义的镜像。这个模板中的 Configmap 就是 Prometheus 的抓取配置，可以直接用到其他的 Prometheus 实例之中。&lt;/p&gt;

&lt;h3 id=&#34;security&#34;&gt;security&lt;/h3&gt;

&lt;p&gt;旧版本中的 Istio-ca&lt;/p&gt;

&lt;p&gt;Security 部分的部署内容包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RBAC&lt;/li&gt;
&lt;li&gt;Job：使用 kubectl 清理旧版本 istio-ca 实例。&lt;/li&gt;
&lt;li&gt;Deployment，原 CA。&lt;/li&gt;
&lt;li&gt;Service：开放两个端口，分别服务于 http 和 gRPC。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;servicegraph&#34;&gt;servicegraph&lt;/h3&gt;

&lt;p&gt;Service Graph 支持，和 Grafana 基本一致。&lt;/p&gt;

&lt;h3 id=&#34;sidecarinjectorwebhook&#34;&gt;sidecarInjectorWebhook&lt;/h3&gt;

&lt;p&gt;这一部分的功能是自动为 K8S 对象注入 Envoy。主要包含：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment 和 Service&lt;/li&gt;
&lt;li&gt;RBAC 相关&lt;/li&gt;
&lt;li&gt;一个 &lt;code&gt;MutatingWebhookConfiguration&lt;/code&gt; 对象，会监听 Pod 的创建事件，用于自动注入。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;tracing&#34;&gt;tracing&lt;/h3&gt;

&lt;p&gt;Jeager 的跟踪支持，总体情况跟 Prometheus 和 Grafana 等监控组件类似，配置项和暴露服务方面稍有区别：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;配置中包含 Jaeger 的环境变量的控制。&lt;/li&gt;
&lt;li&gt;开启 jaeger 开关，会启用 Jaeger 的几个服务端口。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>来自骷髅岛的 Ingress Controller：Kong</title>
      <link>/post/intro-kong/</link>
      <pubDate>Fri, 11 May 2018 12:26:22 +0800</pubDate>
      <guid>/post/intro-kong/</guid>
      <description>

&lt;p&gt;&lt;a href=&#34;https://konghq.com/&#34; target=&#34;_blank&#34;&gt;Kong&lt;/a&gt;，是一个在 Nginx 反向代理基础上发展而来的 API 网关产品。我之前一直在推动的 Service Mesh，主要关注的是集群（Mesh）内微服务之间的关系，而 API 网关所管理的则是微服务集群边缘，对外服务的管理。（据我观测，Istio 近期的文档已经出现了 Gateway 等说法，似乎也对这方面的问题颇有兴趣的样子）。&lt;/p&gt;

&lt;p&gt;传统的 API：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/comp-tra.png&#34; alt=&#34;Traditional api&#34; /&gt;&lt;/p&gt;

&lt;p&gt;API Gateway：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/comp-kong.png&#34; alt=&#34;API Gateway&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://konghq.com/blog/kubernetes-ingress-controller-for-kong/&#34; target=&#34;_blank&#34;&gt;5 月 8 日，Kong 发布了 Ingress Controller&lt;/a&gt;，对 Kubernetes 和对 Kong 自身来说都是个有意思的事情。&lt;/p&gt;

&lt;p&gt;首先，Ingress Controller 本来应该负责集群的对外通信，有些 Ingress Controller，例如 haproxy 和 Traefik 已经初步具备了这方面的能力。
其次，Kong 之前使用 API 调用的方式来进行管理，在 Ingress Controller 的上下文中，改用 CRD 方式进行管理，对于我等 YAML 程序员来说，无疑是个大大的利好消息。&lt;/p&gt;

&lt;p&gt;Kong 使用&lt;a href=&#34;https://konghq.com/plugins/&#34; target=&#34;_blank&#34;&gt;插件&lt;/a&gt;的方式提供了一些常见功能，这些现在也可以用 CRD 方式进行使用，其中包括日志、限流、认证、鉴权几大类。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/intro-illustration.png&#34; alt=&#34;plugins&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Kong 同时提供商业和社区两个版本，目前有部分插件也是商业版独占的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;今天这一篇，就会对 Kong Ingress Controller 从部署到应用的介绍。&lt;/p&gt;

&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;

&lt;p&gt;官方提供了一个简易的 Kubernetes 环境中的&lt;a href=&#34;https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/single/all-in-one-postgres.yaml&#34; target=&#34;_blank&#34;&gt;安装文件&lt;/a&gt;；另外在 Ingress Controller 出现之前，Kong 也有一个相对更丰富的 &lt;a href=&#34;https://getkong.org/install/kubernetes/&#34; target=&#34;_blank&#34;&gt;Kubernetes 下的安装文档&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;全部组件都运行在 kong 命名空间。&lt;/p&gt;

&lt;h3 id=&#34;数据库&#34;&gt;数据库&lt;/h3&gt;

&lt;p&gt;安装过程中会创建一个 Postgres 的 StatefulSet，前面提到，这一版本对 Kubernetes 集群的最低版本要求是 1.8，如果是 1.8 版本，需要将这一个 StatefulSet API 版本改为 &lt;code&gt;apps/v1beta2&lt;/code&gt;。另外这一部分需要用 PVC 的形式给数据提供存储空间，所以集群中应该设置缺省 StorageClass。完整的 Kubernetes 安装文档中，还介绍了 Cassandra 的存储方式。&lt;/p&gt;

&lt;h3 id=&#34;crd&#34;&gt;CRD&lt;/h3&gt;

&lt;p&gt;安装过程中创建了如下的自定义资源：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;凭据：用于身份认证。&lt;/li&gt;
&lt;li&gt;服务消费者：给不同的 API 用户提供不同的消费者身份，以便实施不同的治理方式。&lt;/li&gt;
&lt;li&gt;插件：将过去使用 HTTP API 管理的插件系统，以 CRD 的形式在 Kubernetes 环境下统一管理。&lt;/li&gt;
&lt;li&gt;KongIngress：对反向代理行为本身进行定义。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;服务&#34;&gt;服务&lt;/h3&gt;

&lt;p&gt;其中提供了两组服务：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;kong-ingress-controller：暴露 8001 端口，用于对 Kong 进行管理。这里建议将服务类型改为 ClusterIP，而不是直接暴露于公网。&lt;/li&gt;
&lt;li&gt;kong-proxy：Ingress 服务，对其承载的接口调用都从此经过，可以根据集群情况酌情使用 NodePort 或者 ClusterIP -&amp;gt; Ingress 的方式对外提供服务。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;提供服务&#34;&gt;提供服务&lt;/h2&gt;

&lt;p&gt;安装结束后，就可以使用网关来对外提供服务了。&lt;/p&gt;

&lt;p&gt;官方提供了一个简单的&lt;a href=&#34;https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/manifests/dummy-application.yaml&#34; target=&#34;_blank&#34;&gt;例子应用&lt;/a&gt;，我们当然也可以选择别的应用来试用。为他编写一个 Ingress 资源：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dummy
  annotations:
    kubernetes.io/ingress.class: nginx # 这里仍然是 nginx
spec:
  rules:
  - host: dummy.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: http-svc
          servicePort: 89
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;如果没有公网 Loadbalancer 条件，可以使用 &lt;code&gt;/etc/hosts&lt;/code&gt;、dnsmasq、或者 curl host 几种方式来模拟。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ingress 资源创建成功之后，就可以使用域名来访问这一服务了。&lt;/p&gt;

&lt;h3 id=&#34;tls-加密&#34;&gt;TLS 加密&lt;/h3&gt;

&lt;p&gt;作为 Ingress Controller，添加证书提供 https 服务也是基本要求之一。这方面 Kong Ingress Controller 使用的是 tls secret 的方式：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;首先获取证书，可以自行签名，或者使用已有证书文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubectl create secret tls rocks --key privkey.pem --cert fullchain.pem&lt;/code&gt;：创建一个名为 &lt;code&gt;rocks&lt;/code&gt; 的 Secret，其中包含我们的证书和私钥。&lt;/li&gt;

&lt;li&gt;&lt;p&gt;在 Ingress 资源定义中加入下列内容，引用刚才创建的 Secret：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;tls:
- hosts:
- dummy.example.com
secretName: rocks
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这样就可以使用 https 进行访问了。&lt;/p&gt;

&lt;h2 id=&#34;试用限流插件&#34;&gt;试用限流插件&lt;/h2&gt;

&lt;p&gt;前面提到，Kong Ingress Controller 使用 CRD 方式来实现插件的应用。下面我们创建一个限流插件的 CRD：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: one-per-second-ten-per-hour
config:
  hour: 10
  limit_by: ip
  second: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;目前并没有很完善的插件 CRD 规范的文档，因此其中的具体字段需要去该插件的文档页面去查找，例如这里引用的 &lt;a href=&#34;https://getkong.org/plugins/rate-limiting/&#34; target=&#34;_blank&#34;&gt;Ratelimit&lt;/a&gt;。在这一个 YAML 中我们会发现，其中并没有表明具体使用的插件以及对应的服务，通过对 controller 管理端口的 &lt;code&gt;/plugins&lt;/code&gt; 进行查询，也会看到其中并没有定义活动的插件。&lt;/p&gt;

&lt;p&gt;要把它应用到具体服务上，还需要修改我们要控制的 Ingress 资源，在其中加入注解，来引用这一 CRD：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kubernetes.io/ingress.class: nginx
rate-limiting.plugin.konghq.com: one-per-second-ten-per-hour
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;提交新的 Ingress 之后，再次访问管理端口的 &lt;code&gt;plugins&lt;/code&gt; 路径，会得到以下响应：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
    &amp;quot;total&amp;quot;: 1,
    &amp;quot;data&amp;quot;: [
        {
            &amp;quot;created_at&amp;quot;: 1525966801000,
            &amp;quot;config&amp;quot;: {
                &amp;quot;redis_database&amp;quot;: 0,
                &amp;quot;policy&amp;quot;: &amp;quot;cluster&amp;quot;,
                &amp;quot;redis_timeout&amp;quot;: 2000,
                &amp;quot;hide_client_headers&amp;quot;: false,
                &amp;quot;hour&amp;quot;: 20,
                &amp;quot;limit_by&amp;quot;: &amp;quot;ip&amp;quot;,
                &amp;quot;redis_port&amp;quot;: 6379,
                &amp;quot;second&amp;quot;: 10,
                &amp;quot;fault_tolerant&amp;quot;: true
            },
            &amp;quot;id&amp;quot;: &amp;quot;8539eb6f-5467-11e8-a92e-000d3a07d45d&amp;quot;,
            &amp;quot;name&amp;quot;: &amp;quot;rate-limiting&amp;quot;,
            &amp;quot;enabled&amp;quot;: true,
            &amp;quot;route_id&amp;quot;: &amp;quot;f2961715-11fd-410a-a934-dbd6822e5fac&amp;quot;
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以使用 siege 或者 curl/wrk 等其他工具来访问 API，会发现超过限度之后，服务器返回 429 的状态码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;HTTP/1.1 200     0.20 secs:     727 bytes ==&amp;gt; GET  /
HTTP/1.1 200     0.22 secs:     729 bytes ==&amp;gt; GET  /
HTTP/1.1 429     0.20 secs:      38 bytes ==&amp;gt; GET  /
HTTP/1.1 429     0.21 secs:      38 bytes ==&amp;gt; GET  /
HTTP/1.1 429     0.20 secs:      38 bytes ==&amp;gt; GET  /
HTTP/1.1 200     0.20 secs:     729 bytes ==&amp;gt; GET  /
HTTP/1.1 200     0.20 secs:     727 bytes ==&amp;gt; GET  /
HTTP/1.1 429     0.20 secs:      38 bytes ==&amp;gt; GET  /
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;证明限流功能已经生效。&lt;/p&gt;

&lt;h3 id=&#34;试用消费者&#34;&gt;试用消费者&lt;/h3&gt;

&lt;p&gt;前面提到，可以使用消费者这一概念，对微服务的用户身份加以甄别，从而提供不同的管控方式。在前面的基础上，我们希望为部分用户修改一下响应内容。&lt;/p&gt;

&lt;p&gt;首先创建一个 KongConsumer 对象：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
  name: rich
username: boss
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来，为这个用户创建凭据，&lt;strong&gt;凭据是需要认证的，所以还要启用一个插件：key-auth&lt;/strong&gt;，官方文档中并没有提及这一点：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: configuration.konghq.com/v1
kind: KongCredential
metadata:
  name: rich-login
consumerRef: rich # 如果删除这一字段，就代表面向所有消费者。
type: key-auth
config:
  key: 62eb165c070a41d5c1b58d9d3d725ca1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后，为这个用户创建一个插件配置&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: rich-response
consumerRef: rich
config:
  hour: 100
  limit_by: ip
  second: 10
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后，在 Ingress 资源中启用&lt;strong&gt;两个插件&lt;/strong&gt;，分别是 &lt;code&gt;key-auth&lt;/code&gt; 和 &lt;code&gt;response-transformer&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;response-transformer.plugin.konghq.com: boss
key-auth.plugin.konghq.com: auth
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;重新配置 Ingress 之后，可以使用 curl 进行校验：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;curl --header &amp;quot;apikey: aasome_key_data&amp;quot; -s -i https://dummy.example.com
HTTP/1.1 403 Forbidden
Date: Fri, 11 May 2018 04:17:57 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: kong/0.13.1

{&amp;quot;message&amp;quot;:&amp;quot;Invalid authentication credentials&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到上面的认证没能通过。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;curl --header &amp;quot;apikey: some_key_data&amp;quot; -s -i https://dummy.example.com | grep boss
boss:  true
    x-consumer-username=boss2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Key Auth 认证插件根据 APIKey 取得了用户名，并且激活了 Response Transformer 插件，在 Header 中加入了我们配置的内容。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Kong 原有的 API 在这里还是可以使用的，例如：
1. &lt;code&gt;curl http://[api-url]/plugins&lt;/code&gt; 查询生效插件
2. &lt;code&gt;curl http://[api-url]&lt;/code&gt; 返回 JSON 中的 &lt;code&gt;/plugins/available_on_server&lt;/code&gt; 列出所有可用插件。
3. &lt;code&gt;curl ttp://[api-url]/consumers&lt;/code&gt; 列出所有消费者。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;相关链接&#34;&gt;相关链接：&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;https://konghq.com/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/single/all-in-one-postgres.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/manifests/dummy-application.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Conduit v0.2.0 发布</title>
      <link>/post/conduit-v0.2-ann/</link>
      <pubDate>Thu, 01 Feb 2018 18:27:52 +0800</pubDate>
      <guid>/post/conduit-v0.2-ann/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://buoyant.io/2018/02/01/announcing-conduit-support-http-1-x-tcp/&#34; target=&#34;_blank&#34;&gt;Announcing Conduit support for HTTP/1.x and TCP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;里程碑版本，这次发布中新增了对 HTTP/1.x 和 TCP 支持，这样就可以为绝大多数运行在 Kubernetes 上的应用提供支持了。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;数据面

&lt;ul&gt;
&lt;li&gt;Conduit 现在为包括 HTTP/1.x 和 HTTP/2 在内的所有 TCP 流量提供透明代理。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;控制台界面

&lt;ul&gt;
&lt;li&gt;强化了 &lt;code&gt;tap&lt;/code&gt; 命令的错误处理能力。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tap&lt;/code&gt; 也提供了对 HTTP/1.x 的支持。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Dashboard

&lt;ul&gt;
&lt;li&gt;界面进行了小幅更新。&lt;/li&gt;
&lt;li&gt;可以在 Dashboard 边栏搜索 Deployment。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;预告&#34;&gt;预告&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Conduit 将会为绝大多数协议提供自动支持，然而使用 WebSockets、HTTP 隧道/代理或者 MySQL、SMTP 等协议，需要一些额外配置，&lt;a href=&#34;https://conduit.io/adding-your-service/#protocol-support&#34; target=&#34;_blank&#34;&gt;文档（注 1）&lt;/a&gt;中会有详细说明。&lt;/li&gt;
&lt;li&gt;Conduit 还不支持外部 DNS。这一缺憾将在未来版本提供支持。&lt;/li&gt;
&lt;li&gt;目前 Conduit 的遥测管线无法扩展到某些节点，后续版本会解决这个问题。&lt;/li&gt;
&lt;li&gt;Conduit 还是 Alpha 阶段，请&lt;a href=&#34;https://github.com/runconduit/conduit/issues/new&#34; target=&#34;_blank&#34;&gt;提交 Issue 或 PR 来支持我们（注 2）&lt;/a&gt;！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;引用：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;https://conduit.io/adding-your-service/#protocol-support&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://github.com/runconduit/conduit/issues/new&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Conduit 登场</title>
      <link>/post/conduit-ann/</link>
      <pubDate>Wed, 06 Dec 2017 09:46:58 +0800</pubDate>
      <guid>/post/conduit-ann/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://buoyant.io/2017/12/05/introducing-conduit/&#34; target=&#34;_blank&#34;&gt;Introducing Conduit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;今天我们要介绍 &lt;a href=&#34;http://conduit.io/&#34; target=&#34;_blank&#34;&gt;Conduit&lt;/a&gt;，面向 Kubernetes 的新的开源 Service Mesh。&lt;/p&gt;

&lt;p&gt;Conduit 横空出世，目标是成为最快、最轻、最简单并且最安全的 Service Mesh。他使用 &lt;a href=&#34;https://www.rust-lang.org/&#34; target=&#34;_blank&#34;&gt;Rust&lt;/a&gt; 构建了快速、安全的数据平面，用 &lt;a href=&#34;https://golang.org/&#34; target=&#34;_blank&#34;&gt;Go&lt;/a&gt; 开发了简单强大的控制平面，总体设计围绕着性能、安全性和可用性进行。更重要的是，Conduit 将会完全吸收过去 18 个月中，我们在 Linkerd 的生产级 Service Mesh 中积累沉淀的真实经验。&lt;/p&gt;

&lt;p&gt;为什么要做一个 Conduit？Linkerd 是世界上最多生产级部署的 Service Mesh。这一产品创造了 &amp;ldquo;Service Mesh&amp;rdquo; 这一词汇，在软件基础设施中成功的开辟了新的领域，为遍及全球的企业客户（例如 Salesforce、Paypal、Expedia、AOL 以及 Monzo）承载了数以万亿计的请求。这一段时间里，我们和客户以及用户们甘苦与共——我们一起开会，设计交叉路线图，凌晨三点起床救火。开源的基础设施和现实世界的对撞中，我们学到了弥足珍贵的经验和教训。&lt;/p&gt;

&lt;p&gt;这中间有个突出的问题就是，Linkerd 的部署模型太重了。虽然 Linkerd 的各个组件，例如 Finagle、Netty、Scala 以及 JVM，都是被广泛采用千锤百炼的，有了这些组件的帮助，在有足够 CPU 和内存支持的情况下，Linkerd 能够达到非常高的负载能力；然而设计过程中鲜有考虑在有限的资源情况下，基于 sidecar 模式的 Kubernetes 部署方式。所以今年年初，我们自问：有了 18 个月的生产级 Service Mesh 的经验，如果要做出一个功能完备又可以在低资源环境下运行的 Service Mesh，我们会怎么做？&lt;/p&gt;

&lt;p&gt;答案就是 Conduit。和 Linkerd 类似，Conduit 是让微服务安全可靠的下一代 Service Mesh。他能透明的管理服务之间的通信，自动提供可测性、可靠性、安全性和弹性的支持。还是跟 Linkerd 相仿，他的数据平面是在应用代码之外运行的轻量级代理，控制平面是一个高可用的控制器。然而和 Linkerd 不同的是，Conduit 的设计更加倾向于 Kubernetes 中的低资源部署。&lt;/p&gt;

&lt;p&gt;Conduit 为什么伟大（hhhhh）：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;轻量高速：Conduit 代理只需要不到 10 MB 实际内存（RSS），p99 延迟在分毫秒以内。&lt;/li&gt;
&lt;li&gt;安全：Rust 的内存使用相当安全，同时还缺省使用了 TLS，Conduit 的安全性与生俱来。&lt;/li&gt;
&lt;li&gt;最小化：Conduit 的特性集被设计为尽量的最小化和可编排，便于使用 gRPC 插件进行定制。&lt;/li&gt;
&lt;li&gt;易用性：内置有聚合的服务指标，强大的客户端工具（想想看，微服务界的 tcpdump），Conduit 为运维人员提供了新的强大的工具来对付生产环境的微服务。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;过去半年中我们一直在努力的构建 Conduit。我们聘请了 &lt;a href=&#34;http://philcalcado.com/&#34; target=&#34;_blank&#34;&gt;Phil&lt;/a&gt;、&lt;a href=&#34;https://github.com/carllerche&#34; target=&#34;_blank&#34;&gt;Carl&lt;/a&gt;、&lt;a href=&#34;http://seanmonstar.com/&#34; target=&#34;_blank&#34;&gt;Sean&lt;/a&gt; 以及 &lt;a href=&#34;https://briansmith.org/&#34; target=&#34;_blank&#34;&gt;Brain&lt;/a&gt; 这样的高手；向 &lt;a href=&#34;https://github.com/tokio-rs/tokio&#34; target=&#34;_blank&#34;&gt;Tokio&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/BuoyantIO/tower&#34; target=&#34;_blank&#34;&gt;Tower&lt;/a&gt; 这样的核心技术投资，让 Conduit 又快又安全。最重要的是，我们决心用 Conduit 解决在 Linkerd 社区遇到的实际问题。&lt;/p&gt;

&lt;h2 id=&#34;linkerd-怎么办&#34;&gt;Linkerd 怎么办？&lt;/h2&gt;

&lt;p&gt;简单说说，Linkerd 是世界上最广泛在生产环境进行使用的 Service Mesh，他会长期存在，我们会持续的进行开发、管理并提供商业支持；我们会保证我们的 Linkerd 用户的幸福感。&lt;/p&gt;

&lt;p&gt;Conduit 不是 Linkerd 2.0。Conduit 面向的是非常特定的环境 —— Kubernetes，而不准备像 Linkerd 一样做出众多的集成支持。我们的大量用户在使用 ECS、Consul、Mesos、ZooKeeper、Nomad、Rancher 或者混合的多环境系统，Linkerd 是目前的最好选择，我们会持续投入进行改善。&lt;/p&gt;

&lt;h2 id=&#34;现在开始&#34;&gt;现在开始！&lt;/h2&gt;

&lt;p&gt;我们刚刚发布了 Conduit 0.1。&lt;a href=&#34;https://conduit.io/&#34; target=&#34;_blank&#34;&gt;来跳坑吧&lt;/a&gt;！目前这是个 Alpha 版本，所以理直气壮的仅提供 HTTP/2 的支持（是的，HTTP/1.1 都不支持）。我们希望我们的早期用户能够尽早接触 Conduit，并从中获得反馈。&lt;/p&gt;

&lt;p&gt;接下来的几个月中，我们会积极工作，推进 Conduit 的生产化进程，预计来年初面世的 0.2 中，会加入 HTTP/1.1 和 TCP 的支持（&lt;a href=&#34;https://conduit.io/roadmap/&#34; target=&#34;_blank&#34;&gt;路线图&lt;/a&gt;）。这一产品的进展和目标会非常公开。最后，我们还会提供 Conduit 的商业支持，如果有兴趣的话可以&lt;a href=&#34;mailto:hello@buoyant.io&#34; target=&#34;_blank&#34;&gt;联系我们&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;想要了解更多？可以订阅我们的发布通知邮件列表，加入 &lt;a href=&#34;http://slack.linkerd.io/?__hstc=9342122.eea97f52bb4b2bba60b0c11bd6dc7c15.1506617171339.1510149418766.1512491813854.6&amp;amp;__hssc=9342122.1.1512491813854&amp;amp;__hsfp=3751614787&#34; target=&#34;_blank&#34;&gt;Linkerd 的 Slack 频道&lt;/a&gt;，或者 Follow &lt;a href=&#34;https://twitter.com/runconduit&#34; target=&#34;_blank&#34;&gt;@runconduit&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;Conduit 是基于 Apache 2.0 协议的开源软件。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>istio 三日谈之二 路由规则</title>
      <link>/post/istio-route-rules/</link>
      <pubDate>Fri, 28 Jul 2017 17:35:28 +0800</pubDate>
      <guid>/post/istio-route-rules/</guid>
      <description>

&lt;p&gt;路由控制是 istio 的最常用功能了，经过前面的准备，我们已经基本可以进行这些内容的尝试了。&lt;/p&gt;

&lt;p&gt;注意下面的路由规则都忽略了对来源的过滤，会显得比较呆板或者说没用，但是在加入过滤条件之后，就完全不可同日而语了。具体的过滤规则的写法可以参考官方文档或者 istio 中的 bookinfo 实例。&lt;/p&gt;

&lt;h2 id=&#34;创建-frontend-v2&#34;&gt;创建 frontend-v2&lt;/h2&gt;

&lt;p&gt;为了展示路由分配的能力，我们首先创建一个名为&lt;code&gt;frontend-v2&lt;/code&gt;的&lt;code&gt;Deployment&lt;/code&gt;，这个&lt;code&gt;deploy&lt;/code&gt;跟之前的 v1 共享同一个 PVC，也就是共享同一套页面。利用环境变量来控制输出。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: frontend-v2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: frontend
        version: &amp;quot;2&amp;quot;
    spec:
      containers:
      - name: php
        image: 10.211.55.86:5000/php:7.1.7-apache
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
        - name: wwwroot
          mountPath: /var/www/html
        env:
        - name: &amp;quot;SERVICE_VERSION&amp;quot;
          value: &amp;quot;2&amp;quot;
      volumes:
      - name: wwwroot
        persistentVolumeClaim:
          claimName: frontend-v1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同前面的 v1 一样，这个&lt;code&gt;Deployment&lt;/code&gt;也需要使用&lt;code&gt;istioctl kube-inject&lt;/code&gt;进行注入，最后使用&lt;code&gt;kubectl apply -f&lt;/code&gt; 运行。&lt;/p&gt;

&lt;h2 id=&#34;流量分配&#34;&gt;流量分配&lt;/h2&gt;

&lt;h3 id=&#34;首先创建如下路由规则&#34;&gt;首先创建如下路由规则&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;default.yaml&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: frontend-default
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 1
  route:
  - tags:
      version: &amp;quot;2&amp;quot;
    weight: 100
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;destination：必须是服务的 fqdn&lt;/li&gt;
&lt;li&gt;precedence：整数，优先级，越大越先&lt;/li&gt;
&lt;li&gt;route：数组

&lt;ul&gt;
&lt;li&gt;tag：&lt;strong&gt;Pod&lt;/strong&gt; 的标签选择器。&lt;/li&gt;
&lt;li&gt;weigh: 整数，权重，分配到当前路由的比率。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;创建之后，使用&lt;code&gt;istioctl create -f default.yaml&lt;/code&gt;，创建这一规则。接下来我们使用&lt;code&gt;kubectl exec -it&lt;/code&gt;进入 tool pod 进行测试：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ curl svc-frontend/index.php

-----------------------------
From: frontend-797054967-r12m5
Version: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;返回内容表明这一服务调用的是最初的 v1 版本的 frontend。&lt;/p&gt;

&lt;p&gt;接下来修改 default.yaml 的&lt;code&gt;version &amp;quot;1&amp;quot;&lt;/code&gt;为&lt;code&gt;version &amp;quot;2&amp;quot;&lt;/code&gt;，然后用&lt;code&gt;istioctl replace -f default.yaml&lt;/code&gt;更新路由规则。再次验证：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ curl svc-frontend/index.php

-----------------------------
From: frontend-v2-90739004-xpmrn
Version: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里可以看到，这一响应来自于 v2 版本的 Pod，并且返回的版本号也是 2。&lt;/p&gt;

&lt;p&gt;然后我们再次修改路由规则，测试一下路由分配：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: frontend-default
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 1
  route:
  - tags:
      version: &amp;quot;2&amp;quot;
    weight: 10
  - tags:
      version: &amp;quot;1&amp;quot;
    weight: 90
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;根据上面的路由规则，对  svc-frontend 这一服务的访问，应该有 10% 流量分给版本 2，其余的 90% 分配给了版本 1。我们在 tool pod 中使用如下脚本测试这一分配：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
for i in {1..100}
do
  curl -s svc-frontend/index.php | grep Version
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;执行效果：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$  ./curl.batch.sh  | grep 2 | wc -l
10
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到，完全符合之前我们的路由设置。&lt;/p&gt;

&lt;h2 id=&#34;超时策略&#34;&gt;超时策略&lt;/h2&gt;

&lt;p&gt;为了保障服务质量，我们有时会要求对某些服务的返回时间进行限制&lt;/p&gt;

&lt;p&gt;前面提到，我们生成了一个&lt;code&gt;delay.php&lt;/code&gt;，用于进行延时测试，文件内容如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
header(&amp;quot;Content-type: text/plain&amp;quot;);
sleep(4);
echo &amp;quot;\n-----------------------------\n&amp;quot;;
echo &amp;quot;\nFrom: &amp;quot;.gethostname().&amp;quot;\n&amp;quot;;
echo &amp;quot;Version: &amp;quot;.$_ENV[&#39;SERVICE_VERSION&#39;].&amp;quot;\n&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;正常执行&lt;code&gt;time curl -s svc-frontend/delay.php&lt;/code&gt;，会返回如下结果：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-----------------------------
From: frontend-797054967-r12m5
Version: 1

real    0m4.025s
user    0m0.005s
sys     0m0.004s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们在这里加入一个策略，两秒超时：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: front-timeout
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 9
  route:
  - tags:
      version: &amp;quot;1&amp;quot;
  httpReqTimeout:
    simpleTimeout:
      timeout: 2s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;再次测试，则会返回超时的结果：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;upstream request timeout
real    0m2.015s
user    0m0.006s
sys     0m0.003s
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;重试策略&#34;&gt;重试策略&lt;/h2&gt;

&lt;p&gt;在服务超时的时候，我们可能会希望请求自动重试。&lt;/p&gt;

&lt;p&gt;这一测试要求保留前面的超时策略，以便形成失败结果：&lt;/p&gt;

&lt;p&gt;策略文件如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: front-timeout
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 9
  route:
  - tags:
      version: &amp;quot;1&amp;quot;
  httpReqRetries:
    simpleRetry:
      attempts: 3
      perTryTimeout: 2s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里表示每次尝试的超时时间是两秒，重试三次。使用 curl 测试：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ time curl -s svc-frontend/delay.php
upstream request timeout
real    0m8.136s
user    0m0.005s
sys     0m0.006s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;时间为 8 秒，相当于四次超时的时间。&lt;/p&gt;

&lt;h2 id=&#34;rewrite&#34;&gt;Rewrite&lt;/h2&gt;

&lt;p&gt;和反向代理的情况类似，有时我们需要对进入服务的 URL 进行重写，下面的路由策略会将 &lt;code&gt;/front&lt;/code&gt; 替换为 &lt;code&gt;/&lt;/code&gt; 进行访问：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: front-timeout
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 9
  match:
    httpHeaders:
      uri:
        prefix: &amp;quot;/front/&amp;quot;
  rewrite:
    uri: &amp;quot;/&amp;quot;
  route:
  - tags:
      version: &amp;quot;1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意这里对&lt;code&gt;uri&lt;/code&gt;的操作，是使用 rewrite 中的 uri 替换 match 中的 uri，二者的对应关系是强制的。&lt;/p&gt;

&lt;p&gt;在 tool 里面进行测试访问：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ curl http://svc-frontend/front/index.php
-----------------------------

From: frontend-797054967-r12m5
Version: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;故障注入&#34;&gt;故障注入&lt;/h2&gt;

&lt;p&gt;微服务测试过程中，能够自动生成一些错误，无疑是个很有帮助的事情。目前 istio 支持两种故障的模拟：时延和中断&lt;/p&gt;

&lt;h2 id=&#34;时延&#34;&gt;时延&lt;/h2&gt;

&lt;p&gt;下面的规则会为每个对该服务的请求都生成 7 秒的时延：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: fdelay
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 9
  route:
  - tags:
      version: &amp;quot;1&amp;quot;
  httpFault:
    delay:
      percent: 100
      fixedDelay: 7s
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;percent: 产生时延的比例&lt;/li&gt;
&lt;li&gt;fixedDelay: 时间&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用 curl 验证，可以看到的确多出了 7 秒的处理时间。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ time curl -s http://svc-frontend/index.php
-----------------------------

From: frontend-797054967-r12m5
Version: 1

real    0m7.028s
user    0m0.007s
sys     0m0.003s
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;中断&#34;&gt;中断&lt;/h2&gt;

&lt;p&gt;这一功能可以模拟服务中断的情景，下面的 yaml 定义了该服务 100% 会返回 403 错误。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;type: route-rule
name: fdelay
spec:
  destination: svc-frontend.default.svc.cluster.local
  precedence: 9
  route:
  - tags:
      version: &amp;quot;1&amp;quot;
  httpFault:
    abort:
      percent: 100
      httpStatus: 403
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;curl 的验证结果如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$  curl -v svc-frontend/error.php
* Connected to svc-frontend (10.100.186.68) port 80 (#0)
&amp;gt; GET /error.php HTTP/1.1
&amp;gt; User-Agent: curl/7.38.0
&amp;gt; Host: svc-frontend
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 403 Forbidden
&amp;lt; date: Fri, 28 Jul 2017 01:27:52 GMT
* Server envoy is not blacklisted
&amp;lt; server: envoy
&amp;lt; content-length: 0
&amp;lt;
* Connection #0 to host svc-frontend left intact
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>istio 三日谈之一，环境准备</title>
      <link>/post/istio-env-prepare/</link>
      <pubDate>Fri, 28 Jul 2017 07:00:12 +0800</pubDate>
      <guid>/post/istio-env-prepare/</guid>
      <description>

&lt;p&gt;笔者尝试在一个准生产环境下，利用 istio 来对运行在 Kubernetes 上的微服务进行管理。&lt;/p&gt;

&lt;p&gt;这一篇是第一篇，将一些主要的坑和环境准备工作。&lt;/p&gt;

&lt;p&gt;内容较多，因此无法写成手把手教程，希望读者有一定 Kubernetes 的操作基础。&lt;/p&gt;

&lt;h2 id=&#34;准备镜像&#34;&gt;准备镜像&lt;/h2&gt;

&lt;p&gt;初始运行需要的镜像包括以下几个：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;istio/mixer:0.1.6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pilot:0.1.6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;proxy_debug:0.1.6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;istio-ca:0.1.6&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;首先要解决的自然还是镜像的存放问题，官方在源码中提供了很方便的工具，用来根据模板生成在 Kubernetes 中运行 istio 的 YAML 文件：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./updateVersion.sh \
	-p 10.211.55.86:5000/istio,0.1.6 \
	-c 10.211.55.86:5000/istio,0.1.6 \
	-x 10.211.55.86:5000/istio,0.1.6
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这一脚本在源码的 install 目录下。&lt;/p&gt;

&lt;h2 id=&#34;kubernetes-环境&#34;&gt;Kubernetes 环境&lt;/h2&gt;

&lt;p&gt;这里我们使用的集群大概情况是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.7.1 版本的 Kubernetes&lt;/li&gt;
&lt;li&gt;开启了 RBAC&lt;/li&gt;
&lt;li&gt;预备使用的命名空间为：default&lt;/li&gt;
&lt;li&gt;PVC 自动供给&lt;/li&gt;
&lt;li&gt;无互联网连接&lt;/li&gt;
&lt;li&gt;具有自己的私库&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;启动-istio&#34;&gt;启动 istio&lt;/h2&gt;

&lt;h3 id=&#34;rbac-相关&#34;&gt;RBAC 相关&lt;/h3&gt;

&lt;p&gt;首先，install 目录中提供的 rbac 文件授权范围不足，所以需要手工编辑&lt;code&gt;istio-rbac-beta.yaml&lt;/code&gt;，把其中的几个 RoleBinding，改为 ClusterRoleBinding。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;kubectl create \
	-f istio-rbac-beta.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;另外缺省的 ClusterRole 中缺乏对 Namespace 的权限，新版本已经修正，目前版本仍需添加：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: istio-pilot
rules:
- apiGroups: [&amp;quot;istio.io&amp;quot;]
  resources: [&amp;quot;istioconfigs&amp;quot;, &amp;quot;istioconfigs.istio.io&amp;quot;]
  verbs: [&amp;quot;*&amp;quot;]
- apiGroups: [&amp;quot;extensions&amp;quot;]
  resources: [&amp;quot;thirdpartyresources&amp;quot;, &amp;quot;thirdpartyresources.extensions&amp;quot;, &amp;quot;ingresses&amp;quot;, &amp;quot;ingresses/status&amp;quot;]
  verbs: [&amp;quot;*&amp;quot;]
- apiGroups: [&amp;quot;&amp;quot;]
  resources: [&amp;quot;configmaps&amp;quot;, &amp;quot;endpoints&amp;quot;, &amp;quot;pods&amp;quot;, &amp;quot;services&amp;quot;]
  verbs: [&amp;quot;*&amp;quot;]
- apiGroups: [&amp;quot;&amp;quot;]
  resources: [&amp;quot;namespaces&amp;quot;, &amp;quot;nodes&amp;quot;, &amp;quot;secrets&amp;quot;]
  verbs: [&amp;quot;get&amp;quot;, &amp;quot;list&amp;quot;, &amp;quot;watch&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;启动-istio-组件&#34;&gt;启动 istio 组件&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;kubectl create \
	-f istio.yaml \
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;创建-pvc&#34;&gt;创建 PVC&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: frontend-v1
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;准备工作负载&#34;&gt;准备工作负载&lt;/h2&gt;

&lt;p&gt;这里我们使用官方的 PHP + Apache 镜像作为工作负载来进行下面的测试，例如我们准备好的 YAML 如下：&lt;/p&gt;

&lt;p&gt;Deployment：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: frontend
  labels:
    name: frontend
    version: &amp;quot;1&amp;quot;
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: frontend
        version: &amp;quot;1&amp;quot;
    spec:
      containers:
      - name: php
        image: 10.211.55.86:5000/php:7.1.7-apache
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
        - name: wwwroot
          mountPath: /var/www/html
        env:
        - name: &amp;quot;SERVICE_VERSION&amp;quot;
          value: &amp;quot;1&amp;quot;      
     	volumes:
      - name: wwwroot
        persistentVolumeClaim:
          claimName: frontend-v1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;服务定义：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: Service
apiVersion: v1
metadata:
  name: svc-frontend
  labels:
    name: frontend
    version: &amp;quot;1&amp;quot;
spec:
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 32010  
  selector:
    name: frontend

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在 Web 目录中我们随便做了个&lt;code&gt;index.php&lt;/code&gt;，用来展示当前所在 Pod 和环境变量中的服务版本号，备用。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tips：一般这类测试，我都懒得重新做一个 Docker 镜像，一般是另外用一个 Pod 挂载同一个 PVC，直接编辑页面文件，或者使用&lt;code&gt;kubectl cp&lt;/code&gt;命令进行拷贝。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;index-php&#34;&gt;index.php&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php
header(&amp;quot;Content-type: text/plain&amp;quot;);
echo &amp;quot;From: &amp;quot;.gethostname().&amp;quot;\n&amp;quot;;
echo &amp;quot;Version: &amp;quot;.$_ENV[&#39;SERVICE_VERSION&#39;].&amp;quot;\n&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;delay-php&#34;&gt;delay.php&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;
&amp;lt;?php
header(&amp;quot;Content-type: text/plain&amp;quot;);
sleep(4);
echo &amp;quot;\n-----------------------------\n&amp;quot;;
echo &amp;quot;\nFrom: &amp;quot;.gethostname().&amp;quot;\n&amp;quot;;
echo &amp;quot;Version: &amp;quot;.$_ENV[&#39;SERVICE_VERSION&#39;].&amp;quot;\n&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;运行成功后，访问该服务的 nodePort，会看到相应的输出内容。&lt;/p&gt;

&lt;h2 id=&#34;istio-的注入&#34;&gt;istio 的注入&lt;/h2&gt;

&lt;p&gt;首先用&lt;code&gt;kubectl delete -f&lt;/code&gt;删除上文的服务和 Deployment。&lt;/p&gt;

&lt;p&gt;上面为了测试方便，给 Service 使用了 NodePort 类型，这里我们去掉这一服务的 NodePort，用 ClusterIP 的形式运行：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  selector:
    name: frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来进行注入操作&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;istioctl kube-inject -f frontend-v1.yaml &amp;gt; frontend-v1-istio.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;观察注入操作会发现，其中多了一个 Sidecar Container（下面的 Image 节内容已经被我修改为本地私库）：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    image: 10.211.55.86:5000/istio/proxy_debug:0.1.6
    imagePullPolicy: Always
    name: proxy
    resources: {}
    securityContext:
      runAsUser: 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;另外还在&lt;code&gt;pod.beta.kubernetes.io/init-containers&lt;/code&gt;注解中进行了初始化：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;[{
  &amp;quot;args&amp;quot;: [&amp;quot;-p&amp;quot;, &amp;quot;15001&amp;quot;, &amp;quot;-u&amp;quot;, &amp;quot;1337&amp;quot;],
  &amp;quot;image&amp;quot;: &amp;quot;10.211.55.86:5000/istio/init:0.1&amp;quot;,
  &amp;quot;imagePullPolicy&amp;quot;: &amp;quot;Always&amp;quot;,
  &amp;quot;name&amp;quot;: &amp;quot;init&amp;quot;,
  &amp;quot;securityContext&amp;quot;: {
    &amp;quot;capabilities&amp;quot;: {
      &amp;quot;add&amp;quot;: [&amp;quot;NET_ADMIN&amp;quot;]
    }
  }
}, {
  &amp;quot;args&amp;quot;: [&amp;quot;-c&amp;quot;, &amp;quot;sysctl -w kernel.core_pattern=/tmp/core.%e.%p.%t &amp;amp;&amp;amp; ulimit -c unlimited&amp;quot;],
  &amp;quot;command&amp;quot;: [&amp;quot;/bin/sh&amp;quot;],
  &amp;quot;image&amp;quot;: &amp;quot;10.211.55.86:5000/alpine&amp;quot;,
  &amp;quot;imagePullPolicy&amp;quot;: &amp;quot;Always&amp;quot;,
  &amp;quot;name&amp;quot;: &amp;quot;enable-core-dump&amp;quot;,
  &amp;quot;securityContext&amp;quot;: {
    &amp;quot;privileged&amp;quot;: true
  }
}]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到上面一共涉及三个镜像：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker.io/istio/proxy_debug:0.1&lt;/li&gt;
&lt;li&gt;docker.io/istio/init:0.1&lt;/li&gt;
&lt;li&gt;alpine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;经过一番折腾：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;原有 YAML&lt;/li&gt;
&lt;li&gt;注入，生成新的 YAML&lt;/li&gt;
&lt;li&gt;替换新 YAML 中的镜像地址&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;就把原有的容器应用封装成新的 istio 支持的微服务了。&lt;/p&gt;

&lt;h2 id=&#34;准备测试素材&#34;&gt;准备测试素材&lt;/h2&gt;

&lt;p&gt;另外我们需要准备一个工具服务，用于在 shell 中进行测试：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: tool
  labels:
    name: tool
    version: &amp;quot;1&amp;quot;
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: tool
        version: &amp;quot;1&amp;quot;
    spec:
      containers:
      - name: tool
        image: 10.211.55.86:5000/php:7.1.7-apache
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
        - name: wwwroot
          mountPath: /var/www/html
      volumes:
      - name: wwwroot
        persistentVolumeClaim:
          claimName: frontend-v1
---
kind: Service
apiVersion: v1
metadata:
  name: tool
  labels:
    name: tool
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  selector:
    name: tool
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同样的，这里也需要执行&lt;code&gt;istioctl kube-inject&lt;/code&gt;进行注入，运行之后，就得到一个运行于集群内部的 Linux Shell，&lt;strong&gt;istio 中的路由策略经常是客户端和服务器协同完成的，因此上客户和服务器的 Deployment 都需要进行注入操作&lt;/strong&gt;。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Istio，Kubernetes 的微服务支持</title>
      <link>/post/istio-for-kubernetes-microservice/</link>
      <pubDate>Sun, 16 Jul 2017 17:17:08 +0800</pubDate>
      <guid>/post/istio-for-kubernetes-microservice/</guid>
      <description>

&lt;blockquote&gt;
&lt;p&gt;参考资料：
&lt;a href=&#34;https://developer.ibm.com/dwblog/2017/istio/&#34; target=&#34;_blank&#34;&gt;https://developer.ibm.com/dwblog/2017/istio/&lt;/a&gt;
&lt;a href=&#34;http://blog.kubernetes.io/2017/05/managing-microservices-with-istio-service-mesh.html&#34; target=&#34;_blank&#34;&gt;http://blog.kubernetes.io/2017/05/managing-microservices-with-istio-service-mesh.html&lt;/a&gt;
&lt;a href=&#34;https://istio.io/docs/&#34; target=&#34;_blank&#34;&gt;https://istio.io/docs/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;

&lt;p&gt;Istio 是一个由 IBM、Google 以及 Lyft 联合推出的开源软件，以无痛方式为运行在 Kubernetes 上的微服务提供流量管理，访问策略管理以及监控等功能。这一软件目前仅在 Kubernetes 上运行，今后可能会扩展到其他平台。本文会结合官方例子，完成安装和基础的监控内容。&lt;/p&gt;

&lt;h2 id=&#34;架构和组件&#34;&gt;架构和组件&lt;/h2&gt;

&lt;p&gt;总体架构如图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://istio.io/docs/concepts/what-is-istio/img/architecture/arch.svg&#34; alt=&#34;arch&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;envoy&#34;&gt;Envoy&lt;/h3&gt;

&lt;p&gt;一个 C++ 编写的高性能代理服务器，这里做了扩展，在 Istio 中会以 Sidecar 方式跟应用运行在同一 Pod 内，一方面可以接收并执行关于规则、流量拆分等方面的指令，另一方面能够产生各种指标用于监控和跟踪。&lt;/p&gt;

&lt;h3 id=&#34;mixer&#34;&gt;Mixer&lt;/h3&gt;

&lt;p&gt;Mixer 组件，主要进行访问控制以及策略控制，同时也负责从 Envoy 中获取各项指标。&lt;/p&gt;

&lt;h3 id=&#34;pilot&#34;&gt;Pilot&lt;/h3&gt;

&lt;p&gt;Pilot 是用户和 Isito 之间的桥梁，负责接收各种配置，并发送给各个组件。&lt;/p&gt;

&lt;h3 id=&#34;istio-auth&#34;&gt;Istio auth&lt;/h3&gt;

&lt;p&gt;内置认证和凭证管理，利用 TLS 提供服务之间、用户和服务之间的认证。
可以用来将没有加密支持的服务升级为加密版本，并且在网络策略之外，提供服务级别的策略控制，今后还会增加更多的鉴权和审计方面的能力。&lt;/p&gt;

&lt;h3 id=&#34;功能-特性&#34;&gt;功能 &amp;amp; 特性&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无需对现有服务进行变更&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;支持 http 1.1/2、gRPC 以及 TCP 流量的负载均衡和故障转移&lt;/li&gt;
&lt;li&gt;可替换的组件&lt;/li&gt;
&lt;li&gt;流量监控&lt;/li&gt;
&lt;li&gt;可提供身份认证功能&lt;/li&gt;
&lt;li&gt;可定制的路由规则&lt;/li&gt;
&lt;li&gt;错误处理，例如超时、重试、访问量控制、健康检查和熔断器等。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;

&lt;h3 id=&#34;先决条件&#34;&gt;先决条件&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;互联网连接&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;准备工作&#34;&gt;准备工作&lt;/h3&gt;

&lt;p&gt;首先去&lt;code&gt;https://github.com/istio/istio/releases&lt;/code&gt;下载最新版本（目前为 0.1.6），解压并设置 istioctl 命令的执行路径 。下面命令中描述的相对目录就是基于此目录&lt;/p&gt;

&lt;p&gt;执行命令&lt;code&gt;kubectl api-versions | grep rbac&lt;/code&gt;查看当前集群的 RBAC 设置：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;如果返回内容为空则无需理会。&lt;/li&gt;
&lt;li&gt;如果返回内容中包含&lt;code&gt;beta&lt;/code&gt;，则需要运行&lt;code&gt;kubectl apply -f install/kubernetes/istio-rbac-beta.yaml
&lt;/code&gt;来进行授权&lt;/li&gt;
&lt;li&gt;如果返回内容中只有&lt;code&gt;alpha&lt;/code&gt; ，就要&lt;code&gt;kubectl apply -f install/kubernetes/istio-rbac-beta.yaml
&lt;/code&gt;创建相应的角色和绑定了。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;运行&#34;&gt;运行&lt;/h3&gt;

&lt;p&gt;最后，运行&lt;code&gt;kubectl create -f install/kubernetes/istio.yaml&lt;/code&gt;创建一系列的 Deployment 以及 Services。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;注1： 其实上面的内容照做并不一定能成功，反正我的 1.6.6 上面是没能成功，Pilot Pod 中的 API Server 容器会出错，错误信息大概是“system:serviceaccount:default:istio-pilot-service-account&amp;rdquo; cannot get thirdpartyresources.extensions at the cluster scope.”，也就证明了 beta.yaml 中的 RBAC 设置是不够的，这里我把几个 RoleBinding 都改成为 ClusterRoleBinding，运行就成功了。&lt;/p&gt;

&lt;p&gt;注2：istio.yaml 中的 Ingress 服务是 Loadbalancer 类型的，如果测试集群不具备这样的条件，还请自行修改成其他合适内容。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;至此，基础的 istio 组件就已经运行完毕了。&lt;/p&gt;

&lt;h2 id=&#34;运行应用&#34;&gt;运行应用&lt;/h2&gt;

&lt;p&gt;安装包内包含了一个叫 bookinfo 的小应用，由 Product（入口页）、Detail 和 Review 三部分组成，具体应用 YAML 在安装目录的&lt;code&gt;samples/apps/bookinfo/bookinfo-v1.yaml&lt;/code&gt;文件中。打开文件我们会发现这是个很简单的小应用，无非是几个 Deployment 和 Service 的组合。&lt;/p&gt;

&lt;p&gt;该应用的微服务相互关系大致如下图所示，这个例子很好的展示了 istio 的一大特性——不拘泥于某种语言。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;/sites/default/files/get_image/c26c9a2771b4008770270ba15c6a0f8f.png&#34; alt=&#34;v1&#34; /&gt;&lt;/p&gt;

&lt;p&gt;istio 提供了一个工具叫 istioctl，这个工具的功能之一，就是把普通的应用 YAML 注入为 istio 支持的应用模式，例如：&lt;code&gt;istioctl kube-inject -f bookinfo-v1.yaml &amp;gt; bookinfo-istio-v1.yaml&lt;/code&gt;，比较新旧两个文件不难发现，这一工具为每个 Pod 新增了一个名为 proxy 的容器，以此接管流量，给监控和管理打下基础。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;/sites/default/files/get_image/42138edb04f53163c7a97702cfc66698.png&#34; alt=&#34;v1+istio&#34; /&gt;&lt;/p&gt;

&lt;p&gt;接下来我们运行这一应用：
- &lt;code&gt;kubectl create -f bookinfo-istio-v1.yaml&lt;/code&gt;：运行注入后的 应用
- &lt;code&gt;kubectl create -f bookinfo-ingress.yaml&lt;/code&gt;：创建 Ingress 资源&lt;/p&gt;

&lt;h2 id=&#34;监控&#34;&gt;监控&lt;/h2&gt;

&lt;p&gt;istio 内置了对 ServiceGraph、Prometheus 以及 Zipkin 的支持，简单的运行一下&lt;code&gt;kubectl create -f install/kubernetes/addons&lt;/code&gt;，就会启用这几个服务。注意这几个服务使用的也都是 Loadbalancer 模式，读者应根据集群情况自行修改。&lt;/p&gt;

&lt;p&gt;各个服务启动之后，可以使用负载工具例如 wrk 对 Productpage 页面进行一段时间的访问，来模拟工作情况。&lt;/p&gt;

&lt;h2 id=&#34;grafana&#34;&gt;Grafana&lt;/h2&gt;

&lt;p&gt;Grafana 启动后，我们可以使用服务暴露的地址进行访问，在其中可以看到如下图所示的 Dashboard，展示了我们应用中的几个服务的各种监控数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;/sites/default/files/get_image/16a47d78ee8e4046f8e8a90f5ded543f.png&#34; alt=&#34;grafana&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;service-graph&#34;&gt;Service Graph&lt;/h2&gt;

&lt;p&gt;这一个服务提供了两个入口，一个是 &lt;code&gt;/dotvz&lt;/code&gt;，以图形的方式供服务关系展示，而&lt;code&gt;/graph&lt;/code&gt;则提供了 JSON 格式的文档供结构化使用。例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
    &amp;quot;nodes&amp;quot;: {
        &amp;quot;details&amp;quot;: {},
        &amp;quot;productpage&amp;quot;: {},
        &amp;quot;reviews&amp;quot;: {},
        &amp;quot;unknown&amp;quot;: {}
    },
    &amp;quot;edges&amp;quot;: [
        {
            &amp;quot;source&amp;quot;: &amp;quot;productpage&amp;quot;,
            &amp;quot;target&amp;quot;: &amp;quot;reviews&amp;quot;,
            &amp;quot;labels&amp;quot;: {
                &amp;quot;qps&amp;quot;: &amp;quot;0.030416&amp;quot;,
                &amp;quot;version&amp;quot;: &amp;quot;v1&amp;quot;
            }
        },
        {
            &amp;quot;source&amp;quot;: &amp;quot;unknown&amp;quot;,
            &amp;quot;target&amp;quot;: &amp;quot;productpage&amp;quot;,
            &amp;quot;labels&amp;quot;: {
                &amp;quot;qps&amp;quot;: &amp;quot;0.030416&amp;quot;,
                &amp;quot;version&amp;quot;: &amp;quot;v1&amp;quot;
            }
        },
        {
            &amp;quot;source&amp;quot;: &amp;quot;productpage&amp;quot;,
            &amp;quot;target&amp;quot;: &amp;quot;details&amp;quot;,
            &amp;quot;labels&amp;quot;: {
                &amp;quot;qps&amp;quot;: &amp;quot;0.030416&amp;quot;,
                &amp;quot;version&amp;quot;: &amp;quot;v1&amp;quot;
            }
        }
    ]
}	
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;zipkin&#34;&gt;Zipkin&lt;/h2&gt;

&lt;p&gt;Istio 提供了开箱即用的 Zipkin 跟踪支持，打开页面之后，我们会看到 Zipkin 的查找页面，并无特殊内容，就不展开细谈了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;/sites/default/files/get_image/89331f5f59d1ba743b2d839763198946.png&#34; alt=&#34;zipkin&#34; /&gt;&lt;/p&gt;

&lt;p&gt;文中介绍内容只是很粗浅的入门操作，后续会进一步跟进、挖掘和学习这一系统的其他强大功能。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>微服务</title>
      <link>/post/microservice/</link>
      <pubDate>Wed, 02 Mar 2016 05:41:53 +0800</pubDate>
      <guid>/post/microservice/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://martinfowler.com/articles/microservices.html&#34; target=&#34;_blank&#34;&gt;Microservices&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;翻到 &lt;sup&gt;2&lt;/sup&gt;&amp;frasl;&lt;sub&gt;3&lt;/sub&gt; 的时候看到一篇疑似译文，不过并未注明原作和出处，恩。反正我这是学习笔记。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“微服务”——另一个软件架构的流行词。本来我们应该对这类东西扔去一个蔑视的眼神，不过这个名词所描述的软件系统越来越多。过去几年中我们看到很多这种风格的项目，这甚至成为构建企业应用的缺省方式。然而可惜的是，微服务是什么？如何实现微服务？这两个问题却没有明确的解答。&lt;/p&gt;

&lt;p&gt;简单来说，微服务架构是一种开发应用的方法，应用由一系列的小服务构成，每个小服务都拥有各自的进程，并提供轻量级的通讯机制（一般是 HTTP 资源 API）。这些服务围绕业务能力进行构建，并且通过全自动方式进行部署。这些服务可能用不同语言、使用不同数据存储来进行实现，最大限度的降低对中心化管理中心的需求。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;微服务这个词最早是在 2011 年 5 月威尼斯一个软件架构会议上讨论时，用于描述与会人员当时所探索的通用架构模式。在 2012 年 5 月，同一组人确定了这个最合理的命名。James 在 2012 年 3 月的举行于 Krakow 的 33rd Degree 会议上，以 [Microservices - Java, the Unix Way] 为题，以案例研究的形式阐述了其中的部分理念；几乎同时，Fred George 发表了题为 &lt;a href=&#34;http://www.slideshare.net/fredgeorge/micro-service-architecure&#34; target=&#34;_blank&#34;&gt;MicroService Architecture&lt;/a&gt;的演讲。Netflix 的 Adrian Cockcroft 称这种方式为 &amp;ldquo;细纹理 SOA&amp;rdquo;，他和文中提到的其他人（Joe Walnes, Dan North, Evan Botcher and Graham Tackley），是这一架构的先驱者。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在解释微服务之前，先比较一下 monolithic（ 单一庞大的，整块巨石构成的 ）模式：以单个单元构建而成的引用。企业应用通常由三个部分组成：一个客户端的用户界面（包含 HTML 页面以及运行在客户浏览器中的 JavaScript）、一个数据库（通常是一个关系型数据库管理系统，包含很多数据表）以及一个服务端应用。服务端会处理 HTTP 请求，执行业务逻辑，对数据库中的数据进行读写，选择和处理 HTML 视图，并发送给浏览器。这一个服务端应用就是一个单一逻辑的可执行应用，所有对系统的变更都需要构建和发布这一应用的新版本。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://www.jianshu.com/p/546ef242b6a3&#34; target=&#34;_blank&#34;&gt;巨石&lt;/a&gt; 这个称呼似乎不错。&lt;/p&gt;

&lt;p&gt;monolith 这个词在 Unix 社区用了一段时间了。出自 &lt;a href=&#34;http://www.amazon.com/gp/product/B003U2T5BA?ie=UTF8&amp;amp;tag=martinfowlerc-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B003U2T5BA&#34; target=&#34;_blank&#34;&gt;The Art of Unix Programming&lt;/a&gt;，用来称呼过分庞大的系统。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Monolithic 服务器是一个自然而然的想法。对所有请求的处理都是在同一个进程里面实现，可以使用语言的功能来把应用拆分为类、函数以及命名空间。可以在开发者的笔记本上运行和测试应用，并利用发布管线来确认变更都通过测试并部署到生产环境。应用能够用多实例的方式进行水平扩展。&lt;/p&gt;

&lt;p&gt;Monolithic 应用是可以成功的，不过随着人数的增加，可能就让人不满了 —— 尤其是更多应用被部署到云环境之中的情况下。即使是应用中的一小部分的变更，也会要求整个应用重建和部署。项目的模块化结构也越来越难于维护，这又导致变更的影响范围很难被限制在模块之内。只能对整个应用的负载进行伸缩，也浪费了更多的资源。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/2002807d03556bbb619583259db824e5.png&#34; alt=&#34;sketch&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 1 ：Monoliths 和 微服务&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Monolithic 应用把所有的功能放入一个进程，只能以应用的粒度进行集群设置。&lt;/li&gt;
&lt;li&gt;微服务架构把每个功能元素放入单独的服务中，可以按需对服务进行集群设置。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些不满造就了微服务这样的架构风格：以一套服务的方式构建应用。每个服务都可以独立的部署和伸缩，每个服务都有严格的边界，甚至可以用不同的语言来开发不同的服务。每个服务也可以被不同的团队来管理。&lt;/p&gt;

&lt;p&gt;我们不认为微服务是个创新，他的根基就是 Unix 的设计原则。然而我们认为，使用这一架构思想的人还不够多，否则软件开发的过程会比现在愉快得多。&lt;/p&gt;

&lt;h2 id=&#34;微服务架构的特性&#34;&gt;微服务架构的特性&lt;/h2&gt;

&lt;p&gt;微服务架构还没有一个正式的定义，然而我们可以尝试描述一下这一架构的特性。当然，并不是说所有微服务架构都会具有所有这些特性，我们希望多数微服务应用能够体现多数的特性。这一社区很松散，我们尝试描述我们自己的团队以及我们知道的其他团队的相关工作，而不是尝试制定一个需要严格尊重的定义。&lt;/p&gt;

&lt;h3 id=&#34;服务方式实现的组件化&#34;&gt;服务方式实现的组件化&lt;/h3&gt;

&lt;p&gt;自从我们进入软件工业开始，就重复做着一个美梦——像物理世界一样，用可插接的零件来构建系统。过去二十年中，我们也看到这方面的巨大进步，多数语言平台都有了颇具规模的常规库。&lt;/p&gt;

&lt;p&gt;当说起组件时，我们陷入一个困境——如何定义组件？我们说组件是软件的一个可替换的、可升级的单元。&lt;/p&gt;

&lt;p&gt;微服务架构也会使用库，但是这一架构实现组件化的方式是把软件拆分为服务。库一般会被 Link 到程序之中，并在内存中被调用；而服务是进程外的组件，只能通过 Web 服务或者远程调用等方式进行调用（这是一概念和很多面向对象的服务对象概念是不同的）&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;包括我们自己在内的很多面向对象的设计者，在 &lt;a href=&#34;http://www.amazon.com/gp/product/0321125215?ie=UTF8&amp;amp;tag=martinfowlerc-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0321125215&#34; target=&#34;_blank&#34;&gt;领域驱动设计模型&lt;/a&gt; 中使用服务对象这个词，用于描述一种没有绑定到实体，但却负载业务过程的对象。这跟本文中使用的“服务”一词是完全不同的概念。很不幸，我们必须忍受这个多义词的困扰。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;把服务看成组件（而不是库）的一个主要的原因是，服务是可以独立部署的。如果你有一个应用在单独进程中包含了多个库，对其中任何组件的变更都会导致整个应用的重新部署。如果应用解耦称为多个服务，就可以只对发生变更的服务进行重新部署了。但有些变更会导致服务接口的变更，从而对依赖这一服务的其他服务造成影响，因此独立部署这一特点并不是绝对的。好的微服务架构要通过服务边界的内聚，以及在服务协议中封装升级机制，来减少这种情况的发生。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://martinfowler.com/bliki/ApplicationBoundary.html&#34; target=&#34;_blank&#34;&gt;软件的边界&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;另外一个用服务做组件的结果就是更直接的组件界面。多数语言并没有一个好的机制来定义一个清晰的&lt;a href=&#34;http://martinfowler.com/bliki/PublishedInterface.html&#34; target=&#34;_blank&#34;&gt;公开接口&lt;/a&gt;。一般只会有文档和规则来阻止客户端破坏组件的封装，这就会造成组件间的耦合。服务方式则可以比较容易的通过限制远程调用的机制来避免这种情况。&lt;/p&gt;

&lt;p&gt;如此使用服务也会有坏处。相比进程内调用，远端调用的成本相对高昂，(为了节省调用成本)，就需要把远程 API 设计成为粗粒度的结构，但这样的话，又会难于使用。如果想要在不同组件之间移动功能，这一难度在使用服务（跨越进程）的情况下会大大增加。&lt;/p&gt;

&lt;p&gt;服务似乎可以映射为进程，不过只是似乎。一个服务可以包含很多一起开发和部署的进程，例如一个应用进程和一个该进程专属的数据库。&lt;/p&gt;

&lt;h3 id=&#34;根据业务功能进行组织&#34;&gt;根据业务功能进行组织&lt;/h3&gt;

&lt;p&gt;在尝试将一个大应用拆分开来的过程中，一般是着眼于技术层面，代领 UI 团队、服务端团队、以及数据库团队协作完成任务。如此的组织方式下，即使是简单的变更，也需要团队间的协作，需要完成时间和预算的相关流程。有的团队会围绕这一情况进行优化，两害相权取其轻——能访问哪个应用就把逻辑放到哪里。换句话说，逻辑到处都是。这是康威定律的具体体现。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;http://www.melconway.com/Home/Committees_Paper.html&#34; target=&#34;_blank&#34;&gt;一个组织设计出来的系统，其结构取决于该组织的通讯结构。&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Melvyn Conway, 1967&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&#34;images/conway.png&#34; alt=&#34;conway&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 2：康威定律&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;微服务的拆分规则是不同的，是以业务能力维度来进行拆分的。服务实现某种业务能力的一个方面，包括用户界面、持久存储，以及任何的外部协作。因此，这个团队也要是多功能的，具有这项开发任务的所有必要技能：用户体验、数据库以及项目管理。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/border.png&#34; alt=&#34;服务边界和团队边界&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;http://www.comparethemarket.com/&#34; target=&#34;_blank&#34;&gt;www.comparethemarket.com&lt;/a&gt; 采用了这样的组织结构。具有交叉功能的团队负责建设和运维每个产品，每个产品都被拆分称为一系列独立的服务，服务之间使用消息总线进行通信。&lt;/p&gt;

&lt;p&gt;大型的 Monolithic 应用也能围绕业务能力进行模块化。当然我们可以建立一个庞大团队来建立一个按业务线拆分的大应用。这一做法的主要问题是，这样的应用会跨越很多模块，相应的就会具有很多的上下文，团队成员的短期记忆很难适应任务的切换。而且这里面还有一个问题就是，要制定很多的规则，来保证模块的边界。&lt;/p&gt;

&lt;h3 id=&#34;产品不是项目&#34;&gt;产品不是项目&lt;/h3&gt;

&lt;p&gt;我们所见的多数应用开发都是使用项目模型：把完成的软件交付出去。交付之后，完成软件的团队就解散了。&lt;/p&gt;

&lt;p&gt;微服务的倡导者们建议避开这种模型，而建议由同一个团队掌握产品的整个生命周期。一个例子就是 Amazon 的 “谁开发谁运行”（&lt;a href=&#34;https://queue.acm.org/detail.cfm?id=1142065&#34; target=&#34;_blank&#34;&gt;You build, you run it&lt;/a&gt;），开发团队要承担软件在生产环境的运行情况。这让开发者每天都会关注到产品的运行情况，并增进同用户的沟通，至少会分担一部分支持的责任。&lt;/p&gt;

&lt;p&gt;旧有模式把系统看成一堆待开发完成的功能，而产品心态则更进一步——关注软件是否能够帮助用户增强其业务能力。&lt;/p&gt;

&lt;p&gt;Monolithic 应用也可以采用这一目标，但是小粒度的服务更容易在用户和开发者之间产生联系。&lt;/p&gt;

&lt;h3 id=&#34;智能端点和哑管道&#34;&gt;智能端点和哑管道&lt;/h3&gt;

&lt;p&gt;当在不同进城之间建立通信结构时，我们会看到很多产品和方法都试图把智能嵌入到通信机制当中去。一个明显的例子就是 ESB，ESB 产品经常会包含消息路由、编排、转换以及业务规则等功能。&lt;/p&gt;

&lt;p&gt;微服务社区倾向于另外一种方式：*智能端点和哑管道*。微服务方式构建的应用力求高内聚和低耦合， ——每个服务都像 Unix 中的 Filter 一样工作——接受请求，处理，输出响应。使用简单的 REST 风格的协议，而不是 WX 或者 BPEL 之类的复杂协议。&lt;/p&gt;

&lt;p&gt;最常见的两个协议是 带有资源 API 的 HTTP 请求-响应以及轻量级消息协议。最好的表达是：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;成为 Web，而不是基于 Web
-- Ian Robinson
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;为了追求伸缩性的极致，有些组织最后会转向 &lt;a href=&#34;https://code.google.com/p/protobuf/&#34; target=&#34;_blank&#34;&gt;protobufs&lt;/a&gt; 这样的二进制协议。使用了这样的协议，仍然是智能端点 + 哑管道的，只是为了性能损失了透明性。对大多数 Web 应用来说是无需做出这样的妥协的——透明本身也是明显的优势。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;微服务团队使用的规则和协议，正是互联网的基础。常用的资源可以轻易地由开发或运维人员进行缓存。&lt;/p&gt;

&lt;p&gt;另一种常用的方法是轻量级的消息总线。一般会选择哑管道类型（就是说只使用消息路由功能）例如 RabbitMQ 或者 ZeroMQ 这样的产品，除去提供可靠的异步功能，几乎没有其他能力。智能始终存在于生产或消费消息的服务端点。&lt;/p&gt;

&lt;p&gt;在 Monolith 应用中，组件在进程内执行，通信是通过方法或者函数的调用来实现的。因此从 Monolith 迁移到微服务的最大问题就是通信方式的改变。简单的从内存方法调用转换为 RPC 并不合适。应该把通信从细粒度转为粗粒度。&lt;/p&gt;

&lt;h3 id=&#34;去中心化的治理&#34;&gt;去中心化的治理&lt;/h3&gt;

&lt;p&gt;中心化治理的一个后果就是单技术平台的倾向。经验表明，这种方法有其固有的局限性——不能用一个方案解决所有问题。我们建议用合适的工具对付特定的任务。Monolithic 应用在某些情况下也可以使用多种语言，但这并不是一个常规的情况。&lt;/p&gt;

&lt;p&gt;把 Monolith 应用拆分为服务，我们就有了选择每个服务实现方式的机会。用 Node.js 来启动一个简单的报告页面？用 C++ 来完成一些接近实时的任务？为了更好的读取性能而选择其他风格的数据库？每一种问题我们都有办法分开解决了。&lt;/p&gt;

&lt;p&gt;当然了，能用/会用什么办法，并不意味着就应该用这种办法。但是经过这样的拆分，你就有了选择。&lt;/p&gt;

&lt;p&gt;构建微服务的团队倾向于一种不同的标准化的方法。与纸面规矩不同，我们更愿意制作有用的工具，帮助其他开发者解决类似的问题。这些工具一般来自于项目，分享给更多人，也不排除使用一种内部的开源模型。现在 Git 和 Github 已经成为版本控制系统的事实标准，开源实践也日益盛行。&lt;/p&gt;

&lt;p&gt;Netflix 就是一个遵循这种哲学的例子。分享有用的、经过高强度测试的代码，鼓励其他开发者用相似的办法解决相似的问题，对有实际需求的另辟蹊径的解决方法也抱以开放的心态。分享库集中于一些基础问题，包括数据存储、进程间通信以及后面我们会讨论到的——基础设施自动化。&lt;/p&gt;

&lt;p&gt;对于微服务社区来说，开销不是个有吸引力的问题。这并不是说社区不重视服务合同（协议），而是因为想要做到更多，&lt;a href=&#34;http://martinfowler.com/bliki/TolerantReader.html&#34; target=&#34;_blank&#34;&gt;Tolerant Reader&lt;/a&gt; 和 &lt;a href=&#34;http://martinfowler.com/articles/consumerDrivenContracts.html&#34; target=&#34;_blank&#34;&gt;Consumer Driven&lt;/a&gt; 都是微服务中常见的模式。这些服务协议正在独立发展。在服务中采用 Consumer Driven 方式，能够快速获取服务是否正常工作的反馈信息。澳大利亚的一个团队就是使用这种方式。他们利用简单的工具来为服务定义合同。这一自动过程在服务代码编写之前就得以执行。服务只有在满足合同要求时才能被构建，这样在构建新软件的时候，就优雅的规避了 &amp;lsquo;YAGNI&amp;rsquo; 困境。这些技术和工具不断成长，通过解耦服务的方式避免了对中心管制的依赖。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;YAGNI 或者说“你不需要他”，是一个 &lt;a href=&#34;http://c2.com/cgi/wiki?YouArentGonnaNeedIt&#34; target=&#34;_blank&#34;&gt;XP 原则&lt;/a&gt;，在确定需要之前，不要加入新功能。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;可能去中心化治理的最高点就是 Amazon 了。一个团队要负责其开发的软件的包括 &lt;sup&gt;24&lt;/sup&gt;&amp;frasl;&lt;sub&gt;7&lt;/sub&gt; 运维在内的方方面面的工作。放权到这个层次当然不是很常见，不过我们看到越来越多的公司正在把职权推给开发团队。Netflix 是另外一个采用这种办法的公司。每天凌晨三点钟被传呼机叫醒，绝对会提升你编写代码时对质量的警觉度。这种理念同传统的中心治理模型渐行渐远。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adrian Cockcroft 在 2013 年 11 月的&lt;a href=&#34;http://www.slideshare.net/adrianco/flowcon-added-to-for-cmg-keynote-talk-on-how-speed-wins-and-how-netflix-is-doing-continuous-delivery&#34; target=&#34;_blank&#34;&gt;演讲&lt;/a&gt;中特别的提到“开发者自助”以及“开发者负责运行”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;去中心化的数据管理&#34;&gt;去中心化的数据管理&lt;/h3&gt;

&lt;p&gt;数据管理的去中心化有很多的表现形式。在最抽象的层面，他意味着在不同的系统中，世界的模型是不同的。这在大企业的集成过程中很常见，例如在销售视图中一个称为客户的对象，在客服系统中可能完全不同。在不同环境下一个对象可能有不同的属性。&lt;/p&gt;

&lt;p&gt;这种情况往往发生在应用之间，不过也可能在应用内部的组件之间发生。领域驱动设计观念中的&lt;a href=&#34;http://martinfowler.com/bliki/BoundedContext.html&#34; target=&#34;_blank&#34;&gt;限界上下文（Bounded Context）&lt;/a&gt;是一个有用的思考这一问题的途径。DDD 把一个复杂的域拆分为多个限界上下文，并在中间对关系进行映射。这一过程对 Monolithic 和微服务架构都有效的，不过微服务根据业务能力进行的拆分在这方面更清晰，更接近。&lt;/p&gt;

&lt;p&gt;概念模型上的去中心化之外，微服务还对数据存储进行了去中心化。Monolithic 应用倾向于一个单独的逻辑数据库来存储持久化数据，因为数据库厂商的授权方式，企业往往也希望单独的一个数据库为多个应用服务。微服务则更希望每个服务管理自己的数据库，数据库可以是同样的，也可以是完全不同的，这种方式被称为&lt;a href=&#34;http://martinfowler.com/bliki/PolyglotPersistence.html&#34; target=&#34;_blank&#34;&gt;混合持久化( Polyglot Persistence )&lt;/a&gt;，当然 Monolithic 应用也可以这样使用，但是这种用法更多的还是出现在微服务架构中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/polyglot-persistent.png&#34; alt=&#34;Polyglot Persistence&#34; /&gt;&lt;/p&gt;

&lt;p&gt;微服务之间的数据的去中心化，隐含了更新管理的要求。一般的方法是在更新多个资源的时候，利用事务来保证一致性，这种方式在 Monoliths 应用中最为常见。&lt;/p&gt;

&lt;p&gt;事务在解决一致性问题的同时，也造成了时间上的耦合，在多服务应用中实现也是有困难的。而分布式事务的难度可以说是街知巷闻了，因此微服务架构&lt;a href=&#34;http://www.eaipatterns.com/ramblings/18_starbucks.html&#34; target=&#34;_blank&#34;&gt;强调服务间的非事务协调&lt;/a&gt;，只对最终一致性做出保证，其他的问题通过补偿操作进行处理。&lt;/p&gt;

&lt;p&gt;如何管理不一致的数据，也给开发团队带来新的挑战，不过这本来也是业务实践过程中常见的情况。现实世界中的业务经常会容忍一定程度的不一致，以此换取快速响应需求的能力，然后用某些反冲过程来处理不一致造成的问题。相对于保证高度一致性的成本，这种对不一致的妥协方式是行之有效的。&lt;/p&gt;

&lt;h3 id=&#34;基础设施自动化&#34;&gt;基础设施自动化&lt;/h3&gt;

&lt;p&gt;近年来，针对基础设施的自动化技术取得了长足的进步。云计算、AWS 带来的进步，成功的降低了构建、部署以及运维微服务应用的复杂性。&lt;/p&gt;

&lt;p&gt;很多微服务架构的产品和系统都是由具有&lt;a href=&#34;http://martinfowler.com/bliki/ContinuousDelivery.html&#34; target=&#34;_blank&#34;&gt;持续交付&lt;/a&gt;或&lt;a href=&#34;http://martinfowler.com/articles/continuousIntegration.html&#34; target=&#34;_blank&#34;&gt;持续集成&lt;/a&gt;经验的团队所构建的。这种团队在自动化技术方面有很多应用，下图描述了构建流程：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/build_pipeline.png&#34; alt=&#34;basic build pipeline&#34; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;图 5：基础构建流程&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;编译，单元和功能测试（Build 环境运行）&lt;/li&gt;
&lt;li&gt;验收测试（部署到 Build 环境）&lt;/li&gt;
&lt;li&gt;集成测试（部署到集成环境）&lt;/li&gt;
&lt;li&gt;用户验收测试（部署到用户验收测试环境）&lt;/li&gt;
&lt;li&gt;性能测试（部署到性能测试环境）&lt;/li&gt;
&lt;li&gt;发布到生产环境&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本文的焦点并非持续交付，因此这里只会提到一些关键点。我们要尽可能的保证我们的软件可以运行，所以要运行很多的自动测试。能够运行的软件在流程中的“前进”意味着自动部署到指定环境。&lt;/p&gt;

&lt;p&gt;Monolithic 应用也可以被轻松的 Build，测试和推送到制定环境中。事实证明，一旦应用实现了自动化，那么应用的发布就不再惊悚了。需要强调的是，持续交付的目标就是让部署变得无聊，所以不管是一个还是三个应用，只要是无聊的，就是没有问题的。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;这里似乎不够坦诚。很明显发布多个服务是比发布单个的 Monolith 应用更复杂的，幸运的是，这种复杂度可以通过工具化来降低。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;另外一个需要自动化的场合就是管理正在生产环境运行的微服务应用。上面提到的部署过程区别并不大，然而在运维过程中，自动化就会产生明显的区别了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/deployment.png&#34; alt=&#34;module deployment oftern differs&#34; /&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monolith：多个模块运行在同一进程中。&lt;/li&gt;
&lt;li&gt;微服务：模块运行在不同的进程中&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;图 6：模块部署的区别*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;面向故障的设计-desgin-for-failure&#34;&gt;面向故障的设计（Desgin for failure）&lt;/h3&gt;

&lt;p&gt;既然把服务作为组件来使用，那么自然而然的，应用就应该适当的容忍服务的故障。每个服务的提供者都可能出现调用失败的情况，客户端必须尽可能的对此做出正确的应对。相对于 Monolithic 应用来说，微服务架构在这里引入了更多的复杂性，因此这是微服务架构的劣势之一。后果就是微服务团队必须要考虑服务失败对用户体验的影响。Netflix 的 &lt;a href=&#34;https://github.com/Netflix/SimianArmy&#34; target=&#34;_blank&#34;&gt;Simian Army 项目&lt;/a&gt; 可以模拟服务甚至是数据中心的故障，来检测应用的恢复能力以及监控能力。&lt;/p&gt;

&lt;p&gt;服务既然随时可能失败，那么对服务的快速检测，并尽可能的恢复服务，就很重要了。实时监控对于微服务应用来说是至关重要的，监控内容包括架构级别的要素（数据库的每秒请求数量），以及业务相关的维度（例如每分钟订单数）。监控系统应该能够在系统不正常的时候，警告开发人员介入调查。&lt;/p&gt;

&lt;p&gt;这对微服务架构来说是很重要的，有了服务编排和&lt;a href=&#34;http://martinfowler.com/eaaDev/EventCollaboration.html&#34; target=&#34;_blank&#34;&gt;事件协作&lt;/a&gt;，微服务架构表现了一定的自发性。虽然很多人认为自发行为是一大优势，然而自发行为也有失控的可能。所以监控在这里就至关重要了，当错误的自发行为产生时，能够快速的进行修复。&lt;/p&gt;

&lt;p&gt;Monoliths 也可以像微服务一样的透明化——其实本该如此。不同之处在于，微服务架构中，必须知道断开的功能是属于哪个服务哪个进程的。在同一个进程中的话，这一过程就没什么用处了。&lt;/p&gt;

&lt;p&gt;微服务团队期望有一个承载日志和监控的仪表盘，来展示服务状态以及业务信息。&lt;/p&gt;

&lt;h3 id=&#34;迭代设计&#34;&gt;迭代设计&lt;/h3&gt;

&lt;p&gt;微服务的实践者一般都有迭代式设计的背景，他们看到了服务分解这一特性，让开发者能够在控制应用的变更。变更控制并不意味着减少变更，而是说用正确的态度和工具来频繁、快速，可控的对软件进行变更。&lt;/p&gt;

&lt;p&gt;尝试把软件系统拆分为组件时，会面对一个决策：怎么拆？一个独立的组件，应该是可以独立升级，并可整体被替换的——也就是说我们可以在不影响相关的其他组件的情况下，重写一个组件。事实上很多微服务组织更进一步，宁愿过分细碎，也不愿参杂不清。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dan North 更愿意称之为可替换组件架构，而不是微服务。这似乎是我们后面要谈到的特性的一个子集。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Guardian 网站是一个很好的 Monolith 应用的例子，但是也已经像微服务方向发展了。Monolith 仍然是网站的主干，但是他们利用 Monolith 的 API，建立微服务，来实现新的功能。这种办法对一些短期的内部功能非常有效，例如一个体育赛事的专题页面。这种方式搭建的部分页面能够用快速开发的语言迅速上线，然后在赛事结束之后立刻停掉。我们已经在一些金融机构见到类似的做法：为一个市场机会、活动建立新的服务，结束后再将服务下线。&lt;/p&gt;

&lt;p&gt;模块化设计的规则中，可替换原则，是一个让设计能够经得住变更的考验的重要部分。要保证同一个模块内的变化能够同步进行。系统中较少改变的部分应该沉淀到稳定的经过实践检验的服务中期。重复的同时修改两个服务的情况是一个应当做出服务合并决策的象征。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Kent Beck 以此作为 &lt;a href=&#34;http://www.amazon.com/gp/product/0321413091?ie=UTF8&amp;amp;tag=martinfowlerc-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0321413091&#34; target=&#34;_blank&#34;&gt;实施模式&lt;/a&gt; 中的一个设计规则。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;把组件转换为服务，对于进度的保证也是颇有助益的。在 Monolith 应用中，所有的变更都需要完整的构建和部署。在微服务应用中，只需要重新部署发生变更的服务。这样就简化了发布流程，提高了发布效率。随之而来也有一个问题就是，服务的变更可能会破坏该服务的下游应用。传统的对付这一问题的方法是版本控制，不过在微服务世界中，&lt;a href=&#34;http://martinfowler.com/articles/enterpriseREST.html#versioning&#34; target=&#34;_blank&#34;&gt;这种办法只是权宜之计&lt;/a&gt;，我们要想方设法通过对下游服务的容错设计来应对上有服务的变化。&lt;/p&gt;

&lt;h2 id=&#34;微服务就是未来&#34;&gt;微服务就是未来？&lt;/h2&gt;

&lt;p&gt;本文的主要目的在于阐述微服务的思路和规则。通过这一过程，我们明确了微服务架构是值得认真对待的企业应用构建方式。我们以及其他一些团队最近用这种方式实施了很多项目；其中包括 Amazon，Netflix，&lt;a href=&#34;http://www.theguardian.com/&#34; target=&#34;_blank&#34;&gt;The Guardian&lt;/a&gt;，&lt;a href=&#34;https://gds.blog.gov.uk/&#34; target=&#34;_blank&#34;&gt;英国政府数字服务&lt;/a&gt;，&lt;a href=&#34;http://realestate.com.au&#34; target=&#34;_blank&#34;&gt;realestate.com.au&lt;/a&gt; 以及 &lt;a href=&#34;http://www.comparethemarket.com/&#34; target=&#34;_blank&#34;&gt;comparethemarket.com&lt;/a&gt;。2013 年的会议中充满了转向微服务架构的例子——其中包括 Travis CI。另外，还有很多组织正跃跃欲试——虽然可能不叫微服务（经常还是会被称为 SOA，我们之前也说过，SOA 有很多相互矛盾的表达）&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SOA 也不是这一形式的源头。我记得当 SOA 团队在本世纪初出现的时候就有人说——我们几年前就开始做这个了。有一个争论点就是，这种风格很像早期企业中使用的 COBOL 程序，他们利用数据文件来进行通信。另外也有人说微服务跟 Erlang 的编程模型很相近，只不过是套上了企业应用的外衣。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;尽管有很多肯定的经验，我们也不希望做出微服务代表软件架构未来的结论。虽然对比 Monolith 有很多优点，然而这一方式资历尚浅，仍需时间检验。&lt;/p&gt;

&lt;p&gt;架构方面的决策后果往往要多年以后才能被证明。我们见过具有强烈模块化愿望的优秀团队，开发出的 Monolithic 架构在多年以后轰然坍塌。很多人微服务有清晰的边界，不易被污染，因此这种后果应该不会出现在微服务应用上。然而我们还是需要看到更多的系统，运行更长的时间，才能做出微服务架构已经成熟的判断。&lt;/p&gt;

&lt;p&gt;还有影响微服务成熟的因素是，组件化的成功与否，要看软件和组件的契合程度，组件的边界也是很难清楚界定的。虽然通过软件设计和重构能够降低对组件切分的难度，然而组件服务化之后，对进程间通信进行重构的难度是高于原有的进程内调用的。不同服务之间的代码移动非常困难，任何接口的变化都需要在参与方之间进行协调，需要进行更多的兼容考虑，测试也变得更为复杂。&lt;/p&gt;

&lt;p&gt;另外一个问题是如果组件的边界设计有问题的话，会让一些本应属于组件内的方法变成服务间的调用，这样的情况增加的不只是复杂性，而且还模糊了功能边界，降低了控制能力。大家都认为小的简单的组件能降低难度，却容易忽略服务之间错综复杂的连接问题。&lt;/p&gt;

&lt;p&gt;最后，还需要考虑团队的技能水准。高水准的团队才能更好地掌握新技术。对高水平团队来说很有效的技术可能在低水平团队来说完全无法应用。水平低下的团队是无法做好系统的，不论 Monolith 还是微服务架构，都无法改变这一事实。&lt;/p&gt;

&lt;p&gt;还有一个经常被讨论的问题就是，不应该直接进入微服务架构，而是应该以 &lt;a href=&#34;http://martinfowler.com/bliki/MonolithFirst.html&#34; target=&#34;_blank&#34;&gt;Monolish 架构开始&lt;/a&gt;，然后完成组件化过程，如果 Monolith 无法胜任，才需要迁移到微服务架构。（&lt;a href=&#34;http://martinfowler.com/articles/dont-start-monolith.html&#34; target=&#34;_blank&#34;&gt;这种建议并不理想&lt;/a&gt;，因为进程内的接口并不一定适合转向服务接口。）&lt;/p&gt;

&lt;p&gt;所以我们的建议是谨慎乐观。然而我们毕竟看到了，微服务模式&lt;a href=&#34;http://martinfowler.com/microservices/&#34; target=&#34;_blank&#34;&gt;值得一试&lt;/a&gt;。用于决策的信息永远都是不完整的，这正是是软件最大的挑战之一，微服务也是一样，现在下结论还为时尚早。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
