← 返回首页 K8S Web / 文档 / Ingress 使用指南

☸️ Kubernetes Ingress 使用文档 从零部署七层流量入口:Controller 安装 → 路由规则 → HTTPS → 灰度发布 → 排障手册

01什么是 Ingress

Ingress 是 Kubernetes 中负责 七层(HTTP/HTTPS)流量入口 的资源对象,提供:

⚠️ 最常见的坑:Ingress 资源本身只是"一份规则声明",不处理任何流量。必须部署一个 Ingress Controller(如 ingress-nginx)watch 这些规则并生成实际的反向代理配置,否则创建 Ingress 毫无效果。
ServiceIngress
工作层级四层(TCP/UDP)七层(HTTP/HTTPS)
路由依据端口域名 / 路径 / Header / 权重
典型用途集群内服务发现与负载均衡统一外部入口、TLS 卸载、灰度

Ingress vs 传统独立 NGINX

💡 先说透本质:ingress-nginx 的 Controller 底层就是 nginx,它做的事是把 Ingress 规则"翻译"成 nginx.conf 并热加载。所以两者不是二选一的竞争关系,而是动态 vs 静态、云原生 vs 传统的分工。
维度传统 NGINX(独立部署)K8S Ingress(ingress-nginx)
配置方式手改 nginx.conf + reload,改错易滚挂kubectl apply 声明式,Controller 校验后自动热加载
服务发现手写 upstream IP,后端扩容要人跟改自动 watch Service/Endpoints,Pod 扩缩自动跟进
负载均衡upstream 轮询/权重,目标固定跨 Pod 自动分发,支持会话保持等策略
发布能力Lua/脚本拼灰度,门槛高注解开箱即用:金丝雀、重写、限流、CORS
多团队协作一台机器一份 conf,大家挤着改每个应用一个 Ingress 资源,命名空间隔离、可审计
适用后端任意 IP:端口(虚机/物理机/容器都行)只路由 K8S 集群内的 Service

为什么在 Ingress 前面再挡一层 NGINX(本环境实际架构)

┌──→ 虚机/物理机上的老应用(静态站点、传统服务) 用户 ──→ 外层NGINX ──┤ (主从+VIP) └──→ K8S NodePort :31588 ──→ ingress-nginx ──→ Pod
⚠️ 代价要心里有数:多一跳转发(毫秒级延迟)、多一层组件要运维、Host 头必须透传(丢了 Ingress 404)。小规模纯 K8S 场景直连 NodePort/LB 也够用;存在"老应用混合、需稳定对外、证书集中管理"需求时,这层才真正物有所值 —— 本环境三条全占,所以这么搭。

02⭐ 测试架构总览 —— 本环境实际链路

域名 k8s.ergesanbaishou.com 的完整流量路径:集群外的外层 NGINX(主从架构) 作为统一入口,再进集群内 Ingress → Service → Pod。下面是本站真实部署的架构图与全部配置文件。

👤 用户浏览器
http://k8s.ergesanbaishou.com · hosts/DNS 解析到外层 NGINX 机器(不是 K8S 节点!)
▼
HTTP :80
🏠 外层 NGINX(主从架构)
NGINX-Master(主)NGINX-Backup(备)
VIP 漂移 · 存活方接管 :80
配置:/etc/nginx/conf.d/k8s.ergesanbaishou.com.conf(必须以 .conf 结尾!)
职责:统一入口 · 按 server_name 分流 · 透传 Host(灵魂行)
▼
proxy_pass → 10.18.170.34:31588
★ proxy_set_header Host $host —— 透传域名,丢了 Ingress 必 404
🧱 K8S 节点 NodePort :31588
集群 HTTP 统一入口(kube-proxy 在每个节点都监听该端口)
▼
🚪 ingress-nginx-controller(Pod · ns: ingress-nginx)
watch Ingress 规则 → 动态生成 nginx 反代配置 → 按 Host 头分流
部署:ingress-nginx-deploy.yaml(v1.13.9)· 入口端口由 ingress.yaml 第二段固定为 31588
▼
命中规则 host: k8s.ergesanbaishou.com + path: /
🔌 Service k8s-web :8080(ns: ergesanbaishou)
另挂 NodePort 32001 —— 绕过整个 ingress 链路直连测试用(curl 节点:32001)
▼
selector app: k8s-web → Endpoints(Pod IP:80)
📦 Pod nginx:alpine × 2(Deployment: k8s-web)
容器内 /usr/share/nginx/html ← hostPath 节点目录 /data/git/web(本站所有 html 放这里)
改页面文件即时生效,无需重启 Pod

🧭 分层职责速查

层组件端口对应文件 / 对象
统一入口外层 NGINX 主从:80k8s.ergesanbaishou.com.conf(配置①)
集群入口ingress-nginx-controllerNodePort 31588ingress-nginx-deploy.yaml(配置④)+ ingress.yaml 第二段(配置③)
路由规则Ingress k8s-web—ingress.yaml 第一段(配置③)
服务发现Service k8s-web8080 / 32001k8s-web.yaml 第二段(配置②)
工作负载Deployment k8s-web ×2容器 :80k8s-web.yaml 第一段(配置②)
站点内容hostPath 目录—节点磁盘 /data/git/web/*.html

📄 配置① 外层 NGINX:/etc/nginx/conf.d/k8s.ergesanbaishou.com.conf

⚠️ 血泪教训:此文件名必须以 .conf 结尾 —— nginx 只 include conf.d/*.conf,无后缀文件静默不加载,nginx -t 也不报错,页面表现为"默认欢迎页"。改完必须 nginx -t && nginx -s reload。(文件头部注释是旧站点遗留,可顺手更新)
# =========================================================
# hello1.qingfeng.dev → K8s Ingress → nginx-hello(qingfeng空间)
# 外层nginx:80 → ingress-nginx NodePort:31080 → svc:8080 → pod:80
# (↑ 旧站点遗留注释;实际生效: k8s.ergesanbaishou.com → 31588)
# =========================================================
upstream k8s_ergesanbaishou {
    server 10.18.170.34:31588;     # ★ ingress-nginx 的 NodePort(HTTP 入口)
    keepalive 32;
}

server {
    listen 80;
    server_name k8s.ergesanbaishou.com;

    access_log /var/log/nginx/hello1.access.log;
    error_log  /var/log/nginx/hello1.error.log;

    location / {
        proxy_pass http://k8s_ergesanbaishou;

        # ★★★ 灵魂行:透传 Host,Ingress 全靠它分流,丢了必 404 ★★★
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 支持(可选,有需要就留着)
        proxy_http_version 1.1;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";

        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

📄 配置② 业务负载:k8s-web.yaml(Deployment + Service)

💡 两条验证路径:NodePort 32001 可绕过整条 ingress 链路直连 Pod(排障时先测它:curl http://10.18.170.34:32001/);hostPath 指向 Pod 所在节点 的 /data/git/web,改页面即时生效。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-web
  namespace: ergesanbaishou
  labels:
    app: k8s-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: k8s-web
  template:
    metadata:
      labels:
        app: k8s-web
    spec:
      # (可选)固定到单一节点,只需维护一份文件:
      # kubectl label node <节点名> k8s-web=true
      # nodeSelector:
      #   k8s-web: "true"
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
        # 用 tcpSocket 而非 httpGet:目录为空时 httpGet / 返回 403 会卡住 NotReady
        readinessProbe:
          tcpSocket: {port: 80}
          initialDelaySeconds: 3
        livenessProbe:
          tcpSocket: {port: 80}
          initialDelaySeconds: 10
        resources:
          requests: {cpu: 50m, memory: 64Mi}
          limits:   {cpu: 200m, memory: 128Mi}
        volumeMounts:
        - name: web
          mountPath: /usr/share/nginx/html    # 容器内 nginx 站点根目录 → 节点 /data/git/web
      volumes:
      - name: web
        hostPath:
          path: /data/git/web                 # ★ 节点上的真实目录
          type: DirectoryOrCreate             # 目录不存在时 kubelet 自动创建(空目录访问 / 会 403)
---
apiVersion: v1
kind: Service
metadata:
  name: k8s-web
  namespace: ergesanbaishou
  labels:
    app: k8s-web
spec:
  type: NodePort
  selector:
    app: k8s-web
  ports:
  - name: http
    port: 8080                  # ★ 线上 Service 统一开放 8080(集群内/Ingress 访问用它)
    targetPort: 80              # 容器内 nginx 仍监听 80,这个不用动
    nodePort: 32001    # 全集群唯一;直连测试用(curl 节点:32001)
# 域名/Ingress 访问方式见 ingress.yaml(入口端口 31588)

📄 配置③ 路由规则 + 集群入口:ingress.yaml(Ingress + controller Service)

💡 核心概念:Ingress 规则本身没有入口端口参数!31588 是 ingress-nginx-controller 的 NodePort,由本文件第二段的 Service 固定;Ingress 只声明"host + path → svc:port"。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: k8s-web
  namespace: ergesanbaishou
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"   # 暂无证书,先不强制跳 https
spec:
  ingressClassName: nginx          # 对应 ingress-nginx;其他 controller 名要改
  rules:
  - host: k8s.ergesanbaishou.com   # ★ 指定域名,只有 Host 为它的请求才命中本规则
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: k8s-web         # k8s-web.yaml 里部署的 Service
            port:
              number: 8080        # ← 对应 svc k8s-web 的 port(8080),不是 targetPort(80)

  # ---- 第二个站点(备用,当前注释未生效)----
  # 上线步骤:部署对应 Deployment+Service → 取消注释 → apply → 外层 nginx 加 server 块
  # - host: test2.ergesanbaishou.com
  #   http:
  #     paths:
  #     - path: /
  #       pathType: Prefix
  #       backend:
  #         service:
  #           name: test2-svc
  #           port:
  #             number: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/component: controller
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/component: controller
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
    nodePort: 31588               # ★★★ 集群 HTTP 入口,外层 nginx proxy_pass 指向它
  - name: https
    port: 443
    targetPort: 443
    protocol: TCP
    # https 的 nodePort 不指定 → 保留/自动分配(30255);上证书要固定时照抄 http 段

📄 配置④ ingress-nginx 安装清单:ingress-nginx-deploy.yaml(v1.13.9,点击展开全文)

💡 说明:官方 baremetal manifests 的实际部署版。含 Namespace、RBAC、ConfigMap、两个 Service(controller NodePort + admission ClusterIP)、Deployment、两个证书 Job、IngressClass、ValidatingWebhook。nodePort 31588 由配置③第二段补丁式固定。
▶ 点击展开 ingress-nginx-deploy.yaml 全文(约 630 行)
apiVersion: v1
kind: Namespace
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  name: ingress-nginx
---
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx
  namespace: ingress-nginx
---
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx
  namespace: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - configmaps
  - pods
  - secrets
  - endpoints
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - coordination.k8s.io
  resourceNames:
  - ingress-nginx-leader
  resources:
  - leases
  verbs:
  - get
  - update
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission
  namespace: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  - endpoints
  - nodes
  - pods
  - secrets
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingressclasses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission
rules:
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingwebhookconfigurations
  verbs:
  - get
  - update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-admission
subjects:
- kind: ServiceAccount
  name: ingress-nginx-admission
  namespace: ingress-nginx
---
apiVersion: v1
data: null
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-controller
  namespace: ingress-nginx
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: NodePort
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-controller-admission
  namespace: ingress-nginx
spec:
  ports:
  - appProtocol: https
    name: https-webhook
    port: 443
    targetPort: webhook
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  minReadySeconds: 0
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/name: ingress-nginx
  strategy:
    rollingUpdate:
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app.kubernetes.io/component: controller
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.13.9
    spec:
      automountServiceAccountToken: true
      containers:
      - args:
        - /nginx-ingress-controller
        - --election-id=ingress-nginx-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: LD_PRELOAD
          value: /usr/local/lib/libmimalloc.so
        image: registry.k8s.io/ingress-nginx/controller:v1.13.9@sha256:d803625b20d471400fdae9dce0fdd076c0361f8f7e1ecee0dcd2f84fa0d196f8
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              command:
              - /wait-shutdown
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: controller
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        - containerPort: 443
          name: https
          protocol: TCP
        - containerPort: 8443
          name: webhook
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          requests:
            cpu: 100m
            memory: 90Mi
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - ALL
          readOnlyRootFilesystem: false
          runAsGroup: 82
          runAsNonRoot: true
          runAsUser: 101
          seccompProfile:
            type: RuntimeDefault
        volumeMounts:
        - mountPath: /usr/local/certificates/
          name: webhook-cert
          readOnly: true
      dnsPolicy: ClusterFirst
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
      - name: webhook-cert
        secret:
          secretName: ingress-nginx-admission
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission-create
  namespace: ingress-nginx
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.13.9
      name: ingress-nginx-admission-create
    spec:
      automountServiceAccountToken: true
      containers:
      - args:
        - create
        - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
        - --namespace=$(POD_NAMESPACE)
        - --secret-name=ingress-nginx-admission
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.9@sha256:01038e7de14b78d702d2849c3aad72fd25903c4765af63cf16aa3398f5d5f2dd
        imagePullPolicy: IfNotPresent
        name: create
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsGroup: 65532
          runAsNonRoot: true
          runAsUser: 65532
          seccompProfile:
            type: RuntimeDefault
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      serviceAccountName: ingress-nginx-admission
  ttlSecondsAfterFinished: 0
---
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission-patch
  namespace: ingress-nginx
spec:
  template:
    metadata:
      labels:
        app.kubernetes.io/component: admission-webhook
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
        app.kubernetes.io/version: 1.13.9
      name: ingress-nginx-admission-patch
    spec:
      automountServiceAccountToken: true
      containers:
      - args:
        - patch
        - --webhook-name=ingress-nginx-admission
        - --namespace=$(POD_NAMESPACE)
        - --patch-mutating=false
        - --secret-name=ingress-nginx-admission
        - --patch-failure-policy=Fail
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.9@sha256:01038e7de14b78d702d2849c3aad72fd25903c4765af63cf16aa3398f5d5f2dd
        imagePullPolicy: IfNotPresent
        name: patch
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsGroup: 65532
          runAsNonRoot: true
          runAsUser: 65532
          seccompProfile:
            type: RuntimeDefault
      nodeSelector:
        kubernetes.io/os: linux
      restartPolicy: OnFailure
      serviceAccountName: ingress-nginx-admission
  ttlSecondsAfterFinished: 0
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: nginx
spec:
  controller: k8s.io/ingress-nginx
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  labels:
    app.kubernetes.io/component: admission-webhook
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/version: 1.13.9
  name: ingress-nginx-admission
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: ingress-nginx-controller-admission
      namespace: ingress-nginx
      path: /networking/v1/ingresses
      port: 443
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: validate.nginx.ingress.kubernetes.io
  rules:
  - apiGroups:
    - networking.k8s.io
    apiVersions:
    - v1
    operations:
    - CREATE
    - UPDATE
    resources:
    - ingresses
  sideEffects: None

🔥 踩坑实录(本环境搭建过程真实案例)

症状根因解法
页面显示 nginx 默认欢迎页conf 文件名没有 .conf 后缀 → include conf.d/*.conf 静默不加载,nginx -t 也不报错改名加后缀 + nginx -t && nginx -s reload
默认欢迎页(另一种)hosts 把域名解析到了错误机器(如 K8S 节点,上面另有一个 nginx)hosts 必须指向外层 NGINX 机器的 IP
直连 31588 返回 404裸 curl IP:31588 没带 Host 头 → Ingress 按 host 路由匹配不上(正常行为,非故障)curl -H "Host: k8s.ergesanbaishou.com" http://10.18.170.34:31588/
外层转发放大后 404外层 nginx 没写 proxy_set_header Host $host → 域名没透传给 controller补上灵魂行并 reload(见配置①)
apply 报 namespaces "ingress-nginx" not found先 apply 了 Ingress,但 controller 还没安装先装配置④,再 apply 配置③
apply 报 nodePort 32001 冲突旧 test1 的 Service 还占着 32001kubectl delete svc test1-ergesanbaishou -n ergesanbaishou

🩺 分层排障命令(由外向内,逐层验证)

# ① Pod 层(最内):容器活着吗
kubectl get pods -n ergesanbaishou -o wide

# ② Service 层:绕过 ingress 直测(NodePort 32001)
curl -v http://10.18.170.34:32001/
kubectl get ep -n ergesanbaishou k8s-web          # endpoints 必须非空

# ③ Controller 层:带 Host 头直测集群入口(NodePort 31588)
curl -v -H "Host: k8s.ergesanbaishou.com" http://10.18.170.34:31588/
kubectl get ingress -n ergesanbaishou             # ADDRESS 列应有 IP
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=30

# ④ 外层 NGINX 层:在 nginx 机器上自测
curl -H "Host: k8s.ergesanbaishou.com" http://127.0.0.1/
nginx -T 2>/dev/null | grep -n "k8s.ergesanbaishou"   # 确认配置已加载
tail -f /var/log/nginx/hello1.error.log

# ⑤ 本机层:hosts 解析到哪
ping k8s.ergesanbaishou.com

03✅ 线上架构(fke · 2026-10 实测)

✅ 已实测:线上 fke 集群同样有 ingress-nginx —— 但装在 kube-infra ns(不是默认的 ingress-nginx),NodePort 80:30080 / 443:30443;另有 Istio 网关并存(NodePort 30538/30539)。链路与测试同构:外层 NGINX 主备 VIP → ingress-nginx NodePort → Service → Pod。
# 实测命令(★教训:按 -n ingress-nginx 查到空 ≠ 没有,要 -A 跨 ns 搜)
sudo kubectl get svc -A | grep ingress
# kube-infra     nginx-ingress-nginx-controller  NodePort  10.101.184.130  80:30080/TCP,443:30443/TCP
# istio-system   istio-ingressgateway            NodePort  10.100.189.102  15021:30542/TCP,80:30538/TCP,443:30539/TCP

与测试环境差异速览:① ns 不同(kube-infra vs ingress-nginx);② NodePort 不同(30080/30443 vs 测试 31588);③ 双网关并存(ingress-nginx + Istio,配套 admission/metrics/defaultbackend 齐全)。外层 NGINX 的 upstream 端口按此对应修改即可。完整线上架构见 线上 K8S 集群架构。

04流量链路(完整架构图 + 逐层查询命令)

客户端 ──→ LB / NodePort ──→ Ingress Controller(Pod, nginx) │ 读取 Ingress 规则 ▼ ┌─ host: api.example.com + /foo ──→ foo-svc ──→ Pods ├─ host: api.example.com + /bar ──→ bar-svc ──→ Pods └─ 其他 ──→ defaultBackend(404)

Controller 本身也是一个 Pod,需要先通过 NodePort / LoadBalancer / hostNetwork 之一把自身暴露到集群外。

浏览器 ── http://k8s.ergesanbaishou.com ──→ 外层 NGINX(主从 + VIP)          ← 集群外
                 │  proxy_pass 10.18.170.34:31588 + ★透传 Host 头(丢了必 404)
                 ▼
 ═══════════════════════ K8S 集群边界(节点 :31588, kube-proxy 监听) ═══════════════════════
                 ▼
 ┌─ ns: ingress-nginx ─────────────────────────────────┐
 │ ingress-nginx-controller-xxx (Pod, nginx 进程)      │ ← 查①:controller 活着吗
 │    watch 全集群 Ingress 规则 → 动态生成 nginx.conf   │
 └────────────────────┬────────────────────────────────┘
                      │ 匹配 Host: k8s.ergesanbaishou.com
                      ▼
        Ingress 资源(路由规则)                          ← 查②:域名 → 哪个 svc
                      │ 转发到 svc:80
                      ▼
 ┌─ ns: web ───────────────────────────────────────────┐
 │ Service: k8s-web (ClusterIP 10.x.x.x:80)           │ ← 查③:svc + Endpoints
 │    └─ Endpoints: [10.244.x.x]  (后端 Pod IP 列表)  │
 │         ▼                                           │
 │ Pod: k8s-web-xxxx (nginx:alpine, 提供静态页)        │ ← 查④:Pod 状态
 │ (Deployment 副本控制 / 滚动更新)                    │
 └─────────────────────────────────────────────────────┘
# ★ 逐层排查命令(502/404 从上往下捋,一层一条)
kubectl get pods -n ingress-nginx -o wide     # ① controller Pod 状态
kubectl get svc -n ingress-nginx              #    80/443 映射的 nodePort(如 31588)
kubectl get ingress -A                        # ② 全集群路由规则清单(-A 跨所有 ns)
kubectl describe ingress <名字> -n web        #    细节:Host/Path → Backend(svc:port)
kubectl get svc -n web                        # ③ Service
kubectl get ep -n web                         #    ★ Endpoints 为 <none> = svc 后面没 Pod → 503!
kubectl get pods -n web -o wide               # ④ Pod 状态(真正干活的)
kubectl get deploy -n web                     #    Pod 的管理者(副本数/镜像)
kubectl exec -n web <pod名> -- ss -lntp       # ⑤ 终点:容器里 nginx 在监听吗

05安装 Ingress Controller(ingress-nginx)

方式一:官方 manifests(通用,自建集群常用)

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/baremetal/deploy.yaml

# 验证:Pod Running,且自动创建 ingress-nginx-controller Service(NodePort)
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

方式二:Helm

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace

方式三:云厂商

平台推荐方案
AWSAWS Load Balancer Controller(ALB Ingress)
GCPGCE Ingress(默认支持)
阿里云ALB Ingress / ACK nginx-ingress
💡 提示:注意区分两个项目 —— kubernetes/ingress-nginx(社区官方,本文注解以此为准)与 nginxinc/kubernetes-ingress(NGINX Inc 维护),注解与能力不完全兼容。

06第一个 Ingress:域名 + 路径路由

保存为 ingress-demo.yaml 后 kubectl apply -f ingress-demo.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-ingress
  annotations:
    # 把 /foo/xxx 转发到后端时重写为 /xxx
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx            # 由哪个 Controller 处理
  rules:
    - host: api.example.com          # 省略 host 则匹配所有域名
      http:
        paths:
          - path: /foo(/|$)(.*)      # 正则捕获组配合 $2 使用
            pathType: ImplementationSpecific
            backend:
              service:
                name: foo-svc
                port:
                  number: 80
          - path: /bar
            pathType: Prefix
            backend:
              service:
                name: bar-svc
                port:
                  number: 8080
  defaultBackend:                    # 兜底后端(都不匹配时返回)
    service:
      name: default-svc
      port:
        number: 80

本地没有域名解析时,用 curl -H "Host: api.example.com" http://<INGRESS_IP>/foo/xx 模拟测试。