TerraBoi

This ruby gem was created by Charlie Reese to get rails applications deployed into production as quickly and easily as possible.

Raison d'etre: creating basic infrastructure to house production SaaS applications on AWS is tedious and boring. It's often a similar process every time, and every time it sucks.

List of items created by this gem's generators:

  • Dockerfile
  • Rails initializer file (for setting up config.hosts)
  • Packer repository (for creating AMIs)
  • Terraform repository (for creating infrastructure as code to immediately deploy staging / prod infrastructure as well as rolling out application updates)

Note: generated Terraform files create / support remote state locking, load-balancing, auto-scaling, zero-downtime web app deployments, DBs, and S3 buckets.

Note: after infrastructure files are generated, you will be ready to deploy your application to staging / production on AWS. If you have more advanced infrastructure needs (e.g. Redis / Solr instances), you may add to the generated Terraform files to support this.

Pre-requisites

Installation

Note: below installation steps should be completed in order.

Installation - gem

Add this line to your (Rails) application's Gemfile:

gem 'terra_boi'

And then execute:

$ bundle

Installation - AWS access

Set up your AWS access / secret access keys in ~/.zprofile (or equivalent file for your shell if not using .zsh) as environment variables:

export AWS_ACCESS_KEY_ID=your_access_key_id
export AWS_SECRET_ACCESS_KEY=your_secret_access_key

Then run source ~/.zprofile (or equivalent command for your shell if not using .zsh)

Note: SSH access to created EC2 instances is granted using SSH keys existing at ~/.ssh/id_rsa.pub and ~/.ssh/id_rsa on your local machine. If they do not exist, create them.

Installation - generate infrastructure code

To generate boilerplate infrastructure code (config.host initializer filer, Dockerfile, Packer repository, and terraform repository):

rails generate terra_boi:boilerplate --domain_name DOMAIN.COM --ruby_version 2.5.1

Installation - Packer (creating web server AMIs)

A. Create private DockerHub repository

Create private DockerHub repository for your rails application (if possible, use the exact same name as your rails application).

Note: packer generator will assume your DockerHub repository has the same name as your rails application folder. If this isn't true, update generated Packer ami_build.json file after it is generated.

B. Setup DockerHub access:

Add DockerHub username and access key to ~/.zprofile (or equivalent file for your shell if not using .zsh) as environment variables (if your image is in a private repository):

DOCKERHUB_USERNAME=myname
DOCKERHUB_ACCESS_TOKEN=myAccessToken
export DOCKERHUB_USERNAME DOCKERHUB_ACCESS_TOKEN

Then run source ~/.zprofile (or equivalent command for your shell if not using .zsh)

Note: DockerHub access key can be found at https://hub.docker.com/settings/security

Installation - Terraform (deploying DBs + web server AMIs)

A. Set up remote state:

cd terraform/state

Run terraform init and then terraform apply to set up s3 bucket and dynamoDB for remote state and locking (this will work for both prod and staging).

B. Set up DB / S3:

cd terraform/[ENV]/data

Set terraform data-related environment variables in .zprofile (or your respective shell dotfile)

TF_VAR_db_password=your_password
TF_VAR_db_username=your_username

To deploy infrastructure to AWS:

terraform init # IF NOT ALREADY RUN
terraform apply

C. Set up web servers:

cd terraform/[ENV]/web_servers

To deploy infrastructure to AWS:

terraform init # IF NOT ALREADY RUN
terraform apply

While aws_acm_certificate_validation.cert is creating (it will hang if you don't add CNAME verification record in ACM):

i. Log into AWS console, go to certificate management, and add the created CNAME record specified to the DNS configuration for your domain ii. Redirect domain name to Application load balancer: - Go to your domain registrar of choice - Create alias record that points to the dns name of the application load balancer (use subdomain in alias record like STAGING.example.com for staging) - Create URL redirect record for prod (redirect www.site.com to site.com)

After these changes propogate (should take about an hour or two locally), your webservers should be set up, https should be working, and you should be good to go!

Usage

Note: below usage steps should be completed in order

Usage - Packer (creating web server AMIs)

A. Push latest application image to DockerHub

You can automatically trigger DockerHub image builds when new code is pushed to a repository's master branch using DockerHub's free Github integration.

Otherwise, docker build . && docker container create [IMAGE_ID] && docker commit [CONTAINER_ID] [DOCKER_USERNAME]/[APPLICATION_NAME]:latest && docker push [DOCKER_USERNAME]/[APPLICATION_NAME]:latest. Make sure you are pushing to a private repository.

B. Create Packer AMI:

cd packer 

packer build -var DOCKERHUB_ACCESS_TOKEN=$DOCKERHUB_ACCESS_TOKEN -var DOCKERHUB_USERNAME=$DOCKERHUB_USERNAME -var DB_PASSWORD=$TF_VAR_db_password -var AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID -var AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY application.json

C. Clean up:

Every so often you'll want to remove old AMIs created by Packer (unless you want to be charged a couple cents a month).

To remove them, deregister them on the AWS AMI management page, then delete the associated snapshot on the AWS snapshot management page.

Usage - Terraform (update web server AMIs)

A. Update Terraform web server AMIs:

cd terraform/[ENV]/web_servers

To deploy infrastructure to AWS:

terraform init # IF NOT ALREADY RUN
terraform apply

Usage - ssh access

ssh into ec2 instance and run bash in container:

ssh ubuntu@PUBLICIPOFINSTANCE

docker container exec -it APP_NAME bash

Infrastructure created

The aforementioned generators create a terraform directory with state, prod, and staging subdirectories.

The state directory contains an S3 bucket and a DynamoDB table to store and lock state (for both prod and staging).

The prod and staging subdirectories contain data (DB + S3) and web_servers (SSL cert, load balancing, autoscaling, EC2) directories.

Running tests

From the root directory:

rake test

Other tips

Clean up terraform infrastructure when no longer planning to use (DANGER FOR PROD, WILL DESTROY INFRASTRUCTURE):

terraform destroy

For extra security in staging: update Terraform web_servers main.tf file to only allow ingress web_server connections from your IP / your team's IPs

Contributing

This gem is currently not actively accepting contributions.

With that in mind, if you'd like to make a fix / change, please create a pull request (and when I have a moment - probably in a couple weeks time - I'll have a look)!

License

The gem is available as open source under the terms of the MIT License.

Updating gem version (for maintainers)

1. Update version

In lib/terra_boi/version.rb update version.

2. Build gem

gem build terra_boi.gemspec

3. Push gem

gem push terra_boi-X.X.X.gem (replace X's with version)

4. Tag GitHub

git add -A git commit -m "Msg" git tag -a vX.X.X -m "Msg" git push --tags