Linuxボンディング設定:モードの違いとCentOS 7.9のactive-backup構成
EdwardMoon
Linuxのボンディングは、2つ以上の物理ネットワークインターフェースを1つの論理インターフェースにまとめる機能です。障害時の経路切り替えや複数接続の負荷分散ができますが、効果はモードとスイッチ構成によって異なります。この記事ではモードを比較した後、CentOS 7.9のnetwork-scripts環境でactive-backupを構成し、復旧する方法を説明します。
適用範囲:既存のnetwork.serviceが管理するIPv4固定アドレス環境の保守例です。CentOS Linux 7は2024年6月30日にサポートが終了しているため、新規サーバーの標準にはしないでください。CentOS公式のサポート終了案内
ボンディングでできることと限界
active-backupは1つのNICだけを通信に使い、障害時に別のNICへ切り替えます。1Gbps NICを2つ束ねても、通常時の帯域幅はアクティブなNIC 1つの速度に制限されます。一方、LACPなどの負荷分散モードは複数フローを複数NICへ分散できます。ただし、単一接続の速度が常にNICの数だけ増えるわけではありません。
ボンディングはLinux bondingドライバーを使用します。teamdによるネットワークチーミングは同様の目的に使える別の実装なので、設定方法を混用しないでください。リンク障害へ対応できても、スイッチ全体、上位ルーター、共通の電源系統が故障すると通信が途切れる場合があります。
モードとスイッチ構成
スイッチへ接続する一般的なサーバー環境を比較します。直接接続や特殊なトポロジーは別途検証が必要です。
| モード | 動作 | スイッチ側の要件 | 選択時の注意 |
|---|---|---|---|
| 0: balance-rr | 送信パケットをNICへ順番に分散 | 通常は静的ポート集約が必要 | パケット順序が変わる場合がある |
| 1: active-backup | 1つのNICを使い、障害時に切り替え | LACP・静的集約は不要 | 速度は合算されない |
| 2: balance-xor | ハッシュ方針で送信経路を選択 | 通常は静的ポート集約が必要 | LACPでネゴシエーションするモードではない |
| 3: broadcast | 同じパケットを全NICから送信 | 通常はポート集約が必要 | 通信の複製であり、スループットの合算ではない |
| 4: 802.3ad / LACP | 集約グループ内でフローを分散 | 対応ポートでLACP設定が必要 | 同一グループの速度、duplex、ハッシュ方針を確認 |
| 5: balance-tlb | 送信を分散し、受信は1つのNIC | 特別な集約設定は不要 | ドライバーの対応を確認 |
| 6: balance-alb | TLBにIPv4受信の分散を追加 | 特別な集約設定は不要 | ARPネゴシエーションとMAC変更の対応に依存 |
モード6はモード5と異なり、IPv4受信も分散できます。モード0・2・3の静的集約と、モード4のLACPを区別してください。詳細はLinuxカーネルのボンディング資料のモードとSwitch Configurationの節を参照します。
作業環境と変更前の確認
例ではeth0とeth1をbond0に接続し、サーバーを192.168.1.100/24、ゲートウェイを192.168.1.1にします。実際の名前とアドレスに置き換え、IP重複がなく、ゲートウェイが同じサブネットにあるか確認してください。両ポートを同じVLANと通信網へ接続し、active-backup用ポートをLACPグループに束ねないでください。
設定の適用中は対象NICの通信が止まります。SSHだけで作業せず、IPMI、iDRAC、仮想化コンソールなどの独立した管理経路と保守時間を確保します。VLAN、bridge、仮想IP、複数デフォルトルート、ポリシールーティング、IPv6を使っている場合は、それらをbondへ移す別の計画が必要です。以下の生成スクリプトは、すべての構成を変換するツールではありません。
cat /etc/centos-release
uname -r
ip -br link
ip -br address
ip route show table all
ip rule show
systemctl is-active network
systemctl is-active NetworkManager
modinfo bonding
ethtool eth0
ethtool eth1
既存の管理主体がnetworkで、NetworkManagerが非アクティブであることを確認します。NetworkManager管理下のサーバーでは、この例のためにサービスを停止せず、その環境向けのnmcliボンディング手順を使います。同じNICを2つのツールに同時管理させないでください。
modinfo bondingはモジュール情報を照会します。一般的なディストリビューションのカーネルにはbondingドライバーが含まれるため、lsmodにないという理由だけでkmod-bondingを追加しないでください。modinfo自体が失敗する場合は、稼働中のカーネルとインストール済みモジュールの対応を先に確認します。
ifcfgファイルの完成形
IP、ゲートウェイ、ボンディングオプションを/etc/sysconfig/network-scripts/ifcfg-bond0へ記述します。miimon=100は100ミリ秒間隔のリンク監視であり、上位経路全体の通信を保証するものではありません。
DEVICE=bond0
NAME=bond0
TYPE=Bond
BONDING_MASTER=yes
BOOTPROTO=none
ONBOOT=yes
NM_CONTROLLED=no
IPADDR=192.168.1.100
PREFIX=24
GATEWAY=192.168.1.1
DEFROUTE=yes
PEERDNS=no
IPV6INIT=no
BONDING_OPTS="mode=active-backup miimon=100"
PEERDNS=noにより、このプロファイルからDNS設定を変更しません。適用後は既存の名前解決も確認してください。例のIPV6INIT=noはIPv4専用という前提なので、IPv6を使うサーバーへそのまま適用しないでください。
物理NICにIPやデフォルトゲートウェイを重複指定せず、MASTERとSLAVEを設定します。以下は2つの異なるファイルです。
# /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
NAME=eth0
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
NM_CONTROLLED=no
MASTER=bond0
SLAVE=yes
IPV6INIT=no
# /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
NAME=eth1
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
NM_CONTROLLED=no
MASTER=bond0
SLAVE=yes
IPV6INIT=no
同じNICに対して自動起動する重複ifcfgや別プロファイルを残さないでください。ボンディングごとのオプションはBONDING_OPTSで指定します。適切なifcfgがあればネットワークスクリプトが必要なモジュールをロードするので、/etc/modules-load.d/bonding.confへ毎回行を追加する必要はありません。RHEL 7公式のifcfgボンディング構成
バックアップと設定候補を作るBashスクリプト
次をprepare-bond.shへ保存し、変数を環境に合わせて変更します。現在のifcfgとネットワーク状態をroot専用ディレクトリへ保存し、新しい設定を別の場所へ生成します。実際のifcfgの上書きとNICの再起動は次の節で行います。
#!/bin/bash
# CentOS 7.9 / network.service専用:バックアップと設定候補だけを生成する。
set -euo pipefail
umask 077
BOND_DEVICE="bond0"
ETH0_DEVICE="eth0"
ETH1_DEVICE="eth1"
IP_ADDRESS="192.168.1.100"
PREFIX="24"
GATEWAY="192.168.1.1"
CFG_DIR="/etc/sysconfig/network-scripts"
die() { printf '%s\n' "$*" >&2; exit 1; }
valid_ipv4() {
local value="$1" octet a b c d
[[ "$value" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 1
IFS=. read -r a b c d <<< "$value"
for octet in "$a" "$b" "$c" "$d"; do
(( 10#$octet <= 255 )) || return 1
done
}
[[ "$EUID" -eq 0 ]] || die "root로 실행하세요."
[[ -d "$CFG_DIR" ]] || die "network-scripts 경로가 없습니다."
for nic in "$BOND_DEVICE" "$ETH0_DEVICE" "$ETH1_DEVICE"; do
[[ "$nic" =~ ^[a-zA-Z0-9_-]{1,15}$ ]] || die "인터페이스 이름을 확인하세요."
done
[[ "$ETH0_DEVICE" != "$ETH1_DEVICE" ]] || die "서로 다른 물리 NIC가 필요합니다."
[[ "$BOND_DEVICE" != "$ETH0_DEVICE" && "$BOND_DEVICE" != "$ETH1_DEVICE" ]] ||
die "bond 이름이 물리 NIC와 같습니다."
[[ ! -e "/sys/class/net/$BOND_DEVICE" && ! -e "$CFG_DIR/ifcfg-$BOND_DEVICE" ]] ||
die "기존 bond가 있습니다. 기존 구성 변경은 이 예제 범위 밖입니다."
valid_ipv4 "$IP_ADDRESS" || die "IP 주소 형식이 잘못되었습니다."
valid_ipv4 "$GATEWAY" || die "게이트웨이 주소 형식이 잘못되었습니다."
[[ "$GATEWAY" != "$IP_ADDRESS" ]] || die "게이트웨이는 서버 자신과 달라야 합니다."
[[ "$PREFIX" =~ ^([1-9]|[12][0-9]|3[0-2])$ ]] || die "PREFIX는 1~32여야 합니다."
systemctl is-active --quiet network ||
die "기존 network.service 환경에서만 사용하세요."
if systemctl is-active --quiet NetworkManager; then
die "NetworkManager 환경입니다. 해당 환경의 nmcli 본딩 절차를 사용하세요."
fi
modinfo bonding >/dev/null
for nic in "$ETH0_DEVICE" "$ETH1_DEVICE"; do
[[ -e "/sys/class/net/$nic" ]] || die "NIC가 없습니다: $nic"
[[ -f "$CFG_DIR/ifcfg-$nic" ]] || die "기존 ifcfg 파일이 없습니다: $nic"
[[ ! -L "/sys/class/net/$nic/master" ]] || die "이미 다른 장치에 종속된 NIC입니다: $nic"
[[ ! -e "$CFG_DIR/route-$nic" && ! -e "$CFG_DIR/rule-$nic" &&
! -e "$CFG_DIR/route6-$nic" && ! -e "$CFG_DIR/rule6-$nic" ]] ||
die "별도 경로/정책 설정은 bond에 따로 이전해야 합니다: $nic"
if ip -6 address show dev "$nic" scope global | grep -q 'inet6 '; then
die "IPv6 주소가 있는 NIC입니다. IPv6 이전 계획을 먼저 작성하세요: $nic"
fi
done
PLAN_DIR=$(mktemp -d "/root/bond-plan.XXXXXXXX")
mkdir "$PLAN_DIR/before" "$PLAN_DIR/new"
for nic in "$ETH0_DEVICE" "$ETH1_DEVICE"; do
cp -a "$CFG_DIR/ifcfg-$nic" "$PLAN_DIR/before/"
done
ip address show > "$PLAN_DIR/address-before.txt"
ip route show table all > "$PLAN_DIR/routes-before.txt"
ip rule show > "$PLAN_DIR/rules-before.txt"
printf 'BOND_DEVICE=%q\nETH0_DEVICE=%q\nETH1_DEVICE=%q\n' \
"$BOND_DEVICE" "$ETH0_DEVICE" "$ETH1_DEVICE" > "$PLAN_DIR/names.sh"
cat > "$PLAN_DIR/new/ifcfg-$BOND_DEVICE" <<EOF
DEVICE=$BOND_DEVICE
NAME=$BOND_DEVICE
TYPE=Bond
BONDING_MASTER=yes
BOOTPROTO=none
ONBOOT=yes
NM_CONTROLLED=no
IPADDR=$IP_ADDRESS
PREFIX=$PREFIX
GATEWAY=$GATEWAY
DEFROUTE=yes
PEERDNS=no
IPV6INIT=no
BONDING_OPTS="mode=active-backup miimon=100"
EOF
for nic in "$ETH0_DEVICE" "$ETH1_DEVICE"; do
cat > "$PLAN_DIR/new/ifcfg-$nic" <<EOF
DEVICE=$nic
NAME=$nic
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
NM_CONTROLLED=no
MASTER=$BOND_DEVICE
SLAVE=yes
IPV6INIT=no
EOF
done
printf '백업 및 후보 설정: %s\n' "$PLAN_DIR"
printf '실제 설정과 네트워크 상태는 변경하지 않았습니다.\n'
bash -n prepare-bond.shで構文を確認し、sudo bash prepare-bond.shで設定候補を作成します。出力されたパスとバックアップを記録してください。この検査ではIP重複、VLANの一致、スイッチ方針、すべてのルーティング依存関係までは確認できないため、変更前の確認も必要です。
コンソールから適用する
以下を1段階ずつ実行し、失敗したら先へ進まないでください。diffの終了コード1は差分があるという意味です。意図した変更か確認してからNICを停止します。例のIPへ変更すると、管理接続先のアドレスも変わる場合があります。
# rootコンソールで実行する。以下を生成スクリプトが出力した実際のパスへ変更する。
PLAN_DIR=/root/bond-plan.XXXXXXXX
test -f "$PLAN_DIR/names.sh" || exit 1
source "$PLAN_DIR/names.sh"
CFG_DIR=/etc/sysconfig/network-scripts
# 設定候補の確認:この時点までは通信状態を変更しない。
cat "$PLAN_DIR/new/ifcfg-$BOND_DEVICE"
diff -u "$PLAN_DIR/before/ifcfg-$ETH0_DEVICE" "$PLAN_DIR/new/ifcfg-$ETH0_DEVICE"
diff -u "$PLAN_DIR/before/ifcfg-$ETH1_DEVICE" "$PLAN_DIR/new/ifcfg-$ETH1_DEVICE"
# 確認が終わってから実行する。ここから対象NICの通信が切れる。
# ifdownに失敗したら原因を確認して中止する。
ifdown "$ETH0_DEVICE"
ifdown "$ETH1_DEVICE"
# 次の3ファイルを配置する。1つでも失敗したら有効化せず復旧する。
install -o root -g root -m 600 "$PLAN_DIR/new/ifcfg-$BOND_DEVICE" "$CFG_DIR/ifcfg-$BOND_DEVICE"
install -o root -g root -m 600 "$PLAN_DIR/new/ifcfg-$ETH0_DEVICE" "$CFG_DIR/ifcfg-$ETH0_DEVICE"
install -o root -g root -m 600 "$PLAN_DIR/new/ifcfg-$ETH1_DEVICE" "$CFG_DIR/ifcfg-$ETH1_DEVICE"
restorecon "$CFG_DIR/ifcfg-$BOND_DEVICE" "$CFG_DIR/ifcfg-$ETH0_DEVICE" "$CFG_DIR/ifcfg-$ETH1_DEVICE"
ifup "$BOND_DEVICE"
ifup "$ETH0_DEVICE"
ifup "$ETH1_DEVICE"
アドレス、モード、フェイルオーバーを検証する
ip -br address show bond0
ip -d link show bond0
ip link show master bond0
cat /proc/net/bonding/bond0
ip route
ping -c 4 -I bond0 192.168.1.1
ethtool eth0
ethtool eth1
bond0が表示されるだけでは完了ではありません。アドレスがbondだけにあること、物理NIC 2つがbond配下にあること、デフォルトルートが正しいことを確認します。/proc/net/bonding/bond0では次の項目を読み取ります。
# 表示形式の例であり、特定サーバーで測定した結果ではない。
Bonding Mode: fault-tolerance (active-backup)
Currently Active Slave: eth0
MII Status: up
MII Polling Interval (ms): 100
...
Slave Interface: eth0
MII Status: up
Speed: 1000 Mbps
...
Slave Interface: eth1
MII Status: up
Speed: 1000 Mbps
Currently Active Slaveは現在通信に使うNICです。両NICのMII Statusが正常か確認し、実リンク速度は物理NICごとのethtoolで確認します。bondに表示される合算速度を、実測スループットと解釈しないでください。
- 別クライアントからサーバーIPへ継続的なpingと実サービスへの要求を送る。
- 保守コンソールを維持し、アクティブNICのケーブルまたは対応スイッチポートを1つだけ遮断する。
- アクティブNICが切り替わって通信が回復するか、パケット損失とサービスへの影響を記録する。
- 経路を復旧し、両ポートのリンクを確認する。反対側も同じ方法で試験する。
- 新しい管理セッション、DNS照会、サービス応答を確認し、計画した再起動で永続設定も確認する。
ケーブル障害と上位ネットワーク障害の試験は異なります。miimonはリンク状態を監視するため、リンクが生きている上位スイッチ・ルーターの障害を検出できない場合があります。実サービスで必要な障害範囲とスイッチ構成を別途試験してください。
問題発生時に元へ戻す
コンソールで以下を行います。作業前にbondが存在しなかった新規構成を、準備スクリプトが保存した元のNIC設定へ戻す手順です。各段階のエラーと現在の状態を確認し、原本を確認してから復元します。適用中にルーティング、DNS、ファイアウォールを別途変更した場合は、それぞれも戻す必要があります。
# 適用時に出力された実際のバックアップパスを指定する。rootコンソールで実行する。
PLAN_DIR=/root/bond-plan.XXXXXXXX
test -f "$PLAN_DIR/names.sh" || exit 1
source "$PLAN_DIR/names.sh"
CFG_DIR=/etc/sysconfig/network-scripts
# 各段階の結果を確認する。未作成のbondでifdownが失敗した場合は、まず状態を確認する。
ifdown "$BOND_DEVICE"
ifdown "$ETH0_DEVICE"
ifdown "$ETH1_DEVICE"
# この手順は、作業前にbondが存在しなかった新規構成だけに適用する。
ip link delete "$BOND_DEVICE" type bond
mv "$CFG_DIR/ifcfg-$BOND_DEVICE" "$PLAN_DIR/ifcfg-bond-failed"
cp -a "$PLAN_DIR/before/ifcfg-$ETH0_DEVICE" "$CFG_DIR/ifcfg-$ETH0_DEVICE"
cp -a "$PLAN_DIR/before/ifcfg-$ETH1_DEVICE" "$CFG_DIR/ifcfg-$ETH1_DEVICE"
restorecon "$CFG_DIR/ifcfg-$ETH0_DEVICE" "$CFG_DIR/ifcfg-$ETH1_DEVICE"
ifup "$ETH0_DEVICE"
ifup "$ETH1_DEVICE"
ip -br address
ip route
journalctl -u network --since '-10 minutes' --no-pager
復旧後は既存のアドレスと経路をaddress-before.txt、routes-before.txtと比較し、別クライアントで管理接続とサービスを確認します。Link Failure Countが増える、またはモードが想定と違う場合は、ケーブル、スイッチVLAN、ポート集約、重複プロファイル、BONDING_OPTSから調べてください。