一、背景
目前我們的生產環境一層Nginx已經容器化部署,但是監控並不完善,我們期望其具有Ingress-Nginx-Controller組件上報監控的數據。這樣可以建立請求全鏈路的監控大盤。有利於監控查看關鍵鏈路的狀態信息,並快速定位問題。因此需要研究Ingress-Nginx-Controller組件的監控機制原理,看是否可以移植到一層nginx上實現metrics監控數據的采集。
二、分析
首先,目前常用的Ingress-Nginx-Controller有兩個。一個是K8S官方開源的Ingress-Nginx-Controller,另一個是nginx官方開源的Ingress-Nginx-Controller。我們使用的是K8S官方的版本。
這兩個Controller大致的區別如下:
1)K8S官方的Controller也是采用Go語言開發的,集成了Lua實現的OpenResty;而Nginx官方的Ccontroller是集成了Nginx;
2)兩者對Nginx的配置不同,並且使用的nginx.conf配置模板也是不一樣的,Nginx官方的采用兩個模板文件以include的方式配置upstream;K8S官方版本采用Lua動態配置upstream,所以不需要reload。
所以,在pod頻繁變更的場景下,采用K8S官方版本不需要reload,影響會更小。
接下來,我們來看K8S官方的Ingress-Nginx-Controller是如何實現Metrics監控數據采集上報的。
根據Ingress-Nginx-Controlleroller內部架構圖:

可以知道,Ingress-Nginx-Controller的內部組成部分和通訊機制。這里我們針對Nginx Metrics部分展開分析。於是,我們分析一下Ingress-Nginx-Controller的源碼,找到其Metrics的入口。
在ingress-nginx/cmd/nginx/main.go文件中,我們找到了Metrics的入口,如下:


可以看到,當我們訪問/metrics路徑去獲取監控數據時,程序會返回metrics.NewCollector()中采集的數據。繼續分析metrics.NewCollector的邏輯。在ingress-nginx/internal/ingress/metrics/main.go文件中,我們可以看到:

這就是metrics真實數據來源的采集入口,我們可以看到一共包含了四部分的數據:NGINXStatus,NGINXProcess,SocketCollector和IngressController。下面,我們看下這四部分數據具體是什么?
1)NGINXStatus

其中nginx.StatusPath就是/nginx_status

由此可知,NGINXStatus的數據就是通過/nginx_status有nginx的status模板采集的數據。
2)NGINXProcess

而NGINXProcess的數據則是通過process_exporter的方式在/proc中采集nginx相關的數據。
3)SocketCollector

而NGINXSocket部分的數據則是監聽/tmp/prometheus-nginx.socket這個socket文件來收集的數據。那么這個文件的數據來源是哪里呢?我們grep一下這個文件,發現nginx的lua腳本中有一個monitor.lua文件,就是這個文件采集到nginx的數據后寫進去的。

由此可知,NGINXSocket的數據是通過monitor.lua采集的。
4)IngressController

而NGINXController部分的數據則controller對nginx操作狀態信息的統計,然后直接上報的。
經過以上的分析,我們知道。ingress-nginx-controller的metrics監控包含了四部分數據:NGINXStatus,NGINXProcess,SocketCollector和IngressController。
其中NGINXStatus和SocketCollector都是在nginx中實現,通過nginx自身的status模板和monitor.lua實現,並通過http或者socket的方式暴露。NGINXProcess和IngressController則是有ingress-controller本身實現。
三、一層Nginx實現類似ingress-nginx-controller的metrics監控
由上面的分析我們可以知道,要使得普通的nginx實例具備Ingress-Nginx-Controller的metrics監控能力,需要將NGINXStatus,NGINXProcess,SocketCollector和IngressController四部分的數據采集能力移植到nginx實例上。
1)NGINXStatus不需要移植,nginx開啟status模塊,以/nginx_status暴露即可; 2)SocketCollector移植簡單,將monitor.lua腳本拷貝到nginx中,簡單配置跑起來即可;該部分是SocketCollector監聽在/tmp/prometheus-nginx.socket文件,由monitor.lua采集數據后寫入。 3)NGINXProcess和IngressController移植較復雜,需要深入分析其源碼,從ingress-nginx-controller中剝離出來后單獨運行。其中IngressController部分的數據丟棄。
最后,通過抽離移植NGINXStatus,NGINXProcess,SocketCollector三部分監控內容。(代碼見:https://github.com/wsjhk/nginx-custom-metrics.git)部署到一層nginx中需要做如下變更:
1)抽離出來的代碼編譯為ngxcustom-metrics二進制可執行文件,打包到nginx的鏡像中,隨容器啟動一起啟動。
2)nginx的配置中需要添加Lua相關代碼的部署,具體配置參考源碼。
3)Prometheus配置采集一層nginx的metrics監控信息。驗證監控數據。
(一層Nginx的容器化實現參考:https://mp.weixin.qq.com/s/q_kTlflDMg6MGyNOq6sVjQ)
3.1)deployment中添加annotation:

3.2)添加job_name采集:
- job_name: 'slb-nginx-pods'
honor_labels: false
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- slb-nginx
tls_config:
insecure_skip_verify: true
relabel_configs:
- target_label: dc
replacement: huadong1
- target_label: cloud
replacement: aliyun
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_ngx_mr_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (.+)
采集到的數據樣例如下:

至此,完成了Nginx監控Metrics的改造。
