Benchmark: Docker bridge vs host network vs CNI overlay

Hiện tại đa phần hạ tầng dùng Kubernetes rồi có mấy service từ lâu đang chạy thuần Container và có issue lâu lâu mới lên chia sẻ cùng với anh em.

Trước tôi không để ý lắm tới network mode của container, Hệ thống chậm thì thường anh em sẽ soi DB trước, rồi tới cache, rồi query, rồi code. Phần network giữa các service thường bị xem là chuyện nền. Có overhead thì chắc có, nhưng cảm giác nó không phải thứ tạo ra khác biệt lớn.

7a69f085-8464-4285-a5e6-7c610bab3a4f

Nhưng rồi đến một dạo trực Jira rảnh rảnh ngồi xem p99 của một hệ thống nội bộ.

Hệ thống đó không có dấu hiệu nghẽn rõ ràng. CPU không cao. DB không căng. Redis cũng ổn. Nhưng cứ vào giờ nhiều traffic là p99 nhảy lên khá khó chịu. Không phải kiểu sập hẳn, chỉ là mọi thứ mất độ mượt. Request nào xui thì chậm hẳn ra một đoạn.

Lúc đầu tôi cũng nghĩ chắc do app. Sau đó lôi connection pool ra xem, rồi nhìn GC, rồi soi timeout giữa các service. Cuối cùng mới thấy network path giữa service này với service kia cũng góp phần không nhỏ.

Thế là tôi ngồi đo lại cho rõ, với ba kiểu hay gặp nhất:

  • Docker bridge
  • host network
  • CNI overlay

Điều tôi muốn biết khá đơn giản: với request nhỏ, gọi qua gọi lại liên tục giữa các service, thì phần chênh này thực tế lớn đến đâu. Nhất là ở p95 với p99.

Bối cảnh bài benchmark

Case tôi dùng cũng không có gì lạ.

Một service A gọi sang service B bằng HTTP nội bộ. Request JSON khoảng 1KB. Response cũng cỡ đó. Tôi bỏ TLS ra để nhìn cho sạch phần network, đỡ trộn thêm chi phí mã hóa. Bên service nhận cũng làm rất ít việc, gần như nhận request xong trả response lại luôn.

Ý là tôi không muốn benchmark framework hay business logic. Tôi chỉ muốn bóc phần network giữa service với service ra nhìn riêng.

Tôi chạy hai tình huống.

Một là hai service nằm cùng một node. Case này ngoài đời gặp khá nhiều, nhất là khi hệ thống còn chưa lớn hoặc scheduler đặt trùng máy.

Hai là hai service nằm khác node. Cái này mới sát production hơn, vì khi đã scale ra vài máy thì chuyện gọi khác node là bình thường.

Máy test là 8 vCPU, 16GB RAM, 10GbE. Mỗi bài tôi warm-up 10 giây, đo 60 giây, chạy 5 vòng rồi lấy median. Client benchmark nằm ở máy riêng để đỡ ảnh hưởng lẫn nhau.

Ba kiểu network tôi đem ra so

Bridge thì chắc ai chạy Docker cũng quen. Đây là mode mặc định. Container có network namespace riêng, đi qua bridge của host, thường kèm theo NAT và conntrack trong đường đi.

Host network thì đơn giản hơn nhiều. Container dùng thẳng network stack của host. Ít lớp hơn, ít xử lý hơn, bù lại phần cách ly mạng cũng kém đi.

Overlay thì tôi test theo kiểu phổ biến trong cluster multi-node, dạng VXLAN. Cái được là network model gọn hơn khi chạy nhiều node. Cái mất là packet phải đi vòng hơn và thêm vài lớp xử lý.

Kết quả khi hai service nằm cùng node

Bảng đầu tiên ra khá đúng với những gì tôi đoán, nhưng mức chênh không đến mức quá ghê gớm.

Network mode req/s p50 p95 p99
host network 61k 0.42ms 0.88ms 1.60ms
Docker bridge 56k 0.48ms 1.05ms 2.10ms
CNI overlay 51k 0.57ms 1.30ms 2.70ms

Host network đẹp nhất, cái này không có gì bất ngờ.

Bridge thấp hơn một chút. p99 từ 1.60ms lên 2.10ms. Overlay lên 2.70ms.

Điều tôi thấy đáng nói là bridge thực ra ổn hơn nhiều người hay nghĩ. Nếu chỉ nhìn case cùng node thì tôi không thấy lý do gì phải dị ứng với nó cả. Nó không đẹp bằng host network, nhưng cũng chưa đủ tệ để thành vấn đề trong đa số hệ thống bình thường.

Overlay thì bắt đầu lộ giá rồi. Dù vậy, nếu chỉ dừng ở case cùng node thì vẫn chưa thấy nó đáng sợ.

Sang case khác node mới bắt đầu thấy rõ

Khác biệt lộ ra rõ hơn khi request phải đi từ máy này sang máy kia.

Network mode req/s p50 p95 p99
host network 39k 0.95ms 2.40ms 4.80ms
Docker bridge 35k 1.12ms 3.10ms 6.90ms
CNI overlay 30k 1.45ms 4.60ms 10.80ms

Đến đây thì tôi gần như không còn quan tâm p50 nữa. p50 vẫn khá hiền. Tất cả vẫn quanh 1ms đến 1.5ms. Nếu chỉ nhìn median thì rất dễ kết luận là network mode không ảnh hưởng bao nhiêu.

Nhưng nhìn sang p99 thì câu chuyện khác hẳn.

Host network ở 4.8ms. Bridge lên 6.9ms. Overlay lên 10.8ms.

Một hop thì con số này có thể vẫn chịu được. Nhưng hệ thống microservice hiếm khi chỉ có một hop. Một request đi qua gateway, auth, user service, rồi thêm một hai service phụ nữa thì phần xấu nhất của từng hop cộng lại rất nhanh. Đó là lúc cả hệ thống bắt đầu cho cảm giác ì ra dù nhìn từng chỗ riêng lẻ chưa chắc thấy nghẽn.

Tôi nghĩ đây là chỗ nhiều team dễ bỏ sót nhất. Mỗi service nhìn riêng đều có vẻ ổn. Nhưng phần latency xấu của từng hop cộng lại thì không ổn chút nào.

Tôi thử thêm burst traffic

Production không chạy đều như máy phát nhịp, nên tôi thêm một bài nữa. Traffic tăng gấp đôi trong một đoạn ngắn rồi hạ xuống lại.

Bảng này nhìn phát ra vấn đề luôn.

Network mode p50 p95 p99
host network 1.20ms 4.10ms 7.90ms
Docker bridge 1.40ms 5.60ms 11.80ms
CNI overlay 1.85ms 8.90ms 18.70ms

Lúc có burst thì overlay bắt đầu lộ rõ nhược điểm hơn hẳn. p99 gần 19ms, trong khi host network vẫn dưới 8ms.

Đây là đoạn làm tôi thấy network mode không còn là chi tiết nhỏ nữa. Nếu service của mọi người gọi nhau nhiều, request nhỏ, concurrency cao, thì network path hoàn toàn có thể ăn vào độ trễ cuối cùng nhiều hơn mình tưởng.

Vì sao overlay vấn đề nhất

Thật ra overlay chậm hơn cũng không có gì oan cho nó. Nó đang giải một bài toán khó hơn.

Khi dùng overlay, mọi người đang đổi lấy:

  • network thống nhất trên nhiều node
  • deploy linh hoạt hơn
  • quản lý service với policy dễ hơn
  • scale cụm đỡ rối hơn

Đổi lại packet phải đi qua thêm encapsulation, decapsulation, routing, policy và một loạt xử lý nữa.

Với request nhỏ, mấy phần chi phí cố định kiểu này lộ ra rất nhanh. Không phải do thiếu băng thông. Chủ yếu là vì bản thân request quá ngắn. Chỉ cần thêm một ít xử lý vào mỗi hop là nhìn ra ngay.

Có nên dùng host network cho mọi thứ không

Kết luận một cái gì đó áp dụng tất cả thì chưa hay rồi.

Host network đúng là đẹp nhất nếu chỉ nhìn latency. Nhưng nó cũng mang theo một loạt thứ không vui lắm. Port đụng nhau dễ hơn. Cách ly mạng kém hơn. Vận hành container cũng mất đi một phần tiện.

Bridge thì ở giữa và khá cân bằng. Nếu hệ thống chạy ít node hoặc chưa quá nhạy p99, tôi thấy bridge đủ ổn cho rất nhiều case.

Overlay thì đương nhiên đắt hơn về latency, nhất là lúc đi khác node. Nhưng nếu đang chạy cluster nhiều node thì nhiều khi đó là cái giá phải trả để đổi lấy sự linh hoạt lúc vận hành. Lúc này câu hỏi hợp lý hơn thường là service nào đủ nhạy để mình phải tối ưu riêng cho nó, chứ không phải có nên vứt overlay đi hết hay không.

Vài điều sau bài test này

Điều đáng nhớ nhất không phải là host network đứng đầu. Cái đó quá dễ đoán.

Thứ đáng nhớ hơn là bridge không tệ như tôi từng nghĩ. Còn overlay thì cái giá của nó lộ mạnh nhất ở p95 với p99, nhất là khi khác node và có burst traffic.

Nếu hệ thống của mọi người ít hop, timeout rộng, không quá nhạy vài mili-giây, có thể sự khác biệt này chưa thành vấn đề.

Nhưng nếu đang chạy một đám service gọi nhau liên tục, có fan-out, có retry, và từng thắc mắc vì sao hệ thống không nghẽn hẳn mà vẫn không mượt, thì network mode là một thứ rất đáng mang ra đo lại.

Kết

Sau lần này thì tôi không còn xem network giữa các service là chuyện nền nữa.

Host network vẫn là lựa chọn đẹp nhất nếu chỉ nhìn latency. Bridge là điểm cân bằng khá ổn. Overlay thì không miễn phí, và phần tiền mọi người trả thường nằm ở p95 với p99 chứ không nằm ở p50.

Cuối cùng thì cũng không có đáp án chung cho tất cả. Có chỗ cần sự đơn giản. Có chỗ cần sự linh hoạt. Có chỗ chỉ cần bớt vài mili-giây ở phần đuôi là đủ khác biệt rồi.

Nếu muốn ra quyết định cho đúng, tốt nhất vẫn là đem workload thật của hệ thống mình ra đo. Nhưng ít nhất sau bài test này, tôi thấy phần network giữa service với service đáng để nghiêm túc hơn nhiều so với cách tôi từng nghĩ.

Thông tin nổi bật

Sự kiện phát trực tiếp​

Event Thumbnail

Báo cáo quan trọng

Article Thumbnail
Article Thumbnail
Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận

Tiêu điểm chuyên gia