首页 > 解决方案 > 使用图像参数更改触发 Cloudformation Blue/Green

问题描述

在我的 Cloudformation 模板中,我使用的是蓝绿色部署触发器并具有以下 TaskDefinition

TaskDefinition:
  Type: AWS::ECS::TaskDefinition
  DependsOn: LogGroup
  Properties:
    Family: "task-family-name"
    NetworkMode: awsvpc
    RequiresCompatibilities: [FARGATE]
    Cpu: 512
    Memory: 1024
    ExecutionRoleArn: arn:aws:iam::579072907853:role/ecsTaskExecutionRole
    TaskRoleArn: !Ref ServiceRole
    ContainerDefinitions:
      - Name: !Ref ServiceName
        Essential: true
        Image: !Ref ImageARN
        PortMappings:
          - ContainerPort: !Ref ContainerPort
        LogConfiguration:
          LogDriver: awslogs
          Options:
            awslogs-region: !Ref AWS::Region
            awslogs-group: !Ref LogGroup
            awslogs-stream-prefix: ecs
        Environment: 
          - Name: RACK_ENV
            Value: !Ref EnvironmentName

如果我用参数的新值更新堆栈ImageARN,我会收到以下错误

'CodeDeployBlueGreenHook' of type AWS::CodeDeploy::BlueGreen failed with message: The submitted template's primary task definition 'TaskDefinition' was never updated. The template's 'AWS::CodeDeployBlueGreen' Transform might be missing or removed

如果我不使用参数而是使用硬编码Image然后更新模板它工作正常。

有没有办法使用参数?

例子

以下是存在此问题的模板的完整示例。我在这两个图像之间更改 ImageARN 参数

nginx:1.18.0@sha256:001487e0a8c6abf91351ab45fdb308c0c2b95e8a67260b7ca5cf064462689a9e
nginx:1.19.4@sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
AWSTemplateFormatVersion: 2010-09-09
Parameters: 
  Image: 
    Type: String
  CIDRBlock: 
    Type: String
    Default: 192.168.5.0/24
    Description: CIDR block for VPC
  ExecutionRoleARN:
    Type: String

Transform:
  - AWS::CodeDeployBlueGreen

Hooks:
  CodeDeployBlueGreenHook:
    Type: AWS::CodeDeploy::BlueGreen
    Properties:
      TrafficRoutingConfig:
        Type: AllAtOnce
      Applications:
        - Target:
            Type: AWS::ECS::Service
            LogicalID: ECSService
          ECSAttributes:
            TaskDefinitions:
              - TaskDefinition
              - GreenTaskDefinition
            TaskSets:
              - TaskSet
              - GreenTaskSet
            TrafficRouting:
              ProdTrafficRoute:
                Type: AWS::ElasticLoadBalancingV2::Listener
                LogicalID: HTTPListener
              TargetGroups:
                - TargetGroup
                - TargetGroupGreen
    
