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

Дисклеймер

Цель данного руководства — не обучение Docker, а пример использования этой технологии в контекте телеграмм ботов на aiogram 3. Я не буду сильно акцентировать внимание на таких вещах, как слои, volume, docker-compos, bridge и прочей технической информации более глубокого уровня, чем необходимо для деплоя ботов на VPS сервере.

Далее вы получите пример использования Docker и общее описание методов (команд). Если вам нужны мои обучающие публикации по Docker, сообщите мне об этом любым удобным способом.

Подготовка

Для начала вам нужно обзавестись базой данных PostgreSQL. О том, как развернуть ее на VPS сервере, я писал ТУТ. Также потребуется установить Docker. Новичкам будет удобнее поставить Docker Desktop, если с технологией уже знакомы, используйте консольный вариант.

  1. Установите Docker и запустите его.

  2. Проверьте установку командой: docker –version

Если докер установлен, то вы получите ответ с текущей установленной версией. У меня так:

Docker version 26.1.1, build 4cf5afa

Дальше я открою эту папку через Pycharm, но вы можете вводить все команды через CMD, PowerShell, терминал и так далее.

Функциональность бота мы обсуждали ТУТ, сейчас же сосредоточимся именно на Docker.

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

Если вы клонировали репозиторий, то можете обнаружить в нем следующие файлы:

Вы видите знакомую структуру с тремя новыми файлами:

  • .dockerignore

  • DockerFile

  • Makefile

Основной файл — DockerFile. Вот его содержимое:

FROM python

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["/bin/bash", "-c", "python aiogram_run.py"]

Объяснение строк Dockerfile:

  1. FROM python: Использует официальный образ Python.

  2. WORKDIR /usr/src/app: Устанавливает рабочую директорию.

  3. COPY requirements.txt ./: Копирует файл зависимостей.

  4. RUN pip install --no-cache-dir -r requirements.txt: Устанавливает зависимости.

  5. COPY . .: Копирует все файлы.

  6. CMD ["/bin/bash", "-c", "python aiogram_run.py"]: Запускает скрипт.

Немного кулинарной теории

Образ в контексте Docker — это своего рода шаблон или слепок, который содержит всё необходимое для запуска приложения. В образ входят:

  1. Операционная система (или её часть).

  2. Программы и библиотеки, нужные для работы приложения.

  3. Само приложение и его файлы.

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

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

Контейнер — это запущенный экземпляр образа. Он работает как отдельная маленькая виртуальная машина или изолированная среда, в которой запускается ваше приложение.

Представьте контейнер как кухню, где по рецепту (образу) готовится блюдо (приложение). Контейнеры обеспечивают, чтобы приложение работало одинаково в разных средах, потому что всё необходимое уже упаковано внутри.

Надеюсь, что доступно объяснил.

.dockerignore

Данный файл работает аналогичным образом с .gitignore, с отличием в том, что тут мы настраиваем игнорирование тех файлов, которые мы не будем включать в свой  Docker-образ.

Makefile

Makefile упрощает процесс создания и запуска Docker-контейнера. Пример Makefile:

run:
	docker run -it -d --env-file .env --restart=unless-stopped --name easy_refer easy_bot_image
stop:
	docker stop easy_refer
attach:
	docker attach easy_refer
dell:
	docker rm easy_refer

Для того чтоб использовать Makefile на операционной системе на которой вы работаете должен быть установлен Make. О том как поставить Make и вообще что это такое я писал в этом посте ССЫЛКА.

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

Нужные команды

Сегодня нам необходимо будет:

  • Создать docker-образ (image)

  • Запустить контейнер на основе этого образа

Для создания docker-образа нам необходим Dockerfile (описали выше). Далее просто переходим в папку где расположен этот файл и вводим такую команду:

docker build -t name_image .

Пояснение:

  1. docker build:

    • Эта часть команды говорит Docker, что нужно построить (создать) образ на основе инструкций из Dockerfile.

  2. -t name_image:

    • Опция -t (или --tag) задает тег для создаваемого образа. Тег состоит из имени и, опционально, версии в формате name:tag. В данном случае образ будет называться name_image. Если версия (тег) не указана, Docker по умолчанию присваивает тег latest.

  3. . (точка):

    • Эта часть указывает контекст сборки. Точка обозначает текущую директорию, то есть Docker будет искать Dockerfile и все необходимые файлы в текущей директории для создания образа.

Пример

docker build -t easy_bot_image .

Так как мы указывали в Dockerfile FROM python – автоматически с docker hub подтянется образ python. Так как python у нас записан без тегов – будет установлена версия latest.

Для того чтоб просмотреть все установленные у вас образы воспользуйтесь командой:

docker images

Один из образов будет тот что вы создали. Далее, для взаимодействия с этим образом вам достаточно будет указывать то имя что вы передавали после -t или его IMAGE ID (достаточно первых 2-3 символов, если они уникальны). Я буду использовать имя образа.

Теперь нам остается создать контейнер и запустить его.

За создание контейнера отвечает команда create, а за запуск start, но мы можем объеденить эти две команды через run (синтаксический сахар).

При выполнении команды run есть обязательный параметр – это имя или id образа, который будет лежать в основе контейнера. Так же есть дополнительные параметры, которые задаются через флаги.

Давайте рассмотрим команду:

docker run -it -d --env-file .env --restart=unless-stopped --name easy_refer easy_bot_image 

Пояснение:

  1. docker run:

    • Запускает новый контейнер на основе указанного Docker-образа.

  2. -it:

    • -i (interactive): Запускает контейнер в интерактивном режиме, позволяя взаимодействовать с его стандартным вводом.

    • -t (tty): Подключает терминал к контейнеру, так что у вас будет доступ к его командной строке.

  3. -d:

    • -d (detached): Запускает контейнер в фоновом режиме (демон).

  4. --env-file .env:

    • Загружает переменные окружения из файла .env, чтобы использовать их внутри контейнера.

  5. --restart=unless-stopped:

    • Автоматически перезапускает контейнер, если он завершился с ошибкой, но не перезапускает его, если он был остановлен вручную.

  6. --name easy_refer:

    • Присваивает контейнеру имя easy_refer.

  7. easy_bot_image:

    • Указывает имя Docker-образа, который используется для создания контейнера. В данном случае это easy_bot_image.

Что делает эта команда:

  • Запускает новый контейнер на основе образа easy_bot_image.

  • Назначает контейнеру имя easy_refer.

  • Загружает переменные окружения из файла .env.

  • Запускает контейнер в интерактивном режиме с подключением терминала, но в фоновом режиме.

  • Настраивает контейнер на автоматический перезапуск, если он завершился с ошибкой, но не перезапускает его при ручной остановке.

Перед запуском моего бота необходимо самостоятельно настроить .env файл (положите его в корень бота).

Вот параметры, которые должны быть в файле:

TOKEN=bot_token
ADMINS=список телеграмм айди админов
PG_LINK=ссылка на подключение к постгрес
ROOT_PASS=доп пароль для работы с базой данных

Видим, что бот запустился. Примечательно то, что даже если бы у вас вообще не стоял Python на операционной системе - данный метод бы отработал и бот был запущен.

Давайте посмотрим какие контейнеры у нас есть (запущенные и нет):

docker ps -a

Видим, что контейнер запущен.

Давайте остановим контейнер, а после удалим его. Затем проверим контейнер:

docker stop easy_refer
docker rm easy_refer
docker ps -a

А теперь давайте создадим новый контейнер через Make:

make run

Да, получили тот же результат. Согласитесь, что это удобно.

Теперь давайте зайдем в запущенный контейнер и посмотрим, что там происходит:

docker attach easy_refer

Таким образом мы попали во внутреннюю операционную систему нашего контейнера. Внутри мы видим те же логи, что видели бы при обычном запуске. Тут же мы можем остановить контейнер комбинацией клавиш CTRL+C (если внутри контейнера не происходит ничего, то он сам останавливается) или выйти из интерактивного режима (обратно в фон вывести контейнер).

Для того нужно нажать комбинацию клавиш CTRL+p, а затем CTRL+q.

Обратите внимание. Если бы при создании контейнера мы не указали флаг -it, то возможности выйти из интерактивного режима через указанную выше комбинацию клавиш не было бы.

Давайте снова удалим наш контейнер, чтоб не занимал место, и приступим к непосредственному деплою на VPS сервер.

Отправка образа на Docker hub

  1. Выполняем авторизацию на сайте Docker hub

  2. Переименовываем свой image (образ) на имя в таком формате:

docker_username/image_name

Пример в моем случае:

docker tag easy_bot_image yakvenalexx/easy_bot_image

Авторизация:

docker login

Теперь делаем push нашего образа

docker push yakvenalexx/easy_bot_image

Видим, что все прошло успешно. Теперь давайте зайдем в профиль на docker hub и посмотрим появиился ли у нас там репозиторий:

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

docker rmi easy_bot_image yakvenalexx/easy_bot_image

Отлично! Теперь давайте войдем на свой VPS сервер (я буду пользоваться Ubuntu) и запустим там Docker (на примере вход по SSH с логином и паролем).

Вход по SSH:

ssh user@host
password

Обновление и установка Docker:

sudo apt update -y 
sudo apt upgrade -y
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce
sudo systemctl status docker

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

Установка Make:

sudo apt install make

Теперь можно проверить версии docker и make.

docker --version
make --verson

Если в обоих случаях видим версию – значит все установлено.

Все ок
Все ок

Отлично. Теперь давайте создадим папку с именем my_bot и закинем в него файл .env и Makefile.

cd ../home
mkdir my_bot
cd my_bot

Теперь скачаем образ. Для этого мы можем зайти в профиль на Docker hub и найти там кнопку копирования:

Жмем на нее и команду вставляем в терминал на VPS сервере
Жмем на нее и команду вставляем в терминал на VPS сервере
docker pull yakvenalexx/easy_bot_image

Тут обратите внимание. Проверьте, чтоб файл Makefile содержал корректное название образа. Если это не так, то откорректируйте файл на сервере через nano или закиньте корректный файл любым удобным для себя способом.

Выполняем запуск:

make run

Заглянем в контейнер и убедимся, что все работает:

make attach
Сразу будет чисто. Важно чтоб после подключения attach ваша программа выдала какие-то логи.
Сразу будет чисто. Важно чтоб после подключения attach ваша программа выдала какие-то логи.

Заключение

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

Если у вас возникли вопросы или нужна дополнительная информация, не стесняйтесь обращаться.

Хотели бы вы увидеть цикл моих статей на тему Docker? Подписывайтесь на обновления и ставьте лайки, чтобы не пропустить новые материалы. Ваша поддержка очень важна для меня, ведь без неё все усилия, потраченные на создание контента, теряют смысл.

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


  1. adron_s
    23.06.2024 05:46

    Отличная статья для начинающих разработчиков!

    Хочу указать на небольшие неточности.

    1) docker-compos, а нужно docker-compose.

    2) Собственно docker-compose упомянут, но не используется. Я думал вы его будете использовать для запуска PostgreSQL в виде сервиса (вместе с сервисом бота). Но вместо этого вы используете уже готовую внешнюю БД.


    1. volos4
      23.06.2024 05:46
      +2

      сейчас уже модно не docker-compose, а docker compose. compose - команда как часть docker.


    1. ovchinnikovproger
      23.06.2024 05:46
      +2

      Еще можно добавить, что способ деплоя не оптимален. Зачем грузить образ на Docker Hub, который отрубают для РФ, потом пляски с VPS. Воспользуйтесь Heroku, или российским аналогом - Amvera если иностранной карты нет. Просто запушите в Git код с докерфайлом, и все развернется и заработает. GitOps подход намного удобнее, три команды в консоли и все


  1. nulovkin
    23.06.2024 05:46

    Я только пытаюсь начать работать с докером. Откуда взялся докерфайл, он написан вручную или какой-то командой?


    1. yakvenalex Автор
      23.06.2024 05:46

      Докерфайл всегда пишется руками. Если вы сделаете клонирование моего бота, то там будет написанный и Dockerfile и Makefile


      1. Equillibrium
        23.06.2024 05:46
        +1

        Не всегда. Почитайте про docker init


  1. gerod
    23.06.2024 05:46

    А как получаем доступ к боту? Где настройка портов, нетворка? Или в данную статью это решили не включать?


    1. yakvenalex Автор
      23.06.2024 05:46

      Этот бот достаточно простой. Он подключается к уже готовой базе данных. В данном примере смысла в настройке портов и нетворка нет. Так что решил не грузить народ)


      1. gerod
        23.06.2024 05:46

        Я про внешний мир. https://api.telegram.org/bot*****:******/setWebhook?url=https://my.doker.server/api


        1. makapohmgn
          23.06.2024 05:46

          Бот может сам ходить за getUpdates, тогда вебхук не нужен


          1. gerod
            23.06.2024 05:46

            Спасибо!


  1. gutche1
    23.06.2024 05:46
    +1

    Отличная статья! Спасибо!

    Также было бы отлично, если бы упомянули как запустить и подключить редис. Стейты в aiogram - одна из киллерфич


    1. yakvenalex Автор
      23.06.2024 05:46

      Завтра опубликую статью о том как развернуть и запустить Redis через Docker Compose. Подписывайтесь, чтоб не пропустить)


  1. gutche1
    23.06.2024 05:46
    +1

    Отличная статья! Спасибо!

    Также было бы отлично, если бы упомянули как запустить и подключить редис. Стейты в aiogram - одна из киллерфич


    1. yakvenalex Автор
      23.06.2024 05:46

      Спасибо за отзыв) О том как настроить Redis (без докера правда) писал в одной из своих публикаций. Также в статье про FSM описание делал в контексте RedisStorage