手摸手教你把Ingress Nginx集成进Skywalking
背景
在微服务大行其道的今天,如何观测众多微服务、快速理清服务间的依赖、如何对服务之间的调用性能进行衡量,成了摆在大家面前的难题。对此,Skywalking应运而生,它是托管在 Apache 基金会下的开源项目,旨在帮助开发者监控分布式程序的性能、了解各个服务的调用关系和运行情况。
Skywalking支持多种语言和框架,包含Java、Golang、Python等,功能强大、界面友好等特点使其迅速成为业界最流行的APM软件之一。然而在运用Skywalking的过程中,我们常常更关注服务之间的调用链路、性能数据,往往会忽略流量入口(网关)到服务之间的Trace串联,导致我们经常在网关层面观测到一个错误调用后,无法通过TraceID快速查看本次调用的链路,从而白白浪费宝贵的排障时间。
本文重点介绍如何将 Ingress Nginx 集成进 Skywalking,将其作为 Skywalking 的一个节点,并且在access log 中打印TraceID,从而在出现故障的时候,可以通过日志中的TraceID快速找到调用链路,达到快速故障定位的效果。
注:本文使用的 Kubernetes 版本是 1.24.15,Ingress Nginx controller 版本是 v1.8.1,Skywalking版本是9.2.0。
方案
在介绍方案之前,我们先了解一下相关的背景知识,用于更好的理解集成方案。
- Ingress Nginx Configmap:Ingress Nginx 的各种配置存放地,可以通过该Configmap配置logformat、所开启的插件等。
- Skywalking Nginx Lua:Skywalking 官方提供的 Lua 版本 lib,提供了一系列的操作,自己可以在Nginx的配置文件中编写Lua脚本,适时创建Span、结束Span,从而把 Nginx 当作Skywalking中的一个服务节点集成进Skywalking。
- Ingress Nginx 自定义插件:Lua脚本编写的插件,用于对 Ingress Nginx 做编程,想要使用插件必须要将插件放到 Ingress Controller 容器的 /etc/nginx/lua/plugins/插件名称 目录中,且需要在 Ingress Controller 的configmap中开启它。自定义插件支持以下几个钩子:
- a. init_worker: 用于对Nginx Worker做一些初始化。
- b. rewrite: 用于修改请求、更改标头、重定向、丢弃请求、进行身份验证等。
- c. header_filter: 当接收到后端response header 时调用此函数,通常用来记录和修改后端的response header。
- d. body_filter: 这是在收到后端response body 时调用的,一般用来记录response body。
- e. log: 当请求处理完成并将响应传递给客户端时,会调用此函数。
- sw8:SkyWalking 跨进程传播的Header Key,它的格式是 1-TRACEID-SEGMENTID-3-PARENT_SERVICE-PARENT_INSTANCE-PARENT_ENDPOINT-IPPORT(其中TraceID、SpanID等都通过base64进行编码),我们可以通过此Header解析出对应的 TraceID。
了解了上述原理后,我们的方案就显而易见了,就是将 Skywalking Nginx Lua 集成进 Ingress Nginx中,并编写插件,在不同阶段执行相关操作:
- a. 在 rewrite 阶段生成新Span并解析出TraceID将其放在新Header中(方便access log 打印)
- b. 在 body_filter 阶段结束该Span
- c. 在log阶段提交对应的数据到Skywalking服务端
- d. 修改 Nginx log format,将存储 TraceID 的Header 打印出来
步骤
1. 集成Skywalking Nginx Lua进Ingress Nginx
Skywalking Nginx Lua 的核心是它的 lib 目录,里边包含了所有需要用到的函数操作,所以我们需要将该 lib 目录的内容放到 Ingress Nginx 的Pod 中,让我们编写的插件能够调用到它。具体我们可以将 lib 目录放到网盘中,然后通过 Volume 的形式挂载进去,也可以将 lib 的内容写入configmap,然后挂载Volume到Pod中。本文选择第二种方式,将 lib 的内容放到 configmap 中,然后挂载进去,虽说这种方式不够优雅,但好在不用依赖网盘。
我们先clone Skywalking Nginx Lua 这个库,然后将 lib 下的所有.lua文件打平放到同一个目录中
git clone https://github.com/apache/skywalking-nginx-lua.git
mkdir sk-lua-cm
cp skywalking-nginx-lua/lib/skywalking/*.lua sk-lua-cm/
cp skywalking-nginx-lua/lib/skywalking/dependencies/*.lua sk-lua-cm/
cp skywalking-nginx-lua/lib/resty/*.lua sk-lua-cm/
再通过 kubectl 命令将sk-lua-cm中的所有文件创建到一个configmap中,注意将 -n 后边的参数换成你自己Ingress Nginx 所在的 namespace。
kubectl create cm skywalking-nginx-lua-agent --from-file=./sk-lua-cm/ -n ingress-nginx
2. 编写Ingress Nginx 的插件
引入了 Skywalking 的 lib 后就可以编写对应的 Ingress Nginx 自定义插件了,代码比较简单,以下是代码详情(命名为main.lua)。
local _M = {}
function _M.init_worker()
local metadata_buffer = ngx.shared.tracing_buffer
require("skywalking.util").set_randomseed()
local serviceName = os.getenv("SKY_SERVICE_NAME")
if not serviceName then
serviceName="ingress-nginx"
end
metadata_buffer:set('serviceName', serviceName)
local serviceInstanceName = os.getenv("SKY_INSTANCE_NAME")
if not serviceInstanceName then
serviceName="ingress-nginx"
end
metadata_buffer:set('serviceInstanceName', serviceName)
metadata_buffer:set('includeHostInEntrySpan', false)
require("skywalking.client"):startBackendTimer(os.getenv("SKY_OAP_ADDR"))
skywalking_tracer = require("skywalking.tracer")
end
function _M.rewrite()
local upstreamName = ngx.var.proxy_upstream_name
skywalking_tracer:start(upstreamName)
if ngx.var.http_sw8 ~= "" then
local sw8Str = ngx.var.http_sw8
local sw8Item = require('skywalking.util').split(sw8Str, "-")
if #sw8Item >= 2 then
ngx.req.set_header("trace_id", ngx.decode_base64(sw8Item[2]))
end
end
end
function _M.body_filter()
if ngx.arg[2] then
skywalking_tracer:finish()
end
end
function _M.log()
skywalking_tracer:prepareForReport()
end
return _M
划重点:在上述代码中获取了几个环境变量,需要记住,后边需要用到。
- i. SKY_SERVICE_NAME:Ingress Nginx 在 Skywalking 中的 Service 名称
- ii. SKY_INSTANCE_NAME:Ingress Nginx 实例在 Skywalking 中的实例名称
- iii. SKY_OAP_ADDR:Skywalking后端地址
编写好插件代码后就可以基于此创建configmap了,依然需要注意 -n 后边的namespace,需要改成你实际Ingress Nginx所在的 namespace。
kubectl create cm skywalking-lua-plug --from-file=main.lua -n ingress-nginx
3. 挂载相关 Lua 脚本进 Ingress Nginx Controller 的 Pod 中
修改 Ingress Nginx Controller 的 Deployment 配置,主要修改以下几点:
a. 环境变量
- name: SKY_OAP_ADDR
value: http://skywalking-oap.skywalking.svc.cluster.local:12800
- name: SKY_SERVICE_NAME
value: ingress-nginx
- name: SKY_INSTANCE_NAME
value: ingress-nginx
b. volumes 声明
- name: sky-nginx-plugin
configMap:
name: skywalking-lua-plug
- name: skywalking-nginx-lua-agent
configMap:
name: skywalking-nginx-lua-agent
c. volumeMounts 声明
- mountPath: /etc/nginx/lua/plugins/skywalking/main.lua
subPath: "main.lua"
name: sky-nginx-plugin
- mountPath: /etc/nginx/lua/resty/http.lua
subPath: "http.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/tablepool.lua
subPath: "tablepool.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/resty/http_headers.lua
subPath: "http_headers.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/resty/jit-uuid.lua
subPath: "jit-uuid.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/client.lua
subPath: "client.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/constants.lua
subPath: "constants.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/correlation_context.lua
subPath: "correlation_context.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/dependencies/base64.lua
subPath: "base64.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/management.lua
subPath: "management.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/segment.lua
subPath: "segment.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/segment_ref.lua
subPath: "segment_ref.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/span.lua
subPath: "span.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/span_layer.lua
subPath: "span_layer.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/tracer.lua
subPath: "tracer.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/tracing_context.lua
subPath: "tracing_context.lua"
name: skywalking-nginx-lua-agent
- mountPath: /etc/nginx/lua/skywalking/util.lua
subPath: "util.lua"
name: skywalking-nginx-lua-agent
4. 修改 Ingress Nginx Controller 所使用的configmap配置
plugins: "skywalking"
lua-shared-dicts: "tracing_buffer: 100m"
main-snippet: |
env SKY_SERVICE_NAME;
env SKY_INSTANCE_NAME;
env SKY_OAP_ADDR;
log-format-upstream: |
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] [$proxy_alternative_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $request_id $http_trace_id
该配置中配置了如下几个信息:
- a. plugins:开启skywalking插件
- b. lua-shared-dicts:声明 trace 使用的变量和大小
- c. main-snippet:其中声明了需要使用到的环境变量,切记在插件中使用的环境变量必须放到这里来
- d. log-format-upstream:log 格式,我们在这个里添加了一个 http_trace_id 这个header的打印(上一步解析出来的TraceID)
5. 重启 Pod 生效
把下列的 xxx 换成 Ingress Nginx Controller 的 Pod 名称
kubectl delete pod xxxx -n=ingress-nginx
效果展示
本节展示最终在Skywalking UI 上的展示效果。
- 可以在 Skywalking 的拓扑图中看到有一个 Ingress Nginx 的服务节点加入
- 在Trace查询页面可以看到有一个 Nginx 的 Span
- 如下是打印出来的TraceID
如何更好使用Trace?
通过上述介绍,我们已经把 Ingress Nginx 集成进了Skywalking,接下来介绍一下如何更好的使用Trace数据来快速定位故障。
数据建设
将Skywalking数据源接入Flashcat,接入的方法很简单,只需要填写对应的地址、账号、密码,然后起一个名字即可
采集 Ingress Nginx 日志到 kafka 中,这里可以使用 categraf 的 log 采集器
将日志接入 Flashcat 的日志分析子系统生成报表,在这张报表中可以看到对应的域名、接口、流量、成功率等(当然,这些维度都可以自定义),在创建报表的时候设置好日志中哪个字段是TraceID字段。
- 通过日志报表生成灭火图(IT系统健康度一览表),例如下图就是典型的电商系统核心API健康度一览表。
- 通过数据库、Metrics、日志等不同来源建立北极星指标(核心业务指标),例如:电商系统的下单量、支付量等
串联打通
通过Flashcat的串联能力,建立北极星和灭火图的串联。
故障定位路径
当建设好对应的指标和关联后,就可以开启我们的故障定位之旅了。
- 当北极星指标故障(核心业务受损)时,北极星页面上对应的指标会飘红且发送报警,例如下图中的商品实时下单量掉底了,该业务卡片会飘红
- 此时我们点击曲线上掉底时刻的数据点,可以打开关联的灭火图,一眼就可以看到是订单子系统在飘红(可能发生了故障)
- 我们点击飘红的灭火图路径,可以下钻到具体的卡片组中去
- 然后可以看到灭火图卡片组中的订单更新DB接口成功率为0,我们点击旁边的详情可以打开对应的详情曲线(通过上一步中的日志报表生成)
- 通过详情可以看到,这个时候的成功率已经掉底了,那么我们同样可以点击曲线上掉底的时间点打开日志详情
- 在这个页面中,我们可以直接查看异常日志的日志详情,也可以点击右侧的 trace 按钮打开该调用的Trace链路
通过Trace链路可以看到Redis的端口不通导致更新失败了,这个时候我们就需要去排查依赖的Redis是否正常了。
当然,以上只是Flashcat在整合可观测性三大支柱(Metrics、Log、Trace)方面的一个小例子,如果您对Flashcat这套产品感兴趣,可以随时与我们交流:https://flashcat.cloud/contact/。
手摸手教你把Ingress Nginx集成进Skywalking的更多相关文章
- iOS动画进阶 - 手摸手教你写 Slack 的 Loading 动画
如果移动端访问不佳,可以访问我的个人博客 前几天看了一篇关于动画的博客叫手摸手教你写 Slack 的 Loading 动画,看着挺炫,但是是安卓版的,寻思的着仿造着写一篇iOS版的,下面是我写这个动画 ...
- 手摸手教你如何在 Python 编码中做到小细节大优化
手摸手教你如何在 Python 编码中做到小细节大优化 在列表里计数 """ 在列表里计数,使用 Python 原生函数计数要快很多,所以尽量使用原生函数来计算. &qu ...
- 手摸手教你微信小程序开发之自定义组件
前言 相信大家在开发小程序时会遇到某个功能多次使用的情况,比如弹出框.这个时候大家首先想到的是组件化开发,就是把弹出框封装成一个组件,然后哪里使用哪里就调用,对,看来大家都是有思路的人,但是要怎样实现 ...
- 手摸手教你让Laravel开发Api更得心应手
https://www.guaosi.com/2019/02/26/laravel-api-initialization-preparation/ 1. 起因 随着前后端完全分离,PHP也基本告别了v ...
- 【转】手摸手,带你用vue撸后台 系列一
前言 说好的教程终于来了,第一篇文章主要来说一说在开始写业务代码前的一些准备工作吧,但这里不会教你webpack的基础配置,热更新怎么做,webpack速度优化等等,有需求的请自行google. 目录 ...
- 【转】手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)
前言 做这个 vueAdmin-template 的主要原因是: vue-element-admin 这个项目的初衷是一个vue的管理后台集成方案,把平时用到的一些组件或者经验分享给大家,同时它也在不 ...
- 手摸手带你用Hexo撸博客(一)
原文地址 手摸手带你用Hexo撸博客(一) 环境搭建 安装 node 狂点下一步 命令行输入此条命令 如果能看到版本号则安装成功 node -v 安装Git (同上) 实在不会的小伙伴百度一下,教程很 ...
- 手摸手,和你一起学习 UiPath Studio
学习 RPA 的路上坑比较多,让我们手摸手,一起走…… 以下是一些学习 UiPath 和 RPA 的资源, 拿走不用谢! UiPath Studio 中文文档 机器人流程自动化其实是很好的概念和技术, ...
- 【转】手摸手,带你用vue撸后台 系列二(登录权限篇)
前言 拖更有点严重,过了半个月才写了第二篇教程.无奈自己是一个业务猿,每天被我司的产品虐的死去活来,之前又病了一下休息了几天,大家见谅. 进入正题,做后台项目区别于做其它的项目,权限验证与安全性是非常 ...
- 【转】手摸手,带你用vue撸后台 系列三(实战篇)
前言 在前面两篇文章中已经把基础工作环境构建完成,也已经把后台核心的登录和权限完成了,现在手摸手,一起进入实操. Element 去年十月份开始用vue做管理后台的时候毫不犹豫的就选择了Elemen, ...
随机推荐
- HarmonyOS NEXT应用开发案例——阻塞事件冒泡
介绍 本示例主要介绍在点击事件中,子组件enabled属性设置为false的时候,如何解决点击子组件模块区域会触发父组件的点击事件问题:以及触摸事件中当子组件触发触摸事件的时候,父组件如果设置触摸事件 ...
- 一文带你了解企业上云数据分析首选产品Quick BI
简介: 阿里云Quick BI再度入选,并继续成为该领域唯一入选魔力象限的中国企业,文章将为大家详细介绍上云数据分析首选产品 Quick BI的核心能力. 日前,国际权威分析机构Gartner发布20 ...
- 在 UOS 统信运行 dotnet 程序提示没有通过系统安全验证无法运行
本文记录 dotnet 应用程序在 UOS 统信系统上运行时,提示 没有通过系统安全验证,无法运行 的问题 这个问题是因为没有开启 UOS 统信的开发者模式,直接将自己构建完成的包放上去跑导致的问题 ...
- 手把手教你如何构建 WPF 官方开源框架源代码
从去年微软就将 WPF 开源了,差不多现在所有 WPF 的源代码都开源了.在学习框架的时候,我会做一些改动,期望能构建一个自己的版本进行测试.但是作为一个特别大的框架,想要构建跑起来可不是直接在 Vi ...
- Raft 共识算法1-Raft基础
Raft 共识算法1-Raft基础 Raft算法中译版地址:http://www.redisant.cn/etcd/contact 英原论文地址:https://raft.github.io/raft ...
- Dubbo SPI-Wrapper
前言 在Dubbo SPI中是通过Wrapper实现AOP,对于AOP相信大家都不陌生,这里不做的过多的介绍,我们主要来了解Dubbo SPI中是如何使用Wrapper类以及实现的细节. 使用场景 D ...
- R2_ES中数据的存储测试
基本概念:ES(一): 架构及原理 关系数据库 ⇒ 数据库 ⇒ 表 ⇒ 行 ⇒ 列(Columns) Elasticsearch ⇒ 索引(Index) ⇒ 类型(Type) ⇒ 文档(Documen ...
- 带你十天轻松搞定 Go 微服务系列全集+勘误
官网手册: https://go-zero.dev/cn/ 文档说明: https://zhuanlan.zhihu.com/p/461604538 本地开发运行环境: https://github. ...
- gin框架对接快递100 查询快递跟踪记录 Golang实现快递查询
参考ui效果: https://www.kuaidi100.com/?from=openv gin框架: 请求地址 http://localhost:8822/kd100/auto_com_num?n ...
- 当字符遇上 scanf() 要当心
当字符遇上 scanf() 要当心 看一下程序 char ch1,ch2; printf("请输入ch1,ch2的值:"); scanf("%c %c",&am ...