Istio网关部署与服务网格指南

Istio 网关部署与服务网格完整指南


📋 文档说明

需要修改的配置项标记:

  • 【修改】 - 必须根据实际环境修改的配置
  • 【可选】 - 可根据需求选择性修改的配置

环境说明:

  • 本地环境:用于开发测试,资源配置较小
  • 正式环境:生产环境,需要高可用配置

目录

  1. Istio 与服务网格概述
  2. Istio 网关深度解析
  3. Istio vs Nginx vs Kubernetes Ingress
  4. 边车模式(Sidecar)原理
  5. 核心概念与流量路由详解 ⭐ 新增
  6. 完整的请求流程详解(从浏览器到应用) ⭐⭐⭐ 超级重点
  7. Kubernetes 最佳实践与配置
  8. 常见误区与注意事项
  9. 方案一:Docker 环境部署 Istio
  10. 方案二:Kubernetes 环境部署 Istio
  11. Istio 网关配置(详细注释版)
  12. 外部授权器(External Authorizer)
  13. OAuth2 单点登录:集成 Ory Hydra
  14. 本地环境与正式环境配置对比
  15. 常见问题与故障排查
  16. 高级特性
  17. 清理与卸载
  18. 参考资源
  19. 快速检查清单
  20. 配置参数速查手册 ⭐ 新增
  21. 配置模板库 ⭐ 新增

1. Istio 与服务网格概述

1.1 什么是服务网格(Service Mesh)

服务网格是一个专门用于处理服务间通信的基础设施层,它将服务间的通信逻辑从业务代码中解耦出来。

核心功能:

  • 流量管理:路由、负载均衡、故障恢复、灰度发布
  • 安全:服务间加密通信(mTLS)、身份认证、授权
  • 可观测性:指标收集、分布式追踪、日志聚合
  • 策略执行:访问控制、速率限制、配额管理

1.2 Istio 架构组件

┌─────────────────────────────────────────────────────────┐
│                    控制平面 (Control Plane)              │
│  ┌──────────────────────────────────────────────────┐   │
│  │              Istiod (Pilot + Citadel + Galley)  │   │
│  │  - 服务发现                                       │   │
│  │  - 配置分发                                       │   │
│  │  - 证书管理                                       │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
                           ↓ 配置下发
┌─────────────────────────────────────────────────────────┐
│                    数据平面 (Data Plane)                 │
│  ┌────────────┐    ┌────────────┐    ┌────────────┐    │
│  │  Service A │    │  Service B │    │  Service C │    │
│  │  ┌──────┐  │    │  ┌──────┐  │    │  ┌──────┐  │    │
│  │  │ App  │  │    │  │ App  │  │    │  │ App  │  │    │
│  │  └───┬──┘  │    │  └───┬──┘  │    │  └───┬──┘  │    │
│  │  ┌───▼──┐  │    │  ┌───▼──┐  │    │  ┌───▼──┐  │    │
│  │  │Envoy │←─┼────┼─→│Envoy │←─┼────┼─→│Envoy │  │    │
│  │  │Proxy │  │    │  │Proxy │  │    │  │Proxy │  │    │
│  │  └──────┘  │    │  └──────┘  │    │  └──────┘  │    │
│  └────────────┘    └────────────┘    └────────────┘    │
└─────────────────────────────────────────────────────────┘

组件说明:

  • Istiod:统一的控制平面,负责配置管理、服务发现、证书颁发
  • Envoy Proxy:高性能代理,作为 Sidecar 部署在每个服务旁边
  • Ingress Gateway:入口网关,处理进入网格的流量
  • Egress Gateway:出口网关,处理离开网格的流量

2. Istio 网关深度解析

2.1 Istio 网关的核心优势

2.1.1 与传统网关的本质区别

传统网关(如 Nginx)是集中式架构,而 Istio 网关是分布式架构:

传统架构(Nginx):
外部请求 → Nginx(单点) → Service A
                          → Service B
                          → Service C

Istio 架构:
外部请求 → Istio Gateway → Service A + Envoy Sidecar
                        → Service B + Envoy Sidecar
                        → Service C + Envoy Sidecar
                        ↓
              每个服务都有独立的流量管理能力
2.1.2 十大核心优势

1. 无侵入性(Zero Code Change)

  • ✅ 应用代码无需修改
  • ✅ 通过 Sidecar 注入透明代理流量
  • ✅ 不依赖特定编程语言或框架
  • ❌ 传统方案:需要在代码中集成 SDK(如 Spring Cloud、Dubbo)

2. 统一流量管理

  • 入站流量(Ingress Gateway)
  • 出站流量(Egress Gateway)
  • 服务间流量(Sidecar to Sidecar)
  • 跨集群流量(Multi-Cluster)

3. 高级路由能力

# 示例:基于权重的金丝雀发布
- route:
  - destination:
      host: product-service
      subset: v1
    weight: 90
  - destination:
      host: product-service
      subset: v2
    weight: 10

# 示例:基于 Header 的路由
- match:
  - headers:
      user-agent:
        regex: ".*Mobile.*"
  route:
  - destination:
      host: mobile-service

# 示例:基于地理位置的路由
- match:
  - sourceLabels:
      region: us-west
  route:
  - destination:
      host: product-service
      subset: us-west-pool

4. 自动服务发现

  • 与 Kubernetes 原生集成
  • 自动感知服务上下线
  • 无需手动配置上游服务列表
  • 支持多注册中心(Consul、Eureka 等)

5. 内置安全能力

  • 自动 mTLS:服务间通信自动加密,无需配置证书
  • 证书自动轮换:避免证书过期风险
  • 身份认证:基于 SPIFFE 标准
  • 授权策略:细粒度访问控制(L7 层)

6. 可观测性(三大支柱)

  • Metrics(指标):自动收集延迟、流量、错误率、饱和度
  • Tracing(追踪):自动生成分布式追踪(无需代码埋点)
  • Logging(日志):统一访问日志格式

7. 弹性能力

  • 超时控制:防止级联超时
  • 重试机制:智能重试(幂等性检测)
  • 熔断器:快速失败,保护下游
  • 限流:全局/局部速率限制

8. 多集群与多云支持

  • 跨 Kubernetes 集群通信
  • 跨云服务商(AWS、Azure、GCP、阿里云)
  • 统一管理混合云环境

9. 渐进式交付

  • 金丝雀发布:按比例分配流量
  • 蓝绿部署:瞬间切换流量
  • A/B 测试:基于用户特征路由
  • 流量镜像:影子测试(不影响生产)

10. 策略执行与扩展性

  • 集成外部授权服务(OAuth2、OPA)
  • 自定义 Envoy Filter
  • WebAssembly(Wasm)插件扩展
  • Lua 脚本支持

2.2 适用场景

2.2.1 最适合 Istio 的场景

✅ 强烈推荐使用 Istio:

  1. 微服务架构

    • 服务数量 > 10 个
    • 服务间调用复杂
    • 需要统一流量管理
  2. 多语言技术栈

    • Java、Go、Python、Node.js 混合
    • 无法统一使用 SDK
    • 需要统一可观测性
  3. 需要零信任安全

    • 服务间自动加密(mTLS)
    • 细粒度访问控制
    • 合规性要求(GDPR、HIPAA)
  4. 需要高级流量管理

    • 金丝雀发布、蓝绿部署
    • A/B 测试
    • 流量镜像
  5. 多集群/多云环境

    • 跨 Kubernetes 集群
    • 混合云(公有云 + 私有云)
    • 多区域部署
  6. 需要完整可观测性

    • 统一指标收集
    • 分布式追踪
    • 服务依赖图
2.2.2 不适合 Istio 的场景

❌ 不推荐使用 Istio:

  1. 简单应用

    • 单体应用或 < 5 个微服务
    • 流量管理需求简单
    • 使用 Nginx/Ingress 足够
  2. 资源受限环境

    • 集群资源 < 4GB 内存
    • Sidecar 会消耗额外资源(每个 Pod 增加 50-100MB)
    • 边缘计算、IoT 设备
  3. 超低延迟要求

    • Sidecar 增加 1-5ms 延迟
    • 金融交易系统(毫秒级要求)
    • 实时竞价系统
  4. 团队能力不足

    • 团队不熟悉 Kubernetes
    • 缺乏云原生经验
    • 学习成本高
  5. 非 Kubernetes 环境

    • 虚拟机为主的环境(可用 Istio VM 支持,但复杂)
    • 传统数据中心
    • Serverless 环境(部分适用)

2.3 Istio 网关内部组件详解

2.3.1 控制平面:Istiod

Istiod 是 Istio 1.5+ 版本的统一控制平面,整合了以前的 Pilot、Citadel、Galley:

┌─────────────────────────────────────────────────────┐
│                      Istiod                         │
│                                                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────┐ │
│  │   Pilot      │  │   Citadel    │  │  Galley  │ │
│  │  (流量控制)   │  │  (证书管理)   │  │ (配置验证)│ │
│  └──────┬───────┘  └──────┬───────┘  └─────┬────┘ │
│         │                  │                 │      │
└─────────┼──────────────────┼─────────────────┼──────┘
          ↓                  ↓                 ↓
     配置分发           证书颁发           配置校验
          ↓                  ↓                 ↓
    Envoy Proxy         Envoy Proxy      K8s API

Pilot 功能:

  • 服务发现:从 Kubernetes 获取 Service 和 Endpoint 信息
  • 流量管理:将 VirtualService、DestinationRule 转换为 Envoy 配置
  • 配置分发:通过 xDS 协议推送配置到 Envoy

Citadel 功能:

  • 证书颁发:基于 SPIFFE 标准生成 X.509 证书
  • 证书轮换:自动更新证书(默认 24 小时)
  • 密钥管理:管理根证书和中间证书

Galley 功能:

  • 配置验证:验证 Istio CRD 的语法和语义
  • 配置隔离:屏蔽 Kubernetes API 细节
  • 配置转换:将多种格式转换为统一格式
2.3.2 数据平面:Envoy Proxy

Envoy 是用 C++ 编写的高性能代理:

核心特性:

┌─────────────────────────────────────────────────┐
│              Envoy Proxy 架构                   │
│                                                 │
│  ┌─────────────┐  ┌─────────────┐             │
│  │  Listeners  │  │  Clusters   │             │
│  │  (监听器)    │  │  (上游集群) │             │
│  └──────┬──────┘  └──────┬──────┘             │
│         │                 │                     │
│         ↓                 ↓                     │
│  ┌─────────────────────────────┐               │
│  │      Filter Chains          │               │
│  │  ┌────────┐  ┌────────┐    │               │
│  │  │  HTTP  │  │  TCP   │    │               │
│  │  │ Router │  │ Proxy  │    │               │
│  │  └────────┘  └────────┘    │               │
│  │  ┌────────┐  ┌────────┐    │               │
│  │  │  Authz │  │  Rate  │    │               │
│  │  │ Filter │  │ Limit  │    │               │
│  │  └────────┘  └────────┘    │               │
│  └─────────────────────────────┘               │
└─────────────────────────────────────────────────┘

Envoy 的四大配置概念(xDS):

  1. LDS(Listener Discovery Service)

    • 定义监听端口和协议
    • 例如:监听 15006(入站)、15001(出站)
  2. RDS(Route Discovery Service)

    • 定义 HTTP 路由规则
    • 匹配 Header、URI、Method 等
  3. CDS(Cluster Discovery Service)

    • 定义上游服务集群
    • 配置负载均衡、健康检查
  4. EDS(Endpoint Discovery Service)

    • 定义集群的具体 Endpoint(Pod IP)
    • 动态更新服务实例

Envoy Filter 链路:

请求流向:
Client 
  ↓
Listener (15006/15001)
  ↓
Network Filter (TLS Inspector, HTTP Inspector)
  ↓
HTTP Connection Manager
  ↓
HTTP Filters:
  1. JWT Authentication Filter (JWT 验证)
  2. External Authorization Filter (外部鉴权)
  3. RBAC Filter (访问控制)
  4. CORS Filter (跨域处理)
  5. Fault Injection Filter (故障注入)
  6. Rate Limit Filter (限流)
  7. Router Filter (路由到上游)
  ↓
Upstream Cluster (目标服务)
2.3.3 Ingress Gateway 组件

Istio Ingress Gateway 本质上就是一个独立部署的 Envoy Proxy

# Ingress Gateway 实际上是一个 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-ingressgateway
  namespace: istio-system
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: istio-proxy
          image: docker.io/istio/proxyv2:1.20.1
          # 这就是 Envoy Proxy

与 Sidecar Envoy 的区别:

特性Sidecar EnvoyIngress Gateway Envoy
部署位置每个 Pod 内独立 Deployment
用途服务间通信外部流量入口
配置来源IstiodGateway + VirtualService
监听端口15001/1500680/443(可自定义)
数量与服务数量相同1-10 个(高可用)
2.3.4 Gateway 资源(CRD)

Gateway 是 Istio 的自定义资源,定义网关监听规则:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-gateway
spec:
  selector:
    istio: ingressgateway  # 选择哪个 Gateway Pod
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*.example.com"

Gateway 资源的作用:

  1. 定义监听端口(80、443)
  2. 定义协议(HTTP、HTTPS、TCP、TLS)
  3. 配置 TLS 证书
  4. 定义接受的域名
2.3.5 VirtualService 资源(CRD)

VirtualService 定义路由规则:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-routes
spec:
  hosts:
    - "product.example.com"
  gateways:
    - my-gateway  # 关联 Gateway
  http:
    - match:
        - uri:
            prefix: "/api/v1"
      route:
        - destination:
            host: product-service-v1
    - match:
        - uri:
            prefix: "/api/v2"
      route:
        - destination:
            host: product-service-v2
2.3.6 DestinationRule 资源(CRD)

DestinationRule 定义服务级别策略:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: product-destination
spec:
  host: product-service
  trafficPolicy:
    loadBalancer:
      simple: LEAST_REQUEST  # 负载均衡算法
    connectionPool:
      tcp:
        maxConnections: 100  # 连接池
    outlierDetection:
      consecutiveErrors: 5  # 熔断阈值
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

2.4 Istio 网关实现原理

2.4.1 请求处理全流程
1. 客户端发起请求
   ↓
   https://product.example.com/api/products
   ↓
2. DNS 解析到 LoadBalancer IP
   ↓
   (云服务商 LB: 120.55.xxx.xxx)
   ↓
3. 流量到达 Istio Ingress Gateway Service
   ↓
   kubectl get svc -n istio-system istio-ingressgateway
   TYPE=LoadBalancer EXTERNAL-IP=120.55.xxx.xxx
   ↓
4. Service 转发到 Ingress Gateway Pod
   ↓
   Envoy Proxy (istio-ingressgateway-xxx)
   ↓
5. Envoy 匹配 Gateway 资源
   ↓
   检查 hosts: "product.example.com" ✓
   检查 port: 443 ✓
   检查 TLS 证书 ✓
   ↓
6. Envoy 匹配 VirtualService 资源
   ↓
   检查 hosts: "product.example.com" ✓
   匹配路由规则: prefix: "/api/products" ✓
   ↓
7. 查找目标 Service
   ↓
   destination.host: product-service.product-ns.svc.cluster.local
   ↓
8. 从 Istiod 获取 Endpoint 列表
   ↓
   EDS 返回:
   - 10.244.1.5:8080 (product-service-v1-pod-1)
   - 10.244.2.3:8080 (product-service-v1-pod-2)
   ↓
9. 应用 DestinationRule 策略
   ↓
   负载均衡算法: LEAST_REQUEST
   连接池限制: maxConnections=100
   熔断检测: consecutiveErrors=5
   ↓
10. 选择目标 Pod
    ↓
    选中:10.244.1.5:8080
    ↓
11. 建立连接到目标 Pod
    ↓
    TLS 握手(mTLS)
    ↓
12. 请求到达目标 Pod 的 Sidecar Envoy
    ↓
    入站监听器: 15006
    ↓
13. Sidecar Envoy 执行入站过滤器链
    ↓
    - mTLS 验证 ✓
    - RBAC 检查 ✓
    - Rate Limit 检查 ✓
    ↓
14. 转发到应用容器
    ↓
    localhost:8080 (product-service 应用)
    ↓
15. 应用处理业务逻辑
    ↓
16. 返回响应(原路返回)
    ↓
17. 收集遥测数据
    ↓
    - Metrics: istio_request_duration_milliseconds
    - Tracing: Span 发送到 Jaeger
    - Logging: Access Log 记录
2.4.2 配置热更新机制

Istio 支持无需重启的配置热更新:

1. 用户提交配置变更
   ↓
   kubectl apply -f virtualservice.yaml
   ↓
2. Kubernetes API Server 接收
   ↓
3. Istiod Watch 到变更
   ↓
   (Informer 机制)
   ↓
4. Istiod 验证配置
   ↓
   Galley 验证语法和语义
   ↓
5. Istiod 转换配置
   ↓
   Istio CRD → Envoy xDS 格式
   ↓
6. Istiod 推送配置到 Envoy
   ↓
   通过 gRPC 流(xDS Protocol)
   ↓
7. Envoy 接收配置
   ↓
   LDS/RDS/CDS/EDS 更新
   ↓
8. Envoy 热更新路由
   ↓
   无需重启,零停机时间
   ↓
9. 新配置立即生效
   ↓
   (通常 < 1 秒)

验证配置推送:

# 查看 Envoy 配置同步状态
istioctl proxy-status

# 输出示例:
NAME                                    CDS      LDS      EDS      RDS      ISTIOD
istio-ingressgateway-xxx                SYNCED   SYNCED   SYNCED   SYNCED   istiod-xxx
product-service-v1-xxx                  SYNCED   SYNCED   SYNCED   SYNCED   istiod-xxx

# 查看具体的 Envoy 配置
istioctl proxy-config routes product-service-v1-xxx -o json
2.4.3 mTLS 加密通信原理

Istio 自动实现服务间双向 TLS 加密:

1. Pod 启动
   ↓
2. Sidecar Envoy 启动
   ↓
3. Envoy 请求证书
   ↓
   向 Istiod 发送 CSR (Certificate Signing Request)
   身份标识: spiffe://cluster.local/ns/product-ns/sa/product-sa
   ↓
4. Istiod (Citadel) 签发证书
   ↓
   验证 Pod 身份(基于 ServiceAccount Token)
   签发 X.509 证书(有效期 24 小时)
   ↓
5. Envoy 接收证书
   ↓
   私钥: /etc/certs/key.pem
   证书: /etc/certs/cert-chain.pem
   根证书: /etc/certs/root-cert.pem
   ↓
6. Service A 调用 Service B
   ↓
7. Service A 的 Envoy 发起 mTLS 连接
   ↓
   ClientHello + Client Certificate
   ↓
8. Service B 的 Envoy 验证证书
   ↓
   验证证书链
   验证 SPIFFE ID
   检查证书有效期
   ↓
9. 建立 TLS 连接
   ↓
10. 加密通信
    ↓
11. 证书自动轮换
    ↓
    Envoy 在证书过期前自动请求新证书
    无需重启服务

查看证书:

# 查看 Pod 的证书信息
kubectl exec product-service-xxx -c istio-proxy -- \
  cat /etc/certs/cert-chain.pem | openssl x509 -text -noout

# 查看 SPIFFE ID
kubectl exec product-service-xxx -c istio-proxy -- \
  cat /etc/certs/cert-chain.pem | openssl x509 -noout -text | grep "URI:"

# 输出:
# URI:spiffe://cluster.local/ns/product-system/sa/product-service-sa

3. Istio vs Nginx vs Kubernetes Ingress

3.1 三者对比总览

维度NginxKubernetes IngressIstio Gateway
架构模式集中式代理集中式代理分布式网格
部署方式单独部署Ingress ControllerGateway + Sidecar
配置方式nginx.confIngress YAMLGateway + VirtualService
流量管理基本路由基本路由高级路由(金丝雀、镜像)
负载均衡需手动配置自动(基于 Service)智能负载均衡
服务发现需手动配置自动(K8s Service)自动 + 动态
mTLS需手动配置证书需手动配置证书自动 mTLS
可观测性需集成插件基本指标自动追踪/指标
多协议支持HTTP/TCP/UDPHTTP/HTTPSHTTP/HTTPS/TCP/gRPC
跨集群支持不支持不支持原生支持
学习曲线
性能开销中(Sidecar 额外开销)
适用场景单体/简单微服务中小型 K8s 应用大规模微服务

3.2 详细对比

3.2.1 架构对比

Nginx 架构:

外部流量
   ↓
Nginx Pod (集中式)
   ├─→ Service A Pod
   ├─→ Service B Pod
   └─→ Service C Pod

优点:
- 简单直接
- 性能高
- 配置灵活

缺点:
- 单点故障风险
- 扩展性差
- 服务间流量无法管理

Kubernetes Ingress 架构:

外部流量
   ↓
Ingress Controller (Nginx/Traefik/HAProxy)
   ↓
K8s Service
   ├─→ Pod A
   ├─→ Pod B
   └─→ Pod C

优点:
- K8s 原生支持
- 标准化配置
- 多种实现可选

缺点:
- 功能受限(仅 L7 路由)
- 不管理服务间流量
- 高级功能需要 Annotation

Istio Gateway 架构:

外部流量
   ↓
Istio Ingress Gateway
   ↓
Service A Pod + Sidecar Envoy
   ↓
Service B Pod + Sidecar Envoy
   ↓
Service C Pod + Sidecar Envoy

优点:
- 全链路流量管理
- 自动 mTLS
- 统一可观测性
- 高级路由能力

缺点:
- 复杂度高
- 资源开销大
- 学习成本高
3.2.2 配置复杂度对比

场景:实现金丝雀发布(90% v1, 10% v2)

Nginx 配置:

upstream product_service {
    server product-v1-1:8080 weight=90;
    server product-v1-2:8080 weight=90;
    server product-v2-1:8080 weight=10;
}

server {
    listen 80;
    server_name product.example.com;
    
    location /api/products {
        proxy_pass http://product_service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

❌ 问题:

  • 需要手动维护 Pod IP 列表
  • Pod 重启后 IP 变化需要手动更新
  • 无法动态调整权重

Kubernetes Ingress 配置:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: product-ingress
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
    - host: product.example.com
      http:
        paths:
          - path: /api/products
            pathType: Prefix
            backend:
              service:
                name: product-v1
                port:
                  number: 8080

⚠️ 问题:

  • 需要创建两个 Ingress(主 Ingress + Canary Ingress)
  • 依赖 Ingress Controller 的 Annotation(不标准)
  • 功能受限于 Controller 实现

Istio Gateway 配置:

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

✅ 优点:

  • 声明式配置
  • 自动服务发现
  • 实时调整权重(无需重启)
  • 支持更复杂的路由规则
3.2.3 安全能力对比
功能NginxK8s IngressIstio
TLS 终止✅ 手动配置✅ 手动配置✅ 自动配置
服务间加密❌ 不支持❌ 不支持✅ 自动 mTLS
证书管理手动Cert-Manager自动轮换
身份认证Basic AuthBasic AuthJWT/OIDC
授权策略IP 白名单NetworkPolicy细粒度 RBAC
审计日志Access LogAccess Log完整追踪
3.2.4 可观测性对比

Nginx:

# Access Log(需要手动配置)
log_format json_combined escape=json
'{
    "time_local": "$time_local",
    "remote_addr": "$remote_addr",
    "request": "$request",
    "status": $status,
    "body_bytes_sent": $body_bytes_sent,
    "request_time": $request_time
}';

access_log /var/log/nginx/access.log json_combined;

❌ 需要手动集成 Prometheus Exporter、Jaeger 等

Kubernetes Ingress:

# 基本指标(取决于 Controller)
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "10254"

⚠️ 仅有 Ingress Controller 的指标,无服务间追踪

Istio:

# 无需配置,自动提供:
# 1. Metrics(自动收集)
#    - istio_requests_total
#    - istio_request_duration_milliseconds
#    - istio_request_bytes
# 2. Tracing(自动生成 Span)
#    - 分布式追踪链路
# 3. Logging(统一格式)
#    - 访问日志自动记录

✅ 开箱即用的完整可观测性

3.3 性能对比

3.3.1 延迟测试

测试场景:1000 RPS,响应时间 100ms

方案P50 延迟P95 延迟P99 延迟CPU 使用内存使用
直连(无代理)100ms102ms105ms50m256Mi
Nginx101ms104ms108ms100m50Mi
Ingress (Nginx)102ms105ms110ms100m50Mi
Istio (Sidecar)103ms108ms115ms150m128Mi

结论:

  • Istio 增加 1-5ms 延迟(Sidecar 处理开销)
  • 每个 Sidecar 增加约 50-100MB 内存
  • 对于大多数业务场景,这个开销是可接受的
3.3.2 吞吐量测试

测试场景:简单 HTTP 请求

方案QPS资源消耗
Nginx100,000+
Istio80,000+

结论:

  • Istio 吞吐量略低于 Nginx(约 80%)
  • 但提供了远超 Nginx 的功能

3.4 选型建议

3.4.1 选择 Nginx 的场景

✅ 适合:

  • 单体应用或简单微服务
  • 对性能要求极高(毫秒级)
  • 团队熟悉 Nginx 配置
  • 预算有限(资源受限)

示例场景:

  • 静态网站
  • 简单的反向代理
  • API 网关(< 10 个后端服务)
3.4.2 选择 Kubernetes Ingress 的场景

✅ 适合:

  • 中小型 Kubernetes 应用
  • 只需要简单的 HTTP/HTTPS 路由
  • 不需要服务间流量管理
  • 快速上手 Kubernetes

示例场景:

  • 企业内部应用
  • SaaS 应用(< 20 个微服务)
  • 博客/CMS 系统
3.4.3 选择 Istio Gateway 的场景

✅ 适合:

  • 大规模微服务(> 10 个服务)
  • 需要金丝雀发布、A/B 测试
  • 需要零信任安全(mTLS)
  • 需要完整可观测性
  • 多集群/多云环境

示例场景:

  • 电商平台(订单、支付、库存等多个服务)
  • 金融科技平台
  • SaaS 平台(多租户)
  • 企业级应用

3.5 混合使用方案

方案一:Nginx + Istio(推荐)
外部流量
   ↓
Nginx (边缘负载均衡器)
   ├─ WAF 防护
   ├─ DDoS 防护
   └─ TLS 终止
   ↓
Istio Ingress Gateway
   ↓
服务网格内部

优点:

  • Nginx 处理边缘安全
  • Istio 管理服务网格
  • 各司其职

配置示例:

# Nginx 配置(边缘)
upstream istio_gateway {
    server istio-ingressgateway:80;
}

server {
    listen 443 ssl;
    server_name *.example.com;
    
    ssl_certificate /etc/nginx/certs/cert.pem;
    ssl_certificate_key /etc/nginx/certs/key.pem;
    
    # WAF 规则
    include /etc/nginx/waf.conf;
    
    location / {
        proxy_pass http://istio_gateway;
    }
}
方案二:Ingress + Istio(Kubernetes 内部)
外部流量
   ↓
Kubernetes Ingress (公网入口)
   ↓
Istio Gateway (服务网格入口)
   ↓
服务网格内部

适用场景:

  • 已有 Ingress Controller
  • 逐步迁移到 Istio

4. 边车模式(Sidecar)原理

2.1 什么是 Sidecar

Sidecar(边车)是一种容器设计模式,在主应用容器旁边运行一个辅助容器,两者共享相同的网络命名空间和存储卷。

类比理解:
就像摩托车旁边的边车,主车(应用)负责业务逻辑,边车(Envoy Proxy)负责处理网络流量。

2.2 Sidecar 工作原理

┌──────────────────────────────────────────────────────┐
│                      Pod                             │
│                                                      │
│  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │   Application       │  │   Envoy Sidecar     │  │
│  │   Container         │  │   Container         │  │
│  │                     │  │                     │  │
│  │  业务逻辑处理        │  │  流量拦截与代理      │  │
│  │  localhost:8080     │  │  - 入站流量         │  │
│  └──────────┬──────────┘  │  - 出站流量         │  │
│             │              │  - 加密/解密        │  │
│             │              │  - 监控/追踪        │  │
│  ┌──────────▼──────────────▼─────────────────┐   │
│  │      共享网络命名空间 (localhost)          │   │
│  └────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────┘
              ↓                         ↑
        出站流量                    入站流量

2.3 流量拦截机制

Istio 通过 iptables 规则实现流量透明拦截:

  1. 入站流量:外部请求 → Envoy (15006) → 应用容器
  2. 出站流量:应用容器 → Envoy (15001) → 外部服务
# Sidecar 注入后的 iptables 规则示例
# 拦截所有出站流量到 Envoy 15001 端口
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-port 15001

# 拦截所有入站流量到 Envoy 15006 端口
iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-port 15006

2.4 Sidecar 注入方式

2.4.1 什么是"自动注入"?

自动注入(Auto Injection) 是指 Istio 自动在你的应用 Pod 中注入(添加)一个 Envoy Sidecar 容器的过程。

简单理解:

原本的 Pod(1个容器)        自动注入后的 Pod(2个容器)
┌──────────────────┐         ┌──────────────────────────────┐
│   Pod            │         │   Pod                        │
│  ┌───────────┐   │         │  ┌───────────┐  ┌─────────┐ │
│  │           │   │         │  │           │  │         │ │
│  │   应用    │   │  →      │  │   应用    │  │ Envoy   │ │
│  │   容器    │   │         │  │   容器    │  │ Sidecar │ │
│  │           │   │         │  │           │  │  容器   │ │
│  └───────────┘   │         │  └───────────┘  └─────────┘ │
└──────────────────┘         └──────────────────────────────┘
    你写的 YAML                  Istio 自动修改后的 YAML

注入的是什么?

Istio 会自动添加:

  1. istio-proxy 容器(Envoy Sidecar)
  2. istio-init 初始化容器(配置 iptables 规则)
  3. 卷挂载(证书、配置等)
  4. 环境变量(Istio 配置)

具体对比:

# ============================================================
# 你编写的原始 Deployment(没有 Sidecar)
# ============================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: product
  template:
    metadata:
      labels:
        app: product
    spec:
      containers:
        - name: product-service
          image: my-product:v1
          ports:
            - containerPort: 8080

# 这个 YAML 只定义了 1 个容器
# ============================================================
# Istio 自动注入后的实际 Pod(有 Sidecar)
# ============================================================
apiVersion: v1
kind: Pod
metadata:
  name: product-service-xxx
  namespace: default
  labels:
    app: product
  annotations:
    sidecar.istio.io/status: '{"version":"...", "initContainers":["istio-init"], "containers":["istio-proxy"]}'
spec:
  # 初始化容器:设置 iptables 规则,拦截流量
  initContainers:
    - name: istio-init
      image: docker.io/istio/proxyv2:1.20.1
      command: ['istio-iptables', '-p', '15001', '-z', '15006', ...]
      # 作用:配置 iptables 规则
      #   - 拦截所有出站流量 → 转发到 Envoy 15001 端口
      #   - 拦截所有入站流量 → 转发到 Envoy 15006 端口
      securityContext:
        capabilities:
          add: ["NET_ADMIN", "NET_RAW"]  # 需要网络管理权限
  
  # 应用容器
  containers:
    # 你原来的应用容器
    - name: product-service
      image: my-product:v1
      ports:
        - containerPort: 8080
    
    # Istio 自动添加的 Sidecar 容器
    - name: istio-proxy
      image: docker.io/istio/proxyv2:1.20.1
      args:
        - proxy
        - sidecar
        - --domain
        - $(POD_NAMESPACE).svc.cluster.local
        - --configPath
        - /etc/istio/proxy
        - --binaryPath
        - /usr/local/bin/envoy
        - --serviceCluster
        - product-service.$(POD_NAMESPACE)
        - --drainDuration
        - 45s
        - --parentShutdownDuration
        - 1m0s
        - --discoveryAddress
        - istiod.istio-system.svc:15012  # 连接到 Istiod
        - --proxyLogLevel=warning
        - --proxyComponentLogLevel=misc:error
      env:
        - name: ISTIO_META_DNS_CAPTURE
          value: "true"
        - name: PROXY_CONFIG
          value: |
            {"proxyMetadata":{"DNS_AGENT":""}}
        # ... 更多环境变量
      ports:
        - containerPort: 15090  # Prometheus 指标端口
          protocol: TCP
          name: http-envoy-prom
        - containerPort: 15001  # Envoy 出站端口
        - containerPort: 15006  # Envoy 入站端口
        - containerPort: 15020  # Envoy admin 端口
      volumeMounts:
        - name: istio-envoy
          mountPath: /etc/istio/proxy
        - name: istio-data
          mountPath: /var/lib/istio/data
        - name: istio-podinfo
          mountPath: /etc/istio/pod
        - name: istio-token
          mountPath: /var/run/secrets/tokens
        - name: istiod-ca-cert
          mountPath: /var/run/secrets/istio
      resources:
        requests:
          cpu: 100m
          memory: 128Mi
        limits:
          cpu: 2000m
          memory: 1Gi
  
  # 卷定义
  volumes:
    - name: istio-envoy
      emptyDir: {}
    - name: istio-data
      emptyDir: {}
    - name: istio-podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations
    - name: istio-token
      projected:
        sources:
          - serviceAccountToken:
              path: istio-token
              expirationSeconds: 43200
              audience: istio-ca
    - name: istiod-ca-cert
      configMap:
        name: istio-ca-root-cert

# 注入后的 Pod 有 2 个容器 + 1 个初始化容器

对比总结:

项目原始 YAML注入后的 Pod
容器数量1 个2 个
初始化容器0 个1 个(istio-init)
端口80808080 + 15001/15006/15020/15090
卷挂载0-1 个5+ 个
环境变量用户定义用户定义 + Istio 变量
资源消耗应用资源应用资源 + Sidecar 资源

2.4.2 自动注入的工作原理

核心机制:Kubernetes Admission Webhook

1. 用户创建 Pod
   ↓
   kubectl apply -f deployment.yaml
   ↓
2. API Server 接收请求
   ↓
3. Admission Controller 拦截
   ↓
   检查:Pod 的命名空间有 istio-injection=enabled 标签吗?
   ↓
   是的 → 调用 Istio Mutating Webhook
   ↓
4. Istio Mutating Webhook 修改 Pod 定义
   ↓
   添加 istio-proxy 容器
   添加 istio-init 初始化容器
   添加卷挂载
   添加环境变量
   ↓
5. 修改后的 Pod 定义返回给 API Server
   ↓
6. API Server 创建修改后的 Pod
   ↓
7. Pod 启动(包含 Sidecar)

验证 Webhook 是否安装:

# 查看 Istio 的 Mutating Webhook
kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml

# 输出示例:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: istio-sidecar-injector
webhooks:
  - name: sidecar-injector.istio.io
    clientConfig:
      service:
        name: istiod
        namespace: istio-system
        path: /inject  # 注入的 API 路径
    rules:
      - operations: ["CREATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
    # 命名空间选择器:只注入带标签的命名空间
    namespaceSelector:
      matchLabels:
        istio-injection: enabled

2.4.3 自动注入(推荐)

启用自动注入:

# 方法 1:为整个命名空间启用自动注入
kubectl label namespace default istio-injection=enabled

# 验证标签
kubectl get namespace -L istio-injection

# 输出示例:
# NAME              STATUS   AGE   ISTIO-INJECTION
# default           Active   10d   enabled
# kube-system       Active   10d   
# istio-system      Active   2d    disabled

# 方法 2:为特定命名空间启用
kubectl label namespace product-system istio-injection=enabled

# 方法 3:禁用自动注入
kubectl label namespace default istio-injection=disabled

# 方法 4:删除标签(也会禁用)
kubectl label namespace default istio-injection-

Pod 级别的注入控制:

# 强制注入(即使命名空间没有启用)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "true"  # 强制注入
    spec:
      containers:
        - name: product
          image: product:v1

---
# 禁止注入(即使命名空间启用了)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: legacy-service
spec:
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"  # 禁止注入
    spec:
      containers:
        - name: legacy
          image: legacy:v1

注入后重启 Pod:

# 如果 Pod 在启用注入前就已经运行,需要重启
kubectl rollout restart deployment product-service -n default

# 或者直接删除 Pod,让 Deployment 重新创建
kubectl delete pod -l app=product -n default

验证注入成功:

# 方法 1:查看 Pod 的容器数量
kubectl get pods -n default

# 输出示例(注意 READY 列):
# NAME                              READY   STATUS    RESTARTS   AGE
# product-service-abc123-xyz        2/2     Running   0          1m
#                                   ↑↑↑
#                                   2/2 表示有 2 个容器(应用 + Sidecar)

# 方法 2:查看具体的容器名称
kubectl get pod product-service-abc123-xyz -n default \
  -o jsonpath='{.spec.containers[*].name}'

# 输出:
# product-service istio-proxy
#        ↑              ↑
#     应用容器      Sidecar容器

# 方法 3:查看初始化容器
kubectl get pod product-service-abc123-xyz -n default \
  -o jsonpath='{.spec.initContainers[*].name}'

# 输出:
# istio-init
#     ↑
# iptables 配置容器

# 方法 4:查看 Pod 详情
kubectl describe pod product-service-abc123-xyz -n default

# 输出示例:
# Init Containers:
#   istio-init:
#     Image:      docker.io/istio/proxyv2:1.20.1
#     ...
# 
# Containers:
#   product-service:
#     Image:      product:v1
#     ...
#   istio-proxy:
#     Image:      docker.io/istio/proxyv2:1.20.1
#     ...

常见问题:

# 问题 1:Pod 只有 1 个容器,Sidecar 没有注入
# 原因:命名空间没有启用自动注入
# 解决:
kubectl label namespace default istio-injection=enabled
kubectl rollout restart deployment product-service

# 问题 2:Sidecar 注入了但 Pod 启动失败
# 原因:可能是权限问题(istio-init 需要 NET_ADMIN 权限)
# 检查:
kubectl logs product-service-abc123-xyz -c istio-init

# 问题 3:如何排除某些 Pod 不注入
# 解决:在 Pod 模板添加注解
metadata:
  annotations:
    sidecar.istio.io/inject: "false"

2.4.4 手动注入(不推荐)

使用场景:

  • 命名空间不想全局启用自动注入
  • 需要精确控制哪些 Pod 注入
  • 测试或调试

手动注入命令:

# 方法 1:直接注入并应用
istioctl kube-inject -f deployment.yaml | kubectl apply -f -

# 方法 2:生成注入后的 YAML,再手动应用
istioctl kube-inject -f deployment.yaml -o injected-deployment.yaml
kubectl apply -f injected-deployment.yaml

# 方法 3:从标准输入注入
kubectl get deployment product-service -o yaml | \
  istioctl kube-inject -f - | \
  kubectl apply -f -

自动注入 vs 手动注入对比:

特性自动注入手动注入
配置方式标签命名空间每次执行命令
便捷性⭐⭐⭐⭐⭐⭐⭐
维护成本
适用场景生产环境测试/调试
更新方式自动(重启 Pod)手动重新注入
推荐度✅ 强烈推荐⚠️ 特殊场景

2.4.5 注入配置自定义

自定义 Sidecar 资源:

# 通过注解自定义 Sidecar 资源
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  template:
    metadata:
      annotations:
        # 自定义 Sidecar CPU
        sidecar.istio.io/proxyCPU: "500m"
        sidecar.istio.io/proxyCPULimit: "2000m"
        
        # 自定义 Sidecar 内存
        sidecar.istio.io/proxyMemory: "512Mi"
        sidecar.istio.io/proxyMemoryLimit: "1Gi"
        
        # 自定义并发数
        sidecar.istio.io/concurrency: "4"
        
        # 自定义日志级别
        sidecar.istio.io/logLevel: "debug"
        
        # 排除端口(不拦截这些端口的流量)
        traffic.sidecar.istio.io/excludeOutboundPorts: "3306,6379"
        traffic.sidecar.istio.io/excludeInboundPorts: "9090"
        
        # 包含端口(仅拦截这些端口)
        traffic.sidecar.istio.io/includeOutboundIPRanges: "10.0.0.0/8"
        traffic.sidecar.istio.io/excludeOutboundIPRanges: "169.254.169.254/32"
    spec:
      containers:
        - name: product
          image: product:v1

查看注入配置:

# 查看注入器配置
kubectl get configmap istio-sidecar-injector -n istio-system -o yaml

# 查看注入模板
istioctl profile dump demo

# 查看某个 Pod 的注入信息
kubectl get pod product-service-abc123-xyz -n default \
  -o jsonpath='{.metadata.annotations.sidecar\.istio\.io/status}' | jq

2.4.6 总结

什么是自动注入?

  • Istio 自动在你的 Pod 中添加 Envoy Sidecar 容器
  • 通过 Kubernetes Admission Webhook 实现
  • 完全透明,无需修改应用代码

启用方式:

kubectl label namespace <命名空间> istio-injection=enabled

验证方式:

kubectl get pods  # 查看 READY 列是否是 2/2

关键点:

  • ✅ 推荐使用自动注入(生产环境)
  • ✅ 注入后需要重启 Pod 才能生效
  • ✅ 可以通过注解精确控制单个 Pod
  • ⚠️ Sidecar 会增加资源消耗(~100MB 内存,~0.1 核 CPU)

5. 核心概念与流量路由详解

5.1 Istio 是如何运行的?

5.1.1 Istio 在 Kubernetes 中的运行方式

答案:是的,Istio 完全以 Pod 的形式运行在 Kubernetes 中。

Istio 组件的 Pod 分布:

┌─────────────────────────────────────────────────────────────┐
│              Kubernetes 集群                                 │
│                                                              │
│  ┌────────────────────────────────────────────────────┐    │
│  │  istio-system 命名空间(Istio 控制平面)           │    │
│  │                                                     │    │
│  │  ┌──────────────┐  ┌──────────────────────────┐   │    │
│  │  │   Istiod     │  │  Ingress Gateway         │   │    │
│  │  │   Pod        │  │  Pod                     │   │    │
│  │  │  (控制平面)   │  │  (入口网关,Envoy)       │   │    │
│  │  └──────────────┘  └──────────────────────────┘   │    │
│  │                                                     │    │
│  │  ┌──────────────────────────┐                     │    │
│  │  │  Egress Gateway          │                     │    │
│  │  │  Pod                     │                     │    │
│  │  │  (出口网关,Envoy)        │                     │    │
│  │  └──────────────────────────┘                     │    │
│  └────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌────────────────────────────────────────────────────┐    │
│  │  应用命名空间(你的应用)                           │    │
│  │                                                     │    │
│  │  ┌─────────────────────┐  ┌─────────────────────┐ │    │
│  │  │  Product Service    │  │  Order Service      │ │    │
│  │  │  Pod                │  │  Pod                │ │    │
│  │  │  ┌──────┐           │  │  ┌──────┐          │ │    │
│  │  │  │ App  │           │  │  │ App  │          │ │    │
│  │  │  │容器  │           │  │  │容器  │          │ │    │
│  │  │  └──────┘           │  │  └──────┘          │ │    │
│  │  │  ┌──────┐           │  │  ┌──────┐          │ │    │
│  │  │  │Envoy │           │  │  │Envoy │          │ │    │
│  │  │  │Sidecar│          │  │  │Sidecar│         │ │    │
│  │  │  │容器  │           │  │  │容器  │          │ │    │
│  │  │  └──────┘           │  │  └──────┘          │ │    │
│  │  └─────────────────────┘  └─────────────────────┘ │    │
│  └────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

查看 Istio 的 Pod:

# 查看 Istio 系统组件(都是 Pod)
kubectl get pods -n istio-system

# 输出示例:
# NAME                                    READY   STATUS    RESTARTS   AGE
# istiod-5c868d5b6f-abc123               1/1     Running   0          2d
# istio-ingressgateway-7d8f8c9b5-xyz456  1/1     Running   0          2d
# istio-egressgateway-6b9c4d7f8-def789   1/1     Running   0          2d

# 查看应用 Pod(包含 Sidecar)
kubectl get pods -n product-system

# 输出示例:
# NAME                              READY   STATUS    RESTARTS   AGE
# product-service-v1-abc123-xyz     2/2     Running   0          1d
#                                   ↑↑↑
#                                   注意:2/2 表示 Pod 中有 2 个容器
#                                   容器1:应用容器
#                                   容器2:Envoy Sidecar 容器

# 查看 Pod 中的容器
kubectl get pod product-service-v1-abc123-xyz -n product-system \
  -o jsonpath='{.spec.containers[*].name}'

# 输出:
# product-service istio-proxy
#       ↑              ↑
#    应用容器      Sidecar容器

详细说明:

  1. Istiod Pod(控制平面)

    • 类型:Deployment
    • 容器:1 个(istiod)
    • 作用:配置管理、服务发现、证书颁发
    • 命名空间:istio-system
  2. Ingress Gateway Pod(入口网关)

    • 类型:Deployment
    • 容器:1 个(Envoy Proxy)
    • 作用:接收外部流量,路由到内部服务
    • 命名空间:istio-system
    • 暴露方式:LoadBalancer Service
  3. Egress Gateway Pod(出口网关)

    • 类型:Deployment
    • 容器:1 个(Envoy Proxy)
    • 作用:管理出站流量
    • 命名空间:istio-system
  4. 应用 Pod + Sidecar

    • 类型:你的 Deployment
    • 容器:2 个(应用容器 + istio-proxy)
    • 作用:运行业务应用 + 流量代理
    • 命名空间:你的应用命名空间
5.1.2 Istio 在 Docker 中的运行方式

答案:是的,在 Docker 环境中,Istio 组件以容器的形式运行。

但是,纯 Docker 环境不推荐运行完整的 Istio,因为:

  • Istio 设计为 Kubernetes 原生
  • 需要服务发现、配置管理等 K8s 功能
  • 手动配置 Envoy 非常复杂

推荐方案:使用 Kind(Kubernetes in Docker)

# Kind 会在 Docker 容器中运行 Kubernetes
# 然后在这个 K8s 中运行 Istio

# 查看 Docker 容器(Kind 节点)
docker ps

# 输出示例:
# CONTAINER ID   IMAGE                  COMMAND                  CREATED       STATUS       PORTS                       NAMES
# abc123def456   kindest/node:v1.27.0   "/usr/local/bin/entr…"   2 days ago    Up 2 days    127.0.0.1:6443->6443/tcp   istio-demo-control-plane

# 进入 Kind 容器
docker exec -it istio-demo-control-plane bash

# 在容器内查看 Kubernetes Pod(包括 Istio)
crictl ps  # 或 docker ps(取决于容器运行时)

Docker Compose 运行 Istio 组件(简化版):

# docker-compose.yml(仅演示,不推荐生产)
version: '3.8'

services:
  # 应用容器
  product-service:
    image: my-product-service:v1
    container_name: product-service
    ports:
      - "8080:8080"
    networks:
      - istio-net

  # Envoy Sidecar 容器(手动配置)
  product-service-envoy:
    image: envoyproxy/envoy:v1.28.0
    container_name: product-service-envoy
    volumes:
      - ./envoy-config.yaml:/etc/envoy/envoy.yaml
    ports:
      - "9901:9901"  # Envoy admin
      - "8000:8000"  # 对外端口
    networks:
      - istio-net
    depends_on:
      - product-service

networks:
  istio-net:
    driver: bridge

总结:

  • Kubernetes 环境:Istio 以 Pod 形式运行(推荐)
  • Docker 环境:使用 Kind 在 Docker 容器中运行 K8s,然后运行 Istio
  • ⚠️ 纯 Docker:可以手动配置 Envoy 容器,但非常复杂,不推荐

5.2 外部如何访问 Kubernetes 中的应用?

5.2.1 完整的流量路径
外部用户
   ↓
   ↓ 1. DNS 解析
   ↓    product.example.com → 120.55.xxx.xxx
   ↓
云服务商负载均衡器(LoadBalancer)
   ↓
   ↓ 2. 流量到达 Kubernetes 集群
   ↓
Istio Ingress Gateway Service
   ↓
   ↓ 3. Service 转发到 Pod
   ↓
Istio Ingress Gateway Pod(Envoy)
   ↓
   ↓ 4. Gateway 资源匹配
   ↓    检查:域名、端口、TLS
   ↓
   ↓ 5. VirtualService 路由规则匹配
   ↓    检查:路径、Header、方法
   ↓
   ↓ 6. 查找目标 Service
   ↓
Kubernetes Service(product-manager-service)
   ↓
   ↓ 7. Service 转发到 Pod
   ↓
应用 Pod(product-manager-service-xxx)
   ↓
   ↓ 8. Sidecar Envoy 拦截
   ↓
   ↓ 9. 执行策略(认证、授权、限流)
   ↓
应用容器(处理业务逻辑)
5.2.2 配置步骤详解

步骤 1:部署应用(Deployment + Service)

# ============================================================
# 文件:product-service-deployment.yaml
# 说明:这是你的应用部署配置
# ============================================================

# 1. Deployment:定义如何运行应用
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-manager-service
  namespace: product-system  # 【修改】你的命名空间
  labels:
    app: product-manager
    version: v1
spec:
  replicas: 2  # 【修改】Pod 副本数
  selector:
    matchLabels:
      app: product-manager
      version: v1
  template:
    metadata:
      labels:
        app: product-manager  # 重要:这个标签用于 Service 选择
        version: v1
    spec:
      containers:
        - name: product-manager
          image: your-registry.com/product-manager:v1  # 【修改】镜像地址
          ports:
            - containerPort: 8080  # 【修改】应用监听的端口
              name: http
              protocol: TCP
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"

---
# 2. Service:为 Pod 提供稳定的访问入口
# 说明:Service 是 Kubernetes 的核心概念,提供服务发现和负载均衡
apiVersion: v1
kind: Service
metadata:
  name: product-manager-service  # 重要:这个名字会被 VirtualService 引用
  namespace: product-system
  labels:
    app: product-manager
spec:
  type: ClusterIP  # 集群内部访问(不直接暴露到外部)
  ports:
    - port: 8080  # Service 的端口
      targetPort: 8080  # Pod 的端口
      protocol: TCP
      name: http
  selector:
    app: product-manager  # 选择器:匹配哪些 Pod
    # 说明:Service 会自动找到所有带有这个标签的 Pod

步骤 2:配置 Gateway(定义如何监听外部流量)

# ============================================================
# 文件:istio-gateway.yaml
# 说明:定义 Istio 如何接收外部流量
# 关键:这里配置域名和端口
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: product-gateway
  namespace: product-system
spec:
  selector:
    istio: ingressgateway  # 选择 Istio Ingress Gateway Pod
  servers:
    - port:
        number: 443  # 外部访问端口
        name: https
        protocol: HTTPS
      hosts:
        - "product.example.com"  # 【修改】你的域名
      tls:
        mode: SIMPLE
        credentialName: product-tls-secret  # 【修改】TLS 证书

步骤 3:配置 VirtualService(定义路由规则)

# ============================================================
# 文件:virtual-service.yaml
# 说明:定义如何将请求路由到你的应用
# 关键:这里配置路径匹配和目标服务
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-virtualservice
  namespace: product-system
spec:
  # 1. 域名:必须与 Gateway 中的 hosts 匹配
  hosts:
    - "product.example.com"  # 【修改】必须与 Gateway 一致
  
  # 2. 关联 Gateway
  gateways:
    - product-gateway  # 关联上面创建的 Gateway
  
  # 3. 路由规则
  http:
    - match:
        - uri:
            prefix: "/api/products"  # 【修改】匹配的路径
      route:
        - destination:
            # 目标:这里填写 Service 的名称
            # 格式:<service-name>.<namespace>.svc.cluster.local
            host: product-manager-service.product-system.svc.cluster.local
            port:
              number: 8080  # Service 的端口

步骤 4:应用配置

# 1. 确保命名空间启用了 Sidecar 自动注入
kubectl label namespace product-system istio-injection=enabled

# 2. 部署应用
kubectl apply -f product-service-deployment.yaml

# 3. 配置网关和路由
kubectl apply -f istio-gateway.yaml
kubectl apply -f virtual-service.yaml

# 4. 验证部署
kubectl get pods -n product-system
# 应该看到 READY 是 2/2(应用容器 + Sidecar)

# 5. 获取 Ingress Gateway 的外部 IP
kubectl get svc istio-ingressgateway -n istio-system

# 输出示例:
# NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)
# istio-ingressgateway   LoadBalancer   10.96.xxx.xxx   120.55.xxx.xxx   80:30080/TCP,443:30443/TCP

# 6. 配置 DNS(将域名指向 EXTERNAL-IP)
# product.example.com → 120.55.xxx.xxx

# 7. 测试访问
curl https://product.example.com/api/products
5.2.3 关键配置对应关系
┌────────────────────────────────────────────────────────────┐
│  配置文件                    对应关系                       │
├────────────────────────────────────────────────────────────┤
│  Gateway.spec.hosts         ←→  VirtualService.spec.hosts │
│  "product.example.com"          "product.example.com"     │
│  (必须匹配)                                               │
├────────────────────────────────────────────────────────────┤
│  VirtualService.route.destination.host                    │
│  "product-manager-service"                                │
│         ↓                                                  │
│  Service.metadata.name                                    │
│  "product-manager-service"                                │
│  (必须匹配)                                               │
├────────────────────────────────────────────────────────────┤
│  Service.spec.selector        ←→  Pod.metadata.labels    │
│  app: product-manager             app: product-manager   │
│  (通过标签选择 Pod)                                       │
├────────────────────────────────────────────────────────────┤
│  Service.spec.ports.port      ←→  VirtualService端口     │
│  8080                             8080                    │
├────────────────────────────────────────────────────────────┤
│  Service.spec.ports.targetPort ←→ Pod.containerPort      │
│  8080                             8080                    │
└────────────────────────────────────────────────────────────┘
5.2.4 常见问题排查

问题 1:外部无法访问

# 检查 Ingress Gateway 是否有外部 IP
kubectl get svc istio-ingressgateway -n istio-system

# 如果 EXTERNAL-IP 是 <pending>:
# - 云环境:等待云服务商分配 LoadBalancer
# - 自建环境:改用 NodePort 类型

# 检查 Gateway 配置
kubectl get gateway -n product-system
kubectl describe gateway product-gateway -n product-system

# 检查 VirtualService 配置
kubectl get virtualservice -n product-system
kubectl describe virtualservice product-virtualservice -n product-system

# 分析配置问题
istioctl analyze -n product-system

问题 2:503 Service Unavailable

# 检查 Pod 是否运行
kubectl get pods -n product-system

# 检查 Service 是否有 Endpoints
kubectl get endpoints product-manager-service -n product-system

# 如果没有 Endpoints,检查 Service 的 selector 是否匹配 Pod 标签
kubectl get pods -n product-system --show-labels

问题 3:404 Not Found

# 检查 VirtualService 的路径匹配
kubectl get virtualservice product-virtualservice -n product-system -o yaml

# 确认请求路径与配置匹配
# 例如:配置是 prefix: "/api/products"
# 则访问 https://product.example.com/api/products 才能匹配

5.3 为什么 Ory Hydra 使用 PostgreSQL 而不是 MySQL?

5.3.1 技术原因

1. 事务隔离级别支持更好

-- PostgreSQL 支持更严格的事务隔离
-- OAuth2 的 token 生成需要严格的事务保证,避免重复或冲突

-- PostgreSQL 的 SERIALIZABLE 隔离级别
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO oauth2_access_tokens ...
COMMIT;

-- MySQL 的 SERIALIZABLE 实现不如 PostgreSQL 完善

2. JSON 数据类型支持

-- PostgreSQL 原生支持 JSONB(二进制 JSON)
-- OAuth2 的 metadata、claims 等数据适合用 JSON 存储

-- PostgreSQL
CREATE TABLE oauth2_clients (
    id VARCHAR(255) PRIMARY KEY,
    metadata JSONB,  -- 高效的 JSON 存储和查询
    created_at TIMESTAMP
);

-- 查询 JSON 字段
SELECT * FROM oauth2_clients WHERE metadata->>'redirect_uris' LIKE '%example.com%';

-- MySQL 的 JSON 支持较弱,性能不如 PostgreSQL

3. 并发性能

PostgreSQL 的 MVCC(多版本并发控制):
- 读不阻塞写
- 写不阻塞读
- 高并发场景下性能更好

OAuth2 场景:
- 大量并发的 token 验证(读操作)
- 同时有 token 生成(写操作)
- PostgreSQL 能更好地处理这种混合负载

4. 数据一致性保证

PostgreSQL 的优势:
- 更严格的外键约束
- 更完善的约束检查
- 更好的 ACID 保证

OAuth2 需要:
- Client 和 Token 的关系必须一致
- 避免孤儿 Token(Client 被删除但 Token 还在)
- PostgreSQL 的约束能更好地保证数据完整性
5.3.2 Ory Hydra 官方推荐

官方文档说明:

Ory Hydra 支持的数据库:
✅ PostgreSQL 9.6+(推荐)
✅ MySQL 5.7+(支持,但不推荐)
✅ CockroachDB(推荐,分布式场景)

推荐顺序:
1. PostgreSQL(单机/主从)
2. CockroachDB(分布式/多区域)
3. MySQL(如果已有 MySQL 基础设施)

性能对比(Ory 官方测试):

操作PostgreSQLMySQL差异
Token 生成100ms120ms+20%
Token 验证5ms8ms+60%
Client 查询3ms5ms+67%
并发写入1000 TPS600 TPS+67%
5.3.3 实际使用建议

场景 1:新项目(推荐 PostgreSQL)

# PostgreSQL 配置
services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: hydra
      POSTGRES_USER: hydra
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres_data:/var/lib/postgresql/data

  hydra:
    image: oryd/hydra:v2.2.0
    environment:
      DSN: postgres://hydra:secret@postgres:5432/hydra?sslmode=disable

优点:

  • ✅ 性能最优
  • ✅ 功能最完整
  • ✅ 社区支持最好
  • ✅ 官方推荐

场景 2:已有 MySQL 基础设施

# MySQL 配置(也可以使用)
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: hydra
      MYSQL_USER: hydra
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: rootsecret
    command: --default-authentication-plugin=mysql_native_password

  hydra:
    image: oryd/hydra:v2.2.0
    environment:
      DSN: mysql://hydra:secret@tcp(mysql:3306)/hydra?parseTime=true

注意事项:

  • ⚠️ 需要 MySQL 5.7+ 或 8.0+
  • ⚠️ 必须启用 parseTime=true
  • ⚠️ 性能略低于 PostgreSQL
  • ⚠️ 某些高级功能可能不支持

场景 3:分布式/多区域部署(推荐 CockroachDB)

# CockroachDB 配置
services:
  cockroachdb:
    image: cockroachdb/cockroach:latest
    command: start-single-node --insecure
    ports:
      - "26257:26257"
      - "8080:8080"

  hydra:
    image: oryd/hydra:v2.2.0
    environment:
      DSN: cockroach://root@cockroachdb:26257/hydra?sslmode=disable

优点:

  • ✅ 分布式架构
  • ✅ 自动分片
  • ✅ 跨区域复制
  • ✅ PostgreSQL 兼容
5.3.4 数据库对比总结
特性PostgreSQLMySQLCockroachDB
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
并发性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
JSON 支持⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
事务隔离⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
分布式⭐⭐⭐⭐⭐⭐⭐
易用性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
社区支持⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Hydra 推荐✅ 强烈推荐⚠️ 可用✅ 推荐(分布式)

最终建议:

选择 PostgreSQL 如果:
✅ 新项目
✅ 追求性能
✅ 需要高并发
✅ 单机或主从架构

选择 MySQL 如果:
⚠️ 已有 MySQL 基础设施
⚠️ 团队更熟悉 MySQL
⚠️ 性能要求不是特别高

选择 CockroachDB 如果:
✅ 需要分布式部署
✅ 跨区域/跨云
✅ 需要自动分片
✅ 高可用要求极高

5.4 配置示例对比

5.4.1 PostgreSQL 完整配置(推荐)
# docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    container_name: hydra-postgres
    environment:
      POSTGRES_DB: hydra
      POSTGRES_USER: hydra
      POSTGRES_PASSWORD: YourStrongPassword123!  # 【修改】
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U hydra"]
      interval: 10s
      timeout: 5s
      retries: 5

  hydra-migrate:
    image: oryd/hydra:v2.2.0
    environment:
      DSN: postgres://hydra:YourStrongPassword123!@postgres:5432/hydra?sslmode=disable
    command: migrate sql -e --yes
    depends_on:
      postgres:
        condition: service_healthy

  hydra:
    image: oryd/hydra:v2.2.0
    ports:
      - "4444:4444"  # Public API
      - "4445:4445"  # Admin API
    environment:
      DSN: postgres://hydra:YourStrongPassword123!@postgres:5432/hydra?sslmode=disable
      URLS_SELF_ISSUER: https://auth.example.com  # 【修改】
      URLS_CONSENT: https://auth.example.com/consent
      URLS_LOGIN: https://auth.example.com/login
      SECRETS_SYSTEM: ChangeThisToA32CharRandomString!  # 【修改】
      SECRETS_COOKIE: AnotherRandomString32CharsLong!  # 【修改】
    command: serve all
    depends_on:
      hydra-migrate:
        condition: service_completed_successfully

volumes:
  postgres_data:
5.4.2 MySQL 配置(备选)
# docker-compose-mysql.yml
version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: hydra-mysql
    environment:
      MYSQL_DATABASE: hydra
      MYSQL_USER: hydra
      MYSQL_PASSWORD: YourStrongPassword123!  # 【修改】
      MYSQL_ROOT_PASSWORD: RootPassword123!  # 【修改】
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  hydra-migrate:
    image: oryd/hydra:v2.2.0
    environment:
      DSN: mysql://hydra:YourStrongPassword123!@tcp(mysql:3306)/hydra?parseTime=true
    command: migrate sql -e --yes
    depends_on:
      mysql:
        condition: service_healthy

  hydra:
    image: oryd/hydra:v2.2.0
    ports:
      - "4444:4444"
      - "4445:4445"
    environment:
      DSN: mysql://hydra:YourStrongPassword123!@tcp(mysql:3306)/hydra?parseTime=true
      URLS_SELF_ISSUER: https://auth.example.com  # 【修改】
      URLS_CONSENT: https://auth.example.com/consent
      URLS_LOGIN: https://auth.example.com/login
      SECRETS_SYSTEM: ChangeThisToA32CharRandomString!  # 【修改】
      SECRETS_COOKIE: AnotherRandomString32CharsLong!  # 【修改】
    command: serve all
    depends_on:
      hydra-migrate:
        condition: service_completed_successfully

volumes:
  mysql_data:

6. 完整的请求流程详解(从浏览器到应用)

6.1 流程总览

用户浏览器
   ↓
https://product.example.com/api/products
   ↓
[1] DNS 解析
   ↓
120.55.xxx.xxx (LoadBalancer IP)
   ↓
[2] 云服务商负载均衡器
   ↓
[3] Istio Ingress Gateway Service (K8s)
   ↓
[4] Istio Ingress Gateway Pod (Envoy)
   ↓
[5] Gateway 资源匹配(域名、端口、TLS)
   ↓
[6] VirtualService 路由规则匹配
   ↓
[7] 外部授权器检查(OAuth2 Proxy + Hydra)⭐ 重点
   ↓
[8] Kubernetes Service
   ↓
[9] 应用 Pod(Sidecar Envoy)
   ↓
[10] 应用容器处理
   ↓
[11] 响应原路返回

6.2 第一次访问流程(需要登录)

步骤 1:用户在浏览器输入 URL
用户操作:
在浏览器地址栏输入:https://product.example.com/api/products
然后按回车

浏览器会做什么:
1. 检查 URL 协议(https)
2. 提取域名(product.example.com)
3. 准备发起 HTTPS 请求

步骤 2:DNS 解析
浏览器 → 本地 DNS 缓存 → DNS 服务器
   ↓
查询:product.example.com 的 IP 地址是多少?
   ↓
DNS 服务器返回:120.55.xxx.xxx

DNS 配置位置:

# 方式 1:云服务商 DNS 控制台配置
# 阿里云示例:
# 域名:product.example.com
# 类型:A 记录
# 记录值:120.55.xxx.xxx(LoadBalancer 的公网 IP)
# TTL:600(10分钟)

# 方式 2:查看 LoadBalancer IP
kubectl get svc istio-ingressgateway -n istio-system

# 输出:
# NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)
# istio-ingressgateway   LoadBalancer   10.96.123.45    120.55.xxx.xxx   80:30080/TCP,443:30443/TCP
#                                                       ↑↑↑↑↑↑↑↑↑↑↑↑↑
#                                                    这个 IP 要配置到 DNS

# 方式 3:本地测试(修改 hosts 文件)
# Windows: C:\Windows\System32\drivers\etc\hosts
# Linux/Mac: /etc/hosts
# 添加一行:
120.55.xxx.xxx product.example.com

步骤 3:浏览器建立 TCP 连接
浏览器
   ↓
三次握手建立 TCP 连接
   ↓
120.55.xxx.xxx:443(HTTPS 端口)
   ↓
这个 IP 是云服务商的负载均衡器(LoadBalancer)

步骤 4:TLS/SSL 握手
浏览器 ←→ LoadBalancer ←→ Istio Ingress Gateway
   ↓
1. ClientHello(浏览器):我支持这些加密套件...
2. ServerHello(Istio Gateway):我选择 TLS 1.3
3. 证书发送:这是我的证书(product.example.com)
4. 浏览器验证证书:
   - 证书是否过期?
   - 证书的域名是否匹配?
   - 证书是否由受信任的 CA 签发?
5. 密钥交换:建立加密通道
6. Finished:握手完成,开始加密通信

TLS 证书配置位置:

# ============================================================
# 配置文件:istio-gateway.yaml
# 位置:Gateway 资源的 tls 配置
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: product-gateway
  namespace: product-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "product.example.com"
      tls:
        mode: SIMPLE  # 单向 TLS
        # 证书来源:Kubernetes Secret
        credentialName: product-tls-secret  # ← 这里配置证书

# ============================================================
# 证书创建命令
# ============================================================

# 方法 1:手动创建证书 Secret
kubectl create secret tls product-tls-secret \
  --cert=/path/to/product.example.com.crt \  # 证书文件
  --key=/path/to/product.example.com.key \   # 私钥文件
  -n istio-system  # 注意:必须在 istio-system 命名空间

# 方法 2:使用 cert-manager 自动管理(推荐生产环境)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: product-tls-cert
  namespace: istio-system
spec:
  secretName: product-tls-secret  # 生成的 Secret 名称
  dnsNames:
    - product.example.com
    - "*.example.com"  # 支持通配符
  issuerRef:
    name: letsencrypt-prod  # Let's Encrypt 免费证书
    kind: ClusterIssuer

# 证书位置说明:
# - Secret 必须在 istio-system 命名空间
# - Gateway 资源引用这个 Secret
# - Istio 会自动将证书挂载到 Ingress Gateway Pod

步骤 5:HTTP 请求到达云负载均衡器
LoadBalancer(云服务商提供)
作用:
1. 接收来自互联网的流量
2. 分发到多个 Kubernetes 节点
3. 健康检查:剔除不健康的节点
4. SSL 卸载(可选):解密 HTTPS → HTTP

LoadBalancer 配置位置:

# ============================================================
# 配置文件:istio-operator.yaml 或自动创建
# 位置:Istio Ingress Gateway 的 Service
# ============================================================

apiVersion: v1
kind: Service
metadata:
  name: istio-ingressgateway
  namespace: istio-system
  annotations:
    # 阿里云负载均衡器配置
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: "slb.s2.small"
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-charge-type: "paybytraffic"
    
    # AWS 负载均衡器配置
    # service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    
    # GCP 负载均衡器配置
    # cloud.google.com/load-balancer-type: "External"
spec:
  type: LoadBalancer  # ← 关键:类型是 LoadBalancer
  selector:
    app: istio-ingressgateway  # 选择 Ingress Gateway Pod
  ports:
    - name: status-port
      port: 15021
      targetPort: 15021
    - name: http2
      port: 80
      targetPort: 8080
    - name: https
      port: 443  # 外部访问 443
      targetPort: 8443  # 转发到 Pod 的 8443

# 云服务商会自动:
# 1. 创建负载均衡器实例
# 2. 分配公网 IP(120.55.xxx.xxx)
# 3. 配置健康检查(检查 15021 端口)
# 4. 配置后端服务器(Kubernetes 节点)

步骤 6:请求到达 Istio Ingress Gateway Pod
LoadBalancer
   ↓
Service: istio-ingressgateway
   ↓
选择一个 Ingress Gateway Pod(通过 selector)
   ↓
Pod: istio-ingressgateway-xxx-yyy(Envoy 代理)

Ingress Gateway Pod 内部:

┌─────────────────────────────────────────────┐
│  istio-ingressgateway-xxx-yyy Pod          │
│                                             │
│  ┌───────────────────────────────────────┐ │
│  │  Envoy Proxy 容器                     │ │
│  │                                        │ │
│  │  监听端口:                             │ │
│  │  - 8080 (HTTP)                        │ │
│  │  - 8443 (HTTPS)                       │ │
│  │  - 15021 (健康检查)                    │ │
│  │  - 15090 (Prometheus 指标)            │ │
│  │                                        │ │
│  │  配置来源:                             │ │
│  │  - Istiod 推送的配置                   │ │
│  │  - Gateway 资源                        │ │
│  │  - VirtualService 资源                │ │
│  └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

查看 Ingress Gateway 配置:

# 查看 Ingress Gateway Pod
kubectl get pods -n istio-system -l app=istio-ingressgateway

# 查看 Envoy 配置
kubectl exec -it istio-ingressgateway-xxx-yyy -n istio-system -- curl localhost:15000/config_dump

# 查看监听器(Listeners)
istioctl proxy-config listeners istio-ingressgateway-xxx-yyy.istio-system

# 查看路由(Routes)
istioctl proxy-config routes istio-ingressgateway-xxx-yyy.istio-system

步骤 7:Gateway 资源匹配
Envoy 接收到请求:
请求 URL: https://product.example.com/api/products
请求头:
  Host: product.example.com
  User-Agent: Mozilla/5.0 ...
  Cookie: ...

Envoy 开始匹配 Gateway 资源:
1. 检查端口:443 ✓
2. 检查协议:HTTPS ✓
3. 检查域名:product.example.com ✓
4. 检查 TLS 证书:product-tls-secret ✓

匹配成功!使用这个 Gateway 的配置

Gateway 配置详解:

# ============================================================
# 配置文件:istio-gateway.yaml
# 作用:定义 Envoy 如何监听和接收流量
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: product-gateway
  namespace: product-system
spec:
  # 选择器:这个配置应用到哪个 Gateway Pod
  selector:
    istio: ingressgateway  # 匹配 Ingress Gateway Pod 的标签
  
  # 服务器配置:定义监听规则
  servers:
    - port:
        number: 443  # 监听端口
        name: https
        protocol: HTTPS
      
      # 主机名:接受哪些域名的请求
      hosts:
        - "product.example.com"  # 只接受这个域名
      
      # TLS 配置
      tls:
        mode: SIMPLE  # 单向 TLS(服务器验证)
        credentialName: product-tls-secret  # 证书 Secret

# 这个配置告诉 Envoy:
# "如果收到发往 product.example.com:443 的 HTTPS 请求,
#  就使用 product-tls-secret 的证书处理它"

步骤 8:VirtualService 路由规则匹配
Gateway 匹配成功后,开始查找路由规则

Envoy 查找:
哪个 VirtualService 的:
1. hosts 包含 "product.example.com" ✓
2. gateways 包含 "product-gateway" ✓

找到了!product-virtualservice

然后匹配路由规则:
请求路径:/api/products
匹配规则:prefix: "/api/products" ✓

匹配成功!路由到目标服务

VirtualService 配置详解:

# ============================================================
# 配置文件:virtual-service.yaml
# 作用:定义请求如何路由到后端服务
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-virtualservice
  namespace: product-system
spec:
  # 1. 主机名:这个 VirtualService 处理哪些域名的请求
  hosts:
    - "product.example.com"  # 必须与 Gateway 的 hosts 匹配
  
  # 2. 关联的 Gateway
  gateways:
    - product-gateway  # 引用 Gateway 资源
  
  # 3. HTTP 路由规则
  http:
    # 路由规则 1:API 请求
    - name: "product-api-route"
      
      # 匹配条件:什么样的请求使用这个规则
      match:
        - uri:
            prefix: "/api/products"  # 路径前缀匹配
      
      # 路由目标:发送到哪里
      route:
        - destination:
            # 目标服务(Kubernetes Service)
            host: product-manager-service.product-system.svc.cluster.local
            port:
              number: 8080
          weight: 100  # 100% 流量

# 这个配置告诉 Envoy:
# "如果请求路径是 /api/products 开头,
#  就转发到 product-manager-service:8080"

步骤 9:外部授权器检查(OAuth2 认证)⭐⭐⭐ 重点

这是最关键的一步!Istio 会先调用外部授权器验证用户身份。

Envoy(Ingress Gateway)
   ↓
在转发到后端服务之前,先检查授权
   ↓
调用外部授权器(OAuth2 Proxy)
   ↓
OAuth2 Proxy 检查请求:
1. 请求中有 Cookie 吗?
2. Cookie 中有有效的 Session 吗?
3. Session 对应的 Access Token 还有效吗?

第一次访问:❌ 没有 Cookie
   ↓
OAuth2 Proxy 返回:401 Unauthorized + 重定向
   ↓
Envoy 返回给浏览器:302 重定向到登录页面

外部授权器配置:

# ============================================================
# 1. 配置 Istio 使用外部授权器
# 文件:istio-configmap.yaml
# ============================================================

apiVersion: v1
kind: ConfigMap
metadata:
  name: istio
  namespace: istio-system
data:
  mesh: |-
    # 扩展提供者:定义外部授权器
    extensionProviders:
      - name: "oauth2-proxy"  # 授权器名称
        envoyExtAuthzHttp:  # HTTP 方式的外部授权
          service: "oauth2-proxy.auth-system.svc.cluster.local"  # OAuth2 Proxy 服务地址
          port: 4180  # 端口
          pathPrefix: "/oauth2/auth"  # 授权检查路径
          
          # 授权失败时返回给客户端的 Header
          headersToDownstreamOnDeny:
            - content-type
            - set-cookie
          
          # 授权成功时转发给上游的 Header
          headersToUpstreamOnAllow:
            - authorization
            - cookie
            - x-forwarded-access-token
            - x-forwarded-user
            - x-forwarded-email
          
          # 发送给授权器检查的 Header
          includeHeadersInCheck:
            - "authorization"
            - "cookie"

---
# ============================================================
# 2. 应用授权策略到服务
# 文件:authorization-policy.yaml
# ============================================================

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-oauth2
  namespace: product-system  # 应用的命名空间
spec:
  selector:
    matchLabels:
      app: product-manager  # 应用到哪个服务
  
  action: CUSTOM  # 使用自定义授权器
  provider:
    name: "oauth2-proxy"  # 使用上面定义的授权器
  
  rules:
    - to:
        - operation:
            paths:
              - "/api/*"  # 需要授权的路径
            notPaths:
              - "/health"  # 不需要授权的路径
              - "/api/public/*"  # 公开路径

# 这个配置告诉 Envoy:
# "在转发 /api/* 的请求之前,
#  先调用 oauth2-proxy 检查授权"

步骤 10:OAuth2 登录流程(Ory Hydra)⭐⭐⭐⭐⭐ 超级重点

用户被重定向到登录页面,开始 OAuth2 授权码流程。

┌─────────────────────────────────────────────────────────────┐
│        完整的 OAuth2 授权码流程(Authorization Code Flow)   │
└─────────────────────────────────────────────────────────────┘

[步骤 1] 浏览器重定向到 Hydra
浏览器
   ↓
GET https://auth.example.com/oauth2/auth?
    client_id=product-client
    &response_type=code
    &scope=openid email profile
    &redirect_uri=https://product.example.com/oauth2/callback
    &state=random-state-string

参数说明:
- client_id: OAuth2 客户端 ID(在 Hydra 中注册的)
- response_type: code(授权码模式)
- scope: 请求的权限范围
- redirect_uri: 授权成功后的回调地址
- state: 防止 CSRF 攻击的随机字符串

───────────────────────────────────────────────────────────────

[步骤 2] Hydra 创建 Login Challenge
Ory Hydra 服务器(Public API :4444)
   ↓
Hydra 检查:
1. client_id 是否存在?✓
2. redirect_uri 是否在白名单?✓
3. scope 是否允许?✓
   ↓
Hydra 创建 Login Challenge(登录挑战)
   ↓
生成 login_challenge=abc123...
   ↓
重定向到登录页面(你自己的 UI)

───────────────────────────────────────────────────────────────

[步骤 3] 用户看到登录页面
浏览器 → GET https://auth.example.com/login?login_challenge=abc123
   ↓
登录页面(Login UI)
┌─────────────────────────────────┐
│  请登录                          │
│  ┌─────────────────────────┐   │
│  │ 用户名:[            ]  │   │
│  │ 密码:  [            ]  │   │
│  └─────────────────────────┘   │
│  [     登  录     ]            │
└─────────────────────────────────┘

用户输入用户名和密码,点击"登录"

───────────────────────────────────────────────────────────────

[步骤 4] 登录页面验证用户身份
登录页面(你的后端服务)
   ↓
POST https://auth.example.com/login
Body: {
  username: "user@example.com",
  password: "password123",
  login_challenge: "abc123"
}
   ↓
你的登录服务验证:
1. 查询数据库:用户名是否存在?
2. 验证密码:密码是否正确?
3. 检查账号状态:是否被禁用?
   ↓
验证成功!

───────────────────────────────────────────────────────────────

[步骤 5] 接受 Login Challenge
你的登录服务 → Hydra Admin API (:4445)
   ↓
PUT https://auth.example.com/admin/oauth2/auth/requests/login/accept?login_challenge=abc123
Body: {
  subject: "user@example.com",  # 用户唯一标识
  remember: true,               # 记住登录状态
  remember_for: 3600            # 记住时长(秒)
}
   ↓
Hydra 响应:
{
  "redirect_to": "https://auth.example.com/oauth2/auth?consent_challenge=def456..."
}
   ↓
登录页面重定向浏览器到这个 URL

───────────────────────────────────────────────────────────────

[步骤 6] Hydra 创建 Consent Challenge
浏览器 → GET https://auth.example.com/oauth2/auth?consent_challenge=def456
   ↓
Hydra 检查:
用户需要授权同意吗?
- 如果之前同意过 → 跳过同意页面
- 如果是新的 scope → 显示同意页面
   ↓
Hydra 创建 Consent Challenge(同意挑战)
   ↓
重定向到同意页面

───────────────────────────────────────────────────────────────

[步骤 7] 用户看到同意页面
浏览器 → GET https://auth.example.com/consent?consent_challenge=def456
   ↓
同意页面(Consent UI)
┌─────────────────────────────────────────┐
│  Product App 请求访问您的信息           │
│                                          │
│  此应用将能够:                           │
│  ✓ 读取您的基本信息(姓名、邮箱)         │
│  ✓ 访问您的个人资料                      │
│                                          │
│  [    拒绝    ]  [    授权    ]         │
└─────────────────────────────────────────┘

用户点击"授权"

───────────────────────────────────────────────────────────────

[步骤 8] 接受 Consent Challenge
同意页面 → Hydra Admin API
   ↓
PUT https://auth.example.com/admin/oauth2/auth/requests/consent/accept?consent_challenge=def456
Body: {
  "grant_scope": ["openid", "email", "profile"],  # 授予的权限
  "grant_access_token_audience": ["product-api"],
  "session": {
    "access_token": {
      "user_id": "user@example.com",
      "name": "John Doe"
    },
    "id_token": {
      "email": "user@example.com",
      "email_verified": true
    }
  },
  "remember": true,
  "remember_for": 3600
}
   ↓
Hydra 响应:
{
  "redirect_to": "https://product.example.com/oauth2/callback?code=xyz789&state=random-state-string"
}

───────────────────────────────────────────────────────────────

[步骤 9] 回调到应用(OAuth2 Proxy)
浏览器 → GET https://product.example.com/oauth2/callback?code=xyz789&state=random-state-string
   ↓
OAuth2 Proxy 接收到授权码(code)
   ↓
OAuth2 Proxy 验证:
1. state 参数是否匹配?(防止 CSRF)
2. code 是否有效?

───────────────────────────────────────────────────────────────

[步骤 10] 用授权码交换 Access Token
OAuth2 Proxy → Hydra
   ↓
POST https://auth.example.com/oauth2/token
Headers:
  Authorization: Basic base64(client_id:client_secret)
Body:
  grant_type=authorization_code
  code=xyz789
  redirect_uri=https://product.example.com/oauth2/callback
   ↓
Hydra 验证:
1. 授权码是否有效?
2. 授权码是否过期?(通常 10 分钟)
3. 授权码是否已使用?(只能使用一次)
4. client_id 和 client_secret 是否正确?
5. redirect_uri 是否匹配?
   ↓
验证成功!Hydra 返回 Token

───────────────────────────────────────────────────────────────

[步骤 11] Hydra 返回 Token
Hydra 响应:
{
  "access_token": "ory_at_Qo8wMN3IWUlXDYW...",     # 访问令牌
  "token_type": "bearer",
  "expires_in": 3600,                               # 1 小时后过期
  "refresh_token": "ory_rt_5KFpWN8IYU2lXDY...",    # 刷新令牌
  "scope": "openid email profile",
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..."     # ID 令牌(JWT)
}

Token 说明:
- access_token: 用于访问 API 的令牌(不透明令牌)
- refresh_token: 用于获取新的 access_token
- id_token: 包含用户信息的 JWT(可解码)

───────────────────────────────────────────────────────────────

[步骤 12] OAuth2 Proxy 创建 Session
OAuth2 Proxy
   ↓
1. 验证 ID Token:
   - 签名是否有效?(使用 Hydra 的公钥)
   - 是否过期?
   - issuer 是否正确?
   - audience 是否正确?
   ↓
2. 提取用户信息:
   从 ID Token 中解码:
   {
     "sub": "user@example.com",      # 用户 ID
     "name": "John Doe",              # 姓名
     "email": "user@example.com",     # 邮箱
     "email_verified": true,
     "iat": 1234567890,               # 签发时间
     "exp": 1234571490,               # 过期时间
     "iss": "https://auth.example.com", # 签发者
     "aud": "product-client"          # 受众
   }
   ↓
3. 创建 Session Cookie:
   Cookie 名称:_oauth2_proxy
   Cookie 内容(加密):
   {
     "access_token": "ory_at_...",
     "refresh_token": "ory_rt_...",
     "id_token": "eyJhbG...",
     "email": "user@example.com",
     "user": "John Doe",
     "expires": "2024-01-01T12:00:00Z"
   }
   ↓
4. 设置 Cookie:
   Set-Cookie: _oauth2_proxy=encrypted_session_data;
     Path=/;
     Domain=.example.com;
     Secure;
     HttpOnly;
     SameSite=Lax;
     Max-Age=3600

───────────────────────────────────────────────────────────────

[步骤 13] 重定向到原始请求
OAuth2 Proxy → 浏览器
   ↓
HTTP 302 Redirect
Location: https://product.example.com/api/products
Set-Cookie: _oauth2_proxy=...
   ↓
浏览器接收到:
1. Cookie 被保存
2. 自动重定向到原始 URL

Ory Hydra 核心配置文件:

# ============================================================
# Hydra 配置文件详解
# ============================================================

# 1. Hydra 服务器环境变量
environment:
  # 数据库连接
  DSN: postgres://hydra:password@postgres:5432/hydra?sslmode=disable
  
  # URL 配置
  URLS_SELF_ISSUER: https://auth.example.com  # Hydra 的公开 URL
  URLS_CONSENT: https://auth.example.com/consent  # 同意页面 URL
  URLS_LOGIN: https://auth.example.com/login  # 登录页面 URL
  
  # 密钥配置(⭐ 生产环境必须修改)
  SECRETS_SYSTEM: "32-char-random-string-here!"  # 系统密钥(加密数据库)
  SECRETS_COOKIE: "32-char-random-string-here!"  # Cookie 密钥
  
  # Token 过期时间
  TTL_ACCESS_TOKEN: 1h      # Access Token 1 小时
  TTL_REFRESH_TOKEN: 720h   # Refresh Token 30 天
  TTL_ID_TOKEN: 1h          # ID Token 1 小时
  TTL_AUTH_CODE: 10m        # 授权码 10 分钟

---
# 2. 创建 OAuth2 客户端
# 命令方式(进入 Hydra Pod 执行)
kubectl exec -it hydra-xxx -n auth-system -- \
  hydra create client \
    --endpoint http://localhost:4445 \
    --id product-client \                    # ⭐ Client ID
    --secret product-secret-key \            # ⭐ Client Secret
    --grant-types authorization_code,refresh_token \
    --response-types code,id_token \
    --scope openid,offline,email,profile \
    --callbacks https://product.example.com/oauth2/callback  # ⭐ 回调 URL

# YAML 方式(需要使用 Hydra Admin API)
# 无法直接用 YAML,需要通过 API 创建

---
# 3. OAuth2 Proxy 配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: oauth2-proxy-config
  namespace: auth-system
data:
  oauth2_proxy.cfg: |
    # OAuth2 Provider 配置
    provider = "oidc"
    oidc_issuer_url = "https://auth.example.com"  # ⭐ Hydra 的 Issuer URL
    
    # 客户端配置(与 Hydra 中注册的一致)
    client_id = "product-client"                  # ⭐ 与 Hydra 一致
    client_secret = "product-secret-key"          # ⭐ 与 Hydra 一致
    
    # Redirect URL(OAuth2 Proxy 的回调地址)
    redirect_url = "https://product.example.com/oauth2/callback"  # ⭐ 与 Hydra 一致
    
    # Cookie 配置
    cookie_name = "_oauth2_proxy"
    cookie_secret = "32-char-random-string!"      # ⭐ Cookie 加密密钥
    cookie_domains = ".example.com"               # ⭐ Cookie 域名
    cookie_secure = true                          # HTTPS 必须为 true
    cookie_httponly = true
    cookie_samesite = "lax"
    
    # Scope 配置
    scope = "openid email profile offline"        # ⭐ 请求的权限
    
    # Email 域名白名单
    email_domains = "*"                           # 允许所有邮箱
    
    # 跳过认证的路径
    skip_auth_regex = "^/health$"
    skip_auth_regex = "^/public/"
    
    # 上游服务(OAuth2 Proxy 在这里作为授权器,不直接代理)
    upstreams = "static://200"
    
    # 监听地址
    http_address = "0.0.0.0:4180"

Hydra 数据流动:

Hydra 数据库表结构(PostgreSQL):

1. hydra_client(OAuth2 客户端)
   - id: product-client
   - secret: hashed(product-secret-key)
   - redirect_uris: https://product.example.com/oauth2/callback
   - grant_types: authorization_code, refresh_token
   - scope: openid email profile offline

2. hydra_oauth2_authentication_session(登录会话)
   - id: session-123
   - subject: user@example.com
   - remember: true
   - authenticated_at: 2024-01-01 12:00:00

3. hydra_oauth2_authentication_request(登录请求)
   - challenge: abc123
   - client_id: product-client
   - requested_scope: openid email profile
   - state: random-state
   - redirect_uri: https://product.example.com/oauth2/callback

4. hydra_oauth2_consent_request(同意请求)
   - challenge: def456
   - subject: user@example.com
   - requested_scope: openid email profile
   - granted_scope: openid email profile

5. hydra_oauth2_code(授权码)
   - code: xyz789
   - client_id: product-client
   - expires_at: 2024-01-01 12:10:00  # 10 分钟后过期
   - used: false

6. hydra_oauth2_access(Access Token)
   - signature: hashed(ory_at_...)
   - client_id: product-client
   - subject: user@example.com
   - expires_at: 2024-01-01 13:00:00  # 1 小时后过期
   - scope: openid email profile

7. hydra_oauth2_refresh(Refresh Token)
   - signature: hashed(ory_rt_...)
   - client_id: product-client
   - subject: user@example.com
   - expires_at: 2024-01-31 12:00:00  # 30 天后过期

步骤 14:第二次访问(已登录)
用户浏览器(已有 Cookie)
   ↓
GET https://product.example.com/api/products
Cookie: _oauth2_proxy=encrypted_session_data
   ↓
Istio Ingress Gateway
   ↓
调用外部授权器(OAuth2 Proxy)
   ↓
POST http://oauth2-proxy:4180/oauth2/auth
Headers:
  Cookie: _oauth2_proxy=encrypted_session_data
   ↓
OAuth2 Proxy 验证:
1. 解密 Cookie
2. 检查 Session 是否过期
3. 检查 Access Token 是否有效
   ↓
✓ 全部有效!
   ↓
OAuth2 Proxy 返回:200 OK
Headers:
  X-Forwarded-User: user@example.com
  X-Forwarded-Email: user@example.com
  X-Forwarded-Access-Token: ory_at_...
   ↓
Istio Ingress Gateway:授权通过!
   ↓
转发请求到后端服务

步骤 15:请求到达 Kubernetes Service
Envoy(Ingress Gateway)
   ↓
查找 Kubernetes Service: product-manager-service
   ↓
Kubernetes Service(ClusterIP)
作用:
1. 提供稳定的服务发现(DNS: product-manager-service.product-system.svc.cluster.local)
2. 负载均衡到多个 Pod
3. 健康检查:只转发到健康的 Pod

Service 配置位置:

# ============================================================
# 配置文件:product-service-deployment.yaml
# ============================================================

apiVersion: v1
kind: Service
metadata:
  name: product-manager-service  # ⭐ 服务名称(DNS 名称)
  namespace: product-system
spec:
  type: ClusterIP  # 集群内部服务
  
  # 选择器:通过标签选择 Pod
  selector:
    app: product-manager  # ⭐ 匹配 Pod 的标签
    version: v1
  
  # 端口配置
  ports:
    - name: http
      port: 8080        # ⭐ Service 的端口
      targetPort: 8080  # ⭐ Pod 的端口
      protocol: TCP

# Service 的作用:
# 1. DNS: product-manager-service.product-system.svc.cluster.local
# 2. 负载均衡:自动分发到多个 Pod
# 3. Endpoints: 自动跟踪 Pod IP

# 查看 Service 的 Endpoints:
# kubectl get endpoints product-manager-service -n product-system
# 
# 输出示例:
# NAME                      ENDPOINTS
# product-manager-service   10.244.1.5:8080,10.244.2.3:8080
#                           ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                              Pod 1 IP          Pod 2 IP

步骤 16:请求到达应用 Pod
Kubernetes Service
   ↓
选择一个健康的 Pod(轮询或其他算法)
   ↓
Pod IP: 10.244.1.5
   ↓
Pod: product-manager-service-abc123-xyz

步骤 17:Sidecar Envoy 拦截入站流量
┌──────────────────────────────────────────────┐
│  product-manager-service-abc123-xyz Pod     │
│                                              │
│  请求首先到达 Sidecar Envoy(15006 端口)    │
│  ┌────────────────────────────────────────┐ │
│  │  Envoy Sidecar (istio-proxy)          │ │
│  │                                         │ │
│  │  入站监听器:15006                      │ │
│  │  ↓                                      │ │
│  │  执行策略:                              │ │
│  │  1. mTLS 验证(验证来源)              │ │
│  │  2. RBAC 检查(访问控制)              │ │
│  │  3. Rate Limit(速率限制)             │ │
│  │  4. Metrics 收集(记录指标)           │ │
│  │  5. Tracing(分布式追踪)              │ │
│  │  ↓                                      │ │
│  │  转发到 localhost:8080                 │ │
│  └────────────────────────────────────────┘ │
│                  ↓                           │
│  ┌────────────────────────────────────────┐ │
│  │  应用容器 (product-manager)            │ │
│  │                                         │ │
│  │  监听:localhost:8080                  │ │
│  │  处理业务逻辑                           │ │
│  └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘

步骤 18:应用容器处理请求
应用容器(Spring Boot / Node.js / Go 等)
   ↓
接收到请求:
GET /api/products
Headers:
  Host: product-manager-service:8080
  X-Forwarded-User: user@example.com      # OAuth2 Proxy 添加的
  X-Forwarded-Email: user@example.com     # OAuth2 Proxy 添加的
  X-Forwarded-Access-Token: ory_at_...    # OAuth2 Proxy 添加的
  X-Request-ID: uuid-1234...               # Istio 添加的
  X-B3-TraceId: trace-id                   # Istio 追踪 ID
   ↓
处理业务逻辑:
1. 查询数据库
2. 调用其他服务
3. 计算结果
   ↓
返回响应:
HTTP 200 OK
Content-Type: application/json
{
  "products": [...]
}

应用代码示例(获取用户信息):

// Spring Boot 示例
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @GetMapping
    public ResponseEntity<List<Product>> getProducts(
        @RequestHeader(value = "X-Forwarded-User", required = false) String user,
        @RequestHeader(value = "X-Forwarded-Email", required = false) String email
    ) {
        // 从 Header 中获取用户信息(OAuth2 Proxy 已经验证过)
        log.info("User: {} ({}), requesting products", user, email);
        
        // 处理业务逻辑
        List<Product> products = productService.findByUser(user);
        
        return ResponseEntity.ok(products);
    }
}

步骤 19:响应原路返回
应用容器
   ↓
返回响应到 Sidecar Envoy
   ↓
Sidecar Envoy(出站处理)
   ↓
1. 添加响应 Header(X-Envoy-Upstream-Service-Time)
2. 收集 Metrics(响应时间、状态码)
3. 记录 Access Log
   ↓
返回到 Kubernetes Service
   ↓
Kubernetes Service → Istio Ingress Gateway
   ↓
Istio Ingress Gateway → LoadBalancer
   ↓
LoadBalancer → 浏览器
   ↓
浏览器接收响应:
HTTP 200 OK
Content-Type: application/json
Set-Cookie: _oauth2_proxy=... (刷新 Cookie)

{
  "products": [...]
}

6.3 完整配置文件清单

为了实现上述流程,你需要以下配置文件:

# 1. DNS 配置(云服务商控制台)
product.example.com → 120.55.xxx.xxx

# 2. TLS 证书(创建 Secret)
kubectl create secret tls product-tls-secret \
  --cert=product.example.com.crt \
  --key=product.example.com.key \
  -n istio-system

# 3. Istio Gateway
kubectl apply -f istio-gateway.yaml

# 4. VirtualService
kubectl apply -f virtual-service.yaml

# 5. 应用 Deployment 和 Service
kubectl apply -f product-service-deployment.yaml

# 6. Hydra 部署
kubectl apply -f postgres-deployment.yaml
kubectl apply -f hydra-deployment.yaml

# 7. OAuth2 Proxy 部署
kubectl apply -f oauth2-proxy-deployment.yaml

# 8. Istio 外部授权器配置
kubectl apply -f istio-oauth2-authz.yaml

# 9. 创建 OAuth2 客户端
kubectl exec -it hydra-xxx -n auth-system -- \
  hydra create client \
    --endpoint http://localhost:4445 \
    --id product-client \
    --secret product-secret-key \
    --grant-types authorization_code,refresh_token \
    --response-types code,id_token \
    --scope openid,offline,email,profile \
    --callbacks https://product.example.com/oauth2/callback

6.4 Istio vs Kubernetes Ingress Controller 的区别(重要概念澄清)

6.4.1 常见误解

错误理解:

外部请求 → Istio → Pod(Ingress Controller) → Service → 后台 Pod

这个理解混淆了几个概念:

  1. Istio Ingress Gateway ≠ Kubernetes Ingress Controller(这是两个完全不同的东西)
  2. Istio Ingress Gateway 本身就是一个 Pod,不是转发到另一个 Pod
  3. Istio 不经过 Kubernetes Service 的负载均衡

6.4.2 正确的理解

Istio 的实际流程:

┌─────────────────────────────────────────────────────────────────┐
│                     传统 Kubernetes Ingress                      │
└─────────────────────────────────────────────────────────────────┘

外部请求
   ↓
Ingress Controller Pod (Nginx/Traefik)
   ↓
Kubernetes Service (ClusterIP)
   ├─ kube-proxy 负载均衡
   ↓
后台 Pod(应用容器)

特点:
- 使用 Kubernetes Ingress 资源
- 依赖 Service 做负载均衡
- Ingress Controller 是一个独立的组件

┌─────────────────────────────────────────────────────────────────┐
│                          Istio 网格                              │
└─────────────────────────────────────────────────────────────────┘

外部请求
   ↓
Istio Ingress Gateway Pod (Envoy)
   ├─ 不经过 Kubernetes Service 的负载均衡
   ├─ Envoy 直接根据服务发现获取 Pod IP
   ├─ Envoy 自己做负载均衡
   ↓
直接到达后台 Pod IP
   ↓
Sidecar Envoy (istio-proxy)
   ↓
应用容器

特点:
- 使用 Istio Gateway + VirtualService 资源
- 不依赖 Kubernetes Service 的负载均衡
- Envoy 直接连接 Pod IP

6.4.3 详细对比

传统 Kubernetes Ingress 架构:

┌──────────────────────────────────────────────────────────────┐
│  1. Ingress 资源定义规则                                      │
│     apiVersion: networking.k8s.io/v1                         │
│     kind: Ingress                                            │
└──────────────────────────────────────────────────────────────┘
                           ↓
┌──────────────────────────────────────────────────────────────┐
│  2. Ingress Controller Pod(例如 Nginx)                     │
│     - 这是一个独立的 Deployment                              │
│     - 运行 Nginx 或其他反向代理                              │
│     - 读取 Ingress 资源生成配置                              │
└──────────────────────────────────────────────────────────────┘
                           ↓
┌──────────────────────────────────────────────────────────────┐
│  3. Kubernetes Service(ClusterIP)                          │
│     - Service: product-service                               │
│     - ClusterIP: 10.96.123.45                               │
│     - kube-proxy 维护 iptables 规则                         │
└──────────────────────────────────────────────────────────────┘
                           ↓
                    负载均衡(轮询)
                           ↓
┌──────────────────────────────────────────────────────────────┐
│  4. 后台 Pod                                                  │
│     Pod 1: 10.244.1.5:8080                                   │
│     Pod 2: 10.244.2.3:8080                                   │
│     Pod 3: 10.244.3.7:8080                                   │
└──────────────────────────────────────────────────────────────┘

流程说明:
1. Nginx 收到请求 → 查找 Service DNS
2. DNS 返回 Service ClusterIP (10.96.123.45)
3. 请求发送到 ClusterIP
4. kube-proxy (iptables) 拦截,负载均衡到某个 Pod IP
5. 请求到达 Pod

Istio 架构:

┌──────────────────────────────────────────────────────────────┐
│  1. Gateway + VirtualService 资源                            │
│     apiVersion: networking.istio.io/v1beta1                 │
│     kind: Gateway                                            │
│     kind: VirtualService                                     │
└──────────────────────────────────────────────────────────────┘
                           ↓
                      配置推送
                           ↓
┌──────────────────────────────────────────────────────────────┐
│  2. Istiod(控制平面)                                        │
│     - 读取 Gateway/VirtualService 配置                       │
│     - 从 Kubernetes API 获取 Service 的 Endpoints          │
│     - 将配置转换为 Envoy xDS 格式                            │
│     - 推送配置到 Envoy                                       │
└──────────────────────────────────────────────────────────────┘
                           ↓
                     xDS 协议推送
                           ↓
┌──────────────────────────────────────────────────────────────┐
│  3. Istio Ingress Gateway Pod(Envoy)                      │
│     - 接收 Istiod 推送的配置                                 │
│     - 配置包含:                                              │
│       • 路由规则                                              │
│       • Pod IP 列表(从 Endpoints 获取)                     │
│       • 负载均衡算法                                          │
│     - Envoy 自己做负载均衡                                   │
└──────────────────────────────────────────────────────────────┘
                           ↓
                  直接连接 Pod IP
                  (不经过 Service)
                           ↓
┌──────────────────────────────────────────────────────────────┐
│  4. 后台 Pod + Sidecar                                        │
│     Pod 1: 10.244.1.5:8080 + istio-proxy                    │
│     Pod 2: 10.244.2.3:8080 + istio-proxy                    │
│     Pod 3: 10.244.3.7:8080 + istio-proxy                    │
└──────────────────────────────────────────────────────────────┘

流程说明:
1. Envoy 收到请求 → 匹配 VirtualService 规则
2. VirtualService 指向 Service 名称
3. Envoy 从 Istiod 获取的配置中找到 Pod IP 列表
4. Envoy 根据负载均衡算法选择一个 Pod IP
5. Envoy 直接连接到 Pod IP(不经过 Service ClusterIP)
6. 请求到达 Pod 的 Sidecar Envoy
7. Sidecar 转发到应用容器

6.4.4 关键区别总结
维度Kubernetes IngressIstio Gateway
资源类型IngressGateway + VirtualService
实现组件Ingress Controller(Nginx/Traefik)Envoy Proxy
Controller 是什么独立的 Pod(Nginx 等)Istio Ingress Gateway Pod(Envoy)
服务发现通过 DNS 查询 ServiceIstiod 推送 Endpoints
负载均衡kube-proxy (iptables/IPVS)Envoy 自己实现
是否经过 Service✅ 是❌ 否(直接到 Pod)
配置来源Ingress 资源Gateway + VirtualService
Sidecar❌ 无✅ 每个 Pod 都有

6.4.5 Istio Ingress Gateway 本质

Istio Ingress Gateway 是什么?

# Istio Ingress Gateway 实际上就是一个 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-ingressgateway
  namespace: istio-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: istio-ingressgateway
      istio: ingressgateway
  template:
    metadata:
      labels:
        app: istio-ingressgateway
        istio: ingressgateway
    spec:
      containers:
        - name: istio-proxy
          image: docker.io/istio/proxyv2:1.20.1
          # 这就是 Envoy Proxy
          # 它不是 Ingress Controller,就是一个 Envoy

查看 Istio Ingress Gateway Pod:

# 查看 Ingress Gateway Pod
kubectl get pods -n istio-system -l istio=ingressgateway

# 输出:
# NAME                                    READY   STATUS    RESTARTS   AGE
# istio-ingressgateway-7d8f8c9b5-abc123   1/1     Running   0          2d

# 进入 Pod 查看
kubectl exec -it istio-ingressgateway-7d8f8c9b5-abc123 -n istio-system -- ps aux

# 输出:
# USER  PID  COMMAND
# 1337  1    /usr/local/bin/pilot-agent proxy router ...
# 1337  15   /usr/local/bin/envoy -c /etc/istio/proxy/envoy-rev.json ...
#                                  ↑↑↑↑↑
#                            这就是 Envoy 进程

# 这个 Pod 只有一个容器:istio-proxy (Envoy)
# 它不是 Nginx,不是 Traefik,就是 Envoy

6.4.6 Service 在 Istio 中的作用

Service 仍然需要创建,但作用不同:

# 你仍然需要创建 Kubernetes Service
apiVersion: v1
kind: Service
metadata:
  name: product-manager-service
  namespace: product-system
spec:
  type: ClusterIP
  ports:
    - port: 8080
      targetPort: 8080
  selector:
    app: product-manager

但在 Istio 中,Service 的作用变了:

传统方式(Kubernetes Ingress):
Service 的作用:
1. 提供 ClusterIP(负载均衡入口)
2. kube-proxy 维护 iptables 规则
3. 流量经过 Service 的负载均衡

Istio 方式:
Service 的作用:
1. 提供服务名称(DNS 名称)✓
2. 维护 Endpoints(Pod IP 列表)✓
3. 流量不经过 Service 的 ClusterIP ❌
   - Istiod 直接读取 Endpoints
   - Envoy 直接连接 Pod IP
   - 绕过 kube-proxy

验证 Istio 绕过 Service:

# 1. 查看 Service 的 Endpoints
kubectl get endpoints product-manager-service -n product-system

# 输出:
# NAME                      ENDPOINTS
# product-manager-service   10.244.1.5:8080,10.244.2.3:8080

# 2. 查看 Envoy 的 Endpoint 配置
istioctl proxy-config endpoints istio-ingressgateway-xxx.istio-system

# 输出中你会看到:
# ENDPOINT            STATUS    CLUSTER
# 10.244.1.5:8080    HEALTHY   outbound|8080||product-manager-service.product-system.svc.cluster.local
# 10.244.2.3:8080    HEALTHY   outbound|8080||product-manager-service.product-system.svc.cluster.local
#   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
# Envoy 直接知道 Pod IP,不需要查询 Service ClusterIP

# 3. 在 Envoy 配置中查找 Service ClusterIP
kubectl exec istio-ingressgateway-xxx -n istio-system -- \
  curl localhost:15000/config_dump | grep "10.96.123.45"

# 你会发现:找不到 Service 的 ClusterIP!
# Envoy 的配置中只有 Pod IP,没有 Service ClusterIP

6.4.7 完整的数据流对比

场景:浏览器访问 https://product.example.com/api/products

方式 1:Kubernetes Ingress

1. 浏览器 → DNS → LoadBalancer IP
   ↓
2. LoadBalancer → Nginx Ingress Controller Pod
   ↓
3. Nginx 查找路由规则:
   Host: product.example.com
   Path: /api/products
   → Backend: product-manager-service:8080
   ↓
4. Nginx 发起请求:
   GET http://product-manager-service:8080/api/products
   ↓
5. DNS 解析:
   product-manager-service → 10.96.123.45 (Service ClusterIP)
   ↓
6. 请求发送到 10.96.123.45:8080
   ↓
7. kube-proxy (iptables) 拦截:
   DNAT: 10.96.123.45:8080 → 10.244.1.5:8080 (Pod IP)
   ↓
8. 请求到达 Pod: 10.244.1.5:8080
   ↓
9. 应用容器处理请求

方式 2:Istio Gateway

1. 浏览器 → DNS → LoadBalancer IP
   ↓
2. LoadBalancer → Istio Ingress Gateway Pod
   ↓
3. Envoy 匹配规则:
   Gateway: 检查 Host 和 Port
   VirtualService: 检查 Path
   → Destination: product-manager-service
   ↓
4. Envoy 查找配置(已经从 Istiod 获取):
   Service: product-manager-service
   Endpoints:
     - 10.244.1.5:8080
     - 10.244.2.3:8080
   LoadBalancer: LEAST_REQUEST
   ↓
5. Envoy 选择一个 Endpoint:
   选中: 10.244.1.5:8080
   ↓
6. Envoy 直接建立连接:
   TCP 连接到 10.244.1.5:8080
   (不经过 Service ClusterIP 10.96.123.45)
   ↓
7. 请求到达 Pod: 10.244.1.5:8080
   ↓
8. Sidecar Envoy (istio-proxy) 接收请求
   - 执行 mTLS 验证
   - 执行访问控制
   - 记录指标
   ↓
9. Sidecar 转发到 localhost:8080
   ↓
10. 应用容器处理请求

6.4.8 为什么 Istio 要绕过 Service?

优势:

  1. 更灵活的负载均衡

    # Kubernetes Service 只支持:
    - 轮询(Round Robin)
    - 随机(Random)
    
    # Istio 支持:
    - ROUND_ROBIN:轮询
    - LEAST_REQUEST:最少请求
    - RANDOM:随机
    - CONSISTENT_HASH:一致性哈希
    - RING_HASH:环形哈希
    
  2. 高级路由能力

    # 基于权重的流量分割(金丝雀发布)
    - destination: v1
      weight: 90
    - destination: v2
      weight: 10
    
  3. 更好的性能

    Kubernetes Service:
    Nginx → DNS 查询 → Service ClusterIP → iptables → Pod
    (多个网络跳转)
    
    Istio:
    Envoy → 直接连接 Pod IP
    (减少网络跳转)
    
  4. 完整的可观测性

    Envoy 可以收集:
    - 每个请求的延迟
    - 每个 Pod 的响应时间
    - 每个 Pod 的健康状态
    - 分布式追踪信息
    

6.4.9 总结

正确的理解:

Istio Ingress Gateway 是什么?

  • 一个运行 Envoy 的 Pod
  • 部署在 istio-system 命名空间
  • 不是 Kubernetes Ingress Controller

请求流程:

外部请求
  → Istio Ingress Gateway Pod (Envoy)
  → 直接连接后台 Pod IP
  → Sidecar Envoy
  → 应用容器

Service 的作用:

  • 提供服务名称(DNS)
  • 维护 Endpoints(Pod IP 列表)
  • Istiod 读取 Endpoints 推送给 Envoy
  • 流量不经过 Service 的 ClusterIP

关键概念:

  • Istio Ingress Gateway ≠ Ingress Controller
  • Envoy 直接连接 Pod IP
  • 不依赖 kube-proxy

错误理解:

  • Istio 转发到 Ingress Controller Pod ❌
  • 经过 Service 的负载均衡 ❌
  • Pod 是 Ingress Controller ❌

6.5 配置文件的位置和流转详解

很多人疑惑:Envoy 的配置到底在哪里?是怎么生成的?让我详细解释。

6.5.1 配置的三个层次
┌─────────────────────────────────────────────────────────────┐
│  层次 1:你编写的配置(Kubernetes YAML)                      │
│  位置:你的配置文件目录                                        │
└─────────────────────────────────────────────────────────────┘
                       ↓
                  kubectl apply
                       ↓
┌─────────────────────────────────────────────────────────────┐
│  层次 2:Kubernetes API Server 存储                          │
│  位置:etcd 数据库                                           │
└─────────────────────────────────────────────────────────────┘
                       ↓
                 Istiod 监听
                       ↓
┌─────────────────────────────────────────────────────────────┐
│  层次 3:Envoy 运行时配置(xDS)                             │
│  位置:Envoy 内存 + /etc/istio/proxy/                       │
└─────────────────────────────────────────────────────────────┘

6.5.2 层次 1:你编写的 YAML 配置文件

这些是你需要创建和编辑的配置文件:

# 你的项目目录结构
my-istio-config/
├── 1-gateway.yaml              # ← Gateway 配置
├── 2-virtualservice.yaml       # ← VirtualService 配置
├── 3-destinationrule.yaml      # ← DestinationRule 配置
├── 4-service.yaml              # ← Kubernetes Service 配置
└── 5-deployment.yaml           # ← 应用 Deployment 配置

1. Gateway 配置(定义监听规则)

# ============================================================
# 文件位置:你的配置目录/gateway.yaml
# 作用:告诉 Envoy 监听哪些端口和域名
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: product-gateway
  namespace: product-system
spec:
  selector:
    istio: ingressgateway  # 选择 Ingress Gateway Pod
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "product.example.com"  # ← 你配置:接受哪个域名
      tls:
        mode: SIMPLE
        credentialName: product-tls-secret  # ← 你配置:使用哪个证书

2. VirtualService 配置(定义路由规则)

# ============================================================
# 文件位置:你的配置目录/virtualservice.yaml
# 作用:告诉 Envoy 如何路由请求
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-virtualservice
  namespace: product-system
spec:
  hosts:
    - "product.example.com"  # ← 你配置:匹配哪个域名
  gateways:
    - product-gateway
  http:
    - match:
        - uri:
            prefix: "/api/products"  # ← 你配置:匹配哪个路径
      route:
        - destination:
            host: product-manager-service  # ← 你配置:转发到哪个服务
            port:
              number: 8080

3. DestinationRule 配置(定义流量策略)

# ============================================================
# 文件位置:你的配置目录/destinationrule.yaml
# 作用:告诉 Envoy 负载均衡算法和连接池配置
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: product-destination-rule
  namespace: product-system
spec:
  host: product-manager-service
  trafficPolicy:
    loadBalancer:
      simple: LEAST_REQUEST  # ← 你配置:负载均衡算法
    connectionPool:
      tcp:
        maxConnections: 100  # ← 你配置:最大连接数

4. Service 配置(Kubernetes Service)

# ============================================================
# 文件位置:你的配置目录/service.yaml
# 作用:提供服务名称和 Pod 选择器
# ============================================================

apiVersion: v1
kind: Service
metadata:
  name: product-manager-service
  namespace: product-system
spec:
  selector:
    app: product-manager  # ← 你配置:选择哪些 Pod
  ports:
    - port: 8080
      targetPort: 8080

应用配置:

# 将配置提交到 Kubernetes
kubectl apply -f 1-gateway.yaml
kubectl apply -f 2-virtualservice.yaml
kubectl apply -f 3-destinationrule.yaml
kubectl apply -f 4-service.yaml
kubectl apply -f 5-deployment.yaml

6.5.3 层次 2:Kubernetes API Server(etcd 存储)
当你执行 kubectl apply 后:

┌─────────────────────────────────────────────────────────┐
│  Kubernetes API Server                                  │
│  ┌───────────────────────────────────────────────────┐ │
│  │  etcd 数据库                                       │ │
│  │  ├─ Gateway/product-gateway                       │ │
│  │  ├─ VirtualService/product-virtualservice         │ │
│  │  ├─ DestinationRule/product-destination-rule     │ │
│  │  ├─ Service/product-manager-service               │ │
│  │  └─ Endpoints/product-manager-service             │ │
│  │     (自动生成,包含 Pod IP 列表)                   │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

查看存储在 API Server 的配置:

# 查看 Gateway 配置
kubectl get gateway product-gateway -n product-system -o yaml

# 查看 VirtualService 配置
kubectl get virtualservice product-virtualservice -n product-system -o yaml

# 查看 DestinationRule 配置
kubectl get destinationrule product-destination-rule -n product-system -o yaml

# 查看 Service 的 Endpoints(重要!这是 Pod IP 列表)
kubectl get endpoints product-manager-service -n product-system -o yaml

# 输出示例:
apiVersion: v1
kind: Endpoints
metadata:
  name: product-manager-service
  namespace: product-system
subsets:
  - addresses:
      - ip: 10.244.1.5  # ← Pod 1 的 IP
        targetRef:
          kind: Pod
          name: product-manager-service-abc123-xyz
      - ip: 10.244.2.3  # ← Pod 2 的 IP
        targetRef:
          kind: Pod
          name: product-manager-service-def456-uvw
    ports:
      - port: 8080
        protocol: TCP

6.5.4 层次 3:Istiod 的转换过程
┌─────────────────────────────────────────────────────────┐
│  Istiod(控制平面)                                      │
│                                                          │
│  1. 监听 Kubernetes API                                 │
│     ↓                                                    │
│  2. 读取配置资源                                         │
│     - Gateway                                           │
│     - VirtualService                                    │
│     - DestinationRule                                   │
│     - Service Endpoints                                 │
│     ↓                                                    │
│  3. 转换为 Envoy xDS 配置                               │
│     ┌─────────────────────────────────────────────┐    │
│     │  LDS (Listener Discovery Service)          │    │
│     │  监听器配置:监听哪些端口                    │    │
│     ├─────────────────────────────────────────────┤    │
│     │  RDS (Route Discovery Service)              │    │
│     │  路由配置:如何匹配和路由请求                │    │
│     ├─────────────────────────────────────────────┤    │
│     │  CDS (Cluster Discovery Service)            │    │
│     │  集群配置:上游服务配置                       │    │
│     ├─────────────────────────────────────────────┤    │
│     │  EDS (Endpoint Discovery Service)           │    │
│     │  端点配置:Pod IP 列表                       │    │
│     └─────────────────────────────────────────────┘    │
│     ↓                                                    │
│  4. 通过 gRPC 推送到 Envoy                              │
└─────────────────────────────────────────────────────────┘

转换示例:

你的 Gateway YAML:
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
spec:
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "product.example.com"

            ↓ Istiod 转换
            
Envoy LDS 配置(JSON 格式):
{
  "name": "0.0.0.0_8443",
  "address": {
    "socketAddress": {
      "address": "0.0.0.0",
      "portValue": 8443
    }
  },
  "filterChains": [{
    "filterChainMatch": {
      "serverNames": ["product.example.com"]
    },
    "filters": [...]
  }]
}

查看 Istiod 的日志:

# 查看 Istiod 日志(可以看到配置推送)
kubectl logs -f deployment/istiod -n istio-system

# 输出示例:
# 2024-01-01T12:00:00.123Z info ads Push debounce stable[1] 1: ...
# 2024-01-01T12:00:00.456Z info ads LDS: PUSH for node:istio-ingressgateway-xxx
# 2024-01-01T12:00:00.789Z info ads RDS: PUSH for node:istio-ingressgateway-xxx

6.5.5 层次 4:Envoy 运行时配置
┌─────────────────────────────────────────────────────────┐
│  Istio Ingress Gateway Pod                              │
│  (istio-ingressgateway-xxx)                             │
│                                                          │
│  ┌────────────────────────────────────────────────────┐ │
│  │  Envoy Proxy 进程                                  │ │
│  │                                                     │ │
│  │  配置来源 1:Bootstrap 配置(启动时)              │ │
│  │  位置:/etc/istio/proxy/envoy-rev.json            │ │
│  │  内容:静态配置,包含如何连接 Istiod               │ │
│  │                                                     │ │
│  │  配置来源 2:动态配置(运行时,从 Istiod 获取)    │ │
│  │  位置:内存中(动态更新)                          │ │
│  │  内容:                                             │ │
│  │  ├─ Listeners(监听器)                            │ │
│  │  ├─ Routes(路由规则)                             │ │
│  │  ├─ Clusters(上游服务)                           │ │
│  │  └─ Endpoints(Pod IP 列表)                      │ │
│  │                                                     │ │
│  │  配置来源 3:证书和密钥                            │ │
│  │  位置:/etc/istio/ingressgateway-certs/            │ │
│  │  内容:TLS 证书(来自 Kubernetes Secret)          │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

查看 Envoy 的配置:

# 方法 1:查看完整的 Envoy 配置
kubectl exec istio-ingressgateway-xxx -n istio-system -- \
  curl -s localhost:15000/config_dump > envoy-config.json

# 这个文件包含所有配置,可能有几万行

# 方法 2:查看 Bootstrap 配置(静态配置)
kubectl exec istio-ingressgateway-xxx -n istio-system -- \
  cat /etc/istio/proxy/envoy-rev.json

# 方法 3:使用 istioctl 查看配置(推荐)
# 查看监听器
istioctl proxy-config listeners istio-ingressgateway-xxx.istio-system

# 输出示例:
# ADDRESS       PORT  TYPE
# 0.0.0.0       8443  HTTPS   # ← 来自你的 Gateway 配置

# 查看路由
istioctl proxy-config routes istio-ingressgateway-xxx.istio-system

# 输出示例:
# NAME          DOMAINS                    MATCH                 VIRTUAL SERVICE
# http.8443     product.example.com        /api/products*        product-virtualservice

# 查看集群(上游服务)
istioctl proxy-config clusters istio-ingressgateway-xxx.istio-system

# 输出示例:
# SERVICE FQDN                                          PORT      TYPE
# product-manager-service.product-system.svc.cluster.local  8080      EDS

# 查看端点(Pod IP)⭐ 重点
istioctl proxy-config endpoints istio-ingressgateway-xxx.istio-system

# 输出示例:
# ENDPOINT            STATUS    OUTLIER CHECK     CLUSTER
# 10.244.1.5:8080     HEALTHY   OK                outbound|8080||product-manager-service.product-system.svc.cluster.local
# 10.244.2.3:8080     HEALTHY   OK                outbound|8080||product-manager-service.product-system.svc.cluster.local
#   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
# 这些就是 Pod IP!来自 Service 的 Endpoints

6.5.6 完整的配置流转图
┌─────────────────────────────────────────────────────────────────┐
│  第 1 步:你编写 YAML 配置                                        │
│  你的电脑/CI/CD 系统                                              │
│  ├─ gateway.yaml                                                │
│  ├─ virtualservice.yaml                                         │
│  └─ destinationrule.yaml                                        │
└─────────────────────────────────────────────────────────────────┘
                            ↓
                     kubectl apply
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  第 2 步:Kubernetes API Server 存储                             │
│  etcd 数据库                                                     │
│  ├─ Gateway 资源                                                │
│  ├─ VirtualService 资源                                         │
│  ├─ DestinationRule 资源                                        │
│  ├─ Service 资源                                                │
│  └─ Endpoints 资源(自动生成,包含 Pod IP)                      │
└─────────────────────────────────────────────────────────────────┘
                            ↓
                   Istiod 监听(Watch)
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  第 3 步:Istiod 读取并转换                                       │
│  Istiod Pod (istio-system)                                      │
│  ├─ 读取 Gateway → 转换为 Listener 配置                          │
│  ├─ 读取 VirtualService → 转换为 Route 配置                      │
│  ├─ 读取 DestinationRule → 转换为 Cluster 配置                   │
│  ├─ 读取 Service → 查找对应的 Endpoints                          │
│  └─ 读取 Endpoints → 转换为 Endpoint 配置(Pod IP 列表)         │
│                                                                  │
│  转换为 Envoy xDS 格式:                                          │
│  ├─ LDS (Listener Discovery Service)                            │
│  ├─ RDS (Route Discovery Service)                               │
│  ├─ CDS (Cluster Discovery Service)                             │
│  └─ EDS (Endpoint Discovery Service)                            │
└─────────────────────────────────────────────────────────────────┘
                            ↓
                     gRPC 推送(xDS 协议)
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  第 4 步:Envoy 接收并应用配置                                    │
│  Istio Ingress Gateway Pod                                      │
│  (istio-ingressgateway-xxx)                                     │
│                                                                  │
│  Envoy Proxy 内存中的配置:                                       │
│  ├─ Listeners: 0.0.0.0:8443 (HTTPS)                            │
│  ├─ Routes: product.example.com /api/products → cluster        │
│  ├─ Clusters: product-manager-service                           │
│  └─ Endpoints: [10.244.1.5:8080, 10.244.2.3:8080]             │
│                                                                  │
│  证书文件(来自 Kubernetes Secret):                             │
│  └─ /etc/istio/ingressgateway-certs/tls.crt                    │
└─────────────────────────────────────────────────────────────────┘
                            ↓
                     现在可以处理请求了!

6.5.7 实际操作示例

完整的配置和验证流程:

# ============================================================
# 步骤 1:创建配置文件
# ============================================================

# 创建目录
mkdir -p ~/istio-config
cd ~/istio-config

# 创建 Gateway 配置
cat > gateway.yaml <<EOF
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: product-gateway
  namespace: product-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "product.example.com"
      tls:
        mode: SIMPLE
        credentialName: product-tls-secret
EOF

# 创建 VirtualService 配置
cat > virtualservice.yaml <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-virtualservice
  namespace: product-system
spec:
  hosts:
    - "product.example.com"
  gateways:
    - product-gateway
  http:
    - match:
        - uri:
            prefix: "/api/products"
      route:
        - destination:
            host: product-manager-service
            port:
              number: 8080
EOF

# 创建 DestinationRule 配置
cat > destinationrule.yaml <<EOF
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: product-destination-rule
  namespace: product-system
spec:
  host: product-manager-service
  trafficPolicy:
    loadBalancer:
      simple: LEAST_REQUEST
    connectionPool:
      tcp:
        maxConnections: 100
EOF

# ============================================================
# 步骤 2:应用配置到 Kubernetes
# ============================================================

kubectl apply -f gateway.yaml
kubectl apply -f virtualservice.yaml
kubectl apply -f destinationrule.yaml

# ============================================================
# 步骤 3:验证配置已存储在 API Server
# ============================================================

# 查看 Gateway
kubectl get gateway product-gateway -n product-system

# 查看 VirtualService
kubectl get virtualservice product-virtualservice -n product-system

# 查看 DestinationRule
kubectl get destinationrule product-destination-rule -n product-system

# ============================================================
# 步骤 4:验证 Istiod 已推送配置到 Envoy
# ============================================================

# 查看 Envoy 的同步状态
istioctl proxy-status

# 输出示例:
# NAME                                     CDS      LDS      EDS      RDS      ISTIOD
# istio-ingressgateway-xxx.istio-system    SYNCED   SYNCED   SYNCED   SYNCED   istiod-yyy
#                                          ↑↑↑↑↑↑   ↑↑↑↑↑↑   ↑↑↑↑↑↑   ↑↑↑↑↑↑
#                                          全部同步成功!

# ============================================================
# 步骤 5:查看 Envoy 的具体配置
# ============================================================

# 获取 Ingress Gateway Pod 名称
GATEWAY_POD=$(kubectl get pods -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')

# 查看监听器(来自 Gateway 配置)
istioctl proxy-config listeners $GATEWAY_POD.istio-system --port 8443

# 查看路由(来自 VirtualService 配置)
istioctl proxy-config routes $GATEWAY_POD.istio-system --name https.8443

# 查看集群(来自 DestinationRule 配置)
istioctl proxy-config clusters $GATEWAY_POD.istio-system --fqdn product-manager-service.product-system.svc.cluster.local

# 查看端点(来自 Service Endpoints)⭐ 最重要
istioctl proxy-config endpoints $GATEWAY_POD.istio-system --cluster "outbound|8080||product-manager-service.product-system.svc.cluster.local"

# 输出示例:
# ENDPOINT            STATUS    OUTLIER CHECK     CLUSTER
# 10.244.1.5:8080     HEALTHY   OK                outbound|8080||product-manager-service...
# 10.244.2.3:8080     HEALTHY   OK                outbound|8080||product-manager-service...

# ============================================================
# 步骤 6:对比 Service Endpoints
# ============================================================

# 查看 Service 的 Endpoints
kubectl get endpoints product-manager-service -n product-system

# 输出示例:
# NAME                      ENDPOINTS
# product-manager-service   10.244.1.5:8080,10.244.2.3:8080

# 你会发现 Envoy 的 Endpoints 和 Service 的 Endpoints 完全一致!
# 这证明 Istiod 读取了 Service Endpoints 并推送给了 Envoy

6.5.8 配置文件总结

你需要编辑的配置文件:

1. Gateway.yaml          → 定义监听端口、域名、TLS
2. VirtualService.yaml   → 定义路由规则
3. DestinationRule.yaml  → 定义负载均衡、连接池
4. Service.yaml          → 提供服务名称和 Pod 选择器
5. Deployment.yaml       → 部署应用 Pod

自动生成的配置:

1. Service Endpoints     → Kubernetes 自动维护(Pod IP 列表)
2. Envoy xDS 配置        → Istiod 自动转换并推送
3. Envoy 运行时配置      → Envoy 内存中动态更新

配置存储位置:

1. 你的文件系统         → gateway.yaml, virtualservice.yaml
2. Kubernetes etcd      → Gateway, VirtualService 资源
3. Istiod 内存          → 转换后的 xDS 配置
4. Envoy 内存           → 运行时配置
5. Envoy 文件系统       → Bootstrap 配置 + TLS 证书

6.6 VirtualService 深度解析

6.6.1 VirtualService 是什么意思?

VirtualService = 虚拟服务 = 路由规则

简单理解:

VirtualService 就像一个"路由器"或"交通警察":
- 告诉 Envoy:"当收到什么样的请求时,应该转发到哪里"
- 类似 Nginx 的 location 配置
- 但功能更强大,支持高级路由

为什么叫"虚拟"服务?

对比理解:

Kubernetes Service(真实服务):
- 对应真实的 Pod
- 有实际的 ClusterIP
- 实际处理流量

VirtualService(虚拟服务):
- 不对应真实的 Pod
- 没有实际的 IP
- 只是一个"路由配置"
- 定义如何将流量路由到真实服务

类比:

VirtualService = 导航软件
- 告诉你怎么走
- 不是目的地本身

Kubernetes Service = 实际的建筑物
- 真实存在
- 有具体地址

6.6.2 VirtualService 的核心作用

作用 1:路径路由(最基础)

# 根据请求路径路由到不同服务
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-routes
  namespace: default
spec:
  hosts:
    - "example.com"
  gateways:
    - my-gateway
  http:
    # 规则 1:/api/products → 产品服务
    - match:
        - uri:
            prefix: "/api/products"
      route:
        - destination:
            host: product-service
    
    # 规则 2:/api/orders → 订单服务
    - match:
        - uri:
            prefix: "/api/orders"
      route:
        - destination:
            host: order-service
    
    # 规则 3:/api/users → 用户服务
    - match:
        - uri:
            prefix: "/api/users"
      route:
        - destination:
            host: user-service

作用 2:金丝雀发布(流量分割)

# 将流量按比例分配到不同版本
spec:
  http:
    - route:
        - destination:
            host: product-service
            subset: v1
          weight: 90  # 90% 流量到旧版本
        - destination:
            host: product-service
            subset: v2
          weight: 10  # 10% 流量到新版本

作用 3:高级匹配(Header、Method、Query)

# 根据 HTTP Header 路由
spec:
  http:
    - match:
        - headers:
            user-type:
              exact: "premium"  # VIP 用户
      route:
        - destination:
            host: product-service-premium
    
    - match:
        - headers:
            user-type:
              exact: "free"  # 免费用户
      route:
        - destination:
            host: product-service-basic

6.6.3 可以直接根据域名匹配吗?✅ 可以!

答案:可以!这是非常常见的使用方式。

场景:不同域名路由到不同服务

# ============================================================
# 多域名路由配置
# 场景:一个 Gateway 处理多个域名
# ============================================================

# 1. Gateway 配置(接受多个域名)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: multi-domain-gateway
  namespace: default
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "api.example.com"      # API 域名
        - "www.example.com"      # 网站域名
        - "admin.example.com"    # 管理后台域名
      tls:
        mode: SIMPLE
        credentialName: wildcard-tls-secret  # 使用通配符证书

---
# 2. VirtualService 1:API 域名 → 后端服务
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-routes
  namespace: default
spec:
  hosts:
    - "api.example.com"  # ← 只匹配这个域名
  gateways:
    - multi-domain-gateway
  http:
    - route:
        - destination:
            host: backend-api-service
            port:
              number: 8080

---
# 3. VirtualService 2:网站域名 → 前端服务
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-routes
  namespace: default
spec:
  hosts:
    - "www.example.com"  # ← 只匹配这个域名
  gateways:
    - multi-domain-gateway
  http:
    - route:
        - destination:
            host: frontend-web-service
            port:
              number: 80

---
# 4. VirtualService 3:管理后台域名 → 管理服务
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: admin-routes
  namespace: default
spec:
  hosts:
    - "admin.example.com"  # ← 只匹配这个域名
  gateways:
    - multi-domain-gateway
  http:
    - route:
        - destination:
            host: admin-service
            port:
              number: 8080

工作原理:

请求 1:https://api.example.com/anything
  ↓
Gateway 检查:api.example.com ✓
  ↓
VirtualService 1 匹配:hosts = api.example.com ✓
  ↓
路由到:backend-api-service

---

请求 2:https://www.example.com/index.html
  ↓
Gateway 检查:www.example.com ✓
  ↓
VirtualService 2 匹配:hosts = www.example.com ✓
  ↓
路由到:frontend-web-service

---

请求 3:https://admin.example.com/dashboard
  ↓
Gateway 检查:admin.example.com ✓
  ↓
VirtualService 3 匹配:hosts = admin.example.com ✓
  ↓
路由到:admin-service

6.6.4 前后端分离项目的配置方案

典型的前后端分离架构:

前端:React/Vue/Angular 单页应用(SPA)
  - 部署为静态文件(Nginx 容器)
  - 域名:www.example.com
  - 提供:HTML, CSS, JS 文件

后端:Spring Boot/Go/Node.js API
  - 提供 RESTful API
  - 域名:api.example.com 或 www.example.com/api
  - 提供:JSON 数据

方案 1:使用不同的域名(推荐)

# ============================================================
# 前后端分离方案 1:不同域名
# 前端:www.example.com
# 后端:api.example.com
# ============================================================

# 1. Gateway 配置(接受两个域名)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: frontend-backend-gateway
  namespace: my-app
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "www.example.com"     # 前端域名
        - "api.example.com"     # 后端域名
      tls:
        mode: SIMPLE
        credentialName: example-tls-secret  # 通配符证书 *.example.com

---
# 2. VirtualService for 前端
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: frontend-routes
  namespace: my-app
spec:
  hosts:
    - "www.example.com"  # 前端域名
  gateways:
    - frontend-backend-gateway
  http:
    - route:
        - destination:
            host: frontend-service  # Nginx Pod(提供静态文件)
            port:
              number: 80

---
# 3. VirtualService for 后端
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: backend-routes
  namespace: my-app
spec:
  hosts:
    - "api.example.com"  # 后端 API 域名
  gateways:
    - frontend-backend-gateway
  http:
    - route:
        - destination:
            host: backend-service  # Spring Boot/Node.js API
            port:
              number: 8080

---
# 4. 前端 Deployment(Nginx + 静态文件)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          volumeMounts:
            - name: static-files
              mountPath: /usr/share/nginx/html
            - name: nginx-config
              mountPath: /etc/nginx/conf.d
      volumes:
        - name: static-files
          # 选项 1:从镜像中包含
          # 选项 2:从 ConfigMap
          # 选项 3:从 PVC
          configMap:
            name: frontend-static-files
        - name: nginx-config
          configMap:
            name: frontend-nginx-config

---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: my-app
spec:
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 80

---
# 5. 后端 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: my-backend:v1
          ports:
            - containerPort: 8080
          env:
            - name: CORS_ALLOWED_ORIGINS
              value: "https://www.example.com"  # 允许前端跨域

---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: my-app
spec:
  selector:
    app: backend
  ports:
    - port: 8080
      targetPort: 8080

使用方式:

// 前端代码(React/Vue)
// 访问:https://www.example.com

// API 调用(使用不同的域名)
fetch('https://api.example.com/api/products')
  .then(response => response.json())
  .then(data => console.log(data));

// CORS 配置(后端需要配置)
// Spring Boot 示例:
@CrossOrigin(origins = "https://www.example.com")
@RestController
public class ProductController { ... }

优点:

  • ✅ 前后端完全分离
  • ✅ 可以独立部署
  • ✅ 清晰的域名区分
  • ✅ 便于 CDN 加速前端

缺点:

  • ⚠️ 需要处理 CORS
  • ⚠️ 需要两个域名

方案 2:使用相同域名 + 路径区分(常用)

# ============================================================
# 前后端分离方案 2:同域名不同路径
# 前端:www.example.com/        (所有静态文件)
# 后端:www.example.com/api/*   (所有 API 请求)
# ============================================================

# 1. Gateway 配置(一个域名)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: app-gateway
  namespace: my-app
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "www.example.com"  # 只需要一个域名
      tls:
        mode: SIMPLE
        credentialName: example-tls-secret

---
# 2. VirtualService(一个配置,多个路由规则)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app-routes
  namespace: my-app
spec:
  hosts:
    - "www.example.com"  # 同一个域名
  gateways:
    - app-gateway
  http:
    # ========================================
    # 规则 1:API 请求 → 后端服务
    # 优先级最高,放在最前面
    # ========================================
    - name: "backend-api-route"
      match:
        - uri:
            prefix: "/api/"  # 所有 /api/* 的请求
      route:
        - destination:
            host: backend-service  # 转发到后端
            port:
              number: 8080
      timeout: 30s
      corsPolicy:  # 可选:在 Gateway 层处理 CORS
        allowOrigins:
          - exact: "https://www.example.com"
        allowMethods:
          - GET
          - POST
          - PUT
          - DELETE
        allowHeaders:
          - authorization
          - content-type
        maxAge: "24h"
    
    # ========================================
    # 规则 2:静态资源 → 前端服务
    # 匹配 JS、CSS、图片等
    # ========================================
    - name: "frontend-static-route"
      match:
        - uri:
            regex: ".*\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$"
      route:
        - destination:
            host: frontend-service  # Nginx 静态文件服务
            port:
              number: 80
      # 静态资源缓存配置
      headers:
        response:
          add:
            cache-control: "public, max-age=31536000"  # 1 年缓存
    
    # ========================================
    # 规则 3:所有其他请求 → 前端服务(SPA 路由)
    # 必须放在最后(兜底规则)
    # ========================================
    - name: "frontend-spa-route"
      route:
        - destination:
            host: frontend-service
            port:
              number: 80

---
# 3. 前端 Service 和 Deployment
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: my-app
spec:
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: default.conf
      volumes:
        - name: nginx-config
          configMap:
            name: frontend-nginx-config

---
# 4. 前端 Nginx 配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: frontend-nginx-config
  namespace: my-app
data:
  default.conf: |
    server {
        listen 80;
        server_name _;
        root /usr/share/nginx/html;
        index index.html;
        
        # SPA 路由支持(重要!)
        # 所有请求都返回 index.html
        location / {
            try_files $uri $uri/ /index.html;
        }
        
        # API 请求不应该到这里
        # 但如果到了,返回 404
        location /api/ {
            return 404 "API requests should go to backend";
        }
    }

---
# 5. 后端 Service 和 Deployment
apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: my-app
spec:
  selector:
    app: backend
  ports:
    - port: 8080
      targetPort: 8080

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: my-backend-api:v1
          ports:
            - containerPort: 8080
          env:
            - name: CORS_ALLOWED_ORIGINS
              value: "https://www.example.com"  # 允许前端跨域

请求流程示例:

场景 1:用户访问前端页面
浏览器 → https://www.example.com/
  ↓
Gateway 匹配:www.example.com ✓
  ↓
VirtualService 匹配:
  - 不匹配 /api/* ✗
  - 不匹配静态资源正则 ✗
  - 匹配默认规则 ✓
  ↓
路由到:frontend-service
  ↓
Nginx 返回:index.html

---

场景 2:前端 JS 加载
浏览器 → https://www.example.com/static/js/main.js
  ↓
Gateway 匹配:www.example.com ✓
  ↓
VirtualService 匹配:
  - 不匹配 /api/* ✗
  - 匹配静态资源正则(.*\\.js$)✓
  ↓
路由到:frontend-service
  ↓
Nginx 返回:main.js + Cache-Control header

---

场景 3:前端调用 API
浏览器 → https://www.example.com/api/products
  ↓
Gateway 匹配:www.example.com ✓
  ↓
VirtualService 匹配:
  - 匹配 /api/* ✓(第一个规则)
  ↓
路由到:backend-service
  ↓
Spring Boot 返回:JSON 数据

优点:

  • ✅ 只需要一个域名
  • ✅ 不需要处理 CORS(同域名)
  • ✅ 配置简单

缺点:

  • ⚠️ 前端无法使用 CDN(如果需要)
  • ⚠️ 路由规则需要仔细排序

方案 3:混合方案(推荐生产环境)

# ============================================================
# 前后端分离方案 3:混合方案
# 前端:www.example.com(CDN 加速)
# 后端:api.example.com(通过 Istio)
# 前端管理后台:admin.example.com(通过 Istio)
# ============================================================

# 1. Gateway 配置
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: app-gateway
  namespace: my-app
spec:
  selector:
    istio: ingressgateway
  servers:
    # 后端 API 域名
    - port:
        number: 443
        name: https-api
        protocol: HTTPS
      hosts:
        - "api.example.com"
      tls:
        mode: SIMPLE
        credentialName: api-tls-secret
    
    # 管理后台域名
    - port:
        number: 443
        name: https-admin
        protocol: HTTPS
      hosts:
        - "admin.example.com"
      tls:
        mode: SIMPLE
        credentialName: admin-tls-secret

---
# 2. VirtualService for 后端 API
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-routes
  namespace: my-app
spec:
  hosts:
    - "api.example.com"
  gateways:
    - app-gateway
  http:
    # 产品 API
    - match:
        - uri:
            prefix: "/api/products"
      route:
        - destination:
            host: product-service
            port:
              number: 8080
    
    # 订单 API
    - match:
        - uri:
            prefix: "/api/orders"
      route:
        - destination:
            host: order-service
            port:
              number: 8080
    
    # 用户 API
    - match:
        - uri:
            prefix: "/api/users"
      route:
        - destination:
            host: user-service
            port:
              number: 8080

---
# 3. VirtualService for 管理后台
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: admin-routes
  namespace: my-app
spec:
  hosts:
    - "admin.example.com"
  gateways:
    - app-gateway
  http:
    # 管理后台 API
    - match:
        - uri:
            prefix: "/api/"
      route:
        - destination:
            host: admin-backend-service
            port:
              number: 8080
    
    # 管理后台前端(React/Vue)
    - route:
        - destination:
            host: admin-frontend-service
            port:
              number: 80

架构说明:

┌─────────────────────────────────────────────────────────┐
│  用户端(C 端)                                          │
│  https://www.example.com                                │
│  ↓                                                       │
│  CDN(阿里云 CDN/CloudFlare)                            │
│  - 缓存静态文件(HTML/CSS/JS/图片)                      │
│  - 全球加速                                              │
│  - 不经过 Istio                                          │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  API 端(后端)                                          │
│  https://api.example.com                                │
│  ↓                                                       │
│  Istio Ingress Gateway                                  │
│  ↓                                                       │
│  VirtualService 路由                                     │
│  ├─ /api/products → product-service                    │
│  ├─ /api/orders → order-service                        │
│  └─ /api/users → user-service                          │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  管理端(B 端)                                          │
│  https://admin.example.com                              │
│  ↓                                                       │
│  Istio Ingress Gateway                                  │
│  ↓                                                       │
│  VirtualService 路由                                     │
│  ├─ /api/* → admin-backend-service                     │
│  └─ /* → admin-frontend-service (React 后台)           │
└─────────────────────────────────────────────────────────┘

6.6.5 前后端分离的完整示例

真实项目结构:

电商项目前后端分离配置

前端(用户端 Web):
- 域名:www.mall.com
- 技术:React + Nginx
- 部署:CDN(静态资源)
- API 调用:https://api.mall.com

前端(管理后台):
- 域名:admin.mall.com
- 技术:Vue + Nginx
- 部署:Kubernetes + Istio
- API 调用:https://admin.mall.com/api

后端(微服务):
- 域名:api.mall.com
- 服务:
  ├─ product-service(商品服务)
  ├─ order-service(订单服务)
  ├─ user-service(用户服务)
  ├─ cart-service(购物车服务)
  └─ payment-service(支付服务)

完整配置:

# ============================================================
# 电商项目 Istio 配置
# ============================================================

# 1. Gateway(两个域名)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: mall-gateway
  namespace: mall-system
spec:
  selector:
    istio: ingressgateway
  servers:
    # API 域名
    - port:
        number: 443
        name: https-api
        protocol: HTTPS
      hosts:
        - "api.mall.com"
      tls:
        mode: SIMPLE
        credentialName: api-mall-tls
    
    # 管理后台域名
    - port:
        number: 443
        name: https-admin
        protocol: HTTPS
      hosts:
        - "admin.mall.com"
      tls:
        mode: SIMPLE
        credentialName: admin-mall-tls

---
# 2. VirtualService for API(后端微服务)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-routes
  namespace: mall-system
spec:
  hosts:
    - "api.mall.com"
  gateways:
    - mall-gateway
  http:
    # 商品 API
    - name: "product-api"
      match:
        - uri:
            prefix: "/api/products"
        - uri:
            prefix: "/api/categories"
      route:
        - destination:
            host: product-service.mall-system.svc.cluster.local
            port:
              number: 8080
    
    # 订单 API
    - name: "order-api"
      match:
        - uri:
            prefix: "/api/orders"
        - uri:
            prefix: "/api/cart"
      route:
        - destination:
            host: order-service.mall-system.svc.cluster.local
            port:
              number: 8080
    
    # 用户 API
    - name: "user-api"
      match:
        - uri:
            prefix: "/api/users"
        - uri:
            prefix: "/api/auth"
      route:
        - destination:
            host: user-service.mall-system.svc.cluster.local
            port:
              number: 8080
    
    # 支付 API
    - name: "payment-api"
      match:
        - uri:
            prefix: "/api/payments"
      route:
        - destination:
            host: payment-service.mall-system.svc.cluster.local
            port:
              number: 8080

---
# 3. VirtualService for 管理后台
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: admin-routes
  namespace: mall-system
spec:
  hosts:
    - "admin.mall.com"
  gateways:
    - mall-gateway
  http:
    # 管理后台 API
    - name: "admin-backend-api"
      match:
        - uri:
            prefix: "/api/"
      route:
        - destination:
            host: admin-backend-service
            port:
              number: 8080
    
    # 管理后台前端(Vue SPA)
    - name: "admin-frontend"
      route:
        - destination:
            host: admin-frontend-service
            port:
              number: 80

前端代码示例:

// ============================================================
// 前端(用户端)- React
// 部署在:https://www.mall.com(CDN)
// ============================================================

// src/config/api.js
const API_BASE_URL = 'https://api.mall.com';  // 后端 API 域名

// src/services/productService.js
export const getProducts = async () => {
  const response = await fetch(`${API_BASE_URL}/api/products`, {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getAccessToken()}`  // JWT Token
    },
    credentials: 'include'  // 携带 Cookie
  });
  return response.json();
};

export const createOrder = async (orderData) => {
  const response = await fetch(`${API_BASE_URL}/api/orders`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getAccessToken()}`
    },
    body: JSON.stringify(orderData),
    credentials: 'include'
  });
  return response.json();
};

// ============================================================
// 管理后台(B 端)- Vue
// 部署在:https://admin.mall.com(Kubernetes + Istio)
// ============================================================

// src/config/api.js
const API_BASE_URL = '/api';  // 同域名,相对路径

// src/services/productService.js
export const getProducts = async () => {
  // 请求 URL:https://admin.mall.com/api/products
  const response = await axios.get(`${API_BASE_URL}/products`, {
    headers: {
      'Authorization': `Bearer ${getAccessToken()}`
    }
  });
  return response.data;
};

6.6.6 域名路由完整示例

场景:一个 Gateway 处理多个完全不同的应用

# ============================================================
# 多应用域名路由
# ============================================================

# 1. Gateway(接受多个域名)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: multi-app-gateway
  namespace: istio-system  # 可以放在 istio-system(全局)
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - "*.example.com"  # 通配符,接受所有子域名
      tls:
        mode: SIMPLE
        credentialName: wildcard-example-tls  # 通配符证书

---
# 2. VirtualService for 应用 A
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app-a-routes
  namespace: app-a-system
spec:
  hosts:
    - "app-a.example.com"  # 应用 A 的域名
  gateways:
    - istio-system/multi-app-gateway  # 跨命名空间引用
  http:
    - route:
        - destination:
            host: app-a-service.app-a-system.svc.cluster.local
            port:
              number: 8080

---
# 3. VirtualService for 应用 B
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app-b-routes
  namespace: app-b-system
spec:
  hosts:
    - "app-b.example.com"  # 应用 B 的域名
  gateways:
    - istio-system/multi-app-gateway
  http:
    - route:
        - destination:
            host: app-b-service.app-b-system.svc.cluster.local
            port:
              number: 9090

---
# 4. VirtualService for 应用 C
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app-c-routes
  namespace: app-c-system
spec:
  hosts:
    - "app-c.example.com"  # 应用 C 的域名
  gateways:
    - istio-system/multi-app-gateway
  http:
    - route:
        - destination:
            host: app-c-service.app-c-system.svc.cluster.local
            port:
              number: 3000

工作方式:

一个 Gateway,多个 VirtualService

Gateway 说:
"我接受 *.example.com 的所有请求"

VirtualService A 说:
"如果域名是 app-a.example.com,给我处理"

VirtualService B 说:
"如果域名是 app-b.example.com,给我处理"

VirtualService C 说:
"如果域名是 app-c.example.com,给我处理"

请求流程:
https://app-a.example.com/xxx
  ↓
Gateway 检查:匹配 *.example.com ✓
  ↓
查找 VirtualService:
  - app-a-routes: hosts=app-a.example.com ✓ 选这个
  - app-b-routes: hosts=app-b.example.com ✗
  - app-c-routes: hosts=app-c.example.com ✗
  ↓
路由到:app-a-service

6.7 Istio Gateway vs Kubernetes Ingress:是否可以替代?

6.7.1 直接回答你的问题

✅ 是的,使用了 Istio Gateway 后,Kubernetes Ingress Controller 可以完全不用!

使用 Istio Gateway 后:

❌ 不需要:Nginx Ingress Controller
❌ 不需要:Traefik
❌ 不需要:HAProxy Ingress
❌ 不需要:任何其他 Ingress Controller

✅ 只需要:Istio Ingress Gateway + VirtualService

原因:

Istio Gateway 已经提供了:
1. 外部流量接入(LoadBalancer/NodePort)
2. TLS/SSL 证书管理
3. 域名路由
4. 路径路由
5. 负载均衡
6. 流量管理

这些功能完全覆盖了 Ingress Controller 的所有功能,并且更强大!

6.7.2 架构对比

传统架构:使用 Kubernetes Ingress

┌─────────────────────────────────────────────────────────┐
│  外部流量                                                │
│  https://example.com                                    │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  Ingress Controller Pod (Nginx/Traefik)                │
│  - 读取 Ingress 资源                                     │
│  - 生成 Nginx 配置                                       │
│  - 处理 TLS 终止                                         │
│  - 转发到 Service                                        │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  Kubernetes Service (ClusterIP)                         │
│  - kube-proxy 负载均衡                                   │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  应用 Pod                                                │
│  ┌──────────────────┐                                   │
│  │  应用容器        │  ← 只有一个容器                    │
│  └──────────────────┘                                   │
└─────────────────────────────────────────────────────────┘

配置资源:
- Ingress(定义路由规则)
- Service(服务发现)
- Deployment(应用部署)

使用 Istio 后的架构

┌─────────────────────────────────────────────────────────┐
│  外部流量                                                │
│  https://example.com                                    │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  Istio Ingress Gateway Pod (Envoy)                     │
│  - 读取 Gateway 和 VirtualService 资源                  │
│  - Envoy 动态配置                                        │
│  - 处理 TLS 终止                                         │
│  - 直接转发到 Pod IP(不经过 Service ClusterIP)        │
└─────────────────────────────────────────────────────────┘
                       ↓
                 直接连接 Pod IP
           (绕过 Service 的负载均衡)
                       ↓
┌─────────────────────────────────────────────────────────┐
│  应用 Pod                                                │
│  ┌────────────────┐  ┌─────────────────┐              │
│  │ istio-proxy    │→ │ 应用容器        │              │
│  │ (Sidecar Envoy)│  │                 │              │
│  └────────────────┘  └─────────────────┘              │
└─────────────────────────────────────────────────────────┘

配置资源:
- Gateway(定义监听规则)
- VirtualService(定义路由规则)
- Service(仅用于服务发现,不做负载均衡)
- Deployment(应用部署)

关键区别:

项目Kubernetes IngressIstio Gateway
入口组件Nginx/Traefik PodIstio Ingress Gateway Pod (Envoy)
配置方式Ingress 资源Gateway + VirtualService
是否经过 Service✅ 是(通过 ClusterIP)❌ 否(直接连接 Pod IP)
负载均衡kube-proxyEnvoy
TLS 管理Ingress 配置Gateway TLS 配置
高级路由❌ 有限✅ 强大(Header、金丝雀等)
服务网格❌ 无✅ 有 Sidecar
可观测性❌ 基础✅ 完整(指标、追踪、日志)

6.7.3 Ingress 与 Istio 的功能对比

Kubernetes Ingress 能做的:

# Kubernetes Ingress 示例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - example.com
      secretName: example-tls
  rules:
    - host: example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080
          - path: /web
            pathType: Prefix
            backend:
              service:
                name: web-service
                port:
                  number: 80

功能清单:

  • ✅ 基于域名路由
  • ✅ 基于路径路由
  • ✅ TLS 终止
  • ❌ 流量分割(金丝雀)
  • ❌ Header 路由
  • ❌ 超时重试
  • ❌ 熔断
  • ❌ 流量镜像
  • ❌ 详细指标

Istio Gateway + VirtualService 能做的:

# Istio Gateway 示例(功能更强大)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: example-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - example.com
      tls:
        mode: SIMPLE
        credentialName: example-tls

---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: example-routes
spec:
  hosts:
    - example.com
  gateways:
    - example-gateway
  http:
    # 高级功能 1:金丝雀发布(流量分割)
    - match:
        - uri:
            prefix: "/api"
      route:
        - destination:
            host: api-service
            subset: v1
          weight: 90  # 90% 到旧版本
        - destination:
            host: api-service
            subset: v2
          weight: 10  # 10% 到新版本
      timeout: 30s
      retries:
        attempts: 3
        perTryTimeout: 10s
    
    # 高级功能 2:基于 Header 路由
    - match:
        - uri:
            prefix: "/web"
          headers:
            user-type:
              exact: "premium"  # VIP 用户
      route:
        - destination:
            host: web-service-premium
    
    # 高级功能 3:流量镜像(影子流量)
    - match:
        - uri:
            prefix: "/web"
      route:
        - destination:
            host: web-service
      mirror:
        host: web-service-test  # 复制流量到测试环境
        port:
          number: 80
      mirrorPercentage:
        value: 10.0  # 镜像 10% 的流量

功能清单:

  • ✅ 基于域名路由
  • ✅ 基于路径路由
  • ✅ TLS 终止
  • ✅ 流量分割(金丝雀) ⭐
  • ✅ Header 路由 ⭐
  • ✅ 超时重试 ⭐
  • ✅ 熔断 ⭐
  • ✅ 流量镜像 ⭐
  • ✅ 详细指标和追踪 ⭐
  • ✅ mTLS 加密 ⭐
  • ✅ 授权策略 ⭐

6.7.4 完全替代的实战示例

场景:从 Nginx Ingress 迁移到 Istio Gateway

迁移前(使用 Nginx Ingress):

# ============================================================
# 旧配置:Kubernetes Ingress
# ============================================================

# 1. 安装 Nginx Ingress Controller
# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

# 2. 创建 Ingress 资源
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: my-app
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - www.example.com
        - api.example.com
      secretName: example-tls
  rules:
    # 前端
    - host: www.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80
    
    # 后端 API
    - host: api.example.com
      http:
        paths:
          - path: /api/products
            pathType: Prefix
            backend:
              service:
                name: product-service
                port:
                  number: 8080
          - path: /api/orders
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 8080

迁移后(使用 Istio Gateway):

# ============================================================
# 新配置:Istio Gateway + VirtualService
# ============================================================

# 步骤 1:安装 Istio(替代 Nginx Ingress Controller)
# istioctl install --set profile=default -y

# 步骤 2:启用 Sidecar 注入
# kubectl label namespace my-app istio-injection=enabled

# 步骤 3:创建 Gateway(替代 Ingress)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-app-gateway
  namespace: my-app
spec:
  selector:
    istio: ingressgateway  # 使用 Istio Ingress Gateway Pod
  servers:
    - port:
        number: 443
        name: https
        protocol: HTTPS
      hosts:
        - www.example.com
        - api.example.com
      tls:
        mode: SIMPLE
        credentialName: example-tls  # 使用相同的 TLS Secret

---
# 步骤 4:创建 VirtualService(替代 Ingress rules)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: frontend-routes
  namespace: my-app
spec:
  hosts:
    - www.example.com
  gateways:
    - my-app-gateway
  http:
    - route:
        - destination:
            host: frontend-service  # 相同的 Service 名称
            port:
              number: 80

---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: backend-routes
  namespace: my-app
spec:
  hosts:
    - api.example.com
  gateways:
    - my-app-gateway
  http:
    # 产品 API
    - match:
        - uri:
            prefix: "/api/products"
      route:
        - destination:
            host: product-service  # 相同的 Service 名称
            port:
              number: 8080
    
    # 订单 API
    - match:
        - uri:
            prefix: "/api/orders"
      route:
        - destination:
            host: order-service
            port:
              number: 8080

---
# Service 配置(保持不变)
# 应用 Pod 自动注入 Sidecar

迁移步骤:

# 1. 安装 Istio
istioctl install --set profile=default -y

# 2. 启用 Sidecar 自动注入
kubectl label namespace my-app istio-injection=enabled

# 3. 创建 Gateway 和 VirtualService
kubectl apply -f istio-gateway.yaml
kubectl apply -f istio-virtualservice.yaml

# 4. 重启 Pod 使 Sidecar 生效
kubectl rollout restart deployment -n my-app

# 5. 验证流量正常
curl -v https://www.example.com
curl -v https://api.example.com/api/products

# 6. 确认无误后,卸载 Nginx Ingress Controller
kubectl delete ingress my-app-ingress -n my-app
kubectl delete namespace ingress-nginx

# 迁移完成!

6.7.5 保留 Ingress Controller 的场景

虽然可以完全替代,但某些情况下可能暂时保留:

场景 1:渐进式迁移

同时运行两者,逐步迁移:

┌─────────────────────────────────────────────┐
│  旧应用(未启用 Istio)                      │
│  ↓                                          │
│  Nginx Ingress Controller                  │
│  ↓                                          │
│  Service → Pod(无 Sidecar)               │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│  新应用(已启用 Istio)                      │
│  ↓                                          │
│  Istio Gateway                              │
│  ↓                                          │
│  Service → Pod(有 Sidecar)               │
└─────────────────────────────────────────────┘

配置示例:

# 保留 Nginx Ingress(处理旧应用)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: old-app-ingress
  namespace: old-app
spec:
  ingressClassName: nginx
  rules:
    - host: old.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: old-service
                port:
                  number: 80

---
# 使用 Istio Gateway(处理新应用)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: new-app-gateway
  namespace: new-app
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - new.example.com
      port:
        number: 443
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: new-tls

但建议:尽快完成迁移,移除 Ingress Controller


6.7.6 推荐的架构选择

决策树:

是否需要服务网格功能?
├─ 是 → 使用 Istio Gateway(完全替代 Ingress)
│       ✅ 微服务架构
│       ✅ 需要金丝雀发布
│       ✅ 需要流量管理
│       ✅ 需要 mTLS
│       ✅ 需要详细可观测性
│
└─ 否 → 考虑是否需要 Istio
        ├─ 简单应用 → 使用 Nginx Ingress
        │              ✅ 单体应用
        │              ✅ 简单路由
        │              ✅ 资源有限
        │
        └─ 未来可能扩展 → 使用 Istio Gateway
                           ✅ 为未来准备
                           ✅ 避免二次迁移

推荐方案表:

场景推荐理由
新项目(微服务)✅ Istio Gateway功能强大,避免后续迁移
大型微服务架构✅ Istio Gateway必需的流量管理和可观测性
简单单体应用⚠️ Nginx Ingress简单够用,资源占用少
已有 Istio✅ Istio Gateway一致性,避免重复组件
Legacy 应用 + 新应用🔄 两者共存(临时)渐进式迁移,但尽快统一

6.7.7 完全替代后的清理工作

如果决定完全使用 Istio Gateway,清理步骤:

# ============================================================
# 清理 Nginx Ingress Controller
# ============================================================

# 1. 查看所有 Ingress 资源
kubectl get ingress --all-namespaces

# 2. 删除所有 Ingress 资源
kubectl delete ingress <ingress-name> -n <namespace>

# 3. 卸载 Nginx Ingress Controller
kubectl delete namespace ingress-nginx

# 或者使用 Helm(如果是 Helm 安装的)
helm uninstall nginx-ingress -n ingress-nginx

# 4. 验证清理完成
kubectl get pods --all-namespaces | grep ingress
# 应该没有 nginx-ingress 相关的 Pod

# ============================================================
# 确认 Istio Gateway 正常工作
# ============================================================

# 1. 检查 Istio Ingress Gateway
kubectl get pods -n istio-system -l istio=ingressgateway

# 2. 检查 Gateway 和 VirtualService
kubectl get gateway,virtualservice --all-namespaces

# 3. 查看同步状态
istioctl proxy-status

# 4. 测试访问
curl -v https://your-domain.com

# 5. 查看流量指标
istioctl dashboard kiali

6.7.8 总结对比表
项目Kubernetes Ingress + NginxIstio Gateway
组件数量Nginx Controller Pod + IngressIstio Ingress Gateway + Gateway + VirtualService
流量路径Nginx → Service ClusterIP → kube-proxy → PodEnvoy → 直接连接 Pod IP
配置方式Ingress YAMLGateway + VirtualService YAML
TLS 管理Ingress tls 配置Gateway tls 配置
基础路由✅ 支持✅ 支持
高级路由❌ 有限(需要注解)✅ 强大(原生支持)
金丝雀发布⚠️ 复杂(需要插件)✅ 原生支持
流量镜像❌ 不支持✅ 支持
熔断重试❌ 不支持✅ 支持
mTLS❌ 不支持✅ 支持
可观测性⚠️ 基础日志✅ 完整指标+追踪
Sidecar❌ 无✅ 有
资源占用💚 低🟡 中等(但值得)
学习曲线💚 简单🟡 复杂(但文档完善)
是否可以完全替代-是的,完全可以!

6.7.9 最佳实践建议

我的推荐:

✅ 推荐:完全使用 Istio Gateway,不要混用

理由:
1. ✅ 避免组件重复
2. ✅ 统一流量管理
3. ✅ 更好的可观测性
4. ✅ 避免配置冲突
5. ✅ 降低维护成本
6. ✅ 功能更强大

例外情况:
- 渐进式迁移期间可以短期共存
- 但应该尽快完成迁移,移除 Ingress Controller

推荐架构(纯 Istio):

外部流量
  ↓
Istio Ingress Gateway(唯一入口)
  ↓
Gateway + VirtualService(路由规则)
  ↓
应用 Pod(带 Sidecar)

优点:
✅ 架构简洁
✅ 配置统一
✅ 功能强大
✅ 维护简单

不推荐(混用):

外部流量
  ↓
┌─────────────────┬─────────────────┐
│ Nginx Ingress   │ Istio Gateway   │  ← 两个入口,复杂!
└─────────────────┴─────────────────┘
  ↓                      ↓
旧应用(无 Sidecar)   新应用(有 Sidecar)

缺点:
❌ 架构复杂
❌ 维护成本高
❌ 配置容易冲突
❌ 资源占用多

6.8 核心概念深度解析(回答所有疑惑)

让我逐一回答你的问题,彻底理解 Istio 的工作流程。


6.8.1 问题1:istio: ingressgateway 是从哪里来的?

答案:这是 Istio 安装时自动创建的 Ingress Gateway Pod 的标签。

详细解释:

# 当你安装 Istio 时
istioctl install --set profile=default -y

# Istio 会在 istio-system 命名空间自动创建一个 Ingress Gateway Pod
kubectl get pods -n istio-system

# 输出示例:
NAME                                    READY   STATUS    LABELS
istio-ingressgateway-7d8f8c9b5-abc123   1/1     Running   app=istio-ingressgateway,istio=ingressgateway
                                                           ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
                                                           这就是标签!

查看这个 Pod 的完整信息:

# 查看 Ingress Gateway Pod 的标签
kubectl get pods -n istio-system -l istio=ingressgateway -o yaml

# 输出(简化版):
apiVersion: v1
kind: Pod
metadata:
  name: istio-ingressgateway-7d8f8c9b5-abc123
  namespace: istio-system
  labels:
    app: istio-ingressgateway
    istio: ingressgateway        # ← 这就是我们在 Gateway 中使用的标签!
    version: 1.20.0
spec:
  containers:
    - name: istio-proxy
      image: docker.io/istio/proxyv2:1.20.0
      ports:
        - containerPort: 8080
        - containerPort: 8443
        - containerPort: 15021

所以在 Gateway 配置中:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: mall-gateway
spec:
  selector:
    istio: ingressgateway  # ← 这里是在"选择"具有这个标签的 Pod
                           # 意思是:"使用标签为 istio=ingressgateway 的 Pod 来处理流量"

工作原理:

1. Istiod 读取 Gateway 配置
   ↓
2. 看到 selector: istio=ingressgateway
   ↓
3. 查找 Kubernetes 中所有标签为 istio=ingressgateway 的 Pod
   ↓
4. 找到:istio-ingressgateway-7d8f8c9b5-abc123
   ↓
5. 将 Gateway 的配置推送到这个 Pod 中的 Envoy
   ↓
6. 这个 Pod 现在就知道如何处理外部流量了

验证命令:

# 1. 查看 Ingress Gateway Pod
kubectl get pods -n istio-system -l istio=ingressgateway

# 2. 查看这个 Pod 的完整标签
kubectl get pods -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].metadata.labels}'

# 输出:
# {"app":"istio-ingressgateway","istio":"ingressgateway","version":"1.20.0"}
#                                 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                                 这就是我们要的标签!

# 3. 查看 Gateway 资源使用了哪个 selector
kubectl get gateway mall-gateway -n mall-system -o yaml

# 输出会显示:
spec:
  selector:
    istio: ingressgateway  # ← 匹配上面的标签

类比理解:

Gateway 的 selector = 快递单上的"收件人姓名"

istio: ingressgateway = Envoy Pod 的"姓名标签"

Istiod 的工作 = 快递员,根据"收件人姓名"找到对应的 Pod,把配置送过去

6.8.2 问题2:kind: Gateway 有什么作用?

答案:Gateway 定义"外部流量如何进入 Kubernetes 集群"。

Gateway 的三个核心作用:

apiVersion: networking.istio.io/v1beta1
kind: Gateway  # ← Kubernetes 自定义资源类型(CRD)
metadata:
  name: mall-gateway
spec:
  # 作用 1:选择哪个 Pod 处理流量
  selector:
    istio: ingressgateway  # 选择 Ingress Gateway Pod
  
  # 作用 2:定义监听哪些端口和协议
  servers:
    - port:
        number: 443       # 监听端口
        name: https
        protocol: HTTPS   # 协议类型
      
      # 作用 3:定义接受哪些域名
      hosts:
        - "api.mall.com"
        - "admin.mall.com"
      
      # 作用 4:TLS 配置
      tls:
        mode: SIMPLE
        credentialName: tls-secret

Gateway 的角色类比:

Gateway = 大楼的"前台接待"

作用:
1. 决定哪个门卫(Pod)来值班
2. 告诉门卫:"我们只接待预约了的客人(域名),只在工作时间(端口)接待"
3. 门卫按照这个规则工作

但 Gateway 不决定:
❌ 客人进来后去哪个办公室(这是 VirtualService 的工作)
❌ 办公室里有几个人(这是 Service 和 Deployment 的工作)

Gateway 类型(kind)是 Kubernetes CRD:

# Gateway 是 Istio 定义的自定义资源
kubectl get crd gateways.networking.istio.io

# 输出:
NAME                              CREATED AT
gateways.networking.istio.io      2024-01-01T10:00:00Z

# 这意味着 Gateway 是 Istio 扩展 Kubernetes 的一种资源类型
# 类似于 Deployment、Service、Pod 等内置类型

Gateway vs VirtualService 的区别:

┌─────────────────────────────────────────────────────────┐
│  Gateway:管理"入口"                                     │
│  ├─ 哪个 Pod 处理?(selector)                          │
│  ├─ 监听哪些端口?(port)                               │
│  ├─ 接受哪些域名?(hosts)                              │
│  └─ TLS 配置?(tls)                                    │
└─────────────────────────────────────────────────────────┘
                       ↓
            外部流量通过 Gateway 进来
                       ↓
┌─────────────────────────────────────────────────────────┐
│  VirtualService:管理"路由"                              │
│  ├─ 哪个路径?(/api/products)                         │
│  ├─ 转发到哪里?(product-service)                     │
│  ├─ 超时重试?(timeout, retries)                      │
│  └─ 流量分割?(weight)                                │
└─────────────────────────────────────────────────────────┘

6.8.3 问题3:gateways: - mall-gateway 的作用

答案:这是 VirtualService 在"声明"自己要关联到哪个 Gateway。

详细解释:

# VirtualService 配置
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-routes
  namespace: mall-system
spec:
  hosts:
    - "api.mall.com"  # 匹配的域名
  gateways:
    - mall-gateway    # ← 关联到 mall-gateway(上面定义的 Gateway)
                      # 意思是:"我的路由规则要应用到 mall-gateway 这个入口上"
  http:
    - match:
        - uri:
            prefix: "/api/products"
      route:
        - destination:
            host: product-service

工作流程:

步骤 1:外部请求到达
https://api.mall.com/api/products
  ↓
步骤 2:Envoy 在 Gateway 中检查
Gateway: mall-gateway
  - hosts: api.mall.com ✓ 匹配!
  ↓
步骤 3:Envoy 查找关联到 mall-gateway 的 VirtualService
查找条件:
  - gateways 字段包含 "mall-gateway"
  - hosts 字段包含 "api.mall.com"
  ↓
找到:api-routes VirtualService ✓
  ↓
步骤 4:应用 VirtualService 的路由规则
匹配:uri.prefix = "/api/products" ✓
  ↓
步骤 5:转发到目标
destination: product-service

关联关系图:

Gateway (mall-gateway)
  ↓ 关联
VirtualService (api-routes)
  gateways: [mall-gateway]
  
关系:
- Gateway 定义"入口"
- VirtualService 通过 gateways 字段"挂靠"到这个入口
- 一个 Gateway 可以被多个 VirtualService 使用

示例:一个 Gateway,多个 VirtualService

# 1. 一个 Gateway
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: mall-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - "api.mall.com"
        - "admin.mall.com"
        - "www.mall.com"

---
# 2. VirtualService A(处理 api.mall.com)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-routes
spec:
  hosts:
    - "api.mall.com"
  gateways:
    - mall-gateway  # ← 关联到 mall-gateway
  http:
    - route:
        - destination:
            host: backend-service

---
# 3. VirtualService B(处理 admin.mall.com)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: admin-routes
spec:
  hosts:
    - "admin.mall.com"
  gateways:
    - mall-gateway  # ← 也关联到 mall-gateway
  http:
    - route:
        - destination:
            host: admin-service

---
# 3. VirtualService C(处理 www.mall.com)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-routes
spec:
  hosts:
    - "www.mall.com"
  gateways:
    - mall-gateway  # ← 还是关联到 mall-gateway
  http:
    - route:
        - destination:
            host: web-service

工作方式:

                mall-gateway (入口)
                      ↓
        ┌─────────────┼─────────────┐
        ↓             ↓             ↓
   api-routes    admin-routes   web-routes
   (VS A)         (VS B)         (VS C)
        ↓             ↓             ↓
  backend-svc   admin-svc     web-svc

请求流程:
https://api.mall.com → mall-gateway → api-routes → backend-svc
https://admin.mall.com → mall-gateway → admin-routes → admin-svc
https://www.mall.com → mall-gateway → web-routes → web-svc

6.8.4 问题4:host: product-service.mall-system.svc.cluster.local 是路由到 Service 吗?

答案:是的!但实际流量不经过 Service 的 ClusterIP。

详细解释:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: product-service.mall-system.svc.cluster.local
            # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
            # 这是 Kubernetes Service 的 FQDN(完全限定域名)
            
            # 格式:<service-name>.<namespace>.svc.cluster.local
            # 分解:
            # - product-service:Service 名称
            # - mall-system:命名空间
            # - svc.cluster.local:Kubernetes 固定后缀

host 字段的含义:

host 字段 = 目标服务的名称(引用 Kubernetes Service)

但要注意:
✅ VirtualService 确实"引用"了 Service
❌ 流量并不经过 Service 的 ClusterIP
✅ Istio 用 Service 查找 Pod IP,然后直接连接 Pod

实际工作流程:

步骤 1:你配置 VirtualService
destination:
  host: product-service.mall-system.svc.cluster.local

步骤 2:Istiod 读取这个配置
"好的,目标是 product-service"

步骤 3:Istiod 查找对应的 Kubernetes Service
kubectl get svc product-service -n mall-system

输出:
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)
product-service   ClusterIP   10.96.123.45   <none>        8080/TCP

步骤 4:Istiod 查找 Service 的 Endpoints
kubectl get endpoints product-service -n mall-system

输出:
NAME              ENDPOINTS
product-service   10.244.1.5:8080,10.244.2.3:8080,10.244.3.7:8080
                  ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
                  这些是 Pod 的 IP 地址!

步骤 5:Istiod 将 Pod IP 列表推送给 Envoy
Envoy 配置:
Cluster: outbound|8080||product-service.mall-system.svc.cluster.local
  Endpoints:
    - 10.244.1.5:8080
    - 10.244.2.3:8080
    - 10.244.3.7:8080

步骤 6:当请求到达时,Envoy 直接连接 Pod IP
请求 → Envoy → 选择 10.244.1.5:8080 → 直接连接
              (不经过 10.96.123.45,即 Service ClusterIP)

验证:

# 1. 查看 Service
kubectl get svc product-service -n mall-system

# 输出:
# NAME              TYPE        CLUSTER-IP     PORT(S)
# product-service   ClusterIP   10.96.123.45   8080/TCP
#                               ↑↑↑↑↑↑↑↑↑↑↑↑
#                               这个 IP 不会被 Istio 使用

# 2. 查看 Endpoints(Pod IP)
kubectl get endpoints product-service -n mall-system

# 输出:
# NAME              ENDPOINTS
# product-service   10.244.1.5:8080,10.244.2.3:8080
#                   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                   Istio 使用这些 Pod IP

# 3. 查看 Envoy 的 Endpoints 配置
GATEWAY_POD=$(kubectl get pods -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')
istioctl proxy-config endpoints $GATEWAY_POD.istio-system \
  --cluster "outbound|8080||product-service.mall-system.svc.cluster.local"

# 输出:
# ENDPOINT            STATUS    OUTLIER CHECK     CLUSTER
# 10.244.1.5:8080     HEALTHY   OK                outbound|8080||product-service...
# 10.244.2.3:8080     HEALTHY   OK                outbound|8080||product-service...
#   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#   直接使用 Pod IP,不是 Service ClusterIP(10.96.123.45)

所以:

host: product-service  → 引用 Service(用于查找 Pod)
实际流量路径 → 直接到 Pod IP(不经过 Service ClusterIP)

Service 的作用变成了:
✅ 提供服务名称(DNS)
✅ 维护 Pod IP 列表(Endpoints)
❌ 不做负载均衡(Envoy 做负载均衡)

6.8.5 问题5:number: 8080 是什么意思?

答案:目标服务(Pod)监听的端口号。

详细解释:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: product-service
            port:
              number: 8080  # ← 目标 Pod 的端口号
                            # 意思是:"把请求发送到 Pod 的 8080 端口"

完整的端口链路:

┌─────────────────────────────────────────────────────────┐
│  1. 外部请求                                             │
│     https://api.mall.com/api/products                   │
│     端口:443(HTTPS 默认端口)                          │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  2. Gateway 监听端口                                     │
│     Gateway:                                            │
│       servers:                                          │
│         - port:                                         │
│             number: 443  ← Gateway 监听 443 端口        │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  3. Istio Ingress Gateway Pod                           │
│     容器端口映射:                                       │
│     - containerPort: 8443  (内部端口)                 │
│     - Service 映射:443 → 8443                          │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  4. VirtualService 路由到目标                            │
│     destination:                                        │
│       host: product-service                             │
│       port:                                             │
│         number: 8080  ← 转发到目标 Pod 的 8080 端口     │
└─────────────────────────────────────────────────────────┘
                       ↓
┌─────────────────────────────────────────────────────────┐
│  5. 应用 Pod                                             │
│     product-service-pod                                 │
│     ┌────────────────┐  ┌─────────────────┐           │
│     │ istio-proxy    │  │ 应用容器        │           │
│     │ (Sidecar)      │→ │ (Spring Boot)   │           │
│     │                │  │ 监听 8080 端口  │ ← 最终端口 │
│     └────────────────┘  └─────────────────┘           │
└─────────────────────────────────────────────────────────┘

number 的三个可能位置:

# 位置 1:Gateway 的监听端口
apiVersion: networking.istio.io/v1beta1
kind: Gateway
spec:
  servers:
    - port:
        number: 443  # ← Gateway 监听的端口(外部访问)
        protocol: HTTPS

---
# 位置 2:VirtualService 的目标端口
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: product-service
            port:
              number: 8080  # ← 目标服务的端口(内部转发)

---
# 位置 3:Service 的端口映射
apiVersion: v1
kind: Service
metadata:
  name: product-service
spec:
  ports:
    - port: 8080        # ← Service 暴露的端口
      targetPort: 8080  # ← Pod 容器的端口
      protocol: TCP

端口匹配验证:

# 1. 查看 Service 的端口
kubectl get svc product-service -n mall-system

# 输出:
# NAME              TYPE        CLUSTER-IP     PORT(S)
# product-service   ClusterIP   10.96.123.45   8080/TCP
#                                               ↑↑↑↑
#                                               这个端口要匹配

# 2. 查看 Pod 的端口
kubectl get pods -n mall-system -l app=product -o jsonpath='{.items[0].spec.containers[*].ports}'

# 输出:
# [{"containerPort":8080,"protocol":"TCP"}]
#                   ↑↑↑↑
#                   Pod 确实监听 8080

# 3. VirtualService 的 port.number 必须匹配
destination:
  host: product-service
  port:
    number: 8080  ← 必须匹配 Service 和 Pod 的端口

如果端口不匹配会怎样?

# 错误配置:端口不匹配
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: product-service
            port:
              number: 9999  # ← 错误!Service 没有这个端口
# 结果:请求失败
curl https://api.mall.com/api/products

# 错误:503 Service Unavailable
# 原因:Envoy 尝试连接 Pod 的 9999 端口,但 Pod 只监听 8080

6.8.6 问题6:/api// 哪个优先级高?

答案:更具体的规则优先级更高,即 /api/ 优先于 /

优先级规则:

Istio 路由匹配顺序(从高到低):

1. 精确匹配(exact)      最高优先级
2. 前缀匹配(prefix)     中等优先级
   - 更长的前缀优先
3. 正则匹配(regex)      最低优先级
4. 默认规则(无 match)   兜底规则

示例:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: admin-routes
spec:
  hosts:
    - "admin.mall.com"
  http:
    # 规则 1:更具体的路径(优先级高)
    - name: "admin-backend-api"
      match:
        - uri:
            prefix: "/api/"  # ← 匹配 /api/* 的请求
                             # 优先级:高
      route:
        - destination:
            host: admin-backend-service
            port:
              number: 8080
    
    # 规则 2:默认路径(优先级低)
    - name: "admin-frontend"
      route:  # ← 没有 match,匹配所有请求
              # 优先级:低(兜底规则)
        - destination:
            host: admin-frontend-service
            port:
              number: 80

实际匹配示例:

请求 1:https://admin.mall.com/api/users
  ↓
检查规则 1:match prefix "/api/" ✓ 匹配!
  ↓
路由到:admin-backend-service
  ↓
不再检查规则 2

---

请求 2:https://admin.mall.com/dashboard
  ↓
检查规则 1:match prefix "/api/" ✗ 不匹配
  ↓
检查规则 2:无 match 条件,匹配所有 ✓
  ↓
路由到:admin-frontend-service

---

请求 3:https://admin.mall.com/
  ↓
检查规则 1:match prefix "/api/" ✗ 不匹配
  ↓
检查规则 2:无 match 条件,匹配所有 ✓
  ↓
路由到:admin-frontend-service

复杂优先级示例:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    # 规则 1:精确匹配(优先级最高)
    - name: "exact-match"
      match:
        - uri:
            exact: "/api/products/123"  # 只匹配这个精确路径
      route:
        - destination:
            host: product-detail-service
    
    # 规则 2:长前缀匹配(优先级高)
    - name: "long-prefix"
      match:
        - uri:
            prefix: "/api/products"  # 匹配 /api/products*
      route:
        - destination:
            host: product-service
    
    # 规则 3:短前缀匹配(优先级中等)
    - name: "short-prefix"
      match:
        - uri:
            prefix: "/api"  # 匹配 /api*
      route:
        - destination:
            host: api-service
    
    # 规则 4:正则匹配(优先级低)
    - name: "regex-match"
      match:
        - uri:
            regex: "/.*\\.js$"  # 匹配所有 .js 文件
      route:
        - destination:
            host: static-service
    
    # 规则 5:默认规则(优先级最低)
    - name: "default"
      route:
        - destination:
            host: frontend-service

匹配测试:

请求:/api/products/123
  ↓
规则 1 (exact: "/api/products/123") ✓ 匹配!→ product-detail-service
不再检查后续规则

---

请求:/api/products/456
  ↓
规则 1 (exact: "/api/products/123") ✗ 不匹配
规则 2 (prefix: "/api/products") ✓ 匹配!→ product-service
不再检查后续规则

---

请求:/api/orders
  ↓
规则 1 (exact: "/api/products/123") ✗
规则 2 (prefix: "/api/products") ✗
规则 3 (prefix: "/api") ✓ 匹配!→ api-service

---

请求:/static/main.js
  ↓
规则 1-3 ✗ 都不匹配
规则 4 (regex: ".*\\.js$") ✓ 匹配!→ static-service

---

请求:/about
  ↓
规则 1-4 ✗ 都不匹配
规则 5 (default) ✓ 匹配!→ frontend-service

⚠️ 重要:规则顺序很关键!

# ❌ 错误示例:默认规则在前面
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    # 错误!默认规则在最前面
    - route:
        - destination:
            host: frontend-service  # 所有请求都会匹配这里!
    
    # 这个规则永远不会被执行
    - match:
        - uri:
            prefix: "/api"
      route:
        - destination:
            host: api-service  # ← 永远到达不了

# ✅ 正确示例:从具体到模糊
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    # 正确!具体规则在前
    - match:
        - uri:
            prefix: "/api"
      route:
        - destination:
            host: api-service
    
    # 默认规则在最后(兜底)
    - route:
        - destination:
            host: frontend-service

6.8.7 问题7:需要配置几个 Gateway?

答案:根据实际需求,通常 1-3 个。

推荐配置方案:

方案 1:一个 Gateway(最常见,推荐)

# 一个 Gateway 处理所有域名
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: main-gateway
  namespace: istio-system  # 放在 istio-system 作为全局 Gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "*.example.com"  # 通配符,匹配所有子域名
      tls:
        mode: SIMPLE
        credentialName: wildcard-tls-secret

# 优点:
# ✅ 配置简单
# ✅ 维护方便
# ✅ 一个证书覆盖所有子域名

# 缺点:
# ⚠️ 所有域名共享同一个 TLS 证书

方案 2:按环境分(开发/生产)

# Gateway 1:生产环境
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: prod-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
    env: production  # 选择生产环境的 Ingress Gateway
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "*.example.com"
      tls:
        mode: SIMPLE
        credentialName: prod-tls-secret

---
# Gateway 2:开发环境
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: dev-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
    env: development  # 选择开发环境的 Ingress Gateway
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "*.dev.example.com"
      tls:
        mode: SIMPLE
        credentialName: dev-tls-secret

# 优点:
# ✅ 环境隔离
# ✅ 不同的证书
# ✅ 不同的 Ingress Gateway Pod

# 缺点:
# ⚠️ 需要维护多个 Ingress Gateway

方案 3:按业务域分

# Gateway 1:对外 API
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: public-api-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "api.example.com"
        - "www.example.com"
      tls:
        mode: SIMPLE
        credentialName: public-tls-secret

---
# Gateway 2:内部管理
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: admin-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "admin.example.com"
        - "internal.example.com"
      tls:
        mode: MUTUAL  # mTLS,更安全
        credentialName: admin-tls-secret

# 优点:
# ✅ 安全性分级(对外用单向 TLS,内部用双向 TLS)
# ✅ 业务逻辑清晰

# 缺点:
# ⚠️ 配置稍复杂

推荐配置数量:

场景推荐 Gateway 数量理由
小型项目1 个简单够用
中型项目1-2 个一个全局 + 一个内部管理(可选)
大型项目2-3 个生产/开发分离 或 公开/内部分离
多租户3+ 个每个租户一个 Gateway

6.8.8 问题8:credentialName: wildcard-example-tls 从哪里来?

答案:这是 Kubernetes Secret 的名称,需要你提前创建。

完整流程:

# ============================================================
# 步骤 1:准备 TLS 证书文件
# ============================================================

# 你需要从 CA 机构获取证书,或者自签名
# 通常你会有两个文件:
# - tls.crt(证书文件)
# - tls.key(私钥文件)

# 如果是通配符证书,例如 *.example.com
# 可以从以下途径获取:
# 1. Let's Encrypt(免费,推荐)
# 2. 阿里云/腾讯云 SSL 证书
# 3. DigiCert/GlobalSign 等商业 CA

# ============================================================
# 步骤 2:创建 Kubernetes Secret
# ============================================================

# 方法 1:使用 kubectl 命令创建
kubectl create secret tls wildcard-example-tls \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key \
  -n istio-system
  # ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  # 这个名称就是 credentialName 要用的

# 方法 2:使用 YAML 文件创建
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: wildcard-example-tls  # ← 这个名称
  namespace: istio-system      # ← 必须在 istio-system 命名空间
type: kubernetes.io/tls
data:
  tls.crt: $(cat tls.crt | base64 -w 0)  # 证书(Base64 编码)
  tls.key: $(cat tls.key | base64 -w 0)  # 私钥(Base64 编码)
EOF

# ============================================================
# 步骤 3:验证 Secret 创建成功
# ============================================================

kubectl get secrets -n istio-system

# 输出:
# NAME                    TYPE                DATA   AGE
# wildcard-example-tls    kubernetes.io/tls   2      10s
#   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#   这个名称要在 Gateway 中使用

# 查看 Secret 详情
kubectl describe secret wildcard-example-tls -n istio-system

# 输出:
# Name:         wildcard-example-tls
# Namespace:    istio-system
# Type:         kubernetes.io/tls
# Data
# ====
# tls.crt:  1234 bytes  ← 证书内容
# tls.key:  1024 bytes  ← 私钥内容

# ============================================================
# 步骤 4:在 Gateway 中引用
# ============================================================

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-gateway
spec:
  servers:
    - port:
        number: 443
        protocol: HTTPS
      hosts:
        - "*.example.com"
      tls:
        mode: SIMPLE
        credentialName: wildcard-example-tls  # ← 引用上面创建的 Secret
                                              # 名称必须完全匹配

使用 cert-manager 自动管理证书(推荐):

# ============================================================
# 步骤 1:安装 cert-manager
# ============================================================

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

# ============================================================
# 步骤 2:创建 ClusterIssuer(Let's Encrypt)
# ============================================================

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com  # ← 你的邮箱
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: istio

# ============================================================
# 步骤 3:创建 Certificate(自动生成 Secret)
# ============================================================

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-example-tls
  namespace: istio-system
spec:
  secretName: wildcard-example-tls  # ← 自动创建这个 Secret
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - "*.example.com"  # 通配符域名
    - "example.com"
  duration: 2160h  # 90 天
  renewBefore: 360h  # 提前 15 天续期

# cert-manager 会自动:
# 1. 向 Let's Encrypt 申请证书
# 2. 创建 Secret wildcard-example-tls
# 3. 自动续期(到期前 15 天)

证书位置总结:

证书来源:
1. CA 机构(Let's Encrypt/阿里云/腾讯云)
   ↓
2. 下载证书文件(tls.crt + tls.key)
   ↓
3. 创建 Kubernetes Secret
   kubectl create secret tls wildcard-example-tls \
     --cert=tls.crt --key=tls.key -n istio-system
   ↓
4. Gateway 引用
   credentialName: wildcard-example-tls
   ↓
5. Istio Ingress Gateway Pod 读取证书
   ↓
6. 提供 HTTPS 服务

6.8.9 问题9:完整流程解析(最重要!)

问题:

  • 怎么知道外部进来走的是哪个 Gateway?
  • 网关转发到哪个 VirtualService?
  • 接下来的流程呢?

完整流程图:

┌─────────────────────────────────────────────────────────────┐
│  步骤 1:外部请求到达                                        │
│  浏览器:https://api.mall.com/api/products                  │
│  DNS 解析:api.mall.com → 120.55.xxx.xxx(公网 IP)        │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 2:到达 Kubernetes LoadBalancer Service               │
│  kubectl get svc -n istio-system                            │
│  NAME                   TYPE           EXTERNAL-IP         │
│  istio-ingressgateway   LoadBalancer   120.55.xxx.xxx      │
│                                        ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑      │
│                                        这是 DNS 解析的 IP   │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 3:LoadBalancer 转发到 Ingress Gateway Pod            │
│  istio-ingressgateway-7d8f8c9b5-abc123                      │
│  标签:istio=ingressgateway                                 │
│  容器:Envoy Proxy                                           │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 4:Envoy 检查配置(这里决定使用哪个 Gateway)         │
│                                                              │
│  Envoy 内存中有所有 Gateway 的配置:                         │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ Gateway 1: mall-gateway                              │  │
│  │   servers:                                           │  │
│  │     - port: 443                                      │  │
│  │       hosts: ["api.mall.com", "admin.mall.com"]     │  │
│  │                                                       │  │
│  │ Gateway 2: another-gateway                           │  │
│  │   servers:                                           │  │
│  │     - port: 443                                      │  │
│  │       hosts: ["other.example.com"]                   │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                              │
│  匹配逻辑:                                                  │
│  1. 检查请求的域名:api.mall.com                            │
│  2. 检查请求的端口:443                                      │
│  3. 遍历所有 Gateway 配置                                    │
│  4. mall-gateway 的 hosts 包含 "api.mall.com" ✓            │
│  5. 选择:mall-gateway                                      │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 5:查找关联的 VirtualService                           │
│                                                              │
│  查找条件:                                                  │
│  1. VirtualService.spec.gateways 包含 "mall-gateway"       │
│  2. VirtualService.spec.hosts 包含 "api.mall.com"          │
│                                                              │
│  Envoy 内存中有所有 VirtualService:                         │
│  ┌──────────────────────────────────────────────────────┐  │
│  │ VirtualService 1: api-routes                         │  │
│  │   hosts: ["api.mall.com"]                            │  │
│  │   gateways: ["mall-gateway"]  ← 匹配!               │  │
│  │                                                       │  │
│  │ VirtualService 2: admin-routes                       │  │
│  │   hosts: ["admin.mall.com"]                          │  │
│  │   gateways: ["mall-gateway"]  ← 域名不匹配           │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                              │
│  选择:api-routes VirtualService                            │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 6:应用 VirtualService 的路由规则                      │
│                                                              │
│  api-routes 的配置:                                         │
│  http:                                                      │
│    - name: "product-api"                                    │
│      match:                                                 │
│        - uri:                                               │
│            prefix: "/api/products"  ← 检查请求路径         │
│      route:                                                 │
│        - destination:                                       │
│            host: product-service                            │
│            port:                                            │
│              number: 8080                                   │
│                                                              │
│  匹配逻辑:                                                  │
│  请求路径:/api/products                                     │
│  规则:prefix = "/api/products" ✓ 匹配!                    │
│  目标:product-service:8080                                 │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 7:查找目标服务的 Pod IP                               │
│                                                              │
│  Envoy 查找 Cluster 配置:                                   │
│  Cluster: outbound|8080||product-service.mall-system.svc... │
│  Endpoints:                                                 │
│    - 10.244.1.5:8080  (HEALTHY)                            │
│    - 10.244.2.3:8080  (HEALTHY)                            │
│    - 10.244.3.7:8080  (HEALTHY)                            │
│                                                              │
│  负载均衡算法:LEAST_REQUEST(选择请求最少的)              │
│  选择:10.244.1.5:8080                                      │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 8:直接连接 Pod IP                                     │
│  Envoy → TCP 连接 → 10.244.1.5:8080                        │
│  (不经过 Service ClusterIP)                               │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 9:到达应用 Pod                                        │
│  product-service-pod-abc123                                 │
│  IP: 10.244.1.5                                             │
│  ┌────────────────┐  ┌─────────────────┐                  │
│  │ istio-proxy    │→ │ 应用容器        │                  │
│  │ (Sidecar)      │  │ (Spring Boot)   │                  │
│  │ 接收请求        │  │ 处理请求        │                  │
│  └────────────────┘  └─────────────────┘                  │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  步骤 10:应用处理并返回                                     │
│  Spring Boot 返回 JSON 数据                                 │
└─────────────────────────────────────────────────────────────┘
                         ↓
                  原路返回给浏览器

关键决策点详解:

决策点 1:怎么知道用哪个 Gateway?

匹配条件:
1. 请求的端口(443)
2. 请求的域名(api.mall.com)

Envoy 的匹配逻辑:
for gateway in all_gateways:
    for server in gateway.servers:
        if server.port == 443 and "api.mall.com" in server.hosts:
            return gateway  # 找到了!

结果:使用 mall-gateway

决策点 2:怎么知道用哪个 VirtualService?

匹配条件:
1. VirtualService 的 gateways 字段包含 "mall-gateway"
2. VirtualService 的 hosts 字段包含 "api.mall.com"

Envoy 的匹配逻辑:
for vs in all_virtualservices:
    if "mall-gateway" in vs.gateways and "api.mall.com" in vs.hosts:
        return vs  # 找到了!

结果:使用 api-routes VirtualService

决策点 3:怎么知道路由到哪个服务?

匹配条件:
1. 请求路径:/api/products
2. VirtualService 的 match 规则

Envoy 的匹配逻辑:
for rule in virtualservice.http:
    if rule.match:
        for m in rule.match:
            if request.path.startswith(m.uri.prefix):
                return rule.route.destination  # 找到了!

结果:路由到 product-service:8080

6.9 Istio 的核心机制总结(你的理解完全正确!)

6.9.1 你的理解(100% 正确)

✅ 是的!Istio 访问 Pod 的机制:

1. VirtualService 引用 Service 名称
   ↓
2. Istiod 通过 Service 找到对应的 Pod IP 列表(Endpoints)
   ↓
3. Istiod 将 Pod IP 列表推送给 Envoy
   ↓
4. Envoy 使用自己的负载均衡算法选择一个 Pod IP
   ↓
5. Envoy 直接连接 Pod IP(不经过 Service ClusterIP)

关键点:

  • ✅ Service 只是用来"查找" Pod IP
  • ✅ 流量不经过 Service 的 ClusterIP
  • ✅ 不使用 kube-proxy 的负载均衡
  • ✅ Envoy 自己实现负载均衡

6.9.2 详细工作原理

传统方式 vs Istio 方式对比:

┌─────────────────────────────────────────────────────────────┐
│  传统方式(没有 Istio)                                      │
└─────────────────────────────────────────────────────────────┘

请求
  ↓
Service ClusterIP (10.96.123.45)  ← 流量必须经过这里
  ↓
kube-proxy 负载均衡(iptables/IPVS)
  ↓
选择一个 Pod IP
  ↓
转发到 Pod (10.244.1.5)

特点:
❌ 流量经过 Service ClusterIP
❌ 使用 kube-proxy 做负载均衡
❌ 有性能损耗(iptables 规则)
┌─────────────────────────────────────────────────────────────┐
│  Istio 方式                                                  │
└─────────────────────────────────────────────────────────────┘

1. 配置阶段(只发生一次)
   ↓
VirtualService:
  destination:
    host: product-service  ← 引用 Service 名称
   ↓
Istiod 查询 Kubernetes API:
  "product-service 有哪些 Pod?"
   ↓
Kubernetes 返回 Endpoints:
  - 10.244.1.5:8080
  - 10.244.2.3:8080
  - 10.244.3.7:8080
   ↓
Istiod 推送给 Envoy:
  "这是 product-service 的 Pod 列表"

---

2. 运行阶段(每次请求)
   ↓
请求到达 Envoy
   ↓
Envoy 查看内存中的 Pod IP 列表:
  - 10.244.1.5:8080
  - 10.244.2.3:8080
  - 10.244.3.7:8080
   ↓
Envoy 使用负载均衡算法选择:
  选择 10.244.1.5:8080
   ↓
Envoy 直接连接 Pod IP (10.244.1.5:8080)
   ↓
到达 Pod

特点:
✅ 流量不经过 Service ClusterIP
✅ Envoy 自己做负载均衡
✅ 性能更好(没有 iptables 开销)

6.9.3 详细验证过程

场景:验证 Istio 不使用 Service ClusterIP

# ============================================================
# 步骤 1:部署示例应用
# ============================================================

# 创建 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
  namespace: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: product
  template:
    metadata:
      labels:
        app: product
    spec:
      containers:
        - name: product
          image: nginx:alpine
          ports:
            - containerPort: 80

---
# 创建 Service
apiVersion: v1
kind: Service
metadata:
  name: product-service
  namespace: demo
spec:
  selector:
    app: product
  ports:
    - port: 80
      targetPort: 80

---
# 创建 Gateway 和 VirtualService
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: demo-gateway
  namespace: demo
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"

---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-routes
  namespace: demo
spec:
  hosts:
    - "*"
  gateways:
    - demo-gateway
  http:
    - route:
        - destination:
            host: product-service  # ← 引用 Service
            port:
              number: 80

# ============================================================
# 步骤 2:查看 Service ClusterIP
# ============================================================

kubectl get svc product-service -n demo

# 输出:
# NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)
# product-service   ClusterIP   10.96.123.45   <none>        80/TCP
#                               ↑↑↑↑↑↑↑↑↑↑↑↑
#                               记住这个 IP: 10.96.123.45

# ============================================================
# 步骤 3:查看 Endpoints(Pod IP)
# ============================================================

kubectl get endpoints product-service -n demo

# 输出:
# NAME              ENDPOINTS
# product-service   10.244.1.5:80,10.244.2.3:80,10.244.3.7:80
#                   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                   这些是 Pod IP

# ============================================================
# 步骤 4:查看 Envoy 的 Endpoints 配置
# ============================================================

# 获取 Ingress Gateway Pod 名称
GATEWAY_POD=$(kubectl get pods -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')

# 查看 Envoy 配置中的 Endpoints
istioctl proxy-config endpoints $GATEWAY_POD.istio-system \
  --cluster "outbound|80||product-service.demo.svc.cluster.local"

# 输出:
# ENDPOINT           STATUS    OUTLIER CHECK     CLUSTER
# 10.244.1.5:80      HEALTHY   OK                outbound|80||product-service.demo.svc.cluster.local
# 10.244.2.3:80      HEALTHY   OK                outbound|80||product-service.demo.svc.cluster.local
# 10.244.3.7:80      HEALTHY   OK                outbound|80||product-service.demo.svc.cluster.local
#   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#   Envoy 使用的是 Pod IP,不是 Service ClusterIP (10.96.123.45)

# ============================================================
# 步骤 5:验证 Envoy 配置中没有 Service ClusterIP
# ============================================================

# 导出 Envoy 完整配置
kubectl exec $GATEWAY_POD -n istio-system -- curl -s localhost:15000/config_dump > envoy-config.json

# 搜索 Service ClusterIP
grep "10.96.123.45" envoy-config.json

# 结果:没有找到!
# 这证明 Envoy 不使用 Service ClusterIP

# 搜索 Pod IP
grep "10.244.1.5" envoy-config.json

# 结果:找到了!
# 这证明 Envoy 直接使用 Pod IP

# ============================================================
# 步骤 6:查看 Envoy 的负载均衡配置
# ============================================================

istioctl proxy-config clusters $GATEWAY_POD.istio-system \
  --fqdn product-service.demo.svc.cluster.local -o json

# 输出(简化):
{
  "name": "outbound|80||product-service.demo.svc.cluster.local",
  "type": "EDS",  # ← Endpoint Discovery Service
  "edsClusterConfig": {
    "edsConfig": {
      "ads": {}  # ← 从 Istiod 获取 Endpoints
    }
  },
  "lbPolicy": "LEAST_REQUEST",  # ← Envoy 的负载均衡算法
  "circuitBreakers": { ... },
  "outlierDetection": { ... }
}

# 关键点:
# - type: EDS → 使用 Endpoint Discovery Service
# - lbPolicy: LEAST_REQUEST → Envoy 自己的负载均衡算法
# - 没有 Service ClusterIP 的影子

6.9.4 Service 在 Istio 中的角色

Service 的作用变化:

┌─────────────────────────────────────────────────────────────┐
│  传统方式(没有 Istio)                                      │
└─────────────────────────────────────────────────────────────┘

Service 的作用:
1. ✅ 提供稳定的 ClusterIP(流量入口)
2. ✅ 维护 Endpoints(Pod IP 列表)
3. ✅ 通过 kube-proxy 做负载均衡
4. ✅ 提供服务发现(DNS)

重要性:⭐⭐⭐⭐⭐(核心组件)
┌─────────────────────────────────────────────────────────────┐
│  Istio 方式                                                  │
└─────────────────────────────────────────────────────────────┘

Service 的作用:
1. ❌ ClusterIP 不再使用(流量不经过)
2. ✅ 维护 Endpoints(Pod IP 列表)← 这是唯一重要的作用
3. ❌ 不做负载均衡(Envoy 负责)
4. ✅ 提供服务发现(DNS)

重要性:⭐⭐⭐(辅助组件,但仍需要)

Service 变成了:
- 一个"电话簿"(提供 Pod IP 列表)
- 一个"名称"(VirtualService 通过名称引用)
- 不再是"流量入口"

为什么 Istio 还需要 Service?

# 即使 Istio 不使用 Service ClusterIP,你仍然需要创建 Service

# 原因 1:Istiod 需要通过 Service 查找 Pod
apiVersion: v1
kind: Service
metadata:
  name: product-service
spec:
  selector:
    app: product  # ← Kubernetes 用这个查找 Pod
  ports:
    - port: 80

# Istiod 的工作流程:
# 1. 读取 VirtualService: destination.host = "product-service"
# 2. 查询 Kubernetes API: "product-service 有哪些 Endpoints?"
# 3. Kubernetes 返回: Endpoints = [10.244.1.5, 10.244.2.3]
# 4. Istiod 推送给 Envoy

# 原因 2:提供 DNS 解析
# 在集群内部,Pod 可以通过 DNS 查询 Service
# product-service.demo.svc.cluster.local → ClusterIP
# 虽然 Istio 不用 ClusterIP,但其他组件可能需要

# 原因 3:兼容性
# 如果某些 Pod 没有注入 Sidecar,它们仍然可以通过 Service ClusterIP 访问

可以不创建 Service 吗?

❌ 不可以!

即使 Istio 不使用 Service ClusterIP,你仍然必须创建 Service,因为:

1. Istiod 需要通过 Service 查找 Endpoints
2. VirtualService 通过 Service 名称引用
3. 没有 Service,Istiod 找不到 Pod IP

错误示例:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: product-service  # ← 如果没有对应的 Service
                                  # Istiod 找不到 Endpoints
                                  # 请求会失败:503 Service Unavailable

6.9.5 Envoy 的负载均衡算法

Istio/Envoy 支持的负载均衡算法:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: product-destination-rule
spec:
  host: product-service
  trafficPolicy:
    loadBalancer:
      simple: LEAST_REQUEST  # ← Envoy 的负载均衡算法
      
      # 可选值:
      # 1. ROUND_ROBIN(轮询,默认)
      # 2. LEAST_REQUEST(最少请求)
      # 3. RANDOM(随机)
      # 4. PASSTHROUGH(直通)
      
      # 高级选项:
      # consistentHash:  # 一致性哈希
      #   httpHeaderName: "user-id"

算法详解:

算法 1:ROUND_ROBIN(轮询)
Pod 列表:
  - 10.244.1.5:8080
  - 10.244.2.3:8080
  - 10.244.3.7:8080

请求分配:
请求 1 → 10.244.1.5
请求 2 → 10.244.2.3
请求 3 → 10.244.3.7
请求 4 → 10.244.1.5(循环)

特点:
✅ 简单
✅ 分布均匀
❌ 不考虑实际负载

---

算法 2:LEAST_REQUEST(最少请求,推荐)
Pod 状态:
  - 10.244.1.5:8080(当前请求数:5)
  - 10.244.2.3:8080(当前请求数:3)← 选这个
  - 10.244.3.7:8080(当前请求数:8)

选择策略:
选择当前正在处理请求最少的 Pod

特点:
✅ 动态负载均衡
✅ 自动适应不同的处理能力
✅ 推荐用于生产环境

---

算法 3:RANDOM(随机)
随机选择一个 Pod

特点:
✅ 简单
✅ 无状态
❌ 分布不一定均匀

---

算法 4:CONSISTENT_HASH(一致性哈希)
根据请求的某个特征(如 Header、Cookie)选择 Pod
同一个用户的请求总是路由到同一个 Pod

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
spec:
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpHeaderName: "user-id"  # 根据 user-id Header 哈希

示例:
请求 1:user-id=123 → 哈希 → Pod A
请求 2:user-id=456 → 哈希 → Pod B
请求 3:user-id=123 → 哈希 → Pod A(相同的 user-id)

特点:
✅ 会话保持(Sticky Session)
✅ 适合有状态的应用
❌ 负载可能不均匀

6.9.6 Envoy 如何知道 Pod IP 变化?

动态更新机制:

场景:Pod 重启或扩缩容

┌─────────────────────────────────────────────────────────────┐
│  1. Pod 变化                                                 │
│     - Pod 重启(新的 IP)                                    │
│     - 扩容(新增 Pod)                                        │
│     - 缩容(删除 Pod)                                        │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  2. Kubernetes 自动更新 Endpoints                            │
│     kubectl get endpoints product-service                   │
│     旧:10.244.1.5:8080,10.244.2.3:8080                     │
│     新:10.244.1.9:8080,10.244.2.3:8080  ← Pod 1 的 IP 变了 │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  3. Istiod 监听 Kubernetes API                               │
│     Istiod 通过 Watch 机制实时监听 Endpoints 变化            │
│     检测到:product-service 的 Endpoints 变了!              │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  4. Istiod 推送新配置给 Envoy(xDS 协议)                    │
│     gRPC 推送:EDS (Endpoint Discovery Service) Update      │
│     新 Endpoints:                                           │
│       - 10.244.1.9:8080  ← 新 IP                            │
│       - 10.244.2.3:8080                                     │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  5. Envoy 自动更新内存中的 Pod IP 列表                       │
│     旧列表:[10.244.1.5, 10.244.2.3]                        │
│     新列表:[10.244.1.9, 10.244.2.3]                        │
│     更新完成,无需重启!                                      │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│  6. 新请求使用新的 Pod IP                                    │
│     Envoy 自动将流量路由到正确的 Pod                         │
└─────────────────────────────────────────────────────────────┘

验证动态更新:

# ============================================================
# 实验:验证 Envoy 动态更新 Pod IP
# ============================================================

# 1. 查看当前 Endpoints
kubectl get endpoints product-service -n demo -w
# -w 参数:持续监听变化

# 输出:
# NAME              ENDPOINTS
# product-service   10.244.1.5:80,10.244.2.3:80,10.244.3.7:80

# 2. 在另一个终端,查看 Envoy 的 Endpoints
GATEWAY_POD=$(kubectl get pods -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')
watch -n 1 "istioctl proxy-config endpoints $GATEWAY_POD.istio-system \
  --cluster 'outbound|80||product-service.demo.svc.cluster.local'"

# 输出:
# ENDPOINT         STATUS    OUTLIER CHECK
# 10.244.1.5:80    HEALTHY   OK
# 10.244.2.3:80    HEALTHY   OK
# 10.244.3.7:80    HEALTHY   OK

# 3. 在第三个终端,删除一个 Pod
kubectl delete pod product-service-xxx-yyy -n demo

# 观察第 1 个终端(Endpoints):
# NAME              ENDPOINTS
# product-service   10.244.2.3:80,10.244.3.7:80  ← 10.244.1.5 消失了

# 观察第 2 个终端(Envoy Endpoints):
# ENDPOINT         STATUS    OUTLIER CHECK
# 10.244.2.3:80    HEALTHY   OK
# 10.244.3.7:80    HEALTHY   OK
#   ↑↑↑↑↑↑↑↑↑↑↑↑
#   Envoy 自动更新了!10.244.1.5 也消失了

# 4. 等待新 Pod 创建
# 观察第 1 个终端:
# NAME              ENDPOINTS
# product-service   10.244.2.3:80,10.244.3.7:80,10.244.1.9:80
#                                                ↑↑↑↑↑↑↑↑↑↑↑↑↑
#                                                新 Pod 出现了(新 IP)

# 观察第 2 个终端:
# ENDPOINT         STATUS    OUTLIER CHECK
# 10.244.2.3:80    HEALTHY   OK
# 10.244.3.7:80    HEALTHY   OK
# 10.244.1.9:80    HEALTHY   OK  ← Envoy 也自动添加了新 IP
#   ↑↑↑↑↑↑↑↑↑↑↑↑
#   完全同步!

# 结论:
# ✅ Envoy 实时同步 Pod IP 变化
# ✅ 无需手动干预
# ✅ 无需重启任何组件

6.9.7 性能对比

传统方式 vs Istio 方式的性能:

传统方式(kube-proxy + iptables)
┌─────────────────────────────────────────────────────────────┐
│  请求 → Service ClusterIP (10.96.123.45)                    │
│    ↓                                                         │
│  kube-proxy(查找 iptables 规则)                            │
│    ↓                                                         │
│  iptables 规则链(可能有数千条规则)                         │
│    PREROUTING → KUBE-SERVICES → KUBE-SVC-XXX → KUBE-SEP-XXX │
│    ↓                                                         │
│  DNAT 转换(ClusterIP → Pod IP)                            │
│    ↓                                                         │
│  转发到 Pod IP (10.244.1.5)                                 │
└─────────────────────────────────────────────────────────────┘

性能特点:
⚠️ 每个请求都要遍历 iptables 规则
⚠️ 规则链很长(Service 多时规则数量 = O(n))
⚠️ DNAT 有性能开销
⚠️ 无法感知 Pod 的实际负载

延迟:~1-2ms(iptables 查找 + DNAT)
Istio 方式(Envoy 直连)
┌─────────────────────────────────────────────────────────────┐
│  请求 → Envoy                                                │
│    ↓                                                         │
│  查找内存中的 Pod IP 列表(O(1))                            │
│    [10.244.1.5, 10.244.2.3, 10.244.3.7]                     │
│    ↓                                                         │
│  应用负载均衡算法(LEAST_REQUEST)                           │
│    选择当前请求最少的 Pod                                    │
│    ↓                                                         │
│  直接连接 Pod IP (10.244.1.5)                               │
│    无需 DNAT                                                 │
└─────────────────────────────────────────────────────────────┘

性能特点:
✅ 内存查找(O(1))
✅ 无需遍历 iptables 规则
✅ 无 DNAT 开销
✅ 感知 Pod 实际负载
✅ 支持高级负载均衡算法

延迟:~0.1-0.5ms(内存查找 + 选择算法)

性能对比表:

指标传统方式(kube-proxy)Istio 方式(Envoy)
延迟1-2ms0.1-0.5ms
规则数量影响O(n) - Service 越多越慢O(1) - 不受影响
DNAT 开销✅ 有❌ 无
负载均衡简单随机/轮询智能算法(LEAST_REQUEST)
健康检查❌ 无主动检查✅ 主动健康检查
熔断❌ 不支持✅ 支持
重试❌ 不支持✅ 支持
可观测性❌ 无✅ 完整指标

6.9.8 总结图示

完整流程图:

┌─────────────────────────────────────────────────────────────┐
│  配置阶段(只发生一次或 Pod 变化时)                         │
└─────────────────────────────────────────────────────────────┘

你创建的资源:
├─ VirtualService
│  └─ destination.host: product-service
│
├─ Service (product-service)
│  └─ selector: app=product
│
└─ Deployment
   └─ 创建 Pod(带标签 app=product)

Kubernetes 自动维护:
Service → Endpoints
  - 10.244.1.5:8080
  - 10.244.2.3:8080
  - 10.244.3.7:8080

Istiod 的工作:
1. 读取 VirtualService
2. 查找 Service 的 Endpoints
3. 推送给 Envoy(xDS 协议)

Envoy 的准备:
内存中存储 Pod IP 列表和负载均衡配置

┌─────────────────────────────────────────────────────────────┐
│  运行阶段(每次请求)                                        │
└─────────────────────────────────────────────────────────────┘

请求到达 Envoy
  ↓
Envoy 查看内存中的 Pod 列表
  [10.244.1.5, 10.244.2.3, 10.244.3.7]
  ↓
应用负载均衡算法(LEAST_REQUEST)
  当前请求数:
  - 10.244.1.5: 5 个请求
  - 10.244.2.3: 3 个请求 ← 选这个(最少)
  - 10.244.3.7: 8 个请求
  ↓
直接连接 Pod IP: 10.244.2.3:8080
  (不经过 Service ClusterIP: 10.96.123.45)
  ↓
到达 Pod

6.10 Istio 在 Docker 环境中的运行方式

6.10.1 Istio + Docker 的三种方式

重要说明:Istio 主要是为 Kubernetes 设计的,但可以在 Docker 环境中使用。

方式 1:Kind/Minikube(推荐)
  ├─ Kubernetes 运行在 Docker 容器中
  ├─ Istio 运行在 Kubernetes 上
  └─ 本质上还是 Kubernetes,只是部署在 Docker 里

方式 2:Docker Compose + Envoy Sidecar(手动配置)
  ├─ 每个应用容器手动添加 Envoy Sidecar
  ├─ 没有 Istiod(控制平面)
  └─ 需要手动管理 Envoy 配置

方式 3:Istio VM/Bare Metal 模式
  ├─ Istiod 运行在 Kubernetes
  ├─ 应用运行在 Docker 容器中
  └─ 通过 WorkloadEntry 注册到 Istio

6.10.2 方式 1:Kind(Kubernetes in Docker)- 推荐

这是最常见、最简单的方式。

架构:

你的宿主机(Windows/Mac/Linux)
  ↓
Docker Desktop
  ↓
Kind 容器(运行 Kubernetes)
  ├─ istio-system Namespace
  │  ├─ istiod Pod(控制平面)
  │  └─ istio-ingressgateway Pod(入口网关)
  │
  └─ your-app Namespace
     └─ your-app Pod
        ├─ istio-proxy Container(Sidecar)
        └─ your-app Container(你的应用)

详细步骤:

# ============================================================
# 步骤 1:安装 Kind(Kubernetes in Docker)
# ============================================================

# Windows/Mac/Linux
# 下载 Kind:https://kind.sigs.k8s.io/docs/user/quick-start/
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# 验证安装
kind version

# ============================================================
# 步骤 2:创建 Kind 集群(Kubernetes 运行在 Docker 中)
# ============================================================

# 创建配置文件
cat <<EOF > kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
EOF

# 创建集群
kind create cluster --config kind-config.yaml --name istio-demo

# 查看 Docker 容器(Kind 节点是 Docker 容器)
docker ps

# 输出:
# CONTAINER ID   IMAGE                  COMMAND                  NAMES
# abc123def456   kindest/node:v1.27.0   "/usr/local/bin/entr…"   istio-demo-control-plane
#                ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                这是一个 Docker 容器,里面运行着 Kubernetes

# 验证 Kubernetes
kubectl get nodes

# 输出:
# NAME                       STATUS   ROLES           AGE   VERSION
# istio-demo-control-plane   Ready    control-plane   1m    v1.27.0

# ============================================================
# 步骤 3:安装 Istio(在 Kind 集群中)
# ============================================================

# 下载 istioctl
curl -L https://istio.io/downloadIstio | sh -
cd istio-1.20.0
export PATH=$PWD/bin:$PATH

# 安装 Istio
istioctl install --set profile=demo -y

# 查看 Istio 组件(都是 Docker 容器)
docker exec -it istio-demo-control-plane crictl ps

# 或者
kubectl get pods -n istio-system

# 输出:
# NAME                                    READY   STATUS
# istiod-5b9f8d8b9-xyz                    1/1     Running
# istio-ingressgateway-7d8f8c9b5-abc      1/1     Running
# istio-egressgateway-74df5c8f8f-def      1/1     Running

# ============================================================
# 步骤 4:部署应用
# ============================================================

# 创建命名空间并启用 Sidecar 注入
kubectl create namespace demo
kubectl label namespace demo istio-injection=enabled

# 部署示例应用
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: product
  template:
    metadata:
      labels:
        app: product
    spec:
      containers:
        - name: product
          image: nginx:alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: product-service
  namespace: demo
spec:
  selector:
    app: product
  ports:
    - port: 80
      targetPort: 80
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: demo-gateway
  namespace: demo
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-routes
  namespace: demo
spec:
  hosts:
    - "*"
  gateways:
    - demo-gateway
  http:
    - route:
        - destination:
            host: product-service
            port:
              number: 80
EOF

# 查看 Pod(每个 Pod 有 2 个容器)
kubectl get pods -n demo

# 输出:
# NAME                               READY   STATUS
# product-service-xxx-yyy            2/2     Running
#                                    ↑↑↑
#                                    2 个容器:istio-proxy + product

# 查看容器详情
kubectl describe pod product-service-xxx-yyy -n demo

# 输出:
# Containers:
#   product:          ← 你的应用容器
#     Image: nginx:alpine
#     Port: 80/TCP
#   istio-proxy:      ← Istio Sidecar 容器
#     Image: docker.io/istio/proxyv2:1.20.0
#     Port: 15090/TCP

# ============================================================
# 步骤 5:访问应用
# ============================================================

# 因为 Kind 运行在 Docker 中,访问方式:
curl http://localhost:80

# 或者获取 Ingress Gateway 的地址
kubectl get svc istio-ingressgateway -n istio-system

# 输出:
# NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)
# istio-ingressgateway   LoadBalancer   10.96.123.45    localhost     80:30080/TCP

# 访问
curl http://localhost:80

Docker 层面的视角:

# 查看所有 Docker 容器
docker ps

# 输出:
# CONTAINER ID   IMAGE                  NAMES
# abc123         kindest/node:v1.27.0   istio-demo-control-plane
#                ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                这个容器里运行着整个 Kubernetes 集群

# 进入 Kind 容器查看内部的容器
docker exec -it istio-demo-control-plane bash

# 在 Kind 容器内部
crictl ps

# 输出:
# CONTAINER ID   IMAGE                              NAME
# def456         istio/proxyv2:1.20.0               istio-proxy      ← Istio Sidecar
# ghi789         nginx:alpine                       product          ← 你的应用
# jkl012         istio/pilot:1.20.0                 discovery        ← Istiod
# mno345         istio/proxyv2:1.20.0               istio-ingressgateway ← Ingress Gateway

流程图:

外部请求
  ↓
http://localhost:80
  ↓
Docker Desktop 网络
  ↓
Kind 容器(istio-demo-control-plane)
  ├─ 端口映射:80:80
  │
  └─ Kubernetes 集群(运行在 Kind 容器内)
     │
     ├─ Istio Ingress Gateway Pod(容器中的容器)
     │  └─ Envoy Proxy
     │
     └─ Product Service Pod(容器中的容器)
        ├─ istio-proxy Container(Sidecar Envoy)
        └─ product Container(Nginx)

关键理解:

Docker 层级:
宿主机
  └─ Docker Desktop
     └─ Kind 容器(运行 Kubernetes)
        └─ Kubernetes Pods(容器中的容器)
           ├─ istio-proxy 容器
           └─ 应用容器

所以:
1. Kind 本身是一个 Docker 容器
2. Kubernetes 运行在 Kind 容器内
3. Istio 运行在 Kubernetes 上
4. 应用 Pod 运行在 Kubernetes 上
5. 每个 Pod 实际上是容器中的容器(Docker in Docker)

6.10.3 方式 2:Docker Compose + Envoy Sidecar(手动配置)

这种方式不使用 Kubernetes,完全基于 Docker Compose。

特点:

  • ❌ 没有 Istiod(控制平面)
  • ❌ 需要手动配置每个 Envoy Sidecar
  • ✅ 更轻量级
  • ⚠️ 配置复杂,不推荐生产环境

架构:

Docker 宿主机
  ├─ Ingress Envoy 容器(入口网关)
  │
  ├─ Product Service
  │  ├─ product-envoy 容器(Sidecar)
  │  └─ product-app 容器(应用)
  │
  └─ Order Service
     ├─ order-envoy 容器(Sidecar)
     └─ order-app 容器(应用)

配置示例:

# ============================================================
# docker-compose.yml
# ============================================================

version: '3.8'

services:
  # ============================================================
  # Ingress Envoy(入口网关)
  # ============================================================
  ingress-envoy:
    image: envoyproxy/envoy:v1.28.0
    container_name: ingress-envoy
    ports:
      - "80:10000"    # HTTP
      - "443:10001"   # HTTPS
      - "9901:9901"   # Envoy Admin
    volumes:
      - ./envoy-configs/ingress-envoy.yaml:/etc/envoy/envoy.yaml
    networks:
      - app-network

  # ============================================================
  # Product Service(应用 + Sidecar)
  # ============================================================
  
  # Product 应用容器
  product-app:
    image: nginx:alpine
    container_name: product-app
    volumes:
      - ./app-configs/product:/usr/share/nginx/html
    networks:
      - app-network
    # 注意:不对外暴露端口,只通过 Sidecar 访问

  # Product Envoy Sidecar
  product-envoy:
    image: envoyproxy/envoy:v1.28.0
    container_name: product-envoy
    volumes:
      - ./envoy-configs/product-envoy.yaml:/etc/envoy/envoy.yaml
    networks:
      - app-network
    depends_on:
      - product-app
    # 注意:也不对外暴露端口,只被 ingress-envoy 访问

  # ============================================================
  # Order Service(应用 + Sidecar)
  # ============================================================
  
  # Order 应用容器
  order-app:
    image: my-order-service:latest
    container_name: order-app
    environment:
      - PRODUCT_SERVICE_URL=http://product-envoy:15001
    networks:
      - app-network

  # Order Envoy Sidecar
  order-envoy:
    image: envoyproxy/envoy:v1.28.0
    container_name: order-envoy
    volumes:
      - ./envoy-configs/order-envoy.yaml:/etc/envoy/envoy.yaml
    networks:
      - app-network
    depends_on:
      - order-app

networks:
  app-network:
    driver: bridge

Envoy 配置文件(手动配置):

# ============================================================
# envoy-configs/ingress-envoy.yaml
# 入口 Envoy 配置
# ============================================================

admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  listeners:
    # HTTP 监听器
    - name: listener_0
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 10000
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: ingress_http
                codec_type: AUTO
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains: ["*"]
                      routes:
                        # 路由到 Product Service
                        - match:
                            prefix: "/api/products"
                          route:
                            cluster: product_service
                        
                        # 路由到 Order Service
                        - match:
                            prefix: "/api/orders"
                          route:
                            cluster: order_service
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  # 定义上游集群
  clusters:
    # Product Service 集群
    - name: product_service
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: product_service
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: product-envoy  # ← 指向 Product 的 Sidecar
                      port_value: 15001
    
    # Order Service 集群
    - name: order_service
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: order_service
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: order-envoy  # ← 指向 Order 的 Sidecar
                      port_value: 15001

---
# ============================================================
# envoy-configs/product-envoy.yaml
# Product Sidecar Envoy 配置
# ============================================================

admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  listeners:
    # 入站监听器(接收来自 Ingress Envoy 的请求)
    - name: inbound_listener
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 15001  # Sidecar 入站端口
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: inbound_http
                codec_type: AUTO
                route_config:
                  name: inbound_route
                  virtual_hosts:
                    - name: inbound
                      domains: ["*"]
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: local_service  # 转发到本地应用
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
    # 本地应用集群
    - name: local_service
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: local_service
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: product-app  # ← 指向本地应用容器
                      port_value: 80

启动和测试:

# 创建目录结构
mkdir -p envoy-configs app-configs/product

# 创建配置文件(见上面)
# ...

# 启动所有服务
docker-compose up -d

# 查看容器
docker ps

# 输出:
# CONTAINER ID   IMAGE                    NAMES
# abc123         envoyproxy/envoy:v1.28   ingress-envoy
# def456         nginx:alpine             product-app
# ghi789         envoyproxy/envoy:v1.28   product-envoy
# jkl012         my-order-service:latest  order-app
# mno345         envoyproxy/envoy:v1.28   order-envoy

# 测试访问
curl http://localhost:80/api/products

# 流量路径:
# curl → ingress-envoy:10000 → product-envoy:15001 → product-app:80

流程图:

外部请求
  ↓
http://localhost:80/api/products
  ↓
ingress-envoy 容器(端口 10000)
  ├─ 检查路由规则
  ├─ 匹配:/api/products → product_service
  └─ 转发到:product-envoy:15001
     ↓
product-envoy 容器(Sidecar,端口 15001)
  ├─ 接收入站请求
  ├─ 应用策略(重试、熔断等)
  └─ 转发到:product-app:80
     ↓
product-app 容器(Nginx,端口 80)
  ├─ 处理请求
  └─ 返回响应
     ↓
原路返回

优缺点:

✅ 优点:
- 不需要 Kubernetes
- 更轻量级
- 本地开发简单

❌ 缺点:
- 需要手动配置每个 Envoy
- 没有 Istiod,无法动态更新配置
- 配置复杂,容易出错
- 不支持自动 Sidecar 注入
- 扩缩容麻烦(需要手动更新 Envoy 配置)

6.10.4 方式 3:Istio VM/Bare Metal 模式

架构:Istiod 在 Kubernetes,应用在 Docker

Kubernetes 集群
  └─ istio-system
     └─ istiod Pod(控制平面)

Docker 宿主机(独立于 Kubernetes)
  ├─ product-app 容器
  │  └─ istio-agent + Envoy(手动安装)
  │
  └─ order-app 容器
     └─ istio-agent + Envoy(手动安装)

配置步骤(复杂,不推荐初学者):

# ============================================================
# 步骤 1:在 Kubernetes 中安装 Istio
# ============================================================

istioctl install --set profile=default -y

# ============================================================
# 步骤 2:创建 WorkloadGroup(在 Kubernetes 中)
# ============================================================

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: WorkloadGroup
metadata:
  name: docker-apps
  namespace: default
spec:
  metadata:
    labels:
      app: product
      version: v1
  template:
    serviceAccount: default
    network: vm-network
EOF

# ============================================================
# 步骤 3:生成 VM/Container 配置文件
# ============================================================

istioctl x workload entry configure \
  -f workloadgroup.yaml \
  -o docker-workload-config

# 这会生成:
# - cluster.env(环境变量)
# - istio-token(认证 Token)
# - mesh.yaml(Mesh 配置)
# - root-cert.pem(根证书)

# ============================================================
# 步骤 4:在 Docker 容器中安装 Istio Agent
# ============================================================

# 创建 Dockerfile
cat <<EOF > Dockerfile
FROM your-app-image:latest

# 安装 Istio Agent 和 Envoy
RUN curl -L https://istio.io/downloadIstio | sh -
RUN cp istio-1.20.0/bin/istio-agent /usr/local/bin/
RUN cp istio-1.20.0/bin/envoy /usr/local/bin/

# 复制配置文件
COPY docker-workload-config/* /etc/istio/

# 启动脚本
COPY start.sh /start.sh
RUN chmod +x /start.sh

CMD ["/start.sh"]
EOF

# 创建启动脚本
cat <<EOF > start.sh
#!/bin/bash

# 启动 Istio Agent(会自动启动 Envoy)
istio-agent proxy &

# 启动应用
exec your-app-binary
EOF

# 构建镜像
docker build -t your-app-with-istio:latest .

# ============================================================
# 步骤 5:创建 WorkloadEntry(在 Kubernetes 中注册容器)
# ============================================================

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: WorkloadEntry
metadata:
  name: product-app-docker-1
  namespace: default
spec:
  address: 192.168.1.100  # Docker 容器的 IP
  labels:
    app: product
    version: v1
  serviceAccount: default
EOF

# ============================================================
# 步骤 6:运行 Docker 容器
# ============================================================

docker run -d \
  --name product-app \
  --network istio-network \
  -p 8080:8080 \
  your-app-with-istio:latest

这种方式非常复杂,不推荐使用!


6.10.5 对比总结
方式复杂度功能完整性推荐度适用场景
Kind/Minikube💚 简单✅ 完整⭐⭐⭐⭐⭐本地开发、测试
Docker Compose + Envoy🟡 中等⚠️ 受限⭐⭐简单演示
VM/Bare Metal 模式🔴 复杂✅ 完整混合环境

6.10.6 推荐方案

本地开发/测试:使用 Kind

# 1. 安装 Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/

# 2. 创建集群
kind create cluster --name istio-demo

# 3. 安装 Istio
istioctl install --set profile=demo -y

# 4. 部署应用
kubectl apply -f your-app.yaml

# 完成!Istio 在 Docker 中运行(通过 Kind)

为什么推荐 Kind?

✅ 优点:
1. 简单:一条命令创建 Kubernetes 集群
2. 完整:支持所有 Istio 功能
3. 轻量:比完整的 Kubernetes 集群轻量
4. 隔离:每个集群是独立的 Docker 容器
5. 快速:创建/删除集群只需几秒
6. 免费:完全开源

❌ 缺点:
1. 只适合本地开发
2. 不适合生产环境
3. 性能不如真实 Kubernetes

适用场景:
✅ 本地开发
✅ CI/CD 测试
✅ 学习 Istio
❌ 生产部署

6.10.7 完整示例:Kind + Istio
# ============================================================
# 完整的本地开发环境搭建
# ============================================================

# 步骤 1:创建 Kind 集群配置
cat <<EOF > kind-istio-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: istio-demo
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
EOF

# 步骤 2:创建集群
kind create cluster --config kind-istio-config.yaml

# 步骤 3:安装 Istio
istioctl install --set profile=demo -y

# 步骤 4:部署示例应用
kubectl create namespace demo
kubectl label namespace demo istio-injection=enabled

kubectl apply -n demo -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: product
  template:
    metadata:
      labels:
        app: product
    spec:
      containers:
        - name: product
          image: nginx:alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: product-service
spec:
  selector:
    app: product
  ports:
    - port: 80
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: demo-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: product-routes
spec:
  hosts:
    - "*"
  gateways:
    - demo-gateway
  http:
    - route:
        - destination:
            host: product-service
            port:
              number: 80
EOF

# 步骤 5:测试
curl http://localhost:80

# 步骤 6:查看 Docker 容器
docker ps

# 输出:
# CONTAINER ID   IMAGE                  NAMES
# abc123         kindest/node:v1.27.0   istio-demo-control-plane
#                ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#                Kubernetes + Istio 都在这个容器里

# 步骤 7:清理
kind delete cluster --name istio-demo

6.11 Istio 是否必须基于 Pod?

6.11.1 直接回答

✅ 是的!Istio 的核心设计就是基于 Kubernetes Pod 的。

即使在 Docker 环境中使用 Istio:

方式 1:Kind/Minikube
  → Docker 容器中运行 Kubernetes
  → Kubernetes 中运行 Pod
  → 所以本质上还是基于 Pod ✓

方式 2:Docker Compose + Envoy(手动配置)
  → 不需要 Kubernetes
  → 不需要 Pod
  → 但这不是真正的 Istio(没有 Istiod)
  → 只是手动配置 Envoy Sidecar

结论:
真正的 Istio 功能 = 必须基于 Pod

6.11.2 为什么 Istio 需要 Pod?

Istio 的核心功能依赖 Pod 的特性:

Pod 的关键特性:
1. 多容器共享网络命名空间
   ├─ 应用容器
   └─ istio-proxy 容器(Sidecar)
   └→ 共享 localhost,Sidecar 可以拦截应用的所有流量

2. 共享存储卷(Volume)
   └→ 应用和 Sidecar 可以共享配置、证书

3. 生命周期管理
   └→ Init Container 先启动配置 iptables
   └→ 然后启动应用和 Sidecar

4. 标签(Labels)
   └→ Istiod 通过标签选择和管理 Pod

5. 自动注入
   └→ Kubernetes Admission Webhook 自动注入 Sidecar

6.11.3 详细解析:为什么需要 Pod?

原因 1:Sidecar 模式需要 Pod 的网络共享

# Kubernetes Pod 的结构
apiVersion: v1
kind: Pod
metadata:
  name: product-service-pod
spec:
  containers:
    # 容器 1:应用容器
    - name: product-app
      image: my-product-service:latest
      ports:
        - containerPort: 8080
    
    # 容器 2:Istio Sidecar(自动注入)
    - name: istio-proxy
      image: istio/proxyv2:1.20.0
      ports:
        - containerPort: 15001  # Envoy 入站
        - containerPort: 15006  # Envoy 出站
        - containerPort: 15020  # 健康检查
        - containerPort: 15021  # 状态端口
        - containerPort: 15090  # Prometheus 指标

# 关键点:两个容器在同一个 Pod 中
# 共享:
# 1. 网络命名空间(network namespace)
#    - 共享 localhost
#    - 共享网络接口
#    - 共享 iptables 规则
# 2. IPC 命名空间
# 3. 部分存储卷

为什么需要共享网络命名空间?

应用容器的视角:
- 我监听 8080 端口
- 我以为我直接接收外部流量
- 实际上,所有流量被 Sidecar 拦截了

Sidecar 的工作原理(需要共享网络):

1. Init Container 配置 iptables 规则
   iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-port 15001
   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
   将所有出站流量重定向到 Envoy (15001)

   iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-port 15006
   ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
   将所有入站流量重定向到 Envoy (15006)

2. 流量拦截(入站)
   外部请求 → Pod IP:8080
     ↓
   iptables 拦截 → 重定向到 15006
     ↓
   Envoy (istio-proxy) 接收
     ↓
   Envoy 应用策略(mTLS、授权、重试等)
     ↓
   Envoy 转发到 localhost:8080
     ↓
   应用容器接收(以为是直接收到的)

3. 流量拦截(出站)
   应用调用其他服务:http://order-service:8080
     ↓
   iptables 拦截 → 重定向到 15001
     ↓
   Envoy (istio-proxy) 接收
     ↓
   Envoy 应用策略(负载均衡、熔断、重试等)
     ↓
   Envoy 直接连接目标 Pod IP

验证 iptables 规则:

# 进入 Pod 的网络命名空间查看 iptables
kubectl exec -it product-service-xxx-yyy -c istio-proxy -- iptables -t nat -L -n -v

# 输出(简化):
Chain PREROUTING (policy ACCEPT)
target     prot opt source      destination
ISTIO_INBOUND  tcp  --  0.0.0.0/0   0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source      destination
ISTIO_OUTPUT  tcp  --  0.0.0.0/0   0.0.0.0/0

Chain ISTIO_INBOUND (1 references)
target     prot opt source      destination
REDIRECT   tcp  --  0.0.0.0/0   0.0.0.0/0   tcp dpt:8080 redir ports 15006
#          ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#          所有到 8080 的流量 → 重定向到 15006(Envoy)

Chain ISTIO_OUTPUT (1 references)
target     prot opt source      destination
REDIRECT   tcp  --  0.0.0.0/0   0.0.0.0/0   redir ports 15001
#          ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#          所有出站流量 → 重定向到 15001(Envoy)

原因 2:自动 Sidecar 注入需要 Kubernetes

Istio 的自动注入机制:

1. 你创建 Deployment
   apiVersion: apps/v1
   kind: Deployment
   spec:
     template:
       spec:
         containers:
           - name: product-app
             image: my-product-service:latest
   # 注意:只定义了一个容器

2. 启用自动注入
   kubectl label namespace demo istio-injection=enabled

3. 当你 kubectl apply 时
   Kubernetes API Server
     ↓
   Istio Admission Webhook 拦截
     ↓
   Istio Mutating Webhook 自动修改 Pod 定义
     ↓
   添加 istio-proxy 容器
   添加 istio-init Init Container
   添加必要的 Volume
     ↓
   修改后的 Pod 定义提交给 Kubernetes

4. 最终创建的 Pod
   apiVersion: v1
   kind: Pod
   spec:
     initContainers:
       - name: istio-init  # ← 自动添加(配置 iptables)
     containers:
       - name: product-app
       - name: istio-proxy  # ← 自动添加(Envoy Sidecar)
     volumes:
       - name: istio-envoy  # ← 自动添加
       - name: istio-token  # ← 自动添加
       - name: istio-certs  # ← 自动添加

在纯 Docker 环境中,没有 Kubernetes Admission Webhook:

Docker Compose 环境:
❌ 没有 Admission Webhook
❌ 无法自动注入 Sidecar
❌ 需要手动配置每个容器的 Envoy
❌ 需要手动配置网络规则
❌ 非常麻烦!

结论:
Istio 的自动注入依赖 Kubernetes → 依赖 Pod

原因 3:Istiod 需要 Kubernetes API

Istiod 的工作流程:

1. 监听 Kubernetes API
   watch Pods, Services, Endpoints, ...
     ↓
2. 读取 Pod 信息
   - Pod IP 地址
   - Pod 标签
   - Pod 所属的 Service
     ↓
3. 读取 Service 信息
   - Service 的 Endpoints(Pod IP 列表)
     ↓
4. 生成 Envoy 配置
   - Listeners(监听器)
   - Routes(路由规则)
   - Clusters(上游服务)
   - Endpoints(Pod IP 列表)
     ↓
5. 推送配置给 Envoy(xDS 协议)
   Istiod → Envoy Sidecar

Istiod 依赖的 Kubernetes 资源:

# Istiod 需要读取这些资源
kubectl get pods --all-namespaces
kubectl get services --all-namespaces
kubectl get endpoints --all-namespaces
kubectl get nodes

# 如果没有 Kubernetes:
❌ Istiod 无法获取 Pod 信息
❌ Istiod 无法获取 Service 信息
❌ Istiod 无法生成正确的配置
❌ Istio 无法工作

6.11.4 Docker 环境 vs Kubernetes 环境

完整对比:

┌─────────────────────────────────────────────────────────────┐
│  方式 1:Kubernetes 环境(真正的 Istio)                     │
└─────────────────────────────────────────────────────────────┘

架构:
Kubernetes 集群
  ├─ istio-system Namespace
  │  ├─ istiod Pod(控制平面)
  │  │  └─ discovery 容器(Pilot)
  │  │
  │  └─ istio-ingressgateway Pod(入口网关)
  │     └─ istio-proxy 容器(Envoy)
  │
  └─ demo Namespace
     └─ product-service Pod
        ├─ istio-init Init Container(配置 iptables)
        ├─ istio-proxy 容器(Sidecar Envoy)
        └─ product-app 容器(应用)

特点:
✅ 完整的 Istio 功能
✅ 自动 Sidecar 注入
✅ 动态配置更新
✅ Istiod 自动管理
✅ 所有流量自动拦截

是否基于 Pod:✅ 是
┌─────────────────────────────────────────────────────────────┐
│  方式 2:Kind(Kubernetes in Docker)                        │
└─────────────────────────────────────────────────────────────┘

架构:
Docker 宿主机
  └─ Kind 容器(istio-demo-control-plane)
     └─ Kubernetes 集群(运行在容器内)
        ├─ istio-system Namespace
        │  └─ istiod Pod
        │
        └─ demo Namespace
           └─ product-service Pod
              ├─ istio-proxy 容器
              └─ product-app 容器

特点:
✅ 完整的 Istio 功能
✅ 自动 Sidecar 注入
✅ Kubernetes 运行在 Docker 容器中
✅ 本质上还是 Kubernetes + Pod

是否基于 Pod:✅ 是(容器中的 Pod)
┌─────────────────────────────────────────────────────────────┐
│  方式 3:Docker Compose + Envoy(手动配置)                  │
└─────────────────────────────────────────────────────────────┘

架构:
Docker 宿主机
  ├─ ingress-envoy 容器
  ├─ product-app 容器
  ├─ product-envoy 容器(手动配置的 Sidecar)
  ├─ order-app 容器
  └─ order-envoy 容器(手动配置的 Sidecar)

特点:
❌ 没有 Kubernetes
❌ 没有 Pod
❌ 没有 Istiod
❌ 需要手动配置每个 Envoy
❌ 无法自动注入
❌ 无法动态更新配置
⚠️ 只有基础的 Envoy 功能,不是真正的 Istio

是否基于 Pod:❌ 否(这不是真正的 Istio)

6.11.5 总结图示

Istio 的依赖关系:

真正的 Istio 功能
  ↓
需要 Istiod(控制平面)
  ↓
Istiod 需要 Kubernetes API
  ↓
需要读取 Pod、Service、Endpoints
  ↓
需要自动注入 Sidecar
  ↓
Sidecar 需要共享网络命名空间
  ↓
需要 Pod 的多容器共享特性
  ↓
结论:Istio 必须基于 Pod

6.11.6 如果不想用 Pod,有什么选择?

选择 1:使用 Envoy(不是 Istio)

如果你不想用 Kubernetes/Pod,但想要服务网格功能:

选项 A:手动配置 Envoy
  - 每个服务手动添加 Envoy Sidecar
  - 手动编写 Envoy 配置文件
  - 手动管理配置更新
  - 工作量巨大!

选项 B:使用其他服务网格
  - Consul Connect(支持非 Kubernetes)
  - Linkerd(也需要 Kubernetes)
  - 其他轻量级方案

结论:
想要 Istio 的完整功能 → 必须用 Kubernetes Pod
不想用 Kubernetes → 考虑其他方案

6.11.7 Kind 的本质

Kind 为什么还是基于 Pod?

Kind 的完整结构:

宿主机(Windows/Mac/Linux)
  └─ Docker Desktop
     └─ Kind 容器(名称:istio-demo-control-plane)
        ├─ 运行 Kubernetes 组件:
        │  ├─ kubelet(管理容器)
        │  ├─ kube-apiserver
        │  ├─ kube-controller-manager
        │  └─ kube-scheduler
        │
        └─ 运行容器(通过 containerd)
           ├─ istiod Pod 的容器
           ├─ istio-ingressgateway Pod 的容器
           └─ product-service Pod 的容器
              ├─ istio-proxy 容器
              └─ product-app 容器

查看层级:

# 1. 宿主机层面(Docker)
docker ps
# 输出:istio-demo-control-plane(Kind 容器)

# 2. Kind 容器层面(Kubernetes)
kubectl get pods -A
# 输出:所有 Pod(运行在 Kind 容器内)

# 3. Pod 内部(多个容器)
kubectl get pod product-service-xxx -o jsonpath='{.spec.containers[*].name}'
# 输出:istio-proxy product-app

结论:
Kind 只是把 Kubernetes 打包进 Docker 容器
本质上还是 Kubernetes → 还是基于 Pod

6.11.8 最终结论

你的问题:“也需要基于 pod 是吗?”

答案:✅ 是的!

情况 1:真正的 Istio
  → 必须基于 Kubernetes
  → 必须基于 Pod

情况 2:Kind/Minikube(Docker 环境)
  → Docker 容器中运行 Kubernetes
  → 还是基于 Pod

情况 3:Docker Compose + Envoy
  → 不基于 Pod
  → 但这不是真正的 Istio
  → 只是手动配置的 Envoy

总结:
想要 Istio 的完整功能 = 必须基于 Pod ✓

为什么必须基于 Pod?

原因 1:Sidecar 需要 Pod 的网络共享
原因 2:自动注入需要 Kubernetes Admission Webhook
原因 3:Istiod 需要 Kubernetes API 获取 Pod 信息
原因 4:流量拦截需要 Pod 的 iptables 规则
原因 5:配置管理需要 Pod 的 Volume 共享

所有这些特性 → 都依赖 Pod

6.12 关键问题答疑

6.12.1 Docker 能独立运行 Istio 吗?

答案:❌ 不能!Docker 不能独立运行完整的 Istio,必须要有 Kubernetes。

真正的 Istio = Istiod(控制平面)+ Envoy(数据平面)+ Kubernetes

Docker 单独运行的限制:
❌ 无法运行 Istiod(需要 Kubernetes API)
❌ 无法自动注入 Sidecar(需要 K8s Admission Webhook)
❌ 无法动态配置(Istiod 依赖 K8s 资源)
❌ 无法使用 Gateway、VirtualService 等 CRD

结论:
想要完整的 Istio → 必须有 Kubernetes

两种解决方案:

方案 1:Kind/Minikube(推荐)
  Docker 容器中运行 Kubernetes → Kubernetes 中运行 Istio
  
  docker run kindest/node
    └─ Kubernetes 集群
       └─ Istio(完整功能)

方案 2:手动配置 Envoy(不推荐)
  Docker Compose + 手动配置 Envoy
  ❌ 没有 Istiod
  ❌ 没有自动注入
  ❌ 不是真正的 Istio

详细对比:

┌─────────────────────────────────────────────────────────────┐
│  方案 1:Docker + Kind + Kubernetes + Istio(推荐)         │
└─────────────────────────────────────────────────────────────┘

架构:
宿主机
  └─ Docker
     └─ Kind 容器(Kubernetes in Docker)
        └─ Kubernetes 集群
           └─ Istio

命令:
# 1. 创建 Kind 集群(Kubernetes 运行在 Docker 中)
kind create cluster --name istio-demo

# 2. 安装 Istio
istioctl install --set profile=demo -y

# 3. 完成!拥有完整的 Istio 功能

特点:
✅ 完整的 Istio 功能
✅ 有 Istiod(控制平面)
✅ 自动 Sidecar 注入
✅ 动态配置更新
✅ 支持所有 Istio 特性

是否需要 K8s:✅ 需要(但 K8s 运行在 Docker 容器中)
┌─────────────────────────────────────────────────────────────┐
│  方案 2:纯 Docker + 手动 Envoy(不推荐)                    │
└─────────────────────────────────────────────────────────────┘

架构:
宿主机
  └─ Docker
     ├─ 应用容器
     └─ Envoy 容器(手动配置)

需要做的:
1. 手动创建 Envoy 配置文件
2. 手动为每个服务添加 Envoy Sidecar
3. 手动更新配置(没有 Istiod)
4. 手动管理证书
5. 手动配置网络

特点:
❌ 没有 Istiod
❌ 没有自动注入
❌ 没有动态配置
❌ 配置复杂
⚠️ 只有基础 Envoy 功能,不是真正的 Istio

是否需要 K8s:❌ 不需要(但功能严重受限)

为什么 Docker 不能独立运行 Istio?

原因 1:Istiod 需要 Kubernetes API

Istiod 的工作:
├─ 监听 Kubernetes 资源(Pod, Service, Endpoints)
├─ 读取 Gateway, VirtualService(K8s CRD)
├─ 生成 Envoy 配置
└─ 推送配置给 Envoy

Docker 环境:
❌ 没有 Kubernetes API
❌ Istiod 无法获取 Pod 信息
❌ Istiod 无法工作

---

原因 2:自动注入需要 K8s Admission Webhook

Istio 自动注入流程:
1. kubectl apply -f deployment.yaml
2. K8s API Server 接收请求
3. Istio Mutating Webhook 拦截
4. 自动添加 istio-proxy 容器
5. Pod 创建时已包含 Sidecar

Docker 环境:
❌ 没有 Admission Webhook
❌ 无法自动注入
❌ 需要手动配置

---

原因 3:Gateway/VirtualService 是 K8s CRD

Istio 的配置资源:
apiVersion: networking.istio.io/v1beta1
kind: Gateway
kind: VirtualService
kind: DestinationRule

这些是:
✅ Kubernetes 自定义资源(CRD)
❌ Docker 不支持 CRD
❌ 无法使用 kubectl apply
❌ 需要手动写 Envoy 配置文件

---

原因 4:Pod 的特性无法在纯 Docker 中复现

Pod 的关键特性:
├─ 多容器共享网络命名空间
├─ Init Container(配置 iptables)
├─ 自动 Volume 注入
└─ 标签和选择器

Docker Compose:
⚠️ 只能部分模拟
❌ 无法完全复现
❌ 配置复杂

6.12.2 VirtualService 中的端口是什么?

你的问题:

destination:
  host: backend-service
  port:
    number: 8080  # ← 这是 Service 端口还是 Pod 端口?

答案:引用的是 Service 端口,但流量直接到 Pod 端口。


详细解释:

配置层面(你写的):
VirtualService:
  destination:
    host: backend-service  # ← Service 名称
    port:
      number: 8080         # ← Service 端口

Service:
  name: backend-service
  ports:
    - port: 8080           # ← Service 暴露的端口
      targetPort: 8080     # ← Pod 容器的端口

Pod:
  containers:
    - name: backend
      ports:
        - containerPort: 8080  # ← Pod 实际监听的端口

---

运行时(实际流量):
1. VirtualService 引用 backend-service:8080
   ↓
2. Istiod 读取 Service backend-service
   ↓
3. 查找 Service 的 Endpoints(Pod IP 列表)
   Endpoints:
     - 10.244.1.5:8080  # ← Pod IP + Port
     - 10.244.2.3:8080
   ↓
4. Istiod 推送配置给 Envoy
   Cluster: outbound|8080||backend-service
   Endpoints:
     - 10.244.1.5:8080  # ← 直接使用 Pod IP 和端口
     - 10.244.2.3:8080
   ↓
5. 流量直接到 Pod IP:8080
   Envoy → 10.244.1.5:8080(不经过 Service ClusterIP)

端口映射关系:

# ============================================================
# 完整的端口映射链路
# ============================================================

# 1. VirtualService(配置)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: backend-service  # ← 引用 Service 名称
            port:
              number: 8080         # ← 引用 Service 端口
                                   # 意思:"找到 backend-service 的 8080 端口"

---
# 2. Service(服务定义)
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend  # ← 选择 Pod 的标签
  ports:
    - name: http
      port: 8080         # ← Service 暴露的端口(这是 VirtualService 引用的)
      targetPort: 8080   # ← 转发到 Pod 的哪个端口
      protocol: TCP

---
# 3. Pod(实际应用)
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: backend  # ← 匹配 Service 的 selector
spec:
  containers:
    - name: backend
      image: my-backend:latest
      ports:
        - containerPort: 8080  # ← Pod 实际监听的端口
          name: http
          protocol: TCP

验证端口映射:

# ============================================================
# 验证完整的端口映射
# ============================================================

# 1. 查看 VirtualService 配置
kubectl get virtualservice backend-routes -o yaml

# 输出:
spec:
  http:
    - route:
        - destination:
            host: backend-service
            port:
              number: 8080  # ← VirtualService 引用的端口

# 2. 查看 Service 配置
kubectl get svc backend-service -o yaml

# 输出:
spec:
  ports:
    - port: 8080         # ← 必须匹配 VirtualService 的 port.number
      targetPort: 8080   # ← 转发到 Pod 的端口
  selector:
    app: backend

# 3. 查看 Service 的 Endpoints(Pod IP + Port)
kubectl get endpoints backend-service

# 输出:
NAME              ENDPOINTS
backend-service   10.244.1.5:8080,10.244.2.3:8080
#                           ↑↑↑↑
#                           这是 Pod 的端口(来自 targetPort)

# 4. 查看 Pod 的端口
kubectl get pods -l app=backend -o jsonpath='{.items[*].spec.containers[*].ports}'

# 输出:
[{"containerPort":8080,"protocol":"TCP"}]
#                 ↑↑↑↑
#                 Pod 实际监听的端口

# 5. 验证 Envoy 使用的端口(最终真相)
GATEWAY_POD=$(kubectl get pods -n istio-system -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')
istioctl proxy-config endpoints $GATEWAY_POD.istio-system \
  --cluster "outbound|8080||backend-service.default.svc.cluster.local"

# 输出:
ENDPOINT           STATUS    OUTLIER CHECK     CLUSTER
10.244.1.5:8080    HEALTHY   OK                outbound|8080||backend-service...
10.244.2.3:8080    HEALTHY   OK                outbound|8080||backend-service...
#         ↑↑↑↑
#         Envoy 直接使用 Pod IP:8080,不是 Service ClusterIP

关键理解:

问题:"number: 8080 是 Service 端口还是 Pod 端口?"

答案:
✅ 配置时:引用的是 Service 端口
✅ 运行时:流量直接到 Pod 端口

工作流程:
VirtualService.port.number (8080)
  ↓ 引用
Service.port (8080)
  ↓ 查找
Service.targetPort (8080)
  ↓ 对应
Pod.containerPort (8080)
  ↓ 最终
Envoy 直接连接:Pod IP:8080

端口不匹配会怎样?

# ❌ 错误配置示例 1:VirtualService 端口与 Service 不匹配
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: backend-service
            port:
              number: 9999  # ← 错误!Service 没有这个端口

---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  ports:
    - port: 8080  # ← Service 只有 8080 端口

# 结果:
# 503 Service Unavailable
# Envoy 找不到对应的 Cluster

---

# ❌ 错误配置示例 2:Service targetPort 与 Pod 不匹配
apiVersion: v1
kind: Service
spec:
  ports:
    - port: 8080
      targetPort: 9999  # ← 错误!Pod 没有监听 9999

---
apiVersion: v1
kind: Pod
spec:
  containers:
    - ports:
        - containerPort: 8080  # ← Pod 只监听 8080

# 结果:
# 请求到达 Pod,但 Pod 的 9999 端口没有监听
# Connection refused

---

# ✅ 正确配置:所有端口匹配
VirtualService: port.number = 8080
  ↓
Service: port = 8080
  ↓
Service: targetPort = 8080
  ↓
Pod: containerPort = 8080
  ↓
完美匹配!流量正常

特殊情况:端口名称匹配

# 方式 1:使用端口号(常见)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: backend-service
            port:
              number: 8080  # ← 使用端口号

---
# 方式 2:使用端口名称(推荐)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: backend-service
            port:
              name: http  # ← 使用端口名称
                          # 引用 Service 中定义的端口名

---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  ports:
    - name: http      # ← 端口名称
      port: 8080      # ← 可以改变
      targetPort: 8080

# 优点:
# ✅ 如果 Service 的端口号变化,VirtualService 不需要修改
# ✅ 更清晰(http, https, grpc)
# ✅ Istio 推荐的最佳实践

6.12.3 总结

问题 1:Docker 能独立运行 Istio 吗?

答案:❌ 不能

原因:
1. Istio 需要 Kubernetes API(Istiod 依赖)
2. 需要 K8s CRD(Gateway, VirtualService)
3. 需要 Admission Webhook(自动注入)
4. 需要 Pod 的特性(网络共享、Init Container)

解决方案:
✅ 推荐:Kind/Minikube(在 Docker 中运行 K8s)
⚠️ 备选:手动配置 Envoy(功能受限,不是真正的 Istio)

结论:
想要完整的 Istio → 必须有 Kubernetes
Docker 只是运行 Kubernetes 的容器平台

问题 2:VirtualService 的 port.number 是什么端口?

答案:引用的是 Service 端口,但流量直接到 Pod 端口

配置链路:
VirtualService.port.number (8080)
  → 引用 Service.port (8080)
  → 对应 Service.targetPort (8080)
  → 映射 Pod.containerPort (8080)

流量链路:
Envoy 读取配置
  → 查找 Service 的 Endpoints
  → 获取 Pod IP:8080
  → 直接连接 Pod IP:8080(不经过 Service ClusterIP)

关键点:
✅ 配置时:通过 Service 名称和端口引用
✅ 运行时:直接连接 Pod IP 和端口
✅ 端口必须匹配:VS → Service → Pod

实战建议:

# 本地开发 Istio 应用的最佳实践

# 1. 使用 Kind(不要尝试纯 Docker)
kind create cluster --name istio-dev

# 2. 安装 Istio
istioctl install --set profile=demo -y

# 3. 配置端口(确保匹配)
# VirtualService: number: 8080
# Service: port: 8080, targetPort: 8080
# Pod: containerPort: 8080

# 4. 验证配置
kubectl get vs,svc,endpoints

# 5. 测试
curl http://localhost:80

# 完成!

6.13 调试和验证

# 1. 查看 DNS 解析
nslookup product.example.com

# 2. 查看 LoadBalancer IP
kubectl get svc istio-ingressgateway -n istio-system

# 3. 查看 Gateway 配置
kubectl get gateway -n product-system
istioctl analyze -n product-system

# 4. 查看 VirtualService
kubectl get virtualservice -n product-system

# 5. 查看授权策略
kubectl get authorizationpolicy -n product-system

# 6. 查看 Hydra 日志
kubectl logs -f deployment/hydra -n auth-system

# 7. 查看 OAuth2 Proxy 日志
kubectl logs -f deployment/oauth2-proxy -n auth-system

# 8. 查看 Ingress Gateway 日志
kubectl logs -f deployment/istio-ingressgateway -n istio-system

# 9. 查看应用 Pod 日志
kubectl logs -f deployment/product-manager-service -n product-system -c product-manager
kubectl logs -f deployment/product-manager-service -n product-system -c istio-proxy

# 10. 测试完整流程
curl -v -L https://product.example.com/api/products
# -v: 显示详细信息
# -L: 跟随重定向

7. Kubernetes 最佳实践与配置

5.1 Istio 在 Kubernetes 中的最佳实践

5.1.1 资源规划

生产环境推荐配置:

组件副本数CPU 请求CPU 限制内存请求内存限制
Istiod3500m2000m2Gi4Gi
Ingress Gateway3-10(HPA)1000m2000m1Gi2Gi
Egress Gateway2-5(HPA)500m1000m512Mi1Gi
Sidecar(每个)N/A100m2000m128Mi1Gi

生产配置示例(带详细注释):

# ============================================================
# 文件名:istio-production-values.yaml
# 用途:Istio 生产环境完整配置文件
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值