Как автоматизировать деплой Telegram-бота на сервер

Как автоматизировать деплой Telegram-бота на сервер

В инструкции рассказываем, как настроить репозиторий и workflow для автоматизации деплоя с помощью GitHub Actions.

Введение

Привет! Меня зовут Арсений Помазков. Я — разработчик и создатель одноименного YouTube-канала. Часто в pet-проектах приходится вручную загружать обновления на сервер. Это отнимает много времени и увеличивает вероятность ошибок при изменении кода. Чтобы упростить и ускорить процесс развертывания Telegram-бота, настроим автоматический деплой с помощью GitHub Actions. 

Прежде всего вам понадобится Telegram-бот на любом языке. Я буду использовать пример из предыдущей инструкции о библиотеке Grammy JS. Вы можете создать собственный или использовать с GitHub — все ссылки оставлю в тексте. 

Подготовка бота

Чтобы использовать готовый код с GitHub, нужно убедиться, что на вашем компьютере установлен Git. Открываем командную строку и вводим команду:


    git -v

Если видите актуальную версию Git, программа добавлена. В противном случае — скачиваете файл с официального сайта и устанавливаете. 

Далее переходим в репозиторий, нажимаем на кнопку Code, выбираем HTTPS и копируем ссылку. В командной строке открываем папку с проектами и вводим команду: 


    git clone https://github.com/arseniypom/bot-automatization.git

В результате появится новая папка. Перетаскиваем ее в редактор кода и открываем командную строку, чтобы установить зависимости:


    npm i

Внутри проекта находятся:

  • node_modules — папка с файлами зависимостей;
  • .gitignore — список файлов, которые мы не хотим заливать в Git;
  • index.js — файл с кодом нашего бота;
  • package-lock и package.json — список зависимостей.

В прошлой инструкции я рассказывал, как создать Telegram-бота с помощью Grammy JS, поэтому не буду подробно на нем останавливаться. Кратко пройдемся по основным шагам. 

Создаем в проекте файл .env и добавляем в него переменную BOT_API_KEY:


    BOT_API_KEY=0123456789:GHF3uhO16fM1Zqn7kyGIiYLuDSfDaNbr8mQ

Переменную можно назвать как угодно. Но поскольку в моем коде уже используется index.js, беру его. 

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

Проверим, что все работает: 


    npm start

Переходим в Telegram по ссылке от @BotFather. Здесь простая механика: бот отвечает на команду /start и /menu, чтобы пользователь мог узнать статус заказа и обратиться в поддержку. 

Тестовая версия Telegram-бота. 
Тестовая версия Telegram-бота. 

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

Настройка GitHub-репозитория

Итак, у нас есть рабочий бот. Загрузим его в свой репозиторий на GitHub. Если вы уже это сделали, переходите к следующему разделу. Если нет, пройдем весь процесс вместе. 

Создаем новый репозиторий и указываем имя — в нашем случае bot-automatization-final. После — копируем HTTPS-ссылку:


    https://github.com/arseniypom/bot-automatization-final.git

Переходим обратно в редактор. Поскольку мы скопировали бота из моего репозитория, у него уже есть связь с удаленным. Это можно проверить, введя команду:


    git remote

В ответе видим название origin:

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Теперь если введем команду git remote show origin, получим адрес этого репозитория.


    git remote show origin
Отрывок кода в редакторе.
Отрывок кода в редакторе.

Далее используем команду git remote set-url origin и добавляем скопированную ссылку на только что созданный репозиторий:


    git remote set-url origin https://github.com/arseniypom/bot-automatization-final.git

Перепроверяем результат командой git remote show origin. В ответе видим новый адрес:

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Заливаем код на новый репозиторий:


    git push origin main

Готово! Приступаем к деплою бота. 

Деплой бота на сервер

Настройка сервера

Приступим к настройке сервера с помощью Github Actions. Для этого используем облачный сервер Selectel. Далее буду описывать весь процесс на его примере. 

Переходим в панель управления, авторизуемся или создаем аккаунт, если его нет. В левой секции выбираем Облачная платформа, регион Санкт-Петербург и пул ru-3. Нажимаем Создать сервер.  

Указываем имя сервера как в репозитории и вводим нужные характеристики. 

  • Пул: ru-3b.
  • Источник: любой дистрибутив Linux — Ubuntu, Debian, CentOS, Fedora и другие. В нашем случае — Ubuntu 64 512 Мб.
  • Конфигурация: Shared. Итоговая конфигурация стоит от 10 ₽/день или около 300 ₽/месяц и подходит для небольших pet-проектов — например, Telegram-ботов и других. 

В Shared Line можно арендовать не весь сервер, а его часть — 10, 20 или 50%. Выбираем 10% и наименьшую конфигурацию оперативки. Если получится так, что на сервере нет других арендаторов, все 100% мощности сервера переходят нам без доплат.

  • Диск: базовый SSD на 5 ГБ.
  • Сеть: новая публичная подсеть.

Еще один способ сэкономить на инфраструктуре — взять прерываемый облачный сервер. Он работает не более 24 часов, поэтому отлично подойдет для краткосрочных проектов. Стоимость ресурсов у такого сервера гораздо ниже.

