2.4 Các vấn đề tiếp cận
2.4.6 Vấn đề xếp hàng đợi Queue [14][16]
Hình 2.17: Kiến trúc ELK
Chúng ta nhìn vào cách sử dụng một hàng đợi như là một bước đệm dữ liệu trung gian với các lý do trình bày ở trên cho thấy Redis cũng là giải pháp rất tuyệt vời, giải quyết được nhiều vấn đề. Nhưng quan trọng là chúng ta đang tính tới việc xử lý thời gian thực. Nó có một vài lý do mà khiến chúng ta băn khoăn và có thể không tán thành với việc sử dụng Redis ở đây. Theo kinh nghiệm của cộng đồng - Những người tiền nhiệm đang đối phó với việc xử lý dữ liệu lớn, hàng chục ngàn sự kiện trên giây và hơn thế. Chỉ cần bỏ qua một trong những hàng đợi queue đã giải quyết được vấn đề về tốc độ để việc xử lý gần với thời gian thực nhất.
Ở đây thành phần Logstash-forwarder không hỗ trợ đẩy trực tiếp dữ liệu vào một hàng đợi. Hơn nữa, việc sử dụng Redis như một hàng đợi là không tối ưu vì nó đơn luồng và không chứa sẵn các dữ liệu. Đơn giản nó chỉ lưu trữ dữ liệu vào ổ đĩa theo định kỳ.
Vấn đề nảy sinh khi các dữ liệu đang ở server hoặc ở trong một hàng đợi chứa một khối lượng dữ liệu lớn vượt quá giới hạn. Bất cứ điều gì, hàng đợi chắc chắn sẽ định nghĩa trạng thái quá tải và sẽ ra khỏi ngoài luồng xử lý. Nếu chúng ta sử dụng logstash-forwarder thì nó sẽ dừng lại. Các dịch vụ vẫn chạy bình thường miễn là trên app-server vẫn còn dung lượng ổ đĩa trống để lưu trữ dữ liệu tiếp tục sinh ra. Lúc này dữ liệu ở trên server và trong một hàng đợi không có sự khác nhau. Ở trường hợp này, giải pháp bằng cách sử dụng một hàng đợi lại ít được tin cậy bởi vì nó không
thể hiện được vai trò. Giống như chúng ta đang đem phức tạp vào một hệ thống với đề xuất sử dụng hàng đợi ở đây. Đơn giản chúng ta cần một máy chủ với bộ nhớ RAM vài trăm gigabytes để chịu có thể được các trường hợp như vậy mà không cần tới một hàng đợi. Tức là chọn giải pháp về nâng cấp phần cứng server tập trung.
Redis có 3 cấp độ liên tục: Lưu dữ liệu vào ổ cứng sau mỗi key được ghi, có hoặc không đồng bộ. Chu kỳ dựa trên thời gian hoặc số lượng các key thay đổi và cuối cùng là xóa sạch mọi thứ không lưu bất kể thứ gì. Điều này là đủ tốt cho hầu hết việc xử lý dữ liệu và các dữ liệu tương tự. Redis được đề xuất sử dụng như là một hàng đợi queue trong quá trình xử lý của Logstash. Các mô hình khác hiện có không lựa chọn giải pháp với Redis (ví dụ như: Splunk.com, loggly.com, logentries.com...). Bởi nếu việc quản lý dữ liệu yêu cầu chắc chắn không thể làm mất bất kể bản ghi dữ liệu nào thì Logstash cộng với Redis không phải là công cụ dành cho chúng ta trong trường hợp này. Nó có thể làm mất các dữ liệu trong quá trình đọc và xóa ở chính trên hàng đợi Redis. Cụ thể, điều này sẽ xảy ra như sau:
Nếu Logtash xảy ra sự cố bị treo, chúng ta sẽ mất tất cả các dữ liệu hiện tại
trong một hàng đợi nội bộ - là một hàng đợi tạm mà không chứa được nhiều dữ liệu để chuẩn bị đẩy một lô vào trong DB. Bởi lý do đơn giản là nó không được thiết kế để lưu khối dữ liệu lớn. Với hàng đợi của mô hình ELK luôn bị giới hạn. Chưa kể tới việc đọc và xóa các dữ liệu ở Redis có thể gây ra việc trùng lặp dữ liệu ở Elastichsearch.
Nếu việc xử lý có thể chấp nhận khả năng mất dữ liệu. Redis cùng với các
luồng thực thi (gọi là các thread worker) cũng có thể được đánh giá là một giải pháp tốt. Trong phiên bản beta của ELK hiện tại, chúng ta nhận được nhiều ý kiến tốt đẹp từ những tổ chức, cá nhân đang triển khai mô hình này của ELK. Một lựa chọn khác là để tăng khả năng xử lý khi xảy ra sự cố giống như kịch bản ở trên. Chúng ta cần cấu hình sử dụng hai hay nhiều hơn số lượng hàng đợi Redis đầu vào thay vì sử dụng một. Có nhiều quan điểm khác nhau nhưng ở đây, cá nhân tôi cho rằng đó là một xử lý không khả quan. Chúng ta có thể tấn công và các class cơ sở đầu ra cho phép thêm các plugin output từ chối nhận các dữ liệu message và gửi lại chúng tới Logstash indexer với một kiểu khác nhau. Tại sao làm được như vậy? Đơn giản là mã nguồn mở, source code được publish và ELK là hệ thống tương tác giữa các plugin với
nhau. Các kiểu tấn công như vậy là khá dễ dàng. Dẫn tới có thể gây ra lỗi liên tiếp giữa các thành phần vận chuyển và xử lý trong kiến trúc.
Hình 2.18: Kiến trúc ELK với 2 hàng đợi
Hơn nữa, theo mô hình sử dụng với Redis của hệ thống Sematext (hình 2.19).
Ở đây có duy nhất một điểm khác biệt so với hệ thống ELK là Sematext đã sử dụng Solr để làm bộ lưu trữ thay vì sử dụng Elasticsearch. Chúng ta gọi chung là bộ lưu trữ (Solr có vai trò giống Elasticsearch, rsyslog có vai trò như logstash shipper) và tạm bỏ qua vấn đề này mà nhìn vào quá trình xử lý của Redis. Kiến trúc của Sematext như sau [19]:
Chúng ta nhận thấy rằng trong một thời điểm luôn xảy ra hai quá trình xử lý song song:
(1) File dữ liệu qua thành phần rsyslog để đẩy dữ liệu vào hàng đợi Redis: Việc kết nối giữa rsyslog và Redis là một chiều và chỉ kết nối khi rsyslog có dữ liệu mới để thực hiện đẩy sang.
(2) Dữ liệu từ Redis được kéo vào bộ lưu trữ: Tại đây Logstash đóng vai trò
đọc và xóa dữ liệu từ Redis. Tuy nhiên Logstash không thực hiện đẩy ngay từng message được đọc ra, mà chờ khi đầy một khối dữ liệu trong hàng đợi nội bộ mới thực hiện gọi cơ chế Bulk của ElasticSearch để kéo vào và đánh chỉ số index của một khối dữ liệu. Logstash luôn giữ cả hai kết nối, tới Redis và tới ElasticSearch. Để bất cứ khi nào Redis có dữ liệu mới thì Logstash sẽ nhận được ngay và thực hiện đẩy vào bộ lưu trữ ElasticSearch.
Với kiến trúc như trên sẽ phải rất chú ý tới việc Redis chiếm nhiều bộ nhớ RAM. Trong một khoảng thời gian xử lý, với việc dữ liệu message đến với khối lượng vài ngàn dòng dữ liệu trên giây, nó có thể chiếm tới 100% RAM và CPU để xử lý trong một khoảng thời gian nhất định. Việc này là khó chấp nhận trên một server ở môi trường production.
Ngoài những phân tích từ mô hình kiến trúc như trên, chúng ta tham khảo thêm kết quả thực thi của Sematext để thấy được vấn đề này.