ingress-nginx controller组件裸露了/metrics接口,prometheus能够拉取它的指标。
本文重点关注其外部指标的获取办法。
一. 创立ingress
1. 创立deploy
apiVersion: apps/v1kind: Deploymentmetadata: labels: app: nginx name: nginx-deployspec: replicas: 2 ##2正本 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx:1.14 imagePullPolicy: IfNotPresent name: nginx restartPolicy: Always
2. 创立service
apiVersion: v1kind: Servicemetadata: labels: app: nginx-svc name: nginx-svcspec: ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app: nginx sessionAffinity: None type: ClusterIP
3. 创立ingress
apiVersion: networking.k8s.io/v1 kind: Ingressmetadata: annotations: kubernetes.io/ingress.class: "nginx" name: nginx-ingressspec: rules: - host: example.com http: paths: - backend: service: name: nginx-svc port: number: 80 path: / pathType: ImplementationSpecific
拜访http://example.com:20004/,controller监听在hostNetwork的20004端口
4. 查看指标
二. 指标获取办法
重点关注以下指标:
- nginx_ingress_controller_request_size: ingress申请size
- nginx_ingress_controller_reseponse_size: ingress响应size
1. 指标定义
// ingress-nginx/internal/ingress/metric/collectors/socket.gofunc NewSocketCollector(pod, namespace, class string, metricsPerHost bool) (*SocketCollector, error) { ... sc := &SocketCollector{ ... responseLength: prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "response_size", Help: "The response length (including request line, header, and request body)", Namespace: PrometheusNamespace, ConstLabels: constLabels, }, requestTags, ), requestLength: prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "request_size", Help: "The request length (including request line, header, and request body)", Namespace: PrometheusNamespace, Buckets: prometheus.LinearBuckets(10, 10, 10), // 10 buckets, each 10 bytes wide. ConstLabels: constLabels, }, requestTags, ), ... } ...}
能够看到,这两个指标均是Histogram直方图类型。
2. 指标赋值
那这两个指标的值如何被赋值呢?
// ingress-nginx/internal/ingress/metric/collectors/socket.gofunc (sc *SocketCollector) Start() { for { conn, err := sc.listener.Accept() if err != nil { continue } go handleMessages(conn, sc.handleMessage) }}func (sc *SocketCollector) handleMessage(msg []byte) { ... var statsBatch []socketData err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(msg, &statsBatch) for _, stats := range statsBatch { ... if stats.RequestLength != -1 { requestLengthMetric, err := sc.requestLength.GetMetricWith(requestLabels) if err != nil { klog.Errorf("Error fetching request length metric: %v", err) } else { requestLengthMetric.Observe(stats.RequestLength) // request_size } } if stats.ResponseLength != -1 { bytesSentMetric, err := sc.bytesSent.GetMetricWith(requestLabels) if err != nil { klog.Errorf("Error fetching bytes sent metric: %v", err) } else { bytesSentMetric.Observe(stats.ResponseLength) // sent_bytes } responseSizeMetric, err := sc.responseLength.GetMetricWith(requestLabels) if err != nil { klog.Errorf("Error fetching bytes sent metric: %v", err) } else { responseSizeMetric.Observe(stats.ResponseLength) // response_size } } ... }}
从下面能够看出:
- 指标值从conn网络连接上读取而来,读到的数据是[]socketData类型;
- request_size的值来源于 socketData.RequestLength;
- respoonse_size的值来源于 socketData.ResponseLength;
- sent_bytes指标的值,跟response_size一样,均来源于 socketData.ReponseLength;
3. conn连贯是哪里的数据
下面讲到,数据源是从conn网络连接上读到的[]socketData,那么conn是连贯的谁呢?
// ingress-nginx/internal/ingress/metric/collectors/socket.gofunc NewSocketCollector(pod, namespace, class string, metricsPerHost bool) (*SocketCollector, error) { socket := "/tmp/prometheus-nginx.socket" // unix sockets must be unlink()ed before being used _ = syscall.Unlink(socket) listener, err := net.Listen("unix", socket) ...}
从下面能够看到:
- listener监听的unix socket文件:/tmp/prometheus-nginx.socket;
- conn也就是跟与socket的连贯,数据也是通过socket连贯读取到的;
那么谁会向这个socket写入数据呢?
4.ingress-nginx POD
在ingress-nginx的代码中没有找到写socket(/tmp/prometheus-nginx.socket)的中央,到ingress-nginx的POD内看看。
# kubectl exec -it -n ingress-nginx ingress-nginx-controller-j6mmt — sh/etc/nginx $ grep -r "prometheus-nginx.socket" ././lua/monitor.lua: assert(s:connect("unix:/tmp/prometheus-nginx.socket"))./lua/test/monitor_test.lua: assert.stub(tcp_mock.connect).was_called_with(tcp_mock, "unix:/tmp/prometheus-nginx.socket")
能够看到,nginx中有个monitor的lua插件,它会写socket: /tmp/prometheus-nginx.socket,监控指标应该就是从这里来的。
/etc/nginx $ vi ./lua/monitor.lua…local function send(payload) local s = assert(socket()) assert(s:connect("unix:/tmp/prometheus-nginx.socket")) assert(s:send(payload)) assert(s:close())endlocal function metrics() return { .... requestLength = tonumber(ngx.var.request_length) or -1, responseLength = tonumber(ngx.var.bytes_sent) or -1, ... }...
能够看到:
monitor.lua:
- 将nginx的request_length和bytes_sent变量赋值给requestLength和responseLength;
- 将数据组装成[]socketData发送到soket;
- ingress-nginx:从socket上读取[]socketData,而后给request_size、response_size指标赋值;
三. 总结
controller监听到Ingress对象被创立,将其配置reload到nignx上,而后应用lua脚本监控其读写流量,通过socket写指标写入controller,最初通过/metrics接口裸露进来: