Service Mesh(服务网格)概念及Istio初体验
分布式架构发展史:
单机小型机时代:
1969年,阿帕网诞生,最初是为了军事目的,后来衍变成Internet。2000年左右,互联网在中国开始盛行起来,但是那时候网民数较少,所以多数企业服务单一,采用集中式部署的方式就能满足需求
但是这样的部署方式带来了一定的问题,一旦小型机或者数据库出现问题,会导致整个系统的故障,而且功能修改发布也不方便,所以不妨把大系统拆分成多个子系统,比如“用户”,“商品”,“论坛“等,也就是”垂直拆分“,并且每个子系统都有各自的硬件
集群化负载均衡时代:
用户量越来越大,就意味着需要更多的小型机,价格昂贵,操作维护成本高,不妨把小型机换成普通的PC机,采用多台PC机部署同一个应用的方案,但是此时就需要对这些应用做负载均衡,因为客户端不知道请求哪一个。
负载均衡可以分为硬件和软件,硬件比如F5,软件比如LVS(Linux Virtual Server),nginx,haproxy。负载均衡的思路是对外暴露一个统一的接口,然后根据用户的请求进行对应规则的转发。同时负载均衡还可以做健康检查、限流等有了负载均衡,后端的应用就可以根据流量的大小进行动态的扩缩容了,也就是“水平扩展”。
服务化时代:
此时发现在用户,商品和订单等中有重复的功能,比如登录、注册、邮件等。一旦项目大了,集群部署多了,这些重复的功能无疑是浪费资源,不妨把重复的功能抽取出来,起个名字叫“服务Service”。其实对于“服务”现在已经比较广泛了,比如“基础设施即服务IaaS”、“平台即服务PaaS”、“软件即服务SaaS“等。这时候急需解决的就是服务之间的调用问题,RPC(Remote Procedure Call),同时这种调用关系得维护起来,比如某个服务在哪里,是不是得知道?所以不仅仅要解决服务调用的问题,还要解决服务治理的问题,比如像Dubbo,默认采用Zookeeper作为注册中心,Spring Cloud使用Eureka作为注册中心当然,要关注的还有很多,比如限流、降级、负载均衡、容错等
分布式微服务时代:
在分布式架构下,服务可能拆分的没那么细,可以进一步的拆分 Microservices。Java中微服务主流解决方案Spring Cloud,Spring Cloud可以通过引入几个注解,写少量代码完成微服务架构中的开发,相比Dubbo而言方便很多。并且使用的是HTTP协议,所以对多语言也可以很好地支持。
Service Mesh(服务网格)时代:
微服务时代有了Spring Cloud就完美了吗?不妨想一想会有哪些问题
- 最初是为了业务而写代码,比如登录功能、支付功能等,到后面会发现要解决网络通信的问题,虽然有Spring Cloud里面的组件帮我们解决了,但是细想一下它是怎么解决的?引入以来dependency,加注解,写配置,最后将这些非业务功能的代码打包一起部署,和业务代码在一起,称为“侵入式框架”;
- 微服务中的服务支持不同语言开发,维护不同语言和非业务代码的成本;
- 业务代码开发者应该把更多的精力投入到业务熟悉度上,而不应该是非业务上,Spring Cloud虽然能解决微服务领域的很多问题,但是学习成本还是较大的;
- 对于Spring Cloud而言,并不是微服务领域的所有问题都有对应的解决方案,也就是功能有限,比如Content Based Routing和Version Based Routing。当然可以将Spring Cloud和Service Mesh结合起来使用,也就是对SC的补充、扩展和加强等;
- 互联网公司产品的版本升级是非常频繁的,为了维护各个版本的兼容性、权限、流量等,因为SpringCloud是“代码侵入式的框架”,这时候版本的升级就注定要让非业务代码一起,一旦出现问题,再加上多语言之间的调用,工程师会非常痛苦;
- 其实大家有没有发现,服务拆分的越细,只是感觉上轻量级解耦了,但是维护成本却越高了,那么怎么办呢?网络的问题当然还是要交给网络本身来解决
从微服务发展的角度上来说,我们所面临的首要的问题就是网络问题,TCP/IP协议解决了计算机之间的通信问题。微服务的出现带来了服务与服务之间的通信问题,我们应该知道,对于开发者而言,最好的方式是能实现服务与服务之间就好比计算机与计算机之间的通信一样,是无感知的。
问题解决思路:
本质上是要解决服务之间通信的问题,不应该将非业务的代码融合到业务代码中,也就是从客户端发出的请求,要能够顺利到达对应的服务,这中间的网络通信的过程要和业务代码尽量无关
通信的问题:服务发现、负载均衡、版本控制、蓝绿部署等
在很早之前的单体架构中,其实通信问题也是需要写在业务代码中的,那时候怎么解决的呢?把网络通信,流量转发等问题放到了计算机网络模型中的TCP/UDP层,也就是非业务功能代码下沉
那就不妨把这些通信的问题都交给计算机网络模型组织去解决咯?别人肯定不会答应,怎么办?既然不答应,那我们就自己想办法解决通信问题,搞一些产品岂不快哉?没错,代理嘛
不妨这样在帮每个服务配置一个代理,所有通信的问题都交给这个代理去做大家肯定接触过,比如最初Nginx、HaProxy等,其实它们做反向代理把请求转发给其他服务器,也就为Service Mesh的诞生和完成提供了一个解决思路
一些公司对于代理的探索Sidecar:
很多公司借鉴了Proxy模式,推出了Sidecar的产品,比如像14年Netflix的Prana、15年唯品会的local proxy其实Sidecar模式和Proxy很类似,但是Sidecar功能更全面,也就是Sidecar功能和侵入式框架的功能对应
这种Sidecar是为特定的基础设施而设计的,也就是跟公司原有的框架技术绑定在一起,不能成为通用性的产品,所以还需要继续探索。
Service Mesh之Linkerd:
2016年1月,离开Twitter的基础设施工程师William Morgan和Oliver Gould,在github上发布了Linkerd 0.0.7版本,从而第一个Service Mesh项目由此诞生。并且Linkerd是基于Twitter的Finagle开源项目,实现了通用性。后面又出现了Service Mesh的第二个项目Envoy,并且在17年都加入了CNCF项目。
Linkerd解决了通用性问题,在Linkerd思想要求所有的流量都走Sidecar,而原来的Sidecar方式可以走可以直连,这样一来Linkerd就帮业务人员屏蔽了通信细节,也不需要侵入到业务代码中,让业务开发者更加专注于业务本身。
但是Linkerd的设计思想在传统运维方式中太难部署和维护了,所以就后来没有得到广泛的关注,其实主要的问题是Linkerd只是实现了数据层面的问题,但没有对其进行很好的管理。
Service Mesh之Istio:
由Google、IBM和Lyft共同发起的开源项目,17年5月发布0.1 release版本,17年10月发布0.2 release版本,18年7月发布1.0 release版本。很明显Istio不仅拥有“数据平面(Data Plane)”,而且还拥有“控制平面(Control Plane),也就是拥有了数据接管与集中控制能力。
官网:https://istio.io/docs/concepts/what-is-istio
What is a service mesh?
随着单片应用程序向分布式微服务体系结构的转变,Istio解决了开发人员和操作人员面临的挑战。要了解如何做到这一点,请更详细地查看Istio的服务网格。术语ServiceMesh(服务网格)用于描述组成这些应用程序的微服务网络以及它们之间的交互。随着服务网格的规模和复杂性的增长,它会变得更加难以理解和管理。它的需求可以包括发现、负载平衡、故障恢复、度量和监视。服务网格通常还有更复杂的操作需求,比如A/B测试、canary推出、速率限制、访问控制和端到端身份验证。Istio提供了对整个服务网格的行为洞察和操作控制,提供了一个完整的解决方案来满足微服务应用程序的各种需求。
为何Service Mesh能够迅速走红?随着微服务和容器化技术的发展,使得开发和运维的方式变得非常方便。从而让服务能够方便地迁移到不同的云平台上。回顾一下Service Mesh的发展历程
- 借鉴反向代理,对Proxy模式进行探索,让非业务的代码下沉
- 使用Sidecar弥补Proxy模式功能的不足,解决“侵入式框架”中非业务代码的问题
- Linkerd解决传统Sidecar模式中通用性的问题
- Istio增加了控制平面,解决整个系统中的流量完全控制的问题
what is Istio ?
基于Sidecar模式、数据平面和控制平台、主流Service Mesh解决方案
云平台为使用它们的组织提供了大量的好处。然而,不可否认采用云会给DevOps团队带来压力。开发人员必须使用微服务来构建可移植性,同时运营商也在管理非常大的混合和多云部署。Istio允许您连接、保护、控制和观察服务。在高层次上,Istio有助于降低这些部署的复杂性,并减轻开发团队的压力。它是一个完全开源的服务网格,透明地分层到现有的分布式应用程序上。它也是一个平台,包括一些api,可以将其集成到任何日志平台、遥测技术或策略系统中。Istio的多样化特性集使您能够成功、高效地运行分布式微服务体系结构,并提供统一的方式来保护、连接和监视微服务。
Why use Istio ?
通过负载平衡、service-to-service身份验证、监视等方法,Istio可以轻松地创建部署的服务网络,而服务代码中的代码更改很少或没有更改。通过在整个环境中部署一个特殊的sidecar代理来拦截微服务之间的所有网络通信,从而向服务添加Istio支持,然后使用其控制平面功能来配置和管理Istio,其中包括:
- HTTP、gRPC、WebSocket和TCP流量的自动负载平衡。
- 通过丰富的路由规则、重试、故障转移和故障注入对流量行为进行细粒度控制。
- 支持访问控制、速率限制和配额的可插拔策略层和配置API。
- 集群内所有流量的自动度量、日志和跟踪,包括集群入口和出口。
- 在具有强大的基于身份的身份验证和授权的集群中实现安全的服务到服务通信。
Istio是为可扩展性而设计的,可以满足不同的部署需求。Sidecar的方式解决了数据通信的问题,而在Istio中还加入了控制平面,所有的流量都能够有效地被控制,也就是通过控制平面可以控制整个系统。
在Istio中到底能解决哪些问题:
- 针对HTTP、gRPC、WebSocket等协议的自动负载均衡
- 故障的排查、应用的容错、众多路由
- 流量控制、全链路安全访问控制与认证
- 请求遥测、日志分析以及全链路跟踪
- 应用的升级发布、频率限制和配合等
Istio Architecture:
有了感性的认知和功能了解之后,接下来就要考虑落地的问题了,也就是Istio的架构图该如何设计。说白了就是根据数据平面和控制平面的理念,该怎么设计架构图,这个官方已经帮我们考虑好了。
Architecutre :https://istio.io/docs/ops/deployment/architecture/
Istio服务网格在逻辑上分为数据平面和控制平面。数据平面由一组部署为边车(Sidecar)的智能代理(Envoy)组成。这些代理负责协调和控制微服务之间的所有网络通信,以及一个通用策略和遥测中心。控制平面管理并将代理配置为路由流量。此外,控制平面配置混频器来执行策略和收集遥测数据。下图显示了组成每个平面的不同组件:
Envoy:
Istio使用Envoy代理的扩展版本。Envoy是用c++开发的高性能代理,用于协调服务网格中所有服务的所有入站和出站流量。特使代理是唯一与数据飞机通信交互的Istio组件。Envoy代理被部署为服务的sidecars,在逻辑上增加了Envoy的许多内置特性,例如:
- 动态服务发现
- 负载平衡
- TLS终止
- HTTP/2和gRPC代理
- 断路器
- 健康检查
- 分阶段推出基于%的流量分割
- 故障注入
- 丰富的指标
这种sidecar部署允许Istio提取大量关于流量行为的信号作为属性。反过来,Istio可以在混合器中使用这些属性来执行策略决策,并将它们发送到监控系统以提供关于整个网格的行为的信息。sidecar代理模型还允许您向现有部署添加Istio功能,而不需要重新架构或重写代码。您可以阅读更多关于为什么我们在设计目标中选择这种方法的信息。由Envoy代理启用的一些Istio功能和任务包括:
- 流量控制功能:通过丰富的HTTP、gRPC、WebSocket和TCP流量路由规则来执行细粒度的流量控制。
- 网络弹性特性:设置重试、故障转移、断路器和故障注入。
- 安全性和身份验证特性:执行安全性策略和通过配置API定义的访问控制和速率限制。
Kubernetes CRD(K8s资源拓展):
官网:https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
Kubernetes平台对于分布式服务部署的很多重要的模块都有系统性的支持,借助如下一些平台资源可以满足大多数分布式系统部署和管理的需求。
但是在不同应用业务环境下,对于平台可能有一些特殊的需求,这些需求可以抽象为Kubernetes的扩展资源,而Kubernetes的CRD(CustomResourceDefinition)为这样的需求提供了轻量级的机制,保证新的资源的快速注册和使用。在更老的版本中,TPR(ThirdPartyResource)是与CRD类似的概念,但是在1.9以上的版本中被弃用,而CRD则进入的beta状态。Istio就是使用CRD在Kubernetes上建构出一层Service Mesh的实现
这里就是需要创建 istio-1.0.6/install/kubernetes/helm/istio/templates/crds.yaml 这个资源.
安装 Istio:
官网:https://istio.io/docs/setup/getting-started/ 需要Kuberbetes 集群做支撑
Before you can install Istio, you need a cluster running a compatible version of Kubernetes. Istio 1.4 has been tested with Kubernetes releases 1.13, 1.14, 1.15.Create a cluster by selecting the appropriate platform-specific setup instructions.
1.下载 istio-1.0.6-linux.tar.gz
2.解压 tar -zxvf istio-1.x.y.tar.gz,进入到istio文件目录下
3.将itsioctl添加到环境变量中
export PATH=$PWD/bin:$PATH
4.进入查看 istio-1.0.6/install/kubernetes/istio-demo.yaml 文件 。提前准备好镜像,所有节点
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/proxy_init:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/hyperkube:v1.7.6_coreos.0
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/galley:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/proxyv2:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/grafana:5.2.3
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/mixer:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/pilot:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/prometheus:v2.3.1
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/citadel:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/servicegraph:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/sidecar_injector:1.0.6
docker pull registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/all-in-one:1.5
5.把阿里云镜像仓库的镜像打成原有的tag
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/proxy_init:1.0.6 docker.io/istio/proxy_init:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/hyperkube:v1.7.6_coreos.0 quay.io/coreos/hyperkube:v1.7.6_coreos.0
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/galley:1.0.6 docker.io/istio/galley:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/proxyv2:1.0.6 docker.io/istio/proxyv2:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/grafana:5.2.3 grafana/grafana:5.2.3
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/mixer:1.0.6 docker.io/istio/mixer:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/pilot:1.0.6 docker.io/istio/pilot:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/prometheus:v2.3.1 docker.io/prom/prometheus:v2.3.1
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/citadel:1.0.6 docker.io/istio/citadel:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/servicegraph:1.0.6 docker.io/istio/servicegraph:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/sidecar_injector:1.0.6 docker.io/istio/sidecar_injector:1.0.6
docker tag registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/all-in-one:1.5 docker.io/jaegertracing/all-in-one:1.5
6.删除阿里云镜像仓库下载的镜像
docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/proxy_init:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/hyperkube:v1.7.6_coreos.0 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/galley:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/proxyv2:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/grafana:5.2.3 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/mixer:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/pilot:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/prometheus:v2.3.1 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/citadel:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/servicegraph:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/sidecar_injector:1.0.6 docker rmi registry.cn-hangzhou.aliyuncs.com/wuzz-istio-k8s/all-in-one:1.5
7.安装istio核心组件,根据istio-1.0.6/install/kubernetes/istio-demo.yaml创建资源
kubectl apply -f istio-demo.yaml
第一次创建出现:unable to recognize "install/kubernetes/istio.yaml": no matches for config.istio.io/, Kind=attributemanifest 。耐心等待一下就行,资源较多,速度没那么快。
kubectl get pods -n istio-system
kubectl get svc -n istio-system 可以给某个service配置一个ingress规则,访问试试
大家都知道,K8s中是通过Pod来部署业务,流量的进出是直接跟Pod打交道的那在Istio中如何体现sidecar的作用呢?在Pod中除了业务的container,再增加一个container为sidecar岂不是快哉?
手动注入sidecar:
准备一个资源 first-istio.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: first-istio spec: selector: matchLabels: app: first-istio replicas: 1 template: metadata: labels: app: first-istio spec: containers: - name: first-istio image: registry.cn-hangzhou.aliyuncs.com/wuzz-docker/test-docker-image:v1.0 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: first-istio spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: app: first-istio type: ClusterIP
kubectl apply -f first-istio.yaml
kubectl get pods -> # 注意该pod中容器的数量 可以看到这里只有一个。
kubectl get svc
curl 10.103.54.200:8080/dockerfile
删除上述资源,重新创建,使用手动注入sidecar的方式
kubectl delete -f first-istio.yaml
istioctl kube-inject -f first-istio.yaml | kubectl apply -f -
可以看到这里产生了2个容器,那么我们看看这生成的另一个容器是什么?
kubectl describe pod pod-name -n istio-system
很明显,这个新的容器就是Envoy-proxy
这样一来,就手动给pod中注入了sidecar的container,也就是envoy sidecar。其实这块是先改变了yaml文件的内容,然后再创建的pod:kubectl get pod pod-name -o yaml
删除资源: istioctl kube-inject -f first-istio.yaml | kubectl delete -f -
自动注入sidecar:
上述每次都进行手动创建肯定不爽咯,一定会有好事之者来做这件事情。这块需要命名空间的支持,比如有一个命名空间为istio-demo,可以让该命名空间下创建的pod都自动注入sidecar。
1.创建命名空间 :kubectl create namespace istio-demo
2.给命名空间加上label :kubectl label namespace istio-demo istio-injection=enabled
3.创建资源 :kubectl apply -f first-istio.yaml -n istio-demo
4.查看资源。这个时候达到的效果跟上面是一致的,只不过已经自动创建了 Envoy-proxy
kubectl get pods -n istio-demo
kubectl describe pod pod-name -n istio-demo
kubectl get svc -n istio-demo
5.删除资源:kubectl delete -f first-istio.yaml -n istio-demo
Istio 与 prometheus和grafana:
其实istio已经默认帮我们安装好了grafana和prometheus,只是对应的Service是ClusterIP,我们按照之前K8s的方式配置一下Ingress访问规则即可,但是要提前有Ingress Controller的支持哦
1.寻找prometheus的yaml配置(搜索istio-demo.yaml文件)找到下面这个:
# Source: istio/charts/prometheus/templates/service.yaml apiVersion: v1 kind: Service metadata: name: prometheus # 主要是为了定位到prometheus的Service
namespace: istio-system
2.配置prometheus的Ingress规则 :
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: prometheus-ingress
namespace: istio-system
spec:
rules:
- host: prometheus.istio.wuzz.com
http:
paths:
- path: /
backend:
serviceName: prometheus
servicePort: 9090
3.访问grafana的配置(istio-demo.yaml文件)
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: istio-system
4.配置grafan的Ingress规则 :
#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: grafana-ingress
namespace: istio-system
spec:
rules:
- host: grafana.istio.wuzz.com
http:
paths:
- path: /
backend:
serviceName: grafana
servicePort: 3000
5.根据两个ingress创建资源
kubectl get ingress -n istio-system
访问 prometheus
访问 grafana: