K8s Ingress 实战:NGINX 暴露服务与 TLS

在 Kubernetes 里,Pod 的 IP 是随时变化的,外部流量怎么稳定地访问到服务?Kubernetes Ingress 就是集群统一的 HTTP/HTTPS 入口:它把”域名+路径”映射到后端 Service,再交由 Ingress Controller 真正落地转发。本文用最主流的 Ingress-NGINX 手把手讲清概念、配置、TLS 与灰度,并附一份排障清单。

一、为什么需要 Ingress

把服务暴露给外部,最朴素的办法是给 Service 设 type: NodePort 或 LoadBalancer。但 NodePort 端口范围受限(默认 30000–32767)、每暴露一个服务就要占一个端口;LoadBalancer 在每个服务前都挂一个云厂商负载均衡器,成本高且难以做统一的域名路由与 TLS。当集群里有几十个微服务时,这种”一个服务一个入口”的方式立刻失控。Ingress 的价值,正是用一份声明式的路由规则,把多个服务的入口收敛到同一个 IP 和 80/443 端口之下,按域名和路径分流。它可以理解为集群内置的七层路由器:只负责 HTTP 路由与 TLS 终止,不做限流、鉴权等 API 网关能力(那些交给专门组件)。对于绝大多数”对外暴露 Web 服务”的场景,Ingress 已是事实标准。

二、Ingress 与 Ingress Controller 的关系

这是新手最容易混淆的点:Ingress 只是一份”路由规则”的 API 对象,它本身不干活;真正转发流量的是 Ingress Controller,一个常驻集群、监听 Ingress 变更并据此生成反向代理配置的组件。换句话说,只写 Ingress 资源不会生效,必须先部署一个 Controller。常见的实现有 Ingress-NGINX(基于 Nginx,生态最成熟)、Traefik(原生支持 Let’s Encrypt 自动证书)、以及服务网格方案 Istio 服务网格入门。本文聚焦 Ingress-NGINX。部署时还会顺带创建一个 default-backend,用来接管所有匹配不到规则的请求(返回 404),因此即便规则写错,流量也不会被误导向未知后端。

三、快速部署 Ingress-NGINX

生产环境推荐用 Helm 安装(若你接手的是 GitOps 集群,也可通过 ArgoCD GitOps 实战 把这份配置纳入版本管理)。安装完成后,Controller 会以 DaemonSet 或 Deployment 形式运行,并在云上自动创建一个 LoadBalancer 类型的 Service 作为统一入口 IP。

# 添加仓库并安装(稳定版)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx --create-namespace

# 查看入口 IP(记下 EXTERNAL-IP,后续把域名 A 记录指向它)
kubectl get svc -n ingress-nginx ingress-nginx-controller \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

安装后可用 kubectl get pods -n ingress-nginx 确认 Controller 处于 Running;若长时间 Pending,多半是资源配额或污点容忍问题,优先用 kubectl describe 看事件定位。

四、第一个 Ingress:把服务暴露到域名

假设集群里已有一个名为 web 的 Service(端口 80),下面这份 Ingress 把 demo.example.com 的流量转发给它。注意 host 与 path 的匹配规则:pathType 推荐用 Prefix,且路径是否带结尾斜杠会直接影响转发行为(见第六节坑点)。

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

应用后,把 demo.example.com 的 A 记录指向第二节拿到的入口 IP,稍等 DNS 生效即可通过浏览器访问。底层其实就是 Ingress-NGINX 把规则翻译成了一段 Nginx 反向代理 配置,原理与你手动写 Nginx 反代一致,只是交给了控制器自动维护。如果访问返回 404 而非后端内容,先确认浏览器地址栏的域名与 Ingress 的 spec.rules[].host 完全一致——拼写与大小写都必须匹配,这是最常见的”配了却不通”原因。

五、配置 TLS:让流量走 HTTPS

对外服务必须上 HTTPS。Ingress 通过 tls 字段引用一个存放证书与私钥的 Secret 来启用 443 监听并自动做 80→443 重定向。证书可以手动用 SSL 证书原理 的方式签发后塞进 Secret,更优雅的做法是配合 cert-manager 自动续期,彻底杜绝前文”证书过期致全站中断”这类事故。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress-tls
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - demo.example.com
    secretName: demo-tls   # 存放 demo.example.com 的 cert+key
  rules:
  - host: demo.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80

用 kubectl 创建 Secret 的命令如下(生产请用 cert-manager 自动化):

kubectl create secret tls demo-tls \
  --cert=fullchain.pem --key=privkey.pem -n default

生产环境强烈建议加注解 nginx.ingress.kubernetes.io/force-ssl-redirect: "true" 强制跳转 HTTPS,并视需要开启 HSTS,避免用户以明文 HTTP 提交敏感数据。

六、路径与主机路由:多服务分流

Ingress 支持两种维度的分流。其一是基于路径(path-based):/api 走后端 API 服务,/ 走前端;其二是基于主机(host-based):a.example.com 与 b.example.com 指向不同服务。两者可同时存在。下面用一张表对比常见路由策略的取舍。

路由方式适用场景注意点
Path Prefix /api前后端同域名注意结尾斜杠与 rewrite
Host a.com / b.com多租户/多站点需多条 DNS A 记录
Path 精确匹配健康检查/回调端点用 Exact 避免前缀误伤

若后端期望收到的路径不带前缀(例如 /api/user 应转发为 /user),需加注解做重写:nginx.ingress.kubernetes.io/rewrite-target: /$2,并把 path 写成 /api(/|$)(.*)。

另一处易错点是正则路径:若用 pathType: ImplementationSpecific 配合正则,务必确认 Controller 已开启相应特性,否则规则会被静默忽略,请求落到 default-backend 而非预期服务。

七、灰度发布:按权重切流

上线新版本最怕”一把梭”。Ingress-NGINX 原生支持基于权重的金丝雀(canary)路由:把少量流量导到新版本,验证无误再逐步放大。下面这份 Ingress 会将 10% 的请求切到 web-canary,其余仍走稳定版。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  ingressClassName: nginx
  rules:
  - host: demo.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-canary
            port:
              number: 80

配合 ArgoCD GitOps 实战 这类 GitOps 工具,你可以把”权重从 10% 调到 100%”也变成一次可回滚的 Git 提交,发布过程完全声明化、可审计。

八、常见坑与排障清单

实际落地时,八成问题出在配置细节。按顺序排查:

  • 502/504:先确认后端 Service 与 Pod 本身健康(kubectl port-forward 直连验证),再查 Ingress Controller 日志。
  • 路径斜杠:path: /api 的 Prefix 匹配 /api 但不匹配 /api/;需要子路径时务必用 rewrite-target。
  • 注解拼写:canary-weight 写成 canaryWeight 会静默失效,所有注解都是小写中划线。
  • ingressClassName 缺失:集群有多个 Controller 时,必须显式指定,否则规则无人接管。
  • 证书不匹配:TLS Secret 的 hosts 必须与 Ingress 的 host 完全一致,否则浏览器报证书错误。

把 Ingress 清单纳入 GitHub Actions 部署自动化 流水线,在合并前用 kubectl apply –dry-run=server 做校验,能挡掉大部分低级错误。

最后,Controller 自身的资源也要留余量:高并发入口一旦 OOM 或被限流,所有依赖它的服务会集体失联,排障时别只盯着业务 Pod。

九、小结

Kubernetes Ingress 是集群对外的统一入口,Ingress-NGINX 是当前最成熟的实现。掌握”规则(Ingress) + 实现(Controller)”的分工后,路由、TLS、灰度都能用声明式 YAML 搞定。建议把入口配置、证书自动续期与 CI 流水线串起来,让发布既安全又可回滚。理解透 Ingress,才算真正打通了 K8s 应用交付的”最后一公里”。

上一篇 MySQL 死锁排查与预防实战:从复现到根治
下一篇 连接池耗尽与端口耗尽排查:从 TIME_WAIT 到连接泄漏根治