Сервис на языке Dart: введение, инфраструктура бэкэнд

в 13:09, , рубрики: dart, docker, flutter, fullstack development, nginx, SURF, Блог компании Surf, разработка мобильных приложений
Оглавление

1. Введение
2. Backend
2.1. Инфраструктура.
2.2. Доменное имя. SSL.
2.3. Серверное приложение на Dart.

3. Web
3.1. Заглушка «Under construction»

4. Mobile

Введение

Меня, Flutter-разработчика, знакомые часто спрашивают: «Что же такое язык Dart?». Качают головой со словами: «А вот Петя серьёзные транспорты на Java пишет, а в Яндексе вообще плюсы в проде...». Ну что ж, пожалуй, действительно, Dart далёк от практик «фабрик для создания фабрик» из Java. Однако если стоит задача реализовать клиентские приложения сразу для нескольких платформ, не утонув в потоке задач по синхронизации разработчиков разных целевых ОС; создать целостный UI, узнаваемый, но специфичный для Android, iOS и веб и в целом уложиться в адекватные бюджет и сроки, — здесь Flutter не имеет конкурентов. И эти вопросы стоят вдвойне если у вас… стартап.

Итак, легенда: некий стартап решил создать новый сервис… ну, например, для

обмена списками покупок

Такая себе идея для стартапа, я знаю, но если я выпущу ещё один ToDo лист в этот мир, мне будет стыдно :)

между пользователями сервиса. Цель стартапа — выпустить MVP за три месяца на трех платформах (плюс четвертая — сервер, конечно).

10 лет назад я бы сказал, что этот кейс не имеет решения и постарался бы держаться от него подальше, 3 года назад решением мог стать стек ReactNative/React/NodeJs, в 2020 году для этого есть Dart. Добро пожаловать в атмосферу разработки альфа версии сервиса, я постараюсь наглядно пройти и объяснить весь процесс разработки. Код всех приложений будет выложен в паблик. Комментарии, включая набросы и холивары, приветствуются. Спросить автора «по существу» или просто посоветоваться можно в Telegram канале нашего отдела.

Сервис на языке Dart: введение, инфраструктура бэкэнд - 1

Инфраструктура бекэнд

Типовым способом размещения серверного приложения является, конечно, VPS (виртуальный приватный сервер). Фактически — это часть физического сервера в дата центре, ресурсы которого (ядра процессора и оперативная память) разделены с помощью технологии виртуализации (о наиболее распространённых технологиях аппаратной виртуализации можно почитать здесь XEN, KVM). Внимание: технологии программной виртуализации (OpenVZ, Virtuozzo) для нашей задачи могут не подойти из-за конфликтов с Docker и агрессивным оверселлингом (зачастую, при внимательном прочтении договора аренды такого VPS, можно обнаружить, что провайдеры гарантируют «не менее 5%» (!) утилизации ядра арендуемого процессора. Это означает, что провайдер планирует продать наше ядро процессора 20 (!) раз).

Итак, приобретём бюджетный VPS со следующими характеристиками: 1 ядро процессора, 1ГБ оперативной памяти, 10ГБ накопителя (в моём случае, это гибридный HDD). В качестве операционной системы выберем Ubuntu, желательно одной из LTS версий. После чего в электронную почту придёт сообщение об активации сервера с логином и паролем доступа по SSH (зашифрованный доступ к консоли операционной системы нашего VPS) в формате SSH:

IP-адрес: 91.230.60.120
Пользователь: root
Пароль: <Пароль>

Проверим подключение, введя в командной строке:

ssh root@91.230.60.120

и, по запросу:

password: <Пароль>

Результатом должен быть вывод сведений о виртуальном сервере и поле ввода внизу:

Server is hosted by хххххххххх

Hostname: 91.230.60.120
Kernel: 3.19.0-22-generic (Ubuntu хх.хх LTS)
Uptime: 09:07:06 up 3 days, 17:17, 1 user, load average: 0.00, 0.01, 0.05
CPU: Intel® Xeon® CPU 0 @ 2.00GHz (1 cores)
Memory: 989 MB total / 723 MB free

root@91.230.60.120:~$

Поздравляю, наш виртуальный сервер создан и доступен для работы.

Теперь определимся со структурой бекэнд. Нам понадобится HTTP сервер. Мы будем использовать NGINX. Его задачами будут:

  1. Раздача статических файлов (файлы веб приложения).
  2. Раздача служебных ресурсов, например, файлов подтверждения владения доменом для мобильных приложений, сведений о владельце для получения SSL сертификатов Let’s encrypt и пр.
  3. Reverse proxy для доступа к серверным приложениям.
  4. Шифрование соединений — https.

