Как я Telegram бота на Go писал. Часть третья. Публикация.
В предыдущих частях был разработан и улучшен бот, помогающий найти заклинания для Dungeon and Dragons. Теперь требуется разместить бота на какой-либо платформе. Рассмотрим два варианта: vscale.ru и heroku.
vscale.ru
Плюсы
- Простота развёртывания для новичка
- Отдельный VDS со всеми преимуществами (устанавливай всё, что требуется)
- Удобная панель для управления
- Минимальный сервер - 200 рублей в месяц
- Реферальная программа (при регистрации по реферальной ссылке вам на счёт упадёт 400 рублей)
- Документация на русском
Минусы
- Платный (от 200 рублей в месяц (на текущий момент))
- Отдельный VDS со всеми недостатками (самостоятельное администрирование)
- На сегодняшний день сервера доступны только в Москве и Санкт-Петербурге
Развёртывание
Надеюсь, проблем с регистрацией на vscale не возникнет и сразу перейду к главному - к публикации бота. Во-первых, требуется создать сервер (на vscale так же их называют “скалет”). Для бота более чем достаточно самой дешёвой конфигурации за 200 рублей: 512 Mb RAM, 20 Gb диск, 1 ядро и 1 Tb трафика. ОС
- Ubuntu 16.04. Во-вторых. Надеюсь, код бота, который будет работать на этом
сервере уже на github или bitbucket? В-третьих, необходимо настроить доступ к
серверу. Можно воспользоваться
инструкцией
разработчиков vscale на habrahabr (см. шаг 5 и 6). В-четвёртых, подключаемся к
серверу и настраиваем окружение. Так как бот написан на Go, то требуется
установить его компилятор командой
sudo apt-get install golang-stable
, задать переменные среды GOPATH, GOROOT и добавить GOROOT в PATH. На habrahabr есть статья, подробно объясняющая как это сделать. Далее, в-пятых, клонируем репозиторий на сервер и компилируем. В-шестых, наконец-то, запускаем нашего бота командой./dndspellsbot
(ну или как он называется?). Чтобы бот не умирал при отключении от сервера, можно воспользоваться стандартной UNIX утилитой nohup:nohup ./dndspellsbot
. На этом всё - можете обращаться к своему новому боту.
heroku
Плюсы
- Бота можно хостить бесплатно
- Если наловчиться, то можно довольно просто публиковать приложения
- Широкий выбор дополнительных возможностей (за деньги)
Минусы
- Для бесплатного размещения требуется доработка бота
- Практически за всё дополнительное требуют деньги (в USD)
- В первый раз довольно сложно разобраться что к чему
- Документация на английском
Развёртывание
С heroku всё несколько сложнее. В первую очередь, как мне показалось, сервис предназначен для веб-проектов (веб-сайты, веб-приложения и всё такое). Бот - немного (на самом деле ощутимо) отличается от этого и поэтому потребуется немного его доработать. Об этом ниже.
Создание приложения
Расскажу про то, как сам публиковал бота (в картинках!). Но для начала не
забудьте установить на ваш ПК Heroku
CLI - пригодится
в дальнейшем. Заходим на https://dashboard.heroku.com/ и создаем новое
приложение:
Задаём ему имя (по этому имени в будущем можно будет обращаться к нашему
приложению в браузере) и регион:
Для простоты продублировал репозиторий с bitbucket на github (с ним heroku
работает “из коробки”). Привязал аккаунт на github к heroku и из списка
доступных репозиториев выбрал бота:
Теперь доступна возможность заdeployить бота на сервер при помощи
соответствующего раздела:
Однако, ничего не выйдет, т.к. во-первых, необходимо указать buildset и, во-
вторых, добавить Godep для того, чтобы heroku знал, как собирать проект. С
buildset всё просто: идём в настройки приложения и выбираем там тот, который
нам подходит (heroku/go, конечно):
Окей, с buildset разобрались, теперь надо понять, что за зверь такой Godep.
Всё, что было написано в этом разделе можно так же провернуть в Heroku CLI.
Однако, не так просто. Для дальнейших манипуляций потребуется воспользоваться
этой самой CLI и для этого необходимо залогиниться в heroku командой heroku login
, которая запросит логин и пароль от вашего аккаунта.
Особенность Heroku #1
Если вкратце, что Godep - это утилита для управления зависимостями (что-то типа npm, nuget, pip и вот это всё). В использовании довольно проста, но иногда выдаёт странные финты. Устанавливается как обычный пакет Go командой
go get github.com/tools/godep
Теперь в консоли должна появится команда godep
, которая позволит создать всё
требуемое для heroku (в частности Godep.json). Если команда godep
не
работает, то найти эту утилиту можно в папке bin с установленным Go. После
выполнения команды godep save
в папке проекта будет создана новая директория
Godeps с нужными для heroku файлами. Далее добавляем новый remote, указывающий
на heroku, для нашего репозитория:
git remote add heroku https://git.heroku.com/dndspellsbot.git
и pushим все изменения в него: git push heroku master
. При этом heroku
автоматически попытается собрать проект и запустить его. Если всё сделано
правильно, то теперь наш бот будет работать недолго, но счастливо и умрёт
через непродолжительное время. Почему? На это есть две причины, про которые
ниже.
Особенность Heroku #2
Как я понимаю, heroku предполагает, что ваше приложение - веб-сайт (веб-
приложение) и пробует к нему подключиться по некоторому порту (из переменной
окружения). При неудачной попытке подключиться приложение падает и больше не
поднимается. Если взглянете на логи приложения в heroku (командой heroku logs
), то увидите там сообщения приблизительного следующего содержания:
2016-10-22T10:58:18.996167+00:00 heroku[web.1]: Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch
2016-10-22T10:58:18.996167+00:00 heroku[web.1]: Stopping process with SIGKILL
2016-10-22T10:58:19.116590+00:00 heroku[web.1]: State changed from starting to crashed
2016-10-22T10:58:19.110450+00:00 heroku[web.1]: Process exited with status 137
Это значит, что ваше приложение завершилось по таймауту. Чтобы отложить столь
скорую смерть бота на чуть более длительный период, необходимо добавить
возможность нашему боту как-то реагировать, если к нему стучаться через
HTTP(S). Для этого в Go есть решение “из коробки” в пакете "net/http"
:
import (
...
"net/http"
...
)
func MainHandler(resp http.ResponseWriter, _ *http.Request) {
resp.Write([]byte("Hi there! I'm DndSpellsBot!"))
}
func main() {
...
http.HandleFunc("/", MainHandler)
go http.ListenAndServe(":"+os.Getenv("PORT"), nil)
...
}
Код выше необходимо добавить в код бота перед циклом получения обновлений. Не
сложно догадаться, что в случае, если к серверу, где живёт бот, будут
стучаться по порту означенной в переменной окружения PORT
, то бот ответит
ему сообщением "Hi there! I'm DndSpellsBot!"
. Это можно легко проверить
добавив переменную окружения PORT
и запустив бота локально. Если открыть
браузер по адресу http://localhost:PORT
, то браузер должен показать этот
ответ. Итого: после добавления обработки запросов на указанный порт можно
смело заpushить изменения на heroku и посмотреть, что из этого выйдет. А
выйдет то, что приложение не будет завершаться так быстро, как в прошлый раз,
но, к сожалению, всё равно, будет умирать. Почему? Это еще одна особенность
heroku.
Особенность Heroku #3
Проблемы с умиранием процесса не было бы, если бы наше приложение было веб- сайтом, если бы мы занесли денег в heroku либо наш бот был написан немного по- другому. Затык в том, что бесплатное приложение должно не менее 8 часов в сутки “спать” и платформа “оптимизирует” это убивая его, если к нему никто не обращается по адресу, которое оно (наше приложение) получило. Так как наше приложение - бот, то никто и не должен обращаться к нему через браузер или как-то еще, кроме как через мессенджер. И для решения этого потребуется еще раз доработать бота. Если вы читали документацию telegram, то знаете, что получать сообщения можно либо через long pooling (как было реализовано в нашем боте ранее), либо через webhook. Так вот боты, работающие на платформе heroku, будут себя чувствовать гораздо лучше, если будут получать обновления через webhook. Займёмся этим. Для того, чтобы это реализовать со стороны кода бота сделать требуется довольно только лишь исправить получение канала с обновлениями:
// Для получения через long pooling. Сейчас нам это не нужно.
// updates, err := bot.GetUpdatesChan(u)
// Для получения через webhook
updates := bot.ListenForWebhook("/" + bot.Token)
Далее требуется объяснить telegramу, что теперь следует общаться с ботом через webhook на определённом адресе. Для этого достаточно перейти в браузере по следующему адресу:
https://api.telegram.org/botТОКЕН_БОТА/setWebhook?url=https://ИМЯ_HEROKU_ПРИЛОЖЕНИЯ.herokuapp.com/ТОКЕН_БОТА
Таким образом мы говорим, что если боту пришло сообщение, то необходимо отправить запрос на адрес https://ИМЯ_HEROKU_ПРИЛОЖЕНИЯ.herokuapp.com/ТОКЕН_БОТА. В общем, это, кажется, всё, что было необходимо, чтобы поднять бота на heroku. Осталось только лишь заpushить изменения на сервер heroku и проверить как наш бот работает. Если всё сделано правильно, то бот будет отвечать нам как и прежде. Полный код бота можно найти bitbucket.