From 8ca3fa2485b0209da29a1f8717fb56cee00a6b46 Mon Sep 17 00:00:00 2001 From: Darren McGhee Date: Mon, 20 Apr 2020 15:35:13 +0100 Subject: [PATCH] Commiting new files for building ECS --- buildspec.yml | 6 +- codepipeline.yml | 332 ++++++++++++++++++++++++++++++++++++++++++ ecs.yml | 371 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 707 insertions(+), 2 deletions(-) create mode 100644 codepipeline.yml create mode 100644 ecs.yml diff --git a/buildspec.yml b/buildspec.yml index 604a36d6f..d7efa5e33 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -7,7 +7,7 @@ phases: pre_build: commands: - AWS_ACC=$(echo $CODEBUILD_BUILD_ARN | cut -d':' -f5) - - REPOSITORY_URI=$AWS_ACC.dkr.ecr.$AWS_REGION.amazonaws.com + - REPOSITORY_URI=$AWS_ACC.dkr.ecr.$AWS_REGION.amazonaws.com build: commands: - echo Build started on `date` @@ -19,6 +19,8 @@ phases: - $(aws ecr get-login --region $AWS_REGION --no-include-email) - aws ecr describe-repositories --registry-id ${AWS_ACC} --repository-name ${REPO} - docker push $REPOSITORY_URI/${REPO}:latest + - echo {\"repo\":\"${REPO}\"} > repo.json artifacts: files: - - target/spring-petclinic-2.2.0.BUILD-SNAPSHOT.jar \ No newline at end of file + - target/spring-petclinic-2.2.0.BUILD-SNAPSHOT.jar + - repo.json \ No newline at end of file diff --git a/codepipeline.yml b/codepipeline.yml new file mode 100644 index 000000000..158d98b69 --- /dev/null +++ b/codepipeline.yml @@ -0,0 +1,332 @@ +--- +AWSTemplateFormatVersion: 2010-09-09 + + +Parameters: + GitHubRepo: + Type: String + Description: GitHub repository name + GitHubRepoOwner: + Type: String + Description: The user or organization that owns the repository + GitHubToken: + Type: String + Description: GitHub OAuth Access Token + NoEcho: true + RepoName: + Type: String + AllowedPattern: (?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)* + +Resources: + ECRRepository: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Ref RepoName + Pipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + ArtifactStore: + Location: !Ref ArtifactStoreBucket + Type: S3 + RoleArn: !GetAtt CodePipelineRole.Arn + Name: !Sub '${AWS::StackName}-pipeline' + Stages: + + - Name: Source + Actions: + - Name: Source + ActionTypeId: + Category: Source + Owner: ThirdParty + Provider: GitHub + Version: '1' + Configuration: + Repo: !Ref GitHubRepo + Owner: !Ref GitHubRepoOwner + Branch: master + OAuthToken: !Ref GitHubToken + OutputArtifacts: + - Name: Source + RunOrder: 1 + + - Name: Build + Actions: + - Name: Build + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: '1' + Configuration: + ProjectName: !Ref CodeBuildProject + InputArtifacts: + - Name: Source + OutputArtifacts: + - Name: BuildArtifact + RunOrder: 2 + - Name: Deploy + Actions: + - InputArtifacts: + - Name: BuildArtifact + - Name: Source + Name: CreateEnv + ActionTypeId: + Category: Deploy + Owner: AWS + Version: '1' + Provider: CloudFormation + OutputArtifacts: [] + Configuration: + ActionMode: REPLACE_ON_FAILURE + RoleArn: + Fn::GetAtt: + - CloudFormationTrustRole + - Arn + Capabilities: CAPABILITY_IAM + StackName: + Fn::Join: + - '' + - - "" + - Ref: AWS::StackName + - "-" + - Ref: AWS::Region + - "" + TemplatePath: Source::ecs.yml + ParameterOverrides: | + { + "Repo" : { "Fn::GetParam" : [ "BuildArtifact", "repo.json", "repo" ] } + } + RunOrder: 1 + ArtifactStoreBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Sub '${AWS::StackName}-artifacts' + DeletionPolicy: Delete + CodeBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Artifacts: + Type: CODEPIPELINE + Source: + Type: CODEPIPELINE + Environment: + ComputeType: BUILD_GENERAL1_SMALL + Image: aws/codebuild/standard:3.0 + Type: LINUX_CONTAINER + PrivilegedMode: True + EnvironmentVariables: + - Name: REPO + Type: PLAINTEXT + Value: !Ref ECRRepository + LogsConfig: + CloudWatchLogs: + GroupName: !Sub '${AWS::StackName}-group' + Status: ENABLED + StreamName: !Sub '${AWS::StackName}-stream' + Name: !Ref AWS::StackName + ServiceRole: !Ref CodeBuildServiceRole + CloudFormationTrustRole: + DependsOn: + - ArtifactStoreBucket + Description: Creating service role in IAM for AWS CloudFormation + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: + - cloudformation.amazonaws.com + Path: "/" + Policies: + - PolicyDocument: + Statement: + - Action: + - s3:* + Effect: Allow + Resource: "*" + - Action: + - iam:CreateRole + - iam:AttachRolePolicy + - iam:DetachRolePolicy + - iam:DeleteRole + - iam:DeleteRolePolicy + - iam:GetRole + - iam:PutRolePolicy + - iam:GetRolePolicy + - iam:AddRoleToInstanceProfile + - iam:RemoveRoleFromInstanceProfile + - iam:DeleteInstanceProfile + - iam:CreateInstanceProfile + - iam:PassRole + Effect: Allow + Resource: "*" + - Action: + - cloudformation:CreateChangeSet + Effect: Allow + Resource: + - !Sub 'arn:aws:cloudformation:${AWS::Region}:aws:transform/Serverless-2016-10-31' + - Effect: Allow + Action: + - ec2:DeleteNetworkInterface + - ec2:DescribeAccountAttributes + - ec2:AttachVolume + - ec2:DeleteSubnet + - ec2:DescribeInstances + - ec2:DeleteTags + - ec2:DescribeRegions + - ec2:CreateVpc + - ec2:AttachInternetGateway + - ec2:AuthorizeSecurityGroupIngress + - ec2:RevokeSecurityGroupIngress + - ec2:DescribeVpcAttribute + - ec2:DescribeInternetGateways + - ec2:DeleteRouteTable + - ec2:ModifySubnetAttribute + - ec2:AssociateRouteTable + - ec2:DeleteVolume + - ec2:DescribeNetworkInterfaces + - ec2:DescribeAvailabilityZones + - ec2:CreateRoute + - ec2:CreateInternetGateway + - ec2:CreateSecurityGroup + - ec2:DescribeVolumes + - ec2:ModifyVpcAttribute + - ec2:DeleteInternetGateway + - ec2:DescribeRouteTables + - ec2:DetachVolume + - ec2:DetachNetworkInterface + - ec2:DescribeTags + - ec2:CreateTags + - ec2:DeleteRoute + - ec2:CreateRouteTable + - ec2:DetachInternetGateway + - ec2:DescribeSecurityGroups + - ec2:CreateNetworkInterface + - ec2:DescribeImages + - ec2:DescribeSecurityGroupReferences + - ec2:DescribeVpcs + - ec2:DeleteSecurityGroup + - ec2:AttachNetworkInterface + - ec2:DeleteVpc + - ec2:CreateSubnet + - ec2:DescribeSubnets + - ec2:DisassociateRouteTable + - elasticloadbalancing:* + - application-autoscaling:* + - cloudwatch:* + - logs:* + Resource: "*" + - Effect: Allow + Action: + - ecs:* + Resource: "*" + - Effect: Allow + Action: + - autoscaling:* + Resource: "*" + PolicyName: CloudFormationRolePolicy + RoleName: + Fn::Join: + - "-" + - - serverless + - Ref: AWS::StackName + - CloudFormation + Type: AWS::IAM::Role + CodePipelineRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: + - codepipeline.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: codepipeline-service + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - "logs:*" + Resource: "*" + - Effect: Allow + Action: + - s3:* + Resource: !Sub 'arn:aws:s3:::${ArtifactStoreBucket}/*' + - Effect: Allow + Action: + - "codebuild:*" + Resource: "*" + - Effect: Allow + Action: + - "cloudformation:DescribeStacks" + - "cloudformation:CreateStack" + - "cloudformation:DeleteStack" + - "cloudformation:UpdateStack" + - "cloudformation:CreateStackSet" + - "cloudformation:DeleteStackSet" + - "cloudformation:UpdateStackSet" + - "cloudformation:CreateStackSet" + - "cloudformation:DeleteStackSet" + - "cloudformation:CreateChangeSet" + - "cloudformation:DescribeChangeSet" + - "cloudformation:ExecuteChangeSet" + - "cloudformation:DeleteChangeSet" + Resource: "*" + - Effect: Allow + Action: + - iam:PassRole + Resource: + - Fn::GetAtt: + - CloudFormationTrustRole + - Arn + CodeBuildServiceRole: + Type: AWS::IAM::Role + Properties: + Path: / + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: root + PolicyDocument: + Version: 2012-10-17 + Statement: + - Resource: "*" + Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + - ecr:GetAuthorizationToken + - PolicyName: ecr + PolicyDocument: + Version: 2012-10-17 + Statement: + - Resource: !GetAtt ECRRepository.Arn + Effect: Allow + Action: + - ecr:* + - PolicyName: s3artifact + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - s3:PutObject + Resource: !Sub 'arn:aws:s3:::${ArtifactStoreBucket}/*' + - Effect: Allow + Action: + - s3:GetObject + - s3:GetObjectVersion + Resource: "*" + \ No newline at end of file diff --git a/ecs.yml b/ecs.yml new file mode 100644 index 000000000..b1f14f3a2 --- /dev/null +++ b/ecs.yml @@ -0,0 +1,371 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: + DesiredCapacity: + Type: Number + Default: '1' + Description: Number of instances to launch in your ECS cluster. + Repo: + Type: String + Description: Repo for docker container. + MaxSize: + Type: Number + Default: '1' + Description: Maximum number of instances that can be launched in your ECS cluster. + InstanceType: + Description: EC2 instance type + Type: String + Default: t2.micro + AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, m3.medium, m3.large, + m3.xlarge, m3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.10xlarge, + c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c3.large, c3.xlarge, + c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge, + r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge] + ConstraintDescription: Please choose a valid instance type. +Mappings: + AWSRegionToAMI: + us-east-1: + AMIID: ami-eca289fb + us-east-2: + AMIID: ami-446f3521 + us-west-1: + AMIID: ami-9fadf8ff + us-west-2: + AMIID: ami-7abc111a + eu-west-1: + AMIID: ami-a1491ad2 + eu-central-1: + AMIID: ami-54f5303b + ap-northeast-1: + AMIID: ami-9cd57ffd + ap-southeast-1: + AMIID: ami-a900a3ca + ap-southeast-2: + AMIID: ami-5781be34 +Resources: + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + Tags: + - Key: Application + Value: !Ref 'AWS::StackId' + SubnetA: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref 'VPC' + CidrBlock: 10.0.0.0/24 + MapPublicIpOnLaunch: True + AvailabilityZone: + Fn::Select: + - 0 + - Fn::GetAZs: "" + Tags: + - Key: Application + Value: !Ref 'AWS::StackId' + SubnetB: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref 'VPC' + CidrBlock: 10.0.1.0/24 + MapPublicIpOnLaunch: True + AvailabilityZone: + Fn::Select: + - 1 + - Fn::GetAZs: "" + Tags: + - Key: Application + Value: !Ref 'AWS::StackId' + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Application + Value: !Ref 'AWS::StackId' + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: !Ref 'VPC' + InternetGatewayId: !Ref 'InternetGateway' + RouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref 'VPC' + Tags: + - Key: Application + Value: !Ref 'AWS::StackId' + Route: + Type: AWS::EC2::Route + DependsOn: AttachGateway + Properties: + RouteTableId: !Ref 'RouteTable' + DestinationCidrBlock: '0.0.0.0/0' + GatewayId: !Ref 'InternetGateway' + SubnetRouteTableAssociationA: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref 'SubnetA' + RouteTableId: !Ref 'RouteTable' + SubnetRouteTableAssociationB: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref 'SubnetB' + RouteTableId: !Ref 'RouteTable' + ECSCluster: + Type: AWS::ECS::Cluster + EcsSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: ECS Security Group + VpcId: !Ref 'VPC' + EcsSecurityGroupHTTPinbound: + Type: AWS::EC2::SecurityGroupIngress + Properties: + GroupId: !Ref 'EcsSecurityGroup' + IpProtocol: tcp + FromPort: '8080' + ToPort: '8080' + CidrIp: 0.0.0.0/0 + EcsSecurityGroupALBports: + Type: AWS::EC2::SecurityGroupIngress + Properties: + GroupId: !Ref 'EcsSecurityGroup' + IpProtocol: tcp + FromPort: '31000' + ToPort: '61000' + SourceSecurityGroupId: !Ref 'EcsSecurityGroup' + CloudwatchLogsGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: !Join ['-', [ECSLogGroup, !Ref 'AWS::StackName']] + RetentionInDays: 14 + taskdefinition: + Type: AWS::ECS::TaskDefinition + Properties: + Family: !Join ['', [!Ref 'AWS::StackName', -ecs-spring-petclinic]] + ContainerDefinitions: + - Name: petclinic + Cpu: 10 + Essential: 'true' + Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repo}:latest' + Memory: 300 + LogConfiguration: + LogDriver: awslogs + Options: + awslogs-group: !Ref 'CloudwatchLogsGroup' + awslogs-region: !Ref 'AWS::Region' + awslogs-stream-prefix: ecs-spring-petclinic + PortMappings: + - ContainerPort: 8080 + ECSALB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Name: ECSALB + Scheme: internet-facing + LoadBalancerAttributes: + - Key: idle_timeout.timeout_seconds + Value: '30' + Subnets: [!Ref SubnetA, !Ref SubnetB] + SecurityGroups: [!Ref 'EcsSecurityGroup'] + ALBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + DependsOn: ECSServiceRole + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref 'ECSTG' + LoadBalancerArn: !Ref 'ECSALB' + Port: '8080' + Protocol: HTTP + ECSALBListenerRule: + Type: AWS::ElasticLoadBalancingV2::ListenerRule + DependsOn: ALBListener + Properties: + Actions: + - Type: forward + TargetGroupArn: !Ref 'ECSTG' + Conditions: + - Field: path-pattern + Values: [/] + ListenerArn: !Ref 'ALBListener' + Priority: 1 + ECSTG: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + DependsOn: ECSALB + Properties: + HealthCheckIntervalSeconds: 10 + HealthCheckPath: / + HealthCheckProtocol: HTTP + HealthCheckTimeoutSeconds: 5 + HealthyThresholdCount: 2 + Port: 8080 + Protocol: HTTP + UnhealthyThresholdCount: 2 + VpcId: !Ref 'VPC' + ECSAutoScalingGroup: + Type: AWS::AutoScaling::AutoScalingGroup + Properties: + VPCZoneIdentifier: [!Ref SubnetA, !Ref SubnetB] + LaunchConfigurationName: !Ref 'ContainerInstances' + MinSize: '1' + MaxSize: !Ref 'MaxSize' + DesiredCapacity: !Ref 'DesiredCapacity' + CreationPolicy: + ResourceSignal: + Timeout: PT15M + UpdatePolicy: + AutoScalingReplacingUpdate: + WillReplace: 'true' + ContainerInstances: + Type: AWS::AutoScaling::LaunchConfiguration + Properties: + ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMIID] + SecurityGroups: [!Ref 'EcsSecurityGroup'] + InstanceType: !Ref 'InstanceType' + IamInstanceProfile: !Ref 'EC2InstanceProfile' + UserData: + Fn::Base64: !Sub | + #!/bin/bash -xe + echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config + yum install -y aws-cfn-bootstrap + /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region} + service: + Type: AWS::ECS::Service + DependsOn: ALBListener + Properties: + Cluster: !Ref 'ECSCluster' + DesiredCount: '1' + LoadBalancers: + - ContainerName: petclinic + ContainerPort: '8080' + TargetGroupArn: !Ref 'ECSTG' + Role: !Ref 'ECSServiceRole' + TaskDefinition: !Ref 'taskdefinition' + ECSServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: [ecs.amazonaws.com] + Action: ['sts:AssumeRole'] + Path: / + Policies: + - PolicyName: ecs-service + PolicyDocument: + Statement: + - Effect: Allow + Action: ['elasticloadbalancing:DeregisterInstancesFromLoadBalancer', 'elasticloadbalancing:DeregisterTargets', + 'elasticloadbalancing:Describe*', 'elasticloadbalancing:RegisterInstancesWithLoadBalancer', + 'elasticloadbalancing:RegisterTargets', 'ec2:Describe*', 'ec2:AuthorizeSecurityGroupIngress'] + Resource: '*' + ServiceScalingTarget: + Type: AWS::ApplicationAutoScaling::ScalableTarget + DependsOn: service + Properties: + MaxCapacity: 2 + MinCapacity: 1 + ResourceId: !Join ['', [service/, !Ref 'ECSCluster', /, !GetAtt [service, Name]]] + RoleARN: !GetAtt [AutoscalingRole, Arn] + ScalableDimension: ecs:service:DesiredCount + ServiceNamespace: ecs + ServiceScalingPolicy: + Type: AWS::ApplicationAutoScaling::ScalingPolicy + Properties: + PolicyName: AStepPolicy + PolicyType: StepScaling + ScalingTargetId: !Ref 'ServiceScalingTarget' + StepScalingPolicyConfiguration: + AdjustmentType: PercentChangeInCapacity + Cooldown: 60 + MetricAggregationType: Average + StepAdjustments: + - MetricIntervalLowerBound: 0 + ScalingAdjustment: 200 + ALB500sAlarmScaleUp: + Type: AWS::CloudWatch::Alarm + Properties: + EvaluationPeriods: '1' + Statistic: Average + Threshold: '10' + AlarmDescription: Alarm if our ALB generates too many HTTP 500s. + Period: '60' + AlarmActions: [!Ref 'ServiceScalingPolicy'] + Namespace: AWS/ApplicationELB + Dimensions: + - Name: LoadBalancer + Value: !GetAtt + - ECSALB + - LoadBalancerFullName + ComparisonOperator: GreaterThanThreshold + MetricName: HTTPCode_ELB_5XX_Count + EC2Role: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: [ec2.amazonaws.com] + Action: ['sts:AssumeRole'] + Path: / + Policies: + - PolicyName: ecs-service + PolicyDocument: + Statement: + - Effect: Allow + Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint', + 'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession', + 'ecs:Submit*', 'logs:CreateLogStream', 'logs:PutLogEvents', 'ecr:GetAuthorizationToken'] + Resource: '*' + - PolicyName: ecr-service + PolicyDocument: + Statement: + - Effect: Allow + Action: 'ecr:*' + Resource: !Sub 'arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${Repo}' + ECSTaskRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: [ecs-tasks.amazonaws.com] + Action: ['sts:AssumeRole'] + Path: / + ManagedPolicyArns: + - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy + AutoscalingRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: [application-autoscaling.amazonaws.com] + Action: ['sts:AssumeRole'] + Path: / + Policies: + - PolicyName: service-autoscaling + PolicyDocument: + Statement: + - Effect: Allow + Action: ['application-autoscaling:*', 'cloudwatch:DescribeAlarms', 'cloudwatch:PutMetricAlarm', + 'ecs:DescribeServices', 'ecs:UpdateService'] + Resource: '*' + EC2InstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Path: / + Roles: [!Ref 'EC2Role'] +Outputs: + ecsservice: + Value: !Ref 'service' + ecscluster: + Value: !Ref 'ECSCluster' + ECSALB: + Description: Your ALB DNS URL + Value: !Join ['', [!GetAtt [ECSALB, DNSName]]] + taskdef: + Value: !Ref 'taskdefinition' \ No newline at end of file