Сайт ↗
Разделы документации
Эксплуатация · 0.8.1

Перенос dev-данных в prod-local и на другой компьютер

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

На этой странице

Здесь два локальных сценария: перенести dev-данные в prod-local и продолжить разработку на другом компьютере. Для переноса на сервер есть отдельная полная инструкция dev → VPS.

Нужны дамп PostgreSQL и архив изображений. Они относятся к одной версии данных. Инструкция рассчитана на Local media → Local media. Пользователи и их пароли переедут вместе с БД; существующие и переносимые данные не объединяются.

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

Сначала: сделайте копию dev

Все команды этого раздела выполняйте на исходном компьютере, из корня сгенерированного проекта. Остановите make dev через Ctrl+C и свои задачи, которые пишут в БД или меняют изображения. PostgreSQL оставьте работающим:

make up
make wait-db
docker ps --format '{{.Names}}'

Из списка возьмите имя dev-контейнера PostgreSQL, не prod-local. Ниже пример example_project-postgres-1. Значения POSTGRES_DB и POSTGRES_USER возьмите из своей .env, а DEV_MEDIA — из MEDIA_ROOT, если вы его переопределяли. По умолчанию папка изображений показана в примере:

DEV_POSTGRES=example_project-postgres-1
DEV_DB=example_project
DEV_USER=postgres
DEV_MEDIA="$HOME/.local/share/example_project/media"
umask 077
TRANSFER_DIR="$HOME/example-transfer-$(date +%Y%m%d-%H%M%S)"
mkdir -m 700 "$TRANSFER_DIR"

Сохраните БД, затем все изображения и готовые размеры:

docker exec "$DEV_POSTGRES" \
  pg_dump -U "$DEV_USER" -d "$DEV_DB" -Fc \
  > "$TRANSFER_DIR/database.dump"
COPYFILE_DISABLE=1 tar -czf "$TRANSFER_DIR/media.tar.gz" -C "$DEV_MEDIA" .
printf 'Копия сохранена: %s\n' "$TRANSFER_DIR"

Переходите дальше только после успешного завершения обеих команд. Если изображений в БД никогда не было, архив и все действия с media ниже пропустите. Если изображения были, а папка пропала, одного дампа недостаточно: найдите копию файлов.

Папка копии находится вне проекта и не должна попадать в Git. Пока оба файла не готовы, dev не запускайте. Дальше выберите один из двух сценариев.

Вариант A. Импорт в prod-local

A1. Сначала запустите пустой prod-local

На компьютере назначения должен быть тот же код и те же миграции, что у дампа. В Generator UI создайте профиль local с preset prod-local, сохраните и выполните Generate. Если prod-local ещё не запускали:

bash ./deploy/control provision --env local
bash ./deploy/control up --env local
bash ./deploy/control status --env local --json

Дождитесь успешного запуска: status должен показывать current и не иметь активной операции. Импорт выполняют после этого, не между provision и первым up. Иначе БД окажется непустой без зарегистрированного первого релиза.

Для уже работающего prod-local повторять provision не нужно. Если в нём есть данные, которые надо сохранить, сначала сделайте штатный backup:

bash ./deploy/control backup --env local --confirm local

Сохраните ID копии. Следующий импорт заменяет таблицы, записи и аккаунты целевой БД. Для свежего пустого окружения этот backup можно пропустить.

A2. Выберите контейнеры и остановите приложение

Далее все команды — на компьютере с prod-local, в одном терминале до завершения A6. Если это другая машина, сначала передайте на неё папку копии и проект через свой обычный способ передачи файлов. Здесь scp не нужен, если копия уже на том же компьютере.

docker ps --format '{{.Names}}'

Подставьте имена local, не dev-контейнеров. TRANSFER_DIR — абсолютный путь к папке, где лежат database.dump и media.tar.gz:

TRANSFER_DIR=/absolute/path/to/example-transfer
TARGET_BACKEND=example_project-local-backend-1
TARGET_FRONTEND=example_project-local-frontend-1
TARGET_POSTGRES=example_project-local-postgres-1
TARGET_DB=app

docker stop "$TARGET_FRONTEND" "$TARGET_BACKEND"

PostgreSQL не останавливайте. app — имя БД нового prod-local; если ранее выполняли restore, используйте имя действующей БД. Остановите также свои внешние процессы записи.

A3. Восстановите БД

docker exec -i "$TARGET_POSTGRES" \
  pg_restore -U postgres -d "$TARGET_DB" \
  --clean --if-exists --no-owner --no-privileges --single-transaction \
  < "$TRANSFER_DIR/database.dump"

--clean заменяет содержимое из дампа, --single-transaction отменяет частичное восстановление при SQL-ошибке. Если команда завершилась ошибкой, не переходите к запуску приложения.

A4. Восстановите изображения

Этот шаг нужен, если в дампе есть изображения. Узнайте путь внутри backend и соответствующую папку компьютера:

export BACKEND_MEDIA=$(docker inspect "$TARGET_BACKEND" \
  --format '{{range .Config.Env}}{{println .}}{{end}}' \
  | sed -n 's/^MEDIA_ROOT=//p')
TARGET_MEDIA=$(docker inspect "$TARGET_BACKEND" \
  | jq -er --arg path "$BACKEND_MEDIA" \
    '.[0].Mounts[] | select(.Destination == $path and .Type == "bind") | .Source')
TARGET_IMAGE=$(docker inspect "$TARGET_BACKEND" --format '{{.Config.Image}}')
test -n "$BACKEND_MEDIA" && test -d "$TARGET_MEDIA"

Распакуйте архив и назначьте владельца backend. Временный контейнер использует уже установленный образ: приложение в нём не запускается. Такой способ работает и с Docker Desktop, и на Linux, без ручного изменения владельца всей папки проекта:

docker run --rm -i --platform linux/amd64 --user 0 \
  --entrypoint tar \
  --mount "type=bind,src=$TARGET_MEDIA,dst=/media" \
  "$TARGET_IMAGE" -xzf - -C /media < "$TRANSFER_DIR/media.tar.gz"

docker run --rm --platform linux/amd64 --user 0 \
  --entrypoint chown \
  --mount "type=bind,src=$TARGET_MEDIA,dst=/media" \
  "$TARGET_IMAGE" -R 10001:10001 /media

A5. Обновите привязку к папке изображений

БД перенесена из dev и помнит старый путь. После копирования файлов этот блок вычислит новое значение по пути backend; вручную рассчитывать или сверять идентификаторы не нужно. Нужен Python 3 на компьютере:

MEDIA_ID=$(python3 - <<'PYTHON'
import hashlib, os, posixpath
root = os.environ['BACKEND_MEDIA']
assert root.startswith('/') and root != '/', 'Проверьте BACKEND_MEDIA'
print(hashlib.sha256(b'local\0' + posixpath.normpath(root).encode() + b'\0').hexdigest())
PYTHON
)

docker exec -i "$TARGET_POSTGRES" \
  psql -U postgres -d "$TARGET_DB" \
  -v ON_ERROR_STOP=1 -v media_id="$MEDIA_ID" <<'SQL'
BEGIN;
SELECT pg_advisory_xact_lock(731947120);
INSERT INTO admingen_media.store_identity (singleton, identity)
VALUES (true, :'media_id')
ON CONFLICT (singleton) DO UPDATE SET identity = EXCLUDED.identity;
COMMIT;
SQL

Эта операция не восстанавливает картинки — она применяется только после A4 к соответствующей копии файлов.

A6. Запустите prod-local

docker start "$TARGET_BACKEND"
docker logs --tail 50 "$TARGET_BACKEND"
docker exec "$TARGET_BACKEND" migrate --verify

Если backend запустился без ошибки и миграции подтверждены:

docker start "$TARGET_FRONTEND"
bash ./deploy/control status --env local --json

Войдите в local-админку аккаунтом из dev. Откройте записи и картинки. При необходимости заново войдите: серверные ключи подписи не копировались вместе с БД. Сохраняйте дамп и архив, пока не убедитесь в результате.

Вариант B. Продолжить dev на другом компьютере

B1. Передайте проект и копию

На новый компьютер передайте исходники того же коммита и папку TRANSFER_DIR. В проекте нужны .admingen/project.yaml, custom-код и вся backend/migrations, включая manifest. Файлы можно передать через Git и отдельное копирование архива. Дамп не добавляйте в Git.

На новом компьютере откройте корень проекта. Все блоки B1–B4 выполняйте в одном терминале. Настройте его .env; если файла ещё нет:

make local-environment

Настройка создаёт локальные параметры этой машины. Она не заменяет импорт данных. Запустите только PostgreSQL, пока без make dev:

make up
make wait-db
docker ps --format '{{.Names}}'

Выберите dev-контейнер и заполните значения из новой .env:

TRANSFER_DIR=/absolute/path/to/example-transfer
TARGET_POSTGRES=example_project-postgres-1
TARGET_DB=example_project
TARGET_USER=postgres

B2. Восстановите пустую БД

Этот вариант рассчитан на пустую целевую dev-БД:

docker exec -i "$TARGET_POSTGRES" \
  pg_restore -U "$TARGET_USER" -d "$TARGET_DB" \
  --no-owner --no-privileges --single-transaction \
  < "$TRANSFER_DIR/database.dump"

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

B3. Распакуйте изображения и задайте новый путь

Если изображений не было, переходите к B4. Иначе на новом компьютере:

export MEDIA_ROOT="$HOME/.local/share/example_project/media"
mkdir -p "$MEDIA_ROOT"
tar -xzf "$TRANSFER_DIR/media.tar.gz" -C "$MEDIA_ROOT"

Архив из начала этой статьи содержит файлы без внешней папки media. Распаковка именно такая. После неё обновите привязку БД:

MEDIA_ID=$(python3 - <<'PYTHON'
import hashlib, os
from pathlib import Path
root = Path(os.environ['MEDIA_ROOT']).resolve(strict=True)
assert root.is_dir(), 'MEDIA_ROOT должен быть папкой'
print(hashlib.sha256(b'local\0' + str(root).encode() + b'\0').hexdigest())
PYTHON
)

docker exec -i "$TARGET_POSTGRES" \
  psql -U "$TARGET_USER" -d "$TARGET_DB" \
  -v ON_ERROR_STOP=1 -v media_id="$MEDIA_ID" <<'SQL'
BEGIN;
SELECT pg_advisory_xact_lock(731947120);
INSERT INTO admingen_media.store_identity (singleton, identity)
VALUES (true, :'media_id')
ON CONFLICT (singleton) DO UPDATE SET identity = EXCLUDED.identity;
COMMIT;
SQL

B4. Запустите разработку

Из того же терминала, где установлен MEDIA_ROOT:

make dev

Войдите с перенесённым аккаунтом, проверьте записи и изображения. При следующем запуске используйте тот же MEDIA_ROOT; если это стандартный путь, отдельная настройка не требуется. Для своего пути сохраните его в окружении запуска backend.

Если backend сообщает media storage cannot change while assets exist, сначала проверьте, какой путь он получает и выполнен ли шаг B3 после распаковки. Не удаляйте изображения и таблицу привязки для обхода ошибки.