11 months ago

以往執行Nodejs的web service時,日誌都是用winston或是pm2本身的log做紀錄,一直都是等到有問題才去查;
但我一直希望有更詳細的即時系統監控,包含每日錯誤紀錄、每個URL處理時數(morgan紀錄版),最近打算用influxDB這個Time Series DB(時序性資料庫)與其生態圈完成這項任務。

任務具體包含:

  1. Nodejs koa中間件將資料寫入influxDB
  2. 本地端瀏覽圖表化資料

influxDB

參考官方教學,以下筆記壹些重點

安裝

主機為 Ubuntu 16.04,首先安裝influxDB

> curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
> source /etc/lsb-release
> echo "deb https://repos.influxdata.com/ubuntu xenial stable" | sudo tee -a /etc/apt/sources.list
> sudo apt-get update && sudo apt-get install influxdb
> sudo service influxdb start

注意,influxDB預設使用port 8083(web 操作介面) / 8086(HTTP API),正常安裝後可以用瀏覽器詢問{host_url}:8083看是否正常啟動。
另外,因為我在Ubuntu上跑zsh,所以用官方的安裝指南會出錯,這是我另外找到的安裝指令,這樣才能安裝最新版的influxDB。

接著安裝Grafana - InfluxDB的插件,讓下Query更加直覺

> wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.4.3_amd64.deb 
> sudo dpkg -i grafana_4.4.3_amd64.deb
> sudo service grafana-server start

grafana預設使用port 3000,正常安裝後可以用瀏覽器詢問{host_url}:3000,並用預設的 admin/admin登入。

influxDB核心觀念

一開始我在評估 Time Series DB和以往的RBDMS、NoSQL到底有什麼差別,我一樣可以在MySQL、MongoDB依照時序插入數據,為什麼要在學一款新的influxDB呢?
抱持著這樣的疑惑,我看到這篇文章 medium: Time-series data: Why (and how) to use a relational database instead of NoSQL,RDBMS的優勢顯然在於確保資料的正確關聯性、複雜的Join等、而NoSQL可以應付大量的讀寫但也有著吃更大量Memory、Secondary index支援不佳等問題。
回過頭來看時序性資料的應用場景,如IoT資料蒐集、金融交易資料、系統性能監控等,這些場景的特性都是依照時間大量寫入,所以在influxDB文件中也有寫到

The result is that InfluxDB is not a full CRUD database but more like a CR-ud, prioritizing the performance of creating and reading data over update and destroy
influxDB專注於讀寫,雖然也有支援更新與刪除但效能就相對不佳。

最後放一張文章裡的圖片,用大量寫比對TimesScaleDB與PostgreSQL,量大後性能差十分驚人

influxDB操作

首先操作influxDB

// 進入DB Shell
> influxd 
> CREATE DATABASE mydb
> SHOW DATABASES
> USE mydb
> INSERT cpu,host=serverA,region=us_west value=0.61
> SELECT "host", "region", "value" FROM "cpu"

influxDB的類SQL語法(InfluxQL)跟SQL非常相近,可以參考官方文件Comparison to SQL,其中有幾個對應的術語
measurement -> table; tag -> indexed columns; fields -> unindexed columns;points -> rows
回過頭來看INSERT cpu,host=serverA,region=us_west value=0.61

cpu -> measurement
tag為K-V值,所以 host:serverA、region:us_west都是tag,需注意tag的KV都是字串!
field也是K-V值,但是整筆資料中只能有一個field,且K為字串而V可以是非字串(預設為float,也可以是boolean等),如 value:0.61

field除了整筆唯一與V的資料型態比較多變外,含支援influxQL的內建函示如(SUM/MIN/...)等;
tag用途主要是識別資料的屬性,像是server name/ 地區等等,通常適用於WHERE條件式中。

另外一個重點是,influxDB是schemaless,所以在插入前並不需要先創建measurement,同時之後也可以隨意增加tag的數量,但是有一點特別注意 插入的field後資料型態不能改變,不然會出錯;且每筆資料一定要有field

我先插入float再換成boolean是不被允許的
> INSERT cpu,host=serverA,region=us_west value=0.61
> INSERT cpu,host=serverA,region=us_west value=true
存成其他field名稱就可以接受
> INSERT cpu,host=serverA,region=us_west value1=true
Garfana設定

回到{host_url}:3000登入Garfana,接著要先創建Data Source,也就是influxDB的連線,如圖下


成功創建後,接著就到Dashboard創建圖表,先按下+Add Row接著選擇圖表形式,我是先選擇"Graph",接著點擊Title也就是圖下"cpu比較"的位置,接著選擇"Edit" -> "Metrics",就會顯示類似於下圖的樣式。
很明顯圖表的資料就是來自於influxQL,這裡很棒的地方在於點選欄位就會跳出下拉選單選擇值,非常方便使用,稍微把玩一下其實他的UI/UX蠻好理解的;

Alert

監控系統最重要的一步就是在出錯時發出警訊,Grafana在v4.0時加入了警訊的功能,支援多種通知方式如webhook、LINE(沒錯就是熊大)、Slack、Email等多種方式。
以下分享Email的設定方式,因為Email寄送要透過SMTP協定,這時候要修改設定檔

