使用Istio简化微服务系列一:如何用Isito解决Spring Cloud Netflix部署微服务的挑战?【转载】
概述
Istio 简化了服务间的通信,流量涨落,容错,性能监控,跟踪等太多太多。如何利用它来帮我们从各微服务中抽象萃取出基础架构和功能切面?
我写的这些关于 Istio 的文章是 Istio官网文档的子集。读官网文档可了解更多。
注意::如果你很熟悉微服务,请跳过背景介绍这段。
在本系列的第一部分,将涵盖如下内容:
- 背景: 单体应用及微服务介绍
- Spring Cloud Netflix Stack及其优势
- Istio 介绍
- Istio的服务-服务通信举例
背景
过去,我们运维着“能做一切”的大型单体应用程序。 这是一种将产品推向市场的很好的方式,因为刚开始我们也只需要让我们的第一个应用上线。而且我们总是可以回头再来改进它的。部署一个大应用总是比构建和部署多个小块要容易。
然而,这样的应用开发将导致“爆炸式的”工作量(我们经过数月的工作后将再次部署整个应用),并且增量变更将因为构建/测试/部署/发布周期等的复杂特性而来来回回折腾很长时间。但是,如果你是产品负责人,尤其是在部署一个新版本后发现一个严重的 Bug,那么这就不仅仅是多少钱的问题。 这甚至可能导致整个应用回滚。相对于比较小的组件来说,将这样的一个大型应用部署到云上并弹性扩展它们也并不容易。
进入微服务
微服务是运行在自己的进程中的可独立部署的服务套件。 他们通常使用 HTTP 资源进行通信,每个服务通常负责整个应用中的某一个单一的领域。 在流行的电子商务目录例子中,你可以有一个商品条目服务,一个审核服务和一个评价服务,每个都只专注一个领域。
用这种方法来帮助分布式团队各自贡献各种服务,而不需要在每个服务变更时去构建/测试/部署整个应用,而且调试也无需进入彼此的代码。 将服务部署到云上也更容易,因为独立的服务就能按需进行自动弹性扩展。
用这种方法让多语言服务(使用不同语言编写的服务)也成为可能,这样我们就可以让 Java/C++ 服务执行更多的计算密集型工作,让 Rails / Node.js 服务更多来支持前端应用等等。
Spring Cloud Netflix:
随着微服务的流行,简化服务的创建和管理的框架如雨后春笋。 我个人在2015年最喜欢的是 Netflix OSS 栈(Spring Cloud Netflix),它让我用一个非常简单的方式,通过 Spring Tool Suite IDE 来创建 Java 微服务。
我可以通过 Netflix 套件获得以下功能(图1):
- 通过 Eureka 进行服务注册- 用于注册和发现服务
- 用 Ribbon 做客户端的负载均衡- 客户端可以选择将其请求发送到哪个服务器。
- 声明 REST 客户端 Feign 与其他服务交谈。在内部,使用 Ribbon。
- API 网关用 Zuul —单一入口点来管理所有 API 调用,并按路由规则路由到微服务。
- Hystrix 做熔断器 — 处理容错能力以及在短时间内关闭通信信道(断开回路)并在目标服务宕机时返回用户友好的响应。
- 用 Hystrix 和 Turbine 做仪表板 —— 可视化流量和熔断
当然,这种构建和部署应用的方法也带来了它的挑战。
挑战
部署:怎样才能通过一种统一一致的方式将我们的服务部署到云中,并确保它们始终可用,并让它们按需进行自动弹性扩展?
横切关注点:如何对每个微服务代码改动很少甚至不改代码的情况下能获得更多我们所看到的 Spring Cloud Netflix 中所实现的功能? 另外,如何处理用不同语言编写的服务?
解决方案
部署:Kubernetes 已经为在 Google Kubernetes Engine(GKE)中高效部署和编排 Docker 容器铺平了道路。 Kubernetes 抽象出基础架构,并让我们通过 API 与之进行交互。 请参阅本文末尾的链接以获取更多详细信息。
横切关注点:我们可以用 Istio。 Istio 官网上的解释称:“ Istio 提供了一种简单的方法,来创建一个提供负载均衡、服务间认证、监控等的服务网络,且不需要对服务代码进行任何更改。 通过在整个环境中部署专门的 sidecar 代理服务,来拦截微服务间的所有网络通信,整个配置和管理通过 Istio的控制面板来做。”
Istio介绍:
换句话说,通过Istio,我们可以创建我们的微服务,并将它们与“轻量级 Sidecar 代理”一起部署(下图2),以处理我们的许多横切需求,例如:
- 服务到服务的通信
- 追踪
- 熔断(类 Hystrix 功能)和重试
- 性能监控和仪表板(类似于 Hystrix 和 Turbine 仪表板)
- 流量路由(例如:发送 x% 流量到 V2 版本的应用实例),金丝雀部署
- 一个额外的红利(特别是如果您正在处理医疗保健中的 PHI 等敏感数据时)出站(Istio 服务网格之外的外部可调用服务)需要明确配置,并且可以阻止在服务网格之外的做特殊调用的服务。
在上图2中,我们已经去掉了图1中的许多组件,并添加了一个新组件(Envoy Proxy)。 任何服务(A)如需与另一个服务(B)交谈,则提前对它的代理做路由规则预配置,以路由到对方的代理进行通信。 代理与代理交谈。 由于所有通信都是通过代理进行的,所以很容易监控流量,收集指标,根据需要使用熔断规则等。对横切面的声明式的配置规则和策略,无需更改任何服务代码,让我们可以更专注于最重要的事情:构建高业务价值的高质量的服务。
从高的层面看,Istio 有如下组件:
- Envoy:一个高性能,低空间占用的代理,支持服务之间的通信,并有助于负载平衡,服务发现等;
- 混合器:负责整个生态(服务网格)中所有服务的访问控制策略,并收集通过 Envoy 或其他服务发送的遥测信息;
- Pilot:帮助发现服务,流量缓慢调整和容错(熔断,重试等);
- Istio-Auth :用于服务间认证以及都使用 TLS 的终端用户认证。本文中的示例不使用 Istio-Auth。
用Istio进行服务—服务通信:
让我们在练习中了解它!
我们将举一个简单的例子,展示3个通过 Envoy 代理进行通信的微服务。它们已经用 Node.js 写好,但如前所述,你可以用任何语言。
- 宠物服务:通过调用 PetDetailsService 和 PetMedicalHistoryService 来返回宠物的信息和病史。 它将在9080端口上运行。
- 宠物详细信息服务:返回宠物信息,如姓名,年龄,品种,拥有者等,它将在端口9081上运行。
- 宠物医疗历史信息服务:返回宠物的病史(疫苗接种)。 它将在9082端口运行。
步骤:
在 GKE中创建一个 Kubernetes 集群(我叫 nithinistiocluster)。 确保缺省服务帐户具有以下权限:roles/container.admin
(Kubernetes Engine Admin)。
按照 https://istio.io/docs/setup/kubernetes/quick-start.html 中的说明安装 istio。
- 现在,我们准备将我们的应用程序(上述3个服务)部署到 GKE,并将边车代理注入到部署中。
- 在 github 仓库中,您将看到4个目录(安装各种组件时创建的istio目录和我的微服务的3个目录)。
- 对于每个微服务,我在 petinfo.yaml 文件的 kube 目录中创建了相应的 Kubernetes部署和服务。 服务名为宠物服务,宠物详细信息服务和宠物医疗历史信息服务。 由于PetService可以公开访问,因此它有一个指向 petservice 的 Kubernetes Ingress。
- 你可以转到每个服务目录,在 deploy.sh 文件中更新项目和群集名称并运行它。 它构建服务,创建 Docker 镜像,将其上传到Google Container Registry,然后运行 istioctl 以注入 Envoy 代理。 例如,对于 PetService,它看起来像:
#!/usr/bin/env bash export PROJECT=nithinistioproject export CONTAINER_VERSION=feb4v2 export IMAGE=gcr.io/$PROJECT/petservice:$CONTAINER_VERSION export BUILD_HOME=. gcloud config set project $PROJECT gcloud container clusters get-credentials nithinistiocluster --zone us-central1-a --project $PROJECT echo $IMAGE docker build -t petservice -f "${PWD}/Dockerfile" $BUILD_HOME echo 'Successfully built ' $IMAGE docker tag petservice $IMAGE echo 'Successfully tagged ' $IMAGE #push to google container registry gcloud docker -- push $IMAGE echo 'Successfully pushed to Google Container Registry ' $IMAGE # inject envoy proxy kubectl apply -f <(istioctl kube-inject -f "${PWD}/kube/petinfo.yaml")
在上面的代码中,最后一行显示了我们如何使用 Istio 命令行工具(istioctl)来将代理注入到我们的各种 Kubernetes 部署中。
Petservice 目录下的 petinfo.yaml 文件包含服务、部署和 Ingress的配置。 看起来像:
apiVersion: v1
kind: Service
metadata:
name: petservice
labels:
app: petservice
spec:
ports:
- port: 9080
name: http
selector:
app: petservice
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: petservice-v1
spec:
replicas: 1
template:
metadata:
labels:
app: petservice
version: v1
spec:
containers:
- name: petservice
image: gcr.io/nithinistioproject/petservice:feb4v2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
---
###########################################################################
# Ingress resource (gateway)
##########################################################################
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gateway
annotations:
kubernetes.io/ingress.class: "istio"
spec:
rules:
- http:
paths:
- path: /pet/.*
backend:
serviceName: petservice
servicePort: 9080
---
一旦运行了 deploy.sh,就可以通过执行以下命令来检查确认部署、服务和 Ingress 是否已经创建:
mallyn01$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
petdetailsservice-v1 1 1 1 1 1h
petmedicalhistoryservice-v1 1 1 1 1 58m
petservice-v1 1 1 1 1 54m
mallyn01$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.51.240.1 <none> 443/TCP 2d
petdetailsservice ClusterIP 10.51.255.10 <none> 9081/TCP 1h
petmedicalhistoryservice ClusterIP 10.51.244.19 <none> 9082/TCP 59m
petservice ClusterIP 10.51.242.18 <none> 9080/TCP 1h
petservice mallyn01$ kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
gateway * 108.59.82.93 80 1h
mallyn01$ kubectl get pods
NAME READY STATUS RESTARTS AGE
petdetailsservice-v1-5bb8c65577-jmn6r 2/2 Running 0 12h
petmedicalhistoryservice-v1-5757f98898-tq5j8 2/2 Running 0 12h
petservice-v1-587696b469-qttqk 2/2 Running 0 12h
当查看控制台中 pod 的信息,即使你只为每个容器部署了一项服务,但仍会注意到有2/2个容器正在运行。 另一个容器是 istioctl 命令注入的边缘代理。
5、 一旦上述所有内容都运行完毕,您可以使用 Ingress 的 IP 地址去调用示例端点来获取 Pet 的详细信息。
mallyn01$ curl http://108.59.82.93/pet/123
{
"petDetails": {
"petName": "Maximus",
"petAge": 5,
"petOwner": "Nithin Mallya",
"petBreed": "Dog"
},
"petMedicalHistory": {
"vaccinationList": [
"Bordetella, Leptospirosis, Rabies, Lyme Disease"
]
}
}
注意: 由于 PetService 调用 PetDetailsService 和 PetMedicalHistoryService,实际的调用将如下所示:
fetch('http://petdetailsservice:9081/pet/123/details')
.then(res => res.text())
.then(body => console.log(body));
;
fetch('http://petmedicalhistoryservice:9082/pet/123/medicalhistory')
.then(res => res.text())
.then(body => console.log(body));
;
结论:
我们覆盖了大量内容 (但这只是第一部分!!)
在随后的部分中,将详细介绍如何使用其他 Istio 特性,例如将流量逐步迁移到一个新升级的版本上,使用性能监控仪表板等等。
特别感谢 Ray Tsang 的关于 Istio 的演讲材料 。
参考链接:
- The Istio home page https://istio.io/
- DevOxx 的Ray Tsang的 Istio 演讲材料: https://www.youtube.com/watch?v=AGztKw580yQ&t=231s
- 案例的Github link: https://github.com/nmallya/istiodemo
- Kubernetes: https://kubernetes.io/
- 微服务: https://martinfowler.com/articles/microservices.html
- Spring Cloud Netflix: https://github.com/spring-cloud/spring-cloud-netflix