2. Развертывание

Основной способ развертывания системы - Docker контейнеры.

Контейнеры выполняют роль только среды исполнения и исходный код системы не содержат. Исходный код системы монтируется внутрь контейнера, как и файлы базы данных, логи и прочее. Это упрощает редактирование исходного кода и унифицирует настройки среды разработки и production.

2.1. Общая информация

Мы выделяем несколько сред, в которых система запускается немного по разному:

  • dev Это локальная машина разработчика, который пишет код и проверяет его

  • test Тестовый сервер, на котором экземпляр системы запускается с ограниченными ресурсами, но в общем, так же, как и на prod

  • prod Это рабочая версия системы, на которой работают уже операторы и выполняют работу, для которой система и предназначена. При развертыванием в таком режиме, сервер приложений в корень проекта монтирует socket-файл, который подключается к nginx-серверу, установленном непосредственно на сервере, а не в контейнер. Это делается для того, чтобы через единый экземпляр nginx-сервера работать одновременно с несколькими приложениями, разводя запросы между ними по доменному имени в запросе

Как уже было сказано выше, развертывание происходит через Docker-контейнеры. Каждым по отдельности мы не манипулируем, а используем для этого docker compose. Это способ запуска и управления несколькими контейнерами одновременно, которые могут взаимодействовать друг с другом по внутренней вируальной сети, объединяющей вместе «связку» контейнеров, относящуюся к проекту.

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

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

То есть, в корне каждого проекта есть файлы:

  • docker-compose.yml

  • docker-compose.dev.yml

  • docker-compose.test.yml

  • docker-compose.prod.yml

  • docker-compose.local.yml

В файлах конфигурации, мы описываем контейнеры со следующими именами:

  • db Контейнер с базой данных

  • redis Контейнер с сервером кэширования и очередью Redis

  • app Контейнер с web - сервером приложени

  • queue Контейнер, в котором выполняются фоновые операции системы через очередь

Дополнительно, могут присутствовать:

  • db_pool Контейнер с пулом подключений к БД

  • app_reports Контейнер для web-сервера с отчетами (такой же, как и app, но с увеличенными тайм-аутами). Используем его для изоляции web-обработчиков, так как аналитические отчеты могут выполнятся длительное время. Чтобы в пиковых нагрузках, отчеты не использовали обработчики самого приложения, так как основная часть настроена для систем типа ООТ (оперативной обработке транзакций)

  • pghero Контейнер для сборки и визуализации кумулятивной статистики для БД

  • Прочие контейнеры, которые специфичны для конфигурации

2.2. Установка окружения для разработчика

Если установка идет для серверного окружения (test или prod) пропустите этот пункт

Этот раздел для установки рабочего места разработчика. Исходим из того, что операционная система manjaro. и у пользователя есть доступы к sudo

  • Установим docker, docker-compose, и запустим его

  • добавим пользователя в группу docker

  • создадим псевдоним для команды dc - управления комплексом контейнеров

  • Установим эмулятор терминала по умолчанию bash

# Установка программ
pacman -Syyu
pacman -S docker docker-compose

sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER

echo '
alias dc="docker compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.local.yml"
' >> ~/.bash_aliases

# Добавьте в файл ~/.bashrc наши алиасы. Только убедитесь что там такого уже нет.
# Некоторые операционные системы уже это включают этот файл по умолчанию
echo '
. ~/.bash_aliases
' >> ~/.bashrc

# Установим по умолчанию эмулятор терминала bash (раз уж инструкция про него)
chsh -s /bin/bash

# Сгенерировать ключи (нужны для авторизации). Все оставляем по умолчанию и просто нажимаем Enter
ssh-keygen -t rsa -b 4096

# Перезагрузка (нужна для того чтобы подействовали настройки группы доступа docker)
reboot

2.3. Установка окружения для сервера

Этот раздел описывает установку на сервере. В этом случае, все запросы обрабатывает nginx, установленный непосредственно на сервер без контейнеров (чтобы он мог маршрутизировать запросы к различным системам, установленным на сервере по доменному имени). С сервисами внутри контейнеров, nginx общается через сокет-файлы, которые выбрасывает из себя через монтирование контейнер

Действия команды по установке описываются для сервере debian 10 из-под пользователя с именем www, у которого есть право на выполнение команды sudo

Установим docker, создадим псевдоним для команды управления комплексом контейнеров, назовем его «dc» (то есть, docker compose).

apt install docker nginx

# Замените RUN_MODE на test или prod (в зависмости от среды установки)
echo '
alias dc="docker compose -f docker-compose.yml -f docker-compose.{RUN_MODE}.yml -f docker-compose.local.yml"
' >> ~/.bash_aliases

# Добавьте в файл ~/.bashrc наши алиасы. Только убедитесь что там такого уже нет.
# Некоторые операционные системы уже это включают этот файл по умолчанию
echo '
. ~/.bash_aliases
' >> ~/.bashrc

 # Установим по умолчанию эмулятор терминала bash (раз уж инструкция про него)
 sudo chsh /bin/bash $USER

Примечание

Возможно, у вас старая версия Docker, в который не входит команда compose. В этом случае, установите его отдельно по этой инструкции https://docs.docker.com/compose/install/ И в псевдониме «dc» «docker compose» поменяйте на «docker-compose»

Так как docker, это сервис, работающий из-под суперпользователя (root), то запустим его и настроим на автоматический запуск при загрузке операционной системы. Управление этим сервисом (а работает он под учетной записью root) происходит через внутренние вызовы API. Для этого нам нужно быть в группе docker - добавим туда пользователя. После этого, перезагрузим машину (чтобы система увидела, что теперь пользователь www входит в группу docker)

2.4. Файловая структура

Создание файлов проекта через клонирование репозитория конфигурации и ядра. Сами пути клонирования в данной инструкции не указываем, так как они зависят от конфигурации, которую вам требуется установить. Считаем, что система находится в директории /home/www/projects/myproject/

При развертывании в dev режиме есть отличия от test, prod режима. В dev окружении ядро находится во внешней директории, рядом с «myproject» и называется обязательно «sphere_sqla». При запуске системы, ядро монтируется внутрь контейнеров с app, queue и все это работает. Сделало это для того, чтобы использовать одну директорию с ядром сразу для всех проектов, с которыми работает разработчик, без необходимости постоянно втягивать для каждого из них по отдельности обновления ядра и вообще каждый раз тянуть одно и тоже ядро в каждый проект.

cd /home/www/projects/

# Для test, prod
git clone ssh://{репозиторий}/sphere/myproject.git myproject
cd ./myproject
git clone ssh://{репозиторий}/sphere/core.git sphere

# Для dev (только посмотрите вначале, а нет ли у вас уже директории /home/www/projects/sphere_sqla)
git clone ssh://{репозиторий}/sphere/myproject.git myproject
git clone ssh://{репозиторий}/sphere/core.git sphere_sqla

Создадим директории и файлы, требуемые для работы системы. Эти директории исключены из репозитория, поэтому они не создаются при клонировании из репозитория. Однако, для работы системы необходимы.

Для среды prod, test (в начале раздела «Установка» мы писали, что там нужен установленный на сервере nginx) создаем символическую ссылку для конфигурации nginx

cd /home/www/projects/myproject/

mkdir ./logs
cp ./deploy/example/.env ./.env
cp ./deploy/example/docker-compose.local.yml ./docker-compose.local.yml
mkdir ./deploy/redis_data
mkdir ./deploy/db_data
mkdir ./deploy/db_backup
mkdir ./deploy/db_backup/wal
mkdir ./deploy/logs
mkdir ./deploy/logs/supervisor
touch config_local.py

# Для prod, test
sudo ln -s /home/www/projects/deploy/nginx_prod /etc/nginx/site_enabled/myproject

Примечание

Если вы забудите создать какие-то каталоги, то при запуске контейнеров, в которые они монтируются, docker демон создаст эти каталоги сам, но есть один нюанс. Так как демон работает под учетной записью root, то все каталоги, которые он создаст, будут именно под учетной записью root. А контейнеры у нас работают под не привилегированным пользователем. И процессы в контейнере не смогут получить доступ к этим каталогам. Если вы увидите где-то в логах сообщения о том, что процесс не может получить доступ к какому-то каталогу, то удалите контейнеры dc down и выставьте владельцем себя и свою группу для всех файлов и каталогов (заметите только www:www на имя вашего пользователя и группы - можно посмотреть командой «id») sudo chown www:www -R .

2.5. Инициализация базы данных

Запустим контейнер с базой данных и инициализируем ее. Есть 3 способа развертывания базы данных

  • Запуск системы с пустой базой данной

  • Инициализация базы данных через команду pg_restore (дампо БД, сделанн командой pg_dump)

  • Инициализация из файловой копии БД, которая была сделана на остановленной системе или через базовый бэкап

Убедитесь, что вы закомментировали строки в файле ./docker-compose.yml, которые могут монтировать туда конфиги БД извне в каталог «/var/lib/postgresql/data/». Если этого не сделать, БД не сможет инициализировать для себя файловую структуру и будет ругаться на то, что каталог не пуст.

# Запустим контейнер с базой данных
dc up -d db

# Убедимся что там все нормально
# (последняя строка должна быть database system is ready to accept connections)
dc logs

Если мы запускаемся с пустой базой данных, то ничего дополнительно делать не требуется Далее, выберите наиболее подходящий вариант для инициализации базы данных (наиболее подходящий для вас)

2.5.1. Инициализация БД для машины разработчика

Спросите у вашего куратора каталог с базой данных и скопируйте содержимое в директорию ~/projects/myproject/deploy/db_data/

2.5.2. Инициализация БД из сжатого дампа

Попросите руководителя предоставить файл дампа. Обычно он называется «db.bk» и положите его в каталог «./deploy/db_backup/db.bk» (в некоторых системах может находится на отдельных дисках, смотрите средо-специфичный файл и инструкции монтирования в контейнере db) Этот каталог примонтирован в БД и изнутри контейнера БД будет доступен по имени «/backup/db.bk» Войдем внутрь контейнера и запустим развертывание дампа. Когда процесс будет завершен, выходим из контейнера и переходим к следующему шагу

dc exec db bash
pg_restore --dbname=postgres --format=c --host=localhost --username=postgres < /backup/db.bk
exit  # выходим из контейнера

2.5.3. Инициализация БД из файловой копии без инкрементных логов

Когда база данных достигает значительного размера, развертывание ее из сжатого дампа может занимать длительное время. В этом случае (или при восстановлении после аварии) используется файловая копия БД. Требуется остановить все контейнеры (если они запущены) dc down.

  • Положите файл файлового бэкапа в ./deploy/db_backup/base_backup.tar.gz

  • Каталог ./deploy/db_data/ должен быть пуст

  • В файле ./docker-compose.yml к контейнеру db временно добавьте строку command: sleep 30d

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

# Распаковка дампа в каталог с БД
tar -xvzf ./deploy/db_backup/base_backup.tar.gz -C ./deploy/db_data/
rm ./deploy/db_data/backup_label

dc up -d db
dc exec db pg_resetwal -f /var/lib/postgresql/data/
exit

# удалите command: sleep 30d из ./docker-compose.yml и перезапустите контейнер
dc up -d db

# Убедимся что там все нормально
# (последняя строка должна быть database system is ready to accept connections)
dc logs

2.5.4. Инициализация БД из файловой копии с инкрементными логами

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

  • Положите архив файлового бэкапа в ./deploy/db_backup/base_backup.tar.gz

  • Положите инкрементные файлы в ./deploy/db_backup/wal/

  • Каталог ./deploy/db_data/ должен быть пуст

  • В файле ./docker-compose.yml к контейнеру db временно добавьте строку command: sleep 30d

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

# Распаковка дампа в каталог с БД
tar -xvzf ./deploy/db_backup/base_backup.tar.gz -C ./deploy/db_data/
touch ./deploy/db_data/recovery.signal

dc up -d db
exit

# удалите command: sleep 30d из ./docker-compose.yml и перезапустите контейнер
dc up -d db

# Убедимся что там все нормально
# (последняя строка должна быть database system is ready to accept connections)
dc logs

# Если вы видите там ниже указанные строки, значит все нормально
# restored log file "0000000100000010000000C2" from archive
# ...
# archive recovery complete
# ...
# database system is ready to accept connections

2.6. Запуск остальных контейнеров

Запустите оставшиеся контейнеры системы в зависимости от вашей среды запуска. То есть, вместо «RUN_MODE» вставьте «dev», «test» или «prod»

./upgrade RUN_MODE

# Посмотрите статусы контейнеров. Если все показывают "Up", значит все хорошо.
# если что-то не так, то смотрим логи контейнеров и бъем в админский бубен
dc ps

2.7. Обновление

Когда необходимо выложить очередное обновление системы, требуется перейти в директорию проекта и запустить скрипт обновления ./upgrade.sh - это команда:

  • git pull для ядра (которое находится в директории «sphere»

  • git pull для конфигурации

  • Пересборка контейнеров dc build

  • Выполнение managment операций (миграции БД, сброс кэша с настройками системы и прочее)

  • Перезапуск контейнеров, которые, запускаясь, работают с новой версией системы

После этого, не забудьте проверить статус системы командой dc ps