Docker en pratique : Angular + Asp.NET Core + SQL Server

Introduction

L'intégration de Docker dans l'univers des technologies Microsoft ne fait que s'accentuer, et si la simplicité du démarrage sur Docker n'est plus à démontrer (cf. 5 minutes pour démarrer sur Docker avec Visual Studio 2017), voyons maintenant comment mettre en place une architecture un peu plus complète avec Visual Studio 2017.

L'architecture envisagée est cette fois-ci composée d'une web api aspnetcore, d'une base de données SQL Server 2017, ainsi que d'une application Angular 4 pour l'affichage graphique.

01-architecture-docker-aspnetcore-sqlserver-angular.PNG

L'affichage se contente juste de présenter une simple liste de valeurs provenant de la base de données.

02-affichage-angular-page-web-resultat-final.PNG

Le code source de l'application finalisé est disponible sur github à cette adresse : https://github.com/vfabing/docker-aspnetcore-mssql-sample 

Étape 1 - Création et dockerisation d'une API aspnetcore

Cette première étape est probablement la plus simple. Il est possible de procéder de la même manière que décrit dans l'article de démarrage sur Docker, avec un ajout du support de Docker dès la création de l'API.

Il est également possible de rajouter à une web api existante les fichiers Docker nécessaires (i.e. les fichiers Dockerfile et docker-compose.yml) en faisant un clic droit sur le projet web et en sélectionnant Add >> Docker Support

04-ajouter-docker-support-aspnetcore.png

Le fichier Dockerfile permet de décrire la manière dont est construit le conteneur Docker (L'image de base, où se situe la librairie aspnetcore à démarrer, etc.)

FROM microsoft/aspnetcore:2.0
ARG source
WORKDIR /app
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "OMC.DockerApi.dll"]

Note: En mode de compilation Debug, Visual Studio construit une seule fois l'image du conteneur, et se sert de l'argument source pour que celui-ci utilise le package aspnetcore situé dans le répertoire obj/Docker/publish (présent en local et uniquement par référence dans le conteneur). Pour générer des images Docker avec les binaires embarqués, il est important de bien lancer la compilation en Release.

Les fichiers docker-compose.yml et docker-compose.override.yml permettent quant à eux de décrire l'infrastructure cible, décomposés en services (pour l'instant un seul service "omc.dockerapi" correspondant à l'api)

Étape 2 - Ajout d'une instance d'un conteneur SQL Server 2017 sur l'infrastructure Docker

Pour ajouter un conteneur sql server 2017 dans notre infrastructure, ce sont bien entendu les fichiers docker-compose.yml et/ou docker-compose.override.yml qu'il faut modifier.

À noter que le fichier docker-compose.override.yml permet de rajouter / écraser les valeurs contenues dans le fichier de base docker-compose.yml. Ce fichier est généralement utilisé lors du développement.

Dans l'exemple sur github, la description du service mssql est découpée dans ces 2 fichiers.

Une vision agrégée de ces 2 fichiers donne le résultat suivant :

services:
  omc.dockerapi:
    image: omc.dockerapi
    build:
      context: ./OMC.DockerApi
      dockerfile: Dockerfile
    depends_on:
      - mssql
  mssql:
    image: "microsoft/mssql-server-linux"
    expose:
      - "1433"
    environment:
      SA_PASSWORD: "YourStrong!Passw0rd"
      ACCEPT_EULA: "Y"
      MSSQL_PID: "Express"
    volumes:
      - .\mytestvolume:/var/opt/mssql
    ports:
      - "1433:1433"

Note: Pour des raisons de persistances de données (un conteneur est idéalement stateless), les données du serveur SQL sont stockées dans un sous dossier en dehors du conteneur, appelé mytestvolume

À noter également que l'utilisation de l'instruction "expose" permet d'exposer un port accessible uniquement depuis le réseau interne de notre infrastructure Docker, tandis que l'instruction "ports" permet d'exposer un port à l'extérieur de ce réseau (et donc accessible depuis notre poste en local par exemple) (cf commit)

Pour accéder depuis l'api au serveur SQL, il suffit d'utiliser le nom donné dans le fichier docker-compose au service SQL Server (mssql dans l'exemple sur github) dans n'importe quelle chaine de connexion. Exemple :

"Server=mssql;Database=ValuesDatabase;User=sa;Password=YourStrong!Passw0rd"

Docker se charge alors de résoudre le nom utilisé et de rediriger automatiquement vers le bon conteneur.

Étape 3 - Création et dockerisation d'une application Angular

Pour démarrer une application Angular classique, le plus simple reste encore de créer un nouveau projet via la ligne de commande ng init.

Note: Si besoin, ne pas hésiter à exécuter la ligne de commande ng serve pour vérifier que l'application ainsi créée fonctionne bien !

Une fois l'application Angular créée, 2 modifications sont nécessaires pour exécuter l'application Angular depuis un conteneur Docker :

  • L'ajout d'un fichier Dockerfile à la racine décrivant la création de l'image Docker via copie des sources, exécution de npm install puis npm start.
FROM node:6
ARG source
WORKDIR /app
EXPOSE 4200
COPY ${source:-.} .

RUN npm install

ENTRYPOINT ["npm", "start"]

Note: Le fichier Dockerfile présent dans l'exemple Github est un peu plus complexe qu'une simple copie de fichier car il s'inspire du même système que le Dockerfile de l'api : Les packages Angular ne sont pas directement copiés dans conteneur mais y sont référencés pour éviter sa recréation à chaque compilation

  • L'ajout de l'argument "--host 0.0.0.0" à la commande ng serve exécuté depuis l'instruction "start" du fichier json pour rendre accessible l'application angular sur n'importe quelle adresse IP

Ainsi configurée, l'application Angular est exécutable depuis un conteneur. Pour configurer son lancement automatique depuis le fichier docker-compose, il faut y rajouter un nouveau service comme par exemple :

services:
  angular:
    image: docker-angular
    build:
      context: ./docker-angular
      args:
        source: obj/Docker/empty/
    depends_on:
      - omc.dockerapi
    volumes:
      - .\docker-angular:/app
    ports:
      - "4200:4200"

Note: Les instructions build:args: et volumes: sont utilisées pour passer les packages angular par référence plutôt que par copie directe. L'absence de ces 2 instructions permet de réellement copier les packages à l'intérieur du conteneur (c'est pourquoi elles sont idéalement à placer dans le fichier docker-compose.override.yml)

Étape 4 – Liaison entre l'application Angular et l'API aspnetcoreN

Pour finaliser notre système, il reste à rendre accessible l'API en dehors du réseau interne de nos conteneurs, ainsi qu'à ajouter un composant Angular connecté à l'API.

Le détail de cette mise en place est disponible sur ce commit github

Sur l'API, il est nécessaire de configurer les CORS pour permettre à l'application angular d'y accéder (app.UseCors() dans la méthode Configure du fichier Startup.cs), puis de l'exposer par exemple sur le port 8080 en modifiant l'instruction ports du fichier docker-compose :

ports:
  - "8080:80"

Il reste enfin à rajouter dans le fichier app.component.ts de l'application Angular les instructions pour récupérer les données depuis l'API :

import { Component } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';

  API = 'http://localhost:8080';

  values: any[] = [];

  constructor(private http: Http) { }

  ngOnInit() {
    this.getValues();
  }

  getValues() {
    this.http.get(`${this.API}/api/values`)
      .map(res => res.json())
      .subscribe(values => {
        console.log(values);
        this.values = values;
      });

  }
}

Conclusion

La compréhension de quelques concepts de bases est nécessaire pour bien démarrer sur Docker (Dockerfile, docker-compose.yml), mais une fois ceux-ci maitrisés, il est très simple de prendre n'importe quelle application pouvant être distribuée en tant que Service et de la porter dans un environnement Docker.

L'intégration des outils Docker dans Visual Studio permettent de simplifier son utilisation au quotidien et peuvent permettre, dans un premier temps, une prise en main en douceur du travail avec Docker.

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus