Обновление пользовательских ресурсов вызывает их удаление? - PullRequest
0 голосов
/ 30 мая 2018

При использовании шаблонов CloudFormation я нахожу, что функция «Пользовательский ресурс» с ее реализацией вспомогательной функции Lambda очень полезна для обработки всех видов задач, которые CloudFormation не обеспечивает хорошей поддержки.

Обычно яиспользовать пользовательские ресурсы для настройки объектов при создании стека (например, поиск имен AMI) или очистки объектов при удалении (например, удаление объектов из S3 или Route53, которые могут заблокировать удаление) - и это прекрасно работает.

Нокогда я пытаюсь фактически использовать «пользовательский ресурс» для управления фактическим пользовательским ресурсом, который должен быть создан при создании стека, удален при удалении стека и - в этом и заключается проблема - иногда обновляется новыми значениями во время обновления стекаинтеграция CloudFormation ведет себя неожиданно и приводит к сбою пользовательского ресурса.

Проблема заключается в том, что во время обновления стека, когда изменилось одно из свойств пользовательского ресурса, на этапе UPDATE_IN_PROGRESS стека CloudFormation отправляет событие обновления вспомогательной лямбда-функции, все значения которой установлены правильно, а также отправляется копия старых значений.Но после завершения обновления CloudFormation запускает этап UPDATE_COMPLETE_CLEANUP_IN_PROGRESS и отправляет вспомогательной лямбда-функции событие удаления (RequestType установлено на Delete).

Когда это происходит, вспомогательная лямбда-функция принимает стекудаляется и удаляет пользовательский ресурс.В результате после обновления пользовательский ресурс исчез.

Я просмотрел данные запроса в журналах, и «очистка удаления» выглядит идентично реальному событию «удаления»:

Очистка удаления:

{
RequestType: 'Delete',
ServiceToken: 'arn:aws:lambda:us-east-2:1234567890:function:stackname-resname-J0LWT56QSPIA',
ResponseURL: 'https://cloudformation-custom-resource-response-useast2.s3.us-east-2.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-2%3A1234567890%3Astack/stackname/3cc80cf0-5415-11e8-b6dc-503f3157b0d1%7Cresnmae%7C15521ba8-1a3c-4594-9ea9-18513efb6e8d?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20180511T140259Z&X-Amz-SignedHeaders=host&X-Amz-Expires=7199&X-Amz-Credential=AKISOMEAWSKEYID%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Signature=3abc68e1f8df46a711a2f6084debaf2a16bd0acf7f58837b9d02c805975df91b',
StackId: 'arn:aws:cloudformation:us-east-2:1234567890:stack/stackname/3cc80cf0-5415-11e8-b6dc-503f3157b0d1',
RequestId: '15521ba8-1a3c-4594-9ea9-18513efb6e8d',
LogicalResourceId: 'resname',
PhysicalResourceId: '2018/05/11/[$LATEST]28bad2681fb84c0bbf80990e1decbd97',
ResourceType: 'Custom::Resource',
ResourceProperties: {
    ServiceToken: 'arn:aws:lambda:us-east-2:1234567890:function:stackname-resname-J0LWT56QSPIA',
    VpcId: 'vpc-35512e5d',
    SomeValue: '4'
} 
}

Реальное удаление:

{
RequestType: 'Delete',
ServiceToken: 'arn:aws:lambda:us-east-2:1234567890:function:stackname-resname-J0LWT56QSPIA',
ResponseURL: 'https://cloudformation-custom-resource-response-useast2.s3.us-east-2.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-2%3A1234567890%3Astack/stackname/3cc80cf0-5415-11e8-b6dc-503f3157b0d1%7Cresname%7C6166ff92-009d-47ac-ac2f-c5be2c1a7ab2?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20180524T154453Z&X-Amz-SignedHeaders=host&X-Amz-Expires=7200&X-Amz-Credential=AKISOMEAWSKEYID%2F20180524%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Signature=29ca1d0dbdbe9246f7f82c1782726653b2aac8cd997714479ab5a080bab03cac',
StackId: 'arn:aws:cloudformation:us-east-2:123456780:stack/stackname/3cc80cf0-5415-11e8-b6dc-503f3157b0d1',
RequestId: '6166ff92-009d-47ac-ac2f-c5be2c1a7ab2',
LogicalResourceId: 'resname',
PhysicalResourceId: '2018/05/11/[$LATEST]c9494122976b4ef3a4102628fafbd1ec',
ResourceType: 'Custom::Resource',
ResourceProperties: {
    ServiceToken: 'arn:aws:lambda:us-east-2:1234567890:function:stackname-resname-J0LWT56QSPIA',
    VpcId: 'vpc-35512e5d',
    SomeValue: '0'
}
}

Единственное интересное поле запроса, которое я вижу, это то, что идентификатор физического ресурса отличается, но я не знаюс чем это соотносится, чтобы определить, является ли это настоящим удалением или нет.

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

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

Очень интересно и важно знать, что CloudFormation сравнивает идентификатор физического ресурса, который вы получили от вашегоЛямбда-функция к той, которую вы вернули ранее.Если идентификаторы отличаются, CloudFormation предполагает, что ресурс был заменен новым ресурсом.Затем происходит нечто интересное.

Когда логика обновления ресурса завершается успешно, отправляется запрос на удаление со старым идентификатором физического ресурса.Если обновление стека завершается неудачно и происходит откат, новый идентификатор физического ресурса отправляется в событии Delete.

Подробнее о здесь о пользовательских жизненных циклах и других лучших возможностях читайте здесьпрактика

0 голосов
/ 30 мая 2018

Похоже, проблема заключается в примере реализации функции sendResponse(), которая используется для отправки пользовательского события завершения ресурса обратно в CloudFormation.Этот метод отвечает за установку идентификатора физического ресурса пользовательского ресурса.Насколько я понимаю, это значение представляет глобально уникальный идентификатор «внешнего ресурса», который управляется функцией Lambda, поддерживающей пользовательский ресурс CloudFormation.

Как видно из CloudFormation's Lambdaпользовательского ресурса "с поддержкой пользователя" , а также в модуле cfn-response NPM * send() и встроенном CloudFormation cfn-response модуле , этоу метода есть поведение по умолчанию для расчета идентификатора физического ресурса, если он не указан в качестве 5-го параметра, и он использует поток журнала CloudWatch Logs, который обрабатывает ведение журнала для обрабатываемого запроса:

var responseBody = JSON.stringify({
    ...
    PhysicalResourceId: context.logStreamName,
    ...
})

Потому что CloudFormation(или среда выполнения AWS Lambda?) иногда меняет поток журнала на новый, идентификатор физического ресурса, генерируемый sendResponse(), время от времени неожиданно меняется и сбивает с толку CloudFormation.

Насколько я понимаю,Управляемые объекты CloudFormation иногда необходимо заменить во время обновления (хороший пример: RDS::DBInstance которая нуждается в замене практически для любых изменений).Политика CloudFormation заключается в том, что если ресурс нуждается в замене, новый ресурс создается во время «этапа обновления», а старый ресурс удаляется во время «этапа очистки».

Поэтому используется стандартный физический идентификатор ресурса sendResponse().При расчете процесс выглядит следующим образом:

  1. Создан стек.
  2. Создан новый поток журналов для обработки пользовательских журналов ресурсов.
  3. Лямбда поддержкивызывается функция для создания ресурса, и поведение по умолчанию устанавливает его идентификатор ресурса равным идентификатору потока журнала.
  4. Через некоторое время
  5. Стек обновляется новыми параметрами для настраиваемого ресурса.
  6. Создается новый поток журналов для обработки пользовательских журналов ресурсов с новым идентификатором.
  7. Вызывается вспомогательная лямбда-функция для обновления ресурса, и поведение по умолчанию устанавливает новый идентификатор ресурса дляновый идентификатор потока журнала.
  8. CloudFormation понимает, что был создан новый ресурс для замены старого ресурса, и в соответствии сполитике следует удалить старый ресурс во время «этапа очистки».
  9. CloudFormation достигает «этапа очистки» и отправляет запрос на удаление со старым идентификатором физического ресурса.

Решение, по крайней мере, в моем случае, когда я никогда не «заменяю внешний ресурс», состоит в том, чтобы изготовить уникальный идентификатор для управляемого ресурса, предоставить его в качестве 5-го параметра для процедуры отправки ответа, а затем придерживаться его - продолжать отправлять то же самоеидентификатор физического ресурса, полученный в запросе на обновление, в ответе на обновление.CloudFormation тогда никогда не отправит запрос на удаление во время «этапа очистки».

Моя реализация (в JavaScript) выглядит примерно так:

    var resID = event.ResourceProperties.PhysicalResourceId || uuid();
    ...
    sendResponse(event, context, status, resData, resID);

Другая альтернатива - которая, вероятно, будет иметь смыслесли вам действительно необходимо заменить внешний ресурс и вы хотите придерживаться модели CloudFormation для удаления старого ресурса во время очистки - это использовать фактический идентификатор внешнего ресурса в качестве идентификатора физического ресурса, а при получении запроса на удаление - использовать предоставленныйидентификатор физического ресурса для удаления старого внешнего ресурса.Это то, что дизайнеры CloudFormation, вероятно, имели в виду в первую очередь, но их пример реализации по умолчанию вызывает много путаницы - возможно, потому что пример реализации не управляет реальным ресурсом и не имеет функциональности обновления.В CloudFormation также отсутствует документация, объясняющая дизайн и обоснование.

...