Kubernetes 中的微服务挑战

2025-06-05

Kubernetes 中的微服务挑战

本文将详细描述 Kubernetes 世界中微服务的常见挑战,以及我们如何解决这些挑战。
Istio

让我们开始列出一些挑战:

  1. 每个微服务都需要知道服务地址。通常,它会作为配置依赖注入到微服务业务逻辑中。
    Istio

  2. 一旦请求到达 Kubernetes 集群,由于微服务之间通过 HTTP 通信,通信就变得不安全。集群内的每个微服务都可以自由地与任何其他服务通信。因此,就安全性而言,集群无法阻止攻击。
    Istio

  3. 每个微服务都包含重试逻辑,以提高整个应用程序的健壮性。如果某个微服务无法访问或连接丢失,您自然会希望重试连接。因此,开发人员通常会为每个服务添加重试逻辑,并在添加新的微服务时继续执行相同的过程。

  4. 我们希望能够监控服务的执行情况、返回的响应、微服务接收的请求数量、网络延迟以及跟踪。
    开发团队可能会使用客户端库向 Prometheus 添加监控逻辑,并使用跟踪库(例如 Zipkin)收集跟踪数据。您可能已经注意到,每个微服务的开发团队都需要将所有这些逻辑添加到每个服务中,这非常耗时。
    开发人员并没有集成任何业务需求,而是为每个微服务添加了通信、安全和重试逻辑。这些逻辑会增加服务的复杂性,而不是保持服务的简单性和直接性。

  5. 如果没有合适的服务网格模式,管理金丝雀部署和软件(包括网站和微服务中的后端 API)的向后兼容性将变得非常困难。
    在软件工程中,金丝雀部署是一种分阶段发布的实践。我们首先向一小部分在线用户推出软件更新,以便他们进行测试并提供反馈。一旦更改被接受,更新将向其余用户推出。

    Istio

在金丝雀部署方面,我们将用户分为两组。一小部分用户将使用金丝雀版本,其余用户则继续使用稳定版本。稍后,我们将决定是将所有人都迁移到金丝雀版本,还是将更改回滚到之前的版本。或者,我们可以检查请求的标头,以支持特定用户组进行金丝雀部署。我面临

的一些挑战已在上文列出。因此,将所有非业务逻辑从微服务中抽离出来,放入单独的 Sidecar 应用程序中来处理上述所有问题,将是一个合理的方案。此外,Sidecar 还可以充当代理。我们可以使用“带 Sidecar 的服务网格”模式来解决所有这些挑战。

什么是服务网格?

服务网格的主要目标是洞察先前不可见的服务通信层,并完全控制所有微服务通信逻辑,例如动态服务发现、负载均衡、超时、回退、重试、熔断、分布式跟踪以及服务间安全策略的实施。这些洞察由流量审计和跟踪功能提供。

Kubernetes 已经提供了一个非常基础的开箱即用的“服务网格”;它就是“服务”资源。它通过定位所需的 Pod 并轮询均衡请求来提供服务发现。“服务”的工作原理是管理集群中每台主机上的 iptables,只允许轮询负载均衡方法,没有重试和退避逻辑,也没有我们期望现代服务网格能够处理的其他功能。但是,在集群中实现一个功能齐全的服务网格系统(例如 Istio、Linkerd 或 Conduit),将为您提供以下可能性:

  • 允许服务使用纯 HTTP 通信,而无需担心应用层的 HTTPS。服务网格代理将管理发送方的 HTTPS 封装和接收方的 TLS 终止,从而允许应用程序组件使用纯 HTTP 或 gRPC 以及任何其他无需加密的协议进行传输。代理将负责加密。
  • 安全策略执行:代理知道哪些服务被允许访问其他服务和端点,并将拒绝未经授权的流量。
  • 熔断:当访问过载的服务或端点时,如果延迟已经很高,则自动回退。此功能可以避免越来越多的请求冲击该端点,避免该端点在过载下崩溃。
  • 延迟感知负载均衡:不再使用循环负载均衡(忽略每个目标的延迟),而是根据每个后端目标的响应时间进行更智能的负载均衡。这是现代服务网格中一个至关重要的特性。
  • 队列深度负载均衡:根据当前请求处理量,将新请求路由到最不繁忙的目标。服务网格知道之前所有请求的发送情况,以及哪些请求仍在处理中或已经完成。它会根据该逻辑将新传入的请求发送到队列最短的目标进行处理。
  • 按请求路由:将选定 HTTP 标头标记的特定请求路由到负载均衡器后的特定目标,从而轻松实现金丝雀部署测试和其他创造性用例。这是服务网格提供的最强大功能之一。
  • 指标和跟踪:报告每个目标的请求量、延迟指标、成功率和错误率。

我们将使用 Istio 进行技术实践,并利用 Istio 进行金丝雀部署,以解决上述问题。

Istio

服务网格只是一种模式,而 Istio 是其实现之一。Istio 架构包含一个“控制平面”组件,也称为“Istiod”,它负责管理并在每个单独的 Pod 上注入代理。Istio 使用 Envoy Proxy 作为代理层。Envoy Proxy 是一个独立的开源项目,许多其他网格提供商将其用作代理 sidecar。您可以阅读 Istio 的官方文档 ( https://istio.io/ )
了解更多关于 Istio 的信息。

Istio

如何在 Kubernetes 中配置 Istio?

由于 Istio 配置与应用程序配置分离,因此无需调整 Kubernetes YAML 文件中的部署和服务。Istio 使用 Kubernetes YAML 文件进行配置。它通过扩展 Kubernetes API 来使用 Kubernetes 客户资源定义 (CRD)。

CRD 是自定义的 Kubernetes 组件/对象。例如,Istio、Prometheus 等第三方技术可以像任何其他原生 Kubernetes 对象一样使用。

使用一些 Istio CRD,我们可以在微服务之间配置不同的流量路由,包括哪些服务可以相互通信、流量分割、重试逻辑和金丝雀部署。

服务发现
Istiod 为所有微服务提供了一个中央注册中心。无需为每个微服务静态配置端点,当新的微服务部署时,它将自动注册到服务注册表中,无需我们进行任何配置。

Envoy Proxy 可以使用该注册表查询端点并将流量发送到相关服务。

证书管理
Istiod 还充当证书颁发机构。它为所有集群微服务生成证书,从而允许微服务代理之间进行安全的 TLS 通信。

指标和跟踪
Istiod 从 Envoy 代理接收指标和跟踪数据。这些数据由监控服务器(例如 Prometheus)和跟踪服务器收集,以满足微服务应用程序所需的一切需求。

Istio

Istio Ingress Gateway
它是进入 Kubernetes 集群的入口点,几乎是 Nginx Ingress Controller 的替代品。

Istio 网关以 Pod 的形式运行在集群中,通过接受传入流量来充当负载均衡。然后,网关将使用 VirtualService 组件将流量引导至集群内的某个微服务。

VirtualService
虚拟服务 (VirtualService) 连接所有请求的网关和目标 Pod。任何“主机”(DNS 名称或集群内部服务相互寻址时的 Kubernetes DNS 名称)只能在一个 VirtualService 中定义。

Istio 流量流
用户将向 Kubernetes 集群中的 Web 服务器微服务发起请求 → 然后请求命中 Istio 网关,因为它是集群的入口点 → 网关将评估有关如何路由流量并将其发送到 WebServer 微服务的虚拟服务规则 → 请求到达 Web 服务器微服务的 Envoy 代理,它将评估请求并使用 localhost 将其转发到同一容器内的实际 Web 服务器容器。

例如,Web 服务器向支付微服务发起另一个请求,该请求将从 Web 服务器容器移出到 Envoy 代理。然后,通过应用 VIrtualService 规则,目标规则配置将使用 mTLS(双向 TLS)与支付微服务的 Envoy 代理进行通信。

Istio

使用 Istio 进行金丝雀部署

在我们开始技术实践之前,请确保您的计算机上安装了下面列出的必要工具:

  • Docker(https://docs.docker.com/engine/install/

  • Kubectl ( https://kubernetes.io/docs/tasks/tools/ )
    Kubernetes 命令行工具,允许您针对 Kubernetes 集群运行命令。您可以使用 kubectl 部署应用程序、检查和管理集群资源以及查看日志。

  • Minikube ( https://minikube.sigs.k8s.io/docs/start/ )
    是使用最广泛的本地 Kubernetes 安装程序。它提供了易于使用的指南,指导如何在多个操作系统上安装和运行单个 Kubernetes 环境。它支持将 Kubernetes 部署为容器、虚拟机或裸机,并实现 Docker API 端点,从而更快地推送容器镜像。它拥有负载均衡、文件系统挂载和 FeatureGates 等高级功能,是本地运行 Kubernetes 的最佳选择。通过执行以下命令在本地运行 Minikube,minikube start在本地创建 Kubernetes 资源。

Istio 插件:我们可以随 Istio 安装的附加组件。它将包含所有关于指标、跟踪的数据可视化,基本上可以展示您的微服务正在做什么以及它们的性能如何。下面是每个工具功能的简要说明,但我们不会深入探讨细节,因为这超出了本文的讨论范围。

我们将在本地集群中安装一些插件。您可以按照官方指南 ( https://istio.io/latest/docs/ops/integrations/ ) 进行操作。请安装以下插件:Kiali、Grafana、Jaeger 和 Promethteus。

Prometheus用于监控集群中的任何内容。它可以是服务器本身、内存、CPU 使用率,也可以是 Kubernetes 组件,例如 Pod、服务和其他组件。

Grafana是一个指标数据的数据可视化工具。

Jaeger是一种用于跟踪微服务请求的服务。

Kiali提供惊人的数据可视化功能并配置您的微服务设置和通信。

对于此示例,我们希望在一个隔离的命名空间中工作,因此,请创建一个 Kubernetes 命名空间,并将以下内容保存到namespace.ymal文件中。

apiVersion: v1
kind: Service
metadata:
  name: website
  namespace: website-istio-ns
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: website
Enter fullscreen mode Exit fullscreen mode

然后,运行kubectl create -f service.yaml将这些资源定义提交到集群。

创建 Istio 网关和虚拟服务,以实现服务网格入口端点的基本功能,以便我们可以通过将 Istio 部署到集群时创建的 Istio Ingress 负载均衡器访问我们的应用程序。
将以下定义保存到gateway.yaml文件中:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: website-gateway
  namespace: website-istio-ns
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
Enter fullscreen mode Exit fullscreen mode

然后,运行kubectl create -f gateway.yaml将这些资源定义提交到集群。

将以下定义保存到virtual-service.yaml文件中:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: website-virtual-service
  namespace: website-istio-ns
spec:
  hosts:
  - "*"
  gateways:
  - website-gateway
  http:
  - match:
      - headers:
          qa:
            exact: canary-test
    route:
      - destination:
          host: website
          subset: version-2

  - route:
    - destination:
        host: website
        subset: version-1

Enter fullscreen mode Exit fullscreen mode

“VirtualService” 目标中的 host 字段是要使用的 Kubernetes “服务” 对象的名称。如果我们想通过标签区分不同的 Pod,并在不同场景下分别寻址它们(使用基于 URI 路径或基于 HTTP 头的路由),则可以将目标划分为子集。在这种情况下,我们需要添加一个子集字段,如下所示:

http:
  - route:
    - destination:
        host: website
        subset: something-that-is-defined-in-DestinationRule
Enter fullscreen mode Exit fullscreen mode

路由的“子集”由“DestinationRule”定义。除了基于标签分离服务的目标 Pod 之外,我们还可以应用自定义负载均衡策略。例如,我们有一个子集“version-1”和“version-2”,定义如下,并保存在“destination-rule.yaml”文件中:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: website
  namespace: website-istio-ns
spec:
  host: website
  subsets:
  - name: version-1
    labels:
      version: website-version-1
  - name: version-2
    labels:
      version: website-version-2
Enter fullscreen mode Exit fullscreen mode

现在使用 进行部署kubectl create -f destination-rule.yaml。通过这样做,我们创建了“website”目标的“子集”,名为“version-1”和“version-2”。该子集是任何带有“version: website-version-1”或“version: website-version-1”标签的 pod,以及“website”服务定义中定义的任何其他标签(即“app: website”)。

当“VirtualService”使用目标服务名称时,它将路由到任何“version”标签等于“website-version-1”或“website-version-2”的pod。我们在“spec.host”字段中定义了它(它是Kubernetes“服务”对象的名称,也就是我们在“VirtualService”的“spec.http.route.destination.host”字段中使用的名称)。

下一步是准备服务网格以部署新的网站版本。假设这是一个有实际流量流入的生产集群,“website-version-1”处于活动状态,并且我们有四个接收流量的 Pod。通过创建一个单独的部署,使用网站 Pod 的 version-2 和相同的标签“app: website”,我们将导致流量分流并影响现有用户,而这正是我们想要避免的!

让我们创建一个包含两个不同版本网站的部署舱:

保存以下内容并将其命名为version-1-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-v1
  namespace: website-istio-ns
spec:
  replicas: 4
  selector:
    matchLabels:
      app: website
      version: website-version-1
  template:
    metadata:
      labels:
        app: website
        version: website-version-1
    spec:
      containers:
      - name: website-version-1
        image: hashicorp/http-echo:alpine
        args:
          - -listen=:80
          - --text="echo-v1"
        ports:
          - name: http
            protocol: TCP
            containerPort: 80
        imagePullPolicy: IfNotPresent
Enter fullscreen mode Exit fullscreen mode

通过执行命令进行部署kubectl create -f version-1-deployment.yaml

对于网站的“版本 2”,我们需要单独部署。请创建一个名为 as 的文件,version-2-deployment.yaml其中包含以下内容:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-v2
  namespace: website-istio-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: website
      version: website-version-2
  template:
    metadata:
      labels:
        app: website
        version: website-version-2
    spec:
      containers:
      - name: website-version-2
        image: hashicorp/http-echo:alpine
        args:
          - -listen=:80
          - --text="echo-v2"
        ports:
          - name: http
            protocol: TCP
            containerPort: 80
        imagePullPolicy: IfNotPresent
Enter fullscreen mode Exit fullscreen mode

通过执行命令进行部署kubectl create -f version-2-deployment.yaml

此时,我们有四个“版本 1” pod 和一个“版本 2” 金丝雀 pod。

为了在本地访问“istio ingress gate”,我们必须在 Minikube 的帮助下通过执行以下命令在本地建立隧道:minikube tunnel

最后,我们可以通过访问以下浏览器地址“localhost:80”来测试结果。如屏幕截图所示,我们网站的“version-1”响应为“echo-v1”。默认情况下,所有访问者都会访问“version-1”。

Istio

质量保证团队、内部用户以及一小部分真实用户可以看到网站的更新版本,即金丝雀发布版本。任何 HTTP 请求都必须包含包含“canary-test”值的“qa”标头,才能查看金丝雀发布版本。

Istio 的虚拟服务将检查与其匹配的标题和值,将流量重新路由到网站的“版本 2”。

要添加标头,我们可以使用 Postman 和 Insomnia API 工具。最简单的方法是安装 Google Chrome 的 ModHeader 扩展程序(https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj?hl=en)。

下面的截图将演示 ModHeader 扩展如何在浏览器中运行。

Istio

添加必要的标头后,即可访问“localhost:80”。如您所见,添加标头后,Istio 虚拟服务将我们的请求重新路由到我们网站的“version-2”。

Istio

标头已发送到服务器的证明:

Istio

出于本文的目的,我使用了简化版的网站。它只会根据网站的不同版本显示文本。完整的部署代码可以在 GitHub 链接 ( https://github.com/fir1/istio-ingress ) 中找到。

此设置可作为您在不同项目中进行金丝雀部署的指南。祝您好运!

文章来源:https://dev.to/firdavs_kasymov/challenges-of-microservice-in-kubernetes-53kb
PREV
你需要知道的单体架构与微服务架构对比
NEXT
React + Jitsi + 无服务器 + Twilio SendGrid