Backup de volumes docker avec Borg et Ansible

Introduction

Je possède 2 serveurs dédiés que j’utilisent en tant que swarm pour héberger quelques applications web fonctionnant sous docker. Un des défis que ce genre de setup occasionne est comment sauvegarder les données en toute sécurité (sans poser du problèmes à l’application en cours d’utilisation) et avec simplicité.

Cette article montre la solution que j’ai mise en place en utilisant Borg Backup, cron et Ansible.

Prérequis

Il existe une multitude de solutions de backups. En ce qui me concerne je souhaitais :

  • Trouver des outils open-source uniquement
  • Chiffrer mes backups
  • Une solution supportant la déduplication (cela permet d’économiser de l’espace disque)
  • Effectuer des opérations avant la sauvegarde (effectuer des dumps des base de données avant l’envoi su run serveur distant)
  • Fonctionner dans un conteneur en tant que service d’une stack docker-compose. De cette manière, exécuter docker-compose down supprime à la fois l’application du serveur, mais également le service de backup. Cela me permet 2 choses :
    • Il n’est pas recommandé d’interagir avec les volumes directement depuis le système de fichier de la machine hôte. Ces recommandations sont bien expliquées sur la documentation docker
    • Je préfère éviter autant que possible d’installer trop d’applications différentes sur mon serveur. Cela me permet de réduire les mise à jour et garder un minimum de dépendances. De plus tout ré-installer à partir de zéro devient extrêmement simple

Après quelques recherches la solution de backup Borg est sortie du lot. Les 3 premiers points de ma liste sont couverts et - encore mieux - bien plus que cela. Jetez un oeil aux fonctionnalités de BorgBackup

Pour le 4ème point, j’ai décidé d’utiliser Ansible pour exécuter le backup ainsi que des opérations au préalable. Je trouve Ansible facile d’utilisation et plus facile à maintenir que des scripts bash. Pour le reste, j’ai écris moi-même les Dockerfile nécessaires pour exécuter les playbooks périodiquement.

Quoi de mieux quun exemple ?

Dans ce tutoriel je vais montrer comment périodiquement sauvegarder l’application Nextcloud avec docker. Pour rendre cela simple à essayer, je vais fournir les instructions pour que tout se passe sur une seule machine en local. Si vous avez déjà une stack docker avec des données à sauvegarder c’est encore mieux. Auquel cas, passez directement a cette section. Vous pouvez appliquer les mêmes instructions que ci-dessous en modifiant les IPs et les clés SSH de la machine sur laquelle vous voulez faire la sauvegarde.

Voici une image de ce que sera couvert :

Schéma architecture

Tous les fichiers de l’installation complète et condensée est disponible sur ce repository github

La stack nextcloud

Créez un fichier docker-compose.yml avec le contenu suivant:

version: '3.7'

volumes:
  mysql:
  data:

services:

  mysql:
    image: mysql:5.7.24
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_USER: nextcloud
      MYSQL_DATABASE: nextcloud
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - mysql:/var/lib/mysql
    secrets:
      - db_root_password
      - db_password

  php:
    image: nextcloud:15.0.5-fpm-alpine
    volumes:
      - data:/var/www/html
    depends_on:
      - mysql

  nginx:
    image: nginx:1.15.8-alpine
    ports:
      - 8042:80
    volumes:
      - data:/var/www/html
      - ./nextcloud-nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - php

secrets:
  db_password:
    file: secret_db_password.txt
  db_root_password:
    file: secret_db_root_password.txt

Créez un fichier nommé nextcloud-nginx.conf avec ce contenu

Puis exécutez ces commandes (modifiez les mots de passe) :

echo "KilmpodG65" > secret_db_password.txt
echo "F4gTYjukI" > secret_db_root_password.txt
docker-compose up -d

Attendez que le conteneur mysql soit initialisé. La commande suivante permet de voir si le conteneur est prêt :

docker-compose logs -f mysql

Si vous voyez “MySQL init process done. Ready for start up” vous pouvez continuer.

Ouvrez la page http://localhost:8042/ et remplissez le formulaire. Cliquez sur finish setup et… soyez patient. Nextcloud devrait fonctionner d’ici quelques minutes.

Formulaire de configuration de Nextcloud

Préparer le serveur de backup

Il est recommandé (mais pas obligatoire) d’avoir Borg d’installé sur la machine de backup. Suivez ce guide à ce sujet.

Pour cet exemple je vais simplement l’installer localement. Sur mon laptop fonctionnant sous ubuntu bionic, en voici les commandes :

sudo apt install borgbackup
borg --version

Le conteneur qui sera chargé d’envoyer des données a besoin d’un accès SSH au serveur de backup. Créons une nouvelle paire de clés SSH sur la machine hébergeant Nextcloud. Celle-ci sera sans passphrase car elle sera utilisée dans un cron.

ssh-keygen -f ~/.ssh/id_rsa_borg_backup -t rsa -N ''

Puis - sur le serveur de backup - autorisons la clé.

touch ~/.ssh/authorized_keys
chmod g-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Copiez le contenu de la clé publique à la fin du fichier ~/.ssh/authorized_keys.

