Terraform Lisapraktika
Eeldused: Terraform Alused Labor tehtud, SSH võtmed töötavad, Ubuntu-1 ja Ubuntu-2 saadaval
Platvorm: Terraform + SSH → Ubuntu serverid
Kestus: ~60-90 min
Ülevaade
Kolm progresseerunud harjutust, mis ehitavad põhilabori peale. Igaüks õpetab üht olulist päris-maailma tehnikat:
- Multi-Server Deployment - Count vs For_Each
- Template-Based Configuration - Templatefile() ja dynamic content
- Conditional Deployment - Environment-based logic
Iga harjutus järgib struktuuri: Probleem → Lahendus → Harjutus.
1. Multi-Server Deployment
1.1 Probleem
Põhilabors deploy'sime ühe serveri käsitsi. Aga päris elus on mitu: web servers, API servers, database replicas. Copy-paste on aeglane ja veaaldis - kui muudad ühte, pead muutma kõiki.
Terraform'il on kaks meetodit mitme ressursi loomiseks: count (lihtne numbriline loop) ja for_each (map-based, stabiilsem).
1.2 Lahendus
Count näide:
# main.tf
variable "server_ips" {
type = list(string)
default = ["10.0.208.20", "10.0.208.21"]
}
resource "null_resource" "web_servers" {
count = length(var.server_ips)
connection {
type = "ssh"
host = var.server_ips[count.index]
user = "kasutaja"
private_key = file("~/.ssh/id_ed25519")
}
provisioner "remote-exec" {
inline = [
"sudo apt update -qq && sudo apt install -y nginx",
"echo '<h1>Server ${count.index + 1}</h1>' | sudo tee /var/www/html/index.html",
"sudo systemctl restart nginx",
]
}
}
For_each näide (parem):
variable "servers" {
type = map(object({
ip = string
port = number
}))
default = {
web1 = { ip = "10.0.208.20", port = 8080 }
web2 = { ip = "10.0.208.21", port = 8081 }
}
}
resource "null_resource" "web_servers" {
for_each = var.servers
connection {
type = "ssh"
host = each.value.ip
user = "kasutaja"
private_key = file("~/.ssh/id_ed25519")
}
provisioner "remote-exec" {
inline = [
"sudo apt update -qq && sudo apt install -y nginx",
"echo 'server { listen ${each.value.port}; root /var/www/html; }' | sudo tee /etc/nginx/sites-available/${each.key}",
"sudo ln -sf /etc/nginx/sites-available/${each.key} /etc/nginx/sites-enabled/",
"echo '<h1>${each.key}</h1>' | sudo tee /var/www/html/index.html",
"sudo systemctl reload nginx",
]
}
}
Erinevus:
count: ressursid on[0],[1]→ kui kustutad esimese, nihkub teinefor_each: ressursid on["web1"],["web2"]→ stabiilne
1.3 Harjutus: Deploy Nginx Mõlemale
Nõuded:
- Deploy nginx Ubuntu-1 (10.0.208.20:8080) ja Ubuntu-2 (10.0.208.21:8081)
- Kasuta
for_each(mitte count) - Ubuntu-1 HTML: "Frontend Server"
- Ubuntu-2 HTML: "Backend Server"
- Output mõlemad URL'id
Näpunäiteid:
variable "servers" {
default = {
frontend = { ip = "10.0.208.20", port = 8080, title = "Frontend Server" }
backend = { ip = "10.0.208.21", port = 8081, title = "Backend Server" }
}
}
Testimine:
Boonus:
- Lisa kolmas server (Alma-1: 10.0.208.30:8082)
2. Template-Based Configuration
2.1 Probleem
Inline strings provisioner'ites on loetamatud ja raskesti muudetavad. Päris nginx config on 50+ rida. Kuidas hallata seda elegantelt?
Terraform templatefile() funktsioon võimaldab eraldi template faile kasutada ja dynamic väärtused sisse süstida.
2.2 Lahendus
Loo kaust ja template:
terraform-advanced/
├── main.tf
├── templates/
│ ├── nginx.conf.tpl
│ └── index.html.tpl
└── files/
└── (generated configs)
templates/nginx.conf.tpl:
# ${server_name} - Generated by Terraform
server {
listen ${port};
server_name _;
root /var/www/${server_name};
index index.html;
location / {
try_files $uri $uri/ =404;
}
%{ if enable_monitoring ~}
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
%{ endif ~}
add_header X-Server-Name "${server_name}";
}
templates/index.html.tpl:
<!DOCTYPE html>
<html>
<head>
<title>${server_name}</title>
<style>
body {
font-family: Arial;
background: ${bg_color};
color: white;
padding: 50px;
text-align: center;
}
</style>
</head>
<body>
<h1>🚀 ${server_name}</h1>
<p>Role: <strong>${role}</strong></p>
<p>Port: <strong>${port}</strong></p>
<hr>
<small>Deployed: ${timestamp}</small>
</body>
</html>
main.tf:
variable "servers" {
type = map(object({
ip = string
port = number
role = string
bg_color = string
enable_monitoring = bool
}))
default = {
web = {
ip = "10.0.208.20"
port = 8080
role = "frontend"
bg_color = "#0066cc"
enable_monitoring = true
}
api = {
ip = "10.0.208.21"
port = 8081
role = "api"
bg_color = "#cc6600"
enable_monitoring = true
}
}
}
# Generate config files locally first
resource "local_file" "nginx_configs" {
for_each = var.servers
filename = "${path.module}/files/${each.key}-nginx.conf"
content = templatefile("${path.module}/templates/nginx.conf.tpl", {
server_name = each.key
port = each.value.port
enable_monitoring = each.value.enable_monitoring
})
}
resource "local_file" "html_files" {
for_each = var.servers
filename = "${path.module}/files/${each.key}-index.html"
content = templatefile("${path.module}/templates/index.html.tpl", {
server_name = each.key
role = each.value.role
port = each.value.port
bg_color = each.value.bg_color
timestamp = timestamp()
})
}
# Deploy to servers
resource "null_resource" "deploy" {
for_each = var.servers
depends_on = [
local_file.nginx_configs,
local_file.html_files
]
connection {
type = "ssh"
host = each.value.ip
user = "kasutaja"
private_key = file("~/.ssh/id_ed25519")
}
provisioner "file" {
source = "${path.module}/files/${each.key}-nginx.conf"
destination = "/tmp/nginx.conf"
}
provisioner "file" {
source = "${path.module}/files/${each.key}-index.html"
destination = "/tmp/index.html"
}
provisioner "remote-exec" {
inline = [
"sudo apt update -qq && sudo apt install -y nginx",
"sudo mkdir -p /var/www/${each.key}",
"sudo mv /tmp/index.html /var/www/${each.key}/",
"sudo mv /tmp/nginx.conf /etc/nginx/sites-available/${each.key}",
"sudo ln -sf /etc/nginx/sites-available/${each.key} /etc/nginx/sites-enabled/",
"sudo rm -f /etc/nginx/sites-enabled/default",
"sudo nginx -t && sudo systemctl reload nginx",
]
}
}
2.3 Harjutus: Custom Templates
Nõuded:
- Loo template nginx.conf ja index.html
- Ubuntu-1: sinine taust (#0066cc), "Web Server"
- Ubuntu-2: roheline taust (#00cc66), "API Server"
- HTML näitab deployment aega (
timestamp()) - Nginx config sisaldab
/healthendpoint
Näpunäiteid:
- Template syntax:
${variable}asendab,%{ if }on conditional templatefile(path, vars)genereerib sisu- Esmalt loo
local_file, siis uploadprovisioner "file"
Testimine:
Boonus:
- Lisa
/metricsendpoint, mis näitab serveri info't - Conditional SSL block template'is (kuigi cert'e pole)
3. Conditional Deployment
3.1 Probleem
Dev ja prod keskkonnad vajavad erinevat konfiguratsiooni. Dev'is debug mode, prod'is SSL ja monitoring. Kuidas hallata ühes Terraform projektis?
Terraform toetab conditionals: count = condition ? 1 : 0 ja dynamic blocks.
3.2 Lahendus
variable "environment" {
type = string
default = "dev"
validation {
condition = contains(["dev", "prod"], var.environment)
error_message = "Must be dev or prod"
}
}
variable "servers" {
type = map(object({
ip = string
port = number
}))
}
locals {
# Environment-specific configs
config = {
dev = {
replicas = 1
enable_ssl = false
enable_monitoring = false
log_level = "debug"
}
prod = {
replicas = 2
enable_ssl = true
enable_monitoring = true
log_level = "error"
}
}
current_config = local.config[var.environment]
}
resource "null_resource" "deploy" {
for_each = var.servers
connection {
type = "ssh"
host = each.value.ip
user = "kasutaja"
private_key = file("~/.ssh/id_ed25519")
}
provisioner "remote-exec" {
inline = concat(
[
"sudo apt update -qq && sudo apt install -y nginx",
"echo 'Environment: ${var.environment}' | sudo tee /var/www/html/env.txt",
],
# Conditional: only in prod
local.current_config.enable_monitoring ? [
"sudo apt install -y prometheus-node-exporter",
"sudo systemctl enable prometheus-node-exporter",
] : [],
[
"echo 'log_level: ${local.current_config.log_level}' | sudo tee -a /var/www/html/env.txt",
"sudo systemctl reload nginx",
]
)
}
}
# Conditional resource: monitoring only in prod
resource "null_resource" "monitoring" {
count = var.environment == "prod" ? 1 : 0
connection {
type = "ssh"
host = var.servers["web"].ip
user = "kasutaja"
private_key = file("~/.ssh/id_ed25519")
}
provisioner "remote-exec" {
inline = [
"echo 'Setting up production monitoring...'",
"# Install monitoring tools here",
]
}
}
Kasutamine:
# Dev deployment
terraform apply -var="environment=dev"
# Prod deployment
terraform apply -var="environment=prod"
3.3 Harjutus: Dev vs Prod
Nõuded:
- Loo terraform.tfvars.dev ja terraform.tfvars.prod
- Dev: ainult Ubuntu-1, port 8080, debug mode
- Prod: mõlemad Ubuntu'd, port 443, monitoring enabled
- Kasuta
countvõifor_eachconditional'iga - HTML näitab environment'i
Näpunäiteid:
# terraform.tfvars.dev
environment = "dev"
servers = {
web = { ip = "10.0.208.20", port = 8080 }
}
# terraform.tfvars.prod
environment = "prod"
servers = {
web1 = { ip = "10.0.208.20", port = 443 }
web2 = { ip = "10.0.208.21", port = 443 }
}
Testimine:
# Dev
terraform apply -var-file="terraform.tfvars.dev"
# Prod
terraform apply -var-file="terraform.tfvars.prod"
Boonus:
- Prod'is enable firewall (
ufw) automaatselt - Dev'is install development tools (
curl,vim,htop)
Kasulikud Ressursid
Dokumentatsioon:
Tööriistad:
- Terraform Console - testi expressions:
terraform console - Terraform fmt - vorminda kood:
terraform fmt - Terraform validate - kontrolli syntax:
terraform validate
Näited:
- HashiCorp Learn: https://learn.hashicorp.com/terraform