2017年12月25日

Oracle Fn Project

Oracle發佈了Fn Project ,Fn是一個新的 open source、與雲平台無關的Serverless平台,未來 Fn 的發展將會作為 Oracle Cloud 服務的一部分。

Fn 是一個 container native serverless platform,可稱為 FaaS (Function-as-a-service) 的平台,目前支援了 Java, Go, Ruby, Python, PHP, and Node.js。Fn 是以 Go 撰寫的,包含四個主要的元件: Fn Server、FDK、Flow和Fn負載平衡器。

Fn 最基本的執行單元是 containers,每個 function 定義都會獨立封裝在自己的 container 中,目前以 docker 為最主要的 container 平台。

Programmer 可利用某個程式語言透過 FDK(Function Development Kit) 撰寫 function,然後部署到 Fn Server,Fn Flow 提供工作流程及順序的工具,用以實現更高階的商業邏輯。

Fn Project 這篇文章的內容得知,當 Fn 啟動時,就會產生新的 docker image,並將 function 移動到該 image 內進行運算。

Serverless Architecture

Serverless 架構是由 Amazon 帶領的一種架構,也有人說是新型態的 Internet OS,採用 Serverless 架構,一方面除了改變了 IT 架構,沒有了 Server 甚至連 VM 都沒有,開發者只需要設計 function 跟 流程,其他的部分就交給 serverless 架構服務的提供者處理,不需要煩惱 conncurrent 使用量的硬體問題。

另一方面是成本的考量,Serverless 的計價方式是依照使用量來決定的,而不是 VM 的資源,換句話說,只要使用次數很低,Serverless 的成本會比使用 VM 還低,但一旦使用量持續都很大,那麼 VM 就會比 Serverless 划算。

可口可樂的Serverless之旅 的文章為例,可口可樂北美集團的開發團隊,在開發飲料自動販賣機的會員忠誠行銷計畫時,導入無伺服器運算架構。原本用了 6 台 EC2 T2.M,包含作業系統、系統管理、資安軟體、自動化部署軟體等成本,一年下來總體成本為12,864美元;然而在使用AWS Lambda的情況下,使用量每月3千萬次,一年總體成本是4,490美元,成本省了65%。

一旦達到每月8千萬次的使用量,Lambda的總體成本就會與EC2相當,使用量大於每月8千萬次,Lambda在價格上就沒有優勢了。但因為大多數的促銷活動都有長尾效應,例如可口可樂的行銷計畫可能一推出吸引很多人使用,但過了一段時間可能使用量就降低,因此採用Serverless架構是絕佳的決定。

References

Oracle開源Fn,加入Serverless之爭

Announcing Fn–An Open Source Serverless Functions Platform

fn project github


AWS Lambda大躍進的真義:Part 1

AWS Lambda大躍進的真義:Part 2

serverless

淺析 serverless 架構與實作

2017年12月18日

Elastic Stack (ELK)

Elastic 公司推出的 ELK 模組,E: Elasticsearch L: Logstash K: Kibana,Logstash 為資料收集工具,它將數據進行過濾和格式化,處理後將資料傳送給 ElasticSearch 儲存資料,最後再由 Kibana 前端網頁介面將資料由 ElasticSearch 取出來,可進行搜尋或是繪製圖表。Logstash 和 Elasticsearch 是用 Java 寫的,kibana 使用 node.js。

ELK 在 5.0 版後,加入 Beats 套件後稱為 Elastic Stack。Beats 是安裝在被監控端 Server 的監控 Agent,能夠直接將資料送給 Elasticsearch 或是透過 Logstash 轉換資料後,發送給 Elasticsearch。

安裝 Elastic Stack 建議用以下安裝順序,且建議都使用相同的版本

  1. Elasticsearch
    • X-Pack for Elasticsearch
  2. Kibana
    • X-Pack for Kibana
  3. Logstash
  4. Beats
  5. Elasticsearch Hadoop

X-Pack 是 Elastic Stack extension,將 security, alerting, monitoring, reporting, machine learning 及 graph capability 合併在一個套件中。

這幾個套件之間的關係如下

比較簡單的方式,可以將 Beats 直接連接到 Elasticsearch,再交給 Kibana UI使用。

Logstash 增加了資料轉換的功能,也加強了整個平台的穩定性。

ref: Deploying and Scaling Logstash

以 docker 測試

啟動一個測試用的 docker machine,安裝了 CentOS 7 及 sshd

#elasticsearch TCP 9200
#logstash beats input TCP 5044
#kibana web TCP 5601

docker run -d \
 -p 10022:22\
 -p 80:80\
 -p 9200:9200\
 -p 5044:5044\
 -p 5601:5601\
 --sysctl net.ipv6.conf.all.disable_ipv6=1\
 -e "container=docker" --privileged=true -v /sys/fs/cgroup:/sys/fs/cgroup --name elktest centosssh /usr/sbin/init

Elasticsearch

ref: Installing the Elastic Stack

ref: 如何在 CentOS 7 上安装 Elastic Stack

Elasticsearch 有下列幾種套件的安裝方式:zip/tar.gz, deb, rpm, msi, docker。

首先安裝 OpenJDK

yum -y install java-1.8.0-openjdk

設定環境變數

vi /etc/profile

export JAVA_HOME=/usr/lib/jvm/java-openjdk
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile

安裝 Elasticsearch PGP Key

rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

接下來有兩種方式,一種是設定 RPM Respository,或是直接下載 RPM

  • RPM Repository

vi /etc/yum.repos.d/elasticsearch.repo

[elasticsearch-5.x]
name=Elasticsearch repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

安裝並啟動 elasticsearch

sudo yum install -y elasticsearch

systemctl daemon-reload
systemctl enable elasticsearch.service

systemctl start elasticsearch.service
systemctl stop elasticsearch.service

查看啟動 log

journalctl -f
journalctl --unit elasticsearch
  • RPM
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.3.rpm
sudo rpm --install elasticsearch-5.6.3.rpm

啟動後,以 netstat 檢查 server

> netstat -napl|grep java
tcp        0      0 127.0.0.1:9200          0.0.0.0:*               LISTEN      439/java
tcp        0      0 127.0.0.1:9300          0.0.0.0:*               LISTEN      439/java

TCP 9200 是接收 HTTP Request 的 port,也是 elasticsearch 對外服務的 port TCP 9300 是給多個 elasticsearch nodes 之間溝通使用的


安裝後的相關檔案路徑

  1. /usr/share/leasticsearch/

    elasticsearch home directory

  2. /etc/elasticsearch/*.conf

    config 目錄

  3. /etc/sysconfig/elasticsearch

    環境變數,包含 heap size, file descriptors

  4. /var/lib/elasticsearch/

    data files 的目錄

  5. /var/log/elasticsearch/*.log

    log files

  6. /usr/share/elasticsearch/plugins/

    Plugin files location

  7. /etc/elasticsearch/scripts/

    script files


設定檔的位置在 /etc/elasticsearch/elasticsearch.yml

參考 ES節點memory lock重要性與實現方式 的說明,系統發生 memory swap 時,會嚴重影響到節點的效能及穩定性,導致 Java GC 由數毫秒變成幾分鐘,因此要避免 memory swap。

note: 因為目前是用 docker 測試,docker 在 ulimit 的設定有些限制跟問題,這個部分的設定就跳過,但正視環境必須要處理這個問題。

用下列指令檢查各節點有沒有啟用 memory lock

# curl -XGET 'localhost:9200/_nodes?filter_path=**.mlockall&pretty'
{
  "nodes" : {
    "AhjDVEQJQL6avw43nl3AFQ" : {
      "process" : {
        "mlockall" : false
      }
    }
  }
}

vim /etc/elasticsearch/elasticsearch.yml

# 取消這一行的註解
bootstrap.memory_lock: true

同時要修改系統設定,要不然啟動時會出現 memory locking requested for elasticsearch process but memory is not locked 這樣的錯誤訊息

vi /etc/security/limits.conf

* soft memlock unlimited
* hard memlock unlimited
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
ulimit -l unlimited
systemctl restart elasticsearch

Kibana

安裝 Elasticsearch PGP Key,剛剛在 Elasticsearch 安裝過了,就不用再安裝一次

rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
  • 以 RPM Repository 安裝

note: 這個 repository 跟剛剛的 elasticsearch.repo 是一樣的,不用重複,直接跳到下面安裝的步驟。

vi /etc/yum.repos.d/kibana.repo

[kibana-5.x]
name=Kibana repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

安裝 kibana

sudo yum install -y kibana

systemctl daemon-reload
systemctl enable kibana.service

systemctl start kibana.service
systemctl stop kibana.service

查看啟動 log

journalctl -f
journalctl --unit kibana

啟動後,以 netstat 檢查 server

> netstat -napl|grep node
tcp        0      0 127.0.0.1:5601          0.0.0.0:*               LISTEN      340/node
tcp        0      0 127.0.0.1:43968         127.0.0.1:9200          ESTABLISHED 340/node
tcp        0      0 127.0.0.1:43970         127.0.0.1:9200          ESTABLISHED 340/node
unix  3      [ ]         STREAM     CONNECTED     19517    340/node

TCP Port 5601 是 kibana 對外服務的網頁 Port


安裝後的相關檔案路徑

  1. /usr/share/kibana

    kibana home

  2. /etc/kibana/

    設定檔目錄

  3. /var/lib/kibana/

    資料 data files 目錄

  4. /usr/share/kibana/optimize/

    Transpiled source code

  5. /usr/share/kibana/plugins/

    plugin 目錄

kibana 的服務網頁為 http://localhost:5601/


也可以安裝 Nginx 並設定reverse proxy,就可改用 80 Port 存取 kibana。

yum -y install epel-release
yum -y install nginx httpd-tools
cd /etc/nginx/
vim nginx.conf

刪除 server { } 這個區塊。

vim /etc/nginx/conf.d/kibana.conf

server {
    listen 80;
    server_name elk-stack.co;
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/.kibana-user;
    location / {
        proxy_pass http://localhost:5601;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
sudo htpasswd -c /etc/nginx/.kibana-user admin

輸入密碼

# 測試 nginx 的設定
nginx -t

# 啟動 nginx
systemctl enable nginx
systemctl start nginx

檢查 nginx service

> netstat -napltu | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      510/nginx: master p

kibana 的服務網頁為 http://localhost/

Logstash

安裝 Elasticsearch PGP Key,剛剛在 Elasticsearch 安裝過了,就不用再安裝一次

rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
  • 以 RPM Repository 安裝

note: 這個 repository 跟剛剛的 elasticsearch.repo 是一樣的,不用重複,直接跳到下面安裝的步驟。

vi /etc/yum.repos.d/logstash.repo

[logstash-5.x]
name=Elastic repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

安裝 Logstash

sudo yum install -y logstash
  • RPM
wget https://artifacts.elastic.co/downloads/logstash/logstash-5.6.3.rpm
sudo rpm --install logstash-5.6.3.rpm

修改 Logstash 設定,建立 beat input,使用 SSL,也可以不使用 SSL。

設定 openssl

cd /etc/pki/tls
vim openssl.cnf

在 v3_ca 的區塊,增加 server name

[ v3_ca ]
# Server IP Address
subjectAltName = IP: 127.0.0.1

產生 CA 證書到 /etc/pki/tls/certs/ 和 /etc/pki/tls/private/

openssl req -config /etc/pki/tls/openssl.cnf -x509 -days 3650 -batch -nodes -newkey rsa:2048 -keyout /etc/pki/tls/private/logstash-forwarder.key -out /etc/pki/tls/certs/logstash-forwarder.crt

設定 Logstash的 input, filter, output

vim /etc/logstash/conf.d/filebeat-input.conf

input {
  beats {
    port => 5044
    ssl => true
    ssl_certificate => "/etc/pki/tls/certs/logstash-forwarder.crt"
    ssl_key => "/etc/pki/tls/private/logstash-forwarder.key"
  }
}

使用 grok filter 解析 syslog 文件

vim /etc/logstash/conf.d/syslog-filter.conf

filter {
  if [type] == "syslog" {
    grok {
      match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
      add_field => [ "received_at", "%{@timestamp}" ]
      add_field => [ "received_from", "%{host}" ]
    }
    date {
      match => [ "syslog_timestamp", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss" ]
    }
  }
}

輸出到 elasticsearch

vim /etc/logstash/conf.d/output-elasticsearch.conf

output {
  elasticsearch { hosts => ["localhost:9200"]
    hosts => "localhost:9200"
    manage_template => false
    index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}"
    document_type => "%{[@metadata][type]}"
  }
}

啟動

systemctl daemon-reload
systemctl enable logstash.service

systemctl start logstash.service
systemctl stop logstash.service

查看啟動 log

journalctl -f
journalctl --unit logstash

啟動後,以 netstat 檢查 server

# netstat -naptul |grep java
tcp        0      0 127.0.0.1:9600          0.0.0.0:*               LISTEN      788/java
tcp        0      0 0.0.0.0:5044            0.0.0.0:*               LISTEN      788/java
tcp        0      0 127.0.0.1:9200          0.0.0.0:*               LISTEN      196/java
tcp        0      0 127.0.0.1:9300          0.0.0.0:*               LISTEN      196/java
tcp        0      0 127.0.0.1:9200          127.0.0.1:43986         ESTABLISHED 196/java
tcp        0      0 127.0.0.1:44280         127.0.0.1:9200          ESTABLISHED 788/java
tcp        0      0 127.0.0.1:9200          127.0.0.1:44280         ESTABLISHED 196/java
tcp        0      0 127.0.0.1:9200          127.0.0.1:43988         ESTABLISHED 196/java

TCP Port 5044(SSL) 是 logstash 對外服務的網頁 Port

Beats

Bests 是在客戶端機器上收集資料的 Agent,可將資料發送到 Logstash 或是 Elasticsearch,目前有四種 Beats

  1. Packetbeat: real-time 分析網路封包,搭配 elasticsearch 就可當作 application monitoring 及 performance analytics 的工具。目前可解析以下這些 protocol 的封包: ICMP (v4, v6), DNS, HTTP, AMQP 0.9.1, Cassandra, MySQL, PostgreSQL, Redis, Thrift-RPC, MongoDB, Memcache
  2. Metricbeat: 收集 OS 及 一些 Service 的統計指標,目前支援 Apache, HAProxy, MongoDB, MySQL, Nginx, PostgreSQL, Redis, System, Zookeeper
  3. Filebeat: 檔案類型的 log file
  4. Winlogbeat: Windows event log,包含 application, hardware, security, system events
  5. Heartbeat: 定時檢查 service 狀態,只會知道 service 是 up or down

  • 以 RPM Repository 安裝

使用剛剛的 elasticsearch.repo。

vi /etc/yum.repos.d/elasticsearch.repo

[elasticsearch-5.x]
name=Elasticsearch repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

安裝 filebeat

sudo yum install -y filebeat
  • RPM 直接安裝
wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.6.3-x86_64.rpm
sudo rpm -vi filebeat-5.6.3-x86_64.rpm

filebeat 預設以 output.elasticsearch 為輸出對象,資料寫入到 localhost:9200。以下修改為 監控 /var/log/secure (ssh) 及 /var/log/messages (server log),並輸出到 logstash

vim /etc/filebeat/filebeat.yml

filebeat.prospectors:

- input_type: log
  paths:
    - /var/log/secure
    - /var/log/messages
  document_type: syslog

#--------- Elasticsearch output --------------
#output.elasticsearch:
# Array of hosts to connect to.
#  hosts: ["localhost:9200"]

#--------- Logstash output --------
output.logstash:
  # The Logstash hosts
  hosts: ["localhost:5044"]
  bulk_max_size: 1024
  #ssl.certificate_authorities: ["/etc/pki/tls/certs/logstash-forwarder.crt"]
  template.name: "filebeat"
  template.path: "filebeat.template.json"
  template.overwrite: false

如果剛剛有設定 logstash beat input 有包含 SSL 的部分,必須將 logstash 的 /etc/pki/tls/certs/logstash-forwarder.crt 複製到客戶端機器上,並將這個設定打開。

  ssl.certificate_authorities: ["/etc/pki/tls/certs/logstash-forwarder.crt"]

設定測試

# /usr/bin/filebeat.sh -configtest -e
2017/11/03 05:58:10.538291 beat.go:297: INFO Home path: [/usr/share/filebeat] Config path: [/etc/filebeat] Data path: [/var/lib/filebeat] Logs path: [/var/log/filebeat]
2017/11/03 05:58:10.538350 beat.go:192: INFO Setup Beat: filebeat; Version: 5.6.3
2017/11/03 05:58:10.538463 metrics.go:23: INFO Metrics logging every 30s
2017/11/03 05:58:10.539115 logstash.go:90: INFO Max Retries set to: 3
2017/11/03 05:58:10.539679 outputs.go:108: INFO Activated logstash as output plugin.
2017/11/03 05:58:10.539884 publish.go:300: INFO Publisher name: c0ba72624128
2017/11/03 05:58:10.540376 async.go:63: INFO Flush Interval set to: 1s
2017/11/03 05:58:10.540415 async.go:64: INFO Max Bulk Size set to: 1024
Config OK

啟動 filebeat

sudo systemctl enable filebeat
sudo systemctl start filebeat

查看啟動 log

journalctl -f
journalctl --unit filebeat

References

ELSstack 中文指南

Elastic Stack and Product Documentation

Logstash Reference Docs

logstash日誌分析的配置和使用

How To Install Elasticsearch, Logstash, and Kibana (ELK Stack) on Ubuntu 14.04

Elasticsearch 5.0和ELK/Elastic Stack指南


Elasticsearch 權威指南

ELKstack 中文指南

用 ElasticSearch + FluentD 打造 Log 神器與數據分析工具

Collecting Logs In Elasticsearch With Filebeat and Logstash

ELK+Filebeat 集中式日誌解決方案詳解


Handling stack traces in Elasticsearch Logstash Kibana (ELK)

Handling Stack Traces with Logstash


2017年12月11日

Fluentd

Fluentd 將 data source 及 backend system 分離,提供兩者之間的一個 Unified Logging Layer,可讓 developers 及 data analysts 能同時使用多種資料源,同時也解決格式錯誤的資料所造成的系統變慢或解譯錯誤的問題。

Fluentd 有三種版本,全部都是以 Apache2 License 釋出。

  1. Fluentd

    社群版本,只能用 ruby gems 安裝,沒有 init scripts,如果想要修改 Fluentd 或是做更多事情,可以用這個社群版

  2. ta-agent

    這是 Treasure Data, Inc 這家公司維護並測試的版本,可直接用 rpm/deb/dmg 套件安裝,安裝時同時安裝了一些預設設定值。如果是第一次使用 Fluentd,建議安裝 ta-agent。

  3. Fluent Bit

    Fluent Bit 是 Fluentd 的 lightweight data forwarder,用在 forward 資料給 Fluentd aggregators。可安裝在 embedded system 或是嵌入到 server 系統中。

Architecture

Fluentd 的架構圖為

由於 data inputs 及 output 透過 Fluentd 中繼資料,Fluentd 這個 Unified Logging Layer 野食作為 pluggable 架構,可不斷地增加不同的 inputoutput plugins,目前已經有超過 500+ 的 plugins。

假設有 M 種 data input,N 種 data output,pluggable 架構可讓原本複雜度 O(M*N) 的系統,變成 O(M+N) 的系統。

安裝

Download Fluentd 有列出所有安裝方式的資訊。我們選擇 Installing Fluentd Using rpm Package 安裝到 CentOS 7。

產生一個新的有 sshd 的 docker machine

docker run -d \
 -p 10022:22\
 -p 80:80\
 -p 8888:8888\
 --sysctl net.ipv6.conf.all.disable_ipv6=1\
 -e "container=docker" --privileged=true -v /sys/fs/cgroup:/sys/fs/cgroup --name fluentd centosssh /usr/sbin/init

在安裝前,Before Installing Fluentd 必須要先處理幾項系統設定。

  1. NTP

    要同步時間,確保 log 的 timestamp 是正確的

    CentOS 7 修改 timezone,校正時間

    timedatectl set-timezone Asia/Taipei
    /usr/sbin/ntpdate time.stdtime.gov.tw && /sbin/hwclock -w
  2. Max # of File Descriptors

    ulimit -n 65535

    vi /etc/security/limits.conf

    root soft nofile 65535
    root hard nofile 65535
    * soft nofile 65535
    * hard nofile 65535
  3. Network Kernel Parameters

    解決 TCP_WAIT 的問題,(如果在 docker 測試,會無法修改 kernel 參數,跳過這個步驟就好了,參考這邊的說明 對docker container進行內核參數調優

    vi /etc/sysctl.conf

    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.ip_local_port_range = 10240 65535

    sysctl -p 或是 reboot


以 script 安裝 FluentD,daemon 名稱為 td-agent

curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

安裝後會增加 /etc/yum.repos.d/td.repo,以及 td-agent service

啟動 daemon

systemctl enable td-agent
systemctl status td-agent

systemctl start td-agent
/etc/init.d/td-agent start
/etc/init.d/td-agent stop
/etc/init.d/td-agent restart
/etc/init.d/td-agent status

設定檔在 /etc/td-agent/td-agent.conf 預設是由 HTTP 接收 logs 轉至 stdout /var/log/td-agent/td-agent.log

發送測試資料

curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test

Use Cases

  • Centralized App Logging 收集不同語言實作的 Applcation 的 Log

  • Log Management & Search 以 Fluentd + Elasticsearch 的整合替代 Splunk

  • Data Analysis 將 Log 儲存到 Hadoop 或 MongoDB,以供後續分析處理

  • Data Archiving 將 Log 儲存到 Amazon S3/Riak/GlusterFS Logs

  • Stream Processing

  • Windows Event Collection 收集 Windows Event Logs (目前 stable 版本 v0.12 還不支援 Windows,要到 v0.14 才有支援)

  • IoT Data Logger

    Cloud Data Logger by Raspberry Pi 說明可在 Raspberry Pi 整合其他 Sensor 後,透過 Fluentd 收集資料。

Life of a Fluentd event

以實例解釋 event 是如何倍 Fluentd 處理的,包含 Setup, Inputs, Filters, Matches, and Labels

使用 inhttp 及 outstdout plugins 解釋 events cycle,首先修改 /etc/td-agent/td-agent.conf

# listening for HTTP Requests
<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

# print the data arrived on each incoming request to standard output
<match test.cycle>
  @type stdout
</match>

發送兩個 curl 測試

# curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test

# curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

tail -f /var/log/td-agent/td-agent.log

2017-10-31 15:15:40 +0800 [info]: adding match pattern="test.cycle" type="stdout"
2017-10-31 15:15:40 +0800 [info]: adding source type="http"
2017-10-31 15:15:40 +0800 [info]: using configuration file: <ROOT>
  <source>
    @type http
    port 8888
    bind 0.0.0.0
  </source>
  <match test.cycle>
    @type stdout
  </match>
</ROOT>
2017-10-31 15:15:48 +0800 [warn]: no patterns matched tag="debug.test"
2017-10-31 15:15:58 +0800 test.cycle: {"action":"login","user":2}
Event structure

Fluentd event 包含 tag, time, record 三個部分

  • tag: event 來自哪裡
  • time: Epoch time,event 發生時間
  • record: log content,JSON object

以 apache log 為例,利用 in_tail 會由一行一行的 text line log 產生 event

192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777

tag: apache.access # set by configuration
time: 1362020400   # 28/Feb/2013:12:00:00 +0900
record: {"user":"-","method":"GET","code":200,"size":777,"host":"192.168.0.1","path":"/"}

tag 是由 a.b.c 這樣的字串組成的,用 "." 組合不同部分的字串

設定檔 td-agent.conf

  • source: input source

標準 input 有兩個: http 及 forward,可同時使用

http 將 fluentd 轉變為 HTTP endpoint,由 HTTP 接收 event message

forward 將 fluentd 轉變為 TCP endpoint,接收 TCP packets

ex:

# Receive events from 24224/tcp
# This is used by log forwarding and the fluent-cat command
<source>
  @type forward
  port 24224
</source>

# http://this.host:8888/myapp.access?json={"event":"data"}
<source>
  @type http
  port 8888
</source>
  • match: output destination

比對 event 的 tag,並處理符合定義 tag 的 event

fluentd 的 stdout output plugin 為 file 及 forward

ex:

# Match events tagged with "myapp.access" and
# store them to /var/log/fluent/access.%Y-%m-%d
# Of course, you can control how you partition your data
# with the time_slice_format option.
<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

match 後面的參數有以下規則,依照在設定檔中的順序進行比對

    • matches a single tag part

    ex: a.* matches a.b a.* not match a or a.b.c

  1. ** matches zero or more tag parts

    a.** matches a, a.b and a.b.c

  2. {X,Y,Z} matches X, Y, or Z, where X, Y, and Z are match patterns

    {a,b} matches a and b a.{b,c}.* a.{b,c.**}

  3. 可用 填寫多個 patterns

    match a and b match a, a.b, a.b.c, and b.d

  • filter: 決定 event processing pipelines

Input -> filter 1 -> ... -> filter N -> Output

ex:

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  @type http
  port 9880
</source>

<filter myapp.access>
  @type record_transformer
  <record>
    host_param "#{Socket.gethostname}"
  </record>
</filter>

<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

event 處理過程

收到 {"event":"data"}
-> 送到 record_transformer filter
-> 增加 "host_param" 欄位
-> {"event":"data","host_param":"webserver1"}
-> 送到 file output
  • system: 設定系統參數
<system>
  # equal to -qq option
  log_level error
  # equal to --without-source option
  without_source
  # suppress_repeated_stacktrace
  # emit_error_log_interval
  # suppress_config_dump
  
  # fluentd’s supervisor and worker process names
  process_name fluentd1
</system>
  • label: group output 及 filter for internal routing
<label @SYSTEM>
  <filter var.log.middleware.**>
    @type grep
    # ...
  </filter>
  <match **>
    @type s3
    # ...
  </match>
</label>
  • @include: include other files
# Include config files in the ./config.d directory
@include config.d/*.conf
Processing Events

在設定 Setup 後,Router Engine 就已經包含了幾個基本的 rules,內部會經過幾個步驟處理 Event。

  • Filters

可用來設定一個 rule,決定要不要接受這個 event

ex: filter test.cycle 放棄不處理 logout,這是用 @grep 處理的,判斷 action 的部分,有沒有 "logout" 這個字串

<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

<filter test.cycle>
  @type grep
  exclude1 action logout
</filter>

<match test.cycle>
  @type stdout
</match>

測試

# curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

# curl -i -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

結果在 log 裡面只有看到 login

2017-10-31 15:50:55 +0800 test.cycle: {"action":"login","user":2}
Labels

可用來定義新的 Routing sections,且不遵循 top-bottom 的順序,類似 linked references 的行為。

ex: 在 source 增加了 @label,表示要跳到 @STAGING 處理 event,而不是用上面的 filter

<source>
  @type http
  bind 0.0.0.0
  port 8880
  @label @STAGING
</source>

<filter test.cycle>
  @type grep
  exclude1 action login
</filter>

<label @STAGING>
  <filter test.cycle>
    @type grep
    exclude1 action logout
  </filter>

  <match test.cycle>
    @type stdout
  </match>
</label>
Buffers

在範例中,使用 stdout 是 non-buffered output,但在正式環境,會需要對 output 增加 buffer,例如 forward, mongodb, s3 ...

buffered output plugins 會儲存收到的 events 到 buffers,並在達到 flush condition 時,再將資料一次寫入目標。換句話說,database 可能不會馬上看到新進的 event。

Execution unit

Fluentd events 預設是在 input plugin thread 中處理的,例如 intail -> filtergrep -> outstdout pipeline,就是在 intail 的 thread 中處理的。filtergrep 及 outstdout 並沒有自己的 thread。

但 buffered output plugin 中,另外有一個自己的 thread 可處理 flushing buffer。

Sample

Collecting Tomcat logs using Fluentd and Elasticsearch

fluentd-catch-all-config

Tomcat容器日誌收集方案fluentd+elasticsearch+kilbana

安裝 fluentd 的 elasticsearch plugin

td-agent-gem install fluent-plugin-elasticsearch

定義 tomcat catalina.out 的 source

<source>
  @type tail
  format none
  path /var/log/tomcat*/localhost_access_log.%Y-%m-%d.txt
  pos_file /var/lib/google-fluentd/pos/tomcat.pos
  read_from_head true
  tag tomcat-localhost_access_log
</source>

<source>
  @type tail
  format multiline
  # Match the date at the beginning of each entry, which can be in one of two
  # different formats.
  format_firstline /^(\w+\s\d+,\s\d+)|(\d+-\d+-\d+\s)/
  format1 /(?<message>.*)/
  path /var/log/tomcat*/catalina.out,/var/log/tomcat*/localhost.*.log
  pos_file /var/lib/google-fluentd/pos/tomcat-multiline.pos
  read_from_head true
  tag tomcat.logs
</source>

<match tomcat.logs>
    @type elasticsearch
    host localhost
    port 9200
    logstash_format true
    logstash_prefix tomcat.logs
    flush_interval 1s
</match>

References

fluentd architecture

用 ElasticSearch + FluentD 打造 Log 神器與數據分析工具


logstash + kibana - Make sense of a mountain of logs

LogStash::Inputs::Syslog 性能測試與優化


使用LogHub進行日誌實時採集

Docker日志收集新方案:fluentd-pilot

Fluentd, Logstash, LogHub, Flume, Kafka

而系統的 log,其實就是眾多的 event,我們必須設法將散落在不同地方的內部或外部 log 收集並儲存起來,集中到某個系統進行管理及分析,才能簡化處理異質環境訊息處理的問題。而 Fluentd, Logstash, LogHub, Flume, Kafka 這些技術,是以不同的方式解決問題。

在營運網路服務時,可能會遇到下面這些問題

  1. 在不同的廣告通路上,取得的使用者,要評估不同的廣告得到的收益結果
  2. 使用者抱怨服務的速度太慢,但要分析是在哪一個部分出問題
  3. 發送優惠券時,要如何評估優惠券的效益
  4. 要分析什麼時候該儲備多一點貨品,或是要調配更多人力
  5. 客戶在使用過程中發生問題,要如何分析是在哪個步驟出錯

由於網路服務的系統,可能會有這些特性,連帶造成使用者在處理某個工作時,必須跨越多種異質環境。

  1. 多個促銷或銷售管道
  2. 多個使用介面,如網頁、手機或是 APP
  3. 多台雲端機器
  4. 多種開發程式語言或環境
  5. 多個作業系統平台

通常會將 Fluentd, Logstash, LogHub 在一起比較,而將 Flume, Kafka 一起比較。

LogHub 是阿里雲的 Log Service,在別的環境中,可以先不考慮這個方案。

Fluentd, Logstash 是用 ruby,而 Flume 及 Kafka 是 Java。

Fluentd 有個基本的限制,他並不保證訊息一定會被傳送,如果不能容忍訊息遺失的狀況,就不要考慮 Fluentd。

但 Logstash 及 Flume,為了保證訊息一定會被傳遞,
同樣的訊息可能會收到兩次以上。

Flume 是訊息收集系統,而 Kafka 更接近於訊息cache系統,他可以儲存一定時間內的資訊。因此可以看到很多是採用 Fluentd + Kafka + Storm/ElasticSearch 這樣混搭使用狀況。

至於 Logstash 跟 Flume 的比較,可以看 logstash vs flume 以及 請對logstash與flume做比較 這篇文章。

Logstash 重視資料的預處理,多個 input 會把資料匯總到 input 和 filter 之間的 buffer中。filter則會從buffer中讀取數據,進行過濾解析,然後儲存在 filter 和 output 之間的Buffer中。當 buffer 滿足一定的條件時,會觸發output的刷新。

而 Flume 比較重視資料的傳輸,只有封裝 event 然後就傳送,沒有資料解析處理的部份,傳輸時比較重視資料的可靠性。

References

Fluentd vs. Logstash: A Comparison of Log Collectors

日誌客戶端(Logstash,Fluentd, Logtail)橫評

深度解讀:為何要使用日誌服務LogHub替換Kafka?

公網數據採集比較(LOGHUB VS 前端機+KAFKA)

Flume和Logstash的那些事兒

日誌採集系統flume和kafka有什麼區別及聯繫,它們分別在什麼時候使用,什麼時候又可以結合?

Kafka 與 Flume的區別

Logging 日誌記錄最佳實踐

你一定需要 六款大數據採集平台的架構分析

深夜實堂:從業務需求淺談 Log aggregators

2017年12月4日

statsd

statsd 是 Graphite/Carbon metrics server 的 front-end proxy,最初由 Etsy's Erik Kastner 以 Node.js 撰寫,目前已經有多種程式語言的實作版本。他是一個 event counter/aggregation service,接收 event timeings,做基本計算後,就產生 values,這可用來收集 custom application metrics,而 application 只需要不斷地發送 events。

collectd 在 5.4 版後就支援了 statsd plugin,也就是將 statsd 嵌入了 collectd。

statsd 是一個 UDP (也可換成 TCP) daemon,根據簡單的協議收集statsd客戶端發送來的數據,聚合統計之後,再定時推送給後端,如graphite和influxdb等,然後透過grafana顯示資料。

系統分成三個部分: client, server, backend。client 要植入 application 中,將相應的 metrics 發送給 statsd server。statsd server 聚合這些 metrics 後,定時發送給 backends。backends 負責儲存這些 Time Series Data,再透過適當的圖表工具展示資料。

安裝

要先安裝 nodejs,由 EPEL 安裝的是 nodejs 6.11.3-1.el7 版

yum install -y epel-release
yum install -y nodejs

