Когда два человека одновременно меняют одно и то же состояние инфраструктуры

Представьте: разработчик A и разработчик B оба должны обновить часть инфраструктуры. Они забирают текущее состояние из одного и того же S3-бакета, каждый вносит свои изменения, а затем оба загружают новое состояние обратно в S3. Результат? Изменения одного бесшумно перезаписывают изменения другого. Инфраструктура, работающая в облаке, больше не соответствует тому, что говорит файл состояния. Никто не знает, какие ресурсы на самом деле управляются, и команда теряет контроль.

Речь не о том, что два человека правят один и тот же ресурс напрямую. Речь о том, что два человека правят запись об этих ресурсах. И решение — механизм, который называется блокировкой состояния (state locking).

Что на самом деле делает блокировка состояния

Блокировка состояния проста по своей сути. Прежде чем кто-то начнёт изменять состояние, система пытается его заблокировать. Если блокировка успешна, этот человек может прочитать состояние, внести изменения и сохранить новую версию. Пока это происходит, любой другой, кто попытается получить доступ к тому же состоянию, будет заблокирован. Он ждёт, пока блокировка не будет снята. Как только изменения сохранены, блокировка снимается автоматически.

Следующая диаграмма последовательности показывает, как два разработчика взаимодействуют с состоянием и бэкендом блокировок:

sequenceDiagram participant A as Developer A participant B as Developer B participant S3 as State Backend (S3) participant DB as Lock Backend (DynamoDB) A->>DB: Acquire lock DB-->>A: Lock acquired A->>S3: Read current state S3-->>A: State data B->>DB: Acquire lock DB-->>B: Lock denied (wait) A->>S3: Write new state S3-->>A: OK A->>DB: Release lock DB-->>A: Lock released B->>DB: Acquire lock DB-->>B: Lock acquired B->>S3: Read current state S3-->>B: State data

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

Как разные бэкенды обрабатывают блокировки

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

Вот минимальная конфигурация бэкенда Terraform, которая включает блокировку состояния с S3 и DynamoDB:

terraform {
  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "prod/network/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Атрибут dynamodb_table указывает Terraform использовать таблицу DynamoDB с именем terraform-locks для блокировки. Эта таблица должна существовать до запуска terraform init. Ей нужен первичный ключ с именем LockID (тип String). Terraform будет автоматически создавать и снимать записи блокировки в этой таблице во время операций с состоянием.

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

Когда блокировки ломаются

Блокировки могут ломаться так, что ваше состояние остаётся заблокированным. Вот типичный сценарий: кто-то запускает изменение, а затем его интернет-соединение обрывается в середине операции. Блокировка была получена, но никогда не снята, потому что процесс умер. Теперь состояние заблокировано, и никто не может его изменить.

В этой ситуации нужно принудительно снять блокировку (force unlock). Это не то, что делают наобум. Если вы принудительно снимете блокировку, пока исходный процесс ещё работает где-то, вы рискуете испортить состояние. Команда должна убедиться, что зависший процесс действительно мёртв, прежде чем делать force unlock.

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

Почему это важно не только для блокировки

Блокировка состояния — это мост к более структурированному управлению окружениями. Как только ваше состояние защищено от конкурентных изменений, можно подумать о том, как использовать одну конфигурацию для нескольких окружений без дублирования кода. Здесь в игру вступают workspace'ы.

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

Практический чек-лист для блокировки состояния

  • Выберите бэкенд с поддержкой блокировки. S3 требует DynamoDB. Consul и etcd имеют встроенную блокировку. Знайте, что требуется вашему бэкенду.
  • Протестируйте сценарии сбоя блокировки. Симулируйте обрыв сети во время изменения состояния. Зависает ли блокировка? Может ли ваша команда восстановиться без паники?
  • Документируйте процедуру принудительного снятия блокировки. Опишите точно, как проверить, что зависший процесс мёртв, и как снять блокировку. Не оставляйте это на уровне устных договорённостей.
  • Мониторьте конкуренцию за блокировки. Если несколько человек часто натыкаются на заблокированные состояния, вашей команде может потребоваться лучшая координация или более мелкие и частые изменения.
  • Установите подходящие тайм-ауты. У длительных операций должны быть тайм-ауты, которые автоматически снимают блокировку, если процесс зависает.

Конкретный вывод

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