AWS CDK更新ImageBuilder AMI遇栈依赖错误,求解决方案
问题:CDK跨栈更新AMI时的导出资源占用错误
我通过AWS CDK用ImageBuilder创建AMI,再部署到EC2实例。ImageBuilder相关资源放在ImageBuilderStack栈,EC2资源放在EC2Stack栈,后者依赖前者生成的AMI。但更新AMI时出现错误:
Export ImageBuilderStack:ExportsOutputFnGetAttAmiImagebImageIdEB9587C0 cannot be updated as it is in use by EC2Stack
我知道先销毁EC2Stack再重新部署能解决问题,但想通过单个cdk deploy命令一次性完成更新。目前试过cdk deploy EC2-Stack和cdk deploy --all都无法解决。
相关代码片段
ec2_stack.py
from constructs import Construct from aws_cdk import ( Stack, aws_ec2 as ec2, Duration, CfnOutput, ) class Ec2Stack(Stack): def __init__(self, scope: Construct, construct_id: str, stacks, stage, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) image_builder_stack = stacks["ImageBuilderStack"] iam_stack = stacks["IamStack"] image_b = ec2.MachineImage.generic_linux({"us-west-2": image_builder_stack.ami_image_b.attr_image_id}) # 引用ImageBuilderStack生成的AMI CfnOutput(self, "AmiOutputB", value=image_builder_stack.ami_image_b.attr_image_id) test_ec2 = ec2.Instance(self, "Ec2Instance", instance_name="deployment_test", vpc=shared_vpc_stack.vpc, vpc_subnets=ec2.SubnetSelection( subnet_group_name="TestPrivateSubnet" ), instance_type=ec2.InstanceType.of(ec2.InstanceClass.C5, ec2.InstanceSize.XLARGE9), machine_image=image_b, role=iam_stack.instance_role, security_group=test_security_group, ) test_ec2.node.add_dependency(image_builder_stack.ami_image_b)
image_builder_stack.py
from constructs import Construct from aws_cdk import ( Stack, aws_imagebuilder as imagebuilder, aws_iam as iam, ) from component_documents import test_doc_b # AMI构建用的Shell命令 class ImageBuilderStack(Stack): def __init__(self, scope: Construct, construct_id: str, stacks, stage, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) iam_stack = stacks["IamStack"] instance_profile = iam.CfnInstanceProfile(self, "InstanceProfile", roles=[iam_stack.image_build_role.role_name], instance_profile_name="EC2InstanceProfileForImageBuilder", ) infrastructure_configuration = imagebuilder.CfnInfrastructureConfiguration(self, "InfrastructureConfiguration", instance_profile_name=instance_profile.instance_profile_name, name="InfrastructureConfig", description="Infrastructure configuration for test AMI image", terminate_instance_on_failure=True ) infrastructure_configuration.node.add_dependency(instance_profile) yaml_data = test_doc_b.return_test_component_doc() test_component = imagebuilder.CfnComponent(stack, f"testComponent-{image_doc}", name="Test-Image-Component", platform="Linux", version= version, # 示例:"1.0.3" description="Downloads, installs and configures splunk on Amazon Linux", supported_os_versions=["Amazon Linux 2"], data=yaml_data # 用于生成AMI的Shell脚本 ) test_image_recipe = imagebuilder.CfnImageRecipe(stack, f"TestImageRecipe-{image_doc}", components=[{"componentArn": test_component.attr_arn}], name="Test-Image-Recipe", # Amazon Linux2 X86 64bit Kernel 5.10 parent_image="ami-076bca9dd71a9a514", version="1.0.1", # 示例:"1.0.3",更新时需递增版本号 working_directory="/tmp" ) self.test_ami_image = imagebuilder.CfnImage(self, f"TestAmiImage-{image_doc}", infrastructure_configuration_arn=infrastructure_configuration.attr_arn, image_recipe_arn=test_image_recipe.attr_arn, tags={ "Name": "test AMI" } )
component_documents.test_doc_b.py
def return_test_component_doc(): test_component_doc = f''' name: TestDeploymentDocument_B description: This is document for downloading, installing and deploying schemaVersion: 1.0 phases: - name: build steps: - name: SetEnvironment action: ExecuteBash inputs: commands: - sudo yum update -y - echo “Hello World” '''
stack_creator.py
from constructs import Construct from aws_cdk import ( Stack, Environment ) from test_stacks.ec2 import Ec2Stack from test_stacks.image_builder import ImageBuilderStack from test_stacks.iam import IamStack class ApplicationInfrastructure(Stack): def __init__(self, scope: Construct, **kwargs) -> None: super().__init__(scope, **kwargs) stage = self.node.try_get_context("stage") env_USA = Environment(account=self.account, region="us-west-2") image_builder_stack = ImageBuilderStack(self, "Image-Builder-Stack", env=env_USA, stage="beta") ec2_stack = Ec2Stack(self, "EC2-Stack", env=env_USA, stacks={"ImageBuilderStack": image_builder_stack, "IamStack": IamStack},stage="beta") ec2_stack.add_dependency(image_builder_stack)
解决方案
方法1:给EC2实例添加替换策略
当AMI变化时,让CDK自动销毁并重建EC2实例,避免导出资源被占用的问题。修改ec2_stack.py中的EC2实例定义:
test_ec2 = ec2.Instance(self, "Ec2Instance", # 原有属性保持不变 update_replace_policy=core.RemovalPolicy.DESTROY, deletion_policy=core.RemovalPolicy.DESTROY, ) # 或者直接修改底层CfnResource的替换策略 test_ec2.instance.cfn_options.update_replace_policy = core.RemovalPolicy.DESTROY
添加后,执行cdk deploy --all即可自动更新AMI和EC2实例。
方法2:确保ImageBuilder版本号递增
ImageBuilder只有在镜像配方(test_image_recipe)的版本号变化时才会生成新AMI。修改image_builder_stack.py中的版本号,每次更新时手动递增,或者自动生成版本号:
# 示例:用时间戳自动生成唯一版本号 from datetime import datetime version = datetime.now().strftime("%Y.%m.%d.%H%M") test_image_recipe = imagebuilder.CfnImageRecipe(stack, f"TestImageRecipe-{image_doc}", # 其他属性不变 version=version, )
方法3:用脚本自动化销毁重建流程
如果上述方法无效,可编写Shell脚本一次性完成操作:
#!/bin/bash # 强制销毁EC2Stack cdk destroy EC2-Stack -f # 更新ImageBuilderStack生成新AMI cdk deploy Image-Builder-Stack # 重新部署EC2Stack使用新AMI cdk deploy EC2-Stack
执行脚本即可完成一次性更新。
内容的提问来源于stack exchange,提问作者dredbound
相关产品推荐
相关产品推荐

