在Kubernetes上使用Istio进行微服务流量管理

Microservices Traffic Management Using Istio on Kubernetes

原文作者:Piotr Mińkowski

原文地址:https://dzone.com/articles/microservices-traffic-management-using-istio-on-ku

译者微博:@从流域到海域

译者博客:blog.csdn.net/solo95

在Kubernetes上使用Istio进行微服务流量管理

我已经在之前的一篇文章(5步在Kubernetes上搭建使用Istio的Service Mesh)中介绍了在Kubernetes上部署的两个微服务之间的路由配置的简单示例。如果您对Istio的基本信息以及通过Minikube在Kubernetes上进行的部署感兴趣,可以参考本文。今天,我们将基于上一篇关于Istio的文章中使用的相同示例应用程序,创建一些更高级的流量管理规则。

示例应用程序的源代码可在GitHub的sample-istio-serviceshttps://github.com/piomin/sample-istio-services.git中找到。有两个示例应用程序callme-servicecaller-service,并部署在两个不同的版本1.02.01.0版本在分支v1(https://github.com/piomin/sample-istio-services/tree/v1)中可以找到,而2.0版本在分支v2中(https://github.com/piomin/sample-istio-services/tree/v2)中可以找到。在不同版本中使用这些示例应用程序时,我将根据传入的请求中设置的HTTP标头向您展示不同的流量管理策略。

我们可能会强制callme-service将所有请求路由到特定版本的callme-service,方法是将标头x-version设置为v1v2。我们也可以不在请求中设置此标头,这会导致所有现有版本的服务之间的流量分割。如果请求到达caller-servicev1版本,那么流量在callme-service的两个实例之间按50-50进行分割(即五五开)。如果请求被caller-serviceV2版本的实例接收,那么75%的流量被转发到callme-servicev2版本,而只有25%到达v1版本。上面描述的情形已在下图中进行了说明。

在我们开始这个例子之前,我应该说一些关于Istio流量管理的话。如果你已经阅读过我之前关于Istio的文章,那么你可能会知道每个规则都被分配到了一个目的地。这些规则控制着服务网格内请求的路由过程。关于它们有一个非常重要的事情,特别是对于上图中所示的例子而言,可以将多个规则应用于相同的目的地。每条规则的优先级取决于规则的precedence域。有一个与该字段的值相关的原则:该整数字段的值越高,则对应规则的优先级越高。正如您可以猜到的那样,如果有多个具有相同优先级值的规则,则规则评估的顺序是未定义的。除了目的地之外,我们还可以定义请求的来源,以便仅将规则限制到特定的调用者。如果一个正在调用的服务有多个部署,我们甚至可以通过设置其来源的标签字段将其过滤掉(一部分)。当然,我们也可以指定HTTP请求的属性,例如URl,scheme或用于匹配请求和定义规则的header(即报文头,下面不再注释)。

现在我们来看看具有最高优先级的规则。它的名字是callme-service-v1(1)。它适用于callme-service(2),并且与其他规则(3)相比具有最高的优先级。它仅适用于由caller-service(4)发送的请求,这种请求包含带有v1(5)值的 HTTP header x-version。这条路线的规则只适用于callme-service(6)v1版本 。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: callme-service-v1 # (1)
spec:
  destination:
    name: callme-service # (2)
  precedence: 4 # (3)
  match:
    source:
      name: caller-service # (4)
    request:
      headers:
        x-version:
          exact: "v1" # (5)
  route:
  - labels:
      version: v1 # (6)

这是第一个图的片段,按此路线规则处理。

下一个规则callme-service-v2(1)的优先级较低(2)。但是,它与第一条规则不冲突,因为它仅适用于包含值为v2(3)的x-version header的请求。它把所有请求转发到的callme-service(4)v2版本。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: callme-service-v2 # (1)
spec:
  destination:
    name: callme-service
  precedence: 3 # (2)
  match:
    source:
      name: caller-service
    request:
      headers:
        x-version:
          exact: "v2" # (3)
  route:
  - labels:
      version: v2 # (4)

和上面一样,这里是第一个图的片段,它按这条路线规则处理。

下面代码片段中可见规则callme-service-v1-default(1)的优先级(2)低于前面描述的两个规则。在实践中,这意味着只有在前两条规则中定义的条件未得到满足时才能够执行。如果您没有在HTTP请求中传递headerx-version,或者它的值不同于v1v2,则会出现这种情况。下面可见的规则仅适用于标有v1version(3)的服务实例。最后,到callme-service的流量在两种版本的服务(4)之间以50比50的比例进行负载均衡。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: callme-service-v1-default # (1)
spec:
  destination:
    name: callme-service
  precedence: 2 # (2)
  match:
    source:
      name: caller-service
      labels:
        version: v1 # (3)
  route: # (4)
  - labels:
      version: v1
    weight: 50
  - labels:
      version: v2
    weight: 50

这是第一个图的片段,按此路线规则处理。

最后一条规则与前面描述的callme-service-v1-default非常相似。它的名字是callme-service-v2-default(1) ,它仅适用于caller-servicev2版本(3) 。它具有最低的优先级(2),并且按75-25的比例在callme-service两个版本之间按版本v2的偏好(4)进行分割。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: callme-service-v2-default # (1)
spec:
  destination:
    name: callme-service
  precedence: 1 # (2)
  match:
    source:
      name: caller-service
      labels:
        version: v2 # (3)
  route: # (4)
  - labels:
      version: v1
    weight: 25
  - labels:
      version: v2
    weight: 75

与上面一样,我也提供了说明此规则行为的图表。

所有规则都可以放在一个文件中。在这种情况下,它们应该使用---行分隔开。该文件在callme-service模块的代码仓库中作为multi-rule.yaml文件可以找到。要在Kubernetes上部署所有定义的规则,只需执行以下命令。

$ kubectl apply -f multi-rule.yaml

部署成功后,您可以通过运行该命令来查看可用规则的列表istioctl get routerule

在我们开始任何测试之前,我们显然需要在Kubernetes上部署示例应用程序。这些应用程序非常简单,非常类似于我之前关于Istio的文章中用于测试的应用程序。以下可见的控制器实现了GET /callme/ping方法,该方法打印出从pom.xml中获取的应用程序的版本以及请求中接收到的x-versionHTTP标头的值。

@RestController
@RequestMapping("/callme")
public class CallmeController {
 private static final Logger LOGGER = LoggerFactory.getLogger(CallmeController.class);
 @Autowired
 BuildProperties buildProperties;
 @GetMapping("/ping")
 public String ping(@RequestHeader(name = "x-version", required = false) String version) {
  LOGGER.info("Ping: name={}, version={}, header={}", buildProperties.getName(), buildProperties.getVersion(), version);
  return buildProperties.getName() + ":" + buildProperties.getVersion() + " with version " + version;
 }
}

下面是实现GET /caller/ping方法的控制器类。它打印出从pom.xml获取的caller-service版本并调用由callme-service暴露出的GET callme/ping方法。发送到下游服务时,它需要在请求中包含x-version header。

@RestController
@RequestMapping("/caller")
public class CallerController {
 private static final Logger LOGGER = LoggerFactory.getLogger(CallerController.class);
 @Autowired
 BuildProperties buildProperties;
 @Autowired
 RestTemplate restTemplate;
 @GetMapping("/ping")
 public String ping(@RequestHeader(name = "x-version", required = false) String version) {
  LOGGER.info("Ping: name={}, version={}, header={}", buildProperties.getName(), buildProperties.getVersion(), version);
  HttpHeaders headers = new HttpHeaders();
  if (version != null)
   headers.set("x-version", version); < span id = "mce_SELREST_start"
  style = "overflow:hidden;line-height:0;" > < /span>
  HttpEntity entity = new HttpEntity(headers);
  ResponseEntity response = restTemplate.exchange("http://callme-service:8091/callme/ping", HttpMethod.GET, entity, String.class);
  return buildProperties.getName() + ":" + buildProperties.getVersion() + ". Calling... " + response.getBody() + " with header " + version;
 }
}

现在,我们可以继续在Kubernetes上构建和部署应用程序。以下是后面的步骤。

1.构建应用程序

首先,通过执行命令mvn clean install切换到分支v1并构建整个sample-istio-services项目。

2.构建Docker镜像

Dockerfiles放置在每个应用程序的根目录中。通过执行以下命令来构建Docker镜像。

$ docker build -t piomin/callme-service:1.0 .
$ docker build -t piomin/caller-service:1.0 .

或者,你可以省略这个步骤,因为图像piomin/callme-servicepiomin/caller-service可以在Docker Hub帐户中找到。

3.将Istio组件注入到Kubernetes部署文件中

Kubernetes YAML部署文件在每个应用程序的根目录中都作为deployment.yaml文件可用。以下命令的结果应保存为单独的文件,例如deployment-with-istio.yaml:。

$ istioctl kube-inject -f deployment.yaml

4.在Kubernetes上部署

最后,您可以执行追梦的kubectl命令,以便使用我们的示例应用程序部署Docker容器。

$ kubectl apply -f deployment-with-istio.yaml

然后切换到分支v2,并针对示例应用程序的2.0版本重复上述步骤。最终的部署结果在下图中可以看到。

