E-NO
DevOps 7 min de lecture

Automatisation CI/CD avec Proxmox : un guide pratique de terrain

calendar_today Publié : 2026-09-19
update Dernière mise à jour : 2026-09-19
analytics Efficacité SEO : 100%
Illustration du guide technique pour « Automatisation CI/CD avec Proxmox : un guide pratique de terrain ».

Introduction

Automatiser Proxmox avec le CI/CD signifie traiter les machines virtuelles comme du code : chaque VM est définie dans un dépôt versionné, chaque modification passe par une revue et des vérifications automatisées, et chaque déploiement laisse une trace d'audit. Ce guide s'adresse aux développeurs, ingénieurs DevOps et équipes techniques de startups qui souhaitent remplacer les clics manuels dans Proxmox par des pipelines reproductibles.

Vous apprendrez à :

  • Inventorier votre environnement Proxmox et planifier l'automatisation en toute sécurité.
  • Construire un pipeline CI/CD minimal mais prêt pour la production avec GitLab CI, Terraform et Ansible.
  • Déployer et configurer une VM depuis zéro jusqu'à servir du trafic.
  • Ajouter des contrôles de santé, des plans de retour arrière et des diagnostics de panne.
  • Éviter les erreurs qui rendent l'automatisation Proxmox fragile et dangereuse.

Chaque exemple utilise des espaces réservés explicites (<placeholder>) et suppose Proxmox VE 8.x. Ne copiez jamais de véritables identifiants, jetons ou identifiants de production dans le contrôle de version.

L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, vérifier chaque étape et savoir comment récupérer avant d'en avoir besoin.

Inventaire des versions et de l'environnement

Avant d'écrire la moindre ligne de code de pipeline, collectez les faits concernant votre installation Proxmox. Cela évite les incompatibilités de versions et les hypothèses erronées sur l'API.

Ce qu'il faut capturer

Exécutez ces commandes en lecture seule sur un nœud Proxmox via SSH ou le shell web :

# Version de Proxmox
pveversion
# Sortie attendue : pve-manager/8.1.4/7d9c1b5e (running kernel: 6.5.11-7-pve)

# Statut du cluster (si en cluster)
pvecm status
# Sortie attendue : informations sur le quorum, liste des membres et chaque nœud marqué comme en ligne

# Stockage disponible pour les disques VM
pvesm status
# Sortie attendue : un tableau des identifiants de stockage, types et utilisation. Notez le type : dir, lvm, lvmthin, zfs, cephfs, nfs, etc.

# Ponts réseau
ip link show | grep -E '^[0-9]+: (vmbr|tap)'
# Sortie attendue : lignes montrant vmbr0, vmbr1, etc., avec état UP

Enregistrez ces informations dans un fichier environment.md dans votre dépôt d'automatisation :

# Instantané de l'environnement Proxmox
Date : 2025-02-20
Nœud : pve-node-1
Version PVE : 8.1.4
Cluster : oui (3 nœuds, quorum OK)
Stockage pour VM : local-lvm (lvmthin), ceph-pool (cephfs)
Ponts réseau : vmbr0 (gestion), vmbr1 (public VM)
Utilisateur d'automatisation : terraform@pve (jeton API uniquement)

Automatisation de l'inventaire

Vous pouvez automatiser cet inventaire avec un script planifié. Exemple avec Ansible :

# playbooks/facts.yml
- name: Collecter les faits Proxmox
  hosts: pve_nodes
  gather_facts: false
  vars:
    ansible_user: root
    ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: Exécuter pveversion
      command: pveversion
      register: pve_version

    - name: Afficher la version
      debug:
        msg: "{{ pve_version.stdout }}"

Lancez-le avec ansible-playbook -i inventory/proxmox.yml playbooks/facts.yml. Planifiez-le chaque semaine ou dans la première étape de votre pipeline pour détecter les dérives.

Pourquoi c'est important

Si vous écrivez du code Terraform pour la version 2 de l'API Proxmox et que votre serveur est encore en version 1, tout échouera. Si vous supposez que local-lvm est disponible mais que le nœud utilise ZFS, vos définitions de disques seront incorrectes. Inventoriez d'abord, automatisez ensuite.

Chemin de configuration sécurisé

Les identifiants d'automatisation doivent suivre le principe du moindre privilège. Au lieu d'utiliser le mot de passe root, créez un utilisateur Proxmox dédié avec des autorisations de jeton API.

Créer un jeton API restreint

Dans l'interface web Proxmox :

  1. Allez dans Datacenter > Permissions > Utilisateurs et cliquez sur Ajouter.
  • Nom d'utilisateur : terraform@pve
  • Domaine : Authentification PVE
  • Mot de passe : générez-en un fort, stockez-le dans un gestionnaire de secrets.
  1. Allez dans Datacenter > Permissions > Jetons API et cliquez sur Ajouter.
  • Utilisateur : terraform@pve
  • Identifiant du jeton : ci-token
  • Décochez Séparation des privilèges (afin que le jeton ait les autorisations de l'utilisateur).
  • Notez l'identifiant complet du jeton : terraform@pve!ci-token et le secret (il n'est affiché qu'une seule fois).
  1. Allez dans Datacenter > Permissions > Ajouter > Permission de jeton API.
  • Chemin : /
  • Jeton : terraform@pve!ci-token
  • Rôle : PVEVMAdmin (permet la gestion des VM mais pas les modifications au niveau de l'hôte).

Ce jeton peut créer, démarrer, arrêter et supprimer des VM, mais ne peut pas modifier les paramètres de stockage ou de réseau. Il convient au CI/CD.

Stocker les secrets en toute sécurité

Ne mettez jamais le secret du jeton dans le fichier de pipeline. Dans GitLab CI, utilisez des variables masquées :

  • PROXMOX_URL = https://pve-node-1.example.com:8006/api2/json
  • PROXMOX_TOKEN_ID = terraform@pve!ci-token
  • PROXMOX_TOKEN_SECRET = (la longue chaîne secrète, masquée)

Vous pouvez également utiliser HashiCorp Vault, AWS Secrets Manager ou les variables de GitLab CI. Le jeton doit être renouvelé tous les 90 jours, et le renouvellement doit faire partie de votre liste de contrôle des opérations.

Vérifier l'accès au jeton

Testez le jeton avec curl :

curl -k -H "Authorization: PVEAPIToken=terraform@pve!ci-token=<SECRET>" \
  https://pve-node-1.example.com:8006/api2/json/nodes
# Attendu : tableau JSON des nœuds

Une erreur 401 ou 403 signifie que le jeton est mal configuré.

Construction du pipeline CI/CD avec Terraform et GitLab CI

Nous utiliserons Terraform pour définir une VM Proxmox, Ansible pour la configurer et GitLab CI pour tout orchestrer. Le même schéma fonctionne avec GitHub Actions, Jenkins ou tout exécuteur capable d'exécuter des conteneurs.

Structure du dépôt

proxmox-ci/
├── .gitlab-ci.yml
├── terraform/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
├── ansible/
│   ├── site.yml
│   └── inventory/
│       └── hosts.yml
└── scripts/
    └── healthcheck.sh

Configuration du fournisseur Terraform

terraform/main.tf :

terraform {
  required_providers {
    proxmox = {
      source  = "bpg/proxmox"
      version = "0.60.0"
    }
  }
}

provider "proxmox" {
  endpoint = var.proxmox_url
  insecure = true # uniquement pour les certificats auto-signés ; mettre false avec une CA valide
  api_token = "${var.proxmox_token_id}=${var.proxmox_token_secret}"
}

terraform/variables.tf :

variable "proxmox_url" {
  type    = string
  sensitive = true
}
variable "proxmox_token_id" {
  type    = string
  sensitive = true
}
variable "proxmox_token_secret" {
  type    = string
  sensitive = true
}
variable "vm_name" {
  type    = string
  default = "web-demo"
}
variable "vm_cpu" {
  type    = number
  default = 2
}
variable "vm_memory" {
  type    = number
  default = 2048
}

Définition de la VM

Ajoutez une ressource VM à main.tf en utilisant le modèle d'image cloud Ubuntu :

resource "proxmox_virtual_environment_vm" "web" {
  name      = var.vm_name
  node_name = "pve-node-1"
  vm_id     = 9001

  cpu {
    cores = var.vm_cpu
  }
  memory {
    dedicated = var.vm_memory
  }

  agent {
    enabled = true
  }

  disk {
    datastore_id = "local-lvm"
    file_id      = proxmox_virtual_environment_download_file.ubuntu_cloud_image.id
    interface    = "scsi0"
    size         = 20
  }

  network_device {
    bridge = "vmbr1"
  }

  operating_system {
    type = "l26"
  }

  initialization {
    datastore_id = "local-lvm"
    ip_config {
      ipv4 {
        address = "dhcp"
      }
    }
    user_account {
      username = "ubuntu"
      keys     = [file("~/.ssh/id_rsa.pub")]
    }
  }
}

Avant de l'utiliser, téléchargez un modèle d'image cloud. Vous pouvez le faire manuellement ou avec Terraform :

resource "proxmox_virtual_environment_download_file" "ubuntu_cloud_image" {
  content_type = "iso"
  datastore_id = "local"
  node_name    = "pve-node-1"
  url          = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
  file_name    = "noble-server-cloudimg-amd64.img"
}

Convertissez-le ensuite en modèle de VM. L'automatisation de la création du modèle dépasse le cadre de cet article, mais vous pouvez suivre le wiki Proxmox pour les images cloud-init.

Pipeline GitLab CI

.gitlab-ci.yml :

stages:
  - validate
  - plan
  - deploy
  - configure
  - verify

variables:
  TF_ROOT: terraform
  ANSIBLE_HOST_KEY_CHECKING: "false"

before_script:
  - apt-get update && apt-get install -y curl jq openssh-client
  - cd $TF_ROOT
  - terraform --version

validate:
  stage: validate
  script:
    - terraform init -backend=false
    - terraform validate
  only:
    - merge_requests

plan:
  stage: plan
  script:
    - terraform init
    - terraform plan -out=plan.tfplan -var-file=vars/prod.tfvars
  artifacts:
    paths:
      - terraform/plan.tfplan
    expire_in: 1 week
  only:
    - main

deploy:
  stage: deploy
  script:
    - terraform init
    - terraform apply -auto-approve plan.tfplan
    - terraform output -json vm_ip > /tmp/vm_ip.json
    - cat /tmp/vm_ip.json
  artifacts:
    paths:
      - /tmp/vm_ip.json
    expire_in: 1 day
  when: manual
  only:
    - main

configure:
  stage: configure
  script:
    - apt-get update && apt-get install -y ansible
    - cd ansible
    - echo "[web]" > inventory/hosts.ini
    - IP=$(jq -r '.value' /tmp/vm_ip.json)
    - echo "web ansible_host=$IP ansible_user=ubuntu ansible_ssh_private_key_file=$SSH_PRIVATE_KEY" >> inventory/hosts.ini
    - ansible-playbook -i inventory/hosts.ini site.yml -e vm_ip=$IP
  only:
    - main

verify:
  stage: verify
  script:
    - IP=$(jq -r '.value' /tmp/vm_ip.json)
    - ./scripts/healthcheck.sh $IP
  only:
    - main

Configuration Ansible

ansible/site.yml :

- name: Configurer le serveur web
  hosts: web
  become: true
  tasks:
    - name: Mettre à jour le cache apt
      apt:
        update_cache: yes

    - name: Installer nginx
      apt:
        name: nginx
        state: present

    - name: Démarrer nginx
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Créer index.html
      copy:
        content: "<html><body><h1>Déployé par CI/CD</h1></body></html>"
        dest: /var/www/html/index.html
        owner: www-data
        group: www-data
        mode: '0644'

Script de contrôle de santé

scripts/healthcheck.sh :

#!/bin/bash
IP=$1
for i in {1..5}; do
  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://$IP/)
  if [ "$HTTP_CODE" == "200" ]; then
    echo "Contrôle de santé réussi : HTTP 200"
    exit 0
  fi
  echo "Tentative $i : obtenu $HTTP_CODE, nouvelle tentative dans 5s..."
  sleep 5
done
echo "Contrôle de santé échoué"
exit 1

Résultats attendus et signaux d'échec

  • terraform validate échoue si la syntaxe ou le bloc fournisseur est invalide.
  • terraform plan montre les changements souhaités ; si aucun changement, la VM existe déjà.
  • L'étape deploy applique le plan et affiche l'IP de la VM.
  • L'étape configure installe nginx et le démarre.
  • L'étape verify interroge l'IP et attend un code HTTP 200.

Si une étape échoue, le pipeline s'arrête. Corrigez l'erreur, validez et relancez. L'état Terraform est stocké dans le backend GitLab (vous devez configurer un backend, par exemple HTTP ou S3, ou utiliser l'état Terraform géré par GitLab).

Vérification et diagnostics

Après le déploiement, vérifiez toujours depuis l'extérieur de la VM, pas seulement depuis l'intérieur. Le script de contrôle de santé le fait déjà, mais voici des vérifications supplémentaires.

Vérifier le statut de la VM via l'API Proxmox

curl -k -H "Authorization: PVEAPIToken=terraform@pve!ci-token=<SECRET>" \
  https://pve-node-1.example.com:8006/api2/json/nodes/pve-node-1/qemu/9001/status/current
# Attendu : JSON avec "status": "running"

Vérifier les journaux système dans la VM

Dans le playbook Ansible, ajoutez une tâche pour récupérer les journaux si nécessaire :

- name: Obtenir le journal d'erreurs nginx
  command: tail -n 20 /var/log/nginx/error.log
  register: nginx_log
  ignore_errors: true

- name: Afficher le journal
  debug:
    msg: "{{ nginx_log.stdout }}"

Diagnostiquer les problèmes courants

SymptômeCause probableCommande de diagnostic
Terraform apply échoue avec 401Mauvais jeton ou secretcurl -I -H "Authorization: ..." https://pve/api2/json
VM créée mais n'obtient jamais d'IPCloud-init non configuréVérifiez la sortie console de la VM : qm terminal 9001
SSH Ansible échouePas de clé SSH injectée ou pare-feussh -i key ubuntu@IP depuis l'exécuteur
Le contrôle de santé obtient une connexion refuséenginx non démarré ou pare-feuDans la VM : systemctl status nginx, ufw status
API Proxmox lente ou délais d'attenteMauvaise configuration réseau ou DNSping pve-node-1, curl -v l'API

Capturez toujours la sortie exacte de la commande avant de faire des modifications. Le journal des tâches Proxmox (interface web > Nœud > Historique des tâches) montre les actions et erreurs de l'API.

Modes de défaillance et récupération

L'automatisation peut échouer de nombreuses façons. Voici les échecs les plus probables et comment les récupérer.

1. Corruption de l'état Terraform

Échec : Une exécution de pipeline est interrompue pendant que Terraform applique. Le fichier d'état sur le backend peut être verrouillé ou corrompu.

Récupération :

  • Vérifiez si l'état est verrouillé : terraform force-unlock <lock-id> (obtenez l'ID de verrouillage à partir de l'erreur).
  • Si l'état est corrompu, restaurez à partir d'une sauvegarde. L'état géré par GitLab a une gestion des versions ; vous pouvez revenir en arrière.
  • Relancez terraform plan puis apply.

2. VM déployée mais la configuration échoue

Échec : Terraform crée la VM, mais Ansible échoue à mi-chemin (par exemple, apt update échoue en raison du DNS). La VM reste dans un état partiellement configuré.

Récupération :

  • Diagnostiquez via les journaux Ansible. Si corrigible, relancez uniquement la tâche en échec : ansible-playbook -i inventory site.yml --start-at-task="Installer nginx"
  • Si la VM est inutilisable, détruisez-la avec Terraform : terraform destroy -target=proxmox_virtual_environment_vm.web et redéployez.
  • Pour éviter l'état partiel, définissez ignore_errors: false et utilisez block/rescue dans Ansible pour nettoyer.

3. Retour arrière vers une version précédente de la VM

Scénario : Un nouveau déploiement introduit une version cassée de l'application. Vous devez revenir à l'instantané précédent de la VM.

Récupération :

Si vous avez pris un instantané Proxmox avant le déploiement, le retour arrière est instantané :

qm rollback 9001 snap_before_update
# Attendu : la VM est ramenée à l'état de l'instantané, redémarrée

Vous devez intégrer la création d'instantanés dans votre pipeline avant d'exécuter des modifications de configuration :

qm snapshot 9001 snap_before_update --description "Avant la mise à jour de l'application"

Si vous utilisez une infrastructure immuable (traitez les VM comme jetables), vous déployez simplement la balise de version précédente depuis Git :

  • Sur GitLab, balisez chaque build réussi avec v1.2.3.
  • Dans votre pipeline, autorisez le déploiement manuel de n'importe quelle balise.
  • Pour revenir en arrière, exécutez le job de déploiement pour la balise précédente.

4. Suppression accidentelle de la VM de production

Échec : Une modification Terraform supprime la ressource VM parce que quelqu'un a changé le count ou le nom.

Récupération :

  • Le plan Terraform aurait montré la suppression. Exigez une approbation manuelle pour l'application.
  • Planifiez des sauvegardes Proxmox (par exemple, nocturnes vers PBS ou NFS). Restaurez à partir de la sauvegarde.
  • Utilisez le méta-argument de cycle de vie prevent_destroy sur les VM critiques.
lifecycle {
  prevent_destroy = true
}

5. Le jeton API expire ou est révoqué

Échec : Le pipeline échoue avec 401 parce que le jeton a été renouvelé ou révoqué.

Récupération :

  • Conservez le secret du jeton dans un gestionnaire de secrets avec renouvellement automatique.
  • Dans GitLab, mettez à jour la variable CI et relancez le pipeline.
  • Documentez l'expiration du jeton dans votre runbook.

Liste de contrôle des opérations

Utilisez cette liste avant et après chaque déploiement en production.

Liste de contrôle pré-déploiement

ÉlémentResponsableFréquence
Versions Proxmox et stockage disponiblesIngénieur DevOpsÀ chaque déploiement
Plan Terraform examiné et approuvéChef d'équipeÀ chaque déploiement
Secrets renouvelés et validesIngénieur sécuritéMensuel
Sauvegarde de la VM existante effectuéeIngénieur DevOpsAvant les modifications de configuration
Script de contrôle de santé mis à jour pour le nouveau serviceDéveloppeurÀ chaque déploiement

Liste de contrôle post-déploiement

ÉlémentResponsableFréquence
Contrôle de santé réussiPipeline CIÀ chaque déploiement
VM en cours d'exécution et IP correcteIngénieur DevOpsÀ chaque déploiement
Les journaux ne montrent pas de boucles de crashDéveloppeurDans l'heure
Alertes de surveillance configuréesSREHebdomadaire
Documentation mise à jour avec la nouvelle versionRédacteur techniqueHebdomadaire

Attribuez un responsable unique pour chaque élément de la liste de contrôle. Le responsable décide quand revoir l'élément et signe l'achèvement. Utilisez un système de tickets (Jira, GitLab issues) pour suivre ces tâches.

Pièges courants et comment les éviter

1. Utiliser le compte root pour l'automatisation

Pourquoi cela arrive : Commodité. Vous devez faire avancer les choses rapidement. Conséquence : Un jeton CI compromis donne un contrôle total de l'hôte. Évitement : Créez un utilisateur dédié avec un jeton API et un rôle minimal (PVEVMAdmin). Renouvelez régulièrement les jetons.

2. Coder en dur les secrets dans Git

Pourquoi cela arrive : Les développeurs veulent que le pipeline fonctionne sans configuration supplémentaire. Conséquence : Les secrets se retrouvent dans l'historique des commits, exposés à toute personne ayant accès au dépôt. Évitement : Utilisez les variables CI/CD, Vault ou des secrets externes. Analysez les dépôts à la recherche de secrets avec git-secrets ou la détection de secrets de GitLab.

3. Ne pas épingler les versions des fournisseurs

Pourquoi cela arrive : Utiliser latest ou omettre la version dans le fournisseur Terraform. Conséquence : Une nouvelle version du fournisseur peut casser votre configuration. Évitement : Épinglez des versions exactes dans required_providers et utilisez un fichier de verrouillage (terraform init génère .terraform.lock.hcl). Validez le fichier de verrouillage.

4. Sauter l'approbation du plan Terraform

Pourquoi cela arrive : Pour accélérer les déploiements, vous définissez toujours auto-approve. Conséquence : Un mauvais changement peut détruire la production sans examen humain. Évitement : Utilisez une étape d'approbation manuelle dans le pipeline (when: manual) avant l'application. Examinez la sortie du plan.

5. Pas de plan de retour arrière

Pourquoi cela arrive : Les équipes supposent que les nouvelles versions fonctionnent toujours. Conséquence : Lorsqu'une mauvaise version sort, la panique et les corrections manuelles provoquent des interruptions. Évitement : Ayez toujours un retour arrière par instantané ou un déploiement de la balise précédente prêt. Testez le retour arrière en staging avant la production.

6. Ignorer la configuration cloud-init

Pourquoi cela arrive : Vous créez une VM à partir d'une image disque sans cloud-init et vous vous attendez à ce que les clés SSH fonctionnent. Conséquence : La VM démarre mais vous ne pouvez pas vous connecter, Ansible échoue. Évitement : Utilisez des images cloud-init et un bloc initialization approprié dans Terraform. Testez l'accès SSH avant de continuer.

7. Ne pas surveiller après le déploiement

Pourquoi cela arrive : Le pipeline passe, tout le monde passe à autre chose. Conséquence : Le service échoue plus tard sans être remarqué. Évitement : Intégrez la surveillance (Prometheus, Grafana) et configurez des alertes. Incluez une étape de vérification qui s'exécute périodiquement, pas seulement une fois.

Conclusion

L'automatisation CI/CD avec Proxmox est un moyen puissant de gérer l'infrastructure comme du code, mais elle exige de la discipline. En suivant les étapes de ce guide — inventaire, configuration sécurisée, pipeline avec Terraform et Ansible, vérification et planification de la récupération — vous pouvez déployer des VM de manière fiable et revenir rapidement en arrière lorsque les choses tournent mal.

Commencez avec une VM à faible risque, mettez en œuvre le pipeline et entraînez-vous à un retour arrière. Ensuite, étendez-vous à plus de VM et à des applications complexes. Gardez votre automatisation versionnée, vos secrets en sécurité et vos contrôles de santé significatifs. Un flux de travail fiable rend les échecs visibles et la récupération une routine, pas une crise.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 100%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO