Ingress-nginx工作原理和实践

本文介绍了Ingress与Ingress-nginx的区别,重点阐述了Ingress-nginx如何作为K8s的Ingress控制器工作,通过Paths分流和rewrite-target注解实现服务路由。同时,文章详细探讨了Ingress-nginx到webapp的日志追踪,利用X-Request-ID确保请求在整个架构中的可追溯性,以便于故障排查和责任划分。

在这里插入图片描述
这个图算是一个通用的前后端分离的 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 的日志追踪

熟悉我的朋友知道, 我写了《》,这里面主要是 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, 并在日志分析中起到巨大作用,也便于划清运维/开发的故障责任。

总结

了解了Ingress在应用层工作,根据Host和Path暴露k8s服务
本文梳理了Ingress和常见的Ingress-nginx的关系
对于应用了Ingress的应用,梳理了从Ingress-Nginx到WebApp的日志追踪id, 便于排查网络/业务故障

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值