Si tout est en local, c’est très simple :

cat ~/.ssh/id_rsa_borg_backup.pub >> ~/.ssh/authorized_keys

Évidemment il vous faudra un serveur SSH fonctionnant sur le serveur de backup.

sudo apt install openssh-server

Ajouter le service borg backup

Modifiez le fichier docker-compose.yaml :

services:

  ...

  backup_cron:
    image: ovski/borgbackup-cron:v1.0.1
    volumes:
      - data:/var/docker_volumes/nextcloud/app/data
    environment:
      SSH_CONNECTION: backup_user@your.server.net
      PRIVATE_KEY_PATH: /run/secrets/backup_server_user_private_key
      BORG_REPO_PATH: /home/backup_user/borg_repositories
      BORG_REPO_NAME: nextcloud
      LOCAL_FOLDER: /var/docker_volumes/nextcloud
      MYSQL_USER: nextcloud
      MYSQL_DATABASE: nextcloud
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
      SSH_KNOWN_HOSTS: your.server.net,38.26.55.241
    secrets:
      - backup_server_user_private_key
      - borg_passphrase
      - db_password

secrets:

  ...

  backup_server_user_private_key:
    file: secret_backup_server_user_private_key.txt
  borg_passphrase:
    file: secret_borg_passphrase.txt

Il y a quelque modifications à effectuer :

  • Mettre à jour la variable d’environnement SSH_CONNECTION.

Utilisez l’utilisateur de votre choix avec lequel le conteneur de backup se connectera en SSH pour envoyer les données. Mettez à jour l’IP du serveur ou le nom de domaine.

  • Mettre à jour la variable d’environnement SSH_KNOWN_HOSTS avec l’IP du serveur de backup et/ou le nom de domaine.

Sur mon laptop ubuntu, exécuter hostname -I | awk '{print $2}' en ligne de commande affiche l’IP qui peut être atteindre depuis les conteneurs. Vous pouvez de manière plus générale récupérer cette information en tapant ip a et trouver l’interface réseau docker0.

  • Mettre à jour la variable d’environnement BORG_REPO_PATH. Il s’agit du chemin dans lequel les backups seront placés sur votre serveur. Ce dossier doit exister.

  • Si vous utilisez votre propre stack qui ne contient pas de service mysql, alors vous pouvez vous débarasser des variables d’environnement MYSQL

  • Enfin, on créer les fichier secret_backup_server_user_private_key.txt et secret_borg_passphrase.txt:

cat ~/.ssh/id_rsa_borg_backup > secret_backup_server_user_private_key.txt
echo "mysuperpassphrasefortheborgnextcloudrepo" > secret_borg_passphrase.txt
chmod 400 secret_backup_server_user_private_key.txt

Nous devons exécuter la commande chmod car le mot clé “mode” n’est pas utilisable en dehors d’un swarm pour forcer les permissions sur les fichiers.

Voici ce que j’obtiens en local:

services:

  ...

  backup_cron:
    image: ovski/borgbackup-cron:v1.0.1
    volumes:
      - data:/var/docker_volumes/nextcloud/app/data
    environment:
      SSH_CONNECTION: baptiste@172.17.0.1
      PRIVATE_KEY_PATH: /run/secrets/backup_server_user_private_key
      BORG_REPO_PATH: /home/baptiste/borg_repositories
      BORG_REPO_NAME: nextcloud
      LOCAL_FOLDER: /var/docker_volumes/nextcloud
      MYSQL_USER: nextcloud
      MYSQL_DATABASE: nextcloud
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
      SSH_KNOWN_HOSTS: 172.17.0.1
    secrets:
      - backup_server_user_private_key
      - borg_passphrase
      - db_password

secrets:

  ...

  backup_server_user_private_key:
    file: ./secret_backup_server_user_private_key.txt
  borg_passphrase:
    file: ./secret_borg_passphrase.txt

Le moment de verité

Lancez docker-compose up -d suivi de docker-compose logs -f backup_cron. Vous devriez voir la ligne suivante toutes les 5 minutes :

backup_cron_1  | === I'm alive ===

Cela signifie que le cron fonctionne correctement!

Ok, c’est déjà bien. Malheureusement le cron est configuré pour exécuter les playbooks compris dans l’image docker tous les jours à 1h du matin. Pour vérifier que le backup fonctionne, éditez le fichier docker-compose.yml une nouvelle fois en surchargeant la commande par défaut comme ceci :

services:

  ...

  backup_cron:
    image: ovski/borgbackup-cron:v1.0.1
    ...
    command: /var/backup_script.sh

Lancez une dernière fois docker-compose up -d suivi de docker-compose logs -f backup_cron. Attendez que le script se termine. Si tout s’est bien passé, un backup a été créé. Vérifions cela:

cd /home/your_user/borg_repositories
borg list nextcloud

Entrez la passphrase (mysuperpassphrasefortheborgnextcloudrepo). Vous devriez avec une liste de backups! Si vous n’êtes pas encore familier avec Borg, dirigez-vous vers la documentation.

Si cet article vous a rendu service, n’hésitez pas à starrer ce repo en guise de remerciement ! ⭐