Publication dans un container Docker Linux
Si l'application doit être déployée dans un environnement Docker, il est fortement conseillé de la tester dans cet environnement. Le fichier Dockerfile ci-dessous permet de tester une application Lagoon.
Cette même image Docker permet également de tester en local une application avant son déploiement sur un Azure App Service Linux, l'App Service exécutant en réalité le conteneur Docker de l'application. Construire et lancer l'image comme décrit ci-dessous permet de valider le comportement de l'application dans un environnement Linux proche de celui de l'App Service, avant de la publier sur Azure.
Dockerfile Lagoon
Le fichier Dockerfile suivant permet la compilation de l'application à partir des sources et la création d'un conteneur avec cette application. Il doit se trouver à la racine du projet lors de l'exécution des commandes.
# Arguments passés dans la ligne de commande build
ARG DOTNET_VERSION=8.0
ARG DISTRIB_VERSION=alpine
# === Publication du projet depuis les sources en utilisant une image Docker du SDK Dotnet ===
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-${DISTRIB_VERSION} AS build
# Arguments
ARG PROJECT_NAME
ARG LGN_VERSION=3.*
ARG DISTRIB_VERSION
WORKDIR /src
# Copie des sources dans le conteneur
COPY Server/${PROJECT_NAME}.Server.csproj ./Server/
COPY Client/${PROJECT_NAME}.Client.csproj ./Client/
COPY Shared/${PROJECT_NAME}.Shared.csproj ./Shared/
COPY Model/${PROJECT_NAME}.Model.csproj ./Model/
COPY Services/${PROJECT_NAME}.Services.csproj ./Services/
COPY Batch/${PROJECT_NAME}.Batch.csproj ./Batch/
# Installation du package Lagoon et LGN
RUN dotnet nuget add source https://nuget.lagoon-toolkit.com/index.json -n Lagoon \
&& dotnet tool install --global LGN --version ${LGN_VERSION}
ENV PATH="/root/.dotnet/tools:${PATH}"
# Installation de Node.js et NPM, nécessaires à la publication.
RUN if [ "${DISTRIB_VERSION}" = "alpine" ]; then \
apk update && apk add nodejs && apk add npm && npm install -g npm; \
else \
apt-get update && apt-get install -y nodejs && apt-get install -y npm && npm install -g npm; \
fi
# Publication de l'application
COPY . .
RUN dotnet publish "./Server/${PROJECT_NAME}.Server.csproj" -c Release -o /app/publish
# === Création de l'image finale qui exécute le serveur ===
FROM mcr.microsoft.com/dotnet/aspnet:${DOTNET_VERSION}-${DISTRIB_VERSION} AS base
# Arguments
ARG PROJECT_NAME
ARG ENVIRONNEMENT
ARG DISTRIB_VERSION
ENV PROJECT_NAME_ENV=${PROJECT_NAME}
ENV PROJECT_SETTING_ENV=${ENVIRONNEMENT}
WORKDIR /app
# Création des répertoires applicatifs.
RUN mkdir -p /logs \
&& mkdir -p /eula
# Déclaration du port exposé.
EXPOSE 80
# Mise à jour du système.
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
RUN if [ "${DISTRIB_VERSION}" = "alpine" ]; then \
apk update && apk add --no-cache icu-libs; \
else \
apt-get update; \
fi
# Copie des fichiers de publication.
COPY --from=build /app/publish .
# Création de l'utilisateur afin de ne pas avoir les droits administrateur.
RUN if [ "${DISTRIB_VERSION}" = "alpine" ]; then \
adduser -D lagoon lagoon; \
else \
adduser --disabled-password lagoon; \
fi
RUN chown -R lagoon /app && chown -R lagoon /logs && chown -R lagoon /eula
USER lagoon:lagoon
# Lancement du serveur.
ENTRYPOINT dotnet "${PROJECT_NAME_ENV}.Server.dll"
La construction de l'image se fait en deux phases :
- une image temporaire permet la compilation des sources et la publication ;
- une image finale copie les fichiers de publication et exécute le serveur.
Créer l'image :
docker build -t my_image --build-arg PROJECT_NAME="MYPROJECT" .
Les paramètres qui peuvent être passés à la commande build avec --build-arg PARAMETRE=VALEUR sont :
| Paramètre | Description |
|---|---|
PROJECT_NAME | Obligatoire. Nom du projet. Il doit correspondre au nom des DLL. |
ENVIRONNEMENT | Permet d'utiliser un fichier appsettings différent de celui de base, par exemple --build-arg ENVIRONNEMENT="Production" pour appsettings.Production.json. |
LGN_VERSION | Permet de préciser le numéro de version de LGN. S'il n'est pas renseigné, la dernière version est prise. |
DOTNET_VERSION | Permet de spécifier la version du runtime .NET de la distribution utilisée pour le build et l'exécution. Par défaut, la version 8.0 est utilisée. |
DISTRIB_VERSION | Permet de définir la distribution Linux à utiliser pour le build et l'exécution. Par défaut, la distribution Alpine est utilisée. |
Créer le conteneur :
docker run -p <external port>:80 --name mycontainer my_image
Il est possible de modifier des paramètres du fichier appsettings dans la commande run. Par exemple, pour préciser la chaîne de connexion à la base de données :
docker run -p <external port>:80 -e "ConnectionStrings__LocalDb=<Connexion string>" --name mycontainer my_image
Dans cet exemple, on retrouve l'arborescence du appsettings avec deux caractères underscore comme séparateur de niveau.
Cas particuliers
Volumes
S'il faut monter des volumes particuliers pour la persistance de certaines données, il faut le voir en amont du projet avec le client afin d'être d'accord sur le chemin à définir au niveau Docker.
Si le client a déjà des volumes existants, il est possible de les utiliser :
docker run -p <external port>:80 -v <nom du volume>:<chemin conteneur> --name mycontainer my_image
Pour utiliser un répertoire de l'hôte comme volume :
docker run -p <external port>:80 -v c:/Temp/TEST:/usr/share/test --name mycontainer my_image
Dans cet exemple, le répertoire hôte c:\Temp\Test est accessible dans le conteneur au chemin /usr/share/test.
Problème avec la distribution Linux
Il se peut, notamment dans l'environnement Kubernetes, que l'image Alpine pose des problèmes : https://github.com/kubernetes/kubernetes/issues/64924
Dans ce cas, utiliser une image Debian, par exemple Bookworm, Debian 12 ou plus. Passer le type de distribution dans la commande de build :
docker build -t wiplan_image --build-arg PROJECT_NAME="MYPROJECT" --build-arg ENVIRONNEMENT="Production" --build-arg DISTRIB_VERSION="bookworm-slim" .
Erreur TLS sur la connexion à SQL Server
Si une application utilisant SQL Server retourne une erreur de type A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider : SSL Provider, error: 31 - Encryption(ssl/tls) handshake failed), ajouter dans la partie serveur, avant la commande ENTRYPOINT :
# Fix TLS error
RUN sed -i 's/providers = provider_sect/providers = provider_sect\n\
ssl_conf = ssl_sect\n\
\n\
[ssl_sect]\n\
system_default = system_default_sect\n\
\n\
[system_default_sect]\n\
Options = UnsafeLegacyRenegotiation\n\
MinProtocol = TLSv1.2\n\
CipherString = DEFAULT@SECLEVEL=0/' /etc/ssl/openssl.cnf;
Tester un déploiement Azure App Service Linux
Un Azure App Service Linux exécute l'application dans un conteneur Docker. Il est donc possible, et recommandé, de tester ce conteneur en local avant de le déployer sur Azure, afin de détecter les éventuels problèmes liés à l'environnement Linux (chemins, casse des fichiers, dépendances système, etc.).
Pour cela, construire l'image et l'exécuter comme décrit plus haut, en veillant à utiliser la même version du runtime .NET et de la distribution Linux que celles configurées sur l'App Service cible (paramètres DOTNET_VERSION et DISTRIB_VERSION) :
docker build -t my_image --build-arg PROJECT_NAME="MYPROJECT" --build-arg DOTNET_VERSION="8.0" --build-arg DISTRIB_VERSION="bookworm-slim" .
docker run -p <external port>:80 --name mycontainer my_image
L'application est alors accessible sur http://localhost:<external port>, dans les mêmes conditions que celles rencontrées une fois déployée sur l'App Service.
Pré-requis Pipeline CI/CD
Pour la mise en place d'une livraison ou intégration en continu d'une application Lagoon, vérifier les points suivants :
- Node.js version 20 minimum.
- Les URLs suivantes doivent être accessibles :
- dépôts des images Dotnet : https://mcr.microsoft.com ;
- dépôt des packages NuGet : https://api.nuget.org/v3/index.json ;
- dépôt NPM de la librairie Terser : https://registry.npmjs.org/terser ;
- dépôt Lagoon : https://nuget.lagoon-toolkit.com/index.json.
Les pipelines présents dans le dépôt utilisent le SDK .NET 8.x et installent LGN avant les étapes de build ou de packaging.
dotnet tool update --global LGN --prerelease
dotnet tool update --global LGN --version $(lgnVersion)
Un exemple de publication applicative utilise :
dotnet publish Apps/Demo/Server/Demo.Server.csproj --configuration Release -p:Version=$(lagoonVersion) --output $(Build.ArtifactStagingDirectory)
La publication utilise le fichier NuGet de l'application pour résoudre les packages Lagoon.