Прерываемый сервер в конфигураторе.
Прерываемый сервер в конфигураторе.

При входе в консоль данные для входа отправляются автоматически, поэтому не настраиваем SSH-ключи и пароль. Нажимаем на кнопку Создать.

На странице нам открывается список всех серверов и их статус. Ждем, пока запуститься сервер. Как только статус переведется в Active, переходим к новому серверу. В открывшейся панели видим несколько вкладок и настройки, с помощью которых можно отключить или заморозить сервер. Дополнительно есть кнопка перезагрузки и другие опции.

Подключение к серверу

Ниже выбираем Консоль, вводим логин root и пароль. Настроить сервер можно прямо в консоли панели управления или через терминал компьютера по SSH. Будем использовать второй вариант. 

Чтобы подключиться к компьютеру, нужно получить данные сервера. Пароль уже видели во вкладке Консоль, а IP — во вкладке Порты.

Раздел «Порты» в настройках сервера.
Раздел «Порты» в настройках сервера.

Сперва копируем IP-адрес. В терминале на Mac или командной строке на Windows вводим команду в формате ssh <имя пользователя>@<IP-адрес>. В нашем случае — ssh root@185.10.187.10

После терминал отправляет вопрос: «Хотите ли вы продолжить подключение?» Изначально SSH-клиент не может проверить подлинность сервера, так как его ключ еще не сохранен на вашем компьютере. Пишем «yes», и при следующих попытках входа вопрос не появится.

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Далее будет предложено ввести пароль. Копируем его из панели управления в разделе Консоль и вводим. Видим такой результат:

Отрывок кода в редакторе.

Снизу видим имя пользователя (root) и название сервера. Теперь можно настроить сервер для деплоя!

Деплой бота

Обновляем список пакетов в системе с помощью команды и устанавливаем нужные пакеты: git для работы с гитом, node.js и npm для запуска нашего приложения:


    sudo apt install git nodejs npm

В ответе видим вопрос:


    After this operation, 92.1 MB of additional disk space will be used.
Do you want to continue? [Y/n]

Отвечаем Y и нажимаем Enter. Убеждаемся, что все корректно работает:


    node -v 
npm -v

В результате видим рядом с каждой командой ее версию: 

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Система указывает 12 версию ноды, хотя актуальная на момент подготовки статьи – 20. Все потому, что версии этих пакетов по умолчанию старые. Нужно поставить пакет n и с его помощью самостоятельно установить стабильную версию. 


    sudo npm install -g n
sudo n stable

Повторно проверяем версию node с помощью node -v. Если все еще показывается старая версия, перезагружаем сервер. Это можно сделать кнопкой в правом верхнем углу или командой reboot прямо в консоли. 

Раздел «Консоль» в настройках сервера.
Раздел «Консоль» в настройках сервера.

Если выбрали перезагрузку через консоль, соединение будет разорвано. Нужно подождать пару минут, чтобы снова подключиться к серверу.

После ожидания вводим данные для входа и перепроверяем версии. Теперь они актуальные: 

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Загружаем менеджер процессов pm2, с помощью которого будем запускать бота. Устанавливаем его глобально — на это указывает флаг -g:


    npm i pm2 -g

Далее клонируем репозиторий с ботом на сервер в формате git clone <ссылка на GitHub-репозиторий>:


    git clone https://github.com/arseniypom/bot-automatization-final.git

После клонирование на сервере появляется папка с названием репозитория —заходим в нее:


    cd bot-automatization-final

Для запуска осталось установить зависимости и создать файл .env с ключом бота. Последний включен в файл, поэтому отсутствует на сервере.

Устанавливаем зависимости:


    npm i

Создаем файл .env и открываем с помощью редактора nano:


    nano .env

После открытия редактора добавляем в него содержимое .env из проекта на компьютере:

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Используем сочетанием клавиш Ctrl+O, затем Enter и Ctrl+X, чтобы сохранить прежнее имя файла и выйти из редактора.

Запускаем бота с помощью pm2. Не забудьте перед этим убедиться, что остановили его локально, иначе возникнут конфликты.


    pm2 start index.js --name tg-bot

В консоли видим сообщение об успешном запуске:

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Автоматизация деплоя 

Для настройки деплоя используем GitHub Actions — встроенную в GitHub систему автоматизации, которая позволяет создавать и запускать рабочие процессы (workflow) для сборки, тестирования, деплоя и других задач. Запускаются они автоматически в ответ на события в репозитории — например, при пуше кода или создании pull request.

Возвращаемся в GitHub-репозиторий и переходим на вкладку Actions. На странице предложены уже готовые workflow — нам нужно создать собственный. Нажимаем на set up a workflow yourself.

Страницы GitHub Actions.
Страницы GitHub Actions.

В открывшийся редактор вставляем код:


    name: Node.js CD # Название workflow (процесса автоматизации)

on:
  push:
    branches: [ main ] # Триггер: запускать workflow при пуше в ветку main

jobs:
  build:
    runs-on: ubuntu-latest # Определение окружения: используется последняя версия Ubuntu

        steps:
    - name: Deploy using ssh # Название шага: Деплой с использованием SSH
      uses: appleboy/ssh-action@master # Использование готового действия для SSH-подключения
      with:
        host: ${{ secrets.HOST }} # Хост (сервер) для подключения, берется из секретов Github
        username: ${{ secrets.USERNAME }} # Имя пользователя для SSH, берется из секретов Github
        key: ${{ secrets.PRIVATE_KEY }} # Приватный ключ для SSH, берется из секретов Github
        port: 22 # Порт для SSH-подключения (по умолчанию 22)
        script: |
          cd ~/bot-automatization-final # Переход в директорию с проектом на сервере
          git pull origin main # Вытягивание последних изменений из ветки main
          git status # Проверка состояния git-репозитория
          npm install --only=prod # Установка только продакшн-зависимостей
          pm2 restart tg-bot # Перезапуск процесса tg-bot с помощью PM2

Нажимаем на кнопку Commit changes… в правом верхнем углу. Повторяем действие в открывшемся окне.

Страницы нового workflow.
Страницы нового workflow.

В результате появится новый файл в репозитории с новым workflow ./github/workflows/main.yml

Переходим в консоль, чтобы сгенерировать SSH-ключ на сервере:


    ssh-keygen -t rsa -b 4096 -m PEM -C "github-actions-bot-automatization”

В ответ на вопрос о названии файла нажимаем Enter и оставляем название по умолчанию:


    Enter file in which to save the key (/root/.ssh/id_rsa):

Повторяем действие на вопрос о секретной фразе и ее повторе – оставляем пустыми. Нажимаем Enter.


    Enter passphrase (empty for no passphrase):

Enter same passphrase again:

Получаем сообщение о создании ключа:

Отрывок кода в редакторе.
Отрывок кода в редакторе.

В итоге система автоматически сгенерировала два ключа: публичный и приватный. Они находятся в папке ~/.ssh.

Чтобы вывести публичный ключ в консоль, вводим команду:


    cat ~/.ssh/id_rsa.pub

Ключ нужно скопировать и добавить в файл с authorized_keys:


    nano  ~/.ssh/authorized_keys

Добавляем публичный SSH-ключ и сохраняем сочетанием клавиш Ctrl+O, Enter и Ctrl+X

Теперь уберем приватный SSH-ключ, чтобы добавить его в переменные окружения на GitHub:


    cat ~/.ssh/id_rsa

Копируем значение вместе с фразами в начале и конце:


    -----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEA1KcpcbZPKPyR8KGSo3qPJJ3IeTmkQxC1gohVyhl3R9h/KetB
CiczYsP0xnlM3ZMztfZYSqMNJy9811TDtT1s6I5sb1UJ9nz52T2qQPDBcSyUVwR5
...
wvT6Hy+I/5b4L89IkWcpSQcYg+SyBxMMVLliHDARfXmw5+jbFaG01854tOkvjD81
vJthoKSIwKgrAojmgiOF53/H+mHMUk4ckwRMsSJJuqEESBtxDzh7vmK0dWU=
-----END RSA PRIVATE KEY-----

Далее добавляем в GitHub. Для этого заходим в SettingsSecretsActions и нажимаем New repository secret.

Страница Actions в GitHub.
Страница Actions в GitHub.

Для ранее созданного workflow нужно три секретных ключа: PRIVATE_KEY, HOST и USERNAME. В блок Имя вписываем первую переменную PRIVATE_KEY, а в значение — приватный SSH-ключ. Нажимаем Add secret

Окно создания секретного ключа.
Окно создания секретного ключа.

Также создаем оставшиеся переменные. В HOST помещаем IP-адрес сервера, а в USERNAMEroot. Будьте внимательны, опечатки могут нарушить работу workflow. 

Окно с тремя секретными ключами.
Окно с тремя секретными ключами.

Чтобы проверить бота, нужно внести изменения в код и запушить их в репозиторий.

Важно: перед тем, как использовать локальный репозиторий, необходимо подтянуть обновления из удаленного. Именно в нем создали новый файл с workflow — main.yml.

Обновляем репозиторий с помощью команды:


    git pull origin main

Переходим к тестированию. Дописываем пару слов в приветственное сообщение:

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Далее заливаем обновления на GitHub. Готово!

Отрывок кода в редакторе.
Отрывок кода в редакторе.

Заключение

Пример работы Telegram-бота.
Пример работы Telegram-бота.

Теперь если зайти во вкладку Actions на GitHub, мы увидим запуски workflow.

Раздел Actions в GitHub.
Раздел Actions в GitHub.

Система выполнила первый workflow сразу после создания файла main.yml. На момент создания мы «не добили» нужные секреты, поэтому они находятся в статусе Failed.

Второй запуск успешный. Произошел тогда, когда мы запушили изменения в ветку main из локального репозитория. Также workflow сработает, если добавим merge в главную ветку. На этом процесс автоматизации завершен!

Автор: Арсений Помазков, создатель YouTube-канала.