리눅스 본딩 설정: 모드별 차이·CentOS 7.9 active-backup 구성
EdwardMoon
리눅스 본딩은 두 개 이상의 물리 네트워크 인터페이스를 하나의 논리 인터페이스로 묶는 기능이다. 장애 시 통신 경로를 전환하거나 여러 연결의 부하를 분산할 수 있지만, 효과는 선택한 모드와 스위치 구성에 따라 달라진다. 이 글은 모드별 차이를 정리한 뒤 CentOS 7.9의 network-scripts 환경에서 active-backup 본딩을 구성하고 복구하는 방법을 다룬다.
적용 범위: 기존 network.service가 관리하는 IPv4 정적 주소 환경의 유지보수 예제이다. CentOS Linux 7은 2024년 6월 30일 지원이 종료되었으므로 신규 서버의 기준으로 삼지 않는다. CentOS 공식 지원 종료 안내
본딩으로 얻을 수 있는 것과 얻을 수 없는 것
active-backup은 한 NIC만 통신에 사용하고 장애 시 다른 NIC로 전환한다. 1Gbps NIC 두 개를 묶어도 정상 동작 중 통신 대역폭은 활성 NIC 하나의 속도로 제한된다. 반면 LACP 등 부하 분산 모드는 여러 흐름을 여러 NIC에 나눌 수 있다. 단일 연결 속도가 항상 NIC 개수만큼 증가하는 것은 아니다.
본딩은 Linux bonding 드라이버를 사용한다. teamd 기반의 네트워크 teaming은 같은 목적에 쓰일 수 있는 별도 구현이므로 두 설정 방식을 혼용하지 않는다. 링크가 끊어져도 스위치 전체, 상위 라우터 또는 같은 전원 계통이 동시에 고장 나면 통신이 끊길 수 있다.
본딩 모드와 스위치 구성
스위치에 연결하는 일반적인 서버 환경을 기준으로 비교한다. 직접 연결이나 특수 토폴로지는 별도 검증이 필요하다.
| 모드 | 동작 | 스위치 측 요건 | 선택 시 주의점 |
|---|---|---|---|
| 0: balance-rr | 송신 패킷을 NIC 순서대로 분산 | 일반적으로 정적 포트 집계 필요 | 패킷 순서가 바뀔 수 있음 |
| 1: active-backup | 활성 NIC 한 개, 장애 시 전환 | LACP·정적 집계 불필요 | 속도 합산 없음 |
| 2: balance-xor | 해시 정책에 따라 송신 경로 선택 | 일반적으로 정적 포트 집계 필요 | LACP 협상 모드가 아님 |
| 3: broadcast | 동일 패킷을 모든 NIC로 전송 | 일반적으로 포트 집계 필요 | 트래픽 복제이며 처리량 합산 아님 |
| 4: 802.3ad / LACP | 집계 그룹 안에서 흐름 분산 | 대응 포트의 LACP 구성 필요 | 같은 집계 그룹의 속도·duplex와 해시 정책 확인 |
| 5: balance-tlb | 송신 분산, 수신은 한 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를 동시에 관리하지 않게 한다.
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를 설정한다. 다음은 서로 다른 두 파일의 내용이다.
# /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 일치, 스위치 정책, 모든 라우팅 의존성을 확인하지 못하므로 변경 전 확인 절차도 수행해야 한다.
콘솔에서 적용
아래 명령은 한 단계씩 실행하고 실패하면 다음 단계로 진행하지 않는다. 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"
# 아래 세 파일을 설치한다. 하나라도 실패하면 활성화하지 말고 복구한다.
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 두 개가 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의 케이블 또는 대응 스위치 포트를 하나만 차단한다.
- 활성 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부터 점검한다.