7 astuces pour améliorer le télétravail et juguler la profusion de contenu dans Teams

Vous retrouverez dans cet article, l’ensemble des Questions/Réponses du webinar Infeeny x Avepoint du 12 mai 2020.

1 – A partir de Teams Corporate peut-on interagir avec un contact hors entreprises (consumer) ?

Il est tout à fait possible d’interagir avec un contact externe sur le Teams de votre entreprise. Pour cela, votre administrateur Office 365 doit :

  1. Autoriser l’accès aux personnes externes sur Microsoft Teams, ces derniers se verront donc attribuer le statut « d’invités ».
  2. Avoir activé les fonctionnalités de conversations et de réunions sur Teams. Une fois ces 2 conditions remplies, vous pourrez interagir avec les personnes externes (créer une nouvelle conversation, les inviter dans une équipe et collaborer avec elles, etc …). Il faut bien-sûr que le contact externe ait déjà un compte Microsoft afin de pouvoir aller sur Teams.

 

2 – En mode vidéo calls, on est limité à 20 personnes. Cependant, nous ne voyons que 4 intervenants. Est-il possible de modifier cela ?

Avec la recrudescence des logiciels de visioconférence depuis le Covid-19, Microsoft a décidé d’améliorer cet élément. Vous pourrez bientôt (à partir de mai) afficher 9 flux vidéo à la fois.

 

3 – Comment faciliter le travail entre différents environnements ? Je veux dire dans Teams de différentes sociétés ?

Vous pouvez facilement interagir avec un contact externe à votre entreprise via Teams. Pour cela, votre administrateur Office 365 doit autoriser l’accès aux personnes externes sur Teams, ces derniers se verront donc attribuer le statut « d’invités ». Il faut que l’une des deux sociétés invite les membres de la deuxième dans une équipe et vous pourrez ensuite interagir et collaborer ensemble.

 

4 – Pour vos Webinar, pourquoi utiliser GotoWebinar au lieu de Teams ?

Nous utilisons GoToWebinar plutôt que Microsoft Teams selon les directives de Microsoft, de utiliser les plateformes d’hébergement tierces pour les événements virtuels.

 

5 – Où trouver des ressources (templates) d’organisations / d’équipes déjà fait ? Genre un coworking space, ou une école, ou un bureau d’avocats ?

Vous trouverez plus d’informations sur les modèles de Teams de base (Ex: Standard (default), Éducation (3), Commerce de détails (2) et Santé (2)). Cependant, il est important de noter que ces modèles demandent des configurations globales pour être disponibles dans votre environnement.

 

6 – Dans la roadmap de MS, il est prévu d’interagir les conversations Skype avec Teams avant la fin avril, avez-vous des infos ?

L’interopérabilité entre Skype et Teams est déjà disponible, voici quelques articles techniques qui pourront vous aiguiller sur ce sujet et qui nous l’espérons vous donnera les éléments de réponses que vous recherchez:

https://docs.microsoft.com/fr-fr/microsoftteams/teams-and-skypeforbusiness-coexistence-and-interoperability

https://docs.microsoft.com/fr-fr/microsoftteams/teams-skype-interop

 

7 – Est-ce que Teams fonctionne avec web Outlook ? Merci

Vous pouvez créer une réunion et insérer un lien de conférence Teams à partir du web app Outlook, à condition d’avoir Office 365. Pour cela, il vous suffit créer un « Nouvel événement » et au lieu d’insérer une salle de réunion, choisissez l’option « Réaliser une réunion en ligne » puis « Teams ». Une fois que votre invitation est prête, vous n’avez plus qu’à l’envoyer !

 

8 – Peut-on empêcher l’utilisation des Apps dans Teams ?

Oui… De plusieurs façons, cet élément est contrôlé par l’administrateur de votre environnement Teams.

Vous pouvez bloquer l’utilisation des Apps :

– Premièrement, par la nature de l’App (Microsoft, Tierce-partie ou par App tenant).

– Deuxièmement, par App individuelle et, finalement, par utilisateurs/employés de votre environnement.

 

9 – Comment créer des équipes (Teams) ?

Si activé par votre société, à même l’interface Teams, vous serez en mesure de créer une nouvelle équipe Teams (voir démo). Si cette option n’est pas activée, il se peut que votre société utilise une autre approche via, par exemple, la soumission d’une demande précise par mail ou encore via une application tierce tel que ou similaire à AvePoint MyHub.

 

10 – Pour une réunion de 50 minutes, il faut planifier 50 minutes ou 1 heure ?

Effectivement, tout comme Outlook le permet, Teams permet de planifier des durées de réunions en dehors du standard 30 ou 60 minutes comme, par exemple 50 minutes :

Réunion Teams Infeeny

 

11 – Comment fonctionne la traduction ? Je ne la retrouve pas dans mon environnement.

Le responsable de votre environnement Teams doit dans un 1er temps l’activer via PowerShell ou via le Centre d’Administration de Microsoft Teams.

Si votre administrateur opte pour la seconde option, il devra aller dans les « Stratégies de messagerie » et créer ou modifier une stratégie, puis activer l’option « Messaging Policies » (on/off) > « Activé par défaut ».

Cette fonctionnalité n’est pas disponible pour les communités gouvernementales et en Allemagne.

Pour information, la fonctionnalité utilise le « Translator Text API » basé sur le moteur de traduction machine Microsoft.

 

Abonnez-vous à notre page LinkedIn et retrouvez toutes nos actualités autour de l’univers Modern Workplace. 

 

 

 

L&L Kubernetes et AKS

Retrouvez notre Expert Christophe, dans une vidéo sur Kubernetes et AKS. La vidéo est Level 100, accessible à tous. URL sur Youtube: ici

Contenu: Il s’agit de prendre un projet Web API sous Visual Studio 2019 avec support Docker (container Linux) et de construire l’image Docker en local. Ensuite on pousse cette image dans une registry ACR afin de faire un déploiement dans un cluster AKS. Ensuite, on aborde kubectl, l’outil console de Kubernetes.

Pour réaliser toutes les opérations techniques, un HOWTO est à télécharger sur https://infeeny.com/net-core-sous-aks/

Développement Web ASP .NET Core

Bonjour, dans cet article, nous allons vous présenter la solution eShopWeb, voici comment va se décomposer l’article :

    • Architecture monolithique
    • Design Pattern MVC
    • Entity Framework Core
    •  eShopWeb
      • Introduction
      • Explication de la fonctionnalité Add to Basket
      • Explication de la fonctionnalité Checkout

Architecture monolithique

Le terme monolithique est plutôt utilisé lorsque l’on parle de conception d’application (ou architecture) et représente le modèle dit « traditionnel » d’une application. On peut considérer ce genre d’architecture comme un bloc unifié (contrairement aux micro-services).

Néanmoins, on peut mettre différentes couches de données (par exemple du MVC, mais nous reviendrons sur l’explication de se terme plus loin dans cet article). Ce qui signifie que les différents composants sont intrinsèquement connectés les uns aux autres et sont la plupart du temps dépendant de chacune des couches existantes.

L’inconvénient principal est lors de la mise à jour d’un élément, il faut alors réécrire une bonne partie de l’application (si ce n’est son entièreté). Par opposition, l’architecture monolithique permet un meilleur rendement que l’architecture micro-services. Mais surtout l’aspect monolithique permet de facilité les tests et le débogage car il y a moins d’éléments à prendre en compte.

Patron de conception : Modèle – Vue – Contrôleur (MVC)

Ce qu’on appelle en français un patron de conception est appelé en anglais design pattern. Et celui du MVC permet de séparer la logique du code :

  • Modèle : Un modèle permet de gérer les données du site. Il va donc chercher les informations dans la base de données, les organiser et les assembler pour ensuite être traité par le Contrôleur.
  • Vue : Une vue va comme son nom l’indique s’occuper de l’affichage. Il n’y a quasiment aucun calcul dans cette partie car son seul but est de récupérer des variables pour savoir ce qui va être affiché.
  • Contrôleur : Un contrôleur va gérer la logique du code. Il sert d’intermédiaire entre le modèle et la vue. Globalement, il va demander au modèle les données et les analyser puis il renvoie le texte qu’il faut afficher à la vue.

On peut, en complément du Contrôleur, avoir une couche que l’on va nommer communément Service, qui va se charger de la récupération de données pour une API par exemple.

Entity Framework Core

Entity Framework Core est un ORM créé par Microsoft, il vous permet d’effectuer des opérations CRUD sans avoir besoin d’écrire des requêtes SQL.

Pour reprendre à la base : qu’est-ce qu’un ORM ?

ORM signifie Object Relational Mapping ou Mapping Objet Relationnel en français. C’est une couche supplémentaire à notre application.

Mais qu’est-ce que ça signifie concrètement : que ça se place entre un programme applicatif et une base de données relationnelle afin de simuler une base de données orientée objet. On va donc associer une classe et une table et pour chacun des attributs de la classe qui vont correspondre à un champ de la table associée.

Avantages

  • Réduction de la quantité de code est réduite et que l’on gagne en homogénéité avec le reste du code (pour les langages orientés objets).
  • On va pouvoir travailler directement avec des objets complexes.
  • Plus besoin d’écrire nos requêtes SQL.
  • Moins de travail pour les développeurs

Inconvénients

L’utilisation d’ORM, induit une couche logicielle supplémentaire ce qui forcément nuit aux performances de l’application et rend particulièrement délicate la maintenance de l’application. Ainsi que la durée de vie de l’application.

Modeling

Entity Framework Core va via une commande « mapper » des classes complexes. Il va donc faire une requête à la base de données et remplir les données de nos classes associé au même table.

Les classes à remplir s’appellent des Modèles. Voici un exemple dans la solution eShopWeb :

namespace Microsoft.eShopWeb.Web.ViewModels
{
  public class CatalogItemViewModel
  {
     public int Id { get; set; }
     public string Name { get; set; }
     public string PictureUri { get; set; }
     public decimal Price { get; set; }
  }
}

Comme vous pouvez le voir, ce Modèle correspond aux informations relatives à un objet présent dans le catalogue.

Maintenant, voici l’objet Catalog qui contient tout les items et d’autres informations :

using Microsoft.AspNetCore.Mvc.Rendering; using System.Collections.Generic; namespace Microsoft.eShopWeb.Web.ViewModels {   
  public class CatalogIndexViewModel   
  {     
    public IEnumerable<CatalogItemViewModel> CatalogItems { get; set; } public IEnumerable<SelectListItem> Brands { get; set; }     
    public IEnumerable<SelectListItem> Types { get; set; }     
    public int? BrandFilterApplied { get; set; }     
    public int? TypesFilterApplied { get; set; }     
    public PaginationInfoViewModel PaginationInfo { get; set; }  
 
  } 
}

Pour pouvoir le remplir (binding), nous allons donc maintenant voir un nouvel exemple :

using Microsoft.eShopWeb.ApplicationCore.Entities;
using System.Collections.Generic;
using System.Threading.Tasks; namespace Microsoft.eShopWeb.ApplicationCore.Interfaces {   
  public interface IAsyncRepository<T> where T : BaseEntity, IAggregateRoot   
  {     
    Task<T> GetByIdAsync(int id);     
    Task<IReadOnlyList<T>> ListAllAsync();     
    Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);     
    Task<T> AddAsync(T entity);
    Task UpdateAsync(T entity);     
    Task DeleteAsync(T entity);     Task<int> CountAsync(ISpecification<T> spec);   
  } }

Ci-dessus, c’est là où nous faisons appel à EntityFrameworkCore.

Et dans le Service : CatalogViewModelService.cs

public async Task<CatalogIndexViewModel> GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId) {   
  _logger.LogInformation("GetCatalogItems called.");   
  var filterSpecification = new CatalogFilterSpecification(brandId, typeId);
  var filterPaginatedSpecification =   new CatalogFilterPaginatedSpecification(itemsPage * pageIndex, itemsPage, brandId, typeId);   // the implementation below using ForEach and Count. We need a List.   
  var itemsOnPage = await _itemRepository.ListAsync(filterPaginatedSpecification);   var totalItems = await _itemRepository.CountAsync(filterSpecification);   var vm = new CatalogIndexViewModel()   
{     
  CatalogItems = itemsOnPage.Select(i => new CatalogItemViewModel()     
  {        
    Id = i.Id,        
    Name = i.Name,       
    PictureUri = _uriComposer.ComposePicUri(i.PictureUri),        
    Price = i.Price     
  }),     
  
  Brands = await GetBrands(),     
  Types = await GetTypes(),     
  BrandFilterApplied = brandId ?? 0,     
  TypesFilterApplied = typeId ?? 0,     
  PaginationInfo = new PaginationInfoViewModel()    
  {       
    ActualPage = pageIndex,       
    ItemsPerPage = itemsOnPage.Count,       
    TotalItems = totalItems,       
    TotalPages = int.Parse(Math.Ceiling(((decimal)totalItems / itemsPage)).ToString())     
  }   
};   
  vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : "";   
  vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : "";   
  return vm; }

 

Providers

Un provider n’est ni plus ni moins qu’une librairie utilisable. Dans notre cas, celle qui nous intéresse particulièrement et dont on va parler est la librairie InMemory qui est un Fournisseur en Mémoire.

InMemory Provider

Ce fournisseur est utile lorsque l’on souhaite tester des composants en utilisant quelque chose qui se rapproche de la connexion à la base de données réelle, sans les frais des opérations de la base de données réelle.

L’avantage de ce fournisseur en mémoire et que l’on peut tester notre code par rapport à une base de données en mémoire au lieu de devoir en installer une et de la configurer.

Solution eShopWeb

Nous allons maintenant voir plus en détail cette solution et nous allons nous pencher sur les deux fonctionnalités majeures :

  • Add to Basket
  • Checkout

Add to Basket

Le « Add to Basket » comme son nom l’indique, vous permet d’ajouter un article dans votre panier.

eshopweb.PNG

Accueil du site

Voilà à quoi ressemble le site eShopWeb. Une fois que vous cliquez sur Add to Basket, vous êtes directement redirigé vers votre panier.  via l’URL : https://localhost:44315/Basket

Capturebasket.PNG

Que se passe-t-il au niveau du code ?

L’image représentant l’accueil du site est dans le code, lié au fichier _product.cshtml qui lorsqu’un utilisateur clique sur ADD TO BASKET il sera redirigé ver l’URL suivante : http://localhost:port/Basket

Cette URL correspond au fichier Index.cshtml qui est couplé au fichier Index.cshtml.cs et c’est ce fichier cshtml.cs qui va posséder la « logique » de la page avec une méthode OnPost.

public async Task<IActionResult> OnPost(CatalogItemViewModel productDetails) {   
    if (productDetails?.Id == null)   
    {     
        return RedirectToPage("/Index");   
    }   

    await SetBasketModelAsync(); //Vérifie si l'on est connecté.   
    await _basketService.AddItemToBasket(BasketModel.Id, productDetails.Id, productDetails.Price); //Ajout de l'objet au panier.   
    await SetBasketModelAsync();  
    return RedirectToPage(); }

Dans cette méthode OnPost, on peut voir :

  • Une condition if, afin de vérifier si l’ID du produit n’existe pas
    • Si cette condition se vérifie, l’utilisateur sera redirigé vers l’URL http://localhost:port/Index
    • Sinon, on vérifie d’abord que l’utilisateur est connecté grâce à SetBasketModelAsync()
  • Ce SetBasketModelAsync() va quant à lui, vérifier si l’utilisateur est connecté et s’il a déjà un panier de fait sinon l’utilisateur n’est pas encore connecté mais possède un panier avec les éléments qu’il aura ajouté dedans.
  • Suite à cela, on va ajouter le produit dans le panier en prenant en compte, l’id du panier, l’id du produit ainsi que le prix de celui-ci en passant par la classe BasketService :
public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity = 1) //Cette méthode ajoute le produit en premier. {   
    var basket = await _basketRepository.GetByIdAsync(basketId); //Vérifie à quel panier il doit l'ajouter.   
    basket.AddItem(catalogItemId, price, quantity); //Appel de la méthode dans Basket.cs   
    await _basketRepository.UpdateAsync(basket); }

Pour rappel, la variable _basketRepository basé sur la classe BasketRepository correpond à notre Entity Framework Core.

    • Ce même Service (BasketService) va tout d’abord Get le panier via l’id passé en paramètre (le nom du paramètre : basketId) et le stocker dans la variable basket (qui correspondra à une variable initialisée avec la classe Basket)
    • Puis on va ajouter un produit via la méthode AddItem que nous allons voir par la suite et qui prend en paramètres l’id du catalogue, le prix du produit et la quantité (par défaut initialisée à 1).
    • La dernière ligne va nous permettre de charger notre panier dans la base de donnée.
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1) //méthode d'ajout 
{
  if (!Items.Any(i => i.CatalogItemId == catalogItemId))
    {
       _items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
       return;
    }
  var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId);
  existingItem.AddQuantity(quantity);
}
  •  le If va nous permettre de savoir si le produit se trouve déjà dans notre liste de produit.
  • Si non, nous entrons dans le If et nous l’ajoutons.
  • Si oui, nous allons simplement mettre à jour la quantité de ce produit.

 

Passons maintenant à la partie Paiement (Checkout)

Il existe deux cas pour cette partie :

  • Soit vous êtes connecté
    • Le bouton CHECKOUT dans le fichier Index.cshtml va vous emmener sur le fichier Checkout.cshtml.
      • Ce fichier est lié au fichier Checkout.cshtml.cs qui va s’occuper de toute la logique.
      • La méthode dans Checkout.cshtml.cs qui nous intéresse est la méthode OnPost :
public async Task<IActionResult> OnPost(Dictionary<string, int> items) {   
    await SetBasketModelAsync();   
    await _basketService.SetQuantities(BasketModel.Id, items);   
    await _orderService.CreateOrderAsync(BasketModel.Id, new Address("123 Main St.", "Kent", "OH", "United States", "44240"));   
    await _basketService.DeleteBasketAsync(BasketModel.Id);   
    return RedirectToPage(); 
}
        • La méthode SetBasketModelAsync() comme précédemment va vérifier si vous êtes connectés ou non en tant qu’utilisateur
        • Le _basketService.SetQuantities() va set le nombre d’articles présent dans notre panier
        • Le _orderService.CreateOrderAsync() fait appel au service OrderService et permet l’ajout de notre commande (on va le détailler comment cela fonctionne un peu plus tard).
        • Le _basketService.DeleteBasketAsync() va détruire le panier dans la base de donnée.
    • Pour revenir au _orderService.CreateOrderAsync() :
public async Task CreateOrderAsync(int basketId, Address shippingAddress) { var basket = await _basketRepository.GetByIdAsync(basketId);   Guard.Against.NullBasket(basketId, basket);
var items = new List<OrderItem>();
foreach (var item in basket.Items)
{
var catalogItem = await _itemRepository.GetByIdAsync(item.CatalogItemId);     var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri));
var orderItem = new OrderItem(itemOrdered, item.UnitPrice, item.Quantity);     items.Add(orderItem); }
var order = new Order(basket.BuyerId, shippingAddress, items);
await _orderRepository.AddAsync(order);
}

Que fait cette méthode ?

  • On get d’abord le panier
  • Ensuite on regarde si le panier est null
  • Nous créons une liste d’item à commander
  • Dans la boucle nous allons remplir cette liste en récupérant les éléments se trouvant dans notre panier
  • Nous allons créer un nouvel objet Order
  • Et l’ajouter dans la base de donner (appel de OrderRepository.AddAsync())

MVC : Comment les interactions sont faites

order.PNG

Mes commandes – Images

 

Cette page est la représentation graphique de la View (vue) : MyOrders.cshtml

@model IEnumerable<OrderViewModel>
@{
ViewData["Title"] = "My Order History";
}

<div class="esh-orders">
<div class="container">
<h1>@ViewData["Title"]</h1>
<article class="esh-orders-titles row">
<section class="esh-orders-title col-xs-2">Order number</section>
<section class="esh-orders-title col-xs-4">Date</section>
<section class="esh-orders-title col-xs-2">Total</section>
<section class="esh-orders-title col-xs-2">Status</section>
<section class="esh-orders-title col-xs-2"></section>
</article>
@if (Model != null && Model.Any())
{
@foreach (var item in Model)
{
<article class="esh-orders-items row">
<section class="esh-orders-item col-xs-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section>
<section class="esh-orders-item col-xs-4">@Html.DisplayFor(modelItem => item.OrderDate)</section>
<section class="esh-orders-item col-xs-2">$ @Html.DisplayFor(modelItem => item.Total)</section>
<section class="esh-orders-item col-xs-2">@Html.DisplayFor(modelItem => item.Status)</section>
<section class="esh-orders-item col-xs-1">
<a class="esh-orders-link" asp-controller="Order" asp-action="Detail" asp-route-orderId="@item.OrderNumber">Detail</a>
</section>
<section class="esh-orders-item col-xs-1">
@if (item.Status.ToLower() == "submitted")
{
<a class="esh-orders-link" asp-controller="Order" asp-action="cancel" asp-route-orderId="@item.OrderNumber">Cancel</a>
}
</section>
</article>
}
}
</div>
</div>

Cette View est directement lié au Contrôleur : OrderController, plus particulièrement à la méthode MyOrders :

[HttpGet()]
public async Task<IActionResult> MyOrders()
{
var viewModel = await _mediator.Send(new GetMyOrders(User.Identity.Name));

return View(viewModel);
}

Comme nous pouvons le voir ici, la première ligne correspond au contrôle de données (ici, nous récupérons la liste des commandes dans la base de données). Puis nous retournons la vue de notre Contrôleur (l’image Mes commandes – Images ci-dessus).

Si nous cliquons sur un des boutons DETAIL, nous aurons accès au détail du produit sélectionné (la sélection se fait par l’id du produit) :

<section class="esh-orders-item col-xs-1">
<a class="esh-orders-link" asp-controller="Order" asp-action="Detail" asp-route-orderId="@item.OrderNumber">Detail</a>
</section>

L’appel à Detail dans le fichier order.cshtmlIci, on va appeler la méthode de l’OrderController s’appelant Detail.

[HttpGet("{orderId}")]
public async Task<IActionResult> Detail(int orderId)
{
var viewModel = await _mediator.Send(new GetOrderDetails(User.Identity.Name, orderId));

if (viewModel == null)
{
return BadRequest("No such order found for this user.");
}

return View(viewModel);
}

On peut observer que dans le Contrôleur, nous récupérons le détail de la commande. Et nous retournons la bonne vue associée :

@model OrderViewModel
@{
ViewData["Title"] = "My Order History";
}
@{
ViewData["Title"] = "Order Detail";
}

<div class="esh-orders-detail">
<div class="container">
<section class="esh-orders-detail-section">
<article class="esh-orders-detail-titles row">
<section class="esh-orders-detail-title col-xs-3">Order number</section>
<section class="esh-orders-detail-title col-xs-3">Date</section>
<section class="esh-orders-detail-title col-xs-2">Total</section>
<section class="esh-orders-detail-title col-xs-3">Status</section>
</article>

<article class="esh-orders-detail-items row">
<section class="esh-orders-detail-item col-xs-3">@Model.OrderNumber</section>
<section class="esh-orders-detail-item col-xs-3">@Model.OrderDate</section>
<section class="esh-orders-detail-item col-xs-2">$@Model.Total.ToString("N2")</section>
<section class="esh-orders-detail-item col-xs-3">@Model.Status</section>
</article>
</section>

<section class="esh-orders-detail-section">
<article class="esh-orders-detail-titles row">
<section class="esh-orders-detail-title col-xs-12">Shipping Address</section>
</article>

<article class="esh-orders-detail-items row">
<section class="esh-orders-detail-item col-xs-12">@Model.ShippingAddress.Street</section>
</article>

<article class="esh-orders-detail-items row">
<section class="esh-orders-detail-item col-xs-12">@Model.ShippingAddress.City</section>
</article>

<article class="esh-orders-detail-items row">
<section class="esh-orders-detail-item col-xs-12">@Model.ShippingAddress.Country</section>
</article>
</section>

<section class="esh-orders-detail-section">
<article class="esh-orders-detail-titles row">
<section class="esh-orders-detail-title col-xs-12">ORDER DETAILS</section>
</article>

@for (int i = 0; i < Model.OrderItems.Count; i++)
{
var item = Model.OrderItems[i];
<article class="esh-orders-detail-items esh-orders-detail-items--border row">
<section class="esh-orders-detail-item col-md-4 hidden-md-down">
<img class="esh-orders-detail-image" src="@item.PictureUrl">
</section>
<section class="esh-orders-detail-item esh-orders-detail-item--middle col-xs-3">@item.ProductName</section>
<section class="esh-orders-detail-item esh-orders-detail-item--middle col-xs-1">$ @item.UnitPrice.ToString("N2")</section>
<section class="esh-orders-detail-item esh-orders-detail-item--middle col-xs-1">@item.Units</section>
<section class="esh-orders-detail-item esh-orders-detail-item--middle col-xs-2">$ @Math.Round(item.Units * item.UnitPrice, 2).ToString("N2")</section>
</article>
}
</section>

<section class="esh-orders-detail-section esh-orders-detail-section--right">
<article class="esh-orders-detail-titles esh-basket-titles--clean row">
<section class="esh-orders-detail-title col-xs-9"></section>
<section class="esh-orders-detail-title col-xs-2">TOTAL</section>
</article>

<article class="esh-orders-detail-items row">
<section class="esh-orders-detail-item col-xs-9"></section>
<section class="esh-orders-detail-item esh-orders-detail-item--mark col-xs-2">$ @Model.Total.ToString("N2")</section>
</article>
</section>
</div>
</div>

Fichier .cshtml se traduisant en élément graphique par l’image ci-dessous :

order_details.PNG

Détail du produit Mug

Il en va de même pour toutes les pages, chaque pages fonctionnant de la même façon. Notre Contrôleur va gérer nos données (Modèle) et retourner la Vue correspondante au Contrôleur.

 

Conclusion

Ce qu’on peut tirer de ce projet, est l’application d’une architecture monolithique en opposition avec le eShopOnContainers qui se base sur les micro-services.

Ce sont deux approches différentes et qui ont chacune leurs avantages et leurs inconvénients.

Néanmoins ces derniers temps, les entreprises privilégient les micro-services avec Docker et le concept de conteneurisation.

Article écrit par :

  • Julie LACOGNATA <julie.lacognata@infeeny.com>
  • Kévin ANSARD <kevin.ansard@infeeny.com>

Déploiement d’un projet Web API .NET Core 3.1 avec Azure Kubernetes Services (AKS)

Nous avons eu l’occasion de travailler à deux reprises avec Kubernetes.

Deux univers bien distinct : Windows et Linux

Ce qu’il faut savoir, c’est que Kubernetes ne fonctionne pas sous Windows dans son plus simple appareil, il faut donc utiliser Linux pour pouvoir le découvrir et pour l’apprendre concrètement.

Si vous voulez l’utiliser sous Windows, il vous faudra utiliser des services externes type AKS. Néanmoins, pour ma part, je trouve que la compréhension d’un outil via ce genre de plateforme est assez biaisé, car trop « magique”. Cela demande une certaines rigueur,une curiosité et un intérêt différent que si vous le faisiez directement sous Linux.

Prérequis pour ce tutoriel

  • Visual Studio 2019 (ou Visual Studio Code)
  • Internet (pour accéder au portail Azure)
  • Powershell
  • Helm
  • Chocolatey
  • Docker
  • Azure-CLI

Création d’une Web API .NET Core 3.1 avec un container Docker sous Linux

Aujourd’hui, nous allons voir ensemble comment créer une Web API sous .NET Core 3.1 et la déployer sous Azure.

Ouvrez votre Visual Studio et créer un nouveau projet ASP .NET Core Application.

Choisissez ensuite API (faites attention à bien avoir sélectionné .NET Core ainsi que la version 3.1) puis dans le panneau de configuration à droite dans la partie “Advanced” de cocher “Enable Docker Support” et de choisir Linux.

1_HIycDu3ImbMFl2B6aDqQbA.png

Choix de la création de projet via Visual Studio et support Docker avec des containers Linux.

L’API par défaut que crée Microsoft, s’appelle WeatherForecast, et dans notre cas nous avons choisis de nommer notre projet WeatherForecastLinux (pour les personnes manquant d’inspiration pour le nommage, c’est cadeau…).

On va donc constater qu’un fichier Dockerfile est présent. Si vous pratiquez Docker ou que vous avez quelques bases dessus vous connaissez donc son utilité. Pour expliquer pourquoi il est présent, vous vous doutez fortement que c’est dû au fait qu’on ait coché la case de support de Docker lors de la création du projet.

Pour les personnes ne sachant pas ce qu’est un Dockerfile, voici une définition succincte : c’est un fichier texte contenant des instructions sur la façon de construire une image Docker.

Voilà à quoi ressemble notre Dockerfile auto-généré :

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY [“WeatherForecastLinux.csproj”, “”]
RUN dotnet restore “./WeatherForecastLinux.csproj”
COPY . .
WORKDIR “/src/.”
RUN dotnet build “WeatherForecastLinux.csproj” -c Release -o /app/build

FROM build AS publish
RUN dotnet publish “WeatherForecastLinux.csproj” -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY — from=publish /app/publish .
ENTRYPOINT [“dotnet”, “WeatherForecastLinux.dll”]

Si vous souhaitez vérifier que votre application se lance correctement, je vous suggère de cliquer sur :

1_FzBBfH9h-p7ucjzNtQugBw.png

Le fait de cliquer sur Docker, va permettre de build et de run le Dockerfile et donc de créer l’image Docker.

Nous allons maintenant ouvrir une fenêtre PowerShell :

  • Soit vous l’ouvrez directement dans le dossier de votre projet
  • Soit vous vous placez au bon endroit pour être au niveau de votre Dockerfile

Le build et le run précédent vous on créée une image, pour vérifier le nom de celui-ci vous pouvez directement taper la commande :

docker ps -a

Ou

docker images
Picture1.png

Dans notre cas, l’image qui nous intéresse est : weatherforecastlinux

 

Maintenant que toutes les fondations sont posées, nous pouvons maintenant passer au déploiement.

Azure Kubernetes Service (AKS)

Tout d’abord, assurez-vous d’avoir accès au Portail Azure, si ce n’est pas le cas créer vous un compte via une adresse Microsoft, et utiliser les 150€ gratuit fournit de base par Microsoft (si vous êtes vigilant dans vos différentes opérations avec Azure, vous ne devriez pas utiliser tout ce crédit en un coup).

Petit disclaimer : il y aura forcément un aspect « magique » ou un aspect de confusion pour les nouveaux arrivants sur Azure (et parfois même pour les plus anciens…) car Azure est très vaste, mais ne vous inquiétez pas, c’est tout à fait normal !

Il nous faudra tout d’abord installer Azure-CLI sur notre poste, afin d’utiliser la commande az (qui nous permet de faire de l’Azure directement via notre fenêtre Powershell). Pour se faire, ouvrir Powershell en mode administrateur et utiliser la commande suivante :

Invoke-WebRequest -Uri  -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList ‘/I AzureCLI.msi /quiet’

Redémarrer votre Powershell pour que la commande puisse être active. Si cela ne fonctionne pas chez vous, installez le .msi directement sur le site et installez-le classiquement.

Il vous faut maintenant vous connecter, pour que votre instance et les informations soient bien raccorder à votre Portail Azure :

az login -u votremail@mail.com -p votremotdepasse

Kubernetes

Kubernetes est un orchestrateur. On peut dire que c’est un ensemble de services réseaux qui permet de lancer des pods Docker en cluster avec une gestion de Load Balancing.

Pods

Les pods correspondent au processus en cours d’exécution et encapsulent un ou des conteneurs applicatifs. Ce sont des instances uniques, cela signifie qu’ils possèdent :

  • Une IP unique
  • Un fichier qui indique comment le conteneur doit être exécuté
  • Des ressources de stockage

Ce qu’il faut retenir, si nous devions résumer un peu tout ça, c’est que les pods peuvent correspondre à une application ayant sa propre mémoire, sa propre IP. Néanmoins, un pod a une durée de vie définit et ne sera pas en mesure de se relancer automatiquement de lui-même.

Load Balancing

Ou répartition de charge en français, désigne un processus de répartition d’un ensemble de tâche sur un ensemble de ressources. Le but étant de rendre le traitement global plus efficace, en permettant d’optimiser le temps de réponse pour chaque tâche tout en évitant un surcharge inégale des nœuds de calculs.

Création d’un Resource Group Azure : WeatherForecastAPI_RG

Resource Group (ou RG)

Un resource group ou groupe de ressources est en quelque sorte un conteneur dit « logique » ayant pour objectif de regrouper différentes entités (Web API, VM, Base de données, etc…). Toutes les entités présentes au sein du groupe sont alors accessibles.

Dans notre cas, cela va nous permettre de créer un cluster et de déployer notre application directement dans ce RG.

Pour pouvoir créer un groupe de ressources en ligne de commande :

az group create --name WeatherForecastAPI_RG --location francecentral

La réponse à cette commande devra vous apparaître sous cette forme :

{
  "id": "/subscriptions/caf29a4f-0f21-45d9-b52e-f1e0c0b8e4be/resourceGroups/WeatherForecastAPI_RG",
  "location": "francecentral",
  "managedBy": null,
  "name": "WeatherForecastAPI_RG",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}

Comme nous pouvons le voir, nous retrouvons le nom de notre Resource Group ainsi que son state (Succeeded). Nous pouvons également voir que nous avons bien la localisation précédemment choisie qui nous est retournée.

Build de l’image Docker

Docker

Docker est une plateforme de conteneurisation. Et les conteneurs ressemblent en tout point à des machines virtuelles ou seul le système d’exploitation ou OS aura été virtualisé. Docker permet de regrouper tout le nécessaire de notre application et est donc qualifiée de compacte, puissante et innovante avec une scalabilité importante.

Nous allons donc builder l’image dont on parle un peu plus haut : weatherforecastlinux mais cette fois nous allons récupérer le tag avec le mot clef -t (que vous pouvez également voir sur la capture d’écran plus  haut).

docker build -t weatherforecastlinux:dev .

Si tout se déroule correctement vous êtes censés avoir une mention de toutes les étapes présente dans votre Dockerfile ainsi que la notion Successfully

Création de la registry WEFCACR dans Azure Container Registry (ACR)

Registry

Une registry est une sorte de « repository » d’images. Grossièrement on peut dire que c’est un espace de stockage d’images.

Pour pouvoir la créer sous Azure en ligne de commande :

az acr create --resource-group WeatherForecastAPI_RG --name WEFCACR --sku Basic

La commande ci-dessus permet de créer une registry, en spécifiant le Resource Group ainsi que le nom que l’on souhaite donner à la registry (ici WEFACR). Le –sku Basic est un élément sans importance, d’ailleurs si vous ne l’indiquez pas, par défaut il vous sera rajouté.

La commande suivante, vous permet de lister les différentes registry.

az acr list --resource-group WeatherForecastAPI_RG --query "[].{acrLoginServer:loginServer}" --output table

Enfin la commande az acr login, vous permet comme son nom l’indique de vous connecter à Azure Container Registry (ACR).

az acr login --name WEFCACR

Tag de l’image Docker avec la registry wefcacr.azurecr.io

La commande ci-dessous, vous permet de taguer votre image docker en lui donnant un nouveau nom :

docker tag weatherforecastlinux:dev wefcacr.azurecr.io/weatherforecast:dev

Se connecter dans la registry WEFCACR

az acr login --name WEFCACR

 

Push de l’image Docker dans WEFCACR

On va push l’image dans la registry en faisant un docker push et en spécifiant le nom de la registry (que l’on peut retrouver à l’aide de la commande az acr list) et en spécifiant le nom de l’image ainsi que son tag.

docker push wefcacr.azurecr.io/weatherforecast:dev

 

Création du cluster AKS WEFCACRCluster

Cluster

Un cluster Azure peut être considéré comme une « grappe », il peut utiliser des nœuds et exécuter des conteneurs.

Allez sur le portail Azure et dans la barre de rechercher, taper Kubernetes services :

Kubernetes_services.png

Cliquez sur le bouton Add, vous atteindrez cette page :

Picture2.png

Puis cliquer sur Ajouter, votre Cluster mettra un certains temps à se créer ce qui est tout à fait normal. Il vous faudra donc patienter deux minutes grand maximum, avant de lancer la commande suivante :

Connexion au cluster WEFCACRCluster

az aks get-credentials --resource-group WeatherForecastAPI_RG --name WEFCACRCluster

permet d’associer le ressource group a notre cluster

Liaison entre le cluster AKS et la registry

az aks update -n WEFCACRCluster -g WeatherForecastAPI_RG --attach-acr WEFCACR

ici nous attachons notre cluster a notre registry en spécifiant a qu’elle ressources groupe notre cluster appartient

Installation de Helm sous Windows

Helm est un gestionnaire de paquets pour Kubernetes qui va permettre d’installer et de gérer le cycle de vie de nos applications.

Pour l’installer sous Windows, il vous faut installer Chocolatey et pour se faire voici la première commande permettant d’installer Chocolatey (gestionnaire de paquets pour Windows qui vous permet d’installer ou de désinstaller des applications grâce à la commande choco).

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
  • Installation de Helm via Chocolatey
choco install kubernetes-helm

 

Création des fichiers avec Helm

Nous allons créer un dossier chart, qui sera architecturé de la façon suivante :

  • chart/
    • Chart.yaml
    • values.yaml
    • templates/
      • deployment.yaml
      • service.yaml

Architecture du fichier Chart.yaml

name: aspnet3-demo
version: 1.0.0

Architecture du fichier values.yaml

environment: development

apphost: k8s

label:
name: dockerwithlinux

container:
name: dockerwithlinux
pullpolicy: IfNotPresent
image: jlaacr02.azurecr.io/app1
tag: v1
port: 80
replicas: 3

service:
port: 8888
#type: ClusterIP
type: NodePort

Ce fichier va nous permettre de définir quelle image nous allons pull et quel tag utiliser, ainsi que les port que nous voulons.

Architecture du fichier templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deployment
  labels:
    app: {{ .Values.label.name }}
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Values.label.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.label.name }}
        environment: {{ .Values.environment }}
    spec:
      containers:
        - name: {{ .Values.container.name }}
          image: {{ .Values.container.image }}:{{ .Values.container.tag }}
          imagePullPolicy: {{ .Values.container.pullPolicy }}
          ports:
            - containerPort: {{ .Values.container.port }}
          env:
            - name: apphost
              value: {{ .Values.apphost }}
            - name: appenvironment
              value: {{ .Values.environment}}

Ce fichier forme notre application en type deployement .

Architecture du fichier templates/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-service
  labels:
    app: {{ .Values.label.name }}
spec:
  ports:
  - port: {{ .Values.service.port}}
    protocol: TCP
    targetPort: {{ .Values.container.port }}
  selector:
    app: {{ .Values.label.name }}
  type: {{ .Values.service.type }}

Ce fichier forme notre application pour le mode service.

Déploiement dans AKS via Helm

La commande ci-dessous permet de déployer notre image.

helm install dockerwithlinux ./chart

Déploiement dans AKS sans Helm

Nous allons créer un fichier my1.yaml :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dockerwithlinux-app
spec:
  selector:
    matchLabels:
      run: dockerwithlinux
  replicas: 3
  template:
    metadata:
      labels:
        run: dockerwithlinux
    spec:
      containers:
        - name: dockerwithlinux
          image: jlaacr02.azurecr.io/dockerwithlinux:v1
          ports:
            - containerPort: 80
              protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: dockerwithlinux-service
  labels:
    app: dockerwithlinux
spec:
  ports:
  - port: 8888
    protocol: TCP
    targetPort: 80
  selector:
    app: dockerwithlinux
  type: NodePort

Déploiement dans Kubernetes du fichier my1.yaml

kubectl apply -f my1.yaml

Exposition du déploiement en service

kubectl expose deployment dockerwithlinux-deployment --type=LoadBalancer --name=dockerwithlinux-service

Récupération de l’adresse IP externe du service

kubectl get service dockerwithlinux-service

Cette commande nous permet de récupérer notre service ainsi que l’IP externe qui sera tout d’abord dans un état dit « pending » (en attente en français).

getservices.PNG

Nous en avons créé un deuxième de service ici, mais fiez-vous à la commande get-service que vous voyez.

Comme nous pouvons le voir ici, par rapport à ce que nous disions, nos avons un EXTERNAL-IP ou IP externe qui prend tout d’abord un état « en attente » en fonction de l’état de votre cluster (vous n’aurez pas du tout la même IP que nous donc dans l’étape du dessous pensez bien à mettre votre EXTERNAL-IP que la commande vous indiquera).

Test du service

Comme vu juste au dessus, nous allons donc récupérer notre EXTERNAL-IP est l’inclure dans la commande suivante :

curl 20.40.148.135/controllerName/methodName

Mettre capture d’écran.

Nous allons maintenant nous rendre sur le portail Azure, afin de visualiser le cluster WEFACRCluster.

Monitoring

Cluster

portailcluster.PNG

Nous voici à présent sur le portail Azure, dans notre Cluster (pour rappel, pour y accéder il faut aller au niveau de Kubernetes Services, puis sur votre Cluster, et aller dans l’onglet Insights). On peut constater les diverses activités de celui-ci.

On peut voir la quantité de CPU utilisé, la mémoire, le nombre de nœuds ainsi que l’activité des pods.

Contrôleurs

Vous restez sur votre Cluster, et vous devez sélectionner Controller :

controller.PNG

Cette partie contrôleurs, peut s’apparenter au portail Kubernetes qui nous permet de visualiser les différents états de nos images.

 

Conclusion

Comme nous avons pu le voir tout au long de ce tutoriel, Azure est une sorte de baguette magique surpuissante, mais qui dissimule un pouvoir mal compris ou mal utilisé par beaucoup d’entre nous finalement. Hors malgré cela, il ne faut pas simplement le pointer du doigt en disant que c’est le mal, mais plutôt essayer de comprendre en profondeur où cela veut nous mener. Kubernetes et Docker sont de plus en plus mis sur le devant de la scène et pour une meilleure compréhension, nous avons tendance à penser qu’il vaut mieux que vous vous formiez indépendamment d’Azure, car la dissimulation est le pire ennemi du savoir et dans notre milieu, en tant que développeurs, le savoir et la capacité d’apprendre par nous-même est, selon nous, primordial.

 

Article écrit par :

  • Julie LACOGNATA <julie.lacognata@infeeny.com>
  • Kévin ANSARD <kevin.ansard@infeeny.com>

Infeeny est dans le magazine Programmez de Mars 2020

Retrouvez nos experts Infeeny dans le numéro 238 du magazine Programmez de Mars 2020.

On y trouve un article sur le monde du développement selon Microsoft avec un focus sur NET 5, la plateforme unifiée qui va sortir en Novembre 2020. C’est la fusion entre NET Framework 4.8 et NET Core 3.1.

Les experts Infeeny qui ont participé à cet article sont Timothé LARIVIERE et Christophe PICHAUD.

Chroniques Techniques sur le JDN

Retrouvez les 3 dernières chroniques de notre Leader de Domaine .NET (ChristopheP) sur le Journal du Net (JDN):

.NET 5 : introduction à la plateforme unifiée de Microsoft – A partir de novembre 2020, Microsoft fusionne .NET Framework et .NET Core 3.x pour créer .NET 5. Une plateforme unifiée pour les développeurs combinant dev .NET, Cloud, gaming, IoT, web, et l’IA.

Comment devenir un développeur expert Microsoft ? – Le monde du développement selon Microsoft est complexe. Il faut savoir décrypter les messages envoyés par le marketing du groupe pour pourvoir lire entre les lignes.

Comment j’ai démystifié Kubernetes, sous Linux Ubuntu – Kubernetes est la technologie cloud à la mode. On en parle partout. J’ai voulu m’intéresser à ça et maîtriser la chose en local avant de la mettre en oeuvre dans le cloud. Voici la synthèse de mon expérience.

Docker et les containers sous Linux

Pré-requis

Vous aurez besoin des éléments suivants pour suivre ce tutoriel :

  • Docker
  • Visual Studio 2019
  • Powershell (ou votre terminal favoris)
  • DotNet

Point d’attention : Il est important que vous pensiez à “switch” votre Docker (si ce n’est pas déjà fait) en Switch to Linux container.

On a essayé de comprendre au mieux le fonctionnement de Docker avec les containers sous Linux depuis un poste sous Windows. Dans cette démarche, nous avons effectués deux essais :

  • Le premier avec Visual Studio et le support Docker intégré.
  • Le second en ligne de commande avec un Dockerfile produit par nos soins.

Exemple d’un projet sous Visual Studio avec support Docker sous Linux

Voici les étapes suivies pour la création du projet (accès pour les néophytes qui ne connaîtraient pas l’environnement de Visual Studio) :

  • Créer un projet WebApplication avec ASP .NET Core 3.1
  • Prendre l’option API et cocher la case “Enable Docker support” avec pour option Linux

Le Dockerfile

Dans le cadre d’un projet avec Docker et des containers sous Linux, le Dockerfile est quelques peu différent :

Pour rappel : lors de la création d’un projet sous Visual Studio avec le support Docker, le Dockerfile est généré automatiquement, avec la commande FROM référençant l’ISO de l’OS choisit (dans notre cas : Linux) avec l’import de la librairie ASP .NET Core.

On a cherché à comprendre pourquoi il y avait deux EXPOSE. En nous renseignant au niveau de la documentation Docker officielle, nous avons compris l’utilité de ceux-ci, son fonctionnement in fine. Néanmoins, nous n’avons pas compris pourquoi notre API ne nous renvoie rien si nous changeons les ports avec la commande suivante (sous Powershell) :

docker run -d -p 42422:80 image_name

Cette commande, permet d’ajouter un host au port 80, dans notre cas 42422. Nous vous expliquerons les différentes options de cette ligne de commande un peu plus bas lors de notre second exemple.

Si on exécute avec le programme à l’aide du bouton “Docker” dans Visual Studio, celui-ci est correctement lancé (par défaut ça vous renverra au WeatherForecast si vous avez pris une API classique).

Exemple d’un projet en ligne de commande avec création d’un Dockerfile

Pour information, le début de notre exemple se base sur le tutoriel suivant qui lui se base sur des containers sous Windows. Par conséquent, il n’y a que le début de notre projet qui ressemble à cette documentation.

Dans le cadre de cet exemple, nous allons mettre en place les éléments suivants :

  • Création d’un dossier de travail
  • Création d’une application .NET
  • Création d’un fichier Dockerfile vide
  • Mise en place du Dockerfile Linux / ASP .NET Core
  • Création d’une image Docker de notre application à partir de notre Dockerfile
  • Création d’un container / exécution de notre container
  • Test de notre application
  • Modification des ports utilisés

Nous allons tout d’abord, créer un dossier dans lequel nous allons effectuer les différentes opérations que nous avons résumés juste au-dessus. Ce dossier s’appellera tuto, pour se faire, nous allons écrire dans notre commande Powershell la commande suivante :

mkdir tuto
cd tuto

La commande mkdir permet de créer le dossier tuto, et la commande cd permet de se placer au niveau du dossier tuto.

Pour la suite, nous allons créer un nouveau projet à l’intérieur du dossier tuto (puisque nous nous y trouvons) :

Nous faisons appel à dotnet car nous souhaitons créer une application .NET, le “new console” permet de définir le type de projet souhaité (ici une application Console). Le -o permet d’indiquer le path du projet app et le -n permet de lui donner un nom en l’occurrence ici : myapp.

Sur l’image ci-dessus, nous pouvons constater que nous nous sommes placés au niveau du dossier app, puis nous avons affichés le contenu du dossier.

Nous avons ensuite exécuter l’application avec la commande :

dotnet run

Cela nous affiche la chaîne de caractère “Hello World!”. En effet, lorsque nous lançons Visual Studio et que nous allons sur le fichier Program.cs, nous voyons que par défaut, la méthode Main affiche la string en question.

Ensuite, nous allons créer notre image Docker. Pour mener a bien cette tâche, nous avons besoin de deux choses :

  • De l’exécutable (.dll)
  • Du Dockerfile

Pour créer l’exécuter, nous avons utilisés la commande suivante:

Celle-ci nous sert à compiler l’application dans le dossier publish. Nous pouvons voir qu’après l’exécution de la commande, le path est indiqué.

Ici, nous vérifions bien que myapp a bien été compilé en myapp.dll.

Nous allons créer le fichier Dockerfile dans le dossier tuto (le fichier doit se trouver à la racine du projet). Il va être composé de la façon suivante :

  • Le FROM ressemble quasiment à celui qui se trouve plus haut dans le premier exemple, à la différence qu’il y a pas de AS base en plus.
  • Le COPY va permettre de copier tout se qui se trouve dans notre dossier publishdans le dossier app, qui lui sera crée dans notre container.
  • Le ENTRYPOINT va permettre d’indiquer à Docker qu’il est une application exécutable en partant de dll.

Nous allons maintenant créer notre image, pour se faire, nous allons retourner à la racine du projet tuto. Puis, nous exécuterons :

Nous allons examiner cette commande ensemble :

  • build : Créer une nouvelle image Docker
  • -t: Permet de donner un nom à cette image
  • -f: Permet de désigner le fichier que nous voulons sélectionner
  • Dockerfile: Correspond à notre fichier et le . derrière sert à indiquer qu’il doit chercher dans le dossier actuel (en l’occurrence le dossier tuto)

Nous pouvons constater, que Docker lors de la première étape va pull l’image de l’ISO, ainsi que les dépendances nécessaires. Durant la deuxième étape, la copie du contenu du dossier publish dans le dossier app va être réalisé. Et enfin, la troisième étape permet d’indiquer à Docker que c’est un exécutable.

Si maintenant, nous faisons un :

docker images

Nous pouvons voir que la dépendance aspnet de .NET Core a bien été pull correctement et que notre image est bien existante (myimage).

Pour la prochaine étape, nous allons créer et exécuter notre container via notre image avec la commande :

docker run image_name

Nous pouvons observer que notre commande nous renvoie la même chaîne de caractère que nous renvoyé notre app. Si nous exécutons :

docker ps -a

Cette commande nous permet d’afficher la liste des différents containers présent sur votre poste. On peut y voir :

  • L’ID du container
  • Le nom de l’image sur lequel il est basé
  • Le point d’entrée
  • La date de création
  • Le statut
  • Le ou les ports sur lequel il est hébergé
  • Le nom du container (généré par défaut)

En l’occurrence, nous n’avons pas de port indiqué sur l’image ci-dessus, car notre application n’est pas une application Web.

Dans le cas où vous auriez une application Web et non pas une application Console comme nous. Vous pourriez tout à fait exécuter la commande suivante :

docker run -d -p 42422:80 –name container_name image_name

Elle nous renvoie, l’ID du container crée et celui-ci sera hébergé sur le port 80 pour host le port 42422.

Nous vous proposons un récapitulatif des commandes docker, dotnet et bash
ci-dessous :

Bash :

  • mkdir : Permet de créer un dossier
  • cd : Permet de se déplacer d’un dossier à l’autre
  • ls : Permet d’indiquer le contenu d’un dossier

Dotnet :

  • dotnet run : Permet d’exécuter une application .NET
  • dotnet new : Permet de créer une application
  • dotnet publish : Permet de compiler une application

Docker :

  • docker build : Permet de créer une image
  • docker images : Permet d’afficher l’ensemble des images
  • docker run : Permet de créer et d’exécuter un container
  • docker ps -a : Permet de voir l’ensemble des containers
  • docker rm : Permet de supprimer un container

Article écrit par :

  • Julie LACOGNATA <julie.lacognata@infeeny.com>
  • Pierre KORE <pierre.kore@infeeny.com>
  • Kévin ANSARD <kevin.ansard@infeeny.com>