Làm chủ Linux Traffic Control (tc): Giới hạn băng thông, mô phỏng độ trễ và mất gói tin với Netem và HTB

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Vấn đề thực tế: Ảo ảnh mạng “Chạy tốt trên máy tôi”

Mọi thứ đều vận hành mượt mà trên môi trường staging. Các microservice trao đổi lệnh gọi gRPC với độ trễ khứ hồi dưới một mili-giây, truy vấn Postgres hoàn tất trong 2ms, và test worker không ghi nhận bất kỳ gói tin nào bị rớt. Nhưng khi bạn triển khai lên production qua các edge location, cụm hybrid cloud và người dùng di động từ xa thì câu chuyện lại khác.

Ngay lập tức, các quy trình nhạy cảm với độ trễ bị suy giảm hiệu năng nghiêm trọng. Request bắt đầu timeout sau 5.000ms, throughput sụt giảm thảm hại, và đội ngũ kỹ sư phải cuống cuồng tái hiện các lỗi rớt kết nối vốn biến mất hoàn toàn khi kiểm thử cục bộ. Việc tái lập đường truyền WAN chập chờn hay đường uplink bị nghẽn gần như là bất khả thi khi các container kiểm thử giao tiếp qua interface loopback 10 Gbps hoàn hảo.

Các nhóm phát triển thường tìm đến giải pháp mock ở tầng ứng dụng hoặc rải rác các lệnh time.Sleep(100ms) trong code. Tuy nhiên, việc tạo độ trễ ở cấp ứng dụng không thể phản ánh thực tế vận hành của kernel. Chúng bỏ qua hoàn toàn bộ hẹn giờ retransmission của TCP, quá trình đàm phán Selective Acknowledgement (SACK), hiện tượng cạn kiệt socket buffer và phình hàng đợi (queue bloat) tại các router trung gian.

Phân tích nguyên nhân gốc rễ: Tầm quan trọng của việc xử lý gói tin ở cấp Kernel

Các sự cố mạng bí ẩn hiếm khi chỉ xuất phát từ code ứng dụng. Thay vào đó, chúng xảy ra do networking stack của Linux bị quá tải trước điều kiện thực tế của đường truyền. Khi uplink bị nghẽn hoặc đánh rơi frame, kernel sẽ quyết định cách đệm (buffer), sắp xếp thứ tự và loại bỏ các gói tin gửi đi bằng cách sử dụng các cơ chế xếp hàng (queuing disciplines – qdiscs).

Theo mặc định, các bản phân phối Linux hiện đại gắn các qdisc kiểu work-conserving như fq_codel hoặc pfifo_fast vào network interface. Các thiết lập mặc định này tối ưu hóa việc phân phối công bằng và độ trễ thấp dưới tải thông thường, vô tình che giấu các kịch bản lỗi trên production trong suốt quá trình kiểm thử:

  • Không xử lý rớt gói tin: Kiểm thử tích hợp nội bộ không bao giờ kích hoạt TCP Fast Retransmit, tail drop hay thuật toán exponential backoff.
  • Độ biến thiên trễ (jitter) bằng 0: Thời gian ping nội bộ ổn định tuyệt đối ở mức 0,05ms, che đậy các race condition phát sinh do gói tin đến sai thứ tự.
  • Lưu lượng egress tăng đột biến không kiểm soát: Một tác vụ backup chạy ngầm ngoài tầm kiểm soát có thể chiếm trọn băng thông chiều đi ở mức 950 Mbps, làm tê liệt các heartbeat Redis và phiên kết nối SSH quan trọng.

Để bộc lộ các trường hợp biên này và thực thi cam kết băng thông nghiêm ngặt cho môi trường multi-tenant, bạn phải định hình gói tin (traffic shaping) ngay trong kernel trước khi chúng chạm tới ring buffer của NIC vật lý.

So sánh các giải pháp: Application Mock vs IPTables vs Linux TC

Kỹ sư thường xử lý việc giả lập suy giảm chất lượng mạng bằng ba công cụ riêng biệt:

1. Proxy tầng ứng dụng (Toxiproxy, Chaos Mesh, Custom Interceptor)

Các công cụ này nằm giữa các service ở không gian người dùng (user-space) để ngắt luồng TCP hoặc làm trễ payload.

  • Ưu điểm: Cấu hình theo từng route đơn giản; thuận tiện cho các bài test tích hợp với Docker Compose.
  • Nhược điểm: Tiêu tốn nhiều RAM và CPU. Chúng không thể kiểm tra lưu lượng UDP/ICMP thô một cách chuẩn xác và bỏ qua các socket buffer gốc của hệ điều hành.

2. Netfilter / Quy tắc Drop của IPTables

Quản trị viên thường mô phỏng lỗi đường truyền bằng cách bỏ gói tin với lệnh iptables: iptables -m statistic --mode random --probability 0.05 -j DROP.

  • Ưu điểm: Tích hợp sẵn trong Netfilter mà không cần cài đặt thêm package nào.
  • Nhược điểm: Quá thô bạo. IPTables loại bỏ gói tin ngay lập tức, nhưng nó không thể chèn độ trễ truyền dẫn, mô phỏng jitter theo mili-giây hay định hình lưu lượng một cách mượt mà.

3. Linux Traffic Control (tc) với Netem và HTB

Subsystem gốc này nằm trực tiếp bên trong networking stack của Linux kernel, quản lý việc lập lịch egress, phân loại và giới hạn tốc độ.

  • Ưu điểm: Hoàn toàn không tốn chi phí tài nguyên user-space. Hoạt động minh bạch trên toàn bộ tiến trình hệ thống, xử lý chính xác các đường cong trễ theo mô hình thống kê và áp đặt giới hạn băng thông phân tầng chi tiết đến từng kilobit.
  • Nhược điểm: Cú pháp dòng lệnh ngắn gọn nhưng khá phức tạp và đòi hỏi thời gian làm quen.

Giải pháp tối ưu: Kiểm soát lưu lượng thực tế với Netem và HTB

Phương pháp tối ưu nhất dựa trên Traffic Control (tc) thông qua hai module kernel cốt lõi: Netem (Network Emulator) để mô phỏng đường truyền kém chất lượng, và HTB (Hierarchy Token Bucket) để phân bổ băng thông chặt chẽ.

Kiểm tra Interface mục tiêu

Trước tiên, hãy xác minh tên interface chính và xem các qdisc đang hoạt động:

ip link show
tc qdisc show dev eth0

Mô phỏng Jitter, Độ trễ và Mất gói tin bằng Netem

Netem cho phép bạn kiểm thử các distributed service dưới các điều kiện khắc nghiệt của mạng di động nghẽn mạng hay đường truyền cáp quang xuyên đại dương.

Thêm độ trễ cố định

Chèn thêm 100ms trễ khứ hồi vào mọi gói tin gửi đi trên eth0:

sudo tc qdisc add dev eth0 root netem delay 100ms

Thêm độ trễ với Jitter và Phân phối thực tế

Kết nối thực tế luôn biến động liên tục. Để mô phỏng độ trễ cơ bản 100ms với jitter ±20ms theo phân phối chuẩn Gauss (Gaussian curve):

sudo tc qdisc change dev eth0 root netem delay 100ms 20ms distribution normal

Mô phỏng mất gói và hỏng gói tin

Đánh rơi ngẫu nhiên 5% gói tin chiều đi đồng thời nhân bản 1% số frame để kiểm tra cơ chế chống trùng lặp dữ liệu:

sudo tc qdisc change dev eth0 root netem loss 5% duplicate 1%

Kiểm tra cấu hình mô phỏng đã áp dụng

Xác minh bộ đếm gói tin và số liệu frame bị rớt trong thời gian thực:

tc -s qdisc show dev eth0

Khôi phục cấu hình Netem

Sau khi hoàn tất kiểm thử, hãy xóa root qdisc để đưa interface về lại trạng thái mặc định:

sudo tc qdisc del dev eth0 root

Giới hạn băng thông với Hierarchy Token Bucket (HTB)

Các tác vụ chạy ngầm không bị giới hạn có thể dễ dàng làm nghẽn toàn bộ đường truyền ra ngoài. Một tiến trình upload database lên S3 không bị bóp băng thông chiếm 100 Mbps sẽ nhanh chóng bóp nghẹt các request REST và GraphQL thời gian thực. HTB ngăn chặn tình trạng này bằng cách cấu trúc băng thông thành các phân cấp class cha – con với giới hạn burst chặt chẽ và mức băng thông sàn (floor) được bảo đảm.

Trong các thử nghiệm đo lường của chúng tôi trên máy chủ Ubuntu 22.04 LTS, việc phân chia ranh giới tốc độ rõ ràng đã kéo độ trễ response p99 của API từ 1.240ms xuống chỉ còn 42ms trong thời gian sao lưu cao điểm ban đêm.

Bước 1: Tạo Root HTB Qdisc

Gán HTB làm queuing discipline gốc và điều hướng lưu lượng chưa được phân loại về class dự phòng 1:20:

sudo tc qdisc add dev eth0 root handle 1: htb default 20

Bước 2: Định nghĩa Bandwidth Pool gốc

Khai báo trần băng thông ra ngoài tổng thể khả dụng. Trong ví dụ này, chúng ta giả định uplink chuẩn 100 Mbps:

sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit ceil 100mbit

Bước 3: Tạo các Sub-Class phân vùng

Chia nhỏ pool 100 Mbps thành các làn riêng biệt cho lưu lượng HTTP quan trọng và các pipeline đồng bộ ưu tiên thấp:

# Class 1:10 - Lưu lượng API ưu tiên (Sàn: 80 Mbps, Trần burst: 100 Mbps, Độ ưu tiên: 1)
sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80mbit ceil 100mbit prio 1

# Class 1:20 - Tác vụ đồng bộ nền (Sàn: 10 Mbps, Trần cố định: 20 Mbps, Độ ưu tiên: 2)
sudo tc class add dev eth0 parent 1:1 classid 1:20 htb rate 10mbit ceil 20mbit prio 2

Bước 4: Điều hướng lưu lượng bằng Filter

Ánh xạ các gói tin chiều đi vào các class cụ thể dựa trên cổng đích. Các bộ lọc sau sẽ chuyển tiếp lưu lượng web tiêu chuẩn (port 443 và 80) vào class ưu tiên 1:10:

sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \
    match ip dport 443 0xffff flowid 1:10

sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \
    match ip dport 80 0xffff flowid 1:10

Tất cả các lưu lượng khác—bao gồm backup S3 hàng loạt, log shipper và xuất file—đều được định tuyến vào class 1:20, giới hạn tốc độ truyền tải ở mức 20 Mbps.

Xác minh và giám sát lưu lượng đang hoạt động

Theo dõi mức tiêu thụ token, số gói tin bị rớt và tốc độ truyền tải hiện tại theo thời gian thực:

tc -s class show dev eth0

Chạy kiểm thử throughput bằng lệnh iperf3 -c <target-ip> -p 5201. Bạn sẽ thấy các luồng truyền tải không phân loại chạm mức trần 20 Mbps trong khi lưu lượng web trên cổng 443 bứt phá mượt mà lên 100 Mbps.

Dọn dẹp cấu hình

Để xóa toàn bộ hệ thống phân cấp class, token bucket và các filter, chỉ cần xóa root qdisc:

sudo tc qdisc del dev eth0 root

Checklist tổng kết

  • Muốn mô phỏng sự cố mạng? Chạy tc qdisc add dev <interface> root netem delay 80ms 15ms loss 3% để kiểm thử độ trễ, jitter và gói tin bị mất trực tiếp trong kernel stack.
  • Muốn tránh nghẽn đường truyền chiều đi? Cấu hình tc qdisc add dev <interface> root handle 1: htb để thiết lập mức sàn băng thông tối thiểu và áp trần cứng.
  • Đã kiểm thử xong? Luôn chạy tc qdisc del dev <interface> root để tránh để lại các điểm nghẽn nhân tạo trên môi trường staging hoặc production.
Share: