Publishing in a Linux Docker container
If the application must be deployed in a Docker environment, it is strongly recommended to test it in that environment. The Dockerfile file below is used to test a Lagoon application.
This same Docker image can also be used to test an application locally before deploying it to an Azure App Service Linux, since the App Service actually runs the application's Docker container. Building and launching the image as described below makes it possible to validate the application's behavior in a Linux environment close to that of the App Service, before publishing it to Azure.
Lagoon Dockerfile
The following Dockerfile file compiles the application from source and creates a container with this application. It must be located at the root of the project when running the commands.
# 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"
Building the image is done in two phases:
- a temporary image is used to compile the sources and publish the application;
- a final image copies the published files and runs the server.
Create the image:
docker build -t my_image --build-arg PROJECT_NAME="MYPROJECT" .
The parameters that can be passed to the build command with --build-arg PARAMETRE=VALEUR are:
| Parameter | Description |
|---|---|
PROJECT_NAME | Required. Project name. It must match the DLL name. |
ENVIRONNEMENT | Allows using an appsettings file different from the base one, for example --build-arg ENVIRONNEMENT="Production" for appsettings.Production.json. |
LGN_VERSION | Allows specifying the LGN version number. If not specified, the latest version is used. |
DOTNET_VERSION | Allows specifying the .NET runtime version of the distribution used for build and execution. By default, version 8.0 is used. |
DISTRIB_VERSION | Allows defining the Linux distribution to use for build and execution. By default, the Alpine distribution is used. |
Create the container:
docker run -p <external port>:80 --name mycontainer my_image
It is possible to modify parameters of the appsettings file in the run command. For example, to specify the database connection string:
docker run -p <external port>:80 -e "ConnectionStrings__LocalDb=<Connexion string>" --name mycontainer my_image
In this example, you can see the appsettings hierarchy with two underscore characters as a level separator.
Special cases
Volumes
If specific volumes need to be mounted for the persistence of certain data, this must be discussed with the client at the start of the project to agree on the path to define at the Docker level.
If the client already has existing volumes, they can be used:
docker run -p <external port>:80 -v <nom du volume>:<chemin conteneur> --name mycontainer my_image
To use a host directory as a volume:
docker run -p <external port>:80 -v c:/Temp/TEST:/usr/share/test --name mycontainer my_image
In this example, the host directory c:\Temp\Test is accessible in the container at the path /usr/share/test.
Issue with the Linux distribution
This may happen, particularly in Kubernetes environments, that the Alpine image causes issues: https://github.com/kubernetes/kubernetes/issues/64924
In this case, use a Debian image, for example Bookworm, Debian 12 or later. Pass the distribution type in the build command:
docker build -t wiplan_image --build-arg PROJECT_NAME="MYPROJECT" --build-arg ENVIRONNEMENT="Production" --build-arg DISTRIB_VERSION="bookworm-slim" .
TLS error on the connection to SQL Server
If an application using SQL Server returns an error such as 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), add the following in the server section, before the ENTRYPOINT command:
# 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;
Testing an Azure App Service Linux deployment
An Azure App Service Linux runs the application in a Docker container. It is therefore possible, and recommended, to test this container locally before deploying it to Azure, in order to detect any issues related to the Linux environment (paths, file case sensitivity, system dependencies, etc.).
To do this, build the image and run it as described above, making sure to use the same version of the .NET runtime and the Linux distribution as those configured on the target App Service (parameters DOTNET_VERSION and 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
The application is then accessible at http://localhost:<external port>, under the same conditions as once deployed on the App Service.
CI/CD pipeline prerequisites
To set up continuous delivery or integration for a Lagoon application, check the following points:
- Node.js version 20 minimum.
- The following URLs must be accessible:
- Dotnet image repositories: https://mcr.microsoft.com;
- NuGet package repository: https://api.nuget.org/v3/index.json;
- Terser library NPM repository: https://registry.npmjs.org/terser;
- Lagoon repository: https://nuget.lagoon-toolkit.com/index.json.
The pipelines in the repository use the .NET 8.x SDK and install LGN before the build or packaging steps.
dotnet tool update --global LGN --prerelease
dotnet tool update --global LGN --version $(lgnVersion)
An example of application publishing uses:
dotnet publish Apps/Demo/Server/Demo.Server.csproj --configuration Release -p:Version=$(lagoonVersion) --output $(Build.ArtifactStagingDirectory)
Publishing uses the application's NuGet file to resolve Lagoon packages.