如果要改安裝 nodejs 7,必須改用下面的程序

# Install Node.js 7.x repository
curl -sL https://rpm.nodesource.com/setup_7.x | bash -

# Install Node.js and npm
yum install nodejs

直接由 statsd github clone 並安裝 statsd

cd /usr/local/src

git clone https://github.com/etsy/statsd.git

cd statsd

npm install

設定

首先複製一份設定檔

cp exampleConfig.js config.js

修改 graphite 的設定

vi config.js

{
  graphitePort: 2003, 
  graphiteHost: "localhost",
  port: 8125,
  backends: [ "./backends/graphite" ]
}

修改 graphite 的設定

vi /opt/graphite/conf/storage-schemas.conf

[carbon]
pattern = ^carbon\.
retentions = 60:90d

[stats]
pattern = ^stats.*
retentions = 10s:6h,10m:7d,1d:5y

[stats_counts]
pattern = ^stats_counts.*
retentions = 10s:6h,10m:7d,1d:5y

[collectd]
pattern = ^collectd.*
retentions = 10s:6h,10m:7d,1d:5y

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

10s:6h,10m:7d,1d:5y

  • 6 hours of 10 seconds data
  • 7 days of 10 mins data
  • 5 years of 1 day data

如果 retentions 時間設定為這樣,資料會更多一些

[carbon]
pattern = ^carbon\.
retentions = 60:90d

[stats]
pattern = ^stats.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[stats_counts]
pattern = ^stats_counts.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[collectd]
pattern = ^collectd.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

10s:1d,30s:7d,1m:30d,15m:5y

  • 1 day of 10 seconds data
  • 7 days of 30 seconds data
  • 30 days of 1 minute data
  • 5 years of 15 minutes data

必須要同時修改 /opt/graphite/storage/whisper 路徑的 *.wsp 資料,可參考Whisper Scripts 文件。

# 修改 wsp size
find /opt/graphite/storage/whisper/collectd -type f -name '*.wsp' -exec whisper-resize.py --nobackup {} 10s:6h 10m:7d 1d:5y \;

# 列印 wsp file size
find /opt/graphite/storage/whisper/collectd -type f -name '*.wsp' -exec whisper-info.py {} \;

vim /opt/graphite/conf/storage-aggregation.conf

[lower]
pattern = \.lower$
xFilesFactor = 0.1
aggregationMethod = min

[min]
pattern = \.min$
xFilesFactor = 0.1
aggregationMethod = min

[upper]
pattern = \.upper(_\d+)?$
xFilesFactor = 0.1
aggregationMethod = max

[max]
pattern = \.max$
xFilesFactor = 0.1
aggregationMethod = max

[sum]
pattern = \.sum$
xFilesFactor = 0
aggregationMethod = sum

[gauges]
pattern = ^.*\.gauges\..*
xFilesFactor = 0
aggregationMethod = last

[count]
pattern = \.count$
xFilesFactor = 0
aggregationMethod = sum

[count_legacy]
pattern = ^stats_counts.*
xFilesFactor = 0
aggregationMethod = sum

[default_average]
pattern = .*
xFilesFactor = 0.3
aggregationMethod = average
  • 以 .lower .min 或 .upper .max 結尾的 metrics,只會儲存 max, min values,如果少於 10% datapoints,就只會儲存 None

  • 以 count 或 sum 結尾的 metrics,還有在 'stats_counts' 下面的 metrics,會加總所有 values,如果沒有收到資料,會儲存 None

  • 其他資料庫,會計算平均值,如果少於 30% 的 datapoint,就會儲存 None

重新啟動 graphite

systemctl restart carbon
systemctl restart graphite

啟動

有三種方式

  1. 直接在 console 啟動

    cd /usr/local/src/statsd
    node ./stats.js ./config.js
  2. 以 system service 方式啟動

    vi /usr/lib/systemd/system/statsd.service

    [Unit]
    Description=statsd daemon
    
    [Service]
    ExecStart=/usr/bin/node /usr/local/src/statsd/stats.js /usr/local/src/statsd/config.js
    ExecReload=/bin/kill -HUP $MAINPID
    KillMode=process
    
    [Install]
    WantedBy=multi-user.target

    啟動服務

    systemctl daemon-reload
    systemctl enable statsd
    systemctl start statsd
  3. 透過 npm forever-service 安裝服務

    cd /usr/local/src/statsd
    sudo npm install -g forever
    sudo npm install -g forever-service
    sudo forever-service install statsd -s stats.js -o " config.js"
    sudo service statsd start

statsd 會在 UDP:8125 運作,可檢查

netstat -nap | grep 8125

graphite 中會看到這些 metrics

stats.gauges.statsd.timestamp_lag

stats.statsd.graphiteStats.calculationtime
stats.statsd.graphiteStats.flush_length
stats.statsd.graphiteStats.flush_time
stats.statsd.graphiteStats.last_exception
stats.statsd.graphiteStats.last_flush

stats.statsd.bad_line_seen
stats.statsd.metrics_received
stats.statsd.packets_received
stats.statsd.processing_time

stats_counts.statsd.bad_line_seen
stats_counts.statsd.metrics_received
stats_counts.statsd.packets_received

statsd.numStats

Key Concepts

  • buckets 每一個 stat 都有自己的 bucket,不需要預先定義,最後將會被轉換到 graphite,periods ( . ) 會被換成 folders

  • values 每個 stat 都有自己的 value,解譯方式由 modifier 決定,values 一般都是 integer

  • flush 在 flush interval timeout (config.flushInterval 定義,預設值為 10 秒)後,stats 會被 aggregted 並發送到一個 backend service

使用

stats 是使用最基本的 line protocol

<metricname>:<value> | <type>

可用 nc 測試

echo "foo:1|c" | nc -u 127.0.0.1 8125

graphite 會增加這些 metrics

stats.foo
stats_counts.foo

Metric Types

Metric Types

  • Counting
foo:1|c

把 foo 加 1,flush 後,count 會發送到後端,並 reset 為 0。

如果設定了 config.deleteCounters,在 flush 時,如果 count 是 0,就不會發送 metric 到後端

如果你使用 flush interval(10秒),並在每個間隔通過某個計數器給 statsd 傳送7次 counting。則計時器的 value (stats_counts.foo) 為 7,而 per-second value (stats.foo) 為 0.7,另外 numStats 為 7。

  • Sampling
foo:1|c|@0.1

最後面 @0.1,表示每 1/10 的時間間隔,都會發送一次 counter

  • Timing

用來記錄某個 operation 消耗多少時間

foo:320|ms

foo 要花 320ms 完成

statsd 會自動計算該 flush interval 內的 percetiles, average(mean), 標準差, sum, 上下界

在 flush interval 內,你將下列計數器 values 傳給 statsd

450
120
553
994
334
844
675
496

會計算下面的 values,並傳送給 graphite

mean_90 496
upper_90 844
sum_90 3472
upper 994
lower 120
count 8
sum 4466
mean 558.25
  • Gauges

一個被記錄的任意數值

gaugor:333|g

如果 flush 時,值沒有改變,就會再發送一次。設定 config.deleteGauges,就不會再發送一次。

在數值前面加上 + 或 -,是值的計算,而不是覆寫,這表示不能將 gauge 設定為負整數

gaugor:333|g
gaugor:-10|g
gaugor:+4|g

gaugor 結果為 333 - 10 + 4 = 327

  • Sets

在 flushes 之間,記錄發生的 events,但不重複,可用來記錄某個事件在時間區段中,有哪些使用者曾經使用過

request:1|s  // 1
request:2|s  // 1 2
request:1|s  // 1 2
  • Multi-Metric Packets

可以在一行 packet 中,以 \n 區隔多個欄位的資料。但要注意網路單一 packet 的傳輸長度上限,例如 Fast Ethernet 為 1432 (包含)。

gorets:1|c\nglork:320|ms\ngaugor:333|g\nuniques:765|s

將 statsd 整合到 collectd

雖然會減少一個 daemon,改用 collectd 同時啟動 statsd,但目前不採用這種安裝方式

修改 /etc/collectd.conf

LoadPlugin statsd

<Plugin statsd>
  Host "0.0.0.0"
  Port "8125"
#  DeleteCounters true
#  DeleteTimers   false
#  DeleteGauges   false
  DeleteSets     true
  CounterSum     true
  TimerPercentile 90.0
#  TimerPercentile 95.0
#  TimerPercentile 99.0
  TimerLower     true
#  TimerUpper     false
#  TimerSum       false
#  TimerCount     false
</Plugin>

restart collectd

systemctl restart collectd

statsd 會在 UDP:8125 運作,可用 netstat 檢查,但卻是由 collectd process 處理的

netstat -nap | grep 8125

如果用 nc 測試時

echo "foo:1|c" | nc -u 127.0.0.1 8125

會在 graphite 發現,metrics 是在 collectd 下面

collectd.testserver.statsd.count-foo
collectd.testserver.statsd.derive-foo

clients

StatsD Example Clients 這裡有多種程式語言的獨立的測試 Client

3rd Party Client Implementations 這裡有第三方 StatsD 的 Library

node-statsd 為例。

安裝 node-statsd libray

npm install -g node-statsd

撰寫測試程式,發送 api 回應時間,到 statsd 的 timeing

vi test.js

'use strict';

const StatsD = require('node-statsd'),
client = new StatsD({
  host: 'localhost',
  port: 8125
});

setInterval(function () {
  const responseTime = Math.floor(Math.random() * 100);
  client.timing('api', responseTime, function (error, bytes) {
    if (error) {
      console.error(error);
    } else {
      console.log(`Successfully sent ${bytes} bytes, responseTime: ${responseTime}`);
    }
  });
}, 1000);

執行測試程式

export NODE_PATH=/usr/lib/node_modules
node test.js

在 graphite 中可以取得 stats.timers.api.* 這些 metrics

References

StatsD wiki

statsd學習小結

StatsD!次世代系統監控的核心

使用 Statsd + Graphite 的 Monitoring 心得

聊聊 Statsd 和 Collectd 那點事!

StatsD vs collectd vs fluentd and Other Daemons You Should Know 2016/8

How do StatsD and CollectD relate?

StatsD embedded into CollectD

如何深入理解 StatsD 與 Graphite

使用 StatsD + Grafana + InfluxDB 搭建 Node.js 監控系統


How to install Node.js 7.x on Ubuntu/Debian and CentOS