Skip to main content

OVS Phần 10: Bonding/LACP Trên OVS — Đối Chiếu Với Port-Channel Và MLAG/vPC Vật Lý

Phần 10/21 trong loạt Open vSwitch (OVS) — nối tiếp Phần 9, nơi bạn đã dựng tunnel VXLAN/Geneve/GRE để một OVS host tự làm VTEP, gửi traffic đã đóng gói ra khỏi uplink NIC. Bài này lùi lại một bước, xử lý đúng câu hỏi nằm ngay dưới lớp overlay đó: bản thân cái uplink NIC ấy thường không chỉ có một sợi — một host production luôn có ít nhất hai NIC lên fabric để vừa tăng băng thông vừa chống đứt cáp/đứt switch. Đó chính là bài toán bonding/LACP — y hệt khái niệm port-channel/LAG bạn đã cấu hình hàng ngày trên leaf switch, chỉ khác giờ nó chạy ngay trong OVS. Đọc xong, Bài 11 sẽ sang QoS và rate limiting.

Mục tiêu bài học: sau bài này bạn dựng được một bond LACP thật trên OVS (dùng đúng ovs-vsctl add-bond), phân biệt rõ ba chế độ bonding OVS hỗ trợ và biết chế độ nào cần switch phía bên kia phải nói được 802.3ad, hiểu tại sao OVS không tái sử dụng driver bonding sẵn có của Linux kernel, và biết chính xác — theo đúng tài liệu chính thức, không suy đoán — khi nào một bond OVS được phép "spread" hai uplink sang hai switch vật lý khác nhau như một cặp MLAG/vPC.

Ba câu hỏi mà bài này trả lời:

  1. OVS có hẳn một cơ chế bonding riêng, không đụng tới bond0/bonding.ko của kernel Linux — khác biệt thật sự nằm ở đâu, và tại sao?
  2. Ba chế độ bonding của OVS (active-backup, balance-slb, balance-tcp) khác nhau thế nào, và chế độ nào bắt buộc switch phía kia phải đàm phán LACP thành công?
  3. Một bond OVS có được cắm hai uplink vào hai switch vật lý khác nhau như bạn vẫn làm với MLAG/vPC không — tài liệu chính thức nói gì, và thực tế vận hành có khớp hoàn toàn không?

Mục lục


1. Từ port-channel vật lý đến bonding trong OVS: vì sao không cần driver bonding của Linux kernel

💡 Hình dung như thế này: Trên một leaf switch, tạo một port-channel/ae0 gộp hai port vật lý là việc bạn làm ngay trong chính hệ điều hành switch (Junos/NX-OS) — không cần cài thêm driver gì, vì switch OS vốn đã có sẵn khối logic đó. Bonding trong OVS giống hệt tinh thần đó: ovs-vswitchd — chính "hệ điều hành switch" mà Bài 3 đã mổ xẻ — tự có sẵn khối logic bonding, không cần mượn một driver khác của Linux để làm việc này.

Trên một máy Linux thường, muốn gộp hai NIC thành một logical link, cách kinh điển là dùng driver bonding của kernel: nạp module bonding.ko (modprobe bonding), tạo một netdevice thật tên bond0 (ip link add bond0 type bond), rồi "enslave" từng NIC vật lý vào netdevice đó. Đây là một tầng hoàn toàn tách biệt với OVS — nếu bạn tạo bond0 kiểu này rồi mới add nó làm port của một OVS bridge, bạn đang chồng hai lớp bonding/switching độc lập lên nhau.

OVS không đi đường đó. Theo đúng cú pháp ovs-vsctl:

sudo ovs-vsctl add-br br0
sudo ovs-vsctl add-bond br0 bond0 eth0 eth1

Man page ovs-vsctl mô tả đúng lệnh này: "Creates on bridge a new port named port that bonds together the network devices given as each iface. At least two interfaces must be named." Nói cách khác, bonding trong OVS không phải một loại netdevice/kernel module riêng — nó chỉ là một Port record trong OVSDB (đúng bảng bạn đã thao tác ở Bài 5) có nhiều hơn một Interface con bên trong. Không có bond0 nào xuất hiện trong ip link show sau lệnh trên — toàn bộ quyết định "gói tin nào đi qua interface nào" nằm trong ovs-vswitchd, chạy hoàn toàn ở userspace, đúng bức tranh kernel-vs-userspace Bài 3 đã dựng: bonding là một quyết định thuộc lớp điều khiển/userspace, chỉ có forwarding thật sự mới chạm datapath.

Chi tiết xác nhận trực tiếp điều này nằm ngay trong schema, cột bond_fake_iface: "For a bonded port, whether to create a fake internal interface with the name of the port. Use only for compatibility with legacy software that requires this." Nghĩa là mặc định không hề có một netdevice tên bond0 nào được tạo ra — nó chỉ xuất hiện nếu bạn cố tình yêu cầu bằng cờ --fake-iface (ovs-vsctl --fake-iface add-bond ...), và chỉ để tương thích ngược với phần mềm cũ cần nhìn thấy một interface thật. Đây là đối lập rõ ràng nhất với driver bonding của kernel, nơi bond0 luôn luôn là một netdevice thật, bắt buộc phải tồn tại.

Hệ quả thực hành: cấu hình một bond OVS là một transaction OVSDB bình thường — dùng lại đúng cú pháp -- nối nhiều lệnh đã học ở Bài 5 Mục 5, và bond0 vẫn nhận tag=/trunks=/vlan_mode= y hệt bất kỳ Port nào khác (Bài 5 Mục 3, Bài 8) — vì xét cho cùng, một bonded Port cũng chỉ là một Port, chỉ khác là nó tham chiếu tới nhiều hơn một Interface.


2. Ba chế độ bonding của OVS: active-backup, balance-slb, và balance-tcp

Tài liệu bonding chính thức chia ba chế độ thành hai nhóm, dựa trên một tiêu chí duy nhất: switch phía bên kia có cần biết gì về việc bonding hay không.

Nhóm 1 — hoạt động được với bất kỳ switch nào, không cấu hình gì thêm ở phía switch:

  • balance-slb (Source Load Balancing): "Balances flows among members based on source MAC address and output VLAN, with periodic rebalancing as traffic patterns change." Vì switch phía kia hoàn toàn không biết hai port này thuộc cùng một bond, tài liệu nhấn mạnh: "On the upstream switch, do not configure the interfaces as a bond."
  • active-backup: "Assigns all flows to one member, failing over to a backup member when the active member is disabled." Đây là chế độ đơn giản nhất, và tài liệu nêu đúng một đặc quyền chỉ riêng nó có: "This is the only bonding mode in which interfaces may be plugged into different upstream switches." — chi tiết này chính là hạt giống của Mục 5.

Nhóm 2 — bắt buộc switch phía kia hỗ trợ 802.3ad và đàm phán LACP thành công:

  • balance-tcp: "Balances flows among members based on L3 and L4 protocol information such as IP addresses and TCP/UDP ports." Đây là chế độ có khả năng load-balance chi tiết nhất (theo từng flow L3/L4, thay vì chỉ theo MAC nguồn như balance-slb), nhưng đổi lại đòi hỏi đúng điều kiện tài liệu ghi rõ: "The following modes require the upstream switch to support 802.3ad with successful LACP negotiation. If LACP negotiation fails and other-config:lacp-fallback-ab is true, then active-backup mode is used."

Một chi tiết lịch sử đáng biết khi nhắc "802.3ad": bản thân chuẩn IEEE đã đổi tên từ năm 2008 — phần Link Aggregation được tách khỏi 802.3 (Ethernet) và xuất bản lại thành IEEE 802.1AX, vì bản chất LAG không phải một khái niệm riêng của Ethernet. Tài liệu và schema OVS vẫn dùng tên cũ "802.3ad" — đúng như cách gần như toàn ngành vẫn gọi — nên bài này giữ nguyên cách gọi đó, chỉ ghi chú để không nhầm khi tra cứu chuẩn gốc.

Nếu cột bond_mode không được set, schema xác nhận giá trị mặc định: "The type of bonding used for a bonded port. Defaults to active-backup if unset."

Ba Chế Độ Bonding Của OVSTiêu chí chia nhóm: switch phía kia có cần biết gì về bond khôngactive-backupbalance-slbbalance-tcpHash / chọnmember theoKhông hash — 1 memberactive duy nhất tại 1 thời điểmSource MAC + VLAN(rebalance định kỳ)IP + TCP/UDP port(theo từng flow L3/L4)Switch phía kiacần cấu hình gì?Không — port thườngKhông — port thườngCó — 802.3ad/LACPphải negotiate thành côngCắm 2 uplink vào2 switch khác nhau?Được — mode duy nhấtKhông đảm bảoChỉ nếu 2 switch là1 cặp MLAG/vPC (Mục 5)Độ phức tạp /khi nào dùngĐơn giản nhất — ưu tiênkhi LACP không khả thiCần LACP nhưng switchchưa/không hỗ trợBăng thông + load-balancetốt nhất trong 3 chế độMặc định bond_mode = active-backup nếu không setbalance-slb và active-backup: không cấu hình interface phía switch thành 1 bondbalance-tcp thất bại negotiate → fallback active-backup nếu other-config:lacp-fallback-ab=true (Mục 3)

3. LACP đàm phán thật sự thế nào: off, active, passive, fallback, và timing

