Le but de cet exercices est de manipuler Linkerd une solution de ServiceMesh très utilisée.
1. Installation de Linkerd
Lancez les commandes suivantes afin de récupérer les binaires de Linkerd et de les mettre à disposition dans votre PATH:
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install-edge | sh
export PATH=$PATH:$HOME/.linkerd2/bin
Vérifiez ensuite la version installée:
$ linkerd version
Client version: edge-25.1.1
Server version: unavailable
La commande suivante permet de s’assurer que Linkerd peut être installé sur le cluster:
linkerd check --pre
Vous devriez obtenir le résultat ci-dessous qui montre que toutes les vérifications sont ok:
kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API
kubernetes-version
------------------
√ is running the minimum Kubernetes API version
pre-kubernetes-setup
--------------------
√ control plane namespace does not already exist
√ can create non-namespaced resources
√ can create ServiceAccounts
√ can create Services
√ can create Deployments
√ can create CronJobs
√ can create ConfigMaps
√ can create Secrets
√ can read Secrets
√ can read extension-apiserver-authentication configmap
√ no clock skew detected
linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date
Status check results are √
A l’aide des commandes suivante, installez Linkerd dans votre cluster:
# Installation des CRDs
linkerd install --crds | kubectl apply -f -
# Installation des process linkerd
linkerd install | kubectl apply -f -
Assurez-vous ensuite que l’ensemble des composants a été correctement créé:
linkerd check
Si tout s’est déroulé correctement, vous devriez obtenir un résultat similaire à celui ci-dessous:
result of a successful check
kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API
kubernetes-version
------------------
√ is running the minimum Kubernetes API version
linkerd-existence
-----------------
√ 'linkerd-config' config map exists
√ heartbeat ServiceAccount exist
√ control plane replica sets are ready
√ no unschedulable pods
√ control plane pods are ready
√ cluster networks contains all node podCIDRs
√ cluster networks contains all pods
√ cluster networks contains all services
linkerd-config
--------------
√ control plane Namespace exists
√ control plane ClusterRoles exist
√ control plane ClusterRoleBindings exist
√ control plane ServiceAccounts exist
√ control plane CustomResourceDefinitions exist
√ control plane MutatingWebhookConfigurations exist
√ control plane ValidatingWebhookConfigurations exist
√ proxy-init container runs as root user if docker container runtime is used
linkerd-identity
----------------
√ certificate config is valid
√ trust anchors are using supported crypto algorithm
√ trust anchors are within their validity period
√ trust anchors are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust anchor
linkerd-webhooks-and-apisvc-tls
-------------------------------
√ proxy-injector webhook has valid cert
√ proxy-injector cert is valid for at least 60 days
√ sp-validator webhook has valid cert
√ sp-validator cert is valid for at least 60 days
√ policy-validator webhook has valid cert
√ policy-validator cert is valid for at least 60 days
linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date
control-plane-version
---------------------
√ can retrieve the control plane version
√ control plane is up-to-date
√ control plane and cli versions match
linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match
linkerd-extension-checks
------------------------
√ namespace configuration for extensions
Status check results are √
2. Visualisation
Linkerd permet d’installer des extensions afin de réduire l’utilisation des ressources. Utilisez la commande suivante afin d’installer l’extension viz qui mettra en place Prometheus, le dashboard Linkerd ainsi que les composants permettant la gestion des metrics:
linkerd viz install | kubectl apply -f -
Vérifiez une nouvelle fois que tout a bien été installé, notamment dans la section Linkerd extensions checks
$ linkerd check
...
linkerd-extension-checks
------------------------
√ namespace configuration for extensions
linkerd-viz
-----------
√ linkerd-viz Namespace exists
√ can initialize the client
√ linkerd-viz ClusterRoles exist
√ linkerd-viz ClusterRoleBindings exist
√ tap API server has valid cert
√ tap API server cert is valid for at least 60 days
√ tap API service is running
√ linkerd-viz pods are injected
√ viz extension pods are running
√ viz extension proxies are healthy
√ viz extension proxies are up-to-date
√ viz extension proxies and cli versions match
√ prometheus is installed and configured correctly
√ viz extension self-check
Une fois l’extension viz installée, le dashboard Linkerd est accessible via:
linkerd viz dashboard &
Cette interface montre les différents éléments présents dans le cluster. Pour le moment, seuls les composants de Linkerd font parti du Mesh (notés en tant que Meshed Pods)
3. Emojivoto application
Afin d’illustrer les fonctionnalités de base de Linkerd, vous allez installer l’application emojivoto (application microservices présentée dans la documentation officielle de Linkerd) qui permet de voter entre différents Emojis
- Déploiement de l’application
Utilisez la commande suivante pour créer les différentes ressources de l’application Emojis:
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/emojivoto.yml \
| kubectl apply -f -
3.1. Accès à l’application
Afin d’avoir accès à l’application depuis votre machine locale, utilisez la commande port-forward afin d’exposer le service nommé web-svc:
kubectl -n emojivoto port-forward svc/web-svc 8080:80
L’application emojivoto est alors disponible à l’adresse http://localhost:8080. Il est donc possible de voter pour son Emoji favori :)
Remarquez alors que le nouveau namespace emojivoto est maintenant disponible depuis l’interface web de Linkerd. L’application n’étant pas encore gérée par Linkerd, aucun de ces services ne sont encore dans le mesh (colonne Meshed de l’interface).
3.2. Gestion de l’application avec Linkerd
Afin d’ajouter les fonctionnalités de Linkerd à l’application emojivoto, nous allons modifier chaque Pod de l’application de façon à ajouter un nouveau container faisant office de proxy à l’intérieur de chacun d’entre eux. Cette opération se fait facilement au moyen de la commande ci-dessous qui va modifier la spécification de l’ensemble des Deployments du namespace emojivoto:
kubectl get -n emojivoto deploy -o yaml \
| linkerd inject - \
| kubectl apply -f -
Vérifiez, avec la commande suivante, que les proxy Linkerd sont installés dans chaque microservice de l’application.
$ linkerd -n emojivoto check --proxy
...
3.3. Debugging à l’aide des metrics
Depuis l’interface web de Linkerd, il est possible de visualiser différentes metrics. Celles-ci pouvant être utilisées pour mettre en évidence un fonctionnement anormal de l’application et affiner son origine.
Vous pourrez notamment identifier qu’un vote sur l’emoji Doughnut résulte constamment en une erreur.
Linkerd utilise Prometheus pour sauvegarder l’évolution des metrics. Celles-ci peuvent également être visualisées depuis un dashboard Grafana intégré:
3.4. En résumé
En quelques commandes, vous avez donc déployé une application et ajouté celle-ci au Mesh mis en place par Linkerd. Un nouveau container a été ajouté dans chaque Pod de l’application, ce container est un proxy par lequel passent tous les flux entrants et sortants du container applicatif à côté duquel il se trouve. Linkerd permet alors de gérer ce réseau de proxys et notamment d’obtenir des informations sur les flux réseau qui les traversent. Ces informations sont très importantes pour mettre en évidence certains problèmes et aider à les localiser, voire à les résoudre.
A noter que tout ceci a été effectué sans changer le code source de l’application.
3.5. Cleanup
A l’aide de la commande suivante, supprimez l’application et le namespace associé:
$ curl -sL https://run.linkerd.io/emojivoto.yml \
| kubectl -n emojivoto delete -f -
4. Book application
Afin d’illustrer les fonctionnalités plus avancées de Linkerd, vous allez installer l’application bookapp (application microservices présentée dans la documentation officielle de Linkerd) qui sert à gérer un ensemble de livres.
Cette application comporte 3 microservices comme l’illustre le schéma suivant:
Note: un générateur de trafic est également déployé afin d’envoyer régulièrement des requêtes à l’application
4.1. Déploiement de l’application
Utilisez la commande suivante pour créer le namespace bookapp et les différentes ressources de l’application dans ce namespace:
kubectl create ns booksapp
curl -sL https://run.linkerd.io/booksapp.yml | kubectl -n booksapp apply -f -
Vérifiez ensuite que toutes les ressources ont été correctement créées:
kubectl -n booksapp get all
Vous devriez obtenir un résultat similaire à celui ci-dessous:
NAME READY STATUS RESTARTS AGE
pod/traffic-5b5d84f67d-slfwq 1/1 Running 0 53s
pod/webapp-74f795dc7c-xv5c2 0/1 Running 0 54s
pod/webapp-74f795dc7c-j6khl 0/1 Running 0 54s
pod/webapp-74f795dc7c-k4xq4 0/1 Running 0 54s
pod/books-545c88bfbf-tgsd4 0/1 Running 0 53s
pod/authors-59d7484b9c-7d4k8 1/1 Running 0 54s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/webapp ClusterIP 10.43.111.86 <none> 7000/TCP 55s
service/authors ClusterIP 10.43.70.38 <none> 7001/TCP 54s
service/books ClusterIP 10.43.74.202 <none> 7002/TCP 54s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/webapp 0/3 3 0 55s
deployment.apps/books 0/1 1 0 54s
deployment.apps/traffic 1/1 1 1 53s
deployment.apps/authors 1/1 1 1 54s
NAME DESIRED CURRENT READY AGE
replicaset.apps/webapp-74f795dc7c 3 3 0 54s
replicaset.apps/books-545c88bfbf 1 1 0 53s
replicaset.apps/traffic-5b5d84f67d 1 1 1 53s
replicaset.apps/authors-59d7484b9c 1 1 1 54s
4.2. Accès à l’application
Afin d’avoir accès à l’application depuis votre machine locale, utilisez la commande port-forward afin d’exposer le service nommé webapp:
kubectl -n booksapp port-forward svc/webapp 7000
L’application est alors disponible à l’adresse http://localhost:7000.
Si vous essayez d’ajouter un livre, vous obtiendrez cependant régulièrement une erreur:
Nous verrons dans une des prochaines sections comment identifier ce problème plus précisément.
Depuis l’interface web de Linkerd, on peut également voir que le namespace bookapp a été détecté, mais pour le moment aucun des services n’est présent dans le mesh
4.3. Gestion de l’application avec Linkerd
Afin d’ajouter les fonctionnalités de Linkerd à l’application nous allons modifier chaque Pod de l’application de façon à ajouter un nouveau container faisant office de proxy à l’intérieur de chacun d’entre eux, comme nous l’avions fait pour l’application emojivoto. Utilisez pour cela la commande suivante:
$ kubectl get -n booksapp deploy -o yaml \
| linkerd inject - \
| kubectl apply -f -
deployment "traffic" injected
deployment "authors" injected
deployment "webapp" injected
deployment "books" injected
deployment.apps/traffic configured
deployment.apps/authors configured
deployment.apps/webapp configured
deployment.apps/books configured
Les proxy Linkerd sont maintenant installés dans chaque microservice de l’application. Vous pouvez le vérifier avec la commande suivante:
linkerd -n booksapp check --proxy
Nous pouvons alors voir que les services de l’application sont maintenant présent dans le Mesh:
4.4. Debugging à l’aide de live metrics
Depuis l’interface web de Linkerd, listez les deployments qui sont dans le namespace bookapp:
Vous pourrez notamment voir l’évolution des “Success Rates” pour chaque requête envoyée à l’application par le générateur de trafic.
Sélectionnez le deployment webapp afin d’obtenir davantage d’informations.
Nous pouvons voir que les requêtes POST sur le endpoint /books.json du deployment book posent problème. Il s’agit de la requête de création d’un livre pour laquelle nous avions observé une erreur précédemment.
En allant un peu plus loin, vous pourrez voir que cette requête renvoie souvent des erreurs 500.
Dans la suite, nous allons voir comment en savoir plus sur cette erreur.
4.5. Debugging à l’aide de ServiceProfile
Linkerd utilise des ressources de type ServiceProfile afin d’obtenir des informations supplémentaires sur les services et notamment des metrics, stockées dans Prometheus, pour chaque endpoint. Un ServiceProfile peut être créé manuellement ou bien en utilisant une spécification existante au format OpenAPI (Swagger).
Il existe pour chacun des microservices de l’application bookapp une spécification OpenAPI. La commande ci-dessous:
- récupère cette spécification pour le microservice webapp
- opère quelques manipulations textuelles sur celle-ci afin de créer une spécification de type ServiceProfile
- créé cette ressource dans le cluster
curl -sL https://run.linkerd.io/booksapp/webapp.swagger \
| linkerd -n booksapp profile --open-api - webapp \
| kubectl -n booksapp apply -f -
Après avoir lancé cette commande, vérifiez le contenu du ServiceProfile ainsi créé:
$ kubectl get serviceprofile webapp.booksapp.svc.cluster.local -n booksapp -o yaml
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
...
spec:
routes:
- condition:
method: GET
pathRegex: /
name: GET /
- condition:
method: POST
pathRegex: /authors
name: POST /authors
- condition:
method: GET
pathRegex: /authors/[^/]*
name: GET /authors/{id}
- condition:
method: POST
pathRegex: /authors/[^/]*/delete
name: POST /authors/{id}/delete
- condition:
method: POST
pathRegex: /authors/[^/]*/edit
name: POST /authors/{id}/edit
- condition:
method: POST
pathRegex: /books
name: POST /books
- condition:
method: GET
pathRegex: /books/[^/]*
name: GET /books/{id}
- condition:
method: POST
pathRegex: /books/[^/]*/delete
name: POST /books/{id}/delete
- condition:
method: POST
pathRegex: /books/[^/]*/edit
name: POST /books/{id}/edit
Le nom de ce ServiceProfile correspond au FQDN du service. A chaque fois qu’un proxy recevra une requête contenant ce FQDN dans le header Host, il utilisera la configuration spécifiée dans le ServiceProfile correspondant.
Créez également les ressources de type ServiceProfile pour les microservices authors et books:
$ curl -sL https://run.linkerd.io/booksapp/authors.swagger \
| linkerd -n booksapp profile --open-api - authors \
| kubectl -n booksapp apply -f -
serviceprofile.linkerd.io/authors.booksapp.svc.cluster.local created
$ curl -sL https://run.linkerd.io/booksapp/books.swagger \
| linkerd -n booksapp profile --open-api - books \
| kubectl -n booksapp apply -f -
serviceprofile.linkerd.io/books.booksapp.svc.cluster.local created
La commande suivante peut être utilisée pour visualiser les requêtes en temps réel (cette commande ne vous rendra pas la main)
linkerd viz tap -n booksapp deploy/webapp
Afin de voir les metrics accumulées, utilisez la commande suivante:
linkerd viz -n booksapp routes svc/webapp
Vous devriez obtenir un résultat proche de celui ci-dessous:
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
GET / webapp 100.00% 0.7rps 25ms 45ms 49ms
GET /authors/{id} webapp 100.00% 0.6rps 16ms 26ms 29ms
GET /books/{id} webapp 100.00% 1.3rps 16ms 27ms 29ms
POST /authors webapp 100.00% 0.6rps 15ms 20ms 20ms
POST /authors/{id}/delete webapp 100.00% 0.6rps 30ms 39ms 40ms
POST /authors/{id}/edit webapp - - - - -
POST /books webapp 54.61% 2.4rps 21ms 33ms 39ms
POST /books/{id}/delete webapp 100.00% 0.6rps 15ms 20ms 20ms
POST /books/{id}/edit webapp 52.63% 1.3rps 25ms 33ms 39ms
[DEFAULT] webapp - - - - -
Ces metrics donnent des informations très utiles pour aider à l’identification de la source des erreurs.
Note: il est possible de filtrer le résultat précédent en spécifiant les requêtes à destination du service books seulement. La requête de création de livres, sur laquelle une erreur a été observée précédemment, fait notamment partie de cette catégorie.
$ linkerd viz -n booksapp routes deploy/webapp --to svc/books
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.6rps 9ms 19ms 20ms
GET /books.json books 100.00% 1.3rps 7ms 17ms 19ms
GET /books/{id}.json books 100.00% 2.2rps 4ms 9ms 10ms
POST /books.json books 53.79% 2.4rps 14ms 25ms 29ms
PUT /books/{id}.json books 66.67% 1.0rps 17ms 91ms 98ms
[DEFAULT] books - - - - -
Nous pouvons donc observer que de nombreuses erreurs surviennent lors de la création mais également de la mise à jour d’un livre.
4.6. Retries
Si l’erreur ne peut pas être corrigée tout de suite, il peut être intéressant de relancer la requête automatiquement lorsque celle-ci échoue. Ce comportement peut facilement être mis en place en utilisant une clé retry dans la spécification de la ressource ServiceProfile.
La commande suivante donne des metrics relatives aux requêtes provenant du deployment books et à destination de authors:
$ linkerd viz -n booksapp routes deploy/books --to svc/authors
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /authors/{id}.json authors - - - - -
GET /authors.json authors - - - - -
GET /authors/{id}.json authors - - - - -
HEAD /authors/{id}.json authors 53.55% 3.5rps 3ms 5ms 8ms
POST /authors.json authors - - - - -
[DEFAULT] authors - - - - -
On voit ici que la requête HEAD sur le endpoint /authors/{id}.json a un taux de succès légèrement supérieur à 50%:
Modifiez le ServiceProfile nommé authors.booksapp.svc.cluster.local de façon à ce que la clé isRetryable: true soit ajoutée sur la route /authors/{id}.json utilisant la methode HEAD.
Vous pouvez utiliser pour cela la sous-commande edit de kubectl:
kubectl -n booksapp edit sp/authors.booksapp.svc.cluster.local
puis modifier la condition nommée HEAD /authors/{id}.json de la façon suivante:
spec:
routes:
...
- condition:
method: HEAD
pathRegex: /authors/[^/]*\.json
isRetryable: true # Ajout de cette ligne
name: HEAD /authors/{id}.json
Vous devriez alors observer que le taux de succès remonte graduellement jusqu’à atteindre 100%:
$ linkerd viz -n booksapp routes deploy/books --to svc/authors
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /authors/{id}.json authors - - - - -
GET /authors.json authors - - - - -
GET /authors/{id}.json authors - - - - -
HEAD /authors/{id}.json authors 100.00% 2.3rps 4ms 16ms 19ms
POST /authors.json authors - - - - -
[DEFAULT] authors - - - - -
4.7. Timeout
Il est également possible de configurer un timeout de façon à limiter le traitement d’une requête qui prendrait trop de temps. Ce comportement peut facilement être mis en place en utilisant la clé timeout dans la spécification de la ressource ServiceProfile.
En utilisant la commande suivante, listez les requêtes à destinations du service books et notez que la latence est relativement élevée lorsqu’une requête de type PUT est envoyée sur le endpoint /books/{id}.json:
$ linkerd viz -n booksapp routes deploy/webapp --to svc/books
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.8rps 8ms 36ms 39ms
GET /books.json books 100.00% 1.6rps 7ms 17ms 19ms
GET /books/{id}.json books 100.00% 2.4rps 4ms 9ms 10ms
POST /books.json books 100.00% 1.6rps 19ms 55ms 91ms
PUT /books/{id}.json books 100.00% 0.8rps 18ms *85ms* 97ms
[DEFAULT] books - - - - -
Editez le ServiceProfile sp/books.booksapp.svc.cluster.local
kubectl -n booksapp edit sp/books.booksapp.svc.cluster.local
et modifiez la condition nommée PUT /books/{id}.json de la façon suivante:
spec:
routes:
...
- condition:
method: PUT
pathRegex: /books/[^/]*\.json
name: PUT /books/{id}.json
timeout: 25ms
Après quelques dizaines de secondes, vous devriez observer que la latence des requêtes en question a diminué, au détriment du taux de succès qui a baissé légèrement:
$ linkerd -n booksapp routes deploy/webapp --to svc/books
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.8rps 9ms 18ms 20ms
GET /books.json books 100.00% 1.5rps 7ms 17ms 19ms
GET /books/{id}.json books 100.00% 2.5rps 5ms 9ms 10ms
POST /books.json books 100.00% 1.6rps 17ms 47ms 49ms
PUT /books/{id}.json books 90.57% 0.9rps 22ms 91ms 98ms
[DEFAULT] books - - - - -
4.8. En résumé
Dans cette partie, vous avez utilisé une ressource de type ServiceProfile afin de contrôler les flux entre les différents services d’une application. Vous avez notamment vu comment obtenir des metrics concernant les requêtes reçues et utilisé des Retries ou Timeout pour contrôler plus finement ces flux.
A noter, une nouvelle fois, que tout ceci a été effectué sans changer le code source de l’application.
4.9. Cleanup
Supprimez l’application puis le namespace associé en utilisant la commande suivante:
curl -sL https://run.linkerd.io/booksapp.yml \
| kubectl -n booksapp delete -f - \
&& kubectl delete ns booksapp