CodeDeployでAutoScalingグループにBlue/Greenデプロイする設定をやってみた
以下のブログでインプレースデプロイを使用してAutoScalingグループのEC2にデプロイするCI/CD環境をCloudFormationで作成しました。
今回はBlue/GreenデプロイでAutoScalingグループにデプロイする設定をやってみます。
インプレースデプロイとBlue/Greenデプロイ
インプレースデプロイは既に動いている環境に対してデプロイする方法です。
既存の環境を使用するのでBlue/Greenデプロイに比べるとコストが低くなります。
インプレースデプロイの概要
Blue/Greenデプロイは新しくデプロイ先の環境を作成してデプロイする方法です。
既に動いている環境 (Blue) から新しい環境 (Green) へ切り替えることでデプロイが完了します。
インプレースデプロイと違い新しい環境 (Green) が作成されるのでコストは上がりますが、Green環境で問題があった際にBlue環境へロールバックを素早く行うことが可能となります。
Blue/Green デプロイの概要
作成してみた
CodeDeployの部分以外は基本的に以下のブログと同じなのでCloudFormationテンプレートと実行コマンドだけ記載する形にしています。
CloudFormationでCodeDeployを作成する場合Blue/GreenデプロイはLambdaにしか対応していない (2023/01/30) ので、CodeDeployはマネジメントコンソールから設定します。
DeploymentStyle
For blue/green deployments, AWS CloudFormation supports deployments on Lambda compute platforms only. You can perform Amazon ECS blue/green deployments using AWS::CodeDeploy::BlueGreen hook. See Perform Amazon ECS blue/green deployments through CodeDeploy using AWS CloudFormation for more information.
IAMロール + アーティファクト用S3 + ネットワーク周り + ゴールデンAMI作成用EC2
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: CI/CD test Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for VPC Parameters: - VPCCIDR - Label: default: Parameters for Subnet Parameters: - PublicSubnet01CIDR - PublicSubnet02CIDR - PrivateSubnet01CIDR - PrivateSubnet02CIDR - Label: default: Parameters for ec2 Parameters: - EC2VolumeSize - EC2VolumeIOPS - EC2AMI - EC2InstanceType Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# VPCCIDR: Default: 172.30.0.0/16 Type: String PublicSubnet01CIDR: Default: 172.30.1.0/24 Type: String PublicSubnet02CIDR: Default: 172.30.2.0/24 Type: String PrivateSubnet01CIDR: Default: 172.30.3.0/24 Type: String PrivateSubnet02CIDR: Default: 172.30.4.0/24 Type: String EC2VolumeSize: Default: 32 Type: Number EC2VolumeIOPS: Default: 3000 Type: Number EC2AMI: Default: ami-0bba69335379e17f8 Type: AWS::EC2::Image::Id EC2InstanceType: Default: t3.micro Type: String Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3: Type: AWS::S3::Bucket Properties: BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-artifact OwnershipControls: Rules: - ObjectOwnership: BucketOwnerEnforced PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True Tags: - Key: Name Value: !Sub ${AWS::StackName}-${AWS::AccountId}-artifact # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# EC2IAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "s3:GetObject" - "s3:ListBucket" Resource: - !Join - '' - - !GetAtt S3.Arn - '/*' - !GetAtt S3.Arn ManagedPolicyName: iam-policy-deploy-ec2 EC2IAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - !Ref EC2IAMPolicy RoleName: iam-role-ec2 Tags: - Key: Name Value: iam-role-ec2 EC2IAMInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: iam-instanceprofile-ec2 Roles: - !Ref EC2IAMRole CodeDeployIAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "iam:PassRole" - "ec2:RunInstances" - "ec2:CreateTags" Resource: - "*" ManagedPolicyName: iam-policy-codedeploy CodeDeployIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - codedeploy.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole - !Ref CodeDeployIAMPolicy RoleName: iam-role-codedeploy Tags: - Key: Name Value: iam-role-codedeploy CodePipelineIAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "codecommit:CancelUploadArchive" - "codecommit:GetBranch" - "codecommit:GetCommit" - "codecommit:GetRepository" - "codecommit:GetUploadArchiveStatus" - "codecommit:UploadArchive" Resource: - "*" - Effect: Allow Action: - "codedeploy:CreateDeployment" - "codedeploy:GetApplication" - "codedeploy:GetApplicationRevision" - "codedeploy:GetDeployment" - "codedeploy:GetDeploymentConfig" - "codedeploy:RegisterApplicationRevision" Resource: - "*" - Effect: Allow Action: - "s3:GetObject" - "s3:PutObject" - "s3:ListBucket" Resource: - !Join - '' - - !GetAtt S3.Arn - '/*' - !GetAtt S3.Arn - Effect: Allow Action: - "sns:Publish" Resource: - "*" ManagedPolicyName: iam-policy-codepipeline CodePipelineIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - codepipeline.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - !Ref CodePipelineIAMPolicy RoleName: iam-role-codepipeline Tags: - Key: Name Value: iam-role-codepipeline # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: test-vpc # ------------------------------------------------------------# # InternetGateway # ------------------------------------------------------------# InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub test-igw InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# PublicSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PublicSubnet01CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub test-public-subnet-01 VpcId: !Ref VPC PublicSubnet02: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c CidrBlock: !Ref PublicSubnet02CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub test-public-subnet-02 VpcId: !Ref VPC PrivateSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PrivateSubnet01CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub test-private-subnet-01 VpcId: !Ref VPC PrivateSubnet02: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c CidrBlock: !Ref PrivateSubnet02CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub test-private-subnet-02 VpcId: !Ref VPC # ------------------------------------------------------------# # NatGateWay # ------------------------------------------------------------# NatGateWayEIP: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Name Value: eip-natgw NatGateWay: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGateWayEIP.AllocationId ConnectivityType: public SubnetId: !Ref PublicSubnet01 Tags: - Key: Name Value: test-natgw-1a # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: test-public-rtb PublicRouteTableRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway RouteTableId: !Ref PublicRouteTable PublicRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet01 PublicRtAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet02 PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: test-private-rtb PrivateRouteTableRoute: Type: AWS::EC2::Route Properties: DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateWay RouteTableId: !Ref PrivateRouteTable PrivateRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet01 PrivateRtAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet02 # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# ALBSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for alb GroupName: test-sg-alb SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 80 IpProtocol: tcp ToPort: 80 Tags: - Key: Name Value: test-sg-alb VpcId: !Ref VPC EC2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for ec2 GroupName: test-sg-ec2-web SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 SecurityGroupIngress: - FromPort: 80 IpProtocol: tcp SourceSecurityGroupId: !Ref ALBSG ToPort: 80 Tags: - Key: Name Value: test-sg-ec2-web VpcId: !Ref VPC # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: !Ref EC2VolumeIOPS VolumeSize: !Ref EC2VolumeSize VolumeType: gp3 DisableApiTermination: false IamInstanceProfile: !Ref EC2IAMInstanceProfile ImageId: !Ref EC2AMI InstanceType: !Ref EC2InstanceType NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref EC2SG SubnetId: !Ref PrivateSubnet01 Tags: - Key: Name Value: test-ec2-ami UserData: !Base64 | #!/bin/bash yum update -y yum install ruby -y wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install chmod +x ./install ./install auto service codedeploy-agent start yum install httpd -y echo "CodeDeploy Test" > /var/www/html/index.html systemctl start httpd systemctl enable httpd Outputs: # ------------------------------------------------------------# # Outputs # ------------------------------------------------------------# CodeDeployIAMRoleARN: Value: !GetAtt CodeDeployIAMRole.Arn Export: Name: iam-role-codedeploy-arn CodePipelineIAMRoleARN: Value: !GetAtt CodePipelineIAMRole.Arn Export: Name: iam-role-codepipeline-arn EC2IAMInstanceProfileARN: Value: !Ref EC2IAMInstanceProfile Export: Name: EC2IAMInstanceProfile S3Name: Value: !Ref S3 Export: Name: S3Name ALBSGName: Value: !Ref ALBSG Export: Name: ALBSGName EC2SGName: Value: !Ref EC2SG Export: Name: EC2SGName VPCID: Value: !Ref VPC Export: Name: VPC PublicSubnet01ID: Value: !Ref PublicSubnet01 Export: Name: PublicSubnet01ID PublicSubnet02ID: Value: !Ref PublicSubnet02 Export: Name: PublicSubnet02ID PrivateSubnet01ID: Value: !Ref PrivateSubnet01 Export: Name: PrivateSubnet01ID PrivateSubnet02ID: Value: !Ref PrivateSubnet02 Export: Name: PrivateSubnet02ID
前回と少し違うのが142行目~155行目でCodeDeploy用サービスロールに「iam:PassRole」、「ec2:RunInstances」、「ec2:CreateTags」を許可するポリシーを作成していることです。
これを付けないとデプロイする際にEC2を起動できない+AutoScalingグループのコピーでEC2用のIAMロールを付けられなくなります。
以下のコマンドでデプロイします。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM
デプロイが完了したら起動テンプレートで使用するAMIを以下のコマンドで作成します。
aws ec2 create-image --instance-id インスタンスID --name AMIに付ける名前
ALB + AutoScalingグループ
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: AutoScaling Stack Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# EC2VolumeSize: Default: 32 Type: Number EC2VolumeIOPS: Default: 3000 Type: Number EC2AMI: Type: AWS::EC2::Image::Id EC2InstanceType: Default: t3.micro Type: String Resources: # ------------------------------------------------------------# # ALB # ------------------------------------------------------------# ALB: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: IpAddressType: ipv4 LoadBalancerAttributes: - Key: deletion_protection.enabled Value: false Name: test-alb Scheme: internet-facing SecurityGroups: - !ImportValue ALBSGName Subnets: - !ImportValue PublicSubnet01ID - !ImportValue PublicSubnet02ID Tags: - Key: Name Value: test-alb Type: application TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckEnabled: true HealthCheckIntervalSeconds: 30 HealthCheckPath: / HealthCheckPort: traffic-port HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 5 IpAddressType: ipv4 Matcher: HttpCode: 200 Name: test-alb-tg Port: 80 Protocol: HTTP ProtocolVersion: HTTP1 Tags: - Key: Name Value: test-alb-tg TargetType: instance UnhealthyThresholdCount: 2 VpcId: !ImportValue VPC ALBHTTPListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref TargetGroup Type: forward LoadBalancerArn: !Ref ALB Port: 80 Protocol: HTTP # ------------------------------------------------------------# # AutoScaling # ------------------------------------------------------------# LaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateData: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: !Ref EC2VolumeIOPS Throughput: 125 VolumeSize: !Ref EC2VolumeSize VolumeType: gp3 IamInstanceProfile: Name: !ImportValue EC2IAMInstanceProfile ImageId: !Ref EC2AMI InstanceType: !Ref EC2InstanceType NetworkInterfaces: - AssociatePublicIpAddress: false DeleteOnTermination: true DeviceIndex: 0 Groups: - !ImportValue EC2SGName SubnetId: !ImportValue PrivateSubnet01ID TagSpecifications: - ResourceType: instance Tags: - Key: Name Value: test-ec2 LaunchTemplateName: test-ec2-lt AutoScalingGloup: Type: AWS::AutoScaling::AutoScalingGroup Properties: AutoScalingGroupName: test-ec2-asg AvailabilityZones: - ap-northeast-1a - ap-northeast-1c DesiredCapacity: 2 HealthCheckGracePeriod: 300 HealthCheckType: ELB LaunchTemplate: LaunchTemplateId: !Ref LaunchTemplate Version: 1 MaxSize: 4 MetricsCollection: - Granularity: 1Minute MinSize: 2 TargetGroupARNs: - !Ref TargetGroup VPCZoneIdentifier: - !ImportValue PrivateSubnet01ID - !ImportValue PrivateSubnet02ID Outputs: # ------------------------------------------------------------# # Outputs # ------------------------------------------------------------# AutoScalingGloupName: Value: !Ref AutoScalingGloup Export: Name: AutoScalingGloupName TargetGroupName: Value: !GetAtt TargetGroup.TargetGroupName Export: Name: TargetGroupName
以下のコマンドでデプロイします。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=EC2AMI,ParameterValue=AMI ID
CodeCommit
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: CodeCommit Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for CodeCommit Parameters: - RepositoryDescription - RepositoryName Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# RepositoryDescription: MaxLength: 4000 Type: String RepositoryName: MaxLength: 100 Type: String Resources: # ------------------------------------------------------------# # CodeCommit # ------------------------------------------------------------# CodeCommit: Type: AWS::CodeCommit::Repository Properties: RepositoryDescription: !Ref RepositoryDescription RepositoryName: !Ref RepositoryName Outputs: # ------------------------------------------------------------# # Outputs # ------------------------------------------------------------# CodeCommitRepositoryName: Value: !GetAtt CodeCommit.Name Export: Name: codecommit-repository-name CodeCommitRepositoryARN: Value: !GetAtt CodeCommit.Arn Export: Name: codecommit-repository-arn
以下のコマンドでデプロイします。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=RepositoryDescription,ParameterValue=リポジトリの説明 ParameterKey=RepositoryName,ParameterValue=リポジトリの名前
デプロイが完了したらリポジトリにappspec.ymlをpushします。
pushするappspec.ymlの内容は以下になります。
version: 0.0 os: linux files: - source: / destination: /var/www/html permissions: - object: /var/www/html owner: apache group: apache mode: 755 type: - file - directory
pushは以下のコマンドで行います。
# クローンURL確認 aws codecommit get-repository --repository-name リポジトリの名前 --query repositoryMetadata.cloneUrlHttp # クローン git clone クローンURL # デフォルトブランチ設定 git config --local init.defaultBranch main # push git add appspec.yml git commit -m "add appspec.yml" git push origin main
CodeDeploy
CodeDeployではBlue/Greenデプロイができるように設定します。
EC2/オンプレミス Blue/Green デプロイ用のデプロイグループを作成する (コンソール)
マネジメントコンソールにログインしてCodeDeployの「アプリケーション」へ移動します。
画面が遷移したら「アプリケーションの作成」をクリックします。
クリックしたら任意の「アプリケーション名」を入力して「コンピューティングプラットフォーム」を選択します。
選択したら「アプリケーションの作成」をクリックします。
アプリケーションが作成されて画面が遷移したら「デプロイグループの作成」をクリックします。
クリックして画面が遷移したら任意の「デプロイグループ名」を入力して「サービスロール」を選択します。
次に「デプロイタイプ」で「Blue/Green」を選択して「環境設定」で「Amazon EC2 Auto Scaling グループの自動コピー」を選択後、「Auto Scaling グループ」を選択します。
次に「デプロイ設定」で「すぐにトラフィックを再ルーティング」と「デプロイグループの置き換え元インスタンスを終了」を選択し時間を設定します。
下にある「デプロイ設定」は「CodeDeployDefault.AllAtOnce」にしています。
ここの設定はGreen環境 (切り替え先) が作成された際の動作を設定しています。
「すぐにトラフィックを再ルーティング」で設定するとGreen環境が出来上がったらロードバランサーに自動的に登録するという設定です。
「デプロイグループの置き換え元インスタンスを終了」は指定した時間 (今回の場合は15分) が経過したらBlue環境 (古い方) を削除する設定になります。
次に「Load balancer」で「Application Load Balancer またはNetwork Load Balancer」を選択して「ターゲットグループ」を選択します。
ここまで設定したら「デプロイグループの作成」をクリックします。
CodePipeline
CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09" Description: CodePipeline Stack Metadata: # ------------------------------------------------------------# # Metadata # ------------------------------------------------------------# AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Parameters for SNS Parameters: - MailAddress - Label: default: Parameters for CodePipeline Parameters: - CodePipelineName - ApplicationName - DeploymentGroupName Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# MailAddress: Type: String CodePipelineName: MaxLength: 100 Type: String ApplicationName: Type: String DeploymentGroupName: Type: String Resources: # ------------------------------------------------------------# # SNS # ------------------------------------------------------------# SnsTopic: Type: AWS::SNS::Topic Properties: Subscription: - Endpoint: !Ref MailAddress Protocol: email TopicName: sns-codepipeline-approval SnsTopicPolicy: Type: AWS::SNS::TopicPolicy Properties: PolicyDocument: Version: '2012-10-17' Id: approval Statement: - Sid: approval Effect: Allow Principal: AWS: '*' Action: - 'SNS:GetTopicAttributes' - 'SNS:SetTopicAttributes' - 'SNS:AddPermission' - 'SNS:RemovePermission' - 'SNS:DeleteTopic' - 'SNS:Subscribe' - 'SNS:ListSubscriptionsByTopic' - 'SNS:Publish' Resource: !Ref SnsTopic Condition: StringEquals: 'AWS:SourceOwner': !Sub ${AWS::AccountId} Topics: - !Ref SnsTopic # ------------------------------------------------------------# # CodePipeline # ------------------------------------------------------------# CodePipeline: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Location: !ImportValue S3Name Type: S3 Name: !Ref CodePipelineName RoleArn: !ImportValue iam-role-codepipeline-arn Stages: - Actions: - ActionTypeId: Category: Source Owner: AWS Provider: CodeCommit Version: 1 Configuration: RepositoryName: !ImportValue codecommit-repository-name BranchName: main PollForSourceChanges: false OutputArtifactFormat: CODE_ZIP Name: Source Namespace: SourceVariables OutputArtifacts: - Name: SourceArtifact Region: ap-northeast-1 RunOrder: 1 Name: Source - Actions: - ActionTypeId: Category: Approval Owner: AWS Provider: Manual Version: 1 Configuration: NotificationArn: !Ref SnsTopic Name: Approval Namespace: ApprovalVariables Region: ap-northeast-1 RunOrder: 1 Name: Approval - Actions: - ActionTypeId: Category: Deploy Owner: AWS Provider: CodeDeploy Version: 1 Configuration: ApplicationName: !Ref ApplicationName DeploymentGroupName: !Ref DeploymentGroupName Name: Deploy Namespace: DeployVariables InputArtifacts: - Name: SourceArtifact Region: ap-northeast-1 RunOrder: 1 Name: Deploy Tags: - Key: Name Value: !Ref CodePipelineName # ------------------------------------------------------------# # EventBridge # ------------------------------------------------------------# EventBridgeIAMPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "codepipeline:StartPipelineExecution" Resource: - !Join - '' - - 'arn:aws:codepipeline:ap-northeast-1:' - !Sub '${AWS::AccountId}:' - !Ref CodePipeline ManagedPolicyName: iam-policy-eventbridge EventBridgeIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - !Ref EventBridgeIAMPolicy RoleName: iam-role-eventbridge Tags: - Key: Name Value: iam-role-eventbridge EventBridge: Type: AWS::Events::Rule Properties: Description: for codepipeline EventPattern: source: - aws.codecommit detail-type: - 'CodeCommit Repository State Change' resources: - !ImportValue codecommit-repository-arn detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - main Name: eventbridge-codepipeline State: ENABLED Targets: - Arn: !Join - '' - - 'arn:aws:codepipeline:ap-northeast-1:' - !Sub '${AWS::AccountId}:' - !Ref CodePipeline Id: CodePipeline RoleArn: !GetAtt EventBridgeIAMRole.Arn
以下のコマンドでデプロイします。
aws cloudformation create-stack --stack-name スタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=MailAddress,ParameterValue=SNSで通知するメールアドレス ParameterKey=CodePipelineName,ParameterValue=CodePipeline名 ParameterKey=ApplicationName,ParameterValue=CodeDeployアプリケーション名 ParameterKey=DeploymentGroupName,ParameterValue=CodeDeployデプロイグループ名 --capabilities CAPABILITY_NAMED_IAM
デプロイが完了すると新しいAutoScalingグループが作成され始めます。
下記の画像だと上のAutoScalingグループがGreen環境になるものです。
下のBlue環境は環境の切り替え後15分で削除されます。
動作確認
CodeCommitリポジトリにtest.htmlという名前のHTMLファイルをgit pushしてみます。
ディレクトリの中身は以下のようになっています。
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2023/01/24 14:37 327 appspec.yml -a---- 2022/09/29 1:16 158 test.html
以下のコマンドを実行してCodeCommitリポジトリにpushします。
git add . git commit -m "add test.html" git push origin main
pushして承認フェーズで承認したら正常にGreen環境が作成されることが確認できます。
CodeDeployの画面から各ステップの進行状況が確認できます。
新しく起動したEC2にアクセスしてドキュメントルートを確認するとデプロイされたことが確認できます。
ls -la /var/www/html/ total 12 drwxr-xr-x 2 root root 60 Jan 30 15:02 . drwxr-xr-x 4 root root 33 Jan 30 05:47 .. -rwxr-xr-x 1 apache apache 327 Dec 31 1979 appspec.yml -rw-r--r-- 1 root root 16 Jan 30 05:47 index.html -rwxr-xr-x 1 apache apache 159 Dec 31 1979 test.html
さいごに
CodePipeline周りのサービスはIAMロールの設定がサービスごとにあるので、どこで何が使用されているのか把握しておく必要があると思いました。
いつかCloudFormationでEC2のBlue/Greenデプロイ設定に対応してくれることを祈ります。