Deploying a webserver on private and public subnets using Terraform

Deploying a webserver on private and public subnets using Terraform

Hola !!!

Who doesn't love to learn the core concepts of any technology, right?

In this article, I'll explain the creation and management of VPC, Subnets, Routing tables, Internet gateway, and will finally deploy a webserver on AWS using Terraform. The MySQL database will be in a private subnet, hence in the militarized zone having no connection with the outside world. Yes, WordPress will be in a public subnet and hence exposed.

No alt text provided for this image

The VPC acts as a private cloud for the user. When the user starts an instance on AWS, it gets started in his own VPC. The subnets are like labs. For outside connectivity, a subnet needs a few things. an internet gateway, routing table which will have the address of the gateway. I am going to create my own VPC, two subnets, one internet gateway, and a routing table. I'll associate this routing table to one of the subnets. That means, only this subnet will know where the gateway is, hence will have outside connectivity. I'll deploy WordPress in this subnet and MySQL in another which is actually private. So, let's start right away. For this project, I've created my own AMI as I needed a separate but connected database. So, how did I do it:

  • Create an instance with Amazon Linux
  • Install and enable docker services
  • Pull the images of WordPress and MySQL
  • Create a snapshot of the EBS volume attached to this ec2 instance
  • Finally, create an image of the snapshot
  • You'll find your AMI under 'My AMIs' section


Creating a VPC

resource "aws_vpc" "my_vpc" {
    cidr_block  = "192.168.0.0/16"
    instance_tenancy = "default"
    
    tags = {
        Name = "eric-vpc"
    }
}

The cidr_block is an important input while creating a VPC. It will provide the IP range to the subnets and furthermore to the instances. Here, the netmask I am defining is 255.255.0.0


Let's create two subnets

resource "aws_subnet" "eric_subnet_private" {
    depends_on = [aws_vpc.my_vpc]
    vpc_id = aws_vpc.my_vpc.id
    cidr_block = "192.168.0.0/24"
    availability_zone = "ap-south-1b"
    tags = {
        Name = "subnet-private"
    }
}


resource "aws_subnet" "eric_subnet_public" {
    depends_on = [aws_vpc.my_vpc]
    vpc_id = aws_vpc.my_vpc.id
    cidr_block = "192.168.1.0/24"
    availability_zone = "ap-south-1a"
    map_public_ip_on_launch = "true"
    tags = {
        Name = "subnet-public"
    }
}

Notice, I am enabling it to provide public IP to the instances in the second subnet. One more important thing to note here is that the availability zone has nothing to do with the subnets. I can also create both subnets in one availability zone. Every subnet will have 251 IPs to assign.


Creating security groups for WordPress and MySQL

resource "aws_security_group" "for_wp" {
  depends_on = [aws_vpc.my_vpc]
  name        = "wp-sg"
  vpc_id      = aws_vpc.my_vpc.id


  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [ "0.0.0.0/0" ]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [ "0.0.0.0/0" ]
  }


  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }


  tags = {
    Name = "wp-sg"
  }
}




resource "aws_security_group" "for_mysql" {
  depends_on = [aws_security_group.for_wp]
  name        = "mysql-sg"
  vpc_id      = aws_vpc.my_vpc.id


  ingress {
   from_port = 3306
   to_port = 3306
   protocol = "tcp"
   security_groups = ["${aws_security_group.for_wp.id}"]
  }
  
  ingress {
   from_port = 8888
   to_port = 8888
   protocol = "tcp"
   cidr_blocks = ["0.0.0.0/0"]
  }


  egress {
   from_port = 0
   to_port = 0
   protocol = "-1"
   security_groups = ["${aws_security_group.for_wp.id}"]
  }
}

Here, I am allowing port 80 in WordPress, and 3306 in MySQL.


Internet gateway for outside connectivity

resource "aws_internet_gateway" "eric_ig" {
  depends_on = [aws_vpc.my_vpc]
  vpc_id = aws_vpc.my_vpc.id


  tags = {
    Name = "eric-ig"
  }
}

Let's create a Routing table and associate it with the public subnet

resource "aws_route_table" "wp_rt" {
  depends_on = [aws_internet_gateway.eric_ig]
  vpc_id = aws_vpc.my_vpc.id
   route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.eric_ig.id
  }
  tags = {
    Name = "wp-rt"
  }
}






resource "aws_route_table_association" "rt_a" {
  subnet_id      = aws_subnet.eric_subnet_public.id
  route_table_id = aws_route_table.wp_rt.id
}

The routing table has the information and address of the internet gateway. If it isn't associated with a subnet, that subnet won't be able to send the packets to the gateway and thus will be cut-off from the outside world.


Let's create the database instance

resource "aws_instance"  "mysql_db" {
  depends_on = [aws_route_table.wp_rt]
  ami = "ami-092615e9c8a81d118"
  instance_type = "t2.micro"
  subnet_id = aws_subnet.eric_subnet_private.id
  vpc_security_group_ids = ["${aws_security_group.for_mysql.id}"]
  key_name = "mykey111"
  user_data = <<-EOF
        #!/bin/bash
        sudo docker run -dit -p 8888:3306 --name mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wpdb -e MYSQL_USER=eric -e MYSQL_PASSWORD=password mysql:5.6
  
    EOF
}

I used my personal AMI, which I configured and created earlier. I am also setting the environment and credentials of MySQL.


Now, WordPress

resource "aws_instance" "wordpress" {
    depends_on = [aws_instance.mysql_db]
    ami           = "ami-092615e9c8a81d118"
    instance_type = "t2.micro"
    key_name       = "mykey111"
    vpc_security_group_ids = [ aws_security_group.for_wp.id ]
    subnet_id = aws_subnet.eric_subnet_public.id
}


Now, WordPress needs to be configured to connect with the database running in another subnet which is private and in the same VPC. For this, I created a null resource and provisioner to execute the required commands remotely.

resource "null_resource" "start_wordpress"{
    provisioner "remote-exec" {
        inline = [
            "sudo su << EOF",
            "sudo docker run -dit -e WORDPRESS_DB_HOST=${aws_instance.mysql_db.private_ip}:8888 -e WORDPRESS_DB_USER=eric -e WORDPRESS_DB_PASSWORD=password -e WORDPRESS_DB_NAME=wpdb -p 80:80 --name wp wordpress:4.8-apache",
            "sudo sleep 120",
            "sudo docker start wp"
        EOF
        ]
        
        connection {
            type     = "ssh"
            user     = "ec2-user"
            private_key = file("/home/eric/Downloads/mykey111.pem")
            host     = "${aws_instance.wordpress.public_ip}" 
        }
    }
    depends_on = [aws_instance.wordpress]
}

Let's print the WordPress IP:

output "WordPressIP" {
       value = "${aws_instance.wordpress.public_ip}"
}

After deploying this, one can't access the database since the containing subnet doesn't have the address of the gateway and is private. The docker containers add an extra layer of security to our infrastructure. You can also check out my previous article to make the data permanent by attaching an EBS storage.

Congratulations !!! You've reached the end of another boring yet not-so-boring article.

Until the next time. Peace.

要查看或添加评论,请登录

Sourabh Burnwal的更多文章

社区洞察

其他会员也浏览了