💡 Hình dung như thế này: lacp=active giống một người luôn chủ động bắt tay trước ở mọi cuộc gặp; lacp=passive giống một người lịch sự — sẵn sàng bắt tay nếu người kia đưa tay ra trước, nhưng không bao giờ chủ động đưa tay trước. Nếu cả hai đầu một liên kết đều "passive", không ai chủ động — bắt tay không bao giờ xảy ra. Vì vậy trong thực tế, quy ước phổ biến nhất là để host chạy active, còn switch chạy passive (hoặc cả hai cùng active cũng luôn hoạt động).

Cột lacp trên Port điều khiển hành vi này, và một chi tiết dễ bị bỏ sót: nó không chỉ áp dụng cho Port đã bonded. Schema nói rõ: "LACP may be enabled on non-bonded ports for the benefit of any switches they may be connected to." — nghĩa là bạn có thể bật LACP trên một Port chỉ có đúng một Interface, chỉ để nó biết "nói chuyện" LACP với một switch phía kia đang mong đợi điều đó (Mục 6 sẽ tận dụng đúng chi tiết này để mô phỏng phía "switch" trong lab). Ba giá trị hợp lệ, theo đúng schema:

  • off (mặc định nếu không set) — không chạy LACP.
  • active"allowed to initiate LACP negotiations."
  • passive"allowed to participate in LACP negotiations initiated by a remote switch, but not allowed to initiate such negotiations themselves."

Nếu bật LACP mà phía đối diện không hỗ trợ, hành vi mặc định khá nghiêm khắc: "If LACP is enabled on a port whose partner switch does not support LACP, the bond will be disabled, unless other-config:lacp-fallback-ab is set to true." Cột other_config:lacp-fallback-ab chính là cái van an toàn cho tình huống đó, và schema mô tả rất rõ hai nhánh: "If the partner switch does not support LACP, setting this option to true allows openvswitch to fallback to active-backup. If the option is set to false, the bond will be disabled. In both the cases, once the partner switch is configured to LACP mode, the bond will use LACP." Đây là lý do thực tế quan trọng: nhiều switch vật lý chặn toàn bộ traffic trên các port đã cấu hình LACP cho tới khi đàm phán xong — nếu bạn bật LACP ở OVS trước khi switch kịp cấu hình xong phía nó, lacp-fallback-ab=true giúp bond vẫn hoạt động (ở chế độ active-backup tạm thời) thay vì đứng im chờ.

Vài tham số timing/định danh khác đáng biết, đều nằm trong nhóm "LACP Configuration" của schema:

  • other_config:lacp-time"By default slow is used. When configured to be fast LACP heartbeats are requested at a rate of once per second causing connectivity problems to be detected more quickly. In slow mode, heartbeats are requested at a rate of once every 30 seconds."
  • other_config:lacp-system-id"Must be a nonzero MAC address. Defaults to the bridge Ethernet address if unset." Đây là địa chỉ mà chính Port này tự xưng danh với đối tác LACP — mọi Port thuộc cùng một bridge, nếu không set riêng, sẽ dùng chung một system-id (MAC của bridge). Chi tiết này quay lại đúng lúc ở Mục 5 và Mục 6.
  • other_config:lacp-system-priority — dùng khi hai hệ thống bất đồng quyết định, "the system with the numerically lower priority" thắng.

Ở cấp Interface (không phải Port), ba khóa riêng cho từng thành viên bond: lacp-port-id, lacp-port-priority (ưu tiên interface nào được chọn tham gia aggregate khi có nhiều hơn số lượng switch hỗ trợ), và lacp-aggregation-key — schema lưu ý: "Interfaces with different aggregation keys may not be active within a given Port at the same time.", tức đây là cách buộc chỉ một tập con Interface được phép cùng hoạt động trong một bond tại một thời điểm.


4. Phát hiện lỗi liên kết và rebalancing: updelay, downdelay, carrier hay miimon

Một liên kết vừa "chết" hay vừa "sống lại" cần được phát hiện trước khi bonding có thể phản ứng — cột other_config:bond-detect-mode chọn cơ chế: "Defaults to carrier which uses each interface's carrier to detect failures. When set to miimon, will check for failures by polling each interface's MII" (MII — Media Independent Interface, chuẩn giao tiếp phần cứng giữa NIC và PHY, dùng để hỏi trực tiếp trạng thái liên kết vật lý thay vì chỉ dựa vào tín hiệu carrier do driver báo lên), tần suất polling khi ở chế độ miimon do other_config:bond-miimon-interval (mili-giây) quyết định.

Hai cột bond_updelay/bond_downdelay chống lại hiện tượng "flapping" (liên kết lên-xuống liên tục trong thời gian ngắn, ví dụ khi cắm lại cáp hay switch đang reload):

  • bond_updelay: "The number of milliseconds for which the link must stay up on an interface before the interface is considered to be up... This setting is honored only when at least one bonded interface is already enabled. When no interfaces are enabled, then the first bond interface to come up is enabled immediately." — chi tiết cuối rất quan trọng: nếu bond đang "trắng" hoàn toàn (không interface nào active, ví dụ ngay sau khi khởi động), interface đầu tiên có carrier được bật ngay lập tức, không phải chờ updelay — updelay chỉ áp dụng cho các interface thêm vào sau một interface đã active.
  • bond_downdelay: tương tự, cho chiều ngược lại khi liên kết mất.

Với riêng balance-slb/balance-tcp, còn một chu trình khác chạy song song với phát hiện lỗi: rebalancing. Tài liệu bonding mô tả: "Every 10 seconds, vswitchd rebalances the bond members" — xem xét lưu lượng gần đây (nặng hơn cho dữ liệu mới) trên từng member, và nếu một member đang tải nặng hơn đáng kể so với member tải nhẹ nhất, di chuyển bớt một phần flow sang. Chu kỳ này cấu hình được qua other_config:bond-rebalance-interval (mili-giây): "If zero, load balancing is disabled on the bond (link failure still cause flows to move). If less than 1000ms, the rebalance interval will be 1000ms." Cuối cùng, other_config:bond-primary cho phép ghim một Interface cụ thể làm active mặc định — nhưng chỉ có tác dụng "when other_config:bond_mode is active-backup or if balance-tcp falls back to active-backup", tức vô nghĩa với balance-slb hay balance-tcp đang chạy LACP bình thường (những mode đó không có khái niệm "một active duy nhất" để ghim).

Updelay/Downdelay: Vì Sao Bond Không Phản Ứng Tức ThìHai chu trình đếm ngược độc lập, chống flapping cho hai chiều xuống/lênLink mất carrierđếm bond_downdelay(mili-giây, mặc định 0)member: disabledLink có carriertrở lạiđếm bond_updelay(mili-giây, mặc định 0)member: enabledNgoại lệ: nếu bond đang KHÔNG còn member nào enabled (vd vừa khởi động),member đầu tiên có carrier được bật NGAY LẬP TỨC — bỏ qua updelay hoàn toànupdelay chỉ áp dụng cho member thêm vào SAU khi đã có ít nhất 1 member activebond_updelay/bond_downdelay = 0 (mặc định) nghĩa là bật/tắt tức thì, không đếm gì cả

Verify các giá trị đã set thật sự nằm trong OVSDB (không chỉ tưởng là đã set), dùng ovs-vsctl list — công cụ đã dùng ở Bài 5:

sudo ovs-vsctl set port bond0 \
other_config:bond-detect-mode=miimon \
other_config:bond-miimon-interval=100 \
other_config:bond-rebalance-interval=5000
sudo ovs-vsctl list port bond0
# ===== Output minh họa (rút gọn) =====
_uuid : 6f2b1a44-9e3d-4c8a-b716-2a1f9c0d8b71
bond_active_slave : "4e:3f:aa:11:22:01"
bond_downdelay : 0
bond_fake_iface : false
bond_mode : balance-tcp
bond_updelay : 0
interfaces : [c1a2b3d4-..., c1a2b3d5-...]
lacp : active
name : "bond0"
other_config : {bond-detect-mode=miimon, bond-miimon-interval="100", bond-rebalance-interval="5000"}
# =======================================

(Output minh họa — tên cột đúng theo schema thật, số liệu/UUID chỉ mang tính ví dụ. Chú ý cột bond_active_slave: dù ovs-appctl/tài liệu đã đổi thuật ngữ hiển thị sang "member" từ lâu (Mục 6 đã nhắc), tên cột OVSDB gốc vẫn giữ nguyên bond_active_slave để không phá vỡ tương thích ngược với phần mềm đang tích hợp qua OVSDB — một ví dụ cụ thể cho việc đổi tên chỉ áp dụng ở lớp hiển thị/CLI, không đụng tới tên cột lưu trữ thật.)


5. Đối lập MLAG và vPC: khi nào một bond OVS spread được sang hai leaf switch khác nhau

Đây là câu hỏi kiến trúc quan trọng nhất bài, và may mắn là tài liệu OVS trả lời rất dứt khoát, không mập mờ: theo đúng trích dẫn đã nêu ở Mục 2, chỉ active-backup"the only bonding mode in which interfaces may be plugged into different upstream switches". Nói thẳng ra: về mặt tài liệu chính thức, balance-tcp/LACP không được liệt kê là an toàn khi hai member cắm vào hai switch độc lập.

Lý do kỹ thuật nằm ngay trong bản chất giao thức LACP, không phải một giới hạn tùy tiện của OVS: LACP gom nhiều liên kết vào một aggregate dựa trên việc so khớp system-id mà đối tác (partner) tự xưng danh trên từng liên kết (đúng cột lacp-system-id Mục 3 vừa nói). Nếu hai switch độc lập, không đồng bộ với nhau, mỗi switch tự có một system-id riêng — từ góc nhìn của bond OVS, hai liên kết đó đang nói chuyện với hai đối tác khác nhau, không phải một, nên về nguyên tắc không được phép gộp thành một aggregate LACP.

💡 Hình dung như thế này: LACP giống việc bạn chỉ đồng ý ký một hợp đồng lao động duy nhất với một công ty — nếu hai "công ty" (hai switch) ký tên khác nhau trên hai bản hợp đồng, bạn không thể coi đó là một hợp đồng chung, dù công việc trông giống hệt nhau. MLAG/vPC chính là cách hai công ty đó thống nhất dùng chung một con dấu — về mặt danh tính LACP, cả hai giờ trông như một công ty duy nhất.

Đây chính xác là việc một cặp switch MLAG/vPC làm: đồng bộ để cùng trình diện một system-id logic dùng chung xuống thiết bị downstream, khiến từ góc nhìn của bond OVS, hai liên kết vẫn đang nói chuyện với "một đối tác" — điều kiện LACP cần để gộp aggregate vẫn được thỏa, dù vật lý là hai chassis tách biệt. Nhờ vậy trong thực tế, balance-tcp có thể hoạt động bắc qua một cặp MLAG/vPC thật — miễn là cặp switch đó có tính năng MLAG/vPC và đã bật đúng.

Tuy nhiên, đừng vội coi đây là một sự tương đương hoàn hảo với active-backup. Thảo luận trên mailing-list ovs-discuss (11/2024) ghi lại một vấn đề thật gặp trong vận hành: khi test một bond balance-tcp/LACP bắc qua một cặp switch MLAG với khoảng 1.000 gói UDP/giây, xuất hiện mất gói lẻ tẻ — điều không xảy ra ở cấu hình non-MLAG (hai member cùng cắm một switch). Người báo lỗi xác định gốc rễ: cơ chế rebalancing (Mục 4) của OVS di chuyển một flow đang chạy từ member này sang member khác giữa chừng — với một cặp MLAG/vPC, việc "di chuyển flow" đó đồng nghĩa flow bị chuyển từ chassis vật lý này sang chassis khác. Một người tham gia thảo luận tóm gọn đúng bản chất bất đồng: "None of the MLAG implementations expect distinct flows to flap between legs" — tức nhiều triển khai MLAG ở phía switch được thiết kế với giả định ngầm rằng một flow, một khi đã "dính" vào một chassis, sẽ ở nguyên đó, trong khi bonding OVS lại chủ động di chuyển flow qua lại đúng theo thiết kế cân bằng tải của nó. Đặt bond-rebalance-interval=0 (tắt hẳn rebalancing) loại bỏ hiện tượng mất gói trong bài test đó — nhưng đổi lại mất luôn khả năng tự cân bằng tải theo thời gian, một đánh đổi khác chứ không phải một "cách sửa" miễn phí.

Bond OVS Spread Sang Hai Switch: Cần Gì?LACP chỉ gộp aggregate khi cả hai liên kết thấy CÙNG một partner system-idOVS host — bond0Leaf switch Asystem-id: XLeaf switch Bsystem-id: X (đồng bộ qua MLAG/vPC)peer-link MLAG/vPCCùng system-id → balance-tcp/LACP gộp được 1 aggregateactive-backupHai switch KHÔNG cần đồng bộ gì —tài liệu OVS đảm bảo mode này an toànbalance-tcp/LACPBắt buộc 2 switch là 1 cặp MLAG/vPCthật — không tài liệu nào bảo đảm khácThực tế đã ghi nhận: rebalancing của OVS di chuyển flow giữa 2 member —với MLAG, đó là di chuyển flow giữa 2 chassis, một số triển khai MLAG không kỳ vọng điều này

Điều đáng chú ý là chính tài liệu chính thức của OVN — bộ điều khiển Bài 17 sẽ học kỹ — cũng nhắc tới đúng cơ chế này khi bàn về high-availability cho gateway, và xác nhận thẳng cùng một mô tả: "MLAG has multiple gateways work together to pretend to be a single L2 switch with a large LACP bond." — khớp chính xác cách diễn đạt "giả làm một switch duy nhất" vừa nêu ở trên. Nhưng ngay sau đó tài liệu cũng cảnh báo, dù về nguyên tắc đây là giải pháp đúng hướng: "That said, it's difficult to get right and not recommended." Đây là một tín hiệu độc lập, đến từ chính dự án OVN chứ không phải chỉ một thảo luận cộng đồng, củng cố thêm cho kết luận ở trên: MLAG/vPC không phải một "công tắc bật là xong" về mặt vận hành, dù đúng về mặt giao thức.

Kết luận thực hành, đúng tinh thần "không có đáp án tuyệt đối, chỉ có đánh đổi" mà Bài 9 đã nêu: nếu tiêu chí số một là an toàn tuyệt đối, đơn giản, không phụ thuộc tính năng switch, active-backup là lựa chọn duy nhất được tài liệu chính thức bảo đảm khi hai uplink đi hai switch khác nhau. Nếu cần băng thông tổng và load-balance theo flow (balance-tcp), bạn hoàn toàn có thể chạy nó qua một cặp MLAG/vPC thật — nhưng cần biết đây là một cấu hình đã có ghi nhận vấn đề thực tế xoay quanh rebalancing, không phải một combo "cứ bật là chạy trơn tru như hai NIC cùng một switch".


6. CLI lab: dựng bond LACP thật, verify bằng ovs-appctl, và test failover

Khác với lab tunnel ở Bài 9 — nơi bắt buộc phải có hai host/VM thật để tránh kernel coi cả hai VTEP là "địa chỉ cục bộ" — lab bonding này chạy tốt trên một máy duy nhất. Lý do: LACP là một giao thức thuần L2 chạy trên từng liên kết vật lý (hay veth), không đi qua bảng định tuyến IP, nên hai OVS bridge độc lập trên cùng một ovs-vswitchd vẫn trao đổi LACPDU thật với nhau qua veth, không phải dữ liệu giả lập.

Ý tưởng lab: một bridge br-host đóng vai trò OVS host với bond0 (lacp=active, bond_mode=balance-tcp); một bridge br-sw đóng vai trò switch, cũng có một bond (bond-sw, lacp=passive) — đúng cách một switch thật gộp nhiều port vật lý vào một port-channel/ae để phía kia LACP negotiate được. Vì cả hai member của bond-sw cùng thuộc một Port, chúng tự động dùng chung một lacp-system-id (mặc định là MAC của bridge, theo đúng Mục 3) — đây chính là điều kiện "một partner, một system-id" mà Mục 5 vừa giải thích, chỉ khác ở đây nó tự nhiên đúng vì chỉ có một bridge, thay vì phải đồng bộ qua MLAG như hai switch vật lý riêng biệt.

Dựng hai bridge và bond, nối chéo bằng hai cặp veth:

sudo ovs-vsctl add-br br-host
sudo ovs-vsctl add-br br-sw

# Hai cặp veth mô phỏng hai sợi cáp uplink
sudo ip link add veth-h1 type veth peer name veth-s1
sudo ip link add veth-h2 type veth peer name veth-s2
sudo ip link set veth-h1 up
sudo ip link set veth-h2 up
sudo ip link set veth-s1 up
sudo ip link set veth-s2 up

# Phía "host" — bond LACP active, load-balance theo L3/L4
sudo ovs-vsctl add-bond br-host bond0 veth-h1 veth-h2 \
-- set port bond0 lacp=active bond_mode=balance-tcp

# Phía "switch" — bond LACP passive, đúng cách một port-channel thật chờ host chủ động
sudo ovs-vsctl add-bond br-sw bond-sw veth-s1 veth-s2 \
-- set port bond-sw lacp=passive

Gắn một namespace vào mỗi đầu, đúng mẫu Bài 4/5, để có traffic thật đi qua bond:

# vm1 sau br-host
sudo ip netns add vm1
sudo ip link add veth0 type veth peer name veth0-ovs
sudo ip link set veth0 netns vm1
sudo ovs-vsctl add-port br-host veth0-ovs
sudo ip link set veth0-ovs up
sudo ip netns exec vm1 ip addr add 10.0.0.1/24 dev veth0
sudo ip netns exec vm1 ip link set veth0 up
sudo ip netns exec vm1 ip link set lo up

# vm2 sau br-sw
sudo ip netns add vm2
sudo ip link add veth1 type veth peer name veth1-ovs
sudo ip link set veth1 netns vm2
sudo ovs-vsctl add-port br-sw veth1-ovs
sudo ip link set veth1-ovs up
sudo ip netns exec vm2 ip addr add 10.0.0.2/24 dev veth1
sudo ip netns exec vm2 ip link set veth1 up
sudo ip netns exec vm2 ip link set lo up

Verify LACP đã negotiate thật, không chỉ cấu hình đã ghi vào OVSDB:

sudo ovs-appctl bond/show bond0
# ===== Output minh họa =====
---- bond0 ----
bond_mode: balance-tcp
bond may use recirculation: yes, Recirc-ID : 5
bond-hash-basis: 0
lb_output action: disabled, bond-id: -1
updelay: 0 ms
downdelay: 0 ms
next rebalance: 8452 ms
lacp_status: negotiated
lacp_fallback_ab: false
active-backup primary: <none>
active member mac: 4e:3f:aa:11:22:01(veth-h1)

member veth-h1: enabled
active member
may_enable: true

member veth-h2: enabled
may_enable: true
# =============================

(Output minh họa — cấu trúc và tên field đúng theo mã nguồn ofproto/bond.c thật, số liệu cụ thể — MAC, Recirc-ID, next rebalance — tùy máy. Hai dòng bond may use recirculation/lb_output action là chi tiết nội bộ về cách datapath tối ưu việc hash gói, không phải trọng tâm bài — chỉ cần biết chúng tồn tại. Trọng tâm cần đọc là lacp_status: negotiated, bằng chứng LACPDU đã trao đổi thành công với bond-sw, không phải chỉ cấu hình OVSDB khớp nhau một cách tình cờ. Ở các bản OVS cũ hơn — như bài viết gốc năm 2012 của Scott Lowe — cột này còn được gọi là slave thay vì member; mã nguồn hiện tại vẫn giữ lại bond/set-active-slave, bond/enable-slave, bond/disable-slave như alias tương thích ngược cho bond/set-active-member, bond/enable-member, bond/disable-member — xác nhận đúng việc đổi tên chứ không phải hai cơ chế khác nhau.)

Xem chi tiết đàm phán LACP, đặc biệt để xác nhận cả hai member cùng thấy một partner system-id — đúng điều kiện Mục 5 vừa giải thích:

sudo ovs-appctl lacp/show bond0
# ===== Output minh họa =====
---- bond0 ----
status: active negotiated
sys_id: 4e:3f:aa:11:22:01
sys_priority: 65534
aggregation key: 1
lacp_time: slow

member: veth-h1: current attached
port_id: 1
port_priority: 65535
may_enable: true

actor sys_id: 4e:3f:aa:11:22:01
actor sys_priority: 65534
actor port_id: 1
actor port_priority: 65535
actor key: 1
actor state: activity aggregation synchronized collecting distributing

partner sys_id: 5a:9c:bb:33:44:01
partner sys_priority: 65534
partner port_id: 1
partner port_priority: 65535
partner key: 1
partner state: activity aggregation synchronized collecting distributing

member: veth-h2: current attached
port_id: 2
port_priority: 65535
may_enable: true

actor sys_id: 4e:3f:aa:11:22:01
actor sys_priority: 65534
actor port_id: 2
actor port_priority: 65535
actor key: 1
actor state: activity aggregation synchronized collecting distributing

partner sys_id: 5a:9c:bb:33:44:01
partner sys_priority: 65534
partner port_id: 2
partner port_priority: 65535
partner key: 1
partner state: activity aggregation synchronized collecting distributing
# =============================

(Output minh họa — cấu trúc và tên field đúng theo mã nguồn lib/lacp.c thật. Chi tiết mấu chốt cần tự kiểm khi chạy thật: partner sys_id của cả veth-h1 lẫn veth-h2 phải giống hệt nhau — đó là MAC của bridge br-sw, vì cả hai member của bond-sw mặc định dùng chung một lacp-system-id. Nếu hai giá trị này khác nhau, LACP sẽ coi đó là hai partner riêng biệt và từ chối gộp thành một aggregate — đúng cơ chế đã giải thích ở Mục 5. Muốn xem số liệu PDU trao đổi thật (đếm gói LACP đã gửi/nhận, phát hiện lỗi liên tục), dùng thêm ovs-appctl lacp/show-stats bond0 — hữu ích khi debug một bond LACP đứng yên không negotiate.)

Ping xuyên qua cả hai bond để xác nhận data-plane thật sự thông:

sudo ip netns exec vm1 ping -c 3 10.0.0.2

Một lưu ý quan trọng để không hiểu lầm khi quan sát: balance-tcp chọn member theo hash của IP/port L3-L4 cho từng flow — một chuỗi ping ICMP đơn giản chỉ là một flow duy nhất, nên toàn bộ gói sẽ luôn đi qua đúng một member cố định (active member mac ở output bond/show phía trên), không "chia đều" ra cả hai. Muốn thấy load-balancing thật, cần nhiều flow song song khác 5-tuple (ví dụ nhiều phiên iperf3 với port nguồn khác nhau) — bài ping đơn lẻ ở đây chỉ dùng để xác nhận bond hoạt động, không phải để quan sát cân bằng tải.

Nếu chỉ nhớ được một điều sau bài này, hãy nhớ: bonding trong OVS không phải một hộp đen bí ẩn — nó chỉ là một Port bình thường tham chiếu tới nhiều hơn một Interface, và toàn bộ hành vi phức tạp trông thấy (ai gộp được với ai, ai thất bại thì rớt, ai spread được sang hai switch) đều quay về đúng một câu hỏi gốc: hai đầu liên kết có đang thấy cùng một partner system-id hay không. Một switch vật lý đơn lẻ tự nhiên thỏa điều đó; một cặp MLAG/vPC phải đồng bộ để giả vờ thỏa điều đó; còn active-backup là chế độ duy nhất không cần hỏi câu đó, vì nó chưa bao giờ đòi hỏi có một partner chung ngay từ đầu.


Bài tập cuối bài

  1. Trong khi vm1 đang ping liên tục sang vm2, rút một chân của bond bằng sudo ip link set veth-h1 down. Quan sát: ping có bị gián đoạn gói nào không (nên gần như không, vì bond_downdelay mặc định gần như tức thời và cơ chế fail-over hoạt động trong datapath, không cần đợi ping timeout), sau đó chạy lại ovs-appctl bond/show bond0 để xác nhận veth-h1 chuyển sang disabled còn traffic dồn hết qua veth-h2. Đưa veth-h1 up lại, xác nhận bond phục hồi cả hai member.
  2. Đổi bond0 từ balance-tcp sang active-backup (sudo ovs-vsctl set port bond0 bond_mode=active-backup), giữ nguyên lacp=active. Lặp lại bước 1 (rút một chân), so sánh output bond/show: giải thích tại sao trường active member mac giờ có ý nghĩa rõ ràng hơn hẳn so với khi bond đang ở balance-tcp — liên hệ đúng câu trong schema về other_config:bond-primary chỉ có tác dụng ở active-backup đã nêu ở Mục 4.
  3. Trên br-sw, đổi lacp=passive của bond-sw thành lacp=off. Xác nhận ovs-appctl bond/show bond0 phía br-host báo bond bị vô hiệu hóa hoàn toàn (không còn member nào enabled) — đây chính là hành vi mặc định khi LACP negotiation thất bại mà Mục 3 đã trích. Sau đó set thêm sudo ovs-vsctl set port bond0 other_config:lacp-fallback-ab=true và xác nhận bond hoạt động trở lại — nhưng lần này ở chế độ active-backup tạm thời (kiểm bằng lacp_status: off kèm chỉ một member enabled trong bond/show), đúng cơ chế "van an toàn" đã giải thích ở Mục 3.

Nguồn tham khảo

  • Ba chế độ bonding và định nghĩa từng chế độ (balance-slb hash theo source MAC + VLAN với rebalancing định kỳ; active-backup"the only bonding mode in which interfaces may be plugged into different upstream switches"; balance-tcp hash theo IP/TCP/UDP port nhưng "require the upstream switch to support 802.3ad with successful LACP negotiation", fallback active-backup nếu lacp-fallback-ab bật); chu kỳ rebalancing mặc định 10 giây — Bonding, docs.openvswitch.org.
  • Định nghĩa đầy đủ các cột Port/Interface liên quan bonding: bond_mode mặc định active-backup; cột lacp với 3 giá trị off/active/passive, có thể bật trên port không bonded "for the benefit of any switches they may be connected to"; other_config:lacp-fallback-ab; other_config:lacp-time (fast = 1 lần/giây, slow mặc định = 1 lần/30 giây); other_config:lacp-system-id mặc định là MAC của bridge; bond_updelay/bond_downdelay; other_config:bond-detect-mode (carrier mặc định/miimon); other_config:bond-rebalance-interval; other_config:bond-primary chỉ có tác dụng ở active-backup; bond_fake_iface chỉ tạo netdevice giả khi cần tương thích phần mềm cũ; cột Interface lacp-port-id/lacp-port-priority/lacp-aggregation-key — mã nguồn schema, vswitch.xml, github.com/openvswitch/ovs.
  • Cú pháp và mô tả lệnh add-bond: "Creates on bridge a new port named port that bonds together the network devices given as each iface. At least two interfaces must be named.", cờ --fake-iface chỉ dùng cho tương thích phần mềm cũ — ovs-vsctl(8), man7.org.
  • Ghi nhận thực tế về vấn đề rebalancing của OVS bonding khi chạy balance-tcp/LACP bắc qua một cặp switch MLAG: mất gói lẻ tẻ ở tải thấp (~1.000 UDP/giây), biến mất khi đặt bond-rebalance-interval=0; thảo luận cộng đồng nêu nguyên nhân "None of the MLAG implementations expect distinct flows to flap between legs" — 11/2024, ovs-discuss mailing list, mail.openvswitch.org.
  • Lịch sử đổi tên chuẩn: Link Aggregation được tách khỏi IEEE 802.3 và xuất bản lại thành IEEE 802.1AX từ 2008, dù ngành vẫn quen gọi "802.3ad" — 2008, IEEE 802.1AX, standards.ieee.org.
  • MLAG mô tả như "multiple gateways work together to pretend to be a single L2 switch with a large LACP bond", nhưng "difficult to get right and not recommended" cho OVN gateway HA — OVN Gateway High Availability Plan, docs.ovn.org.
  • Cấu trúc và tên field chính xác của output ovs-appctl bond/show/lacp/show (đối chiếu hàm bond_print_details/lacp_print_details), 3 giá trị lacp_status thật (negotiated/configured/off), và các unixctl command liên quan (bond/set-active-member, lacp/show-stats) cùng alias tương thích ngược bond/set-active-slave/bond/enable-slave/bond/disable-slave xác nhận việc đổi thuật ngữ slavemember — mã nguồn, ofproto/bond.c và lib/lacp.c, github.com/openvswitch/ovs.

Tiếp theo: Module 2, Bài 11 — QoS và rate limiting trên OVS.