2022年6月25日土曜日

同一セグメントにおいてルーティングが必要な場合の対処方法 (ロンゲストマッチはAD値よりも優先される話)

ネットワーク機器やサーバなどにおいては、直接属しているネットワークを「Connected」という状態で認識する。Connectedのネットワークは同一セグメントとなることから、ルーティングはされることなく直接通信することが可能となる。

ただし、同一セグメントにおいても、ネクストホップを指定してルーティングを実施したい場合がある。

本記事では、同一セグメントにおいてもルーティングが必要なケースを例として挙げ、それを解決するための方法について記載する。

Connectedネットワークにおいてもルーティングが必要なケース

例として、負荷分散装置が存在するような以下ネットワーク構成で考えてみよう。負荷分散装置の仮想IP間で通信するサーバが2セット(サーバA-1・A-2とB-1・B-2)ある場合を想定する。すべてのサーバは同一セグメントではあるが、負荷分散装置を経由して通信させたい場合を考える。

この場合、各サーバはConnectedとなっているネットワークとなることから、負荷分散装置経由で通信を受け取っても、戻りの通信を負荷分散装置ではなくサーバに直接返そうとする。これにより、行きと戻りで非対称のルーティングとなるため通信に失敗する。

解消方法①:負荷分散装置でSource NATする

本方法はルーティングによる解決ではないく、負荷分散装置でSource NAT (送信元NAT) をすることで、同一セグメント間の通信として戻り通信を負荷分散装置に戻す方法となる。

Source NATすることで通信は必ず負荷分散装置に戻るため、非対称ルーティングとならない。ただし、通信元のIPアドレスがすべて負荷分散装置のIPアドレスとなってしまうため、通信元のIPアドレスを特定するためには、アプリケーション側で何らかの考慮をする必要がある。

解消方法②:Connectedのネットワークよりもプレフィックス長が長いスタティックルートを設定する

通信元のIPアドレスを必ず実IPで受け取りたい場合など、Source NAT使えない場合がある。そのような場合、あるルールを満たせばスタティックルートをConnectedより優先させることができる。

通常ルーティングには、優先順位として「AD値 (Administrative Distance)」が設定されている。AD値は低いほど優先順位が高く、Cisco機器を例として記載すると、Connectedのネットワークは0、スタティックルートは1、それ以外のダイナミックルーティングは1より大きい数で設定されている。

したがって、一見するとConnectedのネットワークは最優先で処理されてしまい、スタティックルートで制御することは困難であると考えてしまう。しかし、もう一つルーティングの優先順位を決めるルールとして、「ロンゲストマッチ (Longest match)」がある。

ロンゲストマッチとは、同じIPアドレス宛てのルーティングルールが複数ある場合、もっともサブネットのプレフィックス長が長いものを優先するというルールである。例えば、以下のような2つのスタティックルートがある場合は、よりプレフィックス長が長い/28のルートが採用される。

種類 ネットワーク ネクストホップ
Static route 192.168.10.0/24 192.168.20.254
Static route 192.168.10.0/28 192.168.20.201

重要な点は、ロンゲストマッチはAD値よりも優先されて処理されることである。そのため、Connectedのネットワークよりもプレフィックス長が長いスタティックルートを作れば、スタティックルートを優先させることができる

今回の例では、以下の通りサーバB-1およびサーバB-2に/32のスタティックルートを設定すればよい。これによって、サーバA-1およびサーバB-2あての通信は、必ず負荷分散装置のIPアドレスを経由させることができる。

種類 ネットワーク ネクストホップ
Connected 192.168.10.0/24 -
Static route [サーバA-1のIP]/32 [負荷分散装置のIP]
Static route [サーバA-2のIP]/32 [負荷分散装置のIP]

ただし、もしサーバA-1とサーバB-1間で負荷分散装置を経由せず、直接通信させる要件などもある場合はこの方法では対処できない。そのような場合は、解消方法①のSource NATによる対応を検討しよう。

以上。

2022年6月18日土曜日

NGINXの負荷分散対象サーバに対してポートチェックするスクリプト

先日、NGINXを使って負荷分散装置(ロードバランサ)を構築する手順を記載した。

NGINXは非常に簡単に負荷分散装置として構築できる反面、BIG-IPなどの負荷分散装置にあるような、現在の負荷分散装置対象サーバの状態を確認するコマンドなどは用意されていない。

そこで今回、NGINXの負荷分散対象サーバに対してポート開放状態のチェックを行うスクリプトを作成してみた。

環境

環境としては以下の通りAlmaLinuxを利用した。NGINXは前述したURLの通り、負荷分散装置として構築済みである前提とする。

  • AlmaLinux 8.5
  • NGINX 1.14.1

スクリプト導入手順

1. 前提パッケージの導入

ポートチェックはncコマンドで行うため、以下の通りパッケージインストールを行う。

# dnf install nc -y

2. スクリプトを配置

スクリプトの内容は以下の通りとなる。スクリプトはGitHubに公開している。

作りとしては、NGINXの設定ファイルを読み込み、負荷分散対象サーバに対してポート開放チェックをする。設定ファイルの記載フォーマットが以前の記事の通りとなっていない場合うまく動作しない可能性があるため、その場合はスクリプトの修正が必要となるかもしれない。

#!/bin/bash
################
# Scripts name : check_nginx_stream.sh
# Usage        : ./check_nginx_stream.sh
# Description  : NGINX負荷分散対象ポートチェックスクリプト
# Create       : 2022/04/30 tech-mmmm (https://tech-mmmm.blogspot.com/)
# Modify       :
################

rc=0      # Return code
array=()  # サーバ&ポート一覧保存用配列
config_file="/etc/nginx/stream.conf.d/servers.stream.conf" # 設定ファイルパス

# Configからサーバ&ポート情報抽出
while read line || [ -n "${line}" ]; do
  # upstream行の設定から負荷分散対象グループ名を取得
  if [ $(echo ${line} | grep -c -E "^upstream.*{") -ne 0 ]; then
    pool=$(echo ${line} | cut -d" " -f2)
  fi
  # server行の設定から、負荷分散対象サーバの情報を取得
  if [ $(echo ${line} | grep -c -E "^server.*;") -ne 0 ]; then
    member=$(echo ${line} | cut -d" " -f2 | tr -d ";")
    array+=("${pool}:${member}")
  fi
done < "${config_file}"

# ポートスキャン
for i in ${array[@]} ; do
  # ポート番号取得
  server=$(echo $i | cut -d":" -f1)
  port=$(cat /etc/nginx/stream.conf.d/servers.stream.conf | grep -E -B1 "proxy_pass.*${server}" | grep listen | awk '{print $3}' | tr -d ";")
  if [ "${port}" == "" ]; then
    port="tcp"
  fi

  # ポートスキャン
  if [ "${port}" == "tcp" ]; then
    # TCPの場合
    nc -nvz $(echo $i | cut -d":" -f2) $(echo $i | cut -d":" -f3) > /dev/null 2>&1
    if [ $(echo $?) -eq 0 ]; then
      result="up"
    else
      result="down"
      rc=$((${rc} + 1))
    fi
  else
    # UDPの場合
    nc -unvz $(echo $i | cut -d":" -f2) $(echo $i | cut -d":" -f3) > /dev/null 2>&1
    if [ $(echo $?) -eq 0 ]; then
      result="up"
    else
      result="down"
      rc=$((${rc} + 1))
    fi
  fi

  echo "$i:$port -> ${result}"
done

exit ${rc}

上記スクリプトを任意の場所に配置し、実行権限をつければ動作する。今回はgitをインストールしていないため、wgetで取得した。

# wget https://raw.githubusercontent.com/tech-mmmm/check_nginx_stream/main/check_nginx_stream.sh
--2022-04-30 08:41:22--  https://raw.githubusercontent.com/tech-mmmm/check_nginx_stream/main/check_nginx_stream.sh
192.168.33.23:8080 に接続しています... 接続しました。
Proxy による接続要求を送信しました、応答を待っています... 200 OK
長さ: 1853 (1.8K) [text/plain]
`check_nginx_stream.sh' に保存中

check_nginx_stream. 100%[===================>]   1.81K  --.-KB/s 時間 0s

2022-04-30 08:41:22 (62.6 MB/s) - `check_nginx_stream.sh' へ保存完了 [1853/1853]

# chmod +x check_nginx_stream.sh

3. 動作確認

実際に私の環境で実行した結果は以下の通り。

# ./check_nginx_stream.sh
proxy_server:192.168.33.27:8080:tcp -> up
proxy_server:192.168.33.28:8080:tcp -> up
dns_server:192.168.33.27:10053:udp -> up
dns_server:192.168.33.28:10053:udp -> up
mail_server:192.168.33.27:25:tcp -> up
mail_server:192.168.33.28:25:tcp -> up
ntp_server:192.168.33.27:123:udp -> up
ntp_server:192.168.33.28:123:udp -> up

以上 。

2022年6月4日土曜日

NGINXで負荷分散装置(ロードバランサ)を構築する

自宅では、プロキシやDNSなどを負荷分散装置(ロードバランサ)にてロードバランスして利用している。負荷分散装置としてはZEVENETを利用していたが、コミュニティ版は2020年5月から更新がないことや、冗長構成した際にうまく切り替わらないなどの問題が起きていたため、今回別製品に変更することにした。

調べていくと、Webサーバ用途のソフトウェアとして知られる「NGINX (エンジンエックス)」が負荷分散装置としても利用できることがわかった。ただし、OSS版のNGINXにおいては、以下機能が実装されていない点に注意しよう。以下機能は商用のNGINX Plusであれば利用可能となる。

  • セッションパーシステンスの機能なし
  • 負荷分散対象サーバに対するNGINXからの定期的なヘルチェック (アクティブヘルスチェック) の機能なし。通信発生時に都度ヘルスチェックを行うパッシブヘルスチェックのみ利用可能
  • NGINXの機能を利用した冗長化構成の機能なし

今回、「NGINX」を利用して負荷分散装置(ロードバランサ)を構築する手順を記載する。先に言っておくと、NGINXの設定は非常に簡単だった。

環境

環境としては以下の通りAlmaLinuxを利用したが、そのほかのRed Hat系のディストリビューションであれば、ほぼ同様の手順で構築できるはずだ。

  • AlmaLinux 8.5
  • NGINX 1.14.1

負荷分散装置の構成は以下の通りとする。クライアントからの通信と負荷分散対象サーバが同一のインタフェースとなる「ワンアーム」構成となる。なお、NGINXでは、通信は必ずSource NATされるため、ワンアーム構成としたとしても特別な設定は不要となる。

負荷分散対象は以下の通りとする。

用途 待ち受けポート 負荷分散対象サーバ ポート番号
Proxy 8080/tcp 192.168.33.27, 192.168.33.28 8080/tcp
DNS 53/udp 192.168.33.27, 192.168.33.28 10053/udp
MTA 25/tcp 192.168.33.27, 192.168.33.28 25/tcp
NTP 123/udp 192.168.33.27, 192.168.33.28 123/udp

NGINXにて負荷分散装置を構築する手順

1. dnfでNGINXをインストール

Red Hat系のディストリビューションの場合は、NGINXは標準のリポジトリに用意されている。NGINX本体に加え、負荷分散装置の設定に必要なstreamモジュールをインストールする。

# dnf install nginx nginx-mod-stream -y

2. NGINXの設定ファイルを作成

負荷分散装置の設定ファイル用のディレクトリを作成する。

# mkdir /etc/nginx/stream.conf.d

ディレクトリ作成後、以下2つのファイルを作成・追記を行う。

/etc/nginx/stream.conf.d/servers.stream.conf

以下内容で設定ファイルを新規作成する。

# Proxy
upstream proxy_server {
    least_conn;
    server 192.168.33.27:8080;
    server 192.168.33.28:8080;
}

server {
    listen       8080;
    proxy_pass   proxy_server;
}

# DNS
upstream dns_server {
    server 192.168.33.27:10053;
    server 192.168.33.28:10053;
}

server {
    listen       53 udp;
    proxy_pass   dns_server;
}

# MTA
upstream mail_server {
    least_conn;
    server 192.168.33.27:25;
    server 192.168.33.28:25;
}

server {
    listen       25;
    proxy_pass   mail_server;
}

# NTP
upstream ntp_server {
    server 192.168.33.27:123;
    server 192.168.33.28:123;
}

server {
    listen       123 udp;
    proxy_pass   ntp_server;
}

/etc/nginx/nginx.conf

以下を最下行に追記する。

stream {
    include /etc/nginx/stream.conf.d/*.conf;
}

3. サービス起動

これだけで必要な設定は完了となるため、NGINXのサービスを起動しよう。

# systemctl start nginx
# systemctl enable nginx

4. 動作確認

サービス起動後に待ち受けポートが開放されていることをssコマンドで確認する。以下の通り、負荷分散装置の設定を行ったポートがLISTEN (UDPに関してはUNCONN) になっていることがわかる。

# ss -nl | egrep ":(8080|53|25|123)" | sed -e 's/  */\t/g'
udp     UNCONN  0       0       0.0.0.0:123     0.0.0.0:*
udp     UNCONN  0       0       0.0.0.0:53      0.0.0.0:*
tcp     LISTEN  0       128     0.0.0.0:8080    0.0.0.0:*
tcp     LISTEN  0       128     0.0.0.0:25      0.0.0.0:*

負荷分散対象サーバに対しても問題なく通信できていることを確認しておこう。

# ss -n | egrep "192.168.33.27:(8080|53|25|123)" | sed -e 's/  */\t/g'
udp     ESTAB   0       0       192.168.33.41:57935     192.168.33.27:123
tcp     ESTAB   0       0       192.168.33.41:39962     192.168.33.27:8080
tcp     ESTAB   0       0       192.168.33.41:39592     192.168.33.27:8080

# ss -n | egrep "192.168.33.28:(8080|53|25|123)" | sed -e 's/  */\t/g'
udp     ESTAB   0       0       192.168.33.41:37953     192.168.33.28:123
tcp     ESTAB   0       0       192.168.33.41:35096     192.168.33.28:8080
tcp     ESTAB   0       0       192.168.33.41:33832     192.168.33.28:8080

以上を確認したうえで、実際にプロキシ経由でのインターネット閲覧やDNSの名前解決を確認し、問題なく動作することを確認した。

以上にて、「NGINX」を利用して負荷分散装置を構築する手順は完了となる。

参考

人気の投稿