Emballage d'applications ASP.NET Core à l'aide de Docker

Les applications ASP.NET Core sont véritablement multiplateformes et peuvent s'exécuter dans des nix et, en conséquence, dans Docker. Voyons comment ils peuvent être empaquetés pour être déployés sur Linux et utilisés en conjonction avec Nginx. Détails sous la coupe!



Remarque: nous continuons la série de publications de versions complètes d'articles du magazine Hacker. Orthographe et ponctuation de l'auteur enregistrées.


À propos de Docker


Presque tout le monde a entendu parler de l'architecture des microservices. Le concept de diviser l'application en plusieurs parties ne signifie pas qu'elle est nouvelle. Mais, le nouveau est l'ancien bien oublié et recyclé.


Si vous essayez de parler d'architecture en quelques mots, l'application Web est divisée en parties unitaires distinctes - services. Les services n'interagissent pas directement entre eux et n'ont pas de bases de données communes. Ceci est fait pour pouvoir changer chaque service sans conséquences pour les autres. Les services sont conditionnés dans des conteneurs. Parmi les conteneurs, Docker gouverne le ballon.


Pour décrire ce que Docker est très souvent simplifié, utilisez le terme "machine virtuelle". Il y a certainement une similitude, mais il est faux de le dire. La façon la plus simple de comprendre cette différence est de regarder les images suivantes de la documentation officielle de docker:




Les conteneurs utilisent le cœur du système d'exploitation actuel et le partagent entre eux. Alors que les machines virtuelles utilisant l'hyperviseur utilisent des ressources matérielles.
Docker Image est un objet en lecture seule qui stocke essentiellement un modèle pour la construction d'un conteneur. Un conteneur est un environnement dans lequel le code est exécuté. Les images sont stockées dans des référentiels. Par exemple, le référentiel Docker Hub officiel vous permet de stocker une seule image en privé. Cependant, c'est gratuit, donc même pour cela, vous devez les remercier.


INFO


Docker n'est pas le seul représentant de la conteneurisation. En plus de cela, il existe d'autres technologies. Par exemple:


rkt (prononcé 'rocket') par CoreOS


LXD (prononcé 'lexdi') par Ubuntu


Conteneurs Windows - vous ne devinerez jamais personne.


Maintenant que nous nous sommes familiarisés avec la théorie, passons à la pratique.


Il est inutile de démonter l'installation de docker, car elle peut être installée sur de nombreux systèmes d'exploitation. Je vais seulement indiquer que vous pouvez le télécharger pour votre plate-forme à partir du Docker Store . Si vous installez Docker sous Windows, la virtualisation doit être activée dans le BIOS et le système d'exploitation. Vous pouvez lire comment l'activer en 10 ke dans l'article suivant: Installation d'Hyper-V dans Windows10


Création d'un projet compatible Docker


Docker est, bien sûr, un produit Linux, mais si nécessaire, vous pouvez l'utiliser lors du développement pour Mac ou pour Windows. Lors de la création d'un projet dans Visual Studio, pour ajouter la prise en charge de docker, cochez simplement la case Activer la prise en charge de docker.


La prise en charge de Docker peut être ajoutée à un projet existant. Il est ajouté au projet de la même manière que divers nouveaux composants sont ajoutés. Menu contextuel Ajouter - Support Docker.


Si Docker est installé et fonctionne sur votre machine, la console sera automatiquement ouverte et la commande sera exécutée


docker pull microsoft/aspnetcore:2.0 

qui démarre le processus de téléchargement de l'image. Cette image est en fait un blanc sur la base duquel votre image sera créée. ASP.NET Core 2.1 utilise une image différente - Microsoft / Dotnet: SDK


Les fichiers suivants seront automatiquement créés dans le répertoire avec la solution pour vous:
.dockerignore (à l'exclusion des fichiers et répertoires de l'image docker), docker-compose.yml (à l'aide de ce fichier, vous pouvez configurer l'exécution de plusieurs services), docker-compose.override.yml (configuration auxiliaire docker-compose), docker-compose.dcproj ( fichier de projet pour Visual Studio).


Un fichier Dockerfile sera créé dans le répertoire du projet. En fait, avec l'aide de ce fichier, nous créons notre image. Par défaut (dans le cas où le projet s'appelle DockerServiceDemo), il peut ressembler à ceci:


 FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo/DockerServiceDemo.csproj DockerServiceDemo/ RUN dotnet restore DockerServiceDemo/DockerServiceDemo.csproj COPY . . WORKDIR /src/DockerServiceDemo RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"] 

