taxmachine.pl

Kopie MariaDB / MySQL poza TaxMachine

Opublikowano

TaxMachine ma wbudowany mechanizm kopii który działa zarówno dla SQLite, jak i dla MariaDB/MySQL. Wystarcza dla większości użytkowników. Ten artykuł opisuje dodatkowe, niezależne od programu sposoby tworzenia kopii bazy MariaDB — przydatne gdy:

  • prowadzisz biuro rachunkowe i chcesz centralnie zarządzać backupami wielu baz / firm
  • masz dedykowany serwer MariaDB obsługujący wiele aplikacji
  • chcesz mieć kopię w infrastrukturze niezależnej od TaxMachine (np. nocny snapshot serwera + replikacja off-site)
  • chcesz natywnej integracji z politykami backup-owymi firmy (Veeam, Bacula, Proxmox Backup, S3, B2)

Dwa rodzaje kopii MariaDB

1. Kopia logiczna — mysqldump

Eksport struktury i danych w formie pliku SQL (CREATE TABLE + INSERT INTO). Plusy:

  • Tekstowy format — czytelny, łatwo edytować przed restore'em
  • Niezależny od wersji MariaDB (kopia z 10.6 wczyta się do 11.4)
  • Niezależny od architektury (Windows ↔ Linux, x86 ↔ ARM)
  • Możliwość wybrania konkretnych tabel
  • Łatwe przesłanie / rozmieszczenie

Minusy:

  • Wolniejszy przy bardzo dużych bazach (>10 GB)
  • Restore odbudowuje indeksy od zera

To domyślna i zalecana metoda dla typowych instalacji TaxMachine (baza zwykle <1 GB).

2. Kopia fizyczna — mariabackup

Snapshot plików InnoDB na poziomie binarnym. Plusy:

  • Bardzo szybka odtwarzalność
  • Niska narzutka na działający serwer (online backup)
  • Inkrementalność (kolejna kopia tylko zmiany)

Minusy:

  • Wymaga tej samej (lub nowszej) wersji serwera przy restore
  • Format binarny, większe pliki
  • Mniej elastyczna manipulacja zawartością

Sensowna gdy baza jest bardzo duża (>10 GB) lub wymaga RPO (Recovery Point Objective) w minutach.

W tym artykule pokrywamy mysqldump (95% przypadków). Jeżeli potrzebujesz mariabackup — patrz oficjalna dokumentacja: mariadb.com/kb/en/mariabackup-overview/.

Przygotowanie

Konto z minimalnymi uprawnieniami do backup-u

Nie używaj root — utwórz dedykowane konto z prawami tylko do odczytu struktur i lockowania:

CREATE USER 'backup'@'localhost' IDENTIFIED BY 'silne-haslo-backup';

GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES, RELOAD,
      PROCESS, REPLICATION CLIENT
  ON *.*
  TO 'backup'@'localhost';

FLUSH PRIVILEGES;

Wykonaj w HeidiSQL albo mysql -u root -p.

Plik z hasłem (zamiast w command-line)

Hasło w command-line trafia do logów / historii shella. Zamiast tego trzymaj je w pliku zabezpieczonym ACL-ami:

Windows: plik C:\Tools\backup\.my.cnf:

[client]
user=backup
password=silne-haslo-backup
host=localhost

Ogranicz prawa pliku — tylko Twoje konto i SYSTEM:

icacls C:\Tools\backup\.my.cnf /inheritance:r /grant:r "%USERNAME%:(R)" "SYSTEM:(R)"

Linux: ~/.my.cnf, prawa 600:

chmod 600 ~/.my.cnf

W komendach poniżej używamy --defaults-extra-file=.my.cnfmysqldump czyta hasło z pliku.

Skrypt backup — Windows (.cmd)

C:\Tools\backup\backup-taxmachine.cmd:

@echo off
setlocal enabledelayedexpansion

REM ---- konfiguracja --------------------------------------------------------
set BACKUP_DIR=D:\Backup\TaxMachine
set DB_NAME=taxmachine
set MYSQLDUMP="C:\Program Files\MariaDB 11.4\bin\mysqldump.exe"
set DEFAULTS=C:\Tools\backup\.my.cnf
set RETENTION_DAYS=30
set LOG=%BACKUP_DIR%\backup.log

REM ---- przygotowanie -------------------------------------------------------
if not exist "%BACKUP_DIR%" mkdir "%BACKUP_DIR%"
for /f "tokens=2 delims==" %%I in ('wmic os get LocalDateTime /value ^| find "="') do set DT=%%I
set TS=%DT:~0,8%-%DT:~8,4%

REM ---- mysqldump -----------------------------------------------------------
set OUT=%BACKUP_DIR%\%DB_NAME%-%TS%.sql.gz
echo [%date% %time%] start backup %OUT% >> "%LOG%"
%MYSQLDUMP% --defaults-extra-file=%DEFAULTS% ^
  --single-transaction --quick --routines --triggers --events ^
  --default-character-set=utf8mb4 ^
  %DB_NAME% | "C:\Program Files\7-Zip\7z.exe" a -tgzip -si "%OUT%" >> "%LOG%" 2>&1

