NGINX Full Version

NGINX 实验教程:通过速率限制保护 Kubernetes API

注:本教程是 Microservices June 2022 微服务之月项目第二单元“在 Kubernetes 中暴露 API”的配套实验手册,您可点此报名参与这一免费线上教学项目以获取更多学习资源。

您的企业刚刚在 Kubernetes 中启用了第一个应用和 API,您被告知预计会有很多流量涌来(企业已实施了自动扩展,以确保 NGINX Ingress Controller 可以快速路由流量),担心 API 可能会遭到恶意攻击。如果 API 收到大量的 HTTP 请求 —— 可能会是暴力破解密码或 DDoS 攻击,那么 API 和应用都将不堪重负,甚至崩溃。

但您非常幸运!流量控制技术“速率限制”是一种 API 网关用例,可将传入请求速率限制为真实用户的典型值。您将 NGINX Ingress Controller 配置为实施速率限制策略,以防止应用和 API 被过多的请求压垮。干得漂亮!

 

实验和教程概述

本文是 Microservices March 2022 第二单元“在 Kubernetes 中暴露 API”中实验的配套文档,同时您也可以在自己的环境中将其当作教程使用(从我们的 GitHub 仓库中获取示例)。本实验演示了如何使用多个 NGINX Ingress Controller 并结合启用速率限制来防止应用和 API 被压垮。

为了完成实验,您需要在线下载包含 minikube,helm 等工具的虚拟机 VM 镜像,以及一台能够运行以下配置虚拟机 VM 的的电脑:

  • 至少 4 个 空余CPU
  • 4GB 可用内存
  • 40GB 可用磁盘空间
  • 互联网连接
  • 开启在 VM 内支持虚拟化 VT-x 技术
  • 容器或虚拟机管理器, VMware Fusion/工作站

注:本文提到的 minikube 需在可以启动浏览器窗口的台式/笔记本电脑上运行。如果您所处的环境做不到这一点,那么您需要解决如何通过浏览器访问服务的问题。

为了充分利用实验和教程,我们建议您在开始实验之前:

本教程使用了以下技术:

本教程涉及到三个挑战:

  1. 部署集群、应用、API 和 Ingress Controller
  2. 通过超负荷流量请求压垮您的应用和 API
  3. 运用两个 Ingress Controller 和速率限制来分别保护应用和 API

 

挑战 1:部署集群、应用、API和 Ingress Controller

在这项挑战中,您将部署一个 minikube 集群安装 Podinfo 作为示例应用和 API。

创建 Minikube 集群

创建 minikube 集群。几秒钟后将出现一条确认部署成功的消息。

$ minikube start 
  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

安装 Podinfo 应用和 Podinfo API

步骤 1:创建 Deployment

Podinfo 是一个“使用 Go 编写而成的 web 应用,展示了在 Kubernetes 中运行微服务的最佳实践”。它的占用空间较小,所以我们将其用作示例应用。

  1. 使用您选择的文本编辑器,创建一个名为 1-apps.yaml 的 YAML 文件,该文件应包含以下内容。它定义了一个 Deployment,其中包括:

    • 一个名为“Podinfo Frontend”的 Web 应用,用于渲染 HTML 页面
    • 一个名为“Podinfo API”的 API,用于返回 JSON 工作负载
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: api 
    spec: 
      selector: 
        matchLabels: 
          app: api 
      template: 
        metadata: 
          labels: 
            app: api 
        spec: 
          containers: 
            - name: api 
              image: stefanprodan/podinfo 
              ports: 
                - containerPort: 9898 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: api 
    spec: 
      ports: 
        - port: 80 
          targetPort: 9898 
          nodePort: 30001 
      selector: 
        app: api 
      type: LoadBalancer 
    --- 
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: frontend 
    spec: 
      selector: 
        matchLabels: 
          app: frontend 
      template: 
        metadata: 
          labels: 
            app: frontend 
        spec: 
          containers: 
            - name: frontend 
              image: stefanprodan/podinfo 
              ports: 
                - containerPort: 9898 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: frontend 
    spec: 
      ports: 
        - port: 80 
          targetPort: 9898 
          nodePort: 30002 
      selector: 
        app: frontend 
      type: LoadBalancer 
    
  2. 部署应用和 API:

    $ kubectl apply -f 1-apps.yaml
    deployment.apps/api created 
    service/api created 
    deployment.apps/frontend created 
    service/frontend created 
    
  3. 确认 Podinfo pod 已经部署成功,如 STATUS 列中的值 Running 所示。

    $ kubectl get pods  
    NAME                       	READY	STATUS    RESTARTS	AGE 
    api-7574cf7568-c6tr6        1/1     Running   0          87s 
    frontend-6688d86fc6-78qn7   1/1     Running   0          87s 
    

部署 NGINX Ingress Controller

安装 NGINX Ingress Controller 最快的方法是使用Helm.

使用 Helm 在单独的命名空间 (“nginx”) 中安装 NGINX Ingress Controller

  1. 创建命名空间:
  2.  kubectl create namespace nginx 
    
  3. 将 NGINX 仓库添加到 Helm:
  4.  helm repo add nginx-stable https://helm.nginx.com/stable 
    
  5. 在集群中下载并安装 NGINX Ingress Controller:
  6.  helm install main nginx-stable/nginx-ingress \ 
     --set controller.watchIngressWithoutClass=true \ 
     --set controller.ingressClass=nginx \ 
     --set controller.service.type=NodePort \ 
     --set controller.service.httpPort.nodePort=30010 \ 
     --set controller.enablePreviewPolicies=true \ 
     --namespace nginx 
    
  7. 确认 NGINX Ingress Controller pod 已经部署成功,如 STATUS 列中的值 Running 所示。
  8. $ kubectl get pods --namespace nginx 
    NAME                                  READY   STATUS    RESTARTS   AGE 
    main-nginx-ingress-779b74bb8b-d4qtc   1/1     Running   0          92s 
    

将流量路由到应用

  1. 使用您选择的文本编辑器,创建一个名为 2-ingress.yaml 的 YAML 文件,该文件应包含以下内容。它定义了将流量路由到应用和 API 所需的 Ingress manifest。
  2. apiVersion: networking.k8s.io/v1 
    kind: Ingress 
    metadata: 
      name: first 
    spec: 
      ingressClassName: nginx 
      rules: 
        - host: "example.com" 
          http: 
            paths: 
              - backend: 
                  service: 
                    name: frontend 
                    port: 
                      number: 80 
                path: / 
                pathType: Prefix 
        - host: "api.example.com" 
          http: 
            paths: 
              - backend: 
                  service: 
                    name: api 
                    port: 
                      number: 80 
                path: / 
                pathType: Prefix 
    
  3. 部署 Ingress 资源:
  4. $ kubectl apply -f 2-ingress.yaml 
    ingress.networking.k8s.io/first created 
    

测试 Ingress 配置

  1. 创建临时 pod:
  2. 为了确保 Ingress 配置按预期执行,您需要使用临时 pod 对其进行测试。在集群中启动一次性 busybox pod:

    kubectl run -ti --rm=true busybox --image=busybox 
    If you don't see a command prompt, try pressing enter. 
    / # 
    
  3. 测试 Podinfo API:
  4. 向 NGINX Ingress Controller pod 发出主机名为“api.example.com”的请求

    wget --header="Host: api.example.com" -qO- main-nginx-ingress.nginx 
    

    如果 API 能够正常接收流量,输出结果将为:

    { 
      "hostname": "api-687fd448f8-t7hqk", 
      "version": "6.0.3", 
      "revision": "", 
      "color": "#34577c", 
      "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", 
      "message": "greetings from podinfo v6.0.3", 
      "goos": "linux", 
      "goarch": "arm64", 
      "runtime": "go1.16.9", 
      "num_goroutine": "6", 
      "num_cpu": "4" 
    } 
    
  5. 测试 Podinfo Frontend
  6. 使用同一个 busybox 模拟 Web 浏览器,并输入以下代码来检索网页:

    wget --header="Host: example.com" --header="User-Agent: Mozilla" -qO- main-nginx-ingress.nginx 
    

    如果检索成功,将出现一个长响应,开头是:

    <!DOCTYPE html> 
    <html> 
    <head> 
      <title>frontend-596d5c9ff4-xkbdc</title> 
    

使用 minikube service frontend 在浏览器中打开 Podinfo 应用。您将会看到欢迎页面。

恭喜您!NGINX Ingress Controller 现在可以接收请求并将其转发到应用和 API 了。

在返回 Kubernetes 服务器的临时 pod 的命令提示符处输入 exit,即可结束 busybox 会话。

 

挑战 2:压垮您的应用和 API

在这项挑战中,您将使用开源负载测试工具 Locust 模拟流量激增来压垮 API 并导致应用崩溃。

安装 Locust

首先,我们必须部署流量生成器 Locust,以便观察应用和 API 对流量的响应情况。

  1. 使用您选择的文本编辑器,创建一个名为 3-locust.yaml 的 YAML 文件,该文件应包含以下内容。DeploymentService 对象定义 Locust pod。ConfigMap 对象定义一个名为 locustfile.py 的脚本,该脚本生成要发送到 pod 的请求,并包含正确的标头。流量不会均匀地分布到应用和 API —— 请求在分布上会偏向 Podinfo API,只有 1/5 的请求转到 Podinfo Frontend。
  2. apiVersion: v1 
    kind: ConfigMap 
    metadata: 
      name: locust-script 
    data: 
      locustfile.py: |- 
        from locust import HttpUser, task, between 
    
        class QuickstartUser(HttpUser): 
            wait_time = between(0.7, 1.3) 
    
            @task(1) 
            def visit_website(self): 
                with self.client.get("/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("Frontend failed") 
                    else: 
                        response.success() 
      
    
            @task(5) 
            def visit_api(self): 
                with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("API failed") 
                    else: 
                        response.success() 
    --- 
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: locust 
    spec: 
      selector: 
        matchLabels: 
          app: locust 
      template: 
        metadata: 
          labels: 
            app: locust 
        spec: 
          containers: 
            - name: locust 
              image: locustio/locust 
              ports: 
                - containerPort: 8089 
              volumeMounts: 
                - mountPath: /home/locust 
                  name: locust-script 
          volumes: 
            - name: locust-script 
              configMap: 
                name: locust-script 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: locust 
    spec: 
      ports: 
        - port: 8089 
          targetPort: 8089 
          nodePort: 30015 
      selector: 
        app: locust 
      type: LoadBalancer

    Locust 读取了存储在 ConfigMap 里的 locustfile.py:

    from locust import HttpUser, task, between 
    class QuickstartUser(HttpUser): 
        wait_time = between(0.7, 1.3) 
    
        @task(1) 
        def visit_website(self): 
            with self.client.get("/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response: 
                if response.request_meta["response_time"] > 200: 
                    response.failure("Frontend failed") 
                else: 
                    response.success() 
        @task(5) 
        def visit_api(self): 
            with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) as response: 
                if response.request_meta["response_time"] > 200: 
                    response.failure("API failed") 
                else: 
                    response.success() 
    
  3. 部署 Locust:
  4. $ kubectl apply -f  3-locust.yaml 
    configmap/locust-script created 
    deployment.apps/locust created 
    service/locust created 
    
  5. 使用 kubectl get pods 检索 pod,确认 Locust 已部署。Locust pod 此时可能仍位于 ContainerCreating 中。等到显示“Running”之后再进入下一节。
  6. NAME                        READY   STATUS              RESTARTS   AGE 
    api-7574cf7568-c6tr6        1/1     Running             0          33m 
    frontend-6688d86fc6-78qn7   1/1     Running             0          33m 
    locust-77c699c94d-hc76t     0/1     ContainerCreating   0          4s 
    

模拟流量激增并观察其对性能的影响

  1. 使用 minikube service locust 在浏览器中打开 Locust。
  2. 在以下字段中输入以下值:
  • 点击开始压测 (Start swarming)按钮,将流量发送到 Podinfo 应用和 API。
    • “图表”选项卡:该选项卡提供了流量的图形化描述。随着 API 请求的增加,您会发现 Podinfo API 的响应时间会延长。
    • “故障”选项卡:由于 Web 应用和 API 共用一个 Ingress controller,您会发现 Web 应用很快就会返回错误,并由于 API 请求的增加而出现故障。

    这是不正常的,因为区区一名攻击者就能通过 API 击垮由同一个 Ingress controller 支持的所有应用 —— 而不仅仅是 API!

     

    挑战 3:使用两个 Ingress Controller 和限制速率保护应用和 API

    在最后一项挑战中,您将解决先前部署的局限性。

    首先,我们来看一下如何解决架构问题。在之前的挑战中,API 请求压垮了 NGINX Ingress Controller,同时也影响了应用 Podinfo Frontend。这是因为只有一个 Ingress controller 负责将流量路由到应用和 API。

    为每个 service 运行单独的 NGINX Ingress Controller pod 可防止应用受到 API 请求激增的影响。虽然这不见得适用于每个用例,但在我们的模拟中,运行多个 Ingress controller 的好处是显而易见的。

    该解决方案的第二部分是将 NGINX Ingress Controller 用作 API 网关来实施速率限制,从而防止 Podinfo API 被压垮。

    什么是速率限制?

    速率限制技术可限制用户在给定时间段内的请求数量。举例来说,当受到 DDoS 攻击时,您可以使用速率限制将传入的请求速率限制为真实用户的典型值。使用 NGINX 实施速率限制后,任何提交过多请求的客户端都会被重定向到错误页面,从而确保 API 不受负面影响。如欲了解相关流程,请查看NGINX Ingress Controller 文档

    什么是 API 网关?

    API 网关将 API 请求从客户端路由到适当的服务,人们对这个简单定义存在很大的误解,认为 API 网关是一种独特的技术。实则不然。“API 网关”描述了一组可以通过不同类型的代理(ADC 或负载均衡器和反向代理最为常见,Ingress controller 和 service mesh 日渐兴起)实现的用例。速率限制是部署 API 网关的常见用例。如欲了解 Kubernetes 中的 API 网关用例的更多信息,请查看博文《API Gateway vs. Ingress Controller vs. Service Mesh,该怎么选?》

    准备集群

    1. 删除 Ingress 定义:
    2. 在实施这个新的架构和策略之前,必须删除原来的 Ingress 定义:

      kubectl delete -f 2-ingress.yaml 
      ingress.networking.k8s.io "first" deleted 
      
    3. 创建两个命名空间:
    4. 现在,创建两个新的命名空间 —— 为每个 NGINX Ingress Controller 分配一个。

      为 Podinfo Frontend 应用服务:

      kubectl create namespace nginx-web 
      namespace/nginx-web created 
      

      为 Podinfo API 服务:

      kubectl create namespace nginx-api 
      namespace/nginx-api created 
      

    安装 nginx-web NGINX Ingress Controller

    1. 安装 NGINX Ingress Controller:
    2. 该 NGINX Ingress Controller pod 将向 Podinfo Frontend 发送请求。

      helm install web nginx-stable/nginx-ingress \   
        --set controller.ingressClass=nginx-web \ 
        --set controller.service.type=NodePort \ 
        --set controller.service.httpPort.nodePort=30020 \ 
        --namespace nginx-web  
      
    3. 创建 Ingress manifest:
    4. 为 Podinfo Frontend 应用创建一个名为 4-ingress-web.yaml 的 Ingress manifest。

      apiVersion: networking.k8s.io/v1 
      kind: Ingress 
      metadata: 
        name: frontend 
      spec: 
        ingressClassName: nginx-web 
        rules: 
          - host: "example.com" 
            http: 
              paths: 
                - backend: 
                    service: 
                      name: frontend 
                      port: 
                        number: 80 
                  path: / 
                  pathType: Prefix 
      
    5. 部署新 manifest:
    6. kubectl apply -f 4-ingress-web.yaml 
      ingress.networking.k8s.io/frontend created  
      

    安装 nginx-api NGINX Ingress Controller

    上一节中的 manifest 专为 nginx-web Ingress controller 创建,如 ingressClassName 字段中所示。接下来,您需要为 Podinfo API 安装一个 Ingress controller,包括速率限制策略,以防 API 不堪重负。

    使用 NGINX Ingress Controller 配置速率限制的方法有两种:

    选项 1: NGINX Ingress 资源

    NGINX Ingress 资源是 Kubernetes 自定义资源的替代选择。该资源采用了一种原生、类型安全的缩进式配置风格,可简化 Ingress 负载均衡功能的实施,包括:

    • 断路 —— 用于适当地处理应用错误。
    • 复杂路由 —— 用于 A/B 测试和蓝绿部署
    • 标头操作 —— 用于将应用逻辑卸载到 NGINX Ingress controller=3/
    • 双向 TLS 认证 (MTLS) —— 用于基于零信任或身份的安全防护。
    • Web 应用防火墙 (WAF) —— 用于防范 HTTP 漏洞攻击。

    选项 2:代码段

    虽然代码段对本教程有效,但我们建议尽可能避免使用代码段,因为它们容易出错、难以使用、不能提供细粒度控制,并且还可能存在安全问题。

    本教程使用的是 NGINX Ingress 资源,它提供了用于限制速率的各种配置选项。在这项挑战中,您只需使用三个必需的速率限制参数:

    • Rate:允3指定为每秒请求数 (r/s) 或每分请求数 (r/m)。
    • Key:应用速率限制的条件关键字。可包含文本、变量或二者的组合。
    • Zone Size:共享内存分区的大小 —— 可配置为 MB 或 KB。NGINX worker 进程需要跟踪请求。

    我们通过一个示例来看一下这些参数。您可以看到,根据 NGINX 变量${binary_remote_addr}(该变量指示 NGINX Ingress Controller 根据唯一的 IP 地址实施限制),客户端被限制为每秒 1 个请求。

        rateLimit: 
          rate: 1r/s 
          key: ${binary_remote_addr} 
          zoneSize: 10M 
    
    1. 安装 NGINX Ingress Controller:
    2. 此 NGINX Ingress Controller pod 将向 Podinfo API 发送请求。

      helm install api nginx-stable/nginx-ingress  
        --set controller.ingressClass=nginx-api \ 
        --set controller.service.type=NodePort \ 
        --set controller.service.httpPort.nodePort=30030 \ 
        --set controller.enablePreviewPolicies=true \ 
        --namespace nginx-api 
      
    3. 创建 Ingress manifest:
    4. 创建一个名为 5-ingress-api.yaml 的 Ingress controller

      apiVersion: k8s.nginx.org/v1 
      kind: Policy 
      metadata: 
        name: rate-limit-policy 
      spec: 
        rateLimit: 
          rate: 10r/s 
          key: ${binary_remote_addr} 
          zoneSize: 10M 
      --- 
      apiVersion: k8s.nginx.org/v1 
      kind: VirtualServer 
      metadata: 
        name: api-vs 
      spec: 
        ingressClassName: nginx-api 
        host: api.example.com 
        policies: 
        - name: rate-limit-policy 
        upstreams: 
        - name: api 
          service: api 
          port: 80 
        routes: 
        - path: / 
          action: 
            pass: api 
      
    5. 部署新 manifest:
    6. kubectl apply -f 5-ingress-api.yaml 
      ingress.networking.k8s.io/api created 
      

    重新配置 Locust

    现在,重复 Locust 实验。您需要注意两个变化:

    • Podinfo API 未超载。
    • 无论将多少请求发送到 Podinfo API,都不会影响 Podinfo Frontend。
    1. 修改 Locust 脚本:
    2. 更改 Locust 脚本,以便:

    • 所有对 Web 应用发出的请求都经过 nginx-web (http://web-nginx-ingress.nginx-web)
    • 所有 API 请求都转到 nginx-api (http://web-nginx-ingress.nginx-web)

    因为 Locust 在仪表盘中支持单个 URL,所以您需要使用 YAML 6-locust.yaml 在 Python 脚本中硬编码该值。注意每个“任务”中的 URL。

    apiVersion: v1 
    kind: ConfigMap 
    metadata: 
      name: locust-script 
    data: 
      locustfile.py: |- 
        from locust import HttpUser, task, between 
    
        class QuickstartUser(HttpUser): 
            wait_time = between(0.7, 1.3) 
    
            @task(1) 
            def visit_website(self): 
                with self.client.get("http://web-nginx-ingress.nginx-web/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("Frontend failed") 
                    else: 
                        response.success() 
      
    
            @task(5) 
            def visit_api(self): 
                with self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, timeout=0.2) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("API failed") 
                    else: 
                        response.success() 
    --- 
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: locust 
    spec: 
      selector: 
        matchLabels: 
          app: locust 
      template: 
        metadata: 
          labels: 
            app: locust 
        spec: 
          containers: 
            - name: locust 
              image: locustio/locust 
              ports: 
                - containerPort: 8089 
              volumeMounts: 
                - mountPath: /home/locust 
                  name: locust-script 
          volumes: 
            - name: locust-script 
              configMap: 
                name: locust-script 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: locust 
    spec: 
      ports: 
        - port: 8089 
          targetPort: 8089 
          nodePort: 30015 
      selector: 
        app: locust 
      type: LoadBalancer 
    
  • 实施脚本更改:
  • 提交新的 YAML 文件。您将得到以下输出结果,确认脚本已更改(“配置”),而其余部分保持不变。

    kubectl apply -f 6-locust.yaml 
    configmap/locust-script configured 
    deployment.apps/locust unchanged 
    service/locust unchanged 
    
  • 强制重新加载:
  • 删除 Locust pod,强制重新加载新的 config map。以下命令将同时检索 pod 并删除 Locust pod。该命令包含了一些 Linux 命令和管道 (pipe)。

    kubectl delete pod `kubectl get pods | grep locust | awk {'print $1'}` 
    

    最后,检索 pod 并确认 Locust 重新加载成功。您将会发现 Locust pod 的时效只有几秒钟。

    
    kubectl get pods 
    NAME                        READY   STATUS    RESTARTS   AGE 
    api-7574cf7568-jrlvd        1/1     Running   0          9m57s 
    frontend-6688d86fc6-vd856   1/1     Running   0          9m57s 
    locust-77c699c94d-6chsg     1/1     Running   0          6s 
    

    测试速率限制

    1. 返回到 Locust 并更改参数:
  • 点击 Start swarming 按钮将流量发送到 Podinfo 应用和 API。
  • 您会发现,随着用户数量开始攀升,错误率也在逐渐增加。但是,点击 Locust UI 中的故障后,您会发现它们不再是 Web 前端,而是来自 API 服务。另请注意,NGINX 正在返回“Service Temporarily Unavailable(服务暂时不可用)”消息。这是速率限制特性的一部分,可自行定义。您为 API 实施了速率限制,并确保了 Web 应用始终可用,干得漂亮!

    后续步骤

    在现实世界中,仅实施速率限制不能保护应用和 API 的安全。要保护 Kubernetes 应用、API 和基础架构的安全,安全防护要求可能至少包括以下一种或两种方法:

    • 身份验证和授权
    • Web 应用防火墙和 DDoS 防护
    • 端到端加密和零信任
    • 遵从行业法规

    我们将在 Microservices June 2022 微服务之月的第四单元“微服务的安全防护模式”中探讨上述主题。

    如欲继续深入了解和本实验相关的知识技能,您可以加入到 Microservices June 2022 微服务之月项目中来,并继续浏览本项目第二单元“在 Kubernetes 中暴露 API” 的其他学习资源。

    如欲试用适用于 Kubernetes 且基于 NGINX Plus 的 NGINX Ingress Controller 和 NGINX App Protect,请立即下载 30 天免费试用版或者联系我们讨论您的用例

    如欲试用 NGINX 开源版 NGINX Ingress Controller,您可以获取源代码进行编译,或者从 DockerHub 下载预构建的容器。