0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Для запуска веб-приложений нужен веб-сервер, программа, которая принимает входящие HTTP-запросы и передает их на обработку приложению. Приложение делает свою работу и отдает результат веб-серверу, который формирует HTTP-ответ для клиента.</p>
1
<p>Для запуска веб-приложений нужен веб-сервер, программа, которая принимает входящие HTTP-запросы и передает их на обработку приложению. Приложение делает свою работу и отдает результат веб-серверу, который формирует HTTP-ответ для клиента.</p>
2
<p>Часто такой веб-сервер либо встроен в сам язык как модуль, либо написан на этом же языке, для запуска кода внутри себя. Обычно, задачу интеграции берут на себя фреймворки. В нашем приложении<a>devops-example-app</a>за это отвечает фреймворк<a>fastify</a>. Внутри себя он<a>использует встроенный</a>в Node.js веб-сервер:</p>
2
<p>Часто такой веб-сервер либо встроен в сам язык как модуль, либо написан на этом же языке, для запуска кода внутри себя. Обычно, задачу интеграции берут на себя фреймворки. В нашем приложении<a>devops-example-app</a>за это отвечает фреймворк<a>fastify</a>. Внутри себя он<a>использует встроенный</a>в Node.js веб-сервер:</p>
3
<p>Несмотря на простоту такой схемы, ее в большинстве проектов недостаточно. Помимо встроенного веб-сервера, нужен внешний, выполняющий роль реверс-прокси (reverse-proxy), например<a>Nginx</a>или<a>Caddy</a>.</p>
3
<p>Несмотря на простоту такой схемы, ее в большинстве проектов недостаточно. Помимо встроенного веб-сервера, нужен внешний, выполняющий роль реверс-прокси (reverse-proxy), например<a>Nginx</a>или<a>Caddy</a>.</p>
4
<p>Прокси-сервер ставится между клиентами и внутренней инфраструктурой. Он принимает запросы от клиентов и переадресует их на внутренний веб-сервер или веб-сервера, если их много. Именно поэтому он называется реверс (обратный), снаружи внутрь. Зачем он нужен?</p>
4
<p>Прокси-сервер ставится между клиентами и внутренней инфраструктурой. Он принимает запросы от клиентов и переадресует их на внутренний веб-сервер или веб-сервера, если их много. Именно поэтому он называется реверс (обратный), снаружи внутрь. Зачем он нужен?</p>
5
<ol><li>Проекты редко состоят только из кода, который мы запускаем на встроенном веб-сервере. Помимо кода, почти всегда есть файлы, css, js, картинки и другие файлы. Для их "раздачи" тоже нужен веб-сервер, а еще нужно уметь их сжимать и кешировать</li>
5
<ol><li>Проекты редко состоят только из кода, который мы запускаем на встроенном веб-сервере. Помимо кода, почти всегда есть файлы, css, js, картинки и другие файлы. Для их "раздачи" тоже нужен веб-сервер, а еще нужно уметь их сжимать и кешировать</li>
6
<li>Далеко не все внутренние веб-сервера умеют эффективно обрабатывать "медленных клиентов". Это клиенты с медленным соединением, которые очень долго отправляют и принимают данные. Они занимают ресурсы веб-сервера и приложения. Если придет достаточное количество медленных клиентов, то веб-сервер и приложение быстро перестанут отвечать. Пользователи начнут видеть ошибки</li>
6
<li>Далеко не все внутренние веб-сервера умеют эффективно обрабатывать "медленных клиентов". Это клиенты с медленным соединением, которые очень долго отправляют и принимают данные. Они занимают ресурсы веб-сервера и приложения. Если придет достаточное количество медленных клиентов, то веб-сервер и приложение быстро перестанут отвечать. Пользователи начнут видеть ошибки</li>
7
<li>Встроенные веб-сервера достаточно ограниченны в своих возможностях, например в том как они обрабатывают ошибки, как поддерживают HTTPS и так далее</li>
7
<li>Встроенные веб-сервера достаточно ограниченны в своих возможностях, например в том как они обрабатывают ошибки, как поддерживают HTTPS и так далее</li>
8
</ol><p>Эти причины настолько серьезны, что без реверс-прокси в продакшен не ходят. Только какой выбрать? Nginx самый популярный сервер в мире, но Caddy, гораздо проще в настройке и отладке. У него встроенная поддержка Let's Encrypt, Caddy автоматически обновляет сертификаты для работы HTTPS. Поэтому в этом курсе мы воспользуемся Caddy. Изучив его, вы без труда сможете перейти на Nginx, так как концептуально все подобные веб-сервера работают одинаково.</p>
8
</ol><p>Эти причины настолько серьезны, что без реверс-прокси в продакшен не ходят. Только какой выбрать? Nginx самый популярный сервер в мире, но Caddy, гораздо проще в настройке и отладке. У него встроенная поддержка Let's Encrypt, Caddy автоматически обновляет сертификаты для работы HTTPS. Поэтому в этом курсе мы воспользуемся Caddy. Изучив его, вы без труда сможете перейти на Nginx, так как концептуально все подобные веб-сервера работают одинаково.</p>
9
<h2>Caddy</h2>
9
<h2>Caddy</h2>
10
<p>Для начала Caddy нужно подключить к локальной разработке. Так мы сможем тестировать его конфигурацию еще до выкладки в продакшен.</p>
10
<p>Для начала Caddy нужно подключить к локальной разработке. Так мы сможем тестировать его конфигурацию еще до выкладки в продакшен.</p>
11
<h3>Локальное окружение</h3>
11
<h3>Локальное окружение</h3>
12
<p>Когда наш проект состоял из одного приложения, его было достаточно разрабатывать напрямую без Docker Compose. Как только добавляется сервис, в нашем случае внешний веб-сервер Caddy, то без Docker Compose становится сложно. Появляется задача управлять набором сервисов как единым целым, стартовать их, останавливать и перезапускать.</p>
12
<p>Когда наш проект состоял из одного приложения, его было достаточно разрабатывать напрямую без Docker Compose. Как только добавляется сервис, в нашем случае внешний веб-сервер Caddy, то без Docker Compose становится сложно. Появляется задача управлять набором сервисов как единым целым, стартовать их, останавливать и перезапускать.</p>
13
<p>Добавим Docker Compose с такой конфигурацией:</p>
13
<p>Добавим Docker Compose с такой конфигурацией:</p>
14
<p>Почему именно так? Мы придерживаемся структуры, в которой основное приложение располагается в корне проекта, а дополнительные сервисы лежат в директории<em>services</em>. У каждого сервиса своя директория с Dockerfile внутри.</p>
14
<p>Почему именно так? Мы придерживаемся структуры, в которой основное приложение располагается в корне проекта, а дополнительные сервисы лежат в директории<em>services</em>. У каждого сервиса своя директория с Dockerfile внутри.</p>
15
<p>Там же хранятся дополнительные файлы конкретного сервиса. Например у Caddy это файл Caddyfile, в котором содержится конфигурация:</p>
15
<p>Там же хранятся дополнительные файлы конкретного сервиса. Например у Caddy это файл Caddyfile, в котором содержится конфигурация:</p>
16
<p>После того как все файлы добавлены, можно запустить проект локально и убедиться что он работает:</p>
16
<p>После того как все файлы добавлены, можно запустить проект локально и убедиться что он работает:</p>
17
<p>После этого запрос к<em>localhost</em>пойдет в Caddy, который выполнит автоматическое перенаправление на<em>https</em>. Браузер укажет на недоверенный сертификат, что нормально, так как он самоподписной. Все что остается сделать, чтобы увидеть сайт - подвердить что вы согласны с риском.</p>
17
<p>После этого запрос к<em>localhost</em>пойдет в Caddy, который выполнит автоматическое перенаправление на<em>https</em>. Браузер укажет на недоверенный сертификат, что нормально, так как он самоподписной. Все что остается сделать, чтобы увидеть сайт - подвердить что вы согласны с риском.</p>
18
<h3>Продакшен</h3>
18
<h3>Продакшен</h3>
19
<p>Первым делом добавим Caddy в сборку на коммит. Для этого создадим репозиторий для Caddy на Docker Hub и расширим воркфлоу<em>main.yml</em>:</p>
19
<p>Первым делом добавим Caddy в сборку на коммит. Для этого создадим репозиторий для Caddy на Docker Hub и расширим воркфлоу<em>main.yml</em>:</p>
20
<p>Следующим шагом добавим в релиз:</p>
20
<p>Следующим шагом добавим в релиз:</p>
21
<p>Когда файлы добавлены, их нужно закоммитить и выполнить релиз. Если воркфлоу отработал нормально, значит образ с правильным тегом успешно собрался. Пора переходить к деплою.</p>
21
<p>Когда файлы добавлены, их нужно закоммитить и выполнить релиз. Если воркфлоу отработал нормально, значит образ с правильным тегом успешно собрался. Пора переходить к деплою.</p>
22
<p>Если просто добавить старт контейнера в плейбук<em>ansible/release.yml</em>, то сайт не откроется. Caddy попытается обратиться к указанному в конфигурации<em>app</em></p>
22
<p>Если просто добавить старт контейнера в плейбук<em>ansible/release.yml</em>, то сайт не откроется. Caddy попытается обратиться к указанному в конфигурации<em>app</em></p>
23
<p>и выдаст в лог ошибку о недоступности домена. Почему? Каждый контейнер в Docker запускается в изолированной среде и не знает про другие контейнеры. Для их связи используется<strong>network</strong>.</p>
23
<p>и выдаст в лог ошибку о недоступности домена. Почему? Каждый контейнер в Docker запускается в изолированной среде и не знает про другие контейнеры. Для их связи используется<strong>network</strong>.</p>
24
<p>Когда все добавлено, выполните деплой последнего релиза:</p>
24
<p>Когда все добавлено, выполните деплой последнего релиза:</p>
25
<p>Наш проект выложен на сервер и работает. Технически мы можем обратиться к нему по ip-адресу, но так делать не стоит. ip-адрес всегда меняется при пересоздании сервера, из-за чего постоянно придется запоминать новый. Запоминать ip-адрес, в принципе, не очень удобно. Лучше иметь доменное имя, которое никогда не меняется.</p>
25
<p>Наш проект выложен на сервер и работает. Технически мы можем обратиться к нему по ip-адресу, но так делать не стоит. ip-адрес всегда меняется при пересоздании сервера, из-за чего постоянно придется запоминать новый. Запоминать ip-адрес, в принципе, не очень удобно. Лучше иметь доменное имя, которое никогда не меняется.</p>
26
<p>Самый простой способ добавить доменное имя для разработки, внести запись в файл<em>/etc/hosts</em>. Этот файл часть системы DNS, которая выполняет поиск ip-адреса по имени. Поиск всегда начинается с него:</p>
26
<p>Самый простой способ добавить доменное имя для разработки, внести запись в файл<em>/etc/hosts</em>. Этот файл часть системы DNS, которая выполняет поиск ip-адреса по имени. Поиск всегда начинается с него:</p>
27
<p>Попробуйте открыть в браузере<em>devops-example.test</em>. Если все было сделано правильно, то вы увидите сайт.</p>
27
<p>Попробуйте открыть в браузере<em>devops-example.test</em>. Если все было сделано правильно, то вы увидите сайт.</p>
28
<h2>Обработка ошибок</h2>
28
<h2>Обработка ошибок</h2>
29
<p>Во время деплоя приложение перезапускается. Между остановкой и стартом новой версии проходит какое-то время, когда приложение не работает, а пользователи видят ошибку<em>502 Bad Gateway</em>. Иногда это время достаточно большое, есть приложения которые стартуют минуты.</p>
29
<p>Во время деплоя приложение перезапускается. Между остановкой и стартом новой версии проходит какое-то время, когда приложение не работает, а пользователи видят ошибку<em>502 Bad Gateway</em>. Иногда это время достаточно большое, есть приложения которые стартуют минуты.</p>
30
<p>Обработка ошибки<em>502</em>задача реверс-прокси. Для этого создается html-файл, в котором красиво описывается ситуация "мы обновляемся". Этот файл добавляется в образ к веб-серверу, а в конфигурации прописываются такие строки:</p>
30
<p>Обработка ошибки<em>502</em>задача реверс-прокси. Для этого создается html-файл, в котором красиво описывается ситуация "мы обновляемся". Этот файл добавляется в образ к веб-серверу, а в конфигурации прописываются такие строки:</p>
31
<p>Учтите, что эта ошибка возникает не только во время деплоя. Если приложение упало, по какой-либо причине, то мы получим тоже самое.</p>
31
<p>Учтите, что эта ошибка возникает не только во время деплоя. Если приложение упало, по какой-либо причине, то мы получим тоже самое.</p>