一、问题缘起:为什么Nginx Ingress和Linkerd搭伙会搞丢Host头?
做微服务架构的开发者,大概率会选Linkerd当服务网格管流量,Nginx Ingress当对外入口——这俩本来是黄金组合,可我上次踩了个大坑:用户访问的域名对应的Host头,到后端服务里直接没了,导致服务返回404,折腾半天才挖出来:Linkerd和Nginx Ingress协作时,默认会悄悄把关键的Host头给覆盖了。
二、故障还原:我踩过的坑,全流程示例
我们用Kubernetes本地测试环境复现这个问题,技术栈统一用Kubernetes生态的工具,避免混合其他技术。
2.1 准备测试环境
先搭好Minikube集群,装Nginx Ingress和Linkerd,命令和注释都标清楚:
# 1. 启动本地Minikube集群(用Docker驱动,本地测试方便)
minikube start --driver=docker
# 2. 安装Nginx Ingress Controller,作为对外入口
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
# 3. 安装Linkerd服务网格,负责流量治理
linkerd install | kubectl apply -f -
# 4. 验证Linkerd是否安装成功(出现all checks passed就是没问题)
linkerd check
2.2 部署业务应用和Ingress资源
我们搞个简单的Nginx业务,搭配Ingress关联域名example.com,先不做任何修正,复现问题:
# 业务Deployment:跑个Nginx实例用来测试Host头
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-app
spec:
replicas: 1
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
# 业务Service:把Pod端口80暴露成K8s内部服务
apiVersion: v1
kind: Service
metadata:
name: test-app-service
spec:
selector:
app: test-app
ports:
- port: 80
targetPort: 80
---
# Ingress资源:对外暴露example.com域名,指向刚才的业务服务
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test-app-service
port:
number: 80
2.3 复现Host头丢失的问题
把本地域名映射到Minikube的IP,然后发请求看详细头:
# 把example.com指向Minikube的对外IP(本地测试用,别忘改)
echo "$(minikube ip) example.com" | sudo tee -a /etc/hosts
# 发带详细日志的请求,重点看Host头
curl -v http://example.com
结果你会看到,请求发出去了,但返回的不是example.com对应的内容,而是Nginx的默认页面或者404——这就是Host头丢了,后端服务根本不知道你要访问哪个域名。
三、原因拆解:到底是谁搞丢了Host头?
Linkerd给每个K8s Pod都会注入一个叫proxy的sidecar容器,目的是监控和治理服务间的流量。当Nginx Ingress把请求发给Linkerd的proxy时,proxy为了标准化服务间的调用,默认会把请求的Host头,重写成目标服务的K8s服务名(比如我们例子里的test-app-service)。这样一来,用户请求带的原始Host头(比如example.com)就被覆盖了,后端服务收到的Host头不对,自然返回错误。
四、修正策略:3招解决Host头丢失
针对刚才的原因,我们整理了三种适合不同场景的修正方法,从精准到全局覆盖都有。
4.1 方法一:给单个业务Pod加Linkerd注解(精准适配)
只改有需求的业务Pod,不影响其他服务,适合只有少量业务需要对外域名的场景:
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-app
spec:
replicas: 1
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
annotations:
# 关键注解:告诉Linkerd的proxy,转发请求时保留原始Host头
proxy.l5d.io/headers-to-forward: "Host"
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
测试:重新部署Pod后,再发curl -v请求,会看到Host头还是example.com,问题解决。这个方法的优点是只改单个Pod,不影响集群其他配置;缺点是每个需要的Pod都要加注解,麻烦一点。
4.2 方法二:给Ingress加Nginx注解(集中适配)
在Ingress层统一设置,不用改业务Pod,适合Ingress集中管理的场景:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
# 关键注解:强制Nginx转发请求时,用原始的Host头,不被Linkerd覆盖
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Host $http_host;
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test-app-service
port:
number: 80
这个方法的优点是只改Ingress,不用碰业务代码或Pod配置;缺点是如果有多个Ingress,每个都要加这个注解,而且要注意Nginx Ingress的版本,旧版本的snippet格式可能要调整。
4.3 方法三:Linkerd全局配置调整(大规模集群适配)
如果整个集群都用Linkerd,想一劳永逸,不用每个资源改,就改Linkerd的全局配置:
# 生成Linkerd的默认配置,修改headersToForward参数(把Host加进去),然后重新部署Linkerd
linkerd config default | sed 's/headersToForward: \[\]/headersToForward: ["Host"]/' | linkerd install --inline | kubectl apply -f -
这个方法的优点是改一次,集群内所有服务都自动保留原始Host头;缺点是改变了Linkerd的默认设计(Linkerd原本想标准化头部方便追踪),可能带来未知风险,需要提前做充分测试。
五、技术优缺点与注意事项
5.1 三种方法的优缺点对比
- 方法一:精准,不影响其他服务,适合小集群;缺点是维护成本高,多Pod要重复配置。
- 方法二:集中管理,适合Ingress多域名的场景;缺点是需要修改每个Ingress,对Nginx版本有依赖。
- 方法三:全局一劳永逸,适合上百个服务的大集群;缺点是改变服务网格的原生行为,风险较高。
5.2 关键注意事项
- 版本兼容:Linkerd 2.10以上才支持proxy.l5d.io的注解,旧版本要改Linkerd的全局ConfigMap。
- 测试要细:每次改完后,必须用curl -v看请求头的Host字段,确认是否正确传递,避免踩新坑。
- TLS场景要额外注意:如果用HTTPS,Ingress里的tls配置的host必须和请求的Host一致,不然还是会出问题。
- 不滥用:如果是集群内部服务调用,不需要保留原始Host头,只有对外的Ingress相关服务才需要改。
六、应用场景与总结
这个问题最常出现在用K8s做微服务架构,同时搭配服务网格和入口控制器的场景,比如电商平台的多域名路由、SaaS服务的租户隔离路由,这些场景都需要精准的Host头来区分服务。总结一下,遇到Host头丢失的问题,先排查是不是Linkerd Sidecar重写了Host头,再根据场景选合适的修正方法:少量业务选单个Pod注解,Ingress集中管理选Ingress注解,大规模集群选全局配置调整。
评论
围绕“Nginx Ingress与Linkerd协作时Host头丢失,Ingress注解配置修正策略”参与讨论