跳到主要内容

Ingress 入口管理

Ingress 是 Kubernetes 中管理外部 HTTP/HTTPS 访问集群内服务的 API 对象。它充当集群入口的路由器,根据域名和路径将外部流量分发到不同的 Service。相比 NodePort 和 LoadBalancer,Ingress 提供了更灵活、更经济的流量管理方式。

为什么需要 Ingress?​

在了解 Ingress 之前,我们先回顾一下暴露服务的几种方式,看看它们各自的局限性。

现有方案的局限​

NodePort 在每个节点上开放一个端口,外部可以通过 节点IP:端口 访问服务。这种方式简单,但有几个问题:端口范围受限(30000-32767),需要知道节点 IP 地址,而且每个服务都需要一个端口,管理起来很混乱。

LoadBalancer 会为每个服务创建一个云负载均衡器,提供了稳定的外部 IP。但这种方式成本高昂——每个服务都需要一个独立的负载均衡器,如果有 10 个服务就需要 10 个负载均衡器,费用会迅速增长。

Ingress 的优势​

Ingress 解决了上述问题。它只需要一个负载均衡器(或一个节点 IP),就可以根据请求的域名和路径将流量路由到不同的服务。这就像一个智能前台,根据访客要找的部门(域名和路径)将他们引导到正确的位置。

外部请求 → Ingress Controller → 根据域名/路径路由 → Service → Pod

Ingress 的核心价值在于:

  • 统一入口:所有外部流量通过一个入口进入集群
  • 基于域名的虚拟主机:一个 IP 可以服务多个域名
  • 基于路径的路由:根据 URL 路径分发到不同服务
  • TLS 终止:集中处理 HTTPS 证书,后端服务无需关心 TLS
  • 成本优化:减少对外部负载均衡器的需求

Ingress 与 Ingress Controller 的关系​

这是理解 Ingress 最关键的一点:Ingress 资源本身不做任何工作,它只是定义了路由规则。真正实现流量转发的是 Ingress Controller。

打个比方,Ingress 就像一份路由表,而 Ingress Controller 才是真正执行路由的设备。你创建了 Ingress 资源,但如果集群中没有运行 Ingress Controller,那么什么都不会发生。

常见的 Ingress Controller​

控制器特点适用场景
NGINX Ingress社区最活跃,功能全面通用场景
Traefik自动服务发现,配置简单微服务架构
HAProxy高性能,适合大规模高流量场景
Kong基于 Nginx,带 API 网关功能API 管理
Istio GatewayService Mesh 集成服务网格场景

部署 NGINX Ingress Controller​

NGINX Ingress Controller 是最常用的选择。以下是部署方法:

# 使用 Helm 安装(推荐)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace

# 或使用 kubectl 直接应用清单
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml

安装完成后,检查 Ingress Controller 是否正常运行:

kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

在云环境中,Ingress Controller 的 Service 类型通常是 LoadBalancer,云提供商会分配一个外部 IP 地址。在本地开发环境(如 Minikube)中,可以使用以下方式启用:

# Minikube 启用 Ingress 插件
minikube addons enable ingress

# 等待 Ingress Controller 就绪
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120s

Ingress 资源详解​

基本结构​

一个最简单的 Ingress 资源如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test-service
port:
number: 80

这个 Ingress 将所有访问 /testpath 路径的请求转发到 test-service 服务的 80 端口。

Ingress 规则详解​

Ingress 规则定义了如何将外部请求路由到内部服务。每条规则包含以下要素:

主机名(Host):可选,指定域名。如果指定了 foo.bar.com,则规则只对访问该域名的请求生效。如果不指定,则规则对所有请求生效。

路径(Path):URL 路径,如 /api、/web 等。每条路径关联一个后端服务。

路径类型(PathType):Kubernetes 要求每条路径必须指定类型,支持三种:

  • Exact:精确匹配,路径必须完全相同
  • Prefix:前缀匹配,路径以指定值开头即可
  • ImplementationSpecific:由 Ingress Controller 决定如何匹配

