Istio-限流配置

装载了 istio 之后,会默认安装 istio-ingress-gateway,这个组件的能力在 ingress-nginx 之上添加了很多其他的能力底层使用的 envoy 进行网络的转发和配置,包括 sidecar 底层同样使用 envoy 去实现,然后加上了配置同步 安全验证等相关功能。

# --------------------------------------------
# 0) Redis 密码放 Secret(更安全)
#    用 Secret 存放敏感信息(如 Redis 密码),避免明文进入镜像或 Pod 环境变量历史
# --------------------------------------------
apiVersion: v1
kind: Secret
metadata:
  name: ratelimit-redis-secret          # Secret 名称,后续 Deployment 通过 secretKeyRef 引用
  namespace: istio-system               # 放在与 ratelimit 服务相同的命名空间,便于引用与管理
type: Opaque
stringData:
  REDIS_AUTH: "password"       # 明文由 K8s 负责 base64 编码存储

---
# --------------------------------------------
# 1) Ratelimit 配额配置(Envoy Ratelimit Server 的运行时配置)
#    - 该 ConfigMap 被容器以只读方式挂载到 /data/ratelimit/config
#    - domain 必须与 Envoy HTTP 过滤器里的 domain 完全一致,否则不会命中
#    - descriptors 用来描述限流的“维度组合”(actions 组合后形成的描述符)
# --------------------------------------------
apiVersion: v1
kind: ConfigMap
metadata:
  name: ratelimit-config
  namespace: istio-system
data:
  config.yaml: |
    domain: ingress-ratelimit          # 必须与 RateLimit 过滤器的 domain 一致
    descriptors:
      - key: header_match              # 顶层 key:与 HTTP_ROUTE 中的 header_value_match 对应(actions 1/2)
        value: path-api         # 顶层 value:来自路由上 actions 的 descriptor_value
        descriptors:                   # 二级描述符:继续细分 (组合成唯一的限流键)
          - key: generic_key           # 与 HTTP_ROUTE 中 generic_key 对应(actions 2/2)
            value: istio-limit-v1      # 对应路由里设置的 descriptor_value
            rate_limit:
              unit: second             # 时间单位:second / minute / hour / day
              requests_per_unit: 5     # 配额:单位时间内允许的请求数(此处为 1 秒 1 次)

---
# --------------------------------------------
# 2) Ratelimit Service(连接你现有 Redis 单机)
#    - 通过 envoyproxy/ratelimit 实现全局滑窗/令牌桶限流的配置服务
#    - replicas=1:测试环境单副本;生产建议至少 2-3 副本并配合 Redis 高可用
#    - readiness/liveness:通过 6070 端口健康检查
#    - 关键环境变量:REDIS_URL / REDIS_AUTH / RUNTIME_ROOT / RUNTIME_SUBDIRECTORY
# --------------------------------------------
apiVersion: v1
kind: Service
metadata:
  name: ratelimit                         # 供 Envoy 通过集群名解析 (outbound|8081||ratelimit.istio-system.svc.cluster.local)
  namespace: istio-system
spec:
  selector: { app: ratelimit }            # 与 Deployment 的 labels 匹配
  ports:
    - name: grpc
      port: 8081                          # gRPC 服务端口(Envoy RateLimit 过滤器将连此端口)
      targetPort: 8081
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ratelimit
  namespace: istio-system
spec:
  replicas: 1                              # 测试设 1,生产建议 ≥3 并做好 HPA/PodDisruptionBudget
  selector:
    matchLabels: { app: ratelimit }
  template:
    metadata:
      labels: { app: ratelimit }
    spec:
      containers:
        - name: ratelimit
          image: envoyproxy/ratelimit:875d418c   # 指定 commit/tag,保证版本可重现;可考虑升级到官方较新 tag
          imagePullPolicy: IfNotPresent
          command: ["/bin/ratelimit"]            # 启动入口
          ports:
            - containerPort: 8080                # HTTP Admin(可选:metrics/调试)
            - containerPort: 8081                # gRPC 主服务端口
            - containerPort: 6070                # 健康检查端口(/healthcheck)
          env:
            - name: REDIS_SOCKET_TYPE
              value: tcp                         # 通过 TCP 连接 Redis
            - name: REDIS_URL
              value: "redis://1.1.1.1:6379"# 你的 Redis 地址(生产建议内网域名 + 哨兵或集群)
            - name: REDIS_POOL_SIZE
              value: "20"                        # 连接池大小;视并发/副本数调优
            - name: REDIS_AUTH
              valueFrom:
                secretKeyRef:
                  name: ratelimit-redis-secret   # 从 Secret 注入密码,避免明文
                  key: REDIS_AUTH
            - name: USE_STATSD
              value: "false"                     # 如有 statsd/Prometheus sidecar 可打开
            - name: RUNTIME_ROOT
              value: /data                       # 对应挂载点根目录
            - name: RUNTIME_SUBDIRECTORY
              value: ratelimit                   # 子目录,最终配置路径 /data/ratelimit/config
            - name: RUNTIME_IGNOREDOTFILES
              value: "true"                      # 忽略隐藏文件
          volumeMounts:
            - name: config
              mountPath: /data/ratelimit/config  # 与上面 runtime 路径一致,确保装载到 config 子目录
          readinessProbe:
            httpGet:
              path: /healthcheck
              port: 6070
            initialDelaySeconds: 2               # 初始延迟,避免冷启动误判
            periodSeconds: 5                     # 探测频率
          livenessProbe:
            httpGet:
              path: /healthcheck
              port: 6070
            initialDelaySeconds: 10
            periodSeconds: 10
      volumes:
        - name: config
          configMap:
            name: ratelimit-config               # 装载上面的 ConfigMap

