配置 EventListener

本文档帮助您完成的工作

本文档帮助您配置和部署 EventListener,以接收来自外部系统(如 GitHub、GitLab 或自定义 Webhook)的事件,并自动触发您的 Tekton pipelines。您将学习:

  • 如何创建和配置 EventListener 资源
  • 如何将 EventListener 暴露给外部系统(使用 Ingress、LoadBalancer 或 NodePort)
  • 如何配置权限和安全设置
  • 如何根据您的环境选择合适的部署策略

前提条件:您应具备 Tekton Pipelines 和 Kubernetes 基础知识。如果您是首次接触 EventListener 概念,建议先阅读 深入理解 EventListener 文档。

TIP

有关 EventListener 概念、架构和原理的深入理解,请参阅 深入理解 EventListener 文档。

概述

EventListener 是 Tekton Triggers 的核心资源,负责接收和处理外部事件(如 Webhook)。当外部系统触发事件时,EventListener 会根据配置的触发器创建 Kubernetes 资源(如 PipelineRun)。

主要特性

EventListener 具有以下主要特性:

  • 事件监听:提供 HTTP 端点以接收来自外部系统的 Webhook 事件
  • 事件过滤:使用拦截器验证和过滤接收到的事件
  • 资源创建:根据触发器定义自动创建 Kubernetes 资源
  • 可扩展性:支持自定义拦截器和多种事件源
  • 安全性:内置多种安全机制,如 Webhook 验证

配置说明

基本结构

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: eventlistener
spec:
  serviceAccountName: triggers-default-sa
  resources:
    kubernetesResource:
      serviceType: NodePort  # 服务类型
      servicePort: 80       # 服务端口
      spec:
        template:
          spec:
            containers:
              - resources:    # 资源限制
                  requests:
                    memory: "64Mi"
                    cpu: "250m"
                  limits:
                    memory: "128Mi"
                    cpu: "500m"
  triggers:                  # 触发器配置
    - name: trigger-1       # 触发器名称
      interceptors:         # 拦截器配置
        - ref:
            name: "cel"
          params:
            - name: "filter"
              value: "header.match('X-GitHub-Event', 'pull_request')"
      bindings:            # 触发器绑定
        - ref: pipeline-binding
      template:            # 触发器模板
        ref: pipeline-template

主要字段说明

spec.resources.kubernetesResource

用于配置 EventListener 的 Kubernetes 资源:

  • serviceType:服务类型(NodePort/ClusterIP/LoadBalancer)
  • servicePort:服务端口
  • spec:Pod 模板配置

spec.triggers

定义一组触发器配置:

  • name:触发器名称
  • interceptors:拦截器列表配置
  • bindings:触发器绑定配置
  • template:触发器模板配置

安全配置

EventListener 支持多种安全配置:

  1. ServiceAccount
    • 默认 ServiceAccount(triggers-default-sa:如果未指定 spec.serviceAccountName,EventListener 会自动使用命名空间内的 triggers-default-sa ServiceAccount。该 ServiceAccount 会自动被授予 EventListener 在同一命名空间内处理触发器所需的命名空间范围权限。权限会自动绑定到 ServiceAccount 所在的命名空间。
    • 自定义 ServiceAccount:当需要处理跨多个命名空间的触发器(例如配置了 namespaceSelector)时,必须指定具有集群范围权限的自定义 ServiceAccount。请确保所指定的 ServiceAccount 已配置相应权限(详见下文权限指南)。
  2. 拦截器验证:使用 CEL 拦截器进行事件验证。
  3. TLS:支持配置 HTTPS 证书。

权限指南

默认 ServiceAccount 与自定义 ServiceAccount

  • 默认 ServiceAccount(triggers-default-sa:当未指定 spec.serviceAccountName 时,EventListener 自动使用命名空间内的 triggers-default-sa ServiceAccount。该 ServiceAccount 会自动被授予 EventListener 在同一命名空间内处理触发器所需的命名空间范围权限,权限自动绑定到所在命名空间,无需手动配置 Role 或 RoleBinding。

  • 具有集群权限的自定义 ServiceAccount:当配置 namespaceSelector 监听多个命名空间(包括 '*' 表示所有命名空间)时,必须指定具有集群范围权限的自定义 ServiceAccount。triggers-default-sa 仅具有命名空间范围权限,无法访问跨命名空间资源。

所需权限

为确保 EventListener 正常触发 pipeline 和 task,所使用的 ServiceAccount 需要以下权限:

权限资源描述
get, list, watchconfigmaps读取 ConfigMaps
get, list, watchsecrets读取 Secrets
get, list, watchserviceaccounts读取 ServiceAccounts
create, get, list, watch, patch, updatedeployments管理 Deployments
create, get, list, watch, patch, updateservices管理 Services
create, get, list, watch, patch, updatepods管理 Pods
create, get, list, watch, patch, updateevents管理 Events
createpipelineruns创建 PipelineRuns
createtaskruns创建 TaskRuns
get, list, watchclustertriggerbindings读取 ClusterTriggerBindings
get, list, watchtriggerbindings读取 TriggerBindings
get, list, watchclusterinterceptors读取 ClusterInterceptors
get, list, watchinterceptors读取 Interceptors
get, list, watchtriggers读取 Triggers
get, list, watchtriggertemplates读取 TriggerTemplates
get, list, watcheventlisteners读取 EventListeners

参考 ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: eventlistener-role
rules:
- apiGroups: [""]
  resources: ["configmaps", "secrets", "serviceaccounts"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
  resources: ["services", "pods", "events"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: ["tekton.dev"]
  resources: ["pipelineruns", "taskruns"]
  verbs: ["create"]
- apiGroups: ["triggers.tekton.dev"]
  resources:
    - "clustertriggerbindings"
    - "triggerbindings"
    - "clusterinterceptors"
    - "interceptors"
    - "triggers"
    - "triggertemplates"
    - "eventlisteners"
  verbs: ["get", "list", "watch"]

用户指南

部署 EventListener 需要根据环境的规模和实际网络情况进行规划,以下描述了基于不同规划的配置方式:

EventListener 触发器配置场景

EventListener 支持三种不同的触发器配置场景,适用于不同的使用场景:

1. 命名空间级 EventListener

命名空间级 EventListener 通过在 EventListener spec 中显式配置 triggers 字段,绑定同一命名空间内的特定触发器。这是最简单的场景,适合单命名空间部署。

使用场景及建议:

  • 使用场景

    • 单个项目或团队的独立部署和管理
    • 需要明确的资源隔离和权限控制
    • 团队对 EventListener 配置有深入理解,需要对每个触发器进行细粒度控制
    • 不同命名空间需要不同的 EventListener 配置或资源限制
    • 对性能和故障隔离有较高要求
  • 推荐对象

    • 经验丰富的 DevOps 团队或需要高度定制化的场景
    • 不同项目需要不同 EventListener 配置、资源配额或安全策略
    • 多租户环境,要求租户间完全隔离
    • 不同命名空间需要不同副本数、资源限制或网络策略

示例配置:

spec:
  # serviceAccountName 可选,默认为 triggers-default-sa
  triggers:
    - name: trigger-1
      bindings:
        - ref: gitlab-push
          kind: ClusterTriggerBinding
      template:
        ref: pipeline-template
    - name: trigger-2
      bindings:
        - ref: github-pullrequest
          kind: ClusterTriggerBinding
      template:
        ref: pipeline-template

2. 项目级 EventListener

项目级 EventListener 使用 namespaceSelector 绑定多个指定命名空间的触发器,适合管理一组相关命名空间(如一个项目或团队)的触发器。

使用场景及建议:

  • 使用场景

    • 多个相关项目或命名空间共享同一个 EventListener
    • 在项目组或部门级别统一事件处理
    • 需要跨多个命名空间共享资源,但不覆盖整个集群
    • 有一定性能隔离需求,但可接受项目组内资源共享
  • 推荐对象

    • 中型团队或项目组,需要跨多个相关命名空间统一管理触发器
    • 多个项目使用相似配置和资源需求,降低运维成本
    • 需要集中管理一组相关项目的 Webhook 事件
    • 对 EventListener 有一定了解,能配置和管理跨命名空间权限

示例配置:

spec:
  serviceAccountName: eventlistener  # 具有集群权限的自定义 SA
  namespaceSelector:
    matchNames:
      - project-a
      - project-b
      - project-c

3. 全局级 EventListener

全局级 EventListener 使用带通配符('*')的 namespaceSelector,绑定集群内所有命名空间的触发器,适合集群范围的集中事件处理。

使用场景及建议:

  • 使用场景

    • 集群范围内统一事件处理和管理
    • 用户对性能隔离无严格要求,能接受资源共享
    • 一次配置即可让所有用户开箱即用,无需在各命名空间单独配置
    • 用户无需深入了解 EventListener,由平台统一管理
    • 中小规模集群,事件量相对可控
  • 推荐对象

    • 平台场景,由平台管理员统一配置和管理
    • 集群规模较小,所有用户使用相同 EventListener 配置
    • 简化用户工作流程,降低学习成本和配置复杂度
    • 集中运维管理,实现统一监控和故障排查
    • 注意:大规模集群或高并发场景需考虑性能影响和故障隔离

示例配置:

spec:
  serviceAccountName: eventlistener  # 具有集群权限的自定义 SA
  namespaceSelector:
    matchNames:
      - '*'  # 通配所有命名空间

对比表:

场景命名空间范围ServiceAccount配置复杂度使用场景
命名空间级单个命名空间triggers-default-sa(自动)简单单项目/团队
项目级多个指定命名空间自定义 SA(集群权限)中等多相关项目
全局级所有命名空间(*自定义 SA(集群权限)复杂集群范围管理

规模

不同规划场景下,可采用不同配置以满足需求。

规模触发器数量资源设置
小规模2(触发器)× 100(命名空间)× 10(pipeline)= 2,000 触发器/集群适中,副本数大于 2
中等规模2(触发器)× 1,000(命名空间)× 10(pipeline)= 20,000 触发器/集群适中,副本数大于 2,按命名空间部署不同 EventListener

网络配置

根据环境优先级和可用网络资源,可选择不同网络配置。

网络配置协议描述
官方域名和证书https通过 ClusterIP + Ingress 配置 TLS 证书
自定义域名和证书https使用自签名证书配置 TLS。注意:工具端配置存在风险,并非所有工具/平台支持跳过或忽略 TLS 验证。
NodePorthttp通过 NodePort + 集群/节点 IP 设置可访问地址
TIP

选择合适的网络配置

  • 生产环境:使用 LoadBalancer 或 Ingress 并启用 HTTPS,确保安全和可靠性
  • 开发/测试:NodePort 足够且易于搭建
  • 自动管理:如果希望系统自动创建 Ingress 资源,可通过 TektonConfig 配置自动暴露功能
  • 手动控制:如果偏好手动管理 Ingress 资源,可参考以下示例手动创建

NodePort 配置示例

NodePort 适用于开发、测试或无 LoadBalancer/Ingress 控制器的环境。此方式将 EventListener 服务暴露在集群中每个节点的高端口(30000-32767)上。

前提条件

  • Kubernetes 集群节点对外可访问
  • 您应知道至少一个节点的外部 IP 地址
  • 防火墙规则允许访问 NodePort 端口范围(30000-32767)

配置示例

创建带 NodePort 服务的 EventListener

将以下 YAML 保存为 eventlistener-nodeport.yaml

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: el-demo
  namespace: tekton-triggers-demo
spec:
  # serviceAccountName 可选,默认为 triggers-default-sa
  serviceAccountName: triggers-default-sa
  resources:
    kubernetesResource:
      serviceType: NodePort  # 使用 NodePort 服务类型
      servicePort: 80        # 容器端口
  triggers:
    - name: hello-trigger
      bindings:
        - ref: hello-binding
      template:
        ref: hello-template
kubectl apply -f eventlistener-nodeport.yaml

获取 NodePort Webhook 地址

创建 EventListener 后,需获取 NodePort 和节点 IP 以构造 webhook URL:

# 1. 获取 EventListener 服务分配的 NodePort
# 替换 'el-el-demo' 为实际服务名(格式:el-<eventlistener-name>)
# 替换 'tekton-triggers-demo' 为命名空间
export NODEPORT=$(kubectl get svc el-el-demo -n tekton-triggers-demo -o jsonpath='{.spec.ports[0].nodePort}')
echo "NodePort: $NODEPORT"

# 2. 获取一个节点的外部 IP 地址
# 获取第一个节点的外部 IP,如节点不可直接访问可使用内部 IP
export NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')

# 若无 ExternalIP,尝试 InternalIP
if [ -z "$NODE_IP" ]; then
  export NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
fi
echo "Node IP: $NODE_IP"

# 3. 构造 webhook URL
export WEBHOOK_URL="http://${NODE_IP}:${NODEPORT}"
echo "Webhook URL: $WEBHOOK_URL"

备选:获取所有节点 IP 和 NodePort

# 获取 NodePort
NODEPORT=$(kubectl get svc el-el-demo -n tekton-triggers-demo -o jsonpath='{.spec.ports[0].nodePort}')

# 获取所有节点外部 IP
kubectl get nodes -o jsonpath='{range .items[*]}{.status.addresses[?(@.type=="ExternalIP")].address}{"\n"}{end}'

# 若无外部 IP,获取内部 IP
kubectl get nodes -o jsonpath='{range .items[*]}{.status.addresses[?(@.type=="InternalIP")].address}{"\n"}{end}'

webhook URL 格式为:http://<node-ip>:<nodeport>

测试 NodePort Webhook

# 测试 webhook 端点
curl -v -X POST -H 'Content-Type: application/json' \
  -d '{"message":"Hello from NodePort!"}' \
  $WEBHOOK_URL

验证 EventListener 状态

# 查看 EventListener 是否就绪
kubectl get eventlistener el-demo -n tekton-triggers-demo

# 查看服务
kubectl get svc el-el-demo -n tekton-triggers-demo

# 查看 EventListener Pod 日志
kubectl logs -n tekton-triggers-demo -l app=el-el-demo

NodePort 重要注意事项

  • 安全性:NodePort 使用 HTTP(非 HTTPS),不适合对安全性要求高的生产环境
  • 可访问性:节点 IP 必须能被发送 webhook 的外部系统访问
  • 高可用性:节点宕机时需使用其他节点 IP,建议前端使用负载均衡器
  • 端口范围:NodePort 使用 30000-32767 端口范围,确保防火墙允许访问
  • 多节点:NodePort 在所有节点均可用,可使用任一节点 IP

何时使用 NodePort

  • 开发和测试:快速搭建本地开发或测试环境
  • 内部网络:Webhook 发送方与集群节点处于同一内部网络
  • 无 LoadBalancer:集群无 LoadBalancer 服务或 Ingress 控制器
  • 成本考虑:避免 LoadBalancer 费用
WARNING

生产环境建议:生产环境强烈建议使用 LoadBalancer 或 Ingress 并启用 HTTPS,NodePort 缺乏加密和基于域名的路由支持。

从 NodePort 迁移到 Ingress

如果您从 NodePort 进行测试,现需迁移到生产环境的 Ingress:

  1. 更新 EventListener 服务类型:将 serviceTypeNodePort 改为 ClusterIP
  2. 配置自动暴露(推荐):使用 自动暴露功能 自动创建 Ingress 资源
  3. 或手动创建 Ingress:参考以下示例手动创建 Ingress 资源

以下示例展示如何为生产环境使用 Ingress 配置 EventListener。

小规模 + HTTPS + ALB Ingress 配置示例

前提条件

配置 EventListener 使用 Ingress 前,请确保满足以下条件:

  1. LoadBalancer/Gateway 配置

    • 集群中已部署并配置 LoadBalancer 或 Gateway(如 ALB)
    • LoadBalancer 可被发送 webhook 的外部系统访问
    • 详情请参考 配置负载均衡器
  2. 域名和证书配置

    • 域名正确配置并指向 LoadBalancer
    • TLS 证书已准备好(手动创建或通过 cert-manager)
    • DNS 记录配置正确
  3. Ingress 控制器

    • 已部署并正确配置 Ingress 控制器(本示例为 ALB)
    • Ingress 控制器已准备好处理 Ingress 资源
    • 详情请参考 创建 Ingress
INFO

注意:如果使用通过 TektonConfig 配置的自动暴露功能,通常无需手动创建 Ingress 资源。此示例展示手动创建 Ingress 的情况,适用于偏好手动控制或未使用自动暴露功能的场景。

配置示例

创建命名空间(可选)

确保存在用于方便管理 EventListener 及相关权限的命名空间,此处示例使用 tekton-webhooks

kubectl create namespace tekton-webhooks

创建 ClusterRole 和 ServiceAccount 并设置权限

  • 默认 ServiceAccount(triggers-default-sa:未指定 spec.serviceAccountName 时,EventListener 自动使用命名空间内的 triggers-default-sa,该 ServiceAccount 会自动拥有处理同一命名空间触发器所需的权限,无需手动配置 Role 或 RoleBinding。

  • 具有集群权限的自定义 ServiceAccount:配置 namespaceSelector 监听多个命名空间(包括 '*')时,必须指定具有集群范围权限的自定义 ServiceAccount。triggers-default-sa 仅有命名空间权限,无法跨命名空间访问资源。

WARNING

仅 namespaceSelector 需要:以下步骤仅在使用 namespaceSelector 监听多个命名空间触发器时必需。若 EventListener 仅处理同一命名空间触发器,可跳过,使用默认 triggers-default-sa 即可。

以下 YAML 为 eventlistener-role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: eventlistener-role
rules:
- apiGroups: [""]
  resources: ["configmaps", "secrets", "serviceaccounts"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: [""]
  resources: ["services", "pods", "events"]
  verbs: ["create", "get", "list", "watch", "patch", "update"]
- apiGroups: ["tekton.dev"]
  resources: ["pipelineruns", "taskruns"]
  verbs: ["create"]
- apiGroups: ["triggers.tekton.dev"]
  resources:
    - "clustertriggerbindings"
    - "triggerbindings"
    - "clusterinterceptors"
    - "interceptors"
    - "triggers"
    - "triggertemplates"
    - "eventlisteners"
  verbs: ["get", "list", "watch"]
kubectl apply -f eventlistener-role.yaml

使用上述 ClusterRole 和 ServiceAccount 创建绑定。

kubectl -n tekton-webhooks create serviceaccount eventlistener
kubectl create clusterrolebinding tekton-webhooks:eventlistener:eventlistener-role --clusterrole=eventlistener-role --serviceaccount=tekton-webhooks:eventlistener

创建 EventListener

将以下 YAML 保存为 eventlistener.yaml

INFO

ServiceAccount 选择

  • 若仅需处理同一命名空间触发器,可省略 spec.serviceAccountName,使用默认 triggers-default-sa,自动拥有所需命名空间权限。
  • 本示例使用 namespaceSelector 配置 '*' 监听所有命名空间触发器,需自定义具有集群权限的 ServiceAccount,故指定 serviceAccountName: "eventlistener"
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: eventlistener
  namespace: tekton-webhooks
  labels:
    el.tekton.dev/namespaces: all
    el.tekton.dev/size: small
spec:
  serviceAccountName: "eventlistener" # 必需:namespaceSelector 需集群权限自定义 SA
  # 监听所有命名空间触发器
  namespaceSelector:
    matchNames:
    - '*'
  # 声明 Kubernetes 资源,默认为 Deployment
  resources:
    kubernetesResource:
      serviceType: ClusterIP # ClusterIP 服务类型
      servicePort: 80  # 服务端口(容器端口)
      replicas: 2 # 副本数
      spec: # Deployment 规格
        template:
          metadata:
            labels:
              el.tekton.dev/namespaces: all
              el.tekton.dev/size: small
          spec: {}
kubectl apply -f eventlistener.yaml

创建 Ingress 和 TLS Secret

Info

需将 <host> 替换为对应域名及证书信息。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: eventlistener-ingress
  annotations:
    ### 注意 ALB 不需设置 ingressClassName
    ## kubernetes.io/ingress.class: "nginx" ## 根据 ingress 类变化,记得添加对应 ingress 注解
    ### 以下配置根据需求自定义:
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ### 注意 ALB 不需设置 ingressClassName
  ## ingressClassName: alb ## 根据需要更改 ingress 类
  tls:
  - hosts:
    - <host> # 域名设置,确保 DNS 解析配置正确(或测试时添加到 /etc/hosts)
    secretName: <tls secret> # 包含 TLS 证书的 Secret 名称
  rules:
  - host: <host> # 域名设置,确保 DNS 解析配置正确(或测试时添加到 /etc/hosts)
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: el-eventlistener 
            port:
              number: 80
WARNING

DNS 配置:配置 host 字段为域名时,请确保:

  • DNS 记录正确配置,指向 Ingress 控制器 IP
  • 本地测试可将域名添加到 /etc/hosts(Linux/Mac)或 C:\Windows\System32\drivers\etc\hosts(Windows)
  • 域名可被发送 webhook 的系统(GitHub、GitLab 等)解析访问

验证域名可访问性

配置域名和 Ingress 后,确保 webhook 域名可访问:

# 测试域名可访问性(替换 <host> 为实际域名)
curl -kI https://<host>

# 或测试具体 webhook 端点
curl -kI https://<host>/triggers/<eventlistener-namespace>/<eventlistener-name>
WARNING

重要:确保基于 LoadBalancer 的 webhook 域名可被外部系统(如 GitHub、GitLab 或其他 webhook 发送方)访问。若域名不可访问,webhook 将失败。请检查:

  • DNS 记录配置正确且已生效
  • LoadBalancer 配置正确,具有外部 IP/域名
  • 防火墙规则允许访问 LoadBalancer
  • TLS 证书有效(若使用 HTTPS)

验证 Webhook 配置

可使用以下 curl 测试配置是否正常。

curl -k -X POST -H 'Content-Type: application/json' -d '{"hello": "world"}' https://<host>/triggers/<eventlistener-namespace>/<eventlistener-name>

## 预期返回格式:
$> {
  "eventListener": "eventlistener",
  "namespace": "tekton-webhooks",
  "eventListenerUID": "8a7edab2-b426-453a-9f92-xxxxxx",
  "eventID": "aefd3b5b-2b19-4a14-b411-xxxxxxx"
}

最佳实践

  1. 资源限制

    • 为 EventListener Pod 设置合适的资源请求和限制
    • 根据实际负载调整副本数
  2. 安全性

    • 使用 HTTPS 和 Webhook Secrets
    • 配置最小权限的 ServiceAccount
    • 使用拦截器验证所有入站事件
  3. 可用性

    • 使用 LoadBalancer 或 Ingress 暴露服务
    • 配置合适的健康检查
    • 实现高可用部署
  4. 监控

    • 监控 EventListener 日志
    • 设置合适的告警机制
    • 跟踪事件处理性能

常见问题

  1. 事件未触发 Pipeline

    • 检查拦截器配置
    • 验证 Webhook 配置
    • 查看 EventListener 日志
  2. 权限问题

    • 确认 ServiceAccount 权限
    • 使用默认 triggers-default-sa 时,确认其存在于命名空间且已自动授予所需权限
    • 使用自定义 ServiceAccount 时,检查 Role 和 RoleBinding 配置
    • 验证命名空间访问权限
    • 使用 namespaceSelector 跨命名空间监听时,确保使用具有集群权限的自定义 ServiceAccount。triggers-default-sa 仅有命名空间权限。
  3. 性能问题

    • 调整资源限制
    • 优化拦截器配置
    • 考虑水平扩展

参考链接