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. 一个入口,多个服务 —— 1 个 LB 暴露 100 个服务
  2. 域名路由 —— api.example.com 路由到 A 服务,web.example.com 路由到 B 服务
  3. 路径路由 —— example.com/api 路由到 A,example.com/web 路由到 B
  4. 统一 HTTPS —— 一个地方配证书,所有服务自动启用 HTTPS
  5. 自动续期 —— cert-manager + Let's Encrypt 证书 90 天自动续

1.3 两个核心概念

概念含义
Ingress ResourceK8s 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:首次测试用 staginghttps://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"5080100,逐步放量。

八、常见问题排查

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 RolloutsFlagger
  • 超大规模(> 10000 个 Ingress)考虑 Gateway API(Ingress 的下一代)
  • 多集群场景考虑 Multi-cluster Ingress(如 submariner、istio multicluster)

把这套基础打牢,80% 的对外服务都能应付。遇到具体问题看 kubectl describe ingresskubectl logs -n ingress-nginx,基本都能定位。


参考资源

遇到 502 / 403 / 证书问题? 评论区贴 kubectl describe ingress 输出 + kubectl logs -n ingress-nginx --tail=30,我帮你分析。

Kubernetes #Ingress #nginx #HTTPS #运维 #DevOps

发表回复

后才能评论