---
# --------------------------------------------
# 3) IngressGateway 注入全局 RateLimit 过滤器 + 指向 ratelimit 的 CLUSTER
#    - 通过 EnvoyFilter 在 HTTP Router 之前插入全局 ratelimit HTTP 过滤器
#    - workloadSelector:限定只作用于网关工作负载(label: istio=ingressgateway)
#    - rate_limit_service:连到上面定义的 ratelimit gRPC(通过集群名/authority)
#    - 追加一个 Lua 响应过滤器:当后端返回 429 时,改写为统一 JSON
# --------------------------------------------
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: gateway-global-limit-filter
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway                 # 只作用在 IngressGateway 实例
  configPatches:
    # 3.1 在 router 之前插入 ratelimit HTTP 过滤器(全局生效)
    - applyTo: HTTP_FILTER
      match:
        context: GATEWAY
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager    # HTTP 连接管理器
              subFilter:
                name: envoy.filters.http.router                      # 在 Router 之前插入
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.ratelimit
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
            domain: ingress-ratelimit          # 必须与 ConfigMap 里的 domain 一致
            failure_mode_deny: false           # 后端 ratelimit 服务异常时是否拒绝请求;false=放行(推荐)
            timeout: 10s                       # 访问 ratelimit 服务的超时
            rate_limit_service:
              grpc_service:
                envoy_grpc:
                  cluster_name: outbound|8081||ratelimit.istio-system.svc.cluster.local  # Istio 自动生成的集群名
                  authority: ratelimit.istio-system.svc.cluster.local                     # HTTP/2 Host (SNI),可省略
              transport_api_version: V3         # 使用 v3 API(推荐)
    # 3.2 基于端口 1035 的 Listener 再插入 Lua 响应过滤器(可选:仅对该监听端口生效)
    - applyTo: HTTP_FILTER
      match:
        context: GATEWAY
        listener:
          portNumber: 1035                      # 仅对 1035 端口的 Listener 生效(你的网关暴露此端口)
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.lua
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
            inlineCode: |
              function envoy_on_response(handle)
                local s = tonumber(handle:headers():get(":status") or "0")
                if s == 429 then
                  local body = '{"code":429,"message":"Too many requests. Please try again later."}'
                  handle:headers():replace("content-type", "application/json")
                  handle:headers():replace("content-length", tostring(#body))
                  handle:body(true):setBytes(body)
                end
              end
   # 注意:
   # - 如果你的网关并非监听 1035(或多端口),请确认此过滤器是否应该扩大到所有端口(去掉 portNumber 条件)
   # - 也可用本地速率限制本地拒绝时在本机生成 429,再由该 Lua 统一改写

---
# --------------------------------------------
# 4) 给 VirtualHost 注入 per-route 限流动作
#    - 在指定的 Route(或整个 vhost)下配置 actions,从而生成与 ConfigMap 匹配的 descriptors
#    - 这里使用两段 action:
#        a) header_value_match:当 :path 以 /api 前缀命中时,写入 descriptor_value=path-api
#        b) generic_key:再追加 descriptor_value=istio-limit-v1
#    - 两段组合 => (key=header_match,value=path-api) + (key=generic_key,value=istio-limit-v1)
#      正好与 ConfigMap 对应,从而激活“1 秒 1 次”的限流
# --------------------------------------------
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: gateway-limit-descriptor-svc
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway                       # 作用范围限定在网关
  configPatches:
    - applyTo: HTTP_ROUTE
      match:
        context: GATEWAY
        routeConfiguration:
          vhost:
            name: host:port       # 注意此 vhost 名:通常为 "<host>:<port>"
      patch:
        operation: MERGE
        value:
          route:
            rate_limits:
              - actions:
                - header_value_match:
                    headers:
                      - name: ":path"             # 匹配伪首部 :path,基于前缀判断
                        prefix_match: "/api"
                    expect_match: true            # 仅当匹配时才添加该描述符
                    descriptor_value: "path-api"   # 对应 ConfigMap 顶层 key/value
                - generic_key:
                    descriptor_value: "istio-limit-v1"    # 对应 ConfigMap 二级 key/value