本文记录/分享 目前项目的 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, 并在日志分析中起到巨大作用,也便于划清运维/开发的故障责任。

总结

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

Ingress-nginx工作原理和实践的更多相关文章

  1. Nginx系列一:正向代理和反向代理、Nginx工作原理、Nginx常用命令和升级、搭建Nginx负载均衡

    转自https://www.cnblogs.com/leeSmall/p/9351343.html 仅供个人学习 一.什么是正向代理.什么是反向代理 1. 正向代理,意思是一个位于客户端和原始服务器( ...

  2. Nginx 工作原理

    Nginx 工作原理 Nginx由内核和模块组成. Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个location block,而此locat ...

  3. 《浏览器工作原理与实践》 <12>栈空间和堆空间:数据是如何存储的?

    对于前端开发者来说,JavaScript 的内存机制是一个不被经常提及的概念 ,因此很容易被忽视.特别是一些非计算机专业的同学,对内存机制可能没有非常清晰的认识,甚至有些同学根本就不知道 JavaSc ...

  4. 《浏览器工作原理与实践》<11>this:从JavaScript执行上下文的视角讲清楚this

    在上篇文章中,我们讲了词法作用域.作用域链以及闭包,接下来我们分析一下这段代码: var bar = { myName:"time.geekbang.com", printName ...

  5. 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?

    在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...

  6. 《浏览器工作原理与实践》<09>块级作用域:var缺陷以及为什么要引入let和const?

    在前面我们已经讲解了 JavaScript 中变量提升的相关内容,正是由于 JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷 ...

  7. 《浏览器工作原理与实践》<08>调用栈:为什么JavaScript代码会出现栈溢出?

    在上篇文章中,我们讲到了,当一段代码被执行时,JavaScript 引擎先会对其进行编译,并创建执行上下文.但是并没有明确说明到底什么样的代码才算符合规范. 那么接下来我们就来明确下,哪些情况下代码才 ...

  8. 《浏览器工作原理与实践》<07>变量提升:JavaScript代码是按顺序执行的吗?

    讲解完宏观视角下的浏览器后,从这篇文章开始,我们就进入下一个新的模块了,这里我会对 JavaScript 执行原理做深入介绍. 今天在该模块的第一篇文章,我们主要讲解执行上下文相关的内容.那为什么先讲 ...

  9. 《浏览器工作原理与实践》<06>渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?

    在上篇文章中,我们介绍了渲染流水线中的 DOM 生成.样式计算和布局三个阶段,那今天我们接着讲解渲染流水线后面的阶段. 这里还是先简单回顾下上节前三个阶段的主要内容:在 HTML 页面内容被提交给渲染 ...

  10. 《浏览器工作原理与实践》<05>渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?

    在上一篇文章中我们介绍了导航相关的流程,那导航被提交后又会怎么样呢?就进入了渲染阶段.这个阶段很重要,了解其相关流程能让你“看透”页面是如何工作的,有了这些知识,你可以解决一系列相关的问题,比如能熟练 ...

随机推荐

  1. 将从摄像头即时读入的人像放入背景视频中_with_OpenCV_in_Python

    import cv2 import numpy as np import time cap = cv2.VideoCapture(0) background_capture = cv2.VideoCa ...

  2. React Hooks 实现一个计时器组件

    React Hooks 实现一个计时器组件 useEffect https://reactjs.org/docs/hooks-reference.html#useeffect import React ...

  3. user tracker with ETag

    user tracker with ETag 用户追踪, without cookies clear cache bug 实现原理 HTTP cache hidden iframe 1px image ...

  4. Interview of Chinese IT companies Ratings and Reviews website/app

    Interview of Chinese IT companies Ratings and Reviews website/app // js hack const getShitRank = (st ...

  5. d3 & hexagon

    d3 & hexagon https://bl.ocks.org/mbostock/5249328 https://bl.ocks.org/vasturiano/e70e14483fe01eb ...

  6. project generators & project scaffold

    project generators & project scaffold how to write a node cli & Project Scaffold https://www ...

  7. Array in Depth

    Array in Depth Array.concat() & Array.push() https://developer.mozilla.org/en-US/docs/Web/JavaSc ...

  8. babel 常用操作

    astexplorer babel-types code to ast const { parse } = babel; const code = ` for (let k in ${data}) { ...

  9. vue页面切换过渡

    <!--<transition name="slide-left">--> <router-view></router-view>& ...

  10. DHCP (Dynamic Host Configuration Protocol )协议的探讨与分析

    DHCP (Dynamic Host Configuration Protocol )协议的探讨与分析 问题背景 最近在工作中遇到了连接外网的交换机在IPv6地址条件下从运营商自动获取的DNS地址与本 ...