Ingress-nginx工作原理和实践
本文记录/分享 目前项目的 K8s 部署结构和请求追踪改造方案
这个图算是一个通用的前后端分离的 k8s 部署结构:
Nginx Ingress 负责暴露服务(nginx前端静态资源服务), 根据十二要素应用的原
则,将后端 api 作为 nginx 服务的附加动态资源。
Ingress vs Ingress-nginx
Ingress 是一种向 k8s 集群外部的客户端公开服务的方法, Ingress 在网络协议栈的应用层工作,
根据请求的主机名 host 和路径 path 决定请求转发到的服务。
在应用 Ingress对象提供的功能之前,必须强调集群中存在 Ingress Controller, Ingress 资源才能正常工作。
我这里的 web 项目使用的是常见的 Ingress-nginx (官方还有其他用途的 Ingress),Ingress-nginx 是使用 nginx 作为反向代理和负载均衡器的 K8s Ingress 控制器, 作为 Pod 运行在kube-system
命名空间。
了解 Ingress 工作原理,有利于我们如何与运维人员打交道。
下面通过 Ingress-nginx 暴露 Kibana 服务:
--- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: kibana labels: app: kibana annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-connect-timeout: "30" nginx.ingress.kubernetes.io/proxy-read-timeout: "1800" nginx.ingress.kubernetes.io/proxy-send-timeout: "1800" nginx.ingress.kubernetes.io/proxy-body-size: "8m" nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: tls: - hosts: - 'https://logging.internal.gridsum.com/' secretName: tls-cert rules: - host: 'https://logging.internal.gridsum.com' http: paths: - path: / backend: serviceName: kibana servicePort: 5601
Ingress-nginx 中最让我困惑的是它的Paths分流
与rewrite-target
注解。
- Paths 分流
一般用于 根据特定的 Path,将请求转发到特定的后端服务 Pod,后端服务 Pod 能接收到 Path 这个信息。
一般后端服务是作为 api。 - rewrite-target
将请求重定向到后端服务, 那有什么用处呢?
答: 以上面暴露的 kibana 为例, 我们已经可以在https://logging.internal.gridsum.com/
访问完整的 Kibana, 如果我想利用这个域名暴露 ElasticSearch 站点,怎么操作?
这时就可以利用rewrite-target
,
--- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: elasticsearch labels: app: kibana annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/proxy-connect-timeout: "30" nginx.ingress.kubernetes.io/proxy-read-timeout: "1800" nginx.ingress.kubernetes.io/proxy-send-timeout: "1800" nginx.ingress.kubernetes.io/proxy-body-size: "8m" nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/rewrite-target: "/$2" spec: tls: - hosts: - 'logging.internal.gridsum.com' secretName: tls-cert rules: - host: 'logging.internal.gridsum.com' http: paths: - path: /es(/|$)(.*) backend: serviceName: elasticsearch servicePort: 9200
在此 Ingress 定义中,由(.*)
捕获的所有字符都将分配给占位符$2,然后将其用作重写目标注解中的参数。 这样的话:https://logging.internal.gridsum.com/es
将会重定向到后端 elasticsearch 站点,并且忽略了 es 这个 path
Ingress-nginx 到 webapp 的日志追踪
熟悉我的朋友知道, 我写了《一套标准的ASP.NET Core容器化应用日志收集分析方案》,这里面主要是 BackEnd App 的日志,从我上面的结构图看,
Ingress-nginx----> Nginx FrontEnd App--->BackEnd App 需要一个串联的追踪 Id, 便于观察运维网络和业务应用。
幸好 Ingress-nginx, Nginx 强大的配置能力帮助我们做了很多事情:
-
客户端请求到达 Ingress-Nginx Controllerr,Ingress-Nginx Controller 会自动添加一个
X-Request-ID
的请求 Header, 随机值---- 这个配置是默认的 -
请求达到 Nginx FrontEnd App, Nginx 有默认配置
proxy_pass_request_headers on;
, 自动将请求头都传递到上游的 Backend App
这样跨越整个结构图的 request_id 思路已经清楚了,最后一步只需要我们在 Backend App 中提取请求中携带的X-Request-ID
, 并作为日志的关键输出字段。
这就涉及到怎么从自定义日志的 LayoutRender。
下面为 NLog 自定义名为x_request_id
的 Render,该 Render 从请求的 X-Request-ID 标头中提取值。
① 定义 NLog Render
/// <summary> /// Represent a unique identifier to represent a request from the request HTTP header X-Request-Id. /// </summary> [LayoutRenderer("x_request_id")] public class XRequestIdLayoutRender : HttpContextLayoutRendererBase { protected override void Append(StringBuilder builder, LogEventInfo logEvent) { var identityName = HttpContextAccessor.HttpContext?.Request?.Headers?["X-Request-Id"].FirstOrDefault(); builder.Append(identityName); } } /// <summary> /// Represent a http context layout renderer to access the current http context. /// </summary> public abstract class HttpContextLayoutRendererBase : LayoutRenderer { private IHttpContextAccessor _httpContextAccessor; /// <summary> /// Gets the <see cref="IHttpContextAccessor"/>. /// </summary> protected IHttpContextAccessor HttpContextAccessor { get { return _httpContextAccessor ?? (_httpContextAccessor = ServiceLocator.ServiceProvider.GetService<IHttpContextAccessor>()); } } } internal sealed class ServiceLocator { public static IServiceProvider ServiceProvider { get; set; } }
② 从请求中获取 X-Request-Id 依赖 IHttpContextAccessor 组件
这里使用 依赖查找的方式获取该组件, 故请在 Startup ConfigureService 中生成服务
public void ConfigureServices(IServiceCollection services) { // ...... ServiceLocator.ServiceProvider = services.BuildServiceProvider(); }
③ 最后在 Program 中注册这个 NLog Render:
public static void Main(string[] args) { LayoutRenderer.Register<XRequestIdLayoutRender>("x_request_id"); CreateHostBuilder(args).Build().Run(); }
这样从 Ingress-Nginx 产生的request_id
,将会流转到 Backend App, 并在日志分析中起到巨大作用,也便于划清运维/开发的故障责任。
- https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#generate-request-id
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_request_headers
总结
- 了解了Ingress在应用层工作,根据Host和Path暴露k8s服务
- 本文梳理了Ingress和常见的Ingress-nginx的关系
- 对于应用了Ingress的应用,梳理了从Ingress-Nginx到WebApp的日志追踪id, 便于排查网络/业务故障
本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/14554245.html
欢迎关注我的原创技术、职场公众号, 加好友谈天说地,一起进化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-03-18 使用GUI工具Portainer.io管控Docker容器