Automating the deployment of Docker Containers on Amazon EC2 with Infrastructure as Code (IaC) tool - Terraform
Tags: devops, aws, docker, terraform
Published: 2023-11-13 21:38:01

#Introduction:
Docker has revolutionized the way applications are deployed and run, providing a lightweight and consistent environment across different platforms. Amazon EC2, a popular cloud computing service, allows users to deploy virtual servers in the cloud. In this guide, we'll walk you through the process of pulling a Docker image from Docker Hub and running a container on an EC2 instance using Infrastructure as Code (IaC).

##Prerequisites:
Before you begin, ensure you have the following prerequisites in place:

- An AWS account with the necessary permissions to create EC2 instances.
- The AWS CLI installed on your local machine.
- Terraform installed on your local machine.

##Step 1: Create an EC2 Key Pair
To securely access your EC2 instance, you'll need an SSH key pair. If you don't have one, create a new key pair using the AWS Management Console or the AWS CLI.

```
aws ec2 create-key-pair --key-name YourKeyName --query 'KeyMaterial' --output text > YourKeyName.pem
chmod 400 YourKeyName.pem

```

Alternatively, you can use ssh-keygen to create the key-pair on your computer.


#### Generate SSH key pair
```
ssh-keygen -t rsa -b 2048 -f YourKeyName

```
#### Set appropriate permissions
```
chmod 400 YourKeyName
```
Replace YourKeyName with a preferred name for your key pair.


##2. Create a main.tf file 

```
touch main.tf
```
 
### Update main.tf
Modify the aws_instance resource block in main.tf to include the key_name parameter:


```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = "us-west-2"
}


resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr_block

  tags = {
    "Name" = "Production ${var.main_vpc_name}"
  }
}

# Create a subnet
resource "aws_subnet" "web" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.web_subnet
  availability_zone = var.subnet_zone

  tags = {
    Name = "Web Subnet"
  }
}


resource "aws_internet_gateway" "my_web_igw" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.main_vpc_name} IGW"
  }
}

resource "aws_default_route_table" "main_vpc_default_rt" {
  default_route_table_id = aws_vpc.main.default_route_table_id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.my_web_igw.id
  }

  tags = {
    Name = "my-default-rt"
  }
}

resource "aws_default_security_group" "default_sec_group" {
  vpc_id = aws_vpc.main.id

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

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

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

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

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

  tags = {
    "Name" = "Default Security Group"
  }
}

# Create a new Key Pair
resource "aws_key_pair" "terraform_ssh_pair" {
  key_name = "terraform_rsa" 
  public_key = file(var.ssh_public_key)
}


data "aws_ami" "latest_amazon_linux2" {
  owners = [ "amazon" ]
  most_recent = true
  filter {
    name = "name"
    values = ["amzn2-ami-kernel-*-x86_64-gp2"] 
  }

  filter {
    name = "architecture"
    values = [ "x86_64" ]
  }

}

resource "aws_instance" "web-server" {
  ami                         = data.aws_ami.latest_amazon_linux2.id 
  instance_type               = "t2.micro"
  subnet_id                   = aws_subnet.web.id
  vpc_security_group_ids      = [aws_default_security_group.default_sec_group.id]
  associate_public_ip_address = true
  user_data = file("script.sh")
  key_name                    = aws_key_pair.terraform_ssh_pair.key_name
  tags = {
    "Name" : "My Public Web Server"
  }
}

```

The user_data is contained in script.sh below. This installs an apache server (to confirm if everything worked correctly). It also installs docker, terraform and runs an nginx docker container as well. You can modify the script to suit your requirements.

```
#!/bin/bash

# update vm and install server
sudo yum -y update && sudo yum -y install httpd
sudo systemctl start httpd && sudo systemctl enable httpd
sudo echo "<h1>Deployed via Terraform</h1>" > /var/www/html/index.html

# install terraform
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo yum -y install terraform
sudo touch ~/.bashrc
sudo terraform -install-autocomplete

# install docker
sudo yum -y install docker
sudo systemctl start docker
sudo amazon-linux-extras install docker
sudo service docker start
sudo usermod -aG docker ec2-user
sudo chkconfig docker on
sudo yum install -y git
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo systemctl enable docker
sudo docker run -d -p 8080:80 nginx
```


## 3. Create a variables.tf file 

```
touch variables.tf
```


### Update variables.tf

```
variable "vpc_cidr_block" {
  type        = string
  description = "cidr block for the main vpc"
  default     = "10.0.0.0/16"
}

variable "web_subnet" {
  type        = string
  description = "cidr block for the web subnet"
  default     = "10.0.10.0/24"
}

variable "subnet_zone" {
  type        = string
  description = "availability zone for subnet zone"
  # subnet_zone was set as environment variable
}

variable "ssh_public_key" {
  type = string
  description = "public key that permits ssh connection to the ec2 instance"
}

variable "main_vpc_name" {
  type        = string
  description = "name of the main vpc"
}

variable "my_public_ip" {
  description = "my public ip address"
}
```

## 4. Create an output.tf file

```
touch output.tf
```

Update the output.tf file with the configuration below which provides the public IP address of the instance upon creation.

```
output "public_ip" {
  description = "The public IP address of the EC2 instance."
  value       = aws_instance.web-server.public_ip
}
```

## 5. Usage

- Follow the previous steps to clone [the repository](https://github.com/CalebYeboah27/terraform-dev-environment), customize configurations, and run terraform apply.
- After the Terraform apply, you will find the public IP address and the path to the local SSH private key in the outputs.
- Use the following command to SSH into the EC2 instance:


```
ssh -i YourKeyName ec2-user@<public_ip>

```

Replace YourKeyName with the name of your private key file and <public_ip> with the actual public IP address.

Now, you can securely access your EC2 instance using the generated SSH key pair.