Разворачиваем Graphcool Prisma на голом железе

В последнее время я много пишу про GraphQL и т.п., потому что тема действительно очень интересная, технологии очень важные и перспективные, а так же все это отлично состыкуется с MODX, дабы придать жизни внешней части сайта и получить более гибкое API для взаимодействия фронта с бэком.

Сегодня я расскажу про graphcool-prisma. Вообще еще недавно они были просто graphcool (и устанавливались в систему именно как graphcool), затем они внезапно стали graphcool-framework (и еще так они вчера мне были известны, и устанавливались как graphcool-framework, хотя и во всех подсказках выводилось как graphcool, что доставляло много путаницы при наличии в системе и graphcool и graphcool-framework). А сегодня, когда я сел написать статью, они вдруг стали просто Prisma. Ну ппц... (хотя prisma, конечно же, проще запомнить и пишется проще). При этом нпм-страница prisma предлагает устанавливать prisma-cli, а там в свою очередь речь идет о graphcool. Короче, еще раз, ппц... Но это они не со зла. Проект очень быстро развивается и видимо какие-то внутренние архитектурные вещи заставляют так делать, типа, выбирайте: или название будет без изменений, или новые современные фишки. Я за современные фишки, а с названием потерплю...

Уточнение к стилю изложения: я пишу по мере работы с сервером. Однозначно буду сталкиваться с какими-то подводными камнями и т.п., где-то в процессе находить ответы, поэтому в статье будет много уточнений, метаний по поверхности и т.п. Извиняюсь, если где-то запутано будет и не конкретно.

Внимание! Если вы планируете просто запускать свою копию призма-сайта, внизу есть инструкция по быстрой установке одним скриптом.

Итак, что же такое Prisma? Это готовое решение для создания своего собственного API на базе GraphQL+ Apollo и еще много все. Главная задача - быстро развернуть основу под то, чтобы принимать внешние запросы, выполнять операции с хранилищем данных, отдавать ответы и т.п. Отдельная строчка - готовая система авторизации и политик безопасности. В общем, там практически все есть для настройки полноценного бэкэнда. И в процессе мы рассмотрим много классных фишек. И, скорее всего, именно эта платформа послужит основой для обновленной версии ShopModxBox. К концу статьи вы поймете почему.

А теперь от слов к делу. Сегодня мы развернем призму на сервере с нуля. В качестве хостинг-площадки предлагаю flops.ru (ссылка реферальная). Я не первый год с ними работаю и хотя там есть ограничения на дисковые операции, о которых они не говорят (что очень некрасиво), в целом они очень неплохи и для средних проектов годятся очень даже (под блокчейн не годятся точно, я проверял))). А еще там было 500 рублей на счет при регистрации (надо только карту по-моему указать, но с нее ничего не списывается без вас). Этого вполне достаточно на тесты.

Для начала я взял 1Gb памяти, но в процессе работы ожидаемо выяснилось, что этого мало. Поднял до двух (плановая стоимость 700 руб/месяц). Но скорее всего для комфортной работы надо 3Gb (1000 рублей).

Разворачиваем чистый сервер.

Я работаю в основном только с ubuntu, на других версиях linux не проверял, на windows тоже. В данном случае я развернул ubuntu-16 x64.

Первым же делом выполняем apt update и apt upgrade, дабы актуализировать OS.

Устанавливаем nginx

sudo apt install nginx

Устанавливаем прочее ПО

  • Текстовый редактор. Необязательный шаг. Мне нравится mc, поэтому я его ставлю. Но каждый выбирает для себя свое (может даже vim).
sudo apt install mc
  • sudo apt install git (контроль версий файлов, без него никуда).

Создаем нового sudo-пользователя.

Некоторые программы выполняются именно от sudo-пользователей (не обязательно root, просто с правами sudo), например docker.

useradd USERNAME -d /home/USERNAME -G sudo,www-data -s /bin/bash

(USERNAME конечно же заменить на свое, здесь и далее).

passwd USERNAME

(здесь запросит ввести и подтвердить пароль для пользователя).

mkdir /home/USERNAME

Чтобы у пользователя работала история команд, подсветка в консоли и т.п., надо создать заготовленный .bashrc

cd /home/USERNAME
wget https://gist.githubusercontent.com/Fi1osof/2f8ea23f5411c5c7a0e0025f04941aee/raw/.bashrc

Установим нового пользователя владельцем своей директории со всем содержимым

chown USERNAME: /home/USERNAME -R

Чтобы каждый раз при вызове sudo на новом пользователе не приходилось вводить пароль, разрешим ему sudo без пароля. Открываем для редактирования файл

mcedit /etc/sudoers

Находим %sudo ALL=(ALL:ALL) ALL и переписываем на %sudo ALL=(ALL:ALL) NOPASSWD: ALL

Все, теперь можно авторизовываться этим пользователем и продолжать работу от его имени.

Устанавливаем docker

sudo apt-get install software-properties-common python-software-properties
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main'
sudo apt-get update

Убедимся, что мы собираемся установить Docker из репозитория Docker, а не из репозитория по умолчанию Ubuntu 16.04:

apt-cache policy docker-engine

В списке должен быть apt.dockerproject.org

Если да, то выполняем непосредственно установку.

sudo apt-get install -y docker-engine

Проверяем docker -v

Docker version 17.05.0-ce, build 89658be

По умолчанию, запуск команды docker требует привилегий root, что означает, что вы должны использовать sudo. Также эта команда может запускаться пользователем, включённым в группу docker, которая автоматически создаётся при установке Docker. При попытке использования команды docker пользователем без привилегий sudo или пользователем, не входящим в группу docker, вы получите такой результат:

docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See 'docker run --help'.

Для того, чтобы не вводить sudo каждый раз при запуске docker, добавьте имя своего пользователя в группу docker:

sudo usermod -aG docker $(whoami)

(добавляет именно текущего пользователя, поэтому убедитесь, что вы авторизованы нужным пользователем).

Для добавления произвольного пользователя

sudo usermod -aG docker USERNAME

После этого желательно перелогиниться.

Источник: https://www.digitalocean.com/community/tutorials/docker-ubuntu-16-04-ru

Устанавливаем docker-compose

Важно! Обратите внимание на версию в ссылке. Прежде чем качать, лучше проверить какая актуальная версия сейчас и нужную подставить в адрес.

sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Источник: https://docs.docker.com/compose/install/#install-compose

Устанавливаем node-js

После того, как все обновится, устанавливаем node-js (с чем и npm установится).

curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs

Проверяем node -v Должно быть типа v10.15.3

Устанавливаем yarn

Yarn - совместный фейсбука с гуглом менеджер пакетов типа npm. Он здесь тоже понадобится.

Способ проще:

sudo npm i -g yarn

Способ сложнее:

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn

Проверяем yarn -v 1.3.2

Устанавливаем призму глобально.

sudo npm install -g prisma

Проверяем prisma -v Должно быть типа prisma/1.0.4 (linux-x64) node-v9.4.0

Запускаем локальную службу.

Выше я писал инструкцию про установку докера. Здесь докер обязателен, так что если не установили, обязательно надо установить.

Локальная служба призмы - это линукс-сервер внутри нашего линукс-сервера (благодаря использованию докера). То есть призма устанавливает под себя отдельно MySQL и прочее ПО в контейнерах с минимальными рисками конфликтов с уже установленным ПО.

Выполняем

sudo prisma local start

Запускаться будет некоторое время, так как это docker, будет установлена mysql и прочие зависимости.

Если все ОК, получите типа Booting local development cluster 31.6s

Вот теперь, когда локальный сервер призмы запущен, можно создавать и деплоить непосредственно проекты. Уточню, что эта служба единая и может обслуживать сразу несколько отдельных проектов. Ниже я немного раскрою эту кухню.

Создаем Prisma-проект.

sudo chown USERNAME /var/www -R

Неоднозначный шаг, так как веб-проекты должны выполняться от имени веб-сервера и права на файлы должны быть для веб-сервера, но у нас не обычный веб-проект, да и вопросы тонкой настройки прав на веб-сервере мы сейчас не рассматриваем, но этот счет может будет отдельная статья (а может и нет, так как в сети полно материала от более опытных админов). Сейчас в этой папке ничего нет, так что я просто ее под себя резервирую, хотя npm-проект не привязывается к конкретной папке и может быть где угодно. Просто мне /var/www привычней.

cd /var/www/
prisma init
  • Выбираем GraphQL server/fullstack boilerplate (recommended)
  • Directory for new GraphQL project (.) если папка пустая, то можно оставить так (будет создан новый проект в текущей папке). Если нет, то не позволит этого, надо указать название проекта, например, hello-world (как это принято в начале Пути делать).
  • Далее, чтобы интересней было, выбираем react-fullstack-basic React app + GraphQL server (incl. database ), будет создан не только GraphQL-проект с АПИ, но и небольшая веб-морда на реакте.
  • Последний шаг: выбираем local Local cluster (requires Docker), то есть полный стек будет работать на нашем сервере, а не использовать сторонние сервера graph.cool

Если все прошло успешно, вы получите сообщение типа

Added cluster: local to prisma.yml
Creating stage dev for service hello-world ✔
Deploying service `hello-world` to stage `dev` on cluster `local` 144ms

Changes:

  Post (Type)
  + Created type `Post`
  + Created field `id` of type `GraphQLID!`
  + Created field `isPublished` of type `Boolean!`
  + Created field `title` of type `String!`
  + Created field `text` of type `String!`
  + Created field `updatedAt` of type `DateTime!`
  + Created field `createdAt` of type `DateTime!`

Applying changes 1.1s

Hooks:

Writing database schema to `src/generated/prisma.graphql`  206ms
Running $ graphql prepare...

Your GraphQL database endpoint is live:

  HTTP:  http://localhost:4466/hello-world/dev
  WS:    ws://localhost:4466/hello-world/dev


Next steps:
  1. Change directory: `cd hello-world/server`
  2. Start local server: `yarn start` (you can now open a Playground at http://localhost:4000)
  3. Change directory: `cd ..`
  4. Start React app: `yarn start`
  5. Open browser: http://localhost:3000

Чуть распишу указанные пять шагов со своими комментариями.

1,2. Переходим в папку hello-world/dev и запускаем API-сервер командой yarn start. Если все ОК, увидите Server is running on http://localhost:4000

Для выполнения 3-го шага придется открыть еще одну консоль, так как в текущей выполняется yarn start, и если вы закроете ее, то API-сервер остановится.

  1. Открыли новую консоль и переходим в /var/www/hello-world

4,5. Запускаем веб-сервер yarn start. Этот сервер будет отвечать за пользовательский интерфейс. Если все ОК, увидите типа

Local:            http://localhost:3000/
On Your Network:  http://10.7.112.123:3000/

В моем случае призма допустила ошибку из-за своеобразной настройки сервера хостинг-провайдером, и показала внутренний ip-адрес, а не внешний. В любом случае, если вы на localhost запускаете, то можете и указывать localhost. Если нет, то еще немного нам придется повоевать.

Для это открываем файл /www/hello-world/src/index.js, находим в нем localhost и меняет на реальный ip сервера или доменное имя.

После этого уже запускаем веб-сервер командой yarn start

Если все у вас заработало как надо, API-консоль вы увидите в браузере на 4000 порту, например http://prismagraphql.ru:4000/ , веб-морду на 3000-ом, к примеру http://prismagraphql.ru:3000/

Веб-морда тестовая, можно перейти на вкладку http://prismagraphql.ru:3000/drafts (черновики), создать запись и, перейдя в нее, опубликовать. В API-консоли можно сформировать нужный вам запрос и выполнить, получив информацию.

Пара примеров:

Список опубликованных топиков:

{
  feed{
    id
    title
    text
    isPublished
  }
}

Список черновиков:

{
  drafts{
    id
    title
    text
    isPublished
  }
}

Отдельная мега-фича: генерация CURL-строки для API-запросов. Кликаем кнопочку CURL

и в буфер обмена попадает строка типа

curl 'http://prismagraphql.ru:4000/' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: */*' -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: http://prismagraphql.ru:4000' --data-binary '{"query":"{\n  feed{\n    id\n    title\n    text\n    isPublished\n  }\n}","operationName":null}' --compressed

Эту строку можно выполнить просто в консоли на любом сервере и получить JSON-ответ с данными.

Запуск демона и мониторинг.

Выше я писал:

Для выполнения 3-го шага придется открыть еще одну консоль, так как в текущей выполняется yarn start, и если вы закроете ее, то API-сервер остановится.

Конечно же для боевого режима это совсем не годится. Надо иметь возможность запускать процесс так, чтобы он уже жил своей жизнью и можно было спокойно закрывать все консоли. В этом нам поможет очень крутая утилита pm2. Устанавливаем ее тоже глобально.

sudo npm i -g pm2

Теперь запустим api-сервер через него. Переходим в server/ и выполняем

pm2 --name server start npm -- start

После этого посмотрим список всех запущенных процессов на pm2. pm2 list.

Должна вывестись табличка со списком процессов (сейчас скорее всего только один процесс server) и если с ним все хорошо, то статус online и uptime больше нуля. Там же информация об использовании процессора и оперативки.

После этого зайдите в API-консоль на 4000-ом порту и если все ОК, она работает. Чтобы остановить службу, выполняем pm2 stop server. Веб-консоль перестанет быть доступной. При этом если вы выполните pm2 list, процесс server в нем по прежнему будет, просто со статусом stopped. Если надо, можно опять его запустить уже через сам pm2 командой pm2 start server

Согласитесь, так гораздо удобней :)

А теперь через него же запустим и веб-морду. Для этого переходим в корень проекта cd /var/www/hello-world/, но теперь выполним еще один предварительный шаг, а именно сборку конечных скриптов, выполнив команду yarn build

После выполнения этой команды у нас появится папка build со всеми готовыми скриптами.

Теперь надо эти скрипты запустить. В призме пока не указана команда для запуска сервера со сжатыми скриптами, поэтому используем для запуска программу serve.

Устанавливаем serve глобально.

sudo npm i -g serve

И запускаем server через pm2.

PORT=3000 pm2 --name front start serve -- -s build

Выполняем pm2 и смотрим список процессор. Теперь у нас там есть server и front. Открываем браузер на 3000 порту и должно работать. У меня работает http://prismagraphql.ru:3000/

Вот теперь мы можем закрыть все свои консоли, а веб-сервер продолжит работать :)

А где же обещанный мониторинг?

ОК, будет вам и мониторинг :) Для этого опять подключимся к серверу и выполним pm2 monit

Вот теперь откроется более подробная информация о выполняемых процессах (в нашем случае это front и server). Но такой вид не очень радует глаз (хотя и определенно несет свою пользу). Сделаем теперь все гораздо красивее.

Прервем выполнение мониторинга Ctrl+C и выполним pm2 register. В ответ вы получите вопрос есть у вас аккаунт на https://keymetrics.io/ или нет. Выберите свой ответ. Если n, вероятней всего там же будет предложена регистрация (но я не знаю, у меня уже был аккаунт). Так или иначе, дойдите до окончательной регистрации и будете вознаграждены вот таким интерфейсом мониторинга:

Согласитесь, так гораздо красивее :) Плюс к этому не требуется постоянно сидеть в консоли, а можно в любое время зайти в браузер и посмотреть что у вас на сервере творится. И можно даже оттуда перезагружать службы на своем сервере

И напоследок еще один момент: как я и говорил выше, призма запускает под себя мускул в контейнере докер. И вот достучаться до этого контейнера не очень-то и просто, то есть если мы хотим запустить phpMyAdmin и заглянуть в БД, то придется изрядно попотеть (точнее мне пришлось, а далее будет зависеть от того, что и где они еще поменяют, так как вчера еще одно было и все работало, а сегодня уже вчерашние примеры не работают и пришлось покопаться поглубже в поисках точек входа).

Вот строка для установки докер-контейнера phpmyadmin с коннектом в нужный нам докер:

sudo docker run -d --link prisma-db:db --network local_prisma -p 8080:80 phpmyadmin/phpmyadmin

В версии prisma-1.15+ чуть другая строка: docker run -d -e UPLOAD_LIMIT=100M --name pma --link prisma_mysql_1:db --network prisma_default -p 8090:80 phpmyadmin/phpmyadmin

Здесь две нужные нам переменные: prisma-db:db и local_prisma. UPLOAD_LIMIT=100M - задает лимит размера импортируемых файлов.

Чтобы найти первую, выполняем prisma local eject

В результате в домашнюю папку пользователя будут записаны два файла docker-compose.yml и env.

В env мы найдем export SQL_CLIENT_HOST="prisma-db". Вот этот хост нам и нужен для формирования переменной prisma-db:db. Там же указаны и другие полезные для нас переменные типа логина и пароля для доступа к БД.

Для поиска второй переменной выполняем sudo docker network ls

Будет выведена таблица запущенных процессор. Там надо типа a66536c90365 local_prisma bridge local

Вот local_prisma отсюда нам и нужна.

Выполняем вышеуказанную команду и если все ок, на порту 8080 будет запущен phpMyAdmin (порт конечно же можно указать другой). Указываем логин и пароль из env и все, мы в БД. Структуру БД нет смысла приводить, так как вчера она еще была одна, сегодня другая, а завтра может будет третья. Единственное отмечу, что под каждый проект создается отдельная БД (все их видно в списке через phpMyAdmin).

На сегодня, думаю, хватит. Впереди будет еще много сопутствующего материала типа как создавать свои типы и обработчики для API, примеры авторизации, интеграции с MODX и т.д. и т.п. Пока что осваивайте это. очень советую не пропускать этот материал, так как это отправная точка.

UPD: локально у меня на имеющейся системе призма не запускалась. Долго воевал, но победил. Проблему и решение описал здесь: https://www.graph.cool/forum/t/cannot-start-prisma-local-mysql-error-0-0-0-0-3306-bind-address-already-in-use/2190

UPD2: Крайне полезная статья в плане понимания как в призме webpack устроен: https://habrahabr.ru/company/plarium/blog/326520/ А то запускаешь yarn start, дев-сервер работает, а откуда ноги растут - не понятно.

UPD3: Быстрая установка одним скриптом. Если вы планируете в ознакомительных целях запустить призма-сайт, можно воспользоваться скриптом быстрой установки. Сайт будет в итоге запущен от рута, но для тестов это не страшно.

sudo apt update
sudo apt install mc
sudo apt install git

curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs

sudo npm i -g yarn

# Если вы не планируете у себя запускать базу данных сайта, докер можно и не устанавливать.
# То есть скрипты ниже можно и не выполнять.
sudo apt-get install software-properties-common python-software-properties
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main'
sudo apt-get update

sudo apt-get install -y docker-engine

sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose