Aller au contenu
Conteneurs & Orchestration medium

Terraform : déployer une 3-tiers Incus (VPC, ACL, load balancer)

8 min de lecture

logo incus

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é.

  • 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.

Un cloud, ce sont deux rôles distincts, donc deux configurations Terraform :

  1. L'opérateur (admin) crée le tenant (project), pose les quotas et émet le credential de l'utilisateur. Voir Cloud privé multi-tenant.
  2. 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.

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"
}

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"
}
}

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"
},
]
}

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" }
}
}

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"]
}
}
Fenêtre de terminal
tofu init
tofu plan
tofu apply
Plan: 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.

  • 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 de incus_project côté tenant.
  • Ressources clés : incus_network (type ovn), incus_network_acl (listes ingress), incus_instance (blocs device), incus_network_lb (blocs backend/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é.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn