Создаем приложение: Docker, VueJs и Python-Sanic. Часть 2

В предыдущей статье мы рассмотрели, как быстро поднять Docker окружение для разработки, используя возможности docker-compose. В этой статье окунемся в разработку backend-а и «контейнеризируем» API, написанное на Python.

Итак, уточним задачу, которую предстоит реализовать в API:

  1. Пусть новое приложение по ссылке /api/v1.0/user/auth принимает POST запрос с параметрами username и password и, в случае совпадения данных доступа с учетной записью в таблице users, возвращает token идентификации.
  2. Данные об авторизованном пользователе (users.id) пишем в Redis, где ключом (key) будет token, созданный в пункте 1.

Этап 1. Небольшая автоматизация рутины

Во время разработки я довольно часто пользуюсь возможностями утилиты make, позволяющей намного сократить «вербальность» некоторых команд. Для этого в корне проекта создаю Makefile с набором удобных инструкций-сокращений. Приведу список наиболее полезных для меня:

make up — просто запускает, а make upb — еще и полностью перестраивает перед запуском все существующие контейнеры.

make stop — останавливает все работающие контейнеры.

make db — подключаемся к БД PostgreSQL при помощи консольного клиента pgSQL. Обратите внимание, что мы в первой строчке Makefile «заинклюдили» все переменные окружения, которые у нас есть в .env файле. Поэтому здесь могут быть довольно экзотические инструкции, список которых ограничен лишь фантазией разработчика. К примеру, у меня ещё здесь находятся инструкции по деплою кода на продакшен при помощи ansible).

make r — быстрый способ посмотреть значения в базе данных Redis через консольную утилиту redis-cli.

make test — запускает unit/integrity tests, которые нам еще предстоит написать для нашего /api.

Последняя значимая make-инструкция — это (пример) make b c=test_api — подключиться к bash-консоли любого контейнера с явно указанным именем.

Этап 2. Создаем минимально рабочее API

Итак:

Перед тем как мы начнем реализовать логику API, нам необходимо сделать минимально работающее Sanic-приложение, контейнеризировать его и настроить проксирование всех запросов, начинающихся на /api в контейнере с nginx на наш новый контейнер. Для этого «утащим» пример hello world отсюда и слегка модифицируем его. В каталоге api нашего проекта, создаем файл run.py со следующим содержимым:

Теперь нам нужно запустить run.py внутри контейнера. Для этого создаем api/Dockerfile со следующим содержимым:

В этом файле мы указываем компоновщику, что для создания нашего контейнера нужно взять последний Docker образ Python версии 3.6.7. Далее создаем рабочий каталог /app для нашего микросервиса.

Отступление. Я взял за правило помещать весь рабочий код приложения в корневую папку /app контейнера. Аналогично будет и в примере с клиентским app, которое будет реализовано в следующей части.

Далее идет установка дополнительного пакета gcc, без которого не получится инсталлировать некоторые pip-пакеты из api/requirements.txt, которые мы указали чуть выше, при его создании. Далее ничего особенного, перейдем к настройке файла docker-compose.yml.

Этап 3. Добавляем api в docker-compose.yml

Здесь в 4-й строке мы «заставляем» docker-compose изменить контекст и пройти по указанному адресу для чтения инструкций, созданному нами во 2-м этапе «Dockerfile», который создает контейнер с работающим приложением. Особо важной для нас, как для разработчиков, является директива tty, которая позволяет подключаться (docker attach) к консоли вывода работающего процесса в контейнере. Эта жизненно необходимая для dev разработки инструкция позволит нам «дебажить» и «тюнинговать» наше приложение.

Кроме того, мы указали что наш сервис «связан» (links) c контейнерами db и redis через подсеть (network) internal.

В строчке 7 мы «пробрасываем» папку api «внутрь» контейнера (механизм volumes).

В строчке 14, как я уже рассказывал в первой части, мы пробрасываем и делаем доступными для использования приложением переменных окружения из файла .env, созданного в прошлой части. Одна из этих переменных (API_MODE), уже используется в run.py.

Этап 4. Изменяем конфигурацию nginx

Всё, что нам осталось сделать, — добавить правила проксирования для сервиса nginx. Для этого отредактируем файл nginx/server.conf, добавив:

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

Этап 5. Остановка и перестройка контейнеров

С учетом вышеописанного в статье, у нас всё готово для запуска:

В браузере вбиваем http://localhost/api, результатом должно быть {"hello":"world«}

Этап 6. Как этим пользоваться

Благодаря переменной окружения API_MODE=dev из файла .env, мы запустили приложение в режиме debug. Фреймворк sanic, как и множество других Python Web-фреймворков, поддерживает hot-reload. То есть сервер приложения автоматически будет перезапускаться, как только мы изменим один из файлов, связанных с run.py. Проверить это довольно легко: исправьте «world» на «world!» в коде run.py и тут же обновите страницу браузера. Вы увидите, что информация обновилась.

Но перезагрузки кода не достаточно. Нам необходимо «дебажить» код и где-то видеть логи приложения. Для этого существуют две команды:

Первая из них подключается к консольному выводу работающего процесса (чтобы отключиться Ctrl+C), куда мы, при помощи стандартного пакета logging (или просто print), можем выводить любую debug-информацию. Вторая команда необходима для тех случаев, когда мы ловим fatal, «не совместимый с жизнью самого процесса». Вывод этой команды показывает нам backtrace, предшествовавший ошибке.

На заметку. Я выше обращал внимание на инструкцию tty=true. Как раз она позволяет делать «безболезненный» detach по Ctrl+C, не уничтожая сам процесс.

Этап 7. Авторское отступление

Лично я, когда программирую backend, то консоль с выводом (docker attach test_api) у меня открыта в терминальном окне редактора кода. Еще, для поддержки технологии intellisense, которая есть во многих популярных редакторах, я внутри папки api создаю виртуальное окружение .venv, в которое устанавливаю те же пакеты из requirements.txt, что установлены в самом контейнере:

В корневой директории в файле .gitignore указана инструкция пропуска всех папок и файлов, начинающихся с «.», поэтому можно не переживать, что «мусор» попадет в репозиторий.

Этап 8. Реализация логики

В рамках статьи невозможно делать полный копипаст кода, который я написал (много файлов и большой объем), поэтому, как говорил в прошлой статье, будет ссылка на pull request, где можно посмотреть изменения, которые были детально описаны выше. Прокомментирую наиболее важные моменты.

Был добавлен файл api/application.py, содержащий функцию-фабрику, которую будем использовать в 2-х местах: в run.py для сервера приложения и в conftest.py — для создания экземпляра (fixture) приложения, для написания интеграционного теста. Хотел бы обратить особое внимание на функцию db_migrate. Фактически она делает то же самое, что и расширение alembic, которое помогает делать миграцию схемы БД между различными ветками кода. Я реализовал схему миграции следующим образом:

Немного пояснений. В момент перезапуска сервера система «смотрит» в БД в поисках значения таблицы version. Если такой таблицы нет, текущая версия определяется как «0» и приложение автоматически запускает все скрипты из ветки «up» до тех пор, пока значение ключа из SCHEMA не станет равным LATEST_VERSION. Последняя успешная версия фиксируется в version. Обратный процесс аналогичен. Изменяя значение LATEST_VERSION, функция миграции «спускается» вниз на любую версию. При этом она запускает все скрипты из «down», вплоть до полной очистки базы данных, при условии, если LATEST_VERSION указываем равным «0».

Еще хотелось бы обратить внимание на то, что доступ к версиям api реализован при помощи механизма blueprint, который выбран из-за возможной версионности api в будущем.

На заметку. Когда пишем интеграционные тесты, то тестируем URL локального сервера, то есть тестируем /v1.0/user/auth. Но когда в следующей статье перейдем к реализации frontend, мы будем обращаться к «api» через прокси-сервер nginx в формате /api/v1.0/user/auth.

Этап 9. Запуск

Следующая статья будет завершающей. В ней напишем WebSocket сервер fronted на VueJs и заставим всё это работать вместе.

Похожие статьи:
SKILLUP — ОФИЦИАЛЬНЫЙ ПАРТНЕР USQB-ЦЕНТРА В УКРАИНЕ Ближайший старт курса в Киеве — 05.08.2016 Приглашаем вас пройти подготовительный курс "...
В этой статье я попытаюсь рассказать про принцип инверсии зависимостей (Dependency inversion principle, далее DIP). В статье будут упомянуты уровни...
Встречайте свежий выпуск дайджест интересных материалов из мира AI & ML и не забывайте подписываться...
Представляем новую статью из серии «Карьера в IT». Она посвящена должности Support Engineer — этот специалист...
Компания Lenovo объявила о выходе на российский рынок многорежимных ноутбуков YOGA 500. Модели представляют...
Яндекс.Метрика