高性能Webサーバーの世界ではNGINXはその軽量で効率的なアーキテクチャにより大量のトラフィックを処理できることから幅広く利用されています。NGINX JavaScriptモジュール(njs)の一部として共有ディクショナリ機能が導入されたことでNGINXのパフォーマンス機能は次のレベルに到達しました。
この記事ではnjs共有ディクショナリ機能と利点について説明するとともに、SSL/TLS証明書のローテーション時に再起動なくNGINX Open Sourceをセットアップする方法を紹介します。
共有ディクショナリの基本と利点
新しいjs_shared_dict_zone
ディレクティブにより、NGINX Open Sourceユーザーは共有メモリーゾーンを有効にしてワーカープロセス間の効率的なデータ交換を実現できます。これらの共有メモリーゾーンはkey-valueディクショナリとして機能し、リアルタイムでアクセスおよび変更できる動的な構成設定を保存します。
共有ディクショナリの主な利点は次のとおりです。
- オーバーヘッドが最小で使いやすい – njsに直接組み込まれ、直感的なAPIとわかりやすい実装によりプロビジョニングと利用が簡単です。また、ワーカープロセス間のデータ管理と共有のプロセスを簡素化できます。
- 軽量で効率的 – NGINXとシームレスに統合され、イベント駆動型のノンブロッキングI/Oモデルを活用します。このアプローチによりメモリー使用量が削減され、同時実行性が向上し、NGINXで多くの同時接続を効率的に処理できます。
- スケーラビリティ – 複数のワーカープロセス間で水平にスケールできるNGINXの機能を活用することで複雑なプロセス間通信メカニズムを必要とせずにプロセス間でデータを共有および同期できます。Time To Live(TTL)設定により、非アクティブのエントリをゾーンから削除することで共有ディクショナリエントリのレコードを管理できます。evictパラメーターは最も古いkey-valueペアを削除し、新しいエントリのスペースを作ります。
共有ディクショナリによるSSLローテーション
共有ディクショナリの最も影響力のあるユースケースの1つはSSL/TLSローテーションです。js_shared_dict_zone
を使用するとSSL/TLS証明書や鍵の更新時にNGINXを再起動する必要がなくなります。さらに、NGINXで証明書を管理するためのRESTライクなAPIが提供されます。
以下に、js_set
ディレクティブとssl_certificate
ディレクティブを使用してHTTPSサーバーを設定するNGINX設定ファイルの例を示します。js_setssl_certificateJavaScriptハンドラはjs_set
を使用して、ファイルからSSL/TLS証明書または鍵を読み込みます。
この設定のスニペットは共有ディクショナリを使用して、証明書と鍵をキャッシュとして共有メモリに保存します。鍵が存在しない場合、証明書または鍵をディスクから読み込み、キャッシュに保存します。
キャッシュをクリアする位置を公開することもできます。ディスク上のファイルが更新されると(たとえば、証明書や鍵が更新されると)、共有ディクショナリはディスクからの読み取りを強制します。この調整によりNGINXプロセスを再起動することなく証明書または鍵をローテーションできます。
http {
...
js_shared_dict_zone zone=kv:1m;
server {
…
# Sets an njs function for the variable. Returns a value of cert/key
js_set $dynamic_ssl_cert main.js_cert;
js_set $dynamic_ssl_key main.js_key;
# use variable's data
ssl_certificate data:$dynamic_ssl_cert;
ssl_certificate_key data:$dynamic_ssl_key;
# a location to clear cache
location = /clear {
js_content main.clear_cache;
# allow 127.0.0.1;
# deny all;
}
...
}
以下は、js_shared_dict_zone
を使ったSSL/TLS証明書と鍵のローテーションのJavaScript実装です。
function js_cert(r) { if (r.variables['ssl_server_name']) { return read_cert_or_key(r, '.cert.pem'); } else { return ''; } } function js_key(r) { if (r.variables['ssl_server_name']) { return read_cert_or_key(r, '.key.pem'); } else { return ''; } } /** * Retrieves the key/cert value from Shared memory or fallback to disk */ function read_cert_or_key(r, fileExtension) { let data = ''; let path = ''; const zone = 'kv'; let certName = r.variables.ssl_server_name; let prefix = '/etc/nginx/certs/'; path = prefix + certName + fileExtension; r.log('Resolving ${path}'); const key = ['certs', path].join(':'); const cache = zone && ngx.shared && ngx.shared[zone];
if (cache) { data = cache.get(key) || ''; if (data) { r.log(`Read ${key} from cache`); return data; } } try { data = fs.readFileSync(path, 'utf8'); r.log('Read from cache'); } catch (e) { data = ''; r.log(`Error reading from file:${path}. Error=${e}`); } if (cache && data) { try { cache.set(key, data); r.log('Persisted in cache'); } catch (e) { const errMsg = `Error writing to shared dict zone: ${zone}. Error=${e}`; r.log(errMsg); } } return data }
/clear
リクエストを送信することでキャッシュが無効になり、NGINXは次のSSL/TLSハンドシェイクでディスクからSSL/TLS証明書または鍵を読み込みます。さらに、キャッシュも保持および更新しながら、リクエストからSSL/TLS証明書または鍵を取得するjs_content
を実装できます。
この例の完全なコードは、njs GitHubリポジトリにあります。
今すぐ始める
共有ディクショナリ機能は合理化とスケーラビリティに大きな利点をもたらすアプリケーションのプログラマビリティのための強力なツールです。js_shared_dict_zone
の機能を活用することで新たな成長機会を解放し、増加し続けるトラフィック需要に効率的に対処できます。
js_shared_dict_zone
はNGINX導入を強化できます。js_shared_dict_zone
を使用して、NGINX導入をアップグレードし、新しいユースケースを解放できます。この機能について詳しくはこちらのドキュメントをご覧ください。さらに、njsモジュールランタイムとACMEプロバイダーとの連携を可能にする、最近導入されたnjs-acme projectプロジェクトにおける共有ディクショナリ機能のすべての例を見ることができます。
NGINX Open Sourceのご利用に興味がある方、またはご質問がある方はNGINX CommunityのSlackにご参加ください。自己紹介の後、NGINXユーザーのコミュニティと交流することができます。