├── LICENSE ├── README.md ├── ddc_on_aws.json └── images ├── .DS_Store ├── design_0.png ├── design_2.png ├── design_3.png ├── ui_0.png ├── v1.1_rest_results.png └── v1.3_test_results.png /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2014 Evan Hazlett. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Datacenter on AWS 2 | 3 | ### This project is not maintained anymore. The maintained version is [here](https://docs.docker.com/datacenter/install/aws/). 4 | 5 | **TL;DR One click deploy of highly-scalable, production-ready Docker Datacenter on AWS based on Docker and AWS best-practices** 6 | 7 | 8 | ![](images/ui_0.png) 9 | 10 | Docker Datacenter is an integrated solution including open source and commercial software, the integrations between them, full Docker API support, validated configurations and commercial support for your Docker Datacenter environment. A pluggable architecture allows flexibility in compute, networking and storage providers used in your CaaS infrastructure without disrupting the application code. Enterprises can leverage existing technology investments with Docker Datacenter. The open APIs allow Docker Datacenter CaaS to easily integrate into your existing systems like LDAP/AD, monitoring, logging and more. 11 | 12 | 13 | ![](images/design_0.png) 14 | 15 | Docker Data Center is composed of two main components: Docker Universal Control Plane (UCP) and Docker Trusted Registry (DTR). [UCP](https://docs.docker.com/docker-trusted-registry/overview/) is an enterprise-grade cluster management solution from Docker that helps you manage your whole cluster from a single place. UCP is made of the UCP controllers and UCP nodes. 16 | 17 | [DTR](https://docs.docker.com/docker-trusted-registry/overview/) is the enterprise-grade image storage solution from Docker that helps you can securely store and manage the Docker images you use in your applications. DTR is made of DTR replicas only that are deployed on UCP nodes. 18 | 19 | ## Architecture 20 | 21 | ![](images/design_3.png) 22 | 23 | The AWS Cloudformation starts the installation process by creating all the required AWS resources such as the VPC, security groups, public and private subnets, internet gateways, NAT gateways, and S3 bucket. It then launches the first UCP controller instance and goes through the installation process of Docker engine and UCP containers. It backs the Root CAs created by the first UCP controllers to S3. Once the first UCP controller is up and running, the process of creating the other UCP controllers, the UCP cluster nodes, and the first DTR replica is triggered. Similar to the first UCP controller node, all other nodes are started by installing Docker Commercially Supported engine, followed by running the UCP and DTR containers to join the cluster. Three ELBs, one for UCP, one for DTR and a third for your application, are launched and automatically configured to provide resilient loadbalancing across the two AZs. Additionally, UCP controllers and nodes are launched in an ASG to provide scaling functionality if needed. This architecture ensures that both UCP and DTR instances are spread across both AZs to ensure resiliency and high-availability. UCP worker nodes are launched with `interlock` and `nginx` to dynamically register your deployed applications. 24 | 25 | 26 | ![](images/design_2.png) 27 | 28 | ## How to Launch Latest Release([v1.3](https://github.com/nicolaka/ddc-aws/releases/tag/v1.3)) 29 | 30 | You can launch the Cloudformation template using the AWS Console or using the AWS CLI as follows: 31 | 32 | **1) AWS Console:** 33 | 34 | - Click on **Launch Stack** below. This link will take you to AWS cloudformation portal. 35 | - Confirm your AWS Region that you'd like to launch this stack in ( top right corner) 36 | - Provide the required parameters ( listed below ) and click **Next** 37 | - Confirm and Launch. 38 | - Once all done ( it does take between 20-30 mins), click on outputs tab to see the URLs of UCP/DTR, default username and password, and jumphost info 39 | 40 | [![Docker Datacenter on AWS](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=DockerDatacenter&templateURL=https://s3-us-west-2.amazonaws.com/ddc-on-aws-public/ddc_on_aws.json) 41 | 42 | 43 | **2) AWS CLI:** 44 | 45 | 46 | - Upload your Docker Datacenter license to an S3 bucket. 47 | - Run the following Docker container from your local or dev environment: 48 | 49 | ``` 50 | docker run --env AWS_ACCESS_KEY_ID= \ 51 | --env AWS_SECRET_ACCESS_KEY= \ 52 | --env AWS_DEFAULT_REGION= \ 53 | garland/aws-cli-docker aws cloudformation create-stack \ 54 | --stack-name \ 55 | --capabilities CAPABILITY_IAM \ 56 | --template-url https://s3-us-west-2.amazonaws.com/ddc-on-aws-public/ddc_on_aws.json \ 57 | --parameters \ 58 | ParameterKey=KeyName,ParameterValue= \ 59 | ParameterKey=RootVolumeSize,ParameterValue= \ 60 | ParameterKey=UCPFQDN,ParameterValue= \ 61 | ParameterKey=UCPControllersInstanceType,ParameterValue= \ 62 | ParameterKey=DTRInstanceType,ParameterValue= \ 63 | ParameterKey=UCPNodesInstanceType,ParameterValue= \ 64 | ParameterKey=ClusterSize,ParameterValue= \ 65 | ParameterKey=License,ParameterValue= 66 | ``` 67 | 68 | - Once all done ( it does take between 20-30 mins), you can get stack outputs such as UCP and DTR URLs directly from CLI as follows: 69 | 70 | 71 | ``` 72 | docker run --env AWS_ACCESS_KEY_ID= \ 73 | --env AWS_SECRET_ACCESS_KEY= \ 74 | --env AWS_DEFAULT_REGION= \ 75 | garland/aws-cli-docker aws cloudformation describe-stacks --stack-name 76 | ``` 77 | 78 | - Full documentation for using `aws-cli` can be found [here](http://docs.aws.amazon.com/cli/latest/reference/cloudformation/). 79 | 80 | 81 | **Required Paramters** 82 | 83 | - **KeyName**: Name of an existing EC2 KeyPair to enable SSH access to the instances 84 | - **UCPFQDN**: Intended FQDN for UCP used to self-sign a cert with domain name. 85 | - **UCPControllersInstanceType**: AWS EC2 Instance Type for UCP Controllers only. Minimum required is **m3.medium** 86 | - **DTRInstanceType**: AWS EC2 Instance Type for DTR Replicas Only. Minimum required is **m3.medium** 87 | - **UCPNodesInstanceType**: AWS EC2 Instance Type for UCP nodes 88 | - **ClusterSize**: Number of UCP nodes (3-64) 89 | - **License**: Docker Datacenter License (copy+past it in JSON format or URL to download it). You can easily get trial license [here](https://store.docker.com/bundles/docker-datacenter) 90 | - **RootVolumeSize**: Root filesystem size in GB. This will be used for all instances ( UCP Controllers, UCP Nodes, and DTR Nodes) 91 | 92 | **Key Functionalities** 93 | 94 | - Create a New VPC, Private and Public Subnets in different AZs, ELBs, NAT Gateways, Internet Gateways, AutoScaling Groups- all based on AWS best practices 95 | - Creates and configures an S3 bucket for DDC to be used for cert backup and DTR image storage 96 | - Deploys 3 UCP Controllers across multiple AZs within your VPC 97 | - Creates a UCP ELB with preconfigured HTTP healthchecks 98 | - Deploys a scalable cluster of UCP nodes 99 | - Backs up UCP Root CAs to S3 100 | - Create a 3 DTR Replicas across multiple AZs within your VPC 101 | - Creates a DTR with preconfigured healthchecks 102 | - Creates a jumphost ec2 instance to be able to ssh to the DDC nodes 103 | - Creates a UCP Nodes ELB with preconfigured healthchecks (TCP Port 80). This can be used for your application that are deployed on UCP. 104 | - Deploys NGINX+Interlock to dynamically register your application containers. Please see FAQ for more details on launching your application. 105 | - Creates a Cloudwatch Log Group (called `DDCLogGroup`)and allows log streams from DDC instances. It also automatically logs the UCP and DTR installation containers. 106 | 107 | **Software Versions** 108 | 109 | - EC2 instances use Ubuntu 14.04 LTS AMI 110 | - Docker Commercially Supported Engine 1.12 111 | - UCP 1.1.4 112 | - DTR 2.0.3 113 | 114 | **Notes and Caveats** 115 | 116 | - UCP and DTR default username and password are `admin/ddconaws`. **PLEASE CHANGE PASSWORD in UCP portal!!**. To change the password, go to the UCP portal, under `Users and Teams`, click on edit button for the `admin` user. From there you can update the admin account password. 117 | - External Certs: Both UCP and DTR are installed with self-signed certs today. If you wish to use your own certs, you can do so by following the UCP and DTR configuration guides. Full UCP and DTR Configuration guides are found [here](https://docs.docker.com/ucp/configuration/use-externally-signed-certs/) and [here](https://docs.docker.com/docker-trusted-registry/configure/configuration/). 118 | - A Single Security Group is used in this setup. The security group only allows HTTP,HTTPS, and SSH traffic from external IPs. Security group doesn't limit any traffic from within the cluster. Please adjust it as needed. 119 | - SSH: If you need to SSH into the cluster you can do so by using [SSH agent forwarding](https://developer.github.com/guides/using-ssh-agent-forwarding/) and ssh'ing into the jumphost node using the selected private key. Once you're logged into the jumphost, you can use the private IP address of any of the other nodes to ssh into them. 120 | - Default username for `ubuntu` based AMI's is `ubuntu`. 121 | - Supported AWS Regions: 122 | - us-east-1 123 | - us-west-2 124 | - us-west-1 125 | - eu-west-1 126 | - eu-central-1 127 | - ap-northeast-1 128 | - ap-southeast-1 129 | - ap-southeast-2 130 | - ap-south-1 131 | 132 | ## FAQ 133 | 134 | - **My AWS Cloudformation is failing to launch. How should I do ?** 135 | 136 | 1. Open an issue in this repo after checking that it doesn't already exist. 137 | 2. Make sure you capture the stack events and parameters provided. 138 | 3. Make sure you capture the cloudformation stack ID. 139 | 4. Open an issue with Docker Support by emailing support@docker.com 140 | 5. If you know which instance is failing, ssh into the instance( via the jumphost instance) and capture the following: 141 | 142 | ``` 143 | /var/log/cloud-init-output.log 144 | /var/lib/cloud/instance/scripts/part-001 145 | ``` 146 | 147 | - **How can I deploy my application on Docker Datacenter?** 148 | 149 | It is easy to deploy a your Docker applications on Docker Datacenter. You can launch your applications from CLI (instruction [here](https://docs.docker.com/ucp/applications/deploy-app-cli/)) or UCP portal (instruction [here](https://docs.docker.com/ucp/applications/deploy-app-ui/)). 150 | 151 | - **How can I access my application on Docker Datacenter?** 152 | 153 | If you wish to access your application using a DNS name, you need to do two things: 154 | 155 | 1. Create a DNS record (A or CNAME) for your application using the APP ELB that gets created. The APP ELB load-balances traffic across all UCP worker nodes that have NGINX running which automatically get updated when you launch your application. 156 | 157 | 2. Launch your application with `interlock` labels. Note: Interlock+NGINX are already deployed as part of the Cloudformation template. 158 | 159 | For example, to access a Compose app using `app.example.com`, you first need to create a DNS record for it using the APP ELB's DNS name/IP then launch it with following labels: 160 | 161 | ``` 162 | app: 163 | image: ehazlett/docker-demo:latest 164 | ports: 165 | - 8080 166 | labels: 167 | - "interlock.hostname=app" 168 | - "interlock.domain=example.com" 169 | ``` 170 | 171 | More details on interlock can be found [here](https://github.com/ehazlett/interlock). 172 | 173 | - **How can I send container logs to Cloudwatch ?** 174 | 175 | Run your container with the following Docker run options `--log-driver=awslogs --log-opt awslogs-group=DDCLogGroup` and you should see your container logs in AWS's Log Group. 176 | 177 | - **What's the support model for Docker Datacenter on AWS ?** 178 | 179 | The solution provided is based on Docker and AWS best practices and therefore the Docker Dataceter components of the solution are commercially supported by Docker while the AWS components are supported by AWS. If you are facing any issues related to launching the solution please open issues here on Github. If the issues your'e facing are related to Docker Datacenter components please contact Docker support. If the issues your'e facing are related to AWS specifically, please contact AWS support. 180 | 181 | - **How can I obtain Docker Datacenter license?** 182 | 183 | You can obtain trial license quickly and easily by going to [here](https://store.docker.com/bundles/docker-datacenter). If you're interested in buying Docker Datacenter subscription please contact Docker [here](https://goto.docker.com/contact-us.html). 184 | 185 | **Version 1.3 Cloudformation Test Results** 186 | 187 | ![](images/v1.3_test_results.png) 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /ddc_on_aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "QS(0025) Docker Datacenter Quickstart for AWS, Version 1.3, October 2016", 4 | "Parameters": { 5 | "KeyName": { 6 | "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances", 7 | "Type": "AWS::EC2::KeyPair::KeyName", 8 | "Default": "id_rsa_aws", 9 | "ConstraintDescription": "Must be the name of an existing EC2 KeyPair." 10 | }, 11 | "RootVolumeSize": { 12 | "Type": "String", 13 | "Description": "Root filesystem size in GB. This will be used for all instances", 14 | "Default": "120" 15 | }, 16 | "UCPFQDN": { 17 | "Type": "String", 18 | "Default": "localhost", 19 | "Description": "Intended FQDN for UCP used to sign a cert for this domain name" 20 | }, 21 | "UCPControllersInstanceType": { 22 | "Type": "String", 23 | "Description": "EC2 HVM instance type for UCP Controllers only (minimum is m3.medium).", 24 | "AllowedValues": [ 25 | "m4.large", 26 | "m4.xlarge", 27 | "m4.2xlarge", 28 | "m4.4xlarge", 29 | "m4.10xlarge", 30 | "m3.medium", 31 | "m3.large", 32 | "m3.xlarge", 33 | "m3.2xlarge", 34 | "c4.large", 35 | "c4.xlarge", 36 | "c4.2xlarge", 37 | "c4.4xlarge", 38 | "c4.8xlarge", 39 | "c3.large", 40 | "c3.xlarge", 41 | "c3.2xlarge", 42 | "c3.4xlarge", 43 | "c3.8xlarge", 44 | "r3.large", 45 | "r3.xlarge", 46 | "r3.2xlarge", 47 | "r3.4xlarge", 48 | "r3.8xlarge", 49 | "i2.xlarge", 50 | "i2.2xlarge", 51 | "i2.4xlarge", 52 | "i2.8xlarge" 53 | ], 54 | "ConstraintDescription": "Must be a valid EC2 HVM instance type.", 55 | "Default": "m3.medium" 56 | }, 57 | "DTRInstanceType": { 58 | "Type": "String", 59 | "Description": "EC2 HVM instance type for DTR Replicas only (minimum is m3.medium).", 60 | "AllowedValues": [ 61 | "m4.large", 62 | "m4.xlarge", 63 | "m4.2xlarge", 64 | "m4.4xlarge", 65 | "m4.10xlarge", 66 | "m3.medium", 67 | "m3.large", 68 | "m3.xlarge", 69 | "m3.2xlarge", 70 | "c4.large", 71 | "c4.xlarge", 72 | "c4.2xlarge", 73 | "c4.4xlarge", 74 | "c4.8xlarge", 75 | "c3.large", 76 | "c3.xlarge", 77 | "c3.2xlarge", 78 | "c3.4xlarge", 79 | "c3.8xlarge", 80 | "r3.large", 81 | "r3.xlarge", 82 | "r3.2xlarge", 83 | "r3.4xlarge", 84 | "r3.8xlarge", 85 | "i2.xlarge", 86 | "i2.2xlarge", 87 | "i2.4xlarge", 88 | "i2.8xlarge" 89 | ], 90 | "ConstraintDescription": "Must be a valid EC2 HVM instance type.", 91 | "Default": "m3.medium" 92 | }, 93 | "UCPNodesInstanceType": { 94 | "Type": "String", 95 | "Description": "EC2 HVM instance type for UCP Nodes only", 96 | "AllowedValues": [ 97 | "t2.micro", 98 | "t2.small", 99 | "t2.medium", 100 | "t2.large", 101 | "m4.large", 102 | "m4.xlarge", 103 | "m4.2xlarge", 104 | "m4.4xlarge", 105 | "m4.10xlarge", 106 | "m3.medium", 107 | "m3.large", 108 | "m3.xlarge", 109 | "m3.2xlarge", 110 | "c4.large", 111 | "c4.xlarge", 112 | "c4.2xlarge", 113 | "c4.4xlarge", 114 | "c4.8xlarge", 115 | "c3.large", 116 | "c3.xlarge", 117 | "c3.2xlarge", 118 | "c3.4xlarge", 119 | "c3.8xlarge", 120 | "r3.large", 121 | "r3.xlarge", 122 | "r3.2xlarge", 123 | "r3.4xlarge", 124 | "r3.8xlarge", 125 | "i2.xlarge", 126 | "i2.2xlarge", 127 | "i2.4xlarge", 128 | "i2.8xlarge" 129 | ], 130 | "ConstraintDescription": "Must be a valid EC2 HVM instance type.", 131 | "Default": "m3.medium" 132 | }, 133 | "ClusterSize": { 134 | "Type": "Number", 135 | "Default": "3", 136 | "MinValue": "1", 137 | "MaxValue": "64", 138 | "Description": "Number of UCP nodes (non-controller) in the cluster" 139 | }, 140 | "License": { 141 | "Type": "String", 142 | "Description": "Docker Datacenter License in JSON format. Get Trial License here : https://store.docker.com/bundles/docker-datacenter " 143 | } 144 | }, 145 | "Metadata": { 146 | "AWS::CloudFormation::Interface": { 147 | "ParameterGroups": [ 148 | { 149 | "Label": { 150 | "default": "DNS Configuration" 151 | }, 152 | "Parameters": [ 153 | "UCPFQDN" 154 | ] 155 | }, 156 | { 157 | "Label": { 158 | "default": "Cluster Configuration" 159 | }, 160 | "Parameters": [ 161 | "ClusterSize", 162 | "UCPControllersInstanceType", 163 | "DTRInstanceType", 164 | "UCPNodesInstanceType", 165 | "KeyName", 166 | "RootVolumeSize" 167 | ] 168 | }, 169 | { 170 | "Label": { 171 | "default": "Docker Datacenter License" 172 | }, 173 | "Parameters": [ 174 | "License" 175 | ] 176 | } 177 | ] 178 | } 179 | }, 180 | "Mappings": { 181 | "VpcCidrs": { 182 | "vpc": { 183 | "cidr": "192.168.0.0/16" 184 | }, 185 | "pubsubnet1": { 186 | "cidr": "192.168.33.0/24" 187 | }, 188 | "pubsubnet2": { 189 | "cidr": "192.168.34.0/24" 190 | }, 191 | "privatesubnet1": { 192 | "cidr": "192.168.23.0/24" 193 | }, 194 | "privatesubnet2": { 195 | "cidr": "192.168.24.0/24" 196 | } 197 | }, 198 | "AWSInstanceType2Arch": { 199 | "t2.micro": { 200 | "Arch": "HVM64" 201 | }, 202 | "t2.small": { 203 | "Arch": "HVM64" 204 | }, 205 | "t2.medium": { 206 | "Arch": "HVM64" 207 | }, 208 | "t2.large": { 209 | "Arch": "HVM64" 210 | }, 211 | "m3.medium": { 212 | "Arch": "HVM64" 213 | }, 214 | "m3.large": { 215 | "Arch": "HVM64" 216 | }, 217 | "m3.xlarge": { 218 | "Arch": "HVM64" 219 | }, 220 | "m3.2xlarge": { 221 | "Arch": "HVM64" 222 | }, 223 | "m4.large": { 224 | "Arch": "HVM64" 225 | }, 226 | "m4.xlarge": { 227 | "Arch": "HVM64" 228 | }, 229 | "m4.2xlarge": { 230 | "Arch": "HVM64" 231 | }, 232 | "m4.4xlarge": { 233 | "Arch": "HVM64" 234 | }, 235 | "m4.10xlarge": { 236 | "Arch": "HVM64" 237 | }, 238 | "c3.large": { 239 | "Arch": "HVM64" 240 | }, 241 | "c3.xlarge": { 242 | "Arch": "HVM64" 243 | }, 244 | "c3.2xlarge": { 245 | "Arch": "HVM64" 246 | }, 247 | "c3.4xlarge": { 248 | "Arch": "HVM64" 249 | }, 250 | "c3.8xlarge": { 251 | "Arch": "HVM64" 252 | }, 253 | "c4.large": { 254 | "Arch": "HVM64" 255 | }, 256 | "c4.xlarge": { 257 | "Arch": "HVM64" 258 | }, 259 | "c4.2xlarge": { 260 | "Arch": "HVM64" 261 | }, 262 | "c4.4xlarge": { 263 | "Arch": "HVM64" 264 | }, 265 | "c4.8xlarge": { 266 | "Arch": "HVM64" 267 | }, 268 | "g2.2xlarge": { 269 | "Arch": "HVMG2" 270 | }, 271 | "r3.large": { 272 | "Arch": "HVM64" 273 | }, 274 | "r3.xlarge": { 275 | "Arch": "HVM64" 276 | }, 277 | "r3.2xlarge": { 278 | "Arch": "HVM64" 279 | }, 280 | "r3.4xlarge": { 281 | "Arch": "HVM64" 282 | }, 283 | "r3.8xlarge": { 284 | "Arch": "HVM64" 285 | }, 286 | "i2.xlarge": { 287 | "Arch": "HVM64" 288 | }, 289 | "i2.2xlarge": { 290 | "Arch": "HVM64" 291 | }, 292 | "i2.4xlarge": { 293 | "Arch": "HVM64" 294 | }, 295 | "i2.8xlarge": { 296 | "Arch": "HVM64" 297 | }, 298 | "d2.xlarge": { 299 | "Arch": "HVM64" 300 | }, 301 | "d2.2xlarge": { 302 | "Arch": "HVM64" 303 | }, 304 | "d2.4xlarge": { 305 | "Arch": "HVM64" 306 | }, 307 | "d2.8xlarge": { 308 | "Arch": "HVM64" 309 | }, 310 | "hi1.4xlarge": { 311 | "Arch": "HVM64" 312 | }, 313 | "hs1.8xlarge": { 314 | "Arch": "HVM64" 315 | }, 316 | "cr1.8xlarge": { 317 | "Arch": "HVM64" 318 | }, 319 | "cc2.8xlarge": { 320 | "Arch": "HVM64" 321 | } 322 | }, 323 | "AWSRegionArch2AMI": { 324 | "us-east-1": { 325 | "HVM64": "ami-0f8bce65", 326 | "HVMG2": "NOT_SUPPORTED" 327 | }, 328 | "us-west-2": { 329 | "HVM64": "ami-534d5d32", 330 | "HVMG2": "NOT_SUPPORTED" 331 | }, 332 | "us-west-1": { 333 | "HVM64": "ami-31106a51", 334 | "HVMG2": "NOT_SUPPORTED" 335 | }, 336 | "eu-west-1": { 337 | "HVM64": "ami-8328bbf0", 338 | "HVMG2": "NOT_SUPPORTED" 339 | }, 340 | "eu-central-1": { 341 | "HVM64": "ami-74ee001b", 342 | "HVMG2": "NOT_SUPPORTED" 343 | }, 344 | "ap-northeast-1": { 345 | "HVM64": "ami-bb32ddda", 346 | "HVMG2": "NOT_SUPPORTED" 347 | }, 348 | "ap-southeast-1": { 349 | "HVM64": "ami-c4ae7ea7", 350 | "HVMG2": "NOT_SUPPORTED" 351 | }, 352 | "ap-southeast-2": { 353 | "HVM64": "ami-d5cae4b6", 354 | "HVMG2": "NOT_SUPPORTED" 355 | }, 356 | "sa-east-1": { 357 | "HVM64": "NOT_SUPPORTED", 358 | "HVMG2": "NOT_SUPPORTED" 359 | }, 360 | "ap-south-1": { 361 | "HVM64": "ami-c092f8af", 362 | "HVMG2": "NOT_SUPPORTED" 363 | }, 364 | "cn-north-1": { 365 | "HVM64": "NOT_SUPPORTED", 366 | "HVMG2": "NOT_SUPPORTED" 367 | } 368 | } 369 | }, 370 | "Resources": { 371 | "DDCBucket": { 372 | "Type": "AWS::S3::Bucket", 373 | "DeletionPolicy": "Retain" 374 | }, 375 | "vpc": { 376 | "Type": "AWS::EC2::VPC", 377 | "Properties": { 378 | "CidrBlock": { 379 | "Fn::FindInMap": [ 380 | "VpcCidrs", 381 | "vpc", 382 | "cidr" 383 | ] 384 | }, 385 | "EnableDnsSupport": "true", 386 | "EnableDnsHostnames": "true", 387 | "Tags": [ 388 | { 389 | "Key": "Name", 390 | "Value": { 391 | "Fn::Join": [ 392 | "-", 393 | [ 394 | { 395 | "Ref": "AWS::StackName" 396 | }, 397 | "VPC" 398 | ] 399 | ] 400 | } 401 | } 402 | ] 403 | } 404 | }, 405 | "PubSubnetAz1": { 406 | "Type": "AWS::EC2::Subnet", 407 | "Properties": { 408 | "VpcId": { 409 | "Ref": "vpc" 410 | }, 411 | "CidrBlock": { 412 | "Fn::FindInMap": [ 413 | "VpcCidrs", 414 | "pubsubnet1", 415 | "cidr" 416 | ] 417 | }, 418 | "AvailabilityZone": { 419 | "Fn::Select": [ 420 | "0", 421 | { 422 | "Fn::GetAZs": { 423 | "Ref": "AWS::Region" 424 | } 425 | } 426 | ] 427 | }, 428 | "Tags": [ 429 | { 430 | "Key": "Name", 431 | "Value": { 432 | "Fn::Join": [ 433 | "-", 434 | [ 435 | { 436 | "Ref": "AWS::StackName" 437 | }, 438 | "PublicSubnet1" 439 | ] 440 | ] 441 | } 442 | } 443 | ] 444 | } 445 | }, 446 | "PubSubnetAz2": { 447 | "Type": "AWS::EC2::Subnet", 448 | "Properties": { 449 | "VpcId": { 450 | "Ref": "vpc" 451 | }, 452 | "CidrBlock": { 453 | "Fn::FindInMap": [ 454 | "VpcCidrs", 455 | "pubsubnet2", 456 | "cidr" 457 | ] 458 | }, 459 | "AvailabilityZone": { 460 | "Fn::Select": [ 461 | "1", 462 | { 463 | "Fn::GetAZs": { 464 | "Ref": "AWS::Region" 465 | } 466 | } 467 | ] 468 | }, 469 | "Tags": [ 470 | { 471 | "Key": "Name", 472 | "Value": { 473 | "Fn::Join": [ 474 | "-", 475 | [ 476 | { 477 | "Ref": "AWS::StackName" 478 | }, 479 | "PublicSubnet2" 480 | ] 481 | ] 482 | } 483 | } 484 | ] 485 | } 486 | }, 487 | "PrivateSubnetAz1": { 488 | "Type": "AWS::EC2::Subnet", 489 | "Properties": { 490 | "VpcId": { 491 | "Ref": "vpc" 492 | }, 493 | "CidrBlock": { 494 | "Fn::FindInMap": [ 495 | "VpcCidrs", 496 | "privatesubnet1", 497 | "cidr" 498 | ] 499 | }, 500 | "AvailabilityZone": { 501 | "Fn::Select": [ 502 | "0", 503 | { 504 | "Fn::GetAZs": { 505 | "Ref": "AWS::Region" 506 | } 507 | } 508 | ] 509 | }, 510 | "Tags": [ 511 | { 512 | "Key": "Name", 513 | "Value": { 514 | "Fn::Join": [ 515 | "-", 516 | [ 517 | { 518 | "Ref": "AWS::StackName" 519 | }, 520 | "PrivateSubnet1" 521 | ] 522 | ] 523 | } 524 | } 525 | ] 526 | } 527 | }, 528 | "PrivateSubnetAz2": { 529 | "Type": "AWS::EC2::Subnet", 530 | "Properties": { 531 | "VpcId": { 532 | "Ref": "vpc" 533 | }, 534 | "CidrBlock": { 535 | "Fn::FindInMap": [ 536 | "VpcCidrs", 537 | "privatesubnet2", 538 | "cidr" 539 | ] 540 | }, 541 | "AvailabilityZone": { 542 | "Fn::Select": [ 543 | "1", 544 | { 545 | "Fn::GetAZs": { 546 | "Ref": "AWS::Region" 547 | } 548 | } 549 | ] 550 | }, 551 | "Tags": [ 552 | { 553 | "Key": "Name", 554 | "Value": { 555 | "Fn::Join": [ 556 | "-", 557 | [ 558 | { 559 | "Ref": "AWS::StackName" 560 | }, 561 | "PrivateSubnet2" 562 | ] 563 | ] 564 | } 565 | } 566 | ] 567 | } 568 | }, 569 | "InternetGateway": { 570 | "Type": "AWS::EC2::InternetGateway", 571 | "Properties": { 572 | "Tags": [ 573 | { 574 | "Key": "Name", 575 | "Value": { 576 | "Fn::Join": [ 577 | "-", 578 | [ 579 | { 580 | "Ref": "AWS::StackName" 581 | }, 582 | "IGW" 583 | ] 584 | ] 585 | } 586 | } 587 | ] 588 | } 589 | }, 590 | "AttachGateway": { 591 | "DependsOn": "InternetGateway", 592 | "Type": "AWS::EC2::VPCGatewayAttachment", 593 | "Properties": { 594 | "VpcId": { 595 | "Ref": "vpc" 596 | }, 597 | "InternetGatewayId": { 598 | "Ref": "InternetGateway" 599 | } 600 | } 601 | }, 602 | "RouteViaIgw": { 603 | "DependsOn": "AttachGateway", 604 | "Type": "AWS::EC2::RouteTable", 605 | "Properties": { 606 | "VpcId": { 607 | "Ref": "vpc" 608 | } 609 | } 610 | }, 611 | "PublicRouteViaIgw": { 612 | "Type": "AWS::EC2::Route", 613 | "Properties": { 614 | "RouteTableId": { 615 | "Ref": "RouteViaIgw" 616 | }, 617 | "DestinationCidrBlock": "0.0.0.0/0", 618 | "GatewayId": { 619 | "Ref": "InternetGateway" 620 | } 621 | } 622 | }, 623 | "PubSubnet1RouteTableAssociation": { 624 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 625 | "Properties": { 626 | "SubnetId": { 627 | "Ref": "PubSubnetAz1" 628 | }, 629 | "RouteTableId": { 630 | "Ref": "RouteViaIgw" 631 | } 632 | } 633 | }, 634 | "PubSubnet2RouteTableAssociation": { 635 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 636 | "Properties": { 637 | "SubnetId": { 638 | "Ref": "PubSubnetAz2" 639 | }, 640 | "RouteTableId": { 641 | "Ref": "RouteViaIgw" 642 | } 643 | } 644 | }, 645 | "NATAZ1": { 646 | "Type": "AWS::EC2::NatGateway", 647 | "Properties": { 648 | "AllocationId": { 649 | "Fn::GetAtt": [ 650 | "EIP1", 651 | "AllocationId" 652 | ] 653 | }, 654 | "SubnetId": { 655 | "Ref": "PubSubnetAz1" 656 | } 657 | } 658 | }, 659 | "EIP1": { 660 | "DependsOn": "AttachGateway", 661 | "Type": "AWS::EC2::EIP", 662 | "Properties": { 663 | "Domain": "vpc" 664 | } 665 | }, 666 | "NatAZ1Route": { 667 | "Type": "AWS::EC2::Route", 668 | "Properties": { 669 | "RouteTableId": { 670 | "Ref": "RouteViaNatAZ1" 671 | }, 672 | "DestinationCidrBlock": "0.0.0.0/0", 673 | "NatGatewayId": { 674 | "Ref": "NATAZ1" 675 | } 676 | } 677 | }, 678 | "RouteViaNatAZ1": { 679 | "Type": "AWS::EC2::RouteTable", 680 | "Properties": { 681 | "VpcId": { 682 | "Ref": "vpc" 683 | } 684 | } 685 | }, 686 | "PrivateSubnet1RouteTableAssociation": { 687 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 688 | "Properties": { 689 | "SubnetId": { 690 | "Ref": "PrivateSubnetAz1" 691 | }, 692 | "RouteTableId": { 693 | "Ref": "RouteViaNatAZ1" 694 | } 695 | } 696 | }, 697 | "NATAZ2": { 698 | "Type": "AWS::EC2::NatGateway", 699 | "Properties": { 700 | "AllocationId": { 701 | "Fn::GetAtt": [ 702 | "EIP2", 703 | "AllocationId" 704 | ] 705 | }, 706 | "SubnetId": { 707 | "Ref": "PubSubnetAz2" 708 | } 709 | } 710 | }, 711 | "EIP2": { 712 | "DependsOn": "AttachGateway", 713 | "Type": "AWS::EC2::EIP", 714 | "Properties": { 715 | "Domain": "vpc" 716 | } 717 | }, 718 | "NatAZ2Route": { 719 | "Type": "AWS::EC2::Route", 720 | "Properties": { 721 | "RouteTableId": { 722 | "Ref": "RouteViaNatAZ2" 723 | }, 724 | "DestinationCidrBlock": "0.0.0.0/0", 725 | "NatGatewayId": { 726 | "Ref": "NATAZ2" 727 | } 728 | } 729 | }, 730 | "RouteViaNatAZ2": { 731 | "Type": "AWS::EC2::RouteTable", 732 | "Properties": { 733 | "VpcId": { 734 | "Ref": "vpc" 735 | } 736 | } 737 | }, 738 | "PrivateSubnet2RouteTableAssociation": { 739 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 740 | "Properties": { 741 | "SubnetId": { 742 | "Ref": "PrivateSubnetAz2" 743 | }, 744 | "RouteTableId": { 745 | "Ref": "RouteViaNatAZ2" 746 | } 747 | } 748 | }, 749 | "DefaultSG": { 750 | "Type": "AWS::EC2::SecurityGroup", 751 | "Properties": { 752 | "VpcId": { 753 | "Ref": "vpc" 754 | }, 755 | "GroupDescription": "Default VPC Security Group. Allows only port 443 externally and all traffic from within the Security Group." 756 | } 757 | }, 758 | "AllTrafficSecurityGroupIngress": { 759 | "Type": "AWS::EC2::SecurityGroupIngress", 760 | "Properties": { 761 | "GroupId": { 762 | "Ref": "DefaultSG" 763 | }, 764 | "IpProtocol": "-1", 765 | "ToPort": "-1", 766 | "FromPort": "0", 767 | "SourceSecurityGroupId": { 768 | "Ref": "DefaultSG" 769 | } 770 | } 771 | }, 772 | "HTTPSSecurityGroupIngress": { 773 | "Type": "AWS::EC2::SecurityGroupIngress", 774 | "Properties": { 775 | "GroupId": { 776 | "Ref": "DefaultSG" 777 | }, 778 | "IpProtocol": "tcp", 779 | "ToPort": "443", 780 | "FromPort": "443", 781 | "CidrIp": "0.0.0.0/0" 782 | } 783 | }, 784 | "HTTPSecurityGroupIngress": { 785 | "Type": "AWS::EC2::SecurityGroupIngress", 786 | "Properties": { 787 | "GroupId": { 788 | "Ref": "DefaultSG" 789 | }, 790 | "IpProtocol": "tcp", 791 | "ToPort": "80", 792 | "FromPort": "80", 793 | "CidrIp": "0.0.0.0/0" 794 | } 795 | }, 796 | "SSHSecurityGroupIngress": { 797 | "Type": "AWS::EC2::SecurityGroupIngress", 798 | "Properties": { 799 | "GroupId": { 800 | "Ref": "DefaultSG" 801 | }, 802 | "IpProtocol": "tcp", 803 | "ToPort": "22", 804 | "FromPort": "22", 805 | "CidrIp": "0.0.0.0/0" 806 | } 807 | }, 808 | "WaitHandle01": { 809 | "Type": "AWS::CloudFormation::WaitConditionHandle", 810 | "Properties": {} 811 | }, 812 | "WaitCondition01": { 813 | "Type": "AWS::CloudFormation::WaitCondition", 814 | "DependsOn": "Controller", 815 | "Properties": { 816 | "Handle": { 817 | "Ref": "WaitHandle01" 818 | }, 819 | "Timeout": "2400" 820 | } 821 | }, 822 | "WaitHandle02": { 823 | "Type": "AWS::CloudFormation::WaitConditionHandle", 824 | "Properties": {} 825 | }, 826 | "WaitCondition02": { 827 | "Type": "AWS::CloudFormation::WaitCondition", 828 | "DependsOn": "DTRreplica01", 829 | "Properties": { 830 | "Handle": { 831 | "Ref": "WaitHandle02" 832 | }, 833 | "Timeout": "2400" 834 | } 835 | }, 836 | "WaitHandle03": { 837 | "Type": "AWS::CloudFormation::WaitConditionHandle", 838 | "Properties": {} 839 | }, 840 | "WaitCondition03": { 841 | "Type": "AWS::CloudFormation::WaitCondition", 842 | "DependsOn": "DTRreplica02", 843 | "Properties": { 844 | "Handle": { 845 | "Ref": "WaitHandle03" 846 | }, 847 | "Timeout": "2400" 848 | } 849 | }, 850 | "Jumphost": { 851 | "Type": "AWS::EC2::Instance", 852 | "Properties": { 853 | "AvailabilityZone": { 854 | "Fn::Select": [ 855 | "0", 856 | { 857 | "Fn::GetAZs": { 858 | "Ref": "AWS::Region" 859 | } 860 | } 861 | ] 862 | }, 863 | "InstanceType": { 864 | "Ref": "UCPNodesInstanceType" 865 | }, 866 | "KeyName": { 867 | "Ref": "KeyName" 868 | }, 869 | "ImageId": { 870 | "Fn::FindInMap": [ 871 | "AWSRegionArch2AMI", 872 | { 873 | "Ref": "AWS::Region" 874 | }, 875 | { 876 | "Fn::FindInMap": [ 877 | "AWSInstanceType2Arch", 878 | { 879 | "Ref": "UCPControllersInstanceType" 880 | }, 881 | "Arch" 882 | ] 883 | } 884 | ] 885 | }, 886 | "NetworkInterfaces": [ 887 | { 888 | "AssociatePublicIpAddress": "true", 889 | "DeleteOnTermination": "true", 890 | "DeviceIndex": "0", 891 | "SubnetId": { 892 | "Ref": "PubSubnetAz1" 893 | }, 894 | "GroupSet": [ 895 | { 896 | "Ref": "DefaultSG" 897 | } 898 | ] 899 | } 900 | ], 901 | "Tags": [ 902 | { 903 | "Key": "Name", 904 | "Value": { 905 | "Fn::Join": [ 906 | "-", 907 | [ 908 | { 909 | "Ref": "AWS::StackName" 910 | }, 911 | "Jumphost" 912 | ] 913 | ] 914 | } 915 | } 916 | ] 917 | } 918 | }, 919 | "ControllerRole": { 920 | "Type": "AWS::IAM::Role", 921 | "Properties": { 922 | "AssumeRolePolicyDocument": { 923 | "Version": "2012-10-17", 924 | "Statement": [ 925 | { 926 | "Effect": "Allow", 927 | "Principal": { 928 | "Service": [ 929 | "ec2.amazonaws.com" 930 | ] 931 | }, 932 | "Action": [ 933 | "sts:AssumeRole" 934 | ] 935 | } 936 | ] 937 | }, 938 | "Path": "/", 939 | "Policies": [ 940 | { 941 | "PolicyName": "root", 942 | "PolicyDocument": { 943 | "Version": "2012-10-17", 944 | "Statement": [ 945 | { 946 | "Effect": "Allow", 947 | "Action": [ 948 | "s3:PutObject", 949 | "s3:GetObject" 950 | ], 951 | "Resource": { 952 | "Fn::Join": [ 953 | "", 954 | [ 955 | "arn:aws:s3:::", 956 | { 957 | "Ref": "DDCBucket" 958 | }, 959 | "/*" 960 | ] 961 | ] 962 | } 963 | }, 964 | { 965 | "Effect": "Allow", 966 | "Action": "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 967 | "Resource": "*" 968 | } 969 | ] 970 | } 971 | }, 972 | { 973 | "PolicyName": "AWSLogs", 974 | "PolicyDocument": { 975 | "Version": "2012-10-17", 976 | "Statement": [ 977 | { 978 | "Effect": "Allow", 979 | "Action": [ 980 | "logs:CreateLogStream", 981 | "logs:PutLogEvents" 982 | ], 983 | "Resource": "*" 984 | } 985 | ] 986 | } 987 | } 988 | ] 989 | } 990 | }, 991 | "UCPProfile": { 992 | "Type": "AWS::IAM::InstanceProfile", 993 | "Properties": { 994 | "Path": "/", 995 | "Roles": [ 996 | { 997 | "Ref": "ControllerRole" 998 | } 999 | ] 1000 | } 1001 | }, 1002 | "Controller": { 1003 | "DependsOn": "NatAZ1Route", 1004 | "Type": "AWS::EC2::Instance", 1005 | "Properties": { 1006 | "AvailabilityZone": { 1007 | "Fn::Select": [ 1008 | "0", 1009 | { 1010 | "Fn::GetAZs": { 1011 | "Ref": "AWS::Region" 1012 | } 1013 | } 1014 | ] 1015 | }, 1016 | "InstanceType": { 1017 | "Ref": "UCPControllersInstanceType" 1018 | }, 1019 | "KeyName": { 1020 | "Ref": "KeyName" 1021 | }, 1022 | "ImageId": { 1023 | "Fn::FindInMap": [ 1024 | "AWSRegionArch2AMI", 1025 | { 1026 | "Ref": "AWS::Region" 1027 | }, 1028 | { 1029 | "Fn::FindInMap": [ 1030 | "AWSInstanceType2Arch", 1031 | { 1032 | "Ref": "UCPControllersInstanceType" 1033 | }, 1034 | "Arch" 1035 | ] 1036 | } 1037 | ] 1038 | }, 1039 | "NetworkInterfaces": [ 1040 | { 1041 | "AssociatePublicIpAddress": "true", 1042 | "DeleteOnTermination": "true", 1043 | "DeviceIndex": "0", 1044 | "SubnetId": { 1045 | "Ref": "PrivateSubnetAz1" 1046 | }, 1047 | "GroupSet": [ 1048 | { 1049 | "Ref": "DefaultSG" 1050 | } 1051 | ] 1052 | } 1053 | ], 1054 | "BlockDeviceMappings": [ 1055 | { 1056 | "DeviceName": "/dev/sda1", 1057 | "Ebs": { 1058 | "VolumeSize": { 1059 | "Ref": "RootVolumeSize" 1060 | }, 1061 | "VolumeType": "gp2" 1062 | } 1063 | } 1064 | ], 1065 | "UserData": { 1066 | "Fn::Base64": { 1067 | "Fn::Join": [ 1068 | "\n", 1069 | [ 1070 | "#!/bin/bash -ex", 1071 | "echo 'Installing Docker'", 1072 | "sudo hostname ucp-controller \n", 1073 | "export HOSTNAME=ucp-controller \n", 1074 | "sudo sed -i 's/localhost/ucp-controller/g' /etc/hosts \n", 1075 | "echo $HOSTNAME | sudo tee /etc/hostname \n", 1076 | "curl -sSL https://packages.docker.com/1.12/install.sh | sh \n", 1077 | "sudo usermod -aG docker ubuntu \n", 1078 | "echo 'Install CFN-helper + other packages' ", 1079 | "apt-get -y install python-pip unzip ntp", 1080 | "pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz", 1081 | "cp /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup", 1082 | "chmod +x /etc/init.d/cfn-hup", 1083 | "update-rc.d cfn-hup defaults", 1084 | "service cfn-hup start", 1085 | "echo 'Install AWS CLI'", 1086 | "curl \"https://s3.amazonaws.com/aws-cli/awscli-bundle.zip\" -o \"awscli-bundle.zip\"", 1087 | "unzip awscli-bundle.zip", 1088 | "./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws", 1089 | { 1090 | "Fn::Join": [ 1091 | "", 1092 | [ 1093 | "#!/bin/bash -ex\n", 1094 | "export USERNAME=admin\n", 1095 | "export PASSWORD=ddconaws\n", 1096 | "export PRIVATE_IP=`curl http://169.254.169.254/latest/meta-data/local-ipv4`\n", 1097 | "echo \"Loading License\" \n", 1098 | "export LOGGROUP=", 1099 | { 1100 | "Ref": "LogGroup" 1101 | }, 1102 | "\n", 1103 | "INPUT='", 1104 | { 1105 | "Ref": "License" 1106 | }, 1107 | "'\n", 1108 | "if [[ ${INPUT:0:1} == '{' ]]\n", 1109 | "then echo \"Getting License from $INPUT\" \n", 1110 | "echo \"Using Json Direct Input\" \n", 1111 | "echo $INPUT >> /home/ubuntu/docker_subscription.lic \n", 1112 | "elif [[ ${INPUT:0:4} == \"http\" ]]\n", 1113 | "then echo \"Using s3 license file\" \n", 1114 | "curl -s $INPUT >>/home/ubuntu/docker_subscription.lic \n", 1115 | "else echo \"License input must be a valid s3 location or json license key\" \n", 1116 | "fi \n", 1117 | "sudo docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -v /var/run/docker.sock:/var/run/docker.sock -v /home/ubuntu/docker_subscription.lic:/docker_subscription.lic -e UCP_ADMIN_PASSWORD=$PASSWORD --name ucp docker/ucp:1.1.4 install --debug --host-address $PRIVATE_IP --san $PRIVATE_IP --san ", 1118 | { 1119 | "Ref": "UCPFQDN" 1120 | }, 1121 | " --san ", 1122 | { 1123 | "Fn::GetAtt": [ 1124 | "ControllerElasticLoadBalancer", 1125 | "DNSName" 1126 | ] 1127 | }, 1128 | "\n", 1129 | "sleep 3 \n", 1130 | "echo 'Create Root CA Backup' \n", 1131 | "export ID=$(sudo docker run --rm --name ucp -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 id) \n", 1132 | "sudo docker run --rm --name ucp -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 backup --root-ca-only --id $ID --passphrase $PASSWORD > certs.backup.tar \n", 1133 | "echo 'Copy cert bundle to S3' \n", 1134 | "aws s3 cp certs.backup.tar s3://", 1135 | { 1136 | "Ref": "DDCBucket" 1137 | }, 1138 | "/cabackup/", 1139 | { 1140 | "Ref": "UCPFQDN" 1141 | }, 1142 | ".certs.backup.tar", 1143 | " --region ", 1144 | { 1145 | "Ref": "AWS::Region" 1146 | }, 1147 | "\n", 1148 | "# Wait Handle: Controller tests itself to ensure it's responding before signaling CFN. Try in an infinite loop. Wait Handle will tear down stack if timeout exceeded\n", 1149 | "# Defining function:\n", 1150 | "checkcontroller()\n", 1151 | "{\n", 1152 | "if [[ $(curl --insecure --silent --output /dev/null --write-out '%{http_code}' https://$PRIVATE_IP/_ping) -eq 200 ]]\n", 1153 | "then /usr/local/bin/cfn-signal -s true '", 1154 | { 1155 | "Ref": "WaitHandle01" 1156 | }, 1157 | "'\n", 1158 | "else sleep 3 && echo \"checking Controller until CFN times out..\" && checkcontroller\n", 1159 | "fi\n", 1160 | "}\n", 1161 | "# Calling function:\n", 1162 | "checkcontroller\n", 1163 | "#Register instance with ELB\n", 1164 | "INSTANCE=\"$(curl 169.254.169.254/latest/meta-data/instance-id)\"\n", 1165 | "aws elb register-instances-with-load-balancer --load-balancer-name ", 1166 | { 1167 | "Ref": "ControllerElasticLoadBalancer" 1168 | }, 1169 | " --instances $INSTANCE", 1170 | " --region ", 1171 | { 1172 | "Ref": "AWS::Region" 1173 | } 1174 | ] 1175 | ] 1176 | } 1177 | ] 1178 | ] 1179 | } 1180 | }, 1181 | "IamInstanceProfile": { 1182 | "Ref": "UCPProfile" 1183 | }, 1184 | "Tags": [ 1185 | { 1186 | "Key": "Name", 1187 | "Value": { 1188 | "Fn::Join": [ 1189 | "-", 1190 | [ 1191 | { 1192 | "Ref": "AWS::StackName" 1193 | }, 1194 | "Controller" 1195 | ] 1196 | ] 1197 | } 1198 | } 1199 | ] 1200 | } 1201 | }, 1202 | "ControllerRecoveryAlarm": { 1203 | "Type": "AWS::CloudWatch::Alarm", 1204 | "Properties": { 1205 | "AlarmDescription": "EC2 Autorecovery for UCP Controller. Autorecover if we fail EC2 status checks for 15 minutes.", 1206 | "Namespace": "AWS/EC2", 1207 | "MetricName": "StatusCheckFailed_System", 1208 | "Statistic": "Minimum", 1209 | "Period": "60", 1210 | "EvaluationPeriods": "15", 1211 | "ComparisonOperator": "GreaterThanThreshold", 1212 | "Threshold": "0", 1213 | "AlarmActions": [ 1214 | { 1215 | "Fn::Join": [ 1216 | "", 1217 | [ 1218 | "arn:aws:automate:", 1219 | { 1220 | "Ref": "AWS::Region" 1221 | }, 1222 | ":ec2:recover" 1223 | ] 1224 | ] 1225 | } 1226 | ], 1227 | "Dimensions": [ 1228 | { 1229 | "Name": "InstanceId", 1230 | "Value": { 1231 | "Ref": "Controller" 1232 | } 1233 | } 1234 | ] 1235 | } 1236 | }, 1237 | "UCPNodesElasticLoadBalancer": { 1238 | "Type": "AWS::ElasticLoadBalancing::LoadBalancer", 1239 | "Properties": { 1240 | "Subnets": [ 1241 | { 1242 | "Ref": "PubSubnetAz1" 1243 | }, 1244 | { 1245 | "Ref": "PubSubnetAz2" 1246 | } 1247 | ], 1248 | "ConnectionSettings": { 1249 | "IdleTimeout": 600 1250 | }, 1251 | "Listeners": [ 1252 | { 1253 | "LoadBalancerPort": "443", 1254 | "InstancePort": "443", 1255 | "Protocol": "TCP" 1256 | }, 1257 | { 1258 | "LoadBalancerPort": "80", 1259 | "InstancePort": "80", 1260 | "Protocol": "TCP" 1261 | } 1262 | ], 1263 | "HealthCheck": { 1264 | "Target": "TCP:80", 1265 | "HealthyThreshold": "2", 1266 | "UnhealthyThreshold": "10", 1267 | "Interval": "30", 1268 | "Timeout": "5" 1269 | }, 1270 | "SecurityGroups": [ 1271 | { 1272 | "Ref": "DefaultSG" 1273 | } 1274 | ] 1275 | } 1276 | }, 1277 | "ControllerElasticLoadBalancer": { 1278 | "Type": "AWS::ElasticLoadBalancing::LoadBalancer", 1279 | "Properties": { 1280 | "Subnets": [ 1281 | { 1282 | "Ref": "PubSubnetAz1" 1283 | }, 1284 | { 1285 | "Ref": "PubSubnetAz2" 1286 | } 1287 | ], 1288 | "ConnectionSettings": { 1289 | "IdleTimeout": 600 1290 | }, 1291 | "Listeners": [ 1292 | { 1293 | "LoadBalancerPort": "443", 1294 | "InstancePort": "443", 1295 | "Protocol": "TCP" 1296 | } 1297 | ], 1298 | "HealthCheck": { 1299 | "Target": "HTTPS:443/_ping", 1300 | "HealthyThreshold": "2", 1301 | "UnhealthyThreshold": "10", 1302 | "Interval": "30", 1303 | "Timeout": "5" 1304 | }, 1305 | "SecurityGroups": [ 1306 | { 1307 | "Ref": "DefaultSG" 1308 | } 1309 | ] 1310 | } 1311 | }, 1312 | "UCPNodeAsg": { 1313 | "DependsOn": "UCPNode", 1314 | "Type": "AWS::AutoScaling::AutoScalingGroup", 1315 | "Properties": { 1316 | "VPCZoneIdentifier": [ 1317 | { 1318 | "Fn::Join": [ 1319 | ",", 1320 | [ 1321 | { 1322 | "Ref": "PrivateSubnetAz1" 1323 | }, 1324 | { 1325 | "Ref": "PrivateSubnetAz2" 1326 | } 1327 | ] 1328 | ] 1329 | } 1330 | ], 1331 | "LaunchConfigurationName": { 1332 | "Ref": "UCPNode" 1333 | }, 1334 | "MinSize": "3", 1335 | "MaxSize": "64", 1336 | "DesiredCapacity": { 1337 | "Ref": "ClusterSize" 1338 | }, 1339 | "LoadBalancerNames": [ 1340 | { 1341 | "Ref": "UCPNodesElasticLoadBalancer" 1342 | } 1343 | ], 1344 | "Tags": [ 1345 | { 1346 | "Key": "Name", 1347 | "Value": { 1348 | "Fn::Join": [ 1349 | "-", 1350 | [ 1351 | { 1352 | "Ref": "AWS::StackName" 1353 | }, 1354 | "UCP Node" 1355 | ] 1356 | ] 1357 | }, 1358 | "PropagateAtLaunch": "true" 1359 | } 1360 | ] 1361 | } 1362 | }, 1363 | "UCPNode": { 1364 | "DependsOn": "WaitCondition01", 1365 | "Type": "AWS::AutoScaling::LaunchConfiguration", 1366 | "Properties": { 1367 | "InstanceType": { 1368 | "Ref": "UCPNodesInstanceType" 1369 | }, 1370 | "IamInstanceProfile": { 1371 | "Ref": "UCPProfile" 1372 | }, 1373 | "KeyName": { 1374 | "Ref": "KeyName" 1375 | }, 1376 | "ImageId": { 1377 | "Fn::FindInMap": [ 1378 | "AWSRegionArch2AMI", 1379 | { 1380 | "Ref": "AWS::Region" 1381 | }, 1382 | { 1383 | "Fn::FindInMap": [ 1384 | "AWSInstanceType2Arch", 1385 | { 1386 | "Ref": "UCPNodesInstanceType" 1387 | }, 1388 | "Arch" 1389 | ] 1390 | } 1391 | ] 1392 | }, 1393 | "AssociatePublicIpAddress": "true", 1394 | "SecurityGroups": [ 1395 | { 1396 | "Ref": "DefaultSG" 1397 | } 1398 | ], 1399 | "BlockDeviceMappings": [ 1400 | { 1401 | "DeviceName": "/dev/sda1", 1402 | "Ebs": { 1403 | "VolumeSize": { 1404 | "Ref": "RootVolumeSize" 1405 | }, 1406 | "VolumeType": "gp2" 1407 | } 1408 | } 1409 | ], 1410 | "UserData": { 1411 | "Fn::Base64": { 1412 | "Fn::Join": [ 1413 | "", 1414 | [ 1415 | "#!/bin/bash\n", 1416 | "# Installing Docker and Compose \n", 1417 | "export USERNAME=admin\n", 1418 | "export PASSWORD=ddconaws\n", 1419 | "export PRIVATE_IP=`curl http://169.254.169.254/latest/meta-data/local-ipv4`\n", 1420 | "export INSTANCE_ID=`curl http://169.254.169.254/latest/meta-data/instance-id`\n", 1421 | "sudo hostname ucp-node-$INSTANCE_ID \n", 1422 | "export HOSTNAME=ucp-node-$INSTANCE_ID \n", 1423 | "sudo sed -i 's/localhost/ucp-node-'\"$INSTANCE_ID\"'/g' /etc/hosts \n", 1424 | "echo $HOSTNAME | sudo tee /etc/hostname \n", 1425 | "curl -sSL https://packages.docker.com/1.12/install.sh | sh \n", 1426 | "sudo usermod -aG docker ubuntu \n", 1427 | "apt-get -y install python-pip ntp\n", 1428 | "sudo pip install docker-compose \n", 1429 | "export UCP_CONTROLLER_IP=", 1430 | { 1431 | "Fn::GetAtt": [ 1432 | "Controller", 1433 | "PrivateIp" 1434 | ] 1435 | }, 1436 | "\n", 1437 | "export LOGGROUP=", 1438 | { 1439 | "Ref": "LogGroup" 1440 | }, 1441 | "\n", 1442 | "curl --insecure https://$UCP_CONTROLLER_IP/ca > ca.pem\n", 1443 | "export UCP_FINGERPRINT=$(openssl x509 -in ca.pem -noout -sha256 -fingerprint | awk -F= '{ print $2 }' )\n", 1444 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i --name ucp -e UCP_ADMIN_USER=$USERNAME -e UCP_ADMIN_PASSWORD=$PASSWORD -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 join --url https://$UCP_CONTROLLER_IP --fingerprint $UCP_FINGERPRINT \n", 1445 | "echo 'Installing Interlock+NGINX' \n", 1446 | "wget https://raw.githubusercontent.com/nicolaka/interlock-lbs/master/docker-compose.yml \n", 1447 | "sudo -E docker-compose -p interlock-nginx-$HOSTNAME up -d \n" 1448 | ] 1449 | ] 1450 | } 1451 | } 1452 | } 1453 | }, 1454 | "ControllerReplicaAsg": { 1455 | "DependsOn": "ControllerReplica", 1456 | "Type": "AWS::AutoScaling::AutoScalingGroup", 1457 | "Properties": { 1458 | "VPCZoneIdentifier": [ 1459 | { 1460 | "Fn::Join": [ 1461 | ",", 1462 | [ 1463 | { 1464 | "Ref": "PrivateSubnetAz1" 1465 | }, 1466 | { 1467 | "Ref": "PrivateSubnetAz2" 1468 | } 1469 | ] 1470 | ] 1471 | } 1472 | ], 1473 | "LaunchConfigurationName": { 1474 | "Ref": "ControllerReplica" 1475 | }, 1476 | "MinSize": "2", 1477 | "MaxSize": "2", 1478 | "DesiredCapacity": "2", 1479 | "LoadBalancerNames": [ 1480 | { 1481 | "Ref": "ControllerElasticLoadBalancer" 1482 | } 1483 | ], 1484 | "Tags": [ 1485 | { 1486 | "Key": "Name", 1487 | "Value": { 1488 | "Fn::Join": [ 1489 | "-", 1490 | [ 1491 | { 1492 | "Ref": "AWS::StackName" 1493 | }, 1494 | "ControllerReplica" 1495 | ] 1496 | ] 1497 | }, 1498 | "PropagateAtLaunch": "true" 1499 | } 1500 | ] 1501 | } 1502 | }, 1503 | "ControllerReplica": { 1504 | "DependsOn": "WaitCondition01", 1505 | "Type": "AWS::AutoScaling::LaunchConfiguration", 1506 | "Properties": { 1507 | "InstanceType": { 1508 | "Ref": "UCPControllersInstanceType" 1509 | }, 1510 | "IamInstanceProfile": { 1511 | "Ref": "UCPProfile" 1512 | }, 1513 | "KeyName": { 1514 | "Ref": "KeyName" 1515 | }, 1516 | "ImageId": { 1517 | "Fn::FindInMap": [ 1518 | "AWSRegionArch2AMI", 1519 | { 1520 | "Ref": "AWS::Region" 1521 | }, 1522 | { 1523 | "Fn::FindInMap": [ 1524 | "AWSInstanceType2Arch", 1525 | { 1526 | "Ref": "UCPControllersInstanceType" 1527 | }, 1528 | "Arch" 1529 | ] 1530 | } 1531 | ] 1532 | }, 1533 | "SecurityGroups": [ 1534 | { 1535 | "Ref": "DefaultSG" 1536 | } 1537 | ], 1538 | "BlockDeviceMappings": [ 1539 | { 1540 | "DeviceName": "/dev/sda1", 1541 | "Ebs": { 1542 | "VolumeSize": { 1543 | "Ref": "RootVolumeSize" 1544 | }, 1545 | "VolumeType": "gp2" 1546 | } 1547 | } 1548 | ], 1549 | "UserData": { 1550 | "Fn::Base64": { 1551 | "Fn::Join": [ 1552 | "", 1553 | [ 1554 | "#!/bin/bash\n", 1555 | "echo 'Installing Docker' \n", 1556 | "export INSTANCE_ID=`curl http://169.254.169.254/latest/meta-data/instance-id`\n", 1557 | "export PRIVATE_IP=`curl http://169.254.169.254/latest/meta-data/local-ipv4`\n", 1558 | "sudo hostname ucp-controller-replica-$INSTANCE_ID \n", 1559 | "export HOSTNAME=ucp-controller-replica-$INSTANCE_ID \n", 1560 | "sudo sed -i 's/localhost/ucp-controller-replica-'\"$INSTANCE_ID\"'/g' /etc/hosts \n", 1561 | "echo $HOSTNAME | sudo tee /etc/hostname \n", 1562 | "curl -sSL https://packages.docker.com/1.12/install.sh | sh \n", 1563 | "sudo usermod -aG docker ubuntu \n", 1564 | "echo 'Install CFN-helper Package' \n", 1565 | "apt-get -y install python-pip unzip ntp \n", 1566 | "pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz \n", 1567 | "cp /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup \n", 1568 | "chmod +x /etc/init.d/cfn-hup \n", 1569 | "update-rc.d cfn-hup defaults \n", 1570 | "service cfn-hup start \n", 1571 | "echo 'Install AWS CLI' \n", 1572 | "curl \"https://s3.amazonaws.com/aws-cli/awscli-bundle.zip\" -o \"awscli-bundle.zip\" \n", 1573 | "unzip awscli-bundle.zip \n", 1574 | "./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws \n", 1575 | "export LOGGROUP=", 1576 | { 1577 | "Ref": "LogGroup" 1578 | }, 1579 | "\n", 1580 | "export UCP_URL=https://", 1581 | { 1582 | "Fn::GetAtt": [ 1583 | "Controller", 1584 | "PrivateIp" 1585 | ] 1586 | }, 1587 | "\n", 1588 | "export USERNAME=admin\n", 1589 | "export PASSWORD=ddconaws\n", 1590 | "curl --insecure $UCP_URL/ca > ca.pem\n", 1591 | "export UCP_FINGERPRINT=$(openssl x509 -in ca.pem -noout -sha256 -fingerprint | awk -F= '{ print $2 }' )\n", 1592 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i --name ucp -e UCP_ADMIN_USER=$USERNAME -e UCP_ADMIN_PASSWORD=$PASSWORD -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 join --replica --url $UCP_URL --fingerprint $UCP_FINGERPRINT --san ", 1593 | { 1594 | "Ref": "UCPFQDN" 1595 | }, 1596 | " --san ", 1597 | { 1598 | "Fn::GetAtt": [ 1599 | "ControllerElasticLoadBalancer", 1600 | "DNSName" 1601 | ] 1602 | }, 1603 | "\n", 1604 | "echo 'Downloading Root CA from S3' \n", 1605 | "aws s3 cp s3://", 1606 | { 1607 | "Ref": "DDCBucket" 1608 | }, 1609 | "/cabackup/", 1610 | { 1611 | "Ref": "UCPFQDN" 1612 | }, 1613 | ".certs.backup.tar ", 1614 | "certs.backup.tar ", 1615 | " --region ", 1616 | { 1617 | "Ref": "AWS::Region" 1618 | }, 1619 | "\n", 1620 | "echo 'Restoring Root CA' \n", 1621 | "export ID=$(sudo docker run --rm --name ucp -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 id) \n", 1622 | "sudo docker run -i --rm --name ucp -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 restore --id $ID --root-ca-only --passphrase 'ddconaws' < certs.backup.tar \n", 1623 | "echo 'Finished Restoring CAs'\n", 1624 | "#Register instance with ELB\n", 1625 | "INSTANCE=\"$(curl 169.254.169.254/latest/meta-data/instance-id)\"\n", 1626 | "aws elb register-instances-with-load-balancer --load-balancer-name ", 1627 | { 1628 | "Ref": "ControllerElasticLoadBalancer" 1629 | }, 1630 | " --instances $INSTANCE", 1631 | " --region ", 1632 | { 1633 | "Ref": "AWS::Region" 1634 | }, 1635 | "\n" 1636 | ] 1637 | ] 1638 | } 1639 | } 1640 | } 1641 | }, 1642 | "DTRreplica01": { 1643 | "DependsOn": "WaitCondition01", 1644 | "Type": "AWS::EC2::Instance", 1645 | "Properties": { 1646 | "AvailabilityZone": { 1647 | "Fn::Select": [ 1648 | "0", 1649 | { 1650 | "Fn::GetAZs": { 1651 | "Ref": "AWS::Region" 1652 | } 1653 | } 1654 | ] 1655 | }, 1656 | "InstanceType": { 1657 | "Ref": "DTRInstanceType" 1658 | }, 1659 | "KeyName": { 1660 | "Ref": "KeyName" 1661 | }, 1662 | "ImageId": { 1663 | "Fn::FindInMap": [ 1664 | "AWSRegionArch2AMI", 1665 | { 1666 | "Ref": "AWS::Region" 1667 | }, 1668 | { 1669 | "Fn::FindInMap": [ 1670 | "AWSInstanceType2Arch", 1671 | { 1672 | "Ref": "DTRInstanceType" 1673 | }, 1674 | "Arch" 1675 | ] 1676 | } 1677 | ] 1678 | }, 1679 | "NetworkInterfaces": [ 1680 | { 1681 | "AssociatePublicIpAddress": "true", 1682 | "DeleteOnTermination": "true", 1683 | "DeviceIndex": "0", 1684 | "SubnetId": { 1685 | "Ref": "PrivateSubnetAz1" 1686 | }, 1687 | "GroupSet": [ 1688 | { 1689 | "Ref": "DefaultSG" 1690 | } 1691 | ] 1692 | } 1693 | ], 1694 | "BlockDeviceMappings": [ 1695 | { 1696 | "DeviceName": "/dev/sda1", 1697 | "Ebs": { 1698 | "VolumeSize": { 1699 | "Ref": "RootVolumeSize" 1700 | }, 1701 | "VolumeType": "gp2" 1702 | } 1703 | } 1704 | ], 1705 | "UserData": { 1706 | "Fn::Base64": { 1707 | "Fn::Join": [ 1708 | "\n", 1709 | [ 1710 | "#!/bin/bash -ex", 1711 | "echo 'Installing Docker'", 1712 | "sudo hostname dtr-replica-01\n", 1713 | "export HOSTNAME=dtr-replica-01 \n", 1714 | "sudo sed -i 's/localhost/dtr-replica-01/g' /etc/hosts \n", 1715 | "echo $HOSTNAME | sudo tee /etc/hostname \n", 1716 | "curl -sSL https://packages.docker.com/1.12/install.sh | sh \n", 1717 | "sudo usermod -aG docker ubuntu \n", 1718 | "echo 'Install CFN-helper Package' ", 1719 | "apt-get -y install python-pip unzip ntp", 1720 | "pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz", 1721 | "cp /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup", 1722 | "chmod +x /etc/init.d/cfn-hup", 1723 | "update-rc.d cfn-hup defaults", 1724 | "service cfn-hup start", 1725 | "echo 'Install AWS CLI'", 1726 | "curl \"https://s3.amazonaws.com/aws-cli/awscli-bundle.zip\" -o \"awscli-bundle.zip\"", 1727 | "unzip awscli-bundle.zip", 1728 | "./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws", 1729 | { 1730 | "Fn::Join": [ 1731 | "", 1732 | [ 1733 | "#!/bin/bash\n", 1734 | "export USERNAME=admin\n", 1735 | "export PASSWORD=ddconaws\n", 1736 | "export PRIVATE_IP=`curl http://169.254.169.254/latest/meta-data/local-ipv4`\n", 1737 | "export S3_BUCKET_NAME=", 1738 | { 1739 | "Ref": "DDCBucket" 1740 | }, 1741 | "\n", 1742 | "export UCP_URL=https://", 1743 | { 1744 | "Fn::GetAtt": [ 1745 | "Controller", 1746 | "PrivateIp" 1747 | ] 1748 | }, 1749 | "\n", 1750 | "export DTR_ELB_DNS=", 1751 | { 1752 | "Fn::GetAtt": [ 1753 | "DTRElasticLoadBalancer", 1754 | "DNSName" 1755 | ] 1756 | }, 1757 | "\n", 1758 | "export UCP_ELB_DNS=", 1759 | { 1760 | "Fn::GetAtt": [ 1761 | "ControllerElasticLoadBalancer", 1762 | "DNSName" 1763 | ] 1764 | }, 1765 | "\n", 1766 | "export REGION=", 1767 | { 1768 | "Ref": "AWS::Region" 1769 | }, 1770 | "\n", 1771 | "export LOGGROUP=", 1772 | { 1773 | "Ref": "LogGroup" 1774 | }, 1775 | "\n", 1776 | "curl --insecure $UCP_URL/ca > ca.pem\n", 1777 | "export UCP_FINGERPRINT=$(openssl x509 -in ca.pem -noout -sha256 -fingerprint | awk -F= '{ print $2 }' )\n", 1778 | "# Installing UCP Node\n", 1779 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i --name ucp -e UCP_ADMIN_USER=$USERNAME -e UCP_ADMIN_PASSWORD=$PASSWORD -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 join --debug --url $UCP_URL --fingerprint $UCP_FINGERPRINT \n", 1780 | "#Register instance with ELB\n", 1781 | "INSTANCE=\"$(curl 169.254.169.254/latest/meta-data/instance-id)\"\n", 1782 | "aws elb register-instances-with-load-balancer --load-balancer-name ", 1783 | { 1784 | "Ref": "DTRElasticLoadBalancer" 1785 | }, 1786 | " --instances $INSTANCE", 1787 | " --region $REGION", 1788 | "\n", 1789 | "# Installing DTR Replica\n", 1790 | "sleep 60 \n", 1791 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i docker/dtr:2.0.3 install --debug --ucp-url https://$UCP_ELB_DNS --ucp-node dtr-replica-01 --dtr-external-url $DTR_ELB_DNS:443 --ucp-username $USERNAME --ucp-password $PASSWORD --ucp-insecure-tls --replica-id 000000000001 \n", 1792 | "# Wait Handle: DTR tests itself to ensure it's responding before signaling CFN. Try in an infinite loop. Wait Handle will tear down stack if timeout exceeded\n", 1793 | "# Defining function:\n", 1794 | "checkcontroller()\n", 1795 | "{\n", 1796 | "if [[ $(curl --insecure --silent --output /dev/null --write-out '%{http_code}' https://$PRIVATE_IP/load_balancer_status) -eq 200 ]]\n", 1797 | "then /usr/local/bin/cfn-signal -s true '", 1798 | { 1799 | "Ref": "WaitHandle02" 1800 | }, 1801 | "'\n", 1802 | "else sleep 3 && echo \"checking Controller until CFN times out..\" && checkcontroller\n", 1803 | "fi\n", 1804 | "}\n", 1805 | "# Calling function:\n", 1806 | "checkcontroller\n" 1807 | ] 1808 | ] 1809 | } 1810 | ] 1811 | ] 1812 | } 1813 | }, 1814 | "IamInstanceProfile": { 1815 | "Ref": "DTRReplicaProfile" 1816 | }, 1817 | "Tags": [ 1818 | { 1819 | "Key": "Name", 1820 | "Value": { 1821 | "Fn::Join": [ 1822 | "-", 1823 | [ 1824 | { 1825 | "Ref": "AWS::StackName" 1826 | }, 1827 | "DTRreplica01" 1828 | ] 1829 | ] 1830 | } 1831 | } 1832 | ] 1833 | } 1834 | }, 1835 | "DTRReplicaRole": { 1836 | "Type": "AWS::IAM::Role", 1837 | "Properties": { 1838 | "AssumeRolePolicyDocument": { 1839 | "Version": "2012-10-17", 1840 | "Statement": [ 1841 | { 1842 | "Effect": "Allow", 1843 | "Principal": { 1844 | "Service": [ 1845 | "ec2.amazonaws.com" 1846 | ] 1847 | }, 1848 | "Action": [ 1849 | "sts:AssumeRole" 1850 | ] 1851 | } 1852 | ] 1853 | }, 1854 | "Path": "/", 1855 | "Policies": [ 1856 | { 1857 | "PolicyName": "root", 1858 | "PolicyDocument": { 1859 | "Version": "2012-10-17", 1860 | "Statement": [ 1861 | { 1862 | "Effect": "Allow", 1863 | "Action": "elasticloadbalancing:RegisterInstancesWithLoadBalancer", 1864 | "Resource": "*" 1865 | }, 1866 | { 1867 | "Effect": "Allow", 1868 | "Action": [ 1869 | "s3:*" 1870 | ], 1871 | "Resource": "*" 1872 | } 1873 | ] 1874 | } 1875 | }, 1876 | { 1877 | "PolicyName": "AWSLogs", 1878 | "PolicyDocument": { 1879 | "Version": "2012-10-17", 1880 | "Statement": [ 1881 | { 1882 | "Effect": "Allow", 1883 | "Action": [ 1884 | "logs:CreateLogStream", 1885 | "logs:PutLogEvents" 1886 | ], 1887 | "Resource": "*" 1888 | } 1889 | ] 1890 | } 1891 | } 1892 | ] 1893 | } 1894 | }, 1895 | "DTRReplicaProfile": { 1896 | "Type": "AWS::IAM::InstanceProfile", 1897 | "Properties": { 1898 | "Path": "/", 1899 | "Roles": [ 1900 | { 1901 | "Ref": "DTRReplicaRole" 1902 | } 1903 | ] 1904 | } 1905 | }, 1906 | "DTRreplica02": { 1907 | "DependsOn": "WaitCondition02", 1908 | "Type": "AWS::EC2::Instance", 1909 | "Properties": { 1910 | "AvailabilityZone": { 1911 | "Fn::Select": [ 1912 | "1", 1913 | { 1914 | "Fn::GetAZs": { 1915 | "Ref": "AWS::Region" 1916 | } 1917 | } 1918 | ] 1919 | }, 1920 | "InstanceType": { 1921 | "Ref": "DTRInstanceType" 1922 | }, 1923 | "KeyName": { 1924 | "Ref": "KeyName" 1925 | }, 1926 | "ImageId": { 1927 | "Fn::FindInMap": [ 1928 | "AWSRegionArch2AMI", 1929 | { 1930 | "Ref": "AWS::Region" 1931 | }, 1932 | { 1933 | "Fn::FindInMap": [ 1934 | "AWSInstanceType2Arch", 1935 | { 1936 | "Ref": "DTRInstanceType" 1937 | }, 1938 | "Arch" 1939 | ] 1940 | } 1941 | ] 1942 | }, 1943 | "NetworkInterfaces": [ 1944 | { 1945 | "AssociatePublicIpAddress": "true", 1946 | "DeleteOnTermination": "true", 1947 | "DeviceIndex": "0", 1948 | "SubnetId": { 1949 | "Ref": "PrivateSubnetAz2" 1950 | }, 1951 | "GroupSet": [ 1952 | { 1953 | "Ref": "DefaultSG" 1954 | } 1955 | ] 1956 | } 1957 | ], 1958 | "BlockDeviceMappings": [ 1959 | { 1960 | "DeviceName": "/dev/sda1", 1961 | "Ebs": { 1962 | "VolumeSize": { 1963 | "Ref": "RootVolumeSize" 1964 | }, 1965 | "VolumeType": "gp2" 1966 | } 1967 | } 1968 | ], 1969 | "UserData": { 1970 | "Fn::Base64": { 1971 | "Fn::Join": [ 1972 | "\n", 1973 | [ 1974 | "#!/bin/bash -ex", 1975 | "echo 'Installing Docker'", 1976 | "sudo hostname dtr-replica-02\n", 1977 | "export HOSTNAME=dtr-replica-02 \n", 1978 | "sudo sed -i 's/localhost/dtr-replica-02/g' /etc/hosts \n", 1979 | "echo $HOSTNAME | sudo tee /etc/hostname \n", 1980 | "curl -sSL https://packages.docker.com/1.12/install.sh | sh \n", 1981 | "sudo usermod -aG docker ubuntu \n", 1982 | "echo 'Install CFN-helper Package' ", 1983 | "apt-get -y install python-pip unzip ntp", 1984 | "pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz", 1985 | "cp /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup", 1986 | "chmod +x /etc/init.d/cfn-hup", 1987 | "update-rc.d cfn-hup defaults", 1988 | "service cfn-hup start", 1989 | "echo 'Install AWS CLI'", 1990 | "curl \"https://s3.amazonaws.com/aws-cli/awscli-bundle.zip\" -o \"awscli-bundle.zip\"", 1991 | "unzip awscli-bundle.zip", 1992 | "./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws", 1993 | { 1994 | "Fn::Join": [ 1995 | "", 1996 | [ 1997 | "#!/bin/bash\n", 1998 | "export USERNAME=admin\n", 1999 | "export PASSWORD=ddconaws\n", 2000 | "export PRIVATE_IP=`curl http://169.254.169.254/latest/meta-data/local-ipv4` \n", 2001 | "export UCP_URL=https://", 2002 | { 2003 | "Fn::GetAtt": [ 2004 | "Controller", 2005 | "PrivateIp" 2006 | ] 2007 | }, 2008 | "\n", 2009 | "export DTR_ELB_DNS=", 2010 | { 2011 | "Fn::GetAtt": [ 2012 | "DTRElasticLoadBalancer", 2013 | "DNSName" 2014 | ] 2015 | }, 2016 | "\n", 2017 | "export UCP_ELB_DNS=", 2018 | { 2019 | "Fn::GetAtt": [ 2020 | "ControllerElasticLoadBalancer", 2021 | "DNSName" 2022 | ] 2023 | }, 2024 | "\n", 2025 | "export LOGGROUP=", 2026 | { 2027 | "Ref": "LogGroup" 2028 | }, 2029 | "\n", 2030 | "curl --insecure $UCP_URL/ca > ca.pem\n", 2031 | "export UCP_FINGERPRINT=$(openssl x509 -in ca.pem -noout -sha256 -fingerprint | awk -F= '{ print $2 }' )\n", 2032 | "# Installing UCP Node\n", 2033 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i --name ucp -e UCP_ADMIN_USER=$USERNAME -e UCP_ADMIN_PASSWORD=$PASSWORD -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 join --debug --url $UCP_URL --fingerprint $UCP_FINGERPRINT \n", 2034 | "# Installing DTR Replica\n", 2035 | "sleep 60 \n", 2036 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i docker/dtr:2.0.3 join --debug --ucp-url https://$UCP_ELB_DNS --ucp-node dtr-replica-02 --ucp-username $USERNAME --ucp-password $PASSWORD --ucp-insecure-tls --replica-id 000000000002 --existing-replica-id 000000000001 \n", 2037 | "# Wait Handle: DTR tests itself to ensure it's responding before signaling CFN. Try in an infinite loop. Wait Handle will tear down stack if timeout exceeded\n", 2038 | "# Defining function:\n", 2039 | "checkcontroller()\n", 2040 | "{\n", 2041 | "if [[ $(curl --insecure --silent --output /dev/null --write-out '%{http_code}' https://$PRIVATE_IP/load_balancer_status) -eq 200 ]]\n", 2042 | "then /usr/local/bin/cfn-signal -s true '", 2043 | { 2044 | "Ref": "WaitHandle03" 2045 | }, 2046 | "'\n", 2047 | "else sleep 3 && echo \"checking Controller until CFN times out..\" && checkcontroller\n", 2048 | "fi\n", 2049 | "}\n", 2050 | "# Calling function:\n", 2051 | "checkcontroller\n", 2052 | "#Register instance with ELB\n", 2053 | "INSTANCE=\"$(curl 169.254.169.254/latest/meta-data/instance-id)\"\n", 2054 | "aws elb register-instances-with-load-balancer --load-balancer-name ", 2055 | { 2056 | "Ref": "DTRElasticLoadBalancer" 2057 | }, 2058 | " --instances $INSTANCE", 2059 | " --region ", 2060 | { 2061 | "Ref": "AWS::Region" 2062 | } 2063 | ] 2064 | ] 2065 | } 2066 | ] 2067 | ] 2068 | } 2069 | }, 2070 | "IamInstanceProfile": { 2071 | "Ref": "DTRReplicaProfile" 2072 | }, 2073 | "Tags": [ 2074 | { 2075 | "Key": "Name", 2076 | "Value": { 2077 | "Fn::Join": [ 2078 | "-", 2079 | [ 2080 | { 2081 | "Ref": "AWS::StackName" 2082 | }, 2083 | "DTRreplica02" 2084 | ] 2085 | ] 2086 | } 2087 | } 2088 | ] 2089 | } 2090 | }, 2091 | "DTRreplica03": { 2092 | "DependsOn": "WaitCondition03", 2093 | "Type": "AWS::EC2::Instance", 2094 | "Properties": { 2095 | "AvailabilityZone": { 2096 | "Fn::Select": [ 2097 | "0", 2098 | { 2099 | "Fn::GetAZs": { 2100 | "Ref": "AWS::Region" 2101 | } 2102 | } 2103 | ] 2104 | }, 2105 | "InstanceType": { 2106 | "Ref": "DTRInstanceType" 2107 | }, 2108 | "KeyName": { 2109 | "Ref": "KeyName" 2110 | }, 2111 | "ImageId": { 2112 | "Fn::FindInMap": [ 2113 | "AWSRegionArch2AMI", 2114 | { 2115 | "Ref": "AWS::Region" 2116 | }, 2117 | { 2118 | "Fn::FindInMap": [ 2119 | "AWSInstanceType2Arch", 2120 | { 2121 | "Ref": "DTRInstanceType" 2122 | }, 2123 | "Arch" 2124 | ] 2125 | } 2126 | ] 2127 | }, 2128 | "NetworkInterfaces": [ 2129 | { 2130 | "AssociatePublicIpAddress": "true", 2131 | "DeleteOnTermination": "true", 2132 | "DeviceIndex": "0", 2133 | "SubnetId": { 2134 | "Ref": "PrivateSubnetAz1" 2135 | }, 2136 | "GroupSet": [ 2137 | { 2138 | "Ref": "DefaultSG" 2139 | } 2140 | ] 2141 | } 2142 | ], 2143 | "BlockDeviceMappings": [ 2144 | { 2145 | "DeviceName": "/dev/sda1", 2146 | "Ebs": { 2147 | "VolumeSize": { 2148 | "Ref": "RootVolumeSize" 2149 | }, 2150 | "VolumeType": "gp2" 2151 | } 2152 | } 2153 | ], 2154 | "UserData": { 2155 | "Fn::Base64": { 2156 | "Fn::Join": [ 2157 | "\n", 2158 | [ 2159 | "#!/bin/bash -ex", 2160 | "echo 'Installing Docker' ", 2161 | "sudo hostname dtr-replica-03\n", 2162 | "export HOSTNAME=dtr-replica-03 \n", 2163 | "sudo sed -i 's/localhost/dtr-replica-03/g' /etc/hosts \n", 2164 | "echo $HOSTNAME | sudo tee /etc/hostname \n", 2165 | "curl -sSL https://packages.docker.com/1.12/install.sh | sh \n", 2166 | "sudo usermod -aG docker ubuntu \n", 2167 | "echo 'Install CFN-helper Package' ", 2168 | "apt-get -y install python-pip unzip ntp", 2169 | "pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz", 2170 | "cp /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup", 2171 | "chmod +x /etc/init.d/cfn-hup", 2172 | "update-rc.d cfn-hup defaults", 2173 | "service cfn-hup start", 2174 | "echo 'Install AWS CLI'", 2175 | "curl \"https://s3.amazonaws.com/aws-cli/awscli-bundle.zip\" -o \"awscli-bundle.zip\"", 2176 | "unzip awscli-bundle.zip", 2177 | "./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws", 2178 | { 2179 | "Fn::Join": [ 2180 | "", 2181 | [ 2182 | "#!/bin/bash\n", 2183 | "export USERNAME=admin\n", 2184 | "export PASSWORD=ddconaws\n", 2185 | "export PRIVATE_IP=`curl http://169.254.169.254/latest/meta-data/local-ipv4` \n", 2186 | "export UCP_URL=https://", 2187 | { 2188 | "Fn::GetAtt": [ 2189 | "Controller", 2190 | "PrivateIp" 2191 | ] 2192 | }, 2193 | "\n", 2194 | "export DTR_ELB_DNS=", 2195 | { 2196 | "Fn::GetAtt": [ 2197 | "DTRElasticLoadBalancer", 2198 | "DNSName" 2199 | ] 2200 | }, 2201 | "\n", 2202 | "export UCP_ELB_DNS=", 2203 | { 2204 | "Fn::GetAtt": [ 2205 | "ControllerElasticLoadBalancer", 2206 | "DNSName" 2207 | ] 2208 | }, 2209 | "\n", 2210 | "export S3_BUCKET_NAME=", 2211 | { 2212 | "Ref": "DDCBucket" 2213 | }, 2214 | "\n", 2215 | "export LOGGROUP=", 2216 | { 2217 | "Ref": "LogGroup" 2218 | }, 2219 | "\n", 2220 | "export REGION=", 2221 | { 2222 | "Ref": "AWS::Region" 2223 | }, 2224 | "\n", 2225 | "curl --insecure $UCP_URL/ca > ca.pem\n", 2226 | "export UCP_FINGERPRINT=$(openssl x509 -in ca.pem -noout -sha256 -fingerprint | awk -F= '{ print $2 }' )\n", 2227 | "# Installing UCP Node\n", 2228 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i --name ucp -e UCP_ADMIN_USER=$USERNAME -e UCP_ADMIN_PASSWORD=$PASSWORD -v /var/run/docker.sock:/var/run/docker.sock docker/ucp:1.1.4 join --debug --url $UCP_URL --fingerprint $UCP_FINGERPRINT \n", 2229 | "# Installing DTR Replica\n", 2230 | "sleep 60 \n", 2231 | "sudo -E docker run --rm --log-driver=awslogs --log-opt awslogs-group=$LOGGROUP -i docker/dtr:2.0.3 join --debug --ucp-url https://$UCP_ELB_DNS --ucp-node dtr-replica-03 --ucp-username $USERNAME --ucp-password $PASSWORD --ucp-insecure-tls --replica-id 000000000003 --existing-replica-id 000000000001 \n", 2232 | "#Register instance with ELB\n", 2233 | "INSTANCE=\"$(curl 169.254.169.254/latest/meta-data/instance-id)\"\n", 2234 | "aws elb register-instances-with-load-balancer --load-balancer-name ", 2235 | { 2236 | "Ref": "DTRElasticLoadBalancer" 2237 | }, 2238 | " --instances $INSTANCE", 2239 | " --region ", 2240 | { 2241 | "Ref": "AWS::Region" 2242 | }, 2243 | "\n", 2244 | "echo 'Configuring Storage Backend with S3' \n", 2245 | "docker run --rm -e DTR_ELB_DNS=$DTR_ELB_DNS -e S3_BUCKET_NAME=$S3_BUCKET_NAME -e REGION=$REGION -e USERNAME=$USERNAME -e PASSWORD=$PASSWORD nicolaka/dtrs3config:dfa194f845024afdb3b148ec6048ff05" 2246 | ] 2247 | ] 2248 | } 2249 | ] 2250 | ] 2251 | } 2252 | }, 2253 | "IamInstanceProfile": { 2254 | "Ref": "DTRReplicaProfile" 2255 | }, 2256 | "Tags": [ 2257 | { 2258 | "Key": "Name", 2259 | "Value": { 2260 | "Fn::Join": [ 2261 | "-", 2262 | [ 2263 | { 2264 | "Ref": "AWS::StackName" 2265 | }, 2266 | "DTRreplica03" 2267 | ] 2268 | ] 2269 | } 2270 | } 2271 | ] 2272 | } 2273 | }, 2274 | "DTRElasticLoadBalancer": { 2275 | "Type": "AWS::ElasticLoadBalancing::LoadBalancer", 2276 | "Properties": { 2277 | "Subnets": [ 2278 | { 2279 | "Ref": "PubSubnetAz1" 2280 | }, 2281 | { 2282 | "Ref": "PubSubnetAz2" 2283 | } 2284 | ], 2285 | "ConnectionSettings": { 2286 | "IdleTimeout": 600 2287 | }, 2288 | "Listeners": [ 2289 | { 2290 | "LoadBalancerPort": "443", 2291 | "InstancePort": "443", 2292 | "Protocol": "TCP" 2293 | } 2294 | ], 2295 | "HealthCheck": { 2296 | "Target": "HTTPS:443/health", 2297 | "HealthyThreshold": "2", 2298 | "UnhealthyThreshold": "10", 2299 | "Interval": "30", 2300 | "Timeout": "5" 2301 | }, 2302 | "SecurityGroups": [ 2303 | { 2304 | "Ref": "DefaultSG" 2305 | } 2306 | ] 2307 | } 2308 | }, 2309 | "DTRreplica01RecoveryAlarm": { 2310 | "Type": "AWS::CloudWatch::Alarm", 2311 | "Properties": { 2312 | "AlarmDescription": "EC2 Autorecovery for DTR Replica. Autorecover if we fail EC2 status checks for 15 minutes.", 2313 | "Namespace": "AWS/EC2", 2314 | "MetricName": "StatusCheckFailed_System", 2315 | "Statistic": "Minimum", 2316 | "Period": "60", 2317 | "EvaluationPeriods": "15", 2318 | "ComparisonOperator": "GreaterThanThreshold", 2319 | "Threshold": "0", 2320 | "AlarmActions": [ 2321 | { 2322 | "Fn::Join": [ 2323 | "", 2324 | [ 2325 | "arn:aws:automate:", 2326 | { 2327 | "Ref": "AWS::Region" 2328 | }, 2329 | ":ec2:recover" 2330 | ] 2331 | ] 2332 | } 2333 | ], 2334 | "Dimensions": [ 2335 | { 2336 | "Name": "InstanceId", 2337 | "Value": { 2338 | "Ref": "DTRreplica01" 2339 | } 2340 | } 2341 | ] 2342 | } 2343 | }, 2344 | "LogGroup": { 2345 | "Type": "AWS::Logs::LogGroup", 2346 | "Properties": { 2347 | "LogGroupName": "DDCLogGroup", 2348 | "RetentionInDays": 14 2349 | } 2350 | } 2351 | }, 2352 | "Outputs": { 2353 | "Username": { 2354 | "Description": "Default UCP and DTR username", 2355 | "Value": "admin" 2356 | }, 2357 | "Password": { 2358 | "Description": "Default UCP and DTR password. Please Change it!", 2359 | "Value": "ddconaws" 2360 | }, 2361 | "Jumphost": { 2362 | "Description": "Jumphost ssh details to ssh into DDC nodes", 2363 | "Value": { 2364 | "Fn::Join": [ 2365 | "", 2366 | [ 2367 | "ssh -i ", 2368 | { 2369 | "Ref": "KeyName" 2370 | }, 2371 | ".pem ubuntu@", 2372 | { 2373 | "Fn::GetAtt": [ 2374 | "Jumphost", 2375 | "PublicIp" 2376 | ] 2377 | } 2378 | ] 2379 | ] 2380 | } 2381 | }, 2382 | "DTR": { 2383 | "Description": "DTR URL", 2384 | "Value": { 2385 | "Fn::Join": [ 2386 | "", 2387 | [ 2388 | "https://", 2389 | { 2390 | "Fn::GetAtt": [ 2391 | "DTRElasticLoadBalancer", 2392 | "DNSName" 2393 | ] 2394 | } 2395 | ] 2396 | ] 2397 | } 2398 | }, 2399 | "APP": { 2400 | "Description": "Application ELB URL", 2401 | "Value": { 2402 | "Fn::Join": [ 2403 | "", 2404 | [ 2405 | "http://", 2406 | { 2407 | "Fn::GetAtt": [ 2408 | "UCPNodesElasticLoadBalancer", 2409 | "DNSName" 2410 | ] 2411 | } 2412 | ] 2413 | ] 2414 | } 2415 | }, 2416 | "UCP": { 2417 | "Description": "UCP Console URL", 2418 | "Value": { 2419 | "Fn::Join": [ 2420 | "", 2421 | [ 2422 | "https://", 2423 | { 2424 | "Fn::GetAtt": [ 2425 | "ControllerElasticLoadBalancer", 2426 | "DNSName" 2427 | ] 2428 | } 2429 | ] 2430 | ] 2431 | } 2432 | } 2433 | } 2434 | } -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/.DS_Store -------------------------------------------------------------------------------- /images/design_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/design_0.png -------------------------------------------------------------------------------- /images/design_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/design_2.png -------------------------------------------------------------------------------- /images/design_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/design_3.png -------------------------------------------------------------------------------- /images/ui_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/ui_0.png -------------------------------------------------------------------------------- /images/v1.1_rest_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/v1.1_rest_results.png -------------------------------------------------------------------------------- /images/v1.3_test_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolaka/ddc-aws/2a1ba4f1c5064a22f30554cfcccfa9de166a5991/images/v1.3_test_results.png --------------------------------------------------------------------------------