09
novembre 2021

Sauvegarder le contenu d’un container Docker avec BackupPC

dockerToBackupPC

Dans un environnement de production il est important de réaliser des sauvegardes (en mode dev aussi d’ailleurs). Docker nous apporte une très grande souplesse et facilement nous pouvons proposer différents services. Mais comment faire pour sauvegarder le contenu de ces services ? Pour illustrer cette exemple, j’utiliserais Glpi.

Je part du principe que vous maîtrisez BackupPC, Docker, Docker-compose, Glpi et Traefik. Chaque brique est opérationnel et je détaillerais uniquement ce qui est en lien avec le script.

Pourquoi le contenu ?

Nos container mènes leurs petites vies, dans notre exemple Glpi est utilisé pour maintenir l’inventaire du parc informatique (matériel et financier) avec le plugin Fusion Inventory et aussi pour gérer les tickets du support. Vous vous imaginez bien que toutes ces données change en permanence et qu’il est important de pouvoir les restaurer en cas de crash.

Par défaut Docker propose une solution basique pour « sauvegarder » un container : docker export. Au préalable cela demande de faire un commit et espérer qu’aucun enregistrement sera fait dans la base de données lorsque vous exécuterez l’export. Donc autant dire que c’est peine perdu et de plus un container seul ne sert quasiment à rien sans son Dockerfile ou docker-compose.yml (voir les deux). Autre problème : la volumétrie. A chaque export c’est l’intégralité du container que vous copiez dans un fichier « tar » et il devient difficile de l’intégrer dans le système de déduplication de BackupPC.

La genèse

Pour notre exemple nous utiliserons deux serveurs :

  • svbackup : serveur de sauvegarde où sera installé et paramétré BackupPC;
  • svdocker : serveur docker où sera installé docker, docker-compose, Traefik, mysqldump, rsync.

La communication entre ces serveurs ce fera par ssh avec échange de clés pour ne plus avoir besoin de saisir les mots de passe.

Au début il y avait docker-compose

Pour construire mes container j’utilise docker-compose. Dans BackupPC je créé un job qui sera chargé de copier uniquement les fichiers de configurations et créations de mes containers. Sur svdocker nous les stockerons dans le dossier /home de l’utilisateur backuppc.

Naquirent les containers

Pour Glpi j’utilise deux containers, un premier pour la partie web et un autre pour la partie mysql.

docker-compose.yml

version: '3.3'
services:
  glpi_mysql:
    image: mysql:latest
    container_name: glpi_mysql
    hostname: glpi_mysql
    command: --default-authentication-plugin=mysql_native_password
    env_file:
      - ./mysql.env
    volumes:
      - "/var/lib/mysql/glpi:/var/lib/mysql"
    networks:
      - traefik
    restart: always

  # use a Dockerfile
  glpi_www:
    depends_on:
      - glpi_mysql
    build: .
    container_name: glpi_www
    volumes:
      - "/var/www/hml:/var/www/html"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.glpi_www.rule=Host(`glpi.mondomaine.fr`)"
      - "traefik.http.routers.glpi_www.entrypoints=websecure"
      - "traefik.http.routers.glpi_www.tls.certresolver=le"
    networks:
      - traefik
    restart: always
>

networks:
  traefik:
    external: true

Pour le container glpi_www j’utilise un Dockerfile qui se charge de télécharger l’image php:apache-7.3 et installer les dépendances nécessaires pour faire tourner Glpi. En l’état rien de bien exceptionnel. Vous pouvez voir que j’utilise Traefik pour accéder simplement au container glpi_www. En l’état tout devrait marcher.

Pour permettre à BackupPC de récupérer les informations nécessaire à la sauvegarde nous allons utiliser les labels.

docker-compose.yml

version: '3.3'
services:
  glpi_mysql:
    image: mysql:latest
    container_name: glpi_mysql
    hostname: glpi_mysql
    command: --default-authentication-plugin=mysql_native_password
    env_file:
      - ./mysql.env
    volumes:
      - "/sql/glpi/prod:/var/lib/mysql"
    networks:
      - traefik
    restart: always

  # use a Dockerfile
  glpi_www:
    depends_on:
      - glpi_mysql
    build: .
    container_name: glpi_www
    volumes:
      - "/www/prod/glpi/html:/var/www/html"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.glpi_www.rule=Host(`glpi.mondomaine.fr`)"
      - "traefik.http.routers.glpi_www.entrypoints=websecure"
      - "traefik.http.routers.glpi_www.tls.certresolver=le"
      - "backuppc.active=true"
      - "backuppc.services=volume,"
      - "backuppc.volume.path=/var/www/html"

    networks:
      - traefik
    restart: always

networks:
  traefik:
    external: true

J’ai ajouté trois labels :

  • backuppc.active = true : active ou désactive la sauvegarde [true|false];
  • backuppc.services = volume, : type de services à sauvegarde. Pour le moment il est possible de sauvegarder les fichiers et la base de données Mysql. La virgule à la fin est obligatoire;
  • backuppc.volume.path = /var/www/html : répertoire dans le container à sauvegarder.

Télécharger le fichier backup-container.sh (https://github.com/Philippe-M/dockerToBackuppc) sur le serveur svdocker dans le dossier « home » de l’utilisateur backuppc. Sur le serveur svbackup connectez-vous à l’interface de BackupPC pour créer une machine nommé « docker-glpi_www ». Ce nom vous permettra de retrouver rapidement votre sauvegarde dans la liste des machines enregistré. Il y a une subtilité car ce même nom est utilisé par BackupPC pour contrôler si la machine est accessible sur le réseau. Il va falloir contourner le problème en modifiant les champs ClientNameAlias, PingCmd en remplaçant la variable « $host » par le nom DNS de votre serveur Docker, ici : svdocker

Pour lancer la copie des fichiers du container vers l’hôte j’utilise la fonction « DumpPreUserCmd », pour faire simple cela permet de lancer un programme sur l’hôte avant que BackupPC rapatrie les fichiers. Ici nous allons lancer le script backup-container.sh.

La commande :

$sshPath -x -l backuppc -i /home/backuppc/.ssh/id_rsa svdocker sudo /home/backuppc/./backup-container.sh -d glpi_www

Explication :

  • $sshPath -x -l backuppc -i /home/backuppc/.ssh/id_rsa svdocker : se connecte en ssh à svdocker avec l’utilisateur backuppc et la clé associée;
  • sudo /home/backuppc/./backup-container.sh -d glpi_www : lance le script backup-container.sh avec en paramètre le nom du container à sauvegarder. Par défaut les fichiers du container sont copiés dans le dossier /export/glpi_www de l’hôte donc il faut penser à indiquer à BackupPC où aller chercher les fichiers.

Il reste une dernière étape avant de lancer notre première sauvegarde. Le script copie les fichiers du container vers l’hôte dans le dossier /export/glpi_www, il est donc nécessaire de purger ce dossier après chaque sauvegarde. J’utilise cette fois l’option « DumpPostUserCmd »

Avec la commande
$sshPath -x -l backuppc -i /home/backuppc/.ssh/id_rsa svdocker sudo /home/backuppc/./backup-container.sh -d glpi_www -r

Le fonctionnement est le même que pour la commande de « predump ». A la différence que je passe le paramètre « -r » au script backup-container.sh pour effacer le contenu du dossier « /export/glpi_www ».

Vous pouvez enregistrer le jobs et faire vos premiers essais. Si tout ce passe bien vous pouvez suivre en live sur svdocker le répertoire « /export/glpi_www » se remplir, une fois fini BackupPC va les copier avec rsync et lancer la commande de purge du répertoire « /export/glpi_www ».

Et ma base ?

Avec Mysql l’idée est la même. Nous allons éditer le fichier docker-compose.yml de notre ensemble pour ajouter les labels nécessaire à mysql.

version: '3.3'
services:
  glpi_mysql:
    image: mysql:latest
    container_name: glpi_mysql
    hostname: glpi_mysql
    command: --default-authentication-plugin=mysql_native_password
    env_file:
      - ./mysql.env
    volumes:
      - "/var/lib/mysql/glpi:/var/lib/mysql"
    labels:
      - "traefik.enable=true"
      - "traefik.tcp.routers.glpi_mysql.rule=HostSNI(`*`)"
      - "traefik.tcp.services.glpi_mysql.loadBalancer.server.port=3306"
      - "traefik.tcp.routers.glpi_mysql.entrypoints=mysql"
      - "backuppc.active=true"
      - "backuppc.services=mysql,"
    networks:
      - traefik
    restart: always

  […]

Cette fois backuppc.services contient « mysql » comme valeur mais ce n’est pas plus important et délicat. Pour que cela marche correctement vous devez activer la connexion par TCP à Traefik de façon à pouvoir depuis l’hôte lancer mysqldump. Il aurait été possible de lancer mysqldump à l’intérieur du container mais cela imposait d'installer dans tout les container « mysqldump » avec toutes les dépendances.
Sur le serveur svbackup créer un job docker-glpi_mysql en reprenant les mêmes valeur que pour le précédent job à la différence des paramètres à passer au script backup_container :

$sshPath -x -l backuppc -i /home/backuppc/.ssh/id_rsa svdocker sudo /home/backuppc/./backup-container.sh -d glpi_mysql -u dbuser -p dbpassword -h glpi_mysql.mondomaine.fr -n glpi_prod -c

  • $sshPath -x -l backuppc -i /home/backuppc/.ssh/id_rsa svdocker : connexion à svdocker;
  • sudo /home/backuppc/./backup-container.sh -d glpi_mysql -u dbuser -p dbpassword -h glpi_mysql.mondomaine.fr -n glpi_prod -c : lance le script;
    • -d : nom du container;
    • -h : nom dns par lequel le container est joignable : dans notre exemple c’est « glpi_mysql.mondomaine.fr »;
    • -n : nom de la base de données;
    • -u : nom d’utilisateur mysql;
    • -p : mot de passe mysql;
    • -c : si ajouté compresse le fichier sql.

Il ne vous reste plus qu’à faire un test de sauvegarde.

Tout en un

Si votre container est à la fois le serveur web et le serveur mysql il va falloir modifier votre docker-compose.yml

version: '3.3' services:
  glpi:
    image: mysql:latest
    container_name: glpi
    hostname: glpi
    command: --default-authentication-plugin=mysql_native_password
    env_file:
      - ./mysql.env
    volumes:
      - "/sql/glpi/prod:/var/lib/mysql"
      - "/var/www/html:/var/www/html"
    labels:
      - "traefik.enable=true"
      - "traefik.tcp.routers.glpi_mysql.rule=HostSNI(`*`)"
      - "traefik.tcp.services.glpi_mysql.loadBalancer.server.port=3306"
      - "traefik.tcp.routers.glpi_mysql.entrypoints=mysql"
      - "backuppc.active=true"
      - "backuppc.services=mysql,volume"
      - "backuppc.volume.path=/var/www/html"
    networks:
      - traefik
    restart: always

La commande de PreDump reste la même, il faut juste adapté le nom du container :

$sshPath -x -l backuppc -i /home/backuppc/.ssh/id_rsa svdocker sudo /home/backuppc/./backup-container.sh -d glpi -u dbuser -p dbpassword -c -n glpi_prod

Administrateur système de métier mais surtout curieux de découvrir de nouvelles technos très orientées DIY. A mes heures perdues je fais de la photo avec toujours une petite envie d'intégrer des DIY sous forme de timelaps à base de raspberry.

Écrire un commentaire

Quelle est la dernière lettre du mot ptjc ? :

Sauvegarder le contenu d’un container Docker avec BackupPC - Philippe Maladjian - Péripéties bucoliques d'un administrateur systèmes au royaume de la virtualisation, du stockage et accessoirement photographe à ses heures perdues