Step-by-Step Solution for KodeKloud's Terraform Challenge 2

Step-by-Step Solution for KodeKloud's Terraform Challenge 2

KodeKloud is the #1 DevOps course provider and helps students learn trending technologies they need to thrive in their career.

They also have this Terraform Challenges series that consists of a set of challenges that will assist you in mastering provisioning and managing infrastructure using Terraform.

In this challenge we will implement a simple LAMP stack using terraform and docker.

Architecture diagram

1º Task - Install Terraform version 1.1.5 on iac-server (terraform-jump-host)

Install terraform binary version=1.1.5 on iac-server


Solution:

We already completed the exact same task during the Step-by-Step Solution for KodeKloud's Terraform Challenge 1. If you haven’t seen it yet, check it out.

Anyway, these are the commands needed to install it:

sudo apt-get update && sudo apt-get install -y gnupg software-properties-common

wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null

echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update

sudo apt install terraform=1.1.5

Checking if terraform was installed:

Video gif. A gray tabby cat quickly puts on a pair of round sunglasses and looks up. Text, "I'm ready."

2º Task - Check out the docker provider

Docker provider has already been configured using kreuzwerker/docker provider.
Check out the provider.tf given at /root/code/terraform-challenges/challenge2


Solution:

Why just check the provider is relevant?

Because we need to know the exactly namespace and version it´s using, so we can look at the right documentation page.

provider.tf

terraform {
  required_providers {
    docker = {
      source = "kreuzwerker/docker"
      version = "2.16.0"
    }
  }
}

Namespace: kreuzwerker

Provider: docker

Version: 2.16.0

Here´s the provider documentation: https://registry.terraform.io/providers/kreuzwerker/docker/2.16.0/docs

Check It Out Canadian GIF by MOODMAN

3º Task - Create two docker images (php-httpd-image and mariadb-image)

Create a terraform resource named php-httpd-image for building docker image with following specifications:

  • Image name: php-httpd:challenge

  • Build context: lamp_stack/php_httpd

  • Labels: challenge: second

Create a terraform resource named mariadb-image for building docker image with following specifications:

  • Image name: mariadb:challenge

  • Build context: lamp_stack/custom_db

  • Labels: challenge: second


Solution:

Let’s refer to the documentation: https://registry.terraform.io/providers/kreuzwerker/docker/2.16.0/docs/resources/image#build

Although not explicitly stated, there is a convention that says to concentrate all your terraform resources into a single file named main.tf

Let's make use of the example as reference, and modify it according to the requirements.

main.tf

resource "docker_image" "php-httpd-image" {
  name = "php-httpd:challenge"
  build {
    path = "lamp_stack/php_httpd"
    label = {
      challenge: "second"
    }
  }
}

resource "docker_image" "mariadb-image" {
  name = "mariadb:challenge"
  build {
    path = "lamp_stack/custom_db"
    label = {
      challenge: "second"
    }
  }
}

NOTE: the field path represents the context path. You can check it by listing the mentioned directories:

Run the command terraform plan to review the execution plan, and terraform apply to create it.

It can take some minutes to complete the build, so grab a coffee and wait ☕⌛

But First Coffee Basketball GIF by NBA

Once it is finished, check the images with the command docker images

its working GIF

4º Task - Create a docker network

Create a terraform resource named private_network and configure the following:

  • Create a Docker network with name=my_network

  • Enable manual container attachment to the network.

  • User-defined key/value metadata: challenge: second


Solution:

Again, let´s make use of the documentation: https://registry.terraform.io/providers/kreuzwerker/docker/2.16.0/docs/resources/network

Let's make use of the example as reference, and modify it according to the requirements.

main.tf

resource "docker_network" "private_network" {
  name = "my_network"
  attachable = true
  labels {
    label = "challenge"
    value = "second"
  }
}
...

NOTES:

  • The attachable field enables the manual container attachment to the network.

  • The labels field is a nested block that allows you to specify multiple label key-value pairs.

Run the command terraform plan to review the execution plan, and terraform apply to create it.

Once is finished, check the network with the command docker network ls

Happy We Got This GIF by Rosanna Pansino

5º Task - Create a docker volume

Create a terraform resource named mariadb_volume creating a docker volume with name=mariadb-volume


Solution:

You already know what I´m gonna say…. let´s check the documentation: https://registry.terraform.io/providers/kreuzwerker/docker/2.16.0/docs/resources/volume

According to the documentation, this is what we need to do:

main.tf

resource "docker_volume" "mariadb_volume" {
  name = "mariadb-volume"
}
...

I know… really simple and boring. Let´s create it and jump to the last task

Run docker volume ls to check the volume:

Tonight Show GIF by The Tonight Show Starring Jimmy Fallon

6º Task - Create 3 containers (php-httpd, phpmyadmin and mariadb)

Define a terraform resource php-httpd for creating docker container with following specification:

  • Container Name: webserver

  • Hostname: php-httpd

  • Image used: php-httpd:challenge

  • Attach the container to network my_network

  • Publish a container's port(s) to the host:

    • Hostport: 0.0.0.0:80

    • containerPort: 80

  • Labels: challenge: second

  • Create a volume with host_path /root/code/terraform-challenges/challenge2/lamp_stack/website_content/ and container_path /var/www/html within webserver container.

Define a terraform resource phpmyadmin for docker container with following configurations:

  • Container Name: db_dashboard

  • Image Used: phpmyadmin/phpmyadmin

  • Hostname: phpmyadmin

  • Attach the container to network my_network

  • Publish a container's port(s) to the host:

    • Hostport: 0.0.0.0:8081

    • containerPort: 80

  • Labels: challenge: second

  • Establish link based connectivity between db and db_dashboard containers (Deprecated)

  • Explicitly specify a dependency on mariadb terraform resource

Define a terraform resource mariadb for creating docker container with following specification:

  • Container Name: db

  • Image Used: mariadb:challenge

  • Hostname: db

  • Attach the container to network my_network

  • Publish a container's port(s) to the host:

    • Hostport: 0.0.0.0:3306

    • containerPort: 3306

  • Labels: challenge: second

  • Define environment variables inside mariadb resource:

    • MYSQL_ROOT_PASSWORD=1234

    • MYSQL_DATABASE=simple-website

  • Attach volume mariadb-volume to /var/lib/mysql directory within db container.


Solution:

Ok…now we have a challenge.

Movie gif. Keanu Reeves as Neo in The Matrix inside of a dojo hopping and smiling and loosening up in preparation to fight.

Since we have the images, the network, and the volume, everything is ready to spin up the containers.

Let´s start by checking the documentation: https://registry.terraform.io/providers/kreuzwerker/docker/2.16.0/docs/resources/container

Let´s start by creating the webserver container

main.tf

resource "docker_container" "php-httpd" {
  name  = "webserver"
  image = docker_image.php-httpd-image.name
  hostname = "php-httpd"
  networks_advanced {
    name = docker_network.private_network.name
  }
  ports {
    internal = 80
    external = 80
  }
  labels {
    label = "challenge"
    value = "second"
  }
  volumes {
    container_path = "/var/www/html"
    host_path  = "/root/code/terraform-challenges/challenge2/lamp_stack/website_content/"
  }
}
...

NOTE: For the networks_advanced field I am using attribute reference to point to the network we already have. The format is: resource_type.resource_name.attribute

Now let´s create the db container

main.tf

resource "docker_container" "mariadb" {
  name  = "db"
  image = docker_image.mariadb-image.name
  hostname = "db"
  networks_advanced {
    name = docker_network.private_network.name
  }
  ports {
    internal = 3306
    external = 3306
  }
  labels {
    label = "challenge"
    value = "second"
  }
  env  = [
    "MYSQL_ROOT_PASSWORD=1234",
    "MYSQL_DATABASE=simple-website"
  ]
  volumes {
    container_path = "/var/lib/mysql"
    volume_name = docker_volume.mariadb_volume.name
  }
}

NOTE: The env field is a Set of Strings. That´s why I used brackets

Finally, let´s create the db_dashboard container

main.tf

resource "docker_container" "phpmyadmin" {
  name  = "db_dashboard"
  image = "phpmyadmin/phpmyadmin"
  hostname = "phpmyadmin"
  networks_advanced {
    name = docker_network.private_network.name
  }
  ports {
    internal = 80
    external = 8081
  }
  labels {
    label = "challenge"
    value = "second"
  }
  links = [
    "db:db_dashboard"
  ]
  depends_on = [ docker_container.mariadb ]
}

NOTE: We need the depends_on Meta-Argument to make sure the container mariadb will be created first. Also note that links field is a Set of Strings, that´s why I used brackets.

Join all this stuff in the main.tf , add salt, pepper and a pinch of luck 🍲🥄

Hungry Chef GIF by Rosanna Pansino

Run the command terraform plan to review the execution plan, and terraform apply to create it.

Run docker ps to check the containers:

Conclusion

At the end, click on the “Check” button, and you should see the architecture diagram highlighted in green like this:

If you found this helpful, a like and share would mean a lot! Consider following for more insightful DevOps content!

Happy learning! 😊

Season 17 Finals GIF by America's Got Talent