Удаление стека CloudFormation не удалось удалить VPC - PullRequest
1 голос
/ 11 апреля 2019

Я создал инфраструктуру aws с коллекцией EC2, Redshift, VPC и т. Д. Через CLOUDFORMATION. Теперь я хочу удалить его в обратном порядке. Экс. Все ресурсы зависят от VPC. VPC должен быть удален в конце. Но каким-то образом каждый стек удаляется, а стек VPC не удаляется через python BOTO3.Он показывает некоторую ошибку зависимости подсети или сетевого интерфейса. Но когда я пытаюсь удалить через консоль, он удаляет его успешно. Кто-нибудь сталкивался с этой проблемой?

Я попытался удалить все, что связано с loadbalancer. Но все равно VPC не удаляет.

Ответы [ 2 ]

3 голосов
/ 11 апреля 2019

AWS CloudFormation создает граф зависимостей между ресурсами на основе DependsOn ссылок в шаблоне и ссылок между ресурсами.

Затем он пытается развернуть ресурсы параллельно, но учитывает зависимости.

Например, подсеть может быть определена как:

Subnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref ProdVPC

В этой ситуации имеется явная ссылка на ProdVPC, поэтому CloudFormation будет создавать Subnet1 только после создания ProdVPC.

Когда стек CloudFormation удален , применяется обратная логика.В этом случае Subnet1 будет удалено до удаления ProdVPC.

Однако CloudFormation не знает о ресурсах, созданных вне стека. Это означает, что если ресурс (например, экземпляр Amazon EC2) создается внутри подсети, тогда удаление стека завершится неудачно, поскольку подсеть невозможно удалить, пока есть экземпляр EC2, использующий его (или, точнее, ENI, присоединенный к нему).

В таких ситуациях вам необходимо вручную удалить ресурсы , которые вызывают «ошибку удаления», а затем снова попробовать команду удаления.

Хороший способ найти такие ресурсы - этопосмотрите в разделе Сетевые интерфейсы консоли управления EC2.Убедитесь, что к VPC не подключены интерфейсы.

0 голосов
/ 10 мая 2019

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

Технически эти сетевые интерфейсы должны автоматически удаляться, когда лямбда-коды не используются из стека, но, по моему опыту, я наблюдал, что осиротевшие ENI не позволяют VPC быть развернутым.

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

Это часть формирования облака, в которой вы настраиваете пользовательский ресурс и передаете идентификатор VPC

##############################################
#                                            #
#  Custom resource deleting net interfaces   #
#                                            #
##############################################

  NetInterfacesCleanupFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src
      Handler: cleanup/network_interfaces.handler
      Role: !GetAtt BasicLambdaRole.Arn
      DeploymentPreference:
        Type: AllAtOnce
      Timeout: 900

  PermissionForNewInterfacesCleanupLambda:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:invokeFunction
      FunctionName:
        Fn::GetAtt: [ NetInterfacesCleanupFunction, Arn ]
      Principal: lambda.amazonaws.com

  InvokeLambdaFunctionToCleanupNetInterfaces:
    DependsOn: [PermissionForNewInterfacesCleanupLambda]
    Type: Custom::CleanupNetInterfacesLambda
    Properties:
      ServiceToken: !GetAtt NetInterfacesCleanupFunction.Arn
      StackName: !Ref AWS::StackName
      VPCID:
        Fn::ImportValue: !Sub '${MasterStack}-Articles-VPC-Ref'
      Tags:
        'owner': !Ref StackOwner
        'task': !Ref Task

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

import boto3
from botocore.exceptions import ClientError
from time import sleep

# Fix this wherever your custom resource handler code is
from common import cfn_custom_resources as csr
import sys

MAX_RETRIES = 3
client = boto3.client('ec2')


def handler(event, context):

    vpc_id = event['ResourceProperties']['VPCID']

    if not csr.__is_valid_event(event, context):
        csr.send(event, context, FAILED, validate_response_data(result))
        return
    elif event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
        result = {'result': 'Don\'t trigger the rest of the code'}
        csr.send(event, context, csr.SUCCESS, csr.validate_response_data(result))
        return
    try:
        # Get all network intefaces for given vpc which are attached to a lambda function
        interfaces = client.describe_network_interfaces(
            Filters=[
                {
                    'Name': 'description',
                    'Values': ['AWS Lambda VPC ENI*']
                },
                {
                    'Name': 'vpc-id',
                    'Values': [vpc_id]
                },
            ],
        )

        failed_detach = list()
        failed_delete = list()

        # Detach the above found network interfaces
        for interface in interfaces['NetworkInterfaces']:
            detach_interface(failed_detach, interface)

        # Try detach a second time and delete each simultaneously
        for interface in interfaces['NetworkInterfaces']:
            detach_and_delete_interface(failed_detach, failed_delete, interface)

        if not failed_detach or not failed_delete:
            result = {'result': 'Network interfaces detached and deleted successfully'}
            csr.send(event, context, csr.SUCCESS, csr.validate_response_data(result))
        else:
            result = {'result': 'Network interfaces couldn\'t be deleted completely'}
            csr.send(event, context, csr.FAILED, csr.validate_response_data(result))
            # print(response)
    except Exception:
        print("Unexpected error:", sys.exc_info())
        result = {'result': 'Some error with the process of detaching and deleting the network interfaces'}
        csr.send(event, context, csr.FAILED, csr.validate_response_data(result))


def detach_interface(failed_detach, interface):
    try:

        if interface['Status'] == 'in-use':
            detach_response = client.detach_network_interface(
                AttachmentId=interface['Attachment']['AttachmentId'],
                Force=True
            )

            # Sleep for 1 sec after every detachment
            sleep(1)

            print(f"Detach response for {interface['NetworkInterfaceId']}- {detach_response}")

            if 'HTTPStatusCode' not in detach_response['ResponseMetadata'] or \
                    detach_response['ResponseMetadata']['HTTPStatusCode'] != 200:
                failed_detach.append(detach_response)
    except ClientError as e:
        print(f"Exception details - {sys.exc_info()}")


def detach_and_delete_interface(failed_detach, failed_delete, interface, retries=0):

    detach_interface(failed_detach, interface)

    sleep(retries + 1)

    try:
        delete_response = client.delete_network_interface(
            NetworkInterfaceId=interface['NetworkInterfaceId'])

        print(f"Delete response for {interface['NetworkInterfaceId']}- {delete_response}")
        if 'HTTPStatusCode' not in delete_response['ResponseMetadata'] or \
                delete_response['ResponseMetadata']['HTTPStatusCode'] != 200:
            failed_delete.append(delete_response)
    except ClientError as e:
        print(f"Exception while deleting - {str(e)}")
        print()
        if retries <= MAX_RETRIES:
            if e.response['Error']['Code'] == 'InvalidNetworkInterface.InUse' or \
                    e.response['Error']['Code'] == 'InvalidParameterValue':
                retries = retries + 1
                print(f"Retry {retries} : Interface in use, deletion failed, retrying to detach and delete")
                detach_and_delete_interface(failed_detach, failed_delete, interface, retries)
            else:
                raise RuntimeError("Code not found in error")
        else:
            raise RuntimeError("Max Number of retries exhausted to remove the interface")

Ссылка на лямбду: https://gist.github.com/revolutionisme/8ec785f8202f47da5517c295a28c7cb5

Дополнительная информация о настройке лямбды в VPC - https://docs.aws.amazon.com/lambda/latest/dg/vpc.html

...