這系列文章有在我的網站整理成 GitBook 的格式,較易閱讀,提供大家參考。
Elastic Stack 廣泛的被使用在收集 Logs、Metrics、Traces、Uptime 等資料,其中一個最大的目的,就是為了讓散落在各處的資料,能集中化的收集,可以更輕易的存取這些資料,而當資料收集在一起的時候,混亂的格式,就會是遇到的下一個問題,Elastic Common Schema (ECS) 就是被設計出來解決這件事。
Elastic Common Schema (ECS) 是一個規範 (Specification),同時這個規範也是 Open Source 的,是在 Elastic 使用者社群支持之下所開發的,主要定義了存放在 Elasticsearch 之中的 Event (事件) 類型資料常用的欄位,這些欄位的型態、描述、使用與呈現方式,而所謂的 Events 即包含就像是 Logs 或是 Metrics 這樣的資料。
ECS 設計的目的,是為了鼓勵使用 Elasticsearch 存放 Event 類型資料的使用者們,能夠將這些 event 的資料『正規化』,透過正規化之後的資料,在後續的資料分析、資料視覺化呈現、顯示與 event 有關聯的資訊上都能更容易的使用。
ECS 的規範橫跨了以下的範圍:
透過資料正規化,讓查詢可以變得簡單,舉例來說,一般大型架構中,可能有各種的服務元件、第三方產品或工具,每個產生的 Logs 的格式都不同,同樣是針對 IP
的地址,欄位都不一樣,在沒有正規化之前,為了要從 src
、client_ip
、apache.access.remote_ip
、context.user.ip
等各種服務所定義的 IP
欄位查詢是否有存在 10.42.42.42
這個地址,KQL (Kibana Query Language) 的查詢會長成這樣:
src:10.42.42.42 OR client_ip:10.42.42.42 OR apache.access.remote_ip:10.42.42.42 OR
context.user.ip:10.42.42.42 OR src_ip:10.42.42.42
但透過正規化之後,會將這些欄位全部存放至 source.ip
的欄位,查詢就變成:
source.ip:10.42.42.42
簡單,能加快查詢的速度,也能減少犯錯的機會。
透過資料正規化,在製作圖表,讓資料以視覺化方式來呈現時,也會變得更簡單,而且在分析上也有更好的能力,例如透過同一個 IP 的位置,在同一個欄位之中,輕易的就能將各種來源 (如:Web Server、IDS/IPS 裝置、防火牆) 所收集到的資料,透過圖表呈現出時間歷程中的變化,又或是能將資料要進行深入的分析時的資料呈現方式,像是樞紐分析。
在 Data Ingestion Pipeline 的過程之中,能將原始的資料透過 ECS 的定義,轉換成正規化之後的結果,同時 Elastic Stack 中已經在 Beats、Logstash 等 Data Ingest 的工具,實作了許多第三方產品及工具的整合模組,能將這些 Logs 的格式轉換到 ECS 之中。
例如 Apache Logs 原始的內容如下:
10.42.42.42 - - [15/Jul/2020:20:48:32 +0000] "GET /content HTTP/1.1" 200 2571 "-"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/83.0.4103.106 Safari/537.36"
首先會將這些原始的內容轉換到 ECS 定義的欄位之中:
Field Name | Value |
---|---|
@timestamp | 2020-07-15T20:48:32.000Z |
event.original | 10.42.42.42 - - [15/Jul/2020:20:48:32 +0000] "GET /content HTTP/1.1" 200 2571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36 |
http.request.method | GET |
http.response.body.bytes | 2571 |
http.response.status_code | 200 |
http.version | 1.1 |
message | GET /content HTTP/1.1" 200 2571 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36 |
source.address | 10.42.42.42 |
source.ip | 10.42.42.42 |
url.original | /content |
user_agent.original | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36 |
除此之外,ECS 還有定義其他的欄位,也是在轉換過程中會增加的資訊:
ecs.version
:ECS 的版本event.dataset
、event.module
:記錄這個 event 是從哪裡來的,是透過哪個模組所處理的。event.kind
、event.cateogry
、event.type
、event.outcome
:包含 ECS Categorization Fields (分類欄位),透過定義好的類別,描述這個 event 是什麼樣的 event。ECS 定義了一些準則與最佳實踐的規範,接下來我們將介紹這些規範。
ECS 的欄位定義成以下兩種:
@timestamp
欄位。ecs.version
的欄位之中。host
相關的欄位,都要加上 host.
的前綴,當成是 host
類的欄位集 (field sets),進行分類管理。.
而不是使用 _
來描述。host.name
就不要去一般化變成 name
,這樣太一般化會導致使用時不知如何解讀,甚至會發生許多衝突。host.host_ip
應該取名 host.ip
,不過也有例外,如果 hostname
就是一個一般認知的名字,host.hostname
就應該保留使用 hostname
,不要刻意改掉。ip
、geo
、os
等。除非有特別的備註,否則所有的整數的數值型態,應該定義成 long
。
IDs 指的是識別字串,例如 User Id
、Product Id
,而 Codes 指的是編碼,例如 Error Code
,這些都應該使用 keyword
的資料型態。
不過有一些特別的 Codes 只要是大家都共識為數字的,就應該使用數字類型,例如 HTTP Status Code
,這個就應該使用數字。
Elasticsearch 預設的 Dynamic Mapping 會將文字類型的欄位,指定成 text
的資料型態,並且包含 keyword
的子欄位。
這部份 ECS 和 Elasticsearch 相反,預設的文字類型欄位,會指定成 keyword
的資料型態,而另外定義子欄位 text
。
原因是 ECS 處理的資料大部份都是 Logs 與 Metrics,在這樣的應用情境中,大部份的文字欄位都會較適用 keyword
的方式來處理,才能支援較快速的完整比對、Aggregation、Sorting、prefix search…等。
不過也有例外,就是 Logs 當中一般會存放大量文字,要用來做全文檢索的 message
與 error.message
欄位,這兩個欄位預設就是指定 text
並且不會另外宣告 keyword
的子欄位。
在實務的使用上,我們往往會因為實際的需求與情境,會要在結構化的 Logs 之中定義自己的欄位,由於 ECS 還持續在發展中,以下會有一些自訂欄位的使用建議,減少與未來 ECS 新版本發生衝突的機會。
labels
欄位一些簡單的 keyword
類型的資料,可以直接定義在 labels
欄位之中,例如:
{ "labels": { "foo_id": "beef42", "env": "production" },
"message": "...",
"event": { ... }
}
labels
裡的定義,就是完全依照使用者自己來管理。
ECS 在命名時,會使盡量使用概念的名字,而不是工具的名字或是專案的名字,一般在資料的整理時,就會先使用通用的方式來歸類,剩下的才會用特定方式來描述,例如 HAProxy 的 log,屬於 HTTP 相關的資訊,就會先定義在 http
與 url
的 field sets 之中,而剩下的才會放在 haproxy
裡:
{ "http": { "request": { "method": "get", ... },
"response": { "status_code": 200, ... } },
"url": { "original": "/favicon.ico", ... },
"haproxy": { "frontend_name": "myfrontend", "backend_name": "mybackend_prod",
"backend_queue": 0, ... }
}
如果真的要避免與未來的 ECS 版本發生衝突,有一個做法,雖然醜醜的,但是可以考慮,就是打破 ECS 的命名規則,使用大寫開頭的方式來命名欄位:
{ "http": { "request": { "method": "get", ... } },
"url": { "original": "/favicon.ico", ... },
"Proxy": { "FrontendName": "myfrontend", "BackendName": "mybackend_prod" },
"event": { "module": "haproxy" }
}
在上述官方文件提出的例子,Proxy
就是一個自訂的欄位,並且為了避免未來與 proxy
這樣的 ECS 欄位發生衝突,所以使用大寫開頭。
備註:我自己是覺得這部份有點醜,如果真的要使用,以上述的子來說,會命名成
Proxy
,一定是因為想要在自己的領域中定義一個通用的欄位,而剛好這個欄位還沒有被定義在 ECS 之中,如果真的是夠通用,可以接受未來 ECS 推出時再進行轉換,可以這樣考慮,否則是自己領域當中的應用的欄位定義時,最好還是能定義更明確的名字,避免發生衝突。
基本上若是使用我們這系列文章前面介紹到的 Heartbeat、Metricbeat、Filebeat 當中的各種 Modules,所收集到的資料就已經是依照 ECS 的格式存入 Elasticsearch 當中,若是有需要增加客製的欄位用,可以使用先前介紹過 Beats 裡的 properties
來進行設定,以下會先針對 ECS 產生的欄位進行簡介,若是要想查看 Beats 實際產生哪些 ECS 的欄位,可以直接使用 _search
或是 Kibana Discover 的功能來查看。
ECS 欄位的參考,可以參考 官方文件 - ECS Field Reference 裡的定義,包含非常多已經收錄的各種欄位集 (field sets),這部份在這邊就不細部說明。
若是想要一覽 ECS 所有欄位的總表的話,可以查看 ECS GitHub - fields.csv [2]。
ECS Categorization (分類) 欄位,目的是透過一組事先定義的值,用來描述這個欄位是什麼樣的欄位,其中包含了:
event.kind
:用來描述這個 event 包含什麼樣的資訊,可使用的值有 alert
、enrichement
、event
、metric
、state
、pipeline_error
、signal
。event.category
:定義了 ECS 中的主要分類,可使用的值包含 authentication
、configuration
、database
、driver
、file
、host
、iam
、intrusion_detection
、malware
、network
、package
、process
、registry
、session
、threat
、web
。event.type
:這裡面定義的,是基於 cateogry
底下的子分類,可使用的值包含 access
、admin
、allowed
、change
、connection
、creation
、deletion
、denied
、end
、error
、group
、indicator
、info
、installation
、protocol
、start
、user
。event.outcome
:這是定義 event 代表的狀態,可使用的值包含 failure
、succes
、unknown
。透過這些事先定義好的分類,可以協助我們將 events 有效的正規劃,每個欄位可設定的值的說明,可以參考 官方文件 - ECS Categorization Field 的細節說明。
這篇文章所介紹的 Elastic Common Schema,除了讓我們了解 ECS 的能力以及裡面所包含的定義,更重要的一個參考價值,就是 Elastic 發展出這份 Common Schema 的設計重點,相信在許多領域之中,也應該會有類似定義領域內通用 Schema 的需求,這裡所介紹的做法就非常值得參考。
查看最新 Elasticsearch 或是 Elastic Stack 教育訓練資訊: https://training.onedoggo.com
歡迎追蹤我的 FB 粉絲頁: 喬叔 - Elastic Stack 技術交流
不論是技術分享的文章、公開線上分享、或是實體課程資訊,都會在粉絲頁通知大家哦!