nginx のスムーズな再起動を実装する方法

nginx のスムーズな再起動を実装する方法

1. 背景

サーバーの開発プロセスでは、新しいコードや構成をロードするためにサービスを再起動することが避けられません。サーバーの再起動中にサービスが中断されないことが保証されれば、再起動によるビジネスへの影響をゼロにすることができます。最近、nginx のスムーズな再起動について調べて、とても興味深いと感じました。興味のある学生が読めるように記録しました。

2. プロセスを再起動する

  • 再起動とは、古いサーバーを新しいサーバーに置き換えることを意味します。タスクを引き継ぐプロセスでは、必然的に古いサーバーと新しいサーバーが共存することになります。したがって、再起動のプロセスはおおよそ次のようになります。
    • 新しいサーバーを起動する
    • 古いサーバーと新しいサーバーが共存し、両方がリクエストを処理してサービスを提供します。
    • 古いサーバーはすべてのリクエストを処理した後、正常に終了します。
  • ここでの主な問題は、古いサーバーと新しいサーバーが共存できるようにする方法です。再起動前と再起動後のサーバー ポートが同じである場合、両方が同じポートをリッスンできるようにするにはどうすればよいでしょうか。

3. nginxの実装

nginx のスムーズな再起動を検証するために、まず nginx の起動時に新しいサーバー インスタンスを再度起動してみました。結果は図のとおりです。

当然のことながら、古いサーバーと新しいサーバーは同じポート 80 を使用するため、サーバー インスタンスを再度開いても機能しません。ソケットの再利用ポート オプションが有効になっていない場合、ポートを再利用すると、bind システム コールは失敗します。デフォルトでは、nginx はバインドを 5 回再試行し、失敗するとすぐに終了します。 Nginx は IPV4 アドレス 0.0.0.0 と IPV6 アドレス [::] をリッスンする必要があるため、図には 10 個の emerg ログが出力されています。

次に、次の 2 つのコマンドで構成されるスムーズ再起動コマンドを試します。

kill -USR2 `cat /var/run/nginx.pid`
キル -QUIT `cat /var/run/nginx.pid.oldbin`

最初のコマンドは、古いマスター プロセスに USR2 シグナルを送信します。プロセスの pid は /var/run/nginx.pid ファイルに保存されます。nginx.pid ファイル パスは nginx.conf によって設定されます。

2 番目のコマンドは、古いマスター プロセスに QUIT シグナルを送信します。プロセスの pid は /var/run/nginx.pid.oldbin ファイルに保存され、古いマスター プロセスは終了します。

それで疑問なのは、なぜ古いマスター プロセスの pid が 2 つの pid ファイルに存在するのかということです。実際、古いマスター プロセスに USR2 シグナルを送信した後、古いマスター プロセスは pid の名前を変更し、元の nginx.pid ファイルの名前が nginx.pid.oldbin に変更されました。このようにして、新しいマスターはファイル名 nginx.pid を使用できます。

まず最初のコマンドを実行すると、結果は次のようになります。

はい、古いマスタープロセスと新しいマスタープロセスおよびワーカープロセスが共存します。 2 番目のコマンドを実行してみましょう。結果は次のようになります。

ご覧のとおり、古いマスター プロセス 8527 とそのワーカー プロセスはすべて終了し、新しいマスター プロセス 12740 だけが残っています。

新しいインスタンスを手動で起動しても機能しないのに、シグナルで再起動すると機能するのはなぜなのか、不思議に思わざるを得ません。まず、nginx ログ ファイルを確認します。

前のエラー ログに加えて、ソケットが継承され、fd 値が 6 と 7 であることを示す通知がもう 1 つあります。 ログに従って、nginx ソース コードを調べ、nginx.c/ngx_exec_new_binary 関数を見つけました。

ngx_pid_t
ngx_exec_new_binary(ngx_cycle_t *サイクル、char *const *argv)
{
  ...
  ctx.path = argv[0];
  ctx.name = "新しいバイナリプロセス";
  ctx.argv = argv;
  2;
  env = ngx_set_environment(サイクル、&n);
...
  var = ngx_alloc(sizeof(NGINX_VAR)
          + サイクル->listening.nelts * (NGX_INT32_LEN + 1) + 2、
          サイクル->ログ);
...
  p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));
  ls = サイクル->listening.elts;
  (i = 0; i < cycle->listening.nelts; i++) {
    p = ngx_sprintf(p, "%ud;", ls[i].fd);
  }
  *p = '\0';
  env[n++] = var;
...
  env[n] = NULL;
...
  ctx.envp = (char *const *) env;
  ccf は ngx_core_conf_t に格納されます。
  ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR の場合 {
    ...
    NGX_INVALID_PID を返します。
  }
  pid = ngx_execute(サイクル、&ctx);
  (pid == NGX_INVALID_PID)の場合{
    ngx_rename_file(ccf->oldpid.data, ccf->pid.data) の場合
      == NGX_FILE_ERROR)
    {
      ...
    }
  }
...
  pid を返します。
}

機能フローは

  1. 古いマスター プロセスによって監視されているすべての fd を、新しいマスター プロセスの env 環境変数 NGINX_VAR にコピーします。
  2. 名前を変更する pid ファイルの名前を変更する
  3. ngx_execute 関数は子プロセスをフォークし、execve はコマンド ラインを実行して新しいサーバーを起動します。
  4. サーバーの起動プロセスでは、環境変数 NGINX_VAR が解析されます。ngx_connection.c/ngx_add_inherited_sockets の具体的なコードは次のとおりです。
静的 ngx_int_t
ngx_add_inherited_sockets(ngx_cycle_t *cycle)
{
...
  継承 = (u_char *) getenv(NGINX_VAR);
  if (継承 == NULL) {
    NGX_OK を返します。
  }
  if (ngx_array_init(&cycle->listening, cycle->pool, 10,
            サイズ(ngx_listening_t)
    != NGX_OK)
  {
    NGX_ERROR を返します。
  }
  (p = 継承、v = p; *p; p++) {
    (*p == ':' || *p == ';') の場合 {
      s = ngx_atoi(v, p - v);
      ...
      v = p + 1;
      ls = ngx_array_push(&cycle->listening);
      ls == NULLの場合{
        NGX_ERROR を返します。
      }
      ngx_memzero(ls、sizeof(ngx_listening_t));
      ls->fd = (ngx_socket_t) s;
    }
  }
  ...
  ngx_継承 = 1;
  ngx_set_inherited_sockets(cycle) を返します。
}

機能フローは次のとおりです。

環境変数NGINX_VARの値を解析し、配列に格納するfdを取得します。

これらのソケットの情報を保存するために、fd に対応するソケットが ngx_inherited に設定されます。

つまり、新しいサーバーは listen ポートをまったく再バインドしません。これらの fd の状態と値は、新しいマスター プロセスがフォークしたときに引き継がれます。新しいマスター プロセスは、継承されたファイル記述子を listen して処理できます。ここで重要な点は、listen ソケットのファイル記述子が ENV を介して渡されることです。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • nginx のスムーズな再起動とアップグレードを実装する方法
  • Nginxスムーズアップグレードの詳しい操作方法
  • Nginx 1.8.0 バージョンが新しいバージョン 1.9.7 にスムーズにアップグレードされました
  • 1 分で Nginx のバージョンをスムーズにアップグレードおよびロールバックする方法
  • nginxのスムーズなアップグレードのプロセスを詳しく説明
  • nginx のスムーズな再起動とスムーズなアップグレードのグラフィックチュートリアル

<<:  JSはGMTとUTCのタイムゾーンを完全に理解しています

>>:  MySQL クイックデータ比較テクニック

推薦する

nodejs で worker_threads を使用して新しいスレッドを作成する方法

導入前の記事で述べたように、NodeJS には 2 種類のスレッドがあります。1 つは、ユーザー リ...

Vue プロジェクトで addRoutes を使用する際の問題の解決策

目次序文1. 404 ページ1. 原因2. 解決策2.白い画面を更新する1. 原因2. 解決策3. ...

Linux で履歴レコードを表示し、タイムスタンプを追加するためのヒント

Linux で履歴レコードを表示し、タイムスタンプを追加するためのヒントbashに詳しい人なら、hi...

スクロールバーを非表示にしてコンテンツをスクロールする CSS サンプルコード

序文ページの HTML 構造にネストされたボックスが多数含まれている場合、ページに複数の垂直スクロー...

Sublime Text - ブラウザのショートカットキーを設定するための推奨方法

コード効果を異なるブラウザで表示することはよくあることなので、異なるショートカットキーを使用して対応...

フラットスタイルを使用してウェブサイトをデザインする方法

フラットなウェブサイト構造の本質はシンプルさです。コンテンツの重要なポイントを強調し、ページの装飾効...

Vue vee-validateプラグインの簡単な使い方

目次1. インストール2. インポート3. 検証ルールを定義します(エクスポート用に js ファイル...

JavaScript の構造化代入の一般的なシナリオと例 5 つ

目次序文1. データを抽出する2. エイリアス値3. 動的プロパティ4. オブジェクトの分解における...

JavaScriptプロトタイプチェーン図のまとめと実践

目次プロトタイプチェーンプロトタイプチェーンに基づいてシンプルなJQueryライブラリを実装すること...

vue3 統合 API における vue2 の $refs の代替方法についての簡単な説明

vue2 プロジェクト開発の経験があれば、$refs に精通しているでしょう。 vue3 の急激なア...

HTML テーブル マークアップ チュートリアル (48): CSS で変更されたテーブル

<br />では、CSS 構文を巧みに使用してテーブルを美しくする方法を見てみましょう。...

Linux (CentOS) システムで MySQL データベース ディレクトリの場所を変更する方法

CentOS システムで MySQL データベース ディレクトリの場所を変更する方法1. まず、My...

JavaScriptで配列を作成する方法の詳細な説明

目次JavaScript で配列を作成する配列の使用配列を分割文字列に変換する配列に要素を追加する配...

クリーンで美しいウェブデザインのための4つの原則

この記事では、 Webデザインに関連するこれら4 つの原則について説明します。これら4 つの原則を念...

MySQL 5.7 以降のバージョンのダウンロードとインストールのグラフィック チュートリアル

1. ダウンロード1. MySQL公式サイトのダウンロードアドレス: https://downloa...