搜尋此網誌

2016年9月8日 星期四

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

傳統上用來監測網路流量、系統效能數據的常用工具多以 RRD 為基本的技術,這類工具包含 MRTG 與 Cacti。RRD 有幾個明顯的缺點,其中一個就是資料的解析度會隨著時間的過去而降低。

例如在 Cacti 中,過去 24 小時的流量圖表是長這樣:

image

但是一周前的 24 小時的流量圖表就變成這般可怕的模樣:

image

當你不幸地需要歷史數據資料時,別說要如何進行分析了,光拿出這種圖表就需要莫大的勇氣。

在講究高清不失真的這個年代,以 RRD 為基礎的工具已經漸漸不符合我們所需。

目前比較常見用來記錄時間數據的工具,應以 ELK stack 為首。ELK stack 以 Elasticsearch 為核心,可用來分析各式各樣的資料。這個特性是它的強項,不過在某些時候卻也變成它的弱點 (過於複雜)。而我今天要介紹的 collectd + InfluxDB + Grafana,正是以處理時間數據為主,所以在製作與時間數據相關的圖表時會更為簡化。
Grafana 本身類似 ELK 的 Kibana,主要作為圖表呈現的部分。而資料來源的部分,除了 InfluxDB 之外,像是 Graphite、OpenTSDB、甚至是 Elasticsearch 也都可以支援。在此架構中,collectd 會負責收集作業系統的效能數據,並將之”餵進” InfluxDB,最後再透過 Grafana 的圖表予以呈現。

在今天的範例中,我將示範如何在 CentOS 7 中利用 collectd + InfluxDB + Grafana 建置一個呈現系統效能數據的儀表板。需特別注意的是,為了簡化範例的說明,所有的套件皆安裝在同一個系統 (IP 位址為 192.168.111.241) 下。而在實務上,這三個套件通常應分散在不同的系統上。

安裝 InfluxDB

  1. 透過官方 RPM Repo 安裝 InfluxDB 套件
  2. cat<<EOF>/etc/yum.repos.d/influxdb.repo
    [influxdb]
    name = InfluxDB Repository - RHEL $releasever
    baseurl = https://repos.influxdata.com/rhel/\$releasever/\$basearch/stable
    enabled = 1
    gpgcheck = 1
    gpgkey = https://repos.influxdata.com/influxdb.key
    EOF yum install influxdb -y systemctl enable influxdb.service systemctl start influxdb.service
  3. 配置防火牆
  4. cat<<EOF>/etc/firewalld/services/influxdb.xml
    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>InfluxDB</short>
      <description>InfluxDB is an open source time series database with no external dependencies. It's useful for recording metrics, events, and performing analytics.</description>
      <port protocol="tcp" port="8083"/>
      <port protocol="tcp" port="8086"/>
    </service>
    EOF chmod 600 /etc/firewalld/services/influxdb.xml firewall-cmd --add-service=influxdb --permanent firewall-cmd --reload
  5. 透過指令 influx 進入 influxdb 命令列模式,並輸入下列命令創建管理者帳密
  6. create user admin with password 'password' with all privileges;  
    exit
  7. 利用瀏覽器連結 influxdb 管理介面,網址是 http://192.168.111.241:8083,應可看到類似下列畫面
    0001

安裝 Grafana

  1. 透過官方 RPM Repo 安裝 Grafana 套件
  2. cat<<EOF>/etc/yum.repos.d/grafana.repo
    [grafana]
    name=grafana
    baseurl=https://packagecloud.io/grafana/stable/el/6/\$basearch
    repo_gpgcheck=1
    enabled=1
    gpgcheck=1
    gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana
    sslverify=1
    sslcacert=/etc/pki/tls/certs/ca-bundle.crt
    EOF yum install grafana –y systemctl enable grafana-server.service systemctl start grafana-server.service
  3. 配置防火牆
  4. cat<<EOF>/etc/firewalld/services/grafana.xml
    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>Grafana</short>
      <description>Grafana provides a powerful and elegant way to create, explore, and share dashboards and data with your team and the world.</description>
      <port protocol="tcp" port="3000"/>
    </service>
    EOF chmod 600 /etc/firewalld/services/grafana.xml firewall-cmd --add-service=grafana --permanent firewall-cmd --reload

 

設定 influxdb collectd plugin

  1. 安裝 collectd 套件
  2. yum install collectd -y
  3. 修改檔案 /etc/influxdb/influxdb.conf,將 [[collectd]] 區塊的
    enabled = false
    改為
    enabled = true
    bind-address = ":25826" # the bind address
    database = "collections" # 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" #enabled = false
  4. 重新啟動服務
  5. systemctl restart influxdb.service
  6. 配置防火牆
  7. cat<<EOF>/etc/firewalld/services/collectd.xml
    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>collectd</short>
      <description>collectd daemon for InfluxDB</description>
      <port protocol="udp" port="25826"/>
    </service>
    EOF firewall-cmd --add-service=collectd --permanent firewall-cmd --reload
  8. 透過指令 influx 進入 influxdb 命令列模式,並輸入下列命令創建資料庫與存取帳密
  9. CREATE DATABASE collections
    CREATE USER collectdrw WITH PASSWORD 'readwrite'
    GRANT ALL ON collections to collectdrw
    CREATE USER collectdread WITH PASSWORD 'readonly'
    GRANT READ ON collections TO collectdread
    exit

設定 collectd for influxdb

這個動作需在需收集效能數據的各主機上分別執行。
  1. 設定 collectd 服務所需的外掛。在此範例中我們沒有另外新增數據收集的外掛,僅增加了將資料送往 InfluxDB 的外掛。
  2. cat<<EOF>/etc/collectd.d/network.conf
    Hostname    "grafana"
    FQDNLookup  false
    LoadPlugin  network
    LoadPlugin  uptime
    <Plugin network>
      Server "192.168.111.241" "25826"
    </Plugin>
    EOF
    systemctl enable collectd.service
    systemctl start collectd.service
  3. 回到 influxdb 的管理介面,重整後在 Database 下拉式選單中應可看到多了一個名為 collections 的資料庫。
    0002
  4. 選取 collections 後,在 Query Templates 中選取 Show Measurements。
    0003
  5. 按下 Enter 後,應可看到類似下面的結果,表示 collections 中已有 cpu_value 等數據。
    0004
  6. 接著,我們輸入 SHOW TAG KEYS FROM "cpu_value" 並按下 Enter,應可看到類似下列畫面。結果顯示 cpu_value 包含四個 key,分別為 host、instance、type 以及 type_instance。
    0005
  7. 接著,我們輸入 SHOW TAG VALUES FROM "cpu_value" WITH KEY = "host" 並按下 Enter,應可看到類似下列畫面。結果顯示 host 這個 key 的內容僅包含 grafana,也就是我們目前唯一已經設定過的 collectd 服務主機名稱。
    0006
  8. 接著,我們輸入 SHOW TAG VALUES FROM "cpu_value" WITH KEY = "host" 並按下 Enter,應可看到類似下列畫面。結果顯示 InfluxDB 已確實收到來自於 collectd 服務的效能數據。
    0007

設定 Grafana Dashboard

  1. 利用瀏覽器連結 Grafana 管理介面,網址是 http://192.168.111.241:3000,應可看到類似下列畫面
    0008
  2. 使用預設帳密 admin / admin 登入系統。
  3. 點選左上角的圖示後會出現主要選單,點選 admin 後可以修改基本資訊與密碼。
    0009
  4. 同樣利用主選單的 Data Sources 選項,進入後按下右上角的 "+Add data source" 按鈕。
  5. 分別填入下列資訊,其中 Url 欄位因為我們選取 direct 的存取方式,所以不能使用預設的 http://localhost:8086,而必須指定為 http://192.168.111.241:8086
    • Name: Collectd
    • Default: 選取與否皆可
    • Type: InfluxDB
    • Url: http://192.168.111.241:8086
    • Access:direct
    • Http Auth: 兩個選項皆不用選取
    • Database: collections
    • User: collectdread
    • Password: readonly
    • Default group by time: 保留空白
  6. 填寫完畢後按下 "Add" 按鈕。如果 Data Source 可以正常連結的話會出現類似下列的成功畫面,否則會出現錯誤訊息。
    0010
  7. 利用主選單的 Dashboards > + New 開始新增儀表板。
  8. 利用 Manage dashboard 的 Settings 功能可修改儀表板的相關設定。
    0011
  9. 在此範例中我們僅設定名稱即可,將之設定為 System Information,設定完畢後按下右上方的 X 即可回到 Dashboard 的畫面。
    0013
  10. 點選左上方的綠色區塊後會出現 ROW 功能選單,選取 Add Panel > Graph。
    0012
  11. Graph 有多個頁籤設定,其中 Metrics 可用來配置資料相關設定。首先,我們須將 Panel data source 改為 Collectd。
  12. 此時我們會發現 Metrics A 內容有所改變,點選後出現圖形化編輯選項。
    0015
  13. Grafana 的圖形化編輯功能相當方便,不過為了方便說明,所以我們透過 Toggle Edit Mode 進入直接編輯模式。
    0016
  14. 輸入下列內容
    SELECT last("value") FROM "load_shortterm" WHERE "host" = 'grafana' AND $timeFilter GROUP BY time($interval) fill(null)
    0017
  15. 透過 "+ Add query" 按鈕另外新增兩個 Metrics,內容分別為
    SELECT last("value") FROM "load_midterm" WHERE "host" = 'grafana' AND $timeFilter GROUP BY time($interval) fill(null)
    SELECT last("value") FROM "load_longterm" WHERE "host" = 'grafana' AND $timeFilter GROUP BY time($interval) fill(null)
    0018
  16. 切換至 General 頁籤,將 Panel 的標題修改為 Load。
    0019
  17. 切換到 Legend 頁籤,勾選 Min、Max、Avg 以及 Current。
    0020
  18. 設定完畢後按下右方的 X 即可回到 Dashboard 畫面,然後按下 CTRL+S 將 Dashboard 予以儲存。
  19. 之後就可以透過 Dashboard 下拉式選單快速連結至我們剛剛新增的 System Information Dashboard 了。
    0021

透過幾個簡單的設定,我們就建立了一個系統負載的數據圖表。

下一步

Grafana 除了圖形類 (Graph) 的報表之外,另有提供表格式 (Table) 、單一數值  (Singlestat) 與其他種類的報表。
此外,我們的範例是針對特定的單一主機來進行配置。但是我們通常不會只有管理一台主機,此時就可以透過 Dashboard 的 Templating 機制設定一個變數用來代表主機名稱,然後將 Metrics 的條件指定為此一變數,而非原先固定的主機名稱。如此一來,Dashboard 就會提供一個所有主機名稱的下拉式選單,方便我們觀看個別主機的相關數據。

盡管如此,我們會發現自己要從無到有刻出一個合用且美觀的報表其實也不是那麼簡單的事情。幸好 Grafana  有提供儀表板匯出/匯入的功能,而且有善心人士已經將一些常見的效能數據儀表板匯出並提供下載 (清單在此)。不過在選用這些儀表板時,需特別注意的是它使用的資料來源是哪一種 (InfluxDB、Graphtie 或是其他的資料來源),以免發生牛頭不對馬嘴的狀況。

2016年9月4日 星期日

[工具介紹] 以 Apache myfixip 模組支援 Proxy Protocol

我在前一篇文章中提到,HAProxy 推出 Proxy Protocol 用以解決在代理伺服器架構下無法取得用戶端 IP 位址的困境。在這篇文章中,我將以 HAProxy 搭配 Apache + myfixip 模組,實際展示如何透過 Proxy Protocol 解決用戶端 IP 位址的問題。
本次展示的架構如下 :

其中代理伺服器使用 CentOS 7 的 HAProxy 1.5.14 套件,而 Web 服務器使用 CentOS 7 的 Apache 套件。
我們先在 Web 服務器放置內容如下的 PHP 腳本,而在 PHP 中,$_SERVER["HTTP_X_FROWARDED_FOR"] 這個變數就代表 HTTP 檔頭 X-Forwarded-For。
<?php
echo nl2br("X-Forwarded-For: " . $_SERVER["HTTP_X_FORWARDED_FOR"] . PHP_EOL);
echo nl2br("Remote Address: " . $_SERVER["REMOTE_ADDR"] . PHP_EOL);

用戶端直接連結 Web 服務器

Direct
當用戶端直接連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
HTTP-X-Forwarded-For:
Remote Address: 192.168.10.10
在這種單純的情況下,直接使用 REMOTE_ADDR 變數即可取得用戶端的 IP 位址。

用戶端透過 HAProxy 的  HTTP 模式連往 Web 服務器

HTTP Mode
我們在 HAProxy 使用下列設定:
listen http-proxy
bind 0.0.0.0:80
mode http
option httpchk
option forwardfor
balance source
server web-1 192.168.10.12:80 weight 1 check
當用戶端透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For: 192.168.10.10
Remote Address: 192.168.10.11
在這種情況下,儘管 REMOTE_ADDR 變數顯示為代理伺服器的 IP 位址,但是使用 HTTP_X_FROWARDED_FOR 變數依舊可取得用戶端的 IP 位址。

用戶端透過 HAProxy 的  HTTP 模式連往 Web 服務器

TCP Mode
我們將 HAProxy 的設定修改成如下:
listen http-proxy
bind 0.0.0.0:80
mode tcp
balance source
server web-1 192.168.10.12:80 weight 1 check
當用戶端透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For:
Remote Address: 192.168.10.11
在這種情況下,不存在 HTTP_X_FROWARDED_FOR 變數,而 REMOTE_ADDR 顯示的也是代理伺服器 IP 位址。也就是程式無法正確獲得用戶端 IP 位址。

用戶端透過支援 Proxy Protocol 的設定連往 Web 服務器

Proxy Protocol
因為 HAProxy 本身就內建支援 Proxy Protocol,所以不需安裝額外的套件,只要把設定修改成如下即可:
listen http-proxy
bind 0.0.0.0:80
mode tcp
balance source
server web-1 192.168.10.12:80 weight 1 check send-proxy
至於 Apache 的部分,因為 Apache 目前尚未內建 Proxy Protocol 的支援,所以我們需要安裝另外的模組。在此,我以 myfixip 模組為例,相關步驟如下:
yum install git gcc httpd-devel
cd /tmp
git clone https://github.com/ggrandes/apache24-modules.git
cd apache24-modules
apxs –i –c mod_myfixip.c
新增檔案 /etc/httpd/conf.modules.d/00-myfixip.conf,內容如下
LoadModule myfixip_module modules/mod_myfixip.so
<IfModule mod_myfixip.c>
   RewriteIPResetHeader off
   RewriteIPAllow 192.168.10.0/24 127.0.0.1
</IfModule>
systemctl restart httpd
需特別注意的是因為 Proxy Protocol 會改變代理伺服器與 Web 服務器之間的溝通方式,所以需要兩邊同時啟用,否則將無法正常運作,甚至可能導致無法存取服務。
當用戶端再次透過代理伺服器 (192.168.10.11) 連往 Web 服務器 (192.168.10.12) 上的 PHP 腳本後,可以看到如下的輸出:
X-Forwarded-For:
Remote Address: 192.168.10.10
在這種情況下,即使經過代理伺服器的作用,但是 REMOTE_ADDR 變數依舊可以正確地顯示出用戶端的 IP 位址。

透過 myfixip 模組,可以讓 Apache 支援 Proxy Protocol,有效地取得用戶端 IP 位址。
除了 HAProxy 之外,如果你使用了 AWS ELB 的 TCP 或 SSL 模式,透過 myfixip 擴充 Apache 使其支援 Proxy Protocol 也同樣會有很大的助益。比較麻煩的是,目前 AWS 的管理介面並不支援 Proxy Protocol 的設定,而必須使用命令列模式控制 Proxy Protocol 的開啟或關閉。有需要的讀者可參考官方文件

[教戰手則] 狡詐的用戶端真實 IP 位址

vuze_how_to_configure_envelope在 TCP/IP 網路的世界裡,盡管 IP 位址本身有很多的問題,但是用戶端的 IP 位址對許多安全機制來說依舊是一個很基本且重要的資訊。舉例來說,利用用戶端的 IP 位址當做存取控制的依據,就是常見的第一道安全門檻。除此之外,各式各樣的系統日誌,也幾乎都包含用戶端的 IP 位址來當作用戶端的身分紀錄。尤其對像是 HTTP 這類網路服務而言,用戶端的 IP 位址往往更是最主要的身分代表。
誠如 DEVCORE 在 “如何正確的取得使用者 IP?” 這篇文章中所提到,因為所有用戶端的資訊都是不可靠的,再加上眾多代理伺服器從中攪局,所以如何”正確的”取得用戶端 IP 位址這個看似再簡單不過的問題卻變得相當複雜,甚至無法在所有的情況下都能順利完成目的。
我在此先直接引用自該文章的結論
”那我們該怎麼處理呢?我的建議是記錄所有相關的 header 欄位存入資料庫,包含「REMOTE_ADDR」「X-Forwarded-For」等等,真正有犯罪事件發生時,就可以調出所有完整的 IP 資訊進行人工判斷,找出真正的 IP。當然從 header 存入的數值也可能會遭到攻擊者竄改插入特殊字元嘗試 SQL Injection,因此存入值必須先經過過濾,或者使用 Prepared Statement 進行存放。
可以參考的 HTTP Header(依照可能存放真實 IP 的順序)
  • HTTP_CLIENT_IP
  • HTTP_X_FORWARDED_FOR
  • HTTP_X_FORWARDED
  • HTTP_X_CLUSTER_CLIENT_IP
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED
  • REMOTE_ADDR (真實 IP 或是 Proxy IP)
  • HTTP_VIA (參考經過的 Proxy)”
而今天我要分享的是另一種情境,那就是當你的代理伺服器因為太有個性,除了 REMOTE_ADDR 之外其他檔頭 (Header) 都不願意送出呢 (嚴格來說,REMOTE_ADDR 其實根本也不是 HTTP 檔頭) ?在實務上,當你使用 HAProxy 的 TCP Mode 時,它就是這麼有個性。
基本上,HAProxy 有兩種運作模式,一種稱之為 HTTP 模式。在 HTTP 模式下,可以對用戶端送來的檔頭做判斷與處理,當然也包含檔頭的新增與內容修改。而在 TCP 模式下,這些功能都無法獲得。或許有讀者會質疑運作在 TCP 模式下的 HAProxy 是否還算是一個代理伺服器?這種定義的問題,我並不想在此討論,而就實務的情況來看,這種情況就是真實存在的。
既然 TCP 模式比之  HTTP 模式就像被閹割般的無能,那為什麼我們還會需要使用 TCP 模式呢?常見的情況有二,一個是因為 SSL/TLS 加密的因素。如果你的網站支援 SSL/TLS 加密,那麼使用 HAProxy 後你有兩種選擇,一種是把 SSL/TLS 加解密交給 HAProxy,這種情況又稱之為 SSL Termination (請參考下圖)。SSL Termination 常用於 SSL 加速,另外也可能是為了 IDS/IDP/DLP 這類需要分析封包內容的安全機制而必須先將 SSL/TLS 連線予以解密。
SSL Termination
但是因為 HAProxy 本身也是軟體形式,所以對於 SSL 加速沒有甚麼幫助,甚至有可能反而造成效能的瓶頸,所以第二種選擇就是把 SSL/TLS 加解密維持在原有的地方,例如 Web Server 本身。在這種情境下,因為所有經過 HAProxy 的封包內容都已經經過加密,所以 HAProxy 僅能透過 TCP 模式與後端 Web Server 溝通,而無法使用 HTTP 模式。
另外一個因素則是如果你需要代理的服務不是 HTTP,而是其他的網路服務 (像是 MySQL),當然就不能使用 HTTP 模式,而必需使用 TCP 模式。
而不幸的是,不管是在 HTTP 模式或 TCP 模式之下,都是由代理伺服器代替用戶端傳送封包,所以 REMOTE_ADDR 皆會顯示為代理伺服器的 IP 位址。還好在 HTTP 模式下,HAProxy 會加上 X-Forwarded-For 這個檔頭,所以我們還是有機會取得用戶端的真實 IP 位址。但是在 TCP 模式下,HARroxy 無法新增任何檔頭,也讓我們失去獲得用戶端真實 IP 位址的機會。
那麼我不用 HAProxy 不就沒事了?當然不是。即使是其他的 HTTP 代理伺服器,只要考慮到 SSL/TLS 加解密,就都有類似的問題。舉例來說,現在很熱門的 AWS ELB 在某些模式時也會有跟 HAProxy TCP 模式一樣的情形。
有鑑於此,HAProxy 特別訂出了所謂的 Proxy Protocol。只要是支援 Proxy Protocol 的代理伺服器與網路服務相互搭配,透過適當的設定就可以取得用戶端的 IP 位址。當然,這個用戶端 IP 位址,不一定是真實的用戶端 IP 位址,不過通常至少是連往代理伺服器的 IP 位址,而不是全部變成代理伺服器的 IP 位址。除了 HAProxy 本身的支援外,許多代理服務器或服務也都內建或透過外接模組的方式,提供此一功能的支援。詳細清單可參考這裡
下一篇文章中,我將使用 HAProxy + Apache 的 myfixip 模組當作範例,實際展示 Proxy Protocol 的運作。
相關連結:

About