Два серверных приложения:

  1. Приложение регистрации и авторизации пользователей. Назовём его auth_app.
  2. Приложение с данными. Назовём его app.
  3. Для каждого из приложений п.2 нам понадобится отдельная база данных PostgreSQL.
  4. Приложение для автоматического получения и обновления сертификатов шифрования SSL (в следующей статье).

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

image

Разработку будем вести в IDE Visual Studio Code от Microsoft, которая, благодаря множеству доступных плагинов, позволит работать со всеми необходимыми технологиями. Также необходимо установить следующие расширения:

После перезапуска VScode подключимся к нашему VPS. Нажимаем F1 и начнем вводить команду:

Remote-SSH: connect to  Host…

далее новое подключение:

+ Add New Ssh Host

затем:

ssh root@<ip-адрес сервера>

Откроем окно терминала VScode (Menu/Terminal/New terminal) и проверим системные ресурсы командой:

top

Готово, доступ к консоли и файловой системе VPS получен:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 3

Сервис на языке Dart: введение, инфраструктура бэкэнд - 4

Утилита top будет использоваться довольно часто, поэтому установим её псевдографическую версию htop:

Ctrl-C #Завершаем выполнение утилиты top

apt-get update #Обновляем установленные пакеты

apt-get install htop #Устанавливаем htop 

htop #Запускаем 

image

Теперь необходимо установить Docker и Docker compose:

Ctrl-C #Завершаем выполнение утилиты htop

Поскольку docker отсутствует в официальном репозитории Ubuntu, установим дополнительный репозиторий

apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common #Устанавливаем необходимые утилиты 

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - #Устанавливаем ключ репозитория docker 

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" #Добавляем репозиторий 

apt-get install docker-ce docker-ce-cli containerd.io #Устанавливаем

curl -L "https://github.com/docker/compose/releases/download/1.26.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose #Скачиваем менеджер Docker compose 

chmod +x /usr/local/bin/docker-compose #Устанавливаем разрешение для загруженного файла «исполняемый файл»

ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose #Добавляем символьную ссылку в директорию исполняемых файлов 

 docker  --version #Проверяем

docker-compose --version

Отлично, сервер готов к тестовому развёртыванию сервиса.

Теперь установим Docker desktop на нашем локальном ПК для разработки. Установщик для Windows 10, версия для MacOS здесь. Будет установлен не только Docker, но и Docker toolbox, в который входят Docker compose и графические утилиты для работы с контейнерами.

Откроем новое окно VScode, Menu/File/Open folder… Создадим новую папку нашего проекта, например, Srv и откроем её. В этой папке создадим файл docker-compose.yaml:

image

В этом файле описывается сценарий запуска контейнеров сервиса, их зависимости, переменные, команды, сети, хранилища и пр.

Здесь необходимо остановиться и уточнить разницу между образом и контейнером Docker. Контейнер — это приложение + его зависимости (например, пакеты и библиотеки) + операционная система, которую можно с помощью Docker запустить как обычное приложение. А образ — это подготовленный к запуску и упакованный в архив контейнер. Таким образом, весь наш бекэнд будет представлять собой набор контейнеров с описанием сценария их запуска.

Первым контейнером, который мы запланировали станет HTTP сервер NGINX. И давайте подготовим необходимый образ… или нет? Дело в том, что для очень многих веб приложений и сред исполнения их разработчиками или комьюнити уже собраны необходимые образы и размещены в публичном реестре DockerHub. Разумеется такой широко используемый образ уже собран и ожидает нас по этой ссылке.

Обратите внимание на список — это различные версии, они отличаются и версиями самого NGINX и дополнительными инструментами (например, установленным PERL). Для разработки можно использовать тэг «latest» (последняя стабильная версия на момент запроса образа), но для развёртывания на сервере, конечно, стоит использовать конкретную версию. В данный момент это образ nginx:1.19.0.

Здесь и далее необходимые пояснения к содержимому docker-compose.yaml я буду указывать в комментариях в самом листинге файла:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 7

Сохраним изменения в файле, откроем консоль VScode и выполним команду запуска сценария

docker-compose up

Этой командой был не только запущен сценарий, но и вывод консоли контейнера был направлен в консоль хоста. Теперь, если в адресной строке браузера обратиться к локальному хосту на порте 8081 будет получена стандартный ответ NGINX:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 8

Наш первый контейнер уже работает. Обычно запуск сценария выполняется командой:

docker-compose up -d

это позволяет запустить контейнеры в режиме сервиса (без вывода в консоль). Остановка контейнеров сценария выполняется командой:

docker-compose down

Для тестирования http запросов в VScode есть удобное расширение REST client.

Установим его и напишем первый отладочный тест нашего сервиса. Для этого создадим файл client.http в папке test/http_dev/:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 9

Сервис на языке Dart: введение, инфраструктура бэкэнд - 10

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

Теперь давайте заглянем внутрь контейнера. Остановим выполнение сценария в консоли:

Ctrl-C

и запустим с флагом:

docker-compose up -d

Теперь запросим список выполняемых в данный момент контейнеров (процессов):

docker-compose ps

Сервис на языке Dart: введение, инфраструктура бэкэнд - 11

В списке выполняемых только один контейнер. Давайте откроем его:

docker exec -it srv_web_1 bash

Эта команда выполняет (exec) приложение bash (командная оболочка Linux) в контейнере srv_web_1 и не дает закрываться консоли (флаги -it):

Сервис на языке Dart: введение, инфраструктура бэкэнд - 12

Команда ls покажет стандартную структуру папок Linux:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 13

Нас интересует файл /etc/nginx/conf.d/default.conf — настройки NGINX, для просмотра можно использовать утилиту cat

cat /etc/nginx/conf.d/default.conf

Сервис на языке Dart: введение, инфраструктура бэкэнд - 14

В настройках NGINX один блок (так называемый location), в котором включено прослушивание порта 80 и раздача статических файлов из папки контейнера /usr/share/nginx/html. Можно попробовать внести изменения в файл настройки NGINX и перезапустить его, применив изменения, но при перезапуске сценария контейнер будет восстановлен из образа и никакие наши изменения не сохранятся. Это неправильный путь.

Выйдем из консоли контейнера:

Ctrl-D

Мы напишем свой файл настройки и разместим свои статические файлы, а при запуске будем их монтировать в контейнер NGINX. Создадим файл default.conf в папке /conf.d нашего проекта:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 15

Сервис на языке Dart: введение, инфраструктура бэкэнд - 16

Создадим заглушку статического файла /public/index.html:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 17

Теперь в сценарии запуска docker-compose.yaml смонтируем наши папки в файловую систему контейнера:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 18

Обратите внимание, что содержимое папки проекта ./conf.d заменит содержимое контейнера в /etc/nginx/conf.d/, а в корневую папку папку контейнера будет смонтирована папка ./public.

Перезапустим сценарий:

docker-compose restart

Тестовый запрос:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 19

Давайте посмотрим на файл default.conf. Обратите внимание, что мы отключили логирование доступа к статическим файлам access_log off. Это хорошее решение для прода, но очень неудобное при тестировании и разработке. Давайте создадим тестовые файл конфигурации NGINX /conf.dev.d/default.conf и сценарий docker-compose.dev.yaml.

Сервис на языке Dart: введение, инфраструктура бэкэнд - 20

Сервис на языке Dart: введение, инфраструктура бэкэнд - 21

Сервис на языке Dart: введение, инфраструктура бэкэнд - 22

Остановим сценарий:

docker-compose down

и запустим уже с флагами имен файлов:

docker-compose -f docker-compose.yaml -f docker-compose.dev.yaml up

При таком запуске сценария сначала будет прочитаны настройки из файла docker-compose.yaml, а затем будут добавлены или заменены совпадающие поля из docker-compose.dev.yaml (ports, volumes). Проверим логирование повторив запрос:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 23

Сервис на языке Dart: введение, инфраструктура бэкэнд - 24

Итак, нам осталось выполнить копирование и запуск на сервере. Создадим на сервере папку /opt/srv_0/ (мы ведь ещё не закрыли окно VScode c SSH соединением к VPS) и скопируем в неё всё содержимое нашего проекта командой:

scp scp -r ./* root@91.230.60.120:/opt/srv_0/ 

Сервис на языке Dart: введение, инфраструктура бэкэнд - 25

Теперь на сервере в папке проекта /opt/srv_0/ выполним команду:

docker-compose up -d

Напишем ещё один http тест, теперь уже для VPS:

Сервис на языке Dart: введение, инфраструктура бэкэнд - 26

Ну или откройте в браузере ссылка.

→ Исходный код github

Вместо заключения

Итак, сделан первый шаг. Мы успешно раскатили на боевой сервер приложение. Во второй статье мы продолжим настройку сервера: назначим доменное имя и установим сертификат шифрования SSL. В третьей статье напишем flutter web приложение с обратным отсчётом времени до запуска нашего сервиса, соберём его и разместим на нашем сервере. В четвертой статье напишем и соберём нативный сервер под Linux на языке Дарт, который станет основой для приложений авторизации и данных нашего сервиса.

Комментарии и предложения приветствуются. Пообщаться с автором можно в Telegram-канале.

Автор: Andrew

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js