路径匹配的规则需要特别注意。以下是 Prefix 类型的匹配示例:

路径定义请求路径匹配结果
/foo/foo匹配
/foo/foo/匹配
/foo//foo匹配
/foo/foo/bar匹配
/foo/foobar不匹配
/aaa/bbb/aaa/bbb/ccc匹配
/aaa/bbb/aaa/bbbxyz不匹配

注意最后两个例子:/foo/bar 匹配 /foo/bar/baz,但不匹配 /foo/barbaz。这是因为路径是按 / 分隔的元素进行匹配,最后一个元素必须完整匹配。

默认后端(Default Backend)​

当请求不匹配任何规则时,Ingress Controller 会将其发送到默认后端。通常默认后端会返回 404 页面。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-with-default
spec:
ingressClassName: nginx
defaultBackend:
service:
name: default-http-backend
port:
number: 80

如果没有指定规则(spec.rules 为空),则必须指定 defaultBackend。

实战示例​

单服务入口​

最简单的场景:将所有流量路由到单个服务。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: single-service-ingress
spec:
ingressClassName: nginx
defaultBackend:
service:
name: my-app
port:
number: 8080

基于路径的扇出(Fanout)​

将同一个域名的不同路径路由到不同服务,这是最常见的场景。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fanout-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /web
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- path: /static
pathType: Prefix
backend:
service:
name: static-service
port:
number: 80

