Terraform и OpenTofu: что делать с блокировкой state после оборванного запуска

Новый запуск остановился на Error acquiring the state lock. Предыдущий pipeline отменён, поэтому возникает желание удалить lock и повторить apply. Но статус CI не доказывает, что процесс, который менял инфраструктуру, уже остановился. Он мог продолжить работу на Runner или передать выполнение удалённой системе.

Блокировка защищает состояние от нескольких одновременных писателей в Terraform и OpenTofu. Прежде чем снимать её, нужно установить, кто её держит, что делает этот процесс и действительно ли автоматическое освобождение сорвалось. Ошибка lock — полезный сигнал о конфликте доступа, а не самостоятельное доказательство повреждения state.

Сохраним контекст ошибки

Из сообщения выпишите ID блокировки, путь состояния, операцию, владельца и время создания, если backend их сообщает. Сопоставьте их с последним job: commit, рабочий каталог, backend-конфигурация и workspace. Один репозиторий может управлять несколькими независимыми состояниями, поэтому одинаковое имя проекта не гарантирует совпадение объекта блокировки.

Для локального CLI выбранный workspace можно посмотреть командой terraform workspace show или tofu workspace show. Backend определяется настройками рабочего каталога и инициализации. Сравните их с параметрами CI, включая передаваемые backend-config. Не меняйте backend во время диагностики только ради исчезновения ошибки: так легко попасть в другой state и получить убедительный, но нерелевантный plan.

Проверим, продолжается ли операция

Откройте последний журнал владельца lock. Найдите этап, на котором завершился вывод: ожидание API, создание ресурса, запись state или освобождение блокировки. Если Runner доступен администратору, проверьте процесс и его дочерние процессы. Для удалённого выполнения дополнительно смотрите статус run в той системе, которая фактически выполняет операцию.

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

Если очередь нормальная, разрешим ожидание

Когда причина — другой подтверждённый запуск, можно задать ограниченное время ожидания. Команда plan в примере не применяет инфраструктурные изменения, но обращается к backend и провайдерам. Используйте свой инструмент и обычный набор переменных окружения.

terraform plan -lock-timeout=5m
tofu plan -lock-timeout=5m

Пять минут здесь — выбранный бюджет ожидания, а не срок жизни lock. После его истечения команда завершится ошибкой, если блокировку не удалось получить. Значение подбирают под нормальную длительность конкурирующего процесса. Такой режим удобнее, чем многократно перезапускать pipeline и терять исходный контекст.

Флаг -lock=false убирает защиту на стороне команды. Он не чинит зависшего владельца и позволяет двум писателям работать с одним состоянием. Если инфраструктуру обслуживает CI, устраните конкуренцию или зависший процесс, сохранив блокировку как часть протокола доступа.

Когда допустим force-unlock

Ручное освобождение используют для своей оставшейся блокировки, когда процесс завершён и автоматическое снятие не удалось. Подставьте точный ID из ошибки соответствующего состояния; не используйте ID из старого журнала по памяти. Следующие команды меняют блокировку backend и предназначены для оператора после проверки владельца, а не для автоматического шага при каждой ошибке.

terraform force-unlock LOCK_ID_FROM_ERROR
tofu force-unlock LOCK_ID_FROM_ERROR

Выберите одну команду своего инструмента и прочитайте запрос подтверждения. Снятие чужого активного lock создаёт нескольких писателей и риск несогласованного состояния. Для локальной блокировки механизм зависит от локального процесса: force-unlock не заменяет его завершение. Поддержка lock и способ хранения определяются backend, поэтому инструкции по удалению файла или записи из чужого примера нельзя переносить автоматически.

Для своего backend отдельно проверьте реализацию и требуемые права: отказ в доступе к lock-хранилищу отличается от занятого lock. В первом случае потребуется восстановить доступ, во втором — разобраться с владельцем операции.

После освобождения нужен новый plan

Оборванный apply мог успеть создать часть объектов. Освобождение lock не откатывает их и не достраивает оставшиеся. Сначала сохраните доступную резервную копию состояния штатным способом backend, затем постройте свежий plan и сопоставьте предложенные действия с журналом предыдущего запуска и фактическими объектами.

Не повторяйте старый план автоматически: после прерванной операции его предположения могли устареть. Если объект существует, а в state его нет, понадобится отдельное решение о восстановлении привязки, например import после проверки идентичности. Удаление state ради нового старта усложнит эту задачу.

В постоянном процессе назначьте одну очередь изменения на один state и сохраняйте связь job с backend и workspace. Блокировка backend остаётся последним уровнем защиты, а понятная очередь CI уменьшает число обычных столкновений. Тогда следующий lock timeout будет расследоваться по конкретному владельцу и операции, а не лечиться общим снятием всех блокировок.

Ещё по теме

Продолжить изучение

Хотите управлять инфраструктурой через код и понимать последствия изменений? Plan, state, модули и работа с существующими ресурсами — темы программы «Terraform и OpenTofu: инфраструктура как код».

Продолжить на практике

  • Terraform и OpenTofu: инфраструктура как код ↗

    Описывайте инфраструктуру в коде, проверяйте изменения до применения и работайте со state без сюрпризов. HCL, модули, remote state, импорт, CI и различия Terraform/OpenTofu — на воспроизводимом учебном окружении.