在Kubernetes上运行Istio时,一个非常有用的功能是与Zipkin,Grafana或Prometheus等工具的即插即用集成。Istio会自动发送一些由Prometheus收集的指标,例如计算指标istio_request_count的请求的总数。这些插件的YAML部署文件在目录${ISTIO_HOME}/install/kubernetes/addons中可以找到。在使用kubectl命令安装Prometheus之前,我建议将服务类型从默认的ClusterIP通过添加line type: NodePort更改为Nodeport

apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/scrape: 'true'
  labels:
    name: prometheus
  name: prometheus
  namespace: istio-system
spec:
  type: NodePort
  selector:
    app: prometheus
  ports:
  - name: prometheus
    protocol: TCP
    port: 9090

然后我们应该运行kubectl apply -f prometheus.yamlistio-system命令以便在Kubernetes上部署Prometheus。部署在命名空间中可用。要检查服务的外部端口,请运行以下命令。对我而言,它能在http://192.168.99.100:32293/下获得。

在使用Prometheus可视化的下图中,我只过滤了发送给callme-service的请求。绿色指向由服务的v2版本接收的请求,而红色指向由服务的版本v1处理的请求。就像你可以在该图中看到的,在一开始的时候,我发送给caller-service的带有HTTP header x-version的请求设置为值v2,接着我不设置这个header的值,流量在服务的部署实例之间被分配。最后,我将它设置为v1。我定义了一个表达式rate(istio_request_count{callme-service.default.svc.cluster.local}[1m]),它返回每秒callme-service接收到的请求的速率。

测试

在向caller-service发送一些测试请求之前,我们需要在Kubernetes上获取它的地址。执行以下命令后,您会看到它在该地址下可用。http://192.168.99.100:32237/caller/ping

有四种可能的情况。在第一种情况中,当我们设置标题时,请求总是被路由到x-versionv1callme-service-v1。

如果header x-version1未包含在请求中,则流量将在callme-service-v1...

...和callme-service-v2之间分割。

最后,如果我们将header x-version设置为v2,即将请求始终路由到callme-service-v2

结论

通过使用Istio,您可以轻松地为部署在Kubernetes上的应用程序创建并应用简单并且更为先进的流量管理规则。您还可以通过Istio和Zipkin,Prometheus和Grafana之间的集成来监控一些指标和进行溯源。

本文的版权归 Steve Wang 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我和未来有约会

Windows 7 旗舰版 VHD安装体验

indows 7 旗舰版 VHD安装体验 近日为了将开发环境、测试环境互相分离来,体验了一下“特殊的虚拟机 VHD启动”,我现在的机器(IBM X200)上安装...

3406
来自专栏java技术学习之道

详解Tomcat 配置文件server.xml

2902
来自专栏散尽浮华

完整部署CentOS7.2+OpenStack+kvm 云平台环境(1)--基础环境搭建

公司在IDC机房有两台很高配置的服务器,计划在上面部署openstack云平台虚拟化环境,用于承载后期开发测试和其他的一些对内业务。 以下对openstack的...

2.8K11
来自专栏云计算教程系列

如何使用Winston记录Node.js应用程序

在本指南中,我们将重点介绍Winston的日志包,这是一个极其通用的日志库,是基于NPM下载统计信息,可用于Node.js应用程序的日志记录解决方案。Winst...

7135
来自专栏Java后端技术栈

一文带你详解了解Tomcat的Server配置!

Tomcat隶属于Apache基金会,是开源的轻量级Web应用服务器,使用非常广泛。server.xml是Tomcat中最重要的配置文件,server.xml的...

1142
来自专栏日常分享

Java 以及JEE环境快速搭建

  博主最近找了一个Java Development的实习,加上上个月末的考试周,所以很久没有更新博客。   上了一周的班,还没有在熟悉项目的阶段。

1861
来自专栏平凡文摘

Tomcat 的 Server 文件配置详解!

2304
来自专栏Android小菜鸡

在AndroidStudio上搭建SVN

  在Eclipse上直接安装SVN插件即可,但是在AndroidStudio上不行,需要关联SVN客户端。并且安装command line的客户端才可以。co...

973
来自专栏云原生架构实践

JHipster生成微服务架构的应用栈(三)- 业务微服务示例

这里选择Microservice application,所有自定义业务逻辑的微服务都可以选择这个类型。

3452
来自专栏张戈的专栏

Linux系统防CC攻击自动拉黑IP增强版Shell脚本

最新更新:张戈博客已推出功能更强大的轻量级 CC 攻击防御脚本工具 CCKiller==>传送门 前天没事写了一个防 CC 攻击的 Shell 脚本,没想到这么...

8005

扫码关注云+社区

领取腾讯云代金券