Введение: Зачем нам это нужно?

Привет, Хабр! ?

Давайте смоделируем ситуацию, которая знакома каждому начинающему разработчику (и мне тоже).

Вы написали своего первого или десятого Telegram-бота. У вас на компьютере всё работает идеально: вы жмете зеленую кнопку Run в PyCharm или VS Code, бот оживает, отвечает на сообщения и радует глаз. Вы чувствуете себя хакером.

Но потом возникает проблема: бот работает только тогда, когда включен ваш компьютер и запущена программа. Стоит закрыть ноутбук или случайно закрыть терминал — и бот «умирает», а пользователи видят тишину.

Вы решаете пойти дальше: покупаете самый дешевый VPS-сервер за 100 рублей, чтобы бот жил там вечно. И тут начинается ад ручной настройки:

  1. На сервере нет Python или стоит какая-то древняя версия 3.6.

  2. Вы пытаетесь установить библиотеки, но вылезают ошибки совместимости с Linux.

  3. Вы забыли, какие именно библиотеки устанавливали полгода назад, потому что не вели requirements.txt.

  4. В итоге вы тратите 3 часа на настройку окружения вместо того, чтобы писать код.

А теперь представьте, что всего этого можно избежать.

Что такое Docker (простыми словами)?

Представьте, что ваш код — это домашний обед.
Чтобы пообедать в гостях (на сервере), вам обычно нужно прийти к ним на кухню, искать кастрюли, надеяться, что у них есть плита и нужные специи. Если плиты нет — вы голодаете (код падает с ошибкой).

Docker — это ваш ланчбокс (контейнер).
Вы кладете туда не только еду (ваш код), но и сразу свою вилку, любимую приправу и даже маленькую микроволновку. Теперь неважно, куда вы пришли: на пустой сервер, на компьютер друга или на мощный кластер. Вы просто открываете контейнер, и всё работает точно так же, как у вас дома.

Что мы сделаем сегодня?
Мы перестанем запускать ботов «на коленке». За 10 минут мы:

  • Подготовим проект по всем правилам.

  • Напишем простую инструкцию (Dockerfile) для упаковки бота.

  • Запустим его в изолированном контейнере, который будет работать стабильно и предсказуемо.

Поехали!

2. Подготовка проекта (Pre-flight check)

Прежде чем упаковывать нашего бота, давайте убедимся, что у нас есть что упаковывать, и проект выглядит опрятно. Docker любит порядок.

1. Минимальный код бота

Для примера возьмем простейшего бота на aiogram 3.x. Если у вас уже есть свой сложный проект — отлично, используйте его. Если нет — создайте файл bot.py и вставьте туда этот код:

import asyncio
import logging
import os
from aiogram import Bot, Dispatcher, types
from aiogram.filters import CommandStart

# Включаем логирование, чтобы не пропустить важные сообщения
logging.basicConfig(level=logging.INFO)

# Важный момент: мы НЕ пишем токен прямо в коде.
# Мы будем передавать его "снаружи", когда запустим контейнер.
# Это правило безопасности №1.
TOKEN = os.getenv("BOT_TOKEN")

dp = Dispatcher()

@dp.message(CommandStart())
async def cmd_start(message: types.Message):
    await message.answer("Привет! Я работаю внутри Docker-контейнера! ?")

@dp.message()
async def echo_handler(message: types.Message):
    await message.answer(f"Ты написал: {message.text}")

async def main():
    if not TOKEN:
        print("Ошибка: Токен не найден!")
        return
    bot = Bot(token=TOKEN)
    await dp.start_polling(bot)

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("Бот выключен")

Обратите внимание: Мы используем os.getenv("BOT_TOKEN"). Это значит, что бот будет искать токен в переменных окружения своей операционной системы. Позже мы передадим этот токен прямо через настройки Докера.

2. Список зависимостей (requirements.txt)

Представьте, что Docker — это абсолютно чистый лист. Там нет ни aiogram, ни других библиотек, которые вы устанавливали. Ему нужна карта покупок — список того, что нужно скачать, чтобы ваш код заработал.

В Python этот список принято хранить в файле requirements.txt.

Если вы работаете в виртуальном окружении (venv), просто выполните команду в терминале:

pip freeze > requirements.txt

Откройте созданный файл. Он должен выглядеть примерно так:

aiogram==3.x.x
aiohttp==3.x.x
...другие библиотеки...

⚠️ Совет: Если вы НЕ используете виртуальное окружение и ставите всё в системный Python (что не рекомендуется), команда pip freeze выгрузит все библиотеки с вашего компьютера, включая те, что не нужны боту. В таком случае лучше создать файл requirements.txt вручную и вписать туда только aiogram.

3. Итоговая структура

В итоге ваша папка с проектом должна выглядеть так:

my_docker_bot/
├── bot.py              # Наш код
└── requirements.txt    # Список библиотек

3. Пишем Dockerfile (Инструкция для сборки)

Теперь нам нужно создать файл-инструкцию. В нём мы объясним Докеру: «Возьми Python, положи туда мои файлы, скачай библиотеки и приготовься запускать».

Прямо в корне проекта создайте файл с названием Dockerfile.
⚠️ Важно: Именно так, с большой буквы и без какого-либо расширения (.txt, .py не нужны).

Давайте напишем ег�� содержимое шаг за шагом, разбирая каждую строчку.

Шаг 1. Выбираем базу (FROM)

С чистого листа писать операционную систему долго. Поэтому мы берем готовый образ, где уже установлен Linux и Python.

FROM python:3.11-slim
  • FROM — начало любого Dockerfile.

  • python:3.11 — версия языка.

  • -slim — это облегченная версия образа. В ней вырезано всё лишнее, чтобы ваш контейнер весил не 1 ГБ, а 100-200 МБ. Для простых ботов — идеальный выбор.

Шаг 2. Рабочая папка (WORKDIR)

Нам нужно создать папку внутри контейнера, где будет жить наш бот.

WORKDIR /app
  • Это как команда cd /app или «Создать новую папку». Теперь все следующие команды будут выполняться внутри этой папки /app. Это нужно, чтобы не разбрасывать файлы по корню системы контейнера.

Шаг 3. Копируем зависимости и устанавливаем их

Тут есть хитрость, которую отличает новичка от профи.
Мы могли бы скопировать сразу всё. Но Docker умный: он кэширует каждый шаг.

Если мы сначала скопируем requirements.txt и установим библиотеки, то при изменении кода бота (например, поправили текст сообщения) Докер не будет заново качать библиотеки, так как этот шаг не менялся. Это ускоряет сборку в разы.

COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
  • COPY — берем файл requirements.txt с вашего компьютера и кладем его в контейнер.

  • RUN — выполняем команду в терминале контейнера.

  • --no-cache-dir — просим pip не сохранять кэш скачанных файлов (мы же в контейнере, нам нужно экономить место).

Шаг 4. Копируем остальной код

Теперь, когда библиотеки установлены, закидываем сам код бота.

COPY . .
  • Первая точка . — «всё из текущей папки на моем компьютере».

  • Вторая точка . — «в текущую рабочую папку контейнера (/app)».

Шаг 5. Команда запуска (CMD)

Финальный аккорд. Что сделать контейнеру, когда он запустится?

CMD ["python", "bot.py"]
  • Мы говорим: запусти Python и выполни файл bot.py.

  • Почему в квадратных скобках? Это технический нюанс (exec format), просто запомните: так надежнее, чтобы бот корректно реагировал на команду «Стоп».


Итоговый результат

Вот как выглядит ваш полный Dockerfile. Скопируйте это к себе:

# 1. Базовый образ
FROM python:3.11-slim

# 2. Рабочая директория
WORKDIR /app

# 3. Копируем зависимости и ставим их (кэширование слоев!)
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# 4. Копируем сам код
COPY . .

# 5. Команда запуска
CMD ["python", "bot.py"]

4. Безопасность и .dockerignore (Правила хорошего тона)

Мы почти готовы к запуску. Но перед этим нужно обсудить одну деталь, которая отличает «тяп-ляп» сборку от профессиональной.

Помните команду COPY . . из прошлого шага? Она работает как пылесос: засасывает всё из вашей папки в контейнер. Вообще всё.

Проблема «Мусора»

Если у вас в папке проекта есть:

  1. Папка .git (история изменений) — она может весить сотни мегабайт. Зачем она боту на сервере? Не нужна.

  2. Папка venv (ваше виртуальное окружение) — это самое страшное.

    • Во-первых, оно весит много.

    • Во-вторых, если вы работаете на Windows, а Docker собирает Linux-образ, то скопированная папка venv просто сломает контейнер (библиотеки скомпилированы под разные ОС).

  3. __pycache__ — байт-код, который генерируется автоматически.

Решение: .dockerignore

Чтобы Docker не тащил этот мусор с собой, мы создадим файл-фильтр. Это родной брат файла .gitignore.

Создайте файл .dockerignore (с точкой в начале!) и добавьте туда:

venv/
.venv/
.git/
__pycache__/
.idea/
.vscode/
*.pyc
.env

Что мы сделали:

  • Исключили виртуальное окружение (venv), потому что Docker сам установит свежие библиотеки.

  • Убрали .git и настройки редакторов (.idea, .vscode), чтобы уменьшить вес образа.

  • Добавили .env (если он у вас есть). Почему? Читайте ниже.


Пара слов о безопасности (Токены) ?

Вы заметили, что мы добавили .env в игнор-лист? И в коде бота (bot.py) мы использовали os.getenv("BOT_TOKEN")?

Это критически важно.

Никогда, слышите, никогда не «запекайте» свои пароли и токены внутрь Docker-образа.
Если вы напишете токен прямо в коде или скопируете файл .env внутрь образа, то любой, кто получит доступ к этому образу (или вашему репозиторию), украдет вашего бота.

Правильный подход:

  1. Код и Образ — чистые (без паролей).

  2. Токен передается только в момент запуска контейнера.

5. Магия в терминале: Сборка и Запуск

Все файлы на месте. Пора заводить мотор! ?️

Откройте терминал (или командную строку) в папке вашего проекта.
⚠️ Важное напоминание: Убедитесь, что Docker Desktop (или Docker Engine на Linux) запущен и работает. Если кит в трее спит — разбудите его.

Этап 1. Сборка (Build)

Сейчас мы превратим наш код и Dockerfile в Образ (Image).
Образ — это тот самый запечатанный «ланчбокс», готовый к отправке.

Введите команду:

docker build -t my-telegram-bot .

(Не забудьте точку в конце! Она очень важна)

Разбор команды:

  • docker build: Команда «Собери».

  • -t my-telegram-bot: Флаг Tag (имя). Мы называем наш образ my-telegram-bot, чтобы потом удобно к нему обращаться.

  • . (Точка): Говорит Докеру: «Ищи Dockerfile прямо здесь, в текущей папке».

Вы увидите, как побегут строки: скачивание Python, установка библиотек. В первый раз это займет минуту-две. Если увидели в конце что-то вроде FINISHED или exporting layers — поздравляю, сборка прошла успешно!

Этап 2. Запуск (Run)

Образ готов. Но пока он просто лежит на диске. Давайте запустим из него Контейнер (работающую программу).

Помните, мы не вшили токен в код? Сейчас мы передадим его безопасно.

Введите команду (замените ВАШ_ТОКЕН на реальный токен от BotFather):

docker run -d --name my-running-bot -e BOT_TOKEN="12345:ABC-xyz..." my-telegram-bot

Команда выглядит страшновато, но давайте разберем её по косточкам:

  1. docker run: «Запусти контейнер».

  2. -d (Detached): Самый важный флаг для ботов. Он означает «Запустись в фоновом режиме».

    • Без этого флага: Бот захватит ваш терминал, и если вы его закроете — бот умрет.

    • С этим флагом: Бот уйдет работать «под капот», а терминал останется свободным.

  3. --name my-running-bot: Мы даем имя конкретно этому запущенному процессу, чтобы потом легко его найти или остановить.

  4. -e BOT_TOKEN="...": Environment (Переменная окружения). Мы «впрыскиваем» токен внутрь контейнера. В коде os.getenv подхватит именно это значение.

  5. my-telegram-bot: Имя образа, который мы собрали на прошлом шаге.

Этап 3. Проверка

Docker выдал вам длинный набор букв и цифр (ID контейнера) и замолчал? Это хороший знак!

Давайте проверим, работает ли бот. Введите:

docker ps

Вы должны увидеть таблицу. Если в колонке STATUS написано Up ... secondsпобеда! ?

Идите в Телеграм и напишите своему боту /start. Он должен ответить:

"Привет! Я работаю внутри Docker-контейнера! ?"

Если бот ответил — вы официально научились пользоваться Докером. Ваш бот теперь изолирован, упакован и работает независимо от настроек вашей системы.

6. Управление контейнером (Повседневные задачи)

Итак, ваш бот крутится где-то в фоне (в режиме -d). Вы не видите терминала с привычными принтами. Возникает вопрос: как теперь с ним общаться?

Вот 4 команды, которые вы будете использовать каждый день.

1. Смотрим логи (logs)

Если бот вдруг перестал отвечать или вы хотите увидеть те самые print(), которые написали в коде, используйте:

docker logs my-running-bot

Эта команда выведет всё, что произошло в консоли бота с момента запуска.

? Pro-tip: Если вы хотите следить за логами в реальном времени (как в PyCharm), добавьте флаг -f (follow):

docker logs -f my-running-bot

Теперь, когда вы напишете боту сообщение, вы сразу увидите лог в терминале. Чтобы выйти, нажмите Ctrl + C (это не остановит бота, только отключит просмотр логов).

2. Остановка бота (stop)

Решили выключить бота, чтобы он не тратил ресурсы или не отвечал пользователям?

docker stop my-running-bot

Docker вежливо попросит процесс завершиться. Бот уснет. В списке docker ps его больше не будет, но он не удален. Он просто выключен (как компьютер).

Чтобы включить его обратно с теми же настройками:

docker start my-running-bot

3. Удаление контейнера (rm)

Если вы хотите удалить контейнер совсем (например, вы наэкспериментировались или неправильно его назвали), сначала остановите его, а потом удалите:

docker stop my-running-bot
docker rm my-running-bot

⚠️ Важно: Удаление контейнера не удаляет ваш код или Docker-образ. Это просто удаление конкретного запущенного экземпляра.

4. Главный вопрос: «Я обновил код, как обновить бота?»

Это самая частая ошибка новичков.
Запомните: Контейнер — это запечатанная посылка. Если вы поменяли код в файле bot.py у себя на компьютере, внутри контейнера ничего не изменится.

Чтобы обновить бота, нужно пройти полный цикл перерождения:

  1. Остановить и удалить старый контейнер (stop + rm).

  2. Пересобрать образ заново (docker build ...), чтобы "запечь" новый код.

  3. Запустить новый контейнер (docker run ...).

Звучит долго? На практике это три команды, которые вводятся за 10 секунд (стрелочка вверх в терминале спасает).

7. Заключение и «Что дальше?»

Поздравляю! ? Вы только что перешли из лиги «запускаю код в редакторе» в лигу «упаковываю приложения как профи».

Что мы получили в итоге?

  1. Изоляция: Ваш бот больше не зависит от того, что установлено на вашем компьютере. Вы можете удалить Python со своей машины, а бот в контейнере продолжит работать.

  2. Переносимость: Вы можете передать этот Docker-образ другу, и он запустится у него с первого раза. Без установки библиотек и танцев с бубном.

  3. Чистота: Вы не засоряете свою систему десятками библиотек для разных проектов.

Куда развиваться дальше?
Конечно, запуск одной командой docker run с кучей флагов — это удобно, но только для старта. В реальных проектах, где боту нужна база данных (PostgreSQL, Redis), всё становится интереснее.

Вот темы, которые стоит изучить следующими (или ждать моих новых статей ?):

  • Docker Compose: Инструмент, который позволяет описать запуск бота и базы данных в одном файле docker-compose.yml. Больше никаких длинных команд в консоли!

  • Volumes (Тома): Сейчас, если вы удалите контейнер, данные внутри него пропадут. Volumes позволяют сохранять данные (например, базу SQLite) на диске вашего компьютера, даже если контейнер пересоздается.

  • Deploy: Как за 5 минут перенести этот контейнер на арендованный VPS-сервер.


?️ Домашнее задание

Теория без практики быстро забывается. Чтобы закрепить успех, попробуйте выполнить эти задания:

  1. Level Easy (Для уверенности):
    Возьмите любой свой старый проект (калькулятор, парсер, другой бот) и напишите для него Dockerfile. Запустите и убедитесь, что он работает.

  2. Level Medium (Для любопытных):
    Попробуйте изменить в Dockerfile базовый образ с python:3.11-slim на python:3.11-alpine.

    • Соберите образ (docker build ...).

    • Сравните размеры образов командой docker images. Насколько Alpine меньше? (Спойлер: значительно, но с ним бывают нюансы при установке сложных библиотек).

  3. Level Hard (Для будущих сеньоров):
    Добавьте в команду запуска флаг --restart always.

    docker run -d --restart always ...
    

    Запустите бота, а затем... убейте процесс внутри контейнера или перезагрузите свой компьютер (если Docker в автозагрузке). Проверьте, воскреснет ли бот сам?

Анонс новых статей, полезные материалы, а так же если в процессе решения возникнут сложности, обсудить их или задать вопрос по статье можно в моём Telegram-сообществе.

Комментарии (0)