k8s服务发布-Ingress

一、什么是Ingress

在 Kubernetes(K8s)环境中,Ingress 是一种 API 对象,用于管理集群内部服务(通常是 HTTP 服务)的外部访问,它提供了一种灵活的方式来定义和管理基于 HTTP/HTTPS 的路由规则,允许外部流量通过配置的规则访问 Kubernetes 集群中的服务。为集群中的服务提供了一个统一的入口,可以提供负载均衡、SSL终止和基于名称(域名)的虚拟主机,应用的灰度发布等功能。

Ingress 本身不直接处理流量,而是依赖于 Ingress Controller( Ingress Controller有很多种,如 Nginx、Traefik、HAProxy 、Istio或云提供商的实现,例如 AWS ALB)。Ingress Controller 是一个运行在集群中的组件,负责解析 Ingress 对象的规则并将流量路由到正确的服务。Ingress 对象定义了如何根据请求的域名、路径等将流量分发到后端服务(通常通过 Service 对象绑定到 Pod)。

相对于Service,Ingress工作在7层(部分Ingress控制器支持4层和6层),所以可以支持HTTP协议的代理,也就是基于域名的匹配规则。

二、Ingress和Ingress Controller

在 Kubernetes (K8s) 世界中,当您希望将集群内部运行的服务(例如您的应用程序)暴露给外部用户访问时,Ingress 和 Ingress Controller 就扮演了至关重要的角色。

核心概念:

  1. Ingress:
    • 它是什么? Ingress 是 Kubernetes 中的一个 API 对象,它定义了外部 HTTP/HTTPS 流量如何到达集群内部服务的规则集合。可以把它想象成一份“路由规则说明书”或者“交通规则手册”。
    • 它做什么? Ingress 资源本身并不做任何实际的流量转发工作。它只是一个声明性的配置,描述了你希望如何根据域名(主机名)、URL 路径等条件将外部请求路由到不同的后端服务。
    • 关键特性:
      • 主机名路由 (Host-based routing): 例如,foo.example.com 的请求转发到 foo-servicebar.example.com 的请求转发到 bar-service
      • 路径路由 (Path-based routing): 例如,example.com/app1 的请求转发到 app1-serviceexample.com/app2 的请求转发到 app2-service
      • TLS/SSL 终止 (TLS/SSL termination): 可以在 Ingress 层面配置 HTTPS,对外部流量进行加密,而内部服务可能只处理 HTTP 流量。
      • 负载均衡配置: 定义了第七层(应用层)的负载均衡规则。
  2. Ingress Controller:
    • 它是什么? Ingress Controller 是一个实际运行的组件(通常是一个或一组 Pod),它负责读取和实现 Ingress 资源中定义的规则。您可以把它想象成“交通警察”或者“路由规则的执行者”。
    • 它做什么? Ingress Controller 会持续监控 Kubernetes API Server 中 Ingress 资源的变化。当有新的 Ingress 规则创建、更新或删除时,Ingress Controller 会相应地配置其底层的负载均衡器(通常是一个反向代理,如 Nginx, HAProxy, Traefik 等),使得这些规则生效。它真正地接收外部流量,并根据 Ingress 定义的规则将流量转发到相应的内部服务。
    • 关键特性:
      • 规则的实现者: 没有 Ingress Controller,Ingress 资源仅仅是一堆配置,不会产生任何实际效果。
      • 实际的流量处理: 它承担了实际的流量入口和转发工作。
      • 多种实现: 有多种 Ingress Controller 的实现,例如 Nginx Ingress Controller, Traefik Ingress Controller, HAProxy Ingress Controller, Istio Ingress Gateway 等。您需要根据需求选择并部署一个 Ingress Controller 到您的集群中。

Ingress 和 Ingress Controller 的区别:

简单来说:

  • Ingress 是“定义”或“声明” (the “what”): 它描述了您希望流量如何被路由的规则。它是一张蓝图或一份合同。
  • Ingress Controller 是“实现”或“执行者” (the “how”): 它是一个实际运行的程序,读取 Ingress 的规则,并使这些规则生效,处理实际的流量。它是执行蓝图的建筑工人或履行合同的一方。

可以这样理解:

  • 您写了一份详细的派对邀请函(Ingress),上面写着:持有A类邀请函的客人请到A厅(service-a),持有B类邀请函的客人请到B厅(service-b)。
  • 您需要一位门口的接待员(Ingress Controller)来阅读这份邀请函,并根据邀请函上的指示,引导客人去正确的宴会厅。
  • 如果只有邀请函(Ingress)而没有接待员(Ingress Controller),那么客人来了也不知道该去哪里。
  • 如果只有接待员(Ingress Controller)而没有邀请函(Ingress),那么接待员也不知道该如何引导客人。

通过传统架构或服务进行类比:

让我们将 Ingress 和 Ingress Controller 与传统的 Web 服务器架构进行类比:

  1. Ingress Controller 类似于传统的反向代理服务器 / 负载均衡器 (例如 Nginx, HAProxy, Apache httpd + mod_proxy):

    • 在传统的架构中,您通常会在您的应用程序服务器(如 Tomcat, Node.js 应用)前面部署一个反向代理服务器(如 Nginx)。这个 Nginx 服务器接收所有外部的 HTTP/HTTPS 请求,然后根据配置(例如域名、URL路径)将请求转发到后端的某一个应用服务器实例。它还可以处理 SSL 终止、负载均衡、静态内容服务等。
    • Ingress Controller 就扮演了这个反向代理服务器的角色。 它作为流量的入口点,根据 Ingress 规则将流量分发到 Kubernetes 集群内部的各个服务。
  2. Ingress 类似于反向代理服务器的配置文件 (例如 Nginx 的 nginx.conf 中的 serverlocation 块):

    • 在 Nginx 中,您会在 nginx.conf 文件里定义各种 server 块来匹配不同的域名,并在每个 server 块中使用 location 块来匹配不同的 URL 路径,然后通过 proxy_pass 指令将请求代理到后端的应用。

    • Ingress 资源就扮演了这份配置文件的角色。

      通过 YAML 文件定义 Ingress 规则,例如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
      name: my-app-ingress
      spec:
      rules:
      - host: myapp.example.com
      http:
      paths:
      - path: /login
      pathType: Prefix
      backend:
      service:
      name: login-service
      port:
      number: 80
      - path: /data
      pathType: Prefix
      backend:
      service:
      name: data-service
      port:
      number: 8080

      这份 Ingress 规则告诉 Ingress Controller:“当收到访问 myapp.example.com/login 的请求时,将其转发到 login-service的 80 端口;

      当收到访问 myapp.example.com/data的请求时,将其转发到 data-service的 8080 端口。”

    • 主要区别在于: 传统的配置文件是静态的,需要手动修改和重载服务。而 Ingress 资源是 Kubernetes API 的一部分,可以动态创建、更新和删除,Ingress Controller 会自动感知这些变化并更新其路由配置,这使得管理更加灵活和自动化。

总结:

  1. 先理解需求: 在 Kubernetes 中,Pod 和 Service 通常只有内部 IP,外部无法直接访问。我们需要一种机制将外部请求安全、高效地路由到内部服务。
  2. Ingress 是“意图”: 它是一个 Kubernetes 资源,用它来声明“我希望当用户访问 a.com/foo 时,流量被送到 service-x;当用户访问 b.com 时,流量被送到 service-y”。它不关心这个意图是如何实现的。
  3. Ingress Controller 是“实现者”: 它是一个在集群中运行的 Pod(或一组 Pod),它像一个勤奋的管家,时刻关注着您声明的 Ingress “意图”。一旦有新的或更新的 Ingress 规则,它就会去配置其内部的代理服务器(如 Nginx),让这些规则真正生效。它才是真正处理外部请求并将其转发到正确服务的组件。

所以,Ingress 和 Ingress Controller 总是成对出现的。用Ingress定义 规则,然后部署一个 Ingress Controller 来使这些规则生效。这提供了一种强大且灵活的方式来管理对您集群中应用程序的访问。

三、Ingress发布服务流程

1、通过域名发布 K8s 中的服务 (使用 Ingress)

在 Kubernetes 中,我们通常使用 Ingress 资源和 Ingress Controller 来实现这一目标。Ingress 充当了“智能路由器”或“流量规则手册”,而 Ingress Controller 则是这个路由器的“执行者”。

前提条件:

  1. 一个运行中的 Kubernetes 集群。
  2. 您的应用程序已部署在 K8s 中,并通过一个 Service(通常是 ClusterIP 类型)暴露在集群内部。这个 Service 为您的应用 Pod 提供了集群内部的稳定 IP 和端口。
  3. 集群中已部署并运行一个 Ingress Controller。 例如 Nginx Ingress Controller, Traefik, HAProxy Ingress 等。这是外部流量进入集群的入口点。
  4. 您拥有一个域名 (例如 yourdomain.com)。
  5. 您有权限配置该域名的 DNS 记录。

具体实现流程:

  1. 部署您的应用程序和内部 Service (Deployment & Service):

    • 首先,确保您的应用程序(例如一个 Web 应用)已经通过 Deployment、StatefulSet 或其他控制器部署到 K8s 集群中。
    • 然后,为这些 Pod 创建一个 Service,类型通常是 ClusterIP。这个 Service 会为这一组 Pod 分配 一个集群内部的虚拟 IP 地址和端口,供集群内部其他服务或 Ingress Controller 访问。
    • 例如,您可能有一个 myapp-deployment.yamlmyapp-service.yamlmyapp-service 可能监听在集群内部的 http://myapp-service:8080
  2. 创建 Ingress 资源 (Ingress Resource):

    • 这是核心步骤。您需要创建一个 Ingress 类型的 YAML 文件,定义外部流量如何路由到您的 myapp-service

    • 一个简单的 Ingress 规则可能如下所示 myapp-ingress.yaml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
      name: myapp-ingress
      annotations:
      # 根据您使用的 Ingress Controller 可能需要特定注解
      # 例如 nginx.ingress.kubernetes.io/rewrite-target: /
      spec:
      rules:
      - host: "myapp.yourdomain.com" # 您希望使用的域名
      http:
      paths:
      - path: "/" # 访问该域名的根路径时
      pathType: Prefix
      backend:
      service:
      name: myapp-service # 指向第一步中创建的 Service 名称
      port:
      number: 8080 # 指向 myapp-service 暴露的端口
      # (可选, 但强烈推荐) TLS/SSL 配置,用于 HTTPS
      tls:
      - hosts:
      - "myapp.yourdomain.com"
      secretName: myapp-tls-secret # 包含 TLS 证书和私钥的 Secret 名称
    • 使用 kubectl apply -f myapp-ingress.yaml 将此 Ingress 资源应用到集群中。Ingress Controller 会检测到这个新的 Ingress 资源,并根据其规则更新其内部的路由配置。

  3. 获取 Ingress Controller 的外部 IP 地址或主机名:

    • Ingress Controller 本身也是一个 Service,它通常被配置为 LoadBalancer 类型(在云提供商环境中)或 NodePort 类型。您需要获取这个 Service 的外部可访问 IP 地址或主机名。

    • 运行命令(具体命名空间取决于您的 Ingress Controller 安装):

      1
      kubectl get svc -n <ingress-controller-namespace>

      例如,对于 Nginx Ingress Controller,可能是 kubectl get svc -n ingress-nginx,会看到类似

      ingress-nginx-controller的服务,并找到其 EXTERNAL-IP 列的值。如果是在某些云环境中,这里可能是一个长长的主机名(例如 AWS ELB 的 DNS 名称)。

  4. 配置 DNS 解析 (DNS Configuration):

    • 登录到您的域名注册商或 DNS 服务提供商的管理控制台。

    • 为您的域名

      1
      myapp.yourdomain.com

      添加一条 DNS 记录:

      • 如果 Ingress Controller 的外部入口是一个 IP 地址: 创建一条 A 记录,将其指向您在第 3 步中获取到的 Ingress Controller 的外部 IP 地址。
      • 如果 Ingress Controller 的外部入口是一个主机名 (例如 AWS ELB): 创建一条 CNAME 记录,将其指向该主机名。
    • DNS 记录的生效通常需要一些时间(取决于 TTL 配置)。

  5. (可选,但推荐)配置 TLS/SSL 证书:

    • 为了使用 HTTPS,您需要为您的域名获取一个 TLS 证书。
    • 您可以手动获取证书,然后将其创建为一个 Kubernetes Secret (tls 类型),并在 Ingress 资源的 tls.secretName 字段中引用它。
    • 更推荐的做法是使用 cert-manager 这样的工具,它可以自动从 Let’s Encrypt 等机构获取并续订证书,并自动创建和管理所需的 Secret。
  6. 测试访问:

    • 等待 DNS 记录生效后,在浏览器中输入 http://myapp.yourdomain.comhttps://myapp.yourdomain.com (如果您配置了 TLS)。
    • 如果一切配置正确,您的请求将通过以下路径到达您的应用: 用户 -> DNS 解析 -> Ingress Controller (外部 IP) -> Ingress 规则匹配 -> myapp-service -> 您的应用 Pod。

2、传统架构中通过域名发布服务的流程

在没有 Kubernetes 的传统架构中(例如,直接在物理服务器或虚拟机上部署),流程通常如下:

前提条件:

  1. 一台或多台服务器 (物理机或虚拟机)。
  2. 服务器上安装并运行您的应用程序。
  3. 服务器上安装并配置一个 Web 服务器或反向代理 (例如 Nginx, Apache httpd, HAProxy)。
  4. 服务器拥有一个公网 IP 地址。
  5. 防火墙已配置,允许外部访问 Web 服务器的端口 (通常是 80 用于 HTTP,443 用于 HTTPS)。
  6. 您拥有一个域名,并有权限配置 DNS。

具体实现流程:

  1. 部署您的应用程序:

    • 将您的应用程序代码(例如 Java WAR 包, Node.js 代码, Python 应用等)部署到服务器上。
    • 确保应用程序在服务器的某个端口上运行(例如 localhost:8080)。
  2. 配置 Web 服务器/反向代理:

    • 以 Nginx 为例,您需要编辑其配置文件 (通常是 /etc/nginx/nginx.conf/etc/nginx/sites-available/ 下的文件)。

    • 添加一个新的 server 块,如下所示:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      server {
      listen 80; # 监听 HTTP 端口
      server_name myapp.yourdomain.com; # 您希望使用的域名

      location / {
      proxy_pass http://localhost:8080; # 将请求代理到本地运行的应用
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      }

      # (可选, 但强烈推荐) HTTPS 配置
      # listen 443 ssl;
      # server_name myapp.yourdomain.com;
      # ssl_certificate /path/to/your/fullchain.pem;
      # ssl_certificate_key /path/to/your/privkey.pem;
      # ... 其他 SSL 配置 ...
      }
    • 保存配置文件并重新加载 Nginx 配置 (例如 sudo systemctl reload nginx)。

  3. 获取服务器的公网 IP 地址:

    • 记录下您的服务器的公网 IP 地址。
  4. 配置 DNS 解析 (DNS Configuration):

    • 登录到您的域名注册商或 DNS 服务提供商的管理控制台。
    • 为您的域名 myapp.yourdomain.com 添加一条 A 记录,将其指向您在第 3 步中获取到的服务器的公网 IP 地址。
  5. (可选,但推荐)配置 TLS/SSL 证书:

    • 如果您在 Nginx 配置中启用了 HTTPS,您需要获取并安装 TLS 证书。
    • 可以使用 Let’s Encrypt (通过 Certbot 工具) 免费获取证书,或者购买商业证书。
    • 确保证书文件路径在 Nginx 配置中正确指定。
  6. 测试访问:

    • 等待 DNS 记录生效后,在浏览器中输入 http://myapp.yourdomain.comhttps://myapp.yourdomain.com
    • 请求将通过以下路径: 用户 -> DNS 解析 -> 您的服务器公网 IP -> Nginx (或 Apache) -> 您的应用。

总结

特性 Kubernetes (使用 Ingress) 传统架构
服务发现与路由 声明式:通过 Ingress 资源定义路由规则,Ingress Controller 自动实现。Service 负责服务发现。 命令式/手动配置:直接在 Web 服务器配置文件中定义路由规则,指向具体 IP 和端口。
入口点管理 集中式:Ingress Controller 作为共享的入口点,管理多个服务的流量。 分散式:每个服务或一组服务可能需要独立的 Web 服务器或负载均衡器配置。
配置管理 Kubernetes API 对象 (YAML),易于版本控制和自动化 (GitOps)。 通常是服务器上的配置文件,管理可能需要 SSH 和手动编辑,或配置管理工具 (Ansible, Chef)。
可扩展性与高可用性 Kubernetes 内置支持服务的水平扩展和自愈。Ingress Controller 也可以水平扩展。 需要手动配置负载均衡器和健康检查来实现。
SSL/TLS 证书管理 可与 cert-manager 等工具集成,实现证书的自动获取、更新和管理。 通常需要手动获取、安装和续订证书,或编写脚本实现部分自动化。
抽象层次 :开发者更关注应用逻辑和路由规则,而非底层基础设施细节。 :需要管理服务器、操作系统、Web 服务器软件等更多细节。
灵活性与动态性 非常灵活,可以基于主机名、路径、请求头等进行高级路由。Ingress 规则可以动态更新而无需重启服务。 灵活性取决于 Web 服务器功能,配置更新通常需要重载或重启 Web 服务器。
初始复杂度 学习曲线相对陡峭,需要理解 Kubernetes 的核心概念。 对于简单场景,配置相对直接。
  • Kubernetes 的方式 (Ingress) 更加云原生,提供了更高的自动化、可扩展性和灵活性,尤其适合微服务架构和需要频繁变更的动态环境。它将路由逻辑从应用本身和底层基础设施中解耦出来,由 Ingress Controller 集中管理。
  • 传统架构的方式 更为直接,对于小型、静态的应用可能更容易上手。但随着应用规模和复杂度的增加,手动管理配置、扩展性和高可用性会变得越来越困难。

选择哪种方式取决于您的具体需求、团队技能和项目规模。对于现代云应用开发,Kubernetes 及其 Ingress 机制提供了一个强大且标准化的解决方案。

四、Ingress生产级可用架构

ingress-nginx是k8s官方维护的

nginx-ingress是nginx官方维护的

Ingress Controller 是集群的入口,它的地位非常重要,不能受其他服务的影响或干扰,如果Ingress Controller 出现问题会影响整个集群的服务。所以在生产环境中推荐使用单独的节点部署Ingress Controller (利用节点选择器,容忍和污点使Ingress Controller 部署到指定的节点上),同时副本数也要大于2。

五、Ingress Controller安装部署

5.1Ingress Controller 生产级高可用安装方式(推荐)

如果需要在其他环境中部署ingress controller建议参考https://kubernetes.github.io/ingress-nginx/deploy/

该笔记中以裸金属环境中安装部署:https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal-clusters ,通过host network的方式部署

https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.2/deploy/static/provider/baremetal/deploy.yaml文件为基础进行修改并部署。

1.将Deployment修改为DaemonSet

2.删除副本数配置replicas(如果有就删除)

3.为DaemonSet配置添加hostNetwork: true(DaemonSet.spec.template.spec.hostNetwork: true)

4.添加dnsPolicy: ClusterFirstWithHostNet(和hostNetwork: true属于同级)

5.如果需要部署在指定的节点,还需要为节点添加标签和标签选择器,下面以部署在k8s-node01 k8s-node02 k8s-node03中为例:

1
2
3
4
5
6
7
#为k8s-node01 k8s-node02 k8s-node03节点添加标签
kubectl label node k8s-node01 k8s-node02 k8s-node03 ingress=true

#配置文件中DaemonSet添加标签配置
nodeSelector:
kubernetes.io/os: linux
ingress: true #使用该标签将pod部署到指定的节点上
1
2
3
4
[root@k8s-master01 14-ingress]# kubectl label nodes k8s-node01 k8s-node02 k8s-node03 ingress=true
node/k8s-node01 labeled
node/k8s-node02 labeled
node/k8s-node03 labeled

6.部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@k8s-master01 14-ingress]# kubectl create -f deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
daemonset.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

查看部署情况:

kubectl get pod -n ingress-nginx

添加标签注意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#如果使用命令给节点添加标签,准确写法是:kubectl label nodes k8s-node01 ingress=true
#错误写法:kubectl label nodes k8s-node01 ingress="true" #错误!shell 会把引号也当成值的一部分


#如果是在yaml中定义标签,建议标签的value加引号,例如:
metadata:
labels:
app: "myapp"
ingress: "true" # ✅ 推荐写法:显式表示这是字符串

#错误写法如下:
metadata:
labels:
ingress: true # ⚠️ 可能被解析为布尔值,导致出错

使用hostNetwork模式,会在每个宿主机上监听80和443端口:

错误演示:不要将pod部署在master节点上:

pod部署在了k8s-master01、 k8s-master02 、k8s-master03,如下图:(这里不要把pod部署到master节点上,否则会无法创建ingress资源,具体错误见下方《注意》)

注意:不要把ingress-nginx-controller的pod部署到任意master节点上,要部署在node节点上,否则在创建ingress资源时会报以下错误,

1
2
[root@k8s-master01 ~]# kubectl create -f nginx-ingress.yaml
Error from server (InternalError): error when creating "nginx-ingress.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": tls: failed to verify certificate: x509: certificate is valid for kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster, kubernetes.default.svc.cluster.local, not ingress-nginx-controller-admission.ingress-nginx.svc

六、Ingress入门

6.1 Ingress资源定义规范

ingress资源基本配置解读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建一个ingress资源
[root@k8s-master01 ingress-nginx]# vim web-ingress.yaml
king: Ingress #指定创建资源的类型,ingress是一个入口类型的资源,用于管理外部访问集群内部服务的规则
apiVersion: networking.k8s.io/v1 #指定 Kubernetes API 版本,1.19之前用networking.k8s.io/v1beta1
metadata: #定义资源的元数据信息
name: nginx-ingress #定义该资源的名字
namespace: study-ingress #定义该资源所在的命名空间,所有与该 Ingress 相关的服务(如 nginx-service)也应在这个命名空间中,否则需要使用完整 DNS 名称(如 nginx-service.other-ns.svc.cluster.local)。
spec: #定义了该 Ingress 资源的具体配置和行为。
ingressClassName: nginx #指定了该 Ingress 资源使用的 Ingress 控制器。如果你集群中有多个 Ingress 控制器(例如 Traefik 或 Nginx),你可以通过 kubectl get ingressclasses 来查看都有哪些控制器。Ingress 本身只是一个路由规则,真正实现这些规则的是 Ingress Controller(控制器) (比如 Nginx Ingress Controller、Traefik 等)。通过 kubectl get ingressclasses 来查看都有哪些控制器。这个字段告诉 Kubernetes:我这个 Ingress资源定义的规则应该由哪个控制器处理。nginx 是你在集群中定义的一个 IngressClass 的名称。
rules: #定义了路由规则,指定了如何根据请求的主机名和路径将流量路由到后端服务。
- host: nginx.test.com #指定了该规则适用的主机名。表示访问nginx.test.com的HTTP请求都会匹配该规则。
http: #定义了 HTTP 路由规则,表示该规则处理的是 HTTP 流量。
paths: #定义了 HTTP 请求的路径匹配规则。
- path: / #path: / 表示任何请求的路径(从 / 开始)都会匹配该规则。这里的 / 是根路径,意味着访问 nginx.test.com/ 时会触发该规则。 访问 nginx.test.com/ 或 nginx.test.com/abc 等路径时,都会被路由到相应的后端服务。
pathType: ImplementationSpecific #指定路径匹配的类型。
backend: #定义了请求流量的目标后端服务。
service: #定义目标后端服务的信息。
name: nginx-service #定义了目标服务的名称。这里的名字是你要匹配到的的service名字
port: #指定service的端口
number: 80 #service的端口号,这里的端口号是service的port,而不是targetPort

pathType:路径的匹配方式,目前有ImplementationSpecific、Exact和Prefix三种方式:

  • Exact:精确匹配,比如配置的path为/bar,请求路径 必须和 Ingress 中配置的 path 完全一致,不能多一个 /,也不能少一个字符通常用于 API 端点或特定路径。例如/bar/将不能被路由;

    案例:

    1
    2
    3
    4
    5
    6
    7
    - path: /api/v1/users
    pathType: Exact #表示精确匹配
    backend:
    service:
    name: users-v1
    port:
    number: 80
    浏览器输入的 URL 是否匹配? 说明
    https://example.com/api/v1/users ✅ 匹配 完全相同
    https://example.com/api/v1/users/ ❌ 不匹配 多了一个 /
    https://example.com/api/v1/users/detail ❌ 不匹配 多了 /detail
    https://example.com/api/v1 ❌ 不匹配 少了一段
  • Prefix:前缀匹配,只要请求路径 以配置的 path 开头,就能匹配。基于以 / 分隔的 URL 路径。比如path为/abc,可以匹配到/abc/bbb 等,比较常用的配置,对于 Web 应用,通常使用 Prefix

    案例:

    1
    2
    3
    4
    5
    6
    7
    - path: /api/v1/
    pathType: Prefix
    backend:
    service:
    name: users-prefix
    port:
    number: 80
    浏览器输入的 URL 是否匹配? 说明
    https://example.com/api/v1/ ✅ 匹配 一样
    https://example.com/api/v1/users ✅ 匹配 /api/v1/ 开头
    https://example.com/api/v1/users/detail ✅ 匹配 仍以 /api/v1/ 开头
    https://example.com/api/v2/ ❌ 不匹配 前缀不同
  • ImplementationSpecific:这种类型的路由匹配根据Ingress Controller来实现,可以当 做一个单独的类型,也可以当做Prefix和Exact。ImplementationSpecific是1.18版本 引入Prefix和Exact的默认配置。

6.2字段配置

参考https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/

6.3使用域名发布k8s服务

创建一个web服务:

1
kubectl create deploy nginx --image=registry.cn-beijing.aliyuncs.com/dotbalo/nginx:1.15.12 

创建service暴露该deploy:

1
kubectl expose deploy nginx --port 80 

创建Ingress对外提供服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#定义一个ingress文件
vim web-ingress.yaml
apiVersion: networking.k8s.io/v1 # k8s >= 1.22 必须 v1
kind: Ingress
metadata:
name: nginx-ingress #定义ingress资源的名字
spec:
ingressClassName: nginx #指定了该 Ingress 资源使用的 Ingress 控制器的名字。通过 kubectl get ingressclasses 来查看都有哪些控制器。这个字段告诉 Kubernetes:我这个 Ingress资源定义的规则应该由哪个控制器处理。
rules: #定义规则
- host: nginx.test.com #请求的主机名
http:
paths:
- path: / #请求路径
pathType: ImplementationSpecific #匹配规则
backend:
service:
name: nginx #指定转发到哪个service上,这里是service的名字
port:
number: 80 #指定service暴露的端口

在本地hosts文件中添加解析记录,生产环境中需要配置DNS解析:

1
2
3
4
5
6
7
#wendows路径 C:\Windows\System32\drivers\etc
#Linux路径: /etc/hosts

#在hosts添加下面记录,即可通过浏览器访问到项目
192.168.0.84 nginx.test.com
192.168.0.85 nginx.test.com
192.168.0.86 nginx.test.com

6.3特例-不配置域名发布服务

不想通过域名访问,而是通过ip地址访问可以不配置host: nginx.test.com 来实现,不常用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: web-ingress
namespace: default
spec:
ingressClassName: nginx
rules:
-
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80

七、Ingress实战

接下来的操作在单独的namespace中操作,创建命名空间ingress

1
2
[root@k8s-master01 ~]# kubectl create ns ingress
namespace/ingress created

创建deployment服务模拟web:

1
2
[root@k8s-master01 14-ingress]# kubectl create deployment vue --image=registry.cn-beijing.aliyuncs.com/k8s-liujunwei/vue:test -n ingress
deployment.apps/vue created

创建service,暴露上述deployment

1
2
[root@k8s-master01 14-ingress]# kubectl expose deployment -n ingress vue --port=80
service/vue exposed

7.1使用HTTPS发布服务

生产环境对外的服务,一般需要配置https协议,使用Ingress也可以非常方便的添加https 的证书。 由于是测试环境,并没有权威证书,所以需要使用OpenSSL生成一个测试证书。如果 是生产环境,证书为在第三方公司购买的证书,无需自行生成:
注意:证书的配置推荐配置在入口网关或SLB处,如果没有也可以配置在ingress控制器上;

1
2
3
4
5
6
7
8
9
10
11
12
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginx.test.com"

#命令参数解读:
openssl req: 表示创建一个证书签发请求,同时也能直接生成自签名证书。现在用的是自签证书,因此配合 -x509 一起使用。
-x509: 告诉 openssl:不要生成 CSR(证书签名请求),而是直接生成一个自签名证书。使用场景:测试环境、内部系统。
-nodes:表示私钥不加密码。不加密码的理由:K8s中要自动加载证书,Nginx或Ingress不可能每次启动让你手动输入密码,所以必须加 -nodes
-days 365:证书有效期365天。例如:测试用1年:-days 365,短期测试:-days 30。
-newkey rsa:2048:生成一个新的密钥对:算法:RSA长度:2048 。更安全:rsa:4096,更快:rsa:2048
-keyout tls.key:生成私钥文件名称。可以自由改名:-keyout myproject.key
-out tls.crt:生成证书文件名称。同样可以自定义:-out myproject.crt
-subj "/CN=nginx.test.com" 证书的主体信息(subject)。关键字段:CN(Common Name)CN 必须与你访问的域名一致。也可以加入更多字段,例如:-subj "/C=CN/ST=Guangdong/L=Shenzhen/O=TestCompany/OU=Dev/CN=web.jiugen.com" 不过一般测试环境只用 CN。

使用生成的 tls.keytls.crt创建secret:

1
2
3
4
5
6
7
8
[root@k8s-master01 14-ingress]# kubectl create secret tls vue-tls --cert=tls.crt --key=tls.key -n ingress
secret/vue-tls created

# kubectl create secret表示创建一个secret
# tls表示secret的类型是tls类型的
# vue-tls表示创建的sercret的名字是vue-tls
# --cert=tls.crt --key=tls.key 指定tls的文件路径
# -n ingress 将这个secret资源创建在ingress命名空间

配置ingress暴露服务,并添加TLS配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
vim https-ingress.yaml
# Kubernetes Ingress 资源定义
# 用途:配置外部访问集群内服务的规则,Ingress 用于将外部 HTTP/HTTPS 流量路由到集群内的服务
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata: # 元数据部分
name: https-ingress # Ingress 资源的名称,用于在集群中标识这个配置;使用有意义的名称,如 "应用名-ingress"
namespace: ingress # 命名空间,指定该 Ingress 所在的位置;建议与要暴露的服务放在同一命名空间
spec: # 规格定义部分
# 说明:指定使用哪个 Ingress 控制器来处理这个规则
# 配置建议:常见值有 "nginx"、"traefik"、"alb" 等,需与集群安装的控制器匹配,可以使用kubectl get ingressclasses.networking.k8s.io查看集群有哪些控制器
ingressClassName: nginx #定义使用控制器类的名字是nginx,
tls: # TLS/HTTPS 配置部分,配置 SSL 证书以启用 HTTPS 访问
- hosts:
# 指定使用此证书的域名列表
# 该域名必须与下面 rules 中的 host 匹配
- nginx.jiugen.com
# Secret 名称:存储 TLS 证书和私钥的 Kubernetes Secret 资源
# 创建方式:kubectl create secret tls vue-tls --cert=cert.crt --key=cert.key
# Secret 必须在同一命名空间内
secretName: vue-tls
# 路由规则部分
# 说明:定义如何将外部请求转发到内部服务
rules:
- # 域名匹配规则
# 说明:只有访问此域名的请求才会匹配这条规则
# 配置建议:需与 DNS 解析的域名一致,支持泛域名如 "*.example.com"
host: nginx.jiugen.com
# HTTP 路由配置
http:
# 路径匹配规则列表
# 说明:可以配置多个 path,实现不同路径转发到不同服务
paths:
- # 匹配的 URL 路径
# 说明:这里的 "/" 表示匹配所有路径
# 配置示例:"/api" 只匹配 /api 开头的请求
path: /
pathType: Prefix # 路径匹配类型
backend: # 后端服务配置,说明:定义请求转发到哪个服务
service: # 服务名称
name: vue #vue是service的名字,需与实际存在的 Service 资源名称一致
port: # 定义service服务的端口号
number: 80 # 指定service服务暴露的端口,需与 Service 中定义的端口匹配

创建上述ingress:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f https-ingress.yaml
ingress.networking.k8s.io/https-ingress created

查看创建的ingress:

1
2
3
4
[root@k8s-master01 14-ingress]# kubectl get ingress -n ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
https-ingress nginx nginx.jiugen.com 192.168.0.84,192.168.0.85,192.168.0.86 80, 443 51s
#可以看到PORTS 字段多了443端口

接下来通过添加hosts文件,解析该域名,在浏览器进行访问:

1
2
# hosts文件中添加下列记录
192.168.0.84 nginx.jiugen.com

浏览器访问nginx.jiugen.com,点击继续访问网站,可以看到网站是https,由于证书是自己生成的,所以浏览器会提示不安全。生产环境使用权威证书即可,不会提示不安全。

7.2为域名添加用户名密码认证

有些开源工具本身不提供密码认证,如果暴露出去会有很大风险,对于这类工具可以使用 Nginx 的 basic-auth 设置密码访问,具体方法如下,由于需要使用htpasswd工具,所以需要先安装 httpd:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master01 ~]# htpasswd -c auth liujunwei
New password: #这里输入的密码不显示
Re-type new password: ##二次输入密码,密码不显示
Adding password for user liujunwei
# 参数解释:
# htpasswd:Apache 提供的密码文件管理工具,用于创建和管理用于基本认证(Basic Authentication)的用户密码文件
# -c:Create(创建)的缩写。作用:创建一个新的密码文件,注意:如果文件已存在,会被覆盖!所有旧数据会丢失,如果是向已有文件添加用户,不要使用 -c 参数
# auth:密码文件的名称,可以自定义文件名,如 .htpasswd、passwords 等,这个文件会保存加密后的用户名和密码
# liujunwei:要创建的用户名

[root@k8s-master01 ~]# cat auth #加密后的密码
liujunwei:$apr1$6FN/hU6e$LzcerK4LOLHU/a2jfb1ks0

# 扩展
# 2. 向已有文件添加更多用户(不要使用 -c,这样不会覆盖文件,只是添加新用户 bar)
htpasswd auth bar

基于auth密码文件创建secret:

1
2
3
4
5
6
7
[root@k8s-master01 ~]# kubectl create secret generic basic-auth --from-file=auth -n ingress
secret/basic-auth created
# kubectl create secret创建secret指令
# generic表示创建的secret的类型
# basic-auth是创建的Secret的名字
# --from-file=auth --from-file表示基于文件创建,auth是文件名
# -n ingress 表示将这个secret创建到ingress命名空间

查看创建的secret:

1
2
3
4
[root@k8s-master01 ~]# kubectl get secrets  -n ingress
NAME TYPE DATA AGE
basic-auth Opaque 1 21s
vue-tls kubernetes.io/tls 2 20h

基于7.1中创建的ingress(https-ingress),添加密码认证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 添加 annotations 部分
cat https-ingress.yaml
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: https-ingress
namespace: ingress
annotations:
nginx.ingress.kubernetes.io/auth-realm: "请输入用户名和密码" #消息提醒,
nginx.ingress.kubernetes.io/auth-secret: "basic-auth" #指定密码文件的 Secret 名称
nginx.ingress.kubernetes.io/auth-type: "basic" #认证类型,可以是basic和digest
spec:
ingressClassName: nginx
tls:
- hosts:
- nginx.jiugen.com
secretName: vue-tls
rules:
- host: nginx.jiugen.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vue
port:
number: 80

重新应用 https-ingress.yaml,浏览器访问 nginx.jiugen.com 会提示需要登录认证才能访问,这里输入的用户和密码就是htpasswd -c auth liujunwei指令创建的用户和密码:

nginx.ingress.kubernetes.io/auth-type: "basic"作用:告诉 Ingress-NGINX 使用 Basic Auth(用户名密码认证)

​ 常见类型:

  • basic 基本认证(对话框输入用户名密码)
  • digest 摘要认证(较少使用)

nginx.ingress.kubernetes.io/auth-secret: "basic-auth"作用:指定 Basic Auth 的用户名密码存放在哪个 Kubernetes Secret 中。

​ 格式要求:

  • Secret 类型必须是 Opaque
  • 数据必须包含键名 liujunwie

nginx.ingress.kubernetes.io/auth-realm: "请输入用户名和密码"现在浏览器的 Basic Auth 弹窗已经不再显示自定义提示文本。

7.2开启会话保持

和Nginx 一样,Ingress Nginx 也支持基于cookie的会话保持。

