Эксплуатация · 0.8.1

Перенос локальной БД и изображений на VPS

Перенесите локальную БД вместе с native-снимками изображений и файлов в новую пустую установку на VPS.

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

Эта инструкция — для первого переноса в новую пустую установку на одном VPS с Local-хранилищами. Сначала успешно завершите Provision и первый Deploy. Затем переносите согласованный набор: БД, снимок изображений и снимок файлов. Образы Docker этих данных не содержат.

Если на сервере уже есть нужные пользователи или записи, полный restore им не подходит: он заменяет БД. Для сохранения серверных аккаунтов нужен выборочный импорт только бизнес-данных по вашей модели. Исключение локальных auth-строк из дампа само по себе не сохраняет пользователей цели: pg_restore --clean всё равно пересоздаёт таблицы. Не запускайте этот сценарий поверх рабочего набора данных.

Для переноса production-копии используйте backup/restore контроллера, который согласует БД и native snapshots. Ниже — ручной dev → новый VPS. Сценарий рассчитан на Local storage; для S3 сначала подготовьте соответствующую схему доступа и переноса.

Пакет создают команды ниже; JSON-файлы вручную писать не нужно:

TRANSFER_DIR/
  database.dump
  media/                native-снимок изображений
  media-result.json     результат export со storeIdentity
  files/                native-снимок документов
  files-result.json     результат export со storeIdentity

Папки и result-файлы отсутствующего в модели вида вложений пропускаются.

1. Согласовать версию и остановить писателей

Локальная схема и набор миграций должны соответствовать серверному релизу. В Source CI временно выберите точный production-коммит на локальной машине, а не просто main: merge мог создать другой SHA. Основной серверный checkout также обновляйте отдельно — CI не переключает его ветку.

Остановите make dev, cleanup и custom-задачи, пишущие в исходную БД и хранилища. До завершения export не возобновляйте запись. На VPS не создавайте аккаунты или контент до импорта.

PROJECT_NAME, YOUR_SERVER_HOST и имена контейнеров ниже замените своими значениями. Имя dev-контейнера PostgreSQL найдите через docker ps; оно станет DEV_POSTGRES. Во всех следующих локальных блоках оставайтесь в открытой Bash-сессии. set -e останавливает сессию при ошибке: последующие команды не выполняйте, пока не разберёте её.

2. Подготовить локальные параметры

bash

Откроется Bash. В этой сессии выполните:

set -e
cd /path/to/PROJECT_NAME
export APP_PROFILE=local
unset DATABASE_URL DATABASE_URL_FILE
make migrate

Теперь отдельно выполните команду ввода строки подключения, вставьте приватный DATABASE_URL и нажмите Enter:

read -r -s -p 'Local DATABASE_URL: ' DATABASE_URL

После ввода продолжите:

printf '\n'
export DATABASE_URL
export MEDIA_ROOT=/absolute/path/to/local/media
export FILES_ROOT=/absolute/path/to/local/files

ENV_NAME=local
DEV_POSTGRES=YOUR_DEV_POSTGRES_CONTAINER
TRANSFER_DIR="$HOME/project-transfer-$(date +%Y%m%d-%H%M%S)"
umask 077
mkdir -m 700 "$TRANSFER_DIR"

make migrate применяет недостающие миграции до снимка. Если он сообщает ошибку checksum, остановитесь: исправьте соответствие исходников и БД, не редактируйте журнал вручную.

DATABASE_URL возьмите из приватной конфигурации dev-приложения; это подключение к той же БД, что использует DEV_POSTGRES. Ввод через read не сохраняет URL командой в истории. Задайте действительные абсолютные MEDIA_ROOT и FILES_ROOT своей локальной среды. Go-команды ниже не загружают .env автоматически. --env local — метка снимка, она не выбирает конфигурационный файл.

Если проект не использует изображения или файлы, соответствующей snapshot-команды может не быть: пропустите её блок и связанные действия на VPS. TRANSFER_DIR — новая приватная папка вне Git.

3. Экспортировать изображения и файлы

Для изображений:

mkdir -m 700 "$TRANSFER_DIR/media"
(
  cd backend
  go run ./cmd/media-snapshot --mode export \
    --env "$ENV_NAME" --stage "$TRANSFER_DIR/media" --writers-frozen \
    > "$TRANSFER_DIR/media-result.json"
)

Для нативных файлов, включая PDF и презентации:

mkdir -m 700 "$TRANSFER_DIR/files"
(
  cd backend
  go run ./cmd/files-snapshot --mode export \
    --env "$ENV_NAME" --stage "$TRANSFER_DIR/files" --writers-frozen \
    > "$TRANSFER_DIR/files-result.json"
)

Каталоги stage должны быть пустыми. Snapshot-команды сверяют объекты с БД и записывают их manifest/hash. Сохраните также media-result.json и files-result.json: в них находится source storeIdentity, который потребуется при restore. Это не поле snapshot.json.

Экспорт snapshots выполняется до pg_dump: создание первой привязки пустого хранилища тогда тоже попадёт в дамп. После ошибки не переиспользуйте частичный stage — разберите сообщение и выберите новую папку.

4. Сохранить БД и передать весь пакет

docker exec "$DEV_POSTGRES" sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc --no-owner --no-acl' \
  > "$TRANSFER_DIR/database.dump"

scp -r "$TRANSFER_DIR" deploy@YOUR_SERVER_HOST:/home/deploy/

Указанный dump включает локальные аккаунты и сессии. После полного переноса вход будет с локальными учётными данными. Если нужны новые серверные аккаунты, добавьте --exclude-table-data='admingen_auth.*' к pg_dump и после восстановления выполните bootstrap нового суперадминистратора. Этот вариант по-прежнему предназначен только для пустой цели.

Не отправляйте только database.dump: в PostgreSQL нет содержимого медиафайлов. После успешного export можно возобновить локальную разработку; пакет остаётся согласованным снимком выбранного момента.

5. На VPS остановить приложение и подготовить restore

Дальнейшие команды выполняйте под deployment-пользователем в Bash. Убедитесь, что цель действительно пустая. Остановите также CI-выпуски, cleanup и внешних писателей на всё время восстановления.

ENV_NAME — имя профиля. RUNTIME_ROOT и Local storage roots возьмите в Deployment → Environments → Preview effective JSON. TRANSFER_DIR — папка, которую только что передали scp; её имя включает дату. COMPOSE_PROJECT найдите по метке контейнера своего проекта:

docker ps -a --format 'table {{.Names}}\t{{.Label "com.docker.compose.project"}}'
bash

В открытой Bash-сессии:

set -e
cd "$HOME/projects/PROJECT_NAME"
ENV_NAME=production
RUNTIME_ROOT=/srv/admingen/PROJECT_NAME/production
COMPOSE_PROJECT=YOUR_COMPOSE_PROJECT
TRANSFER_DIR="$HOME/project-transfer-YYYYMMDD-HHMMSS"
MEDIA_ROOT=/absolute/production/media
FILES_ROOT=/absolute/production/files

bash ./deploy/control down --env "$ENV_NAME" --confirm "$ENV_NAME"

BACKEND_IMAGE=$(jq -er .backend "$RUNTIME_ROOT/state/current.json")
PG=$(docker ps -a \
  --filter "label=com.docker.compose.project=$COMPOSE_PROJECT" \
  --filter label=com.docker.compose.service=postgres --format '{{.ID}}')
docker start "$PG"
docker exec "$PG" pg_isready -U postgres -d app

RUNTIME_ROOT, storage roots и COMPOSE_PROJECT берите из фактической установки. Для generated server БД называется app, пользователь postgres. У PG должен быть ровно один ID; если контейнер не найден или найдено несколько, остановитесь. Продолжайте только после ответа accepting connections от pg_isready.

Backend/frontend остаются остановленными, запускается только существующая PostgreSQL. Не создавайте новую БД и не выполняйте второй Provision ради переноса.

6. Проверить пустоту хранилищ

Следующая функция запускает native snapshot-команды из текущего backend-образа с производственным подключением и его CA. Укажите именно действительные Local-пути из профиля. Secrets не копируются в пакет.

snapshot() {
  local kind="$1" storage="$2" variable
  shift 2
  case "$kind" in
    media) variable=MEDIA_ROOT ;;
    files) variable=FILES_ROOT ;;
    *) return 1 ;;
  esac
  docker run --rm --platform linux/amd64 \
    --network "$COMPOSE_PROJECT-private" \
    --mount "type=bind,src=$RUNTIME_ROOT/secrets/database_url,dst=/run/secrets/database_url,readonly" \
    --mount "type=bind,src=$RUNTIME_ROOT/pki/ca.crt,dst=/run/secrets/ca.crt,readonly" \
    --mount "type=bind,src=$storage,dst=$storage" \
    --mount "type=bind,src=$TRANSFER_DIR/$kind,dst=/snapshot,readonly" \
    -e APP_PROFILE=production -e DATABASE_URL_FILE=/run/secrets/database_url \
    -e "$variable=$storage" \
    --entrypoint "$kind-snapshot" "$BACKEND_IMAGE" "$@"
}

snapshot media "$MEDIA_ROOT" --mode verify-empty --env "$ENV_NAME" --writers-frozen
snapshot files "$FILES_ROOT" --mode verify-empty --env "$ENV_NAME" --writers-frozen

Verify-empty проверяет физическое хранилище. Если в нём уже есть объекты, команда остановится: существующие данные не удаляют ради продолжения. Не передавайте verify-empty параметры stage/restore.

7. Восстановить БД и native snapshots

Только после успешной проверки обоих используемых хранилищ:

docker exec -i "$PG" pg_restore -U postgres -d app \
  --clean --if-exists --exit-on-error --single-transaction --no-owner --no-acl \
  < "$TRANSFER_DIR/database.dump"

if [ -d "$TRANSFER_DIR/media" ]; then
  sudo chown -R 10001:10001 "$TRANSFER_DIR/media"
fi
if [ -d "$TRANSFER_DIR/files" ]; then
  sudo chown -R 10001:10001 "$TRANSFER_DIR/files"
fi

snapshot media "$MEDIA_ROOT" --mode restore \
  --env "$ENV_NAME" --stage /snapshot --writers-frozen \
  --confirm-restore "$ENV_NAME" \
  --expected-identity "$(jq -er .storeIdentity "$TRANSFER_DIR/media-result.json")"

snapshot files "$FILES_ROOT" --mode restore \
  --env "$ENV_NAME" --stage /snapshot --writers-frozen \
  --confirm-restore "$ENV_NAME" \
  --expected-identity "$(jq -er .storeIdentity "$TRANSFER_DIR/files-result.json")"

Chown меняет владельца только переданных snapshot-папок для чтения стандартным UID backend. Не применяйте его к чужим каталогам. Если deployment-пользователь не имеет sudo, этот шаг выполнит оператор сервера.

Restore проверяет БД, source identity и manifest объектов, переносит файлы и сам регистрирует привязку к новому хранилищу. SQL для ручной замены store_identity и копирование сырой media-папки не нужны. При ошибке оставьте приложение остановленным и разберите сообщение; повторный полный restore вслепую не выполняют.

8. Запустить и проверить

Для Source/локального on-host управления запустите записанный релиз:

bash ./deploy/control up --env "$ENV_NAME" --confirm "$ENV_NAME"
bash ./deploy/control status --env "$ENV_NAME" --json

Если окружением управляет удалённый Archive/Registry CI, используйте тот же контроллер с его штатными доступами, а не SSH к самому себе без ключей. Для Source CI также можно повторить Deploy того же коммита после успешного импорта.

Проверьте записи, переводы, изображения разных размеров, скачивание файлов и вход. При исключённой auth-части сначала создайте нового приватного суперадминистратора; публичную demo-учётку создавайте отдельно с нужными правами. Обычный следующий deploy сохраняет эти данные — перенос заново не требуется.