├── .github └── PULL_REQUEST_TEMPLATE.md ├── LICENSE ├── NOTICE ├── README.md ├── images ├── architecture-overview.graffle │ ├── data.plist │ ├── image12.pdf │ ├── image14.pdf │ ├── image6.pdf │ ├── image8.pdf │ └── image9.pdf ├── architecture-overview.png ├── cloudformation-launch-stack.png └── stack-outputs.png ├── infrastructure ├── ecs-cluster.yaml ├── ecs-windows-cluster.yaml ├── load-balancers.yaml ├── security-groups.yaml └── vpc.yaml ├── master-windows.yaml ├── master.yaml └── services ├── product-service ├── service.yaml └── src │ ├── Dockerfile │ ├── Makefile │ ├── main.go │ └── users └── website-service ├── service.yaml └── src ├── Dockerfile ├── Makefile ├── main.go └── users /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /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 2016 Amazon Web Services, Inc. 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 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ecs-refarch-cloudformation 2 | Copyright 2011-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploying Microservices with Amazon ECS, AWS CloudFormation, and an Application Load Balancer 2 | 3 | This reference architecture provides a set of YAML templates for creating an [Amazon Elastic Container Service (Amazon ECS)](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) cluster that is comprised of Windows container instances using the new ECS optimized AMI for Windows. The stack is launched using [AWS CloudFormation](https://aws.amazon.com/cloudformation/). 4 | 5 | You can only launch this CloudFormation stack in the US West (Oregon) Region in your account: 6 | 7 | [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=Production&templateURL=https://s3-us-west-2.amazonaws.com/ecs-refarch-windows/master-windows.yaml) 8 | 9 | ## Overview 10 | 11 | ![infrastructure-overview](images/architecture-overview.png) 12 | 13 | The repository consists of a set of nested templates that deploy the following: 14 | 15 | - A tiered [VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Introduction.html) with public and private subnets, spanning an AWS region. 16 | - A highly available ECS cluster deployed across two [Availability Zones](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) in an [Auto Scaling](https://aws.amazon.com/autoscaling/) group. 17 | - A pair of [NAT gateways](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-nat-gateway.html) (one in each zone) to handle outbound traffic. 18 | 19 | ## Why use AWS CloudFormation with Amazon ECS? 20 | 21 | Using CloudFormation to deploy and manage services with ECS has a number of nice benefits over more traditional methods ([AWS CLI](https://aws.amazon.com/cli), scripting, etc.). 22 | 23 | #### Infrastructure-as-Code 24 | 25 | A template can be used repeatedly to create identical copies of the same stack (or to use as a foundation to start a new stack). Templates are simple YAML- or JSON-formatted text files that can be placed under your normal source control mechanisms, stored in private or public locations such as Amazon S3, and exchanged via email. With CloudFormation, you can see exactly which AWS resources make up a stack. You retain full control and have the ability to modify any of the AWS resources created as part of a stack. 26 | 27 | #### Self-documenting 28 | 29 | Fed up with outdated documentation on your infrastructure or environments? Still keep manual documentation of IP ranges, security group rules, etc.? 30 | 31 | With CloudFormation, your template becomes your documentation. Want to see exactly what you have deployed? Just look at your template. If you keep it in source control, then you can also look back at exactly which changes were made and by whom. 32 | 33 | #### Intelligent updating & rollback 34 | 35 | CloudFormation not only handles the initial deployment of your infrastructure and environments, but it can also manage the whole lifecycle, including future updates. During updates, you have fine-grained control and visibility over how changes are applied, using functionality such as [change sets](https://aws.amazon.com/blogs/aws/new-change-sets-for-aws-cloudformation/), [rolling update policies](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html) and [stack policies](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html). 36 | 37 | ## Template details 38 | 39 | The templates below are included in this repository and reference architecture: 40 | 41 | | Template | Description | 42 | | --- | --- | 43 | | [master-windows.yaml](master-windows.yaml) | This is the master template - deploy it to CloudFormation and it includes all of the others automatically. | 44 | | [infrastructure/vpc.yaml](infrastructure/vpc.yaml) | This template deploys a VPC with a pair of public and private subnets spread across two Availability Zones. It deploys an [Internet gateway](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Internet_Gateway.html), with a default route on the public subnets. It deploys a pair of NAT gateways (one in each zone), and default routes for them in the private subnets. | 45 | | [infrastructure/security-groups.yaml](infrastructure/security-groups.yaml) | This template contains the [security groups](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_SecurityGroups.html) required by the entire stack. They are created in a separate nested template, so that they can be referenced by all of the other nested templates. | 46 | | [infrastructure/ecs-windows-cluster.yaml](infrastructure/ecs-windows-cluster.yaml) | This template deploys an ECS cluster to the private subnets using an Auto Scaling group. | 47 | 48 | After the CloudFormation templates have been deployed, you're ready to start running Windows containers on ECS! 49 | 50 | ## How do I...? 51 | 52 | ### Get started and deploy this into my AWS account 53 | 54 | You can launch this CloudFormation stack in the US West (Oregon) Region in your account: 55 | 56 | [![cloudformation-launch-button](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=Production&templateURL=https://s3-us-west-2.amazonaws.com/ecs-refarch-windows/master-windows.yaml) 57 | 58 | ### Customize the templates 59 | 60 | 1. [Fork](https://github.com/awslabs/ecs-refarch-cloudformation#fork-destination-box) this GitHub repository. TODO: UPDATE HREF 61 | 2. Clone the forked GitHub repository to your local machine. 62 | 3. Modify the templates. 63 | 4. Upload them to an Amazon S3 bucket of your choice. 64 | 5. Either create a new CloudFormation stack by deploying the master-windows.yaml template, or update your existing stack with your version of the templates. 65 | 66 | ### Create a new ECS service 67 | 68 | 1. Push your container to a registry somewhere (e.g., [Amazon ECR](https://aws.amazon.com/ecr/)). 69 | 2. Copy one of the existing service templates in [services/*](/services). TODO: Add template for creating a Windows service. 70 | 3. Update the `ContainerName` and `Image` parameters to point to your container image instead of the example container. 71 | 4. Increment the `ListenerRule` priority number (no two services can have the same priority number - this is used to order the ALB path based routing rules). 72 | 5. Copy one of the existing service definitions in [master-windows.yaml](master-windows.yaml) and point it at your new service template. Specify the HTTP `Path` at which you want the service exposed. 73 | 6. Deploy the templates as a new stack, or as an update to an existing stack. 74 | 75 | ### Setup centralized container logging 76 | 77 | By default, the containers in your ECS tasks/services are already configured to send log information to CloudWatch Logs and retain them for 365 days. Within each service's template (in [services/*](services/)), a LogGroup is created that is named after the CloudFormation stack. All container logs are sent to that CloudWatch Logs log group. 78 | 79 | You can view the logs by looking in your [CloudWatch Logs console](https://console.aws.amazon.com/cloudwatch/home?#logs:) (make sure you are in the correct AWS region). 80 | 81 | ECS also supports other logging drivers, including `syslog`, `journald`, `splunk`, `gelf`, `json-file`, and `fluentd`. To configure those instead, adjust the service template to use the alternative `LogDriver`. You can also adjust the log retention period from the default 365 days by tweaking the `RetentionInDays` parameter. 82 | 83 | For more information, see the [LogConfiguration](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html) API operation. 84 | 85 | ### Change the ECS host instance type 86 | 87 | This is specified in the [master-windows.yaml](master-windows.yaml) template. 88 | 89 | By default, [t2.large](https://aws.amazon.com/ec2/instance-types/) instances are used, but you can change this by modifying the following section: 90 | 91 | ``` 92 | ECS: 93 | Type: AWS::CloudFormation::Stack 94 | Properties: 95 | TemplateURL: ... 96 | Parameters: 97 | ... 98 | InstanceType: t2.large 99 | InstanceCount: 4 100 | ... 101 | ``` 102 | 103 | ### Adjust the Auto Scaling parameters for ECS hosts and services 104 | 105 | The Auto Scaling group scaling policy provided by default launches and maintains a cluster of 4 ECS hosts distributed across two Availability Zones (min: 4, max: 4, desired: 4). 106 | 107 | It is ***not*** set up to scale automatically based on any policies (CPU, network, time of day, etc.). 108 | 109 | If you would like to configure policy or time-based automatic scaling, you can add the [ScalingPolicy](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-policy.html) property to the AutoScalingGroup deployed in [infrastructure/ecs-windows-cluster.yaml](infrastructure/ecs-windows-cluster.yaml). 110 | 111 | As well as configuring Auto Scaling for the ECS hosts (your pool of compute), you can also configure scaling each individual ECS service. This can be useful if you want to run more instances of each container/task depending on the load or time of day (or a custom CloudWatch metric). To do this, you need to create [AWS::ApplicationAutoScaling::ScalingPolicy](http://docs.aws.amazon.com/pt_br/AWSCloudFormation/latest/UserGuide/aws-resource-applicationautoscaling-scalingpolicy.html) within your service template. 112 | 113 | ### Deploy multiple environments (e.g., dev, test, pre-production) 114 | 115 | Deploy another CloudFormation stack from the same set of templates to create a new environment. The stack name provided when deploying the stack is prefixed to all taggable resources (e.g., EC2 instances, VPCs, etc.) so you can distinguish the different environment resources in the AWS Management Console. 116 | 117 | ### Change the VPC or subnet IP ranges 118 | 119 | This set of templates deploys the following network design: 120 | 121 | | Item | CIDR Range | Usable IPs | Description | 122 | | --- | --- | --- | --- | 123 | | VPC | 10.180.0.0/16 | 65,536 | The whole range used for the VPC and all subnets | 124 | | Public Subnet | 10.180.8.0/21 | 2,041 | The public subnet in the first Availability Zone | 125 | | Public Subnet | 10.180.16.0/21 | 2,041 | The public subnet in the second Availability Zone | 126 | | Private Subnet | 10.180.24.0/21 | 2,041 | The private subnet in the first Availability Zone | 127 | | Private Subnet | 10.180.32.0/21 | 2,041 | The private subnet in the second Availability Zone | 128 | 129 | You can adjust the CIDR ranges used in this section of the [master-windows.yaml](master-windows.yaml) template: 130 | 131 | ``` 132 | VPC: 133 | Type: AWS::CloudFormation::Stack 134 | Properties: 135 | TemplateURL: !Sub ${TemplateLocation}/infrastructure/vpc.yaml 136 | Parameters: 137 | EnvironmentName: !Ref AWS::StackName 138 | VpcCIDR: 10.180.0.0/16 139 | PublicSubnet1CIDR: 10.180.8.0/21 140 | PublicSubnet2CIDR: 10.180.16.0/21 141 | PrivateSubnet1CIDR: 10.180.24.0/21 142 | PrivateSubnet2CIDR: 10.180.32.0/21 143 | ``` 144 | 145 | ### Update an ECS service to a new Docker image version 146 | 147 | ECS has the ability to perform rolling upgrades to your ECS services to minimize downtime during deployments. For more information, see [Updating a Service](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/update-service.html). 148 | 149 | To update one of your services to a new version, adjust the `Image` parameter in the service template (in [services/*](services/) to point to the new version of your container image. For example, if `1.0.0` was currently deployed and you wanted to update to `1.1.0`, you could update it as follows: 150 | 151 | ``` 152 | TaskDefinition: 153 | Type: AWS::ECS::TaskDefinition 154 | Properties: 155 | ContainerDefinitions: 156 | - Name: your-container 157 | Image: registry.example.com/your-container:1.1.0 158 | ``` 159 | 160 | After you've updated the template, update the deployed CloudFormation stack; CloudFormation and ECS handle the rest. 161 | 162 | To adjust the rollout parameters (min/max number of tasks/containers to keep in service at any time), you need to configure `DeploymentConfiguration` for the ECS service. 163 | 164 | For example: 165 | 166 | ``` 167 | Service: 168 | Type: AWS::ECS::Service 169 | Properties: 170 | ... 171 | DesiredCount: 4 172 | DeploymentConfiguration: 173 | MaximumPercent: 200 174 | MinimumHealthyPercent: 50 175 | ``` 176 | 177 | ### Add a new item to this list 178 | 179 | If you found yourself wishing this set of frequently asked questions had an answer for a particular problem, please [submit a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). The chances are that others will also benefit from having the answer listed here. 180 | 181 | ## Contributing 182 | 183 | Please [create a new GitHub issue](https://github.com/awslabs/ecs-refarch-cloudformation/issues/new) for any feature requests, bugs, or documentation improvements. 184 | 185 | Where possible, please also [submit a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) for the change. 186 | 187 | ## License 188 | 189 | Copyright 2011-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 192 | 193 | [http://aws.amazon.com/apache2.0/](http://aws.amazon.com/apache2.0/) 194 | 195 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 196 | 197 | -------------------------------------------------------------------------------- /images/architecture-overview.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.graffle/data.plist -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.graffle/image12.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image14.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.graffle/image14.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.graffle/image6.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.graffle/image8.pdf -------------------------------------------------------------------------------- /images/architecture-overview.graffle/image9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.graffle/image9.pdf -------------------------------------------------------------------------------- /images/architecture-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/architecture-overview.png -------------------------------------------------------------------------------- /images/cloudformation-launch-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/cloudformation-launch-stack.png -------------------------------------------------------------------------------- /images/stack-outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ecs-refarch-cloudformation-windows/b041592d2c6ec7f8bfac955bf215941bfa01e5a5/images/stack-outputs.png -------------------------------------------------------------------------------- /infrastructure/ecs-cluster.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys an ECS cluster to the provided VPC and subnets 3 | using an Auto Scaling Group 4 | 5 | Parameters: 6 | 7 | EnvironmentName: 8 | Description: An environment name that will be prefixed to resource names 9 | Type: String 10 | 11 | InstanceType: 12 | Description: Which instance type should we use to build the ECS cluster? 13 | Type: String 14 | Default: c4.large 15 | 16 | ClusterSize: 17 | Description: How many ECS hosts do you want to initially deploy? 18 | Type: Number 19 | Default: 4 20 | 21 | VPC: 22 | Description: Choose which VPC this ECS cluster should be deployed to 23 | Type: AWS::EC2::VPC::Id 24 | 25 | Subnets: 26 | Description: Choose which subnets this ECS cluster should be deployed to 27 | Type: List 28 | 29 | SecurityGroup: 30 | Description: Select the Security Group to use for the ECS cluster hosts 31 | Type: AWS::EC2::SecurityGroup::Id 32 | 33 | Mappings: 34 | 35 | # These are the latest ECS optimized AMIs as of August 2017: 36 | # 37 | # amzn-ami-2017.03.f-amazon-ecs-optimized 38 | # ECS agent: 1.14.4 39 | # Docker: 17.03.2-ce 40 | # ecs-init: 1.14.4-1 41 | # 42 | # You can find the latest available on this page of our documentation: 43 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html 44 | # (note the AMI identifier is region specific) 45 | 46 | AWSRegionToAMI: 47 | us-east-2: 48 | AMI: ami-1c002379 49 | us-east-1: 50 | AMI: ami-9eb4b1e5 51 | us-west-2: 52 | AMI: ami-1d668865 53 | us-west-1: 54 | AMI: ami-4a2c192a 55 | eu-west-2: 56 | AMI: ami-cb1101af 57 | eu-west-1: 58 | AMI: ami-8fcc32f6 59 | eu-central-1: 60 | AMI: ami-0460cb6b 61 | ap-northeast-1: 62 | AMI: ami-b743bed1 63 | ap-southeast-2: 64 | AMI: ami-c1a6bda2 65 | ap-southeast-1: 66 | AMI: ami-9d1f7efe 67 | ca-central-1: 68 | AMI: ami-b677c9d2 69 | 70 | Resources: 71 | 72 | ECSCluster: 73 | Type: AWS::ECS::Cluster 74 | Properties: 75 | ClusterName: !Ref EnvironmentName 76 | 77 | ECSAutoScalingGroup: 78 | Type: AWS::AutoScaling::AutoScalingGroup 79 | Properties: 80 | VPCZoneIdentifier: !Ref Subnets 81 | LaunchConfigurationName: !Ref ECSLaunchConfiguration 82 | MinSize: !Ref ClusterSize 83 | MaxSize: !Ref ClusterSize 84 | DesiredCapacity: !Ref ClusterSize 85 | Tags: 86 | - Key: Name 87 | Value: !Sub ${EnvironmentName} ECS host 88 | PropagateAtLaunch: true 89 | CreationPolicy: 90 | ResourceSignal: 91 | Timeout: PT15M 92 | UpdatePolicy: 93 | AutoScalingRollingUpdate: 94 | MinInstancesInService: 1 95 | MaxBatchSize: 1 96 | PauseTime: PT15M 97 | SuspendProcesses: 98 | - HealthCheck 99 | - ReplaceUnhealthy 100 | - AZRebalance 101 | - AlarmNotification 102 | - ScheduledActions 103 | WaitOnResourceSignals: true 104 | 105 | ECSLaunchConfiguration: 106 | Type: AWS::AutoScaling::LaunchConfiguration 107 | Properties: 108 | ImageId: !FindInMap [AWSRegionToAMI, !Ref "AWS::Region", AMI] 109 | InstanceType: !Ref InstanceType 110 | SecurityGroups: 111 | - !Ref SecurityGroup 112 | IamInstanceProfile: !Ref ECSInstanceProfile 113 | UserData: 114 | "Fn::Base64": !Sub | 115 | #!/bin/bash 116 | yum install -y aws-cfn-bootstrap 117 | /opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSLaunchConfiguration 118 | /opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSAutoScalingGroup 119 | 120 | Metadata: 121 | AWS::CloudFormation::Init: 122 | config: 123 | commands: 124 | 01_add_instance_to_cluster: 125 | command: !Sub echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config 126 | files: 127 | "/etc/cfn/cfn-hup.conf": 128 | mode: 000400 129 | owner: root 130 | group: root 131 | content: !Sub | 132 | [main] 133 | stack=${AWS::StackId} 134 | region=${AWS::Region} 135 | 136 | "/etc/cfn/hooks.d/cfn-auto-reloader.conf": 137 | content: !Sub | 138 | [cfn-auto-reloader-hook] 139 | triggers=post.update 140 | path=Resources.ECSLaunchConfiguration.Metadata.AWS::CloudFormation::Init 141 | action=/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSLaunchConfiguration 142 | 143 | services: 144 | sysvinit: 145 | cfn-hup: 146 | enabled: true 147 | ensureRunning: true 148 | files: 149 | - /etc/cfn/cfn-hup.conf 150 | - /etc/cfn/hooks.d/cfn-auto-reloader.conf 151 | 152 | # This IAM Role is attached to all of the ECS hosts. It is based on the default role 153 | # published here: 154 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html 155 | # 156 | # You can add other IAM policy statements here to allow access from your ECS hosts 157 | # to other AWS services. Please note that this role will be used by ALL containers 158 | # running on the ECS host. 159 | 160 | ECSRole: 161 | Type: AWS::IAM::Role 162 | Properties: 163 | Path: / 164 | RoleName: !Sub ${EnvironmentName}-ECSRole-${AWS::Region} 165 | AssumeRolePolicyDocument: | 166 | { 167 | "Statement": [{ 168 | "Action": "sts:AssumeRole", 169 | "Effect": "Allow", 170 | "Principal": { 171 | "Service": "ec2.amazonaws.com" 172 | } 173 | }] 174 | } 175 | Policies: 176 | - PolicyName: ecs-service 177 | PolicyDocument: | 178 | { 179 | "Statement": [{ 180 | "Effect": "Allow", 181 | "Action": [ 182 | "ecs:CreateCluster", 183 | "ecs:DeregisterContainerInstance", 184 | "ecs:DiscoverPollEndpoint", 185 | "ecs:Poll", 186 | "ecs:RegisterContainerInstance", 187 | "ecs:StartTelemetrySession", 188 | "ecs:Submit*", 189 | "logs:CreateLogStream", 190 | "logs:PutLogEvents", 191 | "ecr:BatchCheckLayerAvailability", 192 | "ecr:BatchGetImage", 193 | "ecr:GetDownloadUrlForLayer", 194 | "ecr:GetAuthorizationToken" 195 | ], 196 | "Resource": "*" 197 | }] 198 | } 199 | 200 | ECSInstanceProfile: 201 | Type: AWS::IAM::InstanceProfile 202 | Properties: 203 | Path: / 204 | Roles: 205 | - !Ref ECSRole 206 | 207 | Outputs: 208 | 209 | Cluster: 210 | Description: A reference to the ECS cluster 211 | Value: !Ref ECSCluster 212 | 213 | -------------------------------------------------------------------------------- /infrastructure/ecs-windows-cluster.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys an ECS cluster to the provided VPC and subnets 3 | using an Auto Scaling Group 4 | 5 | Parameters: 6 | 7 | EnvironmentName: 8 | Description: An environment name that will be prefixed to resource names 9 | Type: String 10 | 11 | InstanceType: 12 | Description: Which instance type should we use to build the ECS cluster? 13 | Type: String 14 | Default: c4.large 15 | 16 | ClusterSize: 17 | Description: How many ECS hosts do you want to initially deploy? 18 | Type: Number 19 | Default: 4 20 | 21 | VPC: 22 | Description: Choose which VPC this ECS cluster should be deployed to 23 | Type: AWS::EC2::VPC::Id 24 | 25 | Subnets: 26 | Description: Choose which subnets this ECS cluster should be deployed to 27 | Type: List 28 | 29 | SecurityGroup: 30 | Description: Select the Security Group to use for the ECS cluster hosts 31 | Type: AWS::EC2::SecurityGroup::Id 32 | 33 | Mappings: 34 | 35 | # These are the latest ECS optimized AMIs as of [DATE]: 36 | # 37 | # Windows_Server-2016-English-Full-ECS_Optimized-2017.11.24 38 | # ECS agent: 39 | # Docker: 17.06.2-ee-5 40 | # ecs-init: 41 | # 42 | # You can find the latest available on this page of our documentation: 43 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html 44 | # (note the AMI identifier is region specific) 45 | 46 | AWSRegionToAMI: 47 | us-west-2: 48 | AMI: ami-a68e39de 49 | us-west-1: 50 | AMI: ami-e5686b85 51 | us-east-2: 52 | AMI: ami-6b9db60e 53 | us-east-1: 54 | AMI: ami-ec346f96 55 | eu-west-2: 56 | AMI: ami-29574c4d 57 | eu-west-1: 58 | AMI: ami-48eb7931 59 | eu-central-1: 60 | AMI: ami-ee30a281 61 | ca-central-1: 62 | AMI: ami-1e91147a 63 | ap-southeast-2: 64 | AMI: ami-252fd247 65 | ap-southeast-1: 66 | AMI: ami-a6fa89da 67 | ap-northeast-2: 68 | AMI: ami-0e63c360 69 | ap-northeast-1: 70 | AMI: ami-bf3ca4d9 71 | 72 | Resources: 73 | 74 | ECSCluster: 75 | Type: AWS::ECS::Cluster 76 | Properties: 77 | ClusterName: !Ref EnvironmentName 78 | 79 | ECSAutoScalingGroup: 80 | Type: AWS::AutoScaling::AutoScalingGroup 81 | Properties: 82 | VPCZoneIdentifier: !Ref Subnets 83 | LaunchConfigurationName: !Ref ECSLaunchConfiguration 84 | MinSize: !Ref ClusterSize 85 | MaxSize: !Ref ClusterSize 86 | DesiredCapacity: !Ref ClusterSize 87 | Tags: 88 | - Key: Name 89 | Value: !Sub ${EnvironmentName} ECS host 90 | PropagateAtLaunch: 'true' 91 | CreationPolicy: 92 | ResourceSignal: 93 | Timeout: PT15M 94 | UpdatePolicy: 95 | AutoScalingRollingUpdate: 96 | MinInstancesInService: '1' 97 | MaxBatchSize: '1' 98 | PauseTime: PT20M 99 | WaitOnResourceSignals: 'true' 100 | 101 | ECSLaunchConfiguration: 102 | Type: AWS::AutoScaling::LaunchConfiguration 103 | Properties: 104 | ImageId: !FindInMap [AWSRegionToAMI, !Ref "AWS::Region", AMI] 105 | InstanceType: !Ref InstanceType 106 | SecurityGroups: 107 | - !Ref SecurityGroup 108 | IamInstanceProfile: !Ref ECSInstanceProfile 109 | #Need to someone to check this section. Was unable to find a good example in YAML for bootstrapping an instance. 110 | UserData: !Base64 111 | Fn::Join: 112 | - '' 113 | - - ' 133 | Metadata: 134 | AWS::CloudFormation::Init: 135 | config: 136 | commands: 137 | 01_import_powershell_module: 138 | command: !Sub powershell.exe -Command Import-Module ECSTools 139 | 02_add_instance_to_cluster: 140 | command: !Sub powershell.exe -Command Initialize-ECSAgent -Cluster ${ECSCluster} -EnableTaskIAMRole 141 | files: 142 | c:\cfn\cfn-hup.conf: 143 | content: !Join ['', ['[main] 144 | ', stack=, !Ref 'AWS::StackId', ' 145 | ', region=, !Ref 'AWS::Region', ' 146 | ']] 147 | c:\cfn\hooks.d\cfn-auto-reloader.conf: 148 | content: !Join ['', ['[cfn-auto-reloader-hook] 149 | ', 'triggers=post.update 150 | ', 'path=Resources.ECSLaunchConfiguration.Metadata.AWS::CloudFormation::Init 151 | ', 'action=cfn-init.exe -v -s ', !Ref 'AWS::StackId', ' -r ECSLaunchConfiguration', 152 | ' --region ', !Ref 'AWS::Region', ' 153 | ']] 154 | services: 155 | windows: 156 | cfn-hup: 157 | enabled: 'true' 158 | ensureRunning: 'true' 159 | files: 160 | - c:\cfn\cfn-hup.conf 161 | - c:\etc\cfn\hooks.d\cfn-auto-reloader.conf 162 | 163 | # This IAM Role is attached to all of the ECS hosts. It is based on the default role 164 | # published here: 165 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html 166 | # 167 | # You can add other IAM policy statements here to allow access from your ECS hosts 168 | # to other AWS services. Please note that this role will be used by ALL containers 169 | # running on the ECS host. 170 | 171 | ECSRole: 172 | Type: AWS::IAM::Role 173 | Properties: 174 | Path: / 175 | RoleName: !Sub ${EnvironmentName}-ECSRole-${AWS::Region} 176 | AssumeRolePolicyDocument: | 177 | { 178 | "Statement": [{ 179 | "Action": "sts:AssumeRole", 180 | "Effect": "Allow", 181 | "Principal": { 182 | "Service": "ec2.amazonaws.com" 183 | } 184 | }] 185 | } 186 | Policies: 187 | - PolicyName: ecs-service 188 | PolicyDocument: | 189 | { 190 | "Statement": [{ 191 | "Effect": "Allow", 192 | "Action": [ 193 | "ecs:CreateCluster", 194 | "ecs:DeregisterContainerInstance", 195 | "ecs:DiscoverPollEndpoint", 196 | "ecs:Poll", 197 | "ecs:RegisterContainerInstance", 198 | "ecs:StartTelemetrySession", 199 | "ecs:Submit*", 200 | "logs:CreateLogStream", 201 | "logs:PutLogEvents", 202 | "ecr:BatchCheckLayerAvailability", 203 | "ecr:BatchGetImage", 204 | "ecr:GetDownloadUrlForLayer", 205 | "ecr:GetAuthorizationToken" 206 | ], 207 | "Resource": "*" 208 | }] 209 | } 210 | 211 | ECSInstanceProfile: 212 | Type: AWS::IAM::InstanceProfile 213 | Properties: 214 | Path: / 215 | Roles: 216 | - !Ref ECSRole 217 | 218 | Outputs: 219 | 220 | Cluster: 221 | Description: A reference to the ECS cluster 222 | Value: !Ref ECSCluster 223 | 224 | -------------------------------------------------------------------------------- /infrastructure/load-balancers.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys an Application Load Balancer that exposes our various ECS services. 3 | We create them it a seperate nested template, so it can be referenced by all of the other nested templates. 4 | 5 | Parameters: 6 | 7 | EnvironmentName: 8 | Description: An environment name that will be prefixed to resource names 9 | Type: String 10 | 11 | VPC: 12 | Type: AWS::EC2::VPC::Id 13 | Description: Choose which VPC the Application Load Balancer should be deployed to 14 | 15 | Subnets: 16 | Description: Choose which subnets the Application Load Balancer should be deployed to 17 | Type: List 18 | 19 | SecurityGroup: 20 | Description: Select the Security Group to apply to the Application Load Balancer 21 | Type: AWS::EC2::SecurityGroup::Id 22 | 23 | Resources: 24 | 25 | LoadBalancer: 26 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 27 | Properties: 28 | Name: !Ref EnvironmentName 29 | Subnets: !Ref Subnets 30 | SecurityGroups: 31 | - !Ref SecurityGroup 32 | Tags: 33 | - Key: Name 34 | Value: !Ref EnvironmentName 35 | 36 | LoadBalancerListener: 37 | Type: AWS::ElasticLoadBalancingV2::Listener 38 | Properties: 39 | LoadBalancerArn: !Ref LoadBalancer 40 | Port: 80 41 | Protocol: HTTP 42 | DefaultActions: 43 | - Type: forward 44 | TargetGroupArn: !Ref DefaultTargetGroup 45 | 46 | # We define a default target group here, as this is a mandatory Parameters 47 | # when creating an Application Load Balancer Listener. This is not used, instead 48 | # a target group is created per-service in each service template (../services/*) 49 | DefaultTargetGroup: 50 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 51 | Properties: 52 | Name: !Sub ${EnvironmentName}-default 53 | VpcId: !Ref VPC 54 | Port: 80 55 | Protocol: HTTP 56 | 57 | Outputs: 58 | 59 | LoadBalancer: 60 | Description: A reference to the Application Load Balancer 61 | Value: !Ref LoadBalancer 62 | 63 | LoadBalancerUrl: 64 | Description: The URL of the ALB 65 | Value: !GetAtt LoadBalancer.DNSName 66 | 67 | Listener: 68 | Description: A reference to a port 80 listener 69 | Value: !Ref LoadBalancerListener 70 | 71 | 72 | -------------------------------------------------------------------------------- /infrastructure/security-groups.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template contains the security groups required by our entire stack. 3 | We create them in a seperate nested template, so they can be referenced 4 | by all of the other nested templates. 5 | 6 | Parameters: 7 | 8 | EnvironmentName: 9 | Description: An environment name that will be prefixed to resource names 10 | Type: String 11 | 12 | VPC: 13 | Type: AWS::EC2::VPC::Id 14 | Description: Choose which VPC the security groups should be deployed to 15 | 16 | Resources: 17 | 18 | # This security group defines who/where is allowed to access the ECS hosts directly. 19 | # By default we're just allowing access from the load balancer. If you want to SSH 20 | # into the hosts, or expose non-load balanced services you can open their ports here. 21 | ECSHostSecurityGroup: 22 | Type: AWS::EC2::SecurityGroup 23 | Properties: 24 | VpcId: !Ref VPC 25 | GroupDescription: Access to the ECS hosts and the tasks/containers that run on them 26 | SecurityGroupIngress: 27 | # Only allow inbound access to ECS from the ELB 28 | - SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup 29 | IpProtocol: -1 30 | Tags: 31 | - Key: Name 32 | Value: !Sub ${EnvironmentName}-ECS-Hosts 33 | 34 | # This security group defines who/where is allowed to access the Application Load Balancer. 35 | # By default, we've opened this up to the public internet (0.0.0.0/0) but can you restrict 36 | # it further if you want. 37 | LoadBalancerSecurityGroup: 38 | Type: AWS::EC2::SecurityGroup 39 | Properties: 40 | VpcId: !Ref VPC 41 | GroupDescription: Access to the load balancer that sits in front of ECS 42 | SecurityGroupIngress: 43 | # Allow access from anywhere to our ECS services 44 | - CidrIp: 0.0.0.0/0 45 | IpProtocol: -1 46 | Tags: 47 | - Key: Name 48 | Value: !Sub ${EnvironmentName}-LoadBalancers 49 | 50 | Outputs: 51 | 52 | ECSHostSecurityGroup: 53 | Description: A reference to the security group for ECS hosts 54 | Value: !Ref ECSHostSecurityGroup 55 | 56 | LoadBalancerSecurityGroup: 57 | Description: A reference to the security group for load balancers 58 | Value: !Ref LoadBalancerSecurityGroup 59 | 60 | -------------------------------------------------------------------------------- /infrastructure/vpc.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This template deploys a VPC, with a pair of public and private subnets spread 3 | across two Availabilty Zones. It deploys an Internet Gateway, with a default 4 | route on the public subnets. It deploys a pair of NAT Gateways (one in each AZ), 5 | and default routes for them in the private subnets. 6 | 7 | Parameters: 8 | 9 | EnvironmentName: 10 | Description: An environment name that will be prefixed to resource names 11 | Type: String 12 | 13 | VpcCIDR: 14 | Description: Please enter the IP range (CIDR notation) for this VPC 15 | Type: String 16 | Default: 10.192.0.0/16 17 | 18 | PublicSubnet1CIDR: 19 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 20 | Type: String 21 | Default: 10.192.10.0/24 22 | 23 | PublicSubnet2CIDR: 24 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone 25 | Type: String 26 | Default: 10.192.11.0/24 27 | 28 | PrivateSubnet1CIDR: 29 | Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone 30 | Type: String 31 | Default: 10.192.20.0/24 32 | 33 | PrivateSubnet2CIDR: 34 | Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone 35 | Type: String 36 | Default: 10.192.21.0/24 37 | 38 | Resources: 39 | 40 | VPC: 41 | Type: AWS::EC2::VPC 42 | Properties: 43 | CidrBlock: !Ref VpcCIDR 44 | Tags: 45 | - Key: Name 46 | Value: !Ref EnvironmentName 47 | 48 | InternetGateway: 49 | Type: AWS::EC2::InternetGateway 50 | Properties: 51 | Tags: 52 | - Key: Name 53 | Value: !Ref EnvironmentName 54 | 55 | InternetGatewayAttachment: 56 | Type: AWS::EC2::VPCGatewayAttachment 57 | Properties: 58 | InternetGatewayId: !Ref InternetGateway 59 | VpcId: !Ref VPC 60 | 61 | PublicSubnet1: 62 | Type: AWS::EC2::Subnet 63 | Properties: 64 | VpcId: !Ref VPC 65 | AvailabilityZone: !Select [ 0, !GetAZs ] 66 | CidrBlock: !Ref PublicSubnet1CIDR 67 | MapPublicIpOnLaunch: true 68 | Tags: 69 | - Key: Name 70 | Value: !Sub ${EnvironmentName} Public Subnet (AZ1) 71 | 72 | PublicSubnet2: 73 | Type: AWS::EC2::Subnet 74 | Properties: 75 | VpcId: !Ref VPC 76 | AvailabilityZone: !Select [ 1, !GetAZs ] 77 | CidrBlock: !Ref PublicSubnet2CIDR 78 | MapPublicIpOnLaunch: true 79 | Tags: 80 | - Key: Name 81 | Value: !Sub ${EnvironmentName} Public Subnet (AZ2) 82 | 83 | PrivateSubnet1: 84 | Type: AWS::EC2::Subnet 85 | Properties: 86 | VpcId: !Ref VPC 87 | AvailabilityZone: !Select [ 0, !GetAZs ] 88 | CidrBlock: !Ref PrivateSubnet1CIDR 89 | MapPublicIpOnLaunch: false 90 | Tags: 91 | - Key: Name 92 | Value: !Sub ${EnvironmentName} Private Subnet (AZ1) 93 | 94 | PrivateSubnet2: 95 | Type: AWS::EC2::Subnet 96 | Properties: 97 | VpcId: !Ref VPC 98 | AvailabilityZone: !Select [ 1, !GetAZs ] 99 | CidrBlock: !Ref PrivateSubnet2CIDR 100 | MapPublicIpOnLaunch: false 101 | Tags: 102 | - Key: Name 103 | Value: !Sub ${EnvironmentName} Private Subnet (AZ2) 104 | 105 | NatGateway1EIP: 106 | Type: AWS::EC2::EIP 107 | DependsOn: InternetGatewayAttachment 108 | Properties: 109 | Domain: vpc 110 | 111 | NatGateway2EIP: 112 | Type: AWS::EC2::EIP 113 | DependsOn: InternetGatewayAttachment 114 | Properties: 115 | Domain: vpc 116 | 117 | NatGateway1: 118 | Type: AWS::EC2::NatGateway 119 | Properties: 120 | AllocationId: !GetAtt NatGateway1EIP.AllocationId 121 | SubnetId: !Ref PublicSubnet1 122 | 123 | NatGateway2: 124 | Type: AWS::EC2::NatGateway 125 | Properties: 126 | AllocationId: !GetAtt NatGateway2EIP.AllocationId 127 | SubnetId: !Ref PublicSubnet2 128 | 129 | PublicRouteTable: 130 | Type: AWS::EC2::RouteTable 131 | Properties: 132 | VpcId: !Ref VPC 133 | Tags: 134 | - Key: Name 135 | Value: !Sub ${EnvironmentName} Public Routes 136 | 137 | DefaultPublicRoute: 138 | Type: AWS::EC2::Route 139 | DependsOn: InternetGatewayAttachment 140 | Properties: 141 | RouteTableId: !Ref PublicRouteTable 142 | DestinationCidrBlock: 0.0.0.0/0 143 | GatewayId: !Ref InternetGateway 144 | 145 | PublicSubnet1RouteTableAssociation: 146 | Type: AWS::EC2::SubnetRouteTableAssociation 147 | Properties: 148 | RouteTableId: !Ref PublicRouteTable 149 | SubnetId: !Ref PublicSubnet1 150 | 151 | PublicSubnet2RouteTableAssociation: 152 | Type: AWS::EC2::SubnetRouteTableAssociation 153 | Properties: 154 | RouteTableId: !Ref PublicRouteTable 155 | SubnetId: !Ref PublicSubnet2 156 | 157 | 158 | PrivateRouteTable1: 159 | Type: AWS::EC2::RouteTable 160 | Properties: 161 | VpcId: !Ref VPC 162 | Tags: 163 | - Key: Name 164 | Value: !Sub ${EnvironmentName} Private Routes (AZ1) 165 | 166 | DefaultPrivateRoute1: 167 | Type: AWS::EC2::Route 168 | Properties: 169 | RouteTableId: !Ref PrivateRouteTable1 170 | DestinationCidrBlock: 0.0.0.0/0 171 | NatGatewayId: !Ref NatGateway1 172 | 173 | PrivateSubnet1RouteTableAssociation: 174 | Type: AWS::EC2::SubnetRouteTableAssociation 175 | Properties: 176 | RouteTableId: !Ref PrivateRouteTable1 177 | SubnetId: !Ref PrivateSubnet1 178 | 179 | PrivateRouteTable2: 180 | Type: AWS::EC2::RouteTable 181 | Properties: 182 | VpcId: !Ref VPC 183 | Tags: 184 | - Key: Name 185 | Value: !Sub ${EnvironmentName} Private Routes (AZ2) 186 | 187 | DefaultPrivateRoute2: 188 | Type: AWS::EC2::Route 189 | Properties: 190 | RouteTableId: !Ref PrivateRouteTable2 191 | DestinationCidrBlock: 0.0.0.0/0 192 | NatGatewayId: !Ref NatGateway2 193 | 194 | PrivateSubnet2RouteTableAssociation: 195 | Type: AWS::EC2::SubnetRouteTableAssociation 196 | Properties: 197 | RouteTableId: !Ref PrivateRouteTable2 198 | SubnetId: !Ref PrivateSubnet2 199 | 200 | Outputs: 201 | 202 | VPC: 203 | Description: A reference to the created VPC 204 | Value: !Ref VPC 205 | 206 | PublicSubnets: 207 | Description: A list of the public subnets 208 | Value: !Join [ ",", [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]] 209 | 210 | PrivateSubnets: 211 | Description: A list of the private subnets 212 | Value: !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]] 213 | 214 | PublicSubnet1: 215 | Description: A reference to the public subnet in the 1st Availability Zone 216 | Value: !Ref PublicSubnet1 217 | 218 | PublicSubnet2: 219 | Description: A reference to the public subnet in the 2nd Availability Zone 220 | Value: !Ref PublicSubnet2 221 | 222 | PrivateSubnet1: 223 | Description: A reference to the private subnet in the 1st Availability Zone 224 | Value: !Ref PrivateSubnet1 225 | 226 | PrivateSubnet2: 227 | Description: A reference to the private subnet in the 2nd Availability Zone 228 | Value: !Ref PrivateSubnet2 229 | -------------------------------------------------------------------------------- /master-windows.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | This template deploys a VPC, with a pair of public and private subnets spread 4 | across two Availabilty Zones. It deploys an Internet Gateway, with a default 5 | route on the public subnets. It deploys a pair of NAT Gateways (one in each AZ), 6 | and default routes for them in the private subnets. 7 | 8 | It then deploys a highly available ECS cluster using an AutoScaling Group, with 9 | ECS hosts distributed across multiple Availability Zones. 10 | 11 | Finally, it deploys a pair of example ECS services from containers published in 12 | Amazon EC2 Container Registry (Amazon ECR). 13 | 14 | Last Modified: 22nd September 2016 15 | Author: Paul Maddox 16 | 17 | Resources: 18 | 19 | VPC: 20 | Type: AWS::CloudFormation::Stack 21 | Properties: 22 | TemplateURL: https://s3-us-west-2.amazonaws.com/ecs-refarch-windows/infrastructure/vpc.yaml 23 | Parameters: 24 | EnvironmentName: !Ref AWS::StackName 25 | VpcCIDR: 10.180.0.0/16 26 | PublicSubnet1CIDR: 10.180.8.0/21 27 | PublicSubnet2CIDR: 10.180.16.0/21 28 | PrivateSubnet1CIDR: 10.180.24.0/21 29 | PrivateSubnet2CIDR: 10.180.32.0/21 30 | 31 | SecurityGroups: 32 | Type: AWS::CloudFormation::Stack 33 | Properties: 34 | TemplateURL: https://s3-us-west-2.amazonaws.com/ecs-refarch-windows/infrastructure/security-groups.yaml 35 | Parameters: 36 | EnvironmentName: !Ref AWS::StackName 37 | VPC: !GetAtt VPC.Outputs.VPC 38 | 39 | ECS: 40 | Type: AWS::CloudFormation::Stack 41 | Properties: 42 | TemplateURL: https://s3-us-west-2.amazonaws.com/ecs-refarch-windows/infrastructure/ecs-windows-cluster.yaml 43 | Parameters: 44 | EnvironmentName: !Ref AWS::StackName 45 | InstanceType: t2.large 46 | ClusterSize: 4 47 | VPC: !GetAtt VPC.Outputs.VPC 48 | SecurityGroup: !GetAtt SecurityGroups.Outputs.ECSHostSecurityGroup 49 | Subnets: !GetAtt VPC.Outputs.PrivateSubnets 50 | -------------------------------------------------------------------------------- /master.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | 3 | This template deploys a VPC, with a pair of public and private subnets spread 4 | across two Availabilty Zones. It deploys an Internet Gateway, with a default 5 | route on the public subnets. It deploys a pair of NAT Gateways (one in each AZ), 6 | and default routes for them in the private subnets. 7 | 8 | It then deploys a highly available ECS cluster using an AutoScaling Group, with 9 | ECS hosts distributed across multiple Availability Zones. 10 | 11 | Finally, it deploys a pair of example ECS services from containers published in 12 | Amazon EC2 Container Registry (Amazon ECR). 13 | 14 | Last Modified: 22nd September 2016 15 | Author: Paul Maddox 16 | 17 | Resources: 18 | 19 | VPC: 20 | Type: AWS::CloudFormation::Stack 21 | Properties: 22 | TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/infrastructure/vpc.yaml 23 | Parameters: 24 | EnvironmentName: !Ref AWS::StackName 25 | VpcCIDR: 10.180.0.0/16 26 | PublicSubnet1CIDR: 10.180.8.0/21 27 | PublicSubnet2CIDR: 10.180.16.0/21 28 | PrivateSubnet1CIDR: 10.180.24.0/21 29 | PrivateSubnet2CIDR: 10.180.32.0/21 30 | 31 | SecurityGroups: 32 | Type: AWS::CloudFormation::Stack 33 | Properties: 34 | TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/infrastructure/security-groups.yaml 35 | Parameters: 36 | EnvironmentName: !Ref AWS::StackName 37 | VPC: !GetAtt VPC.Outputs.VPC 38 | 39 | ALB: 40 | Type: AWS::CloudFormation::Stack 41 | Properties: 42 | TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/infrastructure/load-balancers.yaml 43 | Parameters: 44 | EnvironmentName: !Ref AWS::StackName 45 | VPC: !GetAtt VPC.Outputs.VPC 46 | Subnets: !GetAtt VPC.Outputs.PublicSubnets 47 | SecurityGroup: !GetAtt SecurityGroups.Outputs.LoadBalancerSecurityGroup 48 | 49 | ECS: 50 | Type: AWS::CloudFormation::Stack 51 | Properties: 52 | TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/infrastructure/ecs-cluster.yaml 53 | Parameters: 54 | EnvironmentName: !Ref AWS::StackName 55 | InstanceType: t2.large 56 | ClusterSize: 4 57 | VPC: !GetAtt VPC.Outputs.VPC 58 | SecurityGroup: !GetAtt SecurityGroups.Outputs.ECSHostSecurityGroup 59 | Subnets: !GetAtt VPC.Outputs.PrivateSubnets 60 | 61 | ProductService: 62 | Type: AWS::CloudFormation::Stack 63 | Properties: 64 | TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/services/product-service/service.yaml 65 | Parameters: 66 | VPC: !GetAtt VPC.Outputs.VPC 67 | Cluster: !GetAtt ECS.Outputs.Cluster 68 | DesiredCount: 2 69 | Listener: !GetAtt ALB.Outputs.Listener 70 | Path: /products 71 | 72 | WebsiteService: 73 | Type: AWS::CloudFormation::Stack 74 | Properties: 75 | TemplateURL: https://s3.amazonaws.com/ecs-refarch-cloudformation/services/website-service/service.yaml 76 | Parameters: 77 | VPC: !GetAtt VPC.Outputs.VPC 78 | Cluster: !GetAtt ECS.Outputs.Cluster 79 | DesiredCount: 2 80 | ProductServiceUrl: !Join [ "/", [ !GetAtt ALB.Outputs.LoadBalancerUrl, "products" ]] 81 | Listener: !GetAtt ALB.Outputs.Listener 82 | Path: / 83 | 84 | 85 | Outputs: 86 | 87 | ProductServiceUrl: 88 | Description: The URL endpoint for the product service 89 | Value: !Join [ "/", [ !GetAtt ALB.Outputs.LoadBalancerUrl, "products" ]] 90 | 91 | WebsiteServiceUrl: 92 | Description: The URL endpoint for the website service 93 | Value: !Join ["", [ !GetAtt ALB.Outputs.LoadBalancerUrl, "/" ]] 94 | -------------------------------------------------------------------------------- /services/product-service/service.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This is an example of a long running ECS service that serves a JSON API of products. 3 | 4 | Parameters: 5 | 6 | VPC: 7 | Description: The VPC that the ECS cluster is deployed to 8 | Type: AWS::EC2::VPC::Id 9 | 10 | Cluster: 11 | Description: Please provide the ECS Cluster ID that this service should run on 12 | Type: String 13 | 14 | DesiredCount: 15 | Description: How many instances of this task should we run across our cluster? 16 | Type: Number 17 | Default: 2 18 | 19 | Listener: 20 | Description: The Application Load Balancer listener to register with 21 | Type: String 22 | 23 | Path: 24 | Description: The path to register with the Application Load Balancer 25 | Type: String 26 | Default: /products 27 | 28 | Resources: 29 | 30 | Service: 31 | Type: AWS::ECS::Service 32 | DependsOn: ListenerRule 33 | Properties: 34 | Cluster: !Ref Cluster 35 | Role: !Ref ServiceRole 36 | DesiredCount: !Ref DesiredCount 37 | TaskDefinition: !Ref TaskDefinition 38 | LoadBalancers: 39 | - ContainerName: "product-service" 40 | ContainerPort: 8001 41 | TargetGroupArn: !Ref TargetGroup 42 | 43 | TaskDefinition: 44 | Type: AWS::ECS::TaskDefinition 45 | Properties: 46 | Family: product-service 47 | ContainerDefinitions: 48 | - Name: product-service 49 | Essential: true 50 | Image: 275396840892.dkr.ecr.us-east-1.amazonaws.com/ecs-refarch-cloudformation/product-service:1.0.0 51 | Memory: 128 52 | PortMappings: 53 | - ContainerPort: 8001 54 | LogConfiguration: 55 | LogDriver: awslogs 56 | Options: 57 | awslogs-group: !Ref AWS::StackName 58 | awslogs-region: !Ref AWS::Region 59 | 60 | CloudWatchLogsGroup: 61 | Type: AWS::Logs::LogGroup 62 | Properties: 63 | LogGroupName: !Ref AWS::StackName 64 | RetentionInDays: 365 65 | 66 | TargetGroup: 67 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 68 | Properties: 69 | VpcId: !Ref VPC 70 | Port: 80 71 | Protocol: HTTP 72 | Matcher: 73 | HttpCode: 200-299 74 | HealthCheckIntervalSeconds: 10 75 | HealthCheckPath: /products 76 | HealthCheckProtocol: HTTP 77 | HealthCheckTimeoutSeconds: 5 78 | HealthyThresholdCount: 2 79 | 80 | ListenerRule: 81 | Type: AWS::ElasticLoadBalancingV2::ListenerRule 82 | Properties: 83 | ListenerArn: !Ref Listener 84 | Priority: 2 85 | Conditions: 86 | - Field: path-pattern 87 | Values: 88 | - !Ref Path 89 | Actions: 90 | - TargetGroupArn: !Ref TargetGroup 91 | Type: forward 92 | 93 | # This IAM Role grants the service access to register/unregister with the 94 | # Application Load Balancer (ALB). It is based on the default documented here: 95 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_IAM_role.html 96 | ServiceRole: 97 | Type: AWS::IAM::Role 98 | Properties: 99 | RoleName: !Sub ecs-service-${AWS::StackName} 100 | Path: / 101 | AssumeRolePolicyDocument: | 102 | { 103 | "Statement": [{ 104 | "Effect": "Allow", 105 | "Principal": { "Service": [ "ecs.amazonaws.com" ]}, 106 | "Action": [ "sts:AssumeRole" ] 107 | }] 108 | } 109 | Policies: 110 | - PolicyName: !Sub ecs-service-${AWS::StackName} 111 | PolicyDocument: 112 | { 113 | "Version": "2012-10-17", 114 | "Statement": [{ 115 | "Effect": "Allow", 116 | "Action": [ 117 | "ec2:AuthorizeSecurityGroupIngress", 118 | "ec2:Describe*", 119 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 120 | "elasticloadbalancing:Describe*", 121 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 122 | "elasticloadbalancing:DeregisterTargets", 123 | "elasticloadbalancing:DescribeTargetGroups", 124 | "elasticloadbalancing:DescribeTargetHealth", 125 | "elasticloadbalancing:RegisterTargets" 126 | ], 127 | "Resource": "*" 128 | }] 129 | } 130 | -------------------------------------------------------------------------------- /services/product-service/src/Dockerfile: -------------------------------------------------------------------------------- 1 | # Start from a small base 2 | FROM scratch 3 | 4 | # Our application requires no privileges 5 | # so run it with a non-root user 6 | ADD users /etc/passwd 7 | USER nobody 8 | 9 | # Our application runs on port 8001 10 | # so allow hosts to bind to that port 11 | EXPOSE 8001 12 | 13 | # Add our application binary 14 | ADD app /app 15 | 16 | # Run our application! 17 | ENTRYPOINT [ "/app" ] 18 | -------------------------------------------------------------------------------- /services/product-service/src/Makefile: -------------------------------------------------------------------------------- 1 | all: run 2 | 3 | # This makefile contains some convenience commands for deploying and publishing. 4 | 5 | # For example, to build and run the docker container locally, just run: 6 | # $ make 7 | 8 | # or to publish the :latest version to the specified registry as :1.0.0, run: 9 | # $ make publish version=1.0.0 10 | 11 | name = ecs-refarch-cloudformation/product-service 12 | registry = 275396840892.dkr.ecr.us-east-1.amazonaws.com 13 | version ?= latest 14 | 15 | binary: 16 | $(call blue, "Building Linux binary ready for containerisation...") 17 | docker run --rm -it -v "${GOPATH}":/gopath -v "$(CURDIR)":/app -e "GOPATH=/gopath" -w /app golang:1.7 sh -c 'CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags="-s" -o app' 18 | 19 | image: binary 20 | $(call blue, "Building docker image...") 21 | docker build -t ${name}:${version} . 22 | $(MAKE) clean 23 | 24 | run: image 25 | $(call blue, "Running Docker image locally...") 26 | docker run -i -t --rm -p 8001:8001 ${name}:${version} 27 | 28 | publish: 29 | $(call blue, "Publishing Docker image to registry...") 30 | docker tag ${name}:latest ${registry}/${name}:${version} 31 | docker push ${registry}/${name}:${version} 32 | 33 | clean: 34 | @rm -f app 35 | 36 | define blue 37 | @tput setaf 6 38 | @echo $1 39 | @tput sgr0 40 | endef 41 | -------------------------------------------------------------------------------- /services/product-service/src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | type product struct { 6 | ID string 7 | Title string 8 | Description string 9 | Price float64 10 | } 11 | 12 | func main() { 13 | 14 | router := gin.Default() 15 | 16 | // Respond to GET requests 17 | router.GET("/products", func(c *gin.Context) { 18 | 19 | products := []product{ 20 | product{ 21 | ID: "0000-0000-0001", 22 | Title: "Fork Handles", 23 | Description: "Got forks? Worn out ones? You need our all new Fork Handles", 24 | Price: 6.95, 25 | }, 26 | product{ 27 | ID: "0000-0000-0002", 28 | Title: "Four Candles", 29 | Description: "One candle never enough? You need our new Four Candles bundle", 30 | Price: 3.75, 31 | }, 32 | product{ 33 | ID: "0000-0000-0003", 34 | Title: "Egg Basket", 35 | Description: "Holds 6 unbroken eggs or 36 broken ones", 36 | Price: 9.99, 37 | }, 38 | } 39 | 40 | c.IndentedJSON(200, products) 41 | 42 | }) 43 | 44 | // Serve all of the things.. 45 | router.Run(":8001") 46 | 47 | } 48 | -------------------------------------------------------------------------------- /services/product-service/src/users: -------------------------------------------------------------------------------- 1 | nobody:x:65534:65534:Nobody:/: 2 | -------------------------------------------------------------------------------- /services/website-service/service.yaml: -------------------------------------------------------------------------------- 1 | Description: > 2 | This is an example of a long running ECS service that needs to connect to another ECS service (product-service) 3 | via it's load balancer. We use environment variables to pass the URL of the product-service to this one's container(s). 4 | 5 | Parameters: 6 | 7 | VPC: 8 | Description: The VPC that the ECS cluster is deployed to 9 | Type: AWS::EC2::VPC::Id 10 | 11 | Cluster: 12 | Description: Please provide the ECS Cluster ID that this service should run on 13 | Type: String 14 | 15 | DesiredCount: 16 | Description: How many instances of this task should we run across our cluster? 17 | Type: Number 18 | Default: 2 19 | 20 | ProductServiceUrl: 21 | Description: The URL of the Product Service (used to fetch product information) 22 | Type: String 23 | 24 | Listener: 25 | Description: The Application Load Balancer listener to register with 26 | Type: String 27 | 28 | Path: 29 | Description: The path to register with the Application Load Balancer 30 | Type: String 31 | Default: / 32 | 33 | Resources: 34 | 35 | Service: 36 | Type: AWS::ECS::Service 37 | DependsOn: ListenerRule 38 | Properties: 39 | Cluster: !Ref Cluster 40 | Role: !Ref ServiceRole 41 | DesiredCount: !Ref DesiredCount 42 | TaskDefinition: !Ref TaskDefinition 43 | LoadBalancers: 44 | - ContainerName: "website-service" 45 | ContainerPort: 8000 46 | TargetGroupArn: !Ref TargetGroup 47 | 48 | TaskDefinition: 49 | Type: AWS::ECS::TaskDefinition 50 | Properties: 51 | Family: website-service 52 | ContainerDefinitions: 53 | - Name: website-service 54 | Essential: true 55 | Image: 275396840892.dkr.ecr.us-east-1.amazonaws.com/ecs-refarch-cloudformation/website-service:1.0.0 56 | Memory: 128 57 | Environment: 58 | - Name: PRODUCT_SERVICE_URL 59 | Value: !Ref ProductServiceUrl 60 | PortMappings: 61 | - ContainerPort: 8000 62 | LogConfiguration: 63 | LogDriver: awslogs 64 | Options: 65 | awslogs-group: !Ref AWS::StackName 66 | awslogs-region: !Ref AWS::Region 67 | 68 | CloudWatchLogsGroup: 69 | Type: AWS::Logs::LogGroup 70 | Properties: 71 | LogGroupName: !Ref AWS::StackName 72 | RetentionInDays: 365 73 | 74 | TargetGroup: 75 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 76 | Properties: 77 | VpcId: !Ref VPC 78 | Port: 80 79 | Protocol: HTTP 80 | Matcher: 81 | HttpCode: 200-299 82 | HealthCheckIntervalSeconds: 10 83 | HealthCheckPath: / 84 | HealthCheckProtocol: HTTP 85 | HealthCheckTimeoutSeconds: 5 86 | HealthyThresholdCount: 2 87 | 88 | ListenerRule: 89 | Type: AWS::ElasticLoadBalancingV2::ListenerRule 90 | Properties: 91 | ListenerArn: !Ref Listener 92 | Priority: 1 93 | Conditions: 94 | - Field: path-pattern 95 | Values: 96 | - !Ref Path 97 | Actions: 98 | - TargetGroupArn: !Ref TargetGroup 99 | Type: forward 100 | 101 | # This IAM Role grants the service access to register/unregister with the 102 | # Application Load Balancer (ALB). It is based on the default documented here: 103 | # http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_IAM_role.html 104 | ServiceRole: 105 | Type: AWS::IAM::Role 106 | Properties: 107 | RoleName: !Sub ecs-service-${AWS::StackName} 108 | Path: / 109 | AssumeRolePolicyDocument: | 110 | { 111 | "Statement": [{ 112 | "Effect": "Allow", 113 | "Principal": { "Service": [ "ecs.amazonaws.com" ]}, 114 | "Action": [ "sts:AssumeRole" ] 115 | }] 116 | } 117 | Policies: 118 | - PolicyName: !Sub ecs-service-${AWS::StackName} 119 | PolicyDocument: 120 | { 121 | "Version": "2012-10-17", 122 | "Statement": [{ 123 | "Effect": "Allow", 124 | "Action": [ 125 | "ec2:AuthorizeSecurityGroupIngress", 126 | "ec2:Describe*", 127 | "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", 128 | "elasticloadbalancing:Describe*", 129 | "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 130 | "elasticloadbalancing:DeregisterTargets", 131 | "elasticloadbalancing:DescribeTargetGroups", 132 | "elasticloadbalancing:DescribeTargetHealth", 133 | "elasticloadbalancing:RegisterTargets" 134 | ], 135 | "Resource": "*" 136 | }] 137 | } 138 | 139 | -------------------------------------------------------------------------------- /services/website-service/src/Dockerfile: -------------------------------------------------------------------------------- 1 | # Start from a small base 2 | FROM scratch 3 | 4 | # Our application requires no privileges 5 | # so run it with a non-root user 6 | ADD users /etc/passwd 7 | USER nobody 8 | 9 | # Our application runs on port 8001 10 | # so allow hosts to bind to that port 11 | EXPOSE 8000 12 | 13 | # Add our application binary 14 | ADD app /app 15 | 16 | # Run our application! 17 | ENTRYPOINT [ "/app" ] 18 | -------------------------------------------------------------------------------- /services/website-service/src/Makefile: -------------------------------------------------------------------------------- 1 | all: run 2 | 3 | # This makefile contains some convenience commands for deploying and publishing. 4 | 5 | # For example, to build and run the docker container locally, just run: 6 | # $ make 7 | 8 | # or to publish the :latest version to the specified registry as :1.0.0, run: 9 | # $ make publish version=1.0.0 10 | 11 | name = ecs-refarch-cloudformation/website-service 12 | registry = 275396840892.dkr.ecr.us-east-1.amazonaws.com 13 | version ?= latest 14 | 15 | binary: 16 | $(call blue, "Building Linux binary ready for containerisation...") 17 | docker run --rm -it -v "${GOPATH}":/gopath -v "$(CURDIR)":/app -e "GOPATH=/gopath" -w /app golang:1.7 sh -c 'CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags="-s" -o app' 18 | 19 | image: binary 20 | $(call blue, "Building docker image...") 21 | docker build -t ${name}:${version} . 22 | $(MAKE) clean 23 | 24 | run: image 25 | $(call blue, "Running Docker image locally...") 26 | docker run -i -t --rm -p 8000:8000 ${name}:${version} 27 | 28 | publish: 29 | $(call blue, "Publishing Docker image to registry...") 30 | docker tag ${name}:latest ${registry}/${name}:${version} 31 | docker push ${registry}/${name}:${version} 32 | 33 | clean: 34 | @rm -f app 35 | 36 | define blue 37 | @tput setaf 6 38 | @echo $1 39 | @tput sgr0 40 | endef 41 | -------------------------------------------------------------------------------- /services/website-service/src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "html/template" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | const VERSION string = "1.0.0" 14 | 15 | type Product struct { 16 | ID string 17 | Title string 18 | Description string 19 | Price float64 20 | } 21 | 22 | func main() { 23 | 24 | // Products template 25 | html := ` 26 | 27 | 28 | Product Listing 29 | 30 | 31 |

Product Listing

32 | {{range .}} 33 |

{{.Title}}

34 |

ID: {{.ID}}

35 |

Description: {{.Description}}

36 |

Price: {{.Price}}

37 | {{end}} 38 | 39 | 40 | ` 41 | tmpl, err := template.New("product-listing").Parse(html) 42 | if err != nil { 43 | log.Fatalf("Error parsing product listing template: %s", err) 44 | } 45 | 46 | router := gin.Default() 47 | router.SetHTMLTemplate(tmpl) 48 | 49 | // Router handlers 50 | router.GET("/", func(c *gin.Context) { 51 | 52 | product := os.Getenv("PRODUCT_SERVICE_URL") 53 | resp, err := http.Get("http://" + product) 54 | if err != nil { 55 | c.IndentedJSON(500, gin.H{ 56 | "status": "error", 57 | "message": "Could not connect to product service", 58 | "detailed": err.Error(), 59 | }) 60 | return 61 | } 62 | 63 | defer resp.Body.Close() 64 | 65 | var products []Product 66 | json.NewDecoder(resp.Body).Decode(&products) 67 | c.HTML(200, "product-listing", products) 68 | 69 | }) 70 | 71 | // Lets go... 72 | router.Run(":8000") 73 | 74 | } 75 | -------------------------------------------------------------------------------- /services/website-service/src/users: -------------------------------------------------------------------------------- 1 | nobody:x:65534:65534:Nobody:/: 2 | --------------------------------------------------------------------------------