Bonding のプライマリインターフェースを動的に切り替える (RHEL/CentOS)

morikawaBonding, CentOS, linux, Network, ネットワーク

インフラグループの morikawa です。

物理サーバ上に Linux を稼働させて運用していると、ネットワークの冗長化に Bonding を用いるというのはよくある方法かと思います。

Active-Backup の設定を用いている際、ソフトウェア的にバックアップ側のネットワークインターフェースをアクティブにする方法として ifenslave コマンドによる方法が有名ですが、今回はプライマリとなっているインターフェースを動的に変更する方法のご紹介です。

なお、以下では OS として RHEL/CentOS 7 を用いているため、他のディストリビューションの場合はファイルのパスなど異なるかもしれません。

プライマリインターフェースの設定を動的に変更する方法

方法自体は簡単で、以下のようにするだけ。これで指定したインターフェースがプライマリに変更されます。

Plain Text

例えば、 bond0 インターフェースが存在し、eth0, eth1 で構成され、eth0 がプライマリインターフェースである場合、動的に eth1 をプライマリインターフェースにする場合はこうなります。

Plain Text

なお、プライマリに設定したインターフェースが有効であれば即座にそちらがアクティブになります。

検証したこと

この方法、私がウェブで検索した限りではそのままの情報としては見当たらず (どこかにはあるんでしょうが、、、)、 /sys/class/net/bondX/bonding/ 以下に Bonding に関する設定や状態の情報が存在すること (これは Linuxのbondingで設定されているパラメータ確認 – id:rx7(@namikawa)の技術メモ – 技術日記 など、ウェブに情報ありました) 、そのディレクトリ内に primary というファイルが存在すること、そのファイルの中身が /proc/net/bonding/bondX 内の “Primary Slave“ の値と一致することから試してみたらできた、というものです。ホントにできるのかな? というのは以下のように試してみました。

なお、以下では分かりやすさのため、Bonding インターフェースとして bond0 が存在し、eth0, eth1 で構成されており、通常は eth0 がプライマリインターフェースである場合を前提としてコマンドなど記載しています。

プライマリインターフェースの変更とアクティブの切り替わりの確認

切り替わりの状態をリアルタイムで把握するため、Bonding 状態とそれぞれのインターフェースにおける通信状態を確認できるようにしておきます。

Bonding 状態の把握

Plain Text

各インターフェースにおける通信パケットの状態の把握

Plain Text

なお、eth0, eth1 の通信パケットのカウントの切り替えを確認するには、 bond0 で何かしらパケットを送出するなどしておく必要があります。 bond0 側で通信していると、特に eth0 側の TX の bytes, packets のカウントがアップすることが確認できるかと思います。

切り替え前はこのような状態になっているかと思われます。

ではここで上記のやり方でプライマリインターフェースを eth1 に切り替えてみます。

Plain Text

すると、Bonding の状態からプライマリインターフェースが eth1 に変更されることが確認できます。 (変更部分のみ記載しています)

Plain Text

アクティブなインターフェースも eth1 に変更されています。

Plain Text

RX, TX のパケットのカウントアップが、元々 eth0 で行われていたのが、 eth1 から行われるようになることも確認できるはずです。

Plain Text

ネットワーク線を抜くなどした場合の挙動

ifenslave によるアクティブインターフェース切り替えの場合、プライマリインターフェースが切り替わるわけではないので、以下の変更を加えるとアクティブインターフェースは元に戻ってしまいます。

  1. 元の状態は eth0 がアクティブ、eth1 がバックアップ
  2. ifenslave で eth1 をアクティブに
  3. eth0 に接続されるネットワーク線を抜く、ないしは対向のネットワーク機器のポートの通信を停止
  4. eth0 へネットワーク線を繋ぎ直す、ないしは対向のネットワーク機器のポートの通信を復帰
  5. eth0 がアクティブに戻る

一方で上記のプライマリインターフェースの動的変更であれば、以下のような挙動になります。

  1. 元の状態は eth0 がアクティブ、eth1 がバックアップ (上記と同様)
  2. 上記の方法で eth1 をプライマリインターフェースに変更
  3. 結果的にアクティブインターフェースも eth1 に変更される
  4. eth0 に接続されるネットワーク線を抜く、ないしは対向のネットワーク機器のポートの通信を停止
  5. eth0 へネットワーク線を繋ぎ直す、ないしは対向のネットワーク機器のポートの通信を復帰
  6. プライマリインターフェースが eth1 なので eth1 がアクティブなままの状態を維持

通信断の影響

環境にも依るでしょうが、通信的なダウンタイムはほぼ無く切り替わります。また、コネクション断にはなりません。

例えば SSH コネクションを確立しておき、上記の方法で切り替えると切り替え後も同じコネクションでリモート操作が可能なことが確認できます。

ICMP の応答にどの程度の影響が出るか、ネットワーク的に近い別のマシンから ping で、対象のネットワークインターフェースが通信に使用する IP アドレスに対して疎通確認を行ってみます。

Plain Text

切り替えます。

Plain Text

ping を終了します。ping 終了時には結果の統計が表示されます。私が検証した環境ではパケットロスは 0% でした。

Plain Text

なお、 ping を実行するマシン上で root 権限が必要ですが、一度 0.1ms 間隔で ping 疎通チェックした場合もパケットロスはありませんでした。 (大量のパケットが飛ぶので実験される場合はご注意ください)。

一方、eth0 の対向のスイッチ上のポートについて停止する (Bonding の Active-Backup の機構で eth0 のダウンを検知させて自動で eth1 に切り替えさせる) 実験を行ってみたところ、 0.2 秒間隔での疎通確認では 2 パケット程度のロスを確認しました。私が検証した環境では 0.4 秒前後パケットがやり取りできない時間帯が発生したことになります。

今回私が検証した環境では、primary ファイル上書きによる切り替え手法は通信影響がほぼなく (観測ができない程度で) ネットワークインターフェースの切り替えを行うことができました。

存在しないインターフェースを指定した場合はどうなる?

さて、設定変更で怖いのが typo などによるイージーミスからの通信影響ですね。特に今回紹介した方法は echo の引数をリダイレクトでファイルに直接書き込むため、eth0 を ethO とか eth1 を ethl に見間違い、入力間違いすると即当該インターフェース通信不可になって結構大変なことになるんじゃないか? と思って試してみました。

Bonding の状態をウォッチしておきます。

Plain Text

primary ファイルの状態をウォッチしておきます。

Plain Text

敢えて存在しないインターフェース名を書き込んでみます。

Plain Text

この場合、 Bonding のプライマリインターフェースが None に変更されます。

Plain Text

ファイルの内容も空になります。

Plain Text

プライマリインターフェースなしという事なので、アクティブなインターフェースの切り替わりも生じません。少なくとも、その時点で通信可能な状態であれば通信不能にはならないと考えられます。

他にもいくつかのパターンで試してみましたが、上記と同様にプライマリインターフェースなしの状態となりました。

存在するけど Bonding に組み込んでないインターフェース名を書き込むパターン

Plain Text

空文字を書き込むパターン

Plain Text

いろいろ試してみましたが、インターフェース名の入力ミスでのによる通信影響は生じにくいようです。

こちら、今回の検証環境の CentOS 7 で確認したものなので他のバージョンや他のディストリビューションで同様に動作するかは不明なので、本番の環境で試される場合は事前にご確認頂いた方が良いかと思います。

どんな場合に役立つの?

そもそも上記のように動的にプライマリインターフェースを切り替えたい場合ってどんな時か、ですが、 サーバの上位のネットワーク機器を切り替える場合 がその一つになるかと思います。

物理サーバをネットワーク冗長化しつつネットワーク機器につなぐパターンとして以下のようなことを良くやります。

この状態でネットワーク機器の入れ替えを行う場合のやり方の一つが以下のようなものです。

この場合に、能動的にネットワークの切り替えを行う上で上記の方法は役立つかと思います。

もちろんネットワーク機器の入れ替えが想定外や誤設定などの考慮不要で全く問題なく進むようであれば、あまりこのような事を考えずに Bonding の Active-Backup に任せてしまうという手もあります。ただ、ネットワーク機器の入れ替えを実施する状況って、やはりいろいろ起こりうるものですので、万が一を想定して手動で通信経路を切替えた方が想定外に対してスピーディに対応できるのかなと思います。

ネットワークインターフェース冗長化の他の実装について

今回、Linux におけるネットワークインターフェースの冗長化の実装の一つである Bonding について、そのプライマリインターフェースの動的な切り替え方法についてご紹介しました。

今回の記事を書くにあたっていろいろ調べていたところ RHEL/CentOS ではより多機能な Teaming を採用していることを知りました。

比較表からはほぼ Teaming の方が優れてますし、teamd というデーモンに対して teamdctl コマンドで制御ができるらしく、設定も JSON で記載する方式であるなどイマドキな感じです。

記事の執筆からリリースまでの間にたまたま物理サーバを構築する機会があったので Teaming を用いた NIC 冗長化も試してみましたので簡単にレビューです。

Teaming による NIC 冗長化の検証

結論から言うと当面 Teaming は使用したくないな、という結果でした (残念)。 Bonding の方が安定して利用できます。

困ったところは以下のとおりです。検証環境は RHEL 7.6 でした。

  • アクティブなインターフェースの切り替えが思ったとおりにいかない
    • 優先度を入れ替えても上記で Bonding で切り替えるときのように切り替わらない。時間がかかると言うかまともに切り替えが行われないようでした
  • JSON 形式の設定を読み込ませても動的に反映が行われない (これはそもそも切り替えの機構がうまく働いてないからかもしれません)
  • 現状の設定の表示は teamdctl team0 config dump のように行うのですが、設定したはずの優先度設定がまともに表示されませんでした
    • 本来可能なはずの設定出力 → 編集 → 設定を teamdctl で読み込み、という本来期待されるフローで制御できない
  • 現状の稼働状況は本来 teamdctl team0 state view のように確認ができるはずなのですが、優先度の肝である prio の値が表示されないので、プライオリティの高いインターフェースが確認できませんでした。上記の通り設定の表示しても内容が確認ができないので優先度制御をまともにやりたい側からすると非常にストレスでした。

1時間ほど格闘したのですが、結果すっかり制御不能という印象で、サービスに投入するのはかなり時期尚早というのが個人的な感想です。Teaming の方が Bonding に比して機能に勝るという話で期待してたんですが、それ以前の問題という感じでした。RHEL/CentOS 8 でもう少しまともに使える状態になってることを期待したいです。

まとめ

今回この記事をまとめていて、しばらくは物理サーバを RHEL/CentOS で運用する場合は Bonding によるネットワークインターフェース冗長化が最適なのだなということを再確認する良い機会になりました。

最近はクラウドをメインの基盤に据えて仕事をしているエンジニアも多く、物理サーバにおける NIC 冗長化に関するノウハウはもうニッチ? いまさら? とは思ったものの、ウェブを探した限りはなかなか見当たらなかったところなので、同じようなところで困っている方のお役に立てたら幸いです。それでは。

morikawaBonding, CentOS, linux, Network, ネットワーク

Posted by morikawa