Как стать автором
Обновить

CI/CD для чайников — разберитесь, и начните автоматизировать рутину в разработке. Часть 3. Его величество, деплой

Уровень сложностиПростой
Время на прочтение14 мин
Количество просмотров7.6K

Пишу про полезные материалы про IT, и собираю свой ламповый нетворкинг тут - https://t.me/+434aQiGpZtAyNTU6. Присоединяйтесь!

Оглавление.

Введение

На прошлых шагах, мы с вами разобрали базовые механизмы CI, которые позволили нам автоматизировать ряд рутинных операций. Самое время перейти к более сложному кейсу. Сказать откровенно – я несколько раз пытался подойти к освоению по части CD. Сложновато было найти руководство под мои нужны. А ознакомление с документацией не принесло пользы, в начале этого пути.

Поэтому, как и в прошлые разы, давайте попробуем взглянуть на задачу максимально просто. Определить – что мы хотим получить, что нам для этого нужно, и какие инструменты потребуются. 🫡

Постановка задачи.

CD Continuous delivery (непрерывная доставка). Простыми словами – деплой, публикация нашего приложения в среду эксплуатации. Если совсем просто – мы разработали приложение, и оно готово для того, чтобы отдать его на растерзание конечному пользователю. Но пока мы работаем над ним – делаем это в локальной среде, на домашнем или рабочем компьютере.

Если брать в пример наше приложение на React – оно отлично запускается в режиме разработки на localhost:3000 (после выполнения стандартной для этого фреймворка команды npm run start). И мы видим результат в браузере. Но на этом этапе – никакой другой пользователь, не сможем открыть этот сайт.

PS. В материале не будет рассматриваться вариант – открыть локальную машину наружу – приобретением белого IP адреса у провайдера, настройкой локального WEB сервера и т.д. Как правило, такой подход не оправдывает ожиданий, но справедливости ради – задачу может выполнить. Даже локальный домашний компьютер можно настроить так, что ваш сайт будет доступен с него в сети интернет. Но, как говорится, это тема совсем другой статьи.

Таким образом, мы приходим к следующим требованиям. Наше приложение должно быть доступно в сети. Чтобы любой пользователь мог открыть браузер, набрать нужный адрес, нажать Enter и увидеть наше приложение. ✅

Что потребуется

Здесь в игру вступает серверное железо. В конечно счете, нам нужно иметь в доступе машину, с «белым» (доступным в сети) ip адресом. Файлы нашего приложения на его жестком диске, и веб сервер – который будет прослушивать входящие запросы по сети, и отдавать в ответ нашу index.html страницу. У нас ведь React приложение – и все что будет уходить на клиент – это одна html страница. Плюс пара файлов – js и css, что является статикой.

Мы пойдем одним из простых путей. Для таких объемов приложения, вполне хватит VPS/VDS сервера. Если вы не знакомы с этим – компании хостинг провайдеры, предоставляют сервера в аренду. По стоимости от нескольких рублей в день, до бесконечности. Забегая вперед – под это приложения (фронтенд, бекенд, админка + БД), отлично справляется тариф стоимость порядка 20-25 рублей в сутки. Не буду советовать конкретные компании, сам пользуюсь несколькими. В общим и целом – услуги серверов в целом похожи. Если Вас интересуют какие-то специфические составляющие, тут уже выбирайте под свои задачи.

На сервере, очевидно, нам понадобится какая-то операционная система – чтобы им можно было бы пользоваться. Из хороших новостей – все серверы, в разных компаниях, с которыми мне довелось поработать, предоставляют очень удобный интерфейс базовой настройки. После регистрации и выбора тарифа – операционную систему можно установить в пару кликов. Вот пример с текущего проекта.

Выбираем операционную систему для нашего сервера.
Выбираем операционную систему для нашего сервера.

Если нет специфических требований, я обычно использую Ubuntu последней версии. Просто больше практического опыта с ней, и подходит для большинства задач. Опять же – вариант ОС нас здесь особо не интересует. Если у Вас появляется специфические задачи – выбираем ось под них.

Также вас попросят либо установить пароль администратора сервера (как правило root), либо задать свой. И где-то в настройках, либо в письме на почту – дадут данные для подключения – ip адрес сервера, имя учетной записи администратора и пароль. Это то, с чего мы начинаем в плане железа.🆗

PS. В случае с корпоративной средой – настройки, разумеется, будут отличаться. Но общая идея – что у нас должен быть в наличии боевой сервер, со всем установленным ПО, и файлами приложения – особо отличатся не будет. Да, он может запускать наш софт в рамках докер контейнера. Быть виртуальным или физическим. Но в общем и целом – это целевая машина (или кластер), до которой можно обратиться из вне по HTTP/HTTPS протоколу, и получить в ответ нужную информацию.

Касательно средств CD – здесь мы также будем использовать GitLab CI и дописывать наш pipeline, с прошлых частей. Из новых инструментов здесь добавиться работа с переменными окружения. Но об этом мы поговорим на соответствующем шаге.

И важный момент – нужно понять, что мы подразумеваем под процессом деплоя. Опять же – здесь все очень сильно будет разниться от приложения, языка и стека, на котором вы работаете. Вам нужно понять – что является фактом запущенного приложения. Это может быть запуск исполняемого файла. Или раздача собранных файлов веб сервером – как будет в нашем случае.

Здесь давайте остановимся чуть более подробнее. Чтобы наше приложение можно было бы открыть в браузере – нам нужен веб сервер. Возьмем популярный вариант Apache. С учетом того, что приложение маленькое – дев и продуктовую версию будем держать в рамках одной машины. Договоримся, что дев – который мы рассматриваем в нашем примере – повесим на 10001 порт. Общая структура url будет следующая

http://<ip-адрес>:10001/admin

Мы не будем на этом этапе подключать https и доменное имя – в нашем случае для приложения в режиме разработки этого не требуется. Для продуктовой версии это будет несложно сделать – внеся нужные правки в конфигурацию Apache. Плюс часть в адресе приложение из примера – админ панель. Она будет доступа по этому префиксу.

/admin

И важный момент по самому приложению. Чего мы хотим добиться? CD – средство непрерывной доставки кода. Когда в develop ветке нашего приложения, а именно с нее мы будем собирать дев стенд – будут появляются изменения – мы хотим, чтобы на сервере они сразу были доступны. Таким образом – нам нужно, чтобы файлы с изменениями, которые хранятся на Gitlab (или в любом другом хранилище кода) – попадали на наш сервер. И приложение актуализировалось – проще говоря, происходила пересборка React приложения – и у нас появлялся актуальный билд. Который раздавался через Apache.✅

PS. Здесь хотелось бы отменить, реальную пользу автоматизации, которую я испытал на себе, настроив этот процесс впервые. Представьте себе ситуацию активной разработки, или исправлению багов. У себя на компьютере – открыть файл, исправить пару символов. Казалось бы – недолго. Сделать коммит и запушить. Тоже терпимо. А теперь нужно подключиться к удаленному серверу. Зайти в папку с проектом, сделать git clone. Запустить пересборку React. Кто делал – тот поймет.

Промежуточные результаты.

В абзаце выше, уже вполне сформировались шаги, которые мы сможем автоматизировать. Но часть настроек нужно будет сделать вручную. Давайте по пунктам

  • Заполучить себе в распоряжение боевой сервер, с установленной ОС (Ubuntu).

  • Установить на него NodeJS и npm.

  • Установить на него вебсервер Apache.

  • Выбрать место хранения файлов приложения. Давайте остановимся на директории

    /var/www/development/admin

    Это будет корневая папка нашего проекта (читай – корень репозитория)

  • Собрать билд React приложения в папку dist, из develop ветки.

  • Настроить Apache на раздачу файлов приложения из папки dist, по нашему айпишнику и префиксу /admin. Плюс позаботится, чтобы он также отдавал файлы js/css.

Эта первичная настройка нужна, во-первых, для проверки работоспособности, во-вторых – для подготовки основных инструментов.

Так что же мы автоматизируем? Все следующие изменения – читай, коммиты и пуллревесты в develop ветку, будут приводить к следующему. На сервере будет актуализировать develop ветка – мы будем пулить свежие изменения из гита. И пересобирать билд. Apache прекрасно будет работать без перезапуска – он просто будет брать свежие файлы из папки.

Таким образом – любой новый код в деве – это запуск всех проверок (мы настроили их на предыдущем шаге) – и доставка этих изменений до дев среды. Через минуту – у вас на сервере проверенный код, приложение работает – все правки в наличии.✅

PS – возможно, часть ручных действий также можно автоматизировать – установка ПО на сервер, настройка Apache и т.д. Я пока до такого не дошел. Если у вас есть практика автоматизации развертывания инфраструктуры – поделитесь в комментариях. Как это делать, и какие инструменты использовать.

Начинаем настраивать.

Давайте начнем с предварительной настройки нашего удаленного сервера. Обычно для этого к нему подключаются по SSH протоколу. В варианте с моим хостинг провайдером – у них есть терминал для подключения прямо из браузера. Но мне больше нравится работать с локальной консолью.

В версиях Unix – утилита для работы с SSH идет сразу из коробки. Тоже самое для Windows, начиная с 10-й версии.

Для подключения к серверу, наберите команду (подставив свои значения – логин и ip).

ssh <имяпользователя>@<ip.address>

Если команда ssh не отзывается, проверьте и при необходимости установите на свою машину open ssh client.

После попытки подключения – вас попросят ввести пароль. Сделайте это. Если все было сделано верно – мы увидим консоль удаленного сервера.

Успешное подключение к консоли сервера по SSH
Успешное подключение к консоли сервера по SSH

Отлично. Из списка требуемых инструментов, у нас была NodeJS + npm. В предыдущих статьях, мы разбирали, как установить их. Можно воспользоваться этим решением.

Выполняем последовательно команды.

apt update

apt install -y curl

curl -fsSL https://deb.nodesource.com/setup_22.x | bash

apt install nodejs -y

Мы обновили данные по пакетам. Установили HTTP клиент curl, и с его помощью загрузили и установили ноду. Успешность установки можно проверить двумя командами.

node -v

npm -v

Если вы видите версии приложений, значит на этом этапе все хорошо.

Успешная установка NodeJS + npm
Успешная установка NodeJS + npm

Дальше устанавливаем Git.

apt install git

Проверяем версию.

git -v

Успешная установка Git
Успешная установка Git

Теперь установим вебсервер Apache.

apt install apache2

Чтобы проверить его установку, проверим – есть ли он в списке запущенных служб.

systemctl status apache2

Если мы видим информацию о службе – значит установка прошла успешно.

Вебсервер Apache успешно запущен
Вебсервер Apache успешно запущен

Теперь давайте займемся файлами проекта. Как показывал выше – я предлагаю разместить их в каталоге.

/var/www/development/admin

/var/www – стандартный путь, который часто используют для хранения данных веб сайтов. development/admin – Это уже внутренние папки. Как говорил, на этом сервере планируется храниться продакшн версия приложения, админ панель, фронт, бекенд. Поэтому эти папки – просто для структурирования информации.

В теории, вы можете выбирать любую файловую структуру.

Переходим в var/www.

cd var/www

Создаем здесь папку development.

mkdir development

Заходим в нее, и в ней создаем папку admin.

cd development

mkdir admin

cd admin

Итак, мы в корне нашего будущего репозитория. Давайте затащим сюда файлы нашего проекта с gitlab. Не забывайте подставить адрес своего репозитория.

git clone –branch develop <your_repo_clone_url> ./

Здесь мы вытаскиваем сразу нужную develop ветку, и размещаем файлы в текущей папке.

Клонируем приложение
Клонируем приложение

