Skip to content

Commit

Permalink
Merge pull request #1 from lucassz/master
Browse files Browse the repository at this point in the history
Merged #1
  • Loading branch information
hdoupe authored Aug 22, 2018
2 parents 4dc654a + c1c8684 commit c9df5d9
Show file tree
Hide file tree
Showing 13 changed files with 510 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.swp
.terraform
*.tfstate*
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,83 @@
# pb_deploy
`pb_deploy` is a [Terraform](https://www.terraform.io) configuration for a
production deployment of [PolicyBrain], as run by the
[Open Source Policy Center], on Amazon Web Services infrastructure.

## Why Terraform?
Terraform is a declarative "infrastructure as code" open-source application,
meaning that it can deploy a new set of infrastructure components with ease
given account information, and it can keep it up-to-date with a programmatically
specified configuration, performing changes only as needed.

This is possible because Terraform keeps track of the [state][Terraform state].
In the case of OSPC, we use [AWS S3 and DynamoDB][Terraform S3 backend] to share
and lock state information among different OSPC collaborators.

## Infrastructure description
The main component of this infrastructure is [Amazon ECS Fargate], which is a
technology for deploying Docker containers without having to manage the
underlying servers. ECS is used to run relatively few Internet-facing Flask
servers, as well as relatively many Celery workers. Rather than being run on
ECS, Redis is managed through [Amazon ElastiCache], and stores the task queue.
[Amazon Route 53] and [Amazon ELB] are used to provide a permanent hostname,
such as `staging.ospcapi.org`, that will always point to the correct ECS task
IP(s). (Currently, the alternative, Route53 Service Discovery for ECS, only
supports private IPs.)

The networking is handled using a [Virtual Public Cloud][Amazon VPC] and a
single public subnet, and all instances are given public IPs. This is to avoid
the expense of a NAT gateway required with private subnets, but AWS security
groups are used to define permissible Internet access as restrictively as
possible.

## Usage
Because of Terraform's declarative nature, the steps for initial setup and for
modification of the infrastructure should be the same. You should specify
[AWS authentication credentials][Terraform AWS authentication] either through
environment variables or through the `~/.aws/credentials` file. The
configuration also assumes that the following resources have already been
created in the region given in the `terraform.tfvars` file:

- An [Amazon ECS task execution role]
- An Amazon Route 53 zone with the name given in `terraform.tfvars`; this can
be any domain or subdomain for which you need to configure the nameservers
- An S3 bucket and a DynamoDB table with the names given in `main.tf` in order
to store Terraform state

This configuration makes extensive use of [Terraform workspaces] in order to
separate state and resources for different deployment environments. The
`default` workspace can be used if this distinction is not needed, but should
**not** be used for OSPC deployments, which will make use of the `production`
and `staging` environments.

An example deployment workflow would be:

```shell
cd ~/pb_deploy
terraform init
terraform workspace select production
terraform plan -out=production.tfplan
terraform apply production.tfplan
```
A deployment environment could be torn down as follows:
```shell
cd ~/pb_deploy
terraform init
terraform workspace select staging
terraform destroy
```
[PolicyBrain]: https://github.com/OpenSourcePolicyCenter/PolicyBrain
[Open Source Policy Center]: https://github.com/OpenSourcePolicyCenter/PolicyBrain
[Terraform state]: https://www.terraform.io/docs/state/index.html
[Terraform S3 backend]: https://www.terraform.io/docs/backends/types/s3.html
[Amazon ECS Fargate]: https://aws.amazon.com/fargate/
[Amazon ElastiCache]: https://aws.amazon.com/elasticache/
[Amazon Route 53]: https://aws.amazon.com/route53/
[Amazon ELB]: https://aws.amazon.com/elasticloadbalancing/
[Amazon VPC]: https://aws.amazon.com/vpc/
[Terraform AWS authentication]: https://www.terraform.io/docs/providers/aws/index.html#authentication
[Amazon ECS task execution role]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
[Terraform workspaces]: https://www.terraform.io/docs/state/workspaces.html
28 changes: 28 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
provider "aws" {
region = "${var.region}"
}

terraform {
backend "s3" {
bucket = "ospc-terraform-state-storage-s3"
key = "terraform.tfstate"
dynamodb_table = "ospc-terraform-state-lock-table"
region = "us-east-2"
encrypt = true
}
}

module "networking" {
source = "./modules/networking"
environment = "${terraform.workspace}"
region = "${var.region}"
}

module "worker" {
source = "./modules/worker"
environment = "${terraform.workspace}"
region = "${var.region}"
vpc_id = "${module.networking.vpc_id}"
subnet_ids = ["${module.networking.public_subnet_ids}"]
api_hostname = "${var.api_hostname}"
}
62 changes: 62 additions & 0 deletions modules/networking/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* Fetch the available AZs in the configured region */
data "aws_availability_zones" "available" {}

/* The VPC */
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true

tags {
Name = "${var.environment}-vpc"
Environment = "${var.environment}"
}
}

/* Subnets */
/* Internet gateway for the public subnet */
resource "aws_internet_gateway" "ig" {
vpc_id = "${aws_vpc.vpc.id}"

tags {
Name = "${var.environment}-igw"
Environment = "${var.environment}"
}
}

/* Public subnet */
resource "aws_subnet" "public" {
vpc_id = "${aws_vpc.vpc.id}"
cidr_block = "${cidrsubnet(aws_vpc.vpc.cidr_block, 8, count.index+1)}"
count = 2
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
map_public_ip_on_launch = true

tags {
Name = "${var.environment}-${data.aws_availability_zones.available.names[count.index]}-public-subnet"
Environment = "${var.environment}"
}
}

/* Routing table for public subnet */
resource "aws_route_table" "public" {
vpc_id = "${aws_vpc.vpc.id}"

tags {
Name = "${var.environment}-public-route-table"
Environment = "${var.environment}"
}
}

resource "aws_route" "public_internet_gateway" {
route_table_id = "${aws_route_table.public.id}"
destination_cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.ig.id}"
}

/* Route table associations */
resource "aws_route_table_association" "public" {
count = "${aws_subnet.public.count}"
subnet_id = "${aws_subnet.public.*.id[count.index]}"
route_table_id = "${aws_route_table.public.id}"
}
7 changes: 7 additions & 0 deletions modules/networking/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "vpc_id" {
value = "${aws_vpc.vpc.id}"
}

output "public_subnet_ids" {
value = ["${aws_subnet.public.*.id}"]
}
2 changes: 2 additions & 0 deletions modules/networking/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
variable "environment" {}
variable "region" {}
Loading

0 comments on commit c9df5d9

Please sign in to comment.