> sudo nano /etc/grafana/grafana.ini
在文件中找到以下
#################################### SMTP / Emailing ##########################
[smtp]
enabled = true
host = smtp.mail.yahoo.com:465
user = 帳號@yahoo.com
# If the password contains # or ; you have to wrap it with trippel quotes. Ex """#password;"""
password = """密碼"""
cert_file =
key_file =
skip_verify = false
from_address = 帳號@yahoo.com
from_name = events

welcome_email_on_sign_up = false
templates_pattern = emails/*.html

需注意 user和from_address需相同,另外一個坑就是我用gmail無法發送,調了各種gmail帳號安全設定依然無法,建議用yahoo一試就成功,不過要記得開啟 允許使用登入方式較不安全的應用程式
接著不忘重啟服務sudo service grafana-server restart

加入Collectd做系統功能蒐集

Collectd會自動定時到系統中蒐集如cpu/memory等資料,甚至通過plugin可以蒐集mysql/nginx/apache/redis等常用服務的數據,相當的方便!
這裡會介紹如何在influxDB蒐集Collectd的資料,資料參考 Monitoring with Collectd, InfluxDB and Grafana,我自己成功的圖樣

> sudo apt-get install collectd
接著修改設定檔
> sudo vim /etc/collectd/collectd.conf
會在文件中看到這幾個字樣,把前面的#去除
Hostname "localhost"
#FQDNLookup true
BaseDir "/var/lib/collectd"
PluginDir "/usr/lib/collectd"
TypesDB "/usr/share/collectd/types.db"

接著你會看到一整排的Plugin,把想要開啟的一樣#去除,並加入<Plugin>設定
LoadPlugin syslog
<Plugin syslog>
        LogLevel info
</Plugin>

LoadPlugin cpu
LoadPlugin cpufreq
LoadPlugin memory
LoadPlugin swap
LoadPlugin rrdtool

// 這個要加,因為是要讀取Collectd的數據
LoadPlugin network
<Plugin "network">
    Server "localhost" "25826"
</Plugin>

// 這些是我自己設定的,可以不開
LoadPlugin nginx
LoadPlugin redis

<Plugin nginx>
        URL "http://127.0.0.1/nginx_status?auto"
        # 其他不要管
</Plugin>
<Plugin redis>
        <Node example>
                Host "localhost"
                Port "6379"
                Timeout 2000
        </Node>
</Plugin>

nginx比較麻煩點,還要改設定檔

> nginx -V 2>&1 | grep -o with-http_stub_status_module
// 會顯示 "with-http_stub_status_module" 就代表成功,這主要是將Nginx輸出導致其他地方
> sudo vim /etc/nginx/sites-available/default
// 在文件最一開始加入
server {
    location /nginx_status {
        stub_status on;

        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}

> sudo service nginx restart
// 重啟服務
> curl http://127.0.0.1/nginx_status
// 檢查設定是否正確,正確會回傳類似
Active connections: 2
server accepts handled requests
 99 99 1479
Reading: 0 Writing: 1 Waiting: 1

最後修改influxDB的設定檔,啟動collectd

> sudo vim /etc/influxdb/influxdb.conf
// 找到下列的字段,依樣去除註解
[[collectd]]
  enabled = true
  bind-address = ":25826" # the bind address
  database = "collectd" # Name of the database that will be written to
  retention-policy = ""
  batch-size = 5000 # will flush if this many points get buffered
  batch-pending = 10 # number of batches that may be pending in memory
  batch-timeout = "10s"
  read-buffer = 0 # UDP read buffer size, 0 means to use OS default
  typesdb = "/usr/share/collectd/types.db"
  
> influx
// 接著進入influxDB Shell,創建剛才文件的collectd 專用的 database
> create database collectd

接著,我建議先用influxDB的client {host_url}:8083比較清楚,此時database選項中會出現collectd,接著可以看measurements中有哪些欄位,最後再到Grafana設定圖表就完成囉~

有一個小小的地方要注意,在Garfana的influxSQL時要記得選WHERE type_instance = ....,我一開始沒有選想說cpu_value跑出來的值好奇怪。

NodeJS - 插入influxDB類似Morgan紀錄與錯誤Log

網路上有nodejs的influxDB package,但簡易起見我還是以RESTFul API方式寫入數據,以下程式碼供參考。

// 類morgan用法,但log發送至influxDB

app.use(async (ctx, next) => {
      ctx.request._startTime = new moment()
      await next();
      let responseTime = new moment() - new moment(ctx.request._startTime) // ms

      axios.post(local.model.influx.host + "write?db=" + local.model.influx.database,
        'url_log,method=' + ctx.method +',path=' + ctx.path + ',status=' + ctx.status + ',ip=' + ctx.ip + ' value=' + responseTime
      , {
        header:{
          'Content-Type': 'application/octet-stream'
        }
      }).then().catch(err => {
        console.error("influxDB wrong", err)
      })
})
其他參考資料

工具介紹 利用 collectd + InfluxDB + Grafana 監測系統效能

← 粉絲專頁留言自動回話與啟動Messenger私訊 HTTP2與HTTPS載入靜態資源速度實測 →
 
comments powered by Disqus