Kubernetes 的 service mesh – 第六部分:轻松预发布微服务

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 概述 在将新代码发布到生产环境之前, 预发布操作是构建一个可靠的、低宕机时间的软件系统的关键组成部分。但是,在微服务体系下,由于拆分出来的许许多多的微服务之间的依赖关系随着微服务数量成指数倍增长,从而增加了预发布操作的复杂性。

概述

在将新代码发布到生产环境之前, 预发布操作是构建一个可靠的、低宕机时间的软件系统的关键组成部分。但是,在微服务体系下,由于拆分出来的许许多多的微服务之间的依赖关系随着微服务数量成指数倍增长,从而增加了预发布操作的复杂性。在这篇文章里,我们将为您介绍 linkerd 的最强大的功能之一,单个请求路由(per-request routing),通过它,您将可以非常轻松的处理这个问题。


注意:这是关于Linkerd、Kubernetes和service mesh的系列文章其中一篇,其余部分包括:

  1. Kubernetes的service mesh – 第一部分:Service的重要指标
  2. Kubernetes的service mesh – 第二部分:以DaemonSet方式运行linkerd
  3. Kubernetes的service mesh – 第三部分:将一切加密
  4. Kubernetes的service mesh – 第四部分:通过流量切换持续部署
  5. Kubernetes的service mesh – 第五部分:DogFood环境,Ingress和Edge路由
  6. Staging microservices without the tears
  7. Distributed tracing made easy
  8. Linkerd as an ingress controller
  9. gRPC for fun and profit
  10. The Service Mesh API
  11. Egress
  12. Retry budgets, deadline propagation, and failing gracefully
  13. Autoscaling by top-line metrics

Linkerd 是云原生应用的一种服务网格(service mesh)。它作为对应用透明的网络代理层通过封装服务间的调用为应用提供如:低延迟负载均衡(latency-aware load balancing)、链接重试(retry budgets)及终止(deadlines)、熔断机制(circuit breaking)等特性来提高应用的适应性(application resilience)。

除了能够提高应用的适应性,linkerd 也能够提供强大的路由语言来改变服务在运行时请求流的方式。这篇文章我们将为您展示 linkerd 如何做到这一点,不仅仅是全局范围,更精细到每一个基础请求。也将为您展示每一个请求路由如何被用来创建临时的预发布环境,从而允许在生产环境上下文中测试新代码而不用真正将其发布到生产环境里。最后,将为您介绍(in contrast to staging with a dedicated staging environment)临时的预发布环境如何做到既不需要与其他团队的协调工作,也不需要花费时间精力来同时保持多个部署环境。

为什么要预发布

为什么预发布如此重要?在现代软件开发当中,代码需要经过一系列预先设计好的实践路线来保证正确性:代码走查(code review),单元测试(unit tests),集成测试(integration tests)等等。经过这些流程之后,需要开始估算代码的表现了:新代码运行的速度如何?高负载下的表现如何?在运行时与其他服务以及相关依赖的交互表现如何?

预发布系统就可以回答这些问题。预发布的基本原则就是越接近生产环境,系统就越切实可行。因此,就像测试环节中的 mocks 和 stub 一样,对于预发布,我们期望能够运行真实的服务。最好的预发布环境就是和生产环境完全一样。

为什么微服务的预发布很难

如果你的应用由许多微服务构成,那么微服务之间的通信交互就会变成像端到端应用行为一样的重要组成部分。其实,应用拆分的越细,那么在运行时应用之间的交互就会越复杂,而此时应用的表现已经不仅仅是每个微服务自己的问题了,很大程度上取决于微服务之间的交互。

实际上,增加微服务的数量不仅仅增加了正确预发布的重要性,也同时增加了实现这一点的难度。我们来看几个常用的预发布方法,以及为什么在微服务环境下这些方法都会变得比较困难。

预发布的常规方法是共享预发布集群,而在这个集群里,除了你的预发布服务之外其他人的预发布服务也在这里。这种方式的弊端就是没有隔离。如下图展示,如果 Alex 把他的服务发布了上去但是出了点问题,整个链条中就很难判断出问题源的所在–因为问题可能出现在 Alex、Alice 或者 Bob 的服务上,又或者干脆就是数据库里的数据有问题。这样预发布环境与生产环境的同步就会非常困难,尤其是当服务、团队以及发行版本数量庞大的时候。

另一种共享环境称为“私人”或者单个开发者的预发布集群,可以解决隔离的问题。在这个例子中,每一个开发者可以根据需要来操作预发布集群。预发布一个服务需要同时预发布这个服务的上游以及下游服务也包括相关的依赖,从而可以保证预发布的有效性。(比如,在下图中,Alex 必须先发布 Web FE 和 API 服务来保证他的 Foo 服务可以正常运行。)然而,根据需要来维护以及部署部分应用拓扑结构会非常复杂,尤其是当应用拓扑结构非常大而且服务又有独立的部署模型。

上面说的是一种极其简单的部署新代码到生产环境并且有问题时可以回滚的方式。当然了,这种方式很有风险,而且不能处理部分应用类型,比如:金融事务。虽然还有很多其他的部署方法,但是本文我们将介绍一种直接的、轻松的方式。

一种更好的方式

使用 Linkerd 创建临时的预发布环境,就可以很好的避免以上提到的弊端。实际上,在 Twitter 里 Finagle 路由层作为linkerd 的底层, 他的主要动机就是解决这个问题。

我们来看一下 Alex 的 Foo 服务。如果,我们不另外部署一个隔离的环境,而是仅仅使用 Foo 的预发布版本替代 Foo 的生产版本,然后通过一个特殊的请求来访问它呢?针对生产环境,这将能够确保 Foo 的预发布版本的安全性,而且除了 Foo 预发布版本之外也不需要部署其他的任何东西。这就是临时预发布环境的本质。而此时,开发者身上的任务一下就轻松了:Alex 只需要预发布他的新代码,然后在 ingress 请求的 header 上设置一个标记就可以了,就这么简单!

Linkered 的单个请求路由可以帮助我们实现这种方式。通过 linkerd 的请求代理,可以给特定的请求上设置一个 l5d-dtab 的 header 。这个 header 可以允许你设置路由规则(叫做 in Finagle parlance, Dtabs)。比如,dtab 规则 /s/foo => /srv/alex-foo 可以覆盖 Foo 服务生产环境的规则。给单个请求添加这个可以使得请求直接到达 Alex 的 Foo 服务,也仅仅作用与这一个请求。Linkerd 可以拦截这个规则,所以生产环境里任何使用 Alex 的 Foo 服务的地方都可以正确的处理。

gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAA

试一下这个功能吧

Service Mesh for Kubernetes 系列文章的读者应该已经知道我们有一个 demo our dogfood blog post。我们部署过一个 world-v2 服务,并且可以通过设置重定向路由规则发送单个的 dogfood 请求。现在我们可以使用相同机制来做些别的事情:创建一个临时的预发布环境。

部署一个服务的两个版本,再使用 linkerd 的路由功能在部署到生产环境之前来测试新服务。我们先部署 hello 和 world-v1 服务来作为我们的生产环境服务,然后再创建一个临时的预发布环境来测试 world 服务的新版本 world-v2。

第一步:部署LINKERD和HELLO-WORLD服务

我们使用前一篇文章里部署的 hello world 服务。它由 hello 服务调用 world 服务组成。这些应用通过 Kubernetes downward API 提供的根据 nodeName 来找到 Linkerd 。如果你不确定你的集群是否支持 nodeName, 你可以运行如下命令:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml

然后查看一下日志:

kubectl logs node-name-test

如果你看到了 ip 就表示成功了。然后再通过如下命令部署 hello world 应用:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world.yml

如果你看到了 “server can’t find …” 错误,那就部署 hello-world 的 legacy 版本,这个版本依赖 hostIP 而不是 nodeName:

kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world-legacy.yml

然后我们来部署生产环境(linkerd 和 hellow 以及 world 服务):

$ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd-ingress.yml

再来部署 linkerd 和预发布版本的服务 world-v2 ,这个服务会返回 “earth” 而不是 “world”。

$ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd-ingress.yml
$ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/world-v2.yml

第二步:在临时预发布环境里使用单个请求覆盖

现在,我们运行了 world-v2 服务,来测试一下是否通了。我们期望的是请求到达的是 `world-v2` 而不是 `world-v1`。首先,先运行一个没有更改的请求,这个请求会走默认的路径。(你可能需要等待 l5d 的 external IP 出现):

$ INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}")
$ curl -H "Host: www.hello.world" $INGRESS_LB
Hello (10.196.2.232) world (10.196.2.233)!!

如果外部负载均衡不起作用,可以使用 hostIP:

INGRESS_LB=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}"):$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}')
$ curl -H "Host: www.hello.world" $INGRESS_LB
Hello (10.196.2.232) world (10.196.2.233)!!

如我们所料,返回了 `Hello (……) World (…..)`,这说明走的是生产环境。

那如何来请求预发布环境呢?我们需要做的就是发送给一个带有覆盖 header 的请求到生产环境中去,它就会访问到 `world-v2` 服务!由于 header 的设置,请求会走 `/srv/world-v2` 而不是 `/host/world`。

$ curl -H "Host: www.hello.world" -H "l5d-dtab: /host/world => /srv/world-v2;" $INGRESS_LB
Hello (10.196.2.232) earth (10.196.2.234)!!

我们看到了 “earch” 而不是 “world”! 这个请求已经成功的到达了 world-v2 服务,而且是在生产环境里,并且没有任何代码变更或者额外的部署工作。就是这样,预发布就变的 so easy 了。

Linkerd 的 Dtabs 和 routing 的文档非常健全。在开发中,你可以使用 linkerd 的 “dtab playground” http://$INGRESS_LB:9990/delegator。By going to the “outgoing” router and testing a request name like /http/1.1/GET/world, you can see linkerd’s routing policy in action.

实践

在实践中,这种方式有一些需要注意的地方。首先,往生产环境的数据库里写东西时必须要小心。相同的 dtab 覆盖机制可以用来发送任何写预发布数据库的请求,或者在一些应用级别里直接 /dev/null。强烈建议,这些覆盖规则不能手动生成,以免发生不必要的错误,毕竟是在生产环境里!

其次,你的应用需要参考 linkerd’s context headers。

最后非常重要的一点,避免外界可以设置 l5d-dtab 请求头。setting up a dogfood environment in Kubernetes 这篇文章里我们阐述了一个 nginx 的 ingress 样例配置,可以有效的去掉不认识的请求头。

结尾

我们举例了如何通过 linkerd 设置单个请求路由规则来达到创建临时预发布环境的问题。通过这种方式,我们可以在生产环境里预发布服务,而不需要更改现有代码,也不需要其他额外的预发布环境资源(当然除了预发布服务自己),同时也不需要处理预发布与生产这两个平行环境。对于微服务众多的应用来说,这种方式提供了一种发布到生产环境之前的简单、高效的预发布方式。

本文转自kubernetes中文社区-Kubernetes 的 service mesh – 第六部分:轻松预发布微服务

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
1月前
|
存储 运维 Kubernetes
容器服务ACK常见问题之修改service的名字失败如何解决
容器服务ACK(阿里云容器服务 Kubernetes 版)是阿里云提供的一种托管式Kubernetes服务,帮助用户轻松使用Kubernetes进行应用部署、管理和扩展。本汇总收集了容器服务ACK使用中的常见问题及答案,包括集群管理、应用部署、服务访问、网络配置、存储使用、安全保障等方面,旨在帮助用户快速解决使用过程中遇到的难题,提升容器管理和运维效率。
|
11天前
|
Kubernetes 监控 Cloud Native
构建高效云原生应用:基于Kubernetes的微服务治理实践
【4月更文挑战第13天】 在当今数字化转型的浪潮中,企业纷纷将目光投向了云原生技术以支持其业务敏捷性和可扩展性。本文深入探讨了利用Kubernetes作为容器编排平台,实现微服务架构的有效治理,旨在为开发者和运维团队提供一套优化策略,以确保云原生应用的高性能和稳定性。通过分析微服务设计原则、Kubernetes的核心组件以及实际案例,本文揭示了在多变的业务需求下,如何确保系统的高可用性、弹性和安全性。
16 4
|
1月前
|
Kubernetes Nacos 微服务
nacos常见问题之v2.2.3 k8s 微服务注册nacos强制删除 pod不消失如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
24 1
nacos常见问题之v2.2.3 k8s 微服务注册nacos强制删除 pod不消失如何解决
|
1月前
|
Kubernetes SDN 微服务
微服务与 Kubernetes 容器云的边界
【2月更文挑战第30天】该文探讨了微服务与Kubernetes集群的关系,主要关注是否应跨多集群部署。理想的状况是每个微服务对应一个Kubernetes集群,配置和注册中心在同一集群内,以减少网络延迟。
|
1月前
|
Kubernetes 开发者 Docker
构建高效微服务架构:Docker与Kubernetes的完美搭档
【2月更文挑战第29天】在当今快速发展的软件开发领域,微服务架构已成为提高系统可维护性、扩展性和敏捷性的关键解决方案。本文将深入探讨如何利用Docker容器化技术和Kubernetes集群管理工具,共同构建一个既高效又可靠的微服务环境。我们将分析Docker和Kubernetes的核心功能,并展示它们如何协同工作以简化部署流程、增强服务发现机制以及实现无缝的服务伸缩。通过实际案例分析,本文旨在为开发者提供一套实用的微服务架构设计和实施指南。
|
2月前
|
开发者 Docker 微服务
深入浅出:使用Docker容器化部署微服务架构
在当今快速迭代的软件开发环境中,微服务架构因其高度解耦和独立性而成为企业首选。然而,微服务的管理和部署可能会变得复杂和繁琐。本文将探讨如何利用Docker,一个轻量级的容器化技术,来简化和加速微服务的部署。我们将从Docker的基础概念入手,详细介绍如何创建、配置和运行微服务容器,最后讨论Docker在微服务架构中的优势和挑战。本文旨在为开发者提供一条清晰的路径,通过容器化技术实现微服务架构的高效部署和管理。
87 0
|
2月前
|
Kubernetes 开发者 Docker
深入浅出:使用Docker容器化部署微服务架构
在当今快速演进的软件开发领域,微服务架构因其高度的模块化和可伸缩性而受到广泛欢迎。然而,微服务的部署和管理也带来了新的挑战。本文旨在通过深入浅出的方式,探讨如何利用Docker容器技术有效地部署和管理微服务架构。我们将从Docker的基本概念出发,逐步深入到如何构建、部署微服务,并讨论在此过程中可能遇到的常见问题及其解决策略。本文不仅适合刚接触Docker和微服务的新手,也为有经验的开发者提供了实用的参考。
56 1
|
2月前
|
JSON JavaScript Docker
深入浅出:使用Docker容器化部署微服务架构
本文旨在向读者展示如何利用Docker技术高效地构建和部署微服务架构。通过深入浅出的方式,我们将探索Docker的基本概念、容器化的优势以及如何将其应用于微服务架构中。此外,文章还将提供一个简单的示例,指导读者实践如何使用Docker将一个现有的后端应用容器化,并部署到本地开发环境中。不同于传统的摘要,这里我们强调实践操作的重要性,鼓励读者通过实际操作来加深对Docker和微服务架构的理解。
52 1
|
2月前
|
Kubernetes 负载均衡 Docker
深入浅出:使用Docker容器化部署微服务
在当今快速变化的软件开发领域,微服务架构因其高度的模块化和可伸缩性而受到广泛欢迎。然而,微服务的部署和管理带来了新的挑战。本文将探讨如何利用Docker容器技术,简化和加速微服务应用的部署过程。我们将从Docker的基础知识入手,逐步深入到如何构建、部署和管理微服务容器。通过本文,读者将获得一套实用的工具和方法论,以便在自己的项目中高效地应用Docker和微服务技术。
|
2月前
|
Java 开发者 Docker
深入浅出:使用Docker容器化部署微服务架构
在本文中,我们将探索Docker容器技术如何革新微服务架构的部署方式,提高开发效率和应用的可扩展性。不同于传统摘要的概述风格,我们将通过一个实际案例,步骤明晰地展示如何将一个简单的微服务应用容器化,并在Docker环境中部署运行。本文旨在为开发者提供一个清晰、易懂的指南,帮助他们理解容器化技术的基本原理和操作流程,无论是初学者还是有经验的开发人员都能从中获益。

推荐镜像

更多