1 为什么需要 Ingress?
K8s 中服务(Service)的默认暴露方式有局限性,Ingress 正是为解决这些问题而生:
| 服务暴露方式 | 缺点 | Ingress 的优势 |
|---|---|---|
| NodePort | 每个 Service 占用一个 Node 端口,端口多了难管理;只能用 "IP + 端口" 访问,无域名 | 1 个入口管理所有服务,支持 "域名 + 路径" 转发;无需暴露大量端口 |
| LoadBalancer | 每个 Service 需要一个云厂商负载均衡器,成本高;无法共享 | 共享 1 个 LoadBalancerr(或 NodePort),低成本;统一配置 SSL / 限流 |
简单说:Ingress 让你能用 http://app1.example.com 访问服务 A,http://app2.example.com 访问服务 B,或 http://example.com/app1 访问服务 A、http://example.com/app2 访问服务 B,实现 "一入口多服务" 的管理。
2 Ingress 的核心组成:2 个关键部分
Ingress 不是一个单一组件,而是 "规则定义 + 控制器实现" 的组合,二者缺一不可:
2.1 Ingress 资源(规则定义)
是 K8s 的 API 对象,用 YAML 定义 "外部流量如何转发到内部 Service" 的规则,比如:
- 哪个域名对应哪个服务?
- 哪个路径对应哪个服务?
- 是否需要 SSL 证书?
它本身不干活,只负责 "写规则"。
2.2 Ingress Controller(规则执行)
是实际 "干活" 的组件(通常是一个 Pod),它会:
- 监听 K8s API 中 Ingress 资源的变化;
- 根据 Ingress 规则生成反向代理配置(比如 Nginx 配置);
- 接收外部流量,按照配置转发到对应的 Service。
主流 Ingress Controller:Nginx Ingress Controller(最常用,基于 Nginx)、Traefik、HAProxy Ingress 等,本文以 "Nginx Ingress Controller" 为例。
3 Ingress 的核心概念与基础配置
先从一个最简单的 Ingress 示例入手,理解核心字段:
3.1 基础示例:用路径转发流量
假设集群内有两个服务:
service-foo:提供/foo路径的服务,端口 80;service-bar:提供/bar路径的服务,端口 80;
我们要实现:外部访问 http://myapp.com/foo 转发到service-foo,http://myapp.com/bar 转发到service-bar。
步骤 1:部署 Ingress Controller(前提)
首先要部署 Nginx Ingress Controller,这是所有 Ingress 规则生效的基础:
bash
# 部署官方Nginx Ingress Controller(适用于K8s 1.19+)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
验证部署(查看ingress-nginx命名空间下的 Pod,Running表示正常):
bash
kubectl get pods -n ingress-nginx
查看 Ingress Controller 的暴露方式(默认用 NodePort,会分配两个端口:HTTP (80)、HTTPS (443)):
bash
kubectl get svc -n ingress-nginx ingress-nginx-controller
输出类似(PORT(S)列的32000:80/TCP表示 NodePort 为 32000,外部可通过 "NodeIP:32000" 访问):
plaintext
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.xxx.xxx <none> 80:32000/TCP,443:32100/TCP 10m
如果部署NodePort而是LoadBalancer,需要修改yaml文件
bash
kubectl edit svc ingress-nginx-controller -n ingress-nginx
# 将LoadBalancer修改成NodePort
步骤 2:创建 Ingress 资源(规则)
创建ingress-demo.yaml文件,定义转发规则:
yaml
apiVersion: networking.k8s.io/v1 # 固定版本(K8s 1.19+推荐)
kind: Ingress
metadata:
name: ingress-demo # Ingress名称
annotations:
# 声明使用Nginx Ingress Controller(不同控制器注解不同)
kubernetes.io/ingress.class: "nginx"
spec:
rules: # 核心规则列表(可配置多个域名)
- host: myapp.com # 外部访问的域名(需配置DNS或本地hosts解析到NodeIP)
http:
paths: # 该域名下的路径转发规则
- path: /foo # 匹配路径:/foo
pathType: Prefix # 路径匹配类型:Prefix(前缀匹配,如/foo/1也会匹配)
backend:
service:
name: service-foo # 转发到的Service名称
port:
number: 80 # Service的端口
- path: /bar
pathType: Prefix
backend:
service:
name: service-bar
port:
number: 80
应用 Ingress 规则:
bash
kubectl apply -f ingress-demo.yaml
查看 Ingress 状态(ADDRESS列会显示 Ingress Controller 的访问地址,即 NodeIP):
bash
kubectl get ingress ingress-demo
步骤 3:测试访问
-
配置域名解析:在本地电脑的
hosts文件(Windows:C:\Windows\System32\drivers\etc\hosts;Linux/macOS:/etc/hosts)中添加一行:plaintext192.168.1.100 myapp.com # 192.168.1.100是K8s Node的IP -
访问测试:
- 访问
http://myapp.com:32000/foo→ 流量转发到service-foo; - 访问
http://myapp.com:32000/bar→ 流量转发到service-bar。
- 访问
3.2 核心字段解析
| 字段 | 作用 | 关键说明 |
|---|---|---|
metadata.annotations |
配置 Ingress Controller 的附加参数 | 如kubernetes.io/ingress.class: "nginx"指定用 Nginx 控制器;还可配置 SSL、限流等 |
spec.rules.host |
外部访问的域名 | 若不填,规则对 "所有域名" 生效(不推荐);支持通配符,如*.example.com |
spec.rules.http.paths.pathType |
路径匹配类型 | 3 种:- Prefix:前缀匹配(如/foo匹配/foo、/foo/1)- Exact:精确匹配(如/foo只匹配/foo)- ImplementationSpecific:由控制器决定(不常用) |
spec.rules.http.paths.backend |
流量转发的目标 Service | 必须指定service.name(Service 名称)和service.port.number(Service 端口) |
3.3 完整案例:用 Ingress 路由到两个服务
目标
部署两个应用(Nginx 和 Httpd),通过 Ingress 实现:
- 访问
example.com/nginx路由到 Nginx 服务。 - 访问
example.com/httpd路由到 Httpd 服务。
环境准备
- 一个可用的 k8s 集群(推荐用
minikube本地测试,简单易操作)。 kubectl命令行工具已配置。
步骤 1:部署 Ingress Controller(以 Nginx 为例)
如果用 minikube,可直接启用内置的 Nginx Ingress Controller:
bash
minikube addons enable ingress
验证部署是否成功(等待所有 Pod 处于 Running 状态):
bash
kubectl get pods -n ingress-nginx
输出类似(名称可能略有不同):
plaintext
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-xxxxxxxxx-xxxxx 1/1 Running 0 5m
步骤 2:部署测试应用(Nginx 和 Httpd)
创建两个 Deployment,分别运行 Nginx 和 Httpd 容器。
新建 apps.yaml 文件:
yaml
# Nginx 应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-app
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
# Httpd 应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd-app
spec:
replicas: 2
selector:
matchLabels:
app: httpd
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd
image: httpd:alpine
ports:
- containerPort: 80
部署应用:
bash
kubectl apply -f apps.yaml
验证部署:
bash
kubectl get deployments
输出:
plaintext
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-app 2/2 2 2 1m
httpd-app 2/2 2 2 1m
步骤 3:创建 Service(连接 Ingress 和应用)
Ingress 需要通过 Service 访问后端应用,这里创建两个 ClusterIP 类型的 Service(仅集群内部可访问,由 Ingress 转发流量)。
新建 services.yaml 文件:
yaml
# Nginx 服务
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # 匹配 Nginx Deployment 的 label
ports:
- port: 80 # Service 暴露的端口
targetPort: 80 # 容器内的端口(与 Deployment 中一致)
---
# Httpd 服务
apiVersion: v1
kind: Service
metadata:
name: httpd-service
spec:
selector:
app: httpd # 匹配 Httpd Deployment 的 label
ports:
- port: 80
targetPort: 80
部署 Service:
bash
kubectl apply -f services.yaml
验证 Service:
bash
kubectl get services
输出:
plaintext
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 10.100.xxx.xxx <none> 80/TCP 1m
httpd-service ClusterIP 10.100.xxx.xxx <none> 80/TCP 1m
步骤 4:创建 Ingress 规则
定义 Ingress 资源,配置路由规则:将 example.com/nginx 路由到 nginx-service,example.com/httpd 路由到 httpd-service。
新建 ingress.yaml 文件:
yaml
# apiVersion:指定 Kubernetes API 版本,networking.k8s.io/v1 是当前 Ingress 资源的稳定版本
apiVersion: networking.k8s.io/v1
# kind:声明资源类型为 Ingress,用于管理外部访问集群内服务的路由规则
kind: Ingress
metadata:
# name:Ingress 资源的名称,用于唯一标识该资源(在同一命名空间内)
name: app-ingress
# annotations:附加配置信息,用于向 Ingress 控制器传递额外参数(此处为 Nginx 控制器专用配置)
annotations:
# nginx.ingress.kubernetes.io/rewrite-target:路径重写规则
# 作用:当请求路径匹配下方的 path 规则时,将路径重写为 /$2($2 对应 path 中第二个捕获组 (.*) 的内容)
# 举例:访问 /nginx/test 时,重写后路径为 /test,避免后端服务(如 Nginx)因前缀 /nginx 不存在而返回 404
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
# ingressClassName:指定使用的 Ingress 控制器类型
# 必须与集群中部署的 Ingress 控制器的 class 名称一致(此处为 nginx,即 Nginx Ingress Controller)
# 若不指定,Ingress 可能无法被正确处理(取决于集群默认配置)
ingressClassName: nginx
# rules:路由规则列表,定义不同主机/路径的请求如何转发
rules:
- host: example.com # 仅对访问 example.com 域名的请求生效
# 注意:需在本地 hosts 文件或 DNS 中配置该域名指向 Ingress 控制器的 IP 才能测试
http: # 定义 HTTP 协议的路由规则(HTTPS 需额外配置 tls 字段)
paths: # 路径匹配规则列表(按顺序匹配,优先匹配靠前的规则)
- path: /nginx(/|$)(.*) # 路径匹配表达式
# 解析:
# - /nginx:匹配以 /nginx 开头的路径
# - (/|$):匹配紧跟的 / 或路径结束(避免误匹配 /nginxxyz 等无关路径)
# - (.*):捕获后续所有字符(作为第二个分组,对应 rewrite-target 中的 $2)
# 最终匹配范围:/nginx、/nginx/、/nginx/test、/nginx/a/b 等
pathType: ImplementationSpecific # 由 Nginx 控制器处理正则匹配
# 结合上述 path 表达式后,实际为精确的前缀匹配(避免默认 Prefix 可能的模糊匹配)
backend: # 匹配该路径时的后端服务配置
service:
name: nginx-service # 转发到名为 nginx-service 的 Service
port:
number: 80 # 转发到该 Service 的 80 端口
- path: /httpd(/|$)(.*) # 与上述 /nginx 规则逻辑一致,匹配以 /httpd 开头的路径
# 匹配范围:/httpd、/httpd/、/httpd/docs 等
pathType: ImplementationSpecific # 由 Nginx 控制器处理正则匹配
backend: # 匹配该路径时的后端服务配置
service:
name: httpd-service # 转发到名为 httpd-service 的 Service
port:
number: 80 # 转发到该 Service 的 80 端口
关键说明:
annotations中的rewrite-target:当访问example.com/nginx/test时,会重写为nginx-service:80/test(否则后端服务会收到/nginx/test路径,可能返回 404)。ImplementationSpecific是 Nginx Ingress 处理正则路径的标准配置,它会让 Nginx 控制器解析path中的正则表达式(如(/|$)(.*)),实现需要的精确前缀匹配和路径捕获。host: example.com:仅匹配该域名的请求(如果不指定 host,会匹配所有域名)。
部署 Ingress:
bash
kubectl apply -f ingress.yaml
验证 Ingress:
bash
kubectl get ingress
输出(ADDRESS 是 Ingress Controller 的 IP,后续访问用这个 IP):
plaintext
NAME CLASS HOSTS ADDRESS PORTS AGE
app-ingress nginx example.com 192.168.49.2 80 1m
步骤 5:测试访问
需要让本地机器识别 example.com 并指向 Ingress Controller 的 IP(即上面的 ADDRESS)。
配置本地 hosts
-
Linux:编辑
/etc/hosts,添加一行:plaintext192.168.49.2 example.com # 替换为你的 Ingress ADDRESS -
Windows :编辑
C:\Windows\System32\drivers\etc\hosts(需管理员权限),添加同上内容。
验证路由
用 curl 或浏览器访问:
-
访问
example.com/nginx:bashcurl example.com/nginx输出 Nginx 的默认页面(说明路由到 Nginx 服务):
plaintext<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ... -
访问
example.com/httpd:bashcurl example.com/httpd输出 Httpd 的默认页面(说明路由到 Httpd 服务):
plaintext<html><body><h1>It works!</h1></body></html>
3.4 常见问题与扩展
1. Ingress 状态一直为 Pending?
- 原因:未部署 Ingress Controller,或控制器未正常运行。
- 解决:检查
kubectl get pods -n ingress-nginx,确保控制器 Pod 正常运行。
2. 访问返回 404?
- 检查 Ingress 规则的
path和service.name是否正确(标签匹配是否错误)。 - 若路径包含前缀(如
/nginx),需配置rewrite-target注解(否则后端服务可能无对应路径)。
3. 扩展:配置 HTTPS
如需启用 HTTPS,需创建 TLS 证书(可通过 cert-manager 自动生成),并在 Ingress 中添加 tls 配置:
yaml
spec:
tls:
- hosts:
- example.com
secretName: example-tls # 存储证书的 Secret 名称
# 其他规则不变...
通过这个案例,你应该已经掌握了 Ingress 的核心用法:部署控制器、定义路由规则、关联服务与应用。实际生产中,可根据需求扩展更复杂的路由策略(如权重分配、跨命名空间路由等)。
4 Ingress 高级功能
4.1 SSL 证书配置(HTTPS 访问)
Ingress 支持 "TLS 终止"(在 Ingress Controller 层处理 SSL,内部服务用 HTTP),只需两步:
步骤 1:创建存储证书的 Secret
将 SSL 证书(tls.crt公钥、tls.key私钥)保存为 Secret(需与 Ingress 在同一命名空间):
bash
kubectl create secret tls myapp-tls \
--cert=./tls.crt \ # 你的证书文件路径
--key=./tls.key # 你的私钥文件路径
步骤 2:在 Ingress 中引用 TLS Secret
修改ingress-demo.yaml,添加spec.tls字段:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-demo
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls: # TLS配置
- hosts:
- myapp.com # 要启用HTTPS的域名(需与证书匹配)
secretName: myapp-tls # 引用前面创建的Secret
rules: # 规则不变,同上
- host: myapp.com
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: service-foo
port:
number: 80
- path: /bar
pathType: Prefix
backend:
service:
name: service-bar
port:
number: 80
应用后,访问 https://myapp.com:32100/foo(32100 是 Ingress Controller 的 HTTPS NodePort),即可通过 HTTPS 访问。
4.2 域名重定向(HTTP→HTTPS)
通过注解配置,强制将 HTTP 流量重定向到 HTTPS:
yaml
metadata:
annotations:
kubernetes.io/ingress.class: "nginx"
# 开启HTTP→HTTPS重定向
nginx.ingress.kubernetes.io/ssl-redirect: "true"
4.3 路径重写(URL Rewrite)
场景:外部访问 http://myapp.com/app1,但内部 Service 实际提供的路径是 /(如服务只认根路径),需用路径重写。
通过注解配置重写规则:
yaml
metadata:
annotations:
kubernetes.io/ingress.class: "nginx"
# 重写规则:将 /app1/(.*) 重写为 /$1
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: myapp.com
http:
paths:
- path: /app1/(.*) # 用正则匹配路径,括号捕获后面的内容
pathType: Prefix
backend:
service:
name: service-app1 # 该服务的根路径是 /
port:
number: 80
效果:访问 http://myapp.com/app1/login → 重写为 http://service-app1/login。
5 Ingress vs 传统服务暴露:核心对比
| 维度 | Ingress(Nginx Controller) | NodePort | LoadBalancer |
|---|---|---|---|
| 访问方式 | 域名 + 路径(友好) | IP + 端口(难记) | IP / 域名(友好) |
| 端口管理 | 只暴露 1-2 个端口(80/443) | 每个服务 1 个端口(混乱) | 每个服务 1 个负载均衡器(成本高) |
| SSL 支持 | 统一配置(方便) | 需服务自身实现(麻烦) | 需云厂商配置(分散) |
| 高级功能 | 路径重写、限流、缓存 | 无 | 部分支持(依赖云厂商) |
| 适用场景 | 生产环境(多服务、需域名) | 测试环境(快速暴露) | 生产环境(单服务、高可用) |
6 常见问题与排查
6.1 Ingress 规则不生效?
- 检查 Ingress Controller 是否正常运行:
kubectl get pods -n ingress-nginx; - 检查 Ingress 是否关联到 Controller:
kubectl describe ingress <ingress-name>,查看Events是否有 "Successfully configured"; - 检查 Service 是否正常:
kubectl get svc <service-name>,确保 Service 能正常访问(可在集群内用 Pod 测试)。
6.2 域名访问不通?
- 检查本地
hosts或 DNS 是否正确解析到 NodeIP; - 检查 Node 的防火墙是否开放 Ingress Controller 的 NodePort(如 32000、32100)。
6.3 HTTPS 证书无效?
- 检查 Secret 是否正确:
kubectl describe secret <tls-secret-name>,确保证书和私钥匹配; - 检查 Ingress 的
spec.tls.hosts是否与证书的 "主题备用名称(SAN)" 一致。
总结
Ingress 的核心是 "统一入口 + 规则转发",学习时要抓住两个重点:
- 理解 "Ingress 资源(规则)" 和 "Ingress Controller(执行)" 的关系;
- 掌握基础的 "域名 + 路径转发" 和生产级的 "SSL 配置、路径重写"。