链路追踪--使用nginx-ingress-controller记录后端pod真实ip
前言
常见的nginx做反向代理架构,nginx转发到后端的多台服务
┌─────────┐
│backend-1│
└─────────┘
┌─────────┐
┌─────┐ │backend-2│
│nginx│ ────► └─────────┘
└─────┘
...
┌─────────┐
│backend-n│
└─────────┘
在传统的部署,如直接部署在nginx上,或者部署在docker中,nginx转发到后面的的backend是固定在nginx的配置文件中的,这也很方便排查问题节点在哪儿:直接看access_log中的upstream_addr即可
该部署的优点非常明显,简单高效易维护,每一条链路,每一个状态都非常清晰。但是缺点也很明显,扩缩容艰难,一旦需要扩容backend,就非常麻烦了,需要首先部署backend,修改nginx转发配置,最后重启nginx
为了提高扩缩容效率,将该架构搬到k8s中
┌─────────┐
│backend-1│
└─────────┘
┌─────────┐
┌─────┐ ┌───────────────┐ │backend-2│
│nginx│ ────► │backend-service│ ────► └─────────┘
└─────┘ └───────────────┘ ...
┌─────────┐
│backend-n│
└─────────┘
通过k8s-service的能力,自动做服务发现,每当上/下线一个backend,就会动态发现backend的个数,从此之后,扩缩容就会变得非常简单
但是新的问题来了,问题出现时,比如某个backend出现问题,导致从nginx的日志出现了502,在access_log中显示的upstream_addr并不是后端backend的地址,而是backend-service的地址,无法立刻知道到底是哪个backend 出问题
问题出现了,如何跟踪一条request,能够明确知道它进入了哪一个pod,就是本文需要探索的内容
nginx-ingress
如果架构迁移还在方案验证阶段,那么恭喜,这个方法可以一劳永逸的解决nginx的问题,那就是利用nginx-ingress-controller来作为入口
安装
这个就不班门弄斧了,直接祭出官网
这一步需要注意的点不多,就是镜像的问题,镜像有可能拉不下来,至于怎么解决,大家可以看我之前的文章,关于如何拉镜像
安装完成之后
▶ kubectl -n ingress-nginx get pod
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-78f7f8bd46-tqjm4 1/1 Running 0 1m
nginx-ingress-controller本质和其他的nginx没有什么区别,只不过有一些额外的功能来辅助nginx提供更好的服务,这个一会再讨论
backend后端服务
接着创建一个backend服务,使用最简单的python tornado作为web容器
test.py
from tornado.ioloop import IOLoop
import tornado.httpserver as httpserver
import tornado.web
import os
class TestFlow(tornado.web.RequestHandler):
def get(self):
ret = 'i am backend in {}'.format(os.environ['HOSTNAME'])
self.write(ret)
def post(self, *args, **kwargs):
print(self.request.body.decode('utf-8'))
def applications():
urls = []
urls.append([r'/', TestFlow])
urls.append([r'/test', TestFlow])
return tornado.web.Application(urls)
def main():
app = applications()
server = httpserver.HTTPServer(app)
server.bind(10000, '0.0.0.0')
server.start(1)
IOLoop.current().start()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt as e:
IOLoop.current().stop()
finally:
IOLoop.current().close()
将其打包成镜像
Dockerfile
FROM python:3.11-alpine
WORKDIR /opt
RUN pip3 install tornado -i https://mirrors.aliyun.com/pypi/simple/
ADD test.py /opt/test.py
CMD ["python3", "test.py"]
docker build . -t backend-service:v1
制作完成之后将其放入k8s,并且使用一个service作为访问入口
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- image: backend-service:v1
imagePullPolicy: Never
name: backend
ports:
- containerPort: 10000
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: default
spec:
ports:
- port: 10000
protocol: TCP
targetPort: 10000
selector:
app: backend
type: ClusterIP
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
backend-6d4cdd4c68-mqzgj 1/1 Running 0 2m 10.244.0.40 wilson <none> <none>
backend-6d4cdd4c68-qjp9m 1/1 Running 0 2m 10.244.0.60 wilson <none> <none>
大功告成
创建ingress
继续创建ingress,ingress其实就是nginx转发到后端服务的配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-test-ingress
namespace: default
spec:
rules:
- host: wilsonchai.com
http:
paths:
- backend:
service:
name: backend-service
port:
number: 10000
path: /
pathType: Prefix
测试结果
折腾了半天,先捋一下整个的路径,我们创建了nginx-ingress-controller,它是整个系统的入口,其次创建了后端服务backend,请求的终点是该服务,还创建了一系列的service以及ingress,他们的关系如图所示

- 请求通过nodePort进来进入nginx-ingress。
▶ kubectl -n ingress-nginx get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.98.224.124 <pending> 80:30296/TCP,443:31592/TCP 30m - nginx-ingress检查ingress规则,将请求转发至backend-service
▶ kubectl get svc backend-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE backend-service ClusterIP 10.105.148.194 <none> 10000/TCP 30m - backend-service随机转发该流量进入一个pod
▶ kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES backend-6d4cdd4c68-mqzgj 1/1 Running 0 30m 10.244.0.40 wilson <none> <none> backend-6d4cdd4c68-qjp9m 1/1 Running 0 30m 10.244.0.60 wilson <none> <none>
访问集群: curl -H "host: wilsonchai.com" 127.0.0.1:30296
▶ curl -H "host: wilsonchai.com" 127.0.0.1:30296
i am backend in backend-6d4cdd4c68-qjp9m
请求顺利到达了backend,再检查一下nginx-ingress的日志
10.244.0.1 - - [28/Nov/2025:09:28:45 +0000] "GET / HTTP/1.1" 200 40 "-" "curl/7.81.0" 78 0.001 [default-backend-service-10000] [] 10.244.0.60:10000 40 0.001 200 1896b48d60ef31861478e713d65c9660
在upstream_addr上面,显示的就是后端pod的ip: 10.244.0.60
至此,终于解决了开篇提出的问题,如果有request报错502、503等,可以精准的定位到时哪个pod出现了问题
小结
Nginx Ingress Controller 能够直接记录后端 Pod IP,是因为它在转发流量时,绕过了 Service 的 Cluster IP,直接与后端 Pod 建立连接,Nginx Ingress Controller 并不是一个普通的客户端,而是一个特殊的 K8s Controller
- Service 与 Endpoints: 当创建一个 Service 时,K8s 会自动创建一个相应的 Endpoints 对象(或 EndpointSlice)。这个 Endpoints 对象会实时记录所有与 Service 标签匹配的 后端 Pod 的实际 IP 地址和端口
- Controller 实时同步: Nginx Ingress Controller 会持续监听 K8s API Server 中所有 Service 对应的 Endpoints 对象的变化(即 Pod 的创建、删除、就绪状态改变)
检验一下,再次访问: curl -H "host: wilsonchai.com" 127.0.0.1:30296
登录到nginx-ingress-controller容器,使用netstat检查连接状态
ingress-nginx-controller-78f7f8bd46-tqjm4:/etc/nginx$ netstat -anpt | grep ESTABLISH
tcp 0 0 10.244.0.16:36074 10.244.0.40:10000 ESTABLISHED -
果然直接与后端的pod建立了连接
后记
盲目的搬进k8s真的是完全错误的,解决了新的问题,又带来更多不可控的问题,所以架构迁移真的需要做好调研:新架构能够满足需求,并且新架构的缺点也能够克服或者忍受,方可行动
有位兄弟说了,如果没有使用nginx-ingress-controller,而是直接把nginx直接搬迁到k8s,那怎么办?并且该文只是解决了nginx访问后端的问题,如果是服务之间的访问,又该怎么办呢?并且这是http协议,如果是其他协议, 比如gRPC,又该怎么办呢?
大家稍安勿躁,本文只是一个引子,后面会把这些问题统统说清楚,敬请期待
联系我
- 联系我,做深入的交流

至此,本文结束
在下才疏学浅,有撒汤漏水的,请各位不吝赐教...
本文来自博客园,作者:it排球君,转载请注明原文链接:https://www.cnblogs.com/MrVolleyball/p/19321441

浙公网安备 33010602011771号