From d93dee83f0e1028a08a066727b35791efd97093b Mon Sep 17 00:00:00 2001 From: korenstin Date: Thu, 7 Aug 2025 23:39:04 +0200 Subject: [PATCH] Config backup/prune zfs --- playbooks/zfsprune.yml | 5 + roles/zfs-backup/defaults/main.yml | 2 +- .../{templates => files}/zfs-snapshot-nas | 92 +++++++++---------- roles/zfs-backup/handlers/main.yml | 6 ++ roles/zfs-backup/tasks/main.yml | 15 +-- .../templates/zfs-backup.service.j2 | 4 + .../zfs-backup/templates/zfs-backup.timer.j2 | 2 + roles/zfs-prune/defaults/main.yml | 4 + roles/zfs-prune/files/zfs-remove-old-snapshot | 66 +++++++++++++ roles/zfs-prune/handlers/main.yml | 11 +++ roles/zfs-prune/tasks/main.yml | 23 +++++ .../zfs-prune/templates/zfs-prune.service.j2 | 23 +++++ roles/zfs-prune/templates/zfs-prune.timer.j2 | 11 +++ shell.nix | 10 ++ 14 files changed, 215 insertions(+), 59 deletions(-) create mode 100755 playbooks/zfsprune.yml rename roles/zfs-backup/{templates => files}/zfs-snapshot-nas (59%) create mode 100644 roles/zfs-prune/defaults/main.yml create mode 100755 roles/zfs-prune/files/zfs-remove-old-snapshot create mode 100644 roles/zfs-prune/handlers/main.yml create mode 100644 roles/zfs-prune/tasks/main.yml create mode 100644 roles/zfs-prune/templates/zfs-prune.service.j2 create mode 100644 roles/zfs-prune/templates/zfs-prune.timer.j2 create mode 100644 shell.nix diff --git a/playbooks/zfsprune.yml b/playbooks/zfsprune.yml new file mode 100755 index 0000000..1e3519f --- /dev/null +++ b/playbooks/zfsprune.yml @@ -0,0 +1,5 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: perceval.adm.auro.re + roles: + - zfs-prune diff --git a/roles/zfs-backup/defaults/main.yml b/roles/zfs-backup/defaults/main.yml index 56b0299..0c3fcba 100644 --- a/roles/zfs-backup/defaults/main.yml +++ b/roles/zfs-backup/defaults/main.yml @@ -1,4 +1,4 @@ --- zfs_backup: - - scriptpath: /var/zfs-backup-nas + scriptpath: /var/zfs-backup-nas ... diff --git a/roles/zfs-backup/templates/zfs-snapshot-nas b/roles/zfs-backup/files/zfs-snapshot-nas similarity index 59% rename from roles/zfs-backup/templates/zfs-snapshot-nas rename to roles/zfs-backup/files/zfs-snapshot-nas index e3eeffd..15386f1 100755 --- a/roles/zfs-backup/templates/zfs-snapshot-nas +++ b/roles/zfs-backup/files/zfs-snapshot-nas @@ -21,7 +21,7 @@ TODAY=$(date "+%Y-%m-%d") TODAY_EPOCH=$(date -d "${TODAY}" +%s) # # 1. On fait les snapshots sur le ZFS du NAS. -# /sbin/zfs snapshot "tank/${TANK}@${TODAY}" +/sbin/zfs snapshot "tank/${TANK}@${TODAY}" # 2. On envoie les snapshots sur le ZFS de backup. @@ -76,48 +76,48 @@ do done -# # 3. On ne garde que les 15 dernières snapshots ZFS -# LIMIT=$(date -d "${TODAY} -15days" "+%Y-%m-%d") -# LIMIT_EPOCH=$(date -d "${LIMIT}" "+%s") -# -# while true -# do -# -# # On vérifie qu'il existe au moins 15 backups -# COUNT=$(zfs list -t snapshot \ -# | grep -c "tank/${TANK}") -# -# if [ "${COUNT}" -le 15 ] -# then -# echo "Il y a moins de 16 backups. Fin." -# break -# fi -# -# -# # On récupère la plus vieille snapshot -# OLDEST_SNAPSHOT=$(zfs list -t snapshot \ -# | grep "tank/${TANK}" \ -# | cut -d' ' -f1 | cut -d'@' -f2 \ -# | sort | head -n1) -# -# OLDEST_SNAPSHOT_EPOCH=$(date -d "${OLDEST_SNAPSHOT}" "+%s") -# -# # Sanity-check: Si la plus vieille backup est celle d'il y a moins de 15 jours: On sort. -# if [ "${OLDEST_SNAPSHOT_EPOCH}" -ge "${LIMIT_EPOCH}" ] -# then -# echo "La backup locale ${TANK} ${OLDEST_SNAPSHOT} est suffisament récente. Fin." -# break -# fi -# -# # Sinon, on supprime la plus vieille snapshot. -# echo "Suppression de la backup ${TANK} ${OLDEST_SNAPSHOT}." -# -# zfs destroy "tank/${TANK}@${OLDEST_SNAPSHOT}" -# -# if [ $? -ne 0 ] -# then -# echo "La suppression s'est mal déroulée. Arrêt." -# exit 1 -# fi -# -# done +# 3. On ne garde que les 15 dernières snapshots ZFS +LIMIT=$(date -d "${TODAY} -15days" "+%Y-%m-%d") +LIMIT_EPOCH=$(date -d "${LIMIT}" "+%s") + +while true +do + + # On vérifie qu'il existe au moins 15 backups + COUNT=$(zfs list -t snapshot \ + | grep -c "tank/${TANK}") + + if [ "${COUNT}" -le 15 ] + then + echo "Il y a moins de 16 backups. Fin." + break + fi + + + # On récupère la plus vieille snapshot + OLDEST_SNAPSHOT=$(zfs list -t snapshot \ + | grep "tank/${TANK}" \ + | cut -d' ' -f1 | cut -d'@' -f2 \ + | sort | head -n1) + + OLDEST_SNAPSHOT_EPOCH=$(date -d "${OLDEST_SNAPSHOT}" "+%s") + + # Sanity-check: Si la plus vieille backup est celle d'il y a moins de 15 jours: On sort. + if [ "${OLDEST_SNAPSHOT_EPOCH}" -ge "${LIMIT_EPOCH}" ] + then + echo "La backup locale ${TANK} ${OLDEST_SNAPSHOT} est suffisament récente. Fin." + break + fi + + # Sinon, on supprime la plus vieille snapshot. + echo "Suppression de la backup ${TANK} ${OLDEST_SNAPSHOT}." + + zfs destroy "tank/${TANK}@${OLDEST_SNAPSHOT}" + + if [ $? -ne 0 ] + then + echo "La suppression s'est mal déroulée. Arrêt." + exit 1 + fi + +done diff --git a/roles/zfs-backup/handlers/main.yml b/roles/zfs-backup/handlers/main.yml index 60f493a..7e0fd5a 100644 --- a/roles/zfs-backup/handlers/main.yml +++ b/roles/zfs-backup/handlers/main.yml @@ -2,4 +2,10 @@ - name: Run systemd daemon-reload systemd: daemon_reload: true + +- name: Restart and enable ZFS-backup timer + systemd: + name: zfs-backup.timer + state: restarted + enabled: true ... diff --git a/roles/zfs-backup/tasks/main.yml b/roles/zfs-backup/tasks/main.yml index 0d27be0..ce06e4f 100644 --- a/roles/zfs-backup/tasks/main.yml +++ b/roles/zfs-backup/tasks/main.yml @@ -11,22 +11,13 @@ - zfs-backup.timer notify: - Run systemd daemon-reload + - Restart and enable ZFS-backup timer - name: Copie du script - template: - src: zfs-snapshot-nas + copy: + src: files/zfs-snapshot-nas dest: "{{ zfs_backup.scriptpath }}" owner: root group: root mode: u=rx,g=r,o= - -- name: Run systemd deamon-reload - systemd: - daemon_reload: true - -- name: Start and enable ZFS-backup timer - systemd: - name: zfs-backup.timer - state: started - enabled: true ... diff --git a/roles/zfs-backup/templates/zfs-backup.service.j2 b/roles/zfs-backup/templates/zfs-backup.service.j2 index 5bfacdf..cc25d10 100644 --- a/roles/zfs-backup/templates/zfs-backup.service.j2 +++ b/roles/zfs-backup/templates/zfs-backup.service.j2 @@ -1,3 +1,5 @@ +{{ ansible_managed | comment }} + [Unit] Description=Service pour ZFS-backup Wants=network-online.target @@ -7,6 +9,8 @@ After=network-online.target Type=simple User=root +TimeoutStartSec = 7200 + Nice=19 CPUSchedulingPolicy=batch diff --git a/roles/zfs-backup/templates/zfs-backup.timer.j2 b/roles/zfs-backup/templates/zfs-backup.timer.j2 index abb0a1a..255ee3f 100644 --- a/roles/zfs-backup/templates/zfs-backup.timer.j2 +++ b/roles/zfs-backup/templates/zfs-backup.timer.j2 @@ -1,3 +1,5 @@ +{{ ansible_managed | comment }} + [Unit] Description=Timer for ZFS-backup diff --git a/roles/zfs-prune/defaults/main.yml b/roles/zfs-prune/defaults/main.yml new file mode 100644 index 0000000..50b8d41 --- /dev/null +++ b/roles/zfs-prune/defaults/main.yml @@ -0,0 +1,4 @@ +--- +zfs_prune: + scriptpath: /var/zfs-prune-snapshot +... diff --git a/roles/zfs-prune/files/zfs-remove-old-snapshot b/roles/zfs-prune/files/zfs-remove-old-snapshot new file mode 100755 index 0000000..b11c443 --- /dev/null +++ b/roles/zfs-prune/files/zfs-remove-old-snapshot @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +if [ $# -ne 1 ] +then + echo "USAGE: $0 " + exit 1 +fi + +TANK="$1" + +IS_TANK_EXIST=$(zfs list | grep -c "tank/${TANK} ") + +if [ "${IS_TANK_EXIST}" -ne 1 ] +then + echo "${TANK} n'existe pas. Arrêt." + exit 1 +fi + + +TODAY=$(date "+%Y-%m-%d") + +# On ne garde que les 365 dernières snapshots ZFS +LIMIT=$(date -d "${TODAY} -1year" "+%Y-%m-%d") +LIMIT_EPOCH=$(date -d "${LIMIT}" "+%s") + +while true +do + + # On vérifie qu'il existe au moins 365 backups + COUNT=$(zfs list -t snapshot \ + | grep -c "tank/${TANK}") + + if [ "${COUNT}" -le 365 ] + then + echo "Il y a moins de 366 backups. Fin." + break + fi + + # On récupère la plus vieille snapshot + OLDEST_SNAPSHOT=$(zfs list -t snapshot \ + | grep "tank/${TANK}" \ + | cut -d' ' -f1 | cut -d'@' -f2 \ + | sort | head -n1) + + OLDEST_SNAPSHOT_EPOCH=$(date -d "${OLDEST_SNAPSHOT}" "+%s") + + # Sanity-check: Si la plus vieille backup est celle d'il y a moins d'un an: On sort. + if [ "${OLDEST_SNAPSHOT_EPOCH}" -ge "${LIMIT_EPOCH}" ] + then + echo "La backup locale ${TANK} ${OLDEST_SNAPSHOT} est suffisament récente. Fin." + break + fi + + # Sinon, on supprime la plus vieille snapshot. + echo "Suppression de la backup ${TANK} ${OLDEST_SNAPSHOT}." + sleep 2 + + zfs destroy "tank/${TANK}@${OLDEST_SNAPSHOT}" + + if [ $? -ne 0 ] + then + echo "La suppression s'est mal déroulée. Arrêt." + exit 1 + fi + +done diff --git a/roles/zfs-prune/handlers/main.yml b/roles/zfs-prune/handlers/main.yml new file mode 100644 index 0000000..e3c0834 --- /dev/null +++ b/roles/zfs-prune/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: Run systemd daemon-reload + systemd: + daemon_reload: true + +- name: Restart and enable ZFS-prune timer + systemd: + name: zfs-prune.timer + state: restarted + enabled: true +... diff --git a/roles/zfs-prune/tasks/main.yml b/roles/zfs-prune/tasks/main.yml new file mode 100644 index 0000000..eab8ff9 --- /dev/null +++ b/roles/zfs-prune/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Copy files for zfs-prune + template: + src: "{{ item }}.j2" + dest: /etc/systemd/system/{{ item }} + owner: root + group: root + mode: u=rw,g=r,o= + loop: + - zfs-prune.service + - zfs-prune.timer + notify: + - Run systemd daemon-reload + - Restart and enable ZFS-prune timer + +- name: Copie du script + copy: + src: files/zfs-remove-old-snapshot + dest: "{{ zfs_prune.scriptpath }}" + owner: root + group: root + mode: u=rx,g=r,o= +... diff --git a/roles/zfs-prune/templates/zfs-prune.service.j2 b/roles/zfs-prune/templates/zfs-prune.service.j2 new file mode 100644 index 0000000..acb9def --- /dev/null +++ b/roles/zfs-prune/templates/zfs-prune.service.j2 @@ -0,0 +1,23 @@ +{{ ansible_managed | comment }} + +[Unit] +Description=Service pour ZFS-backup +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +User=root + +TimeoutStartSec = 7200 + +Nice=19 +CPUSchedulingPolicy=batch + +Restart=no + +LogRateLimitIntervalSec=0 + +ExecStartPre=/usr/bin/bash {{ zfs_prune.scriptpath }} nextcloud_backup +ExecStart=/usr/bin/bash {{ zfs_prune.scriptpath }} gitea_backup + diff --git a/roles/zfs-prune/templates/zfs-prune.timer.j2 b/roles/zfs-prune/templates/zfs-prune.timer.j2 new file mode 100644 index 0000000..d2c8698 --- /dev/null +++ b/roles/zfs-prune/templates/zfs-prune.timer.j2 @@ -0,0 +1,11 @@ +{{ ansible_managed | comment }} + +[Unit] +Description=Timer for ZFS-prune + +[Timer] +OnCalendar=daily + +[Install] +WantedBy=timers.target + diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..83a7687 --- /dev/null +++ b/shell.nix @@ -0,0 +1,10 @@ +{ pkgs ? import {} }: + pkgs.mkShell { + buildInputs = with pkgs.buildPackages; [ + ansible_2_16 + python313Packages.jinja2 + python313Packages.requests + python313Packages.pysocks + ]; + LANG="C.UTF-8"; +}