Управление конфигурацией ПО и баз данных: версионирование, миграции и секреты | AdminWiki

Управление конфигурацией ПО и баз данных: версионирование, миграции и секреты

19 сентября 2026 11 мин. чтения

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

Рабочий минимум выглядит так. Пароли и токены уходят в защищённое хранилище и не попадают в Git. Конфигурационные файлы лежат в репозитории и рендерятся из шаблонов под каждую среду. Схема БД описывается миграциями с прямым и обратным действием, а приложение во время обновления работает и со старой, и с новой версией схемы. Это даёт воспроизводимость окружений, аудит изменений и предсказуемый откат релиза.

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

Почему конфигурация и данные должны быть разделены

Это разделение задаёт границу между тем, что версионируется, и тем, что защищается резервными копиями. Нарушение границы приводит к утечкам, невоспроизводимым окружениям и опасным откатам.

Что относится к конфигурации, а что к данным

Конфигурация описывает, как приложение работает. В неё входят:

  • переменные окружения: адрес БД, порт, уровень логирования, лимиты памяти;
  • строки подключения к внешним сервисам: брокеру сообщений, кэшу, S3-совместимому хранилищу;
  • учётные данные и токены доступа;
  • флаги функций и параметры экспериментов;
  • настройки логирования, метрик и трассировки;
  • параметры пулов соединений и таймаутов.

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

Схема БД стоит отдельно. Это структура: таблицы, столбцы, типы, индексы, ограничения. Её версионируют миграциями, а не копированием строк, и инструменты для неё свои: Flyway, Liquibase, Alembic, встроенные миграции Django.

Конфигурация хранится в окружении, а не в коде. Так формулирует правило методология 12-factor app.

Практический пример для приложения на Python: настройки подключения лежат в файле .env, который внесён в .gitignore, а в репозитории остаётся .env.example с перечнем переменных без значений. Данные при этом находятся в PostgreSQL, а структура таблиц описана файлами миграций.

Последствия смешивания конфигурации и данных

  • Утечка секретов. Токен, закоммиченный в репозиторий, остаётся в истории коммитов даже после удаления файла. Простого удаления мало, нужна ротация ключа и разбор журналов доступа.
  • Невоспроизводимость окружений. Когда параметры зашиты в код или в дампы базы, тестовый стенд не собрать из репозитория, а расхождения между dev и prod растут с каждым релизом.
  • Путаница при откате. Если миграция схемы и правка данных выполняются одним шагом, откат релиза повреждает данные, которые иначе остались бы нетронутыми.
  • Ошибки в CI/CD. Артефакт сборки с продовыми учётными данными уезжает в тестовую среду или попадает в кэш сборщика.
  • Дрейф конфигурации. Правки «руками» на сервере расходятся с тем, что описано в репозитории, и следующий деплой их затирает либо ломается сам.

Версионирование схемы базы данных и миграции

Миграция - это файл с описанием одного изменения схемы. Инструмент ведёт таблицу применённых версий (например, flyway_schema_history или alembic_version) и применяет только новые файлы в заданном порядке. Состояние схемы в любой момент выводится из истории миграций, а не из памяти администратора.

Рабочий цикл обычно такой: разработчик создаёт миграцию, CI прогоняет её на чистой базе и на копии продакшен-данных, ревьюер оценивает блокировки и длительность, затем миграция проходит stage и prod. Обратная миграция или подтверждённый план отката идёт в том же коммите, что и прямая.

Инструменты для миграций: сравнение и выбор

ИнструментФормат миграцийОткатКогда подходит
FlywaySQL и JavaОбратные скрипты пишут вручную, автоматический undo зависит от редакцииJVM-стек и проекты, где миграции ведут на чистом SQL
LiquibaseXML, YAML, JSON, SQL changelogПравила rollback описывают в changelogКоманды с разными стеками, нужен единый журнал изменений
AlembicPython, SQLAlchemyФункция downgrade() в каждой ревизииPython-проекты на SQLAlchemy
Django migrationsPython, Django ORMЧасть операций получает обратное действие автоматически, сложные изменения описывают вручнуюПроекты на Django

Ориентир по выбору: для Java и Spring Boot берите Flyway или Liquibase, для Python с SQLAlchemy - Alembic, для Django - встроенные миграции. Набор флагов отката и редакции инструментов меняются от версии к версии, поэтому перед настройкой сверяйтесь с документацией именно вашей версии.

Как писать обратимые миграции

У каждой миграции должно быть прямое и обратное действие. Проверяемый пример для добавления столбца:

ALTER TABLE orders ADD COLUMN currency varchar(3);

-- обратное действие
ALTER TABLE orders DROP COLUMN currency;

Обратимыми обычно выходят создание и удаление индекса, добавление и удаление столбца, переименование таблицы с обратным переименованием, создание таблицы и её удаление.

Опасные операции, где откат теряет данные: удаление столбца, сужение типа (varchar(255) до varchar(50)), смена типа с потерей точности, добавление NOT NULL без значения по умолчанию на большой таблице, удаление таблицы. Перед каждой такой миграцией делайте бэкап и проверяйте, что он восстанавливается.

Транзакции. PostgreSQL, Oracle и большая часть реляционных СУБД поддерживают DDL внутри транзакции, поэтому неудачная миграция откатывается целиком. В MySQL и MariaDB DDL вызывает неявный коммит, и большой ALTER TABLE назад не откатить, остаётся план восстановления. По этой же причине длительные изменения разносят на шаги: ALTER на большой таблице держит блокировку и останавливает запись.

Идемпотентность помогает при повторном прогоне: CREATE TABLE IF NOT EXISTS, CREATE INDEX IF NOT EXISTS, проверки на существование столбца в SQL. Тестируйте миграцию на копии продакшен-данных: это покажет и длительность, и блокировки, и объём журнала транзакций, который она породит.

Управление секретами: безопасное хранение паролей и токенов

Секреты (пароли БД, ключи API, приватные ключи, токены CI) хранятся вне репозитория и вне образов контейнеров. Проверка простая: если файл с секретом попал в историю Git, секрет считают скомпрометированным и меняют.

Инструменты для управления секретами

ИнструментМодель храненияЧто даётОграничения
HashiCorp VaultЦентрализованный сервисДинамические секреты с TTL, аудит доступа, журнал операций, разные движки секретовНужно развернуть сервис, хранить unseal-ключи и сопровождать его
Kubernetes SecretsОбъекты внутри кластераНативная выдача секретов в Pod, монтирование файлом или переменнойЗначения по умолчанию не шифруются, нужны шифрование etcd и строгий RBAC
SOPSЗашифрованные файлы в GitШифрует значения, оставляя ключи читаемыми, работает с age, GPG и облачными KMSУправление ключами расшифровки на стороне команды
Ansible VaultШифрование файлов и переменныхВстроен в Ansible, отдельный сервис не нуженКлюч расшифровки всё равно нужно где-то хранить
AWS Secrets Manager, GCP Secret Manager, Azure Key VaultУправляемый сервисРотация, версии секретов, интеграция с IAM и журналамиПривязка к облаку и оплата за секреты и запросы к ним

Сценарии выбора: команда без Kubernetes обычно обходится SOPS или Ansible Vault плюс защищённые переменные в CI. Для кластера Kubernetes включают шифрование etcd и ограничивают доступ к Secrets через RBAC. Когда нужны короткоживущие учётные данные для БД с автоматической сменой, подходит Vault с динамическими секретами.

Практики безопасной работы с секретами в CI/CD

  • Держите секреты в защищённых переменных: GitHub Environments и Secrets, переменные GitLab CI с флагами Protected и Masked.
  • Не печатайте значения в лог. Маскирование работает, но не спасает от base64, разбиения строки по символам и вывода внутри многострочного блока.
  • Помните, что pull request из форка не должен получать секреты. В GitHub Actions это поведение по умолчанию, и отключать его ручными настройками не стоит.
  • Заменяйте долгоживущие ключи на OIDC-токены: GitHub Actions и GitLab CI умеют получать временные учётные данные в облаке без статического ключа.
  • Ротируйте секреты по расписанию и после каждого ухода сотрудника с доступом.
  • Ведите аудит обращений к хранилищу и ставьте алерт на всплеск чтения в нерабочие часы.

Конвейер удобно строить так, чтобы секреты подставлялись в момент выполнения задачи, а не хранились в артефакте. Разбор архитектуры пайплайна с этапами сборки, тестов и ручного подтверждения перед prod - в статье автоматизированное развёртывание приложений через CI/CD.

Хранение конфигурационных файлов в репозитории и шаблонизация

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

Шаблонизация конфигов для разных сред

  • Ansible: один шаблон Jinja2 и разные наборы переменных в group_vars/dev.yml и group_vars/prod.yml.
  • Helm: общий values.yaml плюс values-dev.yaml, values-stage.yaml, values-prod.yaml; рендер через helm template в CI для проверки результата.
  • kustomize: базовый набор манифестов и overlays под каждую среду без шаблонного синтаксиса.

Сгенерированные конфиги проверяют до деплоя: yamllint для синтаксиса, схема JSON для обязательных полей, helm lint для чартов, kubectl apply с dry-run на стороне сервера. Ошибка в шаблоне, найденная в CI, стоит минуты, та же ошибка на проде стоит простоя.

Подстановка значений и приоритеты

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

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

Откат миграций и проверка совместимости версий

Откат проектируют до применения миграции, а не в момент инцидента. Рабочих вариантов три, и они не исключают друг друга.

Стратегии отката миграций

  1. Обратная миграция. Подходит, если изменение обратимо без потери данных: добавленный столбец, индекс, новая таблица.
  2. Восстановление из бэкапа с point-in-time recovery. Нужно, когда миграция удалила или переписала данные. Требует проверенного бэкапа и известного времени восстановления.
  3. Откат на уровне приложения. Флаг функции отключает новую логику, схема остаётся на месте. Часто это самый дешёвый способ вернуть рабочее состояние.

Схема expand/contract (расширение и сжатие) делает откат безопасным. Сначала добавляете новый столбец или таблицу, приложение пишет и в старое, и в новое место, затем чтение переключается на новое, и только после периода стабильности старые объекты удаляются отдельной миграцией. Удаление выносят в конец, поэтому откат на любом шаге не теряет данные.

Бэкап без проверки восстановления ничего не гарантирует. Методику проверки копий, расчёт времени восстановления и порядок действий при откате разбирают в материале о планировании миграции и снижении рисков.

Проверка совместимости версий приложения и БД

Правило: во время деплоя приложение версии N и версия N+1 должны работать с одной и той же схемой. Достигается это расширяющими миграциями и дисциплиной релиза.

  • Не удаляйте и не переименовывайте столбец в том же релизе, где меняется код. Переименование идёт в два шага: новый столбец, запись в оба, переключение чтения, удаление старого.
  • Новые столбцы делайте nullable или со значением по умолчанию, чтобы старый код продолжал вставлять строки.
  • Проверяйте контракты API и схемы событий между сервисами, если версии обновляются не одновременно.
  • Прогоняйте миграцию на копии продакшен-данных и запускайте интеграционные тесты уже против мигрированной схемы.
  • Используйте канареечный деплой: часть трафика на новой версии покажет несовместимость до полного переключения.

Если изменения затрагивают не только схему, но и всю инфраструктуру, план отката строится шире: проверка совместимости сервисов, окна простоя и порядок переключения описаны в статье о миграции ИТ-инфраструктуры и её скрытых сложностях.

Типичные ошибки и как их избежать

  • Секреты в Git. Причина обычно в спешке при первом коммите. Решение: .gitignore для .env, .env.example вместо реальных значений, сканер секретов в pre-commit и в CI.
  • Нет обратной миграции. Решение: обратный сценарий в том же коммите, а для необратимых изменений - письменный план отката с бэкапом.
  • Миграции без тестирования. На пустой базе они проходят мгновенно, на проде блокируют таблицу на минуты. Решение: прогон на копии продакшен-данных с замером времени и блокировок.
  • Ручные правки схемы на продакшене. Миграции перестают отражать реальность, и следующий релиз падает. Решение: любые изменения только через миграции, а расхождения ищут сверкой схемы.
  • Миграция данных и схемы одним шагом. При откате теряются данные. Решение: разделять изменение структуры и перенос данных на отдельные шаги.
  • Блокирующие миграции в часы пик. Решение: выносить тяжёлые ALTER в окно обслуживания либо разбивать на создание нового объекта и постепенное переключение.
  • Избыточные права у CI-раннера. Утечка токена даёт полный доступ к базе. Решение: отдельные учётные записи для миграций с правами только на нужную схему.

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

Чек-лист по управлению конфигурацией

  1. Разделите конфигурацию и данные: параметры и секреты вне базы, пользовательские данные вне репозитория.
  2. Проверьте историю репозитория на секреты и отзовите всё найденное, включая ключи, удалённые из последних коммитов.
  3. Выберите инструмент миграций под свой стек и убедитесь, что таблица версий схемы ведётся автоматически.
  4. Опишите прямое и обратное действие для каждой миграции, а необратимые изменения выделите в отдельный список.
  5. Переведите конфиги на шаблоны (Jinja2, Helm, kustomize) вместо копий файлов по средам.
  6. Перенесите секреты в хранилище (Vault, SOPS, Ansible Vault, менеджер секретов облака) и включите маскирование в CI.
  7. Добавьте в пайплайн прогон миграции на копии данных, интеграционные тесты и ручное подтверждение перед prod.
  8. Проверяйте бэкап восстановлением и фиксируйте целевое время восстановления.
  9. Введите правило обратной совместимости схемы на один релиз вперёд и канареечный деплой для проверки на части трафика.

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

Поделиться:
Сохранить гайд? В закладки браузера