k8s服务发布-Ingress

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 就扮演了至关重要的角色。
核心概念:
- Ingress:
- 它是什么? Ingress 是 Kubernetes 中的一个 API 对象,它定义了外部 HTTP/HTTPS 流量如何到达集群内部服务的规则集合。可以把它想象成一份“路由规则说明书”或者“交通规则手册”。
- 它做什么? Ingress 资源本身并不做任何实际的流量转发工作。它只是一个声明性的配置,描述了你希望如何根据域名(主机名)、URL 路径等条件将外部请求路由到不同的后端服务。
- 关键特性:
- 主机名路由 (Host-based routing): 例如,
foo.example.com的请求转发到foo-service,bar.example.com的请求转发到bar-service。 - 路径路由 (Path-based routing): 例如,
example.com/app1的请求转发到app1-service,example.com/app2的请求转发到app2-service。 - TLS/SSL 终止 (TLS/SSL termination): 可以在 Ingress 层面配置 HTTPS,对外部流量进行加密,而内部服务可能只处理 HTTP 流量。
- 负载均衡配置: 定义了第七层(应用层)的负载均衡规则。
- 主机名路由 (Host-based routing): 例如,
- 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 服务器架构进行类比:
Ingress Controller 类似于传统的反向代理服务器 / 负载均衡器 (例如 Nginx, HAProxy, Apache httpd + mod_proxy):
- 在传统的架构中,您通常会在您的应用程序服务器(如 Tomcat, Node.js 应用)前面部署一个反向代理服务器(如 Nginx)。这个 Nginx 服务器接收所有外部的 HTTP/HTTPS 请求,然后根据配置(例如域名、URL路径)将请求转发到后端的某一个应用服务器实例。它还可以处理 SSL 终止、负载均衡、静态内容服务等。
- Ingress Controller 就扮演了这个反向代理服务器的角色。 它作为流量的入口点,根据 Ingress 规则将流量分发到 Kubernetes 集群内部的各个服务。
Ingress 类似于反向代理服务器的配置文件 (例如 Nginx 的
nginx.conf中的server和location块):在 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
23apiVersion: 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 会自动感知这些变化并更新其路由配置,这使得管理更加灵活和自动化。
总结:
- 先理解需求: 在 Kubernetes 中,Pod 和 Service 通常只有内部 IP,外部无法直接访问。我们需要一种机制将外部请求安全、高效地路由到内部服务。
- Ingress 是“意图”: 它是一个 Kubernetes 资源,用它来声明“我希望当用户访问
a.com/foo时,流量被送到service-x;当用户访问b.com时,流量被送到service-y”。它不关心这个意图是如何实现的。 - Ingress Controller 是“实现者”: 它是一个在集群中运行的 Pod(或一组 Pod),它像一个勤奋的管家,时刻关注着您声明的 Ingress “意图”。一旦有新的或更新的 Ingress 规则,它就会去配置其内部的代理服务器(如 Nginx),让这些规则真正生效。它才是真正处理外部请求并将其转发到正确服务的组件。
所以,Ingress 和 Ingress Controller 总是成对出现的。用Ingress定义 规则,然后部署一个 Ingress Controller 来使这些规则生效。这提供了一种强大且灵活的方式来管理对您集群中应用程序的访问。
三、Ingress发布服务流程
1、通过域名发布 K8s 中的服务 (使用 Ingress)
在 Kubernetes 中,我们通常使用 Ingress 资源和 Ingress Controller 来实现这一目标。Ingress 充当了“智能路由器”或“流量规则手册”,而 Ingress Controller 则是这个路由器的“执行者”。
前提条件:
- 一个运行中的 Kubernetes 集群。
- 您的应用程序已部署在 K8s 中,并通过一个 Service(通常是
ClusterIP类型)暴露在集群内部。这个 Service 为您的应用 Pod 提供了集群内部的稳定 IP 和端口。 - 集群中已部署并运行一个 Ingress Controller。 例如 Nginx Ingress Controller, Traefik, HAProxy Ingress 等。这是外部流量进入集群的入口点。
- 您拥有一个域名 (例如
yourdomain.com)。 - 您有权限配置该域名的 DNS 记录。
具体实现流程:
部署您的应用程序和内部 Service (Deployment & Service):
- 首先,确保您的应用程序(例如一个 Web 应用)已经通过 Deployment、StatefulSet 或其他控制器部署到 K8s 集群中。
- 然后,为这些 Pod 创建一个 Service,类型通常是
ClusterIP。这个 Service 会为这一组 Pod 分配 一个集群内部的虚拟 IP 地址和端口,供集群内部其他服务或 Ingress Controller 访问。 - 例如,您可能有一个
myapp-deployment.yaml和myapp-service.yaml。myapp-service可能监听在集群内部的http://myapp-service:8080。
创建 Ingress 资源 (Ingress Resource):
这是核心步骤。您需要创建一个 Ingress 类型的 YAML 文件,定义外部流量如何路由到您的
myapp-service。一个简单的 Ingress 规则可能如下所示
myapp-ingress.yaml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24apiVersion: 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 资源,并根据其规则更新其内部的路由配置。
获取 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 名称)。
配置 DNS 解析 (DNS Configuration):
登录到您的域名注册商或 DNS 服务提供商的管理控制台。
为您的域名
1
myapp.yourdomain.com
添加一条 DNS 记录:
- 如果 Ingress Controller 的外部入口是一个 IP 地址: 创建一条
A记录,将其指向您在第 3 步中获取到的 Ingress Controller 的外部 IP 地址。 - 如果 Ingress Controller 的外部入口是一个主机名 (例如 AWS ELB): 创建一条
CNAME记录,将其指向该主机名。
- 如果 Ingress Controller 的外部入口是一个 IP 地址: 创建一条
DNS 记录的生效通常需要一些时间(取决于 TTL 配置)。
(可选,但推荐)配置 TLS/SSL 证书:
- 为了使用 HTTPS,您需要为您的域名获取一个 TLS 证书。
- 您可以手动获取证书,然后将其创建为一个 Kubernetes Secret (
tls类型),并在 Ingress 资源的tls.secretName字段中引用它。 - 更推荐的做法是使用
cert-manager这样的工具,它可以自动从 Let’s Encrypt 等机构获取并续订证书,并自动创建和管理所需的 Secret。
测试访问:
- 等待 DNS 记录生效后,在浏览器中输入
http://myapp.yourdomain.com或https://myapp.yourdomain.com(如果您配置了 TLS)。 - 如果一切配置正确,您的请求将通过以下路径到达您的应用: 用户 -> DNS 解析 -> Ingress Controller (外部 IP) -> Ingress 规则匹配 ->
myapp-service-> 您的应用 Pod。
- 等待 DNS 记录生效后,在浏览器中输入
2、传统架构中通过域名发布服务的流程
在没有 Kubernetes 的传统架构中(例如,直接在物理服务器或虚拟机上部署),流程通常如下:
前提条件:
- 一台或多台服务器 (物理机或虚拟机)。
- 服务器上安装并运行您的应用程序。
- 服务器上安装并配置一个 Web 服务器或反向代理 (例如 Nginx, Apache httpd, HAProxy)。
- 服务器拥有一个公网 IP 地址。
- 防火墙已配置,允许外部访问 Web 服务器的端口 (通常是 80 用于 HTTP,443 用于 HTTPS)。
- 您拥有一个域名,并有权限配置 DNS。
具体实现流程:
部署您的应用程序:
- 将您的应用程序代码(例如 Java WAR 包, Node.js 代码, Python 应用等)部署到服务器上。
- 确保应用程序在服务器的某个端口上运行(例如
localhost:8080)。
配置 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
19server {
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)。
获取服务器的公网 IP 地址:
- 记录下您的服务器的公网 IP 地址。
配置 DNS 解析 (DNS Configuration):
- 登录到您的域名注册商或 DNS 服务提供商的管理控制台。
- 为您的域名
myapp.yourdomain.com添加一条A记录,将其指向您在第 3 步中获取到的服务器的公网 IP 地址。
(可选,但推荐)配置 TLS/SSL 证书:
- 如果您在 Nginx 配置中启用了 HTTPS,您需要获取并安装 TLS 证书。
- 可以使用 Let’s Encrypt (通过 Certbot 工具) 免费获取证书,或者购买商业证书。
- 确保证书文件路径在 Nginx 配置中正确指定。
测试访问:
- 等待 DNS 记录生效后,在浏览器中输入
http://myapp.yourdomain.com或https://myapp.yourdomain.com。 - 请求将通过以下路径: 用户 -> DNS 解析 -> 您的服务器公网 IP -> Nginx (或 Apache) -> 您的应用。
- 等待 DNS 记录生效后,在浏览器中输入
总结
| 特性 | 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的方式部署
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 | 为k8s-node01 k8s-node02 k8s-node03节点添加标签 |
1 | [root@k8s-master01 14-ingress]# kubectl label nodes k8s-node01 k8s-node02 k8s-node03 ingress=true |
6.部署
1 | [root@k8s-master01 14-ingress]# kubectl create -f deploy.yaml |
查看部署情况:
kubectl get pod -n ingress-nginx
添加标签注意:
1 | 如果使用命令给节点添加标签,准确写法是:kubectl label nodes k8s-node01 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 | [root@k8s-master01 ~]# kubectl create -f nginx-ingress.yaml |
六、Ingress入门
6.1 Ingress资源定义规范
ingress资源基本配置解读:
1 | 创建一个ingress资源 |
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❌ 不匹配 多了 /detailhttps://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 | #定义一个ingress文件 |
在本地hosts文件中添加解析记录,生产环境中需要配置DNS解析:
1 | wendows路径 C:\Windows\System32\drivers\etc |
6.3特例-不配置域名发布服务
不想通过域名访问,而是通过ip地址访问可以不配置host: nginx.test.com 来实现,不常用
1 | kind: Ingress |
七、Ingress实战
接下来的操作在单独的namespace中操作,创建命名空间ingress
1 | [root@k8s-master01 ~]# kubectl create ns ingress |
创建deployment服务模拟web:
1 | [root@k8s-master01 14-ingress]# kubectl create deployment vue --image=registry.cn-beijing.aliyuncs.com/k8s-liujunwei/vue:test -n ingress |
创建service,暴露上述deployment
1 | [root@k8s-master01 14-ingress]# kubectl expose deployment -n ingress vue --port=80 |
7.1使用HTTPS发布服务
生产环境对外的服务,一般需要配置https协议,使用Ingress也可以非常方便的添加https 的证书。 由于是测试环境,并没有权威证书,所以需要使用OpenSSL生成一个测试证书。如果 是生产环境,证书为在第三方公司购买的证书,无需自行生成:
注意:证书的配置推荐配置在入口网关或SLB处,如果没有也可以配置在ingress控制器上;
1 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginx.test.com" |
使用生成的 tls.key 和 tls.crt创建secret:
1 | [root@k8s-master01 14-ingress]# kubectl create secret tls vue-tls --cert=tls.crt --key=tls.key -n ingress |
配置ingress暴露服务,并添加TLS配置:
1 | vim https-ingress.yaml |
创建上述ingress:
1 | [root@k8s-master01 14-ingress]# kubectl create -f https-ingress.yaml |
查看创建的ingress:
1 | [root@k8s-master01 14-ingress]# kubectl get ingress -n ingress |
接下来通过添加hosts文件,解析该域名,在浏览器进行访问:
1 | hosts文件中添加下列记录 |
浏览器访问nginx.jiugen.com,点击继续访问网站,可以看到网站是https,由于证书是自己生成的,所以浏览器会提示不安全。生产环境使用权威证书即可,不会提示不安全。
7.2为域名添加用户名密码认证
有些开源工具本身不提供密码认证,如果暴露出去会有很大风险,对于这类工具可以使用 Nginx 的 basic-auth 设置密码访问,具体方法如下,由于需要使用htpasswd工具,所以需要先安装 httpd:
1 | [root@k8s-master01 ~]# htpasswd -c auth liujunwei |
基于auth密码文件创建secret:
1 | [root@k8s-master01 ~]# kubectl create secret generic basic-auth --from-file=auth -n ingress |
查看创建的secret:
1 | [root@k8s-master01 ~]# kubectl get secrets -n ingress |
基于7.1中创建的ingress(https-ingress),添加密码认证:
1 | # 添加 annotations 部分 |
重新应用 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 | [root@k8s-master01 14-ingress]# kubectl get po -n ingress |
未开启会话保持,同一个主机访问可以看到流量会在三个副本中都有。
通过以下配置,即可看到流量只会进入到一个Pod:
1 | [root@k8s-master01 14-ingress]# cat https-ingress.yaml |
同时浏览器的request headers会添加一个Cookie的属性:
流量重新分配,更改nginx.ingress.kubernetes.io/affinity-mode: balanced即可:
1 | annotations: |
总结:
①、会话保持(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 | [客户端浏览器] |
7.3.3 生产级:SSE + WebSocket + 长连接 Ingress 模板
1 | apiVersion: networking.k8s.io/v1 |
详细参数解释
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")。
- 字段 (Key):
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"是 自定义的,您可以根据需要设置任何表示秒数的字符串。
- 字段 (Key):
4. nginx.ingress.kubernetes.io/proxy-buffering: "off"
- 含义: 控制是否启用 Nginx 的响应缓冲。
- 作用: 设置为
"off"表示关闭缓冲。默认情况下 Nginx ("on") 会先将后端服务的完整响应缓冲起来,然后再发送给客户端。关闭缓冲后,Nginx 会立即将从后端收到的数据块转发给客户端。这对于流式传输(Streaming)或服务器推送事件 (SSE) 至关重要,否则客户端可能要等很久才能收到第一个数据包。 - 字段/值:
- 字段 (Key):
.../proxy-buffering是 固定的 注解名。 - 值 (Value):
"off"是自定义值,选项通常是"on"或"off"。
- 字段 (Key):
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 的名称。
- 字段 (Key):
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"。
- 字段 (Key):
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"。
- 字段 (Key):
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 配置块是完全自定义的。
- 字段 (Key):
7.4 域名重定向Redirect
在使用Nginx作为代理服务器时,Redirect可用于域名的重定向,比如访问old.com被重定 向到new.com。Ingress也可以实现Redirect功能,接下来用nginx.redirect.com作为旧域名, baidu.com作为新域名进行演示:
1 | vim redirect-ingress.yaml |
创建上述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 | [root@k8s-master01 ~]# kubectl create deployment backend-api --image=registry.cn-beijing.aliyuncs.com/dotbalo/nginx:backend-api -n ingress |
创建Service暴露该服务:
1 | [root@k8s-master01 ~]# kubectl expose deployment -n ingress backend-api --port=80 |
查看service并通过ip测试,是否能请求:
1 | [root@k8s-master01 ~]# kubectl get svc -n ingress |
创建ingress:
理想的状态是访问rewrite.jiugen.com/api-a能访问到服务
1 | kind: Ingress |
创建上述ingress资源,然后通过浏览器访问rewrite.jiugen.com/api-a,会发现报404
此时需要配置Ingress Nginx的Rewrite功能,将/api-a重写为“/”,配置示例如下:
1 | kind: Ingress |
重新创建资源: kubectl replace -f rewrite.yaml,再次访问rewrite.jiugen.com/api-a即可访问到后端服务:
注意:需要Rewrite和不需要Rewrite的千万不要写在同一个ingress资源中,一定要分开写,如果在通一个ingress中配置了,会把所有的地址都重写。
7.6访问速率限制
有时候可能需要限制速率以降低后端压力,或者限制单个IP每秒的访问速率防止攻击。此时可以使用Nginx的rate limit进行配置。
首先没有加速率限制,使用ab进行访问,Failed为0:
1 | [root@k8s-master01 14-ingress]# ab -c 10 -n 100 http://rewrite.jiugen.com/ |grep requests |
在看代码前,必须记住一点: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 | kind: Ingress |
创建ingress资源并再次使用ab测试,Failed为74:
1 | [root@k8s-master01 14-ingress]# ab -c 10 -n 100 http://rewrite.jiugen.com/ |grep requests |
其他限制参数如下:
| 功能 | 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 层。
原因如下:
- 全局精准:Istio 可以配置全局限流(使用 Redis 配额),不受 “Pod 副本数” 的影响。
- 更细粒度:Istio 可以针对 HTTP Header(如 JWT 中的 User ID)限流,而 Nginx Ingress 社区版主要基于 IP。
- 统一治理:将流量规则统一收敛在 Service Mesh 层。
7.7局部黑名单-单个ingress生效
使用nginx.ingress.kubernetes.io/denylist-source-range添加黑名单,支持IP、网段,多个黑 名单逗号分隔:
1 | kind: Ingress |
创建ingress资源,然后通过浏览器访问 rewrite.jiugen.com 查看效果:
1 | [root@k8s-master01 14-ingress]# kubectl create -f deny-ingress.yaml |
可以看到提示403拒绝访问:
其他客户端可以访问:
1 | [root@k8s-master01 14-ingress]# curl rewrite.jiugen.com |
7.8 局部白名单-单个ingress生效
白名单就是限制只有指定 IP 才能访问这个 Ingress,其余所有 IP 都被拒绝,在名单中才能进,没在名单里一律不让进。直接在yaml文件中配置即可,比如只允许 192.168.181.141访问,只需要添加一个nginx.ingress.kubernetes.io/whitelist-source-range 注释 即可:
1 | kind: Ingress |
创建上述ingress,并通过不同ip的客户端进行访问,观察效果:
1 | [root@k8s-master01 14-ingress]# kubectl create -f whitelist-ingress.yaml |
192.168.0.4客户端访问测试,看到正常访问:
192.168.0.4以外的客户端访问,这里用k8s-master01节点测试,提示403权限拒绝:
1 | [root@k8s-master01 14-ingress]# curl \rewrite.jiugen.com |
| 类型 | 含义 | 优点 | 缺点 |
|---|---|---|---|
| 白名单 | 允许指定 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 | [root@k8s-master01 14-ingress]# kubectl edit configmaps -n ingress-nginx ingress-nginx-controller |
“192.168.0.4”是我电脑的ip,创建ingress资源,通过电脑浏览器访问域名查看是否可以访问:
1 | #ingress配置如下,该ingress没有单独配置任何黑白名单: |
创建上述ingress资源,然后通过客户端访问测试:
1 | [root@k8s-master01 14-ingress]# kubectl create -f global-denylist.yaml |
“192.168.0.4”客户端访问测试,浏览器报403权限拒绝:
使用其他客户端测试,正常请求:
1 | [root@k8s-master01 14-ingress]# curl rewrite.jiugen.com |
7.10全局白名单-所有ingress生效
相同的道理全局白名单也是限制只有指定 IP 才能访问 Ingress,其余所有 IP 都被拒绝,在名单中才能进,没在名单里一律不让进。
场景: 整个集群的 Ingress 入口只允许特定的 IP 段访问,拒绝其他所有访问(常用于内部测试环境)。
操作步骤: 修改 ingress-nginx-controller 引用的 ConfigMap(通常名为 ingress-nginx-controller)。
1 | [root@k8s-master01 14-ingress]# kubectl edit configmaps -n ingress-nginx ingress-nginx-controller |
“192.168.0.4”是我电脑的ip,创建ingress资源,通过电脑浏览器访问域名查看是否可以访问:
1 | #ingress配置如下,该ingress没有单独配置任何黑白名单: |
创建上述ingress资源,然后通过客户端访问测试:
1 | [root@k8s-master01 14-ingress]# kubectl create -f global-whitelist.yaml |
“192.168.0.4”客户端访问测试,正常访问才对:
使用其他客户端测试,应该报403权限拒绝才对:
1 | [root@k8s-master01 14-ingress]# curl rewrite.jiugen.com |
7.11自定义友好错误页提示
官方文档参考https://kubernetes.github.io/ingress-nginx/examples/customization/custom-errors/
在 Kubernetes 的 Ingress Nginx Controller (v1.12.2) 中配置自定义错误页面,核心逻辑是拦截和转发:
- 拦截:告诉 Nginx Controller 哪些错误码(如 404, 500,501,502, 503,504)不要直接返回给用户,而是拦截下来。
- 转发:将拦截到的请求转发给你自己部署的一个“默认后端服务(Default Backend)”,这个服务负责展示漂亮的 HTML 页面。
以下是完整的配置步骤,分为 准备资源、部署自定义后端、修改 Controller 配置 三大步。
第一步:准备 HTML 页面和 Nginx 配置
创建一个 ConfigMap,用于将错误页挂载到Default Backend服务中:
- 准备想要展示的
404.html和50x.html。 - 这里把404.html、50x.html和nginx配置都在ConfigMap中定义。
1 | ConfigMap配置如下: |
第二步:部署“默认后端服务” (Default Backend)
创建文件 default-backend.yaml:部署“默认后端服务” (Default Backend)
部署一个轻量级的 Nginx Pod 来承载上面的 HTML,并暴露为 Service。
创建文件 default-backend.yaml:
1 | apiVersion: v1 |
配置解释:
- Deployment: 运行一个标准 Nginx。
- VolumeMounts: 将第一步 ConfigMap 中的 HTML 挂载到容器内部,覆盖nginx的默认配置。
- Service: 暴露端口,让 Ingress Controller 能访问到它。
创建应用:
1 | [root@k8s-master01 14-ingress]# kubectl create -f backend-error-pages.yaml |
第三步:配置 Ingress Controller (关键步骤)
现在后端准备好了,需要告诉 Ingress Controller 两件事:
- 全局错误拦截: 开启
custom-http-errors,指定需要拦截的错误码。 - 指定默认后端: 告诉 Controller,拦截指定的错误码后,把流量转给
custom-default-backend这个 Service。
1 | 1. 修改 ConfigMap 开启拦截,配置需要拦截的错误码,找到你的 Ingress Controller 的全局 ConfigMap(通常叫ingress-nginx-controller),添加或修改 data 下的 custom-http-errors |
第四步:验证:
验证 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 | cat mobile.html |
pc端页面如下:
1 | cat pc.html |
创建一个ConfigMap,挂载到nginx容器中来替换nginx的默认页
1 | [root@k8s-master01 pc-m]# kubectl create cm pc-mobile-cm --from-file=pc.html --from-file=mobile.html |
接下来部署两个不同的后端服务,用来代表“PC端应用”和“手机端应用”。
1 | [root@k8s-master01 pc-m]# cat pc-deploy.yaml |
1 | [root@k8s-master01 pc-m]# cat mobile-deploy.yaml |
创建应用:
1 | [root@k8s-master01 pc-m]# kubectl create -f mobile-deploy.yaml -f pc-deploy.yaml |
第二步:配置 Ingress (核心步骤)
需要配置两个 Ingress 规则:
- Mobile Ingress: 专门负责接收
m.demo.com的流量。 - Main (PC) Ingress: 负责接收
www.demo.com的流量,并且包含重定向逻辑。
原因: 重定向逻辑必须写在入口域名(即 PC 域名)的 Ingress 上,因为用户首先访问的是 PC 域名。
1 | # 1. 手机端域名的 Ingress (这是重定向的目标) |
创建ingress资源:
1 | [root@k8s-master01 pc-m]# kubectl create -f ingress.yaml |
报错原因:
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 | [root@k8s-master01 pc-m]# kubectl edit cm -n ingress-nginx ingress-nginx-controller |
继续创建ingress:
1 | [root@k8s-master01 pc-m]# kubectl create -f ingress.yaml |
通过浏览器访问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 | annotations: |
7.13Ingress实现灰度发布或蓝绿部署
什么是灰度发布:
灰度发布(也称为金丝雀发布)是一种软件发布策略,在 Kubernetes 集群中逐步将流量从旧版本服务迁移到新版本服务的过程,这种发布策略可以有效降低升级风险,及时发现潜在问题。旨在以最小化风险的方式逐步将新版本的应用或服务推向生产环境。
蓝绿部署是同时维护两套完全独立、功能等价的环境(称为“蓝环境”和“绿环境”),其中只有一套对外提供服务,新版本部署到非活跃环境(比如当前蓝环境在服务,就把新版本部署到绿环境),验证无误后,通过路由切换(如Service/Ingress切换)瞬间切流到新环境,切换成功后,原环境可保留作回滚之用,或下线。
7.13.1 准备灰度发布环境:
1 | 创建v1版本:首先创建模拟 Production(生产)环境的 Namespace 和服务: |
使用浏览器访问www.canary.com该服务,可以看到 Canary v1 的页面:(添加hosts解析)
7.13.2 准备要上线的新版本
创建v2版本:接下来创建 v2 版本,充当灰度环境(也就是要上线的新版本):
1 | 这里为了好区分,创建单独的命名空间,也可以使用production命名空间,这个根据自己情况来 |
接下来通过创建v2版本的ingress来控制流量:
1 | 创建 v2 版本的 Ingress 时,需要添加两个注释,一个是 nginx.ingress.kubernetes.io/canary, 表明是灰度环境,nginx.ingress.kubernetes.io/canary-weight 表明切多少流量到该环境,本示例为10% |
创建v2版本的ingress:
1 | [root@k8s-master01 14-ingress]# kubectl create -f canary-v2-ingress.yaml |
此时通过 nginx.ingress.kubernetes.io/canary-weight: “10”设置的权重是 10,即 v1:v2 版本的请求比例大约为 9:1
7.13.3测试灰度发布效果
由于浏览器观看效果不明显,这里使用命令观察访问的效果:
接下来使用 Ruby 脚本进行测试,此脚本会分别输出 v1 和 v2 的访问次数:
1 | vim test-canary.rb |
如果没有ruby需要安装:
1 | yum install ruby -y |
执行脚本测试:
1 | 记得在执行脚本的机器上添加hosts解析 |
713.4清理该笔记中产生的数据
1 | 删除canary命名空间中的deploy svc ingress |
八、Ingress常见报错排查
8.1Not Found(404)报错
404 表示访问的路由不存在,通常问题如下:
- Ingress 路径配置的不正确
- Ingress 的配置未被Controller 解析
- 未使用正确的域名和路径访问
- 代理的服务没有该路径
场景分析:有一个项目包含一个前端,两个后端,域名为project.test.com。
其中 路径 / 指向前端;
路径 /userapi 指向 user服务 ;
路径 /paymentapi 指向 payment服务;
部署服务:
1 | 部署user服务: |
配置Ingress:
1 | # cat backend-api.yaml |
创建ingress:
1 | [root@k8s-master01 14-ingress]# kubectl create -f backend-api.yaml |
测试请求:
1 | [root@k8s-master01 14-ingress]# curl -X POST project.test.com/user |
此时访问是没有任何问题的,但是有可能程序提供的接口是/api或者/user,/payment,但是要求的接口并不是/api 和/user,比如要求的是userapi和paymentapi,此时直接配置会报404。
假设要求的接口路径是/userapi和/paymentapi:
1 | kind: Ingress |
重新应用Ingress:
1 | [root@k8s-master01 14-ingress]# kubectl replace -f backend-api.yaml |
更新后访问:
1 | [root@k8s-master01 14-ingress]# curl project.test.com/paymentapi/payment -I |
此时会报404,因为虽然Ingress配置了路径,但是后端服务并没有/paymentapi这个路径, 所以会报404,此时需要用rewrite进行地址重写把paymentapi去除,这样/paymentapi/payment 就被代理到了程序的/payment
1 | # ingress做如下修改 |
更新ingress并访问测试:
1 | [root@k8s-master01 14-ingress]# kubectl replace -f backend-api.yaml |
再次访问:
1 | [root@k8s-master01 14-ingress]# curl project.test.com/paymentapi/payment |
8.2Request Entity Too Large(413)报错
有时候需要上传一些大文件给程序,但是nginx默认允许的最大文件大小只有8M,不足以 满足生产最大上传需求,此时可以通过nginx.ingress.kubernetes.io/proxy-body-size参数进行更 改(也可以在ConfigMap中全局添加):
Ingress配置如下:
1 | kind: Ingress |
如果需要全局设置,修改ConfigMap,全局配置 NGINX Ingress Controller 的 ConfigMap:
1 | kubectl edit configmap ingress-nginx-controller -n ingress-nginx |
重启 Controller Pods 以应用:
1 | kubectl rollout restart deployment ingress-nginx-controller -n ingress-nginx |
8.3Service Unavailable(503)报错
503 一般是代理的服务不可用导致的,通常问题如下:
- Ingress代理配置错误,比如Service名字或端口写错
- Ingress代理的Service不存在
- Ingress代理的Service后端Pod不正常
8.4Gateway Timeout(504)报错
遇到 504 Gateway Time-out 错误,通常意味着 Ingress 控制器在规定的时间内没有收到后端服务(Pod)的完整响应。
简单来说:你的应用处理请求的时间太长了,Ingress 等不及就断开了连接。
504 发生的具体原因:
- 应用处理过慢(最常见): 后端业务逻辑复杂(如大文件上传、生成复杂报表、数据库慢查询),处理时间超过了 Nginx Ingress 默认的超时时间(通常是 60秒)。
- 资源不足导致卡顿: Pod 的 CPU 或内存达到限制(Limits),导致处理速度极慢。
- 连接超时设置不合理: 后端应用与 Ingress 之间的
Keep-Alive连接设置不匹配。 - 网络丢包或拥塞: 极端情况下,集群内部网络问题导致数据包无法及时传输(较少见)。
模拟案例:部署后端程序模拟报错
1 | 创建后端程序 |
请求接口,返回时间比较长:
1 | [root@k8s-master01 14-ingress]# curl 10.96.242.84:8080/longtime |
配置Ingress:
1 | vim longtime.yaml |
创建ingress:
1 | [root@k8s-master01 14-ingress]# kubectl create -f longtime.yaml |
添加host解析并测试访问:如果在访问遇到504 Gateway Time-out,解决方案如下:
找到对应的Ingress,需要在ingress中的annotations:下添加超时时间的参数:
1 | 需要添加以下三个核心超时参数: |
Ingress配置参考如下:
1 | apiVersion: networking.k8s.io/v1 |
重新应用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 生产场景还原
假设架构如下:
- 前端应用 (SPA/Web): 部署在
https://dashboard.example.com - 后端服务 (API): 部署在 Kubernetes 集群内,Service 名为
user-service。 - 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原因分析
为什么会这样?
- 同源策略: 浏览器默认只允许请求同域名、同端口、同协议的资源。
dashboard.example.com和api.example.com属于跨域。 - Preflight (预检请求): 对于非简单请求(如带有
AuthorizationHeader,或Content-Type: application/json的 POST 请求),浏览器会先自动发送一个OPTIONS方法的请求,询问服务器:“你允许dashboard.example.com访问吗?” - 拦截点: 流量到达 Ingress Controller。如果 Ingress 没有配置 CORS 响应头,它(或后端应用)返回的响应中不包含
Access-Control-Allow-Origin。 - 浏览器阻断: 浏览器看到响应头里没有通行证,直接丢弃真正的请求,并抛出红色错误。
8.5.3解决方法
解决这个问题的思路是:在响应头中加入允许跨域的字段。通常可以在ingress中处理,也可以在程序代码中处理推荐在在网关层统一处理。
在 Ingress 层配置 (推荐,运维侧解决):这是最干净的解法,将跨域逻辑与业务代码解耦。以最常用的 Nginx Ingress Controller 为例。
操作步骤: 修改Ingress YAML 文件,添加 annotations。
1 | apiVersion: networking.k8s.io/v1 |
注意:访问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 资源。





































