261 lines
8.7 KiB
YAML
261 lines
8.7 KiB
YAML
---
|
|
# Synology Backup Orchestrator
|
|
# Coordinates backups across Atlantis/Calypso with integrity verification
|
|
# Run with: ansible-playbook -i hosts.ini playbooks/synology_backup_orchestrator.yml --limit synology
|
|
|
|
- name: Synology Backup Orchestration
|
|
hosts: synology
|
|
gather_facts: yes
|
|
vars:
|
|
backup_retention_days: 30
|
|
critical_containers:
|
|
- "postgres"
|
|
- "mariadb"
|
|
- "gitea"
|
|
- "immich-server"
|
|
- "paperlessngx"
|
|
- "authentik-server"
|
|
- "vaultwarden"
|
|
|
|
backup_paths:
|
|
atlantis:
|
|
- "/volume1/docker"
|
|
- "/volume1/media"
|
|
- "/volume1/backups"
|
|
- "/volume1/documents"
|
|
calypso:
|
|
- "/volume1/docker"
|
|
- "/volume1/backups"
|
|
- "/volume1/development"
|
|
|
|
tasks:
|
|
- name: Check Synology system status
|
|
shell: |
|
|
echo "=== System Info ==="
|
|
uname -a
|
|
echo "=== Disk Usage ==="
|
|
df -h
|
|
echo "=== Memory Usage ==="
|
|
free -h
|
|
echo "=== Load Average ==="
|
|
uptime
|
|
register: system_status
|
|
|
|
- name: Display system status
|
|
debug:
|
|
msg: "{{ system_status.stdout_lines }}"
|
|
|
|
- name: Check Docker service status
|
|
shell: systemctl is-active docker
|
|
register: docker_status
|
|
failed_when: false
|
|
|
|
- name: Get running containers
|
|
shell: docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
|
|
register: running_containers
|
|
become: yes
|
|
|
|
- name: Identify critical containers
|
|
shell: docker ps --filter "name={{ item }}" --format "{{.Names}}"
|
|
register: critical_container_check
|
|
loop: "{{ critical_containers }}"
|
|
become: yes
|
|
|
|
- name: Create backup directory structure
|
|
file:
|
|
path: "/volume1/backups/{{ item }}"
|
|
state: directory
|
|
mode: '0755'
|
|
loop:
|
|
- "containers"
|
|
- "databases"
|
|
- "configs"
|
|
- "logs"
|
|
become: yes
|
|
|
|
- name: Stop non-critical containers for backup
|
|
shell: |
|
|
# Get list of running containers excluding critical ones
|
|
critical_pattern="{{ critical_containers | join('|') }}"
|
|
docker ps --format "{{.Names}}" | grep -vE "($critical_pattern)" > /tmp/non_critical_containers.txt || true
|
|
|
|
# Stop non-critical containers
|
|
if [ -s /tmp/non_critical_containers.txt ]; then
|
|
echo "Stopping non-critical containers for backup..."
|
|
cat /tmp/non_critical_containers.txt | xargs -r docker stop
|
|
echo "Stopped containers:"
|
|
cat /tmp/non_critical_containers.txt
|
|
else
|
|
echo "No non-critical containers to stop"
|
|
fi
|
|
register: stopped_containers
|
|
when: stop_containers_for_backup | default(false) | bool
|
|
become: yes
|
|
|
|
- name: Backup Docker volumes
|
|
shell: |
|
|
backup_date=$(date +%Y%m%d_%H%M%S)
|
|
backup_file="/volume1/backups/containers/docker_volumes_${backup_date}.tar.gz"
|
|
|
|
echo "Creating Docker volumes backup: $backup_file"
|
|
tar -czf "$backup_file" -C /volume1/docker . 2>/dev/null || true
|
|
|
|
if [ -f "$backup_file" ]; then
|
|
size=$(du -h "$backup_file" | cut -f1)
|
|
echo "Backup created successfully: $backup_file ($size)"
|
|
else
|
|
echo "Backup failed"
|
|
exit 1
|
|
fi
|
|
register: volume_backup
|
|
become: yes
|
|
|
|
- name: Backup database containers
|
|
shell: |
|
|
backup_date=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Backup PostgreSQL databases
|
|
for container in $(docker ps --filter "ancestor=postgres" --format "{{.Names}}"); do
|
|
echo "Backing up PostgreSQL container: $container"
|
|
docker exec "$container" pg_dumpall -U postgres > "/volume1/backups/databases/${container}_${backup_date}.sql" 2>/dev/null || true
|
|
done
|
|
|
|
# Backup MariaDB databases
|
|
for container in $(docker ps --filter "ancestor=mariadb" --format "{{.Names}}"); do
|
|
echo "Backing up MariaDB container: $container"
|
|
docker exec "$container" mysqldump --all-databases -u root > "/volume1/backups/databases/${container}_${backup_date}.sql" 2>/dev/null || true
|
|
done
|
|
|
|
echo "Database backups completed"
|
|
register: database_backup
|
|
become: yes
|
|
|
|
- name: Backup container configurations
|
|
shell: |
|
|
backup_date=$(date +%Y%m%d_%H%M%S)
|
|
config_backup="/volume1/backups/configs/container_configs_${backup_date}.tar.gz"
|
|
|
|
# Find all docker-compose files and configs
|
|
find /volume1/docker -name "docker-compose.yml" -o -name "*.env" -o -name "config" -type d | \
|
|
tar -czf "$config_backup" -T - 2>/dev/null || true
|
|
|
|
if [ -f "$config_backup" ]; then
|
|
size=$(du -h "$config_backup" | cut -f1)
|
|
echo "Configuration backup created: $config_backup ($size)"
|
|
fi
|
|
register: config_backup
|
|
become: yes
|
|
|
|
- name: Restart stopped containers
|
|
shell: |
|
|
if [ -f /tmp/non_critical_containers.txt ] && [ -s /tmp/non_critical_containers.txt ]; then
|
|
echo "Restarting previously stopped containers..."
|
|
cat /tmp/non_critical_containers.txt | xargs -r docker start
|
|
echo "Restarted containers:"
|
|
cat /tmp/non_critical_containers.txt
|
|
rm -f /tmp/non_critical_containers.txt
|
|
fi
|
|
when: stop_containers_for_backup | default(false) | bool
|
|
become: yes
|
|
|
|
- name: Verify backup integrity
|
|
shell: |
|
|
echo "=== Backup Verification ==="
|
|
|
|
# Check volume backup
|
|
latest_volume_backup=$(ls -t /volume1/backups/containers/docker_volumes_*.tar.gz 2>/dev/null | head -1)
|
|
if [ -n "$latest_volume_backup" ]; then
|
|
echo "Volume backup: $latest_volume_backup"
|
|
tar -tzf "$latest_volume_backup" >/dev/null 2>&1 && echo "✓ Volume backup integrity OK" || echo "✗ Volume backup corrupted"
|
|
fi
|
|
|
|
# Check database backups
|
|
db_backup_count=$(ls /volume1/backups/databases/*.sql 2>/dev/null | wc -l)
|
|
echo "Database backups: $db_backup_count files"
|
|
|
|
# Check config backup
|
|
latest_config_backup=$(ls -t /volume1/backups/configs/container_configs_*.tar.gz 2>/dev/null | head -1)
|
|
if [ -n "$latest_config_backup" ]; then
|
|
echo "Config backup: $latest_config_backup"
|
|
tar -tzf "$latest_config_backup" >/dev/null 2>&1 && echo "✓ Config backup integrity OK" || echo "✗ Config backup corrupted"
|
|
fi
|
|
register: backup_verification
|
|
become: yes
|
|
|
|
- name: Clean old backups
|
|
shell: |
|
|
echo "Cleaning backups older than {{ backup_retention_days }} days..."
|
|
|
|
# Clean volume backups
|
|
find /volume1/backups/containers -name "docker_volumes_*.tar.gz" -mtime +{{ backup_retention_days }} -delete
|
|
|
|
# Clean database backups
|
|
find /volume1/backups/databases -name "*.sql" -mtime +{{ backup_retention_days }} -delete
|
|
|
|
# Clean config backups
|
|
find /volume1/backups/configs -name "container_configs_*.tar.gz" -mtime +{{ backup_retention_days }} -delete
|
|
|
|
echo "Cleanup completed"
|
|
register: backup_cleanup
|
|
become: yes
|
|
|
|
- name: Generate backup report
|
|
copy:
|
|
content: |
|
|
# Synology Backup Report - {{ inventory_hostname }}
|
|
Generated: {{ ansible_date_time.iso8601 }}
|
|
|
|
## System Status
|
|
```
|
|
{{ system_status.stdout }}
|
|
```
|
|
|
|
## Running Containers
|
|
```
|
|
{{ running_containers.stdout }}
|
|
```
|
|
|
|
## Backup Operations
|
|
|
|
### Volume Backup
|
|
```
|
|
{{ volume_backup.stdout }}
|
|
```
|
|
|
|
### Database Backup
|
|
```
|
|
{{ database_backup.stdout }}
|
|
```
|
|
|
|
### Configuration Backup
|
|
```
|
|
{{ config_backup.stdout }}
|
|
```
|
|
|
|
## Backup Verification
|
|
```
|
|
{{ backup_verification.stdout }}
|
|
```
|
|
|
|
## Cleanup Results
|
|
```
|
|
{{ backup_cleanup.stdout }}
|
|
```
|
|
|
|
## Critical Containers Status
|
|
{% for container in critical_containers %}
|
|
- {{ container }}: {{ 'Running' if container in running_containers.stdout else 'Not Found' }}
|
|
{% endfor %}
|
|
dest: "/tmp/synology_backup_{{ inventory_hostname }}_{{ ansible_date_time.epoch }}.md"
|
|
delegate_to: localhost
|
|
|
|
- name: Display backup summary
|
|
debug:
|
|
msg: |
|
|
Backup Summary for {{ inventory_hostname }}:
|
|
- Volume Backup: {{ 'Completed' if volume_backup.rc == 0 else 'Failed' }}
|
|
- Database Backup: {{ 'Completed' if database_backup.rc == 0 else 'Failed' }}
|
|
- Config Backup: {{ 'Completed' if config_backup.rc == 0 else 'Failed' }}
|
|
- Verification: {{ 'Passed' if backup_verification.rc == 0 else 'Failed' }}
|
|
- Report: /tmp/synology_backup_{{ inventory_hostname }}_{{ ansible_date_time.epoch }}.md
|