Terraform guide 2 - Containers
Terraform: Containers
Terraform and Docker
Managing Docker networks
- environment:
mkdir -p ~/terraform/docker/networks
- example
- Crate new files required
1
touch {variables.tf,image.tf,network.tf,main.tf}
- Edit
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29variable "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 = "Public port for Ghost"
default = "8080"
} - Edit
image.tf
1
2
3
4
5
6
7resource "docker_image" "ghost_image" {
name = "ghost:alpine"
}
resource "docker_image" "mysql_image" {
name = "mysql:5.7"
} - Edit
network.tf
1
2
3
4
5
6
7
8
9
10resource "docker_network" "public_bridge_network" {
name = "public_ghost_network"
driver = "bridge"
resource "docker_network" "private_bridge_network" {
name = "ghost_mysql_internal"
driver = "bridge"
internal = true
} - Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44resource "docker_container" "mysql_container" {
name = "ghost_database"
image = "${docker_image.mysql_image.name}"
env = [
"MYSQL_ROOT_PASSWORD=${var.mysql_root_password}"
]
networks_advanced {
name = "${docker_network.private_bridge_network.name}"
aliases = ["${var.mysql_network_alias}"]
}
}
# do not deploy ghost until you have the db ready
resource "null_resource" "sleep" {
depends_on = ["docker_container.mysql_container"]
provisioner "local-exec" {
command = "sleep 15s"
}
}
resource "docker_container" "blog_container" {
name = "ghost_blog"
image = "${docker_image.ghost_image.name}"
depends_on = ["null_resource.sleep", "docker_container.mysql_container"]
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}"
]
ports {
internal = "2368"
external = "${var.ext_port}"
}
networks_advanced {
name = "${docker_network.public_bridge_network.name}"
aliases = ["${var.ghost_network_alias}"]
}
networks_advanced {
name = "${docker_network.private_bridge_network.name}"
aliases = ["${var.ghost_network_alias}"]
}
} - Work with Terraform
1
2
3
4
5
6
7terraform init
terraform validate
terraform plan -out=tfplan -var 'ext_port=8082'
terraform apply tfplan
# clean up
terraform destroy -auto-approve -var 'ext_port=8082'
- Crate new files required
Managing Docker volumes
- environment
1
2cp -r ~/terraform/docker/networks ~/terraform/docker/volumes
cd ../volumes/ - example
- Edit
volumes.tf
1
2
3resource "docker_volume" "mysql_data_volume" {
name = "mysql_data"
} - Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47resource "docker_container" "mysql_container" {
name = "ghost_database"
image = "${docker_image.mysql_image.name}"
env = [
"MYSQL_ROOT_PASSWORD=${var.mysql_root_password}"
]
volumes {
volume_name = "${docker_volume.mysql_data_volume.name}"
container_path = "/var/lib/mysql"
}
networks_advanced {
name = "${docker_network.private_bridge_network.name}"
aliases = ["${var.mysql_network_alias}"]
}
}
resource "null_resource" "sleep" {
depends_on = ["docker_container.mysql_container"]
provisioner "local-exec" {
command = "sleep 15s"
}
}
resource "docker_container" "blog_container" {
name = "ghost_blog"
image = "${docker_image.ghost_image.name}"
depends_on = ["null_resource.sleep", "docker_container.mysql_container"]
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}"
]
ports {
internal = "2368"
external = "${var.ext_port}"
}
networks_advanced {
name = "${docker_network.public_bridge_network.name}"
aliases = ["${var.ghost_network_alias}"]
}
networks_advanced {
name = "${docker_network.private_bridge_network.name}"
aliases = ["${var.ghost_network_alias}"]
}
} - Work with Terraform
1
2
3
4
5
6
7
8
9
10
11terraform init
terraform validate
terraform plan -out=tfplan -var 'ext_port=8082'
terraform apply tfplan
# list Docker volumes
docker volume inspect mysql_data
# list the data in mysql_data
sudo ls /var/lib/docker/volumes/mysql_data/_data
# clean up
terraform destroy -auto-approve -var 'ext_port=8082'
- Edit
Creating swarm services
- environment
1
2cp -r volumes/ services
cd services - example
- Edit
variables.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28variable "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"
} - Edit
images.tf
1
2
3
4
5
6
7resource "docker_image" "ghost_image" {
name = "ghost:alpine"
}
resource "docker_image" "mysql_image" {
name = "mysql:5.7"
} - Edit
network.tf
1
2
3
4
5
6
7
8
9
10resource "docker_network" "public_bridge_network" {
name = "public_network"
driver = "overlay"
}
resource "docker_network" "private_bridge_network" {
name = "mysql_internal"
driver = "overlay"
internal = true
} - Edit
volumes.tf
1
2
3resource "docker_volume" "mysql_data_volume" {
name = "mysql_data"
} - Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51# you do not face issues for having the blog defined before db
# as a swarm, they are on different machines
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}"
}
# volumes as mounts
mounts = [
{
target = "/var/lib/mysql"
source = "${docker_volume.mysql_data_volume.name}"
type = "volume"
}
]
}
networks = ["${docker_network.private_bridge_network.name}"]
}
} - Work with Terraform
1
2
3
4
5
6
7
8
9terraform init
terraform validate
terraform plan -out=tfplan -var 'ext_port=8082'
terraform apply tfplan
docker service ls
docker container ls
s # clean up
terraform destroy -auto-approve -var 'ext_port=8082'
- Edit
Using secrets
- store sensitive data, encrypted with Base64
- environment:
secrets
- example
Encode the password with Base64
1
echo 'p4sSWoRd0!' | base64
Edit
variables.tf
1
2
3
4
5
6
7variable "mysql_root_password" {
default = "cDRzU1dvUmQwIQo="
}
variable "mysql_db_password" {
default = "cDRzU1dvUmQwIQo="
}Create ``image.tf
1
2
3resource "docker_image" "mysql_image" {
name = "mysql:5.7"
}Edit
secrets.tf
1
2
3
4
5
6
7
8
9resource "docker_secret" "mysql_root_password" {
name = "root_password"
data = "${var.mysql_root_password}"
}
resource "docker_secret" "mysql_db_password" {
name = "db_password"
data = "${var.mysql_db_password}"
}Edit
networks.tf
1
2
3
4
5resource "docker_network" "private_overlay_network" {
name = "mysql_internal"
driver = "overlay"
internal = true
}Edit
volumes.tf
1
2
3resource "docker_volume" "mysql_data_volume" {
name = "mysql_data"
}Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39resource "docker_service" "mysql-service" {
name = "mysql_db"
task_spec {
container_spec {
image = "${docker_image.mysql_image.name}"
secrets = [
{
secret_id = "${docker_secret.mysql_root_password.id}"
secret_name = "${docker_secret.mysql_root_password.name}"
file_name = "/run/secrets/${docker_secret.mysql_root_password.name}"
},
{
secret_id = "${docker_secret.mysql_db_password.id}"
secret_name = "${docker_secret.mysql_db_password.name}"
file_name = "/run/secrets/${docker_secret.mysql_db_password.name}"
}
]
env {
MYSQL_ROOT_PASSWORD_FILE = "/run/secrets/${docker_secret.mysql_root_password.name}"
MYSQL_DATABASE = "mydb"
MYSQL_PASSWORD_FILE = "/run/secrets/${docker_secret.mysql_db_password.name}"
}
mounts = [
{
target = "/var/lib/mysql"
source = "${docker_volume.mysql_data_volume.name}"
type = "volume"
}
]
}
networks = [
"${docker_network.private_overlay_network.name}"
]
}
}Work with Terraform
1
2
3
4
5
6
7
8
9
10
11
12
13terraform init
terraform validate
terraform plan -out=tfplan
terraform apply tfplan
# find the MySQL container
docker container ls
# log into the MySQL container
docker container exec -it [CONTAINER_ID] /bin/bash
# access MySQL
mysql -u root -p
# clean up
terraform destroy -auto-approve
Terraform and Kubernetes
Setting up Kubernetes master and installing Terraform
- Edit
kube-config.yml
1
2
3
4
5
6
7
8apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: "v1.13.5"
networking:
podSubnet: 10.244.0.0/16
apiServer:
extraArgs:
service-node-port-range: 8000-31274 - Initialize Kubernetes
1
2
3
4
5
6
7
8
9
10sudo kubeadm init --config kube-config.yml
# copy admin.conf to your home directory
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# install Flannel
sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# untaint the Kubernetes Master
kubectl taint nodes --all node-role.kubernetes.io/master- - Install Terraform 0.11.13 on the Swarm manager
1
2
3
4sudo curl -O https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip
sudo unzip terraform_0.11.13_linux_amd64.zip -d /usr/local/bin/
# check it
terraform version
Creating a pod
- environment:
~/terraform/pod
- example
Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13resource "kubernetes_pod" "ghost_alpine" {
metadata {
name = "ghost-alpine"
}
spec {
host_network = "true"
container {
image = "ghost:alpine"
name = "ghost-alpine"
}
}
}Work with Terraform
1
2
3
4
5
6
7
8
9terraform init
terraform validate
terraform plan
terraform apply -auto-approve
# check pods
kubectl get pods
# clean up
terraform destroy -auto-approve
Creating a pod and service
- environment
~/terraform/service
- example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35resource "kubernetes_service" "ghost_service" {
metadata {
name = "ghost-service"
}
spec {
selector {
app = "${kubernetes_pod.ghost_alpine.metadata.0.labels.app}"
}
port {
port = "2368"
target_port = "2368"
node_port = "8081"
}
type = "NodePort"
}
}
resource "kubernetes_pod" "ghost_alpine" {
metadata {
name = "ghost-alpine"
labels {
app = "ghost-blog"
}
}
spec {
container {
image = "ghost:alpine"
name = "ghost-alpine"
port {
container_port = "2368"
}
}
}
} - Initialize Terraform
1
2
3
4
5
6
7
8
9
10
11terraform init
terraform validate
terraform plan
terraform apply -auto-approve
# check it
kubectl get pods
kubectl get services
# clean up
terraform destroy -auto-approve
- Edit
Creating a deployment
- environment:
~/terraform/deployment
- example
- Edit
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51resource "kubernetes_service" "ghost_service" {
metadata {
name = "ghost-service"
}
spec {
selector {
app = "${kubernetes_deployment.ghost_deployment.spec.0.template.0.metadata.0.labels.app}"
}
port {
port = "2368"
target_port = "2368"
node_port = "8080"
}
type = "NodePort"
}
}
resource "kubernetes_deployment" "ghost_deployment" {
metadata {
name = "ghost-blog"
}
spec {
replicas = "1"
selector {
match_labels {
app = "ghost-blog"
}
}
template {
metadata {
labels {
app = "ghost-blog"
}
}
spec {
container {
name = "ghost"
image = "ghost:alpine"
port {
container_port = "2368"
}
}
}
}
}
} - Working with Terraform
1
2
3
4
5
6
7
8
9
10
11
12terraform init
terraform validate
terraform plan
terraform apply -auto-approve
# check it
kubectl get deployments
kubectl get pods
kubectl delete pod [POD_ID]
# clean up
terraform destroy -auto-approve
- Edit