K8s 服务太多?一个 Ingress 全搞定
一、你肯定遇到过这个场景
线上跑了 5 个微服务,每个都要对外暴露。如果用 NodePort,就需要给每个服务分配一个 30000+ 的端口,前端配了一堆端口号,运维在防火墙上开端口开到崩溃。再配上 TLS 证书,每个服务单独配一遍,重复劳动。
说白了,Service 的 NodePort 只解决**四层(TCP/UDP)**访问,没有应用层路由能力。NodePort 范围固定在 30000-32767,服务多了端口就不够用;如果用 LoadBalancer,云上每个 Service 都要买一个 CLB,成本直线上升。
这时候你需要的不是更多端口,而是Ingress——K8s 里统一流量入口的七层负载均衡,一个入口搞定所有微服务的域名路由、路径分发、TLS 终止。
二、三个概念。
1.概念
很多人一开始把 Ingress、Ingress Controller、IngressClass 当成一回事,其实三层分工明确:
| 概念 | 是什么 | 类比 |
|---|---|---|
| Ingress | 规则文件(YAML),定义"哪个域名走哪个 Service" | 菜单 |
| Ingress Controller | 真正干活的 Pod,读取 Ingress 规则并执行 | 厨师 |
| IngressClass | 标记"这条规则给哪个 Controller 处理" | 厨房编号 |
核心逻辑:你写 Ingress YAML → Ingress Controller 读到规则 → 更新内部 Nginx/Envoy 配置 → 流量按规则转发。
2.常见的 Controller 选型
| Controller | 适用场景 |
|---|---|
| ingress-nginx(k8s社区版) | 最通用,生态最全 |
| nginx-ingress(Nginx公司版) | 需要商业支持 |
| Traefik | 自动发现、自带 Dashboard |
| Istio Gateway | 服务网格场景 |
新手用 ingress-nginx 就够了。
等等,Ingress 是不是 K8s 自带的?不是。Ingress 不是 Kubernetes 核心组件,它是一个可选的 API 资源对象,需要单独安装 Controller 才能工作。把它想象成集群的「智能路由器」或「门户守卫」——根据规则(主机名、路径)将请求分发到不同的后端 Service。只创建 Ingress YAML 不管用,必须配合 Controller 一起部署。
3.Ingress 和 Service 对比
Ingress 和 Service 到底什么关系?很多人一开始也迷糊,看这张表就清楚了:
| 特性 | Ingress | Service |
|---|---|---|
| 工作层 | 七层(HTTP/HTTPS) | 四层(TCP/UDP/SCTP) |
| 路由能力 | 按域名、路径、Header 匹配 | 仅按端口和 ClusterIP |
| 协议支持 | HTTP、HTTPS、gRPC | 任意 TCP/UDP |
| 实现方式 | 依赖 Ingress Controller Pod | kube-proxy(iptables/IPVS)或云 LB |
| 主要目的 | 外部 HTTP 流量入口统一管理 | 集群内部服务发现与负载均衡 |
简单理解:Service 负责把流量送进集群,Ingress 负责把流量送到正确的 Service。两者是配合关系,不是替代关系。
三、从零搭一个 Ingress
1. 安装 ingress-nginx Controller
1.1 kubectl一键部署
#先下载官方完整 YAMLwgethttps://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml-Onginx-deploy.yaml#修改部署nginx-deploy.yaml YAML 中的 image 字段registry.aliyuncs.com/google_containers/nginx-ingress-controller:v1.10.0 registry.aliyuncs.com/google_containers/kube-webhook-certgen:v1.4.0#修改service类型为 NodePort# type: LoadBalancertype: NodePort#修改deploy使用宿主机端口kind: Deployment... ports: - containerPort:80name: http hostPort:80protocol: TCP - containerPort:443name: https hostPort:443protocol: TCP#部署kubectl apply-fnginx-deploy.yaml部署文件里都装了些什么?除了 Controller Deployment,官方 YAML 还自动创建了 Namespace、ServiceAccount、Role/ClusterRole、ValidatingWebhook 等资源。其中 RBAC 权限是 Controller 能正常工作的前提——它需要通过 API Server 监听 Ingress 资源的变化:
# Controller 需要的完整 RBAC 权限(官方 deploy.yaml 自动创建)rules:# 监听 Ingress/IngressClass 变化- apiGroups:["networking.k8s.io"]resources:["ingresses","ingressclasses"]verbs:["get","list","watch"]# 更新 Ingress 状态(ADDRESS 字段)- apiGroups:["networking.k8s.io"]resources:["ingresses/status"]verbs:["update"]# 读取 Service/Endpoints/ConfigMap/Secret/Pod(构建 upstream)- apiGroups:[""]resources:["services","endpoints","configmaps","pods","secrets","namespaces"]verbs:["get","list","watch"]这些权限覆盖了 Controller 的全部工作流:发现 Ingress 规则 → 读取后端 Service/Endpoints → 更新 Nginx 配置 → 回写 Ingress 状态。如果权限不足,Controller 日志会报forbidden,Ingress 规则不会生效。
1.2 使用 Helm 部署,便于后续升级和回滚(生产推荐)
- 添加仓库、拉取代码
# 添加仓库 & 更新helm repoaddingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update#拉取代码[root@master-1 ingress]# helm pull ingress-nginx/ingress-nginx#内网环境可直接从外网下载https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-4.12.3/ingress-nginx-4.12.3.tgz- 修改关键配置
# values.yaml 关键修改(国内环境)[root@master-1 ingress-nginx]# vim values.yamlglobal: image: registry: registry.aliyuncs.com controller: image: image: google_containers/nginx-ingress-controller tag:"v1.10.0"admissionWebhooks: patch: image: image: google_containers/kube-webhook-certgen tag: v1.4.0 hostPort: enabled:trueports: http:80https:443service: type: NodePort- 部署
[root@master-1 ingress-nginx]# cd ../#部署ingress-nginx[root@master-1 ingress]# helm install ingress-nginx ingress-nginx -n ingress-nginx --create-namespace -f ingress-nginx/values.yaml#查看[root@master-1 ingress]# helm list -n ingress-nginxNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION ingress-nginx ingress-nginx12025-07-0114:42:20.362166146 +0800 CST deployed ingress-nginx-4.12.31.12.3如下图所示,我们已经部署好了ingress controller
- 补充helm常用命令
#搜索ingress-nginx[root@master-1 ingress]# helm search repo ingress-nginxNAME CHART VERSION APP VERSION DESCRIPTION ingress-nginx/ingress-nginx4.12.31.12.3 Ingress controllerforKubernetes using NGINX a...# 下载ingress-nginx[root@master-1 ingress]# helm pull ingress-nginx/ingress-nginx#安装 ingress-nginx ingress-nginx 第一个是为ingress服务取名称为ingress-nginx,第二个是目录名[root@master-1 ingress]# helm install ingress-nginx ingress-nginx -n ingress-nginx --create-namespace -f ingress-nginx/values.yaml#卸载 ingress-ngin[root@master-1 ingress]# helm uninstall ingress-nginx -n ingress-nginx⚠️部署前必查:
hostPort会占用宿主机的 80 和 443 端口。如果宿主机上已经跑了原生 Nginx、Apache 或其他 Web 服务,Ingress Controller 会因端口冲突启动失败。先用ss -tlnp | grep -E ':80 |:443 '确认端口空闲再部署。
2. 部署nginx应用
# nginx.yamlapiVersion: apps/v1 kind: Deployment metadata: name: nginx-v1 spec: replicas:2#基于标签关联pod,会关联env=test或者env=prod的podselector: matchExpressions: - key:envvalues: -"test"-"prod"operator: In template: metadata:#为pod设置了两个标签labels: app: nginx-v1 env:testspec: containers: - name: nginx-v1 image: nginx:1.22.1 imagePullPolicy: IfNotPresent --- apiVersion: v1 kind: Service metadata: name: nginx-service-v1 spec:# 指定svc的类型为NodePort,也就是在默认的ClusterIP基础之上多监听所有worker节点的端口而已。type: NodePort# 基于标签选择器关联Podselector: app: nginx-v1# 配置端口映射ports:# 指定Service服务本身的端口号- port:80# 后端Pod提供服务的端口号targetPort:80同样方式部署nginx-v2,标签和名称换成nginx-v2。
3. 写 Ingress 规则
# ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:my-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target:/spec:ingressClassName:nginxrules:-host:nginx.liux.comhttp:paths:-path:/v1pathType:Prefixbackend:service:name:nginx-service-v1port:number:80-path:/v2pathType:Prefixbackend:service:name:nginx-service-v2port:number:80#ingressClassName: nginx 可以通过以下方式查看[root@master-1 ~]# kubectl get ingressclassesNAME CONTROLLER PARAMETERS AGE nginx k8s.io/ingress-nginx <none>31m#部署[root@master-1 ~]# kubectl apply -f nginx-route.yaml关键字段解释:
ingressClassName: nginx— 指定用 nginx Controller 处理host: nginx.liux.com— 只处理这个域名的请求path: /v1+pathType: Prefix— 前缀匹配,/v1/xxx全部命中rewrite-target: /— 去掉路径前缀再转发(/v1/hello→/hello)
4. 验证
# 查看 Ingress 状态kubectl get ingress# NAME CLASS HOSTS ADDRESS PORTS# my-ingress nginx app.example.com 192.168.1.x 80# 本地测试(加 hosts 后)curlhttp://nginx.liux.com/v1浏览器验证:在本地
/etc/hosts(Linux/Mac)或C:\Windows\System32\drivers\etc\hosts(Windows)中添加:
<NodeIP> nginx.liux.com浏览器访问
http://nginx.liux.com/v1即可看到对应服务的返回页面。curl 能通但浏览器不通时,优先检查本地 hosts 是否配置正确。
四、三个进阶配置
1.配置 TLS
spec:#https相关tls:-hosts:-nginx.liux.comsecretName:liux-tls- 没有现成证书?快速生成自签证书用于测试:
# 生成 365 天有效期的自签证书openssl req-x509-newkeyrsa:2048-nodes-keyouttls.key-outtls.crt-days365-subj"/CN=nginx.liux.com"# 证书存入 Secretkubectl create secret tls liux-tls--cert=tls.crt--key=tls.key# 确认 Secret 已创建kubectl get secret|grepliux-tls liux-tls kubernetes.io/tls254s
- 完整 HTTPS 实战示例
[root@master-1 nginx]# cat nginx-route.yaml# ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:my-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target:/spec:ingressClassName:nginxrules:-host:nginx.liux.comhttp:paths:-path:/v1pathType:Prefixbackend:service:name:nginx-service-v1port:number:80-path:/v2pathType:Prefixbackend:service:name:nginx-service-v2port:number:80#https相关tls:-hosts:-nginx.liux.comsecretName:liux-tlskubectl apply-fnginx-route.yaml# 查看 Ingress,确认同时暴露了 80 和 443 端口kubectl get ingress ingress-https浏览器访问https://nginx.liux.com/v1验证。如果浏览器提示证书不安全,属于自签证书正常现象,点击「继续访问」即可。
4.2 路径重写(最实用)
annotations:nginx.ingress.kubernetes.io/rewrite-target:/$2spec:rules:-host:nginx.liux.comhttp:paths:-path:/api(/|$)(.*)pathType:ImplementationSpecificbackend:service:name:user-serviceport:number:80效果:/api/user→ 转发到 user-service 的/user。
4.3 多域名分流
rules:-host:nginx.liux.comhttp:paths:-path:/pathType:Prefixbackend:service:name:nginx-serviceport:number:80-host:api.liux.comhttp:paths:-path:/pathType:Prefixbackend:service:name:api-serviceport:number:80一个 Ingress 搞定多域名,不用多开端口。
五、实战场景:微服务统一入口
假设有一个典型的 web 应用架构:前端页面(Nginx 静态资源)、后端 API(Java 服务)、管理后台(Admin),三个服务架构图如下:
用户请求 │ ├─ app.liux.com/ → frontend (前端页面) ├─ app.liux.com/api → api (后端接口) └─ admin.liux.com/ → admin (管理后台)5.1 部署服务
# 1. 部署前端kubectl create deployment frontend--image=nginx:1.22.1--replicas=2kubectl expose deployment frontend--port=80# 2. 部署后端 API 示例kubectl create deployment api--image=ealen/echo-server--replicas=1kubectl expose deployment api--port=80# 3. 部署管理后台kubectl create deployment admin--image=nginx:1.22.1--replicas=1kubectl expose deployment admin--port=805.2 配置Ingress规则 统一接入
# common-ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:common-ingressannotations:# /api 路径转发时去掉 /api 前缀nginx.ingress.kubernetes.io/rewrite-target:/$2spec:ingressClassName:nginxrules:# 主域名:前端 + API-host:app.liux.comhttp:paths:-path:/pathType:Prefixbackend:service:name:frontendport:number:80-path:/api(/|$)(.*)pathType:ImplementationSpecificbackend:service:name:apiport:number:80# 子域名:管理后台-host:admin.liux.comhttp:paths:-path:/pathType:Prefixbackend:service:name:adminport:number:80#部署kubectl apply-f common-ingress.yaml5.3 验证入口是否生效
# 检查 Ingress ADDRESS 是否分配kubectl get ingress common-ingress NAME CLASS HOSTS ADDRESS PORTS AGE common-ingress nginx app.liux.com,admin.liux.com10.0.0.1458019s# 本地测试(先配 hosts: nodeport app.example.com admin.example.com)curlhttp://app.liux.com/# → frontendcurlhttp://app.liux.com/api/users# → api(转发为 /users)# 重点验证路径重写# 请求 /api/users → api service 收到的是 /users# 或者直接看 Controller 日志kubectl logs-ningress-nginx deployment/ingress-nginx-controller--tail=10可以看到:三个微服务、两个域名、不同路径,全部通过一个 Ingress 入口搞定,不用暴露额外端口,不用单独配负载均衡器。
六、常见排坑
坑1:Ingress 配置了但访问 404
# 先确认 Controller 是否在跑kubectl get pods-ningress-nginx# 检查 Ingress 规则是否被正确读取kubectl describe ingress my-ingress90% 的情况是pathType写错了,Prefix和Exact行为完全不同。不确定就用Prefix。
坑2:rewrite-target 没生效
确认注解写的是nginx.ingress.kubernetes.io/rewrite-target,不是nginx.org/rewrite-target。两个是不同 Nginx 版本的前缀,混用不生效。
坑3:TLS 证书配了但不生效
# 检查 Secret 是否存在kubectl get secret app-tls-secret# 检查 Ingress 引用是否正确kubectl get ingress my-ingress-oyaml|grepsecretName另注意:Secret 必须在同一个 namespace下才能被 Ingress 引用。
坑4:多个 Controller 互相抢流量
如果集群里装了多个 Ingress Controller(比如同时装了 nginx 和 traefik),必须用ingressClassName明确指定,不然流量随机跑到某个 Controller 上。
七、总结
回到最初的问题:线上跑了 N 个微服务,每个都要对外暴露,怎么办?
Ingress 本质就是 K8s 的七层反向代理规则——你写规则,Controller 执行。一个入口搞定域名路由、路径分发、TLS 终止,不用给每个服务开端口、买负载均衡器。
1.速查回顾
| 环节 | 关键操作 | 一句话 |
|---|---|---|
| 选型 | Ingress(规则)+ Controller(执行)+ Class(标记) | 新手直接ingress-nginx |
| 部署 | kubectl apply -f deploy.yaml或helm install | 提前确认 80/443 端口不冲突 |
| 规则 | host+path+backend.service | 写 YAML 绑定域名和路径到 Service |
| TLS | tls.secretName引用 Secret | 测试用自签证书,生产用 cert-manager |
| rewrite | annotationrewrite-target | /api/xxx → /xxx,注意/$2写法 |
| 多域名 | 同一 Ingress 写多条rules | 不同 host 走不同 Service |
| 验证 | curl+kubectl logs看 Controller 日志 | 路径不对先用ealen/echo-server照镜子 |
2.落地清单
- 装 Controller:推荐
ingress-nginx,Helm 部署方便后续升级回滚 - 部署业务:确保 Pod + Service 就绪后再建 Ingress
- 写 Ingress 规则:绑定域名和路径,指定
ingressClassName - 配置 TLS:测试用
openssl自签 →kubectl create secret tls→ Ingress 引用 - 验证生效:配好 hosts,
curl测试 → 浏览器确认 → 查 Controller 日志 - 排坑优先级:404 → 查
pathType;rewrite 不生效 → 查 annotation 前缀;TLS 不生效 → 查 Secret 是否同 namespace
