Ingress_reload_失败_health_api-tpa
Ingress reload 失败问题总结(health controller / api-tpa)
事件时间:2026-06-11(上周四)
0. 排查工具:查 Ingress controller 内部 upstream / backend
通过 controller 的 10246 端口可以直接拿到 controller 内存里的 backend / upstream 状态:
# 列出 controller 当前所有 backend
curl -s http://localhost:10246/configuration/backends | jq .
# 找具体 Ingress 对应的 backend
curl -s http://localhost:10246/configuration/backends | \
jq '.[] | select(.name | contains("api-tpa"))'
# 看某个 service 的 endpoints 是否被正确解析
curl -s http://localhost:10246/configuration/backends | \
jq '.[] | {name, endpoints: .endpoints, service: .service}'
适用场景:
- Ingress reload 失败时,确认 controller 端是否已经生成了新的 backend
- 502 排查时,看 controller 选中的 upstream endpoint 是否和 Service 实际 endpoints 一致
- 后端 Pod 扩缩容后,看 controller 端 endpoints 是否及时刷新
- upstream_addr 显示 upstream group 名字(不是具体 IP)时,配合这个接口看是否所有 peer 都被标记 failed
10246 是 ingress-nginx controller 内部 metrics / debug 端口,需要在 controller Pod 本机(或 kubectl port-forward)访问。
1. 直接故障现象
Ingress controller reload Nginx 失败:
directive "error_log" is not terminated by ";" in /tmp/nginx/nginx-cfg2489516325:7086
nginx: configuration file /tmp/nginx/nginx-cfg2489516325 test failed
这说明 Ingress controller 生成出来的 nginx 配置语法错误,导致 Nginx 无法 reload。
2. 直接原因
生成的 nginx 配置里有这段:
# Custom code snippet configured for host api-tpa.qingsongjkkj.com
access_log /var/log/ingress/api-tpa.qingsongjkkj.com-access.log upstreaminfo; error_log /var/log/ingress/api-tpa.qingsongjkkj.com-error.log
问题是最后这一句:
error_log /var/log/ingress/api-tpa.qingsongjkkj.com-error.log
末尾少了分号:
;
正确应该是:
error_log /var/log/ingress/api-tpa.qingsongjkkj.com-error.log error;
所以 Nginx 解析到下一段配置时,认为 error_log 指令没有正常结束,最终 reload 失败。
3. 为什么 Ingress YAML 里没看到 snippet
查 api-tpa.qingsongjkkj.com 这个 Ingress 时,发现 metadata annotations 里没有:
nginx.ingress.kubernetes.io/server-snippet
nginx.ingress.kubernetes.io/configuration-snippet
但生成的 nginx 配置里却有:
# Custom code snippet configured for host api-tpa.qingsongjkkj.com
说明这段自定义日志配置可能不是来自当前 Ingress YAML 本身,而可能来自:
1. controller ConfigMap
2. 自定义 nginx template
3. 自动生成/注入逻辑
4. 其他配置管理脚本
所以后续要继续 grep 来源:
kubectl get cm -A -o yaml | grep -nA10 -B10 "api-tpa.qingsongjkkj.com-error.log"
kubectl get ingress -A -o yaml | grep -nA10 -B10 "api-tpa.qingsongjkkj.com-error.log"
grep -R "api-tpa.qingsongjkkj.com-error.log" /data/build/k8s/prod/ingress/health -n
4. 为什么 admission 没提前拦住
集群里确实有 ValidatingWebhookConfiguration:
VWC: ingress-nginx-admission
service: ingress-nginx/ingress-nginx-controller-admission
failurePolicy: Fail
但是这个 Service 的 selector 太宽:
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
它没有带:
qsc-platform: health
所以这个 admission Service 会选中多套 ingress controller,而不只是 health controller。
查到的 endpoints 是:
10.111.138.21:8443,10.111.138.22:8443,10.111.138.23:8443 + 9 more...
说明 admission Service 后面挂了多个 controller Pod。
而 health controller 本身确实开了 webhook:
--controller-class=k8s.io/health-ingress-nginx
--ingress-class=health
--validating-webhook=:8443
--validating-webhook-certificate=/usr/local/certificates/cert
--validating-webhook-key=/usr/local/certificates/key
也就是说:health controller 有校验能力,但 apiserver 调 admission 时不一定打到 health controller。
5. 真实问题本质
本质是:
IngressClass health 对应 health-nginx-controller
但是 ValidatingWebhookConfiguration 只配置了一个公共 admission Service
这个 Service selector 太宽,选中了所有平台的 ingress-nginx controller
所以提交 health Ingress 时,admission 请求可能被转发到 crm / med / baoxian / bigdata 等 controller
这些 controller 发现 ingressClassName=health 不是自己负责的 class,可能直接跳过/放行
最终坏 snippet 没有在 kubectl apply 阶段被拦截
等 health controller 真正 reload nginx 时,才暴露语法错误
一句话总结:
不是 admission 完全没开,而是 health 这套 Ingress 的 admission 校验链路不可靠,存在漏检。
6. 影响
影响主要是:
1. 新的 Ingress 配置无法 reload 生效
2. controller 继续使用旧 nginx 配置
3. 后续 Ingress 变更可能被阻塞
4. 如果错误配置持续存在,controller 每次 reload 都会失败
5. snippet 少一个分号就可能影响整套 health ingress controller
旧配置一般还能继续跑,但新变更不会生效。
7. 正确修复方向
短期修复:
找到产生 api-tpa 那段 access_log/error_log 的来源
把 error_log 末尾补上分号
最好写完整日志级别
正确格式:
access_log /var/log/ingress/api-tpa.qingsongjkkj.com-access.log upstreaminfo;
error_log /var/log/ingress/api-tpa.qingsongjkkj.com-error.log error;
长期修复:
每个平台 ingress controller 单独拆 admission Service + ValidatingWebhookConfiguration
比如 health 单独建:
apiVersion: v1
kind: Service
metadata:
name: health-nginx-controller-admission
namespace: ingress-nginx
spec:
type: ClusterIP
ports:
- 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
qsc-platform: health
然后 ValidatingWebhookConfiguration 指向:
clientConfig:
service:
namespace: ingress-nginx
name: health-nginx-controller-admission
path: /networking/v1/ingresses
port: 443
failurePolicy: Fail
但要注意:证书也要重新匹配新的 Service DNS 名称,否则 apiserver 调 webhook 会报 x509 证书不匹配。
8. 最终结论
这次问题可以总结成:
health ingress controller 生成的 nginx 配置中,自定义日志 snippet 的 error_log 少了分号,导致 nginx reload 失败。
本应由 admission webhook 在提交 Ingress 时提前拦截,但当前 admission Service selector 过宽,混选了多套 ingress controller,导致 health Ingress 的校验请求可能打到非 health controller,从而被跳过放行。
所以问题根因有两个:
1. snippet 配置错误;
2. admission webhook 配置不可靠,未按 ingressClass/platform 隔离。