Aller au contenu principal

Création d'un conteneur Docker

La prochaine étape est de configurer un conteneur Docker, c'est-à-dire un environnement virtuel et isolé contenant tout ce que votre application a besoin pour fonctionner (.NET, Postgres, le projet, etc.).

Un conteneur fait référence à plusieurs images et ajoute ensuite des paramètres personnalisés au besoin, comme par exemple les ports, les mots de passe, etc.

Plusieurs images sont déjà disponibles (ex.: Postgres), alors que d'autres ne le sont pas. En effet, il est possible de fournir une image "pré-faite" de Postgres, mais il n'est pas possible de faire une image à l'avance de toutes les applications!

Ainsi, il faudra créer une image pour votre projet. Cette image contiendra entre autres les commandes à exécuter pour compiler le projet et l'exécuter. Elle sera construite à partir d'une image de base fournie par Microsoft pour les projets .NET.

Créer un Dockerfile pour votre projet

Pour définir une image, on crée un fichier Dockerfile.

  1. Dans le projet .NET que vous voulez déployer, créez un fichier Dockerfile (sans extension). Par exemple, dans mon cas, pour l'exemple, je crée un fichier dans Snowfall.Web.Mvc/Dockerfile

  2. Ensuite, prenez une image de base pertinente pour le projet. Dans le cas des projets .NET, Microsoft fournit une image de base pour chaque version de .NET.

    Snowfall.Web.Mvc/Dockerfile
    # Utilise l'image de base ASP.NET Runtime 7.0 pour exécuter des applications ASP.NET.
    FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
    attention

    Vous voudrez remplacer aspnet:7.0 par aspnet:8.0 probablement, ou du moins la version utilisée par votre projet. Le projet que j'utilise est en .NET 7.0.

  3. Ensuite, on construit l'application.

    Snowfall.Web.Mvc/Dockerfile
    # Utilise l'image de base ASP.NET Runtime 7.0 pour exécuter des applications ASP.NET.
    FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base

    # Définit le répertoire de travail dans le conteneur comme étant "/app".
    WORKDIR /app

    # Expose le port 80 pour permettre au conteneur d'accepter des connexions HTTP.
    EXPOSE 80

    # Utilise l'image .NET SDK 7.0 pour la phase de construction (elle contient les outils nécessaires pour compiler l'application).
    FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build

    # Définit le répertoire de travail dans le conteneur pour la phase de construction comme étant "/src".
    WORKDIR /src

    # Copie le fichier .csproj du projet "Snowfall.Web.Mvc" dans le répertoire "/src/Snowfall.Web.Mvc" du conteneur.
    COPY ["Snowfall.Web.Mvc/Snowfall.Web.Mvc.csproj", "Snowfall.Web.Mvc/"]

    # Exécute `dotnet restore` pour télécharger les dépendances spécifiées dans le fichier .csproj.
    RUN dotnet restore "Snowfall.Web.Mvc/Snowfall.Web.Mvc.csproj"

    # Copie l'ensemble du contenu du répertoire local dans le conteneur.
    COPY . .

    # Compile et publie l'application en mode Release, avec les fichiers de sortie placés dans "/app/publish".
    RUN dotnet publish "Snowfall.Web.Mvc/Snowfall.Web.Mvc.csproj" -c Release -o /app/publish

    FROM base AS final

    # Définit à nouveau "/app" comme répertoire de travail.
    WORKDIR /app

    # Copie les fichiers publiés de la phase de construction vers l'image finale.
    COPY --from=build /app/publish .

    # Définit le point d'entrée pour lancer l'application, en exécutant la commande `dotnet Snowfall.Web.Mvc.dll`.
    ENTRYPOINT ["dotnet", "Snowfall.Web.Mvc.dll"]
attention

Si vous avez plusieurs projets à déployer, il vous faudra un dockerfile par projet. La structure devrait être sensiblement la même.

Créer un conteneur pour le projet

Votre projet n'est pas seulement une application .NET, vous avez aussi besoin d'une base de données et d'un serveur Web pouvant accepter les requêtes web.

Pour ce faire, on crée un conteneur à l'aide d'un fichier docker-compose.yml. Le fichier docker-compose.yml contient les références à tout ce que l'application a besoin pour fonctionner.

  1. À la racine du projet, créez un fichier docker-compose.yml
  2. Un fichier docker-compose.yml contient au minimum la section services qui décrit les services à utiliser dans le conteneur et peut aussi contenir d'autres sections comme volumes afin de créer des espaces de stockage.

Service Postgres

Commençons par le service Postgres.

docker-compose.yml
services:
postgres:
image: postgres:17-alpine
container_name: snowfall_postgres
environment:
- POSTGRES_DB=snowfall_db
- POSTGRES_USER=snowfall_user
- POSTGRES_PASSWORD=motdepasse
volumes:
- pgdata:/var/lib/postgresql/data

volumes:
pgdata:
  • services: Définit une section pour configurer les services dans le fichier.
  • postgres: Nom du service (peut être utilisé par d’autres services).
  • image: postgres:17-alpine, utilise l’image Docker officielle de PostgreSQL, version 17, basée sur Alpine Linux.
  • container_name: Attribue un nom spécifique au conteneur Docker (snowfall_postgres) pour faciliter son identification.
  • environment: Définit des variables d’environnement pour configurer PostgreSQL.
    • POSTGRES_DB: Nom de la base de données à créer au démarrage.
    • POSTGRES_USER: Nom d’utilisateur par défaut.
    • POSTGRES_PASSWORD: Mot de passe pour l’utilisateur.
  • volumes: Monte un volume pour persister les données PostgreSQL.
    • pgdata:/var/lib/postgresql/data: Le volume nommé pgdata est lié au répertoire /var/lib/postgresql/data dans le conteneur, où PostgreSQL stocke ses données.

Service client (votre application)

docker-compose.yml
services:
postgres:
image: postgres:17-alpine
container_name: snowfall_postgres
environment:
- POSTGRES_DB=snowfall_db
- POSTGRES_USER=snowfall_user
- POSTGRES_PASSWORD=motdepasse
volumes:
- pgdata:/var/lib/postgresql/data

client:
build:
context: .
dockerfile: Snowfall.Web.Mvc/Dockerfile
container_name: snowfall_client
environment:
- ASPNETCORE_URLS=http://+:80
- DossierStorage=storage-images
expose:
- "80"
volumes:
- shared-images:/storage-images
depends_on:
- postgres

volumes:
pgdata:
shared-images:
  • client: Nom du service pour votre application web.
  • build: Spécifie que l’image Docker pour ce service sera construite à partir d’un Dockerfile.
    • context: Définit le répertoire de construction comme étant le répertoire courant (.).
    • dockerfile: Indique le chemin du Dockerfile spécifique à utiliser.
  • container_name: Nom du conteneur pour ce service (snowfall_client).
  • environment: Définit des variables d’environnement pour configurer l’application.
    • ASPNETCORE_URLS: Configure l’application pour écouter sur le port HTTP (80) de tous les hôtes (+).
      • DossierStorage : Variable spécifique à l’application, définissant le chemin de stockage (par exemple, pour des images).
  • expose: Expose le port 80 pour rendre ce service accessible aux autres services dans le réseau Docker interne (mais pas à l’extérieur).
  • volumes: Monte un volume pour le partage des fichiers.
    • shared-images:/storage-images: Monte le volume nommé shared-images dans le répertoire /storage-images du conteneur.
  • depends_on: Indique que ce service dépend du service postgres et doit attendre que celui-ci soit lancé.

Service de serveur Web (nginx)

Pour que vous puissiez accepter les requêtes web, il faut un serveur web (ou un reverse proxy dans le cas de nginx). En effet, lorsque vous exécutez le projet à l'aide de Rider ou Visual Studio, un serveur web est automatiquement fourni et vous n'avez pas à vous soucier de ce détail.

Ce n'est pas le cas lorsque vous déployez votre application, il faut une couche logicielle pour gérer les requêtes HTTP entrantes et sortantes. Je vous propose d'utiliser nginx.

  1. Créer un fichier nginx.conf avec une configuration de base pour le nom de domaine de votre site:

    nginx.conf
    events { }

    http {
    upstream client_app {
    server client:80;
    }

    server {
    listen 80;
    server_name snowfallweb.com www.snowfallweb.com;
    location / {
    proxy_pass http://client_app;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    }
    }
    }
    attention

    Remplacez snowfallweb.com par votre nom de domaine

    Bloc events

    • events : Ce bloc est requis dans toute configuration Nginx. Il définit les paramètres pour la gestion des connexions, mais ici, il n’est pas utilisé.

    Bloc http

    • http : Définit les configurations liées au protocole HTTP.
    • upstream client_app : Définit un groupe de serveurs backend sous le nom client_app.
    • server client:80; : Indique qu’il faut envoyer les requêtes au service client sur le port 80. Ici, client est le nom du service défini dans docker-compose.yml, car les services partagent un réseau interne Docker.

    Bloc server

    • server : Définit un serveur virtuel pour répondre aux requêtes HTTP.
    • listen 80; : Configure ce serveur pour écouter sur le port 80 (HTTP).
    • server_name snowfallweb.com www.snowfallweb.com; : Spécifie les noms de domaine (ou alias) que ce serveur doit gérer. Ici, il répond aux requêtes pour snowfallweb.com et www.snowfallweb.com.

    Bloc location

    • location / : Configure les règles pour toutes les requêtes avec un chemin commençant par / (la racine).
    • proxy_pass http://client_app; : Redirige les requêtes vers le groupe de serveurs client_app (défini plus haut, qui correspond au service client dans Docker). Les requêtes seront envoyées à http://client:80 (le service client).
    • proxy_set_header Host $host; : Transmet l’en-tête Host de la requête client originale (par exemple, snowfallweb.com). Cela permet au backend de savoir quel domaine a été demandé.
    • proxy_set_header X-Real-IP $remote_addr; : Ajoute l’adresse IP réelle du client ($remote_addr) dans l’en-tête X-Real-IP. Cela permet au backend de savoir l’adresse IP d’origine du client, même si elle passe par le proxy.
  2. Ajoutez un service nginx dans docker-compose faisant référence au fichier de configuration.

    docker-compose.yml
    services:
    postgres:
    image: postgres:17-alpine
    container_name: snowfall_postgres
    environment:
    - POSTGRES_DB=snowfall_db
    - POSTGRES_USER=snowfall_user
    - POSTGRES_PASSWORD=motdepasse
    volumes:
    - pgdata:/var/lib/postgresql/data

    client:
    build:
    context: .
    dockerfile: Snowfall.Web.Mvc/Dockerfile
    container_name: snowfall_client
    environment:
    - ASPNETCORE_URLS=http://+:80
    - DossierStorage=storage-images
    expose:
    - "80"
    volumes:
    - shared-images:/storage-images
    depends_on:
    - postgres

    reverse-proxy:
    image: nginx:alpine
    container_name: reverse_proxy
    ports:
    - "80:80"
    - "443:443"
    volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
    - client

    volumes:
    pgdata:
    shared-images:

Bâtir le conteneur

Pour bâtir le conteneur et compiler l'application, il suffit d'exécuter la commande docker compose up dans un terminal à la racine du projet (au même niveau que le fichier docker-compose.yml).

docker compose up

Si tout se passe sans erreur, vous devriez voir quelque chose comme ceci:

...

snowfall_client | warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
snowfall_client | Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
snowfall_client | info: Microsoft.Hosting.Lifetime[14]
snowfall_client | Now listening on: http://[::]:80
snowfall_client | Now listening on: http://[::]:80
snowfall_client | info: Microsoft.Hosting.Lifetime[0]
snowfall_client | Application started. Press Ctrl+C to shut down.
snowfall_client | Application started. Press Ctrl+C to shut down.
snowfall_client | Hosting environment: Production
snowfall_client | Content root path: /app
snowfall_client | info: Microsoft.Hosting.Lifetime[0]
snowfall_client | Hosting environment: Production
snowfall_client | info: Microsoft.Hosting.Lifetime[0]
snowfall_client | Content root path: /app
snowfall_client | warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
snowfall_client | Failed to determine the https port for redirect.

Votre application devrait écouter sur le port 80, donc vous devriez être en mesure d'accéder à localhost dans votre navigateur et votre site devrait s'afficher.