Transcription
Всем привет. Сегодня мы разберём такую тему, как деплой приложений. В рамках данного видео мы посмотрим, что из себя представляет деплой, какие инструменты мы будем для этого использовать, а также сделаем деплой нашего приложения в нескольких вариантах от простого к сложному.
После просмотра данного ролика вы поймёте, как размещать свои приложения на сервере, подключать домен, SSL-сертификат, настраивать балансировщик нагрузок, как работает Blue-green Deploy. Сделаем контейнеризацию через Docker и будем запускать наши приложение через Docker Compose. А также настроим автоматический деплой приложений с помощью Deploy.
Какими навыками нужно обладать для просмотра данного видео? Вам нужно понимать, как работает DNS и базовое умение работать с консолью в Linux.
Дисклеймер: Так как я не являюсь DevOps инженером, я не покажу вам какие-то продвинутые темы, такие как Kubernetes, Ansible и другие инструменты. Моя же задача — показать вам основу, как можно разворачивать свои приложения на сервере.
Итак, перейдём к базовой теории по деплою. Для начала нам нужно разместить файлы на сервере, при необходимости собрать контейнер. Далее мы запускаем процесс нашего приложения или же контейнер. Также нам нужно настроить доступ к этому приложению, в том числе и по HTTPS, а также производить обновление приложения без его остановки. Так называемое zero downtime deployment.
Базовая схема будет выглядеть таким образом. У нас есть DNS, который связывает домен и IP нашего центрального сервера. На нём установлен какой-либо инструмент для реверс-прокси, а также при необходимости балансировки нагрузок. Это может быть Nginx, это может быть Traefik. После чего он направляет трафик на необходимые нам приложения. Это может быть в рамках одного сервера или нескольких, на которых запущены различные наши приложения, базы данных и так далее. В свою очередь, код приложения на продакшн-серверах должен быть синхронизирован с последней версией нашего кода, который может располагаться на GitHub, GitLab или других подобных сервисов.
Итак, давайте перейдём к практике. Для начала посмотрим пример приложения. Это базовое приложение, которое создаётся с помощью Vite и React. Давайте посмотрим, как оно выглядит. То есть у нас здесь есть какая-то заглавная страничка, где мы можем нажимать на кнопку счётчиком, который будет увеличиваться. И на данном примере мы посмотрим, как мы будем деплоить наше приложение.
Первый пример — это использование готовых систем для развёртывания ваших приложений. Одни из известных — это у нас Vercel, это Netlify, более новый Railway. Мы же рассмотрим на примере нашего аналога Apps в сервисе Timeweb Cloud. Оно позволяет также подключить наш GitHub репозиторий с проектом и автоматически разворачивать приложение.
Итак, давайте для начала проинициализируем Git в нашем проекте. Для этого воспользуемся командой `git init`. После чего добавим все наши файлы с помощью команды `git add .`, после чего сделаем коммит с сообщением. И теперь давайте создадим новый репозиторий на GitHub. Вы также можете использовать в качестве альтернативы GitLab, Bitbucket либо что-то другое.
Теперь давайте создадим репозиторий для нашего проекта. Для этого переходим на GitHub и создаём новый репозиторий. Даём ему название. Я для примера назвал его `vite-react`. Выбираем приватную область видимости и создаём репозиторий. Далее копируем команду для переноса файлов с нашего компьютера на GitHub и вставляем в консоль. Как мы видим, все файлы перенеслись успешно в GitHub.
Теперь давайте задеплоим наше приложение. Для этого мы переходим в Timeweb Cloud в раздел Apps и нажимаем кнопку "Создать". Далее нам нужно указать тип фреймворка, который мы использовали при создании нашего проекта. В нашем случае это React. Вы же можете использовать что-то своё, если этого нету в списке. Например, это Astro или что-то другое. Вы должны указать "Другой". У нас React, поэтому мы выбираем его.
Далее вам нужно здесь будет добавить аккаунт GitHub. Там ничего сложного. Нужно будет разрешить доступы ко всем вашим репозиториям в рамках вашего GitHub аккаунта. После чего вам нужно выбрать необходимый репозиторий. В нашем случае это `vite-react` и выбираем ветку. У нас она одна, и поэтому мы выбираем `main`. По умолчанию сборка будет выполняться по последнему выполненному коммиту. Вы же можете указать, с какого конкретного коммита она будет выполняться.
Далее мы выбираем регион. В данном случае возьмём Санкт-Петербург. Возьмём базовую конфигурацию. Команда для сборки у нас будет `npm run build`. Директорию сборки нам нужно поменять, так как по умолчанию сборка происходит в папку `dist`. Переменные окружения мы добавлять не будем, но если они у вас есть, вы можете их здесь указать либо добавлять ещё. Также можно загрузить из `.env` файла. Можем дать название нашему проекту. Это в целом здесь не принципиально. И указываем, как у нас будет сниматься цена. Либо по часу, либо по дневному тарифу, либо по месячному. Я, для примера, возьму часовой и нажимаем кнопку "Заказать".
После чего нам нужно будет подождать какое-то время, пока у нас произойдёт деплой. Данный сервис использует Docker под капотом, соответственно, ему нужно будет для начала установить все необходимые пакеты, установить дальше наш проект, установить все необходимые зависимости, собрать его, установить SSL-сертификат, после чего он задеплоит приложение.
Пока идёт деплой приложения, давайте посмотрим на интерфейс. Здесь можем посмотреть все наши коммиты. В нашем случае он только один. Далее мы видим логи нашего деплоя. Мы можем посмотреть краткую версию логов. Для этого нам нужно нажать на данную кнопочку. Вернём назад. Мы можем увеличить размер текста, чтобы нам было удобнее это смотреть. И также можно по итогу скачать текстовую версию данных логов.
В настройках нашего проекта мы можем настроить наш деплой, а именно указать фреймворк, версию окружения, то есть какую версию JS он будет использовать, команду сборки, директорию сборки, переменные. В принципе, всё то, что мы указывали при создании данного приложения. Далее мы можем изменить конфигурацию, но в данный момент у нас происходит деплой, поэтому мы её менять не можем.
Далее по умолчанию у нас подключается домен, который выдаёт сам Timeweb Cloud. Соответственно, мы можем нажать на кнопку "Редактировать", купить новый домен, либо разместить его на Timeweb и подключить к нашему приложению. И в рамках Timeweb мы можем создавать разные проекты и, соответственно, таким образом разделять наши различные серверы, сервисы, базы данных между проектами, чтобы не было общего большого списка. Соответственно, здесь мы можем указывать, какому проекту оно у нас принадлежит, и можно посмотреть историю. То есть в данном случае можно посмотреть то, что у нас создано фронтенд приложение.
Итак, наше приложение успешно задеплоилось. Теперь мы перейдём в настройки и скопируем домен. Давайте посмотрим на результат. И как мы видим, наше приложение успешно задеплоилось на сервер. У нас используется production версия сборки React, как мы видим, с помощью специального расширения. И как мы видим, у нас всё отлично работает. Если мы будем нажимать на кнопку, наш счётчик будет увеличиваться.
Теперь давайте посмотрим, как будут происходить изменения, если мы что-то в нашем приложении меняем. Например, перейдём в наше приложение и поставим начальное значение для нашего аккаунта не ноль, а единицу. Теперь нам нужно добавить изменения в нашем проекте и загрузить их на GitHub. Для этого мы также добавляем файлы, делаем коммит наших изменений и после чего пушим наши изменения в GitHub.
Теперь давайте посмотрим, что у нас изменилось. Если мы перейдём в Timeweb Cloud, мы видим то, что у нас сразу пошёл процесс деплоя. В нашем списке коммитов уже есть наш успешный коммит, который мы делали до этого, и по нему сборка в данный момент работает. И сейчас по новому коммиту у нас собирается приложение для того, чтобы изменения появились на сайте. Если мы в данный момент зайдём на сайт и обновим страницу, мы до сих пор увидим то, что у нас count равен нулю. И, соответственно, ваши пользователи продолжают им пользоваться, вне зависимости от того, что у нас уже собирается новая версия проекта. Нам нужно подождать какое-то время для того, чтобы у нас снова собрался наш проект и изменения вступили в силу.
Итак, прошло немного времени, и наши изменения применились. Давайте посмотрим, как это выглядит. Обновим нашу страницу. И как мы видим, изначальное значение count у нас стало единицей.
А теперь представим ситуацию, что мы выпустили наш код с какой-либо ошибкой, и нам нужно откатить назад. Что мы для этого можем сделать? Для этого мы перейдём также в настройки нашего проекта, выбираем нужный нам коммит и нажимаем на кнопку "Выполнить откат к конкретному коммиту". Мы нажимаем, и у нас снова начинается процесс деплоя. Аналогично у нас приложение не падает, оно у нас по последнему коммиту работает, но по прошествии данного деплоя у нас откатится изменение. И теперь, как мы видим по истории наших сборок, у нас произошла успешная сборка по нашему первому коммиту. И если мы посмотрим на сайт, обновим страницу, у нас count стал равен нулю. Таким образом, вы можете откатывать при необходимости изменения на более старые версии вашего приложения.
Сейчас мы рассмотрели первый пример. Он хорошо подойдёт для каких-то небольших проектов. Например, это сайты-портфолио, блоги, документации, то есть где в основном будет статика. В целом здесь также можно размещать и какие-то проекты на React, на других фреймворках, лендинги, Telegram-боты, что-то подобное. Но для подобных проектов я бы настоятельно рекомендовал бы самостоятельно выполнять деплой, а не использовать Apps.
Теперь давайте перейдём к примеру номер два, когда мы будем деплоить наше приложение на сервере самостоятельно. Для этого мы перейдём в раздел "Облачные сервера" и создадим новый сервер. По умолчанию мы выберем Ubuntu двадцать четвёртой версии. В качестве региона я поставлю Москва. Для конфигурации мы выберем для начала самый простой тариф. Далее нам нужно обязательно подключить публичный IP-адрес для того, чтобы мы могли подключиться к нашему серверу по SSH. Бэкапы для данного сервера конкретно мы не будем использовать, поэтому отключаем. В рамках данных примеров мы будем подключаться с помощью пароля, но я вам настоятельно не рекомендую включать подключение по паролю и загружать SSH-ключ для подключения по нему. Также можно указать название сервера. Я оставлю по умолчанию. Далее указываем тарификацию. Я буду использовать часовую и нажимаю кнопку "Заказать".
После чего нам нужно будет подождать какое-то время, пока наш сервер будет готов к работе. Это примерно 2 минуты.
Итак, наш сервер готов к работе. Давайте мы к нему теперь подключимся. У нас есть команда для подключения по SSH. Мы переходим в терминал, вставляем её здесь. Пишем `yes`. И он нас попросит ввести наш пароль. Пароль мы можем найти в данном пункте и скопировать его. Вставляем. И мы подключились к нашему серверу Ubuntu.
Давайте теперь для начала обновим пакеты с помощью команды `sudo apt update`. Отлично. Теперь давайте установим Node.js на наш сервер. Для этого я воспользуюсь статьёй. Также на Timeweb я буду использовать способ номер два через NodeSource. Для этого мы скопируем данную команду, перейдём в терминал и вставим её. Теперь нам осталось установить сам Node.js. Для этого мы переходим, копируем данную команду и вставляем в терминал. Теперь давайте проверим то, что Node.js у нас действительно установился на сервер. Для этого воспользуемся командой `node -v`. Как мы видим, Node.js у нас действительно установлен на наш сервер.
Теперь давайте установим Nginx на наш сервер. Он будет выступать в качестве reverse Proxy для нашего сервера. Для этого мы воспользуемся статьёй также на Timeweb. Воспользуемся командой `sudo apt install nginx`. Nginx у нас теперь установился. Теперь давайте мы добавим его в автозагрузку. Теперь давайте проверим статус, то, что Nginx у нас действительно установлен и он работает. Воспользуемся командой `sudo systemctl status nginx`. И как мы видим, он у нас активен.
Теперь давайте настроим UFW (Uncomplicated Firewall). Для этого, для начала, мы должны его активировать с помощью команды `sudo ufw enable`. Здесь мы говорим `yes`, и он у нас включился. Теперь нам нужно установить несколько правил. Во-первых, нам нужно разрешить подключение по SSH для того, чтобы мы могли подключаться к нашему серверу, а также дать разрешение для Nginx. Для начала мы добавим правила для SSH с помощью команды `sudo ufw allow ssh`. А также следующей командой мы добавляем правила для Nginx, указывая `sudo ufw allow 'Nginx Full'`. Теперь давайте проверим то, что наши правила действительно добавились. Для этого воспользуемся следующей командой `sudo ufw status`. И как мы видим, SSH и Nginx разрешён в брандмауэре.
Теперь давайте перейдём по IP-адресу, который был дан для нашего сервера. И как мы видим, нас приветствует Nginx. Мы сделали подготовительный этап. Теперь давайте перейдём к деплою нашего приложения. Мы также будем использовать наше приложение на Vite и React. И давайте для начала склонируем репозиторий. Для этого давайте создадим наш SSH-ключ с помощью данной команды. Здесь мы отвечаем по умолчанию. И у нас создался наш SSH-ключ. Далее нам нужно выполнить следующую команду для запуска нашего SSH-агента. Теперь давайте посмотрим наш сгенерированный SSH-ключ. Для этого воспользуемся командой `cat ~/.ssh/id_rsa.pub`. Теперь давайте мы его скопируем. И нам его нужно добавить в настройках нашего GitHub аккаунта. Для этого нам нужно перейти в раздел `settings` -> `SSH and GPG keys` и создать новый SSH-ключ. Указываем какое-то название. Я укажу `test`. И далее вставляем наш SSH-ключ. Далее мы должны подтвердить: "Я воспользуюсь паролем". После чего у нас успешно добавился ключ `test`.
Следующим шагом мы склонируем наш репозиторий. Для этого мы в нашем репозитории выберем "Clone" с помощью SSH. Копируем данную команду, переходим, выполняем следующую команду `git clone` и вставляем скопированную ссылку. Говорим `yes`. И наш репозиторий успешно скопировался на наш сервер. Давайте перейдём в папку нашего проекта и давайте посмотрим то, что наши файлы действительно скопировались на наш сервер.
Следующим шагом давайте установим зависимости. Для этого воспользуемся командой `npm install`. Итак, все наши зависимости установились. Теперь давайте соберём проект. Для этого выполним команду `npm run build`. Наш проект успешно собрался, и он у нас находится в папочке `dist`. Теперь нам нужно сделать так, чтобы Nginx раздавал всё, что находится в папочке `dist`. Для этого давайте настроим наш Nginx. Для этого мы настроим конфиг с помощью команды `nano` по пути `/etc/nginx/sites-available/default`. Теперь с помощью комбинации `Ctrl+K` мы уберём все ненужные комментарии.
Теперь, когда мы убрали все лишние комментарии, давайте настроим наш Nginx конфиг. По умолчанию Nginx будет раздавать файлы, которые находятся в папке `/var/www/html`. Давайте вместо `html` мы сделаем папку `dist`. Так, у нас приложение SPA (Single Page Application), то в данном пункте `location` мы должны дополнительно указать `index.html`. Теперь давайте сохраним нашу конфигурацию. Для тех, кто не знает, как это сделать в Nano, мы зажимаем `Ctrl+X`, дальше `Y` (Yes) и `Enter`. Мы сохранили наш Nginx конфиг. Теперь давайте перезапустим наш Nginx. Для этого воспользуемся следующей командой `sudo systemctl restart nginx`. Мы перезапустили наш Nginx. И если мы теперь обновим нашу страницу, мы ничего не найдём, потому что у нас в папочке `/var/www/html` сейчас ничего нет. Мы должны перенести файлы сборки нашего проекта. Для этого воспользуемся следующей командой `mv dist/* /var/www/html/`. Так как мы находимся уже в папке нашего проекта. И дальше указываем конечную точку. Это `/var/www/html/`. Давайте посмотрим на изменение. И как мы видим, наше приложение успешно раздаётся.
Сейчас доступ к нашему приложению осуществляется с помощью IP-адреса и без SSL-сертификата. Давайте подключим домен и установим SSL-сертификат. Для этого перейдём в Timeweb, в раздел "Домены и SSL". У меня уже приобретён домен, поэтому я выбираю не "Купить домен", а "Добавить домен". Указываю этот домен `nonamefolder.ru`. Мы можем сразу его привязать к нашему конкретному сервису и указываем общий проект. NS-записи я уже прописывал, поэтому я нажимаю "Добавить". После чего наш домен успешно добавлен. Давайте проверим то, что действительно по данному домену он будет ссылаться на IP-адрес нашего сервера. Для этого давайте воспользуемся таким сервисом, как `DNSchecker.org`. Здесь мы указываем наш домен и нажимаем "Search". Мы видим то, что IP-адрес действительно нашего сервера. И теперь, если мы перейдём на данный домен, мы увидим наше приложение. Так как мы обрабатывали стандартный конфиг, нам не нужно было как-то указывать это в настройках. Если уже будете использовать несколько конфигов, то вам нужно будет указать этот домен напрямую в вашем конфиге. Как это сделать? Для этого вы снова настраиваете ваш Nginx конфиг и в поле `server_name` вместо нижнего подчёркивания, которое будет ссылаться на IP-адрес нашего сервера, вы указываете непосредственно ваш домен. И дополнительно вы указываете ваш же домен с `www` и ваш домен. Сохраняйте такой конфиг, и у вас всё будет работать.
И последним шагом, который мы сделаем на данном примере — это установим SSL-сертификат. Мы для этого воспользуемся таким сервисом, как Certbot. Для этого перейдём на официальный сайт Certbot. И нам нужно выбрать настройки нашего сервера. У нас это Nginx, который работает на Linux. Мы будем использовать Snap. Для начала давайте установим Snap на наш сервер. Для этого переходим по данной ссылке. У нас сервер на Ubuntu и воспользуемся данной командой. Итак, Snap у нас установился. Теперь давайте устанавливать Certbot. Возвращаемся на страничку установки Certbot и выполняем команду для его установки. У нас Certbot установился. Теперь давайте установим SSL-сертификат для нашего домена. Небольшая ремарка. Перед тем, как мы будем использовать Certbot для выпуска SSL-сертификата для нашего домена, нам нужно перейти в настройки Nginx и в поле `server_name` указать два домена: просто и с `www`. И после чего не забыть перезапустить наш Nginx.
Теперь давайте выпустим SSL-сертификаты для нашего домена. Для этого воспользуемся следующей командой: `sudo certbot --nginx`. Теперь нам нужно цифрами указать, для каких доменов мы выпускаем SSL-сертификат. У нас это будет один и два. Либо мы можем оставить поле пустым для того, чтобы сразу выбрать все домены. Мы так и сделаем. Он у нас начинает запрашивать сертификаты для данных доменов, и нам нужно немного подождать. Итак, как мы видим, у нас успешно установились SSL-сертификаты. Давайте перейдём в браузер, обновим страницу. И как мы видим, теперь у нас защищённое соединение.
Таким образом, мы рассмотрели базовый пример. Но какие у него есть минусы? В данном случае мы всё продолжаем делать вручную. Например, у нас появились какие-то изменения в нашем репозитории. Для этого нам нужно скопировать изменения с нашего GitHub. На данный момент их нет, но, соответственно, если они есть, мы должны будем снова пересобирать проект и вручную всё это перемещать в папку `dist`. Это не очень удобно, поэтому мы рассмотрим далее на примере Next.js, как мы будем раздавать не статику, а по процессу.
Итак, давайте перейдём к третьему примеру. Для этого мы создадим новый сервер. Мы нажимаем кнопку "Создать". И теперь мы выберем не стандартную Ubuntu, а в разделе "Marketplace" мы будем использовать Docker. Он нам потребуется для последующей работы. Выбираем регион Москва и выбираем предложенный оптимальный тариф. У нас уже есть IP-адрес, поэтому мы его и будем использовать. Бэкапы мы отключаем, после чего мы нажимаем "Заказать сервер". И нужно также будет подождать пару минут, пока он создастся.
Итак, наш сервер создался. Сейчас я к нему подключусь. Также установлю Node.js, Nginx, Certbot и подключу домен. И после этого мы рассмотрим следующий пример.
Итак, я создал новый сервер. Я на него всё необходимое установил. Как сейчас мы видим, в нём установлена стандартная конфигурация Nginx. И теперь рассмотрим на примере Next.js. Для этого давайте перейдём в консоль и создадим новый Next.js проект и воспользуемся для этого следующей командой: `npx create-next-app@latest`. Как мы назовём наш проект? Назовём его `test`. Будем использовать рекомендуемые настройки. И у нас пошла установка необходимых зависимостей и создание самого проекта.
Итак, у нас установился Next.js проект. Давайте теперь перейдём в папку `test`. Теперь давайте соберём наш проект, выполнив команду `npm run build`. Итак, наш проект собрался, и теперь, выполнив команду `npm start`, мы можем запустить наш Next.js приложение на тридцатитысячном порту. Но если мы перейдём по нашему домену, мы здесь ничего нового не увидим. Нам нужно настроить наш Nginx конфиг. Перейдём в него. Для этого воспользуемся уже известной нам командой `nano` для дефолтного конфига Nginx. И теперь нам нужно изменить параметры в пункте `location`. Теперь вместо того, чтобы раздавать конкретные файлы, мы будем проксировать наш запрос на определённый порт. Для этого нам нужно указать следующее значение. Мы указываем `proxy_pass`. Дальше мы указываем `http://localhost:3000` и порт, на котором мы будем проксировать запрос нашего Nginx. Сохраняем наши изменения. Теперь с помощью команды `sudo nginx -t` проверим, что наша конфигурация правильная, и перезапустим наш Nginx.
Теперь давайте перейдём по нашему домену. Обновим. Но сейчас мы ничего не увидим, потому что нам нужно запустить наше приложение на трёхтысячном порту. Давайте мы его запустим с помощью команды `npm start`. Перейдём по нашему домену, и мы увидим наше Next.js приложение. Но у нас есть проблема. Если мы закроем наш терминал, выключим компьютер либо просто банально прекратим команду `npm start`, то у нас приложение снова перестанет открываться. Для этого нам нужно управлять данным процессом и для этого установим такой сервис под названием PM2. Для этого воспользуемся следующей командой `npm install -g pm2`, чтобы он установился не как зависимость данного проекта, а в целом на наше рабочее окружение. Итак, PM2 у нас установился. Теперь давайте запустим данный процесс с помощью PM2. Для этого выполним следующую команду: `pm2 start`. Дальше с помощью параметра `name` мы можем как-то его назвать. Например, назовём его `client`. И дальше мы указываем либо входной файл, но поскольку у нас нету входного файла, а у нас есть команда для запуска, мы её передаём в кавычках. Таким образом, команда будет выглядеть следующим образом: `pm2 start "npm start" --name client`, и мы её исполняем. Как мы видим, у нас появился процесс под названием `client`. И если мы перейдём на наш сайт, обновим его, и мы увидим то, что наше Next.js приложение работает. И теперь, если мы закроем терминал, выключим наш компьютер, то у нас всё равно по данному домену данное приложение будет работать. Также давайте дополнительно воспользуемся командой `pm2 save`, чтобы он сохранил все наши текущие процессы. И также выполним команду `pm2 startup`. Это будет для того, чтобы если мы перезапустим наш сервер, он автоматически запустил все необходимые нам процессы.
Таким образом, мы рассмотрели, как деплоить наше приложение не как статические файлы, а как процесс. Теперь давайте представим то, что у нас в нашем проекте появились какие-то изменения. То есть мы его не создавали вручную на сервере, а у нас склонирован репозиторий из GitHub, и у нас появились какие-то изменения. Я их сейчас внесу вручную, но представим то, что мы их получили с нашего GitHub нового коммита. Давайте вручную поменяем в файле `app/page.tsx`. И, например, в нашем заголовке поменяем на "Добро пожаловать". Мы изменили наш заголовок. Теперь давайте сохраним изменения. И теперь давайте соберём новую версию нашего проекта с помощью команды `npm run build`. Итак, у нас появилась новая сборка проекта. Но если мы перейдём на наш сайт и обновим, мы ничего не увидим. Для этого воспользуемся командой `pm2 restart 0`. И далее мы должны указать индекс нашего процесса, в данном случае ноль, для того, чтобы его перезапустить. После чего мы обновляем страницу, и как мы видим, у нас изменился результат на последнюю версию нашего проекта. Таким образом, мы рассмотрели, как мы деплоим наши приложения не как статику, а как процессы.
Для следующего примера нам потребуется Docker. Поэтому давайте подготовимся. Для начала мы остановим данный процесс и удалим его. Воспользуемся командой `pm2 stop 0` и `pm2 delete 0`. Таким образом, мы удалили ненужный нам процесс. Если мы перейдём по нашему сайту, у нас будет 502 ошибка, потому что по данному порту у нас ничего не раздаётся.
Теперь давайте создадим наш Dockerfile в корне нашего приложения. Мы не будем здесь писать с нуля конфигурацию нашего Dockerfile и возьмём из официальной документации. Для этого перейдём в официальную документацию Next.js, в раздел Deployment, выберем Docker и возьмём Dockerfile. И вставим его в наш файл. Удалим ненужные комментарии. Итак, мы удалили все ненужные комментарии и сохраняем данный файл.
Теперь нам нужно настроить конфиг нашего Next.js приложения. Для этого откроем файл `next.config.js`. Давайте вернёмся в репозитории проекта Next.js с Docker и перейдём в файл `next.config.js` и скопируем данный параметр `output` и вставляем его в наш конфиг. Сохраняем изменения.
Теперь давайте настроим наш Docker Compose файл. Для этого воспользуемся командой `nano docker-compose.yml`. Я также здесь не буду ничего писать вручную и возьму готовый конфиг. Ссылку на него я оставлю в описании. Вставляем в наш файл. Для начала мы уберём все ненужные комментарии. Нам также не потребуется здесь `networks`. Мы их тоже убираем. Убираем поле `args`. Они нам сейчас тоже не нужны. В качестве Dockerfile мы указываем `Dockerfile` в текущей директории. В качестве `context` мы также указываем текущую директорию. Сохраняем наш файл.
Теперь давайте запустим Docker Compose. Для этого воспользуемся командой `docker-compose up -d`. Как мы видим, у нас началась сборка нашего образа. Установка Node.js, копирование файлов из нашей директории, установка зависимостей и сборка проекта. Итак, наш образ собрался, и у нас запустился контейнер. Давайте мы это проверим с помощью команды `docker ps`. И как мы видим, у нас запустился наш контейнер по трёхтысячному порту. Перейдём на наш домен. И как мы видим, у нас снова наше приложение заработало.
Теперь давайте рассмотрим тему, как Blue-green Deployment. В чём её суть? У нас уже есть какое-то
Работающее приложение, назовём его условно Blue, и мы хотим выпустить новую версию. Для этого мы должны параллельно уже с работающим сервисом запустить новую версию нашего приложения Green и, соответственно, перевести трафик на него и посмотреть, всё ли нас устраивает.
Для данного примера я создал два новых проекта на NextGS, назвал их тест и тест 2. Теперь давайте посмотрим на конфигурацию нашего Enginex. Как можем видеть, в данный момент у нас идёт проексирование на трёхтысячный порт.
Теперь давайте перейдём в папочку тест. И как мы уже умеем, с помощью PM2 запустим данный процесс. И сейчас мы будем использовать без докера. Просто с помощью PM2 запустим наше приложение. Для этого воспользуемся командой PM2 start. Название нашего процесса и команда PM start. И наше приложение запустилось на трёхтысячном порту. Давайте посмотрим. И как мы можем видеть, наше приложение действительно работает.
Теперь давайте перейдём в папочку Test2 и тоже здесь запустим новую версию приложения, но уже не на трёхтысячном порту, а на 3001. Зайдём в настройки нашего package.json. И здесь для команды start добавим параметр порт 3001. Теперь, если мы запустим наше приложение по команде npm start, оно будет запущено по порту 3001.
Теперь запускаем новую версию нашего приложения также с помощью PM2. И мы видим то, что оно у нас заработало. Но мы не увидим здесь никаких изменений, потому что у нас до сих пор проксирование идёт на старую версию. Для этого давайте настроим конфигурацию нашего, перейдём в редактирование уже известного нам файла и для этого в начале файла добавим такую настройку, как upstream. Назовём её, например, Next App. Далее мы должны указать наше приложение, которое работает на порту тысячном и 3001. Для этого мы указываем server, локальный IP-адрес, трёхтысячный порт и то же самое с портом 3001. И далее мы должны в location proxy_pass указать уже не трёхтысячный порт, а уже Next App. Сохраняем наш файл. Проверяем в конфигурацию нашего Enginex, что всё работает правильно, и перезапускаем наш Engine X с помощью команды service nginx restart.
Давайте перейдём на наш сайт, и мы не увидим никаких изменений, потому что по умолчанию у нас всё на трутычном порту работает. Давайте вернёмся в нашу конфигурацию. И теперь мы закомментируем нашу старую версию, которая работает на трутычном порту. Сохраним изменения и перезапустим Engine X. Теперь давайте перейдём на наш сайт, обновим страницу. И, как мы видим, мы уже попали в новую версию нашего приложения.
Но представим то, что мы выкатили новую версию приложения и при этом обнаружился какой-то баг, и нам нужно откатить до последней версии. Для этого давайте вернёмся обратно и в нашем Enginex. И уже в этом конфиге мы уберём комментарии с первой строчки. нашего апстрима и добавим ко второй. Сохраним изменения и перезапустим наш engine X. Давайте обновим страницу. И мы вернулись на нашу прежнюю версию. Таким образом, при необходимости можем откатить изменения назад.
Если же у нас в новой версии нам всё нравится, то старую версию мы можем удалять. Также не забываю удалить процесс и убрать настройку в Enginex. С другой стороны, то, как мы сейчас настроили наш Enginex, это является балансировщиком нагрузки. У нас есть два приложения. Конкретно сейчас мы разбирали на примере, что это разные версии, но представим, что это одно и то же приложение. И таким образом мы можем балансировать между нашими процессами.
Если мы перейдём в настройки нашего Enginex, если мы уберём комментарий, то таким образом наш engine X будет распределять всех пользователей нашего сайта между этими двумя процессами. Обычно вместо локальных IP-адресов здесь ставятся IP-адреса других серверов в рамках нашей приватной сети. Например, у нас их может быть несколько. И таким образом наш основной сервер с Enginex будет просто распределять трафик между уже другими серверами, где установлены данные приложения, и они уже сами задеплоены.
Давайте посмотрим, какие дополнительные настройки мы здесь можем указать. По умолчанию балансировщик нагрузки Enginex работает в формате Round Robin. Что это такое? Это означает то, что каждый новый пользователь будет отправляться на следующий по указанному списки серверу до тех пор, пока список не закончится, и потом снова это пойдёт с первой строчки. Таким образом, наш первый пользователь попадёт по IP-адресу, который указан в первой строчке. Новый пользователь будет попадать на сервер, указанный во второй строчке. Третий же пользователь будет попадать на сервер в первой строчке и так далее.
Какие есть ещё стратегии? У нас есть ещё такая стратегия, как Weight Round Robin, когда мы определённым серверам добавляем так называемый вес. Например, мы укажем для нашего первого процесса, ну или сервера Weight 5. Что это будет означать? Что первые пять активных подключений будет направляться на наш первый процесс илибоже сервер. Следующий же один будет направляться на второй процесс. По умолчанию для каждого указанного сервера weight будет равен единице.
Ещё одна стратегия - это хэширование по IP. Enginex будет запоминать IP-адрес, откуда у нас поступил запрос. Он него будет хэшировать и прикреплять к определённому серверу. Это важно, например, для сохранения сеансов конкретного пользователя в рамках подключения к конкретному серверу. Когда подойдёт такой вариант, например, когда нам важно сохранить привязанность сессии определённого пользователя на конкретном сервере для того, чтобы эти данные не потерялись.
Рассмотрим ещё один вариант - это least connection. В чём его суть? Когда у нас появляется новое активное соединение, engine X смотрит, сколько всего активных соединений есть с каждым указанным сервером, и проверяет, где больше, где меньше, и направляет на тот, где активных подключений меньше. Также мы можем комбинировать эти варианты. Например, мы можем использовать стратегию Least Connection. При этом мы можем указывать определённый вес к какому-то серверу.
Итак, давайте перейдём к финальному примеру в рамках нашего урока. Здесь мы уже воспользуемся такой системой, как Docploy. А на себя представляю систему, которая позволяет автоматически деплоить наше приложение на сервер, подключить домен и установить SSL-сертификат, автоматически отслеживать все изменения в рамках нашего репозитория, собирать наше приложение и обновлять до последней версии. По сути, это то же самое, что и было в нашем первом примере, но здесь эта система у нас open source, и мы должны сами установить на наш сервер.
Давайте создадим опять новый сервер. Выберем также шаблон с докером, регион Москва, конфигурацию оптимальную, подключим публичный IP и отключим бэкапы. Заказываем. Итак, наш сервер готов к работе. Теперь перейдём на официальный сайт Docploy и скопируем команду для его установки. Перейдём в терминал и исполним её. Итак, наш Doc Play установился. Теперь нас просит подождать 15 секунд, чтобы сервер запустился. И после чего нужно будет перейти по следующему IP-адресу. Отлично, мы перешли по IP-адресу, и нас просят зарегистрироваться. Указываем наше имя, email и пароль. Итак, мы зарегистрировались. Давайте первым делом включим тёмную тему.
И теперь посмотрим, как задеплоить приложение через Docploy. Начала давайте подключим домен для нашего сервера. Перейдём для этого в раздел домены на timeweb. Выберем наш домен DNS. Добавим новую запись типа А и выберем наш сервер. Вернёмся назад в Docploy. Теперь давайте подключим домен к нашему серверу. Для этого перейдём в раздел Webserver. Укажем домен. Далее нам нужно обязательно указать email. Включаем опцию https и выбираем провайдера. Нажимаем save. Домин успешно подключён. Давайте мы теперь по нему перейдём. И теперь наш Docploy располагается по домину с SSL-сертификатом. Давайте войдём в нашу консоль. И вот мы снова в нашей консоли уже не по IP-адресу, а по домену.
Следующий шаг. Для деплоя нам нужно подключить наш GitHub. Для этого перейдём в раздел Git и выберем провайдер. В нашем случае это будет GitHub. Далее, если ваши репозитории, которые вы будете использовать для деплоя, находятся на вашем личном аккаунте GitHub, то нажимаете на кнопку Create GitHub App. У меня же эта организация, поэтому я введу её название здесь. Нажимаем Create GitHub App. Далее нам нужно подтвердить доступ. Я воспользуюсь паролем. Далее нам нужно указать название для нашего App name. Я здесь ничего менять не буду, оставлю как есть. И нажимаем создать. Теперь мы добавили наш GitHub провайдер. Нам нужно теперь нажать на данную кнопку. Далее нам нужно указать, каким репозиториям у нас Doc Play будет иметь доступ. Я укажу ко всем. И нажимаем на кнопку. Итак, мы успешно подключили наш GitHub к Docploy.
Теперь давайте вернёмся во вкладку с нашими проектами, создадим новый, указываем название, можем добавить какое-то описание и нажимаем create. Наши проекты можно представить как некие папки, в которых будут размещены разные сервисы. Это может быть наши front-end приложения, приложения, базы данных и так далее. Теперь давайте создадим наш сервис. Для этого мы нажимаем кнопку Create service. Далее нам нужно указать тип нашего сервиса. Это может быть application, например, наш проект, database, то есть какая-то база данных, compose, если мы используем docker compose, template, если мы хотим выбрать что-то из готовых шаблонов, и AI assistant. В нашем случае это application. Далее нам нужно указать название сервиса, например, клиент. А name мы оставляем по умолчанию и можем написать, а можем не писать описание. Нажимаем create и наш сервис создан. Нажимаем на него. Далее в разделе provider мы выбираем только что подключённый GitHub аккаунт. Далее мы выбираем репозиторий. У нас это React. Выбираем ветку main и нажимаем Save.
Далее перейдём в build type. Здесь нам нужно выбрать, что мы будем использовать для сборки нашего проекта. В наш проект я добавил Dockerfile. В нём у нас сначала собирается наш проект и раздаётся с помощью Enginex. Теперь мы выбираем build type docker file. Нам нужно указать путь к нашему Docker файлу. И нажимаем save.
Теперь давайте задеплоим наше приложение. Нажмём на кнопку deploy. Подтверждаем. В разделе Deployments у нас отображаются все наши деплои. У нас пошёл процесс деплоя. Давайте немного подождём. Итак, как мы видим, наше приложение успешно задеплоилось. Если мы перейдём в логи деплоя, нажав на кнопку View, мы увидим то, что у нас всё прошло успешно.
Теперь давайте перейдём во вкладку Domains, добавим новый домен. Мы можем здесь подключить новый домен, либо же под домен. Я сгенерирую случайно с помощью трафика. Далее нам нужно указать порт. В нашем случае это восьмидесятый. Далее включаем https и выбираем провайдера. Нажимаем create. Давайте перейдём по данному домену. И как мы видим, наше приложение успешно задеплоено.
Ну а на этом у меня для вас на сегодня всё. Спасибо большое, что посмотрели данный ролик. Оставляйте комментарии, лайкайте видео, переходите по ссылке в Telegram-канал и увидимся в новых видео. Yeah.