<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>prometheus on Bloggings</title>
        <link>https://theoutsidelaine.com/tags/prometheus/</link>
        <description>Recent content in prometheus on Bloggings</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Sat, 20 Apr 2024 08:00:00 +0000</lastBuildDate><atom:link href="https://theoutsidelaine.com/tags/prometheus/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Nodejs APP Monitoring with Prometheus &amp; Grafana (&amp; a little Locust)</title>
        <link>https://theoutsidelaine.com/p/nodejs-prometheus-grafana/</link>
        <pubDate>Sat, 20 Apr 2024 08:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/nodejs-prometheus-grafana/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/nodejs-prometheus-grafana/cover.png" alt="Featured image of post Nodejs APP Monitoring with Prometheus &amp; Grafana (&amp; a little Locust)" /&gt;&lt;p&gt;不論是個人專案或是工作項目，當我們完成功能、想要進一步優化系統效能，我們首先要做的事情，便是知道目前系統的表現狀況為何。表現狀況通常可以簡單分成基本與進階，基本的像是 query 頁面的時間、功能是否都健康，而進階的比如說像是當系統遇到 high traffic 時每個 service表現的狀況、系統會在哪邊出現 bottleneck etc。而一旦知道目前系統的表現，我們也可以開始進一步思考有哪一些部分可以再做得更好，比如說讓查詢效率提升、錯誤率下降etc，可以透過微調哪一些元件來達成我們的目標。&lt;/p&gt;
&lt;p&gt;有很多衡量系統效能的工具，比如 Kibana, Datadog, 以及我們今天要介紹的工具：Prometheus + Grafana。&lt;/p&gt;
&lt;h2 id=&#34;why-using-prometheus--grafana&#34;&gt;Why using Prometheus &amp;amp; Grafana&lt;/h2&gt;
&lt;p&gt;一句話來說的話：免費、開源、社群支持度強。&lt;/p&gt;
&lt;p&gt;Prometheus 最早於 2012 年由 SoundCloud 開發並開源，主要用來收集、儲存與系統有關的數據，以便監控其效能。許多的 Prometheus components 皆以 Go 撰寫，便於 static binaries 的建構以及部署。他主要有六個特點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;multi-dimensional time-series data model: Prometheus 使用一個多維度的時間序列數據模型。每個時間序列由指標名稱和一組 labels 作為唯一標識，這使得在進行查詢和分析時，可以非常靈活地做資料過濾和聚合。&lt;/li&gt;
&lt;li&gt;多種數據取得方法：Prometheus 除了可以從 server的 metrics endpoint 自 HTTP 收集數據之外，還支援 Node Exporter, Blackboc Exporter等方法。另外，Prometheus 有一個名為 Pushgateway的元件，可以透過 HTTP 去 cache 收集到的數據，然後定期的讓 Prometheus從中取得資料進行監控與存儲。&lt;/li&gt;
&lt;li&gt;PromQL(Prometheus Query Language): 一種自定義的查詢語言，提供豐富的功能，可以執行各種數據分析與操作，像是 data selection或 aggregation。&lt;/li&gt;
&lt;li&gt;警報機制：透過設定閥值，當條件被觸發，Prometheus內建的 AlertManager 就會透過比如 email, slack 等發送通知。&lt;/li&gt;
&lt;li&gt;簡單可視化metrics的 web ui: Prometheus 提供一個簡單的 web ui, 可以可視化 PromQL expressions, 並透過 table或是 graph做數據呈現。&lt;/li&gt;
&lt;li&gt;活躍的開源社群：社群會維護許多第三方 &lt;a class=&#34;link&#34; href=&#34;https://prometheus.io/docs/instrumenting/exporters/#exporters-and-integrations&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;exporters, integrators&lt;/a&gt;, 以利於 metrics 的收集。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Prometheus architecture diagram.
&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prom.png&#34;
	width=&#34;1351&#34;
	height=&#34;811&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prom_hud5ed5663cc35a882862e3f8b09e662f5_96834_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prom_hud5ed5663cc35a882862e3f8b09e662f5_96834_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Prometheus architecture diagram&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;166&#34;
		data-flex-basis=&#34;399px&#34;
	
&gt;
Credit: &lt;a class=&#34;link&#34; href=&#34;https://prometheus.io/docs/introduction/overview/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Prometheus.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Grafana 最早則於 2014 年由瑞典公司 Grafana Labs 開發與開源。Grafana 會從多種數據來源（datasources）取得資料，並提供豐富的圖表、面板、儀表板，讓這些被處理後的數據可以展示於上。數據來源可以有很多種，而 Prometheus是其中一種常見的來源。原因大概是因為 Prometheus量測的指標比如 cpu, memory, heap, event loop 都是 time series data, 而 Grafana 儀表板中的 panels 在接收格式與呈現方式上很適合這種資料；Grafana query area支援 PromQL，所以會 PromQL 的話也可以輕鬆在 query area製作 Grafana圖表。以下是 Grafana的幾個特點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;支援多種數據來源：除了 Prometheus之外，也支援時間序列型資料庫（如 Prometheus、InfluxDB、Graphite）、日誌數據（如 Elasticsearch、Loki）、關聯式資料庫（如 MySQL、PostgreSQL）等。&lt;/li&gt;
&lt;li&gt;豐富的圖表、面版：包含折線圖、柱狀圖、餅狀圖、地圖、表格等，並支援多種自定義的配置，以滿足不同的可視化需求。&lt;/li&gt;
&lt;li&gt;豐富的查詢語言：除了 PromQL 之外，也提供像是 InfluxQL等查詢語言。另外，Grafana 也有智能提示、syntax highlighting 等功能。&lt;/li&gt;
&lt;li&gt;警報機制：Grafana 內建警報功能，可以根據數據設定相關閥值，一旦條件被觸發，即可發送警報通知。Grafana 支援多種通知方式，比如 email、Slack、PagerDuty 等。&lt;/li&gt;
&lt;li&gt;活躍的開源社群：一樣，社群會維護許多第三方plugins, 便於 Grafana平台不斷的擴展。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;file-structure--docker-compose&#34;&gt;File structure &amp;amp; docker-compose&lt;/h2&gt;
&lt;p&gt;以下先用 top-down 方式簡單呈現一個 Node.js 專案中如何放入 Prometheus 與 Grafana 相關的檔案，之後再說明各個部分的細節。&lt;/p&gt;
&lt;p&gt;folder structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── client/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── server/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── grafana/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   ├── datasources/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   │   └── datasources.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   └── dashboards/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │       ├── dashboard.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │       └── dashboard-basic.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │       └── dashboard-advanced.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── prometheus/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   └── prometheus.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── prom-middleware/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   └── index.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── index.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在 local 分別起 server, prometheus, grafana 三個主要的 containers.&lt;/p&gt;
&lt;p&gt;docker-compose.yml:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;version: &amp;#39;3.8&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  server:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    build:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      context: ./server
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      dockerfile: Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - &amp;#34;127.0.0.1:4000:4000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./server:/usr/app
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - /usr/app/node_modules
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  prometheus:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    image: prom/prometheus:v2.20.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    container_name: prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./server/prometheus:/etc/prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - prometheus_data:/prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - &amp;#34;9090:9090&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    expose:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - 9090
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - monitoring
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  grafana:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    image: grafana/grafana:7.1.5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    container_name: grafana
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./server/grafana/provisioning:/etc/grafana/provisioning
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - grafana_data:/var/lib/grafana
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    environment:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - GF_AUTH_DISABLE_LOGIN_FORM=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - GF_AUTH_ANONYMOUS_ENABLED=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - &amp;#34;3000:3000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    expose:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - 3000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - monitoring
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  monitoring:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    driver: bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  db_data:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  prometheus_data: {}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  grafana_data: {}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;prometheus-configuration&#34;&gt;Prometheus configuration&lt;/h2&gt;
&lt;h3 id=&#34;1-prometheusyml&#34;&gt;1. &lt;code&gt;prometheus.yml&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;global:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  scrape_interval: 5s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;scrape_configs:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - job_name: &amp;#34;synoptic-app&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    static_configs:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - targets: [&amp;#34;docker.for.mac.localhost:4000&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Prometheus 會根據你設定的 scrape_interval 以固定的時間間隔擷取資料。scrape_configs 用於設定不同 job 的任務目標，此處意指從 target 的 [address:port number] 處擷取資料。&lt;/p&gt;
&lt;p&gt;值得注意的是：targets 的bracket中，由於我們使用docker，且運行於 mac上，所以冒號前者的 value 會是 &lt;code&gt;docker.for.mac.localhost&lt;/code&gt;。我在一開始把位置誤寫成 server container name, 導致 prometheus 遲遲無法取得送到 localhost:4000的資料。&lt;/p&gt;
&lt;h3 id=&#34;2-prom-client&#34;&gt;2. &lt;code&gt;prom-client&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;接著便要開始利用 Prometheus 自 APP server 擷取資料。我們需要將 APP server與&lt;a class=&#34;link&#34; href=&#34;https://prometheus.io/docs/instrumenting/exporters/#exporters-and-integrations&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt; 許多metrics exporter &lt;/a&gt;連接，並透過這些 exporters 傳送資料至 Prometheus server 讓其存儲與監控 APP 的表現資料。&lt;/p&gt;
&lt;p&gt;Express APP 會透過 &lt;code&gt;prom-client&lt;/code&gt; 這個 nodejs library 將 metrics export出去。安裝方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install prom-client
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;完成後，在 &lt;code&gt;index.js&lt;/code&gt; 我們可以開始使用 prom-client 去協助我們取得 APP 中的各種數據。在 Prometheus 中開發者重視的數據基本上可以分成三種：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;defaultMetrics&lt;/strong&gt;: Prometheus 官方推薦了一系列值得觀測的指標，在 prom-client中，這些指標被匯總成為「defaultMetrics」。例子： node_process_cpu_system_seconds, nodejs_eventloop_lag_max_seconds, node_process_heap_bytes 等。這些指標會自帶像是 nodejs, node 等 prefix。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;http_request_duration_seconds&lt;/strong&gt;: 這其實是一個主要功能是計時器的 histogramtimer 指標而已，然後我們把它命名為 http_request_duration_seconds。具體來說，時間對於 routing 來說是最基本的表現指標，因此，這個 metric 最主要的目的是協助我們知道每一個 route 的 http request time performances，透過設定這個指標加上撰寫相關的 PromQL，我們可以知道每一個 route的 response time等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;custom metrics&lt;/strong&gt;: Prometheus 提供開發者自定義四大類 custom metrics: Histogram, Summary, Counter, Gauge. 簡單說明一下每一個指標的定義與用途：
&lt;ul&gt;
&lt;li&gt;Histogram: 直方圖，用於監控某個指標的分布狀況。Eg: Request time的時間分佈。&lt;/li&gt;
&lt;li&gt;Summary: 摘要，用於監控某個指標的分布狀況，特別是其詳細的統計分佈區間，比如 max, min, ?% quantile。Eg: Request time的時間分佈摘要。&lt;/li&gt;
&lt;li&gt;Counter: 計數器，用於監控一個 service 或者 event 的發生次數。Eg: 某 request 的 發生次數。&lt;/li&gt;
&lt;li&gt;Gauge: 計量器，用於監控一個隨時間而變化的數值。Eg: Memory usage。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這邊我們主要針對 defaultMetrics 與 http_request_duration_seconds 去監控。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const client = require(&amp;#39;prom-client&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const register = new client.Registry();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;client.collectDefaultMetrics({
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  app: &amp;#39;synoptic-monitoring-app&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  prefix: &amp;#39;node_&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  timeout: 10000,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  gcDurationBuckets: [0.001, 0.01, 0.05, 0.1, 0.5, 1, 2, 5], // These are the default buckets.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  register,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;「Registry」是 Prometheus 數據存處所在之處，我們會 init 一個 registry instance，然後將收集到的 defaultMetrics 數據傳入 registry 中。&lt;code&gt;collectDefaultMetrics()&lt;/code&gt; 提供其他客製化的選項，像是 &lt;code&gt;prefix&lt;/code&gt; 可以在每個 default metrics前面加上前綴、&lt;code&gt;timeout&lt;/code&gt; 可以客製化 timeout 時長、&lt;code&gt;gcDurationBuckets&lt;/code&gt; 則是設定 Garbage Collection Histogram(垃圾回收行為監控)的 bucket 寬度。最後再將 register instance 也放入 options 中完成註冊。&lt;/p&gt;
&lt;h3 id=&#34;3-prom-middleware&#34;&gt;3. prom-middleware&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const express = require(&amp;#39;express&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const app = express();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app.use(httpRequestHistogramMiddleware(register));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;當 defaultMetrics 已經註冊於 register，我們需要將另一個重要指標 http_request_duration_seconds 也放到 register 中。這邊我使用的方法是簡單寫一個 middleware，讓每一個 route 都可以被其經過，並被紀錄 request 打入後 response time 與 response status code。&lt;/p&gt;
&lt;p&gt;middleware 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const promClient = require(&amp;#39;prom-client&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const onFinished = require(&amp;#39;on-finished&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const httpRequestHistogramMiddleware = (register) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  const httpRequestDurationMicroseconds = new promClient.Histogram({
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    name: &amp;#39;http_request_duration_seconds&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    help: &amp;#39;Duration of HTTP requests in seconds&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    labelNames: [&amp;#39;method&amp;#39;, &amp;#39;route&amp;#39;, &amp;#39;code&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    buckets: [0.01, 0.05, 0.1, 0.3, 0.5, 0.7, 1, 5, 7, 10],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    registers: [register],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  return (req, res, next) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    const end = httpRequestDurationMicroseconds.startTimer();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    onFinished(res, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      end({ method: req.method, route: req.route.path, code: res.statusCode });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    next();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;module.exports = httpRequestHistogramMiddleware;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;從這兩段程式碼我們可以發現，基本上我們做的事情如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1) 創建一個 middleware，其中 new 一個 histogram，把它命名為 http_request_duration_seconds，並將其註冊於 register。&lt;/li&gt;
&lt;li&gt;(2) 在我們的 main app 中，使用 app.use() 讓之後出現的 routes 都可以經過它。值得注意的是，如果你的 express app 有使用 session，必須確保 Prometheus configuration 發生於 session 使用之前、routes註冊之前。&lt;/li&gt;
&lt;li&gt;(3) 當 route 被打時，計時器被啟動，將數據記錄於 histogram，當 route結束時，紀錄 method, route, code。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app.get(&amp;#39;/metrics&amp;#39;, async (req, res) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  try {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    const metrics = await register.metrics();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    res.setHeader(&amp;#39;Content-Type&amp;#39;, register.contentType);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    res.end(metrics);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  } catch (error) {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    console.error(&amp;#39;Error generating metrics:&amp;#39;, error);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    res.status(500).json({ error: &amp;#39;Internal server error from Prometheus&amp;#39; });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app.listen(port, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;console.log(`Hello server ${port} port.`);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;最後，創建一個 &lt;code&gt;/metrics&lt;/code&gt; endpoint，當 app 被使用，app會將register收集到的 metrics() 返還。由於 metrics() 會返回一個 Promise, 所以必須使用 async-await 來取得數據結果。&lt;/p&gt;
&lt;h2 id=&#34;grafana-configuration&#34;&gt;Grafana configuration&lt;/h2&gt;
&lt;h3 id=&#34;1-datasourcesyml&#34;&gt;1. &lt;code&gt;datasources.yml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;如同前面介紹所說，grafana 從 datasources取得數據呈現於儀表板上，所以這個 yaml 主要用於 configure datasource 來源。以下為 yaml檔內容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apiVersion: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;datasources:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - name: Prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    type: prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    access: proxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    orgId: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    url: http://prometheus:9090
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    basicAuth: false
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    isDefault: true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    editable: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;configure後，grafana container 會自動註冊 http://prometheus:9090 於 Settings, 不需額外登入 grafana 手動註冊。&lt;/p&gt;
&lt;h3 id=&#34;2-dashboards&#34;&gt;2. dashboards&lt;/h3&gt;
&lt;p&gt;dashboards 為定義 grafana 儀表板的內容，基本上你可以在 grafana container 起起來之後手動拉儀表板，也可以使用 predefined dashboards，使 container 起起來之後快速的看到儀表板。&lt;/p&gt;
&lt;p&gt;這邊借用 &lt;a class=&#34;link&#34; href=&#34;https://github.com/StackAbuse/node-prometheus-grafana/tree/master/grafana/provisioning/dashboards&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Stack Abuse 網站創建的超完整儀表板&lt;/a&gt;。基本上他建立了四種不同的儀表板，當 Prometheus 數據進來之後，可以依此監控各種目標。四個儀表板分別是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Node Application Dashboard: 監控如 Process CPU Usage, Process Memory Usage等基本 metrics.&lt;/li&gt;
&lt;li&gt;Node Service Level Metrics Dashboard: 以 route/service 為單位，監控每隻 route的 Number of request, error rate, request處理時間等。&lt;/li&gt;
&lt;li&gt;Node Request Flow Dashboard: 以 request 為基礎，監控 request的各種面向，如時長 (duration)，並用各種圖表可視化。&lt;/li&gt;
&lt;li&gt;High Level Application Metrics: 監控整體app的 throughput, response time等。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外，dashboard 也需要有 yaml 做 configuration. 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apiVersion: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;providers:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- name: &amp;#39;Prometheus&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  orgId: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  folder: &amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  type: file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  disableDeletion: false
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  editable: true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  options:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    path: /etc/grafana/provisioning/dashboards
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;showcase-each-part&#34;&gt;Showcase Each Part&lt;/h2&gt;
&lt;p&gt;當我們成功 configure Prometheus, Grafana，我們便可以看到以下幾個畫面:&lt;/p&gt;
&lt;h3 id=&#34;1-on-localhostportmetrics&#34;&gt;1. on &lt;code&gt;localhost:{port}/metrics&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;main app metrics endpoint 可以看到許多筆監控數據。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/metrics.png&#34;
	width=&#34;1224&#34;
	height=&#34;664&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/metrics_hud8081b730284b09239c539a7e012321b_152701_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/metrics_hud8081b730284b09239c539a7e012321b_152701_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;app metrics endpoint exposes service monitoring data&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;184&#34;
		data-flex-basis=&#34;442px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;2-on-localhost9090&#34;&gt;2. on &lt;code&gt;localhost:9090&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Prometheus 走 9090 port，有一個簡易 web UI, 讓我們用簡單的 table / graph, 看到這些數據隨時間變化的樣子。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prometheus.png&#34;
	width=&#34;1692&#34;
	height=&#34;883&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prometheus_hu1a2cc50b53e456f684ce571b3b596f4b_107549_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prometheus_hu1a2cc50b53e456f684ce571b3b596f4b_107549_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Prometheus web UI&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;191&#34;
		data-flex-basis=&#34;459px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;3-on-localhost3000&#34;&gt;3. on &lt;code&gt;localhost:3000&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Grafana 走 3000 port，透過 dashboard folder中定義的 dashboard.json，直接 config 出儀表板。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana.png&#34;
	width=&#34;1701&#34;
	height=&#34;985&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana_hu833af1013c844be5794df5931d274c43_281920_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana_hu833af1013c844be5794df5931d274c43_281920_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Grafana Dashboard&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;172&#34;
		data-flex-basis=&#34;414px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana2.png&#34;
	width=&#34;1704&#34;
	height=&#34;994&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana2_hu7d6e6064c60fd70be7c941f19cc8cf2c_224286_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana2_hu7d6e6064c60fd70be7c941f19cc8cf2c_224286_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Grafana Dashboard2&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;171&#34;
		data-flex-basis=&#34;411px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;test-the-monitoring-results&#34;&gt;Test the monitoring results&lt;/h2&gt;
&lt;p&gt;基本指標如 cpu, memory, event loop只要 app在動都可以自動進行監測，不過像是 service routes 沒有打的話我們基本上不會從 Prometheus 與 Grafana上知道他們的 response time。因此，這邊便需要借助壓力測試工具 &lt;code&gt;locust&lt;/code&gt; 來協助我們完成這個目標。&lt;/p&gt;
&lt;p&gt;Locust 是一個以 Python 編寫的開源壓力測試工具，可以執行 app defined actions，比如發起 http request, 查詢資料庫並操作回傳資料等。目的是評估 web app 在高流量下的效能。&lt;/p&gt;
&lt;p&gt;以下說明使用 Locust 的步驟：&lt;/p&gt;
&lt;h3 id=&#34;1-build-up-virtual-environment&#34;&gt;1. build up virtual environment&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python3 -m venv locust_venv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;source locust_venv/bin/activate 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;2-install-locust&#34;&gt;2. install locust&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;3-write-indexpy-and-run-it&#34;&gt;3. write &lt;code&gt;index.py&lt;/code&gt; and run it&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;from locust import HttpUser, task, between
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;class BrowsingUser(HttpUser):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    wait_time = between(1, 2)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    host = &amp;#34;http://localhost:4000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @task
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    def get_products(self):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        self.client.get(&amp;#34;/api/v1/products/women/?paging=1&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;class CheckoutUser(HttpUser):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    wait_time = between(1, 3)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    host = &amp;#34;http://localhost:4000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @task
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    def browse_product_details(self):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        self.client.get(&amp;#34;/api/v1/products/details?id=64&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;這邊假設有兩種 user action: BrowsingUser, 瀏覽主頁面的 /products/women/?paging=1。CheckoutUser, 在結帳之前會點擊某個商品頁面 /products/details?id=64。然後我們使用以下執行執行這兩個用戶行為模擬：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;locust -f index.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著便可以在 http://0.0.0.0:8089/ 看到 Locust Dashboard了。可以指定使用者數量、每秒生成新用戶的速率（ramp up）。由於我們有兩個 user, 所以這邊設定使用者數量=2。
&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust1.png&#34;
	width=&#34;1695&#34;
	height=&#34;582&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust1_hu7a6d3b7699e02772fbf01f34a11940d2_69619_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust1_hu7a6d3b7699e02772fbf01f34a11940d2_69619_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Locust Dashboard&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;291&#34;
		data-flex-basis=&#34;698px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;4-observations&#34;&gt;4. observations&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result1.png&#34;
	width=&#34;1613&#34;
	height=&#34;555&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result1_huf0b2ef071943cb4d9579a9ebc59ad6ce_82487_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result1_huf0b2ef071943cb4d9579a9ebc59ad6ce_82487_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Locust Result: in table view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;290&#34;
		data-flex-basis=&#34;697px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result2.png&#34;
	width=&#34;1608&#34;
	height=&#34;1033&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result2_huc60787304d7be5fef06b8a113640391f_133773_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result2_huc60787304d7be5fef06b8a113640391f_133773_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Locust Result: in graph view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;155&#34;
		data-flex-basis=&#34;373px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;當同時啟動 server / prometheus / grafana container, 還可以把這些打 api的結果實時的傳送給 grafana dashboards。&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;透過這個簡單的練習，我們知道如何將一個 Node.js App 串接 Prometheus 與 Grafana，以及使用簡單的 locust load test觀察兩隻api的表現。日後可以思考的點是：要怎麼觀察出「異常」與「正常」之差異，並且根據異常狀態設計方法，真正達成系統效能優化的目標。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
