[編集者注 – この記事は2016年に公開されたもので、その後改訂されたNGINXの機能を使用するよう更新されています。詳細については、「NGINX JavaScriptモジュールによる高度なロギング」および「NGINX Plusダッシュボード」をご覧ください。]
TCPロードバランシングはNGINX Plus R5で導入され、その後のリリースでも継続的に機能が追加されたほか、UDPロードバランシングもサポートされるようになりました。本記事では、TCPロードバランシングの主な要件と、NGINX Plusがそれらにどのように対応するかを解説します。
NGINX Plusの機能を確認するため、スケールアウトされたデータベースバックエンドを備えたアプリケーションの主要コンポーネントを再現する、シンプルなテスト環境を使用します。テスト環境の構築手順について詳しくは、付録を参照してください。

この環境では、NGINX Plusがデータベースサーバーのリバースプロキシとして機能し、MySQLのデフォルトポートである3306でリクエストを待ち受けます。これにより、クライアントにはシンプルなインターフェースを提供しながら、クライアントに一切影響を与えることなく、バックエンドのMySQLノードをスケールアウトしたり、オフラインにしたりできます。テスト環境では、フロントエンドアプリケーションに相当するクライアントとして、MySQLコマンドラインツールを使用します。
この記事で説明する機能の多くは、NGINX Open SourceとNGINX Plusの両方に適用されます。簡潔にするため、以降は一貫してNGINX Plusと表記し、NGINX Open Sourceでは利用できない機能については明記します。
以下のユースケースについて説明します。
TCPロードバランシング
アプリケーションのロードバランシングを設定する前に、そのアプリケーションがデータベースに接続する仕組みを理解しておくことが重要です。ほとんどのテストでは、MySQLコマンドラインツールの「mysql(1)」を使用してGaleraクラスターに接続し、クエリを実行してから接続を閉じます。ただし、多くのアプリケーションフレームワークでは、レイテンシーを最小限に抑え、データベースサーバーのリソースを効率的に使用するために、コネクションプールを使用します。
TCPロードバランシングはstream設定コンテキストで構成します。そのため、メインのstreamブロックをnginx.confファイルに追加して、MySQLロードバランシングの基本設定を作成します。
コード例を読み込み中…
これにより、TCPロードバランシング設定がメインの設定ファイルから分離されます。次に、stream.confをnginx.confと同じディレクトリに作成します。デフォルトでは、conf.dディレクトリはhttp設定コンテキスト用に予約されているため、このディレクトリにstream設定ファイルを追加しても機能しません。
コード例を読み込み中…
まず、upstreamグループにgalera_clusterという名前を付け、Galeraクラスター内の3つのMySQLノードを含めます。テスト環境では、各ノードにlocalhost上の固有のポート番号でアクセスできます。zoneディレクティブでは、負荷分散の状態を維持するために、すべてのNGINX Plusワーカープロセス間で共有するメモリ容量を定義します。server{}ブロックでは、NGINX Plusがクライアントを処理する方法を設定します。NGINX PlusはMySQLのデフォルトポートである3306でリッスンし、すべてのトラフィックをupstreamブロックで定義されたGaleraクラスターに転送します。
この基本設定が機能していることを確認するには、MySQLクライアントを使用して、接続先となったGaleraクラスター内のノードのホスト名を取得します。
$ echo "SHOW VARIABLES WHERE Variable_name = 'hostname'" | mysql --protocol=tcp --user=nginx --password=plus -N 2> /dev/nullhostname node1ロードバランシングが機能していることを確認するには、同じコマンドを再度実行します。
$ !!;!!;!!hostname node2
hostname node3
hostname node1これは、デフォルトのラウンドロビン方式の負荷分散アルゴリズムが正しく動作していることを示しています。ただし、前述のようにアプリケーションがコネクションプールを使用してデータベースにアクセスする場合、ラウンドロビン方式でクラスターへの接続を確立すると、各ノードの接続数に偏りが生じる可能性があります。また、接続はアイドル状態(アプリケーションからのクエリを待機中)の場合も、クエリを処理中の場合もあるため、接続数をそのままワークロードの大きさとみなすことはできません。長時間維持されるTCP接続には、より適切な負荷分散アルゴリズムとして「最小接続数方式」があり、least_connディレクティブで設定します。
コード例を読み込み中…
これで、クライアントがデータベースへの新しい接続を開くと、NGINX Plusは現在の接続数が最も少ないクラスターノードを選択します。
高可用性とヘルスチェック
データベースのワークロードをクラスター全体で分散する大きな利点は、高可用性も実現できることです。前述の構成では、新しいTCP接続を確立できない場合、NGINX Plusはそのサーバーを「down」としてマークし、TCPパケットの送信を停止します。
このようにダウンしたサーバーを処理するだけでなく、NGINX Plusでは、自動的かつプロアクティブなヘルスチェックを実行するように設定し、クライアントリクエストが送信される前に利用できないサーバーを検出することもできます(これはNGINX Plusのみの機能です)。さらに、アプリケーションレベルのヘルスチェックでサーバーの可用性をテストできます。各サーバーにリクエストを送信し、正常であることを示すレスポンスが返されるかどうかを確認します。設定を次のように拡張します。
コード例を読み込み中…
この例では、matchブロックで、MySQLプロトコルバージョン10のハンドシェイクを開始するために必要なリクエストデータとレスポンスデータを定義します。health_checkディレクティブは、server{}ブロック内でこのパターンを適用し、実際に新しい接続を受け入れられるサーバーにのみNGINX PlusがMySQL接続を転送するようにします。この例では、20秒ごとにヘルスチェックを実行し、1回失敗するとサーバーをTCPロードバランシングプールから除外し、ヘルスチェックが2回連続で成功すると、そのサーバーへのロードバランシングを再開します。
ログ記録と診断
NGINX Plusは柔軟なロギング機能を備えており、すべてのTCP/UDP処理をデバッグやオフライン分析用に記録できます。MySQLなどのTCPプロトコルでは、接続が閉じられたときにNGINX Plusがログエントリを書き込みます。log_formatディレクティブでは、ログに出力する値を定義します。Streamモジュールで使用可能な変数から任意のものを選択できます。ログ形式は、streamコンテキスト内のstream.confファイルの先頭で定義します。
コード例を読み込み中…
ログ記録を有効にするには、access_logディレクティブをserver{}ブロックに追加し、ログファイルのパスと、前のスニペットで定義したログ形式の名前を指定します。
コード例を読み込み中…
これにより、以下の例のようなログエントリが生成されます。
$ tail -3 /var/log/nginx/galera_access.log192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614
192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181
192.168.91.1 [23/Jul/2021:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460NGINX JavaScriptモジュールによる高度なロギング
NGINX JavaScriptは、「NGINXネイティブ」のプログラマブルな設定言語です。NGINXおよびNGINX Plus向けに独自に実装されたJavaScriptで、サーバーサイドのユースケースとリクエスト単位の処理に特化して設計されています。
[編集者 – 以下は、NGINX JavaScriptモジュールの数あるユースケースの1つにすぎません。すべてのユースケースについては、「NGINX JavaScriptモジュールのユースケース」を参照してください。
This post has been updated to use the refactored session (s) object for the Stream module introduced in NGINX JavaScript 0.2.4 and the js_import directive introduced in NGINX JavaScript 0.4.0.]
TCP/UDPロードバランシング用のStreamモジュールでは、NGINX JavaScriptを使用してリクエストパケットとレスポンスパケットの内容にアクセスできます。これにより、SQLクエリに対応するクライアントリクエストを調べ、SQLメソッドなどの有用な要素を抽出できます。たとえば、SELECTやUPDATEです。NGINX JavaScriptでは、このような値を通常のNGINX変数として利用できるようにすることも可能です。この例では、JavaScriptコードを/etc/nginx/sql_method.jsに配置します。
コード例を読み込み中…
The getSqlMethod()関数には、現在のパケットを表すJavaScriptオブジェクト(s)が渡されます。このオブジェクトのfromUpstreamやbufferなどのプロパティから、パケットとそのコンテキストについて必要な情報を取得できます。
まず、TCPパケットがクライアントから送信されたものであることを確認します。アップストリームのMySQLサーバーから送信されたパケットを調べる必要はないためです。ここでは、最初の2つのパケットにはハンドシェイクと認証の情報が含まれているため、クライアントからの3番目のパケットに着目します。3番目のクライアントパケットにはSQLクエリが含まれています。次に、この文字列の先頭を、methods配列で定義されているSQLメソッドのいずれかと比較します。一致するものが見つかった場合は、結果をグローバル変数$methodに格納し、エラーログにエントリを書き込みます。NGINX JavaScriptのログは、重大度error logのinfoとして記録されるため、デフォルトでは表示されません。
The setSqlMethod()関数は、同名のNGINX変数が評価されると呼び出されます。このとき、この変数には、$methodが設定されます。これは、getSqlMethod()関数の呼び出しによって取得されたNGINX JavaScriptのグローバル変数です。
このNGINX JavaScriptコードは、単一のクエリを実行するMySQLコマンドラインクライアント向けに設計されています。複雑なクエリや、長時間維持される接続上の複数のクエリを正確に取得することはできませんが、こうしたユースケースに合わせてコードを変更することは可能です。NGINX JavaScriptモジュールのインストールおよび有効化の手順については、「NGINX Plus Admin Guide」を参照してください。
ログにSQLメソッドを含めるには、$sql_method変数をlog_formatディレクティブに追加します。
コード例を読み込み中…
また、NGINX PlusがNGINX JavaScriptコードを実行するタイミングと方法を指定するために、設定を拡張する必要があります。
コード例を読み込み中…
まず、NGINX JavaScriptコードの場所をjs_importディレクティブで指定し、js_setディレクティブを使用して、$sql_method変数の評価が必要なときにsetSqlMethod()関数を呼び出すようNGINX Plusに指示します。次に、server{}ブロック内でjs_filterディレクティブを使用し、パケットが処理されるたびに呼び出す関数を指定します。必要に応じて、error_logディレクティブにinfoオプションを指定して、NGINX JavaScriptのログ記録を有効にすることもできます。
この追加設定を適用すると、アクセスログは次のようになります。
$ tail -3 /var/log/nginx/galera_access.log192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614 UPDATE
192.168.91.1 [23/Jul/2021:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181 SELECT
192.168.91.1 [23/Jul/2021:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460 UPDATENGINX Plusダッシュボード
[編集者 – このセクションは、当初ここで説明していた独立した拡張Statusモジュールを置き換え、非推奨とするNGINX Plus APIを参照するよう更新されました。]
MySQLのアクティビティを詳細にログに記録できるほか、NGINX Plusのライブアクティビティ監視ダッシュボードでは、リアルタイムのメトリクスとアップストリームMySQLサーバーの健全性を確認できます(NGINX Open Sourceで提供されるメトリクスは限定的で、利用できるのはStub Status API経由のみです)。
NGINX PlusダッシュボードはNGINX Plus R7で導入され、NGINX Plus APIのWebインターフェースを提供します。この機能を有効にするには、別のserver{}ブロックをhttpコンテキスト内の/etc/nginx/conf.d/dashboard.confファイルに追加します:
コード例を読み込み中…
また、MySQLサービスの監視データを収集できるように、server{} block in stream.conf内のstatus_zoneディレクティブを使用してブロックを更新する必要があります。
コード例を読み込み中…
この設定を適用すると、NGINX Plusダッシュボードをポート8080で利用できます。次のスクリーンショットには3台のMySQLサーバーが表示されており、それぞれについて、進行中の多数の接続の詳細と現在のヘルスステータスを確認できます。ポート33062でリッスンしているノードでは、以前に18.97秒間の短い停止が1回発生したことがわかります(DT列に表示)。

同時書き込みに関する考慮事項
Galera Clusterでは、各MySQLサーバーノードが読み取りと書き込みの両方を行うマスターデータベースとして機能します。多くのアプリケーションでは、書き込みに対する読み取りの比率が非常に高いため、マルチマスターデータベースクラスターによって得られる柔軟性を考慮すれば、複数のクライアントが同じテーブル行を同時に更新するリスクは十分に許容できます。同時書き込みが発生するリスクが高い場合は、2つの選択肢があります。
- 読み取り用と書き込み用に2つのアップストリームグループを個別に作成し、それぞれ異なるポートでリッスンするようにします。クラスター内の1つ以上のノードを書き込み専用とし、すべてのノードを読み取りグループに含めます。読み取り操作と書き込み操作に適切なポートを選択するよう、クライアントコードを更新する必要があります。このアプローチについては、F5のブログ記事「NGINX Plusによる高度なMySQLロードバランシング」で説明しています。これは、多数のMySQLサーバーノードを使用する大規模な環境に適しています。
- 単一のアップストリームグループを維持し、書き込みエラーを検出するようにクライアントコードを変更します。書き込みエラーが検出された場合、コードは同時実行が終了するまで指数バックオフを行ってから再試行します。このアプローチについては、F5のブログ記事「NGINX PlusとGalera ClusterによるMySQLの高可用性」で説明しています。この方法は、クラスタノードを書き込み専用にすると高可用性が損なわれるような小規模クラスタに適しています。
まとめ
この記事では、MySQLのようなTCP(またはUDP)アプリケーションの負荷分散に不可欠な要素をいくつか取り上げました。NGINX Plusは、あらゆる種類のトラフィックに対応し、パフォーマンス、信頼性、セキュリティ、スケーラビリティを備えたアプリケーション配信を支援する、充実した機能を持つTCP/UDPロードバランサーを提供します。
NGINX Plusをお試しいただくには、今すぐ30日間の無料トライアルを開始するか、ユースケースについてご相談ください。
付録:テスト環境の作成
テスト環境は、分離された再現可能な環境を確保するため、仮想マシンにインストールします。ただし、物理的な「ベアメタル」サーバーにもインストールできます。
NGINX Plusのインストール
「NGINX Plus Admin Guide」を参照してください。
Galera Cluster for MySQLのインストール
この例では、各ノードにDockerコンテナを使用し、単一のホストにGalera Clusterをインストールします。以下の手順は「Dockerを使用したGaleraの概要」を基にしており、Docker EngineとMySQLコマンドラインツールがすでにインストールされていることを前提としています。
- Dockerイメージによって各Galeraコンテナにコピーされる基本的なMySQL設定ファイル(my.cnf)を作成します。
コード例を読み込み中…
- Galeraの基本Dockerイメージをプルします。
$ sudo docker pull erkules/galera:basic- 最初のGaleraノード(node1)を作成し、デフォルトのMySQLポートを33061として公開します。
$ sudo docker run -p 33061:3306 --detach=true --name node1 -h node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://- 2つ目のGaleraノード(node2)を作成します。MySQLポートは33062として公開され、クラスター間通信のためにnode1へリンクされます。
$ sudo docker run -p 33062:3306 --detach=true --name node2 -h node2 --link node1:node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://node1- 3つ目で最後のGaleraノード(node3)をnode2と同じ方法で作成します。MySQLポートは33063として公開されます。
$ sudo docker run -p 33063:3306 --detach=true --name node3 -h node3 --link node1:node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://node1- ホストからクラスターにリモートアクセスするために使用できる、nginxという名前のユーザーアカウントを作成します。これを行うには、Dockerコンテナ内から
mysql(1)コマンドを実行します。
$ sudo docker exec -ti node1 mysql -e"GRANT ALL PRIVILEGES ON *.* TO 'nginx'@'172.17.0.1' IDENTIFIED BY 'plus'"- ホストからTCPプロトコルを使用してGaleraクラスターに接続できることを確認します。
$ mysql --protocol=tcp -P 33061 --user=nginx --password=plus -e "SHOW DATABASES"mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+- 最後に、別のクラスターノードに対して同じコマンドを実行し、nginxユーザーアカウントがレプリケートされ、クラスターが正常に機能していることを確認します。
$ mysql --protocol=tcp -P 33062 --user=nginx --password=plus -e "SHOW DATABASES"mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+著者について

関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります