# 二、微服务概览

## 微服务概览

前期在调研一些文档，然后回头再看k8s相关的资料的时候，介绍到云原生应用的概念，大致如下：

1.作为单个实体进行管理和部署的应用程序通常称为单体应用。最初开发应用程序时，单体有很多好处。它们更易于理解，并允许您在不影响其他服务的情况下更改主要功能。

2.随着应用程序复杂性的增长，单体应用的益处逐渐减少。它们变得更难理解，而且失去了敏捷性，因为工程师很难推断和修改代码。

3.对付复杂性的最好方法之一是将明确定义的功能分成更小的服务，并让每个服务独立迭代。这增加了应用程序的灵活性，允许根据需要更轻松地更改部分应用程序。每个微服务可以由单独的团队进行管理，使用适当的语言编写，并根据需要进行独立扩缩容。

4.只要每项服务都遵守强有力的合约，应用程序就可以快速改进和改变。当然，转向微服务架构还有许多其他的考虑因素。其中最不重要的是弹性通信，我们在附录A中有讨论。.只要每项服务都遵守强有力的合约，应用程序就可以快速改进和改变。我们无法考虑转向微服务的所有考虑因素。拥有微服务并不意味着您拥有云原生基础设施。虽然微服务是实现您的应用程序灵活性的一种方式，但正如我们之前所说的，它们不是云原生应用程序的必需条件。

我们无法考虑转向微服务的所有考虑因素。拥有微服务并不意味着您拥有云原生基础设施。如果您想阅读更多，我们推荐Sam[Pivotal](https://pivotal.io/)是云原生应用的提出者，并推出了[Pivotal Newman的BuildingCloud Microservices（O'Reilly，2015）。虽然微服务是实现您的应用程序灵活性的一种方式，但正如我们之前所说的，它们不是云原生应用程序的必需条件。Foundry](https://pivotal.io/platform)云原生应用平台和[Spring](https://spring.io/)开源 Java 开发框架，成为云原生应用架构中先驱者和探路者。

后期整个学习主线应该朝如下展开：容器->Kubernetes->微服务->Cloud Native（云原生）->Service Mesh（服务网格）->

使用场景->Open Source（开源）。

#### 微服务优点

微服务是最近几年很流行的一种架构模式，相比其他架构模式有着诸多优点。如：

* 每个微服务很小
  * 易于开发者理解，上手快，易开发、易重构
  * IDE更快，使得开发更具生产力
  * 启动速度更快，也使得开发更具生产力，同时加快应用部署
* 每个服务可以独立部署，从而更容易频繁部署新版本的服务
* 容易扩大开发规模。微服务使你能够跨团队开发。每个团队服务自己的一个或多个单一服务。每个团队可以独立于其他团队开发、部署、伸缩他们的服务
* 改善故障隔离。比如，如果一个服务内存泄漏，那么只有这个服务被影响，其它服务仍然能够继续处理请求。而在单体架构中，一个不工作的组件可能拖垮整个系统
* 每个服务可以独立开发和部署，易于持续集成/交付
* 避免长期绑定到一个技术栈。当开发一个新的服务时，你可以使用一个新的技术栈。类似地，当对一个已有服务做出大的改动时，你可以使用一种新的技术栈轻松重写它。

#### 微服务缺点

微服务不是银弹，它也有着自己的缺点：

* 一个分布式系统天然的复杂性
  * 测试更加困难
  * 开发者必须实现服务通信机制
  * 数据的一致性问题
* 部署复杂性。在生产环境中，还存在着部署和管理一个由很多不同类型服务组成的系统的运维复杂性
* 中小规模下增加的资源消耗。在中小规模下，微服务比单体应用消耗更多资源。在大规模环境下，微服务凭借其独立部署的优势能够比单体应用有更好的资源利用率。

微服务模式

业界对于微服务开发已经有了一套实践模式，如：

* 服务注册
* 服务发现
* 负载均衡
* 配置管理
* 断路器
* API网关
* 每个服务一个数据库
* 消息驱动的微服务
* 事件源
* CQRS
* 访问令牌
* 消费者驱动的契约测试
* 日志聚合
* 应用监控
* 审计日志
* 分布式追踪
* 异常追踪
* 健康监测
* 每个服务一个容器

这些微服务模式中的大多数都已经被Spring Cloud(套件)所实现，且开箱即用。但有一些需要经过一番努力才能够实现，如基于事件源实现最终一致性，命令查询职责分离(CQRS)，消息驱动的微服务。而这些模式对于微服务能否成功落地至关重要。

### 容器 <a href="#rong-qi" id="rong-qi"></a>

> 容器——Cloud Native的基石

容器最初是通过开发者工具而流行，可以使用它来做隔离的开发测试环境和持续集成环境，这些都是因为容器轻量级，易于配置和使用带来的优势，docker和docker-compose这样的工具极大的方便的了应用开发环境的搭建，开发者就像是化学家一样在其中小心翼翼的进行各种调试和开发。

随着容器的在开发者中的普及，已经大家对CI流程的熟悉，容器周边的各种工具蓬勃发展，俨然形成了一个小生态。（图片来自网络）

![](https://jimmysong.io/kubernetes-handbook/images/container-ecosystem.png%20)

该生态涵盖了容器应用中从镜像仓库、服务编排、安全管理、持续集成与发布、存储和网络管理等各个方面，随着在单主机中运行容器的成熟，集群管理和容器编排成为容器技术亟待解决的问题。

### 为什么使用Kubernetes <a href="#wei-shi-mo-shi-yong-kubernetes" id="wei-shi-mo-shi-yong-kubernetes"></a>

> Kubernetes——让容器应用进入大规模工业生产。

**Kubernetes是容器编排系统的事实标准**

在单机上运行容器，无法发挥它的最大效能，只有形成集群，才能最大程度发挥容器的良好隔离、资源分配与编排管理的优势，而对于容器的编排管理，Swarm、Mesos和Kubernetes的大战已经基本宣告结束，Kubernetes成为了无可争议的赢家。

下面这张图是Kubernetes的架构图（图片来自网络），其中显示了组件之间交互的接口CNI、CRI、OCI等，这些将Kubernetes与某款具体产品解耦，给用户最大的定制程度，使得Kubernetes有机会成为跨云的真正的云原生应用的操作系统。

![](https://jimmysong.io/kubernetes-handbook/images/kubernetes-high-level-component-archtecture.jpg)

随着Kubernetes的日趋成熟，“Kubernetes is becoming boring”，基于该“操作系统”之上构建的适用于不同场景的应用将成为新的发展方向，就像我们将石油开采出来后，提炼出汽油、柴油、沥青等等，所有的材料都将找到自己的用途，Kubernetes也是，毕竟我们谁也不是为了部署和管理容器而用Kubernetes，承载其上的应用才是价值之所在。

![](https://jimmysong.io/kubernetes-handbook/images/cloud-native-core-target.jpg)

云已经可以为我们提供稳定可以唾手可得的基础设施，但是业务上云成了一个难题，Kubernetes的出现与其说是从最初的容器编排解决方案，倒不如说是为了解决应用上云（即云原生应用）这个难题。

包括微服务和FaaS/Serverless架构，都可以作为云原生应用的架构。

![](https://jimmysong.io/kubernetes-handbook/images/redpoint-faas-landscape.jpg)

### 微服务 <a href="#wei-fu-wu" id="wei-fu-wu"></a>

> 微服务——Cloud Native的应用架构。

下图是[Bilgin Ibryam](https://developers.redhat.com/blog/author/bibryam/)给出的微服务中应该关心的主题，图片来自[RedHat Developers](https://developers.redhat.com/blog/2016/12/09/spring-cloud-for-microservices-compared-to-kubernetes/)。

![](https://3741439983-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LepFOgvAPfLlrdgMP9H%2F-LepFRUhYu_WfRxbQ_p8%2F-LepHAO2qNLq6RWbGDww%2Fimporsdt.png?generation=1557819604812989\&alt=media)

微服务带给我们很多开发和部署上的灵活性和技术多样性，但是也增加了服务调用的开销、分布式系统管理、调试与服务治理方面的难题。

**当前最成熟最完整的微服务框架可以说非**[**Spring**](https://spring.io/)**莫属，而Spring又仅限于Java语言开发，其架构本身又跟Kubernetes存在很多重合的部分，如何探索将Kubernetes作为微服务架构平台就成为一个热点话题。（后面有针对k8s 和spring cloud 实际项目做说明，包括实际shell 脚本）**

就拿微服务中最基础的**服务注册发现**功能来说，其方式分为**客户端服务发现**和**服务端服务发现**两种，Java应用中常用的方式是使用Eureka和Ribbon做服务注册发现和负载均衡，这属于客户端服务发现，而在Kubernetes中则可以使用DNS、Service和Ingress来实现，不需要修改应用代码，直接从网络层面来实现。

**使用Kubernetes构建云原生应用**

我们都是知道Heroku推出了适用于PaaS的[12 factor app](https://12factor.net/)的规范，包括如下要素：

1. 基准代码
2. 依赖管理
3. 配置
4. 后端服务
5. 构建，发布，运行
6. 无状态进程
7. 端口绑定
8. 并发
9. 易处理
10. 开发环境与线上环境等价
11. 日志作为事件流
12. 管理进程

另外还有补充的三点：

* API声明管理
* 认证和授权
* 监控与告警

如果落实的具体的工具，请看下图，使用Kubernetes构建云原生架构：（图片来自网络）

![](https://jimmysong.io/kubernetes-handbook/images/building-cloud-native-architecture-with-kubernetes.png)

结合这12因素对开发或者改造后的应用适合部署到Kubernetes之上，基本流程如下图所示：

![](https://jimmysong.io/kubernetes-handbook/images/creating-kubernetes-native-app.jpg)

迁移到云端架构，相对单体架构来说会带来很多挑战。比如自动的持续集成与发布、服务监控的变革、服务暴露、权限的管控等。

## 云原生架构定义 <a href="#yun-yuan-sheng-jia-gou-ding-yi" id="yun-yuan-sheng-jia-gou-ding-yi"></a>

**12因素应用**

12因素应用是一系列云原生应用架构的模式集合，最初由Heroku提出。这些模式可以用来说明什么样的应用才是云原生应用。它们关注速度、安全、通过声明式配置扩展、可横向扩展的无状态/无共享进程以及部署环境的整体松耦合。如Cloud Foundry、Heroku和Amazon ElasticBeanstalk都对部署12因素应用进行了专门的优化。

在12因素的背景下，应用（或者叫app）指的是独立可部署单元。组织中经常把一些互相协作的可部署单元称作一个应用。

12因素应用遵循以下模式：

**代码库**

每个可部署app在版本控制系统中都有一个独立的代码库，可以在不同的环境中部署多个实例。

**依赖**

App应该使用适当的工具（如Maven、Bundler、NPM）来对依赖进行显式的声明，而不该在部署环境中隐式的实现依赖。

**配置**

配置或其他随发布环境（如部署、staging、生产）而变更的部分应当作为操作系统级的环境变量注入。

**后端服务**

后端服务，例如数据库、消息代理应视为附加资源，并在所有环境中同等看待。

**编译、发布、运行**

构建一个可部署的app组件并将它与配置绑定，根据这个组件/配置的组合来启动一个或者多个进程，这两个阶段是严格分离的。

**进程**

该app执行一个或者多个无状态进程（例如master/work），它们之间不需要共享任何东西。任何需要的状态都置于后端服务（例如cache、对象存储等）。

**端口绑定**

该应用程序是独立的，并通过端口绑定（包括HTTP）导出任何/所有服务。

**并发**

并发通常通过水平扩展应用程序进程来实现（尽管如果需要的话进程也可以通过内部管理的线程多路复用来实现）。

**可任意处置性**

通过快速迅速启动和优雅的终止进程，可以最大程度上的实现鲁棒性。这些方面允许快速弹性缩放、部署更改和从崩溃中恢复。

**开发/生产平等**

通过保持开发、staging和生产环境尽可能的相同来实现持续交付和部署。

**日志**

不管理日志文件，将日志视为事件流，允许执行环境通过集中式服务收集、聚合、索引和分析事件。

**管理进程**

行政或管理类任务（如数据库迁移），应该在与app长期运行的相同的环境中一次性完成。

这些特性很适合快速部署应用程序，因为它们不需要对将要部署的环境做任何假定。对环境假设能够允许底层云平台使用简单而一致的机制，轻松实现自动化，快速配置新环境，并部署应用。以这种方式，十二因素应用模式能够帮我们优化应用的部署速度。

这些特性也很好地适用于突发需求，或者低成本地“丢弃”应用程序。应用程序环境本身是100％一次性的，因为任何应用程序状态，无论是内存还是持久性，都被提取到后端服务。这允许应用程序以易于自动化的非常简单和弹性的方式进行伸缩。在大多数情况下，底层平台只需将现有环境复制到所需的数目并启动进程。缩容是通过暂停正在运行的进程和删除环境来完成，无需设法地实现备份或以其他方式保存这些环境的状态。就这样，12因素应用模式帮助我们实现规模优化。

最后，应用程序的可处理性使得底层平台能够非常快速地从故障事件中恢复。

此外，将日志作为事件流处理能够极大程度上的增强应用程序运行时底层行为的可见性。

强制环境之间的等同、配置机制的一致性和后端服务管理使云平台能够为应用程序运行时架构的各个方面提供丰富的可见性。以这种方式，十二因素应用模式能够优化安全性。

### 微服务 <a href="#wei-fu-wu" id="wei-fu-wu"></a>

微服务将单体业务系统分解为多个“仅做好一件事”的可独立部署的服务。这件事通常代表某项业务能力，或者最小可提供业务价值的“原子“服务单元。

微服务架构通过以下几种方式为速度、安全、可扩展性赋能：

* 当我们将业务领域分解为可独立部署的有限能力的环境的同时，也将相关的变更周期解耦。只要变更限于单一有限的环境，并且服务继续履行其现有合约，那么这些更改可以独立于与其他业务来进行开展和部署。结果是实现了更频繁和快速的部署，从而实现了持续的价值流动。
* 通过扩展部署组织本身可以加快部署。由于沟通和协调的开销，添加更多的人，往往会使软件构建变得更加苦难。 弗雷德·布鲁克斯（Fred Brooks，人月神话作者）很多年前就教导我们，在软件项目的晚期增加更多的人力将会时软件项目更加延期。 然而，我们可以通过在有限的环境中构建更多的沙箱，而不是将所有的开发者都放在同一个沙箱中。
* 由于学习业务领域和现有代码的认知负担减少，并建立了与较小团队的关系，因此我们添加到每个沙箱的新开发人员可以更快速地提高并变得更高效。
* 可以加快采用新技术的步伐。大型单体应用程序架构通常与对技术堆栈的长期保证有关。这些保证的存在是为了减轻采用新技术的风险。采用了错误的技术在单体架构中的代价会更高，因为这些错误可能会影响整个企业架构。如果我们可以在单个整体的范围内采用新技术，将隔离并最大限度地降低风险，就像隔离和最小运行时故障的风险一样。
* 微服务提供独立、高效的服务扩展。单体架构也可以扩展，但要求我们扩展所有组件，而不仅仅是那些负载较重的组件。当且仅当相关联的负载需要它时，微服务才会被缩放。

### 自服务敏捷架构 <a href="#zi-fu-wu-min-jie-jia-gou" id="zi-fu-wu-min-jie-jia-gou"></a>

使用云原生应用架构的团队通常负责其应用的部署和持续运营。云原生应用的成功采纳者已经为团队提供了自服务平台。

正如我们创建业务能力团队为每个有界的环境构建微服务一样，我们还创建了一个能力小组，负责提供一个部署和运行这些微服务的平台。

这些平台中最大好处是为消费者提供主要的抽象层。通过基础架构即服务（IAAS），我们要求API创建虚拟服务器实例、网络和存储，然后应用各种形式的配置管理和自动化，以使我们的应用程序和支持服务能够运行。现在这种允许我们自定义应用和支持服务的平台正在不断涌现。

应用程序代码简单地以预构建的工件（可能是作为持续交付管道的一部分生成的）或Git远程的原始源代码的形式“推送”。 然后，平台构建应用程序工件，构建应用程序环境，部署应用程序，并启动必要的进程。 团队不必考虑他们的代码在哪里运行或如何到达那里，这些对用户都是透明得，因为平台会关注这些。

这样的模型同样适合于后端服务。需要数据库？ 消息队列或邮件服务器？ 只需要求平台来配合您的需求。平台现在支持各种SQL/NoSQL数据存储、消息队列、搜索引擎、缓存和其他重要的后端服务。这些服务实例然后可以“绑定”到您的应用程序，必要的凭据会自动注入到应用程序的环境中以供其使用。从而消除了大量凌乱而易出错的定制自动化。

这些平台还经常提供广泛的额外操作能力：

* 应用程序实例的自动化和按需扩展
* 应用健康管理
* 请求到或跨应用程序实例间的动态路由和负载均衡
* 日志和指标的聚合

这种工具的组合确保了能力团队能够根据敏捷原则开发和运行服务，从而实现速度，安全性和规模化。

### 基于API的协作 <a href="#ji-yu-api-de-xie-zuo" id="ji-yu-api-de-xie-zuo"></a>

在云原生应用程序架构中，服务之间的唯一互动模式是通过已发布和版本化的API。这些API通常是具有JSON序列化的HTTP REST风格，但也可以是其他协议和序列化格式。

只要有需要，在不会破坏任何现有的API协议的前提下，团队就可以部署新的功能，而不需要与其他团队进行同步。 自助服务基础设施平台的主要交互模式也是通过API，就像其他业务服务一样。供给、缩放和维护应用程序基础设施的方式不是通过提交单据，而是将这些请求提交给提供该服务的API。

通过消费者驱动的协议，可以在服务间交互的双方验证协议的合规性。服务消费者不能访问其依赖关系的私有实现细节，或者直接访问其依赖关系的数据存储。实际上，只允许有一个服务能够直接访问任何数据存储。这种强制解耦直接支持云原生的速度目标。
