背景
在微服務(wù)大行其道的今天,如何觀測眾多微服務(wù)、快速理清服務(wù)間的依賴、如何對服務(wù)之間的調(diào)用性能進(jìn)行衡量,成了擺在大家面前的難題。對此,Skywalking應(yīng)運(yùn)而生,它是托管在 Apache 基金會下的開源項(xiàng)目,旨在幫助開發(fā)者監(jiān)控分布式程序的性能、了解各個(gè)服務(wù)的調(diào)用關(guān)系和運(yùn)行情況。
Skywalking支持多種語言和框架,包含Java、Golang、Python等,功能強(qiáng)大、界面友好等特點(diǎn)使其迅速成為業(yè)界最流行的APM軟件之一。然而在運(yùn)用Skywalking的過程中,我們常常更關(guān)注服務(wù)之間的調(diào)用鏈路、性能數(shù)據(jù),往往會忽略流量入口(網(wǎng)關(guān))到服務(wù)之間的Trace串聯(lián),導(dǎo)致我們經(jīng)常在網(wǎng)關(guān)層面觀測到一個(gè)錯(cuò)誤調(diào)用后,無法通過TraceID快速查看本次調(diào)用的鏈路,從而白白浪費(fèi)寶貴的排障時(shí)間。
本文重點(diǎn)介紹如何將 Ingress Nginx 集成進(jìn) Skywalking,將其作為 Skywalking 的一個(gè)節(jié)點(diǎn),并且在access log 中打印TraceID,從而在出現(xiàn)故障的時(shí)候,可以通過日志中的TraceID快速找到調(diào)用鏈路,達(dá)到快速故障定位的效果。
注:本文使用的 Kubernetes 版本是 1.24.15,Ingress Nginx controller 版本是 v1.8.1,Skywalking版本是9.2.0。
方案
在介紹方案之前,我們先了解一下相關(guān)的背景知識,用于更好的理解集成方案。
- Ingress Nginx Configmap:Ingress Nginx 的各種配置存放地,可以通過該Configmap配置logformat、所開啟的插件等。
- Skywalking Nginx Lua:Skywalking 官方提供的 Lua 版本 lib,提供了一系列的操作,自己可以在Nginx的配置文件中編寫Lua腳本,適時(shí)創(chuàng)建Span、結(jié)束Span,從而把 Nginx 當(dāng)作Skywalking中的一個(gè)服務(wù)節(jié)點(diǎn)集成進(jìn)Skywalking。
- Ingress Nginx 自定義插件:Lua腳本編寫的插件,用于對 Ingress Nginx 做編程,想要使用插件必須要將插件放到 Ingress Controller 容器的 /etc/nginx/lua/plugins/插件名稱 目錄中,且需要在 Ingress Controller 的configmap中開啟它。自定義插件支持以下幾個(gè)鉤子:
- a. init_worker: 用于對Nginx Worker做一些初始化。
- b. rewrite: 用于修改請求、更改標(biāo)頭、重定向、丟棄請求、進(jìn)行身份驗(yàn)證等。
- c. header_filter: 當(dāng)接收到后端response header 時(shí)調(diào)用此函數(shù),通常用來記錄和修改后端的response header。
- d. body_filter: 這是在收到后端response body 時(shí)調(diào)用的,一般用來記錄response body。
- e. log: 當(dāng)請求處理完成并將響應(yīng)傳遞給客戶端時(shí),會調(diào)用此函數(shù)。
- sw8:SkyWalking 跨進(jìn)程傳播的Header Key,它的格式是 1-TRACEID-SEGMENTID-3-PARENT_SERVICE-PARENT_INSTANCE-PARENT_ENDPOINT-IPPORT(其中TraceID、SpanID等都通過base64進(jìn)行編碼),我們可以通過此Header解析出對應(yīng)的 TraceID。
了解了上述原理后,我們的方案就顯而易見了,就是將 Skywalking Nginx Lua 集成進(jìn) Ingress Nginx中,并編寫插件,在不同階段執(zhí)行相關(guān)操作:
- a. 在 rewrite 階段生成新Span并解析出TraceID將其放在新Header中(方便access log 打印)
- b. 在 body_filter 階段結(jié)束該Span
- c. 在log階段提交對應(yīng)的數(shù)據(jù)到Skywalking服務(wù)端
- d. 修改 Nginx log format,將存儲 TraceID 的Header 打印出來
步驟
1. 集成Skywalking Nginx Lua進(jìn)Ingress Nginx
Skywalking Nginx Lua 的核心是它的?lib?目錄,里邊包含了所有需要用到的函數(shù)操作,所以我們需要將該?lib?目錄的內(nèi)容放到 Ingress Nginx 的Pod 中,讓我們編寫的插件能夠調(diào)用到它。具體我們可以將?lib?目錄放到網(wǎng)盤中,然后通過 Volume 的形式掛載進(jìn)去,也可以將?lib?的內(nèi)容寫入configmap,然后掛載Volume到Pod中。本文選擇第二種方式,將?lib?的內(nèi)容放到 configmap 中,然后掛載進(jìn)去,雖說這種方式不夠優(yōu)雅,但好在不用依賴網(wǎng)盤。
我們先clone Skywalking Nginx Lua 這個(gè)庫,然后將 lib 下的所有.lua文件打平放到同一個(gè)目錄中
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中的所有文件創(chuàng)建到一個(gè)configmap中,注意將 -n 后邊的參數(shù)換成你自己Ingress Nginx 所在的 namespace。
kubectl create cm skywalking-nginx-lua-agent --from-file=./sk-lua-cm/ -n ingress-nginx
2. 編寫Ingress Nginx 的插件
引入了 Skywalking 的 lib 后就可以編寫對應(yīng)的 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
劃重點(diǎn):在上述代碼中獲取了幾個(gè)環(huán)境變量,需要記住,后邊需要用到。
- i. SKY_SERVICE_NAME:Ingress Nginx 在 Skywalking 中的 Service 名稱
- ii. SKY_INSTANCE_NAME:Ingress Nginx 實(shí)例在 Skywalking 中的實(shí)例名稱
- iii. SKY_OAP_ADDR:Skywalking后端地址
編寫好插件代碼后就可以基于此創(chuàng)建configmap了,依然需要注意 -n 后邊的namespace,需要改成你實(shí)際Ingress Nginx所在的 namespace。
kubectl create cm skywalking-lua-plug --from-file=main.lua -n ingress-nginx
3. 掛載相關(guān) Lua 腳本進(jìn) Ingress Nginx Controller 的 Pod 中
修改 Ingress Nginx Controller 的 Deployment 配置,主要修改以下幾點(diǎn):
a. 環(huán)境變量
- 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
該配置中配置了如下幾個(gè)信息:
- a. plugins:開啟skywalking插件
- b. lua-shared-dicts:聲明 trace 使用的變量和大小
- c. main-snippet:其中聲明了需要使用到的環(huán)境變量,切記在插件中使用的環(huán)境變量必須放到這里來
- d. log-format-upstream:log 格式,我們在這個(gè)里添加了一個(gè) http_trace_id 這個(gè)header的打?。ㄉ弦徊浇馕龀鰜淼腡raceID)
5. 重啟 Pod 生效
把下列的 xxx 換成 Ingress Nginx Controller 的 Pod 名稱
kubectl delete pod xxxx -n=ingress-nginx
效果展示
本節(jié)展示最終在Skywalking UI 上的展示效果。
- 可以在 Skywalking 的拓?fù)鋱D中看到有一個(gè) Ingress Nginx 的服務(wù)節(jié)點(diǎn)加入
- 在Trace查詢頁面可以看到有一個(gè) Nginx 的 Span
- 如下是打印出來的TraceID
如何更好使用Trace?
通過上述介紹,我們已經(jīng)把 Ingress Nginx 集成進(jìn)了Skywalking,接下來介紹一下如何更好的使用Trace數(shù)據(jù)來快速定位故障。
數(shù)據(jù)建設(shè)
-
將Skywalking數(shù)據(jù)源接入Flashcat,接入的方法很簡單,只需要填寫對應(yīng)的地址、賬號、密碼,然后起一個(gè)名字即可?
-
采集 Ingress Nginx 日志到 kafka 中,這里可以使用?categraf 的 log 采集器
-
將日志接入 Flashcat 的日志分析子系統(tǒng)生成報(bào)表,在這張報(bào)表中可以看到對應(yīng)的域名、接口、流量、成功率等(當(dāng)然,這些維度都可以自定義),在創(chuàng)建報(bào)表的時(shí)候設(shè)置好日志中哪個(gè)字段是TraceID字段。
- 通過日志報(bào)表生成滅火圖(IT系統(tǒng)健康度一覽表),例如下圖就是典型的電商系統(tǒng)核心API健康度一覽表。
- 通過數(shù)據(jù)庫、Metrics、日志等不同來源建立北極星指標(biāo)(核心業(yè)務(wù)指標(biāo)),例如:電商系統(tǒng)的下單量、支付量等
串聯(lián)打通
通過Flashcat的串聯(lián)能力,建立北極星和滅火圖的串聯(lián)。
故障定位路徑
當(dāng)建設(shè)好對應(yīng)的指標(biāo)和關(guān)聯(lián)后,就可以開啟我們的故障定位之旅了。
- 當(dāng)北極星指標(biāo)故障(核心業(yè)務(wù)受損)時(shí),北極星頁面上對應(yīng)的指標(biāo)會飄紅且發(fā)送報(bào)警,例如下圖中的商品實(shí)時(shí)下單量掉底了,該業(yè)務(wù)卡片會飄紅
- 此時(shí)我們點(diǎn)擊曲線上掉底時(shí)刻的數(shù)據(jù)點(diǎn),可以打開關(guān)聯(lián)的滅火圖,一眼就可以看到是訂單子系統(tǒng)在飄紅(可能發(fā)生了故障)
- 我們點(diǎn)擊飄紅的滅火圖路徑,可以下鉆到具體的卡片組中去
- 然后可以看到滅火圖卡片組中的訂單更新DB接口成功率為0,我們點(diǎn)擊旁邊的詳情可以打開對應(yīng)的詳情曲線(通過上一步中的日志報(bào)表生成)
- 通過詳情可以看到,這個(gè)時(shí)候的成功率已經(jīng)掉底了,那么我們同樣可以點(diǎn)擊曲線上掉底的時(shí)間點(diǎn)打開日志詳情
- 在這個(gè)頁面中,我們可以直接查看異常日志的日志詳情,也可以點(diǎn)擊右側(cè)的 trace 按鈕打開該調(diào)用的Trace鏈路
通過Trace鏈路可以看到Redis的端口不通導(dǎo)致更新失敗了,這個(gè)時(shí)候我們就需要去排查依賴的Redis是否正常了。文章來源:http://www.zghlxwxcb.cn/news/detail-856348.html
當(dāng)然,以上只是Flashcat在整合可觀測性三大支柱(Metrics、Log、Trace)方面的一個(gè)小例子,如果您對Flashcat這套產(chǎn)品感興趣,可以隨時(shí)與我們交流:https://flashcat.cloud/contact/。文章來源地址http://www.zghlxwxcb.cn/news/detail-856348.html
到了這里,關(guān)于手摸手教你把Ingress Nginx集成進(jìn)Skywalking的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!