19 điểm bởi xguru 2020-08-10 | 3 bình luận | Chia sẻ qua WhatsApp
<p>Dropbox, nơi đang vận hành hàng chục triệu kết nối đồng thời, hàng triệu request mỗi giây và băng thông ở mức terabyte, có một bài viết giải thích rất rõ những ưu điểm của Envoy so với Nginx<br /> <br /> Trước đây: nginx (phiên bản mã nguồn mở) + python2 + Jinja2 + YAML <br /> → Chỉ cần thay đổi một thứ cũng phải triển khai lại toàn bộ<br /> → Các phần động được phát triển bằng Lua<br /> → Logic phức tạp được xử lý trong Bandaid, một proxy dựa trên Go<br /> <br /> Hệ thống này đã hoạt động tốt gần 10 năm, nhưng không còn phù hợp với môi trường hiện tại<br /> → Các API nội bộ và bên ngoài (không công khai) đang dần chuyển từ REST sang gRPC, nên cần khả năng transcoding ở proxy<br /> → Protocol Buffers đã trở thành chuẩn định nghĩa service nội bộ <br /> → Mọi phần mềm đều được build và test bằng Bazel bất kể ngôn ngữ<br /> → Nhân viên đang tham gia khá sâu vào cộng đồng mã nguồn mở của các dự án hạ tầng chủ chốt<br /> <br /> Nginx cũng tốn kém để duy trì về mặt vận hành<br /> → Logic tạo config quá linh hoạt và bị chia nhỏ giữa YAML, Jinja2 và Python<br /> → Monitoring là sự pha trộn của Lua / phân tích log / giám sát dựa trên hệ thống<br /> → Sự phụ thuộc ngày càng cao vào module bên thứ ba ảnh hưởng tới độ ổn định/hiệu năng và phát sinh chi phí vì phải nâng cấp thường xuyên <br /> → Việc triển khai và quản lý process của nginx khá khác biệt so với các service khác. Nó phụ thuộc nhiều vào những thứ không giống hệ thống cơ bản như syslog, logrotate, v.v.<br /> <br /> Vì vậy, sau 10 năm họ quyết định lần đầu tiên tìm giải pháp thay thế Nginx<br /> <br /> * Vì sao không chuyển sang Bandaid (proxy dựa trên Go do Dropbox tự phát triển)? * <br /> → Go tiêu tốn nhiều tài nguyên hơn C/C++. <br /> → TLS stack của Go không hỗ trợ FIPS (Federal Information Processing Standards của Mỹ)<br /> → Đây là công cụ nội bộ nên không thể nhận hỗ trợ từ cộng đồng bên ngoài <br /> <br /> Hiện tại: đang chuyển sang hạ tầng traffic dựa trên Envoy <br /> <br /> ----- Những điểm Envoy tốt hơn Nginx ------<br /> <br /> * Hiệu năng *<br /> <br /> Kiến trúc của Nginx là hướng sự kiện / đa tiến trình. Hỗ trợ SO_REUSEPORT &amp; EPOLLEXCLUSIVE<br /> Dù dựa trên event loop nhưng không hoàn toàn non-blocking. Khi mở file/ghi log, event loop có thể bị dừng lại. (Ngay cả khi bật aio, aio_write và threadpool)<br /> Điều này gây ra tail latency và đôi khi tạo ra độ trễ lên tới vài giây<br /> <br /> Envoy cũng có kiến trúc hướng sự kiện tương tự, nhưng dựa trên thread thay vì process <br /> Hỗ trợ SO_REUSEPORT (có hỗ trợ BPF filter), event loop thông qua libevent <br /> Không có blocking I/O trong event loop. Việc ghi log sự kiện cũng được triển khai theo cách non-blocking.<br /> <br /> Về lý thuyết, hai bên có vẻ sẽ cho ra đặc tính hiệu năng tương tự, và thực tế trong phần lớn bài test workload cũng khá giống nhau.<br /> Tuy nhiên, ở phần long tail, độ trễ của Nginx cao hơn. Nguyên nhân là event loop bị dừng khi có nhiều I/O.<br /> <br /> Nếu không thu thập thống kê thì Nginx cho hiệu năng gần tương đương Envoy, nhưng công cụ thu thập thống kê bằng Lua đang dùng nội bộ khiến Nginx chậm gấp 3 lần trong các bài test RPS cao. (Nguyên nhân là `lua_shared_dict` được đồng bộ bằng mutex). Dù cách thu thập thống kê của Dropbox cũng có vấn đề, họ đã từ bỏ việc viết lại nó cho hiệu quả hơn. (Vì họ dự đoán nếu thêm instrumentation vào bên trong Nginx thì việc nâng cấp sau này sẽ trở nên khó khăn hơn)<br /> <br /> Dù sao, vì những vấn đề này không xuất hiện ở Envoy nên sau khi di chuyển, họ có thể giải phóng tới 60% số server mà Nginx trước đây đang sử dụng.<br /> <br /> * Khả năng quan sát *<br /> <br /> Phiên bản miễn phí của Nginx chỉ cung cấp 7 loại thống kê qua module stub status <br /> Chừng đó dĩ nhiên là không đủ, nên họ đã gắn thêm handler `log_by_lua` để cung cấp nhiều thống kê hơn.<br /> Ngoài ra còn có parser cho `error.log` để xuất thông tin lỗi, cùng một exporter riêng để xuất trạng thái nội bộ của nginx.<br /> <br /> Thiết lập Envoy cơ bản cung cấp hàng nghìn metric khác nhau theo định dạng Prometheus <br /> Từ thông tin traffic của proxy tới trạng thái nội bộ của server,<br /> bao gồm thống kê theo cluster/upstream/virtual host và thống kê downstream TCP/HTTP/TLS theo từng listener, v.v.<br /> <br /> Cùng với lượng thống kê đa dạng này, Envoy còn cho phép cắm Tracing Provider.<br /> Điều này hữu ích không chỉ cho đội traffic mà cả các nhà phát triển ứng dụng.<br /> <br /> Cuối cùng, Envoy có thể stream access log qua gRPC.<br /> Nhờ đó giảm bớt gánh nặng duy trì bridge syslog-to-hive của đội traffic.<br /> Việc chạy một service gRPC thông thường dễ và an toàn hơn nhiều so với gắn custom TCP/UDP listener.<br /> <br /> * Tích hợp *<br /> <br /> Cách tích hợp của Nginx rất “Unix”. Configuration rất tĩnh.<br /> Nó phụ thuộc vào file config, chứng chỉ TLS, allowlist/blocklist, v.v. dưới dạng file.<br /> Đơn giản và tương thích ngược nên có thể tự động hóa bằng vài shell script,<br /> nhưng khi hệ thống lớn dần thì khả năng kiểm thử và chuẩn hóa ngày càng quan trọng hơn.<br /> <br /> Envoy có cách riêng cho kiểu tích hợp này.<br /> Nó cung cấp API gọi là xDS, khuyến khích sử dụng protobuf và gRPC.<br /> Envoy truy vấn xDS để tìm các tài nguyên động như vậy.<br /> <br /> - xDS hiện đang phát triển vượt ra ngoài phạm vi Envoy để trở thành chuẩn de facto cho load balancer L4/L7 dưới tên Universal Data Place API (UDPA), và theo kinh nghiệm của họ thì hướng đi này đang tiến triển tốt. Họ cũng đang cố dùng UDPA cho cả load balancer L4 Katran eBPF/XDP chứ không chỉ riêng Envoy.<br /> <br /> Vì Dropbox nội bộ đã cho các service tích hợp với nhau qua gRPC nên điều này phù hợp hơn rất nhiều.<br /> <br /> * Cấu hình *<br /> <br /> Nginx có một ưu điểm lớn là file cấu hình dễ đọc với con người. <br /> Nhưng ưu điểm đó dần biến mất khi cấu hình trở nên phức tạp và được sinh tự động.<br /> Vì Dropbox tạo cấu hình thông qua Python2, Jinja2, YAML, v.v. nên mô hình dữ liệu cũng bị rối và phức tạp.<br /> <br /> Envoy có mô hình dữ liệu thống nhất cho cấu hình. Mọi giá trị cấu hình đều được định nghĩa trong Protocol Buffer. Điều này giải quyết vấn đề mô hình dữ liệu và thêm thông tin kiểu cho các giá trị cấu hình.<br /> Vì protobuf đã được dùng rộng rãi bên trong Dropbox nên việc tích hợp cũng dễ dàng hơn <br /> <br /> * Khả năng mở rộng * <br /> <br /> Để mở rộng Nginx, cần viết module C. Muốn viết module an toàn thì cần developer cấp cao. Nó cũng có cung cấp interface Perl / JS để phát triển module nhẹ hơn, nhưng rất hạn chế. Vì vậy cách phổ biến nhất là thông qua `lua-nginx-module`. <br /> <br /> Cơ chế mở rộng chính của Envoy là plugin C++, tài liệu không tốt bằng nginx nhưng lại khá đơn giản. Điều này có được nhờ interface sạch sẽ, được chú thích tốt, cùng C++14 và thư viện chuẩn <br /> <br /> Điểm khác biệt lớn của Envoy so với các web server khác là hỗ trợ WebAssembly (WASM).<br /> Nhờ đó có thể phát triển phần mở rộng bằng nhiều ngôn ngữ như Rust. <br /> Dropbox hiện chưa dùng WASM, nhưng điều đó có thể thay đổi nếu một ngày nào đó có hỗ trợ Go SDK for proxy-wasm<br /> <br /> * Xây dựng và kiểm thử *<br /> <br /> Nginx về cơ bản dùng cấu hình tùy biến dựa trên shell và build dựa trên `make`. Đơn giản và rất tốt, nhưng để tích hợp nó vào monorepo build bằng Bazel thì cần khá nhiều công sức <br /> Nginx có integration test dựa trên Perl nhưng không có unit test.<br /> <br /> Envoy đã có sẵn hệ thống build dựa trên Bazel và được tích hợp vào monorepo của họ khá dễ dàng.<br /> Hỗ trợ unit test dựa trên gtest/gmock và integration test framework<br /> <br /> * Bảo mật *<br /> <br /> Code của Nginx rất nhỏ gọn và phụ thuộc bên ngoài cũng ít nên không có nhiều lỗ hổng bảo mật.<br /> <br /> Envoy có nhiều code hơn nên có vẻ có nhiều bề mặt tấn công hơn. Để đối phó, Envoy phụ thuộc nhiều vào các thực hành bảo mật hiện đại như AddressSanitizer, ThreadSanitizer, MemorySanitizer, v.v. <br /> <br /> * Tính năng * <br /> <br /> Phần này có nhiều ý kiến chủ quan nên chỉ nên tham khảo<br /> <br /> Nginx ban đầu là web server để phục vụ file tĩnh với rất ít tài nguyên. <br /> Nói cách khác, các chức năng chính là static serving, caching, range caching<br /> Xét từ góc nhìn proxy, Nginx còn thiếu nhiều tính năng mà hạ tầng hiện đại ngày nay cần. <br /> Nó không hỗ trợ kết nối HTTP/2 tới backend, không làm được proxy gRPC đa kết nối, cũng không hỗ trợ gRPC transcoding, v.v.<br /> Do mô hình giấy phép open-core nên một số tính năng quan trọng bị thiếu trong “community version”<br /> <br /> Envoy ngay từ đầu đã được thiết kế như ingress/egress proxy và được dùng nhiều trong môi trường có tải gRPC lớn.<br /> Các tính năng web service thì còn rất cơ bản. Nó không phục vụ file, caching vẫn đang được phát triển, cũng chưa hỗ trợ brotli, v.v. <br /> Với những môi trường như vậy, họ vẫn dùng cấu hình Nginx với Envoy làm upstream cluster <br /> Họ dự đoán khi Envoy hỗ trợ HTTP cache thì cả môi trường phục vụ nội dung tĩnh này cũng có thể được di chuyển sang <br /> <br /> Envoy hỗ trợ rất nhiều tính năng liên quan tới gRPC<br /> - gRPC proxying<br /> - HTTP/2 tới backend<br /> - cầu nối gRPC → HTTP (+ chiều ngược lại) <br /> - gRPC-WEB <br /> - gRPC JSON transcoder<br /> <br /> Ngoài ra Envoy cũng có thể được dùng làm outbound proxy <br /> - Egress Proxy<br /> - Service discovery cho phần mềm bên thứ ba với thư viện Courier gRPC <br /> <br /> * Cộng đồng *<br /> <br /> Việc phát triển Nginx mang tính tập trung và phần lớn bị ẩn đi. <br /> Việc phát triển Envoy thì mở và phi tập trung hơn. Nó được thực hiện qua GitHub issue/PR và cũng rất sôi động qua mailing list/Slack, v.v.</p><p>----- Tình trạng migration hiện tại của Dropbox -----<br /> <br /> Họ đã vận hành đồng thời Nginx và Envoy suốt nửa năm, đồng thời chuyển dần traffic thông qua DNS <br /> Quá trình chuyển đổi không phải hoàn toàn không có vấn đề; có một số lỗi nhỏ nhưng không có sự cố nghiêm trọng.<br /> Họ cũng tổng hợp cách xử lý cho những vấn đề phát sinh từ các hành vi “unusual” hoặc “non-RFC” (xem chi tiết trong bài gốc)<br /> <br /> ** Những việc sắp tới **<br /> <br /> - HTTP/3: Envoy cũng đã bắt đầu hỗ trợ ở mức thử nghiệm. Họ dự định sẽ thử khi nâng cấp Linux kernel để tăng tốc UDP<br /> - Load balancer nội bộ dựa trên xDS và Outlier Detection<br /> - Mở rộng Envoy dựa trên WASM <br /> - Thay Bandaid (proxy dựa trên Go) bằng Envoy <br /> - Áp dụng Envoy cho ứng dụng di động bằng Envoy Mobile</p>

3 bình luận

 
baeba 2020-08-13
<p>Nội dung hay, được tóm tắt ngắn gọn <br /> và sắp xếp rõ ràng,<br /> xin cảm ơn.</p>
 
before30 2020-08-11
<p>Cảm ơn bạn. :)</p>
 
loslch 2020-08-11
<p>Bài tổng hợp rất chi tiết, lại còn kèm theo những ý kiến rất tận tình nên đã giúp tôi hiểu hơn rất nhiều. Cảm ơn bạn :)</p>