ブログ | NGINX

NGINX 内部: パフォーマンスとスケールを考慮した設計方法

NGINX-F5 水平黒タイプ RGB の一部
オーウェン・ギャレット サムネイル
オーウェン・ギャレット
2015 年 6 月 10 日公開

NGINX は Web パフォーマンスでトップを走っていますが、それはすべてソフトウェアの設計方法によるものです。 多くの Web サーバーやapplicationサーバーが単純なスレッドベースまたはプロセスベースのアーキテクチャを使用しているのに対し、NGINX は、最新のハードウェア上で数十万の同時接続に拡張できる洗練されたイベント駆動型アーキテクチャを採用しています。

Inside NGINXインフォグラフィックでは、高レベルのプロセス アーキテクチャから掘り下げて、NGINX が単一のプロセス内で複数の接続を処理する方法を説明します。 このブログでは、その仕組みをさらに詳しく説明します。

背景設定 – NGINX プロセス モデル

この設計をよりよく理解するには、NGINX がどのように実行されるかを理解する必要があります。 NGINX には、マスター プロセス (構成の読み取りやポートへのバインドなどの特権操作を実行) と、多数のワーカー プロセスおよびヘルパー プロセスがあります。

# service nginx restart * nginx を再起動しています # ps -ef --forest | grep nginx root 32475 1 0 13:36 ?        00:00:00 nginx: マスター プロセス /usr/sbin/nginx -c /etc/nginx/nginx.conf nginx 32476 32475 0 13:36 ?        00:00:00 _ nginx: ワーカープロセス nginx 32477 32475 0 13:36 ?        00:00:00 _ nginx: ワーカープロセス nginx 32479 32475 0 13:36 ?        00:00:00 _ nginx: ワーカープロセス nginx 32480 32475 0 13:36 ?        00:00:00 _ nginx: ワーカープロセス nginx 32481 32475 0 13:36 ?        00:00:00 _ nginx: キャッシュ マネージャー プロセス nginx 32482 32475 0 13:36 ?        00:00:00 _ nginx: キャッシュローダープロセス

この 4 コア サーバーでは、NGINX マスター プロセスが 4 つのワーカー プロセスと、ディスク上のコンテンツ キャッシュを管理するいくつかのキャッシュ ヘルパー プロセスを作成します。

アーキテクチャはなぜ重要なのか?

あらゆる Unixapplicationの基本的な基盤は、スレッドまたはプロセスです。 (Linux OS の観点から見ると、スレッドとプロセスはほぼ同じですが、主な違いはメモリを共有する程度です。) スレッドまたはプロセスは、オペレーティング システムが CPU コア上で実行するようにスケジュールできる、自己完結型の命令セットです。 ほとんどの複雑なapplicationsでは、次の 2 つの理由により、複数のスレッドまたはプロセスを並行して実行します。

  • 同時により多くのコンピューティング コアを使用できます。
  • スレッドとプロセスを使用すると、並列操作 (たとえば、複数の接続を同時に処理する) が非常に簡単になります。

プロセスとスレッドはリソースを消費します。 それぞれがメモリやその他の OS リソースを使用するため、コア上でスワップオン/オフする必要があります (コンテキスト スイッチと呼ばれる操作)。 最近のサーバーのほとんどは、数百の小さなアクティブなスレッドまたはプロセスを同時に処理できますが、メモリが使い果たされたり、I/O 負荷が高くなって大量のコンテキスト スイッチが発生すると、パフォーマンスが大幅に低下します。

ネットワークapplicationsを設計する一般的な方法は、各接続にスレッドまたはプロセスを割り当てることです。 このアーキテクチャはシンプルで実装も簡単ですが、applicationが数千の同時接続を処理する必要がある場合は拡張できません。

NGINX はどのように機能しますか?

NGINX は、利用可能なハードウェア リソースに合わせて調整された予測可能なプロセス モデルを使用します。

  • マスタープロセスは、構成の読み取りやポートへのバインドなどの特権操作を実行し、少数の子プロセス (次の 3 つのタイプ) を作成します。
  • キャッシュ ローダープロセスは起動時に実行され、ディスクベースのキャッシュをメモリにロードしてから終了します。 スケジュールは保守的であるため、リソースの要求は低くなります。
  • キャッシュ マネージャープロセスは定期的に実行され、ディスク キャッシュからエントリを削除して、構成されたサイズ内に維持します。
  • ワーカープロセスがすべての作業を実行します。 これらは、ネットワーク接続を処理し、ディスクのコンテンツを読み書きし、上流サーバーと通信します。

ほとんどの場合に推奨される NGINX 構成 (CPU コアごとに 1 つのワーカー プロセスを実行する) では、ハードウェア リソースが最も効率的に使用されます。 これを設定するには、 worker_processesディレクティブでautoパラメータを設定します。

ワーカープロセスは自動です。

NGINX サーバーがアクティブな場合、ワーカー プロセスのみがビジー状態になります。 各ワーカー プロセスは複数の接続を非ブロッキング方式で処理し、コンテキスト スイッチの数を削減します。

各ワーカー プロセスはシングル スレッドであり、独立して実行され、新しい接続を取得して処理します。 プロセスは、共有キャッシュ データ、セッション永続データ、およびその他の共有リソース用の共有メモリを使用して通信できます。

NGINXワーカープロセスの内部

NGINX ワーカー プロセスは、Web クライアントからのリクエストを処理するための非ブロッキングのイベント駆動型エンジンです。

各 NGINX ワーカー プロセスは NGINX 構成で初期化され、マスター プロセスによって一連の listen ソケットが提供されます。

NGINX ワーカー プロセスは、リッスン ソケット ( accept_mutexおよびカーネル ソケット シャーディング) 上のイベントを待機することから始まります。 イベントは新しい着信接続によって開始されます。 これらの接続はステート マシンに割り当てられます。最も一般的に使用されるのは HTTP ステート マシンですが、NGINX はストリーム (生の TCP) トラフィックやさまざまなメール プロトコル (SMTP、IMAP、POP3) 用のステート マシンも実装しています。

受信したクライアント要求を処理するために、NGINX は HTTP ヘッダーを読み取り、設定されている場合は制限を適用し、必要に応じて内部リダイレクトとサブ要求を行い、バックエンド サービスに転送し、フィルターを適用し、そのアクションをログに記録します。

ステート マシンは基本的に、NGINX にリクエストの処理方法を指示する一連の命令です。 NGINX と同じ機能を実行するほとんどの Web サーバーは同様のステート マシンを使用しますが、違いは実装にあります。

ステートマシンのスケジュール

ステートマシンをチェスのルールのようなものだと考えてください。 各 HTTP トランザクションはチェス ゲームです。 チェス盤の片側にはウェブ サーバーがいます。これは、非常に迅速に決定を下すことができるグランドマスターです。 もう一方の側は、比較的低速なネットワーク経由でサイトまたはapplicationにアクセスしている Web ブラウザーであるリモート クライアントです。

ただし、ゲームのルールは非常に複雑になる場合があります。 たとえば、Web サーバーは他のパーティと通信したり (アップストリームapplicationにプロキシする)、認証サーバーと通信したりする必要がある場合があります。 Web サーバー内のサードパーティ モジュールは、ゲームのルールを拡張することもできます。

ブロッキングステートマシン

プロセスまたはスレッドは、オペレーティング システムが CPU コア上で実行するようにスケジュールできる、自己完結型の命令セットであると説明したことを思い出してください。 ほとんどの Web サーバーと Webapplicationsは、接続ごとのプロセスまたは接続ごとのスレッドモデルを使用してチェス ゲームをプレイします。 各プロセスまたはスレッドには、1 つのゲームを最後までプレイするための命令が含まれています。 プロセスがサーバーによって実行されている間、プロセスはほとんどの時間を「ブロック」された状態、つまりクライアントが次の動作を完了するのを待機して過ごします。

  1. Web サーバー プロセスは、リッスン ソケットで新しい接続 (クライアントによって開始された新しいゲーム) をリッスンします。
  2. 新しいゲームを取得すると、そのゲームをプレイし、各移動の後にブロックしてクライアントの応答を待ちます。
  3. ゲームが完了すると、Web サーバー プロセスは、クライアントが新しいゲームを開始するかどうかを確認するために待機する場合があります (これは、キープアライブ接続に対応します)。 接続が閉じられた場合 (クライアントが離れるか、タイムアウトが発生した場合)、Web サーバー プロセスは新しいゲームのリッスンに戻ります。

覚えておくべき重要な点は、すべてのアクティブな HTTP 接続 (すべてのチェス ゲーム) には専用のプロセスまたはスレッド (グランドマスター) が必要であるということです。 このアーキテクチャはシンプルで、サードパーティのモジュール(「新しいルール」)を使用して簡単に拡張できます。 しかし、大きな不均衡があります。ファイル記述子と少量のメモリによって表される、かなり軽量な HTTP 接続が、非常に重いオペレーティング システム オブジェクトである別のスレッドまたはプロセスにマップされます。 これはプログラミング上の利便性ですが、非常に無駄が多いです。

NGINXは真のグランドマスターです

チェスのグランドマスター 1 人が同時に数十人の対戦相手と対戦する同時エキシビションゲームについて聞いたことがあるでしょうか。

キリル・ゲオルギエフ
キリル・ゲオルギエフはブルガリアのソフィアで360人を同時に演奏した。 最終成績は284勝、70引き分け、6敗だった。

これが、NGINX ワーカー プロセスが「チェス」をプレイする方法です。 各ワーカー(通常は CPU コアごとに 1 つのワーカーがあることに注意してください)は、数百(実際には数十万)のゲームを同時にプレイできるグランドマスターです。

  1. ワーカーは、リッスン ソケットと接続ソケットでイベントを待機します。
  2. ソケット上でイベントが発生し、ワーカーがそれを処理します。
    • リッスン ソケットのイベントは、クライアントが新しいチェス ゲームを開始したことを意味します。 ワーカーは新しい接続ソケットを作成します。
    • 接続ソケット上のイベントは、クライアントが新しい動きを行ったことを意味します。 作業員は速やかに対応します。

ワーカーは、ネットワーク トラフィックをブロックして、「相手」(クライアント) が応答するのを待つことはありません。 ワーカーは、自分の動きを終えると、すぐに動きの処理を待っている他のゲームに進むか、新しいプレイヤーを迎え入れます。

なぜこれがブロッキング型マルチプロセス アーキテクチャよりも高速なのでしょうか?

NGINX は、ワーカー プロセスごとに数十万の接続をサポートできるほど優れたスケーラビリティを備えています。 新しい接続ごとに別のファイル記述子が作成され、ワーカー プロセスで少量の追加メモリが消費されます。 接続ごとに追加のオーバーヘッドはほとんどありません。 NGINX プロセスは CPU に固定されたままになります。 コンテキストの切り替えは比較的まれであり、実行する作業がないときに発生します。

プロセスごとに接続をブロックするアプローチでは、各接続に大量の追加リソースとオーバーヘッドが必要になり、コンテキスト スイッチ (あるプロセスから別のプロセスへの切り替え) が非常に頻繁に発生します。

より詳細な説明については、NGINX, Inc. のコーポレート開発担当副社長兼共同創設者である Andrew Alexeev による NGINX アーキテクチャに関するこちらの記事をご覧ください。

適切なシステムチューニングを行うことで、NGINX はワーカープロセスごとに数十万の同時 HTTP 接続を処理できるように拡張でき、トラフィックの急増 (新しいゲームの流入) を問題なく吸収できます。

設定の更新とNGINXのアップグレード

NGINX のプロセス アーキテクチャは、ワーカー プロセスの数が少ないため、構成の更新だけでなく、NGINX バイナリ自体の更新も非常に効率的に行えます。

NGINX はダウンタイム (リクエスト処理の中断) なしで構成を再読み込みします。

NGINX 設定の更新は非常にシンプルで軽量、かつ信頼性の高い操作です。 通常は、 nginx -s reloadコマンドを実行することを意味し、このコマンドはディスク上の構成をチェックし、マスター プロセスに SIGHUP 信号を送信します。

マスター プロセスは SIGHUP を受信すると、次の 2 つの処理を実行します。

  1. 構成を再読み込みし、新しいワーカー プロセス セットをフォークします。 これらの新しいワーカー プロセスは、すぐに接続の受け入れとトラフィックの処理を開始します (新しい構成設定を使用)。
  2. 古いワーカープロセスに正常に終了するように通知します。 ワーカー プロセスは新しい接続の受け入れを停止します。 現在の各 HTTP 要求が完了するとすぐに、ワーカー プロセスは接続を正常にシャットダウンします (つまり、キープアライブは残りません)。 すべての接続が閉じられると、ワーカー プロセスは終了します。

このリロード プロセスにより、CPU とメモリの使用量がわずかに増加する可能性がありますが、アクティブな接続によるリソース負荷と比較すると、通常は気付かない程度です。 1 秒間に複数回設定をリロードできます (多くの NGINX ユーザーがまさにそれを行っています)。 非常にまれに、接続の終了を待機している NGINX ワーカー プロセスが多数存在する場合に問題が発生しますが、その場合でもすぐに解決されます。

NGINX のバイナリ アップグレード プロセスは、高可用性の究極の目標を実現します。接続が切断されたり、ダウンタイムが発生したり、サービスが中断したりすることなく、ソフトウェアをオンザフライでアップグレードできます。

NGINX はダウンタイム (リクエスト処理の中断) なしでバイナリを再ロードします。

バイナリ アップグレード プロセスは、構成の正常なリロードのアプローチに似ています。 新しい NGINX マスター プロセスは元のマスター プロセスと並行して実行され、リスニング ソケットを共有します。 両方のプロセスがアクティブであり、それぞれのワーカー プロセスがトラフィックを処理します。 その後、古いマスターとそのワーカーに正常に終了するように信号を送ることができます。

プロセス全体については、 「NGINX の制御」で詳しく説明されています。

結論

Inside NGINX インフォグラフィックは、 NGINX の機能の概要を示していますが、このシンプルな説明の背後には、10 年以上にわたるイノベーションと最適化があり、NGINX は、最新の Webapplicationsに必要なセキュリティと信頼性を維持しながら、幅広いハードウェアで最高のパフォーマンスを実現しています。

NGINX の最適化について詳しく知りたい場合は、次の優れたリソースをご覧ください。


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"