JUIL 17

Service Fabric Mesh

Le mois de juillet a apporté pleins de bonnes nouvelles, la France a gagné une deuxième étoile, mais cela est anecdotique par rapport à l’annonce de Microsoft autour du produit Service Fabric. La public preview de Service Fabric Mesh est enfin disponible !

Pour ceux qui ne l’ont pas vu, voici le lien : https://azure.microsoft.com/en-us/blog/azure-service-fabric-mesh-is-now-in-public-preview/

Ceux qui ont déjà mis en place des clusters Service Fabric en production savent que le côté “Service managé” a quelques limites qui peuvent vous faire perdre quelques cheveux, d’ailleurs j’en ai écrit un retour d’expérience sur ce blog : http://blog.woivre.fr/blog/2018/01/bonne-resolution-prendre-soin-de-son-cluster-service-fabric

En résumé ce qui vous pose des problèmes avec Service Fabric c’est bien souvent l’infrastructure sur laquelle repose votre cluster. Service Fabric Mesh vous permet de ne plus vous soucier de cette infra sous-jacente.

Par contre à ce jour, cela a un coût, il faut dire adieu aux Reliables Services et au SDK fourni par Service Fabric, et mettre un focus sur les Containers. Pour le coût financier, pour le moment c’est gratuit en preview et bien entendu sans SLA.

Dans cet article, je vous propose de regarder comment déployer nos premiers services sous Service Fabric Mesh.

Commençons par installer le module pour la CLI Azure, pour info il n’y a pas encore de module pour Powershell

# Lister les extensions présentes
az extension list
# Supprimer l'installation précédente
az extension remove --name mesh
# Installer la nouvelle version 
az extension add --source https://sfmeshcli.blob.core.windows.net/cli/mesh-0.8.1-py2.py3-none-any.whl

Déployons l’application de démo fournie par Microsoft :

az mesh deployment create --resource-group myResourceGroup --template-uri https://sfmeshsamples.blob.core.windows.net/templates/helloworld/mesh_rp.linux.json --parameters "{'location': {'value': 'westeurope'}}"

On remarquera que Service Fabric Mesh est dispo sur le datacenter West europe, la liste exhaustive n’est pas disponible sur la documentation Azure, mais je pense qu’il s’agit de la même que Service Fabric, c’est-à-dire toutes les régions Azure.

Dans le portail Azure, on peut voir notre application Mesh comme ci-dessous :

Il s’agit là d’une preview, donc on ne va pas avoir beaucoup plus de détails dans le portail Azure.

Maintenant si on regarde un peu plus le template ARM qu’on a déployé précédemment,on voit qu’il s’agit de celui ci :

{
  "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "metadata": {
        "description": "Location of the resources."
      }
    }
  },
  "resources": [
    {
      "apiVersion": "2018-07-01-preview",
      "name": "helloWorldNetwork",
      "type": "Microsoft.ServiceFabricMesh/networks",
      "location": "[parameters('location')]",
      "dependsOn": [],
      "properties": {
        "addressPrefix": "10.0.0.4/22",
        "ingressConfig": {
          "layer4": [
            {
              "name": "helloWorldIngress",
              "publicPort": "80",
              "applicationName": "helloWorldApp",
              "serviceName": "helloWorldService",
              "endpointName": "helloWorldListener"
            }
          ]
        }
      }
    },
    {
      "apiVersion": "2018-07-01-preview",
      "name": "helloWorldApp",
      "type": "Microsoft.ServiceFabricMesh/applications",
      "location": "[parameters('location')]",
      "dependsOn": [
        "Microsoft.ServiceFabricMesh/networks/helloWorldNetwork"
      ],
      "properties": {
        "description": "Service Fabric Mesh HelloWorld Application!",
        "services": [
          {
            "type": "Microsoft.ServiceFabricMesh/services",
            "location": "[parameters('location')]",
            "name": "helloWorldService",
            "properties": {
              "description": "Service Fabric Mesh Hello World Service.",
              "osType": "linux",
              "codePackages": [
                {
                  "name": "helloWorldCode",
                  "image": "seabreeze/azure-mesh-helloworld:1.1-alpine",
                  "endpoints": [
                    {
                      "name": "helloWorldListener",
                      "port": "80"
                    }
                  ],
                  "resources": {
                    "requests": {
                      "cpu": "1",
                      "memoryInGB": "1"
                    }
                  }
                },
                {
                  "name": "helloWorldSideCar",
                  "image": "seabreeze/azure-mesh-helloworld-sidecar:1.0-alpine",
                  "resources": {
                    "requests": {
                      "cpu": "1",
                      "memoryInGB": "1"
                    }
                  }
                }
              ],
              "replicaCount": "1",
              "networkRefs": [
                {
                  "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'helloWorldNetwork')]"
                }
              ]
            }
          }
        ]
      }
    }
  ]
}

On voit dans ce template ARM, 2 objets. Le premier est un composant réseau de type ‘Microsoft.ServiceFabricMesh/networks’

    {
      "apiVersion": "2018-07-01-preview",
      "name": "helloWorldNetwork",
      "type": "Microsoft.ServiceFabricMesh/networks",
      "location": "[parameters('location')]",
      "dependsOn": [],
      "properties": {
        "addressPrefix": "10.0.0.4/22",
        "ingressConfig": {
          "layer4": [
            {
              "name": "helloWorldIngress",
              "publicPort": "80",
              "applicationName": "helloWorldApp",
              "serviceName": "helloWorldService",
              "endpointName": "helloWorldListener"
            }
          ]
        }
      }
    }

Il s’agit là d’un composant réseau où l’on peut mettre nos applications, attention il ne s’agit pas d’un Virtual Network sur Azure.

Les informations essentielles sont donc les suivantes :

  • La plage d’adresse IPs disponibles, ici : 10.0.0.4/22
  • Les points d’entrées réseaux contenant
    • Le nom de votre endpont : helloWorldIngress
    • Le port : 80
    • Le nom de votre application qui sera exposé : helloWorldApp
    • Le nom de votre service qui sera exposé : helloWorldService
    • Le nom de votre endpoint : helloWorldListener

Le deuxième élément correspond à notre service applicatif:

              "osType": "linux",
              "codePackages": [
                {
                  "name": "helloWorldCode",
                  "image": "seabreeze/azure-mesh-helloworld:1.1-alpine",
                  "endpoints": [
                    {
                      "name": "helloWorldListener",
                      "port": "80"
                    }
                  ],
                  "resources": {
                    "requests": {
                      "cpu": "1",
                      "memoryInGB": "1"
                    }
                  }
                }

Il contient par ailleurs le type de l’OS, puisque ici on embarque la définition de notre cluster. Ici, il s’agit d’un OS de type linux, car après tout c’est généralement des images Docker pour linux que l’on crées.

On définit ensuite nos différents containers en renseignant le type de l’image. Ici il s’agit d’images situées sur Docker Hub

Par ailleurs si vous souhaitez déployer depuis une registry privée comme Azure Container Registry, vous pouvez mettre ces informations dans le template ARM :

"image": "azure-mesh-helloworld:1.1-alpine",
"imageRegistryCredential": {
    "server": "[parameters('registry-server')]",
    "username": "[parameters('registry-username')]",
    "password": "[parameters('registry-password')]"
},

Vous pouvez retrouver différents exemples de template ARM pour Service Fabric sur ce github : https://github.com/Azure/azure-rest-api-specs/tree/master/specification/servicefabricmesh/resource-manager/Microsoft.ServiceFabricMesh/preview/2018-07-01-preview

Il existe différents exemples de code fournis par Microsoft, notamment la classique voting-app, mais il y a aussi containo une application de démo créée par la Tom Kerkhove qui montre comment utiliser Service Fabric Mesh : https://github.com/tomkerkhove/containo/blob/master/deploy/service-fabric-mesh/service-fabric-mesh-orders-declaration.json

JUIL 10

Migrer vos vieux comptes de stockage

Mois de juillet oblige, c’est le grand nettoyage de printemps avec quelques mois de retard. Je suis donc en train de faire le grand ménage dans mes différents comptes Azure.

Si comme moi il vous reste des vieux comptes de stockage en mode classic, il est peut être temps de les migrer.

La procédure est très simple à faire en Powershell notamment, à partir du moment ou vous êtes Classic Admin de votre souscription Azure.

Pour commencer il vous faut le module powershell Azure, installer le si vous n’avez que AzureRM.

Install-Module Azure

Ensuite, il vous faut vérifier que le provider Microsoft.ClassicInfrastructureMigrate est bien enregistré sur votre souscription, soit via le portail Azure, soit via powershell via les commandes suivantes :

Install-AzureRmResourceProvider -ProviderNamespace Microsoft.ClassicInfrastructureMigrate

Get-AzureRmResourceProvider -ProviderNamespace Microsoft.ClassicInfrastructureMigrate

Attention, il s’agit là de commande AzureRM, il faut donc être loggué en conséquence.

Maintenant tout le reste se fait en mode classic, on va donc commencer par s’authentifier à l’ancienne

Add-AzureAccount
Select-AzureSubscription -SubscriptionName $subscriptionName

Afin de valider si votre storage est migrable, vous pouvez utiliser la commande suivante :

Move-AzureStorageAccount -Validate -StorageAccountName $storageAccountName

Cette commande vérifie notamment qu’il n’y a pas de disques de VM attaché à ce storage account.

Maintenant que tout est validé, il est possible de migrer via ces 2 commandes powershell

Move-AzureStorageAccount -Prepare -StorageAccountName $storageAccountName

Move-AzureStorageAccount -Commit -StorageAccountName $storageAccountName

Il est possible d’annuler l’opération entre le Prepare et le Commit via la commande

Move-AzureStorageAccount -Abort -StorageAccountName $storageAccountName

Après un temps plus ou moins long, selon votre Storage Account, vous pouvez retrouver votre Storage Account fraichement migré dans un nouveau Resource Group s’appelant “$storageAccountName-Migrated”

Il est bien entendu possible de le Move par la suite afin de le mettre au bon endroit dans votre souscription

A noter au passage qu’il a changé de resourceId, cela va donc impacter les différentes ressources liées telles que :

  • Les dashboard Application Insights
  • L’autoscaling basé sur des métriques provenant de ce storage
  • Tout autre produit se basant sur les resourceId

Bien entendu, cela va de soit, vos clés primaires et secondaires d’accès au storage sont inchangées.

Je vous souhaite une bonne migration !

JUIL 02

Sandbox Azure - Mise en place de policy Azure

Dans le cadre de la sandbox Azure dont je vous ai parlé dans les articles précédents:

Maintenant que les utilisateurs peuvent créer un compte et créer des ressources groups, je souhaite contrôler certaines de leurs actions afin que ce ne soit pas l’anarchie au bout de 2 semaines. Je souhaite donc contrôler les éléments suivants :

  • La localisation des ressources : afin de m’assurer que tout soit situé en Europe
  • Le niveau de SKU des ressources : afin de m’assurer d’avoir encore du crédit à la fin de la semaine

Pour effectuer cela, j’ai plusieurs solutions qui s’offrent à moi.

  • Soit je contrôle ces éléments lors de la création des ressources.

  • Soit je le fais a posteriori, ce qui demande d’analyser les différents objets et de mettre en place du développement spécifique basé sur EventGrid afin de valider les différentes ressources qui ont été créées, et effectuer les opérations nécessaires par la suite.

  • Soit encore je le fais à la création des ressources, pour cela il est possible de mettre en place une surcouche applicative qui prend tous les templates ARM et qui les valide avant les exécuter, mais du coup cela oblige tous les utilisateurs à passer par un système tiers et de ce fait ne pas pouvoir avoir une expérience totale via le portail Azure. Et par ailleurs en cas de changement de règle les ressources existantes ne sont pas prises en compte.

Afin d’allier les deux, une fonctionnalité existe sur Azure, il s’agit de la fonctionnalité “Policy – Compliance” que l’on peut retrouver sur le portail ou via les API REST / Azure Powershell / Azure CLI

Il existe par défaut un certain nombre de policy qu’il est possible d’exploiter, comme par exemple les suivantes :

  • Allowed Locations : Elle permet de limiter l’utilisation de différentes régions sur Azure, utile si par exemple vous ne souhaitez avoir que des ressources en Europe
  • Allowed Resource types : Elle permet de limiter l’usage des ressources avec un filtre plus fin que l’utilisation ou non d’un provider. Par exemple pour autoriser l’utilisation de Service Fabric, et en refusant celle des edgeclusters
  • Enforce tag and its value : Comme son nom l’indique elle permet de forcer l’usage d’un tag, ce qui peut être pratique pour des gestions de coûts par exemple
  • Allowed virtual machine SKUs : Permet de garder uniquement les tailles de machines virtuelles que l’on souhaite utiliser, par exemple en n’autorisant pas celle avec des GPUs

Il est possible d’assigner ces règles à des scopes Azure , un peu comme le système RBAC, sauf qu’ici on est limité à la souscription ou à un groupe de ressource, ce qui est déjà en soit bien suffisant.

Ci-dessous voici un json d’un exemple de politique associé à ma souscription :

{
  "Name": "686a94bb904447afb201f2cf",
  "ResourceId": "/subscriptions/subscription_id/providers/Microsoft.Authorization/policyAssignments/686a94bb904447afb201f2cf",
  "ResourceName": "686a94bb904447afb201f2cf",
  "ResourceType": "Microsoft.Authorization/policyAssignments",
  "SubscriptionId": "subscription_id",
  "Properties": {
    "displayName": "Allowed locations",
    "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c",
    "scope": "/subscriptions/subscription_id",
    "notScopes": [

    ],
    "parameters": {
      "listOfAllowedLocations": {
        "value": [
          "francecentral",
          "francesouth",
          "northeurope",
          "westeurope"
        ]
      }
    },
    "description": "Only specific locations are available for CloudSandbox",
    "metadata": {
      "assignedBy": "Admin Admin",
      "parameterScopes": {
        "listOfAllowedLocations": "/subscriptions/subscription_id"
      }
    }
  },
  "Sku": {
    "name": "A0",
    "tier": "Free"
  },
  "PolicyAssignmentId": "/subscriptions/subscription_id/providers/Microsoft.Authorization/policyAssignments/686a94bb904447afb201f2cf"
}

Cependant vu que rien n’est parfait, il n’y a pas plétore de policy “build-in”, et donc pas toujours celles que l’on souhaite. Bonne nouvelle cependant, tout comme les rôles RBAC, il est possible d’en créer des personnalisables.

Par exemple, si je souhaite limiter le type de SKU d’une instance SQL Database, je peux écrire le JSON suivant :

{
	"if": {
		"allOf": [
			{
				"field": "type",
				"equals": "Microsoft.Sql/servers/databases"
			},
			{
				"not": {
                    "field": "Microsoft.Sql/servers/databases/requestedServiceObjectiveName",
                    "in": "[parameters('listOfSKUId')]"
				}
			}
		]
	},
	"then": {
		"effect": "deny"
	}
}

Il est possible par la suite de l’associer à notre souscription via cette commande Powershell

New-AzureRmPolicyDefinition -Name 'SqlDBSkus' -DisplayName 'My custom policy for SQL DB SKUs' -Policy 'SQLDbsSKUsPolicy.json'

Après, la mise en place de votre policy se fait soit via le portail Azure, soit via cette commande powershell.

$rg = Get-AzureRmResourceGroup -Name 'rg-demo'
$policy = Get-AzureRmPolicyDefinition -Name 'SqlDBSkus'
New-AzureRmPolicyAssignment -Name 'limit-sql-db-skus' -DisplayName 'Custom policy for SQL DB SKUs' -Scope $rg.ResourceId -PolicyDefinition $definition -PolicyParameter .\AllowedSqlDBSkus.json

Les policy Azure vous offrent de ce fait la possibilité de mieux gérer et organiser votre souscription Azure que ce soit pour valider des règles de naming ou mieux maitriser le coût comme ici.

JUIN 19

Service Fabric - Utiliser des contraintes de placements en local

Lors d’un précédent article, je vous ai montré comment mettre en place des contraintes de placement sur votre cluster Service Fabric. Un lien vers l’article si vous ne l’avez pas lu, je le recommande : http://blog.woivre.fr/blog/2018/6/service-fabric-deployer-sur-un-cluster-multinodes

Maintenant je suppose que vous n’utilisez pas un véritable cluster pour déployer vos applications lors des phases de développement, mais votre cluster de dev situé sur votre poste. Il existe plusieurs manières d’utiliser les contraintes de placement en local, et nous allons voir comment les mettre en place dans cet article.

La solution la plus simple, c’est de s’affranchir de la gestion des contraintes de placement en local. Pour cela on va utiliser les différents PublishProfile que nous offre Visual Studio.

Pour cela, dans notre fichier ApplicationManifest.xml, il faut déclarer des valeurs par défaut, comme ci-dessous :

<Parameter Name="Stateful_Constraint" DefaultValue="NodeTypeName==AppNode" />
  <Parameter Name="Web_Constraint" DefaultValue="" />
</Parameters>

Il est par la suite possible de les utiliser dans le même fichier de cette manière :

<Service Name="Stateful" ServicePackageActivationMode="ExclusiveProcess">
  <StatefulService ServiceTypeName="StatefulType" TargetReplicaSetSize="[Stateful_TargetReplicaSetSize]" MinReplicaSetSize="[Stateful_MinReplicaSetSize]">
    <UniformInt64Partition PartitionCount="[Stateful_PartitionCount]" LowKey="-9223372036854775808" HighKey="9223372036854775807" />
    <PlacementConstraints>[Stateful_Constraint]</PlacementConstraints>
  </StatefulService>
</Service>
<Service Name="Web" ServicePackageActivationMode="ExclusiveProcess">
  <StatelessService ServiceTypeName="WebType" InstanceCount="[Web_InstanceCount]">
    <SingletonPartition />
    <PlacementConstraints>[Web_Constraint]</PlacementConstraints>
  </StatelessService>
</Service>

On retrouve ici un usage que je classifierai de classique dans le monde de la programmation, puisqu’après il suffit de modifier nos PublishProfile pour adapter nos contraintes de placement.

Si par contre vous mettez en place des services qui démarrent à la demande via votre code, il est possible de mettre en place dans votre code la notion de contraintes de placement uniquement sur des clusters de production, mais cela peut être source d’erreur en cas d’oubli de configuration sur votre poste local, ou pire une mauvaise configuration lorsque vous déployez sur un véritable cluster.

On va donc s’intéresser au cluster local, et voir à quoi il ressemble de plus près via la cluster map ci-dessous

image

On voit ici que nous avons un cluster local basé sur 5 nœuds, qui ont par défaut les propriétés suivantes :

"nodes": [
  {
    "nodeName": "_Node_0",
    "iPAddress": "ComputerFullName",
    "nodeTypeRef": "NodeType0",
    "faultDomain": "fd:/0",
    "upgradeDomain": "0"
  },
  {
    "nodeName": "_Node_1",
    "iPAddress": "ComputerFullName",
    "nodeTypeRef": "NodeType1",
    "faultDomain": "fd:/1",
    "upgradeDomain": "1"
  },
  {
    "nodeName": "_Node_2",
    "iPAddress": "ComputerFullName",
    "nodeTypeRef": "NodeType2",
    "faultDomain": "fd:/2",
    "upgradeDomain": "2"
  },
  {
    "nodeName": "_Node_3",
    "iPAddress": "ComputerFullName",
    "nodeTypeRef": "NodeType3",
    "faultDomain": "fd:/3",
    "upgradeDomain": "3"
  },
  {
    "nodeName": "_Node_4",
    "iPAddress": "ComputerFullName",
    "nodeTypeRef": "NodeType4",
    "faultDomain": "fd:/4",
    "upgradeDomain": "4"
  }
],

On peut voir que chaque nœud est associé à un type différent, qui est décrit dans le fichier que vous pouvez retrouver sur votre poste, et qui se trouve à cet endroit :

C:\Program Files\Microsoft SDKs\Service Fabric\ClusterSetup\NonSecure\FiveNode\ClusterManifestTemplate.json

Il est possible de modifier ce fichier afin de le configurer selon vos besoins, pour cela je vous conseille de supprimer votre cluster local, ainsi que les différents dossiers résiduels afin de commencer sur un environnement “propre”.

Dans mon cas je vais modifier le node 4 pour que son type soit le suivant :

{
  "name": "AppNode",
  "clientConnectionEndpointPort": "19040",
  "clusterConnectionEndpointPort": "19042",
  "leaseDriverEndpointPort": "19041",
  "serviceConnectionEndpointPort": "19046",
  "httpGatewayEndpointPort": "19088",
  "reverseProxyEndpointPort": "19089",
  "applicationPorts": {
    "startPort": "34001",
    "endPort": "35000"
  },
  "isPrimary": false,
  "placementProperties": {
    "HasSSD": "true",
    "NodeColor": "green",
    "SomeProperty": "5"
  }
}

J’ai donc ici changé son nom afin qu’il corresponde à celui que j’utilise dans mon précédent article, et j’y ajoute différentes propriétés via le champs placementProperties.

Lorsque je relance mon cluster, je peux donc voir dans le manifest de celui-ci mes différents changements, comme on peut le voir ci dessous :

image

C’est donc aussi simple que cela de personnaliser votre cluster local si besoin, car c’est aussi ce fichier qui vous permettra de changer les différents ports utilisés ou bien de changer les différents dossiers par défaut qui sont utilisés.

JUIN 18

Nouveau blog

Et voilà, j’ai encore migré mon blog. En même temps ça ne sera que la 2ème fois depuis sa création.

Petit historique pour ceux qui ne savent pas :

  • Création en 2009 sur un Wordpress hébergé gratuitement sur leur plateforme
  • Migration en 2012 vers Azure avec une solution basée sur les services suivants :
    • Azure Table Storage : pour les métadonnées des articles
    • Azure Blob Storage : pour les articles et les images
    • Azure CDN : parce que plus vite c’est toujours mieux
    • Azure Cloud Services : pour l’hébergement des services, les Web Apps n’existaient pas à l’époque
    • Le tout réalisé en full custom en C# par mes soins
    • Et une absence totale de backend, j’alimentais les articles via Open Live Writer.
  • Nouveau blog basé sur Jekyll et hébergé sur Github

Alors la seule constante dans ces 3 moutures de blog c’est bien entendu que le design est toujours aussi peu travaillé.

J’ai migré sur cette nouvelle mouture pour plusieurs raisons :

  • Arrêter d’utiliser Open Live Writer, c’est un bon produit, mais c’est un client lourd, et qui tourne que sur Windows
  • Plus de code à gérer lorsque je veux implémenter une nouvelle fonctionnalité ou faire un fix rapide.
  • Fini les galères à colorer le code, le Markdown c’est bien plus simple qu’une extension Visual Studio qui génère un HTML approximatif.

Et la dernière raison, qui est celle qui m’a fait mettre cette migration en priorité, c’est qu’à partir de début juillet 2018 je ne serai plus MVP Azure. J’ai perdu le titre car j’ai clairement un manque de contribution public ces dernières années où je n’ai pas assez fait la part des choses entre mon investissement privé chez mes clients, et mes investissements publics.

Cependant ceux qui me suivent, savent qu’en ce moment j’écris des articles et je suis assez régulièrement en meetup, parce que j’ai envie de continuer l’aventure en tant que MVP. Pour moi cette distinction apporte énormément aussi bien en terme professionnel que personnel, je vais donc continuer à partager ce que je sais au sein d’évènements communautaires et tâcher de récupérer ce titre.