Делаем установку зависимостей, и сборку приложения.

npm install

npm run build

Если все было сделано верно, мы получим собранное приложение.

Успешная сборка
Успешная сборка

Теперь вернемся к настройке Apache. Для нашей задачи воспользуемся виртуальными хостами. Если в двух словах – это механизм, позволяющий на одном сервере размещать несколько сайтов (ресурсов). Для наших нужд подойдет – мы настроим виртуальный хост для дев стенда. А в последствии, когда будет готова продакшн версия – создадим ее, попутно настроив доменное имя.

Перейдем в папку с конфигурационными файлами Apache

cd /etc/apache2/sites/available

Здесь по умолчанию 2 конфигурационных файла. Удобно под каждый проект, создавать свой конфиг. Давайте сделаем это.

touch mimoza.conf

Название любое, расширение .conf.

Файл конфигурации Apache, для нашего проекта
Файл конфигурации Apache, для нашего проекта

Открываем файл на редактирование.

nano mimoza.conf

Ниже приведен листинг настройки.

<VirtualHost *:10001>

        DocumentRoot /var/www/development/admin/dist

        <Directory /var/www/development/admin/dist>

                Options Indexes FollowSymLinks

                Require all granted

                RewriteEngine On

                RewriteCond %{REQUEST_URI} ^/admin

                RewriteRule ^admin/.*$ /index.html [L]

        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

        CustomLog ${APACHE_LOG_DIR}/access.log combined

        <FilesMatch "\.js$">

                Header set Content-Type "application/javascript"

        </FilesMatch>

        <FilesMatch "\.css$">

                Header set Content-Type "text/css"

        </FilesMatch>

</VirtualHost>

Здесь 5 важных блоков настроек

  • Мы создаем виртуальный хост на текущем ip адресе, и прослушиваем порт :10001. Теперь все подключения вида <ip>:10001 будут обслуживаться Apache. И нам нужно, чтобы он в ответ отдавал нашу index.html страницу из папки dist

  • DocumentRoot – здесь указываем корневую папку проекта, на которую прицеливается Apache.

  • <Directory> - С учетом того, что все пути в рамках <ip>:10001/admin/* должны приводить на index.html страницу, здесь мы добавляем редиректы. Это связано с особенностью SPA на React. Любой путь на сайте – будет обслуживаться средствами JS, в рамках index.html страницы. Поэтому мы редиректим все запросы на нашу корневую html страницу. Клиент получает ее, загружает – а дальше начинает работать React.

  • ErrorLog – здесь стандартные настройки логирования Apache

  • 2 директивы FilesMatch – здесь мы добавляем http заголовок с нужным типом содержимого. По умолчанию Apache отдает файлы в виде text. Указание нужного Mime type нужно для корректной работы других типов файлов.

Теперь зайдем в файл ports.conf, и добавим правило для прослушивание 10001 порта. Он находится в директории выше - /etc/apache2.

Добавляем прослушивание 10001 порта
Добавляем прослушивание 10001 порта

Активируем 2 модуля – rewrite и mime.

a2emod rewrite

a2emod mime

И перезапустим Apache, чтобы наши изменения вступили в силу.

systemctl restart apache2

Применение изменений Apache
Применение изменений Apache

Теперь если попробовать сделать запрос, например на адрес <ip>:10001/admin/login – будет отзываться наше приложение. Значит первый деплой мы сделали верно. Приложение работает, Apache отдает нашу статику.

Приложение доступно в сети
Приложение доступно в сети

Можно переходить к автоматизации. 🫡

Gitlab CD

Возвращаемся к нашему Pipeline. В интерфейсе Gitlab идем в Pipeline editor, выбираем ветку develop. Добавляем еще одну стадию – dev deploy.

Начинаем дорабатывать наш Pipeline
Начинаем дорабатывать наш Pipeline

Добавим новую Job.

dev deploy:

    stage: dev deploy

    image: ubuntu:22.04

    before_script:

        - apt update

        - apt install openssh-client -y

Указываем название и стадию. Образ выбираем Ubuntu 22. И устанавливаем openssh-client, чтобы подключаться к удаленному серверу. В моих тестах на чистой Ubuntu, клиента не было, поэтому я добавил вариант его установки.

Идея будет заключатся в том – что наш runner, будет подключаться к удаленному серверу. И на нем уже выполнять все требуемые команды. Матрица среди нас – сервера управляют серверами. 😱

Теперь нужно понять, каким образом сервера будут общаться друг с другом. Здесь мы пойдем таким же путем, как это делали локально – через ssh подключение. Но вместо пароля, будем использовать SSH ключи доступа. Чуть более надежно, и удобно, с точки зрения настройки.

Если Вы ранее не работали с парами ssh ключей, краткая теория. Мы генерируем пару ключей – открытый и закрытый ключ. Открытый хранится на сервере – закрытый на клиенте. При попытке подключиться к серверу по SSH – мы передаем закрытый ключ. Сервер проверяет его – сверяясь с открытым. Если пара корректная – значит авторизацию можно разрешить.

Давайте настроим этот процесс.

Первым делом нужно сгенерировать пару ключей. Наверняка вы локально используйте GIT. В его терминале работает команда ssh-keygen.

Запустите git cmd

GIT CMD
GIT CMD

В появившемся терминале выполните

ssh-keygen -t rsa

Вас попросят указать место хранения ключей. Можно оставить по умолчанию – нажимаем Enter

Парольную фразу можно добавить по желанию. Можно пропустить. Дважды прожимаем Enter. Получаем сгенерированную пару.

Сгенерировали новую пару SSH ключей
Сгенерировали новую пару SSH ключей

Теперь нам нужно перенести открытый ключ на клиент. На своей локальной машине, вы найдете 2 пары ключей.

Ключи на жестком диске
Ключи на жестком диске

Откройте в блокноте файл с расширением .pub. Информацию скопируйте. Теперь нам нужно перенести его на наш удаленный сервер.

Вернитесь в терминал, где мы подключены к нашему серверу.

Перейдите в домашнюю директорию текущего пользователя, в папку .ssh. Если вы настраиваете сервер с нуля, здесь нужно создать файл authorized_keys. Если вы уже добавляли пары ключей, то этот файл уже существует. У нас первый вариант. Давайте создадим файл, и вставим сюда скопированный ранее открытый ключ

nano authorized_keys

Ctrl + C наш ключ

Сохраняем файл.

Список публичных ключей на сервере
Список публичных ключей на сервере

Теперь мы можем подключаться к серверу используя команду SSH, и указывая наш ключ.

ssh -I <путь до закрытого локального ключа> <имя_пользователя>@<ip_сервера>

Налаживаем связь между серверами

Локально мы научились подключаться к удаленному серверу. Давайте сделаем тоже самое в нашем runner.

Для этого нам нужно перенести наш приватный SSH ключ в Gitlab. Идем в Settings => CI/CD => Variables. Здесь нажимаем на кнопку Add Variable.

Переходим к добавлению переменных окружения
Переходим к добавлению переменных окружения

Заполняем появившуюся форму. Выбираем тип “File”, Environments оставляем по умолчанию. Ставим флаг “Expand variable reference”. Даем любое имя (у меня это SSH_KEY). И в поле значение – копируем содержимое нашего приватного ключа. Сохраняем.

Добавляем наш SSH ключ, как переменную окружения в Gitlab
Добавляем наш SSH ключ, как переменную окружения в Gitlab

Теперь наш файл с ключом хранится внутри CI/CD инфраструктуры. И мы можем им пользоваться в рамках наших pipeline.

Мы полностью готовы.

Возвращаемся к в редактор.

Здесь добавляем строку в подготовительный скрипт

chmod 400 $SSH_KEY

Где SSH_KEY это имя нашей переменной окружения с ключей. Мы задаем ему требуемый уровень разрешений, чтобы им можно было бы воспользоваться

Устанавливаем нужные права на файл с ключом.
Устанавливаем нужные права на файл с ключом.

С учетом того, что файл со всеми jobs, у нас будет находится в одном месте, давайте выделим текущую стадию, только для dev ветки.

Это можно сделать, добавив правило следующее правило

rules:

- if: $CI_COMMIT_BRANCH == ‘develop’

Если коммит был в develop ветку, делаем текущую job.

А дальше непосредственно процесс автоматизации.

Мы подключаемся из консоли нашего runner к удаленному серверу, и выполняем все необходимые команды. Обратите внимание – в кавычках будут перечисляться команды, которые будут выполнятся уже в контексте удаленного сервера.

Чтобы легче это понять – попробуйте сделать локально. Мы подключаемся через SSH в терминале, и затем в терминале передаем команды – уже удаленной машине. Здесь суть да же.

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

- ssh -i $SSH_KEY root@<ip_address>"  // подключаем, указывая наш приватный ключ

          cd /var/www/development/admin/ && // переходим в директорию с проектом

          rm tsconfig.tsbuildinfo -f && // убираем файл, который TS генерирует при сборке

          git checkout develop && убеждаемся, что стоим на ветве develop

          git pull --force && // забираем последние изменения с репозитория

          source ~/.nvm/nvm.sh && // явно активируем внутреннюю консоль

          npm install && // запускаем установку зависимостей

          npm run build" // запускаем сборку

Полная версия выглядит следующим образом.

Итоговый Pipeline
Итоговый Pipeline

Все команды довольно понятны, кроме двух пунктов.

rm tsconfig.tsbuildinfo -f

При сборке React приложения, генерируется новый файл, с мета информацией. Полезной нагрузки в нашем случае он не несет. Но его наличие ломает команду git pull – мы не сможем забрать измененения, если есть незакомиченный файл. Поэтому здесь я добавил его явное удаление.

source ~/.nvm/nvm.sh && явный запуск консоли

На этапе тестов этого кода в раннере, я получал ошибки при выполнении команды npm install. Решение было найдено – посоветовали явно активировать консоль. Команда выше.

Давайте сохраним конфиг, и попробуем сборку в действии.

Успешная сборка
Успешная сборка

Все прекрасно собралось. На сервере появился билд с последними правками. И Apache начинает раздавать свежую версию приложения.

Теперь любые изменения в деве через пару минут будут появляться на стенде – Profit.

Итого.

Мы неплохо поработали. Настроили CI/CD – теперь все наши коммиты автоматически проверяются на соответствие правилам кода. Мы убеждаемся, что в приложении нет критических ошибок, препятствующих сборке. И происходит автоматический деплой – новая версия приложения доступна для работы, через пару минут после слияния нашего PR с dev веткой.

Я попытался рассмотреть процесс максимально просто – так, как это помогло мне. Пробуйте сформилировать – какую цель мы преследуем?. Какие инструменты у нас должны быть для этого. Какие шаги и команды мы выполняем, для достижения результата. Что нужно настроить, какие файлы добавить и т.д. чтобы процесс заработал.

Вы всегда можете тестировать шаги автоматизации локально – выполняя нужные шаги, проверяя результат. А уже потом перенося рабочие примеры в код pipeline.

Можно изменять подход и инструменты. Не получается сгенировать SSH ключ через GIT cmd - используйте другие инструменты. Не походит Ubuntu, как операционная система runner? Пробуйте альтернативы.

Данное руководство будет полезно для разработчиков на React. Но общие идеи и мысли в целом, могут помочь погрузится в процесс CI/CD.

Вам был полезен материал? Хотите что-нибудь дополнить или посоветовать? Оставляйте комментарии или присоединяйтесь ко мне в телеграм - https://t.me/+434aQiGpZtAyNTU6

Удачи!

Теги:
Хабы:
+4
Комментарии1

Публикации

Работа

DevOps инженер
30 вакансий
React разработчик
36 вакансий

Ближайшие события