· 7 years ago · Nov 18, 2017, 12:00 PM
1# App deploy stack with ECS, CodeBuild & CodePipeline.
2#
3# aws cloudformation deploy \
4# --stack-name app-services-prod \
5# --template-file ./aws-services-stack.yaml \
6# --parameter-overrides \
7# KeyName=<KEY_NAME> \
8# GitHubAuthToken=<ACCESS_TOKEN> \
9# RepoOwner=<OWNER_NAME> \
10# RepoName=<REPO_NAME> \
11# BranchName=<BRANCH_NAME> \
12# --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
13# --no-execute-changeset
14
15AWSTemplateFormatVersion: '2010-09-09'
16Transform: 'AWS::Serverless-2016-10-31'
17
18Description: App services on ECS.
19
20Parameters:
21 KeyName:
22 Description: The EC2 Key Pair to allow SSH access to the instance.
23 Type: AWS::EC2::KeyPair::KeyName
24 GitHubAuthToken:
25 Description: GitHub access token.
26 Type: String
27 RepoOwner:
28 Description: Name of the GitHub user or org who owns the repository.
29 Type: String
30 RepoName:
31 Description: The GitHub repo name.
32 Type: String
33 BranchName:
34 Description: Name of repo branch to watch.
35 Type: String
36 PipelineBucketName:
37 Description: Name of S3 bucket to create for CodePipeline.
38 Type: String
39
40Resources:
41 ## ECS CLUSTER
42 EcsCluster:
43 Type: AWS::ECS::Cluster
44 Properties:
45 ClusterName: !Ref AWS::StackName
46
47 EcsServiceScalingTargetRole:
48 Type: AWS::IAM::Role
49 Properties:
50 Path: /
51 AssumeRolePolicyDocument:
52 Version: '2012-10-17'
53 Statement:
54 - Effect: Allow
55 Action:
56 - sts:AssumeRole
57 Principal:
58 Service: application-autoscaling.amazonaws.com
59 Policies:
60 - PolicyName: Scaling
61 PolicyDocument:
62 Version: '2012-10-17'
63 Statement:
64 - Effect: Allow
65 Action:
66 - application-autoscaling:*
67 - ecs:*
68 - cloudwatch:*
69 Resource: '*'
70
71
72 ## SERVICE
73 LogGroup:
74 Type: AWS::Logs::LogGroup
75 Properties:
76 LogGroupName: !Ref AWS::StackName
77 RetentionInDays: 7
78
79 EcrRepository:
80 Type: AWS::ECR::Repository
81 Properties:
82 RepositoryName: !Ref AWS::StackName
83 RepositoryPolicyText:
84 Version: "2012-10-17"
85 Statement:
86 - Sid: CodeBuildPushPull
87 Effect: Allow
88 Principal:
89 Service: codebuild.amazonaws.com
90 Action:
91 - ecr:GetDownloadUrlForLayer
92 - ecr:BatchGetImage
93 - ecr:BatchCheckLayerAvailability
94 - ecr:PutImage
95 - ecr:InitiateLayerUpload
96 - ecr:UploadLayerPart
97 - ecr:CompleteLayerUpload
98
99 EcsTaskDefinition:
100 Type: AWS::ECS::TaskDefinition
101 DependsOn:
102 - EcrRepository
103 Properties:
104 Family: !Ref AWS::StackName
105 ContainerDefinitions:
106 - Name: !Ref AWS::StackName
107 Image: !Sub ${AWS::AccountId}.dkr.ecr.us-east-1.amazonaws.com/${AWS::StackName}:latest
108 Cpu: 256
109 MemoryReservation: 512
110 LogConfiguration:
111 LogDriver: awslogs
112 Options:
113 awslogs-group: !Ref AWS::StackName
114 awslogs-region: !Ref AWS::Region
115 awslogs-stream-prefix: !Ref AWS::StackName
116 Environment:
117 - Name: NODE_ENV
118 Value: production
119 # any additional env vars
120
121 EcsService:
122 Type: AWS::ECS::Service
123 DependsOn:
124 - EcrRepository
125 Properties:
126 TaskDefinition: !Ref EcsTaskDefinition
127 DesiredCount: 0 # only when creating stack
128 DeploymentConfiguration:
129 MinimumHealthyPercent: 50
130 PlacementStrategies:
131 - Type: spread
132 Field: instanceId
133 Cluster: !Ref EcsCluster
134
135 EcsServiceScalingTarget:
136 Type: AWS::ApplicationAutoScaling::ScalableTarget
137 Properties:
138 MinCapacity: 0
139 MaxCapacity: 0
140 ResourceId:
141 !Sub
142 - service/${EcsCluster}/${EcsService}
143 - EcsCluster: !Ref EcsCluster
144 EcsService: !GetAtt EcsService.Name
145 RoleARN: !GetAtt EcsServiceScalingTargetRole.Arn
146 ScalableDimension: ecs:service:DesiredCount
147 ServiceNamespace: ecs
148
149 EcsServiceScaleOutPolicy:
150 Type: AWS::ApplicationAutoScaling::ScalingPolicy
151 Properties:
152 PolicyName: EcsServiceScaleOutPolicy
153 PolicyType: StepScaling
154 ScalingTargetId: !Ref EcsServiceScalingTarget
155 StepScalingPolicyConfiguration:
156 AdjustmentType: PercentChangeInCapacity
157 StepAdjustments:
158 - MetricIntervalLowerBound: 0
159 MetricIntervalUpperBound: 10
160 ScalingAdjustment: 10
161 - MetricIntervalLowerBound: 10
162 ScalingAdjustment: 30
163
164 EcsServiceScaleInPolicy:
165 Type: AWS::ApplicationAutoScaling::ScalingPolicy
166 Properties:
167 PolicyName: EcsServiceScaleInPolicy
168 PolicyType: StepScaling
169 ScalingTargetId: !Ref EcsServiceScalingTarget
170 StepScalingPolicyConfiguration:
171 AdjustmentType: PercentChangeInCapacity
172 StepAdjustments:
173 - MetricIntervalLowerBound: -10
174 MetricIntervalUpperBound: 0
175 ScalingAdjustment: -10
176 - MetricIntervalUpperBound: -10
177 ScalingAdjustment: -30
178
179 EcsServiceHighCPUAlarm:
180 Type: AWS::CloudWatch::Alarm
181 Properties:
182 EvaluationPeriods: 2
183 Statistic: Average
184 Threshold: 70
185 AlarmDescription: Alarm if ECS Service CPU high.
186 Period: 60
187 AlarmActions:
188 - !Ref EcsServiceScaleOutPolicy
189 Namespace: AWS/ECS
190 Dimensions:
191 - Name: ClusterName
192 Value: !Ref EcsCluster
193 - Name: ServiceName
194 Value: !GetAtt EcsService.Name
195 ComparisonOperator: GreaterThanOrEqualToThreshold
196 MetricName: CPUUtilization
197
198 EcsServiceLowCPUAlarm:
199 Type: AWS::CloudWatch::Alarm
200 Properties:
201 EvaluationPeriods: 2
202 Statistic: Average
203 Threshold: 40
204 AlarmDescription: Alarm if ECS Service CPU low.
205 Period: 60
206 AlarmActions:
207 - !Ref EcsServiceScaleInPolicy
208 Namespace: AWS/ECS
209 Dimensions:
210 - Name: ClusterName
211 Value: !Ref EcsCluster
212 - Name: ServiceName
213 Value: !GetAtt EcsService.Name
214 ComparisonOperator: LessThanOrEqualToThreshold
215 MetricName: CPUUtilization
216
217
218
219 ## BUILD
220 S3Bucket:
221 Type: AWS::S3::Bucket
222 Properties:
223 BucketName: !Ref PipelineBucketName
224
225 CodeBuildRole:
226 Type: AWS::IAM::Role
227 Properties:
228 Path: /
229 AssumeRolePolicyDocument:
230 Version: '2012-10-17'
231 Statement:
232 - Effect: Allow
233 Action:
234 - sts:AssumeRole
235 Principal:
236 Service: codebuild.amazonaws.com
237 Policies:
238 - PolicyName: S3Access
239 PolicyDocument:
240 Version: '2012-10-17'
241 Statement:
242 - Effect: Allow
243 Action:
244 - s3:*
245 Resource:
246 !Sub
247 - arn:aws:s3:::${S3Bucket}*
248 - S3Bucket: !Ref S3Bucket
249 - PolicyName: ServicesAccess
250 PolicyDocument:
251 Version: '2012-10-17'
252 Statement:
253 - Effect: Allow
254 Action:
255 - logs:*
256 - codecommit:*
257 Resource: '*'
258
259 CodePipelineRole:
260 Type: AWS::IAM::Role
261 Properties:
262 Path: /
263 AssumeRolePolicyDocument:
264 Version: '2012-10-17'
265 Statement:
266 - Effect: Allow
267 Action:
268 - sts:AssumeRole
269 Principal:
270 Service: codepipeline.amazonaws.com
271 Policies:
272 - PolicyName: S3Access
273 PolicyDocument:
274 Version: '2012-10-17'
275 Statement:
276 - Effect: Allow
277 Action:
278 - s3:*
279 Resource:
280 !Sub
281 - arn:aws:s3:::${S3Bucket}/*
282 - S3Bucket: !Ref S3Bucket
283 - PolicyName: ServicesAccess
284 PolicyDocument:
285 Version: '2012-10-17'
286 Statement:
287 - Effect: Allow
288 Action:
289 - codepipeline:*
290 - codebuild:*
291 - lambda:*
292 - iam:ListRoles
293 - iam:PassRole
294 Resource: '*'
295
296
297 ## BUILD
298 CodeBuildProject:
299 Type: AWS::CodeBuild::Project
300 Properties:
301 Name: !Ref AWS::StackName
302 Description: App on ECS.
303 ServiceRole: !GetAtt CodeBuildRole.Arn
304 Source:
305 Type: CODEPIPELINE
306 Artifacts:
307 Type: CODEPIPELINE
308 Environment:
309 Type: linuxContainer
310 ComputeType: BUILD_GENERAL1_MEDIUM
311 Image: aws/codebuild/docker:1.12.1
312 EnvironmentVariables:
313 - Name: APP_NAME
314 Value: !Ref AWS::StackName
315 - Name: APP_IMAGE
316 Value: !Sub ${AWS::AccountId}.dkr.ecr.us-east-1.amazonaws.com/${AWS::StackName}:latest
317 TimeoutInMinutes: 10
318 Tags:
319 - Key: Name
320 Value: !Ref AWS::StackName
321 - Key: role
322 Value: !Ref AWS::StackName
323
324 CodePipeline:
325 Type: AWS::CodePipeline::Pipeline
326 Properties:
327 ArtifactStore:
328 Type: S3
329 Location: !Ref S3Bucket
330 Name: !Ref AWS::StackName
331 RoleArn: !GetAtt CodePipelineRole.Arn
332 Stages:
333 - Name: Source
334 Actions:
335 - Name: Source
336 RunOrder: 1
337 ActionTypeId:
338 Version: 1
339 Category: Source
340 Owner: ThirdParty
341 Provider: GitHub
342 OutputArtifacts:
343 - Name: !Ref AWS::StackName
344 Configuration:
345 Owner: !Ref RepoOwner
346 Repo: !Ref RepoName
347 Branch: !Ref BranchName
348 OAuthToken: !Ref GitHubAuthToken
349 - Name: Build
350 Actions:
351 - Name: Build
352 RunOrder: 1
353 Configuration:
354 ProjectName: !Ref AWS::StackName
355 InputArtifacts:
356 - Name: !Ref AWS::StackName
357 ActionTypeId:
358 Version: 1
359 Category: Build
360 Owner: AWS
361 Provider: CodeBuild
362 OutputArtifacts:
363 - Name: !Sub ${AWS::StackName}-built
364 - Name: Deploy
365 Actions:
366 - Name: Deployer
367 RunOrder: 1
368 ActionTypeId:
369 Version: 1
370 Category: Invoke
371 Owner: AWS
372 Provider: Lambda
373 Configuration:
374 FunctionName: !ImportValue ops-lambdas-prod:EcsDeployerLambdaFunctionName
375 UserParameters:
376 !Sub
377 - |
378 {
379 "Service": "${AWS::StackName}",
380 "Family": "${AWS::StackName}",
381 "EcsService": {
382 "Name": "${EcsServiceName}",
383 "Arn": "${EcsServiceArn}",
384 "Cluster": "${EcsServiceCluster}"
385 }
386 }
387 - EcsServiceName: !GetAtt EcsService.Name
388 EcsServiceArn: !Ref EcsService
389 EcsServiceCluster: !GetAtt EcsCluster.Arn
390
391Outputs:
392 EcsClusterName:
393 Description: ECS Cluster Name.
394 Value: !Ref EcsCluster
395
396 ServiceName:
397 Description: ECS service name.
398 Value: !Ref EcsService