3e24aa2ecd
Datenverlust behoben: - Nach hartem Stopp blieb das Original in working/ liegen und wurde nie wieder angefasst (_scan_existing sah nur incoming/). Es wird jetzt beim Start an Ort und Stelle wieder aufgegriffen, mit Kollisionsschutz gegen gleichnamige neue Scans; angefangene __ocr_-Fragmente werden geloescht. - TimeoutStopSec 30 -> 300, damit laufendes OCR zu Ende laufen darf. Config-Drift sichtbar gemacht: - Neues --check-config (Exit 0 sauber / 1 Warnungen / 2 Fehler), das update.sh vor dem Neustart ueber alle Instanz-Configs laufen laesst. - Warnungen fuer [ocr].timeout >= 900 (seit 0.4.0 pro SEITE) und gesetztes pdfa_level, beim Dienststart wie im Check. - Unbekannte Config-Keys werden nicht mehr still verworfen, sondern genannt. Updater feldtauglich: - venv-Health-Check erkennt toten Symlink UND Versions-Drift gegen das System-Python; --rebuild-venv als ausdruecklicher Weg nach einem Debian- Major-Upgrade. Neubau ist ganz-oder-gar-nicht mit Rollback. - apt-Pakete werden auch beim Update synchronisiert (Quelle: install.sh). - Instanz-Erfassung inkl. activating/failed, Verifikation prueft is-failed und NRestarts statt sleep 1 + is-active. - Backup enthaelt Configs, Unit, Drop-ins und pip-freeze.txt, liegt auf 0600 und rotiert auf 5; schlaegt es fehl, bricht das Update vorher ab. - ERR-Trap faehrt die vorher laufenden Instanzen wieder hoch. - lxc-compat.conf wird beim Update nachgezogen. - requirements.txt gepinnt (ocrmypdf 16.13.0, geprueft fuer Python 3.11+3.13). Doku in Installation / Update / OS-Upgrade aufgeteilt (docs/). 135 Tests gruen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
220 lines
6.8 KiB
Markdown
220 lines
6.8 KiB
Markdown
# Debian-Major-Upgrade
|
|
|
|
Wie der Hotfolder ein Distributions-Upgrade übersteht (12 → 13, später 13 → 14).
|
|
|
|
Verwandte Dokumente: [README](../README.md) · [Installation](INSTALLATION.md) · [Update](UPDATE.md)
|
|
|
|
---
|
|
|
|
## Warum das ein eigener Ablauf ist
|
|
|
|
Die venv unter `/opt/pdf-ocr-hotfolder/venv/` hängt an der **Python-Version der
|
|
Distribution**. Ein `apt full-upgrade` von Debian 12 auf 13 tauscht Python 3.11
|
|
gegen 3.13 aus. Danach zeigt `venv/bin/python` auf einen Interpreter, den es so
|
|
nicht mehr gibt — systemd quittiert den Start jeder Instanz mit `203/EXEC`, und
|
|
auch wenn der Interpreter noch existiert, passen die installierten Pakete nicht
|
|
mehr zum System-Python.
|
|
|
|
**Die venv muss nach dem Sprung neu gebaut werden.** Ein normales
|
|
`sudo ./update.sh` genügt dafür nicht sicher genug — es gibt den ausdrücklichen
|
|
Schalter `--rebuild-venv`.
|
|
|
|
---
|
|
|
|
## Der Ablauf
|
|
|
|
### 1. Vorher updaten
|
|
|
|
```bash
|
|
cd /pfad/zum/repo
|
|
git pull
|
|
sudo ./update.sh
|
|
```
|
|
|
|
Das bringt die Installation auf den aktuellen Stand und erzeugt vor allem ein
|
|
**frisches Backup** inklusive `pip-freeze.txt` — die Liste der Paketversionen,
|
|
die auf dem alten System liefen. Inhalt und Ort des Backups:
|
|
[UPDATE.md](UPDATE.md#backup).
|
|
|
|
### 2. Instanzen stoppen
|
|
|
|
```bash
|
|
sudo systemctl stop 'pdf-ocr-hotfolder@*'
|
|
```
|
|
|
|
Während des Upgrades darf kein OCR laufen: Ghostscript, Tesseract und die
|
|
Python-Pakete werden mitten im Betrieb ausgetauscht.
|
|
|
|
> **Geduld.** Die Unit hat `TimeoutStopSec=300`, damit ein laufendes OCR sauber
|
|
> zu Ende kommt. **Ein Stop kann pro Instanz bis zu 5 Minuten dauern** — bei
|
|
> mehreren Instanzen entsprechend länger. Nicht mit `kill -9` nachhelfen; ein
|
|
> harter Stopp lässt das Original in `working/` liegen (der Dienst nimmt es beim
|
|
> nächsten Start zwar wieder auf, aber der Durchlauf ist verloren).
|
|
|
|
Prüfen, dass wirklich alles steht:
|
|
|
|
```bash
|
|
systemctl status 'pdf-ocr-hotfolder@*'
|
|
```
|
|
|
|
### 3. Distribution upgraden
|
|
|
|
Der übliche Debian-Weg — Sources auf das neue Release umstellen, dann:
|
|
|
|
```bash
|
|
sudo apt update
|
|
sudo apt full-upgrade
|
|
sudo reboot
|
|
```
|
|
|
|
Die Instanzen sind `enabled` und starten nach dem Reboot mit; mit der alten venv
|
|
scheitern sie (`203/EXEC`). Das ist erwartet und wird im nächsten Schritt
|
|
behoben.
|
|
|
|
### 4. venv neu bauen
|
|
|
|
```bash
|
|
cd /pfad/zum/repo
|
|
git pull
|
|
sudo ./update.sh --rebuild-venv
|
|
```
|
|
|
|
`--rebuild-venv` erzwingt den Neubau. Der Rest des Updates läuft wie gewohnt
|
|
(siehe [UPDATE.md](UPDATE.md#was-das-skript-tut--in-dieser-reihenfolge)) — die
|
|
System-Pakete werden dabei ebenfalls abgeglichen, auch `python3-venv` für das
|
|
neue Python.
|
|
|
|
Der Neubau ist **ganz oder gar nicht**:
|
|
|
|
1. Die alte venv wird nach `venv.old-<timestamp>` verschoben.
|
|
2. `python3 -m venv` baut neu.
|
|
3. Die Requirements werden installiert.
|
|
4. **Erst bei Erfolg** wird die alte venv gelöscht.
|
|
|
|
### 5. Wenn ein Pin nicht mehr passt
|
|
|
|
Scheitert `pip install` an einer gepinnten Version, bricht das Skript ab und
|
|
sagt genau, was los ist:
|
|
|
|
- die letzten 25 Zeilen der pip-Ausgabe,
|
|
- das **gescheiterte Paket** ("Gescheitertes Paket: …"),
|
|
- die Diagnose: "eine in requirements.txt fest gepinnte Version gibt es für
|
|
Python \<version\> nicht (mehr)",
|
|
- den nächsten Schritt: **requirements.txt anheben** und
|
|
`update.sh --rebuild-venv` erneut laufen lassen.
|
|
|
|
**Die alte venv ist dann zurückgerollt** — es liegt keine halb gefüllte venv
|
|
herum. Sie hängt zwar weiterhin am alten Interpreter und die Instanzen laufen
|
|
damit nicht (das sagt das Skript auch), aber der Zustand ist eindeutig.
|
|
|
|
Also:
|
|
|
|
```bash
|
|
# im Repo, auf einer Testmaschine
|
|
vim requirements.txt # Version des genannten Pakets anheben
|
|
pytest # Suite muss grün bleiben
|
|
git commit -am 'requirements: <paket> auf <version> anheben'
|
|
git push
|
|
|
|
# auf dem Zielsystem
|
|
git pull
|
|
sudo ./update.sh --rebuild-venv
|
|
```
|
|
|
|
### 6. `--rebuild-venv` vergessen?
|
|
|
|
Halb so wild: `update.sh` prüft die venv auch ohne den Schalter und baut sie bei
|
|
Versions-Drift von selbst neu. Geprüft wird
|
|
|
|
- ob das Verzeichnis und `venv/bin/python` überhaupt existieren,
|
|
- ob der Interpreter der venv noch **läuft** (toter Symlink nach dem Upgrade),
|
|
- ob seine `major.minor` zum System-`python3` passt,
|
|
- ob `pyvenv.cfg` dieselbe Version nennt wie der Interpreter.
|
|
|
|
Stimmt eines davon nicht, nennt das Skript den Grund und baut neu.
|
|
`--rebuild-venv` ist also nicht der einzige, aber der **ausdrückliche** Weg —
|
|
und der, den man nach einem Distributions-Upgrade nimmt, statt sich auf die
|
|
Erkennung zu verlassen.
|
|
|
|
---
|
|
|
|
## Danach prüfen
|
|
|
|
**Laufen alle Instanzen?**
|
|
|
|
```bash
|
|
systemctl status 'pdf-ocr-hotfolder@*'
|
|
```
|
|
|
|
`update.sh` hat das schon verifiziert (Wartezeit, `is-failed`, Crash-Loop) und
|
|
in der Zusammenfassung Soll gegen Ist gestellt — ein Exit 0 heißt, dass jede
|
|
Instanz, die vorher lief, auch wieder läuft.
|
|
|
|
**Sind die Configs sauber?**
|
|
|
|
```bash
|
|
for f in /etc/pdf-ocr-hotfolder/*.toml; do
|
|
sudo /opt/pdf-ocr-hotfolder/venv/bin/python -m pdf_ocr_hotfolder \
|
|
--check-config --config "$f"
|
|
done
|
|
```
|
|
|
|
Exit 0/1/2 und was bei Warnungen zu tun ist:
|
|
[UPDATE.md](UPDATE.md#config-prüfung-per---check-config).
|
|
|
|
**Läuft eine echte PDF durch?**
|
|
|
|
```bash
|
|
cp test.pdf /var/lib/pdf-ocr-hotfolder/<instanz>/incoming/
|
|
journalctl -u pdf-ocr-hotfolder@<instanz> -f
|
|
```
|
|
|
|
Im `outgoing/` muss das OCR-PDF liegen, im Journal steht `OCR done`. Das ist der
|
|
einzige Test, der die neue venv **und** die neuen System-Binaries (Tesseract,
|
|
Ghostscript) wirklich anfasst — `systemctl status` sagt darüber nichts.
|
|
|
|
**Ghostscript-Version auf dem neuen Release ansehen:**
|
|
|
|
```bash
|
|
gs --version
|
|
```
|
|
|
|
Auf Debian 13 ist der Ghostscript-Bug aus Debian 12 kein Thema mehr; ein
|
|
bookworm-backports-Eintrag unter `/etc/apt/sources.list.d/` gehört nach dem
|
|
Upgrade entfernt. Hintergrund:
|
|
[INSTALLATION.md](INSTALLATION.md#ghostscript-bug-auf-debian-12).
|
|
|
|
---
|
|
|
|
## Pins in `requirements.txt`
|
|
|
|
Die Python-Abhängigkeiten sind **bewusst fest gepinnt**:
|
|
|
|
```
|
|
ocrmypdf==16.13.0
|
|
watchdog==6.0.0
|
|
requests==2.33.1
|
|
paramiko==4.0.0
|
|
```
|
|
|
|
Ohne Pins würde ein `pip install --upgrade` bei jedem Update ungefragt eine neue
|
|
Major-Version ziehen — ein Sprung von ocrmypdf **16 auf 17** reißt sonst alle
|
|
Instanzen auf einmal, und zwar im Moment des Updates, nicht zu einem Zeitpunkt,
|
|
den man sich ausgesucht hat.
|
|
|
|
Die aktuellen Pins sind gegen Python 3.11 (Debian 12) und 3.13 (Debian 13)
|
|
geprüft; für beide gibt es fertige Wheels, es wird nichts kompiliert.
|
|
|
|
**Beim Anheben:**
|
|
|
|
1. **Testmaschine benutzen** — nie direkt auf dem produktiven Hotfolder.
|
|
2. Dort `update.sh --rebuild-venv` fahren, damit die Pakete wirklich frisch
|
|
aufgelöst werden.
|
|
3. `pytest` muss grün bleiben (135 Tests).
|
|
4. Eine echte PDF durchschieben — die Test-Suite mockt ocrmypdf, ein Major-Sprung
|
|
fällt dort also nicht auf.
|
|
5. Erst dann committen und auf die produktiven Systeme geben.
|
|
|
|
Der ocrmypdf-Sprung 16 → 17 ist ein **Major-Sprung** und gehört in einen eigenen
|
|
Vorgang mit eigenem Test, nicht in ein OS-Upgrade.
|