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
391 lines
11 KiB
HCL
391 lines
11 KiB
HCL
############################################################
|
|
# 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"
|
|
} |