La configuration initiale de .NET Core 2.0 ne vous permettra pas de créer immédiatement l'image à l'aide de la commande docker build. Il est configuré pour lancer le fichier docker-compose à partir d'un répertoire d'un niveau supérieur. Pour que la construction se déroule correctement, le Dockerfile peut être amené à un aspect similaire:


 FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo.csproj DockerServiceDemo.csproj RUN dotnet restore DockerServiceDemo.csproj COPY . . WORKDIR /src RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"] 

Tout ce que j'ai fait, c'est supprimer le répertoire DockerServiceDemo supplémentaire.


Si vous utilisez Visual Studio Code, vous devrez générer les fichiers manuellement. Bien que VS Code ait des fonctionnalités auxiliaires sous la forme d’une extension Docker , j’ajouterai un lien vers le manuel sur la façon de travailler avec le docker à partir de VS Code - Travailler avec Docker . Oui, l'article est en anglais, mais c'est avec des photos


Docker à trois accords


Pour le travail quotidien avec le docker, quelques commandes suffisent à retenir.


L'équipe la plus importante est bien sûr de construire une image. Pour ce faire, vous devez utiliser bash / CMD / PowerShell pour aller dans le répertoire où se trouve le Dockerfile et exécuter la commande:


 docker build -t your_image_name . 

Ici, après l'option -t, le nom de votre image est défini. Attention - à la fin de la commande, un espace après l'espace. Ce point signifie que le répertoire courant est utilisé. Une image peut être étiquetée avec une étiquette (numéro ou nom). Pour ce faire, placez deux points après le nom et spécifiez une balise. Si la balise n'est pas spécifiée, par défaut, elle sera définie avec le nom le plus récent. Pour envoyer une image au référentiel, il est nécessaire que le nom de l'image inclue le nom du référentiel. Quelque chose comme ça:


 docker build -t docker_account_name/image_name:your_tag . 

Ici, your_docker_account_name est le nom de votre compte Docker Hub.


Si vous avez créé l'image uniquement avec un nom local qui n'inclut pas le référentiel, vous pouvez marquer l'image avec un nom différent après la construction à l'aide de la commande suivante:


 docker tag image_name docker_account_name/image_name:your_tag 

Pour envoyer des modifications au concentrateur, vous devez maintenant exécuter la commande suivante:


 docker push docker_account_name/image_name:your_tag 

Avant cela, vous devez vous connecter à votre compte Docker. Sous Windows, cela se fait à partir de l'interface utilisateur de l'application, mais sur * nix, cela se fait avec la commande:


 docker login 

En fait, trois équipes ne suffisent pas. Vous devez également être en mesure de vérifier le fonctionnement du conteneur. La commande avec laquelle vous pouvez démarrer le conteneur ressemble à ceci:


 docker run -it -p 5000:80 image_name 

L'option -it créera un pseudo-ATS et votre conteneur répondra aux demandes. Après avoir exécuté la commande, le service sera disponible sur http: // localhost: 5000 /


-p 5000: 80 associe le port 5000 du conteneur au port 80 de l'hôte.


De plus, il existe de telles commandes:


 docker ps –a 

Vous montrer une liste de conteneurs. Depuis que le commutateur -a a été ajouté, tous les conteneurs seront affichés, pas seulement ceux qui sont en cours d'exécution.


 docker rm container_name 

Cette commande supprimera le conteneur nommé nom_conteneur. rm - court pour supprimer


 docker logs container_name 

Afficher les journaux des conteneurs


 docker rmi image_name 

Supprime une image nommée image_name


Lancement d'un conteneur via un serveur proxy inverse


Le fait est que les applications .NET Core elles-mêmes utilisent leur serveur Web Kestrel. Ce serveur n'est pas recommandé pour la production. Pourquoi? Il y a plusieurs explications.
S'il y a plusieurs applications qui partagent IP et port, alors Kestrel ne pourra pas distribuer le trafic. De plus, le serveur proxy inverse fournit une couche de sécurité supplémentaire, simplifie l'équilibrage de charge et les paramètres SSL, et s'intègre également mieux dans l'infrastructure existante. Pour la plupart des développeurs, la raison la plus importante du besoin de procurations inverses est une sécurité supplémentaire.


Tout d'abord, restaurez la configuration Dockerfile d'origine. Après cela, nous traiterons le fichier docker-compose.yml et essaierons d'exécuter notre service seul. Le format de fichier yml est lu comme "yaml" et est une abréviation de "Yet Another Markup Language", ou de "YAML Ain't Markup Language". Soit un autre langage de balisage, soit pas du tout un langage de balisage. D'une manière ou d'une autre, tout n'est pas certain.


Mon fichier docker-compose par défaut ressemble à ceci:


 version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile 

