├── .gitignore ├── docker ├── simple-api │ ├── app │ │ ├── version.txt │ │ ├── requirements.txt │ │ └── webserver.py │ ├── Dockerfile │ └── Makefile └── simple-gateway │ ├── app │ ├── version.txt │ ├── requirements.txt │ └── webserver.py │ ├── Dockerfile │ └── Makefile ├── architecture.png ├── docker-upgrade.png ├── terraform ├── models │ ├── vpc │ │ ├── aws.tf │ │ ├── var.tf │ │ ├── bastion.tf │ │ ├── private-subnet.tf │ │ └── public-subnet.tf │ ├── registry │ │ ├── aws.tf │ │ ├── simple-api.tf │ │ └── simple-gateway.tf │ ├── terraform │ │ ├── aws.tf │ │ └── s3.tf │ ├── autodiscovery │ │ ├── aws.tf │ │ ├── consul.tf │ │ └── discovery-cluster.tf │ ├── service-simple │ │ ├── aws.tf │ │ └── service.tf │ └── templates │ │ ├── cluster.tpl │ │ └── consul.tpl ├── configurations │ └── resetlogs │ │ └── staging │ │ └── env.tfvars └── deployments │ ├── services │ ├── infrastructure.tf │ └── components.tf │ ├── terraform │ └── state.tf │ └── infrastructure │ └── components.tf ├── bin └── confirm ├── LICENSE ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .terraform -------------------------------------------------------------------------------- /docker/simple-api/app/version.txt: -------------------------------------------------------------------------------- 1 | 0.0.10 2 | -------------------------------------------------------------------------------- /docker/simple-gateway/app/version.txt: -------------------------------------------------------------------------------- 1 | 0.0.6 2 | -------------------------------------------------------------------------------- /docker/simple-gateway/app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | requests 3 | -------------------------------------------------------------------------------- /docker/simple-api/app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | uptime 3 | requests 4 | -------------------------------------------------------------------------------- /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregoryguillou/ecs-unleashed/HEAD/architecture.png -------------------------------------------------------------------------------- /docker-upgrade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gregoryguillou/ecs-unleashed/HEAD/docker-upgrade.png -------------------------------------------------------------------------------- /terraform/models/vpc/aws.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "${var.account}-${var.shortname}" 3 | region = "${var.region}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/models/registry/aws.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "${var.account}-${var.shortname}" 3 | region = "${var.region}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/models/terraform/aws.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "${var.account}-${var.shortname}" 3 | region = "${var.region}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/models/autodiscovery/aws.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "${var.account}-${var.shortname}" 3 | region = "${var.region}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/models/service-simple/aws.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "${var.account}-${var.shortname}" 3 | region = "${var.region}" 4 | } 5 | -------------------------------------------------------------------------------- /bin/confirm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | read -r -p "$(echo -e "$@ [yes/\033[1mno\033[0m]") " response 4 | case $response in 5 | yes) 6 | exit 0 7 | ;; 8 | *) 9 | exit 1 10 | ;; 11 | esac 12 | -------------------------------------------------------------------------------- /terraform/configurations/resetlogs/staging/env.tfvars: -------------------------------------------------------------------------------- 1 | uniquekey = "c531e1f07a5ef008" 2 | region = "eu-west-1" 3 | netprefix = "10.1" 4 | keypair = "mysql-key" 5 | ami-ec2 = "ami-9398d3e0" 6 | ami-ecs = "ami-e988c39a" -------------------------------------------------------------------------------- /terraform/deployments/services/infrastructure.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "infra" { 2 | backend = "s3" 3 | 4 | config { 5 | bucket = "terraform-${var.shortname}-${var.uniquekey}" 6 | key = "tf-state/infrastructure.state" 7 | region = "${var.region}" 8 | profile = "${var.account}-${var.shortname}" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docker/simple-api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk add --no-cache python3 && \ 4 | python3 -m ensurepip && \ 5 | rm -r /usr/lib/python*/ensurepip && \ 6 | pip3 install --upgrade pip setuptools && \ 7 | rm -r /root/.cache 8 | 9 | COPY app /app 10 | WORKDIR /app 11 | 12 | RUN pip3 install -r requirements.txt 13 | 14 | ENTRYPOINT ["python3"] 15 | CMD ["webserver.py"] 16 | 17 | -------------------------------------------------------------------------------- /docker/simple-gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk add --no-cache python3 drill && \ 4 | python3 -m ensurepip && \ 5 | rm -r /usr/lib/python*/ensurepip && \ 6 | pip3 install --upgrade pip setuptools && \ 7 | rm -r /root/.cache 8 | 9 | COPY app /app 10 | WORKDIR /app 11 | 12 | RUN pip3 install -r requirements.txt 13 | 14 | ENTRYPOINT ["python3"] 15 | CMD ["webserver.py"] 16 | 17 | -------------------------------------------------------------------------------- /terraform/deployments/terraform/state.tf: -------------------------------------------------------------------------------- 1 | variable "shortname" {} 2 | variable "account" {} 3 | variable "region" {} 4 | variable "uniquekey" {} 5 | 6 | module "state" { 7 | shortname = "${var.shortname}" 8 | account = "${var.account}" 9 | region = "${var.region}" 10 | uniquekey = "${var.uniquekey}" 11 | source = "../../models/terraform" 12 | } 13 | 14 | output "terraform_bucket" { 15 | value = "${module.state.terraform_bucket}" 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Grégory Guillou 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /terraform/models/vpc/var.tf: -------------------------------------------------------------------------------- 1 | variable "account" { 2 | type = "string" 3 | description = "The actual project account" 4 | } 5 | 6 | variable "shortname" { 7 | type = "string" 8 | description = "The short name of the environment that is used to define it" 9 | } 10 | 11 | variable "region" { 12 | type = "string" 13 | description = "AWS Region to use" 14 | } 15 | 16 | variable "uniquekey" { 17 | type = "string" 18 | description = "A unique key to generate a new bucket" 19 | } 20 | 21 | variable "netprefix" { 22 | type = "string" 23 | description = "Network Prefix for a /16 VPC range" 24 | } 25 | 26 | -------------------------------------------------------------------------------- /terraform/models/vpc/bastion.tf: -------------------------------------------------------------------------------- 1 | variable "keypair" { 2 | type = "string" 3 | description = "The Keypair to use" 4 | } 5 | 6 | variable "ami" { 7 | type = "string" 8 | description = "The AMI to start from" 9 | } 10 | 11 | resource "aws_instance" "bastion" { 12 | ami = "${var.ami}" 13 | key_name = "${var.keypair}" 14 | vpc_security_group_ids = [ 15 | "${aws_security_group.ssh.id}"] 16 | subnet_id = "${aws_subnet.public-subnet-a.id}" 17 | instance_type = "t2.micro" 18 | 19 | tags { 20 | Name = "${var.shortname}-bastion" 21 | env = "${var.shortname}" 22 | uniquekey = "${var.uniquekey}" 23 | } 24 | } 25 | 26 | output "bastion_access" { 27 | value = "ec2-user@${aws_instance.bastion.public_ip}" 28 | } 29 | -------------------------------------------------------------------------------- /terraform/models/terraform/s3.tf: -------------------------------------------------------------------------------- 1 | variable "account" { 2 | type = "string" 3 | description = "The actual project account" 4 | } 5 | 6 | variable "shortname" { 7 | type = "string" 8 | description = "The short name of the environment that is used to define it" 9 | } 10 | 11 | variable "region" { 12 | type = "string" 13 | description = "AWS Region to use" 14 | } 15 | 16 | variable "uniquekey" { 17 | type = "string" 18 | description = "A unique key to generate a new bucket" 19 | } 20 | 21 | resource "aws_s3_bucket" "terraform" { 22 | bucket = "terraform-${var.shortname}-${var.uniquekey}" 23 | acl = "private" 24 | 25 | versioning { 26 | enabled = true 27 | } 28 | 29 | force_destroy = "false" 30 | } 31 | 32 | output "terraform_bucket" { 33 | value = "${aws_s3_bucket.terraform.bucket}" 34 | } 35 | 36 | -------------------------------------------------------------------------------- /terraform/models/templates/cluster.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat >/etc/ecs/ecs.config <>/tmp/awslog.conf </dev/null) 9 | HOST_NAME=$(curl -q http://169.254.169.254/latest/meta-data/hostname 2>/dev/null) 10 | INSTANCE=$(curl -q http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null) 11 | DNS="$(echo $IPADDR | cut -d'.' -f 1-2).0.2" 12 | export AWS_DEFAULT_REGION=$(curl -q http://169.254.169.254/latest/meta-data/placement/availability-zone 2>/dev/null|sed 's/.$//') 13 | IPADRESSES="" 14 | CLUSTERNAMES="" 15 | 16 | for i in $(aws ec2 describe-tags --filters "Name=tag:autodiscovery-key,Values=${clusterkey}" --query "Tags[*].ResourceId" --output=text); do 17 | ipaddress=$(aws ec2 describe-instances --instance-id $i --query Reservations[0].Instances[?State.Name==\`running\`].PrivateIpAddress --output=text) 18 | if [ "$i" != "$INSTANCE" ]; then 19 | ipaddress=$(aws ec2 describe-instances --instance-id $i --query Reservations[0].Instances[?State.Name==\`running\`].PrivateIpAddress --output=text) 20 | IPADRESSES="$IPADRESSES $ipaddress" 21 | fi 22 | clustername=$(aws ec2 describe-instances --instance-id $i --query Reservations[0].Instances[?State.Name==\`running\`].PrivateDnsName --output=text) 23 | CLUSTERNAMES="$CLUSTERNAMES $clustername" 24 | done 25 | 26 | serverconf() { 27 | cat >/var/consul/conf/consul.conf <> /var/consul/conf/consul.conf 48 | else 49 | echo " \"$i\"," >> /var/consul/conf/consul.conf 50 | fi 51 | count=$(($count+1)) 52 | done 53 | 54 | cat >>/var/consul/conf/consul.conf </var/consul/conf/consul.conf </tmp/names.json <>/tmp/names.json <> /tmp/names.json 114 | fi 115 | count=$(($count+1)) 116 | done 117 | 118 | cat >>/tmp/names.json < $(CONFIG_ENV),uniquekey = "$(UNIQUEKEY)") 44 | @$(file >> $(CONFIG_ENV),region = "$(REGION)") 45 | @$(file >> $(CONFIG_ENV),netprefix = "10.1") 46 | @$(file >> $(CONFIG_ENV),keypair = "") 47 | @$(file >> $(CONFIG_ENV),ami-ec2 = "ami-9398d3e0") 48 | @$(file >> $(CONFIG_ENV),ami-ecs = "ami-e988c39a") 49 | @- cd $(TERRAFORM_DIR) && terraform get && \ 50 | TF_VAR_uniquekey=$(UNIQUEKEY) terraform apply && \ 51 | terraform remote config \ 52 | -backend=S3 \ 53 | -backend-config="bucket=terraform-$(ENV)-$(UNIQUEKEY)" \ 54 | -backend-config="key=tf-state/seed.state" \ 55 | -backend-config="region=$(REGION)" \ 56 | -backend-config="profile=$(ACCOUNT)-$(ENV)" 57 | @$(MAKE) FOLDER=$(TERRAFORM_DIR) clean 58 | endif 59 | 60 | clean: 61 | @- cd $(FOLDER) && terraform remote config -disable 2>/dev/null 62 | @- cd $(FOLDER) && rm -rf terraform.tfstate terraform.tfstate.backup .terraform 63 | 64 | config: 65 | @$(eval UNIQUEKEY := $(shell grep uniquekey $(CONFIG_ENV) | awk '{print $$3}')) 66 | @$(MAKE) FOLDER=$(FOLDER) clean 67 | @cd $(FOLDER) && terraform remote config \ 68 | -backend=S3 \ 69 | -backend-config="bucket=terraform-$(ENV)-$(UNIQUEKEY)" \ 70 | -backend-config="key=tf-state/$(STATE).state" \ 71 | -backend-config="region=$(REGION)" \ 72 | -backend-config="profile=$(ACCOUNT)-$(ENV)" 73 | 74 | confirm: 75 | @echo; echo 76 | @$(eval CONTINUE := $(shell $(CONFIRM) "You are about to deploy \033[1;32m$(STATE)\033[0m on $(BOLD_ENV) environment, are you sure you want to proceed ?"; echo $$?)) 77 | @$(MAKE) CONTINUE=$(CONTINUE) exit 78 | 79 | deploy: 80 | @$(MAKE) FOLDER=$(FOLDER) STATE=$(STATE) config 81 | @cd $(FOLDER) && terraform get > /dev/null 82 | @cd $(FOLDER) && terraform plan -var-file=$(CONFIG_ENV) 83 | @$(MAKE) STATE=$(STATE) confirm 84 | @cd $(FOLDER) && terraform apply -var-file=$(CONFIG_ENV) 85 | @$(MAKE) FOLDER=$(FOLDER) clean 86 | 87 | destroy: 88 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) STATE=infrastructure config 89 | @cd $(INFRASTRUCTURE_DIR) && terraform get \ 90 | && terraform plan -destroy -var-file=$(CONFIG_ENV) 91 | @$(MAKE) STATE=infrastructure confirm 92 | @cd $(INFRASTRUCTURE_DIR) && terraform destroy -force -var-file=$(CONFIG_ENV) 93 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) clean 94 | 95 | destroy_services: 96 | @$(MAKE) FOLDER=$(SERVICE_DIR) STATE=services config 97 | @cd $(SERVICE_DIR) && terraform get \ 98 | && terraform plan -destroy -var-file=$(CONFIG_ENV) 99 | @$(MAKE) STATE=services confirm 100 | @cd $(SERVICE_DIR) && terraform destroy -force -var-file=$(CONFIG_ENV) 101 | @$(MAKE) FOLDER=$(SERVICE_DIR) clean 102 | 103 | exit: 104 | ifeq ($(CONTINUE),1) 105 | @$(MAKE) FOLDER=$(FOLDER) clean 106 | @false 107 | endif 108 | 109 | infrastructure: 110 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) STATE=$(@) deploy 111 | 112 | list: 113 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) STATE=infrastructure config 114 | @echo && echo "Infrastructure:" && \ 115 | echo "---------------" && \ 116 | cd $(INFRASTRUCTURE_DIR) && terraform state list && \ 117 | echo 118 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) clean 119 | 120 | output: 121 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) STATE=infrastructure config 122 | @echo && echo "Infrastructure:" && \ 123 | echo "---------------" && \ 124 | cd $(INFRASTRUCTURE_DIR) && terraform output && \ 125 | echo 126 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) clean 127 | 128 | show: 129 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) STATE=infrastructure config 130 | @cd $(INFRASTRUCTURE_DIR) && terraform state show $(RESOURCE) 131 | @$(MAKE) FOLDER=$(INFRASTRUCTURE_DIR) clean 132 | 133 | services: 134 | @$(MAKE) FOLDER=$(SERVICE_DIR) STATE=$(@) deploy 135 | 136 | .PHONY: destroy destroy_services init infrastructure list output show services 137 | -------------------------------------------------------------------------------- /terraform/models/autodiscovery/discovery-cluster.tf: -------------------------------------------------------------------------------- 1 | variable "account" { 2 | type = "string" 3 | description = "The actual project account" 4 | } 5 | 6 | variable "shortname" { 7 | type = "string" 8 | description = "The short name of the environment that is used to define it" 9 | } 10 | 11 | variable "region" { 12 | type = "string" 13 | description = "AWS Region to use" 14 | } 15 | 16 | variable "uniquekey" { 17 | type = "string" 18 | description = "A unique key to generate a new bucket" 19 | } 20 | 21 | variable "ami" { 22 | type = "string" 23 | description = "The AMI to start from" 24 | } 25 | 26 | variable "vpc" { 27 | type = "string" 28 | description = "The Main VPC Identifier" 29 | } 30 | 31 | variable "subnets" { 32 | type = "list" 33 | description = "The list of subnet the cluster will use" 34 | } 35 | 36 | variable "security_group" { 37 | type = "string" 38 | description = "The default SSH security Group" 39 | } 40 | 41 | variable "keypair" { 42 | type = "string" 43 | description = "The Keypair to use" 44 | } 45 | 46 | variable "zone" { 47 | type = "string" 48 | description = "Route53 Private Hosted Zone for the VPC" 49 | } 50 | 51 | resource "aws_ecs_cluster" "autodiscovery" { 52 | name = "${var.shortname}-discovery" 53 | 54 | lifecycle { 55 | create_before_destroy = true 56 | } 57 | } 58 | 59 | output "cluster" { 60 | value = "${aws_ecs_cluster.autodiscovery.id}" 61 | } 62 | 63 | resource "aws_security_group" "consul_sg" { 64 | name = "${var.shortname}-discovery-sg" 65 | description = "Security group for the EC2 instances of the Discovery ECS cluster" 66 | vpc_id = "${var.vpc}" 67 | 68 | egress { 69 | from_port = 0 70 | to_port = 0 71 | protocol = "-1" 72 | cidr_blocks = [ 73 | "0.0.0.0/0"] 74 | } 75 | 76 | ingress { 77 | from_port = 8300 78 | to_port = 8302 79 | protocol = "tcp" 80 | cidr_blocks = [ 81 | "0.0.0.0/0"] 82 | } 83 | 84 | ingress { 85 | from_port = 8300 86 | to_port = 8302 87 | protocol = "udp" 88 | cidr_blocks = [ 89 | "0.0.0.0/0"] 90 | } 91 | 92 | ingress { 93 | from_port = 8400 94 | to_port = 8400 95 | protocol = "tcp" 96 | cidr_blocks = [ 97 | "0.0.0.0/0"] 98 | } 99 | 100 | ingress { 101 | from_port = 8500 102 | to_port = 8500 103 | protocol = "tcp" 104 | cidr_blocks = [ 105 | "0.0.0.0/0"] 106 | } 107 | 108 | ingress { 109 | from_port = 8600 110 | to_port = 8600 111 | protocol = "udp" 112 | cidr_blocks = [ 113 | "0.0.0.0/0"] 114 | } 115 | 116 | } 117 | 118 | resource "aws_security_group" "docker_sg" { 119 | name = "${var.shortname}-docker-sg" 120 | description = "Security group for the EC2 instances to access docker" 121 | vpc_id = "${var.vpc}" 122 | 123 | ingress { 124 | from_port = 32768 125 | to_port = 61000 126 | protocol = "tcp" 127 | cidr_blocks = [ 128 | "0.0.0.0/0"] 129 | } 130 | 131 | } 132 | 133 | resource "aws_autoscaling_group" "autodiscovery" { 134 | name = "${var.shortname}-autodiscovery" 135 | launch_configuration = "${aws_launch_configuration.autodiscovery.name}" 136 | desired_capacity = 3 137 | min_size = 3 138 | max_size = 3 139 | vpc_zone_identifier = [ 140 | "${var.subnets}"] 141 | 142 | lifecycle { 143 | create_before_destroy = true 144 | } 145 | 146 | tag { 147 | key = "Name" 148 | value = "${var.shortname}-discovery" 149 | propagate_at_launch = true 150 | } 151 | 152 | tag { 153 | key = "autodiscovery-key" 154 | value = "${var.uniquekey}" 155 | propagate_at_launch = true 156 | } 157 | } 158 | 159 | resource "aws_launch_configuration" "autodiscovery" { 160 | name_prefix = "${var.shortname}-autodiscovery-" 161 | image_id = "${var.ami}" 162 | instance_type = "t2.small" 163 | key_name = "${var.keypair}" 164 | security_groups = [ 165 | "${aws_security_group.consul_sg.id}", 166 | "${aws_security_group.docker_sg.id}", 167 | "${var.security_group}"] 168 | 169 | iam_instance_profile = "${aws_iam_instance_profile.autodiscovery_profile.name}" 170 | 171 | user_data = "${data.template_cloudinit_config.autodiscovery_cloudinit.rendered}" 172 | 173 | root_block_device { 174 | volume_type = "gp2" 175 | volume_size = 10 176 | } 177 | 178 | ebs_block_device { 179 | device_name = "/dev/xvdcz" 180 | volume_size = 22 181 | volume_type = "gp2" 182 | } 183 | 184 | lifecycle { 185 | create_before_destroy = true 186 | } 187 | 188 | } 189 | 190 | resource "aws_iam_instance_profile" "autodiscovery_profile" { 191 | name = "${var.shortname}-autodiscovery-profile" 192 | roles = [ 193 | "${aws_iam_role.autodiscovery_role.id}"] 194 | 195 | lifecycle { 196 | create_before_destroy = true 197 | } 198 | 199 | provisioner "local-exec" { 200 | command = "sleep 30" 201 | } 202 | } 203 | 204 | resource "aws_iam_role" "autodiscovery_role" { 205 | name = "${var.shortname}-autodiscovery-role" 206 | 207 | assume_role_policy = </dev/null) 119 | sleep 1 120 | done 121 | ``` 122 | 123 | Note: 124 | The reason we run ```echo -e``` is to leverage the color codes that are returned by the ```simple-api``` version 125 | 126 | You can now modify the container. In order to proceed: 127 | 128 | - Change the code of the ```appversion()``` function of ```docker/simple-api/app/webserver.py``` to return another output 129 | - Change the version number in ```docker/simple-api/app/version.txt``` 130 | - Rebuild/Redeploy the container to its repository with the new version 131 | 132 | ``` 133 | cd docker/simple-api 134 | ACCOUNT=[youraccount] ENV=[yourenv] make build 135 | ACCOUNT=[youraccount] ENV=[yourenv] make install 136 | ``` 137 | 138 | - Change the version in the Terraform project to match the one you've just redeployed for the ```simple-api```. That parameter is part of the ```terraform/deployments/services/components.tf``` file. 139 | - Upgrade Terraform Service deployment with the command below: 140 | 141 | ``` 142 | cd ../.. 143 | ACCOUNT=[youraccount] ENV=[yourenv] make services 144 | ``` 145 | 146 | Note: 147 | The container upgrade can take quite some time. Be patient and review the "ECS service Events" to troubleshoot the upgrade if needed. 148 | 149 | ## To continue 150 | 151 | Fork the project, build demonstration/tests of your own. Raise issues, provide feedback. Have fun with Consul and AWS! 152 | --------------------------------------------------------------------------------