Building a scalable web site using AWS/Docker/Terraform: adding RDS and WordPress

In my last post I talked about using Terraform to use infrastructure as code principles to create a VPC environment to host a WordPress site. In this article we will deploy an RDS and an EC2 instance to host WordPress.

The RDS will live in two availability zones in US east.  This will provide redundancy if the AZ hosting the primary replica is lost.

RDS

resource "aws_db_subnet_group" "dbsubnetgroup" {
  name        = "rds-subnet-group"
  description = "Our main group of subnets"
  subnet_ids  = ["${aws_subnet.private-a.id}", "${aws_subnet.private-b.id}"]
  tags {
    Project = "wordpress"
  }
}


resource "aws_db_instance" "wpdb" {
  depends_on             = ["aws_security_group.web"]
  identifier             = "wpdb"
  allocated_storage      = "10"
  engine                 = "mysql"
  engine_version         = "5.7"
  instance_class         = "db.t2.micro"
  name                   = "wpdb"
  username               = "wpdb"
  password               = "wpdbwpdb"
  multi_az               = "True"
  vpc_security_group_ids = ["${aws_security_group.db.id}"]
  db_subnet_group_name   = "${aws_db_subnet_group.dbsubnetgroup.id}"
  tags {
    Project = "wordpress"
  }
}

output "rdshost" {
  
  value = "${aws_db_instance.wpdb.address}"

}

For RDS we must create a subnet group to enumerate the availability zones we wish to host our database in. Once that is done we can create the MySQL instance in the already defined database security group. Lastly we want to output the hostname of the database so we can plug it into our EC2 bootstrap script.

EC2

resource "aws_instance" "docker" {
   ami = "${data.aws_ami.ubuntu_ami.id}"
   instance_type = "t2.micro"
   subnet_id = "${aws_subnet.public-a.id}"
   associate_public_ip_address = true
   key_name = "ellin-aws-us-east-1"
   vpc_security_group_ids=["${aws_security_group.web.id}"]
   #iam_instance_profile = "${aws_iam_instance_profile.web_profile.name}"
   user_data = "${data.template_file.bootstrap.rendered}"

}

##Lookup AMI

data "aws_ami" "ubuntu_ami" {
  most_recent = true
  filter {
    name = "name"
    values = ["ubuntu-trusty-14.04-amd64-server*"]
  }
}

output "ec2_address" {
    value = "${aws_instance.docker.public_dns}"
}


data "template_file" "bootstrap" {
    template = "${file("bootstrap.tpl")}"
      vars {
        dbhost = "${aws_db_instance.wpdb.address}"
  }
}

For the EC2 instance we need to launch the instance using our desired AMI.   In addition we need to pass along the DB host to our user data script.  We use a template for the script and pass in the host name returned by terraform.

#!/bin/bash -xe
whoami
apt-get -y update
apt-get -y install unattended-upgrades
curl 'https://bootstrap.pypa.io/get-pip.py' -o 'get-pip.py'
python get-pip.py
pip install awscli
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo 'deb https://apt.dockerproject.org/repo ubuntu-trusty main' > /etc/apt/sources.list.d/docker.list
apt-get -y update
apt-get -y install docker-engine
apt-get -y install fail2ban
pip install docker-compose
docker run -d -e WORDPRESS_DB_HOST=${dbhost}:3306 -e WORDPRESS_DB_PASSWORD=wpdbwpdb -e WORDPRESS_DB_USER=wpdb -e WORDPRESS_DB_NAME=wpdb -p 80:80 wordpress:latest

The bootstrap prepares the AMI by installing updates, installing the docker engine and starting wordpress.

Route53

Lastly we need to add a DNS record set for our EC2 instance.  We add a regular A record for the EC2 instance public IP address and an DNS Alias to handle the bare apex record ellin.tech.

resource "aws_route53_record" "www_cc" {
   zone_id = "${aws_route53_zone.ellintech.zone_id}"
   name = "www.ellin.tech"
   type = "A"
   ttl = "300"
   records = ["${aws_instance.docker.public_ip}"]
}

resource "aws_route53_record" "apex" {
  zone_id = "${aws_route53_zone.ellintech.zone_id}"
  name    = "ellin.tech"
  type    = "A"

  alias {
    name                   = "www.ellin.tech"
    zone_id                = "${aws_route53_zone.ellintech.zone_id}"
    evaluate_target_health = "false"
  }
}

Once the terraform script is applied we should be able to navigate to ellin.tech and see the WordPress welcome screen.

One thing you may have noticed is that we did not use an Elastic IP Address for the EC2 instance.  This means that if we stop the instance and restart it the DNS entry for ellin.tech will no longer work. This is because the public IP will change after reboot.  I will show you how to deal with this by adding an ELB in the next post when we talk about scaling the number of WordPress EC2 instances.

Code for this section can be found at https://github.com/jeffellin/ellin.com/tree/master/wpdemo/ellin3

While it may be possible to copy the terraform state file from the previous section into the terraform for this section I can not guarantee it will work.  It’s best to remove the resources from the previous section before proceeding.

Continued in: Building a scalable web site using AWS/Docker/Terraform: Adding Redundancy

 

Posts in this Series

comments powered by Disqus