Bastelgedöhns

Installation & Backup-Setup: SQL Server 2019 Express in Proxmox LXC

SQL Server 2019 Express in Proxmox LXC (Ubuntu 20.04) – Installation & Backup auf NAS

Ziele:

  • MSSQL 2019 Express im LXC installieren
  • SQL-Tools (sqlcmd) installieren
  • NAS-Freigabe per CIFS einbinden (ohne Automount)
  • Backup-Script mit Zeitplan: Mo–Fr 09/11/13/15/17 Uhr & täglich 20:00 Uhr
  • Um 20:00 Uhr vor dem Backup: DBCC CHECKDB; Backup läuft immer weiter
  • Bei CHECKDB-Fehlern: Suffix __CHECKDB_FAIL im Dateinamen
  • Wichtig: SQL Express unterstützt keine Backup-Kompression → nicht verwendet

1) LXC-Container vorbereiten

Container mit Ubuntu 20.04 erstellen und danach folgende Optionen in /etc/pve/lxc/CTID.conf ergänzen (CTID ersetzen):

lxc.apparmor.profile: unconfined
lxc.cap.drop:
lxc.mount.auto: sys:rw
lxc.cgroup.devices.allow: a

2) MSSQL 2019 Express installieren

sudo apt update
sudo apt install -y curl gnupg ca-certificates apt-transport-https
sudo install -d -m 0755 /usr/share/keyrings

curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/microsoft.gpg

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.gpg] https://packages.microsoft.com/ubuntu/20.04/mssql-server-2019 focal main" \
| sudo tee /etc/apt/sources.list.d/mssql-server.list

sudo apt update
sudo apt install -y mssql-server
sudo /opt/mssql/bin/mssql-conf setup

Im Setup die Edition Express wählen und SA-Passwort setzen. Status prüfen:

systemctl status mssql-server

3) SQL Tools installieren (sqlcmd)

curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list \
| sudo tee /etc/apt/sources.list.d/msprod.list

sudo apt update
sudo apt install -y mssql-tools unixodbc-dev

echo 'export PATH="$PATH:/opt/mssql-tools/bin"' | sudo tee -a ~/.bashrc
source ~/.bashrc

# Test
sqlcmd -S localhost -U SA -P "DEIN_PASSWORT" -Q "SELECT @@VERSION"

4) NAS-Freigabe (CIFS) einbinden – ohne Automount

Konfiguration so, dass der LXC auch bootet, wenn das NAS nicht erreichbar ist: nofail, soft, x-systemd.device-timeout=10 (kein x-systemd.automount).

sudo apt install -y cifs-utils
sudo mkdir -p /mnt/sqlbackup

# Zugangsdatendatei
sudo bash -c 'cat > /root/.smbcred <<EOF
username=NASUSER
password=NASPASS
domain=WORKGROUP
EOF'
sudo chmod 600 /root/.smbcred

# fstab-Eintrag (IP/Share anpassen)
echo "//192.168.1.100/sqlbackup /mnt/sqlbackup cifs credentials=/root/.smbcred,iocharset=utf8,file_mode=0777,dir_mode=0777,nofail,soft,x-systemd.device-timeout=10 0 0" \
| sudo tee -a /etc/fstab

# Testmount
sudo mount -a
ls -la /mnt/sqlbackup

5) Backup-Script mit integriertem CHECKDB (20:00)

Script speichert alle User-DBs (außer master, model, msdb, tempdb) als .bak. Um 20:00 wird vorab DBCC CHECKDB pro DB ausgeführt. Bei Fehlern wird der Backupdateiname mit __CHECKDB_FAIL markiert. SQL Express → keine Kompression.

Datei: /usr/local/bin/mssql_backup.sh

sudo bash -c 'cat > /usr/local/bin/mssql_backup.sh << "EOF"
#!/bin/bash
set -euo pipefail

# -----------------------------
# MSSQL Backup + (20:00) CHECKDB mit Fail-Suffix (SQL Express)
# -----------------------------
SA_USER="SA"
SA_PASS="DEIN_SA_PASSWORT"   # <— anpassen
SQLHOST="localhost"

DEST="/mnt/sqlbackup/mssql"
RETENTION_DAYS=7
LOG_CHECKDB="/var/log/mssql_checkdb.log"
SQLCMD="/opt/mssql-tools/bin/sqlcmd"
SQL_TIMEOUT=900  # 15 Minuten Timeout je sqlcmd

# Prevent overlapping runs
exec 9>/var/lock/mssql_backup.lock
flock -n 9 || { echo "Backup already running, exiting."; exit 0; }

# Ensure paths
mkdir -p "$DEST" "$(dirname "$LOG_CHECKDB")"

# sqlcmd detection
[ -x "$SQLCMD" ] || SQLCMD="$(command -v sqlcmd || true)"
if [ -z "${SQLCMD}" ]; then
  echo "FATAL: sqlcmd not found (install mssql-tools)." >&2
  exit 3
fi

ts(){ date "+%Y-%m-%d %H:%M:%S"; }

# --- DB-Liste holen (einmalig), CR entfernen, leere/dupl. raus ---
RAW_DBLIST="$(
  timeout "$SQL_TIMEOUT" "$SQLCMD" -S "$SQLHOST" -U "$SA_USER" -P "$SA_PASS" \
    -Q "SET NOCOUNT ON; SELECT name FROM sys.databases WHERE name NOT IN ('master','model','msdb','tempdb');" \
    -h -1 -W || true
)"

SANITIZED="$(printf "%s\n" "$RAW_DBLIST" | tr -d "\r" | sed "/^[[:space:]]*$/d" | sort -u)"
if [ -z "$SANITIZED" ]; then
  echo "[$(ts)] No user databases found. Nothing to do."
  exit 0
fi

# In Array laden
mapfile -t DBS < <(printf "%s\n" "$SANITIZED")

# --- 20:00 CHECKDB (before backups) ---
declare -A CHECKDB_STATUS
HOUR_NOW="$(date +%H)"

if [ "$HOUR_NOW" = "20" ]; then
  echo "[$(ts)] ===== DBCC CHECKDB (pre-backup) start =====" | tee -a "$LOG_CHECKDB"
  for DB in "${DBS[@]}"; do
    DB_TRIM="$(echo "$DB" | xargs)"
    [ -z "$DB_TRIM" ] && continue
    echo "[$(ts)] CHECKDB on [$DB_TRIM]..." | tee -a "$LOG_CHECKDB"

    if ! timeout "$SQL_TIMEOUT" "$SQLCMD" -S "$SQLHOST" -U "$SA_USER" -P "$SA_PASS" \
         -Q "DBCC CHECKDB('$DB_TRIM') WITH NO_INFOMSGS, ALL_ERRORMSGS;" \
         -b -V 16 >>"$LOG_CHECKDB" 2>&1; then
      echo "[$(ts)] ERROR in CHECKDB for [$DB_TRIM]" | tee -a "$LOG_CHECKDB"
      CHECKDB_STATUS["$DB_TRIM"]="FAIL"
    else
      echo "[$(ts)] OK: [$DB_TRIM]" | tee -a "$LOG_CHECKDB"
      CHECKDB_STATUS["$DB_TRIM"]="OK"
    fi
  done
  echo "[$(ts)] ===== DBCC CHECKDB finished =====" | tee -a "$LOG_CHECKDB"
fi

# --- Backups ---
DATE_STR="$(date +%F_%H-%M)"
echo "[$(ts)] ===== Backups start ($DATE_STR) ====="

for DB in "${DBS[@]}"; do
  DB_TRIM="$(echo "$DB" | xargs)"
  [ -z "$DB_TRIM" ] && continue

  SUFFIX=""
  if [ "$HOUR_NOW" = "20" ] && [ "${CHECKDB_STATUS[$DB_TRIM]:-OK}" = "FAIL" ]; then
    SUFFIX="__CHECKDB_FAIL"
  fi

  OUT_FILE="$DEST/${DB_TRIM}_${DATE_STR}${SUFFIX}.bak"
  echo "[$(ts)] Backup DB: $DB_TRIM -> $OUT_FILE"

  # SQL Express: keine Kompression
  timeout "$SQL_TIMEOUT" "$SQLCMD" -S "$SQLHOST" -U "$SA_USER" -P "$SA_PASS" \
    -Q "BACKUP DATABASE [$DB_TRIM] TO DISK = N'${OUT_FILE//\'/\'\'}' WITH INIT, STATS=5;"
done

# Cleanup
find "$DEST" -type f -name "*.bak" -mtime +$RETENTION_DAYS -delete || true

echo "[$(ts)] ===== Backups finished ====="
EOF'
sudo chmod +x /usr/local/bin/mssql_backup.sh

6) Systemd Service & Timer einrichten

Service:

sudo bash -c 'cat > /etc/systemd/system/mssql-backup.service << "EOF"
[Unit]
Description=MSSQL backup + daily CHECKDB (20:00)

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mssql_backup.sh
EOF'

Timer (Mo–Fr: 09/11/13/15/17 Uhr & täglich 20:00 Uhr):

sudo bash -c 'cat > /etc/systemd/system/mssql-backup.timer << "EOF"
[Unit]
Description=Run MSSQL backups on schedule

[Timer]
OnCalendar=Mon..Fri *-*-* 09,11,13,15,17:00:00
OnCalendar=*-*-* 20:00:00
Persistent=true

[Install]
WantedBy=timers.target
EOF'

sudo systemctl daemon-reload
sudo systemctl enable --now mssql-backup.timer

Manuell testen:

systemctl start mssql-backup.service
journalctl -u mssql-backup.service -n 100 --no-pager
tail -n 100 /var/log/mssql_checkdb.log

7) Wichtige Hinweise

  • SQL Express unterstützt keine Backup-Kompression – daher nicht im Script.
  • DBCC CHECKDB repariert nicht automatisch. Bei Fehlern wird die Backupdatei mit __CHECKDB_FAIL markiert; Backup läuft weiter.
  • /etc/fstab Eintrag ist ohne Automount, startet den LXC aber auch bei nicht erreichbarem NAS (nofail, soft, x-systemd.device-timeout=10).
  • Aufbewahrung ist 7 Tage (per find ... -mtime +7 im Script) – anpassbar.

8) Troubleshooting

  • Hängt ein Lauf? → Prüfe Prozesse: ps -ef | grep sqlcmd
  • Timer läuft nicht? → systemctl status mssql-backup.timer
  • NAS nicht gemountet? → mount -a, Pfad prüfen: ls -la /mnt/sqlbackup
  • CHECKDB-Log: /var/log/mssql_checkdb.log

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden.