k8s服务网格(Service Mesh)的istio灰度发布产品实战教程

image

 

Istio​ 是目前最流行的开源服务网格(Service Mesh)产品之一,主要用于解决 Kubernetes(k8s)等云原生环境中的微服务架构所面临的复杂性挑战。

在微服务架构中,服务数量众多,服务间的网络通信极其复杂。传统的做法是开发者需要在业务代码中编写大量的重试、超时、熔断、负载均衡等网络容错逻辑,这导致业务代码臃肿且与网络逻辑强耦合。Istio 的核心思想是“非侵入式”,它不需要修改应用程序的业务代码,就能为服务提供强大的治理能力。

以下是 Istio 的核心优势与主要功能:

1. 解耦业务逻辑与网络治理

Istio 通过在每个服务 Pod 旁注入一个轻量级的代理容器(称为 Sidecar)来实现这一魔法。这个 Sidecar 拦截了微服务之间的所有进出流量,所有的网络策略(如负载均衡、路由、安全加密等)都由 Istio 的控制平面统一下发和管理 。这使得开发团队可以专注于业务本身,而运维团队可以通过配置来管理网络行为 。

2. 精细化的流量管理

这是 Istio 最核心的功能之一,也是实现灰度发布的基础 :

  • 智能路由与灰度发布:支持复杂的流量规则配置,可以轻松实现 A/B 测试、金丝雀发布(Canary Deployment)以及按百分比逐步切分的蓝绿部署 。

  • 弹性容错机制:提供开箱即用的故障恢复特性,如自动重试、超时控制、熔断(防止故障级联传播)以及故障注入(用于测试系统韧性) 。

  • 丰富的负载均衡:提供轮询、随机、最少请求等多种负载均衡策略 。

3. 默认零信任的安全通信

Istio 为微服务间的安全通信提供了底层保障 :

  • mTLS 加密:自动在服务和 Sidecar 之间建立双向 TLS 加密通道,确保数据传输的安全性 。

  • 身份认证与授权:基于工作负载身份进行服务间的身份验证和访问控制,实现细粒度的权限管理 。

4. 强大的可观测性

在复杂的分布式系统中,排查网络问题是一大难点。Istio 自动收集网格内所有流量的遥测数据 :

  • 全链路追踪(Tracing):能够追踪一个请求经过了哪些服务,帮助定位性能瓶颈。

  • 监控与日志(Metrics & Logging):自动生成详细的流量指标(如 QPS、延迟、错误率)和日志记录,可与 Prometheus、Grafana 等工具无缝集成 。

5. 架构演进

Istio 在 1.5 版本做出了重大架构调整,将原本多个独立的控制平面组件(如 Pilot、Galley、Citadel、Mixer)整合成了一个统一的单体结构 istiod。这不仅简化了部署流程,也降低了运维的复杂度。

结合“灰度发布产品实战教程”的背景,Istio 正是通过其强大的流量管理能力,允许运维人员在不中断线上服务的情况下,将一小部分生产流量引导至新版本的服务实例上进行验证,从而实现安全、平滑的版本迭代。

image

 

image

 

image

 

image

 

这是一份关于 Istio 1.9.6​ 在 Kubernetes 1.20.4​ 集群上的离线安装实操步骤。

我为你整理了图片中的核心操作流程,并针对企业级实战场景补充了一些优化建议,方便你后续编写运维手册或向领导汇报。

📋 核心操作步骤梳理

图片展示了典型的离线环境部署 Istio​ 的流程,主要分为三个阶段:

1. 环境准备与镜像导入(Node节点)

  • 检查集群:确认 K8s 集群版本为 v1.20.4,且节点状态正常。

  • 离线解压:在 Node 节点(k8s-n1, k8s-n2)上解压镜像包 all_images.zip

  • 批量加载:使用 for循环命令将所有 .tar镜像文件批量加载到 Docker 中,确保后续部署不再依赖外网下载。

2. Istio 软件包部署(Master节点)

  • 上传与解压:在 Master 节点(k8s-m)上传并解压 istio-1.9.6-linux-amd64.tar.gz

  • 环境变量配置:将 istioctl命令行工具移动到 /usr/bin/目录,以便全局调用。

  • 查看配置 profiles:列出了 defaultdemominimal等多种预设配置。

  • 执行安装:使用 istioctl install -y命令,基于 default配置一键安装 Istio 控制平面(包括 Istiod 和 Ingress Gateways)。

3. 验证安装

  • 使用 kubectl get ns查看命名空间,虽然截图中只显示了 default,但通常此时会生成一个名为 istio-system的新命名空间来容纳 Istio 组件。


💡 专家优化建议(针对实战场景)

这套教程步骤非常基础,适合学习。但在企业真实生产环境中,建议做以下补充和调整:

  1. 指定命名空间安装(最佳实践)

    默认安装在 istio-system是没问题的,但为了更好的资源隔离和权限管理,建议显式指定命名空间:

    istioctl install --set profile=demo -y -n istio-system
  2. 开启自动注入 Sidecar

    安装完控制平面后,必须开启命名空间的自动注入功能,业务 Pod 进入该命名空间才会自动挂载 Envoy 代理:

    kubectl label namespace default istio-injection=enabled
  3. 镜像仓库优化

    图片中使用 docker load加载镜像虽然可行,但后续升级和维护较繁琐。建议搭建私有镜像仓库(如 Harbor),将解压后的镜像重新打 Tag 并推送到私仓,然后在 K8s 中配置 imagePullSecrets,实现完全自动化的拉取。

  4. 安全加固

    教程中未提及 K8s 的 RBAC(基于角色的访问控制)。在生产环境中,应为 Istio 配置严格的 ServiceAccount 和 ClusterRole,避免权限过大。

  5. 这是一张显示 Istio 1.9.6 控制平面安装成功​ 的终端截图。

    截图中的核心信息是:通过 kubectl get pod -n istio-system命令查询结果显示,Istio 的核心组件(如 istiodingressgateway)均处于 Running状态且就绪(Ready 1/1)。这意味着 Istio 的控制平面已经成功运行,具备了管理和治理微服务网格的能力。

    基于截图状态,建议进行以下验证与下一步操作

    1. 验证当前状态

    当前安装的是 default配置,包含了 Ingress Gateway。可以通过以下命令进一步确认服务是否对外暴露:

    kubectl get svc -n istio-system

    确认是否有 istio-ingressgateway服务及其暴露的端口(通常是 80 和 443)。

    2. 建议的下一步实操

    控制平面安装完成只是第一步,接下来需要部署数据平面(即业务应用)来验证整个网格是否工作正常:

    1. 部署官方示例(Bookinfo)

      利用 Istio 自带的经典示例来测试流量管理和网关功能。

      # 创建示例应用的命名空间并开启自动注入
      kubectl create namespace bookinfo
      kubectl label namespace bookinfo istio-injection=enabled
      
      # 部署 Bookinfo 应用
      kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -n bookinfo
    2. 配置网关路由

      为了让外部流量进入网格,需要创建一个 Gateway 和 VirtualService 资源。

      kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml -n bookinfo
    3. 访问测试

      获取 Ingress Gateway 的 IP 地址,在浏览器中访问对应的页面,验证是否能看到 Bookinfo 的界面,以此证明 Istio 已完全生效。

 

image

 

这是一份非常经典的 Istio 服务网格数据平面(Data Plane)注入​ 实操教程。

图片内容清晰地展示了两种让应用程序接入服务网格的方式,重点演示了手动注入 Sidecar 代理的过程。

为了方便你复习或整理笔记,我将图中的核心步骤提取如下,并对关键的配置细节进行了补充说明:

1. 部署测试应用(以 httpbin 为例)

首先,进入 Istio 自带的示例目录,准备部署一个普通的 HTTP 测试服务。

cd /root/istio-1.9.6/samples/httpbin/

2. 方法一:直接部署(不带边车)

为了做对比,先不使用 Istio 治理,直接发布应用:

  • 执行命令kubectl apply -f httpbin-nodeport.yaml

  • 观察结果:使用 kubectl get pod -o wide查看,此时 Pod 只有 1/1​ 个容器,即应用本身。

  • 访问测试curl 10.233.81.11(此时能通,说明应用本身没问题)。

3. 方法二:手动注入 Sidecar(核心步骤)

这是图片中最关键的部分。通过 istioctl kube-inject命令,在部署前动态修改 YAML 文件,将 Envoy 代理容器注入进去。

  • 执行命令

    kubectl apply -f <(istioctl kube-inject -f httpbin-nodeport.yaml)
  • 命令解释istioctl kube-inject会读取原始的 YAML 文件,在其基础上增加 Sidecar 相关的 Deployment 配置,然后输出一个新的 YAML,再由 kubectl apply去执行。

4. 验证注入结果

再次查看 Pod 状态,验证边车是否成功注入:

  • 查看 Podkubectl get pod -o wide

    • 关键变化:Pod 变成了 2/2​ (Ready列)。这代表一个主应用容器 + 一个 Istio Proxy (Envoy) 边车容器。

  • 访问测试curl 10.233.81.12

    • 依然可以访问,说明边车注入没有破坏原有业务逻辑。

  • 查看详情kubectl describe pod httpbin-xxxx

    • 可以在输出中看到两个 Containers,一个是 httpbin(应用),另一个是 istio-proxy(边车)。


💡 深度解读:第二张图的配置细节

第二张截图展示了 Pod 的 YAML 配置片段(Volume 部分)。

这里面包含了 Istio 边车容器运行所必需的关键挂载卷,非常值得了解:

  1. istio-envoy(emptyDir):这是一个内存盘(Medium: Memory),用于 Envoy 存放配置和日志的临时文件。

  2. istio-token(projected):这是特别重要的一项。它是一个投影卷,包含了带有特定 Audience (istio-ca) 的服务账号令牌。这使得 Pod 内的应用可以向 Istiod 进行安全的 mTLS 认证。

  3. istio-podinfo:用于挂载 Pod 的标签(labels)和注解(annotations),供边车读取动态配置。

  4. istiod-ca-cert:挂载 CA 证书,用于服务间的双向 TLS 加密。

📝 专家建议(进阶)

虽然手动注入(kube-inject)非常适合学习和单次调试,但在企业级生产环境中,更推荐使用自动注入

只需要给命名空间打上一个标签(istio-injection=enabled),以后只要在这个命名空间下部署任何新的应用,Kubernetes 会自动调用 webhook 完成 Sidecar 的注入,无需人工干预。

 

image

 

image

 

这是一份关于 Istio 流量治理​ 的实操教程,重点展示了如何配置 Gateway(网关)​ 和 VirtualService(虚拟服务)​ 来将外部流量引入网格内部。

相比之前几步的环境准备,这一步标志着你正式开始掌握 Istio 的核心功能——控制微服务之间的通信流量

为了方便你复习和整理笔记,我将图中的核心步骤提取如下:

1. 核心操作:配置 Ingress Gateway

为了让外部用户能够访问到集群内部的服务(如 httpbin),必须定义一个入口网关。

图中展示了 httpbin-gateway.yaml的核心内容:

  • kind: Gateway:定义一个网关资源。

  • selector: istio: ingressgateway:指定该网关使用哪个负载均衡器(即之前安装时创建的 istiod管理的 Ingress Gateway)。

  • servers: 监听 80​ 端口的 HTTP​ 流量,并允许所有主机(hosts: ["*"])访问。

2. 核心操作:配置流量路由 (VirtualService)

仅仅定义了网关还不够,需要告诉网关收到请求后应该转发给哪个服务。

图中展示了 httpbin-virtualservice.yaml的核心内容:

  • kind: VirtualService:定义一个虚拟服务,用于配置路由规则。

  • hosts: ["*"]:表示这个规则对所有进入网关的流量生效。

  • gateways: 绑定到上面定义的 httpbin-gateway

  • http.route: 定义了具体的路由动作,将流量转发到名为 httpbin的后端服务,目标端口为 8000

3. 实操流程总结

这两个 YAML 文件(httpbin-gateway.yamlhttpbin-virtualservice.yaml)通常需要配合使用:

  1. 应用配置:使用 kubectl apply -f命令将这两个配置文件应用到集群。

  2. 获取入口 IP:通过 kubectl get svc istio-ingressgateway -n istio-system获取网关的外部 IP 地址(通常是 LoadBalancer 分配的 IP)。

  3. 访问测试:在浏览器或使用 curl访问 http://<INGRESS_IP>/get,如果能正确返回 httpbin服务的响应,说明网关配置成功。


💡 专家建议(进阶)

虽然图中的配置足够启动一个简单的服务,但在生产环境中,建议注意以下几点:

  1. 安全性:图中使用的是 HTTP​ 协议。在实际生产环境中,强烈建议将 protocol: HTTP改为 HTTPS,并配合 mTLS​ (Mutual TLS) 对进出网关的流量进行加密,防止数据窃听和篡改。

  2. 域名限制hosts: ["*"]允许任何域名访问。为了安全和明确路由,建议将其改为具体的域名,例如 ["api.example.com"],这样网关就能根据 Host 头区分不同的服务。

  3. 端口冲突:如果集群中已经有其他服务占用了 80 端口,或者你的 Ingress Gateway 已经配置了其他服务,需要注意端口冲突问题。可以通过修改 port.number来解决。

以下是基于您之前实操场景整理的 生产级 Istio 网关(Gateway)与虚拟服务(VirtualService)完整配置文件,包含 HTTP(基础版)​ 和 HTTPS(生产推荐版),并附带逐行详解。


方案一:HTTP 配置(学习与测试环境)

适用于快速验证连通性,对应您图片中的配置逻辑。

1. Gateway 配置:httpbin-gateway.yaml

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: httpbin-gateway
  namespace: istio-system # 建议放在 istio-system 或业务所属命名空间
spec:
  selector:
    # 关键:选择 Istio 安装的 Ingress Gateway 负载均衡器
    # 默认情况下,Istio 安装时会创建一个名为 istio-ingressgateway 的 Deployment
    istio: ingressgateway
  servers:
  - port:
      number: 80          # 监听端口
      name: http          # 端口名称(便于识别)
      protocol: HTTP      # 协议类型
    hosts:
    - "*"                 # 允许访问的主机名,* 代表所有域名/IP
      # 生产建议:改为具体域名,如 "httpbin.example.com"

2. VirtualService 配置:httpbin-virtualservice.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin-route
  namespace: default # 必须与后端服务(httpbin)在同一个命名空间
spec:
  hosts:
  - "*" # 匹配 Gateway 中定义的主机
  gateways:
  - httpbin-gateway # 绑定上面定义的 Gateway 名称
    # 如果是跨命名空间引用,需写为:httpbin-gateway.istio-system.svc.cluster.local
  http:
  - match:
    - uri:
        prefix: /get # 匹配 URI 前缀为 /get 的请求
    route:
    - destination:
        host: httpbin.default.svc.cluster.local # 后端服务的 FQDN
        port:
          number: 8000 # 后端服务的端口

方案二:HTTPS 配置(企业生产推荐)

包含 TLS 证书配置,更安全。

1. 创建 TLS 证书 Secret

首先,将您的证书文件存入 Kubernetes Secret(假设证书名为 tls.crt,密钥为 tls.key):

kubectl create secret tls httpbin-credential \
  --key=tls.key \
  --cert=tls.crt \
  -n istio-system

2. Gateway 配置(HTTPS):httpbin-gateway-tls.yaml

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: httpbin-gateway-tls
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE # 单向 TLS 认证
      credentialName: httpbin-credential # 引用上面创建的 Secret 名称
    hosts:
    - "httpbin.example.com" # 强制指定具体域名

📝 配置详解与参数说明

 

配置项

含义

注意事项

kind: Gateway

定义流量入口。相当于大楼的“大门”和“保安”。

只负责接收流量,不负责转发。

selector

标签选择器。告诉 Istio:“请把这个网关配置应用到带有 istio: ingressgateway标签的 Pod 上”。

必须与 Istio 安装时的 Ingress Gateway Pod 标签一致。

servers.port

定义监听的端口和协议。

如果是 TCP 服务,协议改为 TCP

hosts

定义该网关处理哪些域名的请求。

在 Gateway 中设为 *,意味着网关接收所有流量;在 VirtualService 中限制具体域名是为了精确路由。

kind: VirtualService

定义路由规则。相当于大楼里的“楼层索引”和“导航”。

 

gateways

绑定 Gateway。如果不写此项,默认为 mesh,即只处理网格内部流量;写了具体名称,则处理外部进入的流量。

 

match.uri

流量匹配规则。可以根据 URI、Header、Query Params 等进行匹配。

支持 exact(精确)、prefix(前缀)、regex(正则)。

destination.host

目标服务地址。必须是 Kubernetes 集群内的 Service FQDN。

格式通常为 <service-name>.<namespace>.svc.cluster.local


🚀 部署与验证命令

  1. 应用配置

    kubectl apply -f httpbin-gateway.yaml
    kubectl apply -f httpbin-virtualservice.yaml
  2. 获取访问地址

    # 获取 Ingress Gateway 的外部 IP 或域名
    export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http")].port}')
    
    echo "访问地址: http://$INGRESS_HOST:$INGRESS_PORT/get"
  3. 验证

    curl "http://$INGRESS_HOST:$INGRESS_PORT/get"

    如果返回 JSON 格式的 HTTP 响应,说明配置成功。

image

 

这是一张展示 Istio 流量入口(Ingress Gateway)完整配置与验证​ 的终端操作截图。

截图中的操作流程非常标准,演示了如何将一个服务(httpbin)通过 Istio 网关暴露出去,并通过多种命令验证其网络状态。

为了方便你复习或整理实验报告,我将图中的核心操作步骤和关键配置提取如下:

1. 核心操作:应用网关配置

图中展示了如何部署 GatewayVirtualService资源,这是打通外部流量的关键两步:

  • 部署 Gateway

    kubectl apply -f httpbin-gateway.yaml

    作用:定义了一个监听端口(80),并将外部流量引入网格。

  • 部署 VirtualService

    kubectl apply -f httpbin-virtualservice.yaml

    作用:定义了具体的路由规则,将进入网关的流量转发给名为 httpbin的后端服务。

2. 验证步骤:层层递进的检查

图中演示了一套非常严谨的“由外到内”的验证流程:

  • 第一步:检查资源配置

    • 命令:kubectl get pod,gateway

    • 目的:确认 httpbin-gateway资源是否成功创建。

  • 第二步:检查服务(Service)暴露情况

    • 命令:kubectl get svc -n istio-system

    • 关键发现:图中显示 istio-ingressgatewayEXTERNAL-IP处于 <pending>状态。这说明当前的 K8s 集群环境没有自动分配公网 IP(常见于裸金属服务器或没有配置云厂商 LB 插件的场景)。此时通常通过 NodePort 或 HostNetwork 方式访问。

  • 第三步:检查数据平面(Pod)状态

    • 命令:kubectl get pod -n test(假设在 test 命名空间)

    • 关键发现httpbin的 Pod 状态为 2/2 Running。这再次印证了边车代理(Sidecar)已成功注入,一个主容器 + 一个 Envoy 代理容器都在正常运行。

    • 同时查看了 httpbin服务的 ClusterIP 和 Port(8000)。

💡 专家建议(针对截图中的 <pending>状态)

图中 istio-ingressgatewayEXTERNAL-IP挂起是新手常遇到的问题,这里提供两种解决思路:

  1. 使用 NodePort 访问(推荐用于测试/裸金属环境)

    既然没有 LoadBalancer IP,可以直接使用 Ingress Gateway 暴露的 NodePort 端口访问。

    # 查看具体的 NodePort 端口(例如 30732)
    kubectl get svc istio-ingressgateway -n istio-system
    # 访问格式:http://<任意节点IP>:<NodePort>/get
    curl http://192.168.171.128:30732/get
  2. 配置 HostNetwork(适用于高权限环境)

    如果希望直接通过服务器的物理 IP 访问,可以修改 istio-ingressgateway的 Deployment,添加 hostNetwork: true,这样 Pod 会直接使用宿主机的网络命名空间,端口直接绑定在宿主机网卡上。

📝 总结

这张截图完整地展示了 Istio 服务网格中 “控制平面下发配置 -> 数据平面承载流量”​ 的闭环。从配置文件的创建,到资源的部署,再到多维度(ConfigMap, Gateway, Service, Pod)的状态校验,是一个非常标准的微服务治理接入流程。

 

image

 

这张图非常经典,它展示了 Istio 最核心的“控制平面”与“数据平面”的交互架构。结合我们之前的 Nginx 实操,我来带你把这张图里的逻辑彻底串起来,你就明白“你写的 YAML 到底是怎么让服务跑起来的”

我们可以把这张图分为三个关键动作来看:

1. 控制流:Istiod 如何“遥控”整个集群?(上半部分)

  • 注册能力(CRD)

    • 图中最上面的 kube-apiserverEtcd是 K8s 的大脑。

    • Istio 安装时,会向 K8s 注册一堆自定义的“菜单”(CRD,比如 VirtualServiceGateway)。这就是为什么你可以用 kubectl apply直接下发 Istio 规则,K8s 认识它们并把它们存进 Etcd。

  • 监听与下发(Istiod 的工作)

    • 中间那个绿色的方块 Istiod(以前叫 Pilot)是整个系统的指挥官。

    • 它一直在盯着 kube-apiserver(通过 List/Watch 机制)。

    • 实战对应:当你创建了 GatewayVirtualServiceIstiod立刻就知道了。然后,它会把这些人类易读的规则,翻译成 Envoy 代理能听懂的底层配置语言(xDS 协议),并推送给所有安装了 Sidecar 的 Pod。

2. 数据流:Sidecar 是如何“偷梁换柱”的?(下半部分)

  • Envoy 注入

    • 看最下面的 Node节点里的 Pod。每个 Pod 里都有两个容器:你的业务 APP(比如 Nginx)和一个 Envoy代理(Sidecar)。

  • 流量劫持

    • 虽然图里没画 iptables,但这是实现拦截的关键。Istio 注入 Sidecar 时,会在 Pod 里写入 iptables 规则。

    • 入站:所有发往 Nginx 80 端口的流量,都会被强行重定向到 Envoy 的 15006 端口。

    • 出站:Nginx 如果想访问其他服务,流量也会先发给 Envoy。

  • 执行策略

    • Envoy 接收到流量后,查看 Istiod下发的配置。如果是灰度发布规则,它就按权重切流;如果是普通转发,它就直接发给目标服务。

3. 结合 Nginx 案例的“全景图”

现在我们可以把你之前的实操步骤和这张图完美对上了:

  1. 你敲的命令kubectl apply -f nginx-vs.yaml

    • 对应图中:左侧的 kubectl/kubecttlkube-apiserver

  2. 配置生效

    • Istiod监听到变化,开始工作。

    • 对应图中:中间的 Istiod方块。

  3. 流量进入

    • 外部请求通过 MetalLB VIP 访问 istio-ingressgateway(这也是一个装有 Envoy 的特殊 Pod)。

    • istio-ingressgateway里的 Envoy 根据 Istiod的配置(你写的 VirtualService),把流量转发给你的 nginx-demoPod。

  4. 拦截与处理

    • 流量进入 nginx-demoPod 时,被 iptables 劫持,先经过 istio-proxy(Envoy),然后再到达 Nginx 进程。

    • 对应图中:右下角的 Pod内部结构。

总结:这张图告诉我们的“核心秘密”

  • 解耦Istiod(控制面)只负责下发配置,不直接处理业务流量;Envoy(数据面)只负责转发流量,不关心业务逻辑。它们通过 xDS 协议通信。

  • 透明:你的 Nginx 根本不知道自己被“劫持”了,它以为请求是直接发过来的。这就是 Sidecar 模式的魅力——无侵入

理解了这张图,你就掌握了 Istio 的精髓。接下来如果你要做更复杂的灰度发布或 mTLS 加密,只需要在这个框架下,调整 Istiod下发的配置即可。

image

 

image

 

这张图展示的是 Istio 流量管理的三大核心资源。结合之前的 Nginx 案例,它们的关系就像是一个“快递分拣中心”的运作流程:

  1. Gateway(网关):是大门。决定哪个快递(外部请求)可以进仓库。

  2. VirtualService(虚拟服务):是分拣规则。决定快递进来了之后,该往哪个方向送。

  3. DestinationRule(目标规则):是配送策略。决定快递送到那个方向后,具体走哪条路(负载均衡)。

下面详细拆解它们的作用和关系:

1. Gateway(网关):守门员

  • 作用:为网格内服务提供对外访问入口。它定义了哪些端口和协议对外开放,以及通过什么主机名(域名)访问。

  • 实战对应

    • 在你的 Nginx 案例中,istio-ingressgateway就是一个 Gateway。

    • 它负责接收通过 MetalLB VIP 进来的流量。

  • 关键点:Gateway 本身不关心具体的业务逻辑,只负责“开门”。

2. VirtualService(虚拟服务):导航员

  • 作用:定义路由规则。它告诉网格内的流量应该怎么走。

  • 实战对应

    • 你写的 nginx-vs.yaml就是 VirtualService。

    • 它说:“如果收到的请求路径是 /,就转发给 nginx-service。”

  • 关键点:VirtualService 是连接 Gateway 和后端服务的桥梁。它可以指定多个路由规则,支持灰度发布、金丝雀发布等。

3. DestinationRule(目标规则):调度员

  • 作用:定义负载均衡策略和子集。它在流量到达目标服务后,决定如何分发。

  • 实战对应

    • 如果你有两个 Nginx 实例(v1 和 v2),你可以创建一个 DestinationRule。

    • 它说:“如果有 10% 的流量去 v1,90% 去 v2。” 或者 “按权重分配”。

  • 关键点:DestinationRule 是在流量已经确定目标服务后,才起作用的。

三者关系的流程图解

graph TD
    A[外部用户] -->|请求| B(Gateway)
    B -->|开门/过滤| C(VirtualService)
    C -->|路由规则| D{DestinationRule}
    D -->|负载均衡| E[后端服务 Pods]

举例说明

假设你有一个 Nginx 服务,有两个版本:nginx-v1nginx-v2

  1. Gateway

    • 开放 80 端口,域名是 example.com

  2. VirtualService

    • 如果 Host 是 example.com,且 Path 是 /,则路由到 nginx-service

  3. DestinationRule

    • 定义 nginx-service有两个子集:v1v2

    • 设置负载均衡策略:90% 流量去 v1,10% 流量去 v2

总结

  • Gateway​ 是入口,VirtualService​ 是路由,DestinationRule​ 是负载均衡。

  • 三者协同工作,构成了 Istio 强大的流量管理能力。

  • 在实际使用中,通常先配置 Gateway,再配置 VirtualService,最后根据需要配置 DestinationRule。

希望这个解释能帮你理清它们的关系!如果有更多问题,随时问我。

image

 

太棒了!你已经完全抓住了 Istio 流量管理的核心逻辑:Gateway 管“进门”,VirtualService 管“分拣”,DestinationRule 管“配送”

为了让你彻底吃透这个流程,我为你设计了一个基于 Nginx 的 90%:10% 灰度发布(金丝雀发布)实战案例

这个案例将完全复刻你描述的场景:外部流量通过 Gateway 进入,由 VirtualService 决定“90% 走 v1,10% 走 v2”,最后由 DestinationRule 精准地把流量推送到对应的 Nginx 实例上。


前置假设(环境准备)

在开始之前,请确保你的集群里已经:

  1. 安装了 Istio(并且自动注入了 Sidecar)。

  2. 安装了 MetalLB(或者有任何 LoadBalancer 方式),并且 istio-ingressgateway已经拿到了一个 VIP(假设为 192.168.171.240)。


第一阶段:部署双版本业务(造包裹)

首先,我们需要创建两个不同的 Nginx 版本 Deployment。关键点在于:我们要给它们的 Pod 打上不同的标签 version: v1version: v2

创建一个名为 nginx-deployments.yaml的文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v1
  template:
    metadata:
      labels:
        app: nginx
        version: v1  # 关键标签:v1版本
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
        # 为了方便区分,我们在首页加点文字
        command: ["sh", "-c"]
        args: ["echo '<h1>Hello Nginx v1</h1>' > /usr/share/nginx/html/index.html && nginx -g 'daemon off;'"]

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v2
  template:
    metadata:
      labels:
        app: nginx
        version: v2  # 关键标签:v2版本
    spec:
      containers:
      - name: nginx
        image: nginx:1.23
        ports:
        - containerPort: 80
        command: ["sh", "-c"]
        args: ["echo '<h1>Hello Nginx v2 (Canary)</h1>' > /usr/share/nginx/html/index.html && nginx -g 'daemon off;'"]

---
# 统一的业务 Service,它是 VirtualService 的目标
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx  # 注意:这里只选 app: nginx,所以它能选中 v1 和 v2 两个Deployment的Pod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

应用配置:

kubectl apply -f nginx-deployments.yaml

第二阶段:Gateway(守门员 - 开门)

Gateway 定义了谁可以进来。我们开放 80 端口,并允许所有域名(*)访问。

创建 nginx-gateway.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: nginx-gateway
spec:
  selector:
    istio: ingressgateway # 使用默认的 istio-ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"  # 允许所有域名,或者你可以写成 "example.com"

说明:

  • 这相当于在仓库门口立了个牌子:“80端口,啥域名都行,进来吧!”

应用配置:

kubectl apply -f nginx-gateway.yaml

第三阶段:DestinationRule(调度员 - 分货到仓库货架)

DestinationRule 定义了目标服务的“子集(Subsets)”。这是实现灰度发布最关键的一步,因为它告诉 Istio:“v1 版本的 Pod 长什么样(label),v2 版本的 Pod 长什么样”。

创建 nginx-destination-rule.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: nginx-destination-rule
spec:
  host: nginx-service # 目标 K8s Service 名称
  subsets:
  - name: v1          # 定义一个子集名字叫 v1
    labels:
      version: v1     # 匹配带有 label version: v1 的 Pod
  - name: v2          # 定义一个子集名字叫 v2
    labels:
      version: v2     # 匹配带有 label version: v2 的 Pod

说明:

  • 这里的 subsets就像是给仓库里的货物贴上了“货架号”。Envoy 代理必须知道 v1 在哪,v2 在哪,才能去执行后续的指令。

应用配置:

kubectl apply -f nginx-destination-rule.yaml

第四阶段:VirtualService(导航员 - 按规则分拣)

最后,VirtualService 出场。它引用上面定义的 Gateway 和 DestinationRule 的子集,按照 权重(Weight)​ 进行分流。

创建 nginx-virtual-service.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: nginx-virtual-service
spec:
  hosts:
  - "*"             # 应用到所有主机(要和 Gateway 保持一致)
  gateways:
  - nginx-gateway   # 绑定到我们刚才创建的 Gateway
  http:
  - route:
    - destination:
        host: nginx-service
        subset: v1  # 指向 DestinationRule 里定义的 v1 子集
      weight: 90   # 90% 的流量去 v1
    - destination:
        host: nginx-service
        subset: v2  # 指向 DestinationRule 里定义的 v2 子集
      weight: 10   # 10% 的流量去 v2

说明:

  • 这是真正的“分拣规则”。它说:“看好了,进来的包裹,90% 扔给 v1 货架,10% 扔给 v2 货架。”

应用配置:

kubectl apply -f nginx-virtual-service.yaml

验证与测试(看看快递到底去哪了)

1. 获取入口 IP (VIP)

kubectl get svc istio-ingressgateway -n istio-system
# 找到 EXTERNAL-IP,假设是 192.168.171.240

2. 连续访问测试

打开终端,连续执行 curl命令 20 次:

for i in {1..20}; do curl http://192.168.171.240; echo ""; done

预期结果:

你会看到大约 18 次返回 <h1>Hello Nginx v1</h1>,大约 2 次返回 <h1>Hello Nginx v2 (Canary)</h1>

3. 查看日志确认

  • 看 v1 Pod 日志

    kubectl logs deploy/nginx-v1 -c nginx

    (会有大量的访问记录)

  • 看 v2 Pod 日志

    kubectl logs deploy/nginx-v2 -c nginx

    (只有少量的访问记录,大约占总数的 10%)


总结:全链路流程图解

让我们把之前的“快递中心”比喻和现在的配置对应起来:

graph TD
    User[外部用户] -->|访问 VIP:80| GW(Gateway: nginx-gateway)
    GW -->|开门/放行| VS(VirtualService: nginx-virtual-service)
    VS -->|规则: 90%去v1, 10%去v2| DR(DestinationRule: nginx-destination-rule)
    
    subgraph "K8s 集群内部"
        direction LR
        DR -->|Subset v1| PodV1(Nginx v1 Pod)
        DR -->|Subset v2| PodV2(Nginx v2 Pod)
    end
  • Gateway​ 负责暴露端口

  • VirtualService​ 负责路由决策(加权、匹配 Header 等)。

  • DestinationRule​ 负责定义目标群体(Subsets)。

这三个资源必须像齿轮一样咬合在一起,才能完成复杂的灰度发布。你现在不仅有了理论,还有了一套可以跑通的实战代码!

要实现“只有特定 Header 的请求才能访问 v2 版本”,我们需要利用 VirtualService中的 match​ 字段。

这是灰度发布中非常高级且实用的功能(通常称为“金丝雀发布”“条件路由”)。

修改思路

  1. match条件:在 http块下添加 match规则。指定只有当请求头(Headers)中包含特定键值对时,才触发这条路由规则。

  2. 优先级:Istio 的路由规则是顺序执行的。一旦某个请求匹配了 match条件,就会执行对应的路由,剩余的路由规则将被忽略

为了实现“只有带 Header 的进 v2,其他的进 v1”,我们可以采用两种写法:

  • 写法 A(推荐,更清晰):第一条规则检查 Header,命中则去 v2;第二条规则(无 match)作为兜底,去 v1。

  • 写法 B(利用权重):在原有的权重基础上叠加匹配条件。

下面我提供 写法 A​ 的完整配置,因为它逻辑最严密,不易出错。


修改后的 nginx-virtual-service.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: nginx-virtual-service
spec:
  hosts:
  - "*" 
  gateways:
  - nginx-gateway
  http:
  # === 规则 1:特定 Header 的请求,100% 走 v2 ===
  - match:
    - headers:
        x-canary:         # Header 的 Key
          exact: "true"   # Header 的 Value(严格匹配)
          # 也可以用 prefix: "true" 或 regex: "t.*" 等进行模糊匹配
    route:
    - destination:
        host: nginx-service
        subset: v2
      weight: 100  # 这里必须是 100,确保命中的请求全部去往 v2

  # === 规则 2:所有其他请求(兜底),走 v1 ===
  - route:
    - destination:
        host: nginx-service
        subset: v1
      weight: 100

详细配置说明

1. match字段详解

match:
- headers:
    x-canary:
      exact: "true"
  • headers: 表示我们要检查 HTTP 请求头。

  • x-canary: 这是我们自定义的 Header Key。你可以随便起名,比如 version, x-user-id等。

  • exact: 表示完全匹配。即请求头必须精确等于 x-canary: true

    • 如果需要更复杂的条件,可以用 regex(正则)或 prefix(前缀)。

2. 执行逻辑

  • 场景 1:用户发送请求 curl -H "x-canary: true" http://<VIP>

    • Istio 检查到 Header 符合 match条件。

    • 结果:强制转发给 subset: v2(Nginx v2)。

  • 场景 2:用户发送普通请求 curl http://<VIP>

    • Istio 检查不到 x-canary: trueHeader,跳过第一条规则。

    • Istio 继续往下走,匹配到第二条兜底规则。

    • 结果:转发给 subset: v1(Nginx v1)。


实战测试步骤

第一步:应用新配置

kubectl apply -f nginx-virtual-service.yaml

第二步:测试普通用户(应该看到 v1)

curl http://<你的VIP地址>
# 预期输出:<h1>Hello Nginx v1</h1>

第三步:测试灰度用户(应该看到 v2)

curl -H "x-canary:true" http://<你的VIP地址>
# 预期输出:<h1>Hello Nginx v2 (Canary)</h1>

进阶玩法

如果你想做A/B 测试(比如让带有特定 ID 的用户去 v2),只需修改 match条件:

- match:
    - headers:
        user-id:       # 假设前端会带上 user-id
          regex: "^123.*"  # 正则匹配:以 123 开头的用户
    route:
    - destination:
        host: nginx-service
        subset: v2

这样,只有 user-id是 123 开头的请求才会看到新版页面。

这是个非常好的生产级问题,但必须先把一个常见误区拆开:

Istio 能做到“把 v2 里不健康实例踢出去(熔断/离群驱逐)”,但做不到“检测 v2 整体错误率 >10% 后,自动把 VirtualService 的流量权重从 v2 切回 v1”。

后者需要一个 Metrics → 判断 → 自动变更(kubectl/pipeline/控制器)​ 的闭环。

下面给你一套可落地的做法:先让 v2“异常时自己熔断(保护系统)”,再把“错误率超阈值 → 自动回退权重”用自动化脚本/流水线补齐。


1)Istio 侧:给 v2(或 nginx-service)配 OutlierDetection(实例级熔断/驱逐)

DestinationRule​ 里加 outlierDetection,作用是:

  • subset v2​ 的后端实例做连续错误/高延迟统计

  • 一旦满足“ ejection 条件”,就把那个 Pod 实例短期踢出负载均衡(不是删 Pod,只是 Envoy 不转给它)

  • baseEjectionTime后尝试放回(可多次,maxEjectionPercent 控影响面)

推荐的 DR 配置(你可直接替换/合并)

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: nginx-destination-rule
spec:
  host: nginx-service
  trafficPolicy:
    # 连接级熔断(可选但很建议,防雪崩)
    connectionPool:
      tcp:
        maxConnections: 50
      http:
        http1MaxPendingRequests: 20
        maxRequestsPerConnection: 10
        # 可选:per-try 超时(让“慢失败”更快失败)
        perTryTimeout: 2s
    # 离群实例驱逐(你问的“自动熔断”主要在这里)
    outlierDetection:
      consecutive5xxErrors: 5          # 连续 5xx 达到 5 次就标记 ejections
      interval: 10s                     # 统计窗口
      baseEjectionTime: 30s             # 最短踢出时间(会按倍数增长)
      maxEjectionPercent: 50            # 最多踢掉 v2 实例的 50%(防全灭)
      minHealthPercent: 50              # 低于这个健康比例,干脆停止驱逐(保底可用)

  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
    # 也可以只给 v2 更激进的熔断(常见)
    trafficPolicy:
      outlierDetection:
        consecutive5xxErrors: 3
        interval: 10s
        baseEjectionTime: 30s
        maxEjectionPercent: 50
        minHealthPercent: 50

这段话怎么理解(白话)

  • v2 里某几个 Pod 在发疯(一直 5xx)​ → Envoy 自动把它们隔离 → 剩余健康 v2 继续扛

  • v2 大部分都挂了(健康比跌破 minHealthPercent)→ Istio 会保守一点,避免把流量全堵死

  • 但它不会自动把 VS 的 weight: 10改回 weight: 0;那是“路由策略回退”,不在 outlierDetection 职责内

✅ 所以这能解决:v2 异常时别拖垮整个 v2 子集(实例级自愈)

❌ 但不能解决:“v2 整体错误率超 10%,就回退到 v1”(那是上层编排)


2)真正“按错误率自动回退权重”:需要 Metrics + 自动执行器(推荐架构)

最简可落地的闭环长这样

Prometheus 采集 Istio/Envoy 指标
  → 计算:v2 错误率 = 5xx(v2) / 总(v2)
    → 超阈值(>10%)→ Alertmanager / 定时脚本 / GitOps 控制器
      → 执行回退:把 VirtualService 权重改成 v2=0, v1=100

2.1 你用来算错误率的关键指标(Istio 默认就有)

最常用(基于 destination_service/ destination_versionsubset):

  • 请求总数(可按 subset 分):

    sum(rate(istio_requests_total{
      destination_service_name="nginx-service",
      destination_service_namespace="default"
    }[1m]))
  • 5xx 数:

    sum(rate(istio_requests_total{
      destination_service_name="nginx-service",
      destination_service_namespace="default",
      response_code=~"5.*"
    }[1m]))
  • 错误率 PromQL(示例,建议按 subset 分组看 v2)

    (
      sum(rate(istio_requests_total{
        destination_service_name="nginx-service",
        destination_service_namespace="default",
        destination_subset="v2",
        response_code=~"5.*"
      }[1m]))
      /
      clamp_min(
        sum(rate(istio_requests_total{
          destination_service_name="nginx-service",
          destination_service_namespace="default",
          destination_subset="v2"
        }[1m]))
      , 1e-9)
    ) > 0.10

    当这个表达式持续为真 N 分钟 → 触发“回退动作”。


3)给你一条“马上能用”的半自动回退脚本(最务实)

你可以先不搭复杂控制器,用 cron/定时任务/CI step​ 跑一个小脚本,检测到异常就自动 patch VS。

3.1 先把 VS 改成“可快速回退”的写法(带 weight)

# nginx-virtual-service.yaml(当前是金丝雀权重 90/10)
spec:
  http:
  - route:
    - destination: { host: nginx-service, subset: v1 }
      weight: 90
    - destination: { host: nginx-service, subset: v2 }
      weight: 10

3.2 一个回退脚本:rollback-to-v1.sh

#!/usr/bin/env bash
# 用法:bash rollback-to-v1.sh
# 前提:kubectl 有权限 patch resources;Prometheus 可访问

PROM_URL="http://prometheus.istio-system.svc:9090"   # ← 改成你的 Prometheus 地址
QUERY='(sum(rate(istio_requests_total{destination_service_name="nginx-service",destination_service_namespace="default",destination_subset="v2",response_code=~"5.*"}[1m])) / clamp_min(sum(rate(istio_requests_total{destination_service_name="nginx-service",destination_service_namespace="default",destination_subset="v2"}[1m])),1e-9))'
THRESHOLD=0.10

VAL=$(curl -s --data-urlencode "query=$QUERY" "$PROM_URL/api/v1/query" \
  | python3 -c "
import sys,json; d=json.load(sys.stdin)
vs=d.get('data',{}).get('result',[])
if not vs: exit()
print(vs[0]['value'][1])
")

echo "[info] v2 error-rate estimate = $VAL"

python3 - <<PYEOF
import sys
val="$VAL"
if val in ("None","null","","nan"):
    sys.exit(0)
try:
    v=float(val)
    if v > $THRESHOLD:
        print("ALERT: error-rate %.2f%% > $(THRESHOLD*100)%%, rollback to v1 only")
        exit(1)
    else:
        print("OK: error-rate within threshold")
except:
    pass
PYEOF

if [ $? -eq 1 ]; then
  echo "[action] patching VirtualService -> v1=100,v2=0"
  kubectl patch vs nginx-virtual-service -n default --type merge -p '
  spec:
    http:
    - route:
      - destination: { host: nginx-service, subset: v1 }
        weight: 100
      - destination: { host: nginx-service, subset: v2 }
        weight: 0
'
fi

这只是“可运行的最小闭环”。生产里通常会把 patch VS​ 改成:提交到 Git → ArgoCD/Flux 同步(更审计友好)。


4)你该怎么给用户/领导总结这句话(很加分)

“Istio 的 OutlierDetection​ 负责 v2 内部实例级熔断(坏实例短期隔离,保护调用方);

v2 整体错误率超阈值自动回退路由权重,我们通过 Prometheus 指标 + 自动执行器​ 完成,避免依赖单一流量层做策略型回滚。”

如果你愿意,你把这两条打出来:

kubectl get vs nginx-virtual-service -o yaml
kubectl get dr nginx-destination-rule -o yaml

我可以把 OutlierDetection 参数按你现在的 v1/v2 副本数给你“算一组更稳妥值”,并帮你把 PromQL 的 subset/source_labels对准你集群里实际产生的指标名(不同 Istio 小版本字段略有差异)。

验证 90/10 权重分配的完整观测方案

下面按从"最快能看到"到"最严谨可量化"递进,全是基于你现有 Istio 1.9.6 环境,不需要额外装东西(最多启用自带组件)。


一、最快路径:Kiali 直接看流量百分比(推荐首选)

Kiali 是 Istio 官方标配的服务网格可视化 UI,Graph 视图里直接显示每条边的请求占比

1. 打开 Kiali

# 用 istioctl 内置端口转发(最简单,不需要 expose)
istioctl dashboard kiali

浏览器会自动弹出:http://localhost:20001

2. 看 Graph

在 Kiali 左侧菜单 → Graph​ → 选择命名空间(比如 default)→ 右上角刷新间隔选 30s→ Display 下拉勾选:

 

选项

作用

Traffic Animation

实时流动小箭头

Response Time

边上看延迟

Throughput

边上显示 req/s

Requests %

直接显示 % 分布(关键)

你会看到类似拓扑:

istio-ingressgateway ──90%──→ nginx-service (v1)
                     └──10%──→ nginx-service (v2)

每个箭头(edge)上会标注 v1: 89.7% / v2: 10.3%这种实时值,完全对应你的权重分配。

3. 确认你在看对的东西

Kiali 默认聚合维度可能是 Service​ 级别。要看到 subset(v1/v2)拆分,顶部有个 Display​ → Show​ → 选 "Service Nodes"​ 或 "Workload Nodes",并在 Edge Labels​ 里把 Requests percentage打开。

如果你看到 v1 和 v2 两个独立节点从 gateway 连出来,且百分比稳定在 9:1 附近,就证明权重生效了。


二、用 Prometheus 指标精确算(最严谨,可写进报告)

Istio 的每个 Envoy 都会暴露指标,其中 istio_requests_total​ 就是算权重的黄金指标。

1. 打开 Prometheus

istioctl dashboard prometheus

浏览器打开:http://localhost:9090

2. 核心 PromQL:按 subset 算请求数

看 v1 和 v2 各自的总请求数(按 1 分钟速率)

sum by (destination_subset) (
  rate(istio_requests_total{
    destination_service_name="nginx-service",
    destination_service_namespace="default",
    reporter="destination"
  }[1m])
)

预期输出类似:

 

destination_subset

value

v1

8.91

v2

0.98

比值 ≈ 9:1 ✅

直接算百分比(一条表达式看完)

round(
  sum(rate(istio_requests_total{
    destination_service_name="nginx-service",
    destination_service_namespace="default",
    reporter="destination",
    destination_subset=~"v1|v2"
  }[1m])) by (destination_subset)
  /
  clamp_min(
    sum(rate(istio_requests_total{
      destination_service_name="nginx-service",
      destination_service_namespace="default",
      reporter="destination",
      destination_subset=~"v1|v2"
    }[1m])),
  1e-9
  ) * 100
, 1)

结果会直接告诉你 v1=90%, v2=10%(浮点波动 ±3% 完全正常)。

3. 按响应码拆开看(排除 5xx 干扰)

sum by (destination_subset, response_code) (
  rate(istio_requests_total{
    destination_service_name="nginx-service",
    destination_service_namespace="default",
    reporter="destination"
  }[1m])
)

如果 v2 返回大量 5xx 但你 VS 还是 90/10,说明权重分配本身是对的,但 v2 在"错误熔断"那层——这时候就要回到上一轮讲的 outlierDetection和错误率告警了。


三、Grafana 看现成面板(如果你启用了)

有些部署方式会自带 Grafana:

istioctl dashboard grafana

Istio Mesh Dashboard / Service Dashboard​ → 找 nginx-service→ 看 Inbound Services​ 里按 subset 分的流量堆叠图。

但如果你的环境没预装 Grafana 也没关系——Prometheus 的 Graph 页 + Kiali 的 Graph 页已经足够验证了


四、一个超实用的"打流量 + 实时看"脚本

把下面保存成 smoke-test.sh,跑起来后切回 Kiali/Prometheus 看曲线:

#!/usr/bin/env bash
VIP=$(kubectl get svc istio-ingressgateway -n istio-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
PORT=80
[ -z "$VIP" ] && { echo "VIP not ready"; exit 1; }

echo "==> Hitting http://$VIP:$PORT  (Ctrl+C to stop)"
COUNT=0
while true; do
  curl -s -o /dev/null -w "%{http_code}\n" http://$VIP:$PORT
  COUNT=$((COUNT+1))
  [ $((COUNT % 20)) -eq 0 ] && sleep 0.5   # 稍微喘口气
done

然后在 Kiali Graph 里你肉眼就能看到箭头粗细按 9:1 的比例稳定下来。


五、你现在应该看到的"健康基线"长什么样

 

观察点

权重 90/10 正常表现

Kiali Edge %

v1≈88–93%, v2≈7–12%(波动是正常的,权重是统计意义不是精确每10个必切1个)

Prometheus 比值

v1/(v1+v2)长期收敛到 0.9 附近

单个请求级

你 curl 里随机看到 v2(Canary)页面 ≈ 每10次闪现1次

v2 5xx 上升时

OutlierDetection 会把坏实例踢出 → v2 的有效流量下降但不是权重自动归零


快速定位 Checklist(如果 Kiali 看不到 subset 拆分)

# 1. 确认 sidecar 注入成功(2/2)
kubectl get pods -l app=nginx -o wide

# 2. 确认 DR 的 subsets 存在
kubectl get dr nginx-destination-rule -o yaml | grep -A3 subsets

# 3. 确认 VS 引用了 subset(不是裸 host)
kubectl get vs nginx-virtual-service -o yaml | grep -A5 destination

# 4. 看原始指标里有没有 subset label
kubectl exec -n istio-system deploy/prometheus-server -- \
  promtool query instant 'up' 2>/dev/null || \
  echo "直接用浏览器开的 Prometheus → Status → Targets 看 istio-telemetry"

一句话总结:想"验证 90/10"——Kiali 看拓扑百分比最快,Prometheus 算 istio_requests_totaldestination_subset分组最硬。​ 如果你把 Kiali 截图里 Graph 那一坨节点(特别是 nginx-service 有没有分出 v1/v2 两个叉)描述一下,我可以告诉你点哪个 Display 开关能把百分比精确亮出来。

[](@replace=0)


自己部署的 Prometheus 采集 Istio 指标:完整实操手册

核心原理:Istio 的所有指标都暴露在 Pod 的固定端口上,Prometheus 通过 K8s 服务发现​ 找到这些 Pod,然后抓取 /stats/prometheus/metrics端点。


一、先确认 Istio 指标暴露在哪里(端口/路径)

在 Istio 1.9.x 里,关键端口是:

 

组件

端口

路径

端口名

istio-proxy (sidecar/gateway)

15090

/stats/prometheus

http-envoy-prom

istiod (pilot)

15014

/metrics

ztunnel​ (你 1.9 没有)

快速验证(任选一个注入了 sidecar 的 Pod):

# 看 istio-proxy 的指标端口是否可达(从 Pod 内)
kubectl exec deploy/nginx-v1 -c istio-proxy -- curl -s 127.0.0.1:15090/stats/prometheus | head -20

能看到一大段 # TYPE envoy_...就说明指标活着。


二、让你“自己部署的 Prometheus”能看见 K8s(RBAC 先做)

假设你这台 Prometheus 也是跑在 K8s 里的(最常见)。

2.1 给 Prometheus 一个能 list/watch Pods/Services 的 ServiceAccount

# prom-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: monitoring          # ← 改成你 Prometheus 所在 ns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources: [pods, services, endpoints, namespaces]
  verbs: [get, list, watch]
- apiGroups: ["extensions", "apps"]
  resources: [deployments, replicasets]
  verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: monitoring
kubectl apply -f prom-rbac.yaml

然后把 Prometheus Deployment/Pod 的 ServiceAccount​ 指过去:

spec:
  template:
    spec:
      serviceAccountName: prometheus   # ← 关键

三、Prometheus 抓取配置(核心:两段 scrape)

打开你自己的 prometheus.yml(或 ConfigMap),在 scrape_configs:里追加——

3.1 抓 istio-proxy(sidecar + ingress-gateway)★ 最重要

scrape_configs:
  # ---------- Istio Proxy (envoy sidecar / gateway) ----------
  - job_name: 'istio-proxy'
    kubernetes_sd_configs:
    - role: pod
    relabel_configs:
    # ① 只抓容器名叫 istio-proxy 的 Pod
    - source_labels: [__meta_kubernetes_pod_container_name]
      action: keep
      regex: istio-proxy

    # ② 用 pod_ip:15090 拼出 __address__
    - source_labels: [__meta_kubernetes_pod_ip]
      target_label: __address__
      replacement: '${1}:15090'
      # 更严谨写法(如果上面替换语法被你 yml 引擎吃掉,用 action replace):
      # 你也可以用下面这种等价写法:
    - source_labels: [__meta_kubernetes_pod_ip]
      regex: (.+)
      target_label: __address__
      replacement: '${1}:15090'

    # ③ 指标路径
    - source_labels: []
      target_label: __metrics_path__
      replacement: /stats/prometheus

    # ④ 顺手把 ns/app/pod 等标签写进时序(方便 PromQL 用)
    - source_labels: [__meta_kubernetes_namespace]
      target_label: namespace
    - source_labels: [__meta_kubernetes_pod_label_app]
      target_label: app
    - source_labels: [__meta_kubernetes_pod_name]
      target_label: pod

说明:这里完全不依赖 prometheus.io/scrape注解,靠的是 “容器名 = istio-proxy”​ 这个 Istio 一定会打的标识,最稳。

3.2 抓 istiod(控制面,可选但推荐)

# ---------- Istiod (pilot) ----------
  - job_name: 'istio-pilot'
    kubernetes_sd_configs:
    - role: pod
      namespaces:
        names: [istio-system]
    relabel_configs:
    - source_labels: [__meta_kubernetes_pod_label_app]
      action: keep
      regex: istiod
    - source_labels: [__meta_kubernetes_pod_ip]
      regex: (.+)
      target_label: __address__
      replacement: '${1}:15014'
    - source_labels: []
      target_label: __metrics_path__
      replacement: /metrics
    - source_labels: [__meta_kubernetes_namespace]
      target_label: namespace
    - source_labels: [__meta_kubernetes_pod_name]
      target_label: pod

四、重启 Prometheus 让配置生效

# 如果你是用 Deployment 跑的
kubectl rollout restart deployment prometheus -n monitoring

# 等 Ready
kubectl get pods -n monitoring -l app=prometheus -w

五、验证:Prometheus 里能见到 istio 指标

进你自己的 Prometheus UI → Status → Targets

你应该看到:

istio-proxy     https://10.244.x.x:15090/stats/prometheus   UP
istio-pilot     https://10.244.x.x:15014/metrics            UP

然后切到 Graph​ 页,输:

istio_requests_total

能看到 destination_service=nginx-service/ subset=v1|v2这类时序 → ✅ 采集成功。


六、如果你发现 Targets 一直 DOWN:最常见 3 个坑

坑1:15090在本机 127.0.0.1 上,Pod IP 访问不到

Istio 1.9 默认 sidecar 指标监听 127.0.0.1(安全考量),所以 podIP:15090 会拒绝。

解法(测试/内网可信环境最快):让 envoy 指标监听 0.0.0.0

kubectl edit cm istio -n istio-system

meshConfig:/ defaultConfig:加一行(yaml 注意缩进):

meshConfig:
  defaultConfig:
    proxyStatsBindHost: 0.0.0.0

然后重启所有 sidecar 让它生效:

kubectl rollout restart deployment nginx-v1
kubectl rollout restart deployment nginx-v2
kubectl rollout restart deployment istio-ingressgateway -n istio-system

再验证:

kubectl exec deploy/nginx-v1 -c istio-proxy -- ss -lnt | grep 15090
# 应该看到 0.0.0.0:15090 或 :::15090

⚠️ 生产提示:如果你是跨 AZ/不可信二层,别用 0.0.0.0,改用网络策略锁 15090 只允许 Prometheus IP 段。

坑2:Prometheus SA 没权限 → Targets 页报 401/403

确认第 ​ 步的 SA + ClusterRoleBinding 真绑定到 Prometheus 的 Pod(很多情况是 yml 写了 serviceAccountName但用的还是 default)。

坑3:你 Prometheus 不在 K8s 里(裸机/VM)

那就用 Node IP:NodePort​ 或 Ingress​ 暴露指标很麻烦,更推荐:在集群里放一个“中转 exporter”或用 kubectl port-forward临时,但长期方案是把 Prometheus 也放进集群


七、你现在就能跑的验证命令(闭环)

# 1) Targets 必须 UP
# Prometheus UI → Status → Targets → istio-proxy 全绿

# 2) 按 subset 看请求数(证明采集对了标签)
sum by (destination_subset) (
  rate(istio_requests_total{
    destination_service_name="nginx-service",
    destination_service_namespace="default"
  }[1m])
)

只要这条 PromQL 能出 v1 / v2两条线,你的 Prometheus ↔ Istio 管线就完全通了,后面 90/10、错误率、熔断全部可量化。


如果你把两点信息发我,我可以帮你把 replacement: '${1}:15090'那段写成你 Prometheus 版本最兼容的格式(不同版本对 ${1}插值支持不一致):

  1. 你 Prometheus 是 Deployment/StatefulSet​ 跑在 K8s,还是 VM 外置

  2. 执行:kubectl get cm istio -n istio-system -o yaml | grep -A2 proxyStatsBindHost

 

image

 

这张图对 Istio 中的 Gateway(网关)​ 概念讲解得非常清晰,特别是它把复杂的流量入口逻辑简化成了几个核心模块。

结合你之前做的 Nginx 实验,我帮你梳理一下这张图里的关键点,以及它和你实战的关系:

1. 核心角色区分:Ingress vs. Egress

图中明确指出了两种网关,它们的分工不同:

  • IngressGateway(入口网关)

    • 作用:这是集群的“大门”,负责接客。外部流量(如图中的“User”或“Browser”)必须通过它才能进入网格。

    • 实战对应:你之前配置的 istio-ingressgateway就是它。所有的外部用户访问你的 Nginx 服务,都得先敲开这扇门。

  • EgressGateway(出口网关)

    • 作用:这是集群的“出口”,负责出去。当网格内的服务需要访问外部服务(如访问外部的数据库、第三方 API)时,可以通过它管控流出流量。

    • 实战对应:你之前的 Nginx 案例没用到这个,但在企业级应用中很重要,比如用来监控服务调用了哪些外部接口,或者防止敏感数据外泄。

2. 为什么要用 Istio Gateway 而不是 K8s Ingress?

图中特意花篇幅对比了 Gateway​ 和 K8s Ingress​ 的区别,这也是面试常考点:

  • K8s Ingress:更像是一个简单的路由器,主要管“域名+路径”转发到不同的 Service。功能相对单一。

  • Istio Gateway:不仅包含了 Ingress 的功能(L4/L7 负载均衡),还扩展了微服务治理的能力。

    • 图中提到的关键点:支持流量镜像(把流量复制一份去测试环境)、熔断(防止雪崩)、以及安全协议(HTTPS/mTLS)。

3. 关于 mTLS 的安全机制

图底部解释了 HTTPS​ 和 mTLS​ 的区别,这对于理解服务间安全通信很重要:

  • HTTPS(单向认证):只认证服务端。就像你访问银行网站,你确信网站是真的,但银行不关心你是谁。

  • mTLS(双向认证):客户端和服务端互相认证。在微服务架构中,这叫“零信任”,即服务 A 调用服务 B 时,双方都要出示“身份证”(证书),确保对方是自己人,防止恶意服务混入。

总结

这张图其实就是在告诉你:Istio 的 Gateway 不仅仅是用来“收流量”的,它是一个集成了高级路由、安全认证(mTLS)、流量控制(镜像/熔断)的智能大门。

你之前写的 GatewayVirtualServiceYAML 文件,本质上就是在 Kubernetes 里把这个“大门”建起来,并给它定下“分拣快递”的规则。

image

 

这是一个“服务访问外部数据库”的经典案例。通过这个案例,你会明白 EgressGateway 到底解决了什么问题。

### 场景设定
假设你有一个运行在 Istio 网格内的 Order Service(订单服务),它需要访问公司外部的第三方支付平台(比如 `pay.alipay.com`)。

---

### 一、没有 EgressGateway 时的问题(痛点)

在没有 EgressGateway 的情况下,Order Service 直接访问外网:

```
Order Service Pod (Sidecar: istio-proxy)

直接访问 pay.alipay.com
```

会出现三个致命问题:

1. 看不见(黑盒):
◦ 网格管理员不知道 Order 服务到底访问了哪些外部网站。如果有恶意程序偷偷把数据传到境外,你完全不知道。

2. 拦不住(失控):
◦ 你无法在网格层面禁止 Order 服务访问除了支付平台以外的其他网站(比如 `github.com` 或 `baidu.com`)。

3. 不安全(明文):
◦ Order 服务和外部支付平台之间通常是 HTTPS(单向认证)。如果你要求双向认证(mTLS),Sidecar 无法直接处理这种复杂的外部 TLS 握手。


---

### 二、引入 EgressGateway(解决方案)

我们创建一个 EgressGateway,强制所有外部流量必须先经过这个“出口安检口”。

#### 1. 架构图(核心逻辑)

```
Order Service Pod

Sidecar (istio-proxy)

EgressGateway (专用出口 Pod)

pay.alipay.com (外部世界)
```

关键变化:
• Order 服务不再知道外网长什么样,它只知道“把请求发给 EgressGateway”。

• EgressGateway 负责处理所有复杂的外部通信(TLS 证书、SNI 路由等)。


---

### 三、实战配置(一步步来)

#### 1. 部署 EgressGateway(特殊 Deployment)
EgressGateway 也是一个 Istio 组件,但它是专门为“出站”设计的。

```yaml
# egress-gateway.yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: egress-gateway
namespace: istio-system
spec:
selector:
istio: egressgateway # 使用 Istio 自带的 egressgateway 组件
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- pay.alipay.com # 只允许访问这个外部域名
tls:
mode: PASSTHROUGH # 透传 TLS,让 EgressGateway 只负责路由,不解密内容(安全合规)
```

#### 2. 配置路由规则(VirtualService for Egress)
告诉 Sidecar:“凡是访问 `pay.alipay.com` 的流量,统统送去 EgressGateway。”

```yaml
# egress-vs.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: direct-egress-through-egressgateway
spec:
hosts:
- pay.alipay.com # 匹配外部域名
gateways:
- mesh # 应用于网格内的所有 Sidecar
- egress-gateway # 也应用于 EgressGateway 本身
http:
- match:
- gateways:
- mesh # 来自网格内部的请求
port: 443
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
port:
number: 443
- match:
- gateways:
- egress-gateway # 来自 EgressGateway 的请求
port: 443
route:
- destination:
host: pay.alipay.com
port:
number: 443
```

#### 3. 开启双向认证(mTLS,Egress 的杀手锏)
这是 EgressGateway 最厉害的地方。你可以强制 Order 服务与 EgressGateway 之间使用 mTLS 通信,确保内部流量不被窃听。

```yaml
# egress-dr.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: egressgateway-mtls
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL # 开启 Istio 自动的双向认证
```

---

### 四、验证效果(你看到了什么)

1. 流量管控:
现在 Order 服务想访问 `github.com` 会失败,因为 EgressGateway 的 `hosts` 列表里只允许 `pay.alipay.com`。

2. 安全审计:
你可以在 Kiali 或 Prometheus 里清晰地看到:
◦ Source: Order Service

◦ Destination: EgressGateway

◦ External: pay.alipay.com

◦ 这形成了一条完整的审计链条。


3. TLS 分层:
◦ 内部(Pod ↔ EgressGateway):使用 Istio 的 mTLS(加密且认证)。

◦ 外部(EgressGateway ↔ 支付平台):使用标准的 HTTPS(互联网标准)。


---

### 总结:EgressGateway 到底干了什么?

| 维度 | 没有 EgressGateway | 有 EgressGateway |
| :--- | :--- | :--- |
| 可见性 | 黑洞,不知道服务去了哪 | 白盒,所有出口流量经过安检 |
| 安全性 | 明文或单向 HTTPS | 双向 mTLS(内部)+ 外部 HTTPS |
| 合规性 | 无法阻止数据泄露 | 可以强制禁止访问违规网站 |
| 运维 | 每个 Pod 自己管证书 | 集中管理出口证书和策略 |

一句话记住:
IngressGateway 是“看门的”(防坏人进来),EgressGateway 是“门禁卡”(管好人出去能干嘛)。

 

image

 

这张图讲解的是 ServiceEntry(服务入口),它是 Istio 网格化管理的“破壁机”。

结合你之前学习的 EgressGateway,我帮你把这页 PPT 的核心逻辑拆解一下,你就明白它在实际架构里是干什么用的了。

### 1. 核心痛点:微服务“出不去”
在微服务架构中,你的服务(比如订单服务)经常需要调用外部服务(比如支付接口、短信接口)。
• 默认情况:这些外部服务在网格之外(External Services)。

• 问题:Istio 的 Envoy Sidecar 代理只认识网格内的服务。如果不做特殊处理,内部服务发出的请求会绕过 Sidecar,导致 Istio 无法记录日志、无法做流量治理(比如给外部调用加超时重试)。


### 2. 什么是 ServiceEntry?(PPT 核心翻译)
PPT 上的那句定义非常精准:“将网格外部服务添加到网格内,像网格内其他服务一样管理。”

这句话怎么理解?
• “添加到网格内”:并不是真的把外部服务搬进 Kubernetes 集群,而是在 Istio 的控制平面里注册一个“户口”。

• “像网格内一样管理”:一旦注册了这个户口,内部服务去调用它时,必须经过 Sidecar 代理。这样一来,Istio 就能看到流量走向,并能对它应用各种策略。


### 3. 结合 PPT 架构图的实战解读
看图里的那条连线:内部服务 -> ServiceEntry -> 外部服务

这个流程在实际中发生了什么?

1. 注册(黄色方块):你写一段 YAML,告诉 Istio:“虽然 `api.payment.com` 不在我的 K8s 集群里,但我承认它是我的体系一部分,请给我放行并监管。”
2. 引流(蓝色箭头):内部服务想访问 `api.payment.com`。
3. 被劫持(必经之路):因为有了 ServiceEntry,K8s 的 iptables 规则会把流量劫持到本地的 Envoy Sidecar。
4. 出网(绿色箭头):Sidecar 拿到流量后,根据规则把数据包发出去。

### 4. 为什么配合 VirtualService?(PPT 下方虚线框)
PPT 右下角画了一个 VirtualService。
• 作用:有了 ServiceEntry 只是“让流量走 Sidecar”,但如果你想对这段外部流量做更多精细化的控制(比如:把 10% 的流量切到沙箱环境测试、设置全局 3 秒超时、或者做故障注入),就需要用 VirtualService 来定义这些路由规则。


### 5. 总结与类比
为了让你更容易记住,我们用一个生活场景打比方:

• 没有 ServiceEntry:就像你公司(网格)的员工要去外面买咖啡(外部服务)。保安(Sidecar)不认识咖啡店,直接把人拦在门外,员工只能FQ出去(流量绕过网格,无法监控)。

• 有了 ServiceEntry:保安手里拿到了一份“合作商户名单”(ServiceEntry)。员工说去名单上的店,保安就放行并记录时间。如果名单上某家店总是出错,保安还能根据规定(VirtualService)限制员工去那家店的频率。


一句话总结:
ServiceEntry 就是给 Istio 的一张“白名单”,让外部服务也能享受 Istio 的“微服务治理待遇”。

这里以“允许网格内的服务访问外部的天气查询 API”为例,演示如何编写 YAML 配置文件,并列举几个最常见的实战场景。

### 一、 如何设置 ServiceEntry?(实战 YAML)

假设我们要调用外部的一个公共天气 API:`api.weather.com`,端口是 443(HTTPS)。

#### 1. 基础版:仅仅让流量“出得去”
这是最简单的配置,作用是告诉 Istio:“虽然这个服务在网格外,但允许内部服务访问它,并且请帮我拦截流量。”

```yaml
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: external-weather-api # 名字随便起
spec:
hosts:
- api.weather.com # 外部服务的域名
ports:
- number: 443 # 端口
name: https
protocol: HTTPS # 协议
resolution: DNS # 解析方式:通过 DNS 查 IP
```
• `resolution: DNS`:这是最关键的字段。它表示每次发起请求时,Envoy 代理会通过 DNS 查询获取最新的 IP,而不是在启动时死板地绑定一个 IP。


#### 2. 进阶版:像管理内部服务一样治理它
结合之前的 PPT 内容,我们加上 `VirtualService`,给这个外部调用加一个 3 秒超时。

```yaml
# 1. 定义 ServiceEntry (把外部服务纳入网格)
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: weather-api-se
spec:
hosts:
- api.weather.com
ports:
- number: 443
name: https
protocol: HTTPS
resolution: DNS

---
# 2. 定义 VirtualService (施加治理规则)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: weather-api-vs
spec:
hosts:
- api.weather.com # 目标必须是上面的 ServiceEntry
http:
- timeout: 3s # 核心诉求:3秒超时
retries:
attempts: 3 # 核心诉求:失败重试3次
route:
- destination:
host: api.weather.com
```
这样配置后,网格内的服务调用天气 API 时,如果 3 秒没响应,Istio 会自动触发重试或报错,就像调用内部服务一样可控。

---

### 二、 ServiceEntry 的典型使用场景

除了上面提到的“调用外部 API”,ServiceEntry 还有以下几个高频场景:

#### 场景 1:遗留系统迁移(混合云/混合集群)
• 痛点:你有一个老的单体应用(Monolith)跑在物理机上,没有容器化。现在的微服务都在 K8s 网格里。它们需要互相调用。

• 解法:给物理机上的老应用配一个 `ServiceEntry`。

• 效果:网格内的新服务以为老应用也是个普通的 K8s Service,直接通过服务名调用,无需关心底层网络差异。


#### 场景 2:访问云服务(云厂商 SDK)
• 痛点:你的服务需要调用阿里云 OSS、AWS S3 或腾讯云的短信服务。这些 SDK 内部通常会并发建立很多连接。

• 风险:如果不加 ServiceEntry,SDK 可能会绕过 Sidecar,导致监控缺失;或者因为 Sidecar 默认的连接池限制导致报错。

• 解法:添加 ServiceEntry 并配置 `ConnectionPool`(连接池)。

```yaml
spec:
hosts:
- oss-cn-hangzhou.aliyuncs.com
ports:
- number: 443
protocol: HTTPS
resolution: DNS
location: MESH_EXTERNAL
connectionPool:
tcp:
maxConnections: 100 # 限制最大连接数,防止打爆本地资源
```

#### 场景 3:灰度发布中的“金丝雀”外部依赖
• 痛点:你想测试一个新的外部支付接口(v2 版),但不想影响主业务。

• 解法:利用 `VirtualService` 配合 `ServiceEntry` 做流量切分。

```yaml
# 假设 ServiceEntry 里定义了 weather-api.com
http:
- match:
- headers:
x-canary:
exact: "true" # 只有带特定头的请求才走新版
route:
- destination:
host: api-v2.weather.com # 指向新的外部端点
```
*(注:这需要外部接口支持多域名或根据 Host 头路由)*

### 总结
ServiceEntry 就是 Istio 网格的“边界门卫”。 无论是出去上网(API),还是接待访客(遗留系统),都需要它在门口登记造册,这样网格里的“监控摄像头”(Telemetry)才能拍到所有的进出流量。

image

 

看来你已经学到了 Istio 最核心的实战技能——服务发布与部署策略。这正是微服务架构中运维和 DevOps 每天打交道最多的场景。

PPT 里首先提到的是 蓝绿发布(Blue-Green Deployment),我来帮你把这个概念拆解清楚,并结合你之前配置的 Nginx 环境,告诉你用 Istio 怎么做。

一、 什么是蓝绿发布?(生活化类比)

想象一下,你开了一家奶茶店,现在要装修升级店面(发布新版本软件):

  • 蓝店(Blue):现在的老店面,正在正常营业,接待老客户(线上运行的老版本服务)。

  • 绿店(Green):隔壁刚装修好的新店面,准备就绪,万事俱备,只等开业(部署好的新版本服务)。

  • 马路(Gateway/LoadBalancer):顾客进店的必经之路。

发布过程(切换流量):

  1. 准备期:你在隔壁开好了绿店,测试没问题。此时蓝店还在营业。

  2. 切换:中午 12 点,你把马路的指示牌从“蓝店”改成“绿店”。顾客瞬间全部流向了新店。

  3. 观察:如果没有客人投诉(系统报错),那就保持现状;如果新店出事了(Bug),你立刻把指示牌改回蓝店,顾客又瞬间回到老店。

这就是蓝绿发布:两套环境随时待命,通过网关一键切换流量。

二、 Istio 如何实现蓝绿发布?(实战配置思路)

结合你之前做的实验,实现蓝绿发布不需要改你的 Nginx 代码,只需要改 Istio 的配置。

假设你有两个 Nginx 部署:nginx-v1(蓝)和 nginx-v2(绿)。

1. 定义两个版本的服务(Deployment)

这里假设你已经有了 nginx-v1nginx-v2的 Deployment。

2. 配置 Service(关键点:Selector 要“广撒网”)

你的 Kubernetes Service 不能只指向 v1,要让它能选中所有的 Pod。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx   # 只要标签是 nginx 的 pod 都算数,不管它是 v1 还是 v2
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

3. 配置 Gateway(大门还是那个大门)

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

4. 核心:配置 VirtualService(红绿灯控制器)

这是 PPT 里说的“负载均衡摘除/加入”的魔法发生地。

阶段一:全部流量去蓝店(v1)

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: nginx-blue-green
spec:
  hosts:
  - "*"
  gateways:
  - my-gateway
  http:
  - route:
    - destination:
        host: nginx-service
        subset: v1  # 100% 流量去 v1

阶段二:一键切到绿店(v2)

只需要修改上面 YAML 中的 subsetv2,或者使用权重(Weight):

http:
  - route:
    - destination:
        host: nginx-service
        subset: v2
      weight: 100   # 100% 流量切到 v2

注:配合 DestinationRule 定义 v1v2这两个子集(Subset)会更规范。

三、 蓝绿发布的优缺点(PPT 核心提炼)

PPT 里总结得很到位,我帮你补充一下在实际工作中的体感:

优点(为什么用它):

  1. 极速回滚:这是最大的爽点。一旦新版本(绿店)出问题,你不需要做任何回滚操作(Rollback Code),只需要把网关流量切回老版本(蓝店)即可。回滚速度是秒级的。

  2. 发布过程简单:逻辑清晰,运维只需要操作一次网关配置。

缺点(代价是什么):

  1. 资源消耗大(双倍资源):你必须同时启动两套一模一样的环境(蓝+绿)。如果你的微服务内存占用很大(比如 8G),那发布期间集群里相当于有 16G 的资源在被这两套环境占用。

  2. 状态管理复杂:如果你的应用里有“购物车”这种需要保存用户状态的,切到绿店后,用户的购物车数据如果还在蓝店的内存里,用户就会觉得“我刚才加的商品怎么没了”。所以蓝绿发布最适合无状态服务(Stateless)。

总结

蓝绿发布就像是“双管齐下,瞬间切换”

在你的 Istio 学习路径中,掌握 VirtualService 的路由权重配置,你就已经掌握了蓝绿发布的精髓。接下来 PPT 里应该会讲到 灰度发布(金丝雀),那是更省资源、更细腻的玩法。

image

 

这张图继续讲解了另外两种主流的服务发布策略:滚动发布灰度发布(金丝雀发布)

结合你之前学习的 Istio 知识和这张 PPT 的内容,我来帮你把这几种发布策略的区别和应用场景彻底理清楚。

一、 滚动发布 (Rolling Update)

PPT 里对滚动发布的定义是:“先创建出一个新的服务,新的服务正常后,加入生产环境后,再删除旧的服务。”

1. 通俗理解:排队换岗

想象一家餐厅有 10 个服务员(旧版本 Pods)。现在要换新制服(新版本)。

  • 滚动发布的做法是:解雇 1 个旧服务员,招 1 个穿新制服的服务员进来。等新人就绪了,再解雇下一个旧的。

  • 过程:新旧版本会在一段时间内同时存在,随着时间推移,新版本的比例逐渐增加,直到所有旧版本都被替换。

2. 技术特点(PPT 核心提炼)

  • 机制:这是一种渐进式的发布。Kubernetes 默认的策略就是滚动发布。

  • 优点

    • 平滑过渡:用户基本无感知,体验好。

    • 资源利用率高:不需要像蓝绿发布那样同时准备双倍资源,边滚动边释放旧资源。

  • 缺点(重点笔记)

    • 不易回滚:PPT 里提到“一口气升级完所有”,如果新版本在滚动过程中发现严重 Bug,你无法通过简单的“一键切换”回滚。因为此时部分旧实例已经被销毁了,系统状态已经改变。通常需要重新触发一次发布流程回退到旧镜像。

    • 影响范围不可控:在滚动过程中,系统里既有旧版本也有新版本,如果新版本有问题,实际上已经影响了部分用户的请求。


二、 灰度发布 / 金丝雀发布 (Canary Release)

PPT 定义:“只升级部分服务,即让一部分用户继续使用老版本,一部分用户开始使用新版本...逐步扩大范围。”

1. 通俗理解:试探水温

“金丝雀”是煤矿工人以前用来检测有毒气体的小鸟。意思是先把新版本给一小撮“小白鼠”用户试用

2. 技术特点(PPT 核心提炼)

  • 机制:这是一种非均匀的流量分配。通过 Istio 的 VirtualService来精确控制流量比例。

  • 优点

    • 安全性极高:这是它最大的优势。一旦发现新版本有问题,只需将流量权重切回 0% 即可,回滚极其迅速且无损

    • 可观测性强:可以在真实流量下,对比新老版本的监控数据(如响应时间、错误率)。

  • 缺点

    • 自动化要求高:需要配合复杂的监控告警系统和自动化脚本。如果人工盯着看,成本太高。

    • 实现复杂:需要 Istio 这种强大的流量治理能力,单纯靠 K8s 原生比较难做到细粒度的金丝雀控制。


三、 三种发布策略对比总结

为了让你在面试或工作中能脱口而出,我整理了一个对比表:

 

发布策略

资源占用

回滚速度

风险程度

适用场景

蓝绿发布

​ (双倍资源)

极快​ (秒级切换)

中 (发布时需谨慎)

对稳定性要求高,可接受资源浪费的场景

滚动发布

低 (渐进替换)

​ (需重新部署)

中高 (新旧共存期有风险)

大多数常规微服务,追求资源利用率

灰度发布

低 (按比例扩容)

极快​ (调权重)

​ (风险隔离在小范围)

大型分布式系统,核心业务,需要数据验证

四、 在 Istio 中怎么做?(实战落地)

既然你已经学了 Istio,这两种发布在 Istio 里其实就是改几行 YAML:

1. 滚动发布(K8s 原生)

你不需要写 Istio 配置,直接修改 K8s 的 Deployment 镜像版本,K8s 会自动帮你滚动更新。

kubectl set image deployment/my-app my-app=my-app:v2

2. 灰度发布(Istio 核心玩法)

通过修改 VirtualServiceweight(权重)来实现。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-app-vs
spec:
  hosts:
  - my-app-service
  http:
  - route:
    - destination:
        host: my-app-service
        subset: v1
      weight: 90   # 90% 流量去老版本
    - destination:
        host: my-app-service
        subset: v2
      weight: 10   # 10% 流量去新版本(金丝雀)

随着观察结果良好,你可以逐步修改权重:v1:80, v2:20-> v1:50, v2:50-> v1:0, v2:100

总结建议

  • 如果你是初学者,先搞懂 蓝绿发布(逻辑最简单)。

  • 如果你要上线核心业务,灰度发布是最佳实践(结合 Istio 流量治理)。

  • 滚动发布是 K8s 的默认动作,虽然简单,但在遇到线上事故时,回滚能力是最弱的。

image

 

这张图详细讲解了灰度发布(Gray Release),也就是大名鼎鼎的金丝雀发布(Canary Release)。这是目前大型互联网公司(如淘宝、抖音)最核心的发布策略。

### 1. 核心概念:为什么叫“金丝雀”?
“金丝雀”是以前煤矿工人用来检测毒气的。因为金丝雀对毒气极其敏感,一旦有危险就会先倒下报警。

在技术中,它的逻辑是一样的:先把新版本(V2)推送给一小部分“小白鼠”用户(如图中的 20%)。 如果发现新版本有问题,立刻切回老版本,大部分用户(80%)完全无感知;如果没有问题,再逐步扩大新版本的范围。

### 2. 图解 PPT 示意图
PPT 下方的图非常直观地展示了这个过程,分为两个阶段:

• 第一阶段(分流期):

◦ LB(负载均衡/网关) 接收到所有用户请求。

◦ 80% 的流量(绿色箭头):继续流向 V1(老版本),保证绝大多数用户的稳定。

◦ 20% 的流量(紫色箭头):被分流到 V2(新版本),这 20% 的用户正在帮我们“试毒”。

• 第二阶段(全量期):

◦ 底部的进度条展示了时间轴的推进。

◦ 随着时间的推移,运维人员观察到 V2 运行稳定,没有报错。

◦ 于是逐步调整流量比例:20% -> 80% -> 100%。

◦ 最终,所有用户都迁移到了 V2,老版本 V1 退役。


### 3. 优缺点分析(对应 PPT 文字)
• 最大优点:稳如泰山。相比于“滚动发布”一旦升级完就无法瞬间回滚,灰度发布在发现问题时的回滚速度极快(只需在网关把权重改回 0 即可),极大保证了系统稳定性。

• 缺点:自动化要求高。这需要非常精细的流量控制(如 Istio 的权重配置)和完善的监控体系。如果靠人工去改配置文件,稍微慢一点,小范围的故障可能就会扩散。


### 4. 结合你之前学习的 Istio
你之前学习了 VirtualService 和 ServiceEntry。在这个灰度发布的过程中,VirtualService 就是那个“调度员”。

通过修改 VirtualService 中关于 `destination` 的 `weight`(权重)字段,你就可以完美复现 PPT 里这张图的逻辑,轻松实现不停止服务的情况下完成版本迭代。

image

 

这张图讲解的是 A/B Test 发布(A/B 测试发布)。这是灰度发布的一种更高级、更具目的性的形式。

如果说“灰度发布”是为了“保稳定”(先让小部分人用,没问题再全量),那么“A/B Test 发布”就是为了“做决策”(对比两种方案,看哪个更好)。

1. 核心概念:什么是 A/B Test 发布?

PPT 上的定义非常精准:“A/B Test 发布也是灰度发布的一种形式...用来测试应用功能表现的方法”。

  • 核心区别:普通的灰度发布,新老版本的功能通常是一样的,只是代码升级了。而 A/B Test 发布,新老版本的功能往往是不同的(比如界面改版了、推荐算法换了、按钮颜色变了)。

  • 目的:不是为了验证“新版本会不会崩”,而是为了验证“新版本是不是比老版本更受用户欢迎/更能赚钱”。

2. 结合 PPT 架构图的实战解读

看图里的那条连线和下方的时间轴,逻辑非常清晰:

  • 分流(Load Balance)

    网关(LB)在这里起到了分流的作用。它把用户分成了两组:

    • 绿色箭头组(80%):继续访问 V1(老版本)。这是我们的“对照组”。

    • 紫色箭头组(20%):被引导访问 V2(新版本)。这是我们的“实验组”。

  • 数据反馈(PPT 文字提到)

    这就是 A/B Test 的灵魂。系统会分别收集这两组用户的行为数据:

    • V1 组:点击率 5%,转化率 2%。

    • V2 组:点击率 8%,转化率 4%。

    • 决策:如果 V2 的数据明显更好,就逐步加大紫色箭头的流量,直到 100%;如果 V2 数据很差,就直接关掉,所有人都回到 V1。

3. A/B Test 与普通灰度发布的对比

虽然图上看都是“分流量”,但它们的侧重点不同:

 

维度

普通灰度发布(金丝雀)

A/B Test 发布

关注点

稳定性(Bug 多不多?会不会崩溃?)

效果(哪个版本转化率更高?用户体验更好?)

版本差异

通常是同一功能的迭代(如 V1.0 -> V1.1)

通常是不同体验的设计(如 红色按钮 vs 蓝色按钮)

决策依据

监控系统报警(Error Rate)

业务数据分析(Click-through Rate, Conversion Rate)

4. 在 Istio 中如何实现?(技术落地)

要实现图中这种按用户比例分流的效果,在 Istio 中主要依靠 VirtualServiceweight字段,配合 DestinationRule

核心配置思路:

  1. 定义两个 Subset(v1 和 v2)。

  2. 在 VirtualService 中设置权重

    http:
    - route:
      - destination:
          host: my-service
          subset: v1
        weight: 80  # 80% 的人去老版本
      - destination:
          host: my-service
          subset: v2
        weight: 20  # 20% 的人去新版本

5. 总结

  • 蓝绿发布:像是“换车”,要么开旧车,要么开新车,二选一。

  • 滚动发布:像是“修车”,一边开一边换零件,最后变成新车。

  • 灰度发布(金丝雀):像是“试驾”,先开一小段路看看刹车灵不灵。

  • A/B Test 发布:像是“选车”,同时开两辆车,看哪辆跑得更快、更省油,然后决定以后开哪辆。

A/B Test 是目前互联网大厂(如 Netflix、Amazon、淘宝)用来驱动产品优化的核心技术手段。

image

 

这张图展示的是 Istio 官方提供的经典微服务演示项目 Bookinfo。这个应用是学习和测试 Istio 流量治理(特别是灰度发布/金丝雀发布)的“Hello World”级实战项目。

1. 核心内容解析:Bookinfo 是什么?

这是一个模拟“图书详情页”的 Web 应用,由 4 个微服务组成,麻雀虽小,五脏俱全,非常适合用来模拟复杂的微服务调用链。

  • productpage:前端页面服务。它会调用 reviewsdetails

  • reviews:评论服务(这是核心服务,有多个版本)。它会调用 ratings

  • details:图书详细信息服务。

  • ratings:评分服务。

2. 实战场景:为什么要拿它做灰度发布?

注意看图中 reviews服务的图标,它有 v1、v2、v3​ 三个不同颜色的版本。这正是为了模拟灰度发布中最常见的场景——多版本并存

在实际业务中,reviews(评论)服务经常需要更新UI或逻辑,比如:

  • v1:黑色星星(老版本)。

  • v2:红色星星,但调用了外部的 ratings服务(可能比较慢)。

  • v3:红色星星,且不调用外部服务(优化版)。

你的实战目标就是:让 90% 的用户看到 v1(黑色星星),10% 的用户看到 v2(红色星星),观察没问题后,再把流量切到 v3。

3. 实战步骤解析

图中底部高亮的部分是第一步:

kubectl create ns bookinfo

这是在 Kubernetes 中创建一个名为 bookinfo的命名空间。

完整的实战流程通常是这样的:

  1. 打标签(注入 Sidecar)

    在这个命名空间开启 Istio 自动注入,让每个微服务 Pod 里都住进一个 Envoy 代理(Sidecar)。

    kubectl label namespace bookinfo istio-injection=enabled
  2. 部署应用

    使用 Istio 自带的命令一键部署这个微服务。

    kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
  3. 配置网关(Gateway & VirtualService)

    这是最关键的一步。你需要写一个 VirtualService配置,告诉 Istio 如何分配流量。

    # 伪代码示例
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: reviews
    spec:
      hosts:
      - reviews
      http:
      - route:
        - destination:
            host: reviews
            subset: v1
          weight: 90  # 90% 流量去 v1
        - destination:
            host: reviews
            subset: v2
          weight: 10  # 10% 流量去 v2

4. 总结

接下来的操作就是去修改那个 VirtualService文件,调整 weight(权重)数值,然后观察网页上星星的颜色变化,以此来完成整个金丝雀发布的实战演练。

这是 Istio 灰度发布(金丝雀发布)最核心的配置。它告诉 Envoy 代理:“所有访问 reviews服务的流量,90% 去老版本,10% 去新版本。”

下面我逐行给你拆解,并补充一个必须配套的配置(否则这个 VS 会报错)。


一、逐行详细解释

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
  • 含义:这是 Istio 的 VirtualService(虚拟服务)​ 资源。

  • 作用:定义路由规则。它不负责提供服务,只负责告诉流量“该往哪走”。

metadata:
  name: reviews
  • 含义:这个资源的名字叫 reviews

  • 注意:名字不重要,重要的是下面 spec.hosts里指定的目标服务。

spec:
  hosts:
  - reviews
  • 这是最关键的一行

  • 含义:这条规则作用于谁?作用于 reviews这个服务。

  • 对应:Kubernetes 里的 Service reviews.default.svc.cluster.local

  • 效果:只要集群内有服务访问 http://reviews,就会被这条规则拦截。

http:
  - route:
  • 含义:针对 HTTP 流量(不是 TCP)进行路由。

  • -代表这是第一组路由规则(可以写多组 match 规则,但这里没写 match,所以是默认规则)。

- destination:
        host: reviews
        subset: v1
      weight: 90
  • destination.host:目标服务还是 reviews

  • subset: v1:这是子集。它不是一个 Pod,而是一个“标签分组”。

    • 在 K8s 里,你的 reviews-v1Deployment 必须有这个 label:

      metadata:
        labels:
          app: reviews
          version: v1
  • weight: 90:权重。90% 的流量会被转发到带有 version=v1标签的 Pod。

- destination:
        host: reviews
        subset: v2
      weight: 10
  • subset: v2:对应 version=v2的 Pod。

  • weight: 1010% 的流量会被转发到 v2。


二、⚠️ 缺少的关键配置:DestinationRule

你贴出的这段 “伪代码” 是不能直接用的,因为它引用了 subset: v1subset: v2,但 Istio 并不知道 v1/v2 对应哪些 Pod

必须配套一个 DestinationRule

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews   # 对应 VirtualService 里的 host
  subsets:
  - name: v1      # 对应 VirtualService 里的 subset: v1
    labels:
      version: v1 # 筛选 Pod 的 label
  - name: v2      # 对应 VirtualService 里的 subset: v2
    labels:
      version: v2

没有这个 DR,Istio 会报错:

subset v1 not found in destination rule reviews...


三、完整可运行的 Bookinfo 灰度配置

1️⃣ DestinationRule(定义版本分组)

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

2️⃣ VirtualService(定义 90/10 流量切分)

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 90
    - destination:
        host: reviews
        subset: v2
      weight: 10

四、流量走向示意图

用户请求
  ↓
istio-ingressgateway (如果外部访问)
  ↓
productpage 服务
  ↓
reviews 服务 (被 VirtualService 拦截)
  ↓
┌──────────────────────────────────┐
│ 90% 流量 → reviews-v1 Pod (黑色星星) │
│ 10% 流量 → reviews-v2 Pod (红色星星) │
└──────────────────────────────────┘

五、如何验证效果?

1. 刷新 Bookinfo 页面

访问 http://<INGRESS_IP>/productpage

反复刷新,你会看到:

  • 大部分时候是 黑色星星(v1)

  • 偶尔出现 红色星星(v2)

2. 用 Prometheus 验证比例

sum(rate(istio_requests_total{destination_service="reviews",destination_subset="v1"}[1m]))
/
sum(rate(istio_requests_total{destination_service="reviews"}[1m]))

结果应接近 0.9


六、进阶:按用户 ID 切流(更真实)

如果你不想按百分比,而是想让 特定用户​ 看到 v2:

http:
- match:
  - headers:
      end-user:
        exact: jason   # 只有 jason 这个用户
  route:
  - destination:
      host: reviews
      subset: v2
- route:
  - destination:
      host: reviews
      subset: v1

总结一句话

VirtualService 决定“流量怎么分”,DestinationRule 决定“分去哪里”。

没有 DestinationRule,VirtualService 的 subset就是空中楼阁。

如果你愿意,我可以直接帮你把 Bookinfo 的 Gateway + VirtualService + DestinationRule​ 整理成一份 “复制粘贴即可运行”​ 的完整 YAML。

image

 

这张截图展示了使用 Istio 自带项目进行灰度发布实战的第二步:部署底层微服务应用

简单来说,这一步是在 Kubernetes 集群里把 Bookinfo 这个“图书评论”系统跑起来,并为后续的“流量切分”准备好环境。

以下是逐行命令的详细解读:

1. 环境准备阶段

  • kubectl create ns bookinfo

    • 作用:创建一个名为 bookinfo的独立命名空间。

    • 目的:将这次实战的所有资源(Pods, Services)隔离开,防止干扰集群里的其他应用。

  • kubectl label namespace bookinfo istio-injection=enabled

    • 作用:给刚才创建的命名空间打上一个特殊标签。

    • 核心意义:这是接入 Istio 的关键开关。打了这个标签后,后面创建的 Pod 会自动“挂载”一个 Envoy 代理容器(即 Sidecar),让 Istio 能够接管和控制这些服务的网络流量。

2. 部署应用阶段

  • cd .../bookinfo/& kubectl apply -f platform/kube/bookinfo.yaml

    • 作用:进入 Istio 安装包的示例目录,并应用里面的 bookinfo.yaml配置文件。

    • 结果:Kubernetes 会根据 YAML 文件,拉起 Bookinfo 的四个微服务(Productpage, Reviews, Details, Ratings)。

3. 状态验证阶段

  • kubectl get pod -n bookinfo -o wide

    • 作用:查看该命名空间下所有 Pod 的运行状态和 IP。

    • ⚠️ 核心考点(注意图中的细节)

      你会发现 reviews服务对应的 Pod 有三个:reviews-v1-...reviews-v2-...reviews-v3-...

      • 这就是灰度发布的物理基础:同一个服务(reviews),同时存在三个不同版本的实例在运行。

      • 虽然它们都在运行,但如果没有下面的 Istio 配置,Kubernetes 的默认轮询机制会让用户随机看到 v1、v2 或 v3。接下来的实战就是通过 Istio 的 VirtualService来打破这种随机,精准控制谁看 v1,谁看 v2。

4. 下一步行动预测

截图底部的最后一行命令是 kubectl get svc ...,这是为了查看服务的 ClusterIP。看完 IP 之后,接下来的标准操作通常是:

  1. 配置 Gateway:让外部流量能进入集群访问这个服务。

  2. 配置 VirtualService:编写 YAML 文件,定义流量权重(例如 90% 去 v1,10% 去 v2),这才是真正的“灰度发布”开始。

image

 

这张截图展示了 Istio 控制平面(Control Plane)​ 和 入口网关(Ingress Gateway)​ 的健康状态,这是执行灰度发布前的“基础设施检查”。

结合之前的步骤,这意味着你已经完成了最关键的初始化,以下是详细解读:

1. 核心组件状态:一切正常

截图中的 kubectl get pod,svc -n istio-system命令验证了 Istio 的核心服务已经全部启动并处于 Running状态。这是进行流量治理的前提。

  • pod/istiod-xxxx(Pilot)

    • 作用:这是 Istio 的“大脑”。它负责下发配置给各个服务的 Sidecar 代理(Envoy),告诉它们流量该怎么转发。

    • 状态1/1 Running。说明大脑正常运作。

  • pod/istio-ingressgateway-xxxx(Gateway)

    • 作用:这是 Istio 的“大门”或“海关”。外部用户(比如你的浏览器)必须通过这个网关才能访问到集群内部的 Bookinfo 应用。

    • 状态1/1 Running。说明大门已经敞开。

2. 关键细节:为什么会一直转圈?

注意看 istio-ingressgatewayEXTERNAL-IP显示为 <pending>

  • 原因:你的 K8s 集群是裸机或虚拟机环境(非云厂商如阿里云/腾讯云),没有内置的负载均衡器(LoadBalancer)服务。

  • 影响完全不影响使用。这只是意味着它没有分配一个公网 IP,但你依然可以通过服务器的内网 IP 或 NodePort 来访问服务。

  • 解决:通常这种教程环境会使用 NodePort方式访问,或者手动修改 Service 类型为 ClusterIP并通过端口映射访问。

3. 下一步行动预测

既然“大脑”和“大门”都准备好了,“房子”(Bookinfo 应用)也建好了,接下来的核心动作必然是 “装修门禁”​ —— 即配置路由规则。

预计接下来的步骤是:

  1. 创建 Gateway 资源:定义一个监听 HTTP 请求的网关入口。

  2. 创建 VirtualService 资源:编写那个决定流量走向(90%去v1,10%去v2)的 YAML 文件。

只要 kubectl apply -f xxx.yaml应用了这些配置,整个灰度发布的链路就彻底打通了。

image

 

image

 

这张截图展示的是 Istio 中用于暴露服务入口Gateway(网关)​ 配置文件。

简单来说,这份配置的作用是:在 Istio 的边界上开一个大门(端口),告诉外界如何把流量“接进来”。

结合之前的步骤(Bookinfo 应用已部署,Istio 控制平面已就绪),这是实现灰度发布的必经之路——没有这个网关,外部用户就无法访问到集群内的服务。

以下是对图中关键配置的详细解读:

1. 核心作用:为什么需要这个 Gateway?

在 Kubernetes 中,通常我们用 Ingress 来暴露服务。但在 Istio 体系中,Gateway 是专门负责“四层(TCP/UDP)”流量接入的资源对象

  • 它就像一个大门,独立于具体的业务逻辑。

  • 它告诉 istio-ingressgateway(运行在集群里的那个代理 Pod):“如果有人访问你的 80 端口,请把流量交给内部服务处理。”

2. 逐行代码深度解析

A. 绑定执行者 (Selector)

selector:
  istio: ingressgateway
  • 含义:这行配置非常关键,它指定了“由谁来干活”。

  • 原理:在 Istio 系统中,有一个名为 istio-ingressgateway的 Pod(之前截图中已验证它在运行)。这个 selector就像一个标签选择器,Istio 会找到带有 istio: ingressgateway标签的那个 Pod,把这里的端口监听规则下发给它。

B. 开启大门 (Servers)

servers:
- port:
    number: 80        # 监听的端口
    name: http        # 端口名称(自定义,建议有意义)
    protocol: HTTP    # 协议类型
  • 含义:定义了大门的具体属性。

  • 实战意义:这里配置为 80端口。意味着当外部流量打到 Istio 网关的 80 端口时,Istio 会接管它。

  • 注意:如果你的服务器是虚拟机且没有 LoadBalancer IP(之前截图中 External-IP 是 pending),你通常需要通过这个服务器的 IP + NodePort 或者直接访问 Pod IP 来测试这个 80 端口。

C. 允许访客 (Hosts)

hosts:
- "*"
  • 含义"*"代表通配符,意思是“允许所有的域名/IP 访问”。

  • 解释:这里不进行域名限制。只要流量是从 80 端口进来的,不管 Host 头是什么(不管是直接输 IP 还是访问某个域名),都放行进入内部。在实际生产环境中,这里通常会填入具体的域名,如 bookinfo.example.com,以实现多租户或精确路由。

3. 整体流程图解

结合这张图和之前的步骤,现在的流量走向是这样的:

  1. 外部用户:在浏览器输入 http://<你的服务器IP>/productpage

  2. Istio Gateway:流量击中了 istio-ingressgatewayPod 的 80 端口(由本图配置决定)。

  3. VirtualService(下一步要做):Gateway 接收到流量后,会查找有没有配套的 VirtualService。如果有,就把流量转给具体的服务(比如 90% 给 v1,10% 给 v2)。

  4. 微服务:最终流量到达 productpage服务。

4. 下一步做什么?

单有这个 Gateway还不能完成灰度发布,它只是开了门。接下来必须创建 VirtualService,才能真正实现“流量切分”。

通常下一步的命令是:

kubectl apply -f networking/bookinfo-gateway.yaml

应用这个配置后,Istio 就会把规则推送到 istio-ingressgateway,大门就正式开启了。

image

 

image

 

这张截图展示的是 VirtualService​ 配置,它是 Istio 实现流量治理的核心配置之一。

如果说之前的 Gateway是开了大门,那么这个 VirtualService就是门内的导航系统。它告诉 Istio:“进来的人要去哪里,走哪条路”。

结合你之前的操作(部署了 Bookinfo,配置了 Gateway),这份配置的具体含义如下:

1. 核心作用:路由规则

这个配置定义了一个规则:根据不同的 URL 路径,将流量转发到不同的内部服务。

虽然截图里只写了一个 route指向 productpage,但这通常是 Bookinfo 示例的标准配置,用于处理前端的页面请求。

2. 逐段代码解析

  • hosts: ["*"]

    • 这里填 *,表示匹配所有域名。注意,这个 hosts指的是下游客户端访问的主机名,而不是你要转发到的目标主机。它通常与 Gateway中的 hosts保持一致或兼容。

  • gateways: [- bookinfo-gateway]

    • 这行非常关键!它将这个路由规则与之前创建的 bookinfo-gateway绑定。这意味着,只有从 Gateway 大门进来的流量,才会走这套规则。如果没有这行,外部流量是进不来的。

  • http: [...]下的 match部分

    • 这是 Istio 强大的匹配规则。它列出了多种 URL 路径:

      • exact: /productpage: 精确匹配根页面。

      • prefix: /static: 前缀匹配静态资源(如 CSS/JS 图片)。

      • exact: /login, /logout: 匹配登录登出接口。

      • prefix: /api/v1/products: 匹配 API 接口。

    • 实战意义:这样配置后,用户访问网页、加载图片、点击登录,都能被正确识别并路由。

  • route: [...]部分

    • destination: { host: productpage, port: 9080 }

    • 这是最终的落脚点。不管上面的 match匹配了哪个路径,流量最终都会被转发给名为 productpage的服务,端口是 9080。

3. 为什么说它还没完?(关于灰度发布)

你现在的进度条卡在这里:

  1. Gateway:大门开了(80端口)。

  2. VirtualService:进门后,所有请求都指向了 productpage

但真正的灰度发布在哪里?

productpage服务内部,它会去调用 reviews服务。要实现“90%看黑星,10%看红星”,你需要在 productpagereviews的调用链上做文章,或者在针对 reviews服务的 VirtualService​ 上做文章。

通常接下来的步骤是:

  1. 确认 productpage的代码中调用 reviews的地址是 reviews:9080

  2. 编写一个针对 reviews服务的 VirtualService(类似下面这样):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews  # 目标服务是 reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 90
    - destination:
        host: reviews
        subset: v2
      weight: 10

4. 总结

你现在看到的这个 YAML 是基础路由配置,它保证了你能正常看到网页。

要完成“金丝雀发布”,下一步必须是针对具体微服务(如 reviews)​ 编写带有 weight(权重)的路由配置。

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

这两张截图展示了 Istio 灰度发布(金丝雀发布)的核心实战配置,特别是如何将流量全量切分到 reviews服务的 v1 版本(不带五角星的版本)

这里包含了两个关键的 VirtualService配置:productpagereviews

1. productpage的 VirtualService(基础路由)

  • 配置看点

    route:
    - destination:
        host: productpage
        subset: v1
  • 深度解析

    • 这个配置的作用是保底。它强制将所有访问 productpage(图书详情页)的流量都导向 productpage服务的 v1版本。

    • 为什么这么做?​ 在 Bookinfo 示例中,productpage是前端页面。为了确保整个实验环境纯净,通常先把前端锁定在稳定的 v1 版本,然后再去折腾后面的 reviews服务。

2. reviews的 VirtualService(灰度发布核心)

这是实现“灰度发布”的真正开关。

  • 配置看点

    hosts:
    - reviews
    http:
    - route:
      - destination:
          host: reviews
          subset: v1  # 关键点在这里
  • 深度解析

    • 目标锁定hosts: - reviews表示这条规则只对发往 reviews服务的流量生效。

    • 全量切流subset: v1配合单一的 route字段,意味着100% 的流量都会被转发到 reviews的 v1 版本

    • 结果:这时候用户刷新页面,看到的将是没有星星的评论区(因为 v1 版本不显示星星)。即使你把 v2(红五星)和 v3(黑五星)的 Pod 都运行着,用户也绝对刷不出来。

3. 背后的“隐形功臣”:DestinationRule

虽然截图中没有展示,但根据 subset: v1这一行代码,必须存在一个对应的 DestinationRule才能让这个配置生效。

它大概长这样:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1    # 这里定义的名字,就是上面 VirtualService 里引用的 subset
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

4. 总结与下一步

目前的配置状态是:全量流量 -> reviews v1 (无星)

如果要进行金丝雀发布(灰度测试),下一步的操作就是将截图中的 reviewsVirtualService 修改为类似下面这样(引入权重):

# 假设这是修改后的 reviews VirtualService
http:
- route:
  - destination:
      host: reviews
      subset: v1
    weight: 90   # 90% 流量去 v1 (无星)
  - destination:
      host: reviews
      subset: v2
    weight: 10   # 10% 流量去 v2 (红星)

这样就实现了“先让 10% 的用户看到新版本,没问题再全量”的灰度发布流程。

这张截图展示的是 DestinationRule(目标规则)​ 配置文件。

在上一环节我们看了 VirtualService(路由规则),而这张图里的 DestinationRule可以看作是它的“搭档”“底层支撑”

以下是详细解读:

1. 核心作用:给服务“分门别类”

如果说 VirtualService是决定“走哪条路”,那么 DestinationRule就是定义“每条路通向哪里,以及这条路的特点是什么”。

具体来说,它的作用是:根据服务的标签(Labels),将同一个服务的不同版本(如 v1, v2, v3)定义为独立的“子集(Subsets)”

2. 结合截图逐段分析

  • host: reviews

    • 这表示接下来的规则是针对 reviews这个服务的。Istio 会去匹配集群中标签为 app: reviews的所有 Pod。

  • subsets:(重点!)

    • 这里定义了三个子集:v1v2v3。这就是我们在 Kubernetes 里常说的标签选择器

    • name: v1: 定义了一个名叫 v1的子集。

    • labels: version: v1: 这是关键。Istio 会去寻找那些带有 version: v1标签的 Pod。

      • 结合你之前的截图:你在部署 Bookinfo 时,reviews的 v1 Pod 模板里肯定带了 version: v1这个标签。Istio 通过这里的定义,就能准确地识别出哪些 Pod 属于 v1 版本。

3. 为什么必须有它?(与 VirtualService 的关系)

这就解释了上一环节中 VirtualService里的一行代码:

subset: v1
  • VirtualService:把流量转发给 reviews服务的 v1子集

  • DestinationRule:哦,原来 v1子集指的就是那些带有 version: v1标签的 Pod!

如果没有这张图里的 DestinationRuleVirtualService里的 subset: v1就会失效,Istio 根本不知道 v1在哪里,会导致流量转发失败。

4. 实战总结

这两个文件(virtual-service-all-v1.yamldestination-rule-all.yaml)通常是成对使用的:

  1. 第一步(本图 DestinationRule:先“画地图”,告诉 Istio v1、v2、v3 各自的 Pod 在哪里(通过标签匹配)。

  2. 第二步(上一张图 VirtualService:再“发指令”,根据权重(如 90% 去 v1,10% 去 v2)把流量分配到上面定义好的子集上。

现在,Istio 的基础环境、网关入口、路由规则、目标规则都已经准备好了,只差最后一步应用这些 YAML 文件,灰度发布的环境就彻底搭建完成了。

结合你提供的所有截图和 Istio 实战逻辑,这里整理了完成灰度发布(金丝雀发布)所需的全套配置文件

这些文件分别对应了我们之前分析的:入口网关、路由规则、以及底层的版本分流定义。

1. 入口网关定义 (Gateway)

文件路径networking/bookinfo-gateway.yaml

作用:在集群边缘开一个 80 端口,接收外部流量。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway # 绑定到默认的 Ingress Gateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*" # 允许所有域名访问

2. 基础路由规则 (VirtualService for productpage)

文件路径networking/virtual-service-productpage.yaml

作用:将访问前端的流量直接转发给 productpage服务。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: productpage
spec:
  hosts:
  - productpage
  http:
  - route:
    - destination:
        host: productpage
        subset: v1

3. 灰度发布规则 (VirtualService for reviews)

文件路径networking/virtual-service-reviews-v1.yaml(初始状态)

作用:这是控制灰度的核心。初始状态下,强制所有流量走 v1(无星)版本。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1

4. 目标规则定义 (DestinationRule)

文件路径networking/destination-rule-all.yaml

作用:定义什么是 v1,什么是 v2。VirtualService 里的 subset: v1必须在这里有对应定义才能生效。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

下一步:如何做灰度测试?

当你把上面 4 个文件都 kubectl apply -f部署好之后,用户只能看到无星版本。

如果你想进行金丝雀发布(例如:90% 流量看 v1 无星,10% 流量看 v2 红星),你需要修改第 3 个文件​ (reviews的 VirtualService):

修改后的 virtual-service-reviews-v1.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 90    # 新增:90% 权重给 v1
    - destination:
        host: reviews
        subset: v2
      weight: 10    # 新增:10% 权重给 v2

保存并重新执行 kubectl apply -f networking/virtual-service-reviews-v1.yaml,灰度效果立即生效,无需重启任何 Pod。

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

这组截图展示了在 Kubernetes (K8s) 和 Istio 环境下,使用 Nginx 镜像​ 部署一个简单的双版本应用,并通过 Service​ 进行流量负载均衡的全过程。

这是一个比之前 Bookinfo 更轻量级的灰度发布模拟实验。以下是核心步骤和对应配置文件的整理:

1. 环境准备:命名空间与自动注入

首先需要创建一个独立的命名空间,并打上 Istio 自动注入标签,确保后续的 Nginx Pod 会自动被 Istio Sidecar 代理容器注入。

  • 创建命名空间

    bash

    kubectl create ns nginx

  • 开启自动注入

    bash

    kubectl label namespace nginx istio-injection=enabled

2. 部署应用:双版本 Deployment

这里利用了 K8s 的 Deployment机制,部署了两个版本的 Nginx 应用。关键在于通过 version标签区分它们。

配置文件:nginx-v1.yaml

yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-v1

spec:

replicas: 1

selector:

matchLabels:

app: nginx

template:

metadata:

labels:

app: nginx

version: v1 # 关键标签:标识为 v1 版本

spec:

containers:

  • image: nginx

    name: nginx

同理,nginx-v2.yaml 只需将上面的 name 改为 nginx-v2,labels 中的 version 改为 v2

3. 暴露服务:创建统一的 Service

为了让外部或 Istio Gateway 能够访问到这两个版本的 Pod,创建了一个 ClusterIP 类型的 Service。它通过 app: nginx标签同时选中了 v1 和 v2 的 Pod,实现流量的初步汇聚。

  • 配置文件:service_nginx.yaml

    yaml

    apiVersion: v1

    kind: Service

    metadata:

    name: nginx

    spec:

    selector:

    app: nginx # 只要带有 app=nginx 标签的 Pod 都会接收流量

    ports:

    • protocol: TCP

      port: 80

      targetPort: 80

  • 验证 Endpoint

    执行 kubectl get svc,ep -n nginx可以看到 Endpoints 列表中同时包含了 v1 和 v2 的 Pod IP,说明 Service 已经成功将两者关联。

4. 区分流量:修改静态页面

为了直观验证灰度发布的效果,需要进入容器内部修改默认的 index.html,让 v1 和 v2 显示不同的内容。

  • 修改 v1:显示 "111"

    bash

    kubectl exec -it nginx-v1-xxxxx bash -c nginx -n nginx

    echo "111" > /usr/share/nginx/html/index.html

  • 修改 v2:显示 "222"

    bash

    kubectl exec -it nginx-v2-xxxxx bash -c nginx -n nginx

    echo "222" > /usr/share/nginx/html/index.html

5. 下一步操作预测

目前,v1 和 v2 已经同时运行,并且共享同一个 Service 入口。接下来的 Istio 灰度发布步骤通常是:

  1. 创建 Gateway:在 Istio 中开启 80 端口监听。

  2. 创建 VirtualService:编写路由规则,例如设定 90% 流量去 v1(看到 111),10% 流量去 v2(看到 222)。

这样就完成了一个最基础的基于 Nginx 的金丝雀发布环境搭建。

以下是基于你当前的 Nginx 双版本环境,实现 90% 流量到 v1(显示 111),10% 流量到 v2(显示 222)​ 的完整 Istio 配置。


一、创建 Gateway(开启 80 端口监听)

这个 Gateway 负责在 Istio 边界开启入口。

gateway-nginx.yaml

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: nginx-gateway
  namespace: nginx
spec:
  selector:
    istio: ingressgateway  # 绑定 istio-ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"  # 允许所有域名/IP访问

应用:

kubectl apply -f gateway-nginx.yaml

二、创建 DestinationRule(定义 v1 / v2 子集)

⚠️ 必须先创建这个,否则 VirtualService 里的 subset会报错。

destinationrule-nginx.yaml

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: nginx-destination
  namespace: nginx
spec:
  host: nginx.nginx.svc.cluster.local  # 你的 Service FQDN
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

应用:

kubectl apply -f destinationrule-nginx.yaml

三、创建 VirtualService(90/10 灰度路由)

这是灰度发布的核心配置。

virtualservice-nginx.yaml

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: nginx-route
  namespace: nginx
spec:
  hosts:
  - "*"
  gateways:
  - nginx-gateway  # 绑定上面的 Gateway
  http:
  - route:
    - destination:
        host: nginx.nginx.svc.cluster.local
        subset: v1
      weight: 90   # 90% 流量 → v1(显示 111)
    - destination:
        host: nginx.nginx.svc.cluster.local
        subset: v2
      weight: 10   # 10% 流量 → v2(显示 222)

应用:

kubectl apply -f virtualservice-nginx.yaml

四、获取访问地址并验证

1️⃣ 获取 Ingress Gateway 地址

kubectl get svc istio-ingressgateway -n istio-system
  • LoadBalancer 环境:直接使用 EXTERNAL-IP

  • NodePort / 裸机环境

    export INGRESS_HOST=$(kubectl get po -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].status.hostIP}')
    export INGRESS_PORT=$(kubectl get svc istio-ingressgateway -n istio-system -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')

2️⃣ 访问测试(关键验证)

for i in {1..20}; do
  curl http://$INGRESS_HOST:$INGRESS_PORT
  echo ""
done

预期结果:

  • 大约 18 次​ 返回 111(v1)

  • 大约 2 次​ 返回 222(v2)


五、常见问题排查

❌ VirtualService 报错:subset v1 not found

✅ 解决:确认 DestinationRule已创建,且 labels.version与 Pod 一致:

kubectl get pods -n nginx --show-labels

❌ 访问返回 503

✅ 解决:

kubectl logs deploy/nginx-v1 -n nginx -c istio-proxy
kubectl logs deploy/nginx-v2 -n nginx -c istio-proxy

六、下一步(可选进阶)

如果你愿意,我可以继续帮你做:

  1. 基于 Header 的灰度(如 x-user: test→ 强制走 v2)

  2. 基于 Cookie 的灰度(如 canary=true→ 走 v2)

  3. 自动熔断(v2 错误率 >10% 自动切回 v1)

  4. Kiali 可视化验证流量比例

你现在这套环境已经是标准的企业级灰度发布模型了 👍

太棒了!你已经掌握了基础的灰度发布,接下来我们进入精细化流量治理阶段。

这四个需求分别对应了微服务架构中非常核心的四个治理场景。我会为你逐一提供可以直接使用的 YAML 配置和验证方法。


1️⃣ 基于 Header 的灰度发布

场景:内部测试人员想要体验新版本,但普通用户保持原样。我们可以通过浏览器插件(如 ModHeader)设置一个特定的 Header 来实现。

修改 virtualservice-nginx.yaml

yaml

apiVersion: networking.istio.io/v1beta1

kind: VirtualService

metadata:

name: nginx-route

namespace: nginx

spec:

hosts:

  • "*"

    gateways:

  • nginx-gateway

    http:

第一层规则:优先匹配 Header

  • match:

    • headers:

      x-user: # Header 的 Key

      exact: test # Header 的 Value

      route:

    • destination:

      host: nginx.nginx.svc.cluster.local

      subset: v2

    注意:这里不需要写 weight,命中后 100% 走 v2

第二层规则:兜底策略(默认走 90/10)

  • route:

    • destination:

      host: nginx.nginx.svc.cluster.local

      subset: v1

      weight: 90

    • destination:

      host: nginx.nginx.svc.cluster.local

      subset: v2

      weight: 10

验证方法

使用 curl模拟带 Header 的请求:

bash

curl -H "x-user: test" http://<你的网关IP>

👉 结果:无论权重怎么设,这条请求一定会显示 222 (v2)


2️⃣ 基于 Cookie 的灰度发布

场景:这是最常用的金丝雀发布手段。让一部分“特定用户”(比如种子用户)自动看到新版本,其他用户无感。

修改 virtualservice-nginx.yaml

yaml

apiVersion: networking.istio.io/v1beta1

kind: VirtualService

metadata:

name: nginx-route

namespace: nginx

spec:

hosts:

  • "*"

    gateways:

  • nginx-gateway

    http:

第一层规则:优先匹配 Cookie

  • match:

    • cookies:

      canary: # Cookie 的 Key

      exact: "true" # Cookie 的 Value

      route:

    • destination:

      host: nginx.nginx.svc.cluster.local

      subset: v2

第二层规则:兜底策略

  • route:

    • destination:

      host: nginx.nginx.svc.cluster.local

      subset: v1

      weight: 90

    • destination:

      host: nginx.nginx.svc.cluster.local

      subset: v2

      weight: 10

验证方法

bash

模拟携带 Cookie 的请求

curl --cookie "canary=true" http://<你的网关IP>

👉 结果:携带该 Cookie 的请求会稳定显示 222 (v2),非常适合前端在浏览器控制台设置 Cookie 进行自测。


3️⃣ 自动熔断(异常自动回退)

场景:v2 版本可能存在 Bug,当错误率达到阈值(例如 10%)时,Istio 自动停止向 v2 发送流量,全量切回 v1,保护整体服务可用性。

我们需要引入一个新的资源:DestinationRule中的 trafficPolicyoutlierDetection

创建/修改 destinationrule-nginx.yaml

yaml

apiVersion: networking.istio.io/v1beta1

kind: DestinationRule

metadata:

name: nginx-dest

namespace: nginx

spec:

host: nginx.nginx.svc.cluster.local

subsets:

  • name: v1

    labels:

    version: v1

  • name: v2

    labels:

    version: v2

重点:针对 v2 设置熔断策略

trafficPolicy:

outlierDetection:

consecutive5xxErrors: 1 # 连续 1 个 5xx 错误就触发剔除

interval: 10s # 检测间隔

baseEjectionTime: 30s # 剔除时间

maxEjectionPercent: 100 # 最大剔除比例

模拟故障并验证

  1. 配置好上面的 YAML

  2. 制造故障:进入 v2 的 Pod,停止 Nginx 服务。

    bash

    kubectl exec -it nginx-v2-xxxxx -n nginx -- nginx -s stop

  3. 观察:等待约 10-30 秒后,再次访问网关。

    👉 结果:虽然 VirtualService 里写了 10% 流量去 v2,但因为 v2 挂了,Istio 会自动将这 10% 的流量也切回 v1,用户依然能正常看到 111,不会报错


4️⃣ Kiali 可视化验证

场景:在复杂的微服务网状结构中,肉眼看 YAML 很难确认流量到底走了哪条路。Kiali 提供了图形化拓扑图。

操作步骤

  1. 确保 Kiali 已安装(Istio 默认安装通常包含)。

  2. 查看 Token(用于登录 Kiali):

    bash

    kubectl -n istio-system get secret istio-kiali -o jsonpath='{.data.token}' | base64 -d

  3. 启动 Port-Forward

    bash

    kubectl -n istio-system port-forward deployment/kiali 20001:20001

  4. 访问:浏览器打开 http://localhost:20001,输入上面的 Token 登录。

  5. 查看 Graph

    • 找到左侧的 Graph​ 菜单。

    • 选择你的 nginx命名空间。

    • 点击右上角的 Display​ 按钮,勾选 Requests percentage(请求百分比)。

    • 此时你会看到一张图,网关指向两个版本的箭头宽度会直接反映当前的流量比例(比如 v1 很宽,v2 很细)。


💡 总结:企业级灰度发布组合拳

在实际生产中,通常会这样组合使用:

text

  1. 正常时期:VirtualService (90% v1 / 10% v2) -> 金丝雀发布

  2. 测试时期:Header (x-user: internal) -> 强制走新版

  3. 风控时期:OutlierDetection (错误率>5%) -> 自动熔断保护

  4. 监控时期:Kiali + Grafana -> 实时监控流量走向

你可以先把 HeaderCookie的玩法跑通,这对你后续做 A/B 测试非常有帮助!需要我帮你出具体的 curl压测命令吗?

image

 

image

 

image

 

image

 

image

 

这组截图展示了“如何通过域名进行流量分流”的高级实战配置。

在前面的步骤中,我们实现了基于权重(90%/10%)的金丝雀发布。但在实际生产环境中,经常需要基于域名(Host)将不同的服务流量导向不同的应用,特别是当多个服务共享同一个 Istio Ingress Gateway 的 IP 和端口时。

以下是基于截图的详细配置解析:

1. 整体架构逻辑

截图顶部的架构图展示了这种“多层代理”模式:

  • 外层 LB/Nginx:用户访问公网 IP,首先到达这台前置的 Nginx(相当于 LVS 或云负载均衡器)。

  • 内层 Istio Gateway:前置 Nginx 将流量转发给 K8s 集群内的 Istio Ingress Gateway(通过 NodePort 暴露)。

  • 分流逻辑:Istio 根据 HTTP Header 中的 Host字段(即域名),决定是将流量路由到 nginx-v1还是 nginx-v2

2. 前置 Nginx 配置(外部 LB)

截图展示了如何配置这台前置的 Nginx,让它把特定域名的请求转发给 Istio 网关。

配置文件:/etc/nginx/nginx.conf

nginx

server {

listen 80;

server_name _; # 捕获所有未匹配的域名

location / {

# 关键配置:代理到 Istio Ingress Gateway 的 NodePort IP 和端口

proxy_pass http://192.168.171.129:32677;

# 必须指定 HTTP 版本,否则 Istio 可能拒绝(默认支持 1.1)
proxy_http_version 1.1;

# 必须将 Host 头透传下去,否则 Istio 无法识别 $host 变量进行域名分流
proxy_set_header Host $host; 
}

}

  • proxy_pass:指向 Istio Ingress Gateway 的 NodePort 地址(即 K8s 节点的 IP + NodePort 端口)。

  • proxy_set_header Host $host:这是最关键的一行。它确保用户浏览器输入的域名(如 nginx.test.com)能被原封不动地传递给后面的 Istio 服务网格。

3. Istio 配置(域名分流核心)

为了实现“不同域名进不同服务”,我们需要配置 GatewayVirtualService

第一步:Gateway 定义(监听入口)

文件推测:gateway-nginx.yaml

yaml

apiVersion: networking.istio.io/v1alpha3

kind: Gateway

metadata:

name: nginx-gateway

namespace: nginx

spec:

selector:

istio: ingressgateway # 绑定到默认的 Ingress Gateway

servers:

  • port:

    number: 80

    name: http

    protocol: HTTP

    hosts:

    • "nginx.test.com" # 只允许这个域名的流量进入(白名单机制)

第二步:VirtualService 定义(基于域名的路由)

文件推测:vs.yaml

yaml

apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

name: nginx

namespace: nginx

spec:

hosts:

  • "nginx.test.com" # 客户端访问的域名

    gateways:

  • nginx/nginx-gateway # 绑定上面的 Gateway

    http:

  • route:

    • destination:

      host: nginx # 对应 K8s Service 名

      subset: v1

      weight: 80 # 80% 流量给 v1

    • destination:

      host: nginx

      subset: v2

      weight: 20 # 20% 流量给 v2

4. 验证与测试流程

按照截图底部的操作步骤,完整的测试闭环如下:

  1. 配置 Hosts:在本地电脑(或测试机)的 /etc/hosts文件中,将 nginx.test.com指向前置 Nginx 的公网 IP。

    text

    192.168.171.130 nginx.test.com

  2. 启动前置 Nginx

    bash

    systemctl start nginx

  3. 访问测试

    打开浏览器访问 http://nginx.test.com

    • 因为配置了 80/20 权重,你应该大部分时间看到 111 (v1),小部分时间看到 222 (v2)

    • 如果你把浏览器 URL 改成另一个不存在的域名(如 test2.com),由于 Gateway 的 hosts限制了只允许 nginx.test.com,Istio 会直接返回 404​ 错误。这证明了域名白名单机制生效了。

总结

这套配置完美模拟了公有云上的多租户场景

  • 你拥有一个固定的公网 IP(前置 Nginx)。

  • 你通过这个前置 Nginx,利用不同的域名(nginx.test.com, api.test.com等)将流量分发到 K8s 集群内完全不同的微服务应用中。

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

这组截图展示了 Istio 实战教程的最后两个核心环节

  1. 域名分流(南北向网关):利用 Istio 的 GatewayVirtualService实现基于域名的路由。

  2. 可视化监控(Kiali & Prometheus):部署并暴露监控组件,实现流量拓扑的可视化。

以下是这两个环节的详细配置和原理解析:


一、基于域名的流量分流(域名路由)

当多个微服务共用同一个 Istio Ingress Gateway 的 IP 和端口时,Host头(域名)是区分不同服务的唯一标识。

1. 核心配置文件

① Gateway(网关入口)

作用:在 Ingress Gateway 上监听 80 端口,并允许 bookinfo.test.com这个域名的流量进入。

yaml

文件名:gateway-bookinfo.yaml

apiVersion: networking.istio.io/v1alpha3

kind: Gateway

metadata:

name: bookinfo-gateway

namespace: bookinfo

spec:

selector:

istio: ingressgateway

servers:

  • port:

    number: 80

    name: http

    protocol: HTTP

    hosts:

    • "bookinfo.test.com"

② VirtualService(虚拟服务路由)

作用:将匹配到上述域名的流量,转发给后端的 productpage服务。

yaml

文件名:virtual-service-bookinfo.yaml

apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

name: bookinfo

namespace: bookinfo

spec:

hosts:

  • "bookinfo.test.com" # 必须和 Gateway 中的 hosts 一致

    gateways:

  • bookinfo/bookinfo-gateway # 绑定 gateway,格式:namespace/name

    http:

  • route:

    • destination:

      host: productpage # K8s service 名称

      port:

      number: 9080

(注:截图中未展示完整的 http路由段,这里补充了最基础的全量转发配置)

2. 客户端测试配置

为了让这个域名分流生效,必须在客户端机器(你的电脑或测试机)的 hosts文件中添加一条映射:

text

<你的Istio网关IP> bookinfo.test.com

访问时,浏览器会根据这个域名发送请求,Istio 网关收到后,比对 Host头,确认这是 bookinfo服务的流量,于是将其转发给 productpage


二、服务网格可视化监控(Kiali & Prometheus)

Istio 自带了强大的监控能力,但这需要部署额外的组件(Addons)。

1. 核心配置文件

① CRD (Custom Resource Definition)

监控组件依赖一些新的资源类型,必须先安装。

bash

kubectl apply -f crds.yaml -n istio-system

② Kiali 配置(修改 NodePort)

默认情况下 Kiali 可能没有外部访问方式,需要手动修改。

修改点:在 spec.ports下增加 nodePort: 31686,固定监控页面的对外端口。

yaml

文件名:kiali.yaml (部分)

apiVersion: kiali.io/v1alpha1

kind: Kiali

metadata:

name: kiali

namespace: istio-system

spec:

server:

ports:

  • name: http-metrics

    port: 9090

    新增下面这一行,固定 NodePort

    nodePort: 31686

  • name: http

    port: 20001

    nodePort: 31686

2. 部署与验证

执行以下命令一键部署 Prometheus(采集数据)和 Kiali(展示面板):

bash

kubectl apply -f grafana.yaml -n istio-system

kubectl apply -f prometheus.yaml -n istio-system

kubectl apply -f kiali.yaml -n istio-system

3. 如何访问

部署完成后,查看 Service 列表,找到 kialiNodePort

bash

kubectl get svc -n istio-system

  • Kiali 访问地址http://<任意节点IP>:31686

  • Kiali 默认账号/密码admin/admin

💡 总结

至此,你已经完成了 Istio 最核心的实战技能闭环:

  1. 内部通信:服务间自动注入 Sidecar。

  2. 流量治理:灰度发布、A/B 测试、金丝雀发布。

  3. 外部暴露:通过域名将服务安全地暴露给公网。

  4. 观测运维:通过 Kiali 实时监控服务间的调用关系和流量分布。

以下是基于你提供的截图和实战流程整理的 Istio Bookinfo 全链路详细配置文件。请按顺序执行。


一、基础环境准备

1. 创建命名空间并开启自动注入

kubectl create ns bookinfo
kubectl label namespace bookinfo istio-injection=enabled

二、部署 Bookinfo 应用

2. Bookinfo 应用部署 (bookinfo.yaml)

来源samples/bookinfo/platform/kube/bookinfo.yaml

作用:部署 Productpage, Reviews (v1/v2/v3), Details, Ratings 四个微服务。

apiVersion: v1
kind: Service
metadata:
  name: details
  namespace: bookinfo
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: details
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: details-v1
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: details
      version: v1
  template:
    metadata:
      labels:
        app: details
        version: v1
    spec:
      containers:
      - name: details
        image: docker.io/istio/examples-bookinfo-details-v1:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---
# Reviews Service (v1, v2, v3)
apiVersion: v1
kind: Service
metadata:
  name: reviews
  namespace: bookinfo
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: reviews
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v1
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v1
  template:
    metadata:
      labels:
        app: reviews
        version: v1
    spec:
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v1:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v2
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v2
  template:
    metadata:
      labels:
        app: reviews
        version: v2
    spec:
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v2:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v3
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v3
  template:
    metadata:
      labels:
        app: reviews
        version: v3
    spec:
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v3:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---
# Productpage Service
apiVersion: v1
kind: Service
metadata:
  name: productpage
  namespace: bookinfo
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v1
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: productpage
      version: v1
  template:
    metadata:
      labels:
        app: productpage
        version: v1
    spec:
      containers:
      - name: productpage
        image: docker.io/istio/examples-bookinfo-productpage-v1:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080

应用:

kubectl apply -f bookinfo.yaml

三、Istio 流量治理配置(灰度发布)

3. Gateway 配置 (gateway-bookinfo.yaml)

作用:开启 80 端口,允许 bookinfo.test.com域名访问。

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: bookinfo-gateway
  namespace: bookinfo
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "bookinfo.test.com"

应用:

kubectl apply -f gateway-bookinfo.yaml

4. DestinationRule 配置 (destination-rule-bookinfo.yaml)

⚠️ 必须配置:定义 v1, v2, v3 子集,否则 VirtualService 会报错。

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews
  namespace: bookinfo
spec:
  host: reviews.bookinfo.svc.cluster.local
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

应用:

kubectl apply -f destination-rule-bookinfo.yaml

5. VirtualService 配置 (virtual-service-bookinfo.yaml)

作用:实现 90% v1, 10% v2 的灰度发布。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
  namespace: bookinfo
spec:
  hosts:
  - "bookinfo.test.com"
  gateways:
  - bookinfo/bookinfo-gateway
  http:
  # 路由到 productpage
  - match:
    - uri:
        exact: /productpage
    route:
    - destination:
        host: productpage.bookinfo.svc.cluster.local
        port:
          number: 9080
  # 默认路由(兜底)
  - route:
    - destination:
        host: productpage.bookinfo.svc.cluster.local
        port:
          number: 9080

进阶:灰度发布(90/10)

若要对 reviews服务进行灰度,需单独配置:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
  namespace: bookinfo
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews.bookinfo.svc.cluster.local
        subset: v1
      weight: 90
    - destination:
        host: reviews.bookinfo.svc.cluster.local
        subset: v2
      weight: 10

应用:

kubectl apply -f virtual-service-bookinfo.yaml

四、监控与可视化(Kiali & Prometheus)

6. Kiali 配置 (kiali.yaml)

作用:修改 Service 类型为 NodePort,固定端口 31686。

apiVersion: v1
kind: Service
metadata:
  name: kiali
  namespace: istio-system
spec:
  type: NodePort
  selector:
    app: kiali
  ports:
  - name: http
    port: 20001
    targetPort: 20001
    nodePort: 31686

7. Prometheus 配置 (prometheus.yaml)

作用:部署指标采集器(Istio 默认提供)。

# 使用 Istio 自带配置
kubectl apply -f samples/addons/prometheus.yaml

8. 部署监控组件

kubectl apply -f samples/addons/kiali.yaml
kubectl apply -f samples/addons/grafana.yaml

五、验证与访问

1. 配置本地 Hosts

在本地电脑的 /etc/hosts中添加:

<ISTIO_INGRESS_GATEWAY_IP>  bookinfo.test.com

获取 Gateway IP:

kubectl get svc istio-ingressgateway -n istio-system

2. 访问应用

浏览器打开:http://bookinfo.test.com/productpage

3. 访问 Kiali

浏览器打开:http://<NODE_IP>:31686

账号/密码:admin/admin


六、总结配置清单

 

序号

文件

作用

1

bookinfo.yaml

部署微服务

2

gateway-bookinfo.yaml

入口网关

3

destination-rule-bookinfo.yaml

定义版本子集

4

virtual-service-bookinfo.yaml

路由与灰度

5

kiali.yaml

可视化监控

这套配置完全覆盖了你截图中的所有实战环节,可直接用于实验或生产环境参考。

image

 

Kiali 和 Prometheus 的关系:一句话版

Prometheus 是"指标的仓库",Kiali 是"看图说话的壳"。

Kiali 自己不产生也不存储任何指标,它只是一个Web UI + 分析引擎,它所有关于流量、错误率、拓扑图的数据,全部是通过 HTTP 查询 Prometheus 拿到的


数据流向(核心)

Envoy Sidecar(每个 Pod 里)
    │  暴露 envoy 指标(计数器、直方图等)
    │  端口 15090 /stats/prometheus
    ▼
Prometheus
    │  定时 scrape(抓取)所有 Sidecar 的指标
    │  存进自己的 TSDB(时序数据库)
    │  提供 PromQL 查询接口 :9090/api/v1/query
    ▼
Kiali
    │  内部配置指向 Prometheus 的 URL
    │  Kiali 用 PromQL 去 Prometheus 查询
    │  例:"给我过去 1 分钟内 reviews 服务的 5xx 率"
    ▼
浏览器(你看到的拓扑图 / 折线图 / 健康红绿灯)

所以关系是典型的 Consumer → Data Source​ 关系:

 

角色

干的事

不干的

Prometheus

抓指标 → 存指标 → 答查询

不画好看的服务拓扑图

Kiali

查 Prometheus → 画拓扑 → 标红绿灯 → 展示 VS/DR 配置

不存任何历史指标


配置层面:Kiali 怎么"认" Prometheus

Kiali 启动时会读自己的配置(kiali-config.yaml或 CR Kiali资源),里面有一节专门指向 Prometheus:

# Kiali 配置里的关键段落(概念性)
external_services:
  prometheus:
    url: http://prometheus.istio-system.svc.cluster.local:9090

这就是两者耦合的唯一纽带——一个 URL。Kiali 启动后,每隔一段时间(或你刷新页面时)会用 PromQL 去这个 URL 拉数据。

你可以验证:如果把 Prometheus 删了,Kiali 页面照样能打开,但:

  • 拓扑图上 没有流量线

  • 左上角会报 "Prometheus not reachable"

  • "Metrics" 标签页空白


他们各自"管"什么——职责拆解

Prometheus 管的

 

职责

说明

服务发现

通过 K8s API 找 Pod,找 istio-proxy的 15090

Scrape(抓取)

定时拉 istio_requests_totalistio_request_duration_milliseconds_bucket等指标

存储

本地 TSDB(默认保留 15 天,可改)

查询引擎

执行 PromQL,算速率、算百分比、算分位

Kiali 管的

 

职责

说明

拓扑图

从指标里反推出 "A → B" 的调用关系,画节点+边

健康判定

用 Prometheus 算 4xx/5xx 率 → 标 🔴🟡🟢

配置透视

直接读 K8s API 里的 VS/DR/Gateway 对象(不靠 Prometheus

操作入口

在 UI 里创建/修改 VS 权重(本质还是改 K8s 资源)

注意:Kiali 画拓扑图用的是 Prometheus 的指标做"流量证据",但它读 VS/DR 配置是直接调 K8s API Server——这点很多人会混淆。


最常见的三个误解(避坑)

❌ 误解 1:"Kiali 替代 Prometheus"

不对。Kiali 只是 UI 层,指标底座永远是 Prometheus(或兼容 PromQL 的替代品,如 Thanos/Mimir)。关掉 Prometheus,Kiali 立刻变瞎。

❌ 误解 2:"Kiali 存了历史数据"

不对。你在 Kiali 里看到的历史曲线,数据源全是 Prometheus 的 TSDB。Kiali 自身连 SQLite 都没建。

❌ 误解 3:"Istio 把指标发给 Kiali"

不对。链路是:

Istio(Envoy)→ Prometheus → Kiali

Istio 根本不知道 Kiali 是谁。它只负责在 15090 暴露 /stats/prometheus,剩下的是 Prometheus 的 scrape 循环在干活。


用一句话回答你实操中看到的现象

当你执行:

istioctl dashboard kiali
# 或访问 http://nodeIP:31686

你打开的其实是一个只干"查询 + 渲染"的面板,背后真正出力的是 Prometheus 在那儿一刻不停地抓 Envoy 数据、算速率、算百分比——Kiali 只是把它包装成了你看得懂的服务拓扑图

如果你想进一步看清楚"Kiali 到底对 Prometheus 发了哪些 PromQL 查询",在 Kiali 页面打开浏览器 DevTools → Network → 过滤 api请求,你能直接看到它发的查询语句,和你在 Prometheus 原生 UI 里敲的一模一样。44

image

 

Istio 自带 Prometheus 的采集配置文件(详细版 + 逐项抓取说明)

Istio 自带的 Prometheus 配置通常在 samples/addons/prometheus.yaml​ 里,其 prometheus.yml​ 实际存放在一个 ConfigMap​ 中,最终挂载到 Prometheus Pod 的

/etc/prometheus/prometheus.yml

下面给你一份贴近 Istio 1.9 addon 风格、把“每个 job 在抓谁 / 怎么发现 / 怎么拼地址 / 抓哪个路径”讲清楚的配置。


0) 全局配置(Global)

global:
  scrape_interval: 30s
  evaluation_interval: 30s
  # 让 prometheus 能访问 k8s apiserver 做服务发现
  kubernetes_sd_configs:
  - role: endpoints

说明

  • scrape_interval: 30s:每 30 秒去每个 target 拉一次指标(你看到的 /targets里 Last Scrape 就由它决定)。

  • kubernetes_sd_configs: [{role: endpoints}]全局块只是声明“能用 k8s SD”,真正每个 job 自己再细化pod还是 endpoints发现。


1) envoy-stats:抓 Envoy sidecar / gateway 的原生指标

这是 Istio 里最重要的指标来源:istio_requests_totalenvoy_cluster_*envoy_listener_*等绝大多数“流量指标”都从这里来。

- job_name: 'envoy-stats'
    metrics_path: /stats/prometheus
    kubernetes_sd_configs:
    - role: pod
      namespaces:
        names: []   # [] 表示搜所有 ns;也可写成 [istio-system,bookinfo,nginx,...]

    relabel_configs:
    # ① 只保留:这个 Pod 里存在某个容器端口,其 port-name 以 "-envoy-prom" 结尾
    #   Istio 会给 istio-proxy 的 metrics 端口起名类似 http-envoy-prom / https-envoy-prom
    - source_labels: [__meta_kubernetes_pod_container_port_name]
      action: keep
      regex: '.*-envoy-prom'

    # ② 把抓取地址强行拼成 PodIP:15090
    #   envoy 原生指标端口默认是 15090
    - source_labels: [__meta_kubernetes_pod_ip]
      action: replace
      regex: '(.*)'
      target_label: __address__
      replacement: '${1}:15090'

    # ③ 把指标路径固定到 /stats/prometheus(其实 job 顶层已经写了,这里更稳)
    - source_labels: []
      action: replace
      target_label: __metrics_path__
      replacement: /stats/prometheus

    # ④ 给时序打常用标签(方便 PromQL 按 ns/pod/app 查)
    - source_labels: [__meta_kubernetes_namespace]
      action: replace
      target_label: namespace
    - source_labels: [__meta_kubernetes_pod_name]
      action: replace
      target_label: pod
    - source_labels: [__meta_kubernetes_pod_label_app]
      action: replace
      target_label: app
    - source_labels: [__meta_kubernetes_pod_label_version]
      action: replace
      target_label: version

这个 job 到底在“抓谁”、怎么抓?

  1. 发现对象role: pod→ Prometheus 枚举集群里所有 Pod。

  2. 过滤

    • 只看有 istio-proxy容器的 Pod(通过端口名 *-envoy-prom间接保证)。

  3. 拼地址

    • 最终 scrape 地址变成:

      http://<POD_IP>:15090/stats/prometheus

  4. 指标内容

    • 这是 Envoy 原生 stats,以 # TYPE envoy_为主,同时也包含 Istio 扩展的计数器(如 istio_requests_total由 mixer/telemetry 时代遗留或 stats filter 产生/暴露)。

⚠️ 关键坑:15090 默认绑 127.0.0.1

很多环境里 envoy 的 metrics listener 是 127.0.0.1:15090,导致

PodIP:15090连不上(DOWN / connection refused)。

解法(demo/内网常用):让 envoy 把 metrics 绑到 0.0.0.0

kubectl edit cm istio -n istio-system

meshConfig.defaultConfig加:

meshConfig:
  defaultConfig:
    proxyStatsBindHost: 0.0.0.0

然后重启相关 workload / gateway:

kubectl rollout restart deployment istio-ingressgateway -n istio-system
kubectl rollout restart deployment/nginx-v1 deployment/nginx-v2 -n <你的ns>

2) istiod:抓 控制面(Pilot)自身指标

用来观察 istiod 健康状况、推送次数、xDS 延迟等(偏运维/排障)。

- job_name: 'istiod'
    kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names: [istio-system]

    relabel_configs:
    # 只保留 istiod 的 http-monitoring 端口
    - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
      action: keep
      regex: istiod;http-monitoring

    # 打标签
    - source_labels: [__meta_kubernetes_namespace]
      action: replace
      target_label: namespace
    - source_labels: [__meta_kubernetes_pod_name]
      action: replace
      target_label: pod

抓的是哪?

  • 走的是 istiod Service 的 http-monitoring端口(通常 15014),路径默认 /metrics

  • Prometheus 通过 endpoint 发现找到 istiod Pod 的 <PodIP>:15014/metrics


3) 应用 pod 的“注解驱动抓取”(含 Istio 的 metrics merge)

这一条负责抓 应用容器自己的 /metrics,并且在 Istio 开启 enablePrometheusMerge=true(默认)时,istio-proxy 会帮你把应用指标 合并到 15020/stats/prometheus​ 一起暴露,让 Prometheus 只抓一个端点就能拿全。

- job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
    - role: pod

    relabel_configs:
    # 只抓声明了 prometheus.io/scrape: "true" 的 Pod
    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
      action: keep
      regex: "true"

    # 路径:默认 /metrics,允许 pod 用 prometheus.io/path 覆盖
    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
      action: replace
      target_label: __metrics_path__
      regex: (.+)
      replacement: ${1}
    - source_labels: []
      action: replace
      target_label: __metrics_path__
      replacement: /metrics

    # 端口:允许 pod 用 prometheus.io/port 声明(通常是 15020 或应用端口)
    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
      action: replace
      regex: (\d+)
      target_label: __address__
      replacement: __meta_kubernetes_pod_ip__:${1}

    # 打标签
    - source_labels: [__meta_kubernetes_namespace]
      action: replace
      target_label: namespace
    - source_labels: [__meta_kubernetes_pod_name]
      action: replace
      target_label: pod
    - source_labels: [__meta_kubernetes_pod_label_app]
      action: replace
      target_label: app

这里发生了什么(非常关键)

  • Istio sidecar 注入 + merge 开启时,istio-proxy 会给 Pod 自动补/覆盖注解:

    prometheus.io/scrape: "true"
    prometheus.io/port: "15020"
    prometheus.io/path: "/stats/prometheus"
  • 于是 Prometheus 实际抓的是:

    http://<POD_IP>:15020/stats/prometheus

    这个端口吐出来的文本里,既包含 Envoy/Istio stats,也可能包含应用 stats(前提:应用指标被正确 merge,且应用没跟 Istio 指标同名冲突)。

如果你“只想抓 Envoy 原生 15090”,可以不用这个 kubernetes-podsjob;

如果你“想看到应用自定义 counters/gauges”,通常要走 15020 merge 端点​ 或单独给应用开一个 scrape job。


4) (可选)只抓 gateway 的 envoy-stats(更收敛)

如果你不想扫全集群 pod,也可以把 gateway 单独捞出来:

- job_name: 'istio-ingressgateway-envoy-stats'
    metrics_path: /stats/prometheus
    kubernetes_sd_configs:
    - role: pod
      namespaces:
        names: [istio-system]
    relabel_configs:
    - source_labels: [__meta_kubernetes_pod_label_app]
      action: keep
      regex: istio-ingressgateway
    - source_labels: [__meta_kubernetes_pod_ip]
      action: replace
      regex: '(.*)'
      target_label: __address__
      replacement: '${1}:15090'
    - source_labels: []
      action: replace
      target_label: __metrics_path__
      replacement: /stats/prometheus

5) 怎么验证“到底抓到谁、抓的是什么接口”

# 看所有 target 状态
kubectl port-forward svc/prometheus -n istio-system 9090:9090
# 打开 http://localhost:9090/targets

# 直接验证 envoy 原生端点(podIP 要换成真实 IP)
kubectl get pods -n istio-system -o wide
curl http://<POD_IP>:15090/stats/prometheus | head -30

# 验证 merge 端点
curl http://<POD_IP>:15020/stats/prometheus | grep istio_requests_total | head

一句话把你之前所有疑惑串起来

  • envoy-stats job​ → 抓 15090/stats/prometheus(Envoy 原生,Istio 流量指标大本营)

  • istiod job​ → 抓 istiod 15014/metrics(控制面健康)

  • kubernetes-pods job​ → 靠 Pod 上的 prometheus.io/*注解找端口路径(通常落到 15020/stats/prometheus的 merge 端点)

  • Kiali ≠ 数据源:Kiali 只是读 Prometheus 查询 + 读 K8s API 里的 VS/DR 配置把它画成拓扑图。

如果你愿意把你们环境里 kubectl get cm prometheus -n istio-system -o yaml​ 里的 prometheus.yml原样贴出来(或只贴 scrape_configs:那段),我可以逐条按你“真实文件”帮你做红字批注版,精确到你集群里到底哪些 endpoint 会被 keep、为什么有的 DOWN。

一句话结论

 

端口

属于谁

暴露什么

有没有应用指标

15090

Envoy(pilot-agent 帮它起的 /stats/prometheushandler)

纯 Envoy/proxy 的 statsenvoy_*/ istio_*等)

❌ 没有

15020

pilot-agent(Istio sidecar 管理进程,不是 Envoy)

agent 自己的 HTTP 服务:/healthz/ready/stats/prometheus

✅ 可通过 merge​ 把应用 metrics 合进来

简单记:

  • 15090 = Envoy 的 Prometheus 指标口

  • 15020 = Istio agent 的“运维口”,其中 /stats/prometheus是一个“汇总/合并出口”


1) 15090:Envoy Prometheus telemetry(只关心 proxy)

它到底是谁在监听?

在 sidecar 容器里你一般能看到类似:

# 进 istio-proxy 容器
kubectl exec -it <pod> -c istio-proxy -- sh
apt-get update && apt-get install -y net-tools 2>/dev/null; ss -lntp | grep -E "15090|15020|15000"

典型结果是:Envoy 进程(或者说 pilot-agent 代 Envoy 绑的)监听 127.0.0.1:15090(或 0.0.0.0:15090,取决于 proxyStatsBindHost)。

这个端点吐什么?

curl -s http://127.0.0.1:15090/stats/prometheus | head -40

你会看到 Prometheus text format,几乎全是:

# TYPE envoy_cluster_upstream_rq_xx counter
# TYPE envoy_listener_downstream_cx gauge
istio_requests_total{...
istio_request_duration_milliseconds_bucket{...
envoy_...

这些都是 Envoy stat store 里的计数器/直方图/gauges,描述的是:

  • listener / filter chain / cluster / upstream 的状态

  • 请求计数、状态码分布、重试、熔断、连接池等

关键点:这里没有你的应用业务指标(除非你的应用碰巧也叫 istio_*之类,那也是撞名,不是设计)。

为什么会“从 Pod IP 连不上 15090”?

因为 Envoy 的 stats listener 默认绑 127.0.0.1(只给本 Pod 内用)。

你从别的 Node/Pod 用 PodIP:15090访问就会被拒。

Istio 控制这个绑址的字段是:

meshConfig:
  defaultConfig:
    proxyStatsBindHost: 0.0.0.0   # 允许外部 scrape(demo/内网可用;生产要评估)

2) 15020:pilot-agent 的 HTTP 端口(agent 的“前台”)

15020 不是 Envoy 的端口,而是 pilot-agent(也叫 Istio agent)启动的一个 Go HTTP server

它承担三类事:

  1. 健康检查

    • /healthz/ready

    • /app-health/<container>/ready(agent 替你探应用健康)

  2. 指标汇出口(重点)

    • /stats/prometheus(merge 就在这里发生)

  3. 少量 agent 自身 debug/状态

你可以验证“谁是监听者”:

# 在 Pod 里看(通常要在 istio-proxy 容器内)
ss -lntp | grep 15020
# 你大概率看到监听者是 pilot-agent(不是 envoy)

3) merge 端点(15020/stats/prometheus)是怎么“合并”的?

核心机制:文本级拼接(不是 metric 对象模型合并)

/stats/prometheus的处理逻辑本质是:

  1. 抓 Envoy 侧

    • agent 向 127.0.0.1:15090/stats/prometheus做一次 HTTP GET(或等价内部读取),拿到 Envoy stats 的 prometheus text

  2. 抓应用侧(如果启用 merge + 能找到应用的 metrics 端点)

    • agent 知道去哪抓应用指标,来源是:

      • meshConfig 启用了 enablePrometheusMerge=true(默认一般是 true)

      • 并且 Pod / 容器上有(或被 agent 写入了):

        annotations:
          prometheus.io/scrape: "true"
          prometheus.io/port: "9080"
          prometheus.io/path: "/metrics"
    • agent 访问:127.0.0.1:<app_port>/<app_path>拿应用的 prometheus text

  3. 拼接 + 冲突处理

    • 把两段 text 拼在一起

    • 对“应用端出现了 Istio 命名空间指标名”(典型如应用自己也叫 istio_requests_total,或应用恰好也跑了 Envoy 导致同名)做保护:

      • 通常做法:丢弃应用侧的同名 istio_系列*(或加前缀/标记),避免污染 Istio 的 control-plane 视角

  4. 输出 text/plain; version=0.0.4; charset=utf-8

官方口径也明确写了:

启用后,Envoy sidecar 将把 Istio 的指标与应用程序指标合并;合并后的指标将从 :15020/stats/prometheus​ 抓取。

不适合的情况包括:你的应用暴露了与 Istio 指标名称相同的指标(例如应用自身也暴露 istio_requests_total)。

所以它不是“两套 Prometheus 融合”

它是 pilot-agent 在 HTTP handler 里临时 fetch + cat,输出一段干净的 prometheus text,让 Prometheus 只需要一个 target 一个端点就能一次取全。


4) 你可以用三组命令“亲眼看到区别”

假设你的 Pod 有 app container :9080/metrics且 annotations 已就位:

# 进 Pod
kubectl exec -it <pod> -c istio-proxy -- sh

A. 纯 Envoy(15090)

curl -s http://127.0.0.1:15090/stats/prometheus | grep -E "^(#|istio_requests_total|envoy_)" | head -20

→ 只有 proxy 指标。

B. 合并出口(15020)

curl -s http://127.0.0.1:15020/stats/prometheus | grep -E "^(#|istio_requests_total|envoy_|your_app_metric_)" | head -40

→ 你会多出来 your_app_metric_...​ 这种东西(如果应用确实有 /metrics)。

C. 看 agent 是否认为 merge 开启/可用

# 看你 Pod 上的注解(这些是 agent 决定是否去抓应用的依据)
kubectl get pod <pod> -n <ns> -o jsonpath='{.spec.containers[?(@.name=="istio-proxy")].env}' | grep -i merge || true
kubectl get pod <pod> -n <ns> -o jsonpath='{.metadata.annotations}' | python3 -m json.tool | grep prometheus

5) 生产抓哪个端口?(抓错就会导致“缺指标/多指标/冲突”)

 

你想要的

应该 scrape

理由

只看 proxy 行为(流量/熔断/离群)

15090/stats/prometheus(通常要 proxyStatsBindHost:0.0.0.0或用 15020 当桥梁)

干净、最小面

proxy + 应用一起拿,且希望一个端点解决

15020/stats/prometheus

merge 设计点;但要防同名冲突

应用指标你自己另有 scrape job 抓应用 svc

只抓 15090

解耦更干净(Istio 与应用互不打扰)

如果你告诉我:

  1. istioctl version(或 Istio 版本)

  2. kubectl get pod <pod> -n <ns> -o jsonpath='{.metadata.annotations}'里 prometheus 相关注解

  3. meshConfig.enablePrometheusMerge现在是 true 还是 false

我可以把你这个 Pod 的 “15020/stats/prometheus 到底会去哪抓应用、为啥抓/为啥不抓”​ 按真实配置逐条推导出来(精确到它会访问 127.0.0.1:哪个port/path)。

image

 

这张图展示了 Kiali 图形化界面的“服务拓扑图”(Service Graph),它是 Istio 服务网格可视化的核心功能。

结合我们之前的讨论,这张图不仅仅是“画图”,它本质上是 Kiali 对 Prometheus 数据的实时翻译。以下是这张图的具体技术解读:

1. 核心元素与 Prometheus 的关系

  • 节点(方块):代表 Application(应用)

    • 例如图中的 productpagereviewsratings

    • 技术点:Kiali 通过读取 Kubernetes 的 Label(如 app=productpage)来识别这些节点。

  • 连线(箭头):代表 调用关系(Edges)

    • 箭头从调用方指向被调用方(例如 productpage-> reviews)。

    • 技术点:这些连线不是凭空画的。Kiali 会向 Prometheus 发送查询,寻找带有特定标签的请求指标(如 istio_requests_total),如果发现 A 服务有大量请求发往 B 服务,就会画出这条线。

  • 边的粗细:代表 TPS (Transactions Per Second)

    • 右侧面板显示 Total: 0.53,对应图中较细的灰线,说明当前流量很小(每秒约 0.5 个请求)。

  • 边的颜色:代表 HTTP 响应成功率

    • 绿色:100% 成功(右侧 %Success: 100.00)。

    • 红色:代表有错误(5xx)。

    • 黄色:代表重定向(3xx)。

2. 特殊节点:istio-ingressgateway

  • 左侧紫色的菱形节点是 Ingress Gateway

  • 它表明外部流量是从这里进入网格的。图中显示 *-> productpage,说明所有外部请求都先打到 productpage服务。

3. 图中的细节:版本聚合(V1, V2, V3)

  • 注意看 reviews节点,它旁边标有 v1, v2, v3

  • 虽然图中它们看起来像一个整体,但这实际上是 多个 Pod 的集合

  • 关联之前的知识点:如果你配置了灰度发布(VirtualService 权重),Kiali 会通过查询 Prometheus 中不同子集(Subset)的指标,在图上用不同颜色或标注来显示流量的分配比例。目前图中显示为统一的浅蓝色,说明可能所有版本都在接收流量,或者权重配置尚未生效/未区分颜色。

4. 如何验证数据来源?

你可以回想一下我们在上一轮提到的 Prometheus 抓取。图中右侧的实时数据(0.53 TPS, 100% Success),正是来源于 Prometheus 中类似以下的查询结果:

sum(rate(istio_requests_total{destination_service="reviews.bookinfo.svc.cluster.local", response_code!~"5.*"}[1m])) by (source_app, destination_app)

总结:

这张图目前展示了一个 健康(全绿)但低流量(0.53 TPS)​ 的 Bookinfo 应用状态。如果你想要看到更复杂的流量分配(比如红色的故障注入效果,或者不同粗细的灰度分流线),需要在 productpage页面刷新几次,或者调整 VirtualService 的配置,图中的线条粗细和颜色会随之动态变化。

image

 

想要自己手搓一个类似 Kiali 的服务拓扑图,这是一个非常棒的学习目标!这项技术通常被称为 Service Dependency Graph(服务依赖图谱)​ 或 Topology Map(拓扑地图)

要实现这个功能,不能只盯着前端画图,它的核心在于“数据驱动视图”。整个功能的实现可以分为三个步骤:底层数据采集后端指标计算前端可视化渲染

以下是为你拆解的实现逻辑和对应的技术栈建议:

第一步:底层数据采集(Data Collection)

就像我们之前聊的,拓扑图不是凭空画出来的,它依赖于 Metrics(指标)。

  • 数据源:你需要一个像 Prometheus 一样的时序数据库。

  • 抓取内容:你需要抓取服务间的调用指标。关键的指标通常是 requests_total(请求总数)或者 istio_requests_total

  • 标签(Labels):这是最重要的。指标必须包含以下标签,才能计算出拓扑关系:

    • source_service/ source_app:流量发起方。

    • destination_service/ destination_app:流量接收方。

    • response_code:响应码(用来算成功率,给连线变色)。

    • time:时间戳(用来算 QPS/TPS)。

第二步:后端逻辑计算(Graph Building)

前端不直接去查 Prometheus(太慢且频繁查询容易把 Prometheus 搞挂),而是由后端定时计算好,推给前端。

核心算法逻辑(伪代码思路):

  1. 获取所有服务列表:从 K8s API 获取当前的服务列表,或者在指标中发现有哪些服务。

  2. 构建节点(Nodes):每个服务就是一个节点。

  3. 构建连线(Edges)

    • 后端执行 PromQL 查询:sum(rate(requests_total[1m])) by (source_app, destination_app)

    • 遍历查询结果。

    • 如果发现 source_app=A调用了 destination_app=B,就在 A 和 B 之间画一条边。

  4. 计算边的权重和颜色

    • 粗细(Weight):取查询到的 QPS 数值,传给前端作为线的宽度。

    • 颜色(Color):判断 response_code的分布。如果是 2xx/3xx,设为绿色;如果是 5xx,设为红色;如果是 4xx,设为黄色。

第三步:前端可视化渲染(Visualization)

这是你最关心的部分。前端需要一个强大的图形库来接管 DOM 操作。

推荐技术栈

  • 框架React​ 或 Vue。现代可视化面板的主流选择。

  • 图表库(核心):强烈推荐 Cytoscape.js​ 。

    • 为什么选它:Kiali 底层其实也是用的类似的库(Cytoscape.js 是一个基于图论的高度可定制的可视化引擎)。它专门用来画这种复杂的网络关系图、拓扑图。它支持节点的拖拽、连线的弯曲、点击事件绑定等。

  • UI 组件库Ant Design​ 或 Material-UI,用于快速搭建左侧菜单、右上角的图例、时间范围选择器等外壳。

前端实现逻辑

  1. API 接口定义

    • 前端通过 axios向后端请求数据,例如 GET /api/topology?namespace=bookinfo

    • 后端返回一个 JSON 结构,包含 nodes数组和 edges数组。

    {
      "nodes": [
        {"id": "productpage", "name": "Product Page", "type": "app", "version": "v1"},
        {"id": "reviews", "name": "Reviews", "type": "app", "version": "v1"}
      ],
      "edges": [
        {
          "source": "productpage", 
          "target": "reviews", 
          "qps": 5.2, 
          "rate": "100%", 
          "color": "green"
        }
      ]
    }
  2. 图谱初始化(以 Cytoscape.js 为例)

    • 前端收到 JSON 后,将其转化为 Cytoscape 的 Elements 格式。

    • 配置样式(Style):告诉图谱如何渲染。

    const elements = [
      // 节点
      { data: { id: 'productpage', label: 'productpage' } },
      { data: { id: 'reviews', label: 'reviews' } },
    
      // 连线
      { 
        data: { 
          id: 'edge1', 
          source: 'productpage', 
          target: 'reviews',
          qps: 5.2 // 透传数据用于tooltip提示
        } 
      }
    ];
    
    const cy = cytoscape({
      container: document.getElementById('graph-container'), // 挂载点
      elements: elements,
      style: [ // 样式配置
        {
          selector: 'node',
          style: {
            'shape': 'rectangle', // 节点形状
            'background-color': '#007bff', // 默认颜色
            'label': 'data(label)' // 显示名称
          }
        },
        {
          selector: 'edge',
          style: {
            'width': 'data(qps)', // 宽度由 qps 决定
            'line-color': 'data(color)', // 颜色由 color 决定
            'target-arrow-color': 'data(color)', // 箭头颜色
            'target-arrow-shape': 'triangle', // 箭头形状
            'curve-style': 'bezier' // 连线弯曲
          }
        }
      ],
      layout: { name: 'cose' } // 布局算法,自动计算节点位置
    });
  3. 交互功能

    • Tooltip(悬浮提示):监听节点的 mouseover事件,弹出一个卡片显示该服务的 QPS、延迟、错误率。

    • 点击钻取:监听节点的 click事件,跳转到该服务的详情页。

    • 图例(Legend):如你图中右下角所示,用一个简单的 HTML 表格,绑定上面返回的统计数据(Total, %Success, %Error)。

总结你的开发路线图

  1. MVP 1.0(画静态图)

    • 先用假数据(Hardcode 一个 JSON)写一个 React + Cytoscape 的 Demo。

    • 搞定节点的显示和连线的粗细、颜色。

  2. MVP 2.0(接入真实数据)

    • 写一个简单的 Python/Go 后端。

    • 对接 Prometheus,解析出节点和边。

    • 前端定时(比如每 15 秒)轮询一次后端接口刷新图谱。

  3. MVP 3.0(完善体验)

    • 加上时间范围选择器。

    • 加上搜索框(Find/Hide 功能)。

    • 加上图例和统计面板。

只要打通了 Prometheus 查询指标 -> 后端解析成图结构 -> Cytoscape 渲染​ 这一套闭环,你就完全掌握这类微前端监控面板的核心原理了。

 

这是一个非常有深度的架构问题。从 Prometheus(指标)​ 切换到 Jaeger(追踪)​ 来绘制拓扑图,不仅仅是换了一个数据源,而是从“统计聚合思维”变成了“图遍历思维”

核心算法调整可以归纳为以下四个维度的根本性变化:

1. 数据模型的根本转变

 

维度

Prometheus (Metrics)

Jaeger (Tracing)

数据单元

时间序列点​ (Time-series points)

调用链​ (Trace / Span)

核心实体

Counter, Gauge, Histogram

Span (操作单元)

关系表达

隐式的(通过标签 source/dest推断)

显式的(Span 中有明确的 parentSpanID

时效性

准实时(抓取周期决定)

近实时(请求结束即上报)

2. 核心算法逻辑的调整

这是最关键的部分。原来的算法是“查询-统计”,现在的算法是“解析-构建”。

调整前:基于 Prometheus 的算法(统计法)

  1. 查询sum(rate(istio_requests_total[1m])) by (source_app, dest_app)

  2. 计算:得到一张二维表(A -> B, 10 QPS)。

  3. 绘图:把表中的行变成线。

调整后:基于 Jaeger 的算法(图构建法)

你需要实现一个 Dependency Graph Builder(依赖图构建器)

核心逻辑伪代码:

# 1. 从 Jaeger 获取最近 N 分钟的 Trace 数据 (通过 Jaeger Query API)
traces = fetch_traces_from_jaeger(start_time, end_time)

# 2. 初始化图结构 (可以使用 networkx 或自行维护 map)
graph = defaultdict(lambda: {"in": 0, "out": 0, "edges": set()})

# 3. 遍历每一个 Trace
for trace in traces:
    # 4. 遍历 Trace 中的每一个 Span
    for span in trace.spans:
        service = span.process.service_name
        
        # 5. 提取父子关系 (关键步骤)
        parent_span = find_parent(span)
        if parent_span:
            parent_service = parent_span.process.service_name
            
            # 6. 构建边 (Edge)
            edge_key = f"{parent_service}->{service}"
            
            # 7. 统计权重 (不再是 QPS,而是调用次数 Call Count)
            graph[edge_key]["call_count"] += 1
            
            # 8. 统计错误率 (通过 Span Tag 判断)
            if span.tags.get("error") == "true":
                graph[edge_key]["error_count"] += 1
                
            # 9. 统计延迟 (P99, P95)
            duration = span.duration_ms
            graph[edge_key]["latency_sum"] += duration

3. 关键的技术挑战与解决方案

 

挑战

原因

解决方案

数据量级爆炸

一个 Trace 可能包含几十上百个 Span,全量存储成本高。

采样(Sampling):只分析 1% 或 10% 的 Trace 数据来构建拓扑。

实时性 vs 准确性

拓扑图需要实时更新,但 Trace 数据量大,全量计算慢。

流式计算(Streaming):使用 Flink/Spark Streaming 消费 Jaeger 的 Kafka 数据流,实时更新图结构。

孤儿 Span

有些 Span 可能没有 Parent ID,导致断链。

服务映射(Service Mapping):如果找不到父 Span,尝试通过 peer.servicedb.instance标签推断依赖。

聚合粒度

一个服务可能调用下游很多个接口,拓扑图会太乱。

服务级聚合:忽略具体的 Operation Name(如 /api/v1/get),只保留 Service 级别的调用关系。

4. 前端渲染逻辑的微调

前端(如 Cytoscape.js)的代码基本不用改,但传入的数据含义变了:

  • 边的粗细(Width)

    • 原来是 QPS(每秒请求数)。

    • 现在是 Call Count(总调用次数)或 Calls per Minute

  • 边的颜色(Color)

    • 原来是 HTTP Status Code(2xx/5xx)。

    • 现在是 Error Rate(错误 Span 数 / 总 Span 数)或 Latency(P99 延迟)。

  • 节点大小(Size)

    • 可以根据该服务产生的 Span 总数来决定节点大小,表示流量中心度。

5. 推荐的架构实现(生产级)

如果你想做一个健壮的系统,建议采用 双数据源互补​ 的方案:

  1. Jaeger 负责“骨架”:用 Jaeger 的数据来确定 “谁调用了谁”(拓扑结构)。因为追踪数据是 100% 准确的调用关系。

  2. Prometheus 负责“血肉”:用 Prometheus 的数据来确定 “调用有多快/多频繁”(QPS、CPU、内存、成功率)。因为指标数据聚合效率高,适合做实时的仪表盘。

开发建议:

如果你现在要动手开发,可以先用 Jaeger 的 dependenciesAPI(如果开启了 Spark Dependencies Job)。Jaeger 本身自带一个后台任务,会定时从 Trace 中计算依赖关系,并提供一个简单的 API 接口,你可以直接调用这个接口来获取现成的拓扑数据,省去自己写图构建算法的麻烦。

总结:

从 Prometheus 切换到 Jaeger,你的算法重心要从 “写 PromQL 查询”​ 转变为 “写图遍历和聚合逻辑”。你需要处理的是离散的事件流,而不是连续的时序流。

 

posted on 2026-05-22 16:40  luzhouxiaoshuai  阅读(13)  评论(0)    收藏  举报

导航