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:
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
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.
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 ☕⌛
Once it is finished, check the images with the command docker images
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.
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
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:
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
anddb_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/mysq
l directory within db container.
Solution:
Ok…now we have a challenge.
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 🍲🥄
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! 😊