Resources: 
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP to load balancer
      VpcId: !Ref Vpc
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
      - IpProtocol: -1
        CidrIp: 0.0.0.0/0

  FargateSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow port 80 to service
      VpcId: !Ref Vpc
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        SourceSecurityGroupId: !Ref ALBSecurityGroup
      SecurityGroupEgress:
      - IpProtocol: -1
        CidrIp: 0.0.0.0/0

  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: load-balancer
      Type: application
      IpAddressType: ipv4
      Scheme: internet-facing
      SecurityGroups: [!Ref ALBSecurityGroup]
      Subnets: [!Ref Subnet1, !Ref Subnet2]

  HTTPListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: true
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: "/"
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 5
      Name: targetgroup-blue
      Port: 80
      Protocol: HTTP
      TargetType: ip
      UnhealthyThresholdCount: 10
      VpcId: !Ref Vpc

  TargetGroupGreen:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: true
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: "/"
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 5
      Name: targetgroup-green
      Port: 80
      Protocol: HTTP
      TargetType: ip
      UnhealthyThresholdCount: 10
      VpcId: !Ref Vpc
      
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: "task-family-name"
      NetworkMode: awsvpc
      RequiresCompatibilities: [FARGATE]
      Cpu: 512
      Memory: 1024
      ExecutionRoleArn: !Ref ExecutionRoleARN
      ContainerDefinitions:
        - Name: Container
          Essential: true
          Image: !Ref Image
          PortMappings:
            - ContainerPort: 80

  TaskSet:
    Type: AWS::ECS::TaskSet
    Properties:
      Cluster: !Ref ECSCluster
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsVpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups: [!Ref FargateSecurityGroup]
          Subnets: [!Ref Subnet1, !Ref Subnet2]
      Scale:
        Unit: PERCENT
        Value: 100
      Service: !Ref ECSService
      TaskDefinition: !Ref TaskDefinition
      PlatformVersion: LATEST
      LoadBalancers: 
        - ContainerName: Container
          ContainerPort: 80
          TargetGroupArn: !Ref TargetGroup

  ECSService:
    Type: AWS::ECS::Service
    DependsOn: HTTPListener
    Properties: 
      ServiceName: service
      Cluster: !Ref ECSCluster
      DeploymentController: 
        Type: EXTERNAL
      DesiredCount: 4
      EnableECSManagedTags: true
    
  PrimaryTaskSet:
    Type: 'AWS::ECS::PrimaryTaskSet'
    Properties:
      Cluster: !Ref ECSCluster
      Service: !Ref ECSService
      TaskSetId: !GetAtt 'TaskSet.Id'

  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CIDRBlock
      EnableDnsHostnames: true
      EnableDnsSupport: true

  Subnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: !Select [0, !Cidr [!Ref CIDRBlock, 2, 6 ]]
      AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']

  Subnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: !Select [1, !Cidr [!Ref CIDRBlock, 2, 6 ]]
      AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  InternetGatewayVPCAssoc:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref InternetGateway

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc

  RouteTableSub1Assoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref Subnet1

  RouteTableSub2Assoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref Subnet2

  InternetGatewayRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGateway
    Properties:
       RouteTableId: !Ref RouteTable
       DestinationCidrBlock: 0.0.0.0/0
       GatewayId: !Ref InternetGateway

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: Cluster
      CapacityProviders:
        - FARGATE
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1

标签: amazon-web-servicesamazon-cloudformation

解决方案


我查看了您的示例,以及我自己的项目中的内容。我可以确认您的示例没有按预期工作。

我进行了更多调查并确定该问题可能是由于在您的模板中创建了整个网络堆栈。在我自己的项目中,我的 ecs 模板中没有 VPC 或子网,因此一切正常。

因此,一旦我从您的模板中删除了与 VPC 相关的内容,它就开始按要求工作。还有一些其他较小的更改,但它们可能不是必需的。我认为 VPC 的东西是主要原因。

此外,请使用Replace current template选项更新模板,而不是Use current template. 如果您使用第二个选项,它仍然无法正常工作。我认为这是由于宏在 CFN 中的工作方式,应该替换模板,而不是就地更新。如果替换它,CodeDeploy 中的 B/G是成功触发,您可以在 CodeDeploy 控制台中查看它。

这是修改后的模板:

AWSTemplateFormatVersion: 2010-09-09

Parameters: 

  ImageUrl: 
    Type: String
    
  VpcId:
    Type: 'AWS::EC2::VPC::Id'
    
  Subnet1:
    Type: 'AWS::EC2::Subnet::Id'
    
  Subnet2:
    Type: 'AWS::EC2::Subnet::Id'
    
  ExecutionRoleARN:
    Type: String

  DesiredCount:
    Type: Number
    Default: 2
      
    
Transform:
  - 'AWS::CodeDeployBlueGreen'

Hooks:
  CodeDeployBlueGreenHook:
    Type: 'AWS::CodeDeploy::BlueGreen'
    Properties:
      TrafficRoutingConfig:
        Type: AllAtOnce
      Applications:
        - Target:nginx:1.19.4@sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
            Type: 'AWS::ECS::Service'
            LogicalID: ECSService
          ECSAttributes:
            TaskDefinitions:
              - TaskDefinition
              - GreenTaskDefinition
            TaskSets:
              - TaskSet
              - GreenTaskSet
            TrafficRouting:
              ProdTrafficRoute:
                Type: 'AWS::ElasticLoadBalancingV2::Listener'
                LogicalID: HTTPListener
              TargetGroups:
                - TargetGroupnginx:1.19.4@sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
                - TargetGroupGreen
    
Resources: 

  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP to load balancer
      VpcId: !Ref VpcId
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
      - IpProtocol: -1
        CidrIp: 0.0.0.0/0

  FargateSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow port 80 to service
      VpcId: !Ref VpcId
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        SourceSecurityGroupId: !Ref ALBSecurityGroup
      SecurityGroupEgress:
      - IpProtocol: -1
        CidrIp: 0.0.0.0/0

  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      #Name: load-balancer
      Type: application
      IpAddressType: ipv4
      Scheme: internet-facing
      SecurityGroups: 
        - !Ref ALBSecurityGroup
      Subnets: 
        - !Ref Subnet1
        - !Ref Subnet2

  HTTPListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref LoadBalannginx:1.19.4@sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054cer
      Port: 80
      Protocol: HTTP

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: true
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: "/"
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 5
      #Name: targetgroup-blue
      Port: 80
      Protocol: HTTP
      TargetType: ip
      UnhealthyThresholdCount: 10
      VpcId: !Ref VpcId

  TargetGroupGreen:
    Type: nginx:1.19.4@sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054AWS::ElasticLoadBalancingV2nginx:1.19.4@sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054::TargetGroup
    Properties:
      HealthCheckEnabled: true
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: "/"
      HealthCheckTimeoutSeconds: 10
      HealthyThresholdCount: 5
      #Name: targetgroup-green
      Port: 80
      Protocol: HTTP
      TargetType: ip
      UnhealthyThresholdCount: 10
      VpcId: !Ref VpcId
      
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: "task-family-name"
      NetworkMode: awsvpc
      RequiresCompatibilities: [FARGATE]
      Cpu: 256
      Memory: 512
      ExecutionRoleArn: !Ref ExecutionRoleARN
      ContainerDefinitions:
        - Name: Container
          Essential: true
          Image: !Ref ImageUrl
          PortMappings:
            - ContainerPort: 80
              Protocol: tcp             

  TaskSet:
    Type: AWS::ECS::TaskSet
    Properties:
      Cluster: !Ref ECSCluster
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsVpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups: 
            - !Ref FargateSecurityGroup
          Subnets: 
            - !Ref Subnet1 
            - !Ref Subnet2
      Scale:
        Unit: PERCENT
        Value: 100
      Service: !Ref ECSService
      TaskDefinition: !Ref TaskDefinition
      PlatformVersion: LATEST
      LoadBalancers: 
        - ContainerName: Container
          ContainerPort: 80
          TargetGroupArn: !Ref TargetGroup

  ECSService:
    Type: AWS::ECS::Service
    DependsOn: HTTPListener
    Properties: 
      #ServiceName: service
      Cluster: !Ref ECSCluster
      DeploymentController: 
        Type: EXTERNAL
      DesiredCount: !Ref DesiredCount
      EnableECSManagedTags: true
    
  PrimaryTaskSet:
    Type: 'AWS::ECS::PrimaryTaskSet'
    Properties:
      Cluster: !Ref ECSCluster
      Service: !Ref ECSService
      TaskSetId: !GetAtt TaskSet.Id

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      #ClusterName: Cluster
      CapacityProviders:
        - FARGATE
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1
          
Outputs:
    
  ALBDNSName:
    Value: !GetAtt LoadBalancer.DNSName

推荐阅读