NGINX Plusでエンタープライズ環境のMQTT導入を最適化

NGINX | 2023年6月05日

When announcing the NGINX PlusのR29リリースの発表時に、新たにネイティブ対応したMQTTメッセージの解析について簡単にご紹介しました。本記事では、その内容を踏まえ、エンタープライズ環境でMQTTの導入を最適化するためのNGINX Plusの構成方法について解説します。

MQTTとは

MQTTは、Message Queuing Telemetry Transportの略称です。広く普及している軽量なパブリッシュ/サブスクライブ型メッセージングプロトコルであり、モノのインターネット(IoT)やマシンツーマシン(M2M)のデバイスおよびアプリケーションをインターネット経由で接続するのに適しています。MQTTは、低帯域幅または低消費電力の環境でも効率的に動作するよう設計されているため、多数のリモートクライアントを使用するアプリケーションに適しています。家電、自動車、運輸、製造、医療など、さまざまな業界で利用されています。

NGINX PlusによるMQTTメッセージ処理

NGINX Plus R29は、MQTT 3.1.1およびMQTT 5.0をサポートしています。クライアントとブローカー間のプロキシとして機能し、コアシステムからタスクをオフロードすることで、スケーラビリティを簡素化し、コンピューティングコストを削減します。具体的には、NGINX PlusがMQTTのCONNECTメッセージの一部を解析して書き換えることで、次のような機能を実現します。

  • MQTTブローカーの負荷分散
  • セッション維持(再接続するクライアントを同じブローカーに接続)
  • SSL/TLS終端
  • クライアント証明書認証

MQTTメッセージ処理ディレクティブは、NGINX設定ファイルのstreamコンテキストで定義する必要があり、ngx_stream_mqtt_preread_module
およびngx_stream_mqtt_filter_moduleによって提供されます。

The prereadモジュールは、NGINXの内部プロキシ処理の前にMQTTデータを処理します。これにより、解析されたメッセージデータに基づいて、負荷分散やアップストリームへのルーティングを決定できます。

The filterモジュールを使用すると、受信したCONNECTメッセージ内のclientid、username、passwordフィールドを書き換えることができます。これらのフィールドに変数や複合値を設定できるため、構成オプションが大幅に拡張され、NGINX Plusで機密性の高いデバイス情報をマスクしたり、TLS証明書の識別名などのデータを挿入したりできます。

MQTTのディレクティブと変数

MQTTの導入を最適化し、個別のニーズに対応できるようNGINX構成を調整するための、新しいディレクティブと組み込み変数が複数利用可能になりました。

Prereadモジュールのディレクティブと組み込み変数

  • mqtt_preread – MQTT解析を有効にし、クライアントデバイスから送信されたCONNECTメッセージからclientidフィールドとusernameフィールドを抽出します。これらの値は埋め込み変数を介して利用でき、ロードバランシングされたアップストリームサーバーへのセッションのハッシュ分散に役立ちます(以下の例を参照)。
  • $mqtt_preread_clientid – デバイスから送信されたMQTTクライアント識別子を表します。
  • $mqtt_preread_username – 認証のためにクライアントから送信されるユーザー名を表します。

フィルターモジュールのディレクティブ

  • mqtt – MQTTの書き換えを有効にするかどうかを指定します。
  • mqtt_buffers – 接続ごとに割り当て可能なMQTT処理バッファーの最大数と、各バッファーのサイズを上書きします。デフォルトでは、NGINXは接続ごとに最大100個、各1kのバッファーに制限します。通常、この設定がパフォーマンス上最適ですが、特殊な状況では調整が必要になる場合があります。たとえば、MQTTメッセージが長い場合は、より大きなバッファーサイズが必要です。特定の接続で短時間に大量のMQTTメッセージを処理するシステムでは、バッファー数を増やすことで効果が得られる場合があります。NGINXは内部メモリプールからバッファーを構築するため、ほとんどの場合、バッファーパラメーターの調整が基盤となるシステムのパフォーマンスに与える影響はわずかです。
  • mqtt_rewrite_buffer_size – MQTTメッセージの構築に使用するバッファのサイズを指定します。このディレクティブは非推奨となっており、NGINX Plus R30以降は廃止されています。
  • mqtt_set_connect – クライアントから送信されるCONNECTメッセージのパラメーターを書き換えます。サポートされるパラメーターは次のとおりです:clientid、username、password。

MQTTの例

NGINX PlusでMQTTメッセージを処理するメリットと、関連するベストプラクティスについて詳しく見ていきます。以下の例では、ポート1883と8883を使用します。ポート1883は暗号化されていないデフォルトのMQTTポートで、8883はSSL/TLSで暗号化されたデフォルトのポートです。

MQTTブローカーの負荷分散

MQTTデバイスは一時的に接続される性質があるため、クライアントIPが予期せず変わる可能性があります。その結果、デバイス接続を適切なアップストリームブローカーにルーティングすることが難しくなる場合があります。その後、デバイス接続が別のアップストリームブローカーに移動すると、ブローカー間でコストの高い同期処理が発生し、遅延とコストが増加する可能性があります。

MQTT CONNECTメッセージの「clientid」フィールドを解析することで、NGINXはアップストリームのサービスブローカーとのスティッキーセッションを確立できます。これは、「clientid」をハッシュキーとして使用し、バックエンドのブローカーサービスへの接続を維持することで実現します。

この例では、MQTTデバイスのデータをプロキシし、clientidをトークンとして使用して、3台のアップストリームブローカーへのスティッキーセッションを確立します。consistentパラメーターを使用することで、アップストリームサーバーに障害が発生した場合、そのサーバーが処理していたトラフィックを、残りのサーバーですでに確立されているセッションに影響を与えることなく、各サーバーに均等に分散できます。

stream {      mqtt_preread on; 
     
      upstream backend {
          zone tcp_mem 64k;
          hash $mqtt_preread_clientid consistent;
    
          server 10.0.0.7:1883; # upstream mqtt broker 1
          server 10.0.0.8:1883; # upstream mqtt broker 2
          server 10.0.0.9:1883; # upstream mqtt broker 3 
      }
    
      server {
          listen 1883;
          proxy_pass backend;
          proxy_connect_timeout 1s;
      }
  }

NGINX Plusでは、MQTT CONNECTメッセージのユーザー名フィールドも解析できます。詳細については、ngx_stream_mqtt_preread_moduleの仕様を参照してください。

SSL/TLS終端

デバイス通信の暗号化は、データの機密性を確保し、中間者攻撃から保護するうえで重要です。しかし、TLSハンドシェイク、暗号化、復号は、MQTTブローカーのリソースに負荷をかける可能性があります。この問題を解決するため、NGINX Plusはブローカー(またはブローカーのクラスター)からデータ暗号化処理をオフロードできます。これにより、セキュリティルールが簡素化され、ブローカーはデバイスメッセージの処理に専念できます。

この例では、NGINXを使用して、デバイスからバックエンドブローカーへのTLSで暗号化されたMQTTトラフィックをプロキシする方法を示します。ssl_session_cacheディレクティブでは5 MBのキャッシュを定義します。これは約20,000件のSSLセッションを保存するのに十分な容量です。NGINXは、proxy_connect_timeoutディレクティブの定義に従い、プロキシ先のブローカーへの接続を5秒間試行し、接続できなければタイムアウトします。

stream {      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_session_cache shared:SSL:5m;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 5s;
      }
  } 

クライアントIDの置換

セキュリティ上の理由から、クライアントを識別できる情報をMQTTブローカーのデータベースに保存しないよう選択できます。たとえば、デバイスがMQTT CONNECTメッセージの一部として、シリアル番号などの機密データを送信する場合があります。デバイスの識別子を、クライアントから受信した既知の静的な値に置き換えることで、NGINX Plusがプロキシするブローカーへの接続を試みるデバイスごとに、代替の一意キーを設定できます。

この例では、デバイスのクライアントSSL証明書から一意の識別子を抽出し、その識別子を使用してMQTTクライアントIDをマスクします。クライアント証明書認証(相互TLS)は、ssl_verify_clientディレクティブで制御します。パラメーターをonに設定すると、NGINXはクライアント証明書が信頼できる認証局(CA)によって署名されていることを確認します。信頼できるCA証明書のリストは、ssl_client_certificateディレクティブで定義します。

stream {      mqtt on; 
    
      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_client_certificate /etc/nginx/certs/client-ca.crt;
          ssl_session_cache shared:SSL:10m;
          ssl_verify_client on;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 1s;
          
          mqtt_set_connect clientid $ssl_client_serial;
      }
  }

認証資格情報としてのクライアント証明書

MQTTクライアントを認証する一般的な方法の1つは、クライアント証明書に保存されているデータをユーザー名として使用することです。NGINX Plusはクライアント証明書を解析してMQTTのユーザー名フィールドを書き換え、この処理をバックエンドブローカーからオフロードできます。次の例では、クライアント証明書のサブジェクト識別名(Subject Distinguished Name、Subject DN)を抽出し、MQTT CONNECTメッセージのユーザー名部分にコピーします。

stream {      mqtt on; 
     
      server {
          listen 8883 ssl;
          ssl_certificate /etc/nginx/certs/tls-cert.crt;
          ssl_certificate_key /etc/nginx/certs/tls-key.key;
          ssl_client_certificate /etc/nginx/certs/client-ca.crt;
          ssl_session_cache shared:SSL:10m;
          ssl_verify_client on;
          proxy_pass 10.0.0.8:1883;
          proxy_connect_timeout 1s;
          
          mqtt_set_connect username $ssl_client_s_dn;
      }
  } 

NGINX PlusによるMQTT CONNECTメッセージ書き換えの完全な仕様については、ngx_stream_mqtt_filter_moduleの仕様を参照してください。

今すぐ始める

NGINX PlusにおけるMQTTの今後の開発では、他のMQTTメッセージタイプの解析や、次のような機能を実現するためのCONNECTメッセージのより詳細な解析が含まれる可能性があります。

  • 追加の認証およびアクセス制御メカニズム
  • 大量の通信を行うクライアントをレート制限し、ブローカーを保護
  • メッセージのテレメトリと接続メトリクス

NGINX Plusを初めてご利用になる場合は、30日間の無料トライアルにお申し込みいただき、MQTTをお試しください。また、特に重要だと感じる機能について、ぜひご意見をお聞かせください。コメント欄でお待ちしています。


共有
タグ: F5 NGINX, Tech

関連するブログ記事

チュートリアル: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グループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります