Проксирование запросов в nginx с помощью proxy_pass

Thank you for reading this post, don't forget to subscribe!

модуль ngx_http_proxy_module

Имен­но он реа­ли­зу­ет весь функ­ци­о­нал, о кото­ром пой­дет речь. Допу­стим, у вас в локаль­ной или вир­ту­аль­ной сети есть какие-то сер­ви­сы, не име­ю­щие пря­мо­го досту­па из интер­не­та. А вы хоти­те тако­вой иметь. Мож­но про­бра­сы­вать нуж­ные пор­ты на шлю­зе, мож­но что-то еще при­ду­мы­вать. А мож­но сде­лать про­ще все­го — настро­ить еди­ную точ­ку вхо­да на все свои сер­ви­сы в виде nginx сер­ве­ра и с него прок­си­ро­вать раз­лич­ные запро­сы к нуж­ным серверам.

Где он может использоваться:

  1. Допу­стим, у вас есть боль­шой сер­вер с мно­же­ством кон­тей­не­ров, напри­мер доке­ра. На нем рабо­та­ет мно­же­ство раз­лич­ных сер­ви­сов. Вы уста­нав­ли­ва­е­те еще один кон­тей­нер с чистым nginx, на нем настра­и­ва­е­те прок­си­ро­ва­ние запро­сов на эти кон­тей­не­ры. Сами кон­тей­не­ры мапи­те толь­ко к локаль­но­му интер­фей­су сер­ве­ра. Таким обра­зом, они будут пол­но­стью закры­ты извне, и при этом вы може­те гиб­ко управ­лять доступом.
  2. Допу­стим, у вас есть сер­вер с гипер­ви­зо­ром proxmox или любым дру­гим. Вы настра­и­ва­е­те на одной из вир­ту­аль­ных машин шлюз, созда­е­те локаль­ную сеть толь­ко из вир­ту­аль­ных машин без досту­па в нее извне. Дела­е­те в этой локаль­ной сети для всех вир­ту­аль­ных машин шлюз по-умол­ча­нию в виде вашей вир­ту­аль­ной маши­ны со шлю­зом. На вир­ту­аль­ных сер­ве­рах в локаль­ной сети раз­ме­ща­е­те раз­лич­ные сер­ви­сы и не замо­ра­чи­ва­е­тесь с настрой­ка­ми фаер­во­ла на них. Вся их сеть все рав­но не доступ­на из интер­не­та. А доступ к сер­ви­сам прок­си­ру­е­те с помо­щью nginx, уста­нов­лен­ным на шлюз или на отдель­ной вир­ту­аль­ной машине с про­бро­шен­ны­ми на нее портами
  3. Допу­стим, у вас боль­шой про­ект, раз­би­тый на состав­ные части, кото­рые живут на раз­ных сер­ве­рах. К при­ме­ру, на отдель­ном сер­ве­ре живет форум, по пути /forum от основ­но­го доме­на. Вы про­сто бере­те и настра­и­ва­е­те прок­си­ро­ва­ние всех запро­сов по адре­су /forum на отдель­ный сер­вер. Точ­но так же мож­но без про­блем все кар­тин­ки пере­не­сти на дру­гой сер­вер и прок­си­ро­вать к ним запро­сы. То есть вы може­те создать любой location и пере­ад­ре­со­вы­вать запро­сы к нему на дру­гие сервера.

Настройка proxy_pass в nginx

Рас­смот­рим самый про­стой при­мер. Буду исполь­зо­вать свой тех­ни­че­ский домен test.ru в этом и после­ду­ю­щих при­ме­рах. Допу­стим, у нас есть сайт blog.test.ru. В DNS созда­на A запись, ука­зы­ва­ю­щая на ip адрес сер­ве­ра, где уста­нов­лен nginx — nginx_srv. Мы будем прок­си­ро­вать все запро­сы с это­го сер­ве­ра на дру­гой сер­вер в локаль­ной сети blog_srv, где реаль­но раз­ме­ща­ет­ся сайт. Кон­фиг для сек­ции server:

Захо­дим по адре­су http://blog.test.ru. Мы долж­ны попасть на blog_srv, где тоже дол­жен рабо­тать какой-то веб сер­вер. В моем слу­чае это будет тоже nginx. У вас долж­но открыть­ся содер­жи­мое, ана­ло­гич­ное тому, что вы уви­ди­те, набрав http://192.168.13.31 в локаль­ной сети. Если что-то не рабо­та­ет, то про­верь­те сна­ча­ла, что по адре­су дирек­ти­вы proxy_pass у вас все кор­рект­но работает.

Посмот­рим логи на обо­их сер­ве­ра. На nginx_srv вижу свой запрос:

94.142.141.246 - - [19/Jan/2018:15:15:40 +0300] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "77.37.224.139"

Как мы видим, запрос сна­ча­ла при­шел на nginx_srv, был пере­прав­лен на blog_srv, куда он при­шел уже с адре­сом отпра­ви­те­ля 94.142.141.246. Это адрес nginx_srv. Реаль­ный же ip адрес кли­ен­та мы видим толь­ко в самом кон­це лога. Это неудоб­но, так как дирек­ти­ва php REMOTE_ADDR не будет воз­вра­щать насто­я­щий ip адрес кли­ен­та. А он очень часто быва­ет нужен. Мы это даль­ше испра­вим, а пока созда­дим в корне сай­та на chat_srv тесто­вую стра­нич­ку для про­вер­ки ip адре­са кли­ен­та сле­ду­ю­ще­го содержания:

<?php

echo $_SERVER['REMOTE_ADDR']

?>

Назо­вем ее myip.php. Перей­дем по адре­су http://blog.test.ru/myip.php и про­ве­рим, как сер­вер опре­де­лит наш адрес.  Он пока­жет адрес nginx_srv. Исправ­ля­ем это и учим nginx пере­да­вать реаль­ный ip адрес кли­ен­та на сервер.

Передача реального ip (real ip) адреса клиента в nginx при proxy_pass

В преды­ду­щем при­ме­ре мы на самом деле пере­да­ем реаль­ный ip адрес кли­ен­та с помо­щью дирек­ти­вы proxy_set_header, кото­рая добав­ля­ет в заго­ло­вок X-Real-IP насто­я­щий ip адрес кли­ен­та. Теперь нам нуж­но на при­ни­ма­ю­щей сто­роне, то есть blog_srv сде­лать обрат­ную заме­ну — заме­нить инфор­ма­цию об адре­се отпра­ви­те­ля на ту, что ука­за­на в заго­лов­ке X-Real-IP. Добав­дя­ем в сек­цию server сле­ду­ю­щие параметры

Передача https через nginx с помощью proxy pass

Если у вас сайт рабо­та­ет по https, то доста­точ­но настро­ить ssl толь­ко на nginx_srv, если вы не бес­по­ко­и­тесь за пере­да­чу инфор­ма­ции от nginx_srv к blog_srv. Она может осу­ществ­лять­ся по неза­щи­щен­но­му про­то­ко­лу. Рас­смот­рю при­мер с бес­плат­ным сер­ти­фи­ка­том let’s encrypt. Это как раз один из кей­сов, когда я исполь­зую proxy_pass. Очень удоб­но настро­ить на одном сер­ве­ре авто­ма­ти­че­ское полу­че­ние всех необ­хо­ди­мых сер­ти­фи­ка­тов.  Сей­час будем счи­тать, что у вас сто­ит certbot и все гото­во для ново­го сер­ти­фи­ка­та, кото­рый потом будет авто­ма­ти­че­ски обновляться.

Для это­го нам надо на nginx_srv доба­вить еще один location — /.well-known/acme-challenge/.

Пол­ная сек­ция server наше­го тесто­во­го сай­та на момент полу­че­ния сер­ти­фи­ка­та будет выгля­деть вот так:

 

 

Пере­чи­ты­вай­те кон­фиг nginx и полу­чай­те сер­ти­фи­кат. После это­го кон­фиг меня­ет­ся на следующий:

Про­ве­ря­ем, что полу­чи­лось. Наш сайт рабо­та­ет по https, при том, что мы вооб­ще не тро­га­ли сер­вер, где этот сайт рас­по­ла­га­ет­ся. Кон­крет­но с web сай­том это может быть не так акту­аль­но, но если вы прок­си­ру­е­те запро­сы не на обыч­ный сайт, а на какой-то нестан­дарт­ный сер­вис, кото­рый труд­но пере­ве­сти на https, это может быть хоро­шим решением.

Проксирование определенной директории или файлов

Рас­смот­рим еще один при­мер. Допу­стим, у вас форум живет в дирек­то­рии http://blog.test.ru/forum/, вы хоти­те выне­сти форум на отдель­ный web сер­вер для уве­ли­че­ния быст­ро­дей­ствия. Для это­го к преды­ду­ще­му кон­фи­гу добавь­те еще один location.

Еще одно попу­ляр­ное реше­ние. Вы може­те отда­вать кар­тин­ки с одно­го сер­ве­ра, а все осталь­ное с дру­го­го. В моем при­ме­ре, кар­тин­ки будут жить на том же сер­ве­ре, где nginx, а осталь­ной сайт на дру­гом сер­ве­ре. Тогда у нас долж­на быть при­мер­но такая кон­фи­гу­ра­ция локаций.

Что­бы все это рабо­та­ло кор­рект­но, необ­хо­ди­мо, что­бы сам сайт умел пра­виль­но раз­ме­щать свои изоб­ра­же­ния. Вари­ан­тов это орга­ни­зо­вать мно­же­ство. Мож­но как на сер­ве­ре мон­ти­ро­вать сете­вые паки раз­лич­ны­ми спо­со­ба­ми, так и про­грам­ми­стам изме­нять код для управ­ле­ния раз­ме­ще­ни­ем изоб­ра­же­ний. В любом слу­чае, это ком­плекс­ный под­ход к рабо­те с сайтом.

Суще­ству­ет очень мно­го дирек­тив для управ­ле­ния прок­си-соеди­не­ни­я­ми. Все они опи­са­ны в соот­вет­ству­ю­щей доку­мен­та­ции nginx.

Осо­бое вни­ма­ние сле­ду­ет уде­лить дирек­ти­вам кэши­ро­ва­ния proxy_cache, если в этом есть потреб­ность. Мож­но суще­ствен­но уве­ли­чить отклик веб сай­та, если под­хо­дя­щим обра­зом настро­ить отда­чу кэша. Но это тон­кий момент и нуж­но настра­и­вать в каж­дом кон­крет­ном слу­чае отдель­но. Гото­вых рецеп­тов тут не бывает.