Introduzione
In molti ambienti Microsoft, il deploy non fallisce per il codice. Fallisce per il contesto. Un artifact creato in CI parte bene, poi in produzione trova dipendenze diverse, variabili diverse, o un servizio Windows diverso da quello testato.
Qui confronto due approcci pratici per una CI minima: deploy da artifact immutabile e deploy con rebuild sul server. Il primo riduce il drift. Il secondo è più rapido da mettere in piedi. La scelta dipende da quanta variabilità hai tra build e produzione.
Lo scenario è semplice: una web app o un servizio .NET ospitato su Windows Server, rilasciato da GitLab CI, con artifact versionati, cartella release e rollback immediato. L’obiettivo è evitare il classico problema: “in CI funziona, sul server no”.
Prerequisiti
- GitLab CI attivo.
- Runner con accesso al repository e al server di deploy.
- Server Windows con PowerShell 7 o PowerShell 5.1.
- Una cartella stabile, per esempio C:\apps\miosito.
- Permessi per scrivere release, aggiornare un link o un file di versione, e riavviare il servizio.
Note: i comandi sotto usano PowerShell e YAML. Se il tuo stack è PHP o Node su IIS, la logica resta identica. Cambiano solo i comandi di verifica e di riavvio.
Step 1: scegli il modello giusto prima di scrivere la pipeline
Il primo bivio è questo. Se il server di produzione deve solo estrarre un pacchetto già costruito, usa artifact immutabili. Se invece il server compila dipendenze native o genera file legati all’ambiente, il rebuild può essere inevitabile.
Approccio A: artifact immutabile. La CI compila, testa e impacchetta. Il server scarica e installa lo stesso identico file. È il modello migliore per ridurre environment drift.
Approccio B: rebuild sul server. La CI pubblica il sorgente o uno script. Il server esegue la build finale. È utile quando il build dipende da SDK locali, certificati di macchina, o da asset generati su quel sistema.
Warning: se tra build e deploy cambiano versione di runtime, variabili o pacchetti di sistema, il rebuild sul server nasconde il problema invece di risolverlo.
Quando scegliere A: quasi sempre, se puoi produrre un package deterministico. Quando scegliere B: solo se il server applica un vincolo reale, non per comodità operativa.
Step 2: costruisci un artifact unico e versionato
Con artifact immutabili, la pipeline deve produrre un file identificabile. Evita zip generici come app.zip. Usa il commit SHA o il numero di build. Così ogni release è tracciabile e rollbackabile.
Esempio GitLab CI minimale:
stages:
- build
- test
- package
- deploy
build:
stage: build
script:
- dotnet restore
- dotnet build -c Release --no-restore
test:
stage: test
script:
- dotnet test -c Release --no-build
package:
stage: package
script:
- dotnet publish -c Release -o out
- powershell -Command "Compress-Archive -Path out\* -DestinationPath release-$env:CI_COMMIT_SHORT_SHA.zip"
artifacts:
paths:
- release-$CI_COMMIT_SHORT_SHA.zip
expire_in: 14 days
# Output: un file come release-a1b2c3d.zip disponibile come artifact
Note: se il progetto usa Node, sostituisci dotnet publish con npm ci e una build che genera una cartella statica. Il principio non cambia.
Step 3: deploy con release directory e link stabile
La parte più robusta del deploy su Windows non è sovrascrivere la cartella attiva. È creare una nuova release, verificarla, poi puntare il servizio a quella versione. Questo rende il rollback quasi istantaneo.
Schema pratico:
- C:\apps\miosito\releases\20260325-163542\ contiene la nuova versione.
- C:\apps\miosito\current è un collegamento o un puntatore logico.
- Il servizio legge sempre da current.
Script PowerShell essenziale:
$release = "C:\apps\miosito\releases\$env:CI_COMMIT_SHORT_SHA"
New-Item -ItemType Directory -Force -Path $release | Out-Null
Expand-Archive -Path .\release-$env:CI_COMMIT_SHORT_SHA.zip -DestinationPath $release -Force
# Verifica minima prima del passaggio
Test-Path "$release\appsettings.json"
Test-Path "$release\MyApp.exe"
# Aggiorna il puntatore
cmd /c rmdir C:\apps\miosito\current
cmd /c mklink /D C:\apps\miosito\current $release
# Output: current punta alla nuova release e i file attesi esistono
Se il tuo ambiente usa IIS, puoi gestire il sito con il percorso current. Se usi un Windows Service, il servizio deve leggere l’eseguibile dentro la release attiva.
Note: il rebuild sul server spesso scrive sopra la cartella attiva. Questo sembra semplice, ma complica rollback e rende opaco il difetto introdotto dal deploy.
Step 4: aggiungi una verifica post-deploy che blocca il falso successo
Un deploy non è finito quando il file arriva sul server. È finito quando l’app risponde nel modo atteso. Serve una verifica concreta, breve e automatica.
Per una web app, fai almeno un controllo HTTP e uno funzionale. Per un servizio interno, controlla il processo o l’endpoint di health.
$r = Invoke-WebRequest -UseBasicParsing http://localhost/health
if ($r.StatusCode -ne 200) { throw "Health check failed" }
if ($r.Content -notmatch 'ok') { throw "Invalid health payload" }
# Output: StatusCode 200 e contenuto ok
Se usi IIS, fai anche un controllo dal pannello:
- vai in IIS Manager → Sites → il tuo sito.
- Verifica il Physical Path.
- Controlla che l’Application Pool sia avviato.
- Apri Browse e testa la risposta locale.
Note: il terminale è utile, ma il pannello aiuta quando devi confermare rapidamente che il sito punti alla release giusta.
Step 5: implementa rollback come cambio di puntatore, non come restore completo
Il rollback migliore non copia file da un backup. Cambia release e riavvia. Se il vecchio artifact è ancora presente, il ritorno indietro è veloce e prevedibile.
$previous = "C:\apps\miosito\releases\20260325-162900"
cmd /c rmdir C:\apps\miosito\current
cmd /c mklink /D C:\apps\miosito\current $previous
Restart-Service MyAppService
# Output: il servizio riparte usando la release precedente
Questo approccio vince sul rebuild anche quando il rollback è raro. Il motivo è semplice: il rollback di emergenza deve essere banale, non creativo.
Warning: un rollback che richiede reinstallare dipendenze o rigenerare asset non è un rollback. È un nuovo deploy sotto stress.
Step 6: confronta i due approcci sul drift reale
Il drift nasce quando ciò che esegui in produzione non corrisponde più a ciò che hai validato in CI. L’artifact immutabile riduce il drift perché la stessa build viene promossa. Il rebuild sul server lo aumenta, perché ogni esecuzione dipende dallo stato locale.
Usa artifact immutabili se vuoi:
- ripetibilità;
- audit più semplice;
- rollback rapido;
- meno sorprese tra test e produzione.
Usa rebuild sul server solo se hai un motivo concreto:
- SDK o toolchain installati solo sul server;
- certificati o chiavi locali necessarie alla build;
- asset generati da servizi on-prem non disponibili in CI;
- vincoli di licenza o rete che impediscono la build centralizzata.
Se il tuo unico argomento è “così è più comodo”, stai pagando quella comodità con più drift e meno controllo.
Verifica finale
Prima di considerare chiuso il rilascio, controlla questi punti:
- L’artifact ha un nome univoco e rintracciabile.
- La release è stata estratta in una cartella nuova.
- Il puntatore current è stato aggiornato.
- La health check restituisce 200.
- Il rollback precedente è ancora disponibile.
Se anche uno solo di questi punti manca, il processo non è ancora affidabile. La pipeline non deve solo consegnare codice. Deve permettere ritorno e diagnosi.
Troubleshooting
1) Expand-Archive : The archive file is invalid or corrupted.
Causa: l’artifact è stato caricato male o troncato durante il passaggio tra job e server.
Fix:
Get-FileHash .\release-$env:CI_COMMIT_SHORT_SHA.zip
# confronta l'hash con quello salvato in CI
# Output: hash coerente tra build e deploy
2) mklink : You do not have sufficient privilege to perform this operation.
Causa: il job gira senza privilegi amministrativi o il runner non può creare link simbolici.
Fix:
# usa un puntatore basato su file, oppure esegui il job con privilegi adeguati
New-Item -ItemType File C:\apps\miosito\current.txt -Value $release
# Output: il puntatore viene aggiornato senza symlink
3) Health check failed
Causa: la release è stata copiata correttamente, ma l’app non parte con la nuova configurazione.
Fix:
Get-Content C:\apps\miosito\current\appsettings.json
Restart-Service MyAppService
# Output: il servizio riparte e l’endpoint torna 200
Conclusione
Tra i due approcci, l’artifact immutabile è quello da preferire quando vuoi ridurre drift, semplificare il rollback e avere audit chiaro. Il rebuild sul server resta utile, ma solo quando il vincolo è reale.
Il prossimo passo concreto è trasformare il tuo deploy in release versionate con health check e rollback a puntatore. Se oggi sovrascrivi la cartella attiva, hai già trovato il primo punto da correggere.
Commenti (0)
Nessun commento ancora.
Segnala contenuto
Elimina commento
Eliminare definitivamente questo commento?
L'azione non si può annullare.