Beheer
Deployment
Vereisten, Docker, meerdere containers en eigen hosting.
Nimbly ondersteunt zowel deployment in containers als handmatig op een VPS. Containers verpakken de app en de runtime samen voor herhaalbare updates via images. Handmatig deployen op een VPS is net zo goed een optie voor zelfbeheerde Apache/PHP-servers waar je liever directe controle over de server houdt. Kies het model dat past bij hoe de site wordt beheerd.
Elke site antwoordt op /health voor de controle van een load balancer of container: 200 met ok als PHP draait en ext/data beschrijfbaar is, anders 503. Het antwoord wordt nooit gecachet. Een route ext/uri/health vervangt het.
Achter een load balancer die TLS afhandelt, noem je zijn adres in .env:
TRUSTED_PROXIES=10.0.0.0/8
Adressen en bereiken, gescheiden door komma's. Een request dat rechtstreeks van een van die adressen komt, krijgt zijn schema, poort en bezoekersadres uit X-Forwarded-Proto, X-Forwarded-Port en X-Forwarded-For, zodat de sessiecookie secure is, absolute links met https:// beginnen en de inlogpauze en de statistieken de bezoeker tellen in plaats van de balancer. Vanaf elk ander adres, en zonder deze instelling, worden die headers genegeerd.
Het eigen toegangslog van de webserver, en wat dat log leest (de botregels hieronder), heeft ook het adres van de bezoeker nodig. De containerimages regelen dat bij het starten vanuit dezelfde instelling. Zet op een eigen server mod_remoteip aan en zet de regels die dit commando toont in de <VirtualHost> van de site (niet in .htaccess), en doe dat opnieuw als de instelling verandert:
sudo a2enmod remoteip
./nimbly host:proxy-conf
Een ban op adres houdt achter een load balancer niets tegen: de verbinding komt van de balancer. Laat de balancer of zijn firewall de adressen weigeren die de regels vinden.
Bots
Bots zijn welkom als ze robots.txt volgen, alleen adressen opvragen die de site heeft gepubliceerd, en hooguit één pagina per seconde lezen. Nimbly antwoordt correct en laat het bannen aan de server over:
- Tripwire. Elke site heeft één adres,
/nb-plus twaalf tekens die van dePEPPERzijn afgeleid, datrobots.txtverbiedt en waar elke pagina onzichtbaar naar linkt ([#bot-tripwire-link#]in de pagina-shell). Een request daarvoor krijgt status418. Het adres is per site vast en verschilt tussen sites; een eigen pagina-shell heeft de shortcode ook nodig. - Serverregels.
core/cli/setup/fail2ban/bevat drie fail2ban-filters en één jail-bestand voor een server met Apache-toegangslogs: een ban bij elk418-antwoord, bij meer dan 60 paginarequests per minuut, en bij 20 keer niet gevonden in tien minuten. Afbeeldingen, styles, scripts, API-aanroepen en adminschermen zijn geen paginarequests, en een ontbrekende afbeelding, style, script of document telt niet als niet gevonden: dat is een kapotte link van de site zelf. Kopieer de filters naar/etc/fail2ban/filter.d/en het jail-bestand naar/etc/fail2ban/jail.d/, zet je eigen servers inignoreip, en draai elk filter metfail2ban-regextegen je logs voordat je het aanzet.
Deployment met Docker
Maak vanuit de projectcheckout de bestanden voor het app-image in ext/:
./nimbly docker:init
Commit de gemaakte ext/Dockerfile, ext/.dockerignore en ext/.github/workflows/docker-publish.yml in de repository ext/. Bij een push bouwt GitHub Actions het app-image vanuit het actuele Nimbly-image master en publiceert het naar GHCR. Core publiceert doorlopend ghcr.io/nimbly-web-systems/nimbly-core:master; de gemaakte applicatiebuilds gebruiken dat image en clonen de bijbehorende master-bron voor hun asset-build.
Om deployment lokaal te testen zonder te wachten tot GitHub Actions een nieuw core-image publiceert, bouw je een lokaal core-image en geef je dat mee als basis voor de app-build:
docker build -t nimbly-core-local:master -f docker/prod/Dockerfile .
docker build -t nimbly-app-local:test \
--build-arg NIMBLY_BASE_IMAGE=nimbly-core-local:master \
-f ext/Dockerfile ext
Kies vóór het deployen waar de productiedata leidend is:
- Onveranderlijk van ontwikkeling naar live: bewerk content in ontwikkeling, commit die naar
ext/en deploy een nieuw image. Productie wordt niet als editor gebruikt. Er is geen Git-token met schrijfrechten nodig. - Live bewerken: redacteuren wijzigen content, uploads of records op de productiesite. In deze modus schrijft productie naar
ext/data/, en die wijzigingen moeten terug naar de repositoryext/worden gepusht voordat de volgende deploy de container vervangt.
Voor live bewerken moet het productie-image ext/.git bevatten en moet de container GIT_TOKEN krijgen. De schema's van prod en stage bevatten ext:sync, dat wijzigingen in de data commit en terug pusht naar de repository ext/. Gebruik voor GitHub een fine-grained token dat alleen geldt voor de repository ext/, met Contents: Read and write. De container stelt bij het starten de Git-inloggegevens in vanuit dit token; commit het token niet en bak het niet in het image.
Voor lokale tests met Docker of een zelfbeheerde VPS is een ongevolgd env-bestand prima:
docker run --rm --env-file .env.prod ghcr.io/your-org/your-nimbly-app:main
Houd .env, .env.prod, .env.stage en vergelijkbare bestanden buiten Git. Commit alleen voorbeeldbestanden zoals .env.example of .env.prod.example met nepwaarden. Stel op beheerde containerplatforms deze waarden in als runtime-omgevingsvariabelen of geheimen:
- AWS ECS/Fargate: bewaar geheimen in AWS Secrets Manager of SSM Parameter Store en verwijs ernaar vanuit het blok
secretsvan de task definition. - AWS App Runner: stel runtime-omgevingsvariabelen en geheimen in op de service.
- Andere containerhosts: gebruik het mechanisme voor geheimen en omgevingsvariabelen van het platform, niet de build van het Docker-image.
Minimaal Compose-voorbeeld voor productie:
services:
web:
image: ghcr.io/your-org/your-nimbly-app:main
restart: unless-stopped
env_file:
- .env.prod
ports:
- "80:80"
environment:
APP_ENV: prod
SITE_URL: https://example.com
SITE_NAME: Example
MAIL_SERVICE: resend
MAIL_FROM: no-reply@example.com
MAIL_FROM_NAME: Example
volumes:
- nimbly-data:/var/www/nimbly/ext/data
volumes:
nimbly-data:
Wordt productiecontent ter plekke bewerkt, zet .git dan niet in ext/.dockerignore. Zonder ext/.git of GIT_TOKEN start de container nog wel, maar kan ext:sync live wijzigingen niet pushen, en kunnen die wijzigingen bij de volgende deploy van een image verloren gaan.
Updates van containers zijn updates van images: push ext/, laat de workflow een nieuw image publiceren, pull het image op de host en herstart de container. De container draait system:setup bij het starten en draait elke minuut de scheduler. Zorg in de modus voor live bewerken dat ext:sync de actuele productiedata heeft gepusht voordat je de container vervangt.
Meerdere containers
Een site kan in meerdere identieke containers achter een load balancer draaien. Daar is geen instelling voor: alles wat een site onthoudt, is een bestand onder ext/ (records, uploads, sessies, jobs, de status van de scheduler, requeststatistieken, caches), dus de containers hoeven alleen die map te delen.
- Eén volume. Koppel hetzelfde volume op
/var/www/nimbly/ext/datain elke container. Koppel bij live bewerken in plaats daarvan de heleext/, zodat er één Git-checkout is voorext:syncen niet één per container. Het volume moet bestandslocks (flock) ondersteunen: NFSv4 en Amazon EFS doen dat. - Dezelfde
.env. Elke container krijgt dezelfdePEPPERen instellingen. - Load balancer. Laat zijn controle naar
/healthwijzen en zetTRUSTED_PROXIES(hierboven) op zijn adres. Sticky sessions zijn niet nodig: een login op de ene container geldt ook op de andere. - Scheduler. Elke container draait hem elke minuut; een lock op het gedeelde volume laat één ervan het werk doen, zodat een taak één keer draait.
- Schrijven. Wijzigingen aan de records van een resource gebeuren één voor één over alle containers heen, dus twee redacteuren die hetzelfde record opslaan, verliezen niets.
services:
web:
image: ghcr.io/your-org/your-nimbly-app:main
restart: unless-stopped
env_file:
- .env.prod
deploy:
replicas: 2
volumes:
- nimbly-data:/var/www/nimbly/ext/data
- nimbly-thumbs:/var/www/nimbly/ext/static/_thumb_
volumes:
nimbly-data:
driver_opts:
type: nfs
o: addr=10.0.0.10,nfsvers=4.1,rw
device: ":/nimbly/example/data"
nimbly-thumbs:
driver_opts:
type: nfs
o: addr=10.0.0.10,nfsvers=4.1,rw
device: ":/nimbly/example/thumbs"
Thumbnails worden gecachet in ext/static/_thumb_. Koppel daar ook een tweede gedeeld volume: anders maakt elke container zijn eigen thumbnails, en blijven de thumbnails van een verwijderde afbeelding achter op de containers die het verwijderen niet afhandelden.
Elke container draait bij het starten system:setup en maakt daarna www-data eigenaar van de bestanden van de site, het gedeelde volume inbegrepen. Het volume moet root dus eigenaren laten wijzigen (op NFS: no_root_squash, of een export die al van www-data is), en een volume met heel veel bestanden maakt het starten traag. Twee containers mogen tegelijk op een leeg volume starten.
core/tests/multi-node.sh probeert dit uit op een werkstation: het start een wegwerpsite in twee containers op één map (docker/dev/docker-compose.nodes.yml) en controleert de login, gelijktijdig schrijven naar één record, de scheduler en de requeststatistieken. Het heeft alleen op een lokale schijf gedraaid, niet op een netwerkvolume en niet achter een echte load balancer.
Handmatige deployment op een VPS
Handmatig deployen op een VPS wordt ondersteund voor zelfbeheerde installaties. De basis voor een release is dezelfde weg als in CI:
./nimbly deps
APP_ENV=prod SITE_NAME="My Site" ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD="change-me" PEPPER="$PEPPER" ./nimbly system:setup
./nimbly build
./nimbly help
find core -name '*.php' -print0 | xargs -0 -n1 php -l
Is er geen PHP op de host, gebruik dan de CLI en PHP-lint via Docker:
APP_ENV=prod SITE_NAME="My Site" ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD="change-me" PEPPER="$PEPPER" ./nimbly --docker system:setup
docker compose -f docker/dev/docker-compose.yml run --rm --build nimbly sh -lc "find core -name '*.php' -print0 | xargs -0 -n1 php -l"
Houd .env op de doelserver en zorg dat die de APP_ENV voor productie, een vaste PEPPER, de juiste SITE_URL en de mailinstellingen bevat. system:setup kun je veilig opnieuw draaien, ook als een deployment ontbrekende mappen, routes, rollen, .jobs of .htaccess moet aanmaken.
Scheduler en jobs
Installeer bij handmatige deployments op een VPS de scheduler-orchestrator één keer per server. Registreer elk gedeployd project; planning is ook nodig zonder taken van de applicatie zelf:
sudo php /var/www/site/core/cli/nimbly.php scheduler:install
sudo php /var/www/site/core/cli/nimbly.php scheduler:project:add site /var/www/site
sudo php /var/www/site/core/cli/nimbly.php scheduler:cron:install --user=www-data
Dit maakt /etc/cron.d/nimbly-scheduler, dat elke minuut /usr/local/bin/nimbly-scheduler-orchestrator draait als www-data, en /etc/logrotate.d/nimbly-scheduler, dat het schedulerlog wekelijks roteert. De orchestrator start daarna de schedulers van de geregistreerde projecten los van elkaar. In Docker-containers draait de scheduler vanzelf, dus daar is geen cron op de host nodig.
Draai schedule:init één keer om de schemabestanden van het project in ext/cli/ te maken:
./nimbly schedule:init
Dit maakt drie bestanden:
| Bestand | Gebruikt als |
|---|---|
ext/cli/schedule.inc |
APP_ENV=dev of geen bestand voor de omgeving gevonden |
ext/cli/schedule.prod.inc |
APP_ENV=prod |
ext/cli/schedule.stage.inc |
APP_ENV=stage |
De schema's van prod en stage bevatten ext:sync, dat elk uur wijzigingen in de data van ext/ commit en terug pusht naar de repository. Het schema van dev niet. Pas elk bestand apart aan voor taken die per omgeving verschillen.
Bij het overzetten van een bestaande VPS installeer je eerst de orchestrator, registreer je de projecten, draai je scheduler:run --dry-run, draai je de wrapper één keer met de hand als www-data, en controleer je /var/log/nimbly-scheduler.log. Maak een back-up van de oude crontab van www-data onder /etc/nimbly/backups/, en haal daarna alleen de overgezette regels schedule:run per project weg. Haal geen cronjobs weg die er niets mee te maken hebben.
git pull vanuit de admin blijft beschikbaar onder Settings als eenvoudige manier om een zelfbeheerde site bij te werken. Kies voor sites met een releaseproces liever een herhaalbare deployment: pull de bedoelde Git-revisie, draai setup, build en lint, en zorg dat de scheduler actief is.