Une app .NET Core sur Docker et Kubernetes
TL;DR .NET Core est un framework open source permettant de créer des applications web avec le langage C#. Dans cet article je présente les étapes de la mise en place d’une API REST avec .NET Core.
Article initialement posté sur le blog SofteamOuest.
Quelques technos utilisées:
- .NET Core 2.0 (ASP.NET MVC)
- Docker / Kubernetes / Jenkins pipeline
- Swashbuckle pour l’intégration Swagger
- Moq pour les tests unitaires
L’application
Il s’agit d’une API REST de gestion d’une liste de widgets. Chaque widget a un nom et une URL. Le code source du projet est disponible sous Github.
L’API implémente un CRUD. Pour la mise en place, je me suis basé sur ce tutoriel disponible dans la documentation officielle de Microsoft.
Les ressources REST sont crées à l’aide de classes
contrôleur, décorées avec l’attribut [Route]
. Les verbes
REST sont implémentés à l’aide des attributs [HttpGet]
, [HttpPut]
,
etc.
[Route("api/[controller]")]
public class WidgetsController : Controller
...
[HttpGet]
public IEnumerable<Widget> GetAll()
...
Une base de données en mémoire est accédée à l’aide d’un database context EntityFramework.
L’application est “bootstrapée” dans Startup.cs
, qui
contient la configuration des classes, configure ASP.NET MVC
et active l’injection de dépendances.
Environnement de développement
J’ai préféré utiliser la commande dotnet
(dotnet new
,
dotnet add
, etc.) fournie avec le SDK, pour initialiser
le projet en ligne de commande.
On peut également le faire via Visual Studio, mais je trouve
qu’on comprend mieux ce qui se passe avec dotnet
.
Les tutoriels indiqués présentent les deux approches.
Pour l’environnement de développement, il est possible d’utiliser (en théorie) un simple éditeur ou (mieux) VSCode: on trouve pas mal d’aides et de plugins. A la longue, un IDE plus intégré se justifie plus facilement, pour les outils de refactoring et de génération de code automatique.
J’ai donc installé Visual Studio Community Edition (gratuit, sous conditions) Vous pouvez également essayer Rider de JetBrains, mais il faut trouver une licence 🤑.
Activation de CORS
Pour utiliser l’API avec une autre application, j’ai eu besoin
d’activer CORS et Access-Control-Allow-Origin
. Ca se met
en place en quelques lignes avec .NET MVC, je me suis basé sur
ce tutoriel.
Activation de Swagger
Pour simplifier l’utilisation de l’API lors du développement, j’ai ajouté Swagger et son interface graphique SwaggerUI.
L’explication pour la mise en place de Swagger dans ce tutoriel. En l’occurence j’ai utilisé la librairie Swashbuckle comme décrit ici.
Là aussi c’est assez rapide.
Par contre j’ai rencontré un souci pour
servir Swagger dans un
sous-répertoire de mon domaine, par exemple sous exemple.com/back/swagger.json
.
Je n’ai pu configurer Swashbuckle que
directement à la racine exemple.com/swagger.json
. Il faudrait
creuser 🤔.
Dockerisation
L’idée est de conteneuriser l’application avec Docker pour la déployer dans le cluster Kubernetes de l’usine logicielle de la communauté SofteamOuest.
La théorie
Il existe plusieurs images Docker officielles permettant de conteneuriser et d’exécuter une application .NET Core.
L’image dotnet:sdk
permet de construire les binaires de
l’application (.dll) à partir du code source.
Elle est assez lourde (plus d’un gigaoctet) donc patience.
Il est généralement préférable d’utiliser l’image
dotnet:runtime
, plus légère, pour l’exécution.
Il est évidemment possible tout faire
avec l’image dotnet:sdk
, mais le conteneur généré
pour l’application
est alors très volumineux et ce n’est pas recommandé
en production.
En pratique
La construction de l’image de l’application nécessite donc
d’utiliser consécutivement deux images dans le Dockerfile :
dotnet:sdk
pour compiler le binaire puis dotnet:runtime
pour
l’image finale.
FROM microsoft/dotnet:sdk AS build-env
RUN dotnet publish -c Release -o out
...
FROM microsoft/aspnetcore:2.0
COPY --from=build-env /app/out .
...
C’est ce qui est d’ailleurs conseillé dans les tutoriels Docker et Microsoft
Malheureusement cette solution appelée multistage build nécessite une version de Docker assez récente, non possible sur notre plateforme (mars 2018). Nous allons donc utiliser les deux images mais séparément, grâce aux facilités proposées par le Jenkins pipeline, de la manière suivante:
Déploiement sur le cluster
L’image dotnet:sdk
est donc utilisée localement sur la plateforme
d’intégration pour construire les binaires dans le répertoire
out
de l’environnement de travail de Jenkins
...
containerTemplate(name: 'dotnet', image: 'microsoft/dotnet'),
...
container('dotnet') {
sh 'dotnet publish -c Release -o out'
}
...
Le Dockerfile
utilise alors uniquement l’image dotnet:runtime
et
récupère les binaires générés précédemment.
FROM microsoft/dotnet:runtime
...
COPY ./WallboardBack/out ./
...
Une fois l’image Docker créee, elle est publiée sur le Nexus. Le déploiement sur le clusteur peut alors être réalisé à l’aide d’un job de déploiement Kubernetes (comme expliqué ici et ici)
Conclusion
J’ai trouvé la création de cette API REST à l’aide de .NET Core assez rapide et efficace. J’ai notamment apprécié la facilité avec laquelle on peut ajouter des outils de sécurisation ou d’aide au développement, comme CORS ou Swagger, avec un minimum de code.
C’est l’avantage d’un framework moderne (il a été intégralement réécrit récemment), avec des technos éprouvées, vadaptable rapidement avec une configuration minimale.
Le framework est suffisament bien documenté, sauf lorsqu’on veut faire des choses un peu plus inhabituelles, et là il faut vraiment chercher…