Files
agiledancers/docs/production-deployment.md
Wanja Krah 694bf3fa86
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
S16 postgresql opslag added
2026-11-04 17:10:25 +01:00

16 KiB

Productie uitrollen op de Synology (S6)

De buildworkflow bouwt, test en publiceert automatisch. Productie verandert alleen via Actions → Deploy production → Run workflow of Rollback production. Start beide vanaf main; de workflows verschijnen na de merge.

Voor de eerste S16-deployment: exporteer alle scholen voordat de oude backend stopt. De oude release bewaart gegevens alleen in geheugen. Configureer ook DATABASE_PASSWORD in production.env. Zie database-inrichting en overgang. Nieuwe releases gebruiken een apart PostgreSQL-volume; verwijder dit volume niet. Onderbroken berekeningen worden als Interrupted hersteld en starten niet vanzelf.

Eenmalige DSM-inrichting

Gebruik je bestaande beheeraccount voor deze stappen:

  1. Maak onder Configuratiescherm → Gebruiker en groep → Gebruiker → Maken het account agiledancers-deploy met een uniek sterk wachtwoord. Voeg het toe aan administrators: DSM vereist deze groep voor SSH. Het is daardoor een beheerder, geen beperkt bestandsaccount.
  2. Bij Gedeelde mapmachtigingen: docker op Lezen/Schrijven, homes ongewijzigd, overige mappen op Geen toegang. git is niet nodig: deployment gebruikt de Gitea-API en registry. Blokkeer homes niet, omdat dit ook de eigen thuismap kan blokkeren.
  3. De toepassingen onder Toegangsmachtigingen toewijzen zijn niet nodig en mogen op Weigeren. Gebruik je eigen beheeraccount voor DSM. Deployment gebruikt SSH-shellopdrachten, geen SFTP, rsync of bestandsdeling.
  4. Schakel onder Gebruiker en groep → Geavanceerd de gebruikersbasismapservice in als deze uitstaat.
  5. Schakel Terminal en SNMP → Terminal → SSH-service inschakelen in. De maintainer heeft SSH-poort 22 bevestigd. Beperk toegang tot mini-pc/LAN/VPN; configureer geen internet-portforwarding.
  6. Controleer dat Container Manager/Docker draait, dat Docker Compose v2, curl, timeout met -k en sh beschikbaar zijn. Op de NAS is geen Java, Python of Git nodig. Controleer het Docker-pad, doorgaans /usr/local/bin/docker.

Map- en toepassingsrechten vormen geen beveiligde isolatie voor een account met beheerder-/Dockerrechten. De runner voert ook vertrouwde repositorycode uit. Geef alleen vertrouwde beheerders schrijftoegang tot workflows en credentials.

Bronnen: SSH op DSM, sleutelaanmelding en basismappen, maprechten.

SSH-sleutel en hostkey

Maak op de mini-pc een aparte sleutel buiten de repository. De lege passphrase maakt niet-interactieve uitvoering mogelijk; behandel de private sleutel als secret.

ssh-keygen -t ed25519 -f "$HOME/.config/agiledancers-runner/s6-deploy-key" -N '' -C agiledancers-production
ssh-copy-id -i "$HOME/.config/agiledancers-runner/s6-deploy-key.pub" -p 22 agiledancers-deploy@192.168.2.28

Genereer niet opnieuw als de sleutel al bestaat. De implementatiesessie heeft deze dedicated sleutel op de mini-pc aangemaakt. De gebruiker installeert de publieke sleutel via een eigen interactieve terminal; deel geen wachtwoorden.

Controleer de serverfingerprint via een reeds vertrouwde beheerverbinding, tegen /etc/ssh/ssh_host_ed25519_key.pub. Een keyscan alleen bewijst de serveridentiteit niet. Bewaar de geverifieerde known_hosts-regel; bij een andere poort gebruikt die [host]:poort. De helper vereist StrictHostKeyChecking=yes, gebruikt alleen de dedicated sleutel en negeert SSH-agent en lokale SSH-configuratie.

Op de NAS: eigen ~/.ssh mode 700, authorized_keys mode 600, juiste eigenaar, thuismap niet schrijfbaar voor andere gebruikers. De public-keyregel kan worden beperkt met de werkelijke vaste mini-pc-IP en zonder forwarding/TTY:

from="<mini-pc-LAN-IP>",no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty ssh-ed25519 <public-key> agiledancers-production

Als de sleutel na ssh-copy-id nog wordt geweigerd door ruime DSM-basismaprechten, voer dan eenmaal met wachtwoordauthenticatie uit als dit account:

chmod go-w "$HOME"
chmod 700 "$HOME/.ssh"
chmod 600 "$HOME/.ssh/authorized_keys"

Productiemap en Dockerrechten

Controleer het volume: /volume1/docker/agiledancers-production is een voorbeeld, geen vastgestelde locatie. Maak de map als deploymentaccount, mode 700, met dat account als eigenaar. Houd deze stack gescheiden van Gitea en andere projecten.

Kopieer deploy/production.env.example naar <productiemap>/production.env, mode 600. Stel de private NAS-IP, vrije frontendpoort (voorbeeld 8088), absolute Docker-locatie en DOCKER_CONFIG in. Gebruik een eigen docker-auth-submap. Alleen de frontend bindt aan de private IPv4; de backend krijgt geen hostpoort. De Docker-projectnaam is vast agiledancers-production.

DSM-firewall

De bevestigde productieomgeving gebruikt frontendpoort 8088 op 192.168.2.28 en Docker-subnet 172.18.0.0/16. Maak in Configuratiescherm → Beveiliging → Firewall → Regels bewerken toestaan-regels op Alle interfaces, boven eventuele weigerregels:

Poort Protocol Bron Actie
8088 TCP 192.168.2.0, masker 255.255.255.0 Toestaan
8080 TCP 172.18.0.0, masker 255.255.0.0 Toestaan

De eerste regel geeft LAN-clients toegang; de tweede laat de frontend de backend bereiken. Voeg voor bestaande VPN-clients zo nodig hun eigen bron-subnet toe aan de frontendregel. Er wordt geen backendpoort op de NAS gepubliceerd en de firewall blijft aan. Controleer bij het opnieuw aanmaken van het Docker-netwerk het werkelijke subnet met docker network inspect en pas de regel aan als dat subnet wijzigt. Gewone deployments vervangen containers en behouden het netwerk.

De tijdelijke S6-acceptatieregel voor 172.20.0.0/16 mag na de geïsoleerde tests weg; dat testnetwerk en de testcontainers zijn opgeruimd.

Als Docker sudo vereist, laat een beheerder met visudo een afzonderlijke sudoers-regel installeren en valideren, met het gecontroleerde Docker-pad:

agiledancers-deploy ALL=(root) NOPASSWD: /usr/local/bin/docker

Dit staat alleen dat executable toe, maar Dockerbeheer geeft in de praktijk rootmogelijkheden. Gebruik geen NOPASSWD: ALL, wereldschrijfbare Docker-socket of wachtwoord in de workflow. De helper gebruikt sudo -n. Als Docker al direct toegankelijk is voor het account, stel DOCKER_SUDO=0 in.

Provision apart een pull-only Gitea-packagecredential. Log als deploymentaccount op de NAS in, via de interactieve wachtwoordprompt:

mkdir -p /volume1/docker/agiledancers-production/docker-auth
chmod 700 /volume1/docker/agiledancers-production/docker-auth
sudo -n /usr/local/bin/docker --config /volume1/docker/agiledancers-production/docker-auth login 127.0.0.1:3000 --username <pull-account>
sudo -n /usr/local/bin/docker compose version
sudo -n /usr/local/bin/docker info --format '{{.OSType}}/{{.Architecture}}'

Als DSM geen visudo bevat, is deploy/enable-docker.sh het eenmalige alternatief. Kopieer het naar de eigen NAS-thuismap en voer het daar met sudo uit. Het maakt uitsluitend de bovengenoemde regel, controleert die door als deploymentaccount Docker te starten en verwijdert een nieuwe regel als die controle faalt. Een afwijkende bestaande regel wordt niet overschreven. De implementatiesessie heeft deze stap op de NAS uitgevoerd; Docker rapporteert linux/x86_64.

Pas paden aan voor de echte configuratie. De bevestigde NAS-Dockerdaemon staat HTTP-registryverkeer alleen op loopback toe. Gebruik daarom vanaf die NAS 127.0.0.1:3000: dezelfde Gitea-registry en gepubliceerde SHA-images, zonder een daemonherstart die Gitea en Kimai zou onderbreken. De mini-pc blijft naar 192.168.2.28:3000 publiceren. Deploymentrecords bevatten de op de NAS bruikbare loopback-digestreferenties. Authenticatie-/netwerkfouten zijn deploymentfouten.

Gitea-configuratie

Stel onder repository Settings → Actions in:

Soort Naam Inhoud
Variabele DEPLOY_HOST Private NAS-IP/hostnaam, passend bij known_hosts
Variabele DEPLOY_USER agiledancers-deploy
Variabele DEPLOY_SSH_PORT 22
Variabele DEPLOY_DIRECTORY Absolute productiemap
Variabele DEPLOY_REGISTRY 127.0.0.1:3000, de NAS-lokale alias van dezelfde registry
Secret DEPLOY_SSH_KEY Volledige private sleutel, inclusief begin/eindregels
Secret DEPLOY_KNOWN_HOSTS Onafhankelijk geverifieerde NAS-hostkey-regel
Secret RELEASE_TOKEN Apart Gitea-token voor Actions lezen en Git-tags maken

Het releaseaccount heeft repositorylees-/schrijfrechten en Actions-metadatatoegang nodig (write:repository op deze Gitea). Het NAS-registrycredential is apart en heeft alleen de noodzakelijke package-leestoegang. Hergebruik de publicatie-PAT niet voor deployment. Bewaar secrets buiten Git en logs.

De setuphelper kan deze configuratie en de productiemap zonder containerstart inrichten. Bewaar admin-token, s6-release-token, s6-pull-token en de private s6-deploy-key als owner-only bestanden (mode 600) in ~/.config/agiledancers-runner/. Gebruik de eerder vertrouwde NAS-hostkey in ~/.ssh/known_hosts en voer uit:

java ci/Ci.java configure-deployment

Defaults zijn de bevestigde NAS/account/SSH-poort, repository wanjakrah/agiledancers, productiemap /volume1/docker/agiledancers-production, NAS-IP-binding 192.168.2.28 en frontendpoort 8088. De helper controleert een geslaagde main-build, weigert een bezette poort bij eerste inrichting, bewaart bestaande productieconfiguratie en controleert de aparte registry-login voordat Gitea-secrets/variabelen worden gezet. Er wordt geen release/tag gemaakt en geen container vervangen. Variabelen met de namen uit bovenstaande tabel en DEPLOY_BIND_IP, DEPLOY_FRONTEND_PORT, PULL_USER en de credential-*_FILE- paden kunnen de defaults expliciet vervangen.

Bescherm release-tags v* in Gitea en beperk tagcreatie tot de releasebeheerder. De helper maakt alleen nieuwe tags; hij verplaatst of verwijdert ze nooit. Laat registry-cleanup de images van huidig/vorig niet verwijderen. Gebruik geen algemene Docker-image/volume-prune als onderhoud van deze stack.

Nieuwe release, retry en rollback

  1. Merge naar main en wacht op een geslaagde Build, test and publish voor die SHA, inclusief imagepublicatie.
  2. Open Deploy production → Run workflow, branch main, mode new, keuze patch, minor of major. Laat het versieveld leeg. De eerste release is v1.0.0; daarna telt de hoogste bestaande release-tag, inclusief mislukte deployments. Minor/major zetten de lagere componenten op nul.
  3. De workflow toont versie/SHA. De immutable annotated Git-tag wordt na imagevoorbereiding, vóór vervanging gemaakt en blijft bij falen bestaan. Een tag bewijst geen succesvolle productiedeployment.
  4. Gebruik mode retry met een bestaande tag om dezelfde SHA/versie opnieuw uit te rollen. De incrementkeuze wordt genegeerd: Gitea toont alle velden, ook wanneer ze voor de gekozen mode niet relevant zijn. Een retry mag afwijken van huidig main, maar geen nieuwere succesvolle release verdringen.
  5. Rollback production → Run workflow → main herstelt de vorige succesvolle release zonder versie/tag. Na succes wisselen huidig/vorig om; nogmaals rollback kan dus terugschakelen. De hoogste succesvolle versie blijft bewaard.

De NAS-lock omvat alle modes en versieallocatie. Een gelijktijdige run faalt zichtbaar. Gitea 1.25 negeert workflow-concurrency; de serverlock is leidend. Main wordt direct vóór vervanging opnieuw gecontroleerd. Zodra vervanging start, wordt de gepinde versie afgemaakt of hersteld. Branchbuilds rollen niets uit.

Checks en operationele state

De helper haalt beide SHA-images terug, controleert revisie/platform en legt registry-digests vast. Compose gebruikt die digests, nooit de bewegende main-tag. De recovery-images worden eveneens vooraf gecontroleerd. Binnen vijf minuten moeten backend-readiness, frontendpagina, GET /api/dance-schools via de gepubliceerde frontendpoort en containeridentiteit slagen. Deze checks schrijven geen gebruikersdata. De frontend herstart ook om de nieuwe backend-IP te resolven.

De productiemap bevat:

  • state.json: atomisch geschreven huidig/vorig en hoogste succesvolle versie. Redeployment van dezelfde versie behoudt de afzonderlijke rollbackversie.
  • releases/<attempt>/: behouden Compose- en digest-/versieconfiguratie.
  • pending.json: kandidaat en vorige state, vóór iedere vervanging geschreven.
  • deploy.lock/owner: unieke run/poging; operation/ markeert een actieve vervanging op de NAS.
  • history/: pogingjournalen en uitkomsten; niet opruimen tijdens een release.

Een ongezonde kandidaat herstelt automatisch de vorige succesvolle release en blijft een mislukte workflow. Bij de eerste release zonder voorganger, mislukt herstel, runner/SSH-onderbreking of onduidelijke statecommit blijven journal en lock staan. Nieuwe runs weigeren dan wijzigingen. Remote opdrachten hebben ook op de NAS een timeout; herstel overlapt geen nog actieve vervanging. Geen eindeloze herstelpogingen.

Download artifact deployment-<run>-<attempt> of rollback-<run>-<attempt> voor log, uitkomst, versie/SHA/digests en beschikbare foutdiagnostiek. Retentie is veertien dagen; serverinstellingen mogen dit niet inkorten. Log geen environment, Docker-authconfig of volledige container-inspect met eventuele secrets.

Handmatig herstel

Gebruik een vertrouwde beheerverbinding. Verwijder nooit alleen de lock om een nieuwe release door te drukken.

  1. Annuleer de betrokken Gitea-run. Controleer op de NAS dat geen SSH-deployment, timeout, Compose-vervanging of herstel van deze poging meer actief is. Onderzoek deploy.lock/operation; wacht op de timeout of beëindig alleen de geverifieerde vastgelopen deploymentprocessen.

  2. Bewaar state, pending journal, history en containerlogs. Vergelijk de kandidaat, de before-state en werkelijke images. De statecommit kan al zijn uitgevoerd terwijl de runner geen antwoord ontving.

  3. Behoud een aantoonbaar gezonde kandidaat met overeenkomende state. Anders: herstel before.current uit het journal met de behouden Compose-/env-bestanden, dezelfde projectnaam en digest-images. Start geen tweede stack.

  4. Voer dezelfde checks uit. Vanaf de checkout op de mini-pc kun je het echte controlescript uitvoeren met bestaande lockowner en gekozen release-directory:

    ssh -i <private-key-path> -p <ssh-port> <deploy-user>@<nas> \
      'timeout -k 5 300 sh -s -- apply <production-directory> <lock-owner> <release-directory-id>' < deploy/remote.sh
    

    Gebruik de eerder gepinde hostkey en gecontroleerde waarden uit het journal. Verwijder een achtergebleven lege operation/ alleen als stap 1 heeft bewezen dat de oude operatie gestopt is.

  5. Bij herstel van de oude release: herstel ook de volledige before-state atomisch, inclusief high-watermark en rollbackdoel. Bij een mislukte eerste release: stop de defecte stack en behoud de lege succesvolle state. Verwijder geen volumes of release-tags.

  6. Noteer het herstel in history, archiveer het opgeloste journal en verwijder daarna pas de lockowner en lege lockdirectory. Start vervolgens een retry. Laat de blokkade staan als gezondheid of state onduidelijk is.

Na S16 en lokale verificatie

De databaseservice en het volume agiledancers-production-database blijven buiten de vervanging van backend en frontend. production.env levert het databasewachtwoord ook bij retry en rollback; het staat niet in releasebestanden. De procedure vereist databasecompatibiliteit met de vorige applicatieversie. Plan incompatibele migraties apart: imageherstel maakt geen migratie ongedaan. Rollback naar een oude release van vóór S16 kan de database niet lezen. Backup/restore blijven S17-werk.

java ci/Ci.java test
java ci/Ci.java release-test
sh -n deploy/remote.sh
mvn -N -Dformat.mode=check exec:exec@format

De buildworkflow voert beide Java-testsuites uit vóór publicatie. Release-tests wijzigen geen productie, registry of tags. Leg echte NAS-/Gitea-bewijzen vast in docs/s6-validation.md; configuratie en lokale tests alleen voltooien S6 niet.