Итак, в предыдущих статьях (Введение, Командуем, Дратути) мы уже разобрали многие аспекты написания бота: как начать писать бота, как делать плагины-расширения и как работать с непосредственно пользователем и создавать функционал. Однако, до сих пор мы не разобрали, как, куда и с помощью чего можно публиковать и запускать ботов в проде.
О среде исполнения и ОС в целом
В данной статье в первую очередь будут разбираться вопросы публикации из 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) - останавливает работу контейнера (начинает/перезапускает соответственно
Разливаем по тарелкам
Теперь вам остаётся только экспериментировать с тем, что вам предложено в этом туториале, читать кучу макулатуры документации, всё ломать и потом восстанавливать. В общем, искренне желаю вам удачи и не забывать делать бэкапы - они позволят проклюнуться седине позже.
 
          