Service Mesh sur Kubernetes

Publié le 30 mai 2020
Abdelhak FRIHA
scroll
servicemesh3
N’oubliez pas
de partager
cet article

1. Service Mesh

a. Introduction

L’architecture microservices est une approche permettant de développer une application dite cloud-native unique sous la forme d’une suite de petits services, chacun s’exécutant dans son propre processus et communiquant avec des mécanismes légers.

Ces services sont construits autour de capacités métier et peuvent être déployés indépendamment de façon entièrement automatisée. Il existe un minimum de gestion centralisée de ces services, qui peuvent être écrits dans différents langages de programmation utilisant différentes technologies de stockage de données.

La communication service à service est ce qui rend les microservices possibles. La logique qui gouverne la communication peut être codée dans chaque service sans couche de service mesh – mais plus la communication entre ces services est complexe, plus le service mesh est précieux.

Pour les applications de type cloud-native construites dans une architecture de microservices, un service mesh est un moyen de regrouper un grand nombre de services distincts dans une application fonctionnelle.

b. Définition

Un service mesh est une couche d’infrastructure dédiée destinée à la gestion de la communication entre services. Il est responsable de la livraison fiable des demandes via la topologie complexe des services qui constituent une application cloud-native.

c. Comment ça marche ?

Un service mesh se compose d’un plan de données (DP) et d’un plan de contrôle.

Le DP du service mesh est responsable de la communication des services contenus dans le mesh et peut assurer des fonctionnalités telles que l’équilibrage de charge, le chiffrement et la reprise après défaillance, par l’intermédiaire d’une couche d’infrastructure dédiée.

Le proxy side-car représente le plan de données du service mesh et est attaché au plan de contrôle (CP), qui gère et configure chaque side-car en relation avec son service désigné. Tout le trafic réseau provenant d’un service est filtré par le proxy side-car, qui fonctionne en tant que couche d’infrastructure distincte.

Un proxy side-car (sidecar proxy, en anglais) est un schéma de conception applicatif qui extrait certaines fonctionnalités, telles que les communications entre services, la surveillance et la sécurité, en les isolant de l’architecture principale pour faciliter le suivi et la maintenance de l’application globale.

d. Consul

Consul est une solution de service mesh fournissant un plan de contrôle complet avec des fonctionnalités de découverte, de configuration et de segmentation de services. Chacune de ces fonctionnalités peut être utilisée individuellement selon les besoins ou ensemble pour créer un service mesh complet.

Consul nécessite un plan de données et prend en charge un modèle d’intégration natif et un proxy. Il est livré avec un proxy intégré simple, mais prend également en charge les intégrations de proxy tierces telles qu’Envoy.

e. Istio

Istio est une plate-forme open-source offrant un moyen uniforme d’intégrer des microservices, de gérer le flux de trafic entre microservices, d’appliquer des politiques et de rassembler des données de télémétrie. Le plan de contrôle d’Istio fournit une couche d’abstraction sur la plateforme de gestion de cluster sous-jacente, telle que Kubernetes.

2. Consul + Kubernetes

a. Introduction

Consul a de nombreuses intégrations avec Kubernetes. Vous pouvez déployer Consul sur Kubernetes à l’aide de Helm, synchroniser les services entre Consul et Kubernetes, sécuriser automatiquement la communication entre Pod avec Connect, etc.

Cette section documente les intégrations officielles entre Consul et Kubernetes.

b. Déploiement de Consul sur Kubernetes

Consul peut fonctionner directement sur Kubernetes, en mode serveur ou client. Pour les Workloads purement Kubernetes, cela permet également à Consul d’exister uniquement dans Kubernetes.  Pour les Workloads hétérogènes, les agents Consul peuvent rejoindre un serveur déployé à l’intérieur ou à l’extérieur de Kubernetes.

c. Helm Chart

Helm est un outil de gestion de « chart » Kubernetes. Les charts sont des packages de ressources Kubernetes préconfigurées. La méthode recommandée pour déployer Consul sur Kubernetes consiste à utiliser le Helm Chart. Cela installera et configurera tous les composants nécessaires à l’exécution de Consul. La configuration vous permet de déployer soit un cluster de serveurs, ou un cluster de clients, ou les deux en même temps.

d. Configuration

Avant de lancer l’installation, les charts helm peuvent être personnalisés avec un fichier de « values ». Le chart Consul est hautement personnalisable à l’aide des valeurs de configuration Helm. Chaque variable a une valeur par défaut pour une expérience de démarrage optimale avec Consul.

Voici un exemple d’un fichier de values pour le Consul chart:

 

syncCatalog:
        enabled: true
    connectInject:
        enabled: true
    client:
        grpc: true

 

Ces valeurs activent la synchronisation des services entre Kubernetes et Consul, et l’injection automatique du Connect proxy sidecar qui nécessite le grpc activé.

e. Installation

Le chart Consul est hautement personnalisable à l’aide des valeurs de configuration Helm. Chaque variable a une valeur par défaut pour une expérience de démarrage optimale avec Consul. Dans notre cas, nous utilisons ces valeurs pour activer la synchronisation des services entre Kubernetes et Consul, et l’injection automatique des side-cars.

 

# Clone the chart repo
      git clone https://github.com/hashicorp/consul-helm.git
      cd consul-helm

    # Checkout une version
      git checkout v0.8.0

    # Run Helm
      helm install -f examplevalues.yaml --name consul ./

 

f. Configuration du DNS

L’interface DNS de Consul peut être exposée pour tous les pods de Kubernetes en configurant un sous-domaine.

La configuration du sous-domaine doit pointer sur l’addresse statique d’un DNS. Le chart Helm crée un service consul-dns par défaut qui expose le DNS de Consul. Le cluster IP de ce service peut être utilisé pour configurer un sous-domaine avec kube-dns ou bien coredns.

Cela se fait en modifiant la ConfigMap du kube-dns (coredns) sur Kubernetes. Sur AKS (v1.12.6), la ConfigMap à modifier porte le nom coredns-custom. Ensuite, Il faudra redémarrer les pods de coredns.

 

apiVersion: v1
    kind: ConfigMap
    metadata:
      name: coredns-custom
      namespace: kube-system
    data:
      consul.server: |
        consul {
          errors
          cache 30
          proxy . <consul-dns service cluster ip>
        }

    kubectl apply -f consuldns.yaml
    kubectl -n kube-system delete po -l k8s-app=kube-dns

 

Pour vérifier le bon fonctionnement, nous lançons un job sur Kubernetes qui fait une résolution DNS du service Consul.

 

apiVersion: batch/v1
    kind: Job
    metadata:
      name: dns
    spec:
      template:
        spec:
          containers:
          - name: dns
            image: anubhavmishra/tiny-tools
            command: ["dig",  "consul.service.consul"]
          restartPolicy: Never
      backoffLimit: 4

kubectl apply -f dnsjob.yaml

 

Ensuite, nous consultons les logs du pod en question :

3. Démonstration Consul

Dans cette partie, nous déployons une application pour tester les fonctionnalités de Consul. Cette application est un projet nommé « Projet-31 ».

Pour créer le conteneur, nous avons utilisé Docker. Il suffit simplement de rajouter un fichier Dockerfile aux fichiers sources de l’application. Grâce à ce fichier, Docker installe toutes les dépendances de l’application, NodeJS dans ce cas, et crée un conteneur prêt à être déployé dans n’importe quel environnement pouvant exécuter des conteneurs.

En plus du conteneur de l’application, un conteneur nginx qui redirige les connexions entrantes vers l’API nodejs est ajouté. Cette API, permet à des utilisateurs de s’inscrire pour ensuite les enregistrer dans une base de données MongoDB.

L’ensemble de ces conteneurs sont déployés sur AKS.

a. Déploiement

 

apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: projet-31
    namespace: projet
    labels:
      app: "projet-31"
  spec:
    selector:
      matchLabels:
        app: "projet-31"
        version: "1.0"
    replicas: 3
    template:
      metadata:
        labels:
          app: "projet-31"
          version: "1.0"
        annotations:
          "consul.hashicorp.com/connect-inject": "true"
          "consul.hashicorp.com/connect-service-upstreams":"mongo:27017"
      spec:
        containers:
        - name: projet-31
          image: abdelhakverwest/projet-31:release-1.0

kubectl apply -f mernapp.yaml

  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
    namespace: projet
    labels:
      app: "nginx"
  spec:
    selector:
      matchLabels:
        app: "nginx"
    replicas: 3
    template:
      metadata:
        labels:
          app: "nginx"
        annotations:
          "consul.hashicorp.com/connect-inject": "true"
          "consul.hashicorp.com/connect-service-upstreams":"projet-31:5000"
      spec:
        containers:
        - name : nginx
          image: "nginx:1.11.5"

kubectl apply -f nginxproxy.yaml

 

Nous remarquons deux annotations spécifiques à Consul:

  1. « consul.hashicorp.com/connect-inject »: »true » : L’injection du proxy side-car Connect est activée. Ceci enregistre le service dans le registre de Consul qui, par défaut, prend le nom du premier conteneur.
  2. « consul.hashicorp.com/connect-service-upstreams » : La liste des services auxquels ce pod aura besoin de se connecter via Connect avec un port local statique pour écouter ces connexions. Example: projet-31:5000, va démarrer une connexion locale au service projet-31 sur le port 5000.

Pour exposer l’application, un service de type LoadBalancer est nécessaire. Une adresse IP statique et un label DNS dédiés à ce service ont été créés auparavant avec Terraform.

 

kind: Service
  apiVersion: v1
  metadata:
    name: nginx-projet31
    namespace: projet
    annotations:
      service.beta.kubernetes.io/azure-load-balancer-resource-group: "***"
  spec:
    selector:
      app: nginx
    ports:
      - protocol: "TCP"
        port: 443
        targetPort: 443
    type: LoadBalancer
    loadBalancerIP: "***"

 

b. Service Discovery

Pour vérifier que le Service Discovery de Consul est opérationnel, nous lançons des requêtes de résolutions DNS des services nginx et projet-31.

 

kubectl run --rm utils -it \
  --generator=run-pod/v1 --image arunvelsriram/utils bash

 

Nous remarquons que le DNS de Consul nous renvoie l’adresse des pods de chaque service recherché.

c. Service Segmentation

Pour tester la segmentation de service de Consul, nous allons créer des intentions. Les intentions sont des règles d’accès au service. Nous pouvons permettre ou non à un service de communiquer avec un autre.

Pour cela, nous utilisons soit la UI de Consul ou le Consul CLI. Une façon de faire est d’installer Consul sur notre machine, et faire un port-forward d’un des pods serveurs de Consul. Ensuite, nous créons l’intention « deny » qui va refuser l’accès au service projet-31 à partir du service nginx.

 

kubectl port-forward consul-consul-server-0 8500:8500
  consul intention create -deny nginx projet-31

 

Dans cet exemple, nous interdisons l’accès au service projet-31 (notre application nodejs) à partir du proxy nginx. Pour redonner l’accès au service projet-31, il suffit de supprimer l’intention créée.

 

consul intention delete nginx projet-31

4. Istio + Kubernetes

a. Déploiement d’Istio sur Kubernetes 

Prérequis :

Pour faire partie d’un Istio service mesh, les pods et les services dans un cluster Kubernetes doivent répondre aux exigences suivantes:

  • Les ports définis dans un service doivent être nommés avec la syntaxe suivante: name: <protocol>[-suffix>]. Example: http-example.
  • Les pods doivent avoir un containerPort défini explicitement.
  • Un pod doit être associé à un service Kubernetes même si le pod n’expose aucun port
  • Il est conseillé de labeliser les déploiements avec les label app: et version:
  • Istio utilise un ensemble de ports et de protocoles. Ces ports ne doivent pas être utilisés sur des services TCP headless. [4]

Il est nécessaire d’installer ces CRDs dans un premier temps puisque Istio en dépend.

 

helm install install/kubernetes/helm/istio-init `
  --name istio-init --namespace istio-system

  apiVersion: v1
  kind: Secret
  metadata:
    name: grafana
    namespace: istio-system
    labels:
      app: grafana
  type: Opaque
  data:
    username: ****
    passphrase: ****

  kubectl apply -f grafanasecret.yaml

  apiVersion: v1
  kind: Secret
  metadata:
    name: kiali
    namespace: istio-system
    labels:
      app: kiali
  type: Opaque
  data:
    username: ****
    passphrase: ****

  kubectl apply -f kialisecret.yaml

 

Nous installerons Grafana et Kiali. Ces composants nécessitent des informations d’identification qui doivent être fournies en tant que secret Kubernetes.

Grafana est un outil qui nous permettra de visualiser les métriques réseaux de nos services.
Kiali nous donneras une visibilité globale sur notre architecture service mesh.

b. Installation

 

helm install install/kubernetes/helm/istio --name istio `
  --namespace istio-system `
  --set global.controlPlaneSecurityEnabled=true `
  --set mixer.adapters.useAdapterCRDs=false `
  --set grafana.enabled=true --set grafana.security.enabled=true `
  --set tracing.enabled=true `
  --set kiali.enabled=true

 

Paramètres:

  • global.controlPlaneSecurityEnabled=true : mutual TLS activé
  • grafana.enabled=true : Grafana activé
  • grafana.security.enabled=true : Activation de l’authentification sur grafana
  • tracing.enabled=true : Jaeger pour le tracing activé
  • kiali.enabled=true : Kiali pour l’observabilité du service mesh activé

5. Démonstration Istio

Dans cette section, nous allons voir une démonstration d’un scénario de déploiement en Canary et en A/B Testing en utilisant Istio.

 

 

kubectl create namespace projet

  kubectl label namespace projet istio-injection=enabled

 

a. Déploiement

 

apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: projet-31
    namespace: projet
    labels:
      app: "projet-31"
  spec:
    selector:
      matchLabels:
        app: "projet-31"
        version: "1.0"
    replicas: 3
    template:
      metadata:
        labels:
          app: "projet-31"
          version: "1.0"
      spec:
        containers:
        - name: projet-31
          image: abdelhakverwest/projet-31:release-1.0
  .......
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: projet-31-v2
    namespace: projet
    labels:
      app: "projet-31-v2"
  spec:
    selector:
      matchLabels:
        app: "projet-31"
        version: "2.0"
    replicas: 3
    template:
      metadata:
        labels:
          app: "projet-31"
          version: "2.0"
      spec:
        containers:
        - name: projet-31
          image: abdelhakverwest/projet-31:release-2.0

  kubectl apply -f deployment.yaml

  apiVersion: v1
  kind: Service
  metadata:
   name: projet-31
   namespace: projet
   labels:
     app: "projet-31"
  spec:
   ports:
   - port: 5000
     name: http
     targetPort: 5000
   selector:
     app: "projet-31"

  kubectl apply -f projetservice.yaml

 

b. Déploiement Canary avec Istio

Pour réaliser un scénario de déploiement en Canary, il faut configurer:

  • Une Gateway Istio qui expose notre service au réseau externe.
  • Un VirtualService qui définit les règles qui contrôlent la manière dont les requêtes d’un service sont routées dans un service mesh Istio.
  • Une DestinationRule qui configure l’ensemble de règles à appliquer sur une requête après le routage du VirtualService.

 

apiVersion: networking.istio.io/v1alpha3
  kind: Gateway
  metadata:
    name: projet-31-gateway
    namespace: projet
  spec:
    selector:
      istio: ingressgateway
    servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts: "*" 

  kubectl apply -f projetgateway.yaml

  apiVersion: networking.istio.io/v1alpha3
  kind: VirtualService
  metadata:
    name: projet-31
    namespace: projet
  spec:
    hosts:
    - "*"
    gateways:
    - "projet-31-gateway"
    http:
    - route:
    - destination:
        host: projet-31
        subset: v2-0
        port:
          number: 5000
      weight: 50     
    - destination:
        host: projet-31
        subset: v1-0
        port:
          number: 5000
      weight: 50

  kubectl apply -f projetvirtualservice.yaml

 

50% du trafic sera routé vers la version v1 et 50% vers la version v2. Le « host » est le nom du service destinataire, et le « subset » fait référence aux subset définis dans la DestinationRule.

 

apiVersion: networking.istio.io/v1alpha3
  kind: DestinationRule
  metadata:
    name: projet-31
    namespace: projet
  spec:
    host: projet-31
    subsets:
    - name: v1-0
      labels:
        version: "1.0"
    - name: v2-0
      labels:
        version: "2.0"

  kubectl apply -f projetdestinationrule.yaml

 

Une DestinationRule configure l’ensemble de règles à appliquer à une requête après le routage du VirtualService.

c. Stratégie A/B Testing avec Istio

 

http:
     - match:
       - uri:
           prefix: /
         headers:
           user-agent:
             regex: ".*Firefox.*"
       route:
       - destination:
           host: projet-31
           subset: v2-0
           port:
             number: 5000
     - route:
       - destination:
           host: projet-31
           subset: v1-0
           port:
             number: 5000

 

6. Comparatif Consul vs Istio

Pour comparer Consul et Istio, je me suis basé sur les critères suivants:

  • Performance: Impacte les temps de réponses aux requêtes des clients.
  • Mise en place: Prérequis, configuration et installation.
  • Fonctionnalités: Catalogue des fonctionnalités présentées par chaque outil.
 

Consul

 

 

Istio

– Un binaire unique fournissant à la fois des fonctionnalités serveur et client, et inclut toutes les fonctionnalités pour le catalogue de services, la configuration, les certificats TLS, les autorisations, etc. Cette architecture permet à Consul de s’installer facilement sur n’importe quelle plateforme et d’avoir une bonne performance.

– Facile à mettre en place et à prendre en main.

– Fonctionne comme un service mesh sans services de plan de contrôle centralisés qui pourraient diminuer les performances

– Registre de service intégré, Il n’est pas nécessaire d’installer de systèmes supplémentaires pour utiliser Consul.

– Un serveur DNS efficace pour le Service Discovery. – Une interface Web riche qui facile l’utilisation des fonctionnalités de Consul.

– Assure la résilience des communications et effectue de nouvelles tentatives si une connexion échoue ou trouve une autre instance du service et établit la connexion.

– Fournit des fonctionnalités de couche 7 pour le routage basé sur les chemins, la mise en forme du trafic, l’équilibrage de la charge et la télémétrie.

– Des stratégies de contrôle d’accès peuvent être configurées pour cibler les propriétés des couches 7 et 4 afin de contrôler l’accès, le routage, etc. en fonction de l’identité du service.

– Implémente des cloisons et des disjoncteurs. Les cloisons isolent les éléments d’une application pour s’assurer que toute défaillance d’un service donnée n’affecte aucun autre service. Les disjoncteurs éliminent les services défaillants pour réguler la consommation de ressources et les temps de réponse aux requêtes.

 

Consul

 

 

Istio

– Le proxy par défaut de Consul manque assez de fonctionnalités.

– Pas de routage couche 7 (dans cette version). Les stratégies de déploiement (Canary, A/B, etc.) ne peuvent pas être mises en place.

– Nécessite des annotations dans les fichiers de déploiement pour injecter les proxy side-cars.

– Pour avoir toutes les fonctionnalités d’Istio, plusieurs services doivent être déployés. Pour le plan de contrôle: Pilot, Mixer et Citadel doivent être déployés et pour le plan de données, un sidecar Envoy est déployé. Cela pourrait avoir des impacts sur la performance.

– Requiert un catalogue de services tiers de Kubernetes, Consul ou autre.

– Nécessite un système externe pour stocker l’état, généralement etcd.

 7. En conclusion

Istio offre bien plus de fonctionnalités de couche 7 que Consul, mais rajoute encore plus de complexité.

Si vous voulez plus des fonctionnalités avancées au niveau de la couche 7 telles que les stratégies de déploiement, Istio est le choix évident.

Sinon, si vous voulez un service mesh simple et facile à mettre en place, Consul est fait pour vous.

À noter que les nouvelles versions à venir de Consul vont intégrer de plus en plus de fonctionnalités couche 7.

Les nouvelles versions d’Istio quant à elles vont permettre de faciliter le déploiement du plan de contrôle en groupant plusieurs composants dans un seul et unique binaire

[1] Documentation Consul: https://www.consul.io/docs/

[2] Documentation Istio: https://istio.io/docs/

[3] Consul Helm chart: https://www.consul.io/docs/platform/k8s/helm.html

[4] Ports Istio: https://istio.io/docs/setup/kubernetes/prepare/requirements/#ports-used-by-istio