Distribuire applicazioni in modo efficiente, rapido e riproducibile in ambienti eterogenei può sembrare complesso. In questo articolo vi invito a scoprire il processo dettagliato di distribuzione di un back-end .NET APi in container Docker, abbinato a un'applicazione front-end Angular che ho sviluppato e introdotto in un precedente articolo.
Insieme vedremo come utilizzare Docker per distribuire queste applicazioni in modo efficiente su computer Raspberry Pi. Ognuno di questi piccoli computer, che nella nostra architettura funziona come un server indipendente, sarà collegato a un server IBM i.
La nostra API .NET, utilizzando il provider di dati NTi, funge da livello di servizio, esponendo dati e funzionalità da un database DB2 for i ospitato su IBM i, nonché da un altro database Postgre SQL in esecuzione su un Raspberry 4. L'applicazione Angular consuma e utilizza questi servizi per fornire l'interfaccia utente.
Requisiti e ambiente di sviluppo
💻 IDE:
L'API .NET è stata sviluppata utilizzando Visual Studio, sfruttando le sue funzionalità integrate per la gestione dei container Docker.
L'applicazione ANGULAR è stata sviluppata utilizzando Visual Studio Code, apprezzato per la sua leggerezza e flessibilità, soprattutto per i progetti JavaScript / TypeScript.
🍓Sistema di destinazione:
Le applicazioni sono progettate per essere distribuite su Raspberry Pi 4 e 5. Questi nano computer con processore ARM sono collegati a un IBM i.
🐳 Docker:
Docker desktop, installato sulla mia macchina di sviluppo, mi consentirà di containerizzare le applicazioni, facilitando la loro distribuzione su architetture hardware diverse, in particolare quelle del Raspberry Pi.
PARTE 1 - Distribuzione dell'API .NET.
Passo 1 - Creare il file Docker con VISUAL STUDIO.
Il primo passo consiste nel creare un Dockerfile per l'API .NET. Visual Studio semplifica questo processo grazie all'integrazione nativa con Docker:
Fare clic con il tasto destro del mouse sul progetto > Aggiungi > Supportato da Docker.
Visual Studio genera un file Docker personalizzato per l'applicazione, preconfigurato per l'ambiente .NET.
Questo file definisce le istruzioni per la creazione dell'immagine Docker dell'applicazione, comprese le fasi di base, compilazione, pubblicazione e la fase finale per l'esecuzione dell'applicazione.
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["AccessDataAPI.csproj", "."]
RUN dotnet restore "./././AccessDataAPI.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./AccessDataAPI.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./AccessDataAPI.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "AccessDataAPI.dll"]
- Fase di base:** Utilizzare l'immagine ufficiale ASP.NET Docker per creare un ambiente di runtime per l'applicazione. Le porte 8080 e 8081 sono esposte per il traffico HTTP e HTTPS.
- Fase di compilazione:** Utilizza l'immagine SDK per compilare l'applicazione. La gestione delle dipendenze e la compilazione vengono eseguite in questa fase.
- Fase di pubblicazione:** Pubblica l'applicazione compilata, pronta per la distribuzione.
- Fase finale: ¨Prepara l'immagine finale copiando l'applicazione pubblicata nell'ambiente di runtime.
Passo 2 - Aggiunta di CORS (Cross Origin Resource Sharing).
using AccessDataAPI.Services;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
var builder = WebApplication.CreateBuilder(args);
// Aggiungere la configurazione CORS per autorizzare le richieste dell'applicazione Angular HotelAppManager.
builder.Services.AddCors(options =>
{
options.AddPolicy("MyCorsPolicy", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Per consentire alla nostra API .NET di accettare richieste da altre origini, come la nostra applicazione Angular che sarà ospitata su un server diverso, configuriamo CORS (Cross Origin Resource Sharing) nel file "programma.cs".
I CORS sono essenziali e consentono alla nostra applicazione front-end di comunicare in modo semplice con il nostro back-end.
Passo 3 - Configurazione di LaunchSettings.json
Questo file definisce diversi profili per l'avvio dell'applicazione, compreso lo sviluppo locale tramite HTTP o HTTPS, nonché Docker.
- I profili HTTP e HTTPS:** consentono di avviare l'applicazione in locale, con o senza SSL, specificando l'URL di avvio, le porte utilizzate e le variabili d'ambiente.
- Il profilo Docker:** indica a Visual Studio come lanciare l'applicazione in Docker, compresa la mappatura delle porte e l'uso di SSL. Le variabili d'ambiente sono impostate qui per corrispondere alle porte esposte nel Dockerfile.
Passo 4 - Distribuzione su Docker
Una volta configurati il file Docker, le impostazioni di lancio e i CORS, è ora il momento di creare l'immagine DOCKER di .NET APi usando la riga di comando.
Nella cartella corrente della nostra applicazione, useremo Docker Buildx per creare un'immagine multi-architettura di .NET APi, essenziale per distribuire l'applicazione su Raspberry Pi.
docker buildx build --platform linux/arm64/v8,linux/amd64 -t quentindaumerial/accessdataapi:latest --push -f Dockerfile .
L'immagine viene inviata direttamente al mio Docker Hub, confermando che la compilazione è stata completata.
Per convalidare il funzionamento di .NET APi in un ambiente simile a quello di distribuzione, eseguo l'immagine localmente con "docker run".
Mappando le porte necessarie e configurando le variabili d'ambiente per SSL, simulo l'ambiente di produzione sulla mia macchina di sviluppo.
docker run --rm -it -p 7245:8080 -p 7246:8081 -e ASPNETCORE_HTTPS_PORTS=8081 -e ASPNETCORE_HTTP_PORTS=8080 -e ASPNETCORE_Kestrel__Certificates__Default__Password="password" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v %USERPROFILE%\.aspnet\https:/https/ accessdataapi
Collegandomi a https://localhost:7246/HotelCustomer/clients dal mio browser web, posso accedere ai dati esposti dalla mia API direttamente da DB2 for i. Questa convalida garantisce che la mia API .NET sia pronta per essere distribuita su Raspberry 5.
Passo 5 - Inviare l'immagine al RASPBERRY Pi 5.
Dopo aver spinto l'immagine nel registro del mio hub Docker, è ora il momento di connettersi al Raspberry Pi in SSH, scaricare l'immagine direttamente dal mio hub e lanciarla in un contenitore.
- Connettersi al Raspberry 5 usando SSH:
- Scaricare l'immagine dal mio Hub Docker:
docker image pull quentindaumerial/accessdataapi:latest
Passo 6 - Avviare il contenitore Docker sul Raspberry 5.
Lancio il mio contenitore mappando la porta 8080 sulla porta 5040 del mio Raspberry Pi 5:
docker run -it -p 5040:8080 quentindaumerial/accessdataapi:latest
Ed ecco fatto! La mia API .NET è ora in esecuzione in un contenitore Docker sul mio Raspberry Pi 5, che a sua volta è collegato al mio IBM i. Posso verificarlo direttamente andando su http://192.168.46.34:5040/hotelcustomer/clients.
PARTE 2 - Distribuzione dell'applicazione ANGULAR.
Passo 1 - Preparazione del file Docker.
Come per l'API .NET, per rendere docker la nostra applicazione ANGULAR, dovremo andare nella cartella dell'applicazione e creare un file chiamato Dockerfile senza estensione.
È composto da due fasi: build e run.
### STAGE 1:BUILD ###
FROM node:latest AS build
WORKDIR /app
COPY package.json ./
RUN npm install
RUN npm build --configuration=production
### STAGE 2:RUN ###
FROM nginx:latest
COPY dist/app-data-manager usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
La fase di compilazione utilizzerà l'ultima versione dell'immagine "node" per installare le dipendenze e costruire l'applicazione Angular specificando una configurazione di produzione (dettagliata nella fase 4 ).
La fase di runtime utilizzerà l'ultima versione dell'immagine "nginx" per servire la nostra applicazione. I file di distribuzione generati vengono copiati in /usr/share/nginx/html, la cartella predefinita usata da NGINX per servire i contenitori web.
Passo 2 - Creare il file nginx.conf.
Questo file configura NGINX per servire correttamente la nostra applicazione ANGULAR, supportando il routing lato client.
events{}
http {
include /etc/nginx/mime.types;
server {
root /usr/share/nginx/html/browser;
index /index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
}
Il blocco "server" definisce dove trovare i file dell'applicazione e come rispondere alle richieste HTTP.
La direttiva "try_files" indica a NGINX di servire index.html per tutti i percorsi lato client.
Passo 3 - Configurazione degli ambienti di sviluppo e di produzione.
Come si può vedere dal file Docker, abbiamo specificato una build con "configurazione di produzione".
Questo perché Angular è in grado di differenziare e gestire in modo nativo le configurazioni specifiche per gli ambienti di sviluppo e di produzione, per garantire che l'applicazione si comporti come previsto in ogni contesto (in particolare la connessione alle mie API).
- Per lo sviluppo ("src/environments/environment.ts"):**
Contiene la configurazione usata durante lo sviluppo, compreso l'URL di base dell'API che punta al server di sviluppo, configurato nel file launchsettings della nostra API .NET.
export const environment = {
production: false,
apiBaseUrl: 'https://localhost:7246/'
};
Per la produzione ("src/environments/environment.prod.ts"):**
Contiene la configurazione utilizzata per la versione di produzione dell'applicazione con un URL che punta al server di produzione (in questo caso il nostro secondo Raspberry Pi, accessibile all'indirizzo 192.168.46.34, sulla porta 5040).
export const environment = {
production: true,
apiBaseUrl: 'http://192.168.46.34:5040/'
};
Questi file consentono di passare in modo semplificato dalla configurazione di sviluppo a quella di produzione, semplicemente cambiando il flag "production" durante la creazione dell'applicazione.
Il comando ng serve di Angular mi consente di accedere al mio server di sviluppo su localhost, porta 4200.
Passo 4 - Creare l'immagine Docker.
Come per le API .NET, è importante creare un'immagine multipiattaforma per garantire che l'applicazione Angular sia compatibile con diverse architetture.
Quindi, ancora una volta, usiamo Docker Buildx per creare un'immagine che funzioni sia su architetture amd64 (tipiche di PC e server) sia su arm64/v8 (dispositivi arm come Raspberry).
Il comando seguente avvia la creazione dell'immagine e la invia al mio Dockerhub:
docker buildx build --platform linux/amd64,linux/arm64,linux/arm64/v8 -t quentindaumerial/appdatamanager:latest --push --no-cache .
La mia immagine è aggiornata su Dockerhub:
Passo 5 - Invio dell'immagine al Raspberry 4.
Dopo aver costruito e spinto l'immagine della mia applicazione Angular su Dockerhub, mi collego al Raspberry Pi 4 tramite SSH:
Recupero l'ultima versione della mia immagine dal mio hub, per renderla disponibile sul Raspberry Pi:
docker image pull quentindaumerial/appdatamanager:latest
Passo 6 - Avviare il contenitore Docker sul Raspberry 4.
Ora che l'immagine è stata scaricata, lancio un container Docker per distribuire l'applicazione Angular, mappando la porta 80 del container sulla porta 5000 del Raspberry pi 4:
docker run -it --platform linux/arm64/v8 -p 5000:80 quentindaumerial/appdatamanager:latest
L'applicazione è ora accessibile tramite un browser web all'indirizzo "**http://192.168.46.31:5000**":
In particolare, possiamo vedere che i dati provengono effettivamente dal nostro APi, che consuma i dati esposti da DB2 for i e recuperati tramite NTi da .NET:
CONCLUSIONE
Impostando Dockerfiles specifici, configurando nginx.conf in modo appropriato per Angular e regolando i CORS e i parametri di lancio del nostro APi .NET, è possibile creare semplicemente un ecosistema in cui le due applicazioni funzionano perfettamente in modo isolato. Il risultato? Un recupero dei dati semplice e senza problemi, che illustra l'interconnessione in un ambiente containerizzato.
L'uso del connettore NTi svolge un ruolo essenziale nella nostra API .NET, facilitando la connessione a DB2 for i. Dimostra che è possibile eseguire applicazioni .NET CORE in modo nativo, interagendo con i dati su IBM i, pur rimanendo in un ambiente Dockerizzato. La facilità di implementazione di un'API .NET e di un'applicazione client, unita alla capacità del nostro fornitore di dati NTi di operare in modo nativo in .NET, sottolinea non solo la semplicità di gestione di tali applicazioni, ma anche il loro potenziale di scalabilità. Questo progetto è solo un esempio dei molti modi in cui oggi possiamo ripensare l'uso della tecnologia per creare, innovare e ottimizzare i processi di sviluppo e distribuzione.
Spero che questa guida vi abbia fornito le conoscenze e l'ispirazione per esplorare nuovi metodi e strumenti per i vostri progetti. Docker è qui per rendere la distribuzione delle applicazioni più semplice, veloce e affidabile, consentendovi di concentrarvi su ciò che conta davvero: soddisfare efficacemente le esigenze specifiche di ogni progetto dei clienti.
Quentin Destrade