Деплой на сервер (VPS) и Docker
Учимся переселять «Цыплёнка-помощника» с твоего ноутбука на настоящий сервер, чтобы он отвечал друзьям даже ночью и когда ты на учёбе.
Деплой — это перенос готовой программы туда, где она будет работать постоянно: на сервер, который не выключается, не уходит в спящий режим и не теряет интернет, когда ты закрываешь крышку ноутбука.
Зачем вообще куда-то переезжать
Представь: ты собрал крутого бота-напоминалку про домашку, скинул ссылку друзьям из класса, все подписались. А вечером ты закрыл ноутбук, лёг спать — и бот замолчал. Утром Маша пишет «/start», а в ответ тишина: твоя программа ведь жила на твоём компьютере, а компьютер спит вместе с тобой.
Пока ты учился писать бота, запускать его прямо на своей машине было нормально. Но как только ботом начинают пользоваться другие люди, ему нужен дом, который никогда не закрывается. Такой дом называется VPS — это виртуальный приватный сервер, по сути небольшой компьютер где-то в дата-центре, который работает 24 часа в сутки 7 дней в неделю и всегда подключён к интернету. Ты арендуешь его за пару сотен рублей в месяц, заходишь по сети и запускаешь там своего «Цыплёнка».
К концу этого урока твой бот будет жить на сервере и переживать любые перезагрузки — даже если сервер внезапно выключат и включат, бот поднимется сам. А ещё мы научимся упаковывать его в Docker, чтобы переезжать между серверами было так же просто, как перенести один файл.
VPS — это просто чужой компьютер, к которому ты ходишь по сети
Не пугайся слова «сервер». Сервер — это обычный компьютер, только без монитора и клавиатуры рядом с тобой. Ты управляешь им через текстовую консоль по протоколу SSH: набираешь команды, нажимаешь Enter, видишь ответ. Это как переписка в мессенджере, только собеседник — операционная система Linux.
Когда ты арендуешь VPS (например, у российского хостера), тебе дают три вещи: IP-адрес сервера (что-то вроде 185.22.10.7), имя пользователя (обычно root) и пароль. С ними ты подключаешься так:
ssh [email protected]Результат: сервер спросит пароль (символы при вводе не отображаются — это нормально), и ты окажешься внутри: в строке появится приглашение вроде root@vps:~#. Поздравляю, ты в гостях у своего сервера.
Шаг 1. Ставим Python и инструменты
На свежем сервере часто нет ни Python нужной версии, ни менеджера пакетов pip, ни git. Установим всё одним заходом. Сначала обновим список доступных программ, потом доставим нужное:
apt update
apt install -y python3 python3-pip python3-venv gitРезультат: система скачает и поставит Python 3, инструмент для виртуальных окружений и git. Флаг -y заранее отвечает «да» на вопрос «точно ставим?».
Шаг 2. Забираем код бота на сервер
Код «Цыплёнка» лежит у тебя на GitHub (а если ещё нет — самое время залить, только не забудь про .gitignore, чтобы туда не попал секретный токен). Клонируем репозиторий:
git clone https://github.com/ozhik/chicken-bot.git
cd chicken-botРезультат: на сервере появится папка chicken-bot с твоим bot.py и файлом requirements.txt, в котором перечислены нужные библиотеки.
Шаг 3. Виртуальное окружение и зависимости
Виртуальное окружение — это отдельная коробка с библиотеками только для одного проекта, чтобы версии не путались между разными ботами. Создаём коробку, входим в неё и ставим всё из списка:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtРезультат: в начале строки появится (venv) — значит, ты внутри окружения, и сюда установятся aiogram, aiohttp и остальное из requirements.txt.
Шаг 4. Кладём токен в переменную окружения
Помнишь урок про переменные окружения и секреты? На сервере действует то же правило: токен в коде писать нельзя. Мы кладём его рядом, в файл .env, который никогда не попадает в git. Создадим такой файл прямо на сервере любым текстовым редактором, например nano:
nano .envВнутри пишем одну строку и сохраняем (в nano — это Ctrl+O, Enter, потом Ctrl+X):
BOT_TOKEN=123456789:AaBbCcDdEeFf-твой-секретный-токенРезультат: токен хранится отдельно от кода, а бот достаёт его строкой os.getenv("BOT_TOKEN") — ровно так, как ты уже делал в прошлом уроке.
Теперь можно проверить, что бот в принципе запускается:
python3 bot.pyРезультат: в консоли побегут логи запуска, а в Telegram «Цыплёнок» снова начнёт отвечать на /start. Но если ты сейчас закроешь SSH-сессию, бот опять умрёт — мы запустили его «вручную». Это и решает следующий шаг.
systemd — невидимый дворецкий, который держит бота включённым
Запускать бота вручную и держать окно открытым — это как стоять и держать дверь, чтобы она не закрылась. Неудобно и ненадёжно. В Linux есть systemd — это дворецкий операционной системы: он умеет запускать программы при старте сервера, перезапускать их, если они упали, и писать логи. Мы просто оставим ему записку («сервис»), где опишем, какую программу и как запускать.
Создаём файл сервиса:
nano /etc/systemd/system/chicken-bot.serviceИ пишем внутрь такую записку для дворецкого:
[Unit]
Description=Chicken Helper Telegram Bot
After=network.target
[Service]
WorkingDirectory=/root/chicken-bot
EnvironmentFile=/root/chicken-bot/.env
ExecStart=/root/chicken-bot/venv/bin/python3 bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetРазберём построчно, чтобы это не было магией:
After=network.target— запускать бота только после того, как на сервере появился интернет (иначе боту не с кем разговаривать).WorkingDirectory— папка, из которой запускать; как будто дворецкий сначала зашёл в неё командойcd.EnvironmentFile— тот самый.envс токеном; systemd сам прочитает его и подставит переменные.ExecStart— главная строка: каким именно Python (из нашейvenv!) запускатьbot.py.Restart=alwaysиRestartSec=5— если бот вдруг упадёт, подними его снова через 5 секунд. Это и есть та самая надёжность.
Теперь говорим systemd перечитать записки, включить наш сервис в автозапуск и стартовать его:
systemctl daemon-reload
systemctl enable chicken-bot
systemctl start chicken-botРезультат: бот запущен в фоне и будет подниматься сам после любой перезагрузки сервера. Проверить, жив ли он, можно командой:
systemctl status chicken-botРезультат: увидишь зелёное active (running) — значит, «Цыплёнок» на дежурстве. А посмотреть его логи (что он печатал) можно так:
journalctl -u chicken-bot -fРезультат: побегут свежие строки логов в реальном времени; выйти из просмотра — Ctrl+C (бот при этом не остановится).
Docker — переезд вместе с домом, а не только с вещами
systemd — отличный вариант. Но есть проблема: твой бот работает не сам по себе, ему нужны Python нужной версии, набор библиотек, переменные. На новом сервере всё это придётся ставить заново и молиться, чтобы версии совпали. Знакомая боль: «у меня на ноутбуке работало, а на сервере нет».
Docker решает это радикально. Представь, что вместо того чтобы перевозить вещи в новую квартиру и заново всё собирать, ты перевозишь целую комнату вместе со стенами, мебелью и розетками. Docker упаковывает бота вместе с его Python, библиотеками и настройками в образ (image) — этакую коробку-комнату. Из образа запускается контейнер — работающая копия, изолированная от всего остального. Один и тот же образ одинаково запустится на твоём ноутбуке, на VPS и на сервере друга.
Пишем Dockerfile
Dockerfile — это рецепт сборки коробки. Ты описываешь по шагам, что положить внутрь, а Docker по рецепту собирает образ. Создаём файл с именно таким именем (без расширения) в папке бота:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python3", "bot.py"]Читаем рецепт сверху вниз:
FROM python:3.12-slim— берём готовую заготовку: миниатюрный Linux, в котором уже стоит Python 3.12.slimзначит «лёгкая версия без лишнего».WORKDIR /app— внутри коробки делаем папку/appрабочей.COPY requirements.txt .— копируем сначала только список зависимостей. Хитрость: пока он не меняется, Docker не будет переустанавливать библиотеки заново при каждой сборке.RUN pip install ...— ставим все библиотеки из списка. Флаг--no-cache-dirне захламляет образ.COPY . .— копируем остальной код бота внутрь.CMD [...]— команда, которая выполнится при запуске контейнера: запуститьbot.py.
Сразу создай рядом файл .dockerignore, чтобы лишнее не попало в образ (особенно секреты):
venv
.env
__pycache__
*.pyc
.gitРезультат: при сборке Docker проигнорирует виртуальное окружение, секретный .env и мусор — образ останется чистым и лёгким.
Собираем образ и запускаем контейнер
На сервере должен быть установлен Docker (на свежем VPS обычно ставится командой apt install -y docker.io). Дальше — два шага. Сначала собираем образ по рецепту, давая ему имя-метку:
docker build -t chicken-bot .Результат: Docker пройдёт по строкам Dockerfile, скачает заготовку с Python, поставит библиотеки и соберёт образ с меткой chicken-bot. Точка в конце — это «бери рецепт из текущей папки».
Теперь запускаем контейнер из образа. Токен передаём через файл .env — он не зашит в образ, а подаётся снаружи при запуске:
docker run -d --name chicken --restart=always --env-file .env chicken-botРезультат: «Цыплёнок» запустится в контейнере в фоне и снова начнёт отвечать в Telegram. Разберём флаги:
-d— запустить в фоне (detached), не занимая консоль.--name chicken— дать контейнеру понятное имя, чтобы потом обращаться к нему по нему.--restart=always— поднимать контейнер заново, если он упал или сервер перезагрузился (аналог нашего systemd-правила).--env-file .env— подставить переменные (токен!) из файла.
Посмотреть, что бот жив, и почитать его логи:
docker ps
docker logs -f chickenРезультат: docker ps покажет список работающих контейнеров (среди них наш chicken), а docker logs -f выведет логи бота в реальном времени.
Маленький разбор: как код достаёт токен
Чтобы и systemd, и Docker могли просто «подставить» токен снаружи, в коде бота он читается из окружения. Вот мини-логика этого чтения на чистом Python — её можно запустить прямо в браузере:
import os
# имитируем, что окружение задало переменную (на сервере это делает .env)
os.environ["BOT_TOKEN"] = "123456:SECRET"
token = os.getenv("BOT_TOKEN")
if not token:
print("Токен не найден! Проверь .env")
else:
# никогда не печатаем токен целиком в реальном боте
print("Токен получен, длина:", len(token))
print("Начинается на:", token[:6])Вывод:
Токен получен, длина: 13 Начинается на: 123456
Видишь — код не знает и не должен знать, откуда именно пришёл токен: из .env, из systemd или из --env-file. Он просто берёт его из окружения. Поэтому одну и ту же программу можно деплоить любым из трёх способов.
Частые ошибки и подводные камни
На этих граблях спотыкаются почти все, кто деплоит бота первый раз. Пробежимся, чтобы ты их обошёл.
- Токен уехал в git. Самая болезненная ошибка: забыл добавить
.envв.gitignore, запушил — и твой токен теперь видит весь интернет. Telegram такие токены отзывает, а чужой бот может начать рассылать спам от твоего имени. Всегда проверяй.gitignoreдо первогоgit push, а если уже слил токен — немедленно перевыпусти его у BotFather командой/revoke. - Запустил два экземпляра сразу. Если бот крутится и в systemd, и в Docker (или просто запущен дважды), Telegram начнёт ругаться ошибкой вроде Conflict: terminated by other getUpdates request. Один токен — один работающий polling-бот. Останови лишнее:
systemctl stop chicken-botилиdocker stop chicken. - В сервисе указан системный python вместо venv. Если в
ExecStartнаписать простоpython3вместо пути кvenv/bin/python3, бот не найдёт aiogram и упадёт сModuleNotFoundError. Дворецкий запускает ровно то, что ты написал, — указывай полный путь до Python из виртуального окружения. - Забыл про перезапуск после правок. Поменял код, сделал
git pull— но бот в памяти всё ещё старый. Для systemd нуженsystemctl restart chicken-bot, для Docker — пересобрать образ (docker build) и пересоздать контейнер. Сам по себе код на сервере не подхватывается. - База SQLite пропала вместе с контейнером. Если «Цыплёнок» хранит пользователей в SQLite, а ты удалил и пересоздал контейнер, данные внутри него исчезнут — контейнер по своей природе одноразовый. Чтобы база жила между перезапусками, её папку нужно подключать снаружи через том:
-v /root/chicken-data:/app/data. Тогда файл базы лежит на сервере, а не внутри коробки.
Мини-практика: подними «Цыплёнка» сам
Теперь твоя очередь. Возьми своего бота и доведи его до жизни на сервере одним из двух путей — выбери, что больше нравится.
- Заведи самый дешёвый VPS у любого хостера, подключись по SSH и поставь Python с git (Шаги 1–3 выше).
- Залей бота на GitHub, обязательно добавив
.envиvenvв.gitignore. Проверь глазами, что токена в репозитории нет. - Путь А (systemd): создай файл
chicken-bot.service, включи его черезsystemctl enableи убедись, чтоsystemctl statusпоказываетactive (running). - Путь Б (Docker): напиши
Dockerfileи.dockerignore, собери образdocker build -t chicken-bot .и запусти контейнер с--restart=always. - Финальная проверка: перезагрузи сервер командой
reboot, подожди минуту, переподключись и напиши боту/start. Если «Цыплёнок» ответил без твоего вмешательства — ты сделал настоящий деплой, поздравляю!
Со звёздочкой: добавь подключение тома для SQLite-базы, чтобы список пользователей переживал пересоздание контейнера.
Итоги
Сегодня «Цыплёнок-помощник» переехал с твоего ноутбука в настоящий дом и теперь работает круглосуточно. Ты научился: подключаться к VPS по SSH и ставить туда Python с зависимостями; запускать бота как сервис через systemd, который сам поднимает его после падений и перезагрузок; писать Dockerfile и упаковывать бота в образ; запускать его в контейнере одной командой docker run. А ещё — главное правило безопасности: токен живёт в переменной окружения и никогда не уезжает в git.
Теперь у твоего бота есть постоянная крыша над головой. В следующем уроке мы сделаем так, чтобы обновлять бота на сервере было не больно: настроим автоматический деплой, чтобы после git push свежая версия «Цыплёнка» сама приезжала на сервер и перезапускалась. Больше никаких ручных git pull и restart по ночам.