Deploying a Swarm Service Using Terraform

Hands-On Lab

 

Photo of Travis Thomsen

Travis Thomsen

Course Development Director in Content

Length

00:45:00

Difficulty

Advanced

Our small consulting business is growing. Because of this, we've decided to migrate from running a single Docker host to running multiple Docker servers in Swarm mode. This will allow us to offer Swarm services to our customers using Terraform. Before making this available to customers though, we need to test our configuration to make sure everything is working properly. To do this, we need to finish the setup of Docker Swarm, then create a Terraform root module that will deploy a Ghost blog service.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Deploying a Swarm Service Using Terraform

The Scenario

Our small consulting business is growing. Because of this, we've decided to migrate from running a single Docker host to running multiple Docker servers in Swarm mode. This will allow us to offer Swarm services to our customers using Terraform. Before making this available to customers though, we need to test our configuration to make sure everything is working properly.

To do this, we need to finish the setup of Docker Swarm, then create a Terraform root module that will deploy a Ghost blog service.

Logging In

Use the IP address and credentials provided on the hands-on lab overview page, and log in with SSH to the server.

There's a fair bit of creating and editing files in this lab. All of the examples here involve using Vim, but use whichever text editor is most comfortable.

Complete the Setup of Docker Swarm

On the manager node get the join token:

docker swarm join-token worker

On the worker node run the join command (pasting the join token in the appropriate spot):

docker swarm join --token [JOIN_TOKEN] [IP]:2377

Create the Variables File

Create variables.tf:

vi variables.tf

variables.tf contents:

variable "mysql_root_password" {
  description = "The MySQL root password."
  default     = "P4sSw0rd0!"
}

variable "ghost_db_username" {
  description = "Ghost blog database username."
  default     = "root"
}

variable "ghost_db_name" {
  description = "Ghost blog database name."
  default     = "ghost"
}

variable "mysql_network_alias" {
  description = "The network alias for MySQL."
  default     = "db"
}

variable "ghost_network_alias" {
  description = "The network alias for Ghost"
  default     = "ghost"
}

variable "ext_port" {
  description = "The public port for Ghost"
  default     = "80"
}

Create the Images File

Create images.tf:

vi images.tf

images.tf contents:

resource "docker_image" "ghost_image" {
  name = "ghost:alpine"
}

resource "docker_image" "mysql_image" {
  name = "mysql:5.7"
}

Create the Networks File

Create networks.tf:

vi networks.tf

networks.tf contents:

resource "docker_network" "public_overlay_network" {
  name   = "public_network"
  driver = "overlay"
}

resource "docker_network" "private_overlay_network" {
  name     = "mysql_internal"
  driver   = "overlay"
  internal = true
}

Create the Volumes File

Create volumes.tf:

vi volumes.tf

volume.tf contents:

resource "docker_volume" "mysql_data_volume" {
  name = "mysql_data"
}

Create the main.tf File

Create main.tf:

vi main.tf

main.tf contents:

resource "docker_service" "ghost-service" {
 name = "ghost"

 task_spec {
   container_spec {
     image = "${docker_image.ghost_image.name}"

     env {
        database__client               = "mysql"
        database__connection__host     = "${var.mysql_network_alias}"
        database__connection__user     = "${var.ghost_db_username}"
        database__connection__password = "${var.mysql_root_password}"
        database__connection__database = "${var.ghost_db_name}"
     }
   }
   networks = ["${docker_network.public_bridge_network.name}", "${docker_network.private_bridge_network.name}"]
 }

 endpoint_spec {
   ports {
     target_port    = "2368"
     published_port = "${var.ext_port}"
   }
 }
}

resource "docker_service" "mysql-service" {
 name = "${var.mysql_network_alias}"

 task_spec {
   container_spec {
     image = "${docker_image.mysql_image.name}"

     env {
       MYSQL_ROOT_PASSWORD = "${var.mysql_root_password}"
     }

     mounts = [
       {
         target = "/var/lib/mysql"
         source = "${docker_volume.mysql_data_volume.name}"
         type   = "volume"
       }
     ]
   }
   networks = ["${docker_network.private_bridge_network.name}"]
 }
}

Deploy the Infrastructure

Initialize Terraform:

terraform init

Build a plan:

terraform plan -out=tfplan -var 'ext_port=8080'

Apply the plan:

terraform apply tfplan

Conclusion

We made it. Our apply, once it finishes, will add seven resources and dump us back to a command line. If we want to check whether everything is up and running, we can run:

docker service ls

Once we see db and ghost online (we may have wait a bit before docker service ls shows this), then we can go visit our swarm manager's public IP address on port 8080 (http://<PUBLIC IP>:8080/) and our Ghost blog should pop up. Congratulations!