OPS

IPVSセッションを同期、ロードバランサー(LB)の外から監視する方法

2022.05.19

本記事のポイント

オンプレミスのLinux環境でロードバランサー(負荷分散装/LB)機能を実現する際には、IPVSという標準ソフトウェアがよく利用されます。
IPVSを使用したロードバランサー構成において、セッションの同期処理はUDPマルチキャストでやり取りを行っており、ロードバランサーホスト外からでもセッション同期用のパケットが観測可能です。
そこで本記事では、このパケットを解析・ロードバランサーホスト外からIPVSセッションの取得に挑戦してみます。



はじめに

ITシステムにおいて、クラウド・オンプレミスにかかわらず、システムの可用性や拡張性を求めるためにロードバランサー機能を用いた冗長化構成を検討される方が多いかと思います。

オンプレミスの Linux環境にてロードバランサー機能を実現するにあたり、Linuxカーネル内に実装されているIPVSというソフトウェアと、ロードバランサーホスト自体を冗長化するkeepalivedというソフトウェアがよく使用されます。

このIPVSとkeepalivedを用いた冗長化構成について、ロードバランサーホスト同士でIPVSセッションを同期する設定をすることが可能なのですが、この同期処理はUDPマルチキャストを使用しており、同一セグメントに存在するホストなら同期処理用のパケットを受信することができます。

そこで、今回はこのパケットを監視・解析し、ロードバランサーホスト外でIPVSセッション情報を取得してみようと思います。

> AWS の「システム運用代行」詳細はこちら ビジネス価値と運用のあるべき姿

IPVSについて

IPVSとは

IPVS (IP Virtual Server)とは、Linux環境でロードバランサー機能を実現するためのソフトウェアです。Linuxカーネル内部に実装されており、基本的にL4 までの情報を見てパケットを冗長化対象であるリアルサーバーに転送します。設定の操作に関しては専用管理ツールipvsadmにて可能です。

ただし、ipvsadmだけでは高可用性構成(HA)などが実現できないため、keepalivedというソフトウェアからIPVSを管理し負荷分散構成を構築することが多いです。

IPVSセッションの同期処理について

keepalivedでロードバランサーを冗長化した際、デフォルトではIPVSセッションの情報はMaster側のロードバランサーホストのみ保持しており、Backup側は既存のセッションについては何も知りません。

そのため、フェイルオーバー発生時にBackup側のロードバランサーホストがMasterに昇格した際、昇格後新規に接続してきたコネクションは問題なくリアルサーバーに捌けるものの、昇格前から接続していた既存のセッションに関しては、一度コネクションを切断し新規セッションとして再度取り扱うこととなります。

単にクライアント側が接続リトライすればよいのでは?という意見もあると思いますが、コネクションの切断自体を重度のエラーとしてみなしているなど、そもそも接続が切れては困るという環境は大いに存在するため、できれば避けたほうが好ましいです。

そこで、keepalivedにより冗長化したロードバランサーのMaster側ホストで管理しているIPVSセッション情報を、Backup側のホストに同期するデーモン処理(IPVS connection sync daemon)というものが存在します。こちらの同期処理はUDPマルチキャストを使用しており、Backup側のロードバランサーホスト以外にも、同一セグメントに属するホストであれば同期処理用のパケットを受け取ることが可能です。

(参考サイト)
LVSとは | OSSのデージーネット
IPVS Connection Synchronization

IPVSセッションをLB外から監視するメリット

IPVSセッションの様子を確認する一般的な方法として、ロードバランサーホスト上でipvsadmコマンドを使用する方法が挙げられます。こちらのコマンドですが、デフォルトではrootユーザのみが実行可能です。つまり、ロードバランサーホスト上から監視を行う場合、少なからずrootユーザでの作業が発生します。

もちろんrootユーザで作業しないように「専用ユーザを用意して…、sudo権限を付与して…、ログインして確認する…」というような方法も可能ではありますが、「設定したり確認したりで逐一ログインするのが手間だ」、「そもそもシステム構成の重要なポイントであるロードバランサーホストにはユーザに限らずログインを控えたい」 というケースもあるかもしれません。

その点、ロードバランサーホスト外からIPVSセッションを確認することで、ロードバランサーホストにログインせずともロードバランサー機能の状況をリアルタイムで確認することができるといった省力化、またrootユーザでの作業やsudo権限を付与した専用ユーザの用意などが不要となり、間接的にロードバランサーホストに対するセキュリティの向上が見込めるでしょう。

IPVSセッションの外部監視を実際にやってみよう!

検証環境構成

検証構成図
ロードバランサーサーバー ・Almalinux 8.5
・keepalived 2.1.5-6
・ipvsadm 1.31-1
リアルサーバー ・Almalinux 8.5
・httpd 2.4.37
クライアント ・Almalinux 8.5(任意)
その他 ・Windows 10
・Wireshark

WEBサーバーの設定

SELinux

無効化します。

  # setenforce 0
  # sed -i -e "s/^SELINUX=enforcing$/SELINUX=disabled/g" /etc/selinux/config
  
firewalld設定

今回は負荷分散方式にDSR(Direct Server Return)方式を使用します。
DSR方式を採用する場合、VIPをリアルサーバーのループバックIPアドレスに設定する必要があります。

今回はfirewalldで専用のゾーンを用意し、リダイレクトルールを作成します。

  # systemctl start firewalld.service && systemctl enable firewalld.service
  # firewall-cmd --permanent --new-zone test-dsr-lb
  # firewall-cmd --permanent --zone=test-dsr-lb --set-target=ACCEPT
  # firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 192.168.10.100/32 -j REDIRECT
  # firewall-cmd --reload
  # firewall-cmd --set-default-zone=test-dsr-lb
  # firewall-cmd --reload
  
apache設定

apacheの設定はデフォルトのままで問題ありません。
セルフチェック用にindex.htmlを用意しておきます。

  # dnf -y install httpd
  # echo "Now connect to $HOSTNAME" > /var/www/html/index.html
  # systemctl start httpd.service && systemctl enable httpd.service
  

ロードバランサーサーバーの設定

SELinux

無効化します。

  # setenforce 0
  # sed -i -e "s/^SELINUX=enforcing$/SELINUX=disabled/g" /etc/selinux/config
  
firewalld

無効化します。

  # systemctl stop firewalld.service && systemctl disable firewalld.service
  
IPフォワード設定

無効化します。

  # echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.d/99-sysctl.conf
  # sysctl -p
  
ロードバランサー設定
  # dnf -y install ipvsadm keepalived
  # cp -ip /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org
  # vi /etc/keepalived/keepalived.conf
  # systemctl start keepalived.service && systemctl enable keepalived.service
  

keepalived.confはMaster/Backupともに以下の内容となります。

  # cat /etc/keepalived/keepalived.conf
  ! Configuration File for keepalived
  
  global_defs {
     notification_email {
        〓任意のメールアドレス〓
     }
     notification_email_from local-lb-server@local.com
     smtp_server localhost
     smtp_connect_timeout 30
     router_id TEST_LB_ROUTE1
     lvs_sync_daemon enp0s8 VI_1 id 51 # ★
  }
  
  vrrp_instance VI_1 {
      state BACKUP
      interface enp0s8
      virtual_router_id 51
      priority 100
      advert_int 1
      authentication {
          auth_type AH
          auth_pass password
      }
      virtual_ipaddress {
          192.168.10.100/24 dev enp0s8
      }
  }
  
  virtual_server 192.168.10.100 80 {
      delay_loop 5
      lb_algo lc
      lb_kind DR
      protocol TCP
      real_server 192.168.10.1 80 {
          weight 1
          HTTP_GET {
              url {
                  path /index.html
                  status_code 200
              }
              connect_timeout 5
              delay_before_retry 3
          }
      }
      real_server 192.168.10.2 80 {
          weight 1
          HTTP_GET {
              url {
                  path /index.html
                  status_code 200
              }
              connect_timeout 5
              delay_before_retry 3
          }
      }
  }
  

上記設定ファイルの内容のうち、★の箇所「lvs_sync_daemon」という項目を設定することでIPVSセッション同期処理を有効にすることができます。
項目にて使用する引数の説明などの情報は後述の参考サイトをご確認ください。

IPVSセッション情報をロードバランサーホスト上で確認

検証環境が用意できましたので、まずはロードバランサーホスト上でIPVSセッション情報がどのように見られるか確認してきます。
クライアントサーバーからVIP経由でWEBサーバーにcurl接続してみます。

  # curl -sv -p telnet://192.168.10.100:80
  

接続直後、ロードバランサーホスト上でIPVSセッションの状態を確認すると、この時点ではMaster側ホストでのみ確認できます。

IPVSセッションの状態確認

IPVSセッション同期処理を有効にしている場合、数秒待機し再度ロードバランサーホスト上でIPVSセッションの状態を確認すると、Backup側ホストでも同様のセッションの情報を確認できます。

Backup側ホストのセッション情報確認

IPVSセッション情報をロードバランサーホスト外から確認

それでは、マルチキャストで送信されるパケットからロードバランサーホスト外でIPVSセッション情報を取得できるか検証してみます。
再度クライアントサーバーからVIP経由でWEBサーバーにcurl接続してみます。

  # curl -sv -p telnet://192.168.10.100:80
  

Wiresharkをインストールしたホストにてパケットキャプチャを行いしばらくすると、パケットを受信していることを確認できます。このパケットがIPVSセッション同期用のパケットとなります。

パケットの受信確認画面

中身を見てみると様々な情報が表示されますが、ロードバランサーホスト上で確認できた情報と対応するものは以下のものとなります。こちらの値を使用すれば、ロードバランサーホストにログインせずに同等の情報を取得することができます。

ロードバランサーホスト上で確認できた情報 対応するパケット内の情報
pro Protocol(図内①)
expire Timeout(図内②)
パケット受信時刻からTimeout(単位:秒)の分だけセッション情報の有効期限が設けられます。
クライアントから同じセッションを用いて通信が行われた場合、有効期限が更新されます。
state State(図内③)
この項目に表示される16進数はip_vsカーネルモジュール側でステータスと紐づけられます。
例:1(0x0001)ならESTABLISH, 4(0x0004)ならFIN_WAIT、といった具合で割り当たっています。
source Client Address(図内④)とClient Port(図内⑤)
Virtual Virtual Address(図内⑥)とVirtual Port(図内⑦)
Destination Destination Address(図内⑧)とDestination Port(図内⑨)

またWiresharkのCLIツール「tshark」を用いることで、上記の項目のみをワンライナーかつリアルタイムで抽出することも可能です。

確認できた情報をCLIツール「tshark」で抽出

(参考サイト)
IDCFクラウドでkeepalivedを使ったLVS構築(1)
keepalived.conf(5) – Debian Manpages 
The Linux Kernel Archives

まとめ

いかがでしたでしょうか。
今回はセッション情報を取得、確認するまでにとどめましたが、キャプチャした内容からセッションテーブルをロードバランサーホスト外で作成する、特定セッション状態の検知をトリガーとして別アプリケーションのアクションを発生させるなどの連携も可能かと思います。こちらに関しても今後試していきたいと思います。

また今回の検証を通してロードバランサーの仕組みや運用時の挙動に対する理解を深めることができましたので、本検証が「システムとしてはロードバランサー機能を使用している・これから導入予定ではいるけど、実はちゃんと理解していないかも…」と密かに思っていらっしゃる方の一助になれば幸いです。

ご覧いただきありがとうございました。

> AWS の「システム運用代行」詳細はこちら ビジネス価値と運用のあるべき姿