
上一篇【第16篇】Service的负载均衡原理——kube-proxy到底干了什么下一篇【第18篇】Ingress Controller选型和实战——Nginx Ingress完全指南摘要Service不错——ClusterIP让你内部Pod互相找到NodePort对外开了个后门LoadBalancer更是直接挂上了云厂商的LB。但你有没有发现一个尴尬的问题一个公网LB一个月几百块你有3个服务就得买3个LB10个服务就得买10个——这谁顶得住而且Service只管四层TCP/UDPHTTP Header里那些花活域名路由、路径匹配、Cookie粘滞、Header改写它一概不关心。这就是Ingress出场的原因——它是K8s原生的七层HTTP路由入口。一个Ingress Controller对应一个LB通过域名和路径把流量分发给不同的后端Service。本文先把Ingress和Service的关系理清楚再逐字段拆解Ingress YAML最后演示TLS证书管理。一、Ingress是什么——“HTTP反向代理入口”1.1 Service解决不了的三个问题【Service的局限——为什么要Ingress】 问题1每个公网服务要一个LB 穷死 用 LoadBalancer Service 用 Ingress ┌──────┐ ┌──────┐ ┌──────┐ ┌─────────────────────────┐ │ LB-1 │ │ LB-2 │ │ LB-3 │ │ 一个 LB (省钱) │ │$100/月│ │$100/月│ │$100/月│ │ │ └──┬───┘ └──┬───┘ └──┬───┘ │ api.example.com → API │ │ │ │ │ web.example.com → Front │ ▼ ▼ ▼ │ admin.example.com→Admin │ ┌──────┐┌──────┐┌──────┐ └─────────────────────────┘ │API ││Front ││Admin │ └──────┘└──────┘└──────┘ 10个服务 10个LB ¥1000/月 10个服务 1个LB ¥100/月 问题2域名路由 Service不行 问题3URL路径路由 Service不行 /api/* → API Service, /web/* → Frontend Service要点Service工作在OSI第四层传输层只管IP和端口。Ingress工作在第七层应用层能看HTTP的Host头、URL路径、Cookie等。打个比方Service是邮局的按地址送信Ingress是公司的前台接待员——看你是来面试的、送快递的还是谈合作的把你引到不同的办公室。1.2 Ingress ≠ Ingress Controller这是很多人学Ingress时踩的第一个坑——Ingress只是规则Ingress Controller才是真正转发流量的发动机。【Ingress 和 Ingress Controller 的关系】 你写的 Ingress 资源 实际干活的是 Ingress Controller ┌─────────────────────────┐ ┌─────────────────────────┐ │ apiVersion: networking │ │ Nginx Ingress Controller│ │ .k8s.io/v1 │ │ (一个运行的Pod) │ │ kind: Ingress │ ──监听──▶ │ │ │ │ │ 监听Ingress资源变化 │ │ spec: │ │ → 生成 nginx.conf │ │ rules: │ │ → reload nginx │ │ - host: api.ex.com │ │ → 按规则转发HTTP流量 │ │ paths: │ │ │ │ - path: /users │ │ 类比 │ │ backend: ... │ │ Ingress nginx.conf │ └─────────────────────────┘ │ Ingress Controller │ │ nginx 进程 │ └─────────────────────────┘ Ingress 只是配置文件——自己不干活 跟Service类似Service也是配置真正转发的是kube-proxy要点K8s默认不安装任何Ingress Controller——你装了Nginx Ingress、Traefik、Contour等任一款Controller后Ingress资源才能生效。这个设计很像Java的接口和实现Ingress是接口标准各家Controller是具体实现。二、Ingress YAML——完整拆解一个完整的Ingress资源长这样apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:myapp-ingressnamespace:defaultannotations:# 各种功能开关不同Controller不同nginx.ingress.kubernetes.io/rewrite-target:/nginx.ingress.kubernetes.io/ssl-redirect:truespec:ingressClassName:nginx# ★ 指定用哪个 Ingress Controllertls:# ★ TLS配置-hosts:-api.example.com-web.example.comsecretName:example-tls# TLS证书存在这个Secret里rules:# ★ 路由规则——核心-host:api.example.com# 域名匹配http:paths:-path:/users# 路径前缀匹配pathType:Prefix# Prefix前缀或 Exact精确backend:service:name:users-service# 转发到哪个Serviceport:number:8080-path:/orderspathType:Prefixbackend:service:name:orders-serviceport:number:8080-host:web.example.com# 另一个域名http:paths:-path:/pathType:Prefixbackend:service:name:frontend-serviceport:number:80defaultBackend:# ★ 默认后端——匹配不到任何规则时用resource:apiGroup:k8s.example.comkind:StorageBucketname:static-assets2.1 ingressClassName——“谁来执行这些规则”spec:ingressClassName:nginx# 告诉K8s这个Ingress交给Nginx Ingress Controller处理如果你集群里装了多个Ingress Controller比如同时有Nginx和Traefik这个字段确保Ingress只被指定的Controller处理。K8s 1.18版本强烈推荐设置这个字段。2.2 host和path——“根据什么路由”【Ingress路由决策流程】 HTTP请求: GET http://api.example.com/users/123 │ ▼ ┌─────────────────────────────────────────────┐ │ Ingress Controller │ │ │ │ Step 1: 匹配 host │ │ Host头是 api.example.com 吗 │ │ 遍历所有rules找到 host 匹配的 │ │ │ │ │ ▼ │ │ Step 2: 匹配 path │ │ URL路径是 /users/123 │ │ pathType是Prefix/users能匹配 /users/123 │ │ │ │ │ ▼ │ │ Step 3: 转发到 backend │ │ 转发到 users-service:8080 │ └─────────────────────────────────────────────┘2.3 pathType——前缀还是精确pathType含义示例/users能匹配的请求Prefix路径前缀匹配path: /users/users、/users/、/users/123、/users/123/ordersExact精确匹配path: /users仅/usersImplementationSpecific由Controller决定—取决于具体Controller的实现# Prefix vs Exact 的区别paths:-path:/apipathType:Prefix# /api、/api/、/api/users 都能匹配backend:...-path:/healthpathType:Exact# 只有 /health 能匹配/health/check 不行backend:...2.4 defaultBackend——“如果都没匹配到…”# 所有规则都没匹配上时的兜底处理spec:defaultBackend:service:name:404-handler# 返回404页面port:number:80rules:-host:api.example.com# ...要点defaultBackend不是必选项但我建议都配上——至少指向一个返回404的服务。否则没匹配到的请求可能会被路由到随机后端或者返回Ingress Controller自己的默认页面用户体验极差。三、基于域名的路由——“一个入口多个服务”【域名路由示意】 user1: api.example.com/users ─┐ user2: api.example.com/orders ──┤ │ ▼ ┌─────────────────┐ │ Ingress Controller│ │ (Nginx/Traefik) │ │ :80/:443 │ └──┬──────┬──────┬──┘ │ │ │ host匹配 │ │ │ ┌──────────────┘ │ └──────────────┐ ▼ ▼ ▼ api.example.com web.example.com admin.example.com │ │ │ ┌────────┴────────┐ ┌──────┴──────┐ ┌──────┴──────┐ │ /users │ /orders│ │ / │ │ / │ ▼ ▼ │ ▼ │ ▼ │ ┌──────┐ ┌────────┐ │ ┌──────────┐ │ ┌──────────┐ │ │users │ │orders │ │ │frontend │ │ │admin │ │ │svc │ │svc │ │ │svc │ │ │panel svc │ │ └──────┘ └────────┘ │ └──────────┘ │ └──────────┘ │ └────────────────┘ └────────────────┘# 域名路径双层路由apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:multi-domain-ingressspec:ingressClassName:nginxrules:# 第一个域名api.example.com-host:api.example.comhttp:paths:-path:/userspathType:Prefixbackend:service:name:users-serviceport:number:8080-path:/orderspathType:Prefixbackend:service:name:orders-serviceport:number:8080-path:/productspathType:Prefixbackend:service:name:products-serviceport:number:8080# 第二个域名web.example.com-host:web.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:frontend-serviceport:number:80# 第三个域名admin.example.com-host:admin.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:admin-serviceport:number:8080四、TLS配置——让Ingress终结HTTPSIngress Controller是TLS终结的最佳位置——客户端到Ingress是HTTPSIngress到后端Pod是HTTP。这样后端Pod不用管证书的事省心又安全。【TLS 终结——Ingress处理HTTPS后端Pod只管HTTP】 客户端 Ingress Controller 后端Pod ────── ────────────────── ────── https://api.example.com/users ┌────────────────┐ ┌────┐ ──────────────────────────────► │ 解密TLS │ http://10.244 │ │ 加密传输 │ 检查Host/Path │ ─────────────► │ │ │ 路由到后端 │ 明文传输 │ │ │ (K8s内部网络安全) │ └────┘ └────────────────┘4.1 手动配置TLS证书# Step 1: 用OpenSSL生成自签名证书仅限测试openssl req-x509-nodes-days365-newkeyrsa:2048\-keyouttls.key-outtls.crt\-subj/CNapi.example.com/OMyOrg# Step 2: 创建TLS Secretkubectl create secret tls example-tls\--certtls.crt\--keytls.key\-ndefault# Step 3: 在Ingress中引用apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:api-ingress-tlsspec:ingressClassName:nginxtls:-hosts:-api.example.com-web.example.com# 一个Secret可以服务多个域名secretName:example-tls# 刚才创建的Secretrules:-host:api.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:api-serviceport:number:80804.2 用cert-manager自动管理证书——“再也不用记续期时间”cert-manager是K8s生态中管理TLS证书的事实标准。它自动从Let’s Encrypt免费等CA申请证书、自动续期。# 安装cert-managerHelm方式helm repoaddjetstack https://charts.jetstack.io helminstallcert-manager jetstack/cert-manager\--namespacecert-manager\--create-namespace\--setinstallCRDstrue# 创建Issuer证书颁发者配置apiVersion:cert-manager.io/v1kind:Issuermetadata:name:letsencrypt-prodnamespace:defaultspec:acme:server:https://acme-v02.api.letsencrypt.org/directoryemail:adminexample.comprivateKeySecretRef:name:letsencrypt-prod-keysolvers:-http01:ingress:class:nginx---# Ingress——只需加一个Annotationcert-manager自动帮你拿证书apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:api-ingress-tls-autoannotations:cert-manager.io/issuer:letsencrypt-prod# ← 就这一行spec:ingressClassName:nginxtls:-hosts:-api.example.comsecretName:api-tls-secret# cert-manager自动创建这个Secretrules:-host:api.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:api-serviceport:number:8080要点cert-manager注册免费TLS证书需要域名有真实DNS解析Let’s Encrypt会验证域名所有权。本地测试用自签名证书就够了但生产环境强烈建议用cert-manager Let’s Encrypt——免费、自动续期、不用半夜爬起来更新过期证书。五、rewrite-target——URL路径改写这是Ingress最常见的玩法——外部路径和内部路径不一样apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:rewrite-ingressannotations:# 关键重写目标路径nginx.ingress.kubernetes.io/rewrite-target:/$2spec:ingressClassName:nginxrules:-host:api.example.comhttp:paths:# 外部: /api/users → 内部: /users# 正则 (.*) 捕获组$2 代表第二个捕获组-path:/api(/|$)(.*)pathType:ImplementationSpecificbackend:service:name:api-serviceport:number:8080【URL 重写示意】 外部请求 内部转发 ───────── ──────── /api/users ──rewrite──► /users /api/users/123 ──rewrite──► /users/123 /api/orders ──rewrite──► /orders /health ──不匹配──► /health走其他规则或defaultBackend 注解说明 nginx.ingress.kubernetes.io/rewrite-target: /$2 path: /api(/|$)(.*) 正则解释 /api — 匹配 /api (/|$) — $1: 匹配 / 或 结尾 (.*) — $2: 匹配剩余所有字符 rewrite到: /$2 去掉 /api 前缀六、同一个Ingress管理多个服务——完整示例apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:main-ingressannotations:cert-manager.io/issuer:letsencrypt-prodnginx.ingress.kubernetes.io/ssl-redirect:truespec:ingressClassName:nginxtls:-hosts:-*.example.comsecretName:wildcard-tlsdefaultBackend:service:name:default-backendport:number:80rules:# 用户API-host:api.example.comhttp:paths:-path:/v1/userspathType:Prefixbackend:service:name:users-v1-serviceport:number:8080-path:/v2/userspathType:Prefixbackend:service:name:users-v2-service# v2灰度版port:number:8080# PC Web前端-host:www.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:web-frontend-serviceport:number:80# 移动端API不同的域名-host:m.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:mobile-api-serviceport:number:8080# 管理后台-host:admin.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:admin-serviceport:number:8080# 部署并验证kubectl apply-fmain-ingress.yaml kubectl get ingress# NAME CLASS HOSTS ADDRESS PORTS# main-ingress nginx api.example.com,www.example.com 10.0.1.100 80, 443# m.example.com,admin.example.com# 测试路由curl-HHost: api.example.comhttp://10.0.1.100/v1/userscurl-HHost: www.example.comhttp://10.0.1.100/curl-HHost: admin.example.comhttp://10.0.1.100/本篇小结Ingress是K8s七层路由的标准入口——它让你用一个LB管理N个服务通过域名和路径精准分发HTTP流量Ingress是规则Controller是引擎Ingress资源只是配置文件真正干活的是Nginx Ingress/Traefik等Controller域名路径双层路由host和path组合决定流量去向Prefix/Exact两种匹配模式各有用处TLS终结Ingress处理HTTPS→HTTP转换cert-manager免费自动管理证书告别手动续期pathType和rewrite控制路径匹配粒度和改写规则是Ingress日常使用率最高的功能下一篇咱们深入Ingress Controller的世界——Nginx Ingress怎么工作、常用Annotations大全、金丝雀发布怎么配、Traefik有什么不一样的玩法。上一篇【第16篇】Service的负载均衡原理——kube-proxy到底干了什么下一篇【第18篇】Ingress Controller选型和实战——Nginx Ingress完全指南