Lời nói đầu: bài viết thể hiện quan điểm trọng ZeroMQ khinh Kafka, dựa trên hơn một năm kinh nghiệm sử dụng Kafka vào việc thu thập các nhật ký thời gian thực (real-time logging) qua các cài đặt Auth0 Webtasks. Lý do cụ thể sẽ được đưa tới bạn đọc ngay sau đây.

Auth0 Webtasks là cái gì?

Auth0 Webtasks là một nền tảng máy chủ (hosting platform) cho các vi dịch vụ (microservices) Node.js. Bạn có thể tạo nên những đoạn nho nhỏ code Node.js và triển khai nó đến các dịch vụ cloud trong tích tắc, chẳng cần quan tâm đến những thứ lằng nhằng rắc rối đi kèm trên các host platform khác. Tóm lại, bạn chỉ cần code thôi, còn lại cứ để Auth0 Webtasks lo :)

Vấn đề: real-time logging

Những tác vụ real-time truy cập vào các log ở phía server-side làm cho công việc phát triển back-end trở nên dễ dàng hơn trong kỷ nguyên điện toán đám mây. Là một developer, ắt hẳn bạn muốn chuyển các real-time feedback trả về từ các đoạn mã back-end đã triển khai lên các môi trường cloud thực thi thực tế, đặc biệt là trong quá trình phát triển.

Ví dụ, sau đây là một đoạn code server-side sử dụng Auth0 Webtasks:

module.exports =  function (cb) {
    console.log('A new request received at', new Date());
    cb(null, 'Hello, world');
};

Bạn đọc sẽ có thể thấy những đoạn real-time-logs được đoạn code server trên sinh ra, khi mà ở phía cloud gọi đoạn code sau:

tj-mac:wt-cli tomek$ wt logs
[06:41:13.980Z]  INFO wt: A new request received at Wed Sep 02 ...

Khi tôi nói real-time, ý tôi không phải là dùng mấy cái mánh dùng FTP hay gọi HTTP riêng rẽ để lấy các log mới nhất. Ý của tôi (nhấn mạnh :p) chính là các luồng và các real-time log.

Tập một: Kafka

Kafka là một message broker, đặc biệt được sử dụng cho các đầu vào dữ liệu lớn, các message real-time là một ví dụ. Có một số thỏa hiệp thiết kế làm cho Kafka thực sự là sự lựa chọn thích hợp nhất cho các tiến trình logging. Rất nhiều các hệ thống quy mô lớn sử dụng Kafka nhằm mục đích này, trong đó có Netflix. Thiết kế của Kafka thực sự đáng chú ý. Rất đẹp và chắc chắn.

Trong trường hợp Auth0 Webtasks, Kafka đã được sử dụng hơn một năm để thống nhất thông tin logging (logging information) trên vài bộ components, với các cụm CoreOS (CoreOS cluster) chạy Docker containers. Sơ đồ dưới miêu tả luồng thông tin log:

Tất cả các log sẽ được chia vào các bản ghi bunyan riêng rẽ (tài liệu JSON). Chúng được phổ biến đến Kafka dưới các topic khác nhau, trên toàn hệ thống hay người thuê cụ thể, được cung cấp trong các webtask cluster. Các luồng, real-time log có thể được truy cập thông qua một running request HTTP, trong mã hóa văn bản/ luồng sự kiện (text/event-stream encoding). Thành phần proxy hoạt động như một thuê bao Kafka (Kafka subscriber) và thực thi phiên dịch giao thức thành HTTP. Về mặt chức năng, giải pháp này đáp ứng nhu cầu của chúng ta rất tốt, hiệu năng hoàn hảo phù hợp với công việc.

Những vấn đề xung quanh Kafka

Với Auth0, tính sẵn sàng cao (high availability - HA) được ưu tiên hàng đầu. Không nên có khái niệm về quãng thời gian dừng để bảo trì. Cần sẵn sàng 24/7 quanh năm, mọi lúc khi căn cứ vào thực tế, khách hàng sẽ muốn dịch vụ tốt nhất, nhanh nhất có thể. Do đó nếu chậm trễ sẽ dẫn đến thất bại. Mục tiêu này ảnh hưởng đến rất nhiều ý tưởng thiết kế. Đương nhiên nó cũng áp dụng với thiết kế webtask cluster.

Kafka được triển khai với cấu hình high availability, với một thể hiện mỗi CoreOS VM, và một bản sao chép của hai trường hợp. Kafka dựa trên Zookeeper về mặt phân phối cấu hình, Zookeeper cũng được triển khai theo cùng 1 cách: một thể hiện mỗi CoreOS VM. Về mặt lý thuyết, một cluster 3 VMs có thể hoạt động bình thường nếu một VM đơn (single VM) tạm ngừng, sau đó VM này sẽ được phục hồi trở lại vào cluster. Tình huống như vậy là một sự cố điển hình, một kịch bản bảo trì mà ta cần dự liệu trước khi host một dịch vụ AWS,...

Qua nhiều năm chúng tôi đã rút kinh nghiệm từ một số thất bại khi bộ đôi Kafka/Zookeeper rejoin vào cluster sau khi VM tái phục hồi, chúng đã gây ra những ảnh hưởng do-mi-no đến toàn hệ thống logging.

Tôi vẫn yêu thích Kafka vì tính thực dụng và hiệu năng của nó, chưa kể đến sự thanh lịch cũng như sự trong sáng trong thiết kế của nó. Nhưng việc hỗ trợ triển khai HA, hay đạt mục đích mà không cần quá nhiều hỗ trợ, là nằm ngoài phạm vi dự trù của chúng tôi. Những công ty lớn như Netflix có thể bỏ nhiều tài nguyên kỹ thuật để giải quyết vấn đề hay ít nhất là kiểm soát chúng. Auth0 khởi đầu là một startup với 40 thành viên, tất cả đều là những cá nhân kiệt xuất, hầu hết trong số họ có những việc quan trọng hơn để làm thay vì làm nhiệm vụ "bà đỡ" cho Kafka.

Sau một năm sử dụng Kafka (thỉnh thoảng thêm vài tiếng có lẻ vào ban đêm), tôi đã quyết định xem xét lại vấn đề theo một cách nhìn khác, nghiêm khắc hơn. Chúng tôi đã sẵn sàng để thay đổi.

Cái nhìn mới về vấn đề cũ

Tôi bắt đầu cô đọng vấn đề vào những yếu tố thiết yếu cơ bản: cần thu thập và đưa ra các thông tin logging trong thời gian thực. Chúng ta không cần phải quan tâm quá nhiều về việc hỗ trợ truy cập lịch sử log, đấy là điểm mạnh của Kafka. Tôi nhận ra rằng nếu vấn đề thống nhất real-time log được giải quyết, lưu trữ chúng để kiểm tra sẽ trở nên đơn giản hơn rất nhiều. Chính là phương pháp chia để trị.

Tập hai: ZeroMQ

Ở điểm này tôi bắt đầu thử ZeroMQ, một thư viện message (messaging library) thực thi mẫu message phổ biến trong các ứng dụng phân tán. Nó gồm các mẫu pub/sub trùng khớp với kịch bản thu thập log của chúng tôi.

Một tuần sau đấy, chúng tôi có một (mẫu làm việc) working prototype dựa trên ZeroMQ. So sánh và đối chiếu ảnh dưới với thiết kế của Kafka phía trên:

Thành phần then chốt là bộ hợp nhất ZeroMQ (ZeroMQ Consolidator) chạy trên mỗi VM trong cluster. Công việc của nó là tập hợp log từ tất cả các thành phần chạy trong VM. Bộ hợp nhất đưa ra điểm cuối xpub ZeroMQ (xpub ZeroMQ endpoint). Tất cả các process chạy trên máy kết nối với nó sử dụng loại pub socket ZeroMQ (ZeroMQ's pub socket type) để xuất ra các log message. Bộ hợp nhất cũng đưa ra các điểm cuối xsub (xsub endpoint), là thành phần cần thiết cho quá trình các streaming log kết nối. Bộ hợp nhất hoạt động như đơn vị truyền tải đơn giản giữa các xpubxsub socket.

Bạn có thể thấy ví dụ cốt lõi của việc sử dụng xpub/xsub ZeroMQ socket trong đoạn mã Node.js này.

Trong trường hợp Auth0 webtask, các luồng real-time log được đưa ra thông qua các running request HTTP. Mỗi khi một request HTTP được gửi đến, thành phần proxy ở biểu đồ trên sẽ tạo ra một ZeroMQ sub socket và kết nối nó với điểm cuối xsub ở mỗi VMs trong cluster. Sau đó, nó hoạt động như một bộ truyền tải message và một lớp phiên dịch giao thức (protocol translation layer) giữa các message ZeroMQ và các running response HTTP.

Kafka và ZeroMQ

So sánh Kafka với ZeroMQ cũng khập khiễng như so táo với cam vậy, phạm vi các chức năng và cấp trừu tượng của Kafka về cơ bản là khác so với ZeroMQ. Tuy nhiên, hoàn toàn có thể so sánh chúng trên khía cạnh yêu cầu của vấn đề để chọn lựa một công nghệ tối ưu hơn. Do đó, hãy so sánh Kafka với ZeroMQ trên lĩnh vực thu thập real-time log.

Tham khảo khoá học Node.js xây dựng web site tốc độ cao để học cách sử dụng ZeroMQ.

Topic và message

Khi thu thập log, việc phân vùng các log vào các luồng logic duy nhất là rất quan trọng. Trong trường hợp của chúng ta, cần phải có hai lớp luồng: toàn hệ thống, các log quản trị và các log cụ thể cho người thuê đơn, trong môi trường webtask.

Kafka có khái niệm lớp đầu tiên của một topic, một khái niệm then chốt trong rất nhiều hệ thống message. Topic có thể được phát hành và đăng kí, được quản lý riêng lẻ tùy thuộc cấu hình và đảm bảo phân phát. Các topic của Kafka ánh xạ rất tốt với những yêu cầu hỗ trợ các luồng log logic duy nhất.

ZeroMQ không có khái niệm gì giống với topic, tuy nhiên nó lại có khái niệm lớp đầu tiên về bộ lọc subscription (subscription filter). Subscription filter cho phép bạn quyết định message nào bạn mong nhận được dựa trên sự trùng khớp tiền tố với các message ẩn khác. Tính năng này kết hợp với sự hỗ trợ message đa cấu trúc cho phép chúng ta dễ dàng biểu diễn cùng cú pháp với topic Kafka trong các văn bản requirement. Cấu trúc đầu tiên của mỗi message chứa tên logic (logical name) của luồng, cấu trúc thứ hai chứa các bản ghi log. Sau đó chúng ta cấu hình ZeroMQ để so khớp chính xác với cấu trúc thứ nhất, từ đó nhận được các mục mà chúng ta quan tâm.

Đảm bảo chuyển giao

Kafka hỗ trợ ít nhất một thành phần đảm bảo chuyển giao (delivery guarantee), ZeroMQ thì không. ZeroMQ sẽ drop các message nếu không có subscriber lắng nghe (hay các subscriber bị fall).

Khi mà sự thiếu các thành phần cơ bản delivery guarantee làm ảnh hưởng rất lớn đến các ứng dụng truyền thống tương ứng với các message broker, tôi nhận thấy có một cách tiếp cận thực dụng với tình huống hợp nhất logs, và một thương vụ tốt có thể tránh được sự phức tạp của các required hệ thống, hỗ trợ các delivery guarantee.Cụ thể hơn thì đó chính là bộ phận ổn định (stability section).

Trạng thái bền và hiệu năng

Kafka lưu trữ message trên đĩa để hỗ trợ delivery guarantee, cũng như để xem lại các message (go-back-in-time) sẵn sàng để dùng đến. ZeroMQ chỉ lưu trữ các message trong bộ đệm in-memory giới hạn lưu trữ, và không hỗ trợ xem lại.

Kết quả của sự so sánh, đó là cho dù Kafka thực sự rất nhanh so với các message broker (ví dụ RabbitMQ), nó vẫn chậm hơn ZeroMQ trong quá trình lưu trữ vào disk và lấy ra.

Làm một công việc luôn luôn cần nhiều nỗ lực hơn là chẳng làm gì :)

Do đó chúng tôi quyết định tìm hiểu truy cập vào lịch sử log của những vấn đề đang giải quyết, tập trung chỉ vào việc hợp nhất real-time log. Tính năng đó của Kafka cũng từ đó mà trở nên không cần thiết, không có quá nhiều lợi ích.

Tính ổn định

Tính ổn định là vấn đề lớn nhất ở Kafka mà chúng tôi không hề cảm thấy hài lòng một chút nào, sau một năm song hành cùng nó.

Một quá trình triển khai HA của Kafka yêu cầu triển khai HA của Zookeeper, Kafka sẽ sử dụng để sắp xếp cấu hình và trạng thái phân tán. Như tôi đã giải thích, chúng tôi đã trải qua một số vấn đề về tính ổn định với chính statefull cluster. Một số vấn đề nghiêm trọng đã xảy ra, chúng tôi buộc phải đi sâu vào xem xét, tìm ra chúng, cố gắng ổn định lại. Đấy là còn chưa kể đến cái giá phải trả mỗi khi hệ thống giở chứng ngừng hoạt động.

Khi chuyển sang ZeroMQ, sự phức tạp đó đã biến mất. Chẳng cần phải làm quá nhiều để cấu hình, triển khai, quản lý, hay hỗ trợ khi đã sử dụng thiết kế của ZeroMQ - tất cả các trạng thái đều là ngắn hạn giữa dữ liệu in-memory và network.

Các thành phần không có trong hệ thống sẽ không bao giờ bị hư hỏng 

Việc truy cập vào các lịch sử log?

Vấn đề về việc hợp nhất real-time log đã được giải quyết bằng ZeroMQ, hỗ trợ lưu trữ bền vững các log cho việc xử lý về sau trở nên đơn giản và dễ quản lý hơn rất nhiều. Ví dụ, những giải pháp như Logstash có khả năng bắt lấy những dữ liệu từ ZeroMQ và xuất những dữ liệu đó đến nhiều đích khác nhau.

Với trường hợp cụ thể với Auth0, chúng tôi đã sử dụng AWS Kinesis, ElasticSearch, và Kibana như những kênh xử lý log, ở những bộ phận khác nhau trong hệ thống. Chúng tôi phát triển những bộ chuyển dời message nhỏ, phi trạng thái, hoạt động như một ZeroMQ subscriber ở một phía, và một AWS Kinesis client ở phía còn lại để kết nối đến kênh này.

Tổng hợp

Chuyển từ Kafka sang ZeroMQ trong việc thu thập real-time log là một quyết định đúng đắn (trong trường hợp của chúng tôi). Bằng cách tập trung vào các requirement then chốt, các giải pháp có thể giảm thiểu đáng kể độ phức tạp. Trong khi cải thiện tính ổn định và độ tin cậy là động lực chủ yếu cho sự thay đổi này, sự cải thiện về hiệu năng và giảm thiểu độ phức tạp của hệ thống chắc chắn là những hiệu ứng tốt.

Với bất kì sự thay đổi lớn nào trong các dịch vụ cloud thay cho việc phải vận hành 24/7,cần nhiều thời gian nữa để chúng ta có thể hiểu trọn vẹn sự ảnh hưởng, đặc biệt đề cao tính ổn định. Điểm mấu chốt là: kể từ khi chúng tôi thay thế Kafka bằng ZeroMQ, tôi đã có những giấc ngủ ngon hơn rất nhiều.

 

Nguồn: https://tomasz.janczuk.org/2015/09/from-kafka-to-zeromq-for-log-aggregation.html