Будуємо телеграм чат-бот на Java: від ідеї до деплою. Частина 2
Усі статті, обговорення, новини для початківців — в одному місці. Підписуйтеся на телеграм-канал!
Всім привіт! У попередній частині ми побудували простого чат-бота на Java.
У цій статті зробимо його доступним 24/7. Для цього задеплоїмо його на платформу Heroku, а також налаштуємо мінімальний процес CI/CD, використовуючи BitBucket.
Коли я писав вступ до цієї статті, то зрозумів, що вона також буде корисною і тим, кому цікаво як можна досить швидко і просто задеплоїти будь-яку Java-аплікацію на віддалений сервер.
Вважаю спосіб з Heroku найпростішим з відомих мені, який не вимагає багато знать по клауду, все максимально просто. На Heroku можна деплоїти і аплікації написані на Python, Node.js, PHP, і т. д. (загальний список можна знайти тут). Великим плюсом використання Heroku є дуже хороша і якісна документація і гуглиться все також дуже легко.
Стаття складатиметься з двох головних частин — це налаштування Heroku і конфігурація СI/CD з використанням BitBucket.
Налаштовуємо Heroku
1. Реєструємося на Heroku.
2. Додаємо банківську карточку до акаунту
Додавання банківської карточки дозволить збільшити безкоштовні ліміти, але можете поки що пропустити цей крок, ми повернемося до нього пізніше.
3. Заходимо на Dashboard → New → Create new app.
4. Додаємо назву (наприклад, volunteer-dou-bot). У полі «Регіон» рекомендую залишити «United states», оскільки він дає ширші можливості в межах безкоштовних лімітів.
5. Тиснемо «Сreate app».
6. Тепер підключимо логер. Заходимо на вкладку «Resources» і в пошуку обираємо Papertail. Як на мене, це один із найзручніших агрегаторів для візуального відображення логів для Heroku, він має також непоганий безкоштовний тариф.
Цей плагін також дає круті можливості щодо створення алертів, візуального фільтрування, гнучкого пошуку логів. Безкоштовний план має певні обмеження, оскільки дозволяє відображати логи лише за останні два дні, проте для невеликих проєктів цього буде цілком достатньо.
7. Додаємо в корінь проекту два конфігураційні файли (використовуємо проєкт з попередньої статті), ці файли можна знайти в GitHub-репозиторії в окремій гілці — «deployable»).
system.properties:
java.runtime.version=11
Procfile
web: java -Dserver.port=$PORT $JAVA_OPTS -jar target/*.jar
Якщо з system.properties
все зрозуміло, то Procfile
використовується для того, щоб задати startup-команду. Цей файл також задає тип процесу для Dyno (Dyno — це свого роду контейнер в термінах Heroku).
Також обов’язково додамо у pom.xml
плагін для створення виконуваного jar-файлу:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
Налаштовуємо процес деплойменту
Варіант, який пропонує Heroku з використанням CLI & Git
1. На вкладці «Deploy» є інформація про те, як це можна зробити за допомогою Heroku cli.
Тобто ідея наступна: Heroku на кожну аплікацію створює новий репозиторій, і вам достатньо зробити пуш у цей репозиторій, щоб запустити автоматичний деплой.
2. Виконуємо наступні кроки, за прикладом.
Зверніть увагу: цей і наступний кроки можна буде автоматизувати за допомогою BitBucket pipelines і тим самим не встановлювати Heroku CLI. Детальніше описано в наступному розділі, проте навіть якщо ви плануєте використовувати BitBucket pipelines, все одно ознайомтеся з цим і наступним кроком для розуміння процесу.
3. Після виконання пушу в репозиторій, Heroku використає файлики, які ми додали перед цим (Procfile, system.properties), і запустить аплікацію на віддаленому сервері.
У логах ви побачите, що запуститься побудова проєкту на сервері Heroku.
Зверніть увагу на версію JDK, яка виведеться у консолі. Вона повинна співпадати з тією версією, яку ми перед цим прописали y «system.properties».
4. Перевіряємо логи, і якщо все успішно — значить аплікація задеплоїлася на Heroku.
Поширені помилки на цьому етапі
Помилка «invalid target release: 11»
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project volunteer-bot: Fatal error compiling: invalid target release: 11 -> [Help 1]
Якщо у вас виникає дана помилка при виконанні пушу в репозиторій, то скоріш за все, у логах ви побачите наступне (це означатиме, що Heroku не зумів знайти system.properties і за замовчуванням використовуватиме Java 8).
Щоб виправити це, необхідно звернути увагу на гілку, на якій ви знаходитеся. А якщо ви локально не на гілці «master», то потрібно виконати команду:
git push heroku your_branch_name:master
або
git push heroku HEAD:master
Помилка «failed to push some refs to»
Щоб виправити цю помилку, виконуємо «force push» (це дозволить перезаписати історію і виконувати деплой з різних гілок з різною історією комітів, в тому числі ми зможемо заливати попередні версії аплікації).
git push --force heroku master
Помилка «unable to find jar», «unable to detect main class».
Щоб виправити її, перевірте, чи ви не забули додати у «pom.xml» плагін «spring-boot-maven-plugin» (який необхідно було додати на одному з попередніх етапів).
5. Переходимо далі. Після успішого виконання попередніх команд, на сторінці аплікації в Heroku ми побачимо що деплой відбувся.
І на вкладці «Resources» з’явиться інформація про контейнер (необхідно перезавантажити сторінку і зачекати
- Зверніть увагу на команду на скріншоті вище (її ми прописували у «Procfile»).
- Також за допомогою перемикача ми можемо запускати/зупиняти аплікацію, коли необхідно.
6. Переходимо у логер і перевіряємо, чи не виникло помилок при старті.
7. Якщо ви налаштували так, щоб bot.token, bot.username
передавалися як Environment variables
, то необхідно додати їх на Heroku.
Це можна зробити на вкладці Settings → Config vars.
Cюди додамо bot token, bot username
, а також вкажемо часовий пояс «TZ» (це важливо для коректного відображення часу в логах, і загалом, щоб працювати з очікуваним часом).
8. Якщо все прописали правильно, то мої вітання, ви задеплоїли чат-бота на сервер і він готовий вести діалог з користувачем 😉
Автоматизуємо кроки 2 і 3 з використанням BitBucket pipelines
Для цього підключаємо СI/CD від BitBucket — «BitBucket pipelines». Плюс цього варіанту в тому, що потім можна буде запускати деплой простим натисканням однієї кнопки, при цьому використовуватимемо ту ж ідею з пушем в репозиторій Heroku. Конфігурація, яку ми додамо для цього, буде мінімальною.
1. Створюємо репозиторій на BitBucket і заливаємо туди проєкт.
2. В корінь проєкту (там де лежить pom.xml) додаємо файл.
bitbucket-pipelines.yml
#!yml image: maven:3.6.1 pipelines: custom: deploy: - step: script: - git push --force https://heroku:[email protected]/$HEROKU_APP_NAME.git HEAD:master
- Зверніть увагу на поле script: воно містить команду, яка виконає пуш в репозиторій Heroku.
- Також у цьому скрипті використовуються змінні
HEROKU_APP_NAME
іHEROKU_API_KEY
, в наступних кроках ми додамо їх до налаштувань репозиторію.
3. В налаштуваннях репозиторію на BitBucket включаємо пайплайни:
Repository settings → Pipelines → Settings → Enable pipelines
4. Також необхідно додати змінні репозиторію HEROKU_APP_NAME
і HEROKU_API_KEY
в Repository settings → Pipelines → Repository variables.
- Heroku token можна отримати в налаштуваннях аккаунту на Heroku.
- App name — це назва, яку ми дали аплікації на етапі її створення на Heroku.
5. Після цього на вкладці «Branches» в контекстному меню має з’явитися пункт «Run pipeline for branch», який дозволить запустити пайплайн.
Зверніть увагу на ліміти bitbucket pipelines — не більше ніж 50 хв. на місяць. Але цього буде достатньо для більшості цілей.
6. Після запуску пайплайну, статус можна буде переглянути на відповідній вкладці у BitBucket.
Додаткові важливі налаштування Heroku
Засинаючі аплікації
Безкоштовні аплікації на Heroku засинають, якщо вони не отримують запитів протягом 30 хв. (саме веб-запитів через REST ендпоінти).
Якщо повернутися трохи вище до додавання Procfile, то побачимо, що там ми задали наступне налаштування (зверніть увагу, що воно починається з «web»):
web: java -Dserver.port=$PORT $JAVA_OPTS -jar target/*.jar
А оскільки аплікація на безкоштовному плані, вона засинатиме і чатбот не зможе відповідати на повідомлення.
Щоб аплікація не засинала, будемо її час від часу «пінгати». Це можна зробити через сторонні health-check сервіси, або ж використати наступний варіант з Heroku Scheduler.
Для цього додамо до аплікації spring-boot-starter-actuator
, який додасть ендпоінт /actuator/health
. Не забуваємо також додати spring-boot-starter-web
.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Далі переходимо в Heroku і додаємо плагін «Heroku Scheduler».
В налаштуваннях Scheduler задаємо команду:
curl -X GET https://<app-name>.herokuapp.com/actuator/health
Автоматичне перезавантаження Dyno
Heroku автоматично перезавантажує контейнери хоча б раз на день (це стосується контенерів усіх видів, платних у тому числі), але наперед невідомо, о котрій годині це відбудеться. І хоча перезапуск це, по суті, досить швидка дія, проте можуть виникнути проблеми, якщо ви використовуєте заплановані або відкладені завдання в аплікації. В цьому разі може виникнути ситуація коли неочікуваний рестарт помішає логіці роботи аплікації.
Проте ми можемо уникнути цього і вручну перезавантажити контейнер в зручний і очікуваний для нас час, і тоді Heroku не буде їх чіпати.
Використовуючи Heroku Scheduler, додамо нову команду, що автоматично «вимикатиме» контейнер, і після цього контейнер зразу ж перезапуститься.
curl -X DELETE https://api.heroku.com/apps/volunteer-dou-bot/dynos -H "Content-Type:application/json" -H "Accept: application/vnd.heroku+json; version=3" -H "Authorization: Bearer <token>"
- У команді вказуємо правильний url з назвою аплікації, а також API-токен, який можна отримати тут: Heroku → Manage account → Applications → Authorizations
- Також вказуємо час, коли ми хочемо перезапустити аплікацію.
Підключення бази даних на Heroku
Якщо необхідно підключити базу даних, то наприклад, для PostgreSQL додаємо плагін «Heroku Postgres» — він автоматично дозволить створити базу даних і приєднати її до аплікації.
Якщо ви використовуєте Spring Boot, то його автоконфігурація разом з автоконфігурацією Heroku зробить свою магію і автоматично підключить аплікацію до цієї бази даних (детальніше тут).
Зверніть увагу на дата-сервіси, які підтримує Heroku. Щодо баз даних, то найбільшу підтримку і можливості вони надають саме для Postgres.
Також зверніть увагу на ліміти безкоштовного плану для бази даних. Якщо коротко, то це:
- Максимум 10 000 записів на всю базу даних.
- Максимум 1GB даних.
Проте в більшості випадків вам буде достатньо і цих лімітів.
Зупинка контейнера
Зупинити аплікацію можна на вкладці Resources.
Перезапуск аплікації
Перезапустити аплікацію можна в меню «more → restart all dynos».
Інформація про Dyno-hours і ліміти
З поточними лімітами можна ознайомитися на вкладі Billing →Free dyno usage.
При додаванні банківської карточки, Heroku дозволяє безкоштовно збільшити ці ліміти до 1000 dyno hour.
Що таке «dyno hour»?
Використання Heroku обчислюється в годинах, протягом яких аплікація запущена. Тобто якщо ви запустили аплікацію о 15:00 і зупинили о 19:30, це означатиме, що ви використали 4.5 dyno hours.
Якщо ми хочемо, щоб аплікація була доступна 24/7, то на місяць нам потрібно мінімум 31*24 = 744 dyno-hour.
Поради щодо деплойменту і розробки
При розробці чат-ботів зручно створювати окремі енвайрменти, і окремих ботів:
- наприклад, для dev цілей — створити бота — volunteer_dev_bot і створити додаткову аплікацію на Heroku під цього бота;
- а для того щоб локально працювати — створити бота — volunteer_local_bot.
Тут є важливий момент, Telegram API не дозволяє одночасну роботу двох екзеплярів одного і того ж чат-бота. Тобто в конкретний момент часу може бути запущений лише один «слухач запитів». Інакше отримуватимемо помилки такого плану:
Error getting updates: [409] Conflict: terminated by other getUpdates request; make sure that only one bot instance is running
Налаштування логера Papertrail
Якщо необхідно налаштувати часову зону...
... це можна зробити в «Settings → Profile».
Вимкнути показ дати і часу і додатково налаштувати відображення логів можна тут:
Alerts в Papertrail
На вкладці «Alerts» у головному вікні Papertrail можна налаштувати сповіщення на пошту/Slack і т д. у разі появи логів, які містять певний текст.
Наприклад, можна налаштувати, щоб у разі появи «Exception» чи «Error» ви отримували емейл. На цьому прикладі — Papertrail моніторитиме запит щохвилини і у випадку появи цих логів, відправить емейл отримувачам.
Переривання зв’язку з сервером телеграму
Час від часу (раз у день або раз у кілька днів) можуть з’являтися переривання у інтернет-з’єднанні на Heroku, тому ви можете отримувати схожі помилки від телеграму в логах, як на скріншоті з емейлу вище. Ми їх отримуємо, бо використовуємо Long polling механізм для отримання повідомлень від телеграму, який означає те, що ми тримаємо постійний зв’язок з телеграмом і будь-яке переривання інтернету буде одразу ж помітно у логах.
Проте оскільки перебої тривають лише
Висновки
Отож, у цій статті ми побачили, як можна швидко налаштувати віддалений сервер, цим самим зробивши чат-бота доступним 24/7. Звісно, прийшлося зробити додаткові налаштування для роботи з лімітами, проте в більшості випадків цих лімітів для роботи чат-бота буде достатньо. А якщо не вистачатиме ресурсів, тоді можна розглянути переїзд на інші клауд-платформи, такі як AWS (але які вже вимагатимуть більше налаштувань), або ж використовувати платні плани від Heroku.
Код проєкту разом з файлами налаштуваннь Heroku і BitBucket можна знайти тут.
Думаю, стаття була корисною, залишайте в коментарях ваші думки, радий буду почути і відповісти.
Мій телеграм: @tarasvv
update August 30, 2022: heroku закриває безкоштовні плани, деталі і обговорення альтернатив в коментарях тут
update November 20, 2022: heroku запускає mini — плани (eco dynos — 1000 dyno-hours за 5$, heroku postgres mini — 5$). Також діють спеціальні безкоштовні умови для студентів (в межах 13$ на місяць)
27 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів