
Ce guide présente la mise en place d’une infrastructure haute disponibilité sur Outscale. L’objectif est de fournir un point d’entrée unique capable de basculer automatiquement entre deux nœuds situés dans des zones de disponibilité différentes. La solution combine Terraform pour le provisionnement, Ansible pour la configuration, et un cluster Corosync/Pacemaker en mode actif/passif avec une EIP flottante gérée dynamiquement.
Architecture de la solution
Section intitulée « Architecture de la solution »L’architecture repose sur une approche multi-zones pour maximiser la résilience.
Chaque zone (eu-west-2a et eu-west-2b) héberge un VPC distinct avec un nœud
HAProxy et un serveur web Nginx. Cette séparation physique garantit qu’une
défaillance d’une zone entière n’affecte pas le service.
Le cœur du système est l’EIP flottante qui constitue le point d’accès unique. Cette adresse IP publique statique peut être attachée et détachée dynamiquement, maintenant ainsi la continuité même en cas de panne. Cette solution utilise exclusivement des primitives Outscale sans dépendre de services managés, la rendant adaptée aux environnements souverains.
Un VPC Peering interconnecte les deux réseaux, permettant au cluster de communiquer et aux HAProxy d’accéder aux backends dans l’autre zone tout en préservant l’isolation réseau.
Internet │ Elastic IP (EIP) 80.247.2.238 │ ┌────────┴ ─ ─ ─ ─ ─┐ │ HAProxy A HAProxy B (Actif) (Passif) │ │ Web A Web B Nginx Nginx │ │ eu-west-2a eu-west-2b └──── VPC Peering ───┘L’EIP pointe en permanence vers un seul HAProxy, celui désigné comme actif par le gestionnaire de cluster Pacemaker. Les backends Nginx sont répartis sur les deux zones et restent accessibles via le peering, garantissant ainsi que le HAProxy actif peut toujours distribuer la charge vers les serveurs disponibles, quelle que soit leur localisation.
Concepts et composants de la haute disponibilité
Section intitulée « Concepts et composants de la haute disponibilité »Le système repose sur plusieurs composants détaillés dans le guide Corosync/Pacemaker. Corosync assure la communication inter-nœuds et détecte les pannes, tandis que Pacemaker orchestre les ressources et décide des actions correctives.
Pour un cluster à deux nœuds, l’option two_node: 1 dans Corosync adapte le comportement
du quorum pour permettre au cluster de fonctionner avec un seul nœud actif en cas de perte
de communication.
L’EIP est la ressource critique. Lorsque Pacemaker détecte une défaillance (nœud injoignable ou HAProxy défaillant), il détache automatiquement l’EIP du nœud problématique et la réattache au nœud secondaire avec un temps d’interruption minimal.
Provisionnement de l’infrastructure avec Terraform
Section intitulée « Provisionnement de l’infrastructure avec Terraform »Le provisionnement suit une approche méthodique détaillée dans le guide Infrastructure as Code. La configuration du backend Terraform sur un bucket S3 Outscale garantit que l’état reste accessible et partageable, ce qui est essentiel en production.
terraform { backend "s3" { # Configuration du backend pour stocker l'état Terraform bucket = "tf-state-ha" key = "ha/terraform.tfstate" endpoint = "https://oos.eu-west-2.outscale.com" region = "eu-west-2" }}Chaque zone reçoit un VPC avec un bloc CIDR distinct : 10.0.0.0/24 pour
la zone A et 10.0.1.0/24 pour la zone B, évitant tout conflit d’adressage.
resource "outscale_vpc" "vpc_a" { # VPC pour la zone de disponibilité A cidr_block = "10.0.0.0/24" tags = { Name = "vpc-ha-zone-a" }}
resource "outscale_vpc" "vpc_b" { # VPC pour la zone de disponibilité B cidr_block = "10.0.1.0/24" tags = { Name = "vpc-ha-zone-b" }}Le VPC Peering crée une connexion bidirectionnelle mais ne configure pas automatiquement les routes. Il faut explicitement les ajouter dans les tables de routage de chaque VPC.
resource "outscale_net_peering" "peer" { # Création du peering entre les deux VPC source_net_id = outscale_vpc.vpc_a.net_id acceptor_net_id = outscale_vpc.vpc_b.net_id tags = { Name = "peering-ha" }}
resource "outscale_route" "a_to_b" { # Route du VPC A vers le VPC B via le peering net_id = outscale_vpc.vpc_a.net_id destination_ip_range = "10.0.1.0/24" gateway_id = outscale_net_peering.peer.net_peering_id}
resource "outscale_route" "b_to_a" { # Route du VPC B vers le VPC A via le peering net_id = outscale_vpc.vpc_b.net_id destination_ip_range = "10.0.0.0/24" gateway_id = outscale_net_peering.peer.net_peering_id}Les Security Groups définissent les règles pare-feu. Ports obligatoires : UDP 5404-5405 (Corosync), TCP 2224 et 3121 (Pacemaker), TCP 80 (HAProxy) et TCP 22 (SSH depuis bastion).
resource "outscale_security_group" "cluster_sg" { description = "Security Group pour le cluster HA" net_id = outscale_vpc.vpc_a.net_id
# Port SSH depuis le bastion ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["10.0.0.0/16"] }
# Ports Corosync ingress { from_port = 5404 to_port = 5405 protocol = "udp" cidr_blocks = ["10.0.0.0/16", "10.0.1.0/16"] }
# Ports Pacemaker ingress { from_port = 2224 to_port = 2224 protocol = "tcp" cidr_blocks = ["10.0.0.0/16", "10.0.1.0/16"] }
ingress { from_port = 3121 to_port = 3121 protocol = "tcp" cidr_blocks = ["10.0.0.0/16", "10.0.1.0/16"] }
# Port HTTP depuis Internet ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] }}Les instances utilisent une image Packer personnalisée et reçoivent des tags
(Role, Zone, Type) pour l’inventaire dynamique Ansible.
resource "outscale_vm" "haproxy_a" { # Nœud HAProxy dans la zone A keypair_name = outscale_keypair.main.keypair_name image_id = var.rocky_image instance_type = "tinav5.c2r4p2" subnet_id = outscale_subnet.subnet_a.id security_group_ids = [outscale_security_group.cluster_sg.id]
tags = { Name = "haproxy-a" Role = "haproxy" Zone = "a" Type = "cluster-node" }}
resource "outscale_vm" "haproxy_b" { # Nœud HAProxy dans la zone B keypair_name = outscale_keypair.main.keypair_name image_id = var.rocky_image instance_type = "tinav5.c2r4p2" subnet_id = outscale_subnet.subnet_b.id security_group_ids = [outscale_security_group.cluster_sg.id]
tags = { Name = "haproxy-b" Role = "haproxy" Zone = "b" Type = "cluster-node" }}L’EIP est réservée mais non attachée lors du provisionnement. Pacemaker gérera l’attachement dynamique une fois le cluster configuré, évitant ainsi les conflits.
resource "outscale_public_ip" "eip" { # Réservation de l'EIP sans attachement initial # Pacemaker gérera l'attachement dynamique tags = { Name = "eip-ha-cluster" }}
output "elastic_ip" { description = "Adresse IP publique pour l'accès au cluster" value = outscale_public_ip.eip.public_ip}Configuration du cluster avec Ansible
Section intitulée « Configuration du cluster avec Ansible »Ansible automatise la configuration du cluster de la préparation système à la déclaration des ressources Pacemaker.
L’inventaire dynamique utilise le plugin aws_ec2 compatible avec l’API Outscale.
Point critique : utilisation des adresses IP privées pour éviter les incohérences
liées à la bascule de l’EIP. Les connexions SSH passent par le bastion via ProxyJump.
# Inventaire dynamique aws_ec2.ymlplugin: amazon.aws.aws_ec2aws_profile: outscaleregions: - eu-west-2
# Utilisation des IP privées pour éviter les conflits avec l'EIP flottantecompose: ansible_host: private_ip_address
# Organisation en groupes basée sur les tagskeyed_groups: - key: tags.Role separator: '' - key: tags.Zone separator: '' - key: tags.Type separator: ''
# Configuration du bastion comme jump hostansible_ssh_common_args: '-o ProxyJump=bastion.example.com'La préparation système installe les packages requis (Corosync, Pacemaker,
pcs, HAProxy), désactive le firewall local (filtrage géré par Security Groups)
et configure les hostnames avec mise à jour de /etc/hosts pour la résolution DNS locale.
- name: Préparation système pour le cluster hosts: cluster-node become: true tasks: - name: Installation des packages nécessaires package: name: - corosync - pacemaker - pcs - haproxy state: present
- name: Désactivation du firewall # Le firewall est désactivé car les Security Groups gèrent déjà le filtrage systemd: name: firewalld enabled: false state: stopped
- name: Configuration du hostname hostname: name: "{{ inventory_hostname }}.cluster.local"
- name: Mise à jour de /etc/hosts # Ajout de tous les nœuds du cluster pour la résolution DNS locale lineinfile: dest: /etc/hosts line: "{{ hostvars[item].ansible_host }} {{ item }}.cluster.local {{ item }}" state: present loop: "{{ groups['cluster-node'] }}"Les routes statiques OS sont indispensables pour la communication Corosync via le peering. Important : Terraform configure les routes VPC mais elles ne sont pas automatiquement appliquées dans l’OS. Ansible crée des fichiers de route persistants.
- name: Configuration des routes pour le peering hosts: cluster-node become: true vars: # Gateway par défaut du sous-réseau gateway_ip: "{{ ansible_default_ipv4.gateway }}" tasks: - name: Configuration de route statique vers l'autre zone # Création d'un fichier de route persistant copy: dest: /etc/sysconfig/network-scripts/route-eth0 content: | # Route vers la zone {{ 'B' if 'a' in inventory_hostname else 'A' }} {{ '10.0.1.0/24' if 'a' in inventory_hostname else '10.0.0.0/24' }} via {{ gateway_ip }} mode: '0644' notify: Redémarrage réseau
handlers: - name: Redémarrage réseau systemd: name: network state: restartedLa CLI Outscale (osc-cli) est configurée pour l’agent OCF. Les credentials
sont stockés dans Ansible Vault et déployés dans /root/.osc/config.json. Seul
root nécessite cette configuration car Pacemaker exécute les agents sous cette identité.
- name: Configuration de la CLI Outscale hosts: cluster-node become: true vars_files: - vars/vault.yml tasks: - name: Création du répertoire de configuration file: path: /root/.osc state: directory mode: '0700'
- name: Déploiement du fichier de configuration # Template contenant les credentials depuis le vault template: src: config.json.j2 dest: /root/.osc/config.json mode: '0600' no_log: trueLa récupération dynamique de l’EIP via l’API Outscale évite le codage en dur et rend les playbooks réutilisables.
- name: Récupération de l'adresse EIP hosts: localhost connection: local tasks: - name: Interrogation de l'API pour obtenir l'EIP shell: | osc-cli api ReadPublicIps \ --profile default \ --Filters.Tags '[{"Key": "Name", "Values": ["eip-ha-cluster"]}]' \ --query 'PublicIps[0].PublicIp' \ --output text register: eip_result changed_when: false
- name: Sauvegarde de l'EIP comme fait set_fact: cluster_eip: "{{ eip_result.stdout }}" cacheable: trueLa configuration Corosync définit la topologie cluster. Le transport udpu
(UDP unicast) est préféré en cloud. Chaque nœud a une IP privée et un nodeid
unique. L’option two_node: 1 est fondamentale pour le quorum à deux nœuds.
# Template corosync.conf.j2totem { version: 2 cluster_name: ha-cluster transport: udpu interface { ringnumber: 0 # Utilisation du réseau privé pour la communication cluster bindnetaddr: {{ ansible_default_ipv4.address }} broadcast: yes mcastport: 5405 }}
nodelist { # Déclaration explicite de chaque nœud du cluster node { ring0_addr: 10.0.0.10 name: haproxy-a nodeid: 1 } node { ring0_addr: 10.0.1.10 name: haproxy-b nodeid: 2 }}
quorum { provider: corosync_votequorum # Configuration spécifique pour un cluster à deux nœuds two_node: 1}
logging { to_logfile: yes logfile: /var/log/corosync/corosync.log to_syslog: yes timestamp: on}Ordre de démarrage : Corosync → Pacemaker → pcsd (daemon pour gestion
via pcs). Ansible active aussi le démarrage automatique au boot.
- name: Démarrage des services cluster hosts: cluster-node become: true tasks: - name: Démarrage de Corosync systemd: name: corosync enabled: true state: started
- name: Démarrage de Pacemaker # Pacemaker doit démarrer après Corosync systemd: name: pacemaker enabled: true state: started
- name: Démarrage de pcsd # Nécessaire pour la gestion via pcs systemd: name: pcsd enabled: true state: startedL’authentification cluster utilise un mot de passe hacluster identique
sur tous les nœuds. La commande pcs host auth établit la confiance mutuelle.
- name: Configuration de l'authentification cluster hosts: cluster-node become: true vars_files: - vars/vault.yml tasks: - name: Définition du mot de passe hacluster user: name: hacluster password: "{{ hacluster_password | password_hash('sha512') }}"
- name: Authentification des nœuds # Exécuté depuis un seul nœud pour configurer tout le cluster command: > pcs host auth haproxy-a.cluster.local haproxy-b.cluster.local -u hacluster -p {{ hacluster_password }} run_once: true delegate_to: "{{ groups['cluster-node'][0] }}"Les ressources Pacemaker constituent le cœur de la configuration. HAProxy utilise l’agent systemd, tandis que l’EIP utilise un agent OCF personnalisé. Les contraintes garantissent la colocation (EIP + HAProxy sur même nœud) et l’ordre de démarrage.
- name: Configuration des ressources Pacemaker hosts: cluster-node become: true run_once: true delegate_to: "{{ groups['cluster-node'][0] }}" tasks: - name: Création de la ressource HAProxy # Utilisation de l'agent systemd pour gérer HAProxy command: > pcs resource create haproxy systemd:haproxy op monitor interval=30s timeout=20s op start timeout=60s op stop timeout=60s args: creates: /var/lib/pacemaker/cib/.haproxy_created
- name: Création de la ressource EIP # Agent OCF personnalisé pour gérer l'Elastic IP command: > pcs resource create ElasticIP ocf:custom:osc-eip ip="{{ cluster_eip }}" op monitor interval=30s timeout=60s op start timeout=120s op stop timeout=60s args: creates: /var/lib/pacemaker/cib/.eip_created
- name: Contrainte de colocation # L'EIP doit toujours être sur le même nœud que HAProxy command: > pcs constraint colocation add ElasticIP with haproxy INFINITY
- name: Contrainte d'ordre # HAProxy doit démarrer avant l'attachement de l'EIP command: > pcs constraint order haproxy then ElasticIPLa configuration HAProxy utilise un template Jinja2 qui génère dynamiquement la liste des backends depuis l’inventaire Ansible, incluant tous les serveurs web multi-zones.
# Template haproxy.cfg.j2global log /dev/log local0 chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin stats timeout 30s user haproxy group haproxy daemon
defaults log global mode http option httplog option dontlognull timeout connect 5000 timeout client 50000 timeout server 50000
frontend http-in bind *:80 default_backend webservers
backend webservers balance roundrobin # Génération dynamique des backends depuis l'inventaire {% for host in groups['webservers'] %} server {{ host }} {{ hostvars[host].ansible_host }}:80 check inter 2000 rise 2 fall 3 {% endfor %}Agent OCF pour la gestion de l’EIP
Section intitulée « Agent OCF pour la gestion de l’EIP »L’agent OCF personnalisé fait le pont entre Pacemaker et l’API Outscale.
Il implémente les opérations start, stop, monitor et meta-data
selon les conventions OCF.
Déployé dans /usr/lib/ocf/resource.d/custom/osc-eip, il utilise des fonctions bash
avec des codes de retour OCF stricts pour que Pacemaker interprète correctement
l’état de la ressource.
#!/bin/bash# Agent OCF pour la gestion de l'Elastic IP Outscale# En-têtes OCF obligatoires: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
# Codes de retour OCF standardsOCF_SUCCESS=0OCF_ERR_GENERIC=1OCF_ERR_ARGS=2OCF_ERR_UNIMPLEMENTED=3OCF_ERR_PERM=4OCF_ERR_INSTALLED=5OCF_ERR_CONFIGURED=6OCF_NOT_RUNNING=7
# Récupération des paramètres de la ressourceEIP="${OCF_RESKEY_ip}"PROFILE="${OCF_RESKEY_profile:-default}"
# Fonction pour obtenir l'ID de l'instance localeget_instance_id() { # Utilise les métadonnées d'instance Outscale curl -s http://169.254.169.254/latest/meta-data/instance-id}
# Fonction start: attache l'EIP à cette instanceeip_start() { local instance_id=$(get_instance_id)
ocf_log info "Attachement de l'EIP ${EIP} à l'instance ${instance_id}"
# Appel à l'API Outscale pour attacher l'EIP osc-cli api LinkPublicIp \ --profile "${PROFILE}" \ --PublicIp "${EIP}" \ --VmId "${instance_id}" \ 2>&1 | ocf_log debug
if [ $? -eq 0 ]; then ocf_log info "EIP ${EIP} attachée avec succès" return ${OCF_SUCCESS} else ocf_log err "Échec de l'attachement de l'EIP ${EIP}" return ${OCF_ERR_GENERIC} fi}
# Fonction stop: détache l'EIPeip_stop() { ocf_log info "Détachement de l'EIP ${EIP}"
# Appel à l'API pour détacher l'EIP osc-cli api UnlinkPublicIp \ --profile "${PROFILE}" \ --PublicIp "${EIP}" \ 2>&1 | ocf_log debug
if [ $? -eq 0 ]; then ocf_log info "EIP ${EIP} détachée avec succès" return ${OCF_SUCCESS} else ocf_log warn "Échec du détachement de l'EIP ${EIP}" # On retourne SUCCESS même en cas d'échec car l'EIP peut déjà être détachée return ${OCF_SUCCESS} fi}
# Fonction monitor: vérifie que l'EIP est attachée à cette instanceeip_monitor() { local instance_id=$(get_instance_id)
# Interroge l'API pour connaître l'état actuel de l'EIP local attached_instance=$(osc-cli api ReadPublicIps \ --profile "${PROFILE}" \ --Filters.PublicIps "[\"${EIP}\"]" \ --query 'PublicIps[0].VmId' \ --output text)
if [ "${attached_instance}" = "${instance_id}" ]; then ocf_log debug "EIP ${EIP} correctement attachée à ${instance_id}" return ${OCF_SUCCESS} elif [ -z "${attached_instance}" ] || [ "${attached_instance}" = "None" ]; then ocf_log info "EIP ${EIP} non attachée" return ${OCF_NOT_RUNNING} else ocf_log warn "EIP ${EIP} attachée à une autre instance: ${attached_instance}" return ${OCF_NOT_RUNNING} fi}
# Fonction meta-data: décrit la ressource pour Pacemakereip_meta_data() { cat <<END<?xml version="1.0"?><!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"><resource-agent name="osc-eip"><version>1.0</version>
<longdesc lang="fr">Agent OCF pour gérer une Elastic IP Outscale dans un cluster Pacemaker.Cet agent permet d'attacher/détacher dynamiquement une EIP en fonctionde l'état du cluster.</longdesc>
<shortdesc lang="fr">Gestion d'une Elastic IP Outscale</shortdesc>
<parameters><parameter name="ip" unique="1" required="1"><longdesc lang="fr">Adresse IP publique élastique à gérer</longdesc><shortdesc lang="fr">Elastic IP</shortdesc><content type="string" /></parameter>
<parameter name="profile" unique="0" required="0"><longdesc lang="fr">Profil de configuration osc-cli à utiliser (par défaut: default)</longdesc><shortdesc lang="fr">Profil osc-cli</shortdesc><content type="string" default="default" /></parameter></parameters>
<actions><action name="start" timeout="120s" /><action name="stop" timeout="60s" /><action name="monitor" timeout="60s" interval="30s" depth="0" /><action name="meta-data" timeout="5s" /></actions></resource-agent>END}
# Point d'entrée principalcase "${1}" in start) eip_start ;; stop) eip_stop ;; monitor) eip_monitor ;; meta-data) eip_meta_data ;; validate-all) # Validation de la configuration if [ -z "${EIP}" ]; then ocf_log err "Paramètre 'ip' obligatoire manquant" exit ${OCF_ERR_CONFIGURED} fi exit ${OCF_SUCCESS} ;; *) echo "Usage: ${0} {start|stop|monitor|meta-data|validate-all}" exit ${OCF_ERR_UNIMPLEMENTED} ;;esac
exit $?L’agent nécessite les permissions API : LinkPublicIp, UnlinkPublicIp
et ReadPublicIps. Recommandation : créer un utilisateur dédié avec
politique IAM restrictive.
Le déploiement Ansible copie le script, le rend exécutable (chmod 755) et
vérifie son fonctionnement.
- name: Déploiement de l'agent OCF EIP hosts: cluster-node become: true tasks: - name: Création du répertoire pour les agents personnalisés file: path: /usr/lib/ocf/resource.d/custom state: directory mode: '0755'
- name: Copie de l'agent OCF copy: src: files/osc-eip dest: /usr/lib/ocf/resource.d/custom/osc-eip mode: '0755'
- name: Vérification de l'agent # Test que l'agent répond correctement à meta-data command: /usr/lib/ocf/resource.d/custom/osc-eip meta-data register: agent_test failed_when: agent_test.rc != 0 changed_when: falseGestion opérationnelle du cluster
Section intitulée « Gestion opérationnelle du cluster »La surveillance est essentielle. La commande pcs status fournit une vue
d’ensemble : état des nœuds, ressources et contraintes. C’est le premier réflexe
en cas de problème.
# Vérification de l'état global du clusterpcs status
Cluster name: ha-clusterCluster Summary: * Stack: corosync * Current DC: haproxy-a (version 2.0.5-9) - partition with quorum * Last updated: Sun Jan 15 10:30:45 2024 * Last change: Sun Jan 15 09:15:20 2024 * 2 nodes configured * 2 resource instances configured
Node List: * Online: [ haproxy-a haproxy-b ]
Full List of Resources: * haproxy (systemd:haproxy): Started haproxy-a * ElasticIP (ocf::custom:osc-eip): Started haproxy-a
Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabledLa commande crm_mon offre une surveillance temps réel (option -1 pour
vue instantanée). Les logs sont essentiels au diagnostic :
- Corosync :
/var/log/corosync/corosync.log(communication, détection pannes) - Pacemaker :
journalctl -u pacemaker(décisions ressources, basculements) - HAProxy :
journalctl -u haproxy(trafic)
# Surveillance des logs Pacemaker en temps réeljournalctl -u pacemaker -f
# Logs Corosync pour déboguer la communication clustertail -f /var/log/corosync/corosync.log
# Logs HAProxy pour analyser le traficjournalctl -u haproxy -fLes fail-counts et migration-threshold (détaillés dans le guide
Corosync/Pacemaker) gèrent les échecs. Pacemaker incrémente le
compteur à chaque échec. Atteinte du seuil = basculement automatique.
Réinitialisation via pcs resource cleanup.
# Consultation des compteurs d'échecspcs resource failcount show
# Réinitialisation du compteur pour une ressource spécifiquepcs resource cleanup ElasticIP
# Réinitialisation globale de tous les compteurspcs resource cleanupPour la maintenance, mettre un nœud en standby via pcs node standby
déplace les ressources sans alarmes. Le rechargement HAProxy (systemctl reload)
se fait sans interruption ni détection d’échec par Pacemaker.
# Rechargement de la configuration HAProxy sans interruptionsystemctl reload haproxy
# Vérification que Pacemaker voit toujours la ressource comme activepcs resource show haproxy --fullDépannage et résolution des problèmes courants
Section intitulée « Dépannage et résolution des problèmes courants »Problèmes de communication Corosync
Section intitulée « Problèmes de communication Corosync »Cause la plus fréquente de dysfonctionnement. Vérifier les routes réseau OS
avec ip route show et ping. Contrôler l’ouverture des ports UDP 5404-5405.
# Vérification des routes configuréesip route show
# Test de connectivité vers l'autre nœudping -c 3 10.0.1.10
# Vérification que les ports Corosync sont ouvertsnc -zu 10.0.1.10 5404nc -zu 10.0.1.10 5405Basculement EIP
Section intitulée « Basculement EIP »Généralement : authentification API ou erreurs agent OCF. Tester avec
pcs resource debug-monitor ElasticIP et vérifier les credentials osc-cli.
# Test manuel de l'agent OCF/usr/lib/ocf/resource.d/custom/osc-eip monitor
# Vérification des logs de la ressourcepcs resource debug-monitor ElasticIP
# Test de l'API Outscale avec osc-cliosc-cli api ReadPublicIps --profile defaultDésynchronisation temporelle
Section intitulée « Désynchronisation temporelle »Corosync est sensible aux décalages d’horloge. Utiliser NTP ou chrony
est indispensable (timedatectl status pour vérifier).
# Vérification de l'heure systèmetimedatectl status
# Synchronisation immédiate avec NTPchronyc makestep
# Activation de la synchronisation automatiquesystemctl enable --now chronydBackends HAProxy DOWN
Section intitulée « Backends HAProxy DOWN »Vérifier : connectivité réseau, Security Groups, santé services Nginx.
Utiliser socat pour consulter les stats HAProxy.
# Consultation des statistiques HAProxyecho "show stat" | socat stdio /run/haproxy/admin.sock
# Test de connectivité vers un backendcurl -v http://10.0.0.20:80
# Vérification des health checkspcs resource show haproxy --full | grep monitorSplit-brain
Section intitulée « Split-brain »Situation rare avec bonne config. Solution : arrêter un nœud, vérifier l’autre, puis redémarrer pour resynchronisation.
# Arrêt complet d'un nœudpcs cluster stop haproxy-b
# Vérification sur l'autre nœudpcs status
# Redémarrage du nœud arrêtépcs cluster start haproxy-bOptimisations et évolutions possibles
Section intitulée « Optimisations et évolutions possibles »Troisième nœud
Section intitulée « Troisième nœud »Améliore drastiquement la stabilité avec quorum natif. Retirer two_node: 1
et le cluster tolère l’isolation d’un nœud sans split-brain.
Fencing STONITH
Section intitulée « Fencing STONITH »Amélioration majeure : isolation complète du nœud défaillant avant basculement, éliminant les corruptions de données. Nécessite un agent fencing pour API Outscale.
TLS et monitoring
Section intitulée « TLS et monitoring »- TLS : termination sur HAProxy ou passthrough, automatisable avec Let’s Encrypt
- Monitoring : Prometheus + Grafana pour métriques HAProxy, Corosync et Pacemaker
Extensions
Section intitulée « Extensions »- Multi-applications : frontends HAProxy multiples avec contraintes colocation
- Persistance : DRBD pour réplication volumes entre nœuds, géré par Pacemaker
Conclusion
Section intitulée « Conclusion »Cette architecture démontre qu’une infrastructure haute disponibilité robuste et souveraine est possible sur Outscale CloudGouv sans dépendre de services managés. L’approche multi-AZ avec VPC Peering offre une excellente résilience, tandis que le cluster Corosync/Pacemaker garantit des basculements automatiques rapides.
L’automatisation complète (Terraform + Ansible) rend cette infrastructure reproductible et maintenable, réduisant considérablement les erreurs humaines.
L’EIP flottante gérée par un agent OCF personnalisé illustre la flexibilité de l’open source : un mécanisme de basculement en quelques centaines de lignes, parfaitement intégré aux primitives Outscale.
Cette solution constitue une base solide pour des environnements de production critiques nécessitant un contrôle complet de la pile, extensible selon les besoins (fencing, TLS, nœuds supplémentaires, multi-services).