Продакшен и Деплой
Теория: Прокси-сервер (Caddy)
Для запуска веб-приложений нужен веб-сервер, программа, которая принимает входящие HTTP-запросы и передает их на обработку приложению. Приложение делает свою работу и отдает результат веб-серверу, который формирует HTTP-ответ для клиента.
Часто такой веб-сервер либо встроен в сам язык как модуль, либо написан на этом же языке, для запуска кода внутри себя. Обычно, задачу интеграции берут на себя фреймворки. В нашем приложении devops-example-app за это отвечает фреймворк fastify. Внутри себя он использует встроенный в Node.js веб-сервер:
Несмотря на простоту такой схемы, ее в большинстве проектов недостаточно. Помимо встроенного веб-сервера, нужен внешний, выполняющий роль реверс-прокси (reverse-proxy), например Nginx или Caddy.
Прокси-сервер ставится между клиентами и внутренней инфраструктурой. Он принимает запросы от клиентов и переадресует их на внутренний веб-сервер или веб-сервера, если их много. Именно поэтому он называется реверс (обратный), снаружи внутрь. Зачем он нужен?
- Проекты редко состоят только из кода, который мы запускаем на встроенном веб-сервере. Помимо кода, почти всегда есть файлы, css, js, картинки и другие файлы. Для их "раздачи" тоже нужен веб-сервер, а еще нужно уметь их сжимать и кешировать
- Далеко не все внутренние веб-сервера умеют эффективно обрабатывать "медленных клиентов". Это клиенты с медленным соединением, которые очень долго отправляют и принимают данные. Они занимают ресурсы веб-сервера и приложения. Если придет достаточное количество медленных клиентов, то веб-сервер и приложение быстро перестанут отвечать. Пользователи начнут видеть ошибки
- Встроенные веб-сервера достаточно ограниченны в своих возможностях, например в том как они обрабатывают ошибки, как поддерживают HTTPS и так далее
Эти причины настолько серьезны, что без реверс-прокси в продакшен не ходят. Только какой выбрать? Nginx самый популярный сервер в мире, но Caddy, гораздо проще в настройке и отладке. У него встроенная поддержка Let's Encrypt, Caddy автоматически обновляет сертификаты для работы HTTPS. Поэтому в этом курсе мы воспользуемся Caddy. Изучив его, вы без труда сможете перейти на Nginx, так как концептуально все подобные веб-сервера работают одинаково.
Caddy
Для начала Caddy нужно подключить к локальной разработке. Так мы сможем тестировать его конфигурацию еще до выкладки в продакшен.
Локальное окружение
Когда наш проект состоял из одного приложения, его было достаточно разрабатывать напрямую без Docker Compose. Как только добавляется сервис, в нашем случае внешний веб-сервер Caddy, то без Docker Compose становится сложно. Появляется задача управлять набором сервисов как единым целым, стартовать их, останавливать и перезапускать.
Добавим Docker Compose с такой конфигурацией:
Почему именно так? Мы придерживаемся структуры, в которой основное приложение располагается в корне проекта, а дополнительные сервисы лежат в директории services. У каждого сервиса своя директория с Dockerfile внутри.
Там же хранятся дополнительные файлы конкретного сервиса. Например у Caddy это файл Caddyfile, в котором содержится конфигурация:
После того как все файлы добавлены, можно запустить проект локально и убедиться что он работает:
После этого запрос к localhost пойдет в Caddy, который выполнит автоматическое перенаправление на https. Браузер укажет на недоверенный сертификат, что нормально, так как он самоподписной. Все что остается сделать, чтобы увидеть сайт - подвердить что вы согласны с риском.
Продакшен
Первым делом добавим Caddy в сборку на коммит. Для этого создадим репозиторий для Caddy на Docker Hub и расширим воркфлоу main.yml:
Следующим шагом добавим в релиз:
Когда файлы добавлены, их нужно закоммитить и выполнить релиз. Если воркфлоу отработал нормально, значит образ с правильным тегом успешно собрался. Пора переходить к деплою.
Если просто добавить старт контейнера в плейбук ansible/release.yml, то сайт не откроется. Caddy попытается обратиться к указанному в конфигурации app
и выдаст в лог ошибку о недоступности домена. Почему? Каждый контейнер в Docker запускается в изолированной среде и не знает про другие контейнеры. Для их связи используется network.Когда все добавлено, выполните деплой последнего релиза:
Наш проект выложен на сервер и работает. Технически мы можем обратиться к нему по ip-адресу, но так делать не стоит. ip-адрес всегда меняется при пересоздании сервера, из-за чего постоянно придется запоминать новый. Запоминать ip-адрес, в принципе, не очень удобно. Лучше иметь доменное имя, которое никогда не меняется.
Самый простой способ добавить доменное имя для разработки, внести запись в файл /etc/hosts. Этот файл часть системы DNS, которая выполняет поиск ip-адреса по имени. Поиск всегда начинается с него:
Попробуйте открыть в браузере devops-example.test. Если все было сделано правильно, то вы увидите сайт.
Обработка ошибок
Во время деплоя приложение перезапускается. Между остановкой и стартом новой версии проходит какое-то время, когда приложение не работает, а пользователи видят ошибку 502 Bad Gateway. Иногда это время достаточно большое, есть приложения которые стартуют минуты.
Обработка ошибки 502 задача реверс-прокси. Для этого создается html-файл, в котором красиво описывается ситуация "мы обновляемся". Этот файл добавляется в образ к веб-серверу, а в конфигурации прописываются такие строки:
Учтите, что эта ошибка возникает не только во время деплоя. Если приложение упало, по какой-либо причине, то мы получим тоже самое.
