【Linux】netplanで設定変更したらリモートホストに接続できなくなった話

こんにちは、キクです。

先日、新規構築中の物理ホストにおいてnetplanでネットワーク設定を変更したらリモートからの接続ができなくなってしまったという体験をしました。

netplanでの設定変更についてもほとんど経験がなかったので、いい機会と思って記事にしておこうと思います!

それでは、よろしくお願いします。

はじめに

まず、今回の作業対象は初期構築フェーズの筐体であり、ネットワーク接続および設定が完全な状態ではありませんでした。
データセンターでの設置と最低限の配線は行い、「とりあえずはリモートで作業可能な状態」を整えていました。

図にすると以下のような構成となります。

管理用ポート(portA)は接続可能な状態で、サービス用ポート(portB, portC)はケーブル未接続の状態でした。

「ケーブルはまだ接続してないけど、設定は先行して入れておこう」ということで、2つのサービス用ポートでBondingを組んでIPアドレス設定を入れる作業に取り組みました。

しかし、そんな中で通信できなくなってしまう状況が発生してしまった話になります。

本題

1. 作業前の状態

[/etc/netplan/50-cloud-init.yaml]
network:
  version: 2
  ethernets:
    portA:
      dhcp4: true

2. bondに関する設定を追加

root@host01:~# vi /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    portA:
      dhcp4: true
    portB: {}
    portC: {}
  bonds:
    bond0:
      interfaces:
        - portB
        - portC
      parameters:
	lacp-rate: slow
	mode: 802.3ad
	transmit-hash-policy: layer3+4
      addresses:
        - 192.168.2.100/24
      nameservers:
        addresses:
          - 192.168.2.53
      routes:
        - to: default
          via: 192.168.2.1

3. 設定を適用するために以下のコマンドを実行

root@host01:~# netplan apply

設定適用後、元々通信できていたportAでの通信(SSH含む)ができなくなってしまいました。

プチトラブルが発生した話

本題とは逸れますが、このタイミングで少し困ったエピソードをご紹介します。

上記通信不良によりリモートからのSSH接続はできなくなってしまいました。
しかし、幸いにも対象ホストへのBMC接続経路は残っていたので、そこからコンソールを開いて何とか作業はできました。

ただ、本ホストには正常時はLDAP認証を用いてログインしていたのですが、通信不良によりLDAP認証もできない状態に・・・。
加えて、OSがUbuntuということもありrootでのリモート接続ができず。

「あー、どうしよう」となったのですが、使用可能なローカルユーザがいてくれたのが救いでした。
LDAPユーザやrootユーザでは接続不可な状態でしたが、何とかローカルユーザでログインして作業を継続することができました。

通信できなくなった原因

デフォルトのルートテーブル「main」に同優先度(metric = 100)のデフォルトゲートウェイが2つ登録されてしまったことが原因でした。

root@host01:~# ip route show table main
default via 192.168.1.1 dev portA proto dhcp src 192.168.1.100 metric 100
default via 192.168.2.1 dev bond0 proto static linkdown
192.168.1.0/24 dev portA proto kernel scope link src 192.168.1.100 metric 100
192.168.2.0/24 dev bond0 proto kernel scope link src 192.168.2.100 linkdown

portAはDHCP経由でデフォルトゲートウェイを取得しており、bond0はnetplanの設定ファイルで「to: default」として明示的に手動でデフォルトゲートウェイを設定していたので自然な流れではあります。

備忘録①
項目名説明
proto kernel LinuxカーネルがネットワークインターフェイスにIPアドレスを割り当てる際に自動的に作成したルート
proto dhcpDHCPにより自動取得されたルート情報
proto static手動で設定したルート

後続の作業でproto kernelの内容と同等の設定を手動(static)で入れるが、基本的にカーネルにより自動設定されたルート(kernel)はそのままにしておいて問題なし

備忘録②

scope link :linkスコープは「このルートはリンク上(同一サブネット内)のホスト通信にのみ使用される」ということを意味する

少し余談になるが、対象ホストのportAと同一セグメントの他ホストとはportA経由での通信はできる状態だった
これが「デフォルトゲートウェイ設定が原因」と特定できた要因のひとつだった

一時しのぎ

2つのデフォルトゲートウェイが同じ優先度だから困っている状態なので、ひとまずbond0側に優先度の低いmetricを設定してみました。

root@host01:~# vi /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    portA:
      dhcp4: true
    portB: {}
    portC: {}
  bonds:
    bond0:
      interfaces:
        - portB
        - portC
      parameters:
	lacp-rate: slow
	mode: 802.3ad
	transmit-hash-policy: layer3+4
      addresses:
        - 192.168.2.100/24
      nameservers:
        addresses:
          - 192.168.2.53
      routes:
        - to: default
          via: 192.168.2.1
          metric: 200 ★
root@host01:~# ip route show table main
default via 192.168.1.1 dev portA proto dhcp src 192.168.1.100 metric 100
default via 192.168.2.1 dev bond0 proto static metric 200 linkdown ★
192.168.1.0/24 dev portA proto kernel scope link src 192.168.1.100 metric 100
192.168.2.0/24 dev bond0 proto kernel scope link src 192.168.2.100 linkdown

bond0のdefault行にmetric情報が表示されるようになりました。

また、デフォルトゲートウェイの優先度が定まったことでportAへの通信もできるようになりました。

ポイント

複数のデフォルトゲートウェイ

  • DHCPでportAに関するデフォルトゲートウェイ(default via)が自動的に設定されること
  • netplan設定でbond0には手動でのデフォルトゲートウェイが設定されること
  • この結果、デフォルトゲートウェイが複数登録され、どちらのゲートウェイを使うべきか曖昧になった

ルートテーブルの競合

  • 同じルートテーブル(今回の場合はmain)に2つのデフォルトルートが存在すると、通信に使うインターフェースが予測不能になる
  • その結果、意図したインターフェース経由での通信が通らなくなった

対応策

本項では、前項に記載したような一時的な対策ではなく、「根本的にどうすれば良さそうか」という対策を調べた範囲で書いてみます。

パターン1:DHCPでのデフォゲ取得を無効化する

1つ目の対策としては、DHCPによるデフォルトゲートウェイ情報の自動取得を無効化するという方法です。

root@host01:~# vi /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    portA:
      dhcp4: true
      dhcp4-overrides: ★
        use-routes: false ★
    portB: {}
    portC: {}
  bonds:
    bond0:
      interfaces:
        - portB
        - portC
      addresses:
        - 192.168.2.100/24
      nameservers:
        addresses:
          - 192.168.2.53
      parameters:
        lacp-rate: slow
        mode: 802.3ad
        transmit-hash-policy: layer3+4
      routes:
        - to: default
          via: 192.168.2.1

これによりデフォルトゲートウェイはbond0側のみとなります。

しかし、冒頭でも触れましたがbond0は現在リンクダウン状態であり、無効化を実施してしまうとportAを使った通信までできなくなってしまいます。
これは実質的に対象サーバへの通信経路がなくなってしまうことを意味するので、今回は実施しませんでした。

キク

厳密にはBMCポート経由でのコンソール接続は可能ではあるんですけどね・・・笑

パターン2:ポリシールーティングを利用する例

1. カスタムルートテーブルの定義

デフォルトのルートテーブル「main」とは別に、新たなルートテーブル「bond0_table(ID:100)」を作成します。

root@host01:~# vi /etc/iproute2/rt_tables
#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
#1      inr.ruhep

# custom
100      bond0_table ★
備忘録③

本ファイル(rt_tables)はルーティングテーブルに名前を付けるための設定ファイル
ファイル内の数値(テーブルID)は優先度を示すものではなく、識別子として使われる

優先順位としてはルーティングポリシー(ip rule)で指定されたテーブルの順序に従って評価される

root@host01:~# ip rule show
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default
項目名説明
数値小さいほど優先順位が高い
from allルールが適用される対象の送信元IPアドレス
lookup 〜適用するルートテーブル

2. netplan設定への適用

bond0に先ほど作成したカスタムルーティングテーブル(ID:100)を適用します。

root@host01:~# vi /etc/netplan/50-cloud-init.yaml
network:
  version: 2
  ethernets:
    portA:
      dhcp4: true
    portB: {}
    portC: {}
  bonds:
    bond0:
      interfaces:
        - portB
        - portC
      parameters:
	lacp-rate: slow
	mode: 802.3ad
	transmit-hash-policy: layer3+4
      addresses:
        - 192.168.2.100/24
      nameservers:
        addresses:
          - 192.168.2.53
      routes:
       - to: 192.168.2.0/24
          via: 0.0.0.0
          table: 100
        - to: default
          via: 192.168.2.1
          table: 100
      routing-policy:
	- from: 192.168.2.0/24
          table: 100

メモ

tableの指定において、上記のようなIDではなくテーブル名(bond0_table)を指定すると、適用時に以下のようにエラーとなってしまう

root@host01:~# netplan apply
/etc/netplan/50-cloud-init.yaml:30:18: Error in network definition: invalid unsigned int value 'bond0_table'
          table: bond0_table

3. 設定の反映

root@host01:~# netplan apply
root@host01:~# ip rule show
0:      from all lookup local
32765:  from 192.168.2.0/24 lookup bond0_table proto static
32766:  from all lookup main
32767:  from all lookup default
root@host01:~# ip route show table bond0_table
default via 192.168.2.1 dev bond0 proto static linkdown
192.168.2.0/24 dev bond0 proto static scope link linkdown
root@host01:~# ip route show table main
default via 192.168.1.1 dev portA proto dhcp src 192.168.1.100 metric 100
192.168.1.0/24 dev portA proto kernel scope link src 192.168.1.100 metric 100
192.168.2.0/24 dev bond0 proto kernel scope link src 192.168.2.100 linkdown

無事にルートテーブルが分割されて、適切に通信できる状態になりました。

キク

「せっかくbond0_tableとしてテーブルを分けたのにmainにbond0の情報が残ってるー」と思いましたが、これはカーネルがbond0にIPアドレスを割り当てた際に自動的に追加されるものであり、通常はそのままでも問題ないみたい。

備忘録④

routesセクションだけだと「ip rule show」の結果にルーティング情報が反映されなかった

また、routing-policyセクションにfrom / toの双方を記載して、routesセクションにはデフォルトゲートウェイのみ設定した場合、「ip rule show」の結果には反映されるものの、ルートテーブル「bond0_table」にはルーティング情報が反映されなかった(デフォゲのみの状態)

備忘録⑤

今回当たり前のように「50-cloud-init.yaml」を編集していましたが、「やめておいた方がいい」という内容の記事を発見
ファイル名にもある通り「cloud-init」の管理物であるため、意図せず上書きされてしまうことがあるのだとか

netplanは/etc/netplan/*.yamlをアルファベット順に全部参照するみたいなので、50以降で適当にファイル(99-manual.yamlなど)を作って管理するのがいいみたい

今後は意識してみよう

-Linux
-