Итак, в предыдущих статьях (Введение, Командуем, Дратути) мы уже разобрали многие аспекты написания бота: как начать писать бота, как делать плагины-расширения и как работать с непосредственно пользователем и создавать функционал. Однако, до сих пор мы не разобрали, как, куда и с помощью чего можно публиковать и запускать ботов в проде.

О среде исполнения и ОС в целом

В данной статье в первую очередь будут разбираться вопросы публикации из Linux окружения, поскольку даже если вы несчастливый обладатель Windows/MacOS, почти всегда непосредственно публикацию и хостинг бота вы будете проводить в Linux окружении: Github Workflows, Gitlab CI, Jenkins и т.д.

Оглавление

Читаем рецепт

Публиковаться мы будем с помощью Docker контейнеров и публикации куда-нибудь, хотя для примера возьмём официальный Docker Hub (на момент написания статьи в нем можно зарегистрироваться и публиковать свои образы). После публикации образа мы будем запускать этот образ где-то, например на VPS с Ubuntu. Также небольшой ликбез по необходимым командам в линуксе:

# Создаем папку sample
mkdir sample

# Переходим в папку sample
cd sample

# Редактируем файл file (если его нет - он создастся при сохранении)
# Перемещение по редактору происходит стрелочками и page up/page down/home/end
# Для выхода и сохранения используем последовательность `Ctrl + x`, `y`
# Для выхода без сохранения используем последовательность `Ctrl + x`, `n`
nano file

# Выходим в предыдущую папку
cd ../

Нарезаем ингредиенты

Итак, предположим, вы имеете вот такой проект. Структура его в целом вряд ли будет важна, поскольку основной задачей будет выделение некоторого модуля, который будет уметь запускать бота. Обычно для этого я создаю отдельный модуль и называю его как-то вроде runner для наглядности. Что особенного в этом модуле:

  • В build.gradle

    • В секции plugins подключен плагин application - это специальный gradle плагин для обозначения модуля как конечного приложения

    • Присутствует настройка application.mainClassName = 'dev.inmo.plagubot.AppKt', обозначающая, что мы будем запускать именно бота

  • Присутствует Dockerfile, настройка которого самодостаточна для запуска любого собираемого с помощью application плагина приложения

  • Присутствует deploy скрипт, запуска которого достаточно для публикации образа

Важные моменты:

  • FROM директива в Dockerfile спокойно может быть заменена почти любой альтернативной базовой JDK (например, bellsoft/liberica-openjdk-alpine)

  • deploy скрипт как есть работает через sudo docker , что означает, что вы авторизованы на целевом сервере с помощью sudo docker login . Это важно, потому что если ваша публикация основана на условном Github Actions , вам нужно будет убирать sudo отовсюду в скрипте, поскольку там докер авторизован и работает без sudo (nonsudo_deploy скрипт)

  • server переменная в deploy скрипте может быть либо выставлена в реальный адрес докер сервера (см. docker registry), либо это должен быть юзернейм на dockerhub, куда публикуется образ

  • app переменная, также как и version во всё том же deploy фактически произвольны и вы можете поставить туда соответствующие вашему бота названия и версию

  • При желании, вы можете даже добавить какой-то код в рамках модуля-раннера, но я строго рекомендую располагать в таком модуле только код, конфигурирующий каким-то образом запуск

Local bash

Для публикации из терминала Ubuntu, достаточно запустить deploy:

# Переходим в папку раннера
cd runner
# Деплоим
./deploy
# Всё :)

Github Actions

На своих проектах я использую вот такую настройку Github Action:

.github/workflows/docker-publish.yml
name: Docker

on: [push]

jobs:
  publishing:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Log into registry
        uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
        with:
          username: ${{ secrets.DOCKER_LOGIN }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Deploy
        run: ./gradlew build && ./nonsudo_deploy

Пример

Для проекта-примера можно адаптировать это вот так:

.github/workflows/docker-publish.yml
name: Docker

on: [push]

jobs:
  publishing:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Log into registry
        uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
        with:
          username: ${{ secrets.DOCKER_LOGIN }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      - name: Deploy
        run: ./gradlew build && cd runner && ./nonsudo_deploy

Соответственно, чтобы оно заработало, нужно в настройках репозитория установить следующие переменные окружения для экшенов (на данный момент лежат по пути Secrets and variables -> Actions):

  • DOCKER_LOGIN

  • DOCKER_PASSWORD

Тушим, жарим, вот это всё

Образ на докерхабе, теперь бы всё это великолепие как-то запустить. Для этого понадобится устройство с линуксом (VPS/VDS, домашний комп, теоретически старый телефон и желание потерять неделю своей жизни). Приглашаю вас к нам в форум в телеграме для обсуждения всяких хостингов.

Теперь для запуска нам понадобится три вещи:

  • Установленные docker и docker-compose (sudo apt install docker docker-composeна Ubuntu)

  • docker-compose.yml

  • config.json

И начнем разбирать сразу со второго пункта, потому что с первым всё более-менее понятно.

Чево-то.yml

docker-compose.yml - это файл-конфигурация для docker-compose . Полную спецификацию можно глянуть тут, но для старта мы будем использовать следующий пример:

docker-compose.yml
version: "3.4"

services:
  runner:
    image: user/runner
    container_name: runner
    restart: unless-stopped
    volumes:
      - "./data/:/data/"
      - "./config.json:/runner/config.json:ro"

А теперь разберем важные моменты в этом файле:

  • user - это пользователь/сервер, указанный в deploy скрипте в переменной server

  • runner в значении image (после user/) - это название приложения из deploy переменной app

  • При желании, после user/runner можно добавить суффикс :версия , где версия - значение переменной version из deploy скрипта

  • volumes - секция с монтируемыми папками и файлами. Тут слева (до : ) - значение из системы, откуда запускается docker-compose.yml, а справа - где папка/файл будут в контейнере

    • ./data/:/data/ - монтирование папки данных, в которую можно складывать данные между сессиями. Например, по умолчанию, именно туда попадёт файл local.db с базой данных, если не менять секцию database в config.json

    • ./config.json:/config.json:ro - конфигурация бота. Пример есть в config.json

config.json

Как ни странно, этот файл содержит конфигурацию бота. В нашем случае, для примера вам нужно будет поменять только поле botToken - установить туда токен, полученный от BotFather.

Щепотка соли

Таким образом, на сервере у вас должна получиться некоторая папка (например, runner), в которой есть следующие файлы/папки:

  • docker-compose.yml - конфигурация для запуска с помощью docker-compose

  • config.json - конфигурация бота

  • data - папка с данными бота

Пробуем на вкус

Теперь всё это великолепие можно запустить одной простой командой:

sudo docker-compose up -d

Когда вам скажут, что контейнер был создан, бот должен начать отвечать на ваши действия.

Небольшой список полезных команд docker-compose

У docker-compose, как и у любого инструмента Linux есть огромная куча параметров. Как и у любого инструмента Linux, у docker-compose я использую ограниченное число команд и параметров просто в силу их достаточности:

  • sudo docker-compose up -d - запустить контейнер в режиме демона, то есть с автоперезапусками и прочим таким. Если убрать -d, то вы автоматически подключитесь к логам бота, а по ctrl + C - останосите контейнер и отключитесь от него

  • sudo docker-compose pull - стянет последнюю версию используемого образа

  • sudo docker-compose logs -f --tail=100 - привяжется к выводу бота с выводом последних 100 строк логов. По ctrl + C бот не остановится, но от логов вы отключитесь

  • sudo docker-compose down - собственно, остановка и удаление контейнера. Будьте аккуратны с этой командой, поскольку она затрёт все данные, не находящиеся в папках из секции volumes

  • sudo docker-compose stop - (наравне с start и restart) - останавливает работу контейнера (начинает/перезапускает соответственно

Разливаем по тарелкам

Теперь вам остаётся только экспериментировать с тем, что вам предложено в этом туториале, читать кучу макулатуры документации, всё ломать и потом восстанавливать. В общем, искренне желаю вам удачи и не забывать делать бэкапы - они позволят проклюнуться седине позже.

Комментарии (8)