Деплой бота на сервер.
Соединение с сервером по протоколу SSH.
Данные для аутентификации:
Для подключения к серверу вам необходимо иметь (получить) следующие данные:
логин,
пароль,
IP адрес сервера,
порт подключения по протоколу SSH.
Например, логин: root, пароль: my_password, ip: 127.92.0.131, port: 22111.
Инструменты:
Для соединения с сервером и удаленной работы с ним нам потребуются следующие инструменты, в зависимости от вашей операционной системы:
Для Linux, MacOS:
Обычный встроенный в систему терминал.
Для Windows любой удобный вариант:
WinSCP
– клиент для работы по протоколу SSH и для передачи файлов с графическим интерфейсом. Ссылка для скачивания с официального сайта: https://winscp.net/eng/download.phpPuTTY
– клиент для работы по протоколу SSH и, опционально, консольная утилитаpSCP
для передачи файлов. Ссылка для скачивания с официального сайта: https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html Выбираем раздел Alternative binary – это файлы приложений, не требующих установки в систему. Скачиваем нужной разрядности x32 или x64 putty.exe – это сам клиент, и pscp.exe для передачи файлов на сервер.PowerShell
встроенный SSH-клиент c помошью командыssh
и командыscp
для передачи файлов. Запускается с помощьюWin+R
командаpowershell
, или в Проводникеshift + контекстное меню
(правая клавиша мыши) «Открыть окно Powershell здесь»
Подключение:
Открываем терминал Linux, MacOS или Windows PowerShell и в командной строке выполняем команду ssh имя_пользователя@IP_адрес -p номер_порта
:
ssh root@127.92.0.131 -p 22111
При первом подключении у вас запросят подтвердить обмен ключами:
The authenticity of host '127.92.0.131 (127.92.0.131)' can't be established.
ECDSA key fingerprint is SHA256:ABCDEasdfgAsDhgf+ffgIHKJHgLbvcxxcctvv6S3bvvY.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Необходимо ввести yes
.
Затем будет предложено ввести пароль:
root@127.92.0.131's password:
Обратите внимание, что при вводе пароля знаки и их количество не отображаются в целях безопасности. Нужно ввести или вставить пароль, а затем нажать ввод.
Появится приглашение ввода команд:
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.4.0-146-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
* Introducing Expanded Security Maintenance for Applications.
Receive updates to over 25,000 software packages with your
Ubuntu Pro subscription. Free for personal use.
https://ubuntu.com/pro
Last login: Tue Jun 20 12:02:39 2023 from 192.168.8.7
root@bot-111:~#
При использовании PuTTY
в разделе Session поле Host Name (or IP adress) вводится IP адрес и также указываем порт в поле Port. И нажать снизу кнопку Open, для установления соединения.
Логин и пароль нужно будет ввести после соединения с сервером.
При использовании WinSCP
нужно нажать на кнопку Новое соединение.. (New connection..) и так же ввести все имеющиеся учетные данные. В целях безопасности, пароль желательно не сохранять в приложении, а вводить через менеджер паролей или вручную.
Итак, если вы видите такую строку:
root@bot-111:~#
что означает имя_пользователя@имя_сервера:путь_к_папке#
, и вы успешно вошли в систему.
Знак #
в конце приглашения командной строки терминала означает, что вы суперпользователь root, а тильда ~
перед ним – что вы в домашней папке пользователя, в нашем случае root.
Внимание
По соображениям безопасности бот не должен работать от суперпользователя, поэтому потребуется дополнительная настройка, о которой речь пойдет в 3 разделе.
Пока же, нам нужно подготовить и разложить по папкам файлы нашего бота.
Копирование файлов проекта.
Если для работы и соединения с сервером, вы используете WinSCP, то просто нужно в одном окне открыть соединение с сервером, и скопировать файлы в нужные папки.
Если вы подключились через ssh, вам необходимо выполнить копирование с помощью команд в окне терминала.
В ubuntu сторонние приложения по соглашению находятся в папке /opt
. Поэтому файлы бота будут скопированы в эту папку.
Для копирования нам потребуется утилита scp
, которая есть в Linux, MacOS и Windows PowerShell, или pscp.exe
из проекта Putty. В случае с Putty вам необходимо открыть приложение командной строки Windows cmd.exe
через команду выполнить Win + R
. В WinSCP, в свою очередь, копирование файлов доступно после подключения к серверу через графический интерфейс.
Откроем новое окно терминала на локальном компьютере и перейдем в вышестоящую папку нашего проекта. Предположим, файлы нашего бота находятся на локальном компьютере в папке c:\my_bot
или /home/user/my_bot
(путь специально написал такой, чтоб не заморачиваться с длинной путей). Тогда нам нужно открыть терминал, и перейти командой cd c:\
или cd ~
.
Копируем папку с локального компьютера на удаленный командой scp
-r``(рекурсивно, то есть все файлы) ``-P
(SSH порт) путь_к_папке_my_bot
имя_пользователя@IP_адрес_сервера
:
путь_к_папке_/opt
Чтобы не было проблем с пробелами в локальном пути, его можно заключить в кавычки "
:
scp -r -P 22111 "c:\my_bot" root@127.92.0.131:/opt
Произойдет подключение к серверу. Нужно ввести пароль и файлы скопируются.
Если пути поменять местами, то скопируются наоборот на локальный компьютер.
Для удобства установим файловый менеджер, на пример Midnight commander
:
apt install mc
И запустим его:
mc
Находясь в этом консольном файловом менеджере мы можем переключаться между окнами интерфейса и командной строкой клавишами Ctrl + O
.
Возвращаемся в терминал, где открыт Midnight Commander, и проверяем что все прошло ок.
Настройка окружения Python.
Для того, чтобы приложение бота заработало и не было конфликта с библиотеками, которые уже установлены в системе, создадим новое чистое виртуальное окружение.
Перейдем в папку /opt/my_bot и создадим чистое окружение:
cd /opt/my_bot
python3 -m venv venv
Создастся папка venv, в которой будут находиться интерпретатор, pip и библиотеки нашего проекта.
Для установки библиотек нам необходимо активировать виртуальное окружение, обновить менеджер пакетов pip и установить все из файла зависимостей:
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
Когда все работы по настройке и проверке скрипта завершены, виртуальное окружение нужно деактивировать:
deactivate
Настройка бота как сервиса с автозапуском.
После того, как мы установили все библиотеки и проверили работоспособность бота, нам необходимо сделать автоматический запуск бота и перезапуск в случае ошибок, внезапных аварий и пр.
В целях безопасности, бот будет работать от пользователя с минимальными привилегиями. Создадим пользователя tgbot без возможности sudo и без прав логиниться в систему:
adduser --system tgbot
Добавим права на файлы бота для нового пользователя:
chown -R tgbot /opt/my_bot
Отвечать за запуск и перезапуск бота, а также ротацию логов будет systemd.
Для этого нам необходимо создать новый юнит — службу для управления нашим ботом.
Создадим в нашем проекте папку systemd и файл tgbot.service в ней:
cat > tgbot.service
Вставляем текст файла, приведенный ниже:
mkdir systemd
cd systemd/
touch tgbot.service
Добавим в файл tgbot.service
следующее содержимое:
[Unit]
Description=Test echo Bot
After=syslog.target
After=network.target
[Service]
User=tgbot
Type=simple
WorkingDirectory=/opt/my_bot
ExecStart=/opt/my_bot/venv/bin/python /opt/my_bot/cli.py
Restart=on-failure
RestartSec=5
StartLimitBurst=5
# Переменные окружения. Для более подробной информации см. раздел 5 "Переменные окружения" настоящего руководства.
# (измените переменные перед вставкой на свои или удалите эти строки, если не используете).
# В виде ключ = значение:
# Environment="VAR1=word1 word2" VAR2=word3
# Environment=Var3=word4
# Из файла
# EnvironmentFile=-/etc/sysconf/mysqld
[Install]
WantedBy=multi-user.target
Не забываем в конце добавить пустую строку и нажимаем Ctrl + D
.
В Midnight Commander редактирование файла F4
(при первом вызове выберите редактор mcedit, нажав на нужную цифру в окне выбора).
Обратим внимание на параметры в файле tgbot.service
:
Description=Test echo Bot
– это описание нашего бота.
After=network.target
– это указание, что бот должен быть запущен только после того, как стартует сервис сети. Можно указывать еще, что после старта базы данных:
After=mysql.service
Requires=mysql.service
User=tgbot
– указываем от имени какого пользователя запускать сервис
WorkingDirectory=/opt/my_bot/
– рабочая директория проекта.
ExecStart=/opt/my_bot/venv/bin/python /opt/my_bot/cli.py
— здесь указываем путь к интерпретатору в нашем виртуальном окружении и через пробел путь к основному файлу бота. У меня это cli.py.
Эти параметры определяют как будет происходить перезапуск:
Restart=always
– Перезапускать всегда. Может быть значение on-failure, как в моем случае.
RestartSec=5
– Запустить через 5 секунд.
StartLimitBurst=5
– Запустить 5 попыток.
Получившийся файл нам необходимо скопировать в папку /etc/systemd/system/
:
cp /opt/my_bot/systemd/tgbot.service /etc/systemd/system/
Обновляем конфигурацию systemd, чтобы он увидел новый юнит нашего сервиса. Эта команда будет нужна после каждой правки файла tgbot.service
:
systemctl daemon-reload
Запускаем сервис нашего бота:
systemctl start tgbot
Проверяем, что сервис запущен и нет ошибок.
systemctl status tgbot
Если все прошло удачно, то выведется, что статус активен, бот запущен:
root@bot-111:/opt/my_bot# systemctl status tgbot
● tgbot.service - Test echo Bot
Loaded: loaded (/etc/systemd/system/tgbot.service; enabled; vendor preset: enabled)
Drop-In: /run/systemd/system/service.d
└─zzz-lxc-service.conf
Active: active (running) since Wed 2023-06-21 11:13:16 UTC; 2h 21min ago
Main PID: 2955 (python)
Tasks: 2 (limit: 309168)
Memory: 35.0M
CGroup: /system.slice/tgbot.service
└─2955 /opt/my_bot/venv/bin/python /opt/my_bot/cli.py
Jun 21 11:13:16 bot-111 systemd[1]: Started Test echo Bot.
Проверяем, что процесс запущен от нужного пользователя tgbot. Для этого берем из вывода выше номер процесса Main PID: 2955 (python) и подставляем в команду:
ps -u -p 2955
и видим:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
tgbot 2955 0.0 10.2 137256 51344 ? Ssl Jun21 0:14 /opt/my_bot/venv/bin/python /opt/my_bot/cli.py
Показано, что процесс запущен от нашего пользователя tgbot.
Если ошибки есть, то они будут отображены примерно так:
systemctl status tgbot
● tgbot.service - Test echo Bot
Loaded: loaded (/etc/systemd/system/tgbot.service; enabled; vendor preset: enabled)
Drop-In: /run/systemd/system/service.d
└─zzz-lxc-service.conf
Active: activating (auto-restart) (Result: exit-code) since Wed 2023-06-21 10:47:29 UTC; 1s ago
Process: 2730 ExecStart=/opt/my_bot/venv/bin/python /opt/my_bot/cli.py (code=exited, status=200/CHDIR)
Main PID: 2730 (code=exited, status=200/CHDIR)
Jun 21 10:47:29 bot-111 systemd[2730]: tgbot.service: Failed at step CHDIR spawning /opt/bbt/venv/bin/python: No such file or directory
Jun 21 10:47:29 bot-111 systemd[1]: tgbot.service: Main process exited, code=exited, status=200/CHDIR
Jun 21 10:47:29 bot-111 systemd[1]: tgbot.service: Failed with result 'exit-code'.
Ошибка произошла, поскольку я ошибся в строках WorkingDirectory=/opt/my_bot/
и ExecStart=/opt/my_bot/venv/bin/python /opt/my_bot/cli.py
.
Проверьте пути!
После того, как вы все настроили, бот работает и ошибок нет — сохраните рабочую версию конфигурации tgbot.service. Не забывайте после правок настроек сервиса делать релоад systemd: systemctl daemon-reload
:
Устанавливаем бота в автозапуск:
systemctl enable tgbot
При необходимости останавливаем бота:
systemctl stop tgbot
Команда для удаления из автозагрузки:
systemctl disable tgbot
Переменные окружения.
Поскольку данное руководство предполагает разворачивание приложения через создание юнита systemd и запуск как сервиса, то имеется несколько вариантов работы с переменными окружения. Нужно обратить внимание, что пользовательский процесс systemd не наследует какую-либо из переменных окружения, установленных в .bashrc или других. Более того, мы изначально создали системного пользователя, у которого нет домашней папки, профиля, пароля и прочего, для ограничения прав в системе.
Вариант I. Директива Environment.
В systemd есть директива Environment, которая устанавливает переменные окружения для выполняемых процессов. Она принимает список назначений переменных, разделенных пробелами. Этот параметр может быть указан более одного раза, в этом случае будут установлены все перечисленные переменные.
Если одна и та же переменная задана дважды, более поздняя установка отменяет более раннюю. Если этой опции присвоена пустая строка, список переменных окружения обнуляется, все предыдущие назначения не имеют эффекта.
В файле tgbot.service в разделе [Service] добавляем или несколько директив Environment=Key=value
:
...
[Service]
...
Environment=BOT_CA_FILE=/path/to/CA.pem
Environment=BOT_CERT_FILE=/path/to/server.crt
Environment=BOT_KEY_FILE=/path/to/server.key
...
Несколько переменных можно добавлять через пробел (если в значении имеется пробел, то значение нужно заключить в кавычки, так как символ пробела является разделителем): Environment=Key=value Key2=value2 Key3="value space 3"
:
...
[Service]
...
Environment=BOT_CA_FILE=/path/to/CA.pem BOT_CERT_FILE=/path/to/server.crt BOT_KEY_FILE=/path/to/newserver.key
...
Вариант II. Директива EnvironmentFile (Рекомендуется к использованию).
EnvironmentFile аналогична директиве Environment, но считывает сразу все переменные окружения из текстового файла, что намного удобнее. Текстовый файл должен содержать назначения переменных, разделенных новыми строками.
Создадим файл переменных tgbot_envlist
такого содержания:
IPV4_ANCHOR_0=X.X.X.X
IPV4_PRIVATE_0=X.X.X.X
HOSTNAME=test.example.com
В файле tgbot.service
в разделе [Service] добавляем Environment=path_to_file
:
...
[Service]
...
EnvironmentFile=/opt/my_bot/env_dir/tgbot_envlist
...
Вариант III. Создание drop-in файла конфигурации (Необязательно для использования).
Когда у вас уже создан файл вашего юнита tgbot.service, мы можем создать drop-in файл. Это файл конфигурации, в котором будут находиться дополнительные настройки или настройки, которые заменяют значения основного файла юнита.
Выполняем команду systemctl edit tgbot.service
. Откроется редактор, который будет содержать закомментированное содержимое исходного файла юнита, и можно указывать только те секции и только те значения, которые мы хотим добавить или заменить.
Например:
systemctl edit tgbot.service
Откроется окно редактирования файла override.conf
:
### Editing /etc/systemd/system/tgbot.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file
### Editing /etc/systemd/system/tgbot.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file
### Lines below this comment will be discarded
### /etc/systemd/system/tgbot.service
# [Unit]
# Description=Test echo Bot
# After=network.target
#
# [Service]
# User=tgbot2
# Group=tgbot
# Type=simple
# WorkingDirectory=/opt/bbt
# ExecStart=/opt/bbt/venv/bin/python /opt/bbt/cli.py
# Restart=on-failure
# RestartSec=5
# StartLimitBurst=5
#
# [Install]
# WantedBy=multi-user.target
Добавим внизу строки:
[Service]
Environment=TEST_ENV="ABCDEF"
и сохраним файл под именем local.conf. В папке /etc/systemd/system
будет создана папка с именем вашего сервиса tgbot.service.d
. Внутри будет создан файл local.conf
.
Теперь после перезагрузки systemd daemon-reload
нам становится доступна переменная окружения TEST_ENV
со значением ABCDEF
. И мы ее можем импортировать в нашем коде бота так:
import os
NEW_ENV_VAR = os.environ.get("TEST_ENV")
print(NEW_ENV_VAR)
Drop-in файлы используются чаще всего для хранения настроек баз данных.
После всех правок перезагружаем systemd и перезагружаем сервис бота:
systemctl daemon-reload
systemctl restart tgbot.service
Вариант IV. Временные переменные окружения (Необязательно для использования).
Для временного изменения используйте команду systemctl set-environment
. Применяется ко всем пользовательским службам, созданным после установки переменных окружения, но не к службам, которые уже были запущены. Данные значения НЕ заменяют значения файла tgbot.service
:
systemctl set-environment VAR1=value1 VAR2=value2
systemctl restart tgbot.service
Для удаления временной переменной используйте команду systemctl unset-environment VARIABLE
:
systemctl unset-environment VAR1 VAR2
systemctl restart tgbot.service
Работа с логами.
После того, как бот запущен, или при запуске возникли ошибки, нам необходимо посмотреть логи нашего приложения. Для этого используется команда journalctl
. Эта команда выведет все записи из всех журналов, включая ошибки и предупреждения, начиная с того момента, когда система начала загружаться. Старые записи событий будут наверху, более новые — внизу, вы можете использовать PageUp
и PageDown
чтобы перемещаться по списку, Enter
— чтобы пролистывать журнал построчно и q
— чтобы выйти. Обычно объем логов огромный и нам потребуется их отфильтровать, чтобы найти нужное.
Команда journalctl -u название_сервиса
выведет все логи сервиса, в нашем случае tgbot.service:
root@bot-111:/opt# journalctl -u tgbot
Будет выведены примерно такого вида сообщения:
...
Jun 18 19:07:43 bot-103 systemd[1]: Started Test echo Bot.
Jun 18 19:07:45 bot-103 python[119]: INFO:aiogram.dispatcher:Start polling
Jun 18 19:07:45 bot-103 python[119]: INFO:aiogram.dispatcher:Run polling for bot @testhost_echobot id=00000000000 - 'tes>
Jun 18 19:07:45 bot-103 python[119]: INFO:aiogram.event:Update id=000000000 is handled. Duration 194 ms by bot id=00159>
Jun 18 19:07:51 bot-103 python[119]: INFO:aiogram.event:Update id=000000000 is handled. Duration 78 ms by bot id=001599>
Jun 18 19:09:38 bot-103 python[119]: INFO:aiogram.event:Update id=000000000 is handled. Duration 205 ms by bot id=00159>
Jun 18 19:09:58 bot-103 python[119]: INFO:aiogram.event:Update id=000000000 is handled. Duration 242 ms by bot id=00159>
Jun 18 19:11:21 bot-103 systemd[1]: Stopping Test echo Bot...
Jun 18 19:11:21 bot-103 python[119]: WARNING:aiogram.dispatcher:Received SIGTERM signal
Jun 18 19:11:21 bot-103 python[119]: INFO:aiogram.dispatcher:Polling stopped for bot @testhost_echobot id=0015000024 - >
Jun 18 19:11:21 bot-103 python[119]: INFO:aiogram.dispatcher:Polling stopped
Jun 18 19:11:21 bot-103 systemd[1]: tgbot.service: Deactivated successfully.
Jun 18 19:11:21 bot-103 systemd[1]: Stopped Test echo Bot.
Jun 18 19:11:21 bot-103 systemd[1]: Started Test echo Bot.
...
Journalctl позволяет использовать такие служебные слова как “yesterday” (вчера), “today” (сегодня), “tomorrow” (завтра), или “now” (сейчас).
Поэтому мы можем использовать опции --since
(с начала какого периода выводить журнал), --until
(по какой период не включительно) с использованием этих служебных слов, либо даты и времени.
С определенной даты и времени: journalctl --since "2023-06-18 19:00:00"
С определенной даты и по определенные дату и время: journalctl --since "2023-06-18" --until "2023-06-19 10:00:00"
Со вчерашнего дня: journalctl --since yesterday
Выведем журнал нашего бота за день:
journalctl -u tgbot --since "2023-06-18" --until "2023-06-19"
Система записывает события с различными уровнями важности, какие-то события могут быть предупреждением, которое можно проигнорировать, какие-то могут быть критическими ошибками.
Для уровней важности, приняты следующие обозначения:
0: emergency (неработоспособность системы)
1: alerts (предупреждения, требующие немедленного вмешательства)
2: critical (критическое состояние)
3: errors (ошибки)
4: warning (предупреждения)
5: notice (уведомления)
6: info (информационные сообщения)
7: debug (отладочные сообщения)
Если мы хотим просмотреть только ошибки нашего бота, введем команду с указанием кода важности:
journalctl -u tgbot -p 3
Отобразятся сообщения с уровнем важности 3 и до 0.
Посмотреть все уровни сообщений кроме сообщений отладки можно так:
journalctl -u tgbot -p 6
Ну и, конечно, можно все объединить - посмотреть журнал сервиса tgbot за период и показать только ошибки:
journalctl -u tgbot -p 3 --since "2023-06-18 09:00:00" --until "2023-06-19 10:00:00"