При удалении стека CloudFormation не удается удалить VPC

#python-3.x #amazon-web-services #amazon-cloudformation #boto3 #amazon-vpc

#python-3.x #amazon-веб-сервисы #aws-cloudformation #boto3 #amazon-vpc

Вопрос:

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

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

Комментарии:

1. Консоль AWS выполняет работу по удалению зависимостей за вас. Не могли бы вы объяснить, что вы имеете в виду, говоря: «Я пытался удалить все, что связано с loadbalancer, который к нему подключен. Однако это не удаление» — loadbalancer не удаляет или VPC?

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

3. @gp42 я обновил queston. VPC не удаляет

4. Есть ли у вас лямбда-выражение, которое выполняется в VPC? Если да, то я могу предоставить вам лямбда-выражение, которое очистит все связанные сетевые интерфейсы. У меня была аналогичная проблема, и я создал этот лямбда-код, который запускается с использованием пользовательского ресурса

5. Да, @Biplob, у меня есть лямбда, который выполняется внутри VPC. Можете ли вы поделиться со мной этим сценарием? Может быть, я могу попробовать это.

Ответ №1:

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 не подключены никакие интерфейсы.

Комментарии:

1. 1 Иоанна. Итак, предположим, я удалил ресурсы, которые использовали VPC. Затем, каков будет правильный способ удаления VPC через python boto3 с такими зависимостями.

2. Вы должны иметь возможность снова попробовать удалить стек. Это должно удалить VPC. Однако, если стек был удален без удаления VPC, вы можете выполнить delete_subnet() delete_vpc() команды and . AWS CloudFormation просто вызывает обычные API для создания / удаления ресурсов, поэтому вы можете сделать то же самое.

3. Спасибо @John за ваш ответ. Проблема решена. Сначала я удалил все балансировщики нагрузки, а затем удалил правила и группы группы безопасности. Затем я устанавливаю time.sleep на 300 секунд для автоматического отключения сетевых интерфейсов. После этого он может успешно удалить VPC.

Ответ №2:

Поскольку вы указали, что у вас возникли проблемы с удалением 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

Комментарии:

1. Спасибо @Biplob за вашу передышку. Проблема решена. Сначала я удалил все балансировщики нагрузки, а затем удалил правила и группы группы безопасности. Затем я устанавливаю time.sleep на 300 секунд для автоматического отключения сетевых интерфейсов. После этого он может успешно удалить VPC.