如何让重建的EC2实例触发新的AWS CodeDeploy部署?
如何让重建后的EC2实例触发新的AWS CodeDeploy部署?
说明:问题并非出在EC2实例的首次部署阶段,而是在EC2实例被重建并重新部署时出现。
在将AWS CodeDeploy部署到一组EC2实例时遇到循环依赖问题。AWS AppSpec元素的更新会按预期重新部署到现有EC2实例上,但如果仅对EC2实例进行修改(不会导致AWS CodeDeploy发生任何变化),当EC2实例重启并完成重新初始化后,AWS CodeDeploy并不会被重新应用到新的EC2实例上。
基础设施通过Terraform部署。EC2实例完成初始化大约需要15分钟,因此任何AWS CodeDeploy操作都必须等待这15分钟才能在EC2实例上启动。因此,在AWS CodeDeploy配置中添加了depends_on依赖于Terraform的time_sleep资源,等待EC2实例完全启动后再尝试执行实际的AWS CodeDeploy部署。
Terraform伪代码
aws_instance "ec2_instance" {} time_sleep "ec2_instance_delay" { create_duration = var.ec2_creation_delay triggers = { instance_arn = aws_instance.ec2_instance.arn } } template_dir { depends_on = [time_sleep.ec2_instance_delay] provisioner "local-exec" { interpreter = ["/bin/bash", "-c"] command = <<EOC true; export AWS_ACCESS_KEY_id=${}; export AWS_SECRET_ACCESS_KEY=${}; export AWS_SESSION_TOKEN=${}; aws deploy push --application-name ${aws_codedeploy_app.my_app.name} --description ${} --s3-location <s3-location> --source "${template_dir.appspec[count.index].destination_dir}"; deploy_id=$(aws deploy create-deployment --application-name <appName> --s3-location <s3 location> --deployment-group-name <group name> --query 'deploymentId' --output text); aws deploy wait deployment-successful --deployment-id $deploy_id; EOC } } aws_codedeploy_app "my_app" {} aws_codedeploy_deployment_group "my_app_group" { deployment_config_name = "CodeDeploymentDefault.AllAtOnce" ec2_tag_set { ec2_tag_filter { key = "Name" type = "KEY_AND_VALUE" value = aws_instance.ec2_instance.tags["Name"] } } }
说明:Terraform没有
aws_codedeploy_deployment资源,因此使用template_dir的appspec结合provisioner "local-exec"生成实际部署操作,最终命令行类似:
aws deploy create-deployment --application-name <appName> --s3-location <s3 location> --deployment-group-name <group name>
当前配置在部署脚本或AppSpec.yml文件更改时能正常工作,但仅修改EC2实例(比如修改userdata)时,EC2重建后CodeDeploy不会重新应用。
1. 让部署触发资源感知EC2实例变化
当前template_dir仅在AppSpec内容变化时触发,需将EC2实例的ARN加入其触发器,确保EC2重建(ARN更新)时自动执行部署命令:
template_dir { triggers = { instance_arn = aws_instance.ec2_instance.arn appspec_hash = filemd5("${path.module}/appspec.yml") } depends_on = [time_sleep.ec2_instance_delay] provisioner "local-exec" { # 原有命令逻辑不变 } }
2. 修正CodeDeploy部署组标签匹配逻辑
检查aws_codedeploy_deployment_group中的标签配置(原伪代码存在语法错误:tags["Name]需修正为tags["Name"]),确保新重建的EC2实例标签与部署组过滤条件完全匹配。建议使用固定环境标签(如Environment: Production)替代实例名称,避免名称变动导致匹配失败。
3. 在EC2用户数据中主动触发部署
在EC2的userdata中添加初始化完成后的触发脚本,由实例自身主动发起CodeDeploy部署:
#!/bin/bash # 等待实例初始化完成(根据实际调整时长) sleep 900 # 确保CodeDeploy Agent已安装并运行 sudo yum install -y aws-codedeploy-agent sudo service codedeploy-agent start # 触发部署(需EC2实例IAM角色具备CodeDeploy操作权限) aws deploy create-deployment --application-name <appName> --deployment-group-name <group name> --s3-location <s3 location>
4. 使用null_resource替代template_dir
null_resource更适合执行无状态本地命令,触发器配置更灵活:
null_resource "codedeploy_deployment" { triggers = { instance_arn = aws_instance.ec2_instance.arn appspec_hash = filemd5("${path.module}/appspec.yml") deployment_group_id = aws_codedeploy_deployment_group.my_app_group.id } depends_on = [time_sleep.ec2_instance_delay] provisioner "local-exec" { interpreter = ["/bin/bash", "-c"] command = <<EOC export AWS_ACCESS_KEY_ID=${var.aws_access_key} export AWS_SECRET_ACCESS_KEY=${var.aws_secret_key} export AWS_SESSION_TOKEN=${var.aws_session_token} aws deploy push --application-name ${aws_codedeploy_app.my_app.name} --description "Deploy for instance ${aws_instance.ec2_instance.id}" --s3-location <s3-location> --source "${path.module}/deploy-source" deploy_id=$(aws deploy create-deployment --application-name ${aws_codedeploy_app.my_app.name} --deployment-group-name ${aws_codedeploy_deployment_group.my_app_group.name} --s3-location <s3-location> --query 'deploymentId' --output text) aws deploy wait deployment-successful --deployment-id $deploy_id EOC } }
内容的提问来源于stack exchange,提问作者A. Rick