if errorlevel 1 (
  echo [%date% %time%] BLAD podczas backup-u >> "%LOG%"
  exit /b 1
)

echo [%date% %time%] OK rozmiar: >> "%LOG%"
for %%F in ("%OUT%") do echo   %%~zF bajt >> "%LOG%"

REM ---- retencja: usun starsze niz RETENTION_DAYS dni ----------------------
forfiles /p "%BACKUP_DIR%" /m "%DB_NAME%-*.sql.gz" /d -%RETENTION_DAYS% /c "cmd /c del @path" 2>nul
echo [%date% %time%] retencja: %RETENTION_DAYS% dni >> "%LOG%"
echo. >> "%LOG%"

endlocal

Wymaga zainstalowanego 7-Zip (C:\Program Files\7-Zip\7z.exe) do kompresji. Jeśli nie chcesz kompresować — wytnij pipe-a do 7z.exe i zmień OUT na .sql zamiast .sql.gz.

Harmonogram (Task Scheduler)

$action = New-ScheduledTaskAction -Execute "C:\Tools\backup\backup-taxmachine.cmd"
$trigger = New-ScheduledTaskTrigger -Daily -At 2:30am
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
Register-ScheduledTask -TaskName "TaxMachine Backup" `
  -Action $action -Trigger $trigger -Principal $principal `
  -Description "Codzienna kopia bazy taxmachine, 02:30"

Sprawdzanie:

Get-ScheduledTaskInfo -TaskName "TaxMachine Backup"

Skrypt backup — Linux (.sh)

/usr/local/bin/backup-taxmachine.sh:

#!/usr/bin/env bash
set -euo pipefail

# ---- konfiguracja ---------------------------------------------------------
BACKUP_DIR="/var/backups/taxmachine"
DB_NAME="taxmachine"
DEFAULTS="/root/.my.cnf"
RETENTION_DAYS=30
LOG="$BACKUP_DIR/backup.log"

# ---- przygotowanie --------------------------------------------------------
mkdir -p "$BACKUP_DIR"
TS=$(date +%Y%m%d-%H%M)
OUT="$BACKUP_DIR/$DB_NAME-$TS.sql.gz"

echo "[$(date '+%F %T')] start backup $OUT" >> "$LOG"

# ---- mysqldump ------------------------------------------------------------
mysqldump --defaults-extra-file="$DEFAULTS" \
  --single-transaction --quick --routines --triggers --events \
  --default-character-set=utf8mb4 \
  "$DB_NAME" | gzip -9 > "$OUT"

if [[ ! -s "$OUT" ]]; then
  echo "[$(date '+%F %T')] BLAD: pusty plik" >> "$LOG"
  exit 1
fi

echo "[$(date '+%F %T')] OK rozmiar: $(stat -c %s "$OUT") B" >> "$LOG"

# ---- retencja -------------------------------------------------------------
find "$BACKUP_DIR" -name "$DB_NAME-*.sql.gz" -mtime +"$RETENTION_DAYS" -delete
echo "[$(date '+%F %T')] retencja: $RETENTION_DAYS dni" >> "$LOG"
echo "" >> "$LOG"

Pamiętaj o chmod +x /usr/local/bin/backup-taxmachine.sh.

Harmonogram (cron)

sudo crontab -e

Dodaj wiersz (codziennie 02:30):

30 2 * * *  /usr/local/bin/backup-taxmachine.sh

Albo jako systemd timer dla precyzyjnego zarządzania logami:

# /etc/systemd/system/backup-taxmachine.service
[Unit]
Description=TaxMachine MariaDB backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-taxmachine.sh

# /etc/systemd/system/backup-taxmachine.timer
[Unit]
Description=TaxMachine MariaDB backup — codziennie 02:30

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl enable --now backup-taxmachine.timer
sudo systemctl status backup-taxmachine.timer
journalctl -u backup-taxmachine.service -n 50

Szyfrowanie kopii

Wszystkie kopie wychodzące poza serwer powinny być zaszyfrowane — dane księgowe zawierają NIP-y, numery kont, adresy klientów.

gpg (Linux + Windows z gpg4win)

Jednorazowa generacja klucza:

gpg --gen-key

Szyfrowanie kopii (asymetryczne, hasło w kluczu):

gpg --encrypt --recipient backup@firma.pl < taxmachine.sql.gz > taxmachine.sql.gz.gpg

Deszyfrowanie (wymaga klucza prywatnego):

gpg --decrypt taxmachine.sql.gz.gpg > taxmachine.sql.gz

openssl (jeśli wolisz hasło symetryczne)

openssl enc -aes-256-cbc -salt -pbkdf2 -in taxmachine.sql.gz \
  -out taxmachine.sql.gz.enc -pass file:/root/backup-passphrase.txt

Najważniejsze: klucz prywatny GPG / passphrase do openssl trzymaj poza serwerem na którym jest baza. Awaria serwera + brak dostępu do klucza = utrata kopii.

Off-site upload — rclone

rclone (rclone.org) wgrywa do dowolnej chmury (S3, Backblaze B2, Wasabi, Dropbox, OneDrive, Mega, FTP, SFTP).

Konfiguracja jednorazowa:

rclone config
# wybierz n (new remote), nazwa: b2, typ: Backblaze B2 / S3 / cokolwiek
# wpisz klucze API

Upload do bucketa:

rclone copy /var/backups/taxmachine b2:taxmachine-backups/$(hostname)/ \
  --include "taxmachine-*.sql.gz" --transfers 4 --checkers 8

Dorzuć w skrypcie backup po find ... -delete. Można też skonfigurować rclone sync z lokalnym retention by mirror działał wstecznie.

Backblaze B2 — typowy koszt

Dla porównania — 10 GB kopii × 30 dni × ~2 zł / GB / miesiąc = ~5-6 zł / miesiąc za off-site backup biura rachunkowego (stan 2026-05). Najtańsza enterprise-grade chmura w Polsce / Europie.

Restore — odtwarzanie z kopii

1. Plik .sql (logiczny mysqldump)

# Linux
gunzip < taxmachine-20260504-0230.sql.gz | mysql -u root -p taxmachine_recovered

# Windows (PowerShell, wymaga 7-Zip):
& "C:\Program Files\7-Zip\7z.exe" x -so taxmachine-20260504-0230.sql.gz | `
  & "C:\Program Files\MariaDB 11.4\bin\mysql.exe" -u root -p taxmachine_recovered

Tworzymy nową, pustą bazę (taxmachine_recovered) zamiast nadpisywania działającej:

CREATE DATABASE taxmachine_recovered CHARACTER SET utf8mb4 COLLATE utf8mb4_polish_ci;

Po weryfikacji że dane są kompletne, możemy w TaxMachine wskazać nową bazę w opcjach i ewentualnie zrzucić starą.

2. Plik zaszyfrowany (gpg)

gpg --decrypt taxmachine.sql.gz.gpg | gunzip | mysql -u root -p taxmachine_recovered

3. Test po restore

USE taxmachine_recovered;

-- czy są tabele?
SHOW TABLES;

-- czy dane?
SELECT COUNT(*) FROM Faktury;
SELECT MAX(Data) FROM Dokumenty;

-- integralność?
CHECK TABLE Faktury;

Strategia 3-2-1 z mysqldump

Przykładowa konfiguracja zgodna z zasadą 3-2-1:

CoJakGdzie
Oryginałlive MariaDBdysk SSD serwera
Kopia 1mysqldump co dzień 02:30dysk wewnętrzny inny niż produkcyjny
Kopia 2rclone copy → B2 po backup-ieBackblaze B2 (off-site)

Cron / Task Scheduler:

30 2 * * *  /usr/local/bin/backup-taxmachine.sh && \
            rclone copy /var/backups/taxmachine \
              b2:taxmachine-backups/$(hostname)/ \
              --include "taxmachine-*.sql.gz"

Monitoring i alerty

Backup który cicho zawodzi przez tydzień to gorzej niż żaden — masz złudzenie ochrony. Dorzuć monitoring:

Healthchecks.io (najszybsze, darmowe do 20 checków)

  1. Załóż konto, utwórz Check „TaxMachine backup", interwał 24h, grace 1h
  2. Skopiuj UUID
  3. W skrypcie backup, na końcu:
    curl -fsS --retry 3 "https://hc-ping.com/<UUID>"
    
  4. Gdy backup nie wykona się dobę — Healthchecks wysyła email/SMS

Powiadomienia w Healthchecks

  • start ping przed backup-em (hc-ping.com/<UUID>/start)
  • success po sukcesie (hc-ping.com/<UUID>)
  • fail po błędzie (hc-ping.com/<UUID>/fail) — z || w skrypcie

Częste błędy do uniknięcia

  • Backup root-em zamiast dedykowanego konta — przy lekach hasła ekspozycja całego serwera
  • Hasło w argumencie mysqldump -ppassword — w ps/historii shell-a; używaj --defaults-extra-file
  • Brak --single-transaction — backup może być niespójny przy równoczesnym zapisie z TaxMachine
  • Brak retencji — backupy rosną w nieskończoność, dysk się zapełnia, nowe backupy padają
  • Brak monitoringu — backupy mogą cicho zawodzić tygodniami
  • Brak testu restore — backup którego nigdy nie sprawdzono nie jest backupem; raz na kwartał odtwórz na test-bazę
  • Klucz szyfrowania na tym samym serwerze — przy awarii tracisz kopię i klucz jednocześnie

Linki przydatne