├── README.md ├── LICENSE └── amazon-efs-ecs.json /README.md: -------------------------------------------------------------------------------- 1 | # amazon-ecs-amazon-efs 2 | Using Amazon EFS to Persist Data from Amazon ECS Containers 3 | 4 | This repository includes the CloudFormation template referenced in this blog https://aws.amazon.com/blogs/compute/using-amazon-efs-to-persist-data-from-amazon-ecs-containers/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /amazon-efs-ecs.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Parameters": { 4 | "KeyName": { 5 | "Type": "AWS::EC2::KeyPair::KeyName", 6 | "Description": "Name of an existing EC2 KeyPair to enable SSH access to the ECS instances" 7 | }, 8 | "DesiredCapacity": { 9 | "Type": "Number", 10 | "Default": "1", 11 | "Description": "Number of instances to launch in your ECS cluster" 12 | }, 13 | "AsgMaxSize": { 14 | "Type": "Number", 15 | "Default": "1", 16 | "Description": "Maximum number of instances that can be launched in your ECS cluster" 17 | }, 18 | "InstanceType": { 19 | "Description": "The EC2 instance type", 20 | "Type": "String", 21 | "Default": "t2.micro", 22 | "AllowedValues": [ 23 | "t2.micro", 24 | "t2.small", 25 | "t2.medium", 26 | "m3.medium", 27 | "m3.large", 28 | "m3.xlarge", 29 | "m3.2xlarge", 30 | "c3.large", 31 | "c3.xlarge", 32 | "c3.2xlarge", 33 | "c3.4xlarge", 34 | "c3.8xlarge", 35 | "c4.large", 36 | "c4.xlarge", 37 | "c4.2xlarge", 38 | "c4.4xlarge", 39 | "c4.8xlarge", 40 | "r3.large", 41 | "r3.xlarge", 42 | "r3.2xlarge", 43 | "r3.4xlarge", 44 | "r3.8xlarge", 45 | "i2.xlarge", 46 | "i2.2xlarge", 47 | "i2.4xlarge", 48 | "i2.8xlarge", 49 | "d2.xlarge", 50 | "d2.2xlarge", 51 | "d2.4xlarge", 52 | "d2.8xlarge", 53 | "hi1.4xlarge", 54 | "hs1.8xlarge", 55 | "cr1.8xlarge", 56 | "cc2.8xlarge" 57 | ], 58 | "ConstraintDescription": "must be a valid EC2 instance type." 59 | }, 60 | "SSHLocation": { 61 | "Description": " The IP address range that can be used to SSH to the EC2 instances", 62 | "Type": "String", 63 | "MinLength": "9", 64 | "MaxLength": "18", 65 | "Default": "0.0.0.0/0", 66 | "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", 67 | "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." 68 | }, 69 | "EFSNameTag": { 70 | "Description": "The name of the EFS volume", 71 | "Type": "String", 72 | "MinLength": "1", 73 | "Default": "myEFSvolume" 74 | }, 75 | "CIDRVPC": { 76 | "Description": "Enter the CIDR Range for your VPC", 77 | "Type": "String", 78 | "MinLength": "9", 79 | "MaxLength": "18", 80 | "Default": "10.10.0.0/16", 81 | "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", 82 | "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." 83 | }, 84 | "CIDRSubnet1": { 85 | "Description": "Enter the CIDR Range for your VPC", 86 | "Type": "String", 87 | "MinLength": "9", 88 | "MaxLength": "18", 89 | "Default": "10.10.1.0/24", 90 | "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", 91 | "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." 92 | }, 93 | "CIDRSubnet2": { 94 | "Description": "Enter the CIDR Range for your VPC", 95 | "Type": "String", 96 | "MinLength": "9", 97 | "MaxLength": "18", 98 | "Default": "10.10.2.0/24", 99 | "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", 100 | "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." 101 | } 102 | }, 103 | "Mappings": { 104 | "AWSRegionToAMI": { 105 | "us-east-1": { 106 | "AMIID": "ami-a88a46c5" 107 | }, 108 | "us-west-2": { 109 | "AMIID": "ami-ae0acdce" 110 | }, 111 | "us-west-1": { 112 | "AMIID": "ami-34a7e354" 113 | }, 114 | "eu-west-1": { 115 | "AMIID": "ami-ccd942bf" 116 | }, 117 | "eu-central-1": { 118 | "AMIID": "ami-4a5eb625" 119 | }, 120 | "ap-southeast-1": { 121 | "AMIID": "ami-24c71547" 122 | }, 123 | "ap-northeast-1": { 124 | "AMIID": "ami-4aab5d2b" 125 | }, 126 | "ap-southeast-2": { 127 | "AMIID": "ami-0bf2da68" 128 | } 129 | } 130 | }, 131 | "Resources": { 132 | "VPC": { 133 | "Type": "AWS::EC2::VPC", 134 | "Properties": { 135 | "CidrBlock": { 136 | "Ref": "CIDRVPC" 137 | }, 138 | "EnableDnsSupport": true, 139 | "EnableDnsHostnames": true, 140 | "Tags": [ 141 | { 142 | "Key": "Name", 143 | "Value": "EFS-ECS Demo VPC" 144 | } 145 | ] 146 | } 147 | }, 148 | "Subnet1": { 149 | "Type": "AWS::EC2::Subnet", 150 | "Properties": { 151 | "VpcId": { 152 | "Ref": "VPC" 153 | }, 154 | "MapPublicIpOnLaunch": true, 155 | "CidrBlock": { 156 | "Ref": "CIDRSubnet1" 157 | }, 158 | "AvailabilityZone": { 159 | "Fn::Select": [ 160 | "0", 161 | { 162 | "Fn::GetAZs": { 163 | "Ref": "AWS::Region" 164 | } 165 | } 166 | ] 167 | } 168 | } 169 | }, 170 | "Subnet2": { 171 | "Type": "AWS::EC2::Subnet", 172 | "Properties": { 173 | "VpcId": { 174 | "Ref": "VPC" 175 | }, 176 | "MapPublicIpOnLaunch": true, 177 | "CidrBlock": { 178 | "Ref": "CIDRSubnet2" 179 | }, 180 | "AvailabilityZone": { 181 | "Fn::Select": [ 182 | "1", 183 | { 184 | "Fn::GetAZs": { 185 | "Ref": "AWS::Region" 186 | } 187 | } 188 | ] 189 | } 190 | } 191 | }, 192 | "InternetGateway": { 193 | "Type": "AWS::EC2::InternetGateway", 194 | "Properties": { 195 | "Tags": [ 196 | { 197 | "Key": "Application", 198 | "Value": { 199 | "Ref": "AWS::StackName" 200 | } 201 | }, 202 | { 203 | "Key": "Network", 204 | "Value": "Public" 205 | } 206 | ] 207 | } 208 | }, 209 | "GatewayToInternet": { 210 | "Type": "AWS::EC2::VPCGatewayAttachment", 211 | "Properties": { 212 | "VpcId": { 213 | "Ref": "VPC" 214 | }, 215 | "InternetGatewayId": { 216 | "Ref": "InternetGateway" 217 | } 218 | } 219 | }, 220 | "PublicRouteTable": { 221 | "DependsOn": [ 222 | "VPC" 223 | ], 224 | "Type": "AWS::EC2::RouteTable", 225 | "Properties": { 226 | "VpcId": { 227 | "Ref": "VPC" 228 | }, 229 | "Tags": [ 230 | { 231 | "Key": "Application", 232 | "Value": { 233 | "Ref": "AWS::StackName" 234 | } 235 | }, 236 | { 237 | "Key": "Network", 238 | "Value": "Public" 239 | } 240 | ] 241 | } 242 | }, 243 | "PublicRoute": { 244 | "DependsOn": [ 245 | "PublicRouteTable", 246 | "InternetGateway" 247 | ], 248 | "Type": "AWS::EC2::Route", 249 | "Properties": { 250 | "RouteTableId": { 251 | "Ref": "PublicRouteTable" 252 | }, 253 | "DestinationCidrBlock": "0.0.0.0/0", 254 | "GatewayId": { 255 | "Ref": "InternetGateway" 256 | } 257 | } 258 | }, 259 | "PublicSubnetRouteTableAssociation": { 260 | "DependsOn": [ 261 | "Subnet1", 262 | "PublicRouteTable" 263 | ], 264 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 265 | "Properties": { 266 | "SubnetId": { 267 | "Ref": "Subnet1" 268 | }, 269 | "RouteTableId": { 270 | "Ref": "PublicRouteTable" 271 | } 272 | } 273 | }, 274 | "PublicSubnetRouteTableAssociation2": { 275 | "DependsOn": [ 276 | "Subnet2", 277 | "PublicRouteTable" 278 | ], 279 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 280 | "Properties": { 281 | "SubnetId": { 282 | "Ref": "Subnet2" 283 | }, 284 | "RouteTableId": { 285 | "Ref": "PublicRouteTable" 286 | } 287 | } 288 | }, 289 | "InstanceSecurityGroup": { 290 | "Type": "AWS::EC2::SecurityGroup", 291 | "Properties": { 292 | "VpcId": { 293 | "Ref": "VPC" 294 | }, 295 | "GroupDescription": "Enable SSH access via port 22", 296 | "SecurityGroupIngress": [ 297 | { 298 | "IpProtocol": "tcp", 299 | "FromPort": "22", 300 | "ToPort": "22", 301 | "CidrIp": { 302 | "Ref": "SSHLocation" 303 | } 304 | }, 305 | { 306 | "IpProtocol": "tcp", 307 | "FromPort": "3306", 308 | "ToPort": "3306", 309 | "CidrIp": "0.0.0.0/0" 310 | } 311 | ] 312 | } 313 | }, 314 | "MountTargetSecurityGroup": { 315 | "Type": "AWS::EC2::SecurityGroup", 316 | "Properties": { 317 | "VpcId": { 318 | "Ref": "VPC" 319 | }, 320 | "GroupDescription": "Security group for mount target", 321 | "SecurityGroupIngress": [ 322 | { 323 | "IpProtocol": "tcp", 324 | "FromPort": "2049", 325 | "ToPort": "2049", 326 | "CidrIp": { 327 | "Ref": "CIDRVPC" 328 | } 329 | } 330 | ] 331 | } 332 | }, 333 | "FileSystem": { 334 | "Type": "AWS::EFS::FileSystem", 335 | "Properties": { 336 | "FileSystemTags": [ 337 | { 338 | "Key": "Name", 339 | "Value": { 340 | "Ref": "EFSNameTag" 341 | } 342 | } 343 | ] 344 | } 345 | }, 346 | "MountTarget": { 347 | "Type": "AWS::EFS::MountTarget", 348 | "Properties": { 349 | "FileSystemId": { 350 | "Ref": "FileSystem" 351 | }, 352 | "SubnetId": { 353 | "Ref": "Subnet1" 354 | }, 355 | "SecurityGroups": [ 356 | { 357 | "Ref": "MountTargetSecurityGroup" 358 | } 359 | ] 360 | } 361 | }, 362 | "MountTarget2": { 363 | "Type": "AWS::EFS::MountTarget", 364 | "Properties": { 365 | "FileSystemId": { 366 | "Ref": "FileSystem" 367 | }, 368 | "SubnetId": { 369 | "Ref": "Subnet2" 370 | }, 371 | "SecurityGroups": [ 372 | { 373 | "Ref": "MountTargetSecurityGroup" 374 | } 375 | ] 376 | } 377 | }, 378 | "EcsElasticLoadBalancer": { 379 | "Type": "AWS::ElasticLoadBalancing::LoadBalancer", 380 | "Properties": { 381 | "Subnets": [ 382 | { 383 | "Ref": "Subnet1" 384 | }, 385 | { 386 | "Ref": "Subnet2" 387 | } 388 | ], 389 | "SecurityGroups": [ 390 | { 391 | "Ref": "InstanceSecurityGroup" 392 | } 393 | ], 394 | "Listeners": [ 395 | { 396 | "LoadBalancerPort": "3306", 397 | "InstancePort": "3306", 398 | "Protocol": "TCP" 399 | } 400 | ], 401 | "HealthCheck": { 402 | "Target": "TCP:3306", 403 | "HealthyThreshold": "2", 404 | "UnhealthyThreshold": "2", 405 | "Interval": "30", 406 | "Timeout": "5" 407 | } 408 | } 409 | }, 410 | "ECSCluster": { 411 | "Type": "AWS::ECS::Cluster", 412 | "Metadata": { 413 | "AWS::CloudFormation::Designer": { 414 | "id": "14de148d-c95d-4f5f-bcf7-6e123d54844b" 415 | } 416 | } 417 | }, 418 | "service": { 419 | "Type": "AWS::ECS::Service", 420 | "DependsOn": [ 421 | "ECSAutoScalingGroup" 422 | ], 423 | "Properties": { 424 | "Cluster": { 425 | "Ref": "ECSCluster" 426 | }, 427 | "DesiredCount": "1", 428 | "LoadBalancers": [ 429 | { 430 | "ContainerName": "efsdemo", 431 | "ContainerPort": "3306", 432 | "LoadBalancerName": { 433 | "Ref": "EcsElasticLoadBalancer" 434 | } 435 | } 436 | ], 437 | "Role": { 438 | "Ref": "ECSServiceRole" 439 | }, 440 | "TaskDefinition": { 441 | "Ref": "taskdefinition" 442 | } 443 | } 444 | }, 445 | "taskdefinition": { 446 | "Type": "AWS::ECS::TaskDefinition", 447 | "Properties": { 448 | "ContainerDefinitions": [ 449 | { 450 | "Name": "efsdemo", 451 | "Cpu": "10", 452 | "Essential": "true", 453 | "Image": "mysql", 454 | "Memory": "500", 455 | "Environment": [ 456 | { 457 | "Name": "MYSQL_ROOT_PASSWORD", 458 | "Value": "password" 459 | } 460 | ], 461 | "MountPoints": [ 462 | { 463 | "ContainerPath": "/var/lib/mysql", 464 | "SourceVolume": "my-efs" 465 | } 466 | ], 467 | "PortMappings": [ 468 | { 469 | "HostPort": 3306, 470 | "ContainerPort": 3306 471 | } 472 | ] 473 | } 474 | ], 475 | "Volumes": [ 476 | { 477 | "Host": { 478 | "SourcePath": "/mnt/efs/mysql" 479 | }, 480 | "Name": "my-efs" 481 | } 482 | ] 483 | } 484 | }, 485 | "ECSAutoScalingGroup": { 486 | "Type": "AWS::AutoScaling::AutoScalingGroup", 487 | "DependsOn": [ 488 | "MountTarget", 489 | "MountTarget2" 490 | ], 491 | "Properties": { 492 | "AvailabilityZones": [ 493 | { 494 | "Fn::Select": [ 495 | "0", 496 | { 497 | "Fn::GetAZs": { 498 | "Ref": "AWS::Region" 499 | } 500 | } 501 | ] 502 | }, 503 | { 504 | "Fn::Select": [ 505 | "1", 506 | { 507 | "Fn::GetAZs": { 508 | "Ref": "AWS::Region" 509 | } 510 | } 511 | ] 512 | } 513 | ], 514 | "VPCZoneIdentifier": [ 515 | { 516 | "Ref": "Subnet1" 517 | }, 518 | { 519 | "Ref": "Subnet2" 520 | } 521 | ], 522 | "LaunchConfigurationName": { 523 | "Ref": "ContainerInstances" 524 | }, 525 | "MinSize": "1", 526 | "MaxSize": { 527 | "Ref": "AsgMaxSize" 528 | }, 529 | "DesiredCapacity": { 530 | "Ref": "DesiredCapacity" 531 | } 532 | }, 533 | "CreationPolicy": { 534 | "ResourceSignal": { 535 | "Timeout": "PT15M" 536 | } 537 | }, 538 | "UpdatePolicy": { 539 | "AutoScalingRollingUpdate": { 540 | "MinInstancesInService": "1", 541 | "MaxBatchSize": "1", 542 | "PauseTime": "PT15M", 543 | "WaitOnResourceSignals": "true" 544 | } 545 | } 546 | }, 547 | "ContainerInstances": { 548 | "Type": "AWS::AutoScaling::LaunchConfiguration", 549 | "Metadata": { 550 | "AWS::CloudFormation::Init": { 551 | "configSets": { 552 | "Install": [ 553 | "Install" 554 | ] 555 | }, 556 | "Install": { 557 | "packages": { 558 | "yum": { 559 | "nfs-utils": [], 560 | "python27": [] 561 | } 562 | }, 563 | "files": { 564 | "/etc/cfn/cfn-hup.conf": { 565 | "content": { 566 | "Fn::Join": [ 567 | "", 568 | [ 569 | "[main]\n", 570 | "stack=", 571 | { 572 | "Ref": "AWS::StackId" 573 | }, 574 | "\n", 575 | "region=", 576 | { 577 | "Ref": "AWS::Region" 578 | }, 579 | "\n" 580 | ] 581 | ] 582 | }, 583 | "mode": "000400", 584 | "owner": "root", 585 | "group": "root" 586 | }, 587 | "/etc/cfn/hooks.d/cfn-auto-reloader.conf": { 588 | "content": { 589 | "Fn::Join": [ 590 | "", 591 | [ 592 | "[cfn-auto-reloader-hook]\n", 593 | "triggers=post.update\n", 594 | "path=Resources.ContainerInstances.Metadata.AWS::CloudFormation::Init\n", 595 | "action=/opt/aws/bin/cfn-init -v ", 596 | " --stack ", 597 | { 598 | "Ref": "AWS::StackName" 599 | }, 600 | " --resource ContainerInstances ", 601 | " --region ", 602 | { 603 | "Ref": "AWS::Region" 604 | }, 605 | "\n", 606 | "runas=root\n" 607 | ] 608 | ] 609 | } 610 | } 611 | }, 612 | "services": { 613 | "sysvinit": { 614 | "cfn-hup": { 615 | "enabled": "true", 616 | "ensureRunning": "true", 617 | "files": [ 618 | "/etc/cfn/cfn-hup.conf", 619 | "/etc/cfn/hooks.d/cfn-auto-reloader.conf" 620 | ] 621 | } 622 | } 623 | } 624 | } 625 | }, 626 | "AWS::CloudFormation::Designer": { 627 | "id": "d86a3e51-ef74-4f2a-9591-8c547b7bee6c" 628 | } 629 | }, 630 | "Properties": { 631 | "ImageId": { 632 | "Fn::FindInMap": [ 633 | "AWSRegionToAMI", 634 | { 635 | "Ref": "AWS::Region" 636 | }, 637 | "AMIID" 638 | ] 639 | }, 640 | "InstanceType": { 641 | "Ref": "InstanceType" 642 | }, 643 | "IamInstanceProfile": { 644 | "Ref": "EC2InstanceProfile" 645 | }, 646 | "KeyName": { 647 | "Ref": "KeyName" 648 | }, 649 | "AssociatePublicIpAddress": true, 650 | "SecurityGroups": [ 651 | { 652 | "Ref": "InstanceSecurityGroup" 653 | } 654 | ], 655 | "UserData": { 656 | "Fn::Base64": { 657 | "Fn::Join": [ 658 | "", 659 | [ 660 | "#!/bin/bash\n", 661 | "echo ECS_CLUSTER=", 662 | { 663 | "Ref": "ECSCluster" 664 | }, 665 | " >> /etc/ecs/ecs.config\n", 666 | "yum install -y aws-cfn-bootstrap\n", 667 | "/opt/aws/bin/cfn-init", 668 | " --stack ", 669 | { 670 | "Ref": "AWS::StackName" 671 | }, 672 | " ", 673 | " --resource ContainerInstances ", 674 | " --configsets Install ", 675 | " --region ", 676 | { 677 | "Ref": "AWS::Region" 678 | }, 679 | "\n", 680 | "PATH=$PATH:/usr/local/bin\n", 681 | "yum update\n", 682 | "EC2_AVAIL_ZONE=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone`\n", 683 | "EC2_REGION=", 684 | { 685 | "Ref": "AWS::Region" 686 | }, 687 | "\n", 688 | "mkdir /mnt/efs\n", 689 | "EFS_FILE_SYSTEM_ID=", 690 | { 691 | "Ref": "FileSystem" 692 | }, 693 | "\n", 694 | "DIR_SRC=$EC2_AVAIL_ZONE.$EFS_FILE_SYSTEM_ID.efs.$EC2_REGION.amazonaws.com\n", 695 | "DIR_TGT=/mnt/efs\n", 696 | "touch /home/ec2-user/echo.res\n", 697 | "echo $EFS_FILE_SYSTEM_ID >> /home/ec2-user/echo.res\n", 698 | "echo $EC2_AVAIL_ZONE >> /home/ec2-user/echo.res\n", 699 | "echo $EC2_REGION >> /home/ec2-user/echo.res\n", 700 | "echo $DIR_SRC >> /home/ec2-user/echo.res\n", 701 | "echo $DIR_TGT >> /home/ec2-user/echo.res\n", 702 | "mount -t nfs4 $DIR_SRC:/ $DIR_TGT >> /home/ec2-user/echo.res\n", 703 | "cp -p /etc/fstab /etc/fstab.back-$(date +%F)\n", 704 | "echo -e \"$DIR_SRC:/ \t\t $DIR_TGT \t\t nfs \t\t defaults \t\t 0 \t\t 0\" | tee -a /etc/fstab\n", 705 | "docker ps\n", 706 | "service docker stop\n", 707 | "service docker start\n", 708 | "/opt/aws/bin/cfn-signal -e $? ", 709 | " --stack ", 710 | { 711 | "Ref": "AWS::StackName" 712 | }, 713 | " --resource ECSAutoScalingGroup ", 714 | " --region ", 715 | { 716 | "Ref": "AWS::Region" 717 | }, 718 | "\n" 719 | ] 720 | ] 721 | } 722 | } 723 | } 724 | }, 725 | "ECSServiceRole": { 726 | "Type": "AWS::IAM::Role", 727 | "Properties": { 728 | "AssumeRolePolicyDocument": { 729 | "Statement": [ 730 | { 731 | "Effect": "Allow", 732 | "Principal": { 733 | "Service": [ 734 | "ecs.amazonaws.com" 735 | ] 736 | }, 737 | "Action": [ 738 | "sts:AssumeRole" 739 | ] 740 | } 741 | ] 742 | }, 743 | "Path": "/", 744 | "Policies": [ 745 | { 746 | "PolicyName": "ecs-service", 747 | "PolicyDocument": { 748 | "Statement": [ 749 | { 750 | "Effect": "Allow", 751 | "Action": [ 752 | "elasticloadbalancing:Describe*", 753 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 754 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 755 | "ec2:Describe*", 756 | "ec2:AuthorizeSecurityGroupIngress" 757 | ], 758 | "Resource": "*" 759 | } 760 | ] 761 | } 762 | } 763 | ] 764 | } 765 | }, 766 | "EC2Role": { 767 | "Type": "AWS::IAM::Role", 768 | "Properties": { 769 | "AssumeRolePolicyDocument": { 770 | "Statement": [ 771 | { 772 | "Effect": "Allow", 773 | "Principal": { 774 | "Service": [ 775 | "ec2.amazonaws.com" 776 | ] 777 | }, 778 | "Action": [ 779 | "sts:AssumeRole" 780 | ] 781 | } 782 | ] 783 | }, 784 | "Path": "/", 785 | "ManagedPolicyArns": [ 786 | "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" 787 | ] 788 | } 789 | }, 790 | "EC2InstanceProfile": { 791 | "Type": "AWS::IAM::InstanceProfile", 792 | "Properties": { 793 | "Path": "/", 794 | "Roles": [ 795 | { 796 | "Ref": "EC2Role" 797 | } 798 | ] 799 | } 800 | } 801 | }, 802 | "Outputs": { 803 | "ecscluster": { 804 | "Value": { 805 | "Ref": "ECSCluster" 806 | } 807 | }, 808 | "ecsservice": { 809 | "Value": { 810 | "Ref": "service" 811 | } 812 | }, 813 | "MountTargetID": { 814 | "Description": "Mount target ID", 815 | "Value": { 816 | "Ref": "MountTarget" 817 | } 818 | }, 819 | "FileSystemID": { 820 | "Description": "File system ID", 821 | "Value": { 822 | "Ref": "FileSystem" 823 | } 824 | } 825 | } 826 | } 827 | 828 | --------------------------------------------------------------------------------