├── .gitignore ├── terraform.tfvars.example ├── outputs.tf ├── README.md ├── variables.tf ├── Makefile ├── templates ├── follower-cloud-config.yml.template └── leader-cloud-config.yml.template └── main.tf /.gitignore: -------------------------------------------------------------------------------- 1 | discovery_url 2 | *.yml 3 | plan 4 | terraform.tfstate 5 | terraform.tfvars 6 | terraform.tfstate.backup 7 | -------------------------------------------------------------------------------- /terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | ## copy this to terraform.tfvars and edit 2 | 3 | stack_name = "coreos-cluster" 4 | 5 | # aws creds 6 | access_key = "YOUR_AWS_ACCESS_KEY" 7 | secret_key = "YOUR_AWS_SECRET_KEY" 8 | key_name = "YOUR_KEY_PAIR_NAME" -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "consul_url" { 2 | value = "http://${aws_elb.coreos_leader_elb.dns_name}:8500/" 3 | } 4 | 5 | output "fleet_env" { 6 | value = "export FLEETCTL_TUNNEL=${aws_elb.coreos_leader_elb.dns_name}:2222\nexport FLEETCTL_STRICT_HOST_KEY_CHECKING=false" 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | How To Stand Up A 10-Node CoreOS Cluster With Consul+Registrator In AWS Using Terraform 2 | === 3 | ``` 4 | $ make 5 | ``` 6 | 7 | To make it easier on yourself, run `cp terraform.tfvars.example terraform.tfvars` and then edit as appropriate. 8 | 9 | How To Access Fleet 10 | === 11 | ``` 12 | $ $(terraform output fleet_env) 13 | $ fleetctl list-machines 14 | ``` 15 | 16 | If you don't have fleetctl and you're on a Mac, you can run `make fleetctl` and it will install it via Homebrew. 17 | 18 | How To Access Consul 19 | === 20 | ``` 21 | $ open $(terraform output consul_url) # assuming again you're on a Mac 22 | ``` -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "stack_name" {} 2 | 3 | # aws creds 4 | variable "access_key" {} 5 | variable "secret_key" {} 6 | variable "key_name" {} 7 | 8 | # aws network stuff 9 | variable "region" { 10 | default = "us-west-2" 11 | } 12 | variable "az" {} 13 | variable "vpc" {} 14 | variable "lb_subnet" {} 15 | variable "instance_subnet" {} 16 | variable "leader_instance_size" { 17 | default = "m3.medium" 18 | } 19 | variable "follower_instance_size" { 20 | default = "m3.medium" 21 | } 22 | variable "num_leaders" { 23 | default = 3 24 | } 25 | variable "num_followers" { 26 | default = 7 27 | } 28 | 29 | # coreos images 30 | variable "coreos_amis" { 31 | default = { 32 | us-east-1 = "ami-705d3d18" 33 | us-west-2 = "ami-4dd4857d" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: packages deploy 2 | 3 | deploy: plan terraform 4 | terraform apply -input=false < plan 5 | 6 | plan: templates terraform 7 | terraform plan -input=false -out plan 8 | 9 | templates: leader-cloud-config.yml follower-cloud-config.yml 10 | 11 | leader-cloud-config.yml: discovery_url 12 | cat templates/leader-cloud-config.yml.template | sed -e "s#{{ discovery_url }}#`cat discovery_url`#" > leader-cloud-config.yml 13 | 14 | follower-cloud-config.yml: discovery_url 15 | cat templates/follower-cloud-config.yml.template | sed -e "s#{{ discovery_url }}#`cat discovery_url`#" > follower-cloud-config.yml 16 | 17 | discovery_url: 18 | curl -s https://discovery.etcd.io/new > discovery_url 19 | 20 | destroy: terraform 21 | terraform destroy -input=false 22 | 23 | clean: 24 | rm -f plan 25 | rm -f discovery_url 26 | rm -f leader-cloud-config.yml 27 | rm -f follower-cloud-config.yml 28 | 29 | clean-all: destroy clean 30 | rm -f terraform.tfstate 31 | rm -f terraform.tfstate.backup 32 | 33 | packages: /usr/local/bin/fleetctl /usr/local/bin/terraform 34 | 35 | fleetctl: /usr/local/bin/fleetctl 36 | 37 | /usr/local/bin/fleetctl: 38 | brew install fleetctl 39 | 40 | terraform: /usr/local/bin/terraform 41 | 42 | /usr/local/bin/terraform: 43 | brew install terraform -------------------------------------------------------------------------------- /templates/follower-cloud-config.yml.template: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | coreos: 3 | etcd: 4 | discovery: {{ discovery_url }} 5 | addr: "$private_ipv4:4001" 6 | peer-addr: "$private_ipv4:7001" 7 | fleet: 8 | metadata: "region=us-west-2,consul_role=follower" 9 | units: 10 | - name: etcd.service 11 | command: start 12 | - name: fleet.service 13 | command: start 14 | - name: consul.service 15 | command: start 16 | content: | 17 | [Unit] 18 | Description=Consul Follower Agent 19 | After=docker.service 20 | After=etcd.service 21 | After=fleet.service 22 | [Service] 23 | Restart=on-failure 24 | RestartSec=240 25 | ExecStartPre=-/usr/bin/docker kill consul 26 | ExecStartPre=-/usr/bin/docker rm consul 27 | ExecStartPre=/usr/bin/docker pull progrium/consul 28 | ExecStart=/bin/sh -c "/usr/bin/docker run --name consul -h $HOSTNAME -p $private_ipv4:8300:8300 -p $private_ipv4:8301:8301 -p $private_ipv4:8301:8301/udp -p $private_ipv4:8302:8302 -p $private_ipv4:8302:8302/udp -p $private_ipv4:8400:8400 -p $private_ipv4:8500:8500 -p 172.17.42.1:53:53/udp -e SERVICE_IGNORE=true -v /var/run/docker.sock:/var/run/docker.sock progrium/consul -advertise $private_ipv4 -retry-join $(etcdctl get $(etcdctl ls /consul/bootstrap/machines | tail -1))" 29 | ExecStop=/usr/bin/docker stop consul 30 | - name: registrator.service 31 | command: start 32 | content: | 33 | [Unit] 34 | Description=Registrator 35 | PartOf=consul.service 36 | After=consul.service 37 | [Service] 38 | Restart=on-failure 39 | ExecStartPre=-/usr/bin/docker kill registrator 40 | ExecStartPre=-/usr/bin/docker rm registrator 41 | ExecStartPre=/usr/bin/docker pull progrium/registrator 42 | ExecStart=/bin/sh -c "/usr/bin/docker run --name registrator -h registrator -v /var/run/docker.sock:/tmp/docker.sock progrium/registrator consul://$private_ipv4:8500" 43 | ExecStop=/usr/bin/docker stop registrator 44 | -------------------------------------------------------------------------------- /templates/leader-cloud-config.yml.template: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | coreos: 3 | etcd: 4 | discovery: {{ discovery_url }} 5 | addr: "$private_ipv4:4001" 6 | peer-addr: "$private_ipv4:7001" 7 | fleet: 8 | metadata: "region=us-west-2,consul_role=leader" 9 | units: 10 | - name: etcd.service 11 | command: start 12 | - name: fleet.service 13 | command: start 14 | - name: consul.service 15 | command: start 16 | content: | 17 | [Unit] 18 | Description=Consul Server Agent 19 | After=docker.service 20 | After=etcd.service 21 | After=fleet.service 22 | [Service] 23 | Restart=on-failure 24 | RestartSec=240 25 | ExecStartPre=-/usr/bin/docker kill consul 26 | ExecStartPre=-/usr/bin/docker rm consul 27 | ExecStartPre=/usr/bin/docker pull progrium/consul 28 | ExecStart=/bin/sh -c "eval $(/usr/bin/docker run --rm progrium/consul cmd:run $private_ipv4 -e SERVICE_IGNORE=true -v /var/run/docker.sock:/var/run/docker.sock)" 29 | ExecStop=/usr/bin/docker stop consul 30 | - name: consul-announce.service 31 | command: start 32 | content: | 33 | [Unit] 34 | Description=Consul Server Announcer 35 | PartOf=consul.service 36 | After=consul.service 37 | [Service] 38 | ExecStart=/bin/sh -c "while true; do etcdctl set /consul/bootstrap/machines/$(cat /etc/machine-id) $private_ipv4 --ttl 60; /usr/bin/docker exec consul consul join $(etcdctl get $(etcdctl ls /consul/bootstrap/machines | tail -1)); sleep 45; done" 39 | ExecStop=/bin/sh -c "/usr/bin/etcdctl rm /consul/bootstrap/machines/$(cat /etc/machine-id)" 40 | - name: registrator.service 41 | command: start 42 | content: | 43 | [Unit] 44 | Description=Registrator 45 | PartOf=consul.service 46 | After=consul.service 47 | [Service] 48 | Restart=on-failure 49 | ExecStartPre=-/usr/bin/docker kill registrator 50 | ExecStartPre=-/usr/bin/docker rm registrator 51 | ExecStartPre=/usr/bin/docker pull progrium/registrator 52 | ExecStart=/bin/sh -c "/usr/bin/docker run --name registrator -h registrator -v /var/run/docker.sock:/tmp/docker.sock progrium/registrator consul://$private_ipv4:8500" 53 | ExecStop=/usr/bin/docker stop registrator 54 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | access_key = "${var.access_key}" 3 | secret_key = "${var.secret_key}" 4 | region = "${var.region}" 5 | } 6 | 7 | resource "aws_autoscaling_group" "coreos_leader_autoscale" { 8 | name = "${var.stack_name}_leader_autoscale" 9 | load_balancers = ["${aws_elb.coreos_leader_elb.id}"] 10 | vpc_zone_identifier = ["${var.instance_subnet}"] 11 | availability_zones = ["${var.az}"] 12 | min_size = 3 13 | max_size = 5 14 | desired_capacity = "${var.num_leaders}" 15 | launch_configuration = "${aws_launch_configuration.coreos_leader_launchconfig.name}" 16 | } 17 | 18 | resource "aws_launch_configuration" "coreos_leader_launchconfig" { 19 | name = "${var.stack_name}_leader_config" 20 | image_id = "${lookup(var.coreos_amis, var.region)}" 21 | instance_type = "${var.leader_instance_size}" 22 | security_groups = ["${aws_security_group.coreos_securitygroup.id}"] 23 | key_name = "${var.key_name}" 24 | user_data = "${file("leader-cloud-config.yml")}" 25 | } 26 | 27 | resource "aws_autoscaling_group" "coreos_follower_autoscale" { 28 | vpc_zone_identifier = ["${var.instance_subnet}"] 29 | availability_zones = ["${var.az}"] 30 | name = "${var.stack_name}_follower_autoscale" 31 | min_size = 0 32 | max_size = 95 33 | desired_capacity = "${var.num_followers}" 34 | launch_configuration = "${aws_launch_configuration.coreos_follower_launchconfig.name}" 35 | } 36 | 37 | resource "aws_launch_configuration" "coreos_follower_launchconfig" { 38 | name = "${var.stack_name}_follower_config" 39 | image_id = "${lookup(var.coreos_amis, var.region)}" 40 | instance_type = "${var.follower_instance_size}" 41 | security_groups = ["${aws_security_group.coreos_securitygroup.id}"] 42 | key_name = "${var.key_name}" 43 | user_data = "${file("follower-cloud-config.yml")}" 44 | } 45 | 46 | resource "aws_security_group" "coreos_securitygroup" { 47 | name = "${var.stack_name}_coreos_securitygroup" 48 | description = "allow a bunch of stuff" 49 | vpc_id = "${var.vpc}" 50 | 51 | ingress { 52 | from_port = 0 53 | to_port = 65535 54 | protocol = "tcp" 55 | cidr_blocks = ["0.0.0.0/0"] 56 | } 57 | 58 | ingress { 59 | from_port = 0 60 | to_port = 65535 61 | protocol = "udp" 62 | cidr_blocks = ["0.0.0.0/0"] 63 | } 64 | } 65 | 66 | resource "aws_elb" "coreos_leader_elb" { 67 | name = "${var.stack_name}-coreos-leader-elb" 68 | security_groups = ["${aws_security_group.coreos_securitygroup.id}"] 69 | internal = false 70 | subnets = ["${var.lb_subnet}"] 71 | 72 | listener { 73 | lb_port = 4001 74 | instance_port = 4001 75 | lb_protocol = "http" 76 | instance_protocol = "http" 77 | } 78 | 79 | listener { 80 | lb_port = 8400 81 | instance_port = 8400 82 | lb_protocol = "tcp" 83 | instance_protocol = "tcp" 84 | } 85 | 86 | listener { 87 | lb_port = 8500 88 | instance_port = 8500 89 | lb_protocol = "http" 90 | instance_protocol = "http" 91 | } 92 | 93 | listener { 94 | lb_port = 2222 95 | instance_port = 22 96 | lb_protocol = "tcp" 97 | instance_protocol = "tcp" 98 | } 99 | 100 | health_check { 101 | target = "HTTP:4001/version" 102 | healthy_threshold = 4 103 | unhealthy_threshold = 2 104 | timeout = 5 105 | interval = 30 106 | } 107 | 108 | } 109 | --------------------------------------------------------------------------------