Инструкция как запустить проект в докере.

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

Версии:
- docker 19.0+
- docker-compose 1.25+

Только прежде чем приступить к установке: так как у тебя диск жесткий разбит и постоянные траблы с местом на диске, сначала на большом диске (который не основной, а под всякие данные) создай папку docker и залинкуй ее на основной диск
sudo ln -s {путь к созданной папке} /var/lib/docker

Чтобы докер все образы и т.п. там хранил. А то каждый день чиститься будешь :)

Процесс установки описан здесь. Только не все ставь, а только указанное ПО. И там написано как пользователя добавить в группу docker, чтобы можно было контейнерами управлять без вызова sudo.

Запуск докер-проекта

Прежде всего, если ты с гитом все еще не работаешь через ssh, то ключ себе сделай. Вот тут описана процедура: https://docs.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

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

Этот же ключ можно будет использовать для доступа к серверам на digitalocaen ;-)

- Скачиваем проект
git clone --recurse-submodules https://github.com/Pivkarta/docker docker-pivkarta (или если ssh настроил, то git clone --recurse-submodules git@github.com:Pivkarta/docker.git docker-pivkarta )
cd docker-pivkarta

- Читаем readme.md, там прописаны актуальные первичные шаги. Сейчас прописано следующее:
# Copy and edit environments file cp .env.sample .env # Copy and edit web-server config file cp caddy/Caddyfile.sample caddy/Caddyfile #Copy and edit coturn config file cp coturn/turnserver.conf.sample coturn/turnserver.conf
На локальной машине можно .env и не редактировать (оставить пароли по умолчанию), но на проде конечно же надо отредактировать.

Запускаем контейнеры

Я специально в композ-файле не прописываю зависимости контейнеров, чтобы можно было на одном сервере сразу несколько проектов запускать, так что придется запускать контейнеры с явным указанием оных. Да и лучше их все прописать, чтобы было понятно что для чего.

Быстрый старт (только фронт)

Для того, чтобы быстро получить результат и в меньшие дебри лезть, запустим пока только фронт.

API_ENDPOINT=https://pivkarta.ru/api/ docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build pivkarta.ru-2

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

Successfully built f6ecca7db83d Successfully tagged docker-pivkarta_pivkarta.ru-2:latest Recreating docker-pivkarta_pivkarta.ru-2_1 ... done Attaching to docker-pivkarta_pivkarta.ru-2_1
Когда контейнер будет запущен, можно зайти на адрес http://localhost:3100. Должен открыться сайт. В дев-конфиге прописана линковка на исходники в локальной файловой системе, так что можно редактировать файлы в своей IDE как есть, изменения будут учитываться и в контейнере.

Если все ОК, то жмем CTRL+C, чтобы завершить выполнение программы. В этот момент у нас и контейнер остановится, потому что он работал в режиме программы. Чтобы этого не происходило, и он продолжал работать дальше, выполняем то же самое, только с флагом -d
API_ENDPOINT=https://pivkarta.ru/api/ docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build pivkarta.ru-2

Теперь, когда контейнер пересоберется (а сейчас он очень быстро это сделает, потому что никаких изменений в нем не было), команда успешно завершится и все. При этом контейнер продолжет работать. Чтобы его остановить, надо будет выполнить
docker-compose stop pivkarta.ru-2

Если надо будет удалить потом, то docker-compose rm pivkarta.ru-2

Выполнение команд внутри контейнера.

Иногда надо выполнить какую-нибудь команду в самом контейнере, например yarn generate:types.
Для этого нам надо зайти в нужный нам контейнер. Откроем еще один терминал и выполним в директории проекта docker-compose ps. Найдем в списке наш контейнер и его имя (скорее всего будет docker-pivkarta_pivkarta.ru-2_1).

Выполняем docker exec -it docker-pivkarta_pivkarta.ru-2_1 bash

Если ОК, то мы оказываемся внутри контейнера, то есть как бы в другом компьютере. Только следует учитывать, что конкретно этот образ не на базе ubuntu создан, а alpine-linux. Некоторые командыы могут отличаться (например, не apt install, а apk add). И вот, когда мы внутри, выполняем yarn generate:types.

Полный запуск (с базой данных и прочим)

MySQL
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d mysql

Расшифрую:
1. docker-compose - команда
2. -f docker-compose.yml -f docker-compose.dev.yml - указал для конфигурационных файла.
На проде надо будет -f docker-compose.yml -f docker-compose.prod.yml
3. up -d - субкоманда запуск с флагом -d - запуск в режиме демона. То есть запущенный контейнер будет крутиться как служба, то есть терминал сразу освобождается и его можно будет даже закрыть.

Проверим работу контейнера.

docker-compose ps

Должно быть типа такого:

$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------
docker-pivkarta_mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp

Здесь видно, что контейнер с персональным именем docker-pivkarta_mysql_1 работает (State Up) и открыт внешний порт 3306 указывающий на внутренний 3306 (0.0.0.0:3306->3306/tcp), а так же работает внутренний 33060.

То есть все как и ожидалось.

4. имя службы (service).
Все службы и их конфигурации прописаны в docker-compose.yml. Дополнительные файлы типа docker-compose.dev.yml содеражат переопределяющие конфиги для этих служб (не обязательно для всех).

PhpMyAdmin
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d pma

Закрепим материал и еще раз выполним docker-compose ps
Видим:
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------------
docker-pivkarta_mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp
docker-pivkarta_pma_1 /docker-entrypoint.sh apac ... Up 0.0.0.0:8090->80/tcp

У нас уже два работающих контейнера. Обрати внимание, что у pma внешний и внутренний порты отличаются. Это не страшно. Это как раз фишка докера - можно запустить несколько сервисов, каждый из которых используют один и тот же порт, но указать им разные, чтобы они не конфликтовали. То есть хотя PhpMyAdmin в контейнере и крутится на порту 80, для доступа к нему мы используем адрес http://localhost:8090

Откроем этот адрес в браузере и увидим привычный PhpMyAdmin. Если пароль в .env не менял, то используем указанные по умолчанию логин/пароль: root/prisma

Инспектирование контейнеров

Вот здесь важно сразу понять что за что отвечает.

docker-compose - это всего лишь утилита оркестрации докер-контейнеров. При чем ее даже не рекомендуют использовать в продакшене, типа есть более правильные инструменты (но я их пока не освоил). Само же ядро этой системы - сам docker. То есть все команды так или иначе выполняются через docker. Просто в докере одна команда - один контейнер (ну, кроме типа docker ps, который выводит список контейнеров). А docker-compose создан, чтобы управлять проектами. При этом проекты могут содержать и по одному контейнеру, не обязательно несколько. Суть просто в том, что создается папка с файлом docker-compose.yml, в котором прописано все необходимое для запуска, и когда мы запускаем из этой папки через docker-compose, последний берет на себя работу по неймингу конечных контейнеров (как у нас получился docker-pivkarta_mysql_1, а не просто mysql), быстрому доступу к контейнерам именно этого проекта и т.п. Без этого, запустив через сам docker из разных проектов кучу контейнеров, нам сложно будет ориентироваться какой контейнер к чему относится. То есть если мы выполним docker ps, то мы увидим все запущенные на этом сервере контейнеры. А выполнив docker-compose ps в папке проекта, мы увидим только те контейнеры, которые относятся конкретно к этому проекту. К примеру, если мы захотим остановить все контейнеры текущего проекта, мы выполним docker-compose stop. А с чистым докером нам пришлось бы явно перечислять контейнеры (или использовать хитрости с фильтрацией, тегами).

Но docker-compose не все команды поддерживает. К примеру нельзя выполнить docker-compose inspect pma. А нам нужна именно эта команда. Поэтому мы обратимся к докеру напрямую docker inspect docker-pivkarta_pma_1

Здесь следует отметить, что в docker-compose мы оперируем именами сервисов, а в docker именами контейнеров, поэтому используем docker-pivkarta_pma_1, а не pma.

Команда inspect покажет нам много полезной информации, в том числе из какого образа контейнер собран, с какими параметрами, какие виртуальные диски подключены и т.п. Нам же сейчас надо просто посмотреть какой ip задан этому контейнеру. Вот эта информация (может конечно же отличаться на вашей машине):

"NetworkSettings": { "Bridge": "", "SandboxID": "56210129d737e6c1c17ed9b6e87eb073ff9044aa567adb3c70c11d5717a103e5", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": { "80/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "8090" } ] }, "SandboxKey": "/var/run/docker/netns/56210129d737", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "", "Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "", "IPPrefixLen": 0, "IPv6Gateway": "", "MacAddress": "", "Networks": { "pivkarta-default": { "IPAMConfig": null, "Links": null, "Aliases": [ "090faba4fdd5", "pma" ], "NetworkID": "cc2dd7dc6b30478229053bec043bce80801020ddfd87b4dbf6472413d6bf1fc5", "EndpointID": "6194e69e837d55cf2717683e3201b8e982eb2db3a46724b302935110ac4b0ffd", "Gateway": "172.16.6.1", "IPAddress": "172.16.6.2", "IPPrefixLen": 24, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:10:06:02", "DriverOpts": null } } }
И здесь самая главная для нас информация - "IPAddress": "172.16.6.2", а так же Ports, где видно, что 80-му порту соответствует 8090

То есть контейнеру задан ip 172.16.6.2. А значит мы можем к его внутренним адресам обращаться напрямую по этому адресу. Откроем в браузере http://172.16.6.2 (то есть именно с 80 портом, а не внешним 8090) и увидим, что нам так же откроется PhpMyAdmin. Сейчас смысл всего этого наверняка покажется неочевидным, но просто помни, что здесь эта инфа есть, и она наверняка еще пригодится.

Prisma-сервер

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build prisma

В дев-режиме будет крутиться на порту 4466

API-сервер

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build api

После успеешного запуска (который включает в себя в том числе и деплой схемы в призма-сервер), можно увидеть призма-АПИ, который админский и который не должен быть виден напрямую на прод-сервере. С этим призма-АПИ и работает фронтовый АПИ-сервер. Смотри эндпоинты в .env файле. У тебя их там два (обнови репу докера, я там фикс вылил, и у себя .env поправь):
dev_endpoint=http://prisma:4466/pivkarta/dev prod_endpoint=http://prisma:4466/pivkarta/prod

Соответственно, если ты АПИ-сервер запускаешь в дев-режиме (или в данном случае правильней сказать не в прод-режиме, потому что только в docker-compose.prod.yml прописано endpoint=${prod_endpoint}), то у тебя призма-АПИ и открывается по адресу http://prisma:4466/pivkarta/dev. Если ты изменишь переменные и пересоберешь АПИ-сервер, то и призма-АПИ заработают на новом адресе (но старые просто так никуда не удалятся).

В базе данных имена баз данных будут со знаками @ вместо /, то есть pivkarta@dev и pivkarta@prod. После первого деплоя надо сделать следующее:
1. Снять актуальный дамп с боевого сайта.
2. В целевой базе призмы удалить вообще все таблицы.
3. Залить дамп базы.

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

proxy-сервер

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build proxy

Конфигурация прокси-сервера прописана в caddy/Caddyfile

Пример отдельного хоста:
0.0.0.0:2016 { tls off gzip proxy / api:4000 { transparent websocket } }
То есть запросы на порт 2016 будут проксироваться на api-сервер на порт 4000. В данном случае не важно открыт порт 4000 у api-сервера или нет (вспоминаем написанное выше про внешние и внутренние порты). То есть даже если 4000 порт у него не открыт и нельзя зайти на http://localhost:4000, все равно, когда мы заходим на http://localhost:2016, мы попадаем на http://api:4000