WebSocketプロキシとしてのNGINX

NGINX | 2014年5月16日

The WebSocketプロトコルを使用すると、クライアントとサーバー間のリアルタイムな双方向通信に対応するWebアプリケーションを作成できます。HTML5の一部であるWebSocketにより、従来の方法よりも、このようなアプリケーションをはるかに容易に開発できます。Chrome、Firefox、Internet Explorer、Opera、Safariなど、最新ブラウザのほとんどがWebSocketに対応しており、WebSocketに対応するサーバーアプリケーションフレームワークも増えています。

エンタープライズの本番環境で、パフォーマンスと高可用性を確保するために複数のWebSocketサーバーが必要な場合は、WebSocketプロトコルを認識する負荷分散レイヤーが必要です。NGINXはバージョン 1.3以降WebSocketをサポートしており、WebSocketアプリケーションのリバースプロキシとして機能し、負荷分散を行うことができます。(NGINX PlusのすべてのリリースもWebSocketをサポートしています。)

WebSocket接続の負荷分散におけるNGINXのスケーラビリティについて、最新の「パフォーマンステスト」をご覧ください。

WebSocketプロトコルはHTTPプロトコルとは異なりますが、WebSocketハンドシェイクはHTTPと互換性があり、HTTP Upgrade機能を使用して接続をHTTPからWebSocketへアップグレードします。そのため、WebSocketアプリケーションを既存のインフラストラクチャに容易に組み込むことができます。たとえば、WebSocketアプリケーションでは標準のHTTPポート80と443を使用できるため、既存のファイアウォールルールを利用できます。

WebSocketアプリケーションは、クライアントとサーバー間の接続を長時間維持するため、リアルタイムアプリケーションの開発が容易になります。HTTPからWebSocketへ接続をアップグレードするHTTP Upgradeメカニズムでは、UpgradeヘッダーとConnectionヘッダーを使用します。リバースプロキシサーバーがWebSocketをサポートする際には、いくつかの課題があります。その1つは、WebSocketがホップバイホッププロトコルであることです。そのため、プロキシサーバーがクライアントからのUpgradeリクエストをインターセプトした場合、適切なヘッダーを含む独自のUpgradeリクエストをバックエンドサーバーに送信する必要があります。また、WebSocket接続は、HTTPで一般的に使用される短時間の接続とは異なり長時間維持されるため、リバースプロキシは、アイドル状態に見えることを理由に接続を閉じず、開いたままにしておく必要があります。

NGINXは、クライアントとバックエンドサーバー間にトンネルを確立できるようにすることで、WebSocketをサポートします。NGINXがクライアントからのUpgradeリクエストをバックエンドサーバーに送信するには、次の例のように、UpgradeヘッダーとConnectionヘッダーを明示的に設定する必要があります。

location /wsapp/ {    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
}

この設定が完了すると、NGINXはこれをWebSocket接続として処理します。

NGINX WebSocketの例

以下は、NGINXがWebSocketプロキシとして動作することを示す実例です。この例では、ws(Node.js上に構築されたWebSocket実装)を使用します。NGINXは、wsとNode.jsを使用したシンプルなWebSocketアプリケーションのリバースプロキシとして機能します。この手順はUbuntu 13.10とCentOS 6.5でテスト済みですが、その他のOSやバージョンでは調整が必要になる場合があります。この例では、WebSocketサーバーのIPアドレスは192.168.100.10、NGINXサーバーのIPアドレスは192.168.100.20です。

  1. Node.jsとnpmがまだインストールされていない場合は、次のコマンドを実行します。
    • DebianおよびUbuntuの場合:
$ sudo apt-get install nodejs npm
  • RHELおよびCentOSの場合:
$ sudo yum install nodejs npm
  1. Node.jsはUbuntuでは「nodejs」、CentOSでは「node」としてインストールされます。この例では「node」を使用するため、Ubuntuでは「nodejs」から「node」へのシンボリックリンクを作成する必要があります:
$ ln -s /usr/bin/nodejs /usr/local/bin/node
  1. To install wsをインストールするには、次のコマンドを実行します。
$ sudo npm install ws
  1. 注:「Error: failed to fetch from registry: ws」というエラーメッセージが表示された場合は、問題を解決するために次のコマンドを実行してください:
$ sudo npm config set registry http://registry.npmjs.org/
  1. 次に、sudo npm install wsコマンドをもう一度実行します。
  2. wsには、クライアントとして使用するプログラム/root/node_modules/ws/bin/wscatが付属していますが、サーバーとして動作するプログラムを作成する必要があります。次の内容でserver.jsというファイルを作成します。
console.log("Server started");
var Msg = '';
var WebSocketServer = require('ws').Server
    , wss = new WebSocketServer({port: 8010});
    wss.on('connection', function(ws) {
        ws.on('message', function(message) {
        console.log('Received from client: %s', message);
        ws.send('Server received from client: ' + message);
    });
 });
  1. サーバープログラムを実行するには、次のコマンドを実行します。
$ node server.js
  1. サーバーは最初に"Server started"というメッセージを出力した後、ポート8010で待ち受け、クライアントからの接続を待ちます。クライアントからリクエストを受信すると、その内容をエコーし、受信したメッセージを含む応答をクライアントに返します。これらのリクエストをNGINXでプロキシするため、次の設定を作成します。リクエストのmapブロックを追加し、Connectionヘッダーがcloseに正しく設定されるようにします。これは、リクエストのUpgradeヘッダーが''に設定されている場合に適用されます。
http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }
 
    upstream websocket {
        server 192.168.100.10:8010;
    }
 
    server {
        listen 8020;
        location / {
            proxy_pass http://websocket;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_set_header Host $host;
        }
    }
}
  1. NGINXはポート8020でリクエストを受け付け、バックエンドのWebSocketサーバーにプロキシします。proxy_set_headerディレクティブにより、NGINXはWebSocketプロトコルを適切に処理できます。
  2. サーバーをテストするため、クライアントとしてwscatを実行します:
$ /root/node_modules/ws/bin/wscat --connect ws://192.168.100.20:8020
  1. wscatは、NGINXプロキシを介してWebSocketサーバーに接続します。wscatからサーバーに送信するメッセージを入力すると、そのメッセージがサーバー側に表示され、続いてサーバーからのメッセージがクライアント側に表示されます。以下に通信例を示します。サーバー:クライアント:$node server.js
    Server started 
     

    wscat --connect ws://192.168.100.20:8020
    Connected (press CTRL+C to quit)
    > Hello
    Received from client: Hello < Server received from client: Hello この例から、クライアントとサーバーがプロキシとして機能するNGINXを介して通信でき、いずれかが切断するまでメッセージを双方向に送信し続けられることが分かります。NGINXでWebSocketを適切に処理するには、HTTP接続をWebSocket接続にアップグレードするUpgradeリクエストを処理できるよう、ヘッダーを正しく設定するだけです。

関連情報


共有
タグ: Tech, F5 NGINX, 2014

著者について

Rick Nelson
Rick Nelsonソリューションエンジニアリング担当リージョナルバイスプレジデント

Rick Nelsonのその他のブログ記事

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります