217 lines
11 KiB
Markdown
217 lines
11 KiB
Markdown
# Unraid-deployment
|
||
|
||
Deze handleiding gebruikt `docker-compose.unraid.yml` als declaratieve bron van waarheid. De Unraid-variant bundelt web, worker, scheduler, PostgreSQL en Redis bewust in één Dockerman-container met Supervisor als procesbewaker. De gewone `docker-compose.yml` behoudt de gescheiden productieprocessen voor andere hosts.
|
||
|
||
## Vooraf
|
||
|
||
Alle paden in `docker-compose.unraid.yml` zijn relatief aan de map waarin dat bestand staat. Je kopieert de projectmap dus naar een plek naar keuze; de aanbevolen locatie blijft:
|
||
|
||
```text
|
||
/mnt/user/appdata/VacatureRadar
|
||
```
|
||
|
||
De container maakt `local/`, `local/media`, `local/logs`, `local/postgres-aio17`, `local/redis-aio` en `local/backups` bij de eerste start zelf aan en zet de juiste eigenaar. Voor back-ups blijft `/mnt/user/backups/vacatureradar` de aanbevolen doelmap.
|
||
|
||
Gebruik bij voorkeur een cache-backed `appdata`-share en neem die afzonderlijk in je back-upregime op. De PostgreSQL-datadir mag niet gelijktijdig door file-syncsoftware worden gemuteerd.
|
||
|
||
## Manueel kopiëren en uitrollen
|
||
|
||
Wanneer je niet via `scripts/deploy_unraid.sh` werkt maar de map met de hand kopieert (rsync, SMB-share, ZIP):
|
||
|
||
```bash
|
||
cd /mnt/user/appdata/VacatureRadar
|
||
cp .env.unraid.example .env # alleen de eerste keer
|
||
nano .env # alle CHANGE_ME-waarden vervangen
|
||
docker compose -f docker-compose.unraid.yml up -d --build --force-recreate
|
||
docker compose -f docker-compose.unraid.yml logs -f
|
||
```
|
||
|
||
Kopieer `local/` niet mee vanaf je werkstation: die map bevat de databasedirectory van de server. Kopieer `.env` evenmin over een bestaand serverbestand heen.
|
||
|
||
Een afwijkende hostpoort zet je in `.env` via `VACATURERADAR_HOST_PORT`; standaard is dat `1226`.
|
||
|
||
## Dockerman
|
||
|
||
De container verschijnt als **VacatureRadar** en bevat metadata voor:
|
||
|
||
- WebUI: `http://[IP]:1226/`;
|
||
- icoon: het lokale 256×256 VacatureRadar-PNG op `/static/img/vacatureradar-docker.png`;
|
||
- één herstart- en healthcheckpunt voor alle interne processen.
|
||
|
||
Dockerman haalt het tegelicoon via de gepubliceerde webpoort op en cachet het. De
|
||
PNG is daarom een afzonderlijke rasterasset met een stabiel pad; de SVG-favicon
|
||
blijft voor browsers behouden. Het icoonlabel gebruikt bewust de concrete
|
||
server-side URL `http://127.0.0.1:1226/...`: Dockerman vervangt `[IP]` en
|
||
`[PORT:...]` alleen voor de WebUI, niet voordat het een icoon downloadt. Na een
|
||
upgrade waarbij het oude icoon nog zichtbaar is, herlaad de Docker-pagina zodat
|
||
Dockerman de lokale cache opbouwt.
|
||
|
||
## Imagekeuze
|
||
|
||
De Unraid-compose bouwt lokaal met `Dockerfile.unraid` en tagt de image als `vacatureradar:unraid`. Pin voor reproduceerbare releases aanvullend een commitgebonden tag.
|
||
|
||
1. bouw lokaal op de Unraid-host en vervang `image:` door een vaste lokale tag;
|
||
2. publiceer vanuit CI naar een private registry en pin op een immutable digest;
|
||
3. gebruik `build:`, mits de gekozen Compose-plugin builds betrouwbaar ondersteunt.
|
||
|
||
Gebruik in productie geen zwevende `latest` zonder gecontroleerd rollbackpad.
|
||
|
||
## Configuratiebestand
|
||
|
||
`docker-compose.unraid.yml` leest `.env` naast het composebestand zelf:
|
||
|
||
```text
|
||
<projectmap>/.env
|
||
```
|
||
|
||
Een afwijkende locatie kan vóór Compose via `VACATURERADAR_ENV_FILE` worden ingesteld. Start van [`.env.unraid.example`](../../.env.unraid.example) voor een all-in-one container die rechtstreeks op poort 1226 draait, of van [`deployment/production.env.example`](../../deployment/production.env.example) zodra er een reverse proxy met TLS voor staat. Kopieer nooit ongewijzigde `CHANGE_ME`-waarden.
|
||
|
||
Configureer een bestaand bestand voor de publieke URL zonder secrets te overschrijven:
|
||
|
||
```bash
|
||
cd /mnt/user/appdata/VacatureRadar
|
||
python scripts/configure_public_url.py \
|
||
https://vacatureradar.example.be \
|
||
--env-file .env \
|
||
--cache-url redis://127.0.0.1:6379/1
|
||
```
|
||
|
||
Beperk de bestandsrechten van `.env` tot de beheerder. Voeg geen secrets toe aan de ZIP, Git, screenshots of supportlogs.
|
||
|
||
## Eerste uitrol
|
||
|
||
1. Controleer alle `CHANGE_ME`-waarden en maak een back-up van een bestaande `.env`.
|
||
2. Start of hermaak de Unraid-container vanuit de bronmap:
|
||
|
||
```bash
|
||
cd /mnt/user/appdata/VacatureRadar
|
||
docker compose -f docker-compose.unraid.yml up -d --build --force-recreate
|
||
```
|
||
|
||
3. De entrypoint valideert de productieconfiguratie, initialiseert PostgreSQL, voert migraties uit en start alle processen onder Supervisor.
|
||
4. Controleer intern `GET /health/live/` en `GET /health/ready/`.
|
||
5. Voer alleen wanneer bootstrap niet automatisch is ingeschakeld eenmalig uit:
|
||
|
||
```bash
|
||
docker exec -it VacatureRadar python manage.py bootstrap_instance
|
||
```
|
||
|
||
6. Meld lokaal aan en wijzig het bootstrapwachtwoord.
|
||
7. Activeer nog geen live bron of mailbox voordat bronbeleid, retentie en back-up zijn gecontroleerd.
|
||
|
||
## Beheerverbinding en heruitrol
|
||
|
||
Gebruik vanaf een beheerwerkstation altijd een benoemde SSH-hostalias met een afzonderlijke deploysleutel. Een rechtstreekse verbinding naar het IP-adres kan de lokale standaardgebruiker en standaardsleutel kiezen en daardoor ten onrechte als een server-side authenticatiefout lijken.
|
||
|
||
```sshconfig
|
||
Host unraid-itworx
|
||
HostName 192.168.10.150
|
||
Port 22
|
||
User root
|
||
IdentityFile ~/.ssh/itworx_unraid_deploy
|
||
IdentitiesOnly yes
|
||
```
|
||
|
||
Controleer de verbinding zonder wachtwoordprompt en voer daarna de beveiligde synchronisatie, imagebuild, containervervanging en health-smoke uit:
|
||
|
||
```bash
|
||
ssh -o BatchMode=yes unraid-itworx 'id -un && docker info >/dev/null'
|
||
bash scripts/deploy_unraid.sh
|
||
```
|
||
|
||
`deploy_unraid.sh` weigert een IP-adres, niet-rootprofiel of generieke standaardsleutel. Alleen door Git gekende of niet-genegeerde projectbestanden worden verpakt; `.env`, `local/`, databasebestanden en andere genegeerde runtimegegevens worden niet verzonden. De helper wacht op Docker-health en controleert `/health/ready/` voordat hij succes meldt. Een afwijkende veilige alias kan expliciet via `UNRAID_SSH_HOST` worden ingesteld.
|
||
|
||
## Releaseverificatie en upgradepad
|
||
|
||
VR-117 voegt `./scripts/release_verify.sh` toe als release-check met artifacts voor package, checksum, configdiff, sbom en upgrade/rollback.
|
||
Het script bewaart:
|
||
|
||
- `release-artifacts/<ts>/artifacts/release-smoke.json`
|
||
- `release-artifacts/<ts>/artifacts/smoke-restored-validate.json`
|
||
- `release-artifacts/<ts>/artifacts/upgrade-rollback.md`
|
||
- `release-artifacts/<ts>/artifacts/checksums.txt`
|
||
- `release-artifacts/<ts>/artifacts/config-diff.txt`
|
||
|
||
De restorecontrole vergelijkt primaire en gerestoreerde modelcounts in beide smoke-rapporten.
|
||
|
||
## Reverse proxy en TLS
|
||
|
||
Expose de applicatie niet rechtstreeks op internet. Plaats Nginx Proxy Manager, Traefik, Caddy of een gelijkwaardig beheerd reverse-proxyprofiel voor poort 1226. Vereisten:
|
||
|
||
- geldig TLS-certificaat;
|
||
- alleen HTTPS extern;
|
||
- `X-Forwarded-Proto` correct doorgeven;
|
||
- request-bodylimiet klein houden; er is geen algemene upload-API;
|
||
- optioneel extra access control/VPN voor persoonlijke installatie;
|
||
- geen publieke toegang tot PostgreSQL, Redis of Ollama.
|
||
|
||
Wanneer een proxy op hetzelfde Docker-netwerk draait, publiceer poort 1226 alleen intern. Wanneer Unraid routing een hostpoort vereist, beperk die via firewall/VLAN tot de proxy of het beheernetwerk. Volg voor host-, CSRF-, proxyheader- en TLS-instellingen de [Nginx Proxy Manager-handleiding](NGINX_PROXY_MANAGER.md).
|
||
|
||
## Mailbox
|
||
|
||
Gebruik per vacatureplatform een aparte mailbox of alias, niet de hoofdmailbox. Genereer eerst buiten Git een Fernet-sleutel:
|
||
|
||
```bash
|
||
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
||
```
|
||
|
||
Plaats de uitvoer uitsluitend in de Unraid-secretconfiguratie:
|
||
|
||
```dotenv
|
||
MAILBOX_CREDENTIAL_KEYS=<fernet-key>
|
||
IMAP_CONNECT_TIMEOUT_SECONDS=15
|
||
IMAP_MAX_MESSAGES_PER_POLL=200
|
||
IMAP_MAX_MESSAGE_BYTES=1000000
|
||
```
|
||
|
||
Herstart web, worker en scheduler. Open daarna **Bronnen → Platformmailboxen** en koppel ieder gewenst platform afzonderlijk: VDAB, Indeed, LinkedIn, ictjob.be, Jobat, StepStone, Careerjet, Randstad of Robert Half. Gmail en aangepaste publieke IMAP-hosts gebruiken een app-wachtwoord. Outlook / Microsoft 365 gebruikt centraal app-only OAuth via de vier `M365_*`-waarden en `EMAIL_BACKEND=apps.notifications.backends.Microsoft365OAuthEmailBackend`; geef de Entra-app alleen `IMAP.AccessAsApp` en `SMTP.SendAsApp` en beperk de Exchange-serviceprincipal tot de bedoelde mailbox. Laat `INBOX` geselecteerd tenzij de alert naar een aparte map wordt gefilterd. De scheduler controleert iedere vijf minuten welke mailbox volgens haar eigen interval verschuldigd is.
|
||
|
||
Voor een immutable registryrelease zet je `VACATURERADAR_IMAGE=gitea.itworx.tech/jens/vacatureradar:<versie>@sha256:<digest>` en start je met `docker compose -f docker-compose.unraid.yml up -d --no-build`.
|
||
|
||
Voor sleutelrotatie zet je `nieuwe-sleutel,oude-sleutel` in `MAILBOX_CREDENTIAL_KEYS`, herstart je de processen en draai je:
|
||
|
||
```bash
|
||
python manage.py rotate_mailbox_credentials
|
||
```
|
||
|
||
Verwijder de oude sleutel pas na een geslaagde sync. Een rollback van migratie `0005` verwijdert de mailboxkoppelingen; maak daarom eerst een versleutelde databaseback-up en pauzeer de scheduler.
|
||
|
||
## Ollama
|
||
|
||
Ollama is optioneel. Start de composeprofile pas wanneer de deterministische kern goed werkt:
|
||
|
||
```bash
|
||
docker compose -f docker-compose.unraid.yml --profile ai up -d
|
||
```
|
||
|
||
Pin een modelnaam in `OLLAMA_MODEL`; zonder model blijft `OLLAMA_ENABLED=0`. Stel Ollama niet extern bloot. Modeloutput wordt als onbetrouwbare gestructureerde data behandeld en mag geen harde regel of sollicitatieactie uitvoeren.
|
||
|
||
## Upgraden
|
||
|
||
1. Maak en verifieer een database- en configuratieback-up.
|
||
2. Lees `CHANGELOG.md` en de releaseartefacten.
|
||
3. Als de release nog niet geverifieerd is, voer `./scripts/release_verify.sh` lokaal uit.
|
||
4. Pull/bouw de nieuwe immutable image.
|
||
5. Stop de appcontainer.
|
||
6. Start de nieuwe appcontainer en laat de entrypoint migraties uitvoeren.
|
||
7. Controleer health, login en release-smoke-rapport.
|
||
8. Controleer in de containerlogs dat web, worker en scheduler actief zijn.
|
||
9. Bewaar de vorige image/digest totdat één volledige schedulercyclus goed verliep.
|
||
|
||
## Rollback
|
||
|
||
Bij een codefout zonder incompatibele migratie: pin de vorige image en herstart web/worker/scheduler. Bij een incompatibele datamigratie: stop alle schrijvers, herstel de vooraf gemaakte databaseback-up en gebruik de overeenkomende vorige image. Voer nooit een restore uit terwijl de worker of scheduler schrijft.
|
||
|
||
## Productiechecklist
|
||
|
||
- [ ] alle `CHANGE_ME`-waarden verwijderd;
|
||
- [ ] debug uit en secure cookies aan;
|
||
- [ ] TLS en host/origin exact ingesteld;
|
||
- [ ] PostgreSQL/Redis/Ollama niet publiek;
|
||
- [ ] dagelijkse back-up plus periodieke hersteltest;
|
||
- [ ] aparte IMAP-mailbox met minimale rechten;
|
||
- [ ] bronpolicy per actieve bron op `allow` met reviewdatum;
|
||
- [ ] adminwachtwoord gewijzigd;
|
||
- [ ] healthmonitoring en vrije schijfruimtebewaking;
|
||
- [ ] retentiejob en logrotatie gecontroleerd.
|