aula-07 e aula-08: Cluster Talos HA na Hetzner com Autoscaler
aula-07: Criação de imagem Talos customizada na Hetzner Cloud - Usa Talos Factory para gerar imagem ARM64/AMD64 - Inclui extensões: qemu-guest-agent, hcloud aula-08: Provisionamento de cluster Kubernetes Talos via OpenTofu - 3 Control Planes em HA (CAX11 ARM64) - 1 Worker Node (CAX11 ARM64) - Rede privada, Floating IP, Firewall - Cluster Autoscaler para Hetzner (0-5 workers extras) - Setup interativo com validação de pré-requisitos - Custo estimado: ~€18/mês (base) Também inclui: - .gitignore para ignorar arquivos sensíveis - CLAUDE.md com instruções do projeto
This commit is contained in:
391
aula-08/main.tf
Normal file
391
aula-08/main.tf
Normal file
@@ -0,0 +1,391 @@
|
||||
############################################################
|
||||
# Hetzner Talos Kubernetes Cluster - Base Infrastructure
|
||||
# Using custom Talos image created from ISO
|
||||
############################################################
|
||||
|
||||
############################################################
|
||||
# PROVIDERS CONFIGURATION
|
||||
############################################################
|
||||
|
||||
provider "hcloud" {
|
||||
token = var.hcloud_token
|
||||
}
|
||||
|
||||
############################################################
|
||||
# DATA SOURCES
|
||||
############################################################
|
||||
|
||||
# Use the custom Talos image created in aula-07
|
||||
data "hcloud_image" "talos" {
|
||||
id = var.talos_image_id
|
||||
}
|
||||
|
||||
############################################################
|
||||
# RANDOM RESOURCES
|
||||
############################################################
|
||||
|
||||
resource "random_string" "cluster_id" {
|
||||
length = 6
|
||||
special = false
|
||||
lower = true
|
||||
upper = false
|
||||
}
|
||||
|
||||
locals {
|
||||
cluster_name = "talos-${random_string.cluster_id.result}"
|
||||
common_labels = {
|
||||
cluster = local.cluster_name
|
||||
environment = var.environment
|
||||
managed_by = "terraform"
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# SSH KEY (for emergency access only)
|
||||
############################################################
|
||||
|
||||
data "hcloud_ssh_keys" "all" {}
|
||||
|
||||
locals {
|
||||
ssh_key_normalized = trimspace(split(" ", var.ssh_public_key)[0] == "ssh-rsa" ?
|
||||
join(" ", slice(split(" ", var.ssh_public_key), 0, 2)) :
|
||||
var.ssh_public_key)
|
||||
|
||||
ssh_key_matches = [
|
||||
for key in data.hcloud_ssh_keys.all.ssh_keys : key.id
|
||||
if key.public_key == local.ssh_key_normalized || key.public_key == var.ssh_public_key
|
||||
]
|
||||
|
||||
ssh_key_id = length(local.ssh_key_matches) > 0 ? local.ssh_key_matches[0] : hcloud_ssh_key.admin[0].id
|
||||
}
|
||||
|
||||
resource "hcloud_ssh_key" "admin" {
|
||||
count = length(local.ssh_key_matches) == 0 ? 1 : 0
|
||||
name = "${local.cluster_name}-admin"
|
||||
public_key = var.ssh_public_key
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
############################################################
|
||||
# NETWORK CONFIGURATION
|
||||
############################################################
|
||||
|
||||
resource "hcloud_network" "cluster" {
|
||||
name = "${local.cluster_name}-network"
|
||||
ip_range = "10.0.0.0/16"
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
resource "hcloud_network_subnet" "cluster" {
|
||||
type = "cloud"
|
||||
network_id = hcloud_network.cluster.id
|
||||
network_zone = "eu-central"
|
||||
ip_range = "10.0.1.0/24"
|
||||
}
|
||||
|
||||
############################################################
|
||||
# FIREWALL CONFIGURATION
|
||||
############################################################
|
||||
|
||||
resource "hcloud_firewall" "cluster" {
|
||||
name = "${local.cluster_name}-firewall"
|
||||
labels = local.common_labels
|
||||
|
||||
# Talos API access
|
||||
rule {
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "50000"
|
||||
source_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
# Kubernetes API
|
||||
rule {
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "6443"
|
||||
source_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
# Allow HTTP/HTTPS for Ingress
|
||||
rule {
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "80"
|
||||
source_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
rule {
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "443"
|
||||
source_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
# Allow NodePort range (for services)
|
||||
rule {
|
||||
direction = "in"
|
||||
protocol = "tcp"
|
||||
port = "30000-32767"
|
||||
source_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
# Allow all outbound traffic
|
||||
rule {
|
||||
direction = "out"
|
||||
protocol = "tcp"
|
||||
port = "any"
|
||||
destination_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
rule {
|
||||
direction = "out"
|
||||
protocol = "udp"
|
||||
port = "any"
|
||||
destination_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
|
||||
rule {
|
||||
direction = "out"
|
||||
protocol = "icmp"
|
||||
destination_ips = ["0.0.0.0/0", "::/0"]
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# PLACEMENT GROUP (keep nodes close for low latency)
|
||||
############################################################
|
||||
|
||||
resource "hcloud_placement_group" "cluster" {
|
||||
name = "${local.cluster_name}-pg"
|
||||
type = "spread"
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
############################################################
|
||||
# CONTROL PLANE NODES (HA with 3 CAX11 nodes)
|
||||
############################################################
|
||||
|
||||
resource "hcloud_server" "control_plane" {
|
||||
count = 3
|
||||
name = "${local.cluster_name}-cp-${count.index}"
|
||||
server_type = "cax11"
|
||||
image = data.hcloud_image.talos.id
|
||||
location = "nbg1" # CAX11 only available in Nuremberg
|
||||
ssh_keys = [local.ssh_key_id]
|
||||
|
||||
firewall_ids = [hcloud_firewall.cluster.id]
|
||||
placement_group_id = hcloud_placement_group.cluster.id
|
||||
|
||||
labels = merge(local.common_labels, {
|
||||
role = "control-plane"
|
||||
node = "cp-${count.index}"
|
||||
arch = "arm64"
|
||||
})
|
||||
|
||||
public_net {
|
||||
ipv4_enabled = true
|
||||
ipv6_enabled = true
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [ssh_keys]
|
||||
}
|
||||
}
|
||||
|
||||
resource "hcloud_server_network" "control_plane" {
|
||||
count = 3
|
||||
server_id = hcloud_server.control_plane[count.index].id
|
||||
network_id = hcloud_network.cluster.id
|
||||
ip = "10.0.1.${10 + count.index}"
|
||||
}
|
||||
|
||||
# Floating IP for stable control plane access
|
||||
resource "hcloud_floating_ip" "control_plane" {
|
||||
type = "ipv4"
|
||||
name = "${local.cluster_name}-cp-ip"
|
||||
home_location = "nbg1"
|
||||
labels = local.common_labels
|
||||
}
|
||||
|
||||
resource "hcloud_floating_ip_assignment" "control_plane" {
|
||||
floating_ip_id = hcloud_floating_ip.control_plane.id
|
||||
server_id = hcloud_server.control_plane[0].id
|
||||
}
|
||||
|
||||
############################################################
|
||||
# WORKER NODE (Single CAX11)
|
||||
############################################################
|
||||
|
||||
resource "hcloud_server" "worker" {
|
||||
count = 1
|
||||
name = "${local.cluster_name}-worker-${count.index}"
|
||||
server_type = "cax11"
|
||||
image = data.hcloud_image.talos.id
|
||||
location = "nbg1"
|
||||
ssh_keys = [local.ssh_key_id]
|
||||
|
||||
firewall_ids = [hcloud_firewall.cluster.id]
|
||||
placement_group_id = hcloud_placement_group.cluster.id
|
||||
|
||||
labels = merge(local.common_labels, {
|
||||
role = "worker"
|
||||
node = "worker-${count.index}"
|
||||
arch = "arm64"
|
||||
})
|
||||
|
||||
public_net {
|
||||
ipv4_enabled = true
|
||||
ipv6_enabled = true
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [ssh_keys]
|
||||
}
|
||||
}
|
||||
|
||||
resource "hcloud_server_network" "worker" {
|
||||
count = 1
|
||||
server_id = hcloud_server.worker[count.index].id
|
||||
network_id = hcloud_network.cluster.id
|
||||
ip = "10.0.1.${20 + count.index}"
|
||||
}
|
||||
|
||||
############################################################
|
||||
# TALOS CONFIGURATION
|
||||
############################################################
|
||||
|
||||
# Generate Talos machine secrets
|
||||
resource "talos_machine_secrets" "this" {
|
||||
talos_version = var.talos_version
|
||||
}
|
||||
|
||||
# Generate Talos client configuration
|
||||
data "talos_client_configuration" "this" {
|
||||
cluster_name = local.cluster_name
|
||||
client_configuration = talos_machine_secrets.this.client_configuration
|
||||
endpoints = [hcloud_floating_ip.control_plane.ip_address]
|
||||
}
|
||||
|
||||
# Control plane configuration
|
||||
data "talos_machine_configuration" "control_plane" {
|
||||
count = 3
|
||||
cluster_name = local.cluster_name
|
||||
machine_type = "controlplane"
|
||||
cluster_endpoint = "https://${hcloud_floating_ip.control_plane.ip_address}:6443"
|
||||
machine_secrets = talos_machine_secrets.this.machine_secrets
|
||||
talos_version = var.talos_version
|
||||
|
||||
config_patches = [
|
||||
templatefile("${path.module}/talos-patches/control-plane.yaml", {
|
||||
cluster_name = local.cluster_name
|
||||
node_name = hcloud_server.control_plane[count.index].name
|
||||
is_ha = true
|
||||
is_first_cp = count.index == 0
|
||||
etcd_peers = [for i in range(3) : "10.0.1.${10 + i}"]
|
||||
floating_ip = hcloud_floating_ip.control_plane.ip_address
|
||||
})
|
||||
]
|
||||
|
||||
depends_on = [
|
||||
hcloud_server.control_plane,
|
||||
hcloud_floating_ip_assignment.control_plane
|
||||
]
|
||||
}
|
||||
|
||||
# Worker configuration
|
||||
data "talos_machine_configuration" "worker" {
|
||||
count = 1
|
||||
cluster_name = local.cluster_name
|
||||
machine_type = "worker"
|
||||
cluster_endpoint = "https://${hcloud_floating_ip.control_plane.ip_address}:6443"
|
||||
machine_secrets = talos_machine_secrets.this.machine_secrets
|
||||
talos_version = var.talos_version
|
||||
|
||||
config_patches = [
|
||||
templatefile("${path.module}/talos-patches/worker.yaml", {
|
||||
cluster_name = local.cluster_name
|
||||
node_name = hcloud_server.worker[count.index].name
|
||||
})
|
||||
]
|
||||
|
||||
depends_on = [
|
||||
hcloud_server.worker,
|
||||
hcloud_floating_ip_assignment.control_plane
|
||||
]
|
||||
}
|
||||
|
||||
############################################################
|
||||
# APPLY TALOS CONFIGURATION
|
||||
############################################################
|
||||
|
||||
resource "talos_machine_configuration_apply" "control_plane" {
|
||||
count = 3
|
||||
client_configuration = talos_machine_secrets.this.client_configuration
|
||||
machine_configuration_input = data.talos_machine_configuration.control_plane[count.index].machine_configuration
|
||||
endpoint = hcloud_server.control_plane[count.index].ipv4_address
|
||||
node = hcloud_server.control_plane[count.index].ipv4_address
|
||||
|
||||
depends_on = [
|
||||
hcloud_server_network.control_plane,
|
||||
data.talos_machine_configuration.control_plane
|
||||
]
|
||||
}
|
||||
|
||||
resource "talos_machine_configuration_apply" "worker" {
|
||||
count = 1
|
||||
client_configuration = talos_machine_secrets.this.client_configuration
|
||||
machine_configuration_input = data.talos_machine_configuration.worker[count.index].machine_configuration
|
||||
endpoint = hcloud_server.worker[count.index].ipv4_address
|
||||
node = hcloud_server.worker[count.index].ipv4_address
|
||||
|
||||
depends_on = [
|
||||
hcloud_server_network.worker,
|
||||
data.talos_machine_configuration.worker,
|
||||
talos_machine_configuration_apply.control_plane
|
||||
]
|
||||
}
|
||||
|
||||
############################################################
|
||||
# BOOTSTRAP KUBERNETES
|
||||
############################################################
|
||||
|
||||
resource "talos_machine_bootstrap" "this" {
|
||||
client_configuration = talos_machine_secrets.this.client_configuration
|
||||
node = hcloud_server.control_plane[0].ipv4_address
|
||||
|
||||
depends_on = [
|
||||
talos_machine_configuration_apply.control_plane,
|
||||
talos_machine_configuration_apply.worker
|
||||
]
|
||||
}
|
||||
|
||||
############################################################
|
||||
# GET KUBECONFIG
|
||||
############################################################
|
||||
|
||||
resource "talos_cluster_kubeconfig" "this" {
|
||||
client_configuration = talos_machine_secrets.this.client_configuration
|
||||
node = hcloud_server.control_plane[0].ipv4_address
|
||||
|
||||
depends_on = [talos_machine_bootstrap.this]
|
||||
}
|
||||
|
||||
############################################################
|
||||
# SAVE CONFIGURATIONS
|
||||
############################################################
|
||||
|
||||
resource "local_sensitive_file" "kubeconfig" {
|
||||
# Replace the internal hostname with the floating IP for external access
|
||||
content = replace(
|
||||
talos_cluster_kubeconfig.this.kubeconfig_raw,
|
||||
"https://${local.cluster_name}.local:6443",
|
||||
"https://${hcloud_floating_ip.control_plane.ip_address}:6443"
|
||||
)
|
||||
filename = "${path.root}/kubeconfig"
|
||||
}
|
||||
|
||||
resource "local_sensitive_file" "talosconfig" {
|
||||
content = data.talos_client_configuration.this.talos_config
|
||||
filename = "${path.root}/talosconfig"
|
||||
}
|
||||
Reference in New Issue
Block a user