Proxmox, OpenTofu et Talos, Kubernetes as code
Dans un contexte où la gestion des infrastructures et des systèmes d’informations devient de plus en plus complexe, l’Infrastructure as Code (IaC) s’impose comme une solution incontournable pour automatiser, standardiser et optimiser les déploiements.
Parmi les outils phares de cet écosystème, Proxmox, OpenTofu et Talos se distinguent par leur efficacité et leur complémentarité. Je vous propose d’utiliser ces outils pour déployer des machines virtuelles dans Proxmox grâce à OpenTofu, et y instancier un cluster Kubernetes avec Talos .
Proxmox est une plateforme de virtualisation open-source qui permet de gérer des machines virtuelles et des conteneurs. OpenTofu, un fork de Terraform, est utilisé pour l’automatisation de l’infrastructure, permettant de définir et de provisionner des ressources de manière déclarative. Talos, quant à lui, est un système d’exploitation minimaliste conçu spécifiquement pour exécuter Kubernetes, offrant une empreinte réduite et une sécurité renforcée.
Toujours dans l’optique d’héberger mes services localement, j’ai décidé d’utiliser ces outils pour mettre en place une infrastructure complexe, robuste et capable d’être mise à l’échelle.
Pour éviter une redite, je me suis basé sur plusieurs articles concernant Proxmox et OpenTofu :
Utiliser Terraform ou OpenTofu pour créer une VM dans ProxmoxSuite à un article portant sur l’utilisation de Terraform pour créer des LXC pour Proxmox, je souhaite approfondir le sujet en abordant cette fois la création de machines virtuelles. Prérequis : Une connaissance préalable de Terraform, des droits d’administration sur Proxmox, et la capacité à effectuer des manipulations sans contraintes majeures.J.HOMMET.NETJulien HOMMETDéployer plusieurs VM avec Terraform ou OpenTofu dans ProxmoxLa puissance des outils comme Terraform et OpenTofu résident dans leur capacité de mise à l’échelle. Créer une machine virtuelle est assez rapide, mais créons plusieurs machines pour exploiter au mieux l’outil. J’utilise OpenTofu depuis début 2025 sur différentes infrastructures Proxmox, en ligne et déconnectées. Les exemples de code ci-dessousJ.HOMMET.NETJulien HOMMETVolontairement, je pars du principe que vous avez déjà une connaissance du monde IaC, d’OpenTofu et de Kubernetes.
À titre d’exemple, je créerai deux machines virtuelles : une pour le « control plane » et l’autre pour le « worker ». Talos et Kubernetes seront préconfigurés pour utiliser Cilium comme CNI. Il n’y aura pas de CSI, pas d’exemple de déploiement d’application ou d’observabilité. L’objectif est uniquement de mettre en place le cluster et de le rendre fonctionnel.
Contexte Proxmox (8.4.1)#
Je n’utilise qu’une seule machine physique pour la virtualisation, au lieu d’en avoir plusieurs dédiées à des services spécifiques. Cette machine exécute Debian 12 et est équipée de divers outils comme QEMU, bridge-utils et autres. En combinant ces éléments, on obtient Proxmox.
Pour ce document, je reste dans la simplicité. Proxmox a été installé en suivant ce guide
, le stockage est local sur un disque NVMe, un seul pont réseau a été créé (celui par défaut, vmbr0). La version de Proxmox, au moment de l’écriture, est la 8.4.1. Les machines virtuelles seront créées avec les drivers virtio, sans aucune optimisation côté Proxmox ni dans la MV. Le principe est toujours le même : SIMPLICITÉ. Restons le plus simple possible, partout.
Une petite subtilité concernant le stockage des données de Talos : une machine virtuelle sans OS sera créée avec deux disques durs virtuels, qui seront attachés à chacune des machines virtuelles Talos. Chaque machine virtuelle Talos disposera donc de son disque système et d’un disque de données. Ce disque de données n’est pas indispensable ; il est présent pour exploiter l’outil local-path-provisioner et tester différentes fonctionnalités ultérieurement.
Contexte Talos (1.9.4)#
Talos, de Siderolabs, a une approche pragmatique et minimaliste que j’apprécie fortement. Afin de minimiser l’empreinte du système et la complexité, seuls les binaires strictement nécessaires à l’exécution de Kubernetes et du noyau Linux sont présents. Il n’y a ni shell, ni SSH, ni snap : rien de superflu.
L’éditeur fournit les outils requis pour personnaliser l’image ISO ou le disque virtuel de Talos afin de répondre à vos besoins et à la configuration de votre système. En naviguant sur « https://factory.talos.dev », vous trouverez un formulaire qui vous guidera à travers les différentes options. De plus, un « schematic ID » est fourni, un code qui vous permet de conserver les mêmes options et personnalisations entre les versions de Talos.
Ici, j’ai choisi le type de machine “cloud server”, la version 1.9.4 et Nocloud, une architecture AMD64 (sans secureboot), les extensions “amd-ucode ; amdgpu” (parce que mon hyperviseur dispose d’un processeur AMD), “iscsi-tools” et “qemu-guest-agent”, et j’ai saisi les options de noyau suivantes : net.ifnames=0 biosdevname=0 console=tty0 console=ttyS0,115200n8 mitigations=off elevator=none. Ces options de noyau permettent de forcer le nom des cartes réseaux en eth au lieu de noms prédictibles ens, d’afficher les erreurs noyaux directement dans la console (tty0), et de désactiver deux optimisations inutiles dans un contexte virtuel (mitigations et elevator). La page finale de l’assistant vous donnera un résumé de votre configuration et le fameux schematic ID en plus des liens de téléchargement pour l’image ISO ou le disque virtuel.
La gestion simplifiée des machines et des outils Kubernetes constitue un autre avantage. Avec Talos, toutes les dépendances sont à jour et compatibles entre elles. Cependant, il est important de vérifier la compatibilité entre la version Kubernetes et vos CNI, CSI, ainsi que des dépendances comme cert-manager et gateway-api.
Contexte OpenTofu (1.9.0)#
Passons maintenant au code OpenTofu. Je vais créer plusieurs fichiers dans ce projet pour définir les machines virtuelles et le cluster Kubernetes. Je m’excuse par avance si la lisibilité de l’article n’est pas optimale.
OpenTofu facilite l’automatisation et la mise à l’échelle des déploiements de machines, et dans notre cas, de Kubernetes. Grâce à l’utilisation de fichiers de configuration comme talos.tf et vm.tf, il est possible de déployer rapidement de nouveaux nœuds et de mettre à jour les configurations existantes. La mise à l’échelle horizontale peut être réalisée en ajoutant de nouveaux nœuds workers, tandis que la mise à l’échelle verticale peut être effectuée en ajustant les ressources (CPU, mémoire vive, stockage) allouées à chaque nœud.
L’arborescence du projet est la suivante :
opentofu
> talos_nocloud
>> main.tf
>> variables.tf
>> vm.tf
>> talos.tf
>> talos-img.tfLe fichier main.tf va comporter la déclaration des providers et les valeurs associées :
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.76.0"
}
talos = {
source = "siderolabs/talos"
version = "0.7.1"
}
random = {
source = "hashicorp/random"
version = "3.7.1"
}
}
}
provider "proxmox" {
api_token = "user@realm!token_name=token_id"
endpoint = "https://your.proxmox.endpoint:8006/api2/json"
insecure = true # because self-signed TLS certificate is in use
tmp_dir = "/var/tmp/"
ssh {
agent = true
username = "root"
}
}
provider "talos" {}Le fichier variables.tf comporte la définition des variables qui seront utilisées dans les fichiers vm.tf et talos.tf. J’utiliserai l’approche “Utiliser un fichier .tfvars comportant plusieurs blocs de ressources” du document https://j.hommet.net/deployer-plusieurs-vm-avec-terraform-opentofu-dans-proxmox/
.
variable "kubernetes_version" {
type = string
}
variable "talos_cluster_endpoint" {
description = "The endpoint for the Talos cluster"
type = string
}
variable "talos_cluster_name" {
description = "A name to provide for the Talos cluster"
type = string
}
variable "talos_version" {
type = string
}
variable "nodes" {
description = "Values of VM resources"
type = map(object({
data_vm_interface_disk = string
pve = string
role = string
usage = string
vm_boot_disk_format = optional(string, "raw")
vm_boot_disk_size = number
vm_cpu_cores = number
vm_cpu_flags = optional(list(string))
vm_cpu_type = optional(string, "x86-64-v2-AES")
vm_data_disk_interface = string
vm_datastore_id = string
vm_datastore_id_boot_disk = string
vm_datastore_id_data_disk = string
vm_datastore_id_efi_disk = string
vm_datastore_id_initialization = string
vm_datastore_id_tpm = string
vm_description = string
vm_dns = list(string)
vm_domain = string
vm_efi = optional(bool, true)
vm_eth_rate_limit = optional(number, 0)
vm_gateway = string
vm_id = number
vm_install_disk = string
vm_ip = string
vm_kvm_args = optional(string)
vm_mac_address = string
vm_memory_dedicated = number
vm_memory_floating = number
vm_name = string
vm_on_boot = optional(bool, true)
vm_pool_id = optional(string)
vm_startup_order = optional(number, 1)
vm_started = optional(bool, true)
vm_tags = optional(list(string))
vm_timeservers = optional(list(string), ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"])
vm_tpm = optional(bool, true)
}))
}
variable "meta_config_metadata" {
description = "Metadata for cloud-init configuration"
type = map(object({
snippet_datastore_id = string
snippet_pve = string
data = string
}))
}La variable “nodes” est un ensemble de valeurs dans lequel je spécifie les données pour chaque VM. Si vous souhaitez ajouter d’autres machines, il vous suffira de dupliquer un bloc de valeurs de nodes.
Le fichier vm.tf est assez classique (similaire à ce que vous pouvez trouver dans d’autres articles sur mon site) :
# Machines pour le stockage de données statiques pour Talos
resource "proxmox_virtual_environment_vm" "talos_vm_data" {
description = "Managed by OpenTofu. Talos data disks."
name = "k8s-data"
node_name = "miniquarium"
on_boot = false
protection = true
started = false
tags = sort(["infra", "ne_pas_demarrer", "ne_pas_supprimer"])
vm_id = 9000
disk { # control plane data disk 1
backup = true
datastore_id = "local-nvme"
file_format = "raw"
interface = "scsi10"
size = 48
}
disk { # worker 1 data disk 1
backup = true
datastore_id = "local-nvme"
file_format = "raw"
interface = "scsi11"
size = 64
}
}
# Déclaration de machine virtuelle pour Talos
resource "proxmox_virtual_environment_vm" "talos_vm" {
depends_on = [
# proxmox_virtual_environment_download_file.talos_nocloud_image,
proxmox_virtual_environment_file.meta_cloud_config
]
for_each = var.nodes
acpi = true
bios = "ovmf"
description = each.value.vm_description
keyboard_layout = "fr"
kvm_arguments = each.value.vm_kvm_args
machine = "pc-q35-9.0"
migrate = true
name = each.value.vm_name
node_name = each.value.pve
on_boot = each.value.vm_on_boot ? true : false
pool_id = each.value.vm_pool_id
scsi_hardware = "virtio-scsi-single"
started = each.value.vm_started ? "true" : "false"
stop_on_destroy = true
tablet_device = false
tags = each.value.vm_tags
timeout_create = 180
timeout_shutdown_vm = 30
timeout_stop_vm = 30
vm_id = each.value.vm_id
agent {
enabled = true
timeout = "5m"
trim = true
}
cpu {
cores = each.value.vm_cpu_cores
flags = each.value.vm_cpu_flags
numa = true
sockets = 1
type = each.value.vm_cpu_type
}
disk { # boot disk
aio = "native"
backup = false
cache = "none"
datastore_id = each.value.vm_datastore_id_boot_disk
discard = "on"
file_format = each.value.vm_boot_disk_format
file_id = "local:iso/talos-v1.9.4-nocloud-amd64.img"
interface = "scsi0"
iothread = true
replicate = false
size = each.value.vm_boot_disk_size
}
dynamic "disk" { # data disk
for_each = { for idx, disk in proxmox_virtual_environment_vm.talos_vm_data.disk : idx => disk if disk.interface == each.value.data_vm_interface_disk }
iterator = data_disk
content {
datastore_id = data_disk.value.datastore_id
discard = "on"
file_format = data_disk.value.file_format
size = data_disk.value.size
interface = each.value.vm_data_disk_interface
}
}
dynamic "efi_disk" {
for_each = each.value.vm_efi ? [1] : []
content {
datastore_id = each.value.vm_datastore_id_efi_disk
file_format = "raw"
pre_enrolled_keys = false
type = "4m"
}
}
initialization {
datastore_id = each.value.vm_datastore_id_initialization
dns {
domain = each.value.vm_domain
servers = each.value.vm_dns
}
ip_config {
ipv4 {
address = "${each.value.vm_ip}/24"
gateway = each.value.vm_gateway
}
ipv6 {
address = "dhcp"
}
}
}
memory {
dedicated = each.value.vm_memory_dedicated
floating = each.value.vm_memory_floating
}
network_device {
bridge = "vmbr0"
firewall = false
mac_address = each.value.vm_mac_address
model = "virtio"
rate_limit = each.value.vm_eth_rate_limit
}
operating_system {
type = "l26"
}
serial_device {}
startup {
order = each.value.vm_startup_order
up_delay = 15
down_delay = 60
}
dynamic "tpm_state" {
for_each = each.value.vm_tpm ? [1] : []
content {
datastore_id = each.value.vm_datastore_id_tpm
version = "v2.0"
}
}
vga {
clipboard = "" # false if empty
type = "virtio"
}
}
resource "proxmox_virtual_environment_file" "meta_cloud_config" {
for_each = var.meta_config_metadata
content_type = "snippets"
datastore_id = each.value.snippet_datastore_id
node_name = each.value.snippet_pve
source_raw {
data = each.value.data
file_name = "${each.key}_ci_meta-data.yml"
}
}Passons à un fichier plutôt conséquent, le parameters.auto.tfvars. Modifiez les valeurs selon votre environnement :
kubernetes_version = "1.31.6"
talos_cluster_name = "poctalos"
talos_cluster_endpoint = "172.16.255.1"
talos_version = "1.9.4"
meta_config_metadata = {
"ber" = {
snippet_datastore_id = "local"
snippet_pve = "pve"
data = <<-EOF
instance-id: talos-cp
local-hostname: ber
EOF
}
"nar" = {
snippet_datastore_id = "local"
snippet_pve = "pve"
data = <<-EOF
instance-id: talos-wkr
local-hostname: nar
EOF
}
}
nodes = {
"ber" = {
data_vm_interface_disk = "scsi10" # port on the data VM disk
pve = "pve"
role = "controlplane"
usage = "controlplane"
vm_boot_disk_format = "raw"
vm_boot_disk_size = 24
vm_cpu_cores = 2
vm_cpu_type = "x86-64-v2-AES"
vm_data_disk_interface = "scsi1" # port on the control plane VM
vm_datastore_id = "local"
vm_datastore_id_boot_disk = "local"
vm_datastore_id_data_disk = "local"
vm_datastore_id_efi_disk = "local"
vm_datastore_id_initialization = "local"
vm_datastore_id_tpm = "local"
vm_description = "Managed by OpenTofu. Talos controlplane"
vm_dns = ["172.16.255.253"]
vm_domain = "dc.home.arpa"
vm_efi = true
vm_eth_rate_limit = 0
vm_gateway = "172.16.255.254"
vm_id = 99121
vm_install_disk = "/dev/sda"
vm_ip = "172.16.255.1"
vm_kvm_args = ""
vm_mac_address = "BC:24:11:CA:FE:21"
vm_memory_dedicated = 6144
vm_memory_floating = 6144
vm_name = "ber"
vm_on_boot = true
vm_pool_id = ""
vm_started = true
vm_startup_order = 2
vm_tags = ["controlplane", "k8s", "opentofu", "talos"]
vm_timeservers = ["fr.pool.ntp.org", "time.cloudflare.com"]
vm_tpm = true
}
"nar" = {
data_vm_interface_disk = "scsi11" # port on the data VM disk
pve = "pve"
role = "worker"
usage = "infra"
vm_boot_disk_format = "raw"
vm_boot_disk_size = 24
vm_cpu_cores = 3
vm_cpu_type = "x86-64-v2-AES"
vm_data_disk_interface = "scsi1" # port on the worker VM
vm_datastore_id = "local"
vm_datastore_id_boot_disk = "local"
vm_datastore_id_data_disk = "local"
vm_datastore_id_efi_disk = "local"
vm_datastore_id_initialization = "local"
vm_datastore_id_tpm = "local"
vm_description = "Managed by OpenTofu. Talos worker"
vm_dns = ["172.16.255.253"]
vm_domain = "dc.home.arpa"
vm_efi = true
vm_eth_rate_limit = 0
vm_gateway = "172.16.255.254"
vm_id = 99122
vm_install_disk = "/dev/sda"
vm_ip = "172.16.255.2"
vm_kvm_args = ""
vm_mac_address = "BC:24:11:CA:FE:22"
vm_memory_dedicated = 16384 # 16 GB
vm_memory_floating = 16384 # 16 GB
vm_name = "nar"
vm_on_boot = true
vm_pool_id = ""
vm_started = true
vm_startup_order = 3
vm_tags = ["k8s", "opentofu", "talos", "worker"]
vm_timeservers = ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"]
vm_tpm = true
}
}Le fichier talos.tf comporte la déclaration du cluster Kubernetes. En plus, il y a des configurations spécifiques selon le type de nœud, l’ajout de manifests pour installer Cilium (et les configurations nécessaires pour gateway-api et l’annoncement L2), la création des fichiers de configuration pour les control planes et worker. Le code n’est pas entièrement de moi, j’ai repris une partie du travail de Vegard S. Hagen (https://blog.stonegarden.dev/
) et aussi de Quentin JOLY (https://une-tasse-de.cafe/blog/talos/
).
# Getting the kubeconfig and talosconfig can be done with "terraform output -raw kubeconfig > $HOME/.kube/config"
# and "terraform output -raw talosconfig > $HOME/.talos/config"".
# source : https://github.com/siderolabs/contrib/tree/main/examples/terraform/basic
# source 2 : https://github.com/vehagn/homelab/blob/main/tofu/kubernetes/talos/config.tf
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "controlplane" {
for_each = var.nodes
cluster_endpoint = "https://${var.talos_cluster_endpoint}:6443"
cluster_name = var.talos_cluster_name
kubernetes_version = var.kubernetes_version
machine_secrets = talos_machine_secrets.this.machine_secrets
machine_type = "controlplane"
talos_version = var.talos_version
config_patches = [
yamlencode({
machine = {
kubelet = {
nodeIP = {
validSubnets = ["172.16.255.0/24"]
}
}
features = {
hostDNS = {
enabled = true
forwardKubeDNSToHost = false
resolveMemberNames = true
}
}
network = {
hostname = each.value.vm_name
nameservers = each.value.vm_dns
interfaces = [
{
addresses = ["${each.value.vm_ip}/24"]
dhcp = false
interface = "eth0"
routes = [
{
network = "0.0.0.0/0"
gateway = each.value.vm_gateway
}
]
}
]
}
install = {
disk = each.value.vm_install_disk
}
time = {
servers = each.value.vm_timeservers
}
}
cluster = {
etcd = {
advertisedSubnets = ["172.16.255.0/24"]
}
discovery = {
enabled = false
registries = {
service = {
disabled = true
}
}
}
network = {
cni = { # Cilium will replace it
name = "custom"
urls = [
"https://raw.githubusercontent.com/julienhmmt/homelab/refs/heads/main/kubernetes/00-cilium/00-cilium-custom.yaml"
]
}
}
proxy = { # Cilium will replace it
disabled = true
}
allowSchedulingOnControlPlanes = false
extraManifests = [
# Gateway API
"https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml",
"https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml",
"https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml",
"https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml",
"https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml",
"https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml",
# Cilium
"https://raw.githubusercontent.com/julienhmmt/homelab/refs/heads/main/kubernetes/00-cilium/00-cilium-gapi.yaml",
"https://raw.githubusercontent.com/julienhmmt/homelab/refs/heads/main/kubernetes/00-cilium/00-cilium-l2announcement.yaml"
]
}
})
]
}
resource "talos_machine_configuration_apply" "controlplane" {
depends_on = [data.talos_machine_configuration.controlplane]
for_each = { for key, value in var.nodes : key => value if value.role == "controlplane" }
lifecycle { replace_triggered_by = [proxmox_virtual_environment_vm.talos_vm[each.key]] } # re-run config apply if vm changes
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.controlplane[each.key].machine_configuration
node = each.value.vm_ip
}
data "talos_machine_configuration" "worker" {
for_each = var.nodes
cluster_endpoint = "https://${var.talos_cluster_endpoint}:6443"
cluster_name = var.talos_cluster_name
kubernetes_version = var.kubernetes_version
machine_secrets = talos_machine_secrets.this.machine_secrets
machine_type = "worker"
talos_version = var.talos_version
config_patches = [
yamlencode({
machine = {
network = {
hostname = each.value.vm_name
}
install = {
disk = each.value.vm_install_disk
}
time = {
servers = ["fr.pool.ntp.org", "time.cloudflare.com"]
}
}
})
]
}
resource "talos_machine_configuration_apply" "worker" {
depends_on = [data.talos_machine_configuration.worker]
for_each = { for key, value in var.nodes : key => value if value.role == "worker" }
lifecycle { replace_triggered_by = [proxmox_virtual_environment_vm.talos_vm[each.key]] } # re-run config apply if vm changes
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.worker[each.key].machine_configuration
node = each.value.vm_ip
}
data "talos_client_configuration" "this" {
cluster_name = var.talos_cluster_name
endpoints = [for node in var.nodes : node.vm_ip if node.role == "controlplane"]
client_configuration = talos_machine_secrets.this.client_configuration
}
resource "talos_machine_bootstrap" "this" {
depends_on = [talos_machine_configuration_apply.controlplane]
client_configuration = talos_machine_secrets.this.client_configuration
node = [for k, v in var.nodes : v.vm_ip if v.role == "controlplane"][0]
}
data "talos_cluster_health" "this" {
depends_on = [
talos_machine_configuration_apply.controlplane,
talos_machine_configuration_apply.worker,
talos_machine_bootstrap.this
]
endpoints = data.talos_client_configuration.this.endpoints
client_configuration = data.talos_client_configuration.this.client_configuration
control_plane_nodes = [for k, v in var.nodes : v.vm_ip if v.role == "controlplane"]
skip_kubernetes_checks = true
timeouts = {
read = "10m"
}
worker_nodes = [for k, v in var.nodes : v.vm_ip if v.role == "worker"]
}
resource "talos_cluster_kubeconfig" "this" {
depends_on = [
talos_machine_bootstrap.this,
data.talos_cluster_health.this
]
client_configuration = talos_machine_secrets.this.client_configuration
endpoint = var.talos_cluster_endpoint
node = [for k, v in var.nodes : v.vm_ip if v.role == "controlplane"][0]
timeouts = {
read = "1m"
}
}
output "talosconfig" {
value = data.talos_client_configuration.this.talos_config
sensitive = true # Empêche l’affichage en clair dans les logs
}
output "kubeconfig" {
value = talos_cluster_kubeconfig.this.kubeconfig_raw
sensitive = true # Empêche l’affichage en clair dans les logs
}Quelques explications :
- La ressource
talos_machine_secretsgénère les secrets nécessaires pour sécuriser les communications entre les nœuds Talos. Ces secrets sont utilisés pour chiffrer les communications et authentifier les nœuds, garantissant ainsi que seules les machines autorisées peuvent rejoindre le cluster. - La ressource
talos_machine_configurationconfigure les nœuds Talos avec des paramètres spécifiques, tels que les configurations réseau, les paramètres Kubernetes, et les patches de configuration. Ces configurations permettent de personnaliser chaque nœud en fonction de son rôle (control plane ou worker) et de ses besoins spécifiques. - La ressource
talos_machine_configuration_applyapplique ensuite ces configurations aux nœuds, assurant que chaque machine est correctement configurée avant de rejoindre le cluster. - Cilium est utilisé comme CNI (Container Network Interface). Cilium est choisi pour ses performances élevées et sa capacité à fournir une sécurité réseau avancée grâce à l’utilisation de eBPF. Comparé à d’autres solutions comme Calico ou Flannel, Cilium offre une meilleure visibilité et un contrôle plus granulaire du trafic réseau. Les configurations réseau appliquées incluent la définition des interfaces réseau, des adresses IP, et des routes, garantissant que chaque nœud peut communiquer efficacement au sein du cluster.
OpenTofu exécutera les manifests dans la rubrique extraManifests dans l’ordre indiqué. Par exemple, un manifeste Gateway ne peut être lancé avant GatewayClass, car le type Gateway nécessite la GatewayClass pour être créé. Soyez vigilant quant à l’ordre des lignes.
De plus, dans cette rubrique, j’ajoute les manifests correspondant à gateway-api (qui remplace les ingress). Pour Cilium 1.17.2, Talos 1.9.4 et Kubernetes 1.31.6, la version de Gateway-API à utiliser est la 1.2.0. Voici les commandes :
curl -O https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml
curl -O https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yamlJ’ajoute également deux autres manifests pour finaliser la configuration gateway-api pour Cilium. L’intérêt de ce bloc réside dans sa capacité à intégrer des manifests pour déployer des applications ou des configurations dans Kubernetes via OpenTofu, sans avoir besoin de l’outil kubectl local. Une fonctionnalité très pratique !
Le dernier fichier du projet, talos-img.tf, est assez simple à comprendre. Il télécharge l’image virtuelle de Talos directement dans le stockage Proxmox.
locals {
talos = {
version = "v1.9.4"
schema = "ca84112fe212adcded1df4faf04156b49a915b14926c43e19fc09b06be2d06ae"
}
}
resource "proxmox_virtual_environment_download_file" "talos_nocloud_image" {
content_type = "iso"
datastore_id = "local"
node_name = "miniquarium"
file_name = "talos-${local.talos.version}-nocloud-amd64.img"
url = "https://factory.talos.dev/image/${local.talos.schema}/${local.talos.version}/nocloud-amd64.raw.gz"
decompression_algorithm = "gz"
overwrite = false
}Dans le bloc locals, changez la valeur dans schema pour correspondre à votre besoin et au résultat obtenu suite au formulaire du site factory.talos.dev.
Désormais, vous avez tous les fichiers pour concevoir votre cluster Kubernetes grâce à Talos, dans des machines virtuelles sur votre hôte Proxmox, le tout piloté par OpenTofu. Lancez les commandes tofu plan et tofu apply pour initier la mise en place. Après plusieurs minutes, vous devriez voir les machines en cours d’exécution, Talos démarré et votre cluster Kubernetes en cours d’instanciation. La durée d’installation peut varier selon les performances de vos machines physiques et virtuelles. À titre d’illustration, sur un processeur Ryzen 7 7700, 64 Go de mémoire vive DDR5 et un disque NVMe PCIe de génération 5, le délai total, de la création à la finalisation (instances prêtes à l’emploi et déploiement des applications compris), a été d’environ 3 minutes et 30 secondes.
Enfin, n’oubliez pas de récupérer les fichiers kubeconfig et talosconfig via les commandes tofu output :
mkdir -p ~/.talos ~/.kube
tofu output -raw kubeconfig > ~/.kube/config
tofu output -raw talosconfig > ~/.talos/configVous pouvez retrouver une version à jour dans le dépôt GitHub à l’adresse suivante : https://github.com/julienhmmt/homelab/tree/main/opentofu/talos-nocloud .
Et maintenant ?#
Lorsque le déploiement est terminé, contrôler son bon fonctionnement et son accessibilité avec le fichier kubeconfig en faisant un kubectl get nodes ou encore la commande kubectl get pods -n kube-system -o wide. Ces deux commandes vous permettent d’attester du bon fonctionnement du cluster Kubernetes. Les pods peuvent mettre un peu de temps à démarrer et être dans l’état running, notamment ceux de Cilium.
FAQ et résolution des problèmes courants#
Q : Comment résoudre les problèmes de connectivité réseau entre les nœuds Talos ?
R : Vérifiez les configurations réseau dans le fichier parameters.auto.tfvars, notamment les adresses IP, les interfaces réseau, les bridges, et les routes. Assurez-vous que les nœuds peuvent se joindre et que les règles de pare-feu ne bloquent pas le trafic nécessaire.
Q : Comment mettre à jour les configurations des nœuds Talos ?
R : Modifiez les fichiers de configuration dans le projet OpenTofu et appliquez les changements en utilisant les commandes tofu plan et tofu apply. Les nœuds seront automatiquement mis à jour avec les nouvelles configurations.
Q : Comment ajouter de nouveaux nœuds workers au cluster ?
R : Dupliquez un bloc de configuration de nœud dans le fichier parameters.auto.tfvars et appliquez les changements avec tofu plan && tofu apply. Le nouveau nœud sera automatiquement configuré et ajouté au cluster.
Q : Comment surveiller la santé du cluster Talos ?
R : Comme tout cluster Kubernetes, il est indispensable de déployer des outils de supervision comme une pile technique Prometheus + Grafana, VictoriaMetrics, ou encore une pile Elasticsearch + Metricbeat. Vous pouvez aussi utiliser l’outil talosctl:
talosctl health: Cette commande exécute un ensemble de vérifications de santé sur tous les nœuds du cluster et donne un aperçu global de l’état du cluster (connectivité, etcd, Kubernetes, etc.)talosctl cluster show: Affiche les informations générales sur le cluster local provisionné, y compris les nœuds, leur rôle et leur étattalosctl -n <IP_DU_NOEUD> logs <service>: Récupère les événements d’un nœud. Par exemple, pouretcd:talosctl -n <IP_DU_NOEUD> logs etcd.talosctl -n <IP_DU_NOEUD> containers --kubernetes: Permet de vérifier l’état des pods système Kubernetes (kube-apiserver, kube-controller-manager, etc.) si le control-plane n’est pas encore accessible via kubectl.
Liens complémentaires#
- 📚 Documentation officielle Talos
- 🛠️ OpenTofu (Terraform fork)
- 🖥️ Proxmox VE
Talos Kubernetes on Proxmox using OpenTofuTalos is an immutable operating system designed to only run Kubernetes. The advantage of Talos is an out-of-the-box Kubernetes install, as well as a smaller attack surface, and easier maintenance.StonegardenVegard S. HagenTalos - Un OS immuable pour KubernetesTalos est un système d’exploitation pour Kubernetes. Il est conçu pour être léger, sécurisé et facile à utiliser. Dans cet article, je vais vous présenter Talos et ses particularités.Une tasse de caféQuentin JOLY
- Mots-clés
- #kubernetes #linux #opentofu #proxmox #terraform
- Auteur
- Julien HOMMET
- date +"%Y-%m-%d"
- Temps_lecture
- 19 minutes
- quantité_mots
- 3868 mots
- Catégorie
- tuto
- maj $(date +"%Y-%m-%d")