В рамках статьи мы создадим одностраничное (SPA) приложение с использованием VueJs. Оно будет общаться с WebSocket-сервером, предварительно авторизировавшись через backend api. WebSocket-сервер и api реализуем при помощи фреймворка Sanic.
В этом материале хочу поделиться наработками и изложить свое субъективное видение связки технологий, которая, как мне кажется, наиболее эффективна для fullstack разработчика с акцентом на Python. Постараюсь быть максимально кратким.
Кратко обо мне
Я вошел в веб 15 лет назад, во времена тотального доминирования PHP в качестве серверной платформы. Впоследствии я перешел на Python. Разработка web-приложений из монолитного кода начала «растекаться», сначало на серверную и интерфейсную части, потом стало модным растягивать по микросервисам еще и backend. Сейчас зачастую мне приходиться брать на себя роль бекенд- и фронтенд-разработчика. При этом сильно прокачиваются навыки DevOps, чтобы заставить кучу всего разрозненного работать вместе.
Стек, который я использую в работе сейчас:
- Python framework: Flask; Sanic;
- DB: PostgreSQL, Redis, RebbitMQ, MongoDB, MySQL;
- JavaScript: VueJs, Jquery;
- DevOps: Docker, Ansible.
Постановка задачи
Пишем приложение для http://localhost:
- По ссылке /db открывается web-клиент adminer для работы с PostgreSQL.
- По ссылке /app открывается web-клиент на VueJs, который содержит форму авторизации и после ее успешного выполнения позволяет получать/отправлять сообщения через webSocket.
- По ссылке /api реализовать API (python-Sanic):
- авторизовывать пользователя из PostgreSQL;
- данные сессии записывать в Redis.
- По ссылке /ws реализовать WebSocket server, принимающий запросы от авторизованного через API клиента и отвечающий ему.
Аргументация выбранного стека
Почему Redis?— Тут без комментариев.
Почему PostgreSQL?— Благодаря огромному опыту работы с MySQL, эмпирическим путем пришел к наиболее оптимальному решению.
Почему VueJs?— Не хотелось бы священных войн, изложу кратко свое видение. Для меня, как для fullstack разработчика с упором на backend, VueJs оказался весьма прост в освоении и понятен в своей концепции. Более популярные фреймворки React и особенно Angular имеют, на мой взгляд, несопоставимо более высокий порог вхождения. Они требуют гораздо больше времени на изучение и удержание достаточного уровня квалификации.
Почему backend на Sanic?— В среде Python в последнее время произошла довольно серьезная революция, связанная с развитием асинхронного программирования. У меня был опыт написания кода на Twisted и Tornado, но современная реализация async/await выше всяких похвал. Как грибы после дождя появились различные асинхронные веб-фреймворки. Но мой выбор пал на Sanic, потому как он практически полностью копирует интерфейсность Flask (в котором у меня наибольший опыт работы последних лет), при этом добавляет приложению огромную производительность и функциональность (тот же WebSockets).
Нюанс: Sanic еще весьма молод, под него мало пакетов. Производятся попытки миграции c Flask такого полезного расширения, как Restfull. На момент написания статьи там еще далеко даже до бета-релиза. Но, по моему мнению, сам фреймворк стабилен и великолепен — можно пользоваться.
Реализация и вводные данные
Мой рабочий компьютер:
Этап 1: Инициализация проекта
Этап 2. Как это все будет работать
Согласно постановки задачи, у нас есть 2 приложения: Redis, PostgreSQL, которые должны быть изолированы от доступа извне, но должны быть доступны для использования из Python. C другой стороны, есть 4 точки входа, которые коммуницируют с внешним миром, то есть доступны через браузер. Это /app, /api, /ws, /db -за каждым из этих интерфейсов стоит свой работающий процесс.
Для /app — это процесс webpack watcher, задача которого — остлеживать изменение кода, который мы будем вносить, и налету преобразовывать его при помощи подключенных плагинов (broserify, cssminify и т. д.) в исполняемый браузером код.
Для /apiи /ws — это будет один общий python worker, который также будет отслеживать изменения и перезапускать самого себя.
Все вышеописанные правила довольно просто реализовать при помощи Nginx, проксируя URL-запросы к им соответствующим демонам, которые работают на выделенных портах.
Этап 3. Контейнеризация
Поместим в файл .env, созданный ранее, несколько переменных окружения, которые понадобятся нам при построении контейнеров:
Обращаю внимание, что этот файл лишь в рамках этой статьи был добавлен в Git-репозиторий. В реальной жизни ему не место в репозитории. На сервере этот файл создается ручками, со своими значениями переменных. Далее:
В конфигурации сервера мы декларируем запуск Nginx на
Перейдем непосредственно к реализации. На момент написания статьи версия 3.7 — последняя для спецификации содержимого docker-compose.yml. Ее и будем использовать:
Cети (networks)
Строки
Создавать разные сети для Dev-окружения совершенно нет необходимости. Но, как уже говорил выше, для одного разработчика хранить и актуализировать параллельно 2 сценария контейнеризации — излишняя трата времени. В рамках ограниченных ресурсов необходимо стараться делать окружение таким, каким оно будет выглядеть в продакшене с минимальными изменениями. Также можно оспорить необходимость делать веб-доступ к базе данных продакшена через adminer. Локально я предпочитаю коннектиться к базе через консоль, но иногда для оперативности — веб-доступ. Это экономит время.
Redis
Строки
PostgreSQL
Строки
В строке 29 мы «заставляем» базу данных хранить данные вне контейнера.
В строке 30 мы в момент создания контейнера запускаем SQL-инструкцию по созданию базы данных «test», которую будем использовать при написании тестов.
Adminer
Строки
Отступление.Наряду с Adminer на продакшене возможна контейнеризация любого другого множества вспомогательных приложений (скажем, redis-commander или phpMyAdmin). Рекомендую поднимать их в отдельной подсети, с доступом к ней только из-под VPN. Можно не заморачиваться с VPN, а ограничить доступ к определенным сервисам по IP, но такой подход менее гибок.
Строки
В следующих частях статьи рассмотрим, как расширить файл server.conf, дополнить для работы с двумя другими сервисами app и api.
Этап 4. Построение и запуск контейнеров
На этапах
Теперь:
Открываем в браузере http://localhost/db и видим страницу входа в Adminer. Логинимся в базу данных при помощи значений переменных файла .env. Убеждаемся, что выполнился скрипт из директории db_entrypoint/, создавший базу test.
Итог
Весь исходный код я выложил на GitHubв ветку master. Следующая часть, чтобы можно было сравнить изменения, будет сделана в отдельной ветке. Чтобы покрутить самостоятельно, выполните из консоли:
После чего пробуйте зайти на localhost/db.
Продолжение следует...