这个配置的效果是:

  • example.com/api/* → api-service
  • example.com/web/* → web-service
  • example.com/static/* → static-service

基于域名的虚拟主机​

不同的域名路由到不同的服务,实现一个 IP 托管多个网站。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-based-virtual-hosting
spec:
ingressClassName: nginx
rules:
- host: foo.example.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: foo-service
port:
number: 80
- host: bar.example.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: bar-service
port:
number: 80

混合配置​

结合域名和路径,实现更复杂的路由:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: complex-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1
port:
number: 80
- path: /v2
pathType: Prefix
backend:
service:
name: api-v2
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-frontend
port:
number: 80

IngressClass​

从 Kubernetes 1.18 开始,引入了 IngressClass 资源,用于指定 Ingress 应该由哪个 Ingress Controller 处理。

为什么需要 IngressClass?​

当一个集群中有多个 Ingress Controller 时,需要明确每个 Ingress 由谁来处理。比如你可能同时运行了 NGINX Ingress Controller 和 Traefik,它们处理流量的方式不同。IngressClass 让你可以指定使用哪个控制器。

定义 IngressClass​

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx

controller 字段指定控制器的名称,不同的控制器有不同的标识符:

  • NGINX: k8s.io/ingress-nginx
  • Traefik: traefik.io/ingress-controller

ingressclass.kubernetes.io/is-default-class 注解标记这是默认的 IngressClass。当 Ingress 没有指定 ingressClassName 时,会使用默认类。

在 Ingress 中使用 IngressClass​

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
ingressClassName: nginx # 指定使用的 IngressClass
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80

TLS 配置​

Ingress 可以终结 TLS 连接,让后端服务使用明文 HTTP,简化证书管理。

基本 TLS 配置​

首先创建一个 Secret 存储证书和私钥:

apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: default
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi... # Base64 编码的证书
tls.key: LS0tLS1CRUdJTi... # Base64 编码的私钥

然后在 Ingress 中引用这个 Secret:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
- www.example.com
secretName: tls-secret
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80

TLS 配置说明:

  • hosts 列表中的域名将使用此证书
  • 一个 Ingress 可以配置多个 TLS 条目,每个使用不同的证书
  • 如果不指定 hosts,则证书对所有域名生效

自动证书管理(cert-manager)​

手动管理证书很繁琐,cert-manager 可以自动申请和续期证书。

首先安装 cert-manager:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml

配置 Let's Encrypt 颁发者:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx

在 Ingress 中添加注解,自动获取证书:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auto-tls-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
secretName: auto-tls-secret # cert-manager 会自动创建
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80

常用注解​

Ingress Controller 通过注解提供高级功能。以下是一些常用的 NGINX Ingress 注解:

重写路径​

annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2

将 /api/v2/(.*) 重写为 /$1。

速率限制​

annotations:
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "10"

限制每秒请求数和并发连接数。

白名单​

annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,192.168.0.0/16"

只允许指定 IP 段访问。

代理配置​

annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"

配置代理缓冲区大小、超时时间等。

SSL 重定向​

annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"

强制 HTTPS 重定向。

多路径匹配优先级​

当一个请求匹配多条路径时,Ingress Controller 会按以下规则选择:

  1. 最长路径优先:/foo/bar 优先于 /foo
  2. 精确匹配优先于前缀匹配:Exact 类型优先于 Prefix

例如,以下配置中,访问 /foo 会使用精确匹配规则:

paths:
- path: /foo
pathType: Exact
backend:
service:
name: exact-service
port:
number: 80
- path: /foo
pathType: Prefix
backend:
service:
name: prefix-service
port:
number: 80

通配符域名​

Ingress 支持通配符域名,可以匹配子域名:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wildcard-ingress
spec:
ingressClassName: nginx
rules:
- host: "*.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wildcard-service
port:
number: 80

*.example.com 可以匹配 foo.example.com、bar.example.com 等,但不能匹配 example.com(没有子域名前缀)。

故障排查​

常见问题​

Ingress 没有分配地址

检查 Ingress Controller 是否正常运行:

kubectl get pods -n ingress-nginx
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller

404 Not Found

  • 检查 Service 是否存在且端口正确
  • 检查 Service 的 selector 是否匹配 Pod 标签
  • 检查 Ingress 规则的路径是否正确

502 Bad Gateway

  • 后端服务可能未就绪
  • 检查 Service 的 targetPort 是否正确
  • 检查应用是否监听正确的端口

调试命令​

# 查看 Ingress 详情
kubectl describe ingress my-ingress

# 查看 Ingress Controller 日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

# 查看 Ingress Controller 配置
kubectl exec -n ingress-nginx -it <pod-name> -- cat /etc/nginx/nginx.conf

# 测试后端服务连通性
kubectl run test --rm -it --image=busybox -- wget -qO- http://my-service:80

Ingress 与 Gateway API​

Kubernetes 社区正在开发 Gateway API 作为 Ingress 的继任者。Gateway API 提供了更丰富的功能,包括:

  • 面向角色的设计,分离基础设施和应用配置
  • 原生支持 TCP/UDP 路由
  • 更强的扩展性
  • 更好的跨命名空间路由支持

Ingress API 已被标记为冻结,不再接受新功能。对于新项目,建议考虑使用 Gateway API。但对于现有项目和简单场景,Ingress 仍然是可行的选择。

最佳实践​

1. 使用 IngressClass 明确指定控制器​

spec:
ingressClassName: nginx

2. 配置健康检查后端​

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: health-check-ingress
spec:
defaultBackend:
service:
name: health-check
port:
number: 80

3. 使用 cert-manager 自动管理证书​

避免手动管理证书,减少过期风险。

4. 合理配置资源限制​

annotations:
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "50"

5. 启用访问日志​

annotations:
nginx.ingress.kubernetes.io/enable-access-log: "true"

小结​

本章我们学习了:

  1. Ingress 概念:统一入口管理外部流量
  2. Ingress Controller:真正处理流量的组件
  3. 路由规则:基于域名和路径的流量分发
  4. TLS 配置:HTTPS 终止和证书管理
  5. 常用注解:扩展 Ingress 功能
  6. 故障排查:常见问题的解决方法

Ingress 是暴露 HTTP/HTTPS 服务的主要方式,掌握它对于生产环境部署至关重要。

练习​

  1. 部署 NGINX Ingress Controller
  2. 创建一个基于路径的 Ingress,将 /api 和 /web 路由到不同服务
  3. 配置 TLS 并验证 HTTPS 访问
  4. 使用 cert-manager 自动获取 Let's Encrypt 证书

参考资料​