3.1 Xây dựng giải pháp
3.1.1 eLMS đa luồng
Hình 3.1: Mô hình eLMS đa luồng
Chúng ta xây dựng kiến trúc eLMS với 5 layer xử lý dữ liệu như sau:
1. Layer 1: eLMS-shipper
Chúng ta phát triển một thành phần eLMS-shipper dựa trên mã nguồn mở logstash-forwarder, tùy biến và phát triển bổ sung thêm các chức năng đầu ra. Bởi logstash-forwarder không hỗ trợ các đầu ra sử dụng trong hệ thống, và chúng ta cũng lược bỏ đi rất nhiều các xử lý trong logstash-forwarder. Đảm bảo cho nó là một công cụ nhẹ nhàng, chỉ xử lý cho mục đích nhằm vận chuyển dữ liệu log từ các app-server tới một đầu ra output, cụ thể là:
(1) Events read from the logs (eLMS-shipper)--> plain text format --> kafka
Thành phần này cung cấp các chức năng theo dõi tình trạng dữ liệu bởi một file đánh dấu trạng thái. Nó sử dụng công nghệ streaming để vận chuyển dòng dữ liệu tích hợp tới thành phần eLMS-parser. Ngoài ra nó cũng có thể xử lý chạy nền đối với những file dữ liệu offline với dung lượng lớn.
Chúng ta sử dụng một file cấu hình quản lý các tiến trình eLMS-shipper chạy ngầm ở client, nơi mà đang thực hiện giám sát các dữ liệu tích hợp. Nó bao gồm các thông tin về Kafka server và eLMS-parser server. Ngoài ra, nó cũng bao gồm các thông tin về định nghĩa regex cho bộ lọc, tag nhãn dữ liệu...để lọc các dữ liệu trên từng dòng dữ liệu, và chuyển về định dạng key value của json trong thành phần xử lý eLMS-parser.
2. Layer 2: Message Queue [9]
Bởi nhiều ưu điểm và nền tảng mới đến từ Apache Kafka sẽ trình bày ở bên dưới. Hệ thống eLMS sử dụng Apache Kafka đóng vai trò như là một hàng đợi thay thế Redis. eLMS-shipper thực hiện nhiệm vụ gửi các luồng dữ liệu và đẩy vào Kafka. Nó sẽ nhận tất cả dữ liệu đến từ client và lưu tạm vào các phân vùng cụ thể bởi công cụ Zookeeper. Tại một thời điểm, có thể sẽ xảy ra nhiều trường hợp eLMS-parser đang bận xử lý và không thể quay lại kéo dữ liệu từ kafka ra để xử lý lập tức. Thì các dữ liệu này vẫn được giữ lại bởi Kafka. Do vậy mà eLMS vẫn đảm bảo rằng các dữ liệu không bị mất mát. Trừ khi ổ đĩa bị tràn dung lượng.
Chi tiết về hàng đợi sử dụng trong hệ thống eLMS: eLMS không sử dụng hàng đợi là một DB lưu trữ dữ liệu tạm thời giống như Redis mà áp dụng một giải pháp xử lý quay vòng là Kafka ở trên.
Hình 3.2: Xử lý vòng tròn
Với mô hình này, eLMS-shipper thực hiện gửi các luồng dữ liệu bằng các task nhỏ tới một thành phần được gọi là Message Cycle. Nó sẽ nhận tất cả dữ liệu từ eLMS-shipper gửi sang và lưu tạm vào các phân vùng cụ thể, nối đuôi theo thứ tự thời gian chúng được gửi tới.
Hình 3.3: Lưu trữ của một Message Cycle
Tại một thời điểm eLMS-parser đang bận xử lý và chưa thể nhận thêm dữ liệu được ngay. Message Cylce coi như gói tin đấy là gói tin mới đến và thực hiện lưu lại một lần nữa vào các phân vùng. Điều này xảy ra quay vòng giống như một con đập nước [20].
Hình 3.4. Quay vòng các phân vùng lưu trữ dữ liệu
Bởi chúng ta sử dụng công nghệ streaming, cho nên bất cứ khi nào eLMS-parser quay lại xử lý, chúng ta đều có dữ liệu mới nhất ở thời gian thực.
Khi có sự bất thường xảy ra ở eLMS-parser và Message Cycle không thể tiếp tục đẩy dữ liệu sang, cái vòng dữ liệu chúng ta đang nói tới sẽ phình to ra. Tuy nhiên, tới một ngưỡng nhất định mà chúng ta cấu hình ở server. Có thể tạm dừng không nhận thêm dữ liệu từ eLMS-shipper hoặc ra lệnh cho các eLMS-shipper ngưng hoạt động.
Message Cycle và Queue có sự khác nhau cơ bản. Mô hình sau sẽ giải thích rõ cơ chế của 2 module này.
Hình 3.5: Hàng đợi Queue Hình 3.6: Message Cycle
3. Layer 3: eLMS-parser
Chúng ta phát triển thành phần này dựa trên mã nguồn của Logstash. Nó chỉ giữ lại bộ thư viện lọc grok theo regex, xử lý đầu ra tới Elasticsearch. Và được tùy biến thành xử lý riêng biệt ở các controller. Việc xử lý của controller trong một luồng bao gồm 3 thành phần như sau:
1/ Receiver: Thành phần nhận dữ liệu trực tiếp từ eLMS-shipper hoặc kéo dữ liệu từ hàng đợi Kafka ra để xử lý. Việc lắng nghe dữ liệu ở eLMS-parser luôn kết nối tới cả 2 thành phần eLMS-shipper và Kafka, để bất cứ khi nào ở các thành phần này có dữ liệu mới thì bên xử lý Receiver có thể nhận được ngay lập tức.
2/ Bussiness: Thành phần này xử lý các nghiệp vụ chính trong hệ thống. Nó cung cấp xử lý các nhiệm vụ nặng nề: Lọc dữ liệu bởi grok, format cấu trúc chung, đánh dấu thẻ tags (success, error, warn) và có thể thực hiện phân loại vào các category
khác nhau dựa trên phân lớp của một mô hình Data Mining được xây dựng sẵn từ dữ liệu quá khứ. Việc xử lý cuối cùng của thành phần này là trả lại dữ liệu định dạng JSON, chứa dữ liệu với các trường có ý nghĩa ở dạng key-value.
Ví dụ, thành phần business nhận được đầu vào với dòng dữ liệu log như sau:
3/12/2015 1:04:04 PM [17] From: (192.167.26.1) Fac:16 Sev:6 Msg >>> Mar 12 13:03:58 pf: 00:00:00.004827 rule
Qua xử lý của thành phần business, chúng ta nhận được dữ liệu đầu ra với định dạng JSON như sau:
{ "Datetime": "3/12/2015 1:04:04 PM", "Thread" : "17", "Host" : "192.167.26.1", "Facility": "16", "severity": "6", "datetransfer": "Mar 12 13:03:58", "message": "pf: 00:00:00.004827 rule" }
3/ DAE (Data Access Easticsearch): Thành phần này cung cấp các thư viện API cho mục đích read/write/query với Elasticsearch.
Do chúng ta phải sử dụng thêm file cấu hình trên server cho mỗi thành phần, nên có một vài ghi chú tới file cấu hình với các tham số quan trọng cho mỗi thành phần như sau:
Ghi chú 1/ Receiver: {shipper_port, kafka_port}
Việc gửi dữ liệu từ eLMS-shipper eLMS-parser và từ hàng đợi Kafka eLMS-parser có thể sử dụng tới hai cổng khác nhau, hoặc có thể giống nhau là tùy ý. Chúng ta xây dựng một tiện ích quản lý các cổng này trên dashboard để có thể bật/tắt được việc kết nối từ eLMS-shipper KafKa và từ Kafka eLMS-parser.
Mặc định thì eLMS-parser luôn mở một cổng riêng cho thành phần eLMS- shipper và một cổng khác cho thành phần Kafka.
Ví dụ: shipper_port=1234, kafka_port=5678
Điều này cho phép có thể chạy song song, một app-server dùng hàng đợi Kafka nhưng một app-server khác chúng ta không sử dụng hàng đợi trên cùng một kiến trúc. Ngoài ra cũng giúp ích cho việc ưu tiên đẩy nhanh các dữ liệu quan trọng lên ngay lập tức.
Ghi chú 2. Elasticsearch{flush_size, idle_flush_time, replication} flush_size:
Nhằm tăng hiệu suất đánh chỉ số index của ES, khi thành phần Business xử lý đủ một khối lượng dữ liệu được gửi đến thì mới thực hiện gọi API Bulk để lưu trữ vào ES.
Ví dụ: flush_size = 10 thì cứ 10 dòng dữ liệu xử lý xong mới đẩy vào ES
idle_flush_time: Khi mà flush_size chưa nhận đủ số lượng, nhưng thành phần Business đã xử lý vượt quá một khoảng thời gian nhất định. Thì để đảm bảo tỷ lệ truyền tải dữ liệu không đồng đều, và không đợi việc xử lý quá lâu, nếu vượt ngưỡng thời gian mà idle_flush_time khai báo thì thực hiện đẩy luôn dữ liệu vào ES.
Replication: Thiết lập loại Replication mà ES sử dụng. có hai loại “async” và
“notsync”. Nếu khai báo “async” thì việc yêu cầu đánh chỉ số index sẽ được thực hiện sau khi dữ liệu được ghi vào các sharding chính của ES. Nếu khai báo
“notsync”, thì hệ thống sẽ không tự động đánh index. Mặc định, eLMS khai báo
replication =“async”.
Ghi chú 3. Dành cho việc triển khai hệ thống
Nếu chúng ta không không sử dụng thành phần Kafka trong việc triển khai hệ thống, thì việc cấu hình chỉ còn 3 thành phần: eLMS-shipper, eLMS-parser và ES. Khi việc triển khai eLMS-parser và ES trên cùng một server chung, thì chúng ta sử dụng nhãn "embedded" để khai báo việc sử dụng trực tiếp các thư viện jar của ES. Mặt khác, khi việc triển khai cài đặt eLMS-parser và Elasticsearch là khác server, chúng ta cần phải có thông tin về địa chỉ host, mật khẩu truy cập để vận chuyển và đẩy dữ liệu vào ES.
Trường hợp khác, chúng ta sử dụng cài đặt thành phần Kafka, các tham số cấu hình cũng tương tự. Nhưng eLMS-parser sẽ ngắt kết nối tới thành phần eLMS- shipper và nó chỉ kết nối tới thành phần Kafka.
Việc triển khai trên cùng một mạng network luôn có hiệu quả tối ưu. Khi khác mạng network, chúng ta phải xây dựng thêm các cơ chế bảo mật và xử lý các vấn đề liên quan đến tường lửa.
4. Layer 4: Elasticsearch
Thành phần này sẽ lưu trữ và đánh chỉ số index dữ liệu. Việc đánh index dữ liệu chúng ta sẽ sử dụng các con số tăng dần, được định dạng bởi năm, tháng, ngày YYYYMMDD, với số ngày tăng đều đặn.
Bên cạnh đó, với ES chúng ta có thể thiết lập để lưu trữ dữ liệu dưới các node: Hot, warm, cold.
Node “Hot”: Là chỉ những dữ liệu được cập nhật ở thời gian thực và có những khoảng thời gian refresh lại khá thấp cho các index, vì vậy việc query dữ liệu node "hot" làm cho bộ nhớ cache query của ES không có nhiều ý nghĩa.
Node “Warm”: Các index warm được cập nhật vào mỗi đêm, mỗi tuần hoặc được thiết lập bởi người dùng. Nó lưu trữ dữ liệu được cập nhật gần đây và thường xuyên được truy vấn.
Node “Cold”: Khi dữ liệu lưu trữ vượt ngưỡng thời gian nhất định thì sẽ chuyển trạng thái từ node warm sang node cold. Nó lưu trữ dữ liệu cho tháng hoặc lâu hơn và hiếm khi được truy vấn cho việc tìm kiếm dữ liệu.
Ví dụ:
"index.routing.allocation.Exclude.tag" : "hot" "index.routing.allocation.include.tag" : "warm", "index.routing.allocation.include.tag" : "cold"
5. Layer 5: Analayzer library and Dashboard [8]
Trên dashboard, hệ thống cho phép hiển thị các dữ liệu, đồ thị thống kê, và quản lý các thông tin eLMS-shipper đang được theo dõi, quản lý các file cấu hình.
Ngoài ra, thành phần này còn có thể gọi controller Analayzer business
DAE để truy vấn tới ES và trả về dữ liệu để thống kê bởi người sử dụng.
Chúng ta có thể sử dụng một vài công nghệ truyền tải dữ liệu giống như Websocket từ server xuống client. Cứ mỗi khi ES có thêm dữ liệu mới được đẩy vào thì tự động gửi về thành phần Analyzer để hiển thị dữ liệu ở thời gian thực. Công nghệ này tương tự như cơ chế của các phần mềm chat trực tuyến.
Ở thành phần Analayzer cần thực hiện các câu lệnh truy vấn để lập các biểu đồ thống kê sử dụng các công nghệ như Node.js (gửi dữ liệu xuống biểu đồ). Nó cũng hỗ trợ cơ chế truyền tải bởi công nghệ comet hay Websocket. Và sử dụng công nghệ d3.js hoặc chart.js để tạo lập biểu đồ.