Aller au contenu
Cloud medium

Chapitre 2 — Net Peering full-mesh sur OUTSCALE en Terraform

15 min de lecture

logo 3ds outscale

Ce chapitre interconnecte les 3 Nets créés au Chapitre 1 en mesh complet : A↔B, A↔C, B↔C. Trois peerings, acceptés explicitement, et 18 routes inter-Net (3 tiers × 3 Nets × 2 Nets distants). Le piège central est OUTSCALE-spécifique : sur ce cloud, les peerings intra-compte ne sont pas auto-acceptés — il faut une ressource outscale_net_peering_acceptation séparée, sans quoi les routes pointant vers le peering tombent en blackhole. Public visé : intermédiaire à avancé, avec le Chapitre 2 déployé. À la sortie de ce chapitre, toute VM de Net-A peut joindre toute VM de Net-B et Net-C, et réciproquement.

  • Coder les 3 peerings full-mesh en Terraform avec for_each sur des couples ordonnés.
  • Gérer l'acceptation explicite des peerings via outscale_net_peering_acceptation (piège OUTSCALE n°1 du capstone).
  • Produire les 18 routes inter-Net sans copier-coller, en aplatissant Nets × Tiers × Distants en une seule map.
  • Consommer le state du stack 00-nets via data "terraform_remote_state".
  • Diagnostiquer une route en blackhole (le symptôme qu'on rencontre 1 fois sur 2 quand on porte un module AWS).
  • Le Chapitre 1 — 3 Nets /22 appliqué sur le compte cible (48 ressources actives).
  • Backend OOS opérationnel — variables AWS_*_CHECKSUM_* exportées (cf. Chapitre 1).
  • Familiarité avec for_each sur une map(object) et avec les fonctions merge, flatten, sort.

Le piège n°1 — l'acceptation explicite des peerings

Section intitulée « Le piège n°1 — l'acceptation explicite des peerings »

C'est la spécificité OUTSCALE à connaître avant de toucher au code. Sur AWS, un peering VPC créé dans le même compte est auto-accepté ; on déclare une seule ressource aws_vpc_peering_connection et c'est terminé. Sur OUTSCALE, le peering naît en état pending-acceptance, même si la source et l'accepter appartiennent au même compte. Tant qu'il reste dans cet état :

  • Aucun trafic ne traverse.
  • Toute outscale_route qui le référence apparaît en état blackhole côté OUTSCALE — la route existe mais le paquet est jeté.
  • terraform apply réussit sans erreur — le piège est silencieux.

Le fix est une ressource Terraform de plus :

resource "outscale_net_peering" "mesh" {
for_each = local.peering_pairs
source_net_id = local.nets[each.value.source].net_id
accepter_net_id = local.nets[each.value.accepter].net_id
# tags...
}
resource "outscale_net_peering_acceptation" "mesh" {
for_each = local.peering_pairs
net_peering_id = outscale_net_peering.mesh[each.key].net_peering_id
}

La ressource outscale_net_peering_acceptation fait passer le peering en état active. Sans elle, c'est blackhole garanti — j'ai vu cette erreur arriver sur tous les portages depuis AWS, sans exception.

Trois topologies sont envisageables pour interconnecter trois Nets :

TopologiePeeringsPourContre
Linéaire A↔B, B↔C2Coût peering minimalA→C transite par B (interdit, le peering OUTSCALE n'est pas transitif)
Hub-and-spoke A↔B, A↔C2Centralisation des règles via Net-ANet-A devient SPOF logique ; même limite de transit
Full-mesh A↔B, A↔C, B↔C3Pas de SPOF, latence directe entre n'importe quels Nets1 peering de plus à payer + à gérer

Le piège des deux premières topologies est la non-transitivité du peering OUTSCALE (et AWS — c'est partagé). Un paquet de Net-A à destination de Net-C ne peut pas rebondir via Net-B même si A↔B et B↔C existent. Sans peering direct A↔C, il faut un NAT applicatif (proxy, load balancer interne) sur le Net pivot, ce qui ajoute une latence et un point de défaillance.

Le full-mesh ferme cette discussion. Sur N Nets, ça fait N×(N-1)/2 peerings — 3 pour N=3, 6 pour N=4, 10 pour N=5. Au-delà de 5-6 Nets, on bascule vers une Transit Gateway (que OUTSCALE n'expose pas en service managé à ce jour — cf. roadmap). Pour le capstone à 3 Nets, le full-mesh reste l'option idéale.

La paramétrisation — couples ordonnés pour l'idempotence

Section intitulée « La paramétrisation — couples ordonnés pour l'idempotence »

Trois Nets a, b, c produisent trois couples non orientés : {a,b}, {a,c}, {b,c}. L'écueil est de les coder par accident en orienté (a-b, b-a) — Terraform créerait alors deux peerings au lieu d'un. La discipline est de trier lexicographiquement chaque couple :

locals {
peering_pairs = {
"a-b" = { source = "a", accepter = "b" }
"a-c" = { source = "a", accepter = "c" }
"b-c" = { source = "b", accepter = "c" }
}
}

La clé <source>-<accepter> reste stable pour la durée de vie du stack. Choisir qui est source et qui est accepter n'a pas d'incidence fonctionnelle (le trafic est bidirectionnel une fois le peering actif) — c'est juste un choix de convention figée pour Terraform.

Les 18 routes inter-Net — un seul for_each pour tout générer

Section intitulée « Les 18 routes inter-Net — un seul for_each pour tout générer »

Chaque tier (public, private, data) de chaque Net doit pouvoir joindre les CIDR des deux autres Nets. Soit 3 tiers × 3 Nets × 2 Nets distants = 18 routes. À la main, c'est 18 blocs outscale_route quasi identiques — donc une bombe à retardement de cohérence. Le bon pattern est un double for_each aplati :

locals {
# Pour chaque Net : la liste des CIDR distants à router via leur peering.
remote_routes = {
for net_key, net in local.nets : net_key => [
for other_key, other in local.nets : {
cidr = other.cidr
peering_key = "${sort([net_key, other_key])[0]}-${sort([net_key, other_key])[1]}"
} if other_key != net_key
]
}
# Aplatir Nets × Tiers × Distants en une seule map indexée.
inter_net_routes = merge([
for net_key, routes in local.remote_routes : {
for entry in flatten([
for r in routes : [
for tier in ["public", "private", "data"] : {
key = "${net_key}_${tier}_to_${r.peering_key}"
net_key = net_key
tier = tier
cidr = r.cidr
peering_key = r.peering_key
}
]
]) : entry.key => entry
}
]...)
}
resource "outscale_route" "inter_net" {
for_each = local.inter_net_routes
route_table_id = local.route_tables[each.value.net_key][each.value.tier]
destination_ip_range = each.value.cidr
net_peering_id = outscale_net_peering.mesh[each.value.peering_key].net_peering_id
}

Lisons l'expression. remote_routes["a"] produit la liste des deux Nets distants vus de A : [{cidr="10.11.0.0/22", peering_key="a-b"}, {cidr="10.12.0.0/22", peering_key="a-c"}]. Le inter_net_routes croise ces couples avec les 3 tiers de chaque Net pour produire 18 entrées dont la clé est lisible : a_private_to_a-b, b_data_to_b-c

Le stack 05-peering/ ne possède aucune ressource outscale_net ni outscale_route_table — il les emprunte au stack 00-nets/ via le state distant :

data "terraform_remote_state" "nets" {
backend = "s3"
config = {
bucket = "capstone-outscale-tfstate"
key = "00-nets/terraform.tfstate"
region = "eu-west-2"
endpoints = {
s3 = "https://oos.eu-west-2.outscale.com"
}
skip_credentials_validation = true
skip_region_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_s3_checksum = true
use_path_style = true
}
}
locals {
nets = data.terraform_remote_state.nets.outputs.nets
route_tables = data.terraform_remote_state.nets.outputs.route_tables
}

Trois bénéfices à découper en stacks :

  • Cycle de vie indépendant. Vous pouvez terraform destroy le stack 05-peering (~10 secondes) sans toucher aux Nets — pratique pour tester un re-câblage.
  • Permissions plus fines. Le compte EIM qui pilote 05-peering n'a pas besoin de outscale_net:Create, juste outscale_net_peering:* et outscale_route:*.
  • Surface de blast réduite. Une erreur dans 05-peering ne peut pas casser les Nets eux-mêmes.

Le prix à payer : vous devez terraform apply les stacks dans l'ordre (00-nets puis 05-peering) et tenir un contrat d'output stable côté 00-nets. C'est un pattern standard de pipeline IaC — il vaut largement le découpage.

  1. Vérifier que le stack 00-nets est bien applied.

    Fenêtre de terminal
    cd terraform/00-nets/
    terraform output -json | jq '.nets.value | keys'
    # → ["a", "b", "c"]

    Si le state est vide, retournez au Chapitre 1.

  2. Initialiser le stack peering.

    Fenêtre de terminal
    cd ../05-peering/
    terraform init

    Le init télécharge le provider 1.5.0 (déjà en cache local depuis le stack précédent) et configure le backend.

  3. Lire le plan — vous devez voir 24 ressources à créer :

    • 3 outscale_net_peering (un par couple)
    • 3 outscale_net_peering_acceptation (un par peering)
    • 18 outscale_route (3 tiers × 3 Nets × 2 Nets distants)
    Fenêtre de terminal
    terraform plan

    Plan: 24 to add, 0 to change, 0 to destroy.

  4. Appliquer.

    Fenêtre de terminal
    terraform apply

    Compter ~30 secondes. La création est très rapide — pas d'IGW ni de NAT à provisionner ici.

  5. Vérifier que les 3 peerings sont en active (pas en pending-acceptance).

    Fenêtre de terminal
    oapi-cli ReadNetPeerings --Filters.Tags '["project=capstone"]' \
    | jq '.NetPeerings[] | {tag: (.Tags[] | select(.Key=="Name") | .Value), state: .State.Name}'

    Doit afficher state: "active" × 3. Si vous voyez pending-acceptance, l'acceptation Terraform a échoué — relire la section piège n°1.

  6. Vérifier que les routes ne sont PAS en blackhole.

    Fenêtre de terminal
    oapi-cli ReadRouteTables --Filters.Tags '["project=capstone"]' \
    | jq '.RouteTables[].Routes[] | select(.NetPeeringId != null) | {dest: .DestinationIpRange, peering: .NetPeeringId, state: .State}'

    Toutes les entrées doivent avoir state: "active". Si une seule est blackhole, c'est qu'un peering est resté en pending-acceptance.

Le seul test qui ne ment pas est de pinger une VM dans Net-B depuis une VM dans Net-A. Vous ferez ce test au Chapitre 4 quand le bastion sera déployé. À ce stade, on se contente de la validation par API — peering active + routes active côté OUTSCALE = trafic théoriquement OK. La validation fonctionnelle viendra avec la première VM joignable.

Une variante consisterait à instancier 3 fois un module Terraform peering paramétré par (source, accepter). Le code serait plus court mais le tradeoff penche en faveur du for_each direct :

  • Lisibilité du plan — un terraform plan montre 3 ressources outscale_net_peering.mesh["a-b"], lisibles d'un coup. Avec un module, c'est module.peering_ab.outscale_net_peering.this × 3, plus verbeux.
  • Refactor moins coûteux — passer de 3 à 4 Nets ajoute une ligne dans peering_pairs, contre 3 invocations de module supplémentaires.
  • Pas de plus-value modulaire — la logique tient en 30 lignes ; un module ajouterait une indirection sans encapsuler de complexité réelle.

La règle générale : for_each pour 2-10 instances, module quand on dépasse une centaine de lignes ou qu'on veut publier la logique en réutilisable. Ici, on est très loin du seuil.

SymptômeCauseSolution
Routes en blackhole côté APIPeering en pending-acceptanceS'assurer que outscale_net_peering_acceptation est créée pour chaque peering
Error: argument min/max requires number au planComparaison de strings avec min()/max()Remplacer par sort([a, b])[0] et sort([a, b])[1]
Peerings doublés (6 au lieu de 3)Couples non triés (a-b et b-a)Trier lexicographiquement la clé du couple
Error: data.terraform_remote_state.nets — Output not found00-nets non appliqué ou clé d'output renomméeRe-apply le stack 00-nets, vérifier les noms d'outputs
Error: trailing checksum is not supported au lecteur de stateVariables AWS_*_CHECKSUM_* non exportéesdirenv allow à la racine du dépôt
Trafic OK A→B mais pas B→ARoute manquante côté Net-BVérifier que inter_net_routes produit bien 18 entrées (6 par Net)
  • Sur OUTSCALE, l'acceptation est explicite, même intra-compte — la ressource outscale_net_peering_acceptation est mandatory.
  • 3 Nets en mesh = 3 peerings + 18 routes (3 tiers × 3 Nets × 2 distants), tout généré par un for_each aplati.
  • Le peering n'est pas transitif — un mesh complet est requis dès qu'il faut plus que de l'étoile.
  • HCL ne compare pas les strings avec </>/min()/max() — le pattern idiomatique est sort([a, b])[0].
  • Le state distant (data.terraform_remote_state) est le contrat propre pour empiler des stacks IaC sans tout fusionner dans un mégastack.
  • Une route en blackhole est silencieuse côté Terraform — la vérification se fait côté API OUTSCALE après l'apply.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn