Skip to content

发版后接口全线 404

01 前置理论与核心机制

1. Ingress 404 的底层逻辑

  • 核心定界:在 K8s Ingress 体系中,出现 404 报错通常说明:七层网关已正常接收到流量,但在其路由表中未匹配到对应的转发规则。
  • 404 与 503/502 的本质区别:
  • 404 Not Found:路由规则未匹配(Path、Host 或 IngressClass 配置有误)。
  • 503 Service Unavailable:路由规则已匹配,但后端无可用 Pod,或 Service 的 Label Selector 未关联到 Pod,导致 Endpoints 列表为空。
  • 502 Bad Gateway:路由规则已匹配且后端 Pod 存在,但容器业务端口未响应、连接被拒绝或进程异常。

2. 区分网关 404 与业务代码 404

  • 检查 Upstream (上游):排查 404 需优先查看 Ingress Controller 的访问日志。如果日志中的 upstream 字段指向 default-backend,说明请求未匹配到任何 Ingress 规则,由网关默认后端返回了 404;如果 upstream 已显示为具体的业务 Service 名称或 Pod IP,且响应体为业务自定义的 JSON 格式,则说明网关转发成功,404 是由后端业务代码抛出的(如接口路径变更或资源不存在)。

02 故障排查与恢复闭环 SOP

当新版本发布后,Pod 和 Service 状态均正常,但外部访问接口全线报 404 时,按以下七层网关排查链路执行:

步骤一:查看 Ingress Controller 入口日志

从外向内定界,确认流量中断位置。直接查看网关组件(如 ingress-nginx)的访问日志:

# 获取 nginx-controller 的最新 10 条日志
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=10
  • 定性判断:只要在网关日志中看到对应客户端请求的 404 记录,即可确认外部四层网络(防火墙、负载均衡、节点端口)完全正常,排查重点直接锁定在七层 Ingress 路由配置。同时观察该条日志的 upstream 字段是否为 default-backend。

步骤二:比对 Ingress 规则与实际请求 Path

查看故障业务对应的 Ingress 详细配置:

kubectl describe ingress user-center-ingress
  • 观测点:重点核对 Rules 下的 Path 配置与客户端实际请求的 URI。例如实际请求路径为 /api/v1,但 Ingress 规则仅配置了精确匹配的 /api,路径不一致会导致网关直接返回 404。

步骤三:核查 Host 与 IngressClass (常见排查点)

若 Path 配置完全一致但依然报 404,需立即核对另外两项关键配置: - Host 域名:客户端请求携带的 Host 请求头(如 api.prod-center.com)必须与 Ingress YAML 中的 host 字段严格一致。 - IngressClass:在 K8s 1.18+ 版本中,需显式声明 ingressClassName: nginx。若未指定且集群未配置默认的 IngressClass,Ingress Controller 将忽略该规则。

步骤四:动态修补与验证

明确原因后,修正路由配置。在应急恢复阶段,可使用 patch 命令动态修改配置以快速止损(验证恢复后需同步更新代码仓库中的 YAML 文件,避免下次发版被覆盖):

# 动态将路由 Path 更新为正确的 /api
kubectl patch ingress user-center-ingress --type='json' -p='[{"op": "replace", "path": "/spec/rules/0/http/paths/0/path", "value":"/api"}]'
配置更新后,Ingress Controller 会监听到变更并自动生成新路由表执行热加载(Hot Reload)。随后重新发起请求验证:
# 携带正确的 Host 请求头访问网关暴露的端口
curl -I -H "Host: api.prod-center.com" [http://192.168.0.84:30731/api](http://192.168.0.84:30731/api)

  • 验证点:确认返回 HTTP/1.1 200 OK,故障恢复。

03 真实生产故障现象与案例库

案例 1:发版未同步更新网关配置 (Path Mismatch)

  • 故障现象:后端研发在 Spring Boot 中将接口上下文路径从 /api 调整为 /api/v1,发版后外部调用全线返回 404。
  • 排查与处置:发版时仅更新了业务镜像,未同步修改 K8s 的 Ingress 路由规则,导致网关与后端路径不匹配。更新 Ingress YAML 中的 Path 配置后恢复正常。

案例 2:绕过域名直接通过 IP 访问导致的 404

  • 故障现象:测试人员为规避本地 DNS 缓存,直接在浏览器中使用 http://:/api 访问接口,返回 404;但通过正式域名访问正常。
  • 排查与处置:Ingress 规则中限定了 host: api.prod-center.com。直接使用 IP 访问时,HTTP 报文的 Host 头为 IP 地址,无法匹配该域名对应的虚拟主机路由树,请求被转发至 default-backend。测试时需使用 curl -H "Host: xxx" 或通过配置本地 hosts 文件绑定域名访问。

案例 3:缺失 IngressClass 导致规则未被加载

  • 故障现象:集群内同时部署了 Ingress-Nginx 和 Traefik 两套网关,新发布的业务 Ingress 资源创建成功,但访问始终报 404。
  • 排查与处置:业务 Ingress YAML 中未声明 ingressClassName 字段,且集群未设置默认 IngressClass,导致两套网关 Controller 均未识别并加载该路由规则。在 YAML 中显式补充 ingressClassName: nginx 后恢复正常访问。

04 生产排障速查表(七层网关 404 故障)

排查层级执行命令 / 观测点异常定性处置动作01 网络连通kubectl logs -n ingress-nginx ...网关日志中存在该请求的 404 记录确认四层网络连通,转入七层路由排查02 错误定界检查日志的 upstream 字段值为 default-backend确认网关层路由未匹配,排查 Ingress 配置03 错误定界检查日志的 upstream 字段值为 <业务Service名> 或 Pod IP确认网关转发成功,排查后端业务代码路由04 规则比对kubectl describe ingress <名>Rules -> Path 与实际请求 URI 不符修正 Ingress Path 与后端服务接口路径保持一致05 域名比对检查 Host 字段与客户端请求头客户端直接使用 IP 发起请求配置本地 hosts 解析或在请求中显式传递 Host 头06 类名比对检查 ingressClassName 字段字段缺失且集群无默认 IngressClass显式指定目标网关的 ingressClassName

05 面试应答参考话术

面试官问:“K8s 新版本刚发布,Pod 是 Running 状态,Service 也是正常的,但是外部调用接口突然全线报 404,你怎么排查?”

可以这样回答:在 K8s 体系中,如果外部调用报 404,通常说明七层网关已经收到了流量,但在路由表中没有匹配到对应的转发规则。如果是后端 Pod 异常或 Service 标签未关联上,网关通常会返回 502 或 503,而 404 核心指向的是路由匹配问题。我会按照从外向内的顺序分三步排查:第一步:查看网关日志定界。

首先查看 Ingress Controller(如 ingress-nginx)的访问日志。只要日志中有该请求的 404 记录,就说明四层网络是通的。同时重点观察日志里的 upstream 字段:如果显示为 default-backend,说明请求确实在网关层没匹配上 Ingress 规则;如果 upstream 已经指向了具体的业务 Service,说明网关转发成功,404 是后端业务容器自己返回的,此时直接排查业务代码或配置。第二步:比对 Path 路由规则。

确认是网关层 404 后,执行 kubectl describe ingress 查看路由表,重点比对 Rules 中的 Path 与实际请求的 URI。生产环境中最常见的原因是研发发版时修改了接口路径前缀(例如增加了 /v1)或误改了 pathType 匹配模式,但漏改了对应的 Ingress 配置。第三步:核查 Host 与 IngressClass。

如果 Path 路径无误,则继续核对客户端请求携带的 Host 域名是否与 YAML 中定义的一致(例如排查调用方是否直接使用了 IP 访问),同时检查 Ingress 资源是否漏配了 ingressClassName 导致规则未被网关控制器加载。定位到具体原因后,可先使用 kubectl patch 动态修正 Ingress 规则,触发网关热加载使线上接口立即恢复 200 OK,随后再同步更新 Git 仓库中的 YAML 文件完成闭环。

06 面试高频追问与应答预案

追问 1:修改了 Ingress 配置后,网关需要重启才能生效吗?会导致现有存量流量中断吗?

  • 回答要点:
  • 不需要重启容器,也不会中断存量流量。 Ingress Controller 通过 Watch 机制实时监听 K8s APIServer 的资源变更事件。
  • 当检测到 Ingress 规则更新后,Controller 会在容器内重新渲染生成最新的 nginx.conf 配置文件,并执行 nginx -s reload 触发平滑重载。旧的 Nginx Worker 进程会在处理完存量连接后安全退出,新进入的连接则直接由加载了新配置的 Worker 进程接管,对线上业务无感知。

追问 2:为什么生产环境通常不直接使用 NodePort 向外暴露服务,而要引入一层 Ingress?

  • 回答要点:
  • 端口维护成本高:NodePort 会在集群每个节点上占用一个宿主机物理端口(默认范围 30000-32767)。当微服务数量较多时,直接对外暴露大量高危端口不仅管理复杂,还会增加安全攻击面。
  • 缺乏七层路由与治理能力:NodePort 仅工作在四层(TCP/UDP),无法基于 HTTP 域名(Host)和请求路径(Path)进行细粒度的路由分发,也无法在入口层统一处理 TLS/HTTPS 证书卸载、限流及安全防护。引入 Ingress 可以将集群收敛为统一的七层流量网关入口。

07 Ingress 路由配置自动化巡检脚本(check_ingress_routes.sh)

该脚本用于快速扫描指定 Namespace 下所有 Ingress 资源的控制器挂载状态、关联域名及路由映射表,排查未生效的路由规则。

#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: check_ingress_routes.sh
# 功能描述: K8s 集群 Ingress 路由规则扫描与挂载状态巡检 (纯只读)
# 使用方式: ./check_ingress_routes.sh [namespace]
# ==============================================================================

set -o pipefail

NAMESPACE=${1:-"default"}

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'

echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE}           K8S Ingress 七层路由表自动诊断与巡检                 ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${YELLOW}[INFO] 正在扫描 Namespace: ${NAMESPACE}${PLAIN}\n"

# 1. 检查是否存在 Ingress 资源
INGRESS_LIST=$(kubectl get ingress -n "$NAMESPACE" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null)

if [ -z "$INGRESS_LIST" ]; then
    echo -e "${GREEN}[OK] 命名空间 ${NAMESPACE} 下暂无 Ingress 资源。${PLAIN}"
    exit 0
fi

# 2. 遍历并解析 Ingress 配置
RISK_COUNT=0

for ing in $INGRESS_LIST; do
    echo -e "${BLUE}----------------------------------------------------------------${PLAIN}"
    echo -e " 路由资源名称: ${GREEN}${ing}${PLAIN}"

    # 提取 IngressClass (兼容 spec.ingressClassName 与旧版 annotation)
    ING_CLASS=$(kubectl get ingress "$ing" -n "$NAMESPACE" -o jsonpath='{.spec.ingressClassName}')
    if [ -z "$ING_CLASS" ]; then
        ING_CLASS=$(kubectl get ingress "$ing" -n "$NAMESPACE" -o jsonpath='{.metadata.annotations.kubernetes\.io/ingress\.class}')
    fi

    if [ -z "$ING_CLASS" ]; then
        echo -e " -> ${RED}[WARN] 未声明 IngressClass!若集群无默认 Class,Controller 将忽略该规则。${PLAIN}"
        ((RISK_COUNT++))
    else
        echo -e " -> 关联网关类: ${ING_CLASS}"
    fi

    # 提取 Address (判断是否已被 Controller 成功接管加载)
    ADDRESS=$(kubectl get ingress "$ing" -n "$NAMESPACE" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    if [ -z "$ADDRESS" ]; then
        ADDRESS=$(kubectl get ingress "$ing" -n "$NAMESPACE" -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    fi

    if [ -n "$ADDRESS" ]; then
        echo -e " -> 网关端点  : ${ADDRESS} ${GREEN}[已加载生效]${PLAIN}"
    else
        echo -e " -> 网关端点  : ${RED}Pending (尚未被 Controller 分配端点,访问易报 404)${PLAIN}"
        ((RISK_COUNT++))
    fi

    # 提取 Host 和 Path 规则
    echo -e "\n -> [ 路由映射明细 ]"
    kubectl get ingress "$ing" -n "$NAMESPACE" -o custom-columns="HOST:.spec.rules[*].host,PATH:.spec.rules[*].http.paths[*].path,BACKEND:.spec.rules[*].http.paths[*].backend.service.name" --no-headers | while read -r line; do
        echo -e "    ${YELLOW}${line}${PLAIN}"
    done
done

echo -e "\n${BLUE}================================================================${PLAIN}"
if [ "$RISK_COUNT" -gt 0 ]; then
    echo -e "${RED}[汇总] 发现 $RISK_COUNT 处潜在的 Ingress 配置异常 (未指定 Class 或未分配端点),请及时核查。${PLAIN}"
else
    echo -e "${GREEN}[汇总] 巡检完毕,所有 Ingress 资源均已正常加载。请确保 Path 与后端服务实际接口路径一致。${PLAIN}"
fi
echo -e "${BLUE}================================================================${PLAIN}"