Привет!
Сегодня разберем заключительную часть цикла «Первые шаги в aiohttp». В первой статье мы создали и настроили проект, а во второй подключили базу данных.
В этой части мы рассмотрим размещение нашего веб-сервиса в Интернете, используя платформу Heroku.
Что будет в статье:
Эта статья дополняет код второй части:
Код части 2
Если хотите лучше разобраться в асинхронном программировании, приходите к нам на курс в KTS, где мы подробно разберем эту тему.
1 — Подготовка
Прежде всего необходимо зарегистрироваться на Heroku. Сделать это можно по этой ссылке.
Чтобы работать с Heroku из терминала, установите интерфейс командной строки Heroku CLI. Через него вы сможете работать с приложением на платформе Heroku. Инструкции по установке можно посмотреть здесь.
Обратите внимание: Heroku CLI требует установленный Git. Как его установить, читайте здесь.
Для сборки приложений также будет необходим Docker, его можно скачать для установки здесь.
2 — Создаем необходимые файлы
Прежде всего необходимо создать еще один конфигурационный файл, который будет использовать Heroku. В папке config/ создадим файл heroku_config.yaml и запишем в него следующее:
common:
port: $PORT
postgres:
database_url: $DATABASE_URL
require_ssl: true
Знак доллара значит, что значение — переменная окружения, которая будет заменена в процессе публикации приложения.
Рассмотрим конфигурацию детальнее:
port: $PORT — Heroku каждый раз будет ждать, что ваше приложение запущено на новом порту, который выделила платформа. Поэтому конкретное значение записать нельзя.
database_url: $DATABASE_URL — url для подключения к Postgresql-базе данных. Мы воспользуемся бесплатным дополнением (addon) и создадим базу на платформе Heroku. В настройках Heroku мы можем узнать текущий адрес базы, который нам выделили, но он может динамически меняться, поэтому это значение тоже зависит от окружения.
require_ssl: true — база данных не находится в локальном месте, поэтому общение с ней происходит по Интернету. Чтобы данные не перехватили, нам нужно их шифровать по протоколу SSL. Heroku, кстати, вообще не позволяет устанавливать незащищенные соединения.
Чтобы Heroku смог применить миграции нашего приложения к базе в момент запуска, надо добавить в корень проекта файл run.sh и написать в нем:
# подставляем переменные из окружения в подготовленный конфиг
cat config/heroku_config.yaml | envsubst > config/config.yaml
# необходимо для того, чтобы alembic смог найти наше приложение
export PYTHONPATH=.
# обновляем версию базы до последней
alembic upgrade head
# запускаем сервер
python main.py
3 — Пакуем приложение в Docker-контейнер
Перед публикацией мы упакуем наше приложение в Docker-контейнер, потому что это один из самых простых и универсальных способов для публикации. Docker-контейнер — виртуальная операционная система, которая обладает своей памятью и не хранит данные на жестком диске без дополнительной ручной настройки.
В корне приложения создадим файл Dockerfile:
# Docker-команда FROM указывает базовый образ контейнера
# Наш базовый образ - это Linux с предустановленным python-3.7
FROM python:3.7
# gettext-base нужен для того, чтобы установить envsubst
RUN apt update && apt -y install gettext-base
# Скопируем файл с зависимостями в контейнер
COPY requirements.txt .
# Установим зависимости внутри контейнера
RUN pip install -r requirements.txt
# Скопируем остальные файлы в контейнер
COPY . .
# разрешаем наш скрипт на исполнение операционной системой
RUN chmod +x run.sh
# запускаем скрипт
CMD ["./run.sh"]
Рассмотрим каждую команду:
FROM python:3.7
— берем образ ОС с предустановленным python версии 3.7.RUN apt update && apt -y install gettext-base
— устанавливаем необходимые пакетыCOPY requirements.txt .
— копируем локальный файл в виртуальный контейнерRUN pip install -r requirements.txt
— устанавливаем необходимые python-зависимостиCOPY . .
— копируем остальные файлы нашего проекта в контейнер. Docker создает слой кэша на каждую команду, кроме команд запуска. Поэтому удобнее, если на первых строчках файла стоят команды, которые работают с редко изменяемыми данными — например с установкой модулей. А после них уже можно писать команды с часто изменяемыми — например? с копированием кода сервиса.
Пример: если мы не будем менять requirements.txt, то после первой сборки Docker будет заново использовать слои, созданные до командыRUN pip install -r requirements.txt
, что заметно ускорит сборку.RUN cat config/heroku_config.yaml | envsubst > config/config.yaml
— заменяем наш конфигурационный файл, подставляя на место значений со знаком$
одноименные переменные из окружения, используя pipe (знаки|
и>
).
Пример: у нас в файле записана строкаport: $PORT
. Обрабатывая ее, envsubst постарается найти в окружении переменную с именем $PORT и подставить ее значение вместо текста “$PORT” в файле. Heroku сам добавит в наш контейнер все необходимые для работы переменные, например,$PORT
и$DATABASE_URL
, а envsubst подставит их на нужные места в файле конфигурации.CMD ["./run.sh"]
— запускаем наш скрипт, в котором выполняются миграции и запускается приложение. Это команда будет выполнена, когда мы или Heroku будем запускать контейнер с помощью командыdocker run
.
Подробней о Dockerfile, его командах и базовых образах можно прочитать в официальном руководстве.
В проекте может быть несколько приложений одновременно, каждое из которых будет упаковано в отдельный Docker-контейнер. Например, API-сервис и микросервис для загрузки файлов. Чтобы Heroku мог понять, какое приложение нужно собирать и публиковать, добавьте в корень файл docker-compose.yaml со следующим содержимым:
version: '3'
services:
web:
build: .
Docker-compose — инструмент для одновременного запуска и управления несколькими Docker-контейнерами, объединенными между собой. Например, с помощью docker-compose можно объединить контейнер нашего сервиса с контейнером базы данных, указав общую для них сеть, в которой они смогут «общаться», а также привязать volume — постоянное хранилище данных, которое будет автоматически создано или подключено. Мы создавали Volume во второй части. Также с помощью docker-compose можно указать зависимость контейнеров друг от друга. Это позволит избежать ситуаций, когда приложение запущено раньше базы данных, из-за чего происходит критическая ошибка при подключении. Это лишь часть способов применения docker-compose. Подробнее — в официальном руководстве.
Если в проекте несколько приложений и несколько Dockerfile, их необходимо публиковать по очереди. Поэтому каждый service в docker-compose.yaml должен иметь уникальное имя. В нашем случае приложение только одно, и в файле мы назвали его web.
4 — Публикуем приложение с помощью Heroku
Шаг 1: Создаем приложение
Для этого в терминале выполним команду:
heroku create forum
Если вы еще ни разу не использовали Heroku, в консоли должна появится такая надпись, :
Нажмите любую кнопку (кроме q) и откроется браузер со страницей входа в Heroku. После входа на сайте вы можете закрыть браузер и продолжить работу в консоли. Также можно явно авторизоваться, вызвав команду heroku login
.
При выполнении команды create
Heroku создает новый git-репозиторий для проекта или привязывает новый удаленный репозиторий к существующему локально.
forum
— название приложения, которое одновременно будет префиксом в url: после публикации приложение будет доступно по url forum.heroku.com. Если не указать имя приложения при выполнении команды create
, Heroku сгенерирует случайное название.
Если указанное вами имя уже используется, то Heroku напишет об этом.
Шаг 2: Выделяем бесплатную площадку для работы с базой данных
Нужно «попросить» Heroku о выделении базы командой:
heroku addons:create heroku-postgresql:hobby-dev
Рассмотрим команду подробнее:
addons:create
— добавляем к нашему Heroku-приложению новое дополнение, которое называется heroku-postgresql
, используя беплатный тарифный план hobby-dev
.
Тарифный план позволяет выбрать требуемую конфигурацию выбранного дополнения. В нашем случае подойдет бесплатная postgresql-база с небольшой производительностью и вместимостью, но для больших проектов необходима более ресурсоемкая база из платного тарифа.
После выполнения команды перейдите на сайт Heroku, зайдите в приложение и проверьте, что дополнение действительно подключилось:
При клике на Heroku Postgres вы увидите детальную информацию о дополнении. На вкладке settings можно посмотреть реальные данные для подключения к базе:
Шаг 3: Авторизуемся в хранилище образов
У Heroku есть собственное хранилище, которое называется Registry. В Registry хранятся собранные, но не запущенные контейнеры — образы. Для выполнения следующего шага нам необходимо авторизовать наш CLI в этом хранилище. Это можно сделать командой:
heroku container:login
В случае успеха вы должны увидеть "Login Succeeded".
Шаг 4: Собираем и загружаем образ приложения в хранилище
Необходимо собрать образ и отправить образ в хранилище Heroku, которое называется Registry. Сборка и отправка образа в Registry обычно выполняется командой push
, а получение командой pull
. Соберем и отправим образ (убедитесь, что Docker запущен перед выполнением этой команды):
heroku container:push web
Если взглянуть на логи после выполнения этой команды, мы увидим два этапа:
Сборка образа
Отправка образа
Действительно, сначала Heroku запустил сборку, выполнив все команды в Dockerfile, кроме последней — команды запуска приложения. Потом образ получил уникальный идентификатор, тесно связанный с его содержимым. Это сделано, чтобы в Registry не хранились абсолютно одинаковые по содержимому образы под разными именами. На последнем этапе Heroku загрузил локально собранный образ в Registry.
Шаг 5: Публикуем
Мы сделали все необходимое. Осталось попросить Heroku запустить контейнер и открыть к нему доступ через Интернет. Выполним команду:
heroku container:release web
Heroku опубликует последний добавленный в Registry образ приложения web.
Шаг 6: Любуемся
После завершения публикации приложение можно посмотреть в Интернете. В терминале напишем:
heroku open
Должна открыться страница в браузере с опубликованным приложением:
Шаг 7: Дополняем
Если вы сделали какие-то изменения в коде, для публикации новой версии достаточно выполнить две команды:
heroku container:push web
heroku container:release web
5 — Исправляем ошибки
Если на странице вашего сервиса появилось сообщение об ошибке, можно посмотреть ее детали, написав в консоли:
heroku logs --tail
Эта команда выведет последние сообщения из вашего приложения и будет транслировать их в реальном времени.
Какие проблемы могут возникнуть при публикации?
Проблема 1: Приложение не смогло подключиться к выделенному ему порту в течении 60 секунд:
Решение: Необходимо проверить, что содержимое файла heroku_config.yaml соответствует приведенному в статье, а в частности строкаport: $PORT
.
Проблема 2: Python попытался выполнить миграции, но не смог получить доступ к базе данных:
Решение: Проверить, что вы действительно подключили дополнение PostgreSQL в Heroku. Если с дополнением все в порядке, тогда необходимо проверить, что heroku_config.yaml существует и соответствует примеру в статье.
Проблема 3: Приложение не смогло запуститься из-за ошибки при настройке:
Решение: Ошибка скорее всего воспроизведется при локальном запуске приложения. Изучив локальные логи или логи Heroku, необходимо исправить ошибку.
Проблема 4: произошла программная ошибка, детали ошибки будут описаны в выведенных логах:
Решение: чтобы быстрее найти проблему необходимо запустить приложение локально, в режиме дебага, и вручную послать запрос во View, в котором произошла эта ошибка.
6 — Вместо заключения
Поздравляю! Теперь любой человек, у которого есть доступ в Интернет, может зайти на ваш сайт и оставить новую запись на стене.
В ходе этой статьи мы:
Добавили конфигурационный файл для другого окружения и научились подменять его при публикации
Использовали Docker и Docker-compose для упаковки нашего приложения в контейнер
Создали приложение на Heroku и подключили к нему базу данных
Опубликовали наш сервис в Интернете
Исходный код для третьей части цикла статей можно найти в этом репозитории.
Полный цикл статей «Первые шаги в aiohttp»:
Другие наши статьи по бэкенду и асинхронному программированию для начинающих:
Другие наши статьи по бэкенду и асинхронному программированию для продвинутого уровня:
? Как разобраться в асинхронном программировании на Python?
Асинхронный подход в Python реализуется через модуль Asyncio — это стандартная возможность языка. Если разработчик планирует развиваться в профессии и браться за более сложные задачи, рано или поздно ему придётся освоить Asyncio. Лучше рано :)
Разобраться в асинхронных принципах работы систем и научиться применять их на практике, можно на нашем курсе по асинхронному программированию на Python. В течение курса мы рассмотрим теорию работы асинхронных инструментов вообще и в Python в частности, а также напишем телеграм-бота и event loop.
? Посмотреть программу: https://vk.cc/cmUy1v