Le fichier docker-compose.override.yml ajoute plusieurs paramètres à la configuration:
version: '3.4'


 services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80" 

Nous pouvons construire la solution créée en utilisant la construction docker-compose, en appelant la commande docker-compose up, nous lancerons notre conteneur. Est-ce que tout fonctionne? Passez ensuite à l'étape suivante. Créez le fichier nginx.info. La configuration sera approximativement la suivante:


 worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:80; } server { listen 80; location / { proxy_pass http://app_servers; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

Ici, nous indiquons que nginx écoutera sur le port 80 (écouter 80;). Et les demandes reçues seront redirigées vers le 80e port de l'hôte dans le conteneur dockerservicedemo. De plus, nous indiquons à nginx les en-têtes à transmettre.


Nous pouvons utiliser http dans nginx et accéder au site Web via https. Lorsqu'une demande https passe par un proxy http, de nombreuses informations provenant de https ne sont pas transmises à http. De plus, lors de l'utilisation d'un proxy, l'adresse IP externe est perdue. Pour que ces informations soient transmises dans les en-têtes, vous devez modifier le code de notre projet ASP.NET et ajouter le code suivant au début de la méthode Configure du fichier Startup.cs:


  app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); 

La plupart des serveurs proxy utilisent les en-têtes X-Forwarded-For et X-Forwarded-Proto. Ce sont ces en-têtes qui sont spécifiés maintenant dans la configuration nginx.


Incluez maintenant l'image nginx et le fichier nginx.conf dans la configuration de doker-compose. La prudence dans les espaces YAML est importante:


 version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile ports: - 5000:80 proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf ports: - 80:80 

Ici, nous ajoutons des proxys à notre configuration en tant qu'image nginx. Nous attachons à cette image un fichier de paramètres externes. Nous le montons en quelque sorte dans le système de fichiers conteneur en utilisant un mécanisme appelé volume. Si vous ajoutez à la fin: ro, l'objet sera monté en lecture seule.


Le proxy écoute le 80e port externe de la machine sur laquelle le conteneur s'exécute et envoie une demande au 80e port interne du conteneur.


En exécutant la commande doker-compose up, nous allons remplir, c'est-à-dire extraire l'image nginx du référentiel et démarrer notre conteneur avec le conteneur proxy. Maintenant sur http: // localhost: 80 / il sera accessible via nginx. Sur le 5000e port, l'application «tourne» également sous Kestrel.


Nous pouvons vérifier que la demande à l'application Web passe par le proxy inverse. Ouvrez les outils de développement dans le navigateur Chrome et accédez à l'onglet Réseau. Cliquez sur localhost ici et sélectionnez l'onglet En-têtes.



Nous lançons le conteneur via des proxys et HTTPS


ASP.NET Core 2.1 a apporté des améliorations dans la prise en charge HTTPS.
Supposons que le middleware suivant vous permet de rediriger d'une connexion non sécurisée vers une connexion sécurisée:


 app.UseHttpsRedirection(); 

Et le suivant vous permet d'utiliser le protocole HTTP Strict Transport Security Protocol - HSTS.


 app.UseHsts(); 

HSTS est une fonctionnalité du protocole HTTP / 2, dont la spécification a été publiée en 2015. Cette fonctionnalité est prise en charge par les navigateurs modernes et informe que le site Web utilise uniquement https. Ainsi, une protection contre une attaque de déclassement se produit au cours de laquelle l'attaquant peut profiter de la situation en utilisant la transition vers un protocole http non sécurisé. Par exemple, rétrograder TLS ou même remplacer un certificat.


En règle générale, ce type d'attaque est utilisé en conjonction avec les attaques de l'homme du milieu. Vous devez savoir et vous rappeler que HSTS ne vous sauve pas d'une situation où un utilisateur visite le site en utilisant le protocole http puis redirige vers https. Il existe la soi-disant liste de préchargement Chrome , qui contient des liens vers des sites prenant en charge https. D'autres navigateurs (Firefox, Opera, Safari, Edge) prennent également en charge les listes de sites https créées à partir de la liste Chrome. Mais toutes ces listes sont loin de tous les sites.


La première fois que vous exécutez une application Core sous Windows, vous recevrez un message indiquant qu'un certificat de développeur a été créé et installé. En cliquant sur le bouton et en installant le certificat, vous le rendrez ainsi fiable. À partir de la ligne de commande sur macOS, vous pouvez ajouter une approbation au certificat à l'aide de la commande:
dotnet dev-certs https –trust


Si l'utilitaire dev-certs n'est pas installé, vous pouvez l'installer avec la commande:


 dotnet tool install --global dotnet-dev-certs 

La façon d'ajouter un certificat à Trusted sous Linux dépend de la distribution.
À des fins de test, nous utilisons le certificat du développeur. Les actions avec un certificat signé par CA sont similaires. Si vous le souhaitez, vous pouvez utiliser des certificats LetsEncrypt gratuits


Vous pouvez exporter le certificat de développeur dans un fichier à l'aide de la commande


 dotnet dev-certs https -ep ___.pfx 

Le fichier doit être copié dans le répertoire% APPDATA% / ASP.NET / Https / sous Windows ou dans /root/.aspnet/https/ sous macOS / Linux.


Pour que le conteneur récupère le chemin d'accès au certificat et son mot de passe, créez des secrets d'utilisateur avec le contenu suivant:


 { "Kestrel":{ "Certificates":{ "Default":{ "Path": "/root/.aspnet/https/__.pfx", "Password": "___" } } } } 

Ce fichier stocke des données non chiffrées et n'est donc utilisé que pendant le développement. Un fichier est créé dans Visual Studio en appelant le menu contextuel sur l'icône du projet ou en utilisant l'utilitaire de secrets d'utilisateurs sous Linux.


Sous Windows, le fichier sera enregistré dans le répertoire% APPDATA% \ Microsoft \ UserSecrets \ <user_secrets_id> \ secrets.json, et sous macOS et Linux, il sera enregistré dans ~ / .microsoft / usersecrets / <user_secrets_id> /secrets.json


Pour enregistrer les paramètres de production, certaines distributions Linux peuvent utiliser systemd. Les paramètres sont enregistrés sous l'attribut Service. Par exemple, comme ceci:


 [Service] Environment="Kestrel _ Certificates _ Default _Path=/root/.aspnet/https/__.pfx" Environment="Kestrel _ Certificates _ Default _Password=___" 

Ensuite, je vais donner et analyser immédiatement la version de travail de la configuration du docker pour le proxy et le conteneur via https.


Fichier Docker-compose:


 version: '3.4' services: dockerservicedemo21: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile  override: version: '3.4' services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:44392;http://+:80 - ASPNETCORE_HTTPS_PORT=44392 ports: - "59404:80" - "44392:44392" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf - ./DockerServiceDemo/cert.crt:/etc/nginx/cert.crt - ./DockerServiceDemo/cert.rsa:/etc/nginx/cert.rsa ports: - "5001:44392" 

Je vais maintenant décrire des moments incompréhensibles. ASPNETCORE_URLS nous permet de ne pas indiquer dans le code d'application en utilisant app.UseUrl le port sur lequel l'application écoute.


ASPNETCORE_HTTPS_PORT effectue une redirection similaire à ce que ferait le code suivant:
services.AddHttpsRedirection (options => options.HttpsPort = 44392)


Autrement dit, le trafic provenant des requêtes http sera redirigé vers un port spécifique de requêtes https.
En utilisant les ports, il est indiqué que la demande du port externe 59404th sera redirigée vers le 80ème conteneur, et du port externe 44392nd vers le 44392nd. Théoriquement, puisque nous avons configuré un serveur proxy inverse, nous pouvons supprimer les ports avec ces redirections.
À l'aide des volumes, un répertoire avec un certificat pfx et une application UserSecrets sont montés avec un mot de passe et un lien vers le certificat.


La section proxy indique que les demandes du 5001e port externe seront redirigées vers le 44392e port nginx. De plus, un fichier de configuration nginx est monté, ainsi qu'un certificat et une clé de certificat.


Pour qu'ils puissent créer un seul certificat pfx (que nous avons déjà) pour créer des fichiers crt et rsa, vous pouvez utiliser OpenSSL. Vous devez d'abord extraire le certificat:


 openssl pkcs12 -in ./_.pfx -clcerts -nokeys -out domain.crt 

Et puis la clé privée:


 openssl pkcs12 -in ./_.pfx -nocerts -nodes -out domain.rsa 

La configuration de nginx est la suivante:


 worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:44392; } server { listen 44392 ssl; ssl_certificate /etc/nginx/cert.crt; ssl_certificate_key /etc/nginx/cert.rsa; location / { proxy_pass https://app_servers; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

Le serveur proxy écoute sur le port 44392. Ce port reçoit les demandes du 5001e port hôte. Ensuite, le proxy redirige les demandes vers le port 44392e du conteneur dockerdemoservice.


Après avoir compris ces exemples, vous aurez une bonne expérience pour travailler avec docker, microservices et nginx.


Nous vous rappelons qu'il s'agit de la version complète d'un article du magazine Hacker . Son auteur est Alexey Sommer .

Source: https://habr.com/ru/post/fr435914/


All Articles