├── docker.options ├── README.md ├── variables.tf ├── bastion-sg.tf ├── bastion.tf ├── docker.json ├── swarm-sg.tf ├── swarm.tf └── provider.tf /docker.options: -------------------------------------------------------------------------------- 1 | DOCKER_OPTS=-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-docker-swarm-aws 2 | 3 | Source files used to create a VPC, bastion host, and EC2 instances that form a docker swarm mode cluster. 4 | 5 | For more information, read the tutorial: http://ngerakines.me/2016/11/20/terraform_docker_swarm_and_aws/ 6 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region to launch servers." 3 | default = "us-west-2" 4 | } 5 | 6 | variable "vpc_key" { 7 | description = "A unique identifier for the VPC." 8 | default = "nickg" 9 | } 10 | 11 | variable "cluster_manager_count" { 12 | description = "Number of manager instances for the cluster." 13 | default = 1 14 | } 15 | 16 | variable "cluster_node_count" { 17 | description = "Number of node instances for the cluster." 18 | default = 3 19 | } 20 | -------------------------------------------------------------------------------- /bastion-sg.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "bastion" { 2 | name = "${var.vpc_key}-sg-bastion" 3 | description = "Security group for bastion instances" 4 | vpc_id = "${aws_vpc.vpc.id}" 5 | 6 | ingress { 7 | from_port = 22 8 | to_port = 22 9 | protocol = "tcp" 10 | cidr_blocks = [ 11 | "0.0.0.0/0" 12 | ] 13 | } 14 | 15 | egress { 16 | from_port = 0 17 | to_port = 0 18 | protocol = "-1" 19 | cidr_blocks = [ 20 | "0.0.0.0/0" 21 | ] 22 | } 23 | 24 | tags { 25 | Name = "${var.vpc_key}-sg-bastion" 26 | VPC = "${var.vpc_key}" 27 | Terraform = "Terraform" 28 | } 29 | } 30 | 31 | output "sg_bastion" { 32 | value = "${aws_security_group.bastion.id}" 33 | } 34 | -------------------------------------------------------------------------------- /bastion.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "bastion" { 2 | ami = "ami-1a6cc07a" 3 | instance_type = "t2.small" 4 | count = "1" 5 | associate_public_ip_address = "true" 6 | key_name = "foo" 7 | subnet_id = "${aws_subnet.a.id}" 8 | vpc_security_group_ids = [ 9 | "${aws_security_group.bastion.id}" 10 | ] 11 | 12 | root_block_device = { 13 | volume_size = 10 14 | } 15 | 16 | connection { 17 | user = "ubuntu" 18 | private_key = "${file("~/.ssh/foo")}" 19 | agent = false 20 | } 21 | 22 | tags { 23 | Name = "${var.vpc_key}-bastion" 24 | VPC = "${var.vpc_key}" 25 | Terraform = "Terraform" 26 | } 27 | } 28 | 29 | output "bastion_host" { 30 | value = "${aws_instance.bastion.public_dns}" 31 | } 32 | -------------------------------------------------------------------------------- /docker.json: -------------------------------------------------------------------------------- 1 | { 2 | "builders": [ 3 | { 4 | "ami_name": "docker-swarm {{timestamp}}", 5 | "ami_virtualization_type": "hvm", 6 | "associate_public_ip_address": "true", 7 | "instance_type": "t2.small", 8 | "region": "us-west-2", 9 | "source_ami_filter": { 10 | "filters": { 11 | "name": "*ubuntu-yakkety-16.10-amd64-server-*", 12 | "root-device-type": "ebs", 13 | "virtualization-type": "hvm" 14 | }, 15 | "most_recent": true 16 | }, 17 | "ssh_username": "ubuntu", 18 | "subnet_id": "subnet-ff0def98", 19 | "tags": { 20 | "OS_Version": "Ubuntu", 21 | "Release": "16.10" 22 | }, 23 | "type": "amazon-ebs", 24 | "security_group_ids": ["sg-11a68368"] 25 | } 26 | ], 27 | "post-processors": null, 28 | "provisioners": [ 29 | { 30 | "destination": "/tmp/docker.options", 31 | "source": "docker.options", 32 | "type": "file" 33 | }, 34 | { 35 | "execute_command": "{{ .Vars }} sudo -E sh '{{ .Path }}'", 36 | "inline": [ 37 | "apt-get install -y aptitude", 38 | "aptitude -y update", 39 | "aptitude install -y docker docker-compose unzip", 40 | "mv /tmp/docker.options /etc/default/docker", 41 | "systemctl enable docker.service", 42 | "usermod -aG docker ubuntu" 43 | ], 44 | "type": "shell" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /swarm-sg.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "swarm" { 2 | name = "${var.vpc_key}-sg-swarm" 3 | description = "Security group for swarm cluster instances" 4 | vpc_id = "${aws_vpc.vpc.id}" 5 | 6 | ingress { 7 | from_port = 2375 8 | to_port = 2377 9 | protocol = "tcp" 10 | cidr_blocks = [ 11 | "${aws_vpc.vpc.cidr_block}" 12 | ] 13 | } 14 | 15 | ingress { 16 | from_port = 7946 17 | to_port = 7946 18 | protocol = "tcp" 19 | cidr_blocks = [ 20 | "${aws_vpc.vpc.cidr_block}" 21 | ] 22 | } 23 | 24 | ingress { 25 | from_port = 7946 26 | to_port = 7946 27 | protocol = "udp" 28 | cidr_blocks = [ 29 | "${aws_vpc.vpc.cidr_block}" 30 | ] 31 | } 32 | 33 | ingress { 34 | from_port = 4789 35 | to_port = 4789 36 | protocol = "tcp" 37 | cidr_blocks = [ 38 | "${aws_vpc.vpc.cidr_block}" 39 | ] 40 | } 41 | 42 | ingress { 43 | from_port = 4789 44 | to_port = 4789 45 | protocol = "udp" 46 | cidr_blocks = [ 47 | "${aws_vpc.vpc.cidr_block}" 48 | ] 49 | } 50 | 51 | ingress { 52 | from_port = 80 53 | to_port = 80 54 | protocol = "tcp" 55 | cidr_blocks = [ 56 | "0.0.0.0/0" 57 | ] 58 | } 59 | 60 | ingress { 61 | from_port = 443 62 | to_port = 443 63 | protocol = "tcp" 64 | cidr_blocks = [ 65 | "0.0.0.0/0" 66 | ] 67 | } 68 | 69 | ingress { 70 | from_port = 22 71 | to_port = 22 72 | protocol = "tcp" 73 | cidr_blocks = [ 74 | "0.0.0.0/0" 75 | ] 76 | } 77 | 78 | egress { 79 | from_port = 0 80 | to_port = 0 81 | protocol = "-1" 82 | cidr_blocks = [ 83 | "0.0.0.0/0" 84 | ] 85 | } 86 | 87 | tags { 88 | Name = "${var.vpc_key}-sg-swarm" 89 | VPC = "${var.vpc_key}" 90 | Terraform = "Terraform" 91 | } 92 | } 93 | 94 | output "sg_swarm" { 95 | value = "${aws_security_group.swarm.id}" 96 | } 97 | -------------------------------------------------------------------------------- /swarm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "swarm-manager" { 2 | ami = "ami-1a6cc07a" 3 | instance_type = "t2.small" 4 | count = "${var.cluster_manager_count}" 5 | associate_public_ip_address = "true" 6 | key_name = "foo" 7 | subnet_id = "${aws_subnet.a.id}" 8 | vpc_security_group_ids = [ 9 | "${aws_security_group.swarm.id}" 10 | ] 11 | 12 | root_block_device = { 13 | volume_size = 100 14 | } 15 | 16 | connection { 17 | user = "ubuntu" 18 | private_key = "${file("~/.ssh/foo")}" 19 | agent = false 20 | } 21 | 22 | tags { 23 | Name = "${var.vpc_key}-manager-${count.index}" 24 | VPC = "${var.vpc_key}" 25 | Terraform = "Terraform" 26 | } 27 | 28 | provisioner "remote-exec" { 29 | inline = [ 30 | "sudo docker swarm init" 31 | ] 32 | } 33 | 34 | depends_on = [ 35 | "aws_instance.bastion" 36 | ] 37 | } 38 | 39 | resource "aws_instance" "swarm-node" { 40 | ami = "ami-1a6cc07a" 41 | instance_type = "t2.small" 42 | count = "${var.cluster_node_count}" 43 | associate_public_ip_address = "true" 44 | key_name = "foo" 45 | subnet_id = "${aws_subnet.a.id}" 46 | vpc_security_group_ids = [ 47 | "${aws_security_group.swarm.id}" 48 | ] 49 | 50 | root_block_device = { 51 | volume_size = 100 52 | } 53 | 54 | connection { 55 | user = "ubuntu" 56 | private_key = "${file("~/.ssh/foo")}" 57 | agent = false 58 | } 59 | 60 | tags { 61 | Name = "${var.vpc_key}-node-${count.index}" 62 | VPC = "${var.vpc_key}" 63 | Terraform = "Terraform" 64 | } 65 | 66 | provisioner "remote-exec" { 67 | inline = [ 68 | "sudo docker swarm join ${aws_instance.swarm-manager.0.private_ip}:2377 --token $(docker -H ${aws_instance.swarm-manager.0.private_ip} swarm join-token -q worker)" 69 | ] 70 | } 71 | 72 | depends_on = [ 73 | "aws_instance.swarm-manager" 74 | ] 75 | } 76 | 77 | resource "null_resource" "cluster" { 78 | # Changes to any instance of the cluster requires re-provisioning 79 | triggers { 80 | cluster_instance_ids = "${join(",", aws_instance.swarm-node.*.id)}" 81 | } 82 | 83 | connection { 84 | host = "${aws_instance.bastion.public_dns}" 85 | user = "ubuntu" 86 | private_key = "${file("~/.ssh/deis")}" 87 | agent = false 88 | } 89 | 90 | provisioner "remote-exec" { 91 | # Bootstrap script called with private_ip of each node in the clutser 92 | inline = [ 93 | "docker -H ${element(aws_instance.swarm-manager.*.private_ip, 0)}:2375 network create --driver overlay appnet", 94 | "docker -H ${element(aws_instance.swarm-manager.*.private_ip, 0)}:2375 service create --name nginx --publish 80:80 --network appnet nginx" 95 | ] 96 | } 97 | } 98 | 99 | output "swarm_managers" { 100 | value = "${concat(aws_instance.swarm-manager.*.public_dns)}" 101 | } 102 | 103 | output "swarm_nodes" { 104 | value = "${concat(aws_instance.swarm-node.*.public_dns)}" 105 | } 106 | -------------------------------------------------------------------------------- /provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.aws_region}" 3 | } 4 | 5 | resource "aws_internet_gateway" "main" { 6 | vpc_id = "${aws_vpc.vpc.id}" 7 | 8 | tags { 9 | Name = "${var.vpc_key}-ig" 10 | VPC = "${var.vpc_key}" 11 | Terraform = "Terraform" 12 | } 13 | } 14 | 15 | resource "aws_network_acl" "network" { 16 | vpc_id = "${aws_vpc.vpc.id}" 17 | subnet_ids = [ 18 | "${aws_subnet.a.id}", 19 | "${aws_subnet.b.id}", 20 | "${aws_subnet.c.id}" 21 | ] 22 | 23 | ingress { 24 | from_port = 0 25 | to_port = 0 26 | rule_no = 100 27 | action = "allow" 28 | protocol = "-1" 29 | cidr_block = "0.0.0.0/0" 30 | } 31 | 32 | egress { 33 | from_port = 0 34 | to_port = 0 35 | rule_no = 100 36 | action = "allow" 37 | protocol = "-1" 38 | cidr_block = "0.0.0.0/0" 39 | } 40 | 41 | tags { 42 | Name = "${var.vpc_key}-network" 43 | VPC = "${var.vpc_key}" 44 | Terraform = "Terraform" 45 | } 46 | } 47 | 48 | resource "aws_route_table" "main" { 49 | vpc_id = "${aws_vpc.vpc.id}" 50 | 51 | route { 52 | cidr_block = "0.0.0.0/0" 53 | gateway_id = "${aws_internet_gateway.main.id}" 54 | } 55 | 56 | tags { 57 | Name = "${var.vpc_key}-route" 58 | VPC = "${var.vpc_key}" 59 | Terraform = "Terraform" 60 | } 61 | } 62 | 63 | resource "aws_route_table_association" "a" { 64 | route_table_id = "${aws_route_table.main.id}" 65 | subnet_id = "${aws_subnet.a.id}" 66 | } 67 | 68 | resource "aws_route_table_association" "b" { 69 | route_table_id = "${aws_route_table.main.id}" 70 | subnet_id = "${aws_subnet.b.id}" 71 | } 72 | 73 | resource "aws_route_table_association" "c" { 74 | route_table_id = "${aws_route_table.main.id}" 75 | subnet_id = "${aws_subnet.c.id}" 76 | } 77 | 78 | resource "aws_subnet" "a" { 79 | vpc_id = "${aws_vpc.vpc.id}" 80 | cidr_block = "${cidrsubnet(aws_vpc.vpc.cidr_block,8,1)}" 81 | availability_zone = "${var.aws_region}a" 82 | map_public_ip_on_launch = false 83 | 84 | tags { 85 | Name = "${var.vpc_key}-a" 86 | VPC = "${var.vpc_key}" 87 | Terraform = "Terraform" 88 | } 89 | } 90 | 91 | resource "aws_subnet" "b" { 92 | vpc_id = "${aws_vpc.vpc.id}" 93 | cidr_block = "${cidrsubnet(aws_vpc.vpc.cidr_block,8,2)}" 94 | availability_zone = "${var.aws_region}b" 95 | map_public_ip_on_launch = false 96 | 97 | tags { 98 | Name = "${var.vpc_key}-b" 99 | VPC = "${var.vpc_key}" 100 | Terraform = "Terraform" 101 | } 102 | } 103 | 104 | resource "aws_subnet" "c" { 105 | vpc_id = "${aws_vpc.vpc.id}" 106 | cidr_block = "${cidrsubnet(aws_vpc.vpc.cidr_block,8,3)}" 107 | availability_zone = "${var.aws_region}c" 108 | map_public_ip_on_launch = false 109 | 110 | tags { 111 | Name = "${var.vpc_key}-c" 112 | VPC = "${var.vpc_key}" 113 | Terraform = "Terraform" 114 | } 115 | } 116 | 117 | resource "aws_vpc" "vpc" { 118 | cidr_block = "10.25.0.0/16" 119 | enable_dns_hostnames = true 120 | enable_dns_support = true 121 | instance_tenancy = "default" 122 | 123 | tags { 124 | VPC = "${var.vpc_key}" 125 | Name = "${var.vpc_key}-vpc" 126 | Terraform = "Terraform" 127 | } 128 | } 129 | 130 | output "vpc_id" { 131 | value = "${aws_vpc.vpc.id}" 132 | } 133 | 134 | output "vcp_cidr_1" { 135 | value = "${cidrhost(aws_vpc.vpc.cidr_block,1)}" 136 | } 137 | output "vcp_cidr_sub_1" { 138 | value = "${cidrsubnet(aws_vpc.vpc.cidr_block,8,1)}" 139 | } 140 | 141 | output "vpc_subnet_a" { 142 | value = "${aws_subnet.a.id}" 143 | } 144 | output "vpc_subnet_b" { 145 | value = "${aws_subnet.b.id}" 146 | } 147 | output "vpc_subnet_c" { 148 | value = "${aws_subnet.c.id}" 149 | } 150 | --------------------------------------------------------------------------------