Ingress:Kubernetes外部访问入口配置指南
Kubernetes Ingress 完全指南:从零开始部署外部访问入口
摘要:Ingress 是 K8s 集群对外提供 HTTP/HTTPS 服务的"大门"。本文从零开始讲解 Ingress 是什么、为什么需要它、与 Service 的区别、nginx-ingress-controller 的安装与配置、域名路由、Path 规则、HTTPS/TLS 配置(含 cert-manager 自动续期),最后给出常见坑点和生产 Checklist。每个术语都会先解释,跟着一遍就能跑通。
适用版本:Kubernetes 1.28+、ingress-nginx 1.9+、cert-manager 1.15+
零、读前必看
0.1 适合谁
- ✅ K8s 初学者,刚接触 Service 概念
- ✅ 需要把内部服务暴露给外网用户访问
- ✅ 想给多个服务配置统一域名
- ❌ 生产大流量场景(生产看 Nginx Ingress 生产调优指南)
0.2 你需要准备什么
| 项目 | 要求 |
|---|---|
| K8s 集群 | 1.24+(建议 1.28+),至少 1 个 Master + 1 个 Worker |
| kubectl | 已配置好 ~/.kube/config |
| helm(可选) | 3.x(用 helm 装 nginx-ingress 更方便) |
| 公网 IP | 用来对外暴露服务(云服务器有弹性 IP / NAT 网关) |
| 域名 | 推荐(自签名证书只能本机测试) |
一、为什么需要 Ingress
1.1 没 Ingress 之前怎么暴露服务
K8s 里默认的 Service 有几种类型:
| Service 类型 | 暴露方式 | 适合场景 |
|---|---|---|
| ClusterIP | 集群内部 | 默认 |
| NodePort | 每台机器开 30000-32767 端口 | 开发测试 |
| LoadBalancer | 云厂商创建 LB(每个 Service 一个) | 生产单服务 |
问题:假设你有 20 个微服务,每个都用 LoadBalancer,就要创建 20 个云 LB,贵且难管理。
1.2 Ingress 解决什么问题
Ingress = 集群级别的"统一入口"。它做两件事:
┌─────────────────────────┐
互联网用户 ───> │ Ingress Controller │
│ (nginx-ingress) │
└────────────┬────────────┘
│
根据域名 + 路径 智能路由
│
┌─────────────┬───────────┼───────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
api-service web-service admin-svc static-svc grafana-svc
(cluster IP) (cluster IP) (cluster IP)(cluster IP) (cluster IP)
关键优势:
- 一个入口,多个服务 —— 1 个 LB 暴露 100 个服务
- 域名路由 ——
api.example.com路由到 A 服务,web.example.com路由到 B 服务 - 路径路由 ——
example.com/api路由到 A,example.com/web路由到 B - 统一 HTTPS —— 一个地方配证书,所有服务自动启用 HTTPS
- 自动续期 —— cert-manager + Let's Encrypt 证书 90 天自动续
1.3 两个核心概念
| 概念 | 含义 |
|---|---|
| Ingress Resource | K8s API 对象(YAML),描述"哪个域名走哪个服务" |
| Ingress Controller | 真正干活的组件(nginx、traefik、HAProxy 等),监听 Ingress 资源变化并配置反向代理 |
小白解释:Ingress Resource 是"图纸",Ingress Controller 是"建筑工人"。你写好图纸(apply YAML),Controller 看到就去配置 nginx。
本文用 nginx-ingress(社区最广泛使用的 Controller)。
二、安装 nginx-ingress Controller
2.1 方式 A:kubectl apply(最简单)
# 安装最新稳定版(v1.9.4 截至 2024-09 是稳定版,建议固定版本)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.4/deploy/static/provider/cloud/deploy.yaml
# 查看安装进度
kubectl get pods -n ingress-nginx -w
# 看到 controller-xxx Running 就装好了
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-7c4dbf8d8-xxxxx 1/1 Running 0 2m
2.2 方式 B: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 \
--set controller.replicaCount=3 \
--set controller.resources.requests.cpu=200m \
--set controller.resources.requests.memory=256Mi \
--set controller.service.type=LoadBalancer
# 查看
kubectl get svc -n ingress-nginx
2.3 安装后验证
# 1. 看 Service 状态
kubectl get svc -n ingress-nginx
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# ingress-nginx-controller LoadBalancer 10.96.100.50 <pending 或 IP> 80:31234/TCP,443:31235/TCP
# 2. 拿到外部 IP(云厂商会自动分配,本地用 kind/metallb 需要额外配置)
kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
# 输出类似:203.0.113.42
# 3. 测试 Controller 是否工作
curl -I http://<外部 IP>
# 应该返回 404(正常,因为还没配 Ingress Resource)
坑点 1:本地用 kind / minikube 测试时
EXTERNAL-IP会是<pending>,因为没有云 LB。需要装 MetalLB 模拟 LB。
三、第一个 Ingress:从 Demo 开始
3.1 部署 demo 应用
# 1. 部署 backend Pod
cat > demo-app.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-app
spec:
replicas: 2
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 80
EOF
kubectl apply -f demo-app.yaml
kubectl get pods,svc
3.2 创建 Ingress Resource
cat > demo-ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: hello.example.com # 域名
http:
paths:
- path: / # 路径
pathType: Prefix # 路径匹配规则
backend:
service:
name: hello-service # 路由到哪个 Service
port:
number: 80
EOF
kubectl apply -f demo-ingress.yaml
# 查看
kubectl get ingress
# NAME CLASS HOSTS ADDRESS PORTS AGE
# hello-ingress nginx hello.example.com 203.0.113.42 80 1m
3.3 测试访问
# 1. 模拟域名解析(本地测试时)
echo "203.0.113.42 hello.example.com" | sudo tee -a /etc/hosts
# 2. 测试
curl http://hello.example.com
# 应该返回 nginx 默认页("Server address: 10.244.x.x")
# 3. 看 Ingress Controller 的访问日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=10
四、Ingress 规则详解
4.1 pathType 三种类型
| 类型 | 含义 | 示例 |
|---|---|---|
Prefix | 前缀匹配(推荐) | /api 匹配 /api、/api/v1 |
Exact | 精确匹配(k8s 1.22+) | /api 只匹配 /api 不匹配 /api/v1 |
ImplementationSpecific | 由 Controller 决定(已废弃) | 默认等同于 Prefix,但 v1 API 不推荐 |
# 推荐:pathType 用 Prefix 或 Exact
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix # /v1、/v1/users、/v1/orders 都匹配
backend:
service:
name: api-v1
port:
number: 80
- path: /v2
pathType: Prefix # /v2、/v2/users、/v2/orders 都匹配
backend:
service:
name: api-v2
port:
number: 80
- path: /healthz
pathType: Exact # 只匹配 /healthz,不匹配 /healthz/foo
backend:
service:
name: health-check
port:
number: 80
4.2 多域名配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
ingressClassName: nginx
rules:
# 域名 1 走 api 服务
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
# 域名 2 走 web 服务
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
# 域名 3 走 admin 服务
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 80
4.3 同一域名多路径
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
annotations:
# 自动加 X-Forwarded-Prefix 头,标记原始路径
nginx.ingress.kubernetes.io/x-forwarded-prefix: "/api"
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /web
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- path: /static
pathType: Prefix
backend:
service:
name: static-service
port:
number: 80
五、HTTPS / TLS 配置
5.1 方式 A:手动 TLS 证书(自签名 / 商业证书)
5.1.1 生成自签名证书(仅测试)
# 生成自签名证书(CN 是你的域名)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=hello.example.com"
# 创建 K8s TLS Secret
kubectl create secret tls hello-tls --cert=tls.crt --key=tls.key
# 验证
kubectl get secret hello-tls
5.1.2 创建 TLS Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- hello.example.com
secretName: hello-tls # 引用上面创建的 Secret
rules:
- host: hello.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-service
port:
number: 80
5.1.3 测试
# 用 curl 测(-k 跳过证书校验)
curl -k https://hello.example.com
# 看证书详情
openssl s_client -connect hello.example.com:443 -servername hello.example.com < /dev/null | openssl x509 -noout -text
5.2 方式 B:cert-manager + Let's Encrypt(生产推荐)
痛点:商业证书每年手动续期很麻烦,Let's Encrypt 证书 90 天到期。cert-manager 自动申请 + 续期,0 运维。
5.2.1 安装 cert-manager
# 推荐用 Helm
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--version v1.15.0 \
--set installCRDs=true
# 验证
kubectl get pods -n cert-manager
# 应该看到 cert-manager、webhook、cainjector 三个 Pod 都 Running
5.2.2 配置 ClusterIssuer(证书颁发机构)
cat > letsencrypt-prod.yaml << 'EOF'
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Let's Encrypt 生产环境(**不是** staging)
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com # 你的邮箱(接收过期提醒)
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
# HTTP-01 验证(最常用,需要 80 端口可访问)
- http01:
ingress:
ingressClassName: nginx
EOF
kubectl apply -f letsencrypt-prod.yaml
# 验证
kubectl get clusterissuer
# NAME READY AGE
# letsencrypt-prod True 10s
坑点 2:首次测试用 staging(
https://acme-staging-v02.api.letsencrypt.org/directory),免得申请失败次数过多被 Let's Encrypt 限流。
5.2.3 自动签发证书
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auto-tls-ingress
annotations:
# 关键注解:让 cert-manager 自动签证书
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
- www.example.com
secretName: example-tls # cert-manager 会自动创建
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-service
port:
number: 80
- host: www.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-service
port:
number: 80
5.2.4 验证证书
# 1. 看证书状态(应该 Ready=True)
kubectl get certificate
# NAME READY SECRET AGE
# example-tls True example-tls 2m
# 2. 测 HTTPS
curl -I https://example.com
# HTTP/2 200
# 3. 看证书详情
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | head
cert-manager 自动续期:证书到期前 30 天自动续期,完全免运维。
六、常用注解(Annotations)
nginx-ingress 通过注解调整行为:
metadata:
annotations:
# 强制 HTTPS
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# 上传文件大小限制
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
# 请求超时
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
# 限流(按 IP)
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "50"
# 跨域(CORS)
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization,Content-Type"
# WebSocket 支持
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
# 后端协议(gRPC)
nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
# 负载均衡算法
nginx.ingress.kubernetes.io/load-balance: "round_robin"
# 可选: round_robin, least_conn, ip_hash, random
七、灰度发布(金丝雀)
Ingress 可以做按权重的金丝雀:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: canary-ingress
annotations:
# 默认主流量
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 80
---
# 金丝雀 Ingress(**单独**的 Ingress,用 canary 注解)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: canary-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10% 流量给 v2
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
调整比例:改 canary-weight: "10" 到 50、80、100,逐步放量。
八、常见问题排查
8.1 502 Bad Gateway
症状:访问 Ingress 报 502。
排查:
# 1. 看 Ingress 后端服务状态
kubectl get svc hello-service
# 看 ENDPOINTS 是否有 IP(没有说明 Selector 不匹配)
kubectl get endpoints hello-service
# 2. 看 Controller 日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=50
# 3. 看 Service 端口是否正确
kubectl describe svc hello-service
常见原因:Service targetPort 配错(Pod 实际监听 8080,你配成 80)。
8.2 404 Not Found
症状:访问报 404。
排查:
# 看 Ingress 的 host 配置
kubectl describe ingress hello-ingress
# 看 Rules: Host 是否匹配你的请求域名
# 默认 Ingress Controller 是 404,因为没匹配任何 Ingress
常见原因:域名没配或 DNS 没解析。
8.3 证书不生效
# 看 certificate 状态
kubectl describe certificate example-tls
# 看最近事件
kubectl get events --sort-by='.lastTimestamp' | tail -20
# 看 cert-manager 日志
kubectl logs -n cert-manager -l app=cert-manager --tail=50
常见原因:80 端口(HTTP-01 验证需要)被防火墙挡住。
8.4 TLS: bad certificate
自签名证书测试时:
# 用 -k 跳过证书校验
curl -k https://hello.example.com
生产:必须用真实证书(Let's Encrypt / 商业)。
九、生产 Checklist
部署 Ingress 到生产前,逐项打勾:
架构:
- [ ] Controller ≥ 2 副本(高可用)
- [ ] Controller 反亲和性打散到不同节点
- [ ] Controller 资源 requests/limits 配齐
- [ ] PodDisruptionBudget 保护 Controller
TLS:
- [ ] 所有对外 Ingress 都启用 HTTPS(
ssl-redirect: "true") - [ ] 用 cert-manager 自动签证书
- [ ] 证书续期监控(cert-manager 告警)
- [ ] 强制 TLS 1.2+(nginx 配置)
可观测:
- [ ] Prometheus 抓 Controller 指标
- [ ] Grafana 告警规则(4xx/5xx/P99 延迟)
- [ ] 访问日志聚合到 ES/Loki
安全:
- [ ] 不暴露 Controller 的 admin 端口(默认 18080)
- [ ] 后端 Service 用 ClusterIP(不要 NodePort/LoadBalancer)
- [ ] rate-limit 注解防刷
- [ ] Web Application Firewall(可选:ModSecurity)
十、写在最后
Ingress 是 K8s 暴露服务的"标准姿势",但不是银弹:
- 单域名单路径走 Ingress 没问题
- 复杂的灰度、A/B 测试建议用 Argo Rollouts 或 Flagger
- 超大规模(> 10000 个 Ingress)考虑 Gateway API(Ingress 的下一代)
- 多集群场景考虑 Multi-cluster Ingress(如 submariner、istio multicluster)
把这套基础打牢,80% 的对外服务都能应付。遇到具体问题看 kubectl describe ingress 和 kubectl logs -n ingress-nginx,基本都能定位。
参考资源:
- 官方文档:https://kubernetes.io/docs/concepts/services-networking/ingress/
- nginx-ingress 注解大全:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
- cert-manager 文档:https://cert-manager.io/docs/
- Gateway API(下一代):https://gateway-api.sigs.k8s.io/
遇到 502 / 403 / 证书问题? 评论区贴 kubectl describe ingress 输出 + kubectl logs -n ingress-nginx --tail=30,我帮你分析。






