Envoy service mesh、Prometheus和Grafana下的微服務監控

ServiceMesher2018-11-24 21:37:16

作者 Arvind Thangamani

譯者 蒙奕錕

審校者 楊傳勝

3100字 | 閱讀大約需要6分鐘

如果你剛接觸“Service Mesh“和“Envoy”,我這裡有一篇文章可以幫你入門。

這是Envoy service mesh下的可觀測性系列的第二篇文章,你可以在這裡閱讀第一篇關於分佈式追蹤的文章。

中談及監控時,你可不能被矇在鼓裡,至少要知道問題出在哪兒了。

讓我們看看Envoy是怎樣幫助我們瞭解我們的服務運行狀況的。在service mesh下,所有的通信都會通過mesh,這意味著沒有任何服務會與其它服務直接通信,服務向Envoy發起調用請求,然後Envoy將調用請求路由到目標服務,所以Envoy將持有傳入和傳出流量的上下文。Envoy通常提供關於傳入請求、傳出請求和Envoy實例狀態的指標。

準備

這是我們將要構建的系統概覽。

Statsd

Envoy支持通過兩到三種格式來暴露指標,但本文中我們將statsd格式。

所以流程將是這樣,首先Envoy推送指標到statsd,然後我們用prometheus(一個時序數據庫)從statsd指標,最後通過grafana可視化這些指標。

在準備概覽圖中,我提到了statsd exporter而不是statsd,這是因為我們並不會直接使用statsd,而是使用一個接收statsd格式數據,並將其以prometheus格式輸出的轉換器(服務)。下面讓我們來搞定它吧。

Envoy的指標主要分為兩類:

  1. Counter(計數器):一個只增不減的指標。如:請求總數

  2. Gauge(量表):一個可增可減的指標,類似於一個瞬時值。如:當前CPU使用量

讓我們看一個包含stats sink的Envoy配置

  1. ---

  2. admin:

  3.  access_log_path: "/tmp/admin_access.log"

  4.  address:

  5.    socket_address:

  6.      address: "127.0.0.1"

  7.      port_value: 9901

  8. stats_sinks:

  9.  -

  10.    name: "envoy.statsd"

  11.    config:

  12.      tcp_cluster_name: "statsd-exporter"

  13.      prefix: front-envoy    

  14. static_resources:

  15.  listeners:

  16.    -

  17.      name: "http_listener"

  18.      address:

  19.        socket_address:

  20.          address: "0.0.0.0"

  21.          port_value: 80

  22.      filter_chains:

  23.          filters:

  24.            -

  25.              name: "envoy.http_connection_manager"

  26.              config:

  27.                use_remote_address: true

  28.                add_user_agent: true

  29.                access_log:

  30.                - name: envoy.file_access_log

  31.                  config:

  32.                    path: /dev/stdout

  33.                    format: "[ACCESS_LOG][%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\" \"%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%\"\n"

  34.                stat_prefix: "ingress_443"

  35.                codec_type: "AUTO"

  36.                generate_request_id: true

  37.                route_config:

  38.                  name: "local_route"

  39.                  virtual_hosts:

  40.                    -

  41.                      name: "http-route"

  42.                      domains:

  43.                        - "*"

  44.                      routes:

  45.                        -

  46.                          match:

  47.                            prefix: "/"

  48.                          route:

  49.                            cluster: "service_a"

  50.                http_filters:

  51.                  -

  52.                    name: "envoy.router"

  53.  clusters:

  54.    -

  55.      name: "statsd"

  56.      connect_timeout: "0.25s"

  57.      type: "strict_dns"

  58.      lb_policy: "ROUND_ROBIN"

  59.      hosts:

  60.        -

  61.          socket_address:

  62.            address: "statsd_exporter"

  63.            port_value: 9125

  64.    -

  65.      name: "service_a"

  66.      connect_timeout: "0.25s"

  67.      type: "strict_dns"

  68.      lb_policy: "ROUND_ROBIN"

  69.      hosts:

  70.        -

  71.          socket_address:

  72.            address: "service_a_envoy"

  73.            port_value: 8786

第8-13行告訴Envoy我們需要statsd格式的指標、我們的統計信息前綴(通常是你的服務名)是什麼和statsd sink的地址。

第55-63行配置了我們的環境中的statsd sink。

這就是讓Envoy輸出統計信息所需要的所有配置。現在讓我們來看看第2-7行做了哪些事情:

  1. Envoy在9901端口暴露了一個管理端,你可以通過它動態地改變日誌級別,查看當前配置、統計數據等

  2. Envoy也可以生成與nginx類似的訪問日誌,你可以通過它瞭解服務間的通信狀況。訪問日誌的格式也是可配置的,如第29-33行

你需要將相同的配置添加到系統中的其它Envoy sidecar上(是的,每個服務都有自己的Envoy sidecar)。

這些服務本身是用go寫的,它們做的事情很簡單,僅僅是通過Envoy調用其它服務。你可以在這裡查看服務和Envoy的配置。

現在,雖然我們只有圖中的statsd exporter,但有了它,如果我們運行docker容器(docker-compose build & docker-compose up),然後向Front Envoy(localhost:8080)發送一些流量,Envoy 將把這些流量的指標發送到statsd exporter,隨後statsd exporter會把這些指標轉換成prometheus格式,並將其暴露在9102端口。

Statsd exporter中的統計信息格式如下圖所示

這裡邊將有上百個指標,同時,在上面的截圖中我們能看到Service A和Service B之間的通信延遲指標。上圖的指標是遵循prometheus格式的

  1. metric_name ["{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"] value [ timestamp ]

你可以在這裡瞭解更多。

Prometheus

我們將使用Prometheus作為時序數據庫來保存我們的指標。Prometheus不僅是一個時序數據庫,它本身還是一個監控系統,但本文我們只用它來存儲指標數據。需要注意的是,prometheus是一個通過主動拉取來獲取指標的系統,這意味著你必須告訴prometheus從何處拉取指標,在我們的例子中是從statsd exporter處拉取。

將Prometheus添加到系統中非常簡單而又直接,我們只需要將拉取目標(statsd exporter)作為配置文件傳遞給Prometheus就可以了。配置看起來是這樣的

  1. global:

  2.  scrape_interval:  15s

  3. scrape_configs:

  4.  - job_name: 'statsd'

  5.    scrape_interval: 5s

  6.    static_configs:

  7.      - targets: ['statsd_exporter:9102']

  8.        labels:

  9.          group: 'services'

scrape_interval的值表示Prometheus從目標處拉取配置的頻率。

現在啟動Prometheus,裡面應該有一些數據了。讓我們打開localhost:9090來看一看

如圖所示,可以看到我們的指標。你能做的可不僅僅是選擇已有的指標,從這裡可以閱讀關於prometheus查詢語言的更多信息。它還可以基於查詢結果繪製圖表,除此之外還有一個報警系統。

如果我們打開prometheus的targets頁面,將能看到所有的拉取目標和它們的健康狀態

Grafana

Grafana是一個很棒的監控可視化解決方案,它支持Prometheus,Graphite,InfluxDB,ElasticSearch等多種後端。

Grafana有兩大主要組件需要我們配置

(1). 數據源(Datasource):指定grafana從哪個後端獲取指標。你可以通過配置文件來配置數據源,代碼如下所示

  1. apiVersion: 1

  2. datasources:

  3.  - name: prometheus

  4.    type: prometheus

  5.    access: Server

  6.    url: http://prometheus:9090

  7.    editable: true

(2). 儀表盤(Dashboard):你可以從儀表盤查看來自數據源的指標。Grafana支持多種可視化元素,如Graphs,Single Stats,Heatmaps……你可以繼承這些元素並使用插件來構造自己的元素。

我在使用Grafana時遇到的唯一一個問題是,缺少一種標準的方法來用代碼開發那些儀表盤。所幸有一些第三方的庫提供了支持,我們將使用來自weaveworks的grafanalib。

下面是我們通過 python 代碼嘗試構建的一個儀表盤

  1. from grafanalib.core import *

  2. import os

  3. dashboard = Dashboard(

  4.    title="Services Dashboard",

  5.    templating=Templating(

  6.        [

  7.            Template(

  8.                name="source",

  9.                dataSource="prometheus",

  10.                query="metrics(.*_cluster_.*_upstream_rq_2xx)",

  11.                regex="/(.*)_cluster_.*_upstream_rq_2xx/",

  12.                default="service_a"

  13.            ),

  14.            Template(

  15.                name="destination",

  16.                dataSource="prometheus",

  17.                query="metrics(.*_cluster_.*_upstream_rq_2xx)",

  18.                regex="/.*_cluster_(.*)_upstream_rq_2xx/",

  19.                default="service_b"

  20.            )

  21.        ]

  22.    ),

  23.    rows=[

  24.        Row(

  25.            panels=[

  26.                Graph(

  27.                    title="2XX",

  28.                    transparent=True,

  29.                    dataSource="prometheus",

  30.                    targets=[

  31.                        Target(

  32.                            expr="[[source]]_cluster_[[destination]]_upstream_rq_2xx - [[source]]_cluster_[[destination]]_upstream_rq_2xx offset $__interval",

  33.                            legendFormat="2xx"

  34.                        )

  35.                    ]

  36.                ),

  37.                Graph(

  38.                    title="5XX",

  39.                    transparent=True,

  40.                    dataSource="prometheus",

  41.                    targets=[

  42.                        Target(

  43.                            expr="[[source]]_cluster_[[destination]]_upstream_rq_5xx - [[source]]_cluster_[[destination]]_upstream_rq_5xx offset $__interval",

  44.                            legendFormat="5xx"

  45.                        ),

  46.                    ]

  47.                ),

  48.                Graph(

  49.                    title="Latency",

  50.                    transparent=True,

  51.                    dataSource="prometheus",

  52.                    targets=[

  53.                        Target(

  54.                            expr="[[source]]_cluster_[[destination]]_upstream_rq_time",

  55.                            legendFormat="{{quantile}}"

  56.                        )

  57.                    ]

  58.                )

  59.            ]

  60.        ),

  61.    ]

  62. ).auto_panel_ids()

在這段代碼中,我們為2xx,5xx和延遲數據構建了圖表。其中第5-22行很重要,它從我們的設置中提取可用的service names作為grafana的變量,為我們創建一個動態的儀表盤,這意味著我們能夠選擇性地查看特定源服務和目標服務的統計數據。如果想了解更多關於變量的內容請參考這裡。

你需要通過grafanalib命令來從上述python文件生成儀表盤

  1.     generate-dashboard -o dashboard.json service-dashboard.py

注意這裡生成的dashboard.json可不容易閱讀。

所以,啟動Grafana時我們只需要傳遞儀表盤和數據源就好了。當訪問http:localhost:3000時,你將看到:

現在你應該能看到2xx,5xx和延遲的圖表,同時還能看到一個下拉菜單,你可以通過它選擇源服務和目標服務。關於Grafana還有許多內容我們沒有討論到,包括強大的查詢編輯器和告警系統。更重要的是,這一切都是可以通過插件和應用擴展的,可以參考這裡的例子。如果你正想可視化常見服務如redis,rabbitmq等的指標,grafana有一個公共儀表盤庫,你只需要導入它們就可以使用了。使用Grafana 還有一個好處,你可以通過配置文件和代碼創建和管理所有東西,而不需要過多地通過UI來操作。

我建議你試用一下prometheus和grafana以瞭解更多信息。感謝閱讀,如有建議和意見,請寫在評論中。

在這裡可以找到所有代碼和配置文件。

活動推薦

推薦閱讀

Istio

IBM Istio

  • 111 Istio

  • 118 Istio上手

  • 1115 Istio

  • 11月22日 Envoy

  • 1129 使Istio

  • 126 Istio mixer -

  • 1213 Istio

  • 1220 Istio使Serverless knative

  • IBMIstio

點擊【閱讀原文】跳轉到ServiceMesher網站上瀏覽可以查看文中的鏈接。

  • SOFAMesh(https://github.com/alipay/sofa-mesh)基於Istio的大規模服務網格解決方案

  • SOFAMosn(https://github.com/alipay/sofa-mosn)使用Go語言開發的高性能Sidecar代理

合作社區

參與社區

以下是參與ServiceMesher社區的方式,最簡單的方式是聯繫我!

  • 加入微信交流群:關注本微信公眾號後訪問主頁右下角有獲取聯繫方式按鈕,添加好友時請註明姓名-公司

  • 社區網址:http://www.servicemesher.com

  • Slack:https://servicemesher.slack.com (需要邀請才能加入)

  • GitHub:https://github.com/servicemesher

  • Istio中文文檔進度追蹤:https://github.com/servicemesher/istio-official-translation

  • Twitter: https://twitter.com/servicemesher

  • 提供文章線索與投稿:https://github.com/servicemesher/trans


閱讀原文

TAGS: