Николай Ланец
17 янв. 2018 г., 11:12

Разворачиваем 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

Установим нового пользователя владельцем своей директории со всем содержимым 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

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


Устанавливаем 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

Устанавливаем 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-сервер остановится.

3. Открыли новую консоль и переходим в /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

Отличная статья, спасибо!
Не могу войти в phpMyAdmin. Указываю пароль по умолчанию из env
SQL_CLIENT_USER="root"
SQL_LOGS_PASSWORD="graphcool"
Не входит. Других логинов и паролей не нашел.
Где ошибаюсь?
Попробуйте prisma:prisma или root:prisma, они писали о планах теперь такой использовать
Классное пособие, было бы здорово его обновить и добавить поддержку https. Сам не могу разобраться)

Читайте документацию по react-scripts, для фронта он используется. Если просто тестовый сертификат сгенерировать, то запускаете HTTPS=true yarn start. Если использовать сторонний рабочий сертификат, то я лично настраиваю его на уровне nginx.
Также настраиваю его на уровне Nginx для сайтов по этому учебнику

, но к сожалению не смог настроить его для сервера Prisma.
Будет здорово, если вы продолжите цикл своих познавательных статей по интеграции с Prisma.
Вам не надо настраивать ssl на уровне призмы, если вы настраиваете ssl на уровне nginx. На nginx вы принимаете шифрованный трафик, и с него проксируете на призму (уже дешифрованный). Призма, в свою очередь, работает по обычному протоколу, но при этом напрямую не доступна извне, только через nginx.
Спасибо! В теории все понятно, но на практике не хватает опыта без учебника, так как я больше фронт =)
@edit, сайт не "все". Просто пользователи не хотят ничего писать и спрашивать, видимо.

Добавить комментарий