先将服务扩容至多个副本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@k8s-master01 14-ingress]# kubectl get po -n ingress
NAME READY STATUS RESTARTS AGE
vue-d6bdf9fd8-wqrjs 1/1 Running 0 41h

# 扩容
[root@k8s-master01 14-ingress]# kubectl scale deployment -n ingress vue --replicas=3
deployment.apps/vue scaled

# kubectl scale 扩容指令
# deployment 表示你要扩容的资源是deployment
# -n ingress 指定扩容的资源所在的命名空间
# vue 是deployment资源的名字
# --replicas=3 将副本扩容为3个

#检查扩容后的pod
[root@k8s-master01 14-ingress]# kubectl get po -n ingress
NAME READY STATUS RESTARTS AGE
vue-d6bdf9fd8-2jn5w 1/1 Running 0 17m
vue-d6bdf9fd8-ssp86 1/1 Running 0 32m
vue-d6bdf9fd8-wqrjs 1/1 Running 0 42h

未开启会话保持,同一个主机访问可以看到流量会在三个副本中都有。

通过以下配置,即可看到流量只会进入到一个Pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[root@k8s-master01 14-ingress]# cat https-ingress.yaml
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: https-ingress
namespace: ingress
annotations:
nginx.ingress.kubernetes.io/auth-realm: "请输入用户名和密码"
nginx.ingress.kubernetes.io/auth-secret: "basic-auth"
nginx.ingress.kubernetes.io/auth-type: "basic"
nginx.ingress.kubernetes.io/affinity: "cookie" #开启基于 Cookie 的会话亲和性,也就是会话保持,确保同一用户的请求始终被转发到同一个后端 Pod。
nginx.ingress.kubernetes.io/session-cookie-name: "ingress_session" #指定 Cookie 的名称,可以自定义。
nginx.ingress.kubernetes.io/session-cookie-expires: "14400" #设置 Cookie 的过期时间(秒),Expires 是绝对时间(兼容旧浏览器)
nginx.ingress.kubernetes.io/session-cookie-max-age: "14400" #设置 Cookie 的最大存活时间(秒),Max-Age 是相对时间(更现代,优先级更高)
nginx.ingress.kubernetes.io/affinity-mode: "persistent" #设置亲和性模式为持久化,persistent(持久化):即使后端 Pod 重启或扩缩容,只要 Cookie 有效,仍然尝试路由到原 Pod;balanced(平衡):当后端 Pod 发生变化时,会重新分配会话.如果需要在ingress开启会话保持推荐使用persistent参数。
spec:
ingressClassName: nginx
tls:
- hosts:
- nginx.jiugen.com
secretName: vue-tls
rules:
- host: nginx.jiugen.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vue
port:
number: 80

同时浏览器的request headers会添加一个Cookie的属性:

流量重新分配,更改nginx.ingress.kubernetes.io/affinity-mode: balanced即可:

1
2
3
4
5
6
7
8
9
annotations:
nginx.ingress.kubernetes.io/auth-realm: "请输入用户名和密码"
nginx.ingress.kubernetes.io/auth-secret: "basic-auth"
nginx.ingress.kubernetes.io/auth-type: "basic"
nginx.ingress.kubernetes.io/affinity: "cookie" #开启基于 Cookie 的会话亲和性,也就是会话保持,确保同一用户的请求始终被转发到同一个后端 Pod。
nginx.ingress.kubernetes.io/session-cookie-name: "ingress_session" #指定 Cookie 的名称,可以自定义。
nginx.ingress.kubernetes.io/session-cookie-expires: "14400" #设置 Cookie 的过期时间(秒),Expires 是绝对时间(兼容旧浏览器)
nginx.ingress.kubernetes.io/session-cookie-max-age: "14400" #设置 Cookie 的最大存活时间(秒),Max-Age 是相对时间(更现代,优先级更高)
nginx.ingress.kubernetes.io/affinity-mode: "balanced" # balanced(平衡):当后端 Pod 发生变化时,会重新分配会话

总结:

①、会话保持(Session Stickiness)是什么?

一句话:让同一个用户的请求一直落到同一个后端 Pod 上。

适用场景:

  • 你的应用把用户 Session 放在内存里
  • 登录信息不共享给其他 Pod

如果没有会话保持,会发生:

  • 用户第一次登录落到 Pod A
  • 第二次请求被转到 Pod B
  • Pod B 不认识这个用户
  • 用户就莫名其妙掉线、重新登录

负载均衡器通过“Cookie 绑定”或“源 IP 绑定”把流量固定到一个 Pod,就能避免这个问题。

②、会话共享(Session Sharing)是什么?

一句话:无论用户的请求落到哪个 Pod,所有 Pod 都能读取统一的 Session 数据。

实现方式:

  • Redis Session(最常用)
  • 数据库存 Session
  • JWT Token(服务端不存 Session,最现代方案)

好处:

  • 用户访问 Pod A、Pod B、Pod C 都不影响登录状态
  • 不需要会话保持
  • 服务可以随便扩容、缩容,无状态更稳定

这就是所谓的 真正的分布式集群设计

③、会话保持 VS 会话共享(最关键的区别)

项目 会话保持 (Session Stickiness) 会话共享 (Session Sharing)
原理 同一个用户固定访问同一个 Pod 所有 Pod 共用 Session 数据
会话存储 每个 Pod 自己内存 Redis/数据库等
扩容能力 差,Pod 多了会不均衡 强,真正的分布式
故障场景 Pod 挂了 => 用户掉线 Pod 挂了 => 不影响用户登录
实现复杂度 简单 略复杂但很好维护
适合场景 测试环境、Demo 生产环境

④、你在生产环境应该选择哪种?

很明确:生产环境必须使用“会话共享”方案(Redis Session 或 JWT)

理由:

  • 多副本微服务中最稳定
  • 即使某个 Pod 被重启,用户不掉线
  • 扩容缩容不影响
  • 对接 Kubernetes 的弹性伸缩更自然
  • 不受 Ingress、Service、负载均衡策略影响
  • 现代架构通用方案

⑤、实际生产怎么做?

方案 A:Redis 分布式 Session(Java Spring 体系最常用)

架构如下:

1
用户 -> Ingress -> 任意 Pod -> Redis 中读取用户 Session

Java 项目只要加入:

1
spring-session-data-redis

即可自动把 Session 存 Redis。

方案 B:JWT(推荐给新系统)

无状态方案:

  • 用户登录 -> 后端返回 JWT Token
  • Token 存在浏览器
  • 后端不存 Session
  • 每个 Pod 都能解析 Token,完全无状态

这是云原生最现代的方式。

扩容、缩容、滚动更新完全不受影响。

六、那在 Kubernetes Ingress 中,要不要开启会话保持(Sticky Session)?

测试环境:可以开;生产环境:不要开,会话共享才是正解。

7.3 配置流式返回SSE(代理大模型服务)

7.3.1什么场景需要“持续输出/长连接”?

场景一:SSE(Server-Sent Events)持续推送数据,浏览器用 EventSource,不断接收后端推送。

场景二:WebSocket 长连接,例如聊天、通知服务、实时状态更新等。

场景三:HTTP 长轮询 / 流式响应(例如接口返回大文件)

这些都属于“长连接 / 流式”的后端特性,Ingress 默认是短连接, 不配置会:

  • 连接中断
  • WebSocket 握手失败
  • SSE 无法持续下发数据
  • 超过60秒就 504 Gateway Timeout

7.3.2为什么必须在 Ingress 层面配置?

即使应用本身支持长连接,Ingress 也必须配置长连接支持,原因是:Ingress 作为反向代理,默认会断开长连接。 如果不配置,连接会在 Ingress 层被中断,永远到不了后端应用。

客户端的请求经过多个环节才能到达应用,请求链路中的各个环节,每一层都需要支持长连接,否则会在该层断开!

1
2
3
4
5
6
7
[客户端浏览器] 
↓ (WebSocket/长连接请求)
[Ingress Controller - Nginx] ← 需要配置!
↓ (转发到后端)
[Service]
↓ (负载均衡)
[Pod - 你的应用] ← 已支持长连接

7.3.3 生产级:SSE + WebSocket + 长连接 Ingress 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: realtime-ingress #定义ingress资源的名字,可以根据自己的情况修改
annotations:
# ---------- 基础 & 长连接支持 ----------
# HTTP 1.1 才能支持 WebSocket / SSE,新版本 Ingress(1.3+)通常自动升级为 1.1,建议保留该参数
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"

# 长连接必须放大超时(默认 60s 会断)
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"

# 支持流式响应(SSE 必配),默认 Nginx 会把后端的响应先缓存到 buffer 里再一起发给客户端。
# SSE(Server-Sent Events):必须加
# WebSocket:不需要加,因为 WebSocket 不是走 HTTP body,不涉及 buffering。
# 大文件下载:可以加(改善内存压力)
nginx.ingress.kubernetes.io/proxy-buffering: "off" #off表示关闭缓冲

# ---------- WebSocket 自动升级支持 ----------
# 新版 Ingress 已自动处理,但显式指定更保险,通知 Ingress Controller 为名为 realtime-service 的后端服务启用 WebSocket 代理所需的特定配置
nginx.ingress.kubernetes.io/websocket-services: "realtime-service"

#手动设置 Upgrade 头
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# ---------- 真实 IP ----------
nginx.ingress.kubernetes.io/use-forwarded-headers: "true"

# ---------- 大文件流式下载优化(可选) ----------
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"

spec:
ingressClassName: nginx
rules:
- host: realtime.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: realtime-service
port:
number: 8080
tls:
- hosts:
- realtime.example.com
secretName: realtime-tls

详细参数解释

1. nginx.ingress.kubernetes.io/proxy-http-version: "1.1"

  • 含义: 设置 Nginx 与后端服务(Upstream)通信时使用的 HTTP 协议版本。
  • 作用: 强制 Nginx 使用 HTTP/1.1 协议。这对于需要 keep-alive(持久连接)和 WebSockets 的后端服务至关重要。
  • 字段/值:
    • 字段 (Key): nginx.ingress.kubernetes.io/proxy-http-version固定的 注解名。
    • 值 (Value): "1.1" 是自定义值,但通常选项有限(例如 "1.0""1.1")。

2. nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"

3. nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"

  • 含义:
    • proxy-read-timeout: Nginx 等待后端服务响应数据的最长时间(秒)。
    • proxy-send-timeout: Nginx 向后端服务发送请求数据的最长时间(秒)。
  • 作用: 将默认的超时时间(通常是 60 秒)延长到 3600 秒(1小时)。这对于处理长时间运行的请求(如大文件下载、复杂的报表生成)或长时间保持连接(如 WebSocket)非常有用,可以防止连接因超时而过早断开。
  • 字段/值:
    • 字段 (Key): .../proxy-read-timeout.../proxy-send-timeout固定的 注解名。
    • 值 (Value): "3600"自定义的,您可以根据需要设置任何表示秒数的字符串。

4. nginx.ingress.kubernetes.io/proxy-buffering: "off"

  • 含义: 控制是否启用 Nginx 的响应缓冲
  • 作用: 设置为 "off" 表示关闭缓冲。默认情况下 Nginx ("on") 会先将后端服务的完整响应缓冲起来,然后再发送给客户端。关闭缓冲后,Nginx 会立即将从后端收到的数据块转发给客户端。这对于流式传输(Streaming)或服务器推送事件 (SSE) 至关重要,否则客户端可能要等很久才能收到第一个数据包。
  • 字段/值:
    • 字段 (Key): .../proxy-buffering固定的 注解名。
    • 值 (Value): "off" 是自定义值,选项通常是 "on""off"

5. nginx.ingress.kubernetes.io/websocket-services: "realtime-service"

  • 含义:这是一个较旧或特定版本的注解)显式声明哪个 Kubernetes Service 用于 WebSocket 连接。
  • 作用: 通知 Ingress Controller 为名为 realtime-service 的后端服务启用 WebSocket 代理所需的特定配置。在现代的 Nginx Ingress 中,这通常是自动检测的,如果您的 Ingress 规则中有多个后端服务,这个注解可以用来指明哪一个是 WebSocket 服务。或者通过 configuration-snippet(如下文8所示)手动配置。
  • 字段/值:
    • 字段 (Key): .../websocket-services固定的 注解名。
    • 值 (Value): "realtime-service"自定义的,它必须是您在同一 Ingress 中定义的 Kubernetes Service 的名称。

6. nginx.ingress.kubernetes.io/use-forwarded-headers: "true"

  • 含义: 控制 Nginx 是否应信任和处理来自客户端的 X-Forwarded-* 头部(如 X-Forwarded-For, X-Forwarded-Proto)。
  • 作用: 当 Nginx Ingress 运行在另一个负载均衡器(如云服务商的 LB)后面时,设置为 "true" 非常重要。这使 Nginx 能够正确识别客户端的真实 IP 地址原始协议(HTTP/HTTPS),并将这些信息传递给后端应用,便于日志记录和安全策略。
  • 字段/值:
    • 字段 (Key): .../use-forwarded-headers固定的 注解名。
    • 值 (Value): "true" 是自定义值,选项是 "true""false"

7. nginx.ingress.kubernetes.io/proxy-request-buffering: "off"

  • 含义: 控制是否启用 Nginx 的请求缓冲
  • 作用: 设置为 "off" 表示关闭对客户端请求体(Request Body)的缓冲。默认 ("on") 情况下,Nginx 会先接收完客户端的全部请求(例如一个大文件上传),然后再将其转发给后端。关闭后,Nginx 会在收到数据时立即将其流式传输到后端。这可以减少延迟,并防止 Nginx 因缓冲大型上传而消耗过多内存或磁盘。
  • 字段/值:
    • 字段 (Key): .../proxy-request-buffering固定的 注解名。
    • 值 (Value): "off" 是自定义值,选项是 "on""off"

8. nginx.ingress.kubernetes.io/configuration-snippet: | ...

  • 含义: 配置片段。允许您将原始的 Nginx 配置代码直接注入到该 Ingress 规则对应的 location 配置块中。
  • 作用: 这是一个“后门”,用于实现标准注解无法提供的复杂或特定 Nginx 功能。在这里,它被用来设置 WebSocket 代理所需的 HTTP 头部:
    • proxy_set_header Upgrade $http_upgrade;
    • proxy_set_header Connection 'upgrade';
    • 这两行是实现 WebSocket 代理的关键。它们告诉后端服务:“客户端请求将连接从 HTTP 升级(Upgrade)到 WebSocket”。
  • 字段/值:
    • 字段 (Key): .../configuration-snippet固定的 注解名。
    • 值 (Value): | ... 后面跟随的 Nginx 配置块是完全自定义的

7.4 域名重定向Redirect

在使用Nginx作为代理服务器时,Redirect可用于域名的重定向,比如访问old.com被重定 向到new.com。Ingress也可以实现Redirect功能,接下来用nginx.redirect.com作为旧域名, baidu.com作为新域名进行演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
vim redirect-ingress.yaml
# 配置如下
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: redirect-ingress
namespace: ingress
annotations:
#它指示 Ingress:“请将所有匹配这个规则的请求重定向到这个 URL:https://www.baidu.com,,不要将流量转发给后端的Service
nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com
#默认重定向的状态码是301,该注销覆盖了301,改为302状态码
nginx.ingress.kubernetes.io/permanent-redirect-code: "302"
spec:
ingressClassName: nginx
tls:
- hosts:
- nginx.jiugen.com
secretName: vue-tls
rules:
- host: nginx.jiugen.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vue
port:
number: 80

创建上述ingress,通过浏览器访问nginx.jiugen.com域名时,浏览器会自动跳转到https://www.baidu.com

7.5访问地址重写Rewrite

重定向(Redirect)让浏览器跳到新的 URL(地址栏会变化),服务器给浏览器返回一个 301/302/308 跳转,让浏览器自己访问新的 URL:

  • 老域名跳转到因域名,如:old.dpdns.org 跳转到 new.dpdns.org
  • 强制HTTPS,如:old.dpdns.org 跳转到https://old.dpdns.org

重写(Rewrite)只在服务器内部换路径(浏览器地址栏不变),Ingress / Nginx 在服务器内部把请求路径换一下,但浏览器地址栏不变:

  • 前端反向代理,把路径缩短或调整,如:用户访问:https://api.example.com/user/get ;后端实际服务路径是:/v1/users/get。就需要使用 Rewrite

创建一个应用来模拟后端服务:

1
2
[root@k8s-master01 ~]# kubectl create deployment backend-api --image=registry.cn-beijing.aliyuncs.com/dotbalo/nginx:backend-api -n ingress
deployment.apps/backend-api created

创建Service暴露该服务:

1
2
[root@k8s-master01 ~]# kubectl expose deployment -n ingress backend-api --port=80
service/backend-api exposed

查看service并通过ip测试,是否能请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@k8s-master01 ~]# kubectl get svc -n ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-api ClusterIP 10.96.147.56 <none> 80/TCP 17s
vue ClusterIP 10.96.22.163 <none> 80/TCP 3d20h

#使用curl请求svc的ip地址,可以正常请求。
[root@k8s-master01 ~]# curl 10.96.147.56
<h1> backend for ingress rewrite </h1>

<h2> Path: /api-a </h2>


<a href="http://gaoxin.kubeasy.com"> Kubeasy </a>

# 请求地址带/api-a返回404,接下来利用ingress将地址重写,可以正常请求
[root@k8s-master01 ~]# curl 172.16.58.239/api-a
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.15.12</center>
</body>
</html>

创建ingress:

理想的状态是访问rewrite.jiugen.com/api-a能访问到服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: rewrite-ingress
namespace: ingress
spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /api-a
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

创建上述ingress资源,然后通过浏览器访问rewrite.jiugen.com/api-a,会发现报404

此时需要配置Ingress Nginx的Rewrite功能,将/api-a重写为“/”,配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: rewrite-ingress
namespace: ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /api-a(/|$)(.*) #匹配 /api-a,后面必须紧跟着“一个斜杠 /” 或者 “行尾 `$”(即路径结束)”。匹配逻辑: 强制要求 /api-a 就是一个完整的路径段,不允许它是其他单词的前缀。
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

重新创建资源: kubectl replace -f rewrite.yaml,再次访问rewrite.jiugen.com/api-a即可访问到后端服务:

注意:需要Rewrite和不需要Rewrite的千万不要写在同一个ingress资源中,一定要分开写,如果在通一个ingress中配置了,会把所有的地址都重写。

7.6访问速率限制

有时候可能需要限制速率以降低后端压力,或者限制单个IP每秒的访问速率防止攻击。此时可以使用Nginx的rate limit进行配置。

首先没有加速率限制,使用ab进行访问,Failed为0:

1
2
3
4
5
6
7
8
9
10
[root@k8s-master01 14-ingress]# ab -c 10 -n 100 http://rewrite.jiugen.com/ |grep requests
Complete requests: 100
Failed requests: 0
Time per request: 1.142 [ms] (mean, across all concurrent requests)
Percentage of the requests served within a certain time (ms)

# 压测结果表示:
# 100 个请求全部成功
# 失败0个
# 平均响应时间非常快(1ms 左右)

在看代码前,必须记住一点:Nginx Ingress 的限流是“基于单个 Pod”的。

公式: 实际总限流阈值 ≈ 配置的阈值 × Ingress Controller Pod 的副本数

  • 举例: 你在 YAML 里配置了 limit-rps: 10 (每秒10个请求)。
  • 现状: 你的集群里部署了 3 个 Ingress Controller Pod 用于负载均衡。
  • 结果: 整个集群对该域名的实际限流能力大约是 30 RPS (10 × 3)。

限制并发连接数 (防止资源耗尽),配置nginx.ingress.kubernetes.io/limit-connections: "1"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: rewrite-ingress
namespace: ingress
annotations:
nginx.ingress.kubernetes.io/limit-connections: "1"

spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

创建ingress资源并再次使用ab测试,Failed为74:

1
2
3
4
5
[root@k8s-master01 14-ingress]# ab -c 10 -n 100 http://rewrite.jiugen.com/ |grep requests
Complete requests: 100
Failed requests: 74
Time per request: 1.360 [ms] (mean, across all concurrent requests)
Percentage of the requests served within a certain time (ms)

其他限制参数如下:

功能 Annotation 示例 说明
按秒限流 nginx.ingress.kubernetes.io/limit-rps: "5" 每个 IP 每秒限制 5 次请求
按分限流 nginx.ingress.kubernetes.io/limit-rpm: "300" 每个 IP 每分钟限制 300 次请求
突发缓冲区 nginx.ingress.kubernetes.io/limit-burst-multiplier: "2" 允许短时间的突发流量超过限制
白名单 nginx.ingress.kubernetes.io/limit-whitelist: "10.0.0.0/8, 192.168.1.0/24" 设置不受限流影响的客户端 IP

如果集群里安装了 Istio,通常建议尽量在 Istio 层做限流,而不是 Nginx Ingress 层

原因如下:

  1. 全局精准:Istio 可以配置全局限流(使用 Redis 配额),不受 “Pod 副本数” 的影响。
  2. 更细粒度:Istio 可以针对 HTTP Header(如 JWT 中的 User ID)限流,而 Nginx Ingress 社区版主要基于 IP。
  3. 统一治理:将流量规则统一收敛在 Service Mesh 层。

7.7局部黑名单-单个ingress生效

使用nginx.ingress.kubernetes.io/denylist-source-range添加黑名单,支持IP、网段,多个黑 名单逗号分隔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: denylist-ingress
namespace: ingress
annotations:
nginx.ingress.kubernetes.io/denylist-source-range: "192.168.0.4" #这个ip是我电脑ip
spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

创建ingress资源,然后通过浏览器访问 rewrite.jiugen.com 查看效果:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f deny-ingress.yaml
ingress.networking.k8s.io/denylist-ingress created

可以看到提示403拒绝访问:

其他客户端可以访问:

1
2
3
4
5
6
7
[root@k8s-master01 14-ingress]# curl rewrite.jiugen.com
<h1> backend for ingress rewrite </h1>

<h2> Path: /api-a </h2>


<a href="http://gaoxin.kubeasy.com"> Kubeasy </a>

7.8 局部白名单-单个ingress生效

白名单就是限制只有指定 IP 才能访问这个 Ingress,其余所有 IP 都被拒绝,在名单中才能进,没在名单里一律不让进。直接在yaml文件中配置即可,比如只允许 192.168.181.141访问,只需要添加一个nginx.ingress.kubernetes.io/whitelist-source-range 注释 即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: whitelist-ingress
namespace: ingress
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: 192.168.0.4 #表示只允许这个ip地址访问

spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

创建上述ingress,并通过不同ip的客户端进行访问,观察效果:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f whitelist-ingress.yaml
ingress.networking.k8s.io/whitelist-ingress created

192.168.0.4客户端访问测试,看到正常访问:

192.168.0.4以外的客户端访问,这里用k8s-master01节点测试,提示403权限拒绝:

1
2
3
4
5
6
7
8
[root@k8s-master01 14-ingress]# curl \rewrite.jiugen.com
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
类型 含义 优点 缺点
白名单 允许指定 IP,其余全部禁止 安全最高 需要维护
黑名单 坏 IP 禁止 简单 安全性弱,只能挡特定 IP

生产中:

后台系统 + 运维面板 = 必须上白名单
黑名单只用来补充特定攻击 IP

7.9全局黑名单-所有ingress生效

在 Kubernetes 的 ingress-nginx-controller 中,配置全局(针对所有 Ingress 资源生效)的黑白名单,不是在单个 Ingress YAML 中通过注解(Annotations)配置,而是需要修改 Nginx Controller 自身的 ConfigMap

配置全局黑名单 (Global Whitelist):

场景: 想要屏蔽某些恶意攻击 IP 或特定网段,允许其他所有人访问。

操作步骤: 修改 ingress-nginx-controller 引用的 ConfigMap(通常名为 ingress-nginx-controller)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@k8s-master01 14-ingress]# kubectl edit  configmaps -n ingress-nginx ingress-nginx-controller

#在ingress-nginx-controller的ConfigMap的data:下添加denylist-source-range: "192.168.0.4"
apiVersion: v1
data:
allow-snippet-annotations: "true"
denylist-source-range: "192.168.0.4" #添加改行参数,表示全局黑名单ip为192.168.0.4
kind: ConfigMap
metadata:
creationTimestamp: "2025-06-03T09:56:52Z"
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-controller
namespace: ingress-nginx
resourceVersion: "18591166"
uid: a1aaf655-f3f2-4ad2-b101-cfb9337080d2

“192.168.0.4”是我电脑的ip,创建ingress资源,通过电脑浏览器访问域名查看是否可以访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ingress配置如下,该ingress没有单独配置任何黑白名单:
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: global-denylist-ingress
namespace: ingress
spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

创建上述ingress资源,然后通过客户端访问测试:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f global-denylist.yaml
ingress.networking.k8s.io/global-denylist-ingress created

“192.168.0.4”客户端访问测试,浏览器报403权限拒绝:


使用其他客户端测试,正常请求:

1
2
3
4
5
6
7
[root@k8s-master01 14-ingress]# curl rewrite.jiugen.com
<h1> backend for ingress rewrite </h1>

<h2> Path: /api-a </h2>


<a href="http://gaoxin.kubeasy.com"> Kubeasy </a>

7.10全局白名单-所有ingress生效

相同的道理全局白名单也是限制只有指定 IP 才能访问 Ingress,其余所有 IP 都被拒绝,在名单中才能进,没在名单里一律不让进。

场景: 整个集群的 Ingress 入口只允许特定的 IP 段访问,拒绝其他所有访问(常用于内部测试环境)。

操作步骤: 修改 ingress-nginx-controller 引用的 ConfigMap(通常名为 ingress-nginx-controller)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@k8s-master01 14-ingress]# kubectl edit  configmaps -n ingress-nginx ingress-nginx-controller
#在ingress-nginx-controller的ConfigMap的data:下添加whitelist-source-range: 192.168.0.4
apiVersion: v1
data:
allow-snippet-annotations: "true"
whitelist-source-range: 192.168.0.4 #表示只有该ip的客户端允许访问,其他的所有ip不允许访问
# denylist-source-range: 192.168.0.4
kind: ConfigMap
metadata:
creationTimestamp: "2025-06-03T09:56:52Z"
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-controller
namespace: ingress-nginx
resourceVersion: "19454191"
uid: a1aaf655-f3f2-4ad2-b101-cfb9337080d2

“192.168.0.4”是我电脑的ip,创建ingress资源,通过电脑浏览器访问域名查看是否可以访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ingress配置如下,该ingress没有单独配置任何黑白名单:
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: global-whitelist-ingress
namespace: ingress
spec:
ingressClassName: nginx
rules:
- host: rewrite.jiugen.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80

创建上述ingress资源,然后通过客户端访问测试:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f global-whitelist.yaml
ingress.networking.k8s.io/global-whitelist-ingress created

“192.168.0.4”客户端访问测试,正常访问才对:

使用其他客户端测试,应该报403权限拒绝才对:

1
2
3
4
5
6
7
8
[root@k8s-master01 14-ingress]# curl rewrite.jiugen.com
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

7.11自定义友好错误页提示

官方文档参考https://kubernetes.github.io/ingress-nginx/examples/customization/custom-errors/

在 Kubernetes 的 Ingress Nginx Controller (v1.12.2) 中配置自定义错误页面,核心逻辑是拦截转发

  1. 拦截:告诉 Nginx Controller 哪些错误码(如 404, 500,501,502, 503,504)不要直接返回给用户,而是拦截下来。
  2. 转发:将拦截到的请求转发给你自己部署的一个“默认后端服务(Default Backend)”,这个服务负责展示漂亮的 HTML 页面。

以下是完整的配置步骤,分为 准备资源部署自定义后端修改 Controller 配置 三大步。

第一步:准备 HTML 页面和 Nginx 配置

创建一个 ConfigMap,用于将错误页挂载到Default Backend服务中:

  1. 准备想要展示的 404.html50x.html
  2. 这里把404.html、50x.html和nginx配置都在ConfigMap中定义。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# ConfigMap配置如下:
cat error-pages-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: error-pages-cm
namespace: ingress-nginx
data:
404.html: |
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>页面不存在</title></head>
<body style="text-align:center;padding-top:60px;">
<h1>😕 页面走丢了</h1>
<p>您访问的页面不存在,请检查访问路径。</p>
</body></html>

50x.html: |
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>服务异常</title></head>
<body style="text-align:center;padding-top:60px;">
<h1>😥 服务暂时不可用</h1>
<p>服务器开小差了,请稍后再试~</p>
</body></html>

# 【新增】关键配置:不管请求什么路径,都根据 Header 返回对应 HTML
default.conf: |
server {
listen 80;
root /usr/share/nginx/html;

location / {
# 默认视为 404
set $code "404";

# 读取 Ingress 传来的错误码 Header
if ($http_x_code != "") {
set $code $http_x_code;
}

# 如果是 5xx 错误,返回 50x.html
if ($code ~ ^5) {
rewrite ^ /50x.html break;
}

# 其他情况返回 404.html
rewrite ^ /404.html break;
}

# 健康检查
location /healthz { return 200 'OK'; }
}

#创建configmap ,configmap创建在ingress-nginx命名空间中
[root@k8s-master01 14-ingress]# kubectl create -f error-pages-cm.yaml
configmap/error-pages-cm created


#查看创建的configmap
[root@k8s-master01 14-ingress]# kubectl get cm -n ingress-nginx
NAME DATA AGE
error-pages-cm 3 9s

第二步:部署“默认后端服务” (Default Backend)

创建文件 default-backend.yaml部署“默认后端服务” (Default Backend)

部署一个轻量级的 Nginx Pod 来承载上面的 HTML,并暴露为 Service。

创建文件 default-backend.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
apiVersion: v1
kind: Service
metadata:
name: custom-default-backend
namespace: ingress-nginx
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: custom-default-backend

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-default-backend
namespace: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app: custom-default-backend
template:
metadata:
labels:
app: custom-default-backend
spec:
containers:
- name: nginx-error-page
image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx:1.27-alpine
ports:
- containerPort: 80
volumeMounts:
- name: html-volume
mountPath: /usr/share/nginx/html/404.html
subPath: 404.html
- name: html-volume
mountPath: /usr/share/nginx/html/50x.html
subPath: 50x.html
# 【新增】挂载配置文件覆盖nginx的默认配置文件
- name: html-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
volumes:
- name: html-volume
configMap:
name: error-pages-cm

配置解释:

  • Deployment: 运行一个标准 Nginx。
  • VolumeMounts: 将第一步 ConfigMap 中的 HTML 挂载到容器内部,覆盖nginx的默认配置。
  • Service: 暴露端口,让 Ingress Controller 能访问到它。

创建应用:

1
2
3
[root@k8s-master01 14-ingress]# kubectl create -f backend-error-pages.yaml
service/custom-default-backend created
deployment.apps/custom-default-backend created

第三步:配置 Ingress Controller (关键步骤)

现在后端准备好了,需要告诉 Ingress Controller 两件事:

  1. 全局错误拦截: 开启 custom-http-errors,指定需要拦截的错误码。
  2. 指定默认后端: 告诉 Controller,拦截指定的错误码后,把流量转给 custom-default-backend 这个 Service。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1. 修改 ConfigMap 开启拦截,配置需要拦截的错误码,找到你的 Ingress Controller 的全局 ConfigMap(通常叫ingress-nginx-controller),添加或修改 data 下的 custom-http-errors

[root@k8s-master01 14-ingress]# kubectl edit cm -n ingress-nginx ingress-nginx-controller
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
allow-snippet-annotations: "true"
# 在此指定需要拦截的错误码,用逗号分隔
custom-http-errors: 404,500,501,502,503,504
kind: ConfigMap
metadata:
creationTimestamp: "2025-06-03T09:56:52Z"
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-controller
namespace: ingress-nginx
resourceVersion: "19676465"
uid: a1aaf655-f3f2-4ad2-b101-cfb9337080d2


2. 修改 Controller 的启动参数指向 `custom-default-backend` 这个 Service。需要修改 Ingress Controller 的 daemonsets (通常叫 ingress-nginx-controller)。
[root@k8s-master01 14-ingress]# kubectl edit daemonsets.apps -n ingress-nginx ingress-nginx-controller
# 找到daemonsets部分,在- args:添加参数 - --default-backend-service=ingress-nginx/custom-default-backend,格式为 <namespace>/<service-name>,ingress-nginx是命名空间,custom-default-backend是第二步中Default Backend后端服务所对应的service名字。
38 spec:
39 containers:
40 - args:
41 - /nginx-ingress-controller
42 - --election-id=ingress-nginx-leader
43 - --controller-class=k8s.io/ingress-nginx
44 - --ingress-class=nginx
45 - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
46 - --validating-webhook=:8443
47 - --validating-webhook-certificate=/usr/local/certificates/cert
48 - --validating-webhook-key=/usr/local/certificates/key
49 - --default-backend-service=ingress-nginx/custom-default-backend
50 env:
51 - name: POD_NAME
52 valueFrom:
53 fieldRef:
54 apiVersion: v1
55 fieldPath: metadata.name
56 - name: POD_NAMESPACE

# 注意: 保存退出后,Ingress Controller 的 Pod 会重启。

第四步:验证:

验证 404: 访问任意ingress的一个域名下不存在的路径,例如 http://rewrite.jiugen.com/random-path-123

  • 预期: 应该看到你定义的 “页面走丢了 (404)”,如下图。

验证 503/502: 可以将一个正常的 Ingress 对应的后端 Pod 副本数缩容为 0,或者故意改错 Service 的 selector。 访问该域名 http://rewrite.jiugen.com

  • 预期: 应该看到你定义的“系统繁忙 (5xx)”,如下图。

7.12根据请求头匹配不同客户端

目标:通过配置 Ingress-nginx 实现基于客户端类型(PC/移动端)的智能路由,也就是

PC 用户访问 www.demo.com -> 留在 www.demo.com,访问 PC 版服务。

手机用户访问 www.demo.com -> 被重定向到 m.demo.com,访问 手机版服务。

第一步:准备pc端和手机端的服务

我准备了两个测试页面,用来充当手机端和pc端的展示页,用来测试和查看效果,手机端页面如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
cat mobile.html
<!-- Mobile Page -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>手机端页面</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f5f5f5;
padding: 16px;
}
.card {
background: #fff;
padding: 20px;
border-radius: 16px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.title {
font-size: 20px;
margin-bottom: 10px;
font-weight: bold;
}
.btn {
display: block;
text-align: center;
margin-top: 20px;
padding: 12px;
background: #007bff;
color: #fff;
border-radius: 12px;
text-decoration: none;
}
</style>
</head>
<body>
<div class="card">
<div class="title">欢迎来到手机端页面</div>
<p>这是为手机用户设计的简洁界面。</p>
<a class="btn" href="#">立即体验</a>
</div>
</body>
</html>

pc端页面如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cat pc.html
<!-- PC Page -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>PC端页面</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background: #eef1f5;
}
header {
background: #2b2f77;
color: white;
padding: 20px 40px;
font-size: 24px;
}
.content {
padding: 40px;
}
.box {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<header>PC端页面示例</header>
<div class="content">
<div class="box">
<h2>欢迎来到 PC 页面</h2>
<p>这里是为桌面用户设计的内容展示区,适合更大屏幕。</p>
</div>
</div>
</body>
</html>

创建一个ConfigMap,挂载到nginx容器中来替换nginx的默认页

1
2
[root@k8s-master01 pc-m]# kubectl create cm pc-mobile-cm --from-file=pc.html --from-file=mobile.html
configmap/pc-mobile-cm created

接下来部署两个不同的后端服务,用来代表“PC端应用”和“手机端应用”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[root@k8s-master01 pc-m]# cat pc-deploy.yaml
# pc端服务的配置如下:
kind: Deployment
apiVersion: apps/v1
metadata:
name: pc-deployment
labels:
app: pc
spec:
replicas: 1
selector:
matchLabels:
app: pc
template:
metadata:
labels:
app: pc
spec:
containers:
- name: pc
image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx:1.27-alpine
ports:
- containerPort: 80
volumeMounts:
- name: pc-volumes
mountPath: /usr/share/nginx/html/index.html
subPath: pc.html
volumes:
- name: pc-volumes
configMap:
name: pc-mobile-cm
---
kind: Service
apiVersion: v1
metadata:
name: pc-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: pc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[root@k8s-master01 pc-m]# cat mobile-deploy.yaml
# 移动端服务的配置如下:
kind: Deployment
apiVersion: apps/v1
metadata:
name: mobile-deployment
labels:
app: mobile
spec:
replicas: 1
selector:
matchLabels:
app: mobile
template:
metadata:
labels:
app: mobile
spec:
containers:
- name: pc
image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx:1.27-alpine
ports:
- containerPort: 80
volumeMounts:
- name: mobile-volumes
mountPath: /usr/share/nginx/html/index.html
subPath: mobile.html
volumes:
- name: mobile-volumes
configMap:
name: pc-mobile-cm
---
kind: Service
apiVersion: v1
metadata:
name: mobile-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: mobile

创建应用:

1
2
3
4
5
[root@k8s-master01 pc-m]# kubectl create -f mobile-deploy.yaml -f pc-deploy.yaml
deployment.apps/mobile-deployment created
service/mobile-service created
deployment.apps/pc-deployment created
service/pc-service created

第二步:配置 Ingress (核心步骤)

需要配置两个 Ingress 规则:

  1. Mobile Ingress: 专门负责接收 m.demo.com 的流量。
  2. Main (PC) Ingress: 负责接收 www.demo.com 的流量,并且包含重定向逻辑

原因: 重定向逻辑必须写在入口域名(即 PC 域名)的 Ingress 上,因为用户首先访问的是 PC 域名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 1. 手机端域名的 Ingress (这是重定向的目标)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mobile-ingress
spec:
ingressClassName: nginx
rules:
- host: m.demo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mobile-service
port:
number: 80
---
# 2. PC端域名的 Ingress (这里包含识别和重定向逻辑)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
nginx.ingress.kubernetes.io/service-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Mobile|Android|iPhone|iPad|Windows Phone|UC|Kindle)") {
set $agentflag 1;
}
if ($agentflag = 1) {
rewrite 301 http://m.demo.com;
}
spec:
ingressClassName: nginx
rules:
- host: www.demo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: pc-service
port:
number: 80

创建ingress资源:

1
2
3
[root@k8s-master01 pc-m]# kubectl create -f ingress.yaml
ingress.networking.k8s.io/mobile-ingress created
Error from server (BadRequest): error when creating "ingress.yaml": admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: annotation group ServerSnippet contains risky annotation basedon ingress configuration

报错原因:

1
# 在1.9.0以后的版本 默认关闭了server snippet, Ingress-NGINX 控制器中	为了安全原因,注解现在按风险级别(低、中、高、关键)进行分类,除了现有的 allow-snippet-annotations 切换。nginx.ingress.kubernetes.io/server-snippet 注解属于 ServerSnippet 组,并被分类为关键风险。在ingress-nginx控制器的 ConfigMap(名字通常是ingress-nginx-controller) 中设置 allow-snippet-annotations: "true" 可以一般性地启用片段注解,但入站 webhook 仍会根据配置的风险阈值进行验证。默认情况下,annotations-risk-level 设置为 "High"(或在某些设置中等效),这会阻止关键风险注解,如 server-snippet,因此您看到的 webhook 拒绝错误。

解决方法:

1
# 在ingress-nginx控制器的 ConfigMap(名字通常是ingress-nginx-controller) 中添加 annotations-risk-level: "Critical"(data的配置块下)以允许高达关键风险的注解。删除pod重建使修改生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@k8s-master01 pc-m]#   kubectl edit cm -n ingress-nginx ingress-nginx-controller
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
allow-snippet-annotations: "true" #添加该参数
annotations-risk-level: Critical #添加该参数
custom-http-errors: 404,500,501,502,503,504
kind: ConfigMap
metadata:
creationTimestamp: "2025-06-03T09:56:52Z"
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-controller
namespace: ingress-nginx
resourceVersion: "20077380"
uid: a1aaf655-f3f2-4ad2-b101-cfb9337080d2

继续创建ingress:

1
2
3
[root@k8s-master01 pc-m]# kubectl create -f ingress.yaml
ingress.networking.k8s.io/mobile-ingress created
ingress.networking.k8s.io/main-ingress created

通过浏览器访问www.demo.com测试,浏览器显示的是pc的页面:

通过浏览器的f12来模拟手机访问,查看会不会跳转到手机端页面:

点击《尺寸》选择任意一款手机后刷新浏览器

此时页面已经显示手机端的页面了,测试没问题。

总结:实际上就是手机端有一个访问页面,对外提供的访问域名是m.demo.com,pc端一个访问页面,对外提供的访问域名是www.demo.com,当用户访问www.demo.com,如果客户端是手机,则跳转到m.demo.com,如果是pc端则访问www.demo.com。配置实现则是通过ingress的 nginx.ingress.kubernetes.io/server-snippet注解,具体配置参考laptop-ingress.yaml中annotations配置,下面部分:

1
2
3
4
5
6
7
8
9
annotations:
nginx.ingress.kubernetes.io/service-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Mobile|Android|iPhone|iPad|Windows Phone|UC|Kindle)") {
set $agentflag 1;
}
if ($agentflag = 1) {
rewrite 301 http://m.demo.com;
}

7.13Ingress实现灰度发布或蓝绿部署

什么是灰度发布:

灰度发布(也称为金丝雀发布)是一种软件发布策略,在 Kubernetes 集群中逐步将流量从旧版本服务迁移到新版本服务的过程,这种发布策略可以有效降低升级风险,及时发现潜在问题。旨在以最小化风险的方式逐步将新版本的应用或服务推向生产环境。

蓝绿部署是同时维护两套完全独立、功能等价的环境(称为“蓝环境”和“绿环境”),其中只有一套对外提供服务,新版本部署到非活跃环境(比如当前蓝环境在服务,就把新版本部署到绿环境),验证无误后,通过路由切换(如Service/Ingress切换)瞬间切流到新环境,切换成功后,原环境可保留作回滚之用,或下线。

7.13.1 准备灰度发布环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 创建v1版本:首先创建模拟 Production(生产)环境的 Namespace 和服务:


#创建一个生产环境的命名空间
[root@k8s-master01 ~]# kubectl create ns production
namespace/production created

#创建deployment服务,用于运行服务程序
[root@k8s-master01 ~]# kubectl create deploy canary-v1 --image=registry.cn-beijing.aliyuncs.com/dotbalo/canary:v1 -n production
deployment.apps/canary-v1 created

#为名字是canary-v1 的deployment创建service,用来暴露服务
[root@k8s-master01 ~]# kubectl expose deploy canary-v1 --port 8080 -n production
service/canary-v1 exposed

#创建ingress用来对外发布canary-v1服务
vim canary-v1-ingress.yaml
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: canary-v1-ingress
namespace: production

spec:
ingressClassName: nginx
rules:
- host: www.canary.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: canary-v1
port:
number: 8080


#创建该ingress
[root@k8s-master01 ~]# kubectl create -f 14-ingress/canary-v1-ingress.yaml
ingress.networking.k8s.io/canary-v1-ingress created

使用浏览器访问www.canary.com该服务,可以看到 Canary v1 的页面:(添加hosts解析)

7.13.2 准备要上线的新版本

创建v2版本:接下来创建 v2 版本,充当灰度环境(也就是要上线的新版本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#这里为了好区分,创建单独的命名空间,也可以使用production命名空间,这个根据自己情况来
[root@k8s-master01 ~]# kubectl create ns canary
namespace/canary created

#创建 v2 版本的应用服务和 Service:
[root@k8s-master01 ~]# kubectl create deploy canary-v2 --image=registry.cn-beijing.aliyuncs.com/dotbalo/canary:v2 -n canary
deployment.apps/canary-v2 created

#创建v2版本的service
[root@k8s-master01 ~]# kubectl expose deploy canary-v2 --port 8080 -n canary
service/canary-v2 exposed

#等待程序启动完成后,通过 Service的ip 访问该服务,会返回 Canary v2:
[root@k8s-master01 ~]# kubectl get svc -n canary
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
canary-v2 ClusterIP 10.96.20.37 <none> 8080/TCP 43s
[root@k8s-master01 ~]# curl 10.96.20.37:8080
<h1>Canary v2</h1>

接下来通过创建v2版本的ingress来控制流量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 创建 v2 版本的 Ingress 时,需要添加两个注释,一个是 nginx.ingress.kubernetes.io/canary, 表明是灰度环境,nginx.ingress.kubernetes.io/canary-weight 表明切多少流量到该环境,本示例为10%
vim canary-v2-ingress.yaml
# 配置如下:
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: canary-v2-ingress
namespace: canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" #表示10%的流量到v2版本

spec:
ingressClassName: nginx
rules:
- host: www.canary.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: canary-v2 #这里配置的是v2版本的service名字
port:
number: 8080 #这里配置的是v2版本的service暴露的端口

创建v2版本的ingress:

1
2
[root@k8s-master01 14-ingress]#  kubectl create -f canary-v2-ingress.yaml
ingress.networking.k8s.io/canary-v2-ingress created

此时通过 nginx.ingress.kubernetes.io/canary-weight: “10”设置的权重是 10,即 v1:v2 版本的请求比例大约为 9:1

7.13.3测试灰度发布效果

由于浏览器观看效果不明显,这里使用命令观察访问的效果:

接下来使用 Ruby 脚本进行测试,此脚本会分别输出 v1 和 v2 的访问次数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vim test-canary.rb
#脚本内容如下
counts = Hash.new(0)

100.times do
output = `curl -s canary.com | grep 'Canary' | awk '{print $2}' | awk -F"<" '{print $1}'`
counts[output.strip.split.last] += 1
end

puts counts


#给脚本添加执行权限
chmod +x test-canary.rb

如果没有ruby需要安装:

1
yum install ruby -y

执行脚本测试:

1
2
3
4
5
6
7
8
9
#记得在执行脚本的机器上添加hosts解析
[root@k8s-master01 study-ingress]# ruby canary.rb
{"v2"=>9, "v1"=>91}
[root@k8s-master01 study-ingress]# ruby canary.rb
{"v1"=>89, "v2"=>11}
[root@k8s-master01 study-ingress]# ruby canary.rb
{"v1"=>88, "v2"=>12}

#通过上述的结果,可以看到v2和v1的请求比例大概是1:9左右,100次请求,大概10%左右的流量被分配到了v2

713.4清理该笔记中产生的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#删除canary命名空间中的deploy  svc ingress
[root@k8s-master01 study-ingress]# kubectl delete deploy,svc,ingress -n canary --all
deployment.apps "canary-v2" deleted
service "canary-v2" deleted
ingress.networking.k8s.io "canary-v2" deleted
#删除canary命名空间
[root@k8s-master01 study-ingress]# kubectl delete ns canary
namespace "canary" deleted

#删除production命名空间中的deploy svc ingress
[root@k8s-master01 study-ingress]# kubectl delete deployment,svc,ingress -n production --all
deployment.apps "canary-v1" deleted
service "canary-v1" deleted
ingress.networking.k8s.io "canary-v1" deleted
#删除production命名空间
[root@k8s-master01 study-ingress]# kubectl delete ns production
namespace "production" deleted


#删除 study-ingress命名空间中的deploy svc ingress
[root@k8s-master01 study-ingress]# kubectl delete deployment,svc,ingress -n study-ingress --all
deployment.apps "backend-api" deleted
deployment.apps "laptop" deleted
deployment.apps "nginx" deleted
deployment.apps "pay-api" deleted
deployment.apps "phone" deleted
service "backend-api" deleted
service "laptop" deleted
service "nginx" deleted
service "pay-api" deleted
service "phone" deleted
ingress.networking.k8s.io "basic-auth" deleted
ingress.networking.k8s.io "laptop-ingress" deleted
ingress.networking.k8s.io "phone-ingress" deleted
ingress.networking.k8s.io "rewrite-ingress" deleted
ingress.networking.k8s.io "web-ingress" deleted

#删除 study-ingress命名空间
[root@k8s-master01 study-ingress]# kubectl delete ns study-ingress
namespace "study-ingress" deleted

八、Ingress常见报错排查

8.1Not Found(404)报错

404 表示访问的路由不存在,通常问题如下:

  1. Ingress 路径配置的不正确
  2. Ingress 的配置未被Controller 解析
  3. 未使用正确的域名和路径访问
  4. 代理的服务没有该路径

场景分析:有一个项目包含一个前端,两个后端,域名为project.test.com。

其中 路径 / 指向前端;

路径 /userapi 指向 user服务 ;

路径 /paymentapi 指向 payment服务;

部署服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
#部署user服务:
[root@k8s-master01 ~]# kubectl create deploy user --image=registry.cn-beijing.aliyuncs.com/dotbalo/ingress-err:v1
deployment.apps/user created

#部署payment服务:
[root@k8s-master01 ~]#kubectl create deploy payment --image=registry.cn-beijing.aliyuncs.com/dotbalo/ingress-err:v1
deployment.apps/payment created

#为user服务和payment创建service:
[root@k8s-master01 ~]# kubectl expose deployment user --port=8080
service/user exposed
[root@k8s-master01 ~]# kubectl expose deployment payment --port=8080
service/payment exposed

配置Ingress:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# cat backend-api.yaml
# ingress资源配置如下
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: backend-api
namespace: default
spec:
ingressClassName: nginx
rules:
- host: project.test.com
http:
paths:
- path: /user
pathType: ImplementationSpecific
backend:
service:
name: user
port:
number: 8080
- path: /payment
pathType: ImplementationSpecific
backend:
service:
name: payment
port:
number: 8080

创建ingress:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f backend-api.yaml
ingress.networking.k8s.io/backend-api created

测试请求:

1
2
3
4
[root@k8s-master01 14-ingress]# curl  -X POST project.test.com/user
{"msg":"登录成功","用户名":"匿名用户"}
[root@k8s-master01 14-ingress]# curl project.test.com/payment
{"msg":"支付服务","商品名称":"未知商品"}

此时访问是没有任何问题的,但是有可能程序提供的接口是/api或者/user,/payment,但是要求的接口并不是/api 和/user,比如要求的是userapi和paymentapi,此时直接配置会报404。

假设要求的接口路径是/userapi和/paymentapi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: backend-api
namespace: default
spec:
ingressClassName: nginx
rules:
- host: project.test.com
http:
paths:
- path: /userapi
pathType: ImplementationSpecific
backend:
service:
name: user
port:
number: 8080
- path: /paymentapi
pathType: ImplementationSpecific
backend:
service:
name: payment
port:
number: 8080

重新应用Ingress:

1
2
[root@k8s-master01 14-ingress]# kubectl replace  -f backend-api.yaml
ingress.networking.k8s.io/backend-api replaced

更新后访问:

1
2
3
4
5
6
[root@k8s-master01 14-ingress]# curl project.test.com/paymentapi/payment -I 
HTTP/1.1 404 Not Found
Date: Thu, 22 Aug 2024 13:00:41 GMT
Content-Type: */*
Content-Length: 60
Connection: keep-alive

此时会报404,因为虽然Ingress配置了路径,但是后端服务并没有/paymentapi这个路径, 所以会报404,此时需要用rewrite进行地址重写把paymentapi去除,这样/paymentapi/payment 就被代理到了程序的/payment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# ingress做如下修改
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: backend-api
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2 #添加rewrite参数
spec:
ingressClassName: nginx
rules:
- host: project.test.com
http:
paths:
- path: /userapi(/|$)(.*) #修改此处路径
pathType: ImplementationSpecific
backend:
service:
name: user
port:
number: 8080
- path: /paymentapi(/|$)(.*) #修改此处路径
pathType: ImplementationSpecific
backend:
service:
name: payment
port:
number: 8080

更新ingress并访问测试:

1
2
[root@k8s-master01 14-ingress]# kubectl replace  -f backend-api.yaml
ingress.networking.k8s.io/backend-api replaced

再次访问:

1
2
3
4
5
[root@k8s-master01 14-ingress]# curl project.test.com/paymentapi/payment
{"msg":"支付服务","商品名称":"未知商品"}
[root@k8s-master01 14-ingress]# curl project.test.com/userapi/user -X POST
{"msg":"登录成功","用户名":"匿名用户"}

8.2Request Entity Too Large(413)报错

有时候需要上传一些大文件给程序,但是nginx默认允许的最大文件大小只有8M,不足以 满足生产最大上传需求,此时可以通过nginx.ingress.kubernetes.io/proxy-body-size参数进行更 改(也可以在ConfigMap中全局添加):

Ingress配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: backend-api
namespace: default
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "100m" # 允许最大 100MB 上传(根据需求调整,如 500m)
nginx.ingress.kubernetes.io/proxy-read-timeout: "300s" # 读取超时 5 分钟
nginx.ingress.kubernetes.io/proxy-send-timeout: "300s" # 发送超时 5 分钟
nginx.ingress.kubernetes.io/proxy-buffer-size: "64k" # 缓冲区大小
nginx.ingress.kubernetes.io/proxy-buffers: "8 64k" # 缓冲区数量和大小
nginx.ingress.kubernetes.io/client-max-body-size: "100m" # 客户端最大体大小(可选冗余)
spec:
ingressClassName: nginx
rules:
- host: project.test.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: nginx
port:
number: 80

如果需要全局设置,修改ConfigMap,全局配置 NGINX Ingress Controller 的 ConfigMap:

1
2
3
4
5
6
7
8
9
kubectl edit configmap ingress-nginx-controller -n ingress-nginx
# 在 data 下添加:
data:
proxy-body-size: "100m"
proxy-read-timeout: "300"
proxy-send-timeout: "300"
proxy-buffer-size: "64k"
proxy-buffers: "8 64k"
client-max-body-size: "100m"

重启 Controller Pods 以应用:

1
kubectl rollout restart deployment ingress-nginx-controller -n ingress-nginx

8.3Service Unavailable(503)报错

503 一般是代理的服务不可用导致的,通常问题如下:

  1. Ingress代理配置错误,比如Service名字或端口写错
  2. Ingress代理的Service不存在
  3. Ingress代理的Service后端Pod不正常

8.4Gateway Timeout(504)报错

遇到 504 Gateway Time-out 错误,通常意味着 Ingress 控制器在规定的时间内没有收到后端服务(Pod)的完整响应

简单来说:你的应用处理请求的时间太长了,Ingress 等不及就断开了连接。

504 发生的具体原因:

  1. 应用处理过慢(最常见): 后端业务逻辑复杂(如大文件上传、生成复杂报表、数据库慢查询),处理时间超过了 Nginx Ingress 默认的超时时间(通常是 60秒)。
  2. 资源不足导致卡顿: Pod 的 CPU 或内存达到限制(Limits),导致处理速度极慢。
  3. 连接超时设置不合理: 后端应用与 Ingress 之间的 Keep-Alive 连接设置不匹配。
  4. 网络丢包或拥塞: 极端情况下,集群内部网络问题导致数据包无法及时传输(较少见)。

模拟案例:部署后端程序模拟报错

1
2
3
4
5
6
7
8
9
10
11
12
#创建后端程序
[root@k8s-master01 14-ingress]# kubectl create deploy ingress-err --image=registry.cn-beijing.aliyuncs.com/dotbalo/ingress-err:v1
deployment.apps/ingress-err created

#暴露服务
[root@k8s-master01 14-ingress]# kubectl expose deployment ingress-err --port=8080
service/ingress-err exposed


#查看创建的svc
[root@k8s-master01 14-ingress]# kubectl get svc|grep ingress-err
ingress-err ClusterIP 10.96.242.84 <none> 8080/TCP 64s

请求接口,返回时间比较长:

1
2
[root@k8s-master01 14-ingress]# curl 10.96.242.84:8080/longtime
{"msg":"任务处理成功","处理时间":75} #处理时间随机,有时候会很久有时很快

配置Ingress:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vim longtime.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: longtime-ingress
spec:
ingressClassName: nginx
rules:
- host: longtime.example.com
http:
paths:
- path: /longtime
pathType: Prefix
backend:
service:
name: ingress-err
port:
number: 8080

创建ingress:

1
2
[root@k8s-master01 14-ingress]# kubectl create -f longtime.yaml
ingress.networking.k8s.io/longtime-ingress created

添加host解析并测试访问:如果在访问遇到504 Gateway Time-out,解决方案如下:

找到对应的Ingress,需要在ingress中的annotations:下添加超时时间的参数:

1
2
3
4
# 需要添加以下三个核心超时参数:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "80" # 连接后端服务的超时时间。
nginx.ingress.kubernetes.io/proxy-read-timeout: "300" # Nginx 发送请求给后端的超时时间。
nginx.ingress.kubernetes.io/proxy-send-timeout: "300" # 等待后端响应的超时时间(解决 504 的关键参数)。

Ingress配置参考如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: longtime-ingress
annotations:
#--- 解决 504 问题的核心配置 ---
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" # 连接超时:通常设为 60s 即可,除非网络极差
nginx.ingress.kubernetes.io/proxy-read-timeout: "300" # 读取超时:Nginx 等待后端响应的时间 (这是解决 504 最重要的参数),这里设置300s
nginx.ingress.kubernetes.io/proxy-send-timeout: "300" # 发送超时:Nginx 发送数据给后端的超时时间
nginx.ingress.kubernetes.io/proxy-body-size: "100m" # (可选)允许请求的最大 body 大小,默认是 1m, 如果涉及大文件上传,建议同时增大包体限制可以适当调大
spec:
ingressClassName: nginx
rules:
- host: longtime.example.com
http:
paths:
- path: /longtime
pathType: Prefix
backend:
service:
name: ingress-err
port:
number: 8080

重新应用ingress配置:

1
[root@k8s-master01 14-ingress]# kubectl apply -f longtime.yaml

再次访问测试,如果程序相应时间在ingress配置的时间之内,就可以决绝504报错。

8.5CORS跨域报错

如果浏览器有如下报错,说明被跨域给拦截了,可以添加跨域配置。 官方文档:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors

8.5.1 生产场景还原

假设架构如下:

  1. 前端应用 (SPA/Web): 部署在 https://dashboard.example.com
  2. 后端服务 (API): 部署在 Kubernetes 集群内,Service 名为 user-service
  3. Ingress: 配置了域名 https://api.example.com 指向 user-service

故障复现: 当用户访问 dashboard.example.com 时,前端 JavaScript 发起一个 POST 请求到 https://api.example.com/login

浏览器控制台报错:

1
Access to XMLHttpRequest at 'https://api.example.com/login' from origin 'https://dashboard.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

后台日志: 查看 user-service 的日志,可能发现根本没有收到请求,或者只收到了一个 OPTIONS 请求。

8.5.2原因分析

为什么会这样?

  1. 同源策略: 浏览器默认只允许请求同域名、同端口、同协议的资源。dashboard.example.comapi.example.com 属于跨域
  2. Preflight (预检请求): 对于非简单请求(如带有 Authorization Header,或 Content-Type: application/json 的 POST 请求),浏览器会先自动发送一个 OPTIONS 方法的请求,询问服务器:“你允许 dashboard.example.com 访问吗?”
  3. 拦截点: 流量到达 Ingress Controller。如果 Ingress 没有配置 CORS 响应头,它(或后端应用)返回的响应中不包含 Access-Control-Allow-Origin
  4. 浏览器阻断: 浏览器看到响应头里没有通行证,直接丢弃真正的请求,并抛出红色错误。

8.5.3解决方法

解决这个问题的思路是:在响应头中加入允许跨域的字段。通常可以在ingress中处理,也可以在程序代码中处理推荐在在网关层统一处理。

在 Ingress 层配置 (推荐,运维侧解决):这是最干净的解法,将跨域逻辑与业务代码解耦。以最常用的 Nginx Ingress Controller 为例。

操作步骤: 修改Ingress YAML 文件,添加 annotations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: prod
annotations:
# 1. 开启 CORS
nginx.ingress.kubernetes.io/enable-cors: "true"

# 2. 允许跨域的域名 (生产环境建议指定具体域名,不要用 *)
nginx.ingress.kubernetes.io/cors-allow-origin: "https://dashboard.example.com"

# 3. 允许跨域的请求方法
nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS, DELETE, PATCH"

# 4. 允许携带的请求头 (包含前端发送的所有自定义 Header)
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,X-app123-XPTO"
# 5. 是否允许携带 Cookie/凭证 (关键!如果前端 axios 配置了 withCredentials: true,这里必须为 true)
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
# 6. 预检请求的缓存时间 (秒),减少 OPTIONS 请求频率
nginx.ingress.kubernetes.io/cors-max-age: "600"

spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080

注意:访问https://dashboard.example.com浏览器报跨域,应该在https://api.example.com的ingress上配置允许跨域的参数

为什么是在 api.example.com 上配置?

可以把这个过程比喻为 “访客进入小区”

  • 访客 (Origin): https://dashboard.example.com (前端)
  • 目的地 (Target): https://api.example.com (后端)
  • 保安 (Ingress): api.example.com 的 Ingress 网关

前端 试图访问 后端 时,浏览器会先问 后端的保安(API Ingress):

“我是从 dashboard.example.com 来的,我能进去吗?”

如果 API 的 Ingress 也就是“保安”手里没有“白名单”(即没有配置 CORS),它就会拒绝或者默认不回答,浏览器就会拦截请求。因此,必须修改 api.example.com 对应的 Ingress 资源