nginxでのマップの可能性を明らかにします

mapは、設定をシンプルかつ簡単にすることができる強力なディレクティブです。

おそらく誰もがその能力のすべてを知っているわけではないという事実のために、これは最も過小評価されている指令です。

コンパクトなフォームでは、変数、GETパラメーター、ヘッダー、Cookie、およびバックストリームセット(上流)の処理に役立ちます。

その能力をhabrauserに解き放とうとします。



簡単にするために、例では、mapディレクティブはlocationからのディレクティブに隣接しています。

実際の構成では、場所マップはhttpブロックにあります。

短い説明マップ
これは、1つの変数(右)の値を別の変数(左)に応じて設定するディレクティブです。

次のようになります。

map $arg_one $var_two { "one" "two"; }
      
      





左側では、名前付き選択を含む正規表現を使用できます。

右側には、左側から行、変数、および正規表現を選択できます。

mapディレクティブについては、httpブロックで説明されています。

詳細な説明はドキュメントにあります



ifをマップに置き換える



Nginxの機能の場合
nginxで独自の方法で実装されている場合、かなり明白ではありません。

これについては、 特別ページで簡単に説明しています

私が理解している限りでは、nginxがカテゴリからのものである場合、世界はあなたの周りを回転し、その逆ではありません。

場所にあるifごとに、nginxは、if = trueとif = falseで、2つの設定ファイルを生成します。

さらに、一部のディレクティブは奇妙な動作をするか、同じ場所にあるとまったく動作しません。

したがって、予期したすべての誤った動作が常に発生する可能性がある場合に作業する場合。

動作を保証するために、ifをmapに置き換える方が適切です。



単純なものから複雑なものまで、置換の例を検討してください。



変数を1つの値に設定します


このために、ほとんどの場合、すでに次の構成が記述されています。

 if ($arg_one = "one") { set $var_two "two"; }
      
      





あなたはマップでこれを行うことができます:

 map $arg_one $var_two { "one" "two"; }
      
      





そして、適切な場所で、単に$var_two



使用します。

このような単純な場合でも、マップには次の利点があります。



変数をいくつかの値のいずれかに設定します


このために、彼らはすでに地図に頼っていると思いますが、次の場合、いくつかのオプションを見つけることができます:

 if ($arg_one = "one") { set $var_two "two"; } if ($arg_one = "three") { set $var_two "four"; }
      
      





あなたはマップでこれを行うことができます:

 map $arg_one $var_two { "one" "two"; "three" "four"; }
      
      





コンパクトさと読みやすさの向上はすでに明らかです。

それでも条件を編集する必要がある場合は、マップ内の行を修正する必要があります。



ネストされた場合(複数の条件に依存)


いくつかの条件を考慮する必要がある場合、ネストされたifに関する質問をnginxメーリングリストで見つけました。

ネストされたifを作成することはできません;複数のifから松葉杖を作成できます。

または、1つのマップを作成できます。

おそらくわからないかもしれませんが、元の部分(最初の変数)では、1つの変数ではなく、引用符で囲まれた複数の変数を含むテキスト全体を指定できます。

たとえば、ユーザーエージェント "HackYou"からユーザーをブロックする必要があります。アドレス "/ admin / some / url"で "POST"要求をクソします。



これは、原則として、次の場合に実行できます。

 if ($http_user_agent ~ "HackYou") { set $block "A"; } if ($method = "POST") { set $block "${block}B"; } if ($uri = "/admin/some/url") { set $block "${block}C"; } if ($block = "ABC") { return 403; }
      
      





あなたはマップでこれを行うことができます:

 map "$http_user_agent:$method:$uri" $block { "HackYou:POST:/admin/some/url" "1"; } if ($block) { return 403; }
      
      





コロンは読みやすくするためのものです。

この例では、マップの方向に「戻りのないポイント」が発生します。

条件の数が増えると(たとえば、複数のユーザーエージェント)、ifセットで条件を実装すると失敗するか、面倒な構成になります。



httpヘッダー



いくつかの条件に応じてヘッダーを追加する必要がある場合、これが問題になる可能性がある場合。



たとえば、次のような構造:

 if ($arg_a = "1") { add_header X-one "one"; } add_header X-two "two";
      
      





ヘッダーは1つだけです(arg_a = trueの場合はX-one、falseの場合はX-two)。

これはadd_headerの欠点であり、開発者は修正しません。

ifヘッダーが複数ある場合、同時に複数の異なるヘッダーを追加できない場合があります。



しかし、ここでマップが助けになります:

 map $arg_a $header_one { "1" "one"; } add_header X-one $header_one; add_header X-two "two";
      
      





複数のヘッダー-複数のマップ。

変数が空の場合、nginxはヘッダーを作成しません。

一般に、ifの場合、headers_moreモジュールは役立ちます。add_headerにはifがありません。

headers_moreモジュールはそれ自体興味深いものであり、その助けにより(バックエンドで)応答と要求の両方のヘッダーを柔軟に管理することができます。

mapディレクティブと組み合わせて、このモジュールは複数のCookieの生成を含む多くのウィッシュリストを実装できます。



上流の選択



アップストリームをバックエンドとして指定できるproxy_pass(fastcgi_passなど* _pass)のようなディレクティブでは、変数を使用できます。

つまり この定義は機能します:

 proxy_pass http://$php_backend;
      
      





これはドキュメントに記載されています

この場合、記述されたサーバーグループの中からサーバー名が検索され、見つからない場合はリゾルバーを使用して決定されます。


マップと組み合わせることで、これは想像力のフィールドを提供します。

これは大まかな例です-これが必要だとしましょう:

ユーザーID Cookieがあります。

その値の最初の数が0から4の場合、上流のold_php_backendにリクエストを送信します。

5から9の場合-new_php_backendで。

Cookieがない場合、空の場合、または最初の文字が数字でない場合は、default_php_backendで。



通常、if、rewrite、およびいくつかの場所で行われます。

 location /some/url/ { if ($cookie_userid ~ "~^[0-4]") { rewrite ^(.+)$ /old/$1 last; } if ($cookie_userid ~ "~^[5-9]") { rewrite ^(.+)$ /new/$1 last; } proxy_pass http://default_php_backend; } location /old/some/url/ { internal; rewrite ^/old/(.+)$ $1 break; proxy_pass http://old_php_backend; } location /new/some/url/ { internal; rewrite ^/new/(.+)$ $1 break; proxy_pass http://new_php_backend; }
      
      





マップを使用すると、すべてが簡素化されます。

 map $cookie_userid $php_backend { "~^[0-4]" "old_php_backend"; "~^[5-9]" "new_php_backend"; default "default_php_backend"; } location /some/url/ { proxy_pass http://$php_backend; }
      
      





本当に機能し、ロードされたサービスに適用され、問題はありません。

主なことは、デフォルトを忘れないことです。そのため、常にリクエストを送信する場所があります。

この手法は、geo / split_clientsに適用できます。

たとえば、split_clientsでは、リクエストの1%を選択し、テスト用に別のバックエンドに送信します。



マップの変数は別のマップで使用できます



そのようなコードは機能します:

 map $arg_a $var_a { "0" "1"; } map $var_a $var_b { "1" "2"; } map $var_b $var_c { "2" "3"; }
      
      





$var_c



$var_c



と、3つのmapディレクティブが順番にトリガーされます。

GETパラメーターa = 0の場合、 $var_c



には「3」が含まれます。

nginxは12個のマップのチェーンを噛んで、それを試したことはありません。

興味のある目的で、チェーンの最大長を調べることができます。

通常、2枚のマップで十分であり、お互いに依存しています。

nginxツールを使用して複雑なヘッダーとCookieを作成すると便利です(ヘッダーにテキストを追加し、一方のヘッダーをバックエンドに送信し、もう一方をクライアントに送信する必要がある場合)。

これは、ネストされたifを使用した開発例として便利です。

1つのマップでは$var_a



を計算し、別のマップでは$var_b



を計算し、3番目のマップは"$var_a:var_b



」に依存します。



マップは、geoおよびsplit_clientsの変数でも機能します。

geoおよびsplit_clientsでは、結果の変数に単純な文字列のみを割り当てることができ、変数または正規表現は使用できません。

IPによっては、単純な線よりも複雑なものが必要な場合は、geo + mapが役立ちます。

split_clients + mapリンクは、たとえば、1%のユーザーのヘッダーを柔軟に変更するのに役立ちます。



たとえば、次のように:

 split_clients "${remote_addr}XXX" $test_percent { 1% "1"; * "0"; } map "$test_percent:$http_user_agent" $test_mobile_users { "~*^1:.*iphone.*" "X-tester: iphone"; } more_set_input_headers $test_mobile_users;
      
      





ユーザーのIPがテストの1%のユーザーにあり、iphoneという単語がuser-agentにある場合、ヘッダー「X-tester:iphone」がバックエンドのリクエストとともに送信されます。

開発者はこの見出しに対応し、iPhone用のサイトのテストバージョンを提供する必要があります。



おわりに



ご覧のとおり、mapは少数のコマンドで複雑なロジックを作成するのに役立ちます。

ほとんどの場合、それを取り除くことができます。

そして、他のディレクティブと一緒に、いくつかの行でトリッキーな変換を作成します。

これらの機能が、一方で設定を削減し、他方でトリッキーなウィッシュリストを実装するのに役立つことを願っています。



All Articles