
Le guide Terraform de base provisionne des instances. On va plus loin : décrire tout un cloud privé 3-tiers en Infrastructure as Code, avec le provider lxc/incus. Un VPC OVN, des Security Groups, des instances et un load balancer, dans un seul fichier déclaratif. Et surtout, on le déploie avec le compte du tenant, pas celui de l'administrateur, comme dans un vrai cloud. Testé avec OpenTofu. Public avancé.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Le modèle à deux étages : l'opérateur provisionne, le tenant déploie.
- Configurer le provider pour le compte du tenant.
- Décrire en HCL un VPC OVN, des ACL, des instances et un load balancer.
- Appliquer et vérifier.
Prérequis
Section intitulée « Prérequis »- Les bases du provider : voir Gérer Incus avec Terraform.
- Un réseau OVN et un uplink : voir OVN dans Incus.
- OpenTofu ou Terraform installé.
Le modèle à deux étages
Section intitulée « Le modèle à deux étages »Un cloud, ce sont deux rôles distincts, donc deux configurations Terraform :
- L'opérateur (admin) crée le tenant (project), pose les quotas et émet le credential de l'utilisateur. Voir Cloud privé multi-tenant.
- Le tenant déploie son infra (VPC, instances, load balancer) avec son propre credential. Il ne peut d'ailleurs pas créer son project : c'est une opération d'admin.
Ce guide couvre le deuxième étage : le plan que le tenant applique.
Le provider, côté tenant
Section intitulée « Le provider, côté tenant »On pointe le provider sur la config du tenant (son certificat scopé) et son remote :
terraform { required_providers { incus = { source = "lxc/incus" } }}
provider "incus" { config_dir = "/home/alice/.config/incus" # config du tenant (cert scopé) default_remote = "moncloud"}
locals { project = "tenant-a"}Décrire le VPC OVN
Section intitulée « Décrire le VPC OVN »On fixe le sous-réseau pour obtenir des IP déterministes (utile pour le load balancer) :
resource "incus_network" "vpc" { name = "tf-vpc" type = "ovn" project = local.project config = { "network" = "UPLINK" "ipv4.address" = "10.100.0.1/24" "ipv4.nat" = "true" }}Décrire les Security Groups
Section intitulée « Décrire les Security Groups »Les ACL se déclarent avec des listes ingress/egress. Une règle référence une autre ACL par son nom, et on ajoute le drop explicite (le reject par défaut est buggé) :
resource "incus_network_acl" "db" { name = "tf-db" project = local.project ingress = [ { action = "allow" protocol = "tcp" source = "tf-app" destination_port = "5432" state = "enabled" }, { action = "drop" # workaround : web ne joint jamais la db source = "tf-web" state = "enabled" }, ]}Décrire les instances
Section intitulée « Décrire les instances »Chaque instance porte son device réseau (VPC + IP + ACL) et son disque (pool Ceph). Le count fabrique les deux web :
resource "incus_instance" "web" { count = 2 name = "tf-web${count.index + 1}" project = local.project image = "images:debian/13/cloud"
device { name = "eth0" type = "nic" properties = { network = incus_network.vpc.name "ipv4.address" = "10.100.0.1${count.index + 1}" "security.acls" = "tf-web" "security.acls.default.ingress.action" = "drop" "security.acls.default.egress.action" = "allow" } } device { name = "root" type = "disk" properties = { path = "/", pool = "ceph-rbd" } }}Décrire le load balancer
Section intitulée « Décrire le load balancer »La ressource incus_network_lb porte les backends et les ports en blocs :
resource "incus_network_lb" "web" { network = incus_network.vpc.name project = local.project listen_address = "192.168.10.221"
config = { "healthcheck" = "true" "healthcheck.interval" = "10" }
backend { name = "web1" target_address = "10.100.0.11" target_port = "80" } backend { name = "web2" target_address = "10.100.0.12" target_port = "80" } port { protocol = "tcp" listen_port = "80" target_backend = ["web1", "web2"] }}Appliquer et vérifier
Section intitulée « Appliquer et vérifier »tofu inittofu plantofu applyPlan: 9 to add, 0 to change, 0 to destroy....Apply complete! Resources: 9 added.Le tenant a déployé son VPC, ses ACL, ses instances et son load balancer, avec son propre compte, confiné à son project. Un curl sur l'adresse du load balancer répond, réparti sur les deux web. L'infra entière est désormais reproductible : tofu destroy la démonte, tofu apply la reconstruit à l'identique.
À retenir
Section intitulée « À retenir »- Un cloud se décrit en deux étages : l'opérateur provisionne le tenant, le tenant déploie son infra.
- Le provider pointe la config du tenant (
config_dir+default_remote) ; pas deincus_projectcôté tenant. - Ressources clés :
incus_network(typeovn),incus_network_acl(listesingress),incus_instance(blocsdevice),incus_network_lb(blocsbackend/port). - Pensez au drop explicite dans les ACL (le default-reject est buggé).
- Le résultat est reproductible : la 3-tiers se démonte et se reconstruit à volonté.