博客 | NGINX

在 CDN77 上记录 NGINX 与上游服务器之间的流量

NGINX-F5-horiz-black-type-RGB 的一部分
Vojtěch Štafa 缩略图
沃伊捷赫·什塔法
2018 年 12 月 12 日发布

无论您运行什么平台,日志记录通常都是大数据处理、统计、审计、客户端报告和交易以及调试客户端-服务器通信和可能出现的问题的核心要求。

在这篇博客中,我们讨论了日志在调试中的作用,它提供了定位互联网通信中常见的各种问题的终极工具。

使用客户端和 Web 服务器之间的直接连接进行记录

记录客户端与 Web 服务器的通信对于调试与浏览器版本、客户端网络以及正在访问的文件相关的可能问题有很大帮助。 然而,事情并没有结束。 记录的内容有无数种可能性,完全取决于您的平台和要求。

NGINX 的日志架构

当您运行反向代理、负载均衡器或内容分发网络 (CDN) 时,您会在客户端和服务器之间的通信流中添加另一个节点。 操作数据的中间服务器(例如反向代理)经常会引入意外问题。

作为反向代理,NGINX 通过终止来自客户端的连接并创建到服务器的新连接,将从客户端到 Web 服务器(或其他应用服务器)的连接转换为两个单独的连接。 “拆分”为两个连接还会创建两个单独的日志记录上下文,并且 NGINX 对它们的日志记录支持略有不同。

  • 对于客户端和反向代理之间的流量,NGINX 提供了错误日志访问日志,记录了非错误的处理事件和操作。

    您可以使用error_log指令启用错误日志,并可以设置要记录的错误的严重性级别。

    您可以使用access_log指令启用访问日志。 相关的log_format指令使您能够自定义日志中包含的信息类型和日志条目的格式。 您可以将日志写入文件、系统日志或两者。

  • 对于反向代理和 Web 或应用服务器(NGINX 称之为上游服务器)之间的流量,NGINX 支持错误日志。 但是,它不支持此流量的访问日志记录。

    查看代理服务器和上游服务器之间的非错误事件的唯一方法是将错误日志中的严重性级别设置为调试。 此设置的缺点是会记录大量数据。 这不仅会减慢请求处理速度,还会创建非常大的文件,从而很快填满存储空间。 (请注意,您还必须使用configure命令中的--with-debug参数重新编译 NGINX,因为默认情况下不启用对调试的支持。)

我们的上游日志解决方案

作为 CDN 提供商,我们经常会遇到无法与我们的反向代理服务器和客户端正确通信的上游服务器。 错误日志中的调试级别消息并不总是提供我们修复上游服务器问题所需的信息。

解决方案很快变得非常明显:通过添加一个仅从反向代理和上游服务器之间的通信中收集必要信息的功能来生成上游访问日志。

使用客户端和 Web 服务器之间的拆分连接进行日志记录

总体思路是让 NGINX 每次向上游服务器发出请求时调用我们的函数。 这使得我们可以在函数本身中编写与上游日志相关的所有逻辑。

标准的ngx_http_upstream_module处理上游请求,我们需要它为我们调用该函数。 该模块目前不支持此类功能,因此我们对其进行了修补,以在需要时启用回调。

日志记录本身由我们编写的单独模块处理,该模块使用我们在修补的上游模块中添加的日志回调功能。 新模块定义了一个新的upstream_log指令来配置日志功能。 该指令使用与access_log指令相同的解析器,因此数据可以写入文件中或使用套接字发送到 syslog 服务器。

当 NGINX 在启动期间读取nginx.conf中的upper_log指令时,会调用两个函数:

  • ngx_http_upstream_log_set_log ,它解析指令并在内部使用ngx_log_set_log准备日志结构本身( ngx_log_t
  • ngx_http_upstream_log_init (在配置后步骤中),将我们的主要日志记录功能注册到上游模块

这样,当请求需要代理上游时,一切都准备就绪。 上游模块启动与上游服务器的连接,我们的补丁确保调用日志函数来记录请求详细信息。

日志格式被硬连线到上游模块中。 我们仍然可以选择使用log_format指令添加对配置的支持,但对于我们的用例来说这不是必需的。

来自上游的记录值

与上游服务器的连接关闭后立即调用日志记录函数。 其参数是一个指向当前正在处理的请求( ngx_http_ request_t结构)的指针,使得该函数能够访问并记录结构中的所有数据。 上游字段(指向ngx_http_upstream_t的指针)特别令人感兴趣,因为它包含有关上游请求的数据。 我们特别感兴趣的是:

  • 源响应状态代码
  • 请求的持续时间
  • 响应中的 HTTP 标头或主机标头的值

访问整个请求结构赋予了模块灵活性,因为可以记录各种各样的信息。

我们遇到的问题

我们最初在ngx_http_core_module中实现了该功能。 这对于原型来说已经足够好了,但它不是一个非常干净的解决方案,因为它可能会使未来的更新和修改变得复杂。 最终,我们将上游日志记录功能分离为独立模块,如我们的上游日志解决方案中所述。

当然,也存在一些实施问题。 最明显的是,在某些地方我们错误处理了ngx_str_t字符串,例如使用 C 库函数sprintf而不是ngx_snprintf 。 这可能导致将未定义的数据写入上游日志,甚至导致工作线程出现分段错误。 通过使用 Valgrind 和 AddressSanitizer 等工具进行大量调试和测试后,这些问题得到了解决。

CDN77 中使用的 NGINX 功能

CDN77 使用 NGINX 的主要原因是其缓存功能。 CDN服务器是引入在客户端和Web服务器(上游服务器)之间的一个节点,传递客户端的请求,向上游服务器请求合适的文件。 当文件被缓存时,它会被传递给从同一位置请求同一文件的其他用户。

“本地提供文件”是我们用来让 NGINX 在收到请求时从服务器的磁盘提供文件的功能之一。

安全缓存需要一些附加功能和配置。 我们使用 SSL(带有 0-RTT 的 TLS 1.3)或可为特定 IP 地址生成的安全令牌来妥善保护内容。

我们使用的其他功能包括为客户端自定义错误页面、OCSP 装订的默认 NGINX 实现以及用于 PHP 的 FastCGI,以减少所需的 PHP 进程数量。

结论

上游日志记录不仅帮助了我们并继续帮助我们调试各种问题,它还提供了深入了解 NGINX 核心功能的绝佳机会,并简化了我们开展的许多其他项目。

关于 CDN77

CDN77 使全球内容交付变得更好、更便捷。 我们拥有超过 30 个数据中心,能够有效地缓存和传递全球范围内的内容。 这包括您网站上的静态内容、软件分发、视频点播 (VoD) 以及通过各种协议(例如使用专用流媒体引擎的 HLS 或 MPEG-DASH)进行的实时流媒体。

开始使用 CDN77 非常简单、快速且直接。 注册免费试用,创建 CDN 资源,并使用生成的 CDN URL 或自定义的CNAME记录将其与您的网站或流媒体解决方案集成。 所有功能、设置和可能的自定义解决方案确保 CDN77 满足您的要求。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”