QA Fest — конференция №1 по тестированию и автоматизации. Финальная программа уже на сайте >>
×Закрыть

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

В рамках статьи мы создадим одностраничное (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:

  1. По ссылке /db открывается web-клиент adminer для работы с PostgreSQL.
  2. По ссылке /app открывается web-клиент на VueJs, который содержит форму авторизации и после ее успешного выполнения позволяет получать/отправлять сообщения через webSocket.
  3. По ссылке /api реализовать API (python-Sanic):
    • авторизовывать пользователя из PostgreSQL;
    • данные сессии записывать в Redis.
  4. По ссылке /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 на 80-м порту. При этом все запросы, URL которых начинается с /db, перебрасываем на сервис adminer-а, который будет запущен на порту 8080. Я вынесу за рамки статьи информацию о том, что такое Docker, как связан с ним docker-compose и как это все работает. Ознакомиться с этими приложениями можно в официальной документации.

Перейдем непосредственно к реализации. На момент написания статьи версия 3.7 — последняя для спецификации содержимого docker-compose.yml. Ее и будем использовать:

Cети (networks)

Строки 2-6: мы определили 2 вида сетей, через которые будут коммуницировать наши контейнеры. Необходимость и назначение сетей я оставлю за рамками этой статьи, благо есть исчерпывающая документация.

Создавать разные сети для Dev-окружения совершенно нет необходимости. Но, как уже говорил выше, для одного разработчика хранить и актуализировать параллельно 2 сценария контейнеризации — излишняя трата времени. В рамках ограниченных ресурсов необходимо стараться делать окружение таким, каким оно будет выглядеть в продакшене с минимальными изменениями. Также можно оспорить необходимость делать веб-доступ к базе данных продакшена через adminer. Локально я предпочитаю коннектиться к базе через консоль, но иногда для оперативности — веб-доступ. Это экономит время.

Redis

Строки 9-15: стандартные инструкции создания Redis контейнера. Обратите внимание, что доступ к нему другие контейнеры могут получить из подсети «internal».

PostgreSQL

Строки 18-30: создаем контейнер на базе официального образа PostgreSQL. Обратите внимание на строки 23-25. По умолчанию перед запуском docker-compose автоматически читает переменные, заданные в .env файле, но не передает их дальше в контейнер для создания базы данных, пользователя и пароля. Для этой цели существует 2 способа. Первый — через переменную сервиса environment, где необходимо задать пару ключ-значение. Второй способ — через переменную env_file, когда мы пробрасываем все переменные файла .env непосредственно в контейнер (сделаем это чуть позже на примере серверного приложения).

В строке 29 мы «заставляем» базу данных хранить данные вне контейнера.
В строке 30 мы в момент создания контейнера запускаем SQL-инструкцию по созданию базы данных «test», которую будем использовать при написании тестов.

Adminer

Строки 33-39: поднимаем оффициальный Docker контейнер от Adminer. Обращаем внимание, что в конфигурации нужно явно указать сервис(ы), к которым он будет иметь доступ (в нашем случае это DB). Также указываем internal подсеть, по которой будет идти коммуникация.

Отступление. Наряду с Adminer на продакшене возможна контейнеризация любого другого множества вспомогательных приложений (скажем, redis-commander или phpMyAdmin). Рекомендую поднимать их в отдельной подсети, с доступом к ней только из-под VPN. Можно не заморачиваться с VPN, а ограничить доступ к определенным сервисам по IP, но такой подход менее гибок.

Строки 41-50: запускаем контейнер на базе официального образа Nginx, пробрасывая наш ранее созданный конфигурационный файл. Обратите внимание: необходимо указать связь с контейнером adminer через подсеть internal. Без этого указанный в server.conf сервис http://adminer:8080 доступен не будет.

В следующих частях статьи рассмотрим, как расширить файл server.conf, дополнить для работы с двумя другими сервисами app и api.

Этап 4. Построение и запуск контейнеров

На этапах 1-3 мы полностью подготовили необходимое для разработки окружения. Осталось запустить все и проверить, как это работает.

Теперь:

Открываем в браузере http://localhost/db и видим страницу входа в Adminer. Логинимся в базу данных при помощи значений переменных файла .env. Убеждаемся, что выполнился скрипт из директории db_entrypoint/, создавший базу test.

Итог

Весь исходный код я выложил на GitHub в ветку master. Следующая часть, чтобы можно было сравнить изменения, будет сделана в отдельной ветке. Чтобы покрутить самостоятельно, выполните из консоли:

После чего пробуйте зайти на localhost/db.


Следующие части:

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

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

LinkedIn

14 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Гарна стаття.
Єдине, що трохи незручно коли файл на GitHub називається не так як мало б бути. Наприклад: article-1-stage-31
а в статті видно, що це docker-compose.yml

да, я думал над этим во время 1й части. Мне хотелось добавить «разукрашивание» схемы файла которая у gist самоопределяется на основании названия файла. В части 2 и 3 я ушел от подобной практики, сейчас использую формат

article-X-stage-XX

Справа навіть не в розфарбовуванні, а просто у вас намішано і команди, які треба виконати і файли, які треба створити.
Для першого можна було використати HTML <code>
А от для файлів GitHub

Спасибо за статью.
Я так понимаю контейнер работает от имени рут пользователя. Например все файлы, которые будут созданы в контейнере с БД, будут созданы от имени рута. Вы как-то решали эту проблему?

Да, контейнеры физически создают свои файлы в папке .data от лица root. Но мне они совершенно не нужны. Для того чтобы работать с базой данных я использую shell, подключаясь непосредсвенно к работающему контейнеру командой docker exec -it test_db /bin/bash, а там утилитой psql «проваливаюсь» в базу. Аналогично выполняется backup базы. Надеюсь, я правильно понял ваш вопрос. Я сейчас заканчиваю как раз статью, в которой коснулся именно прикладной части работы с контейнерами, думаю в ней более развернуто получиться показать все плюшки docker-a.

Vuejs + jquery — две либы делающие одно и тоже, используйте только одну. Жду следующую часть, интересно будет посмотреть на контейнерезацию hot reload фронтенда

Vuejs + jquery — две либы делающие одно и тоже

Wut?
Are you serious?

Представляете!? Vue.js фреймоврк не монолитный, задуманный таким образом чтоб можно было быстро прототипировать — можно взять ядро отдельно как либу и работать с view и также можно взять jQuery и также работать с view слоем.
ШОК! СЕНСАЦИЯ!

Годно! Сам как раз докером занимаюсь, бизнес-логику в контейнер уже в целом запихал, но для меня это первый шаг. Конечная цель — запихнуть все Кубер и пусть он в клауде сам выделяет ресурсы и все такое — т.е. у меня будут выполняться куча однотипных задач в параллель.

Т.е. Вы держите БД postgresql целиком в контейнере?
Немного не понял этот момент.

P.S. Просто есть устоявшееся мнение, что это плохая практика, что для больших, что для маленьких БД.

Нет. В контейнере база данных не храниться. Здесь мы явно используем механизм volumes, т.е. как бы «подставляем» удобный для нас каталог, в качестве рабочего каталога для контейнера с Postgres. Описание рабочих путей контейнера я брал отсюда. Т.е. в данном случае, у нас есть в корне проекта папка .data, в которой хранятся как данные Postgres, так и Redis.

Год назад выкладывал стартер для docker, k8s, spring boot, elk. Для любителей еще можно в вагранте все делать. Пользуйтель кому надо github.com/iazarny/msstarter

Жду продолжения!!!

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

Подписаться на комментарии