By Pascal LIBENZI.
LinkerD est historiquement le premier service Mesh à avoir été créé en 2016. La société Buoyant l’a ensuite cédé à la CNCF en 2017. Il se veut plus simple d’utilisation que d’autres services Mesh, sans pour autant perdre de fonctionnalité primordiale.
Notons par ailleurs qu’il a été réécrit en Rust à partir de la version 2.0 afin d’être plus léger et plus performant que la première version. Son fonctionnement est basé sur l’injection d’un sidecar proxy développé par LinkerD, contrairement à d’autres services Mesh qui utilisent un proxy déjà existant comme Envoy par exemple.
Cet article s’inscrit dans notre série sur les grands acteurs du marché des services Mesh. Nous suivrons donc la même trame pour cette série afin de vous aider à facilement comparer les avantages d’un service Mesh à l’autre.
L’installation peut se faire en utilisant:
linkerd
Dans cette documentation de l’installation, on peut voir différents encadrés importants selon la distribution utilisée.
Ainsi des étapes supplémentaires sont nécessaires et une documentation spécifique est disponible :
Nous resterons dans un cas simple pour cet article, en utilisant un simple cluster KinD :
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: linkerd
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
EOF
Vous noterez que nous utilisons la configuration qui nous permettra par la suite d’installer un ingress controller facilement en suivant la documentation KinD, ceci nous sert plus loin dans la découverte des fonctionnalités de LinkerD.
Une fois notre cluster créé, nous pouvons nous atteler à l’installation du control plane de notre service Mesh.
Pour ceci nous utiliserons le client, et donc nous suivons la documentation Get Started qui explique le cas facile d’installation :
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
export PATH=$PATH:$HOME/.linkerd2/bin
linkerd version
Commençons par vérifier que notre cluster est bien compatible:
linkerd check --pre
Puis nous passons à l’installation du service Mesh. Il faut d’abord installer les crds puis le controlplane :
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
Un namespace linkerd
a été créé spécifiquement pour le control-plane, et nous pouvons voir 3 pods dans ce nouveau namespace:
Nous pouvons vérifier notre installation grâce au client:
linkerd check
L’installation prend une poignée de minutes pour installer LinkerD sur notre cluster.
Il va de soi que selon nos besoins, et surtout si nous ne sommes plus en phase de POC, il nous faudra à minima changer de profil, ou encore choisir une méthode d’installation plus appropriée à notre besoin, selon les personnalisations que nous voulons apporter à notre déploiement de LinkerD.
Nous verrons par la suite les différentes extensions intéressantes pour notre service Mesh, et les différents dashboarding.
Nous allons en revanche d’ores et déjà installer l’extension responsable du dashboarding, car celle-ci nous permet aussi de vérifier la bonne mise en place de certaines fonctionnalités :
linkerd viz install | kubectl apply -f -
Ceci crée un namespace dédié avec les différents composants utiles à l’extension viz
.
La désinstallation se fait très simplement en utilisant le cli une fois encore :
linkerd uninstall | kubectl delete -f -
Notons que les crds sont supprimées directement avec cette commande.
Une application type est proposée par LinkerD, et c’est l’application de référence qui est utilisée dans la documentation pour diverses illustrations des fonctionnalités offerte par LinkerD : emojivoto
. Il s’agit d’une application très simple qui permet de voter pour son emoji préféré et de consulter les votes qui ont été effectués.
Nous regrettons en revanche que ce ne soit pas la même application qui soit utilisée à travers toute la documentation, car cela nous force à la base à installer d’autres applications pour tester différentes fonctionnalités, et donc la notion de fil rouge est un peu perdue.
Sans plus attendre installons cette application sur notre cluster :
curl -sSfL https://run.linkerd.io/emojivoto.yml | kubectl apply -f -
Si nous observons nos pods dans le namespace emojivoto
nous pouvons constater que le conteneur sidecar n’est pas présent (un conteneur unique par pod, ce qui indique que les proxies n’ont pas été injectés) :
Si on exécute la commande check
pour vérifier l’état de l’injection dans le data-plane, nous constatons qu’effectivement il nous manque quelque chose :
linkerd -n emojivoto check --proxy --wait 10s
Nous conseillons l’utilisation du –wait avec une valeur assez petite, sans quoi la valeur par défaut est à 5 minutes, ce qui vous fait attendre longtemps pour obtenir un résultat en cas d’échec.
Vous voyez alors un lien dans la sortie de la commande qui vous indique la documentation avec la démarche à suivre (linkerd inject
).
Nous devons appliquer l’injection linkerd à nos différents déploiements :
kubectl get -n emojivoto deploy -o yaml
| linkerd inject -
| kubectl apply -f -
En utilisant cette commande, nous n’avons pas besoin de redémarrer les pods, ceci est fait automatiquement puisque ce sont les déploiements qui sont modifiés :
ou simplement à notre namespace:
kubectl get ns emojivoto -o yaml
| linkerd inject -
| kubectl apply -f -
En utilisant l’injection sur le namespace , nous devons redémarrer les pods, car aucun déploiement n’est modifié au sens Kubernetes; une fois nos pods redémarrés, nous voyons bien que les sidecar containers sont présents :
Nous pouvons aussi simplement faire la même chose que ce que fait le client en réalité, à savoir pousser une annotation linkerd.io/inject: enabled
, soit sur les déploiements, soit sur le namespace. Idéalement il faut donc pousser cette annotation dés la création du namespace pour s’assurer que tout pod créé dans le namespace contient le sidecar proxy, et c’est bien sur la préconisation de SoKube, tout spécialement pour aller vers des déploiements GitOps.
La commande de vérification passée précédemment linkerd -n emojivoto check --proxy --wait 10s
nous confirmera alors que la configuration est correcte.
La documentation est complète et présente les rubriques suivantes :
Chacune de ces rubriques comporte énormément de sections; nous trouvons par contre un peu confus le fait de ne pas avoir regroupé les différentes sections dans des sous-rubriques (comme "Réseau" ou encore "Routage").
Le chiffrage mTLS entre les services s’active automatiquement via les sidecar proxies, et vous pouvez le vérifier avec l’extension viz sur emojivoto :
linkerd viz -n emojivoto edges deployment
Notons que c’est le service Identity
qui est chargé de produire les certificats SSL lorsqu’un proxy vient lui demander (cf Architecture). Il est possible de lui fournir l’autorité de certification racine en suivant cette documentation.
Dans les dernières versions, rendre le mTLS facultatif ou s’en soustraire lorsque le déploiement est meshé n’est pas possible. Ceci est un point qui peut parfois devenir pénible, et on relèvera donc ce manque de souplesse comme un point non pas critique mais légèrement négatif lors de l’adoption de ce service Mesh.
En utilisant l’extension viz nous pouvons accéder au dashboard LinkerD :
linkerd viz dashboard
Nous arrivons alors sur le dashboard par namespace, ce qui permet en un coup d’oeil de voir via les différentes métriques (http et tcp) l’état de santé global de nos applications.
Si nous cliquons sur un namespace où le mesh est actif (par exemple emojivoto
), alors nous rentrons dans le détail de celui-ci, et pouvons analyser plus finement les erreurs qui peuvent survenir au niveau :
Nous pouvons alors suivre les différents liens pour avoir plus de détails sur chaque objet, par exemple sur le déploiement web
qui n’est pas à 100% de succès.
Nous retrouvons alors une visualisation graphique de qui appelle ce déploiement et de ce que lui-même appelle :
Dans cet écran, on peut voir le taux de succès (SR = Success Rate), le nombre de requête par seconde (RPS = Request Per Second), et le 99e percentile des temps de latence (P99).
Nous retrouvons aussi le trafic en direct, avec le décompte et le sens (entrant ou sortant) :
Enfin, nous voyons dans le détail le taux de succès des appels entrants, sortants, ou encore la sécurisation de la communication, tout ceci en un clin d’oeil.
Notons qu’on peut aussi voir les routes utilisées pour appeler notre déploiement (dans notre cas on verra le service web-svc
qui est utilisé par les autres déploiements pour appeler le frontend); ainsi que les "routes" que notre déploiement appelle.
Nous pouvons changer de namespace directement dans le volet à gauche. Nous pouvons aussi regarder les différents objets du namespace dans ce même volet.
LinkerD vous propose un tooling très simple d’accès, pour une analyse en temps réel du trafic :
Sur ces trois outils, lorsque graphiquement vous sélectionnez la cible de votre analyse, vous avez en dessous la commande correspondante que vous pouvez exécuter dans un terminal, ce qui est assez appréciable pour se familiariser avec la ligne de commande du cli, qui sera peut-être votre moyen principal pour debugguer dans des environnements de production.
LinkerD ne propose pas d’ingress controller ou d’ingress gateway spécifique, préférant se reposer sur l’existant et supporter un grand nombre d’ingress controllers.
Le service Mesh considèrera les pods de notre ingress controller comme toute autre ressource du service Mesh, et vous pouvez donc mesher votre ingress controller sans difficulté.
Ici dans notre cluster KinD, nous allons tester avec un ingress controller classique: nginx.
Installation de l’ingress-controller et attente qu’il soit prêt :
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl wait --namespace ingress-nginx
--for=condition=ready pod
--selector=app.kubernetes.io/component=controller
--timeout=90s
Création d’une ingress, nous suivons la documentation fournie par LinkerD :
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: emojivoto-web-ingress
namespace: emojivoto
annotations:
nginx.ingress.kubernetes.io/service-upstream: "true"
spec:
ingressClassName: nginx
defaultBackend:
service:
name: web-svc
port:
number: 80
EOF
Nous pouvons alors atteindre notre application de vote sur https://localhost, mais en regardant dans tap nous n’identifions pas les requêtes provenant de l’extérieur.
Meshons notre ingress-controller :
kubectl get -n ingress-nginx deploy -o yaml
| linkerd inject -
| kubectl apply -f -
Nous allons ensuite voir dans notre dashboard les appels à l’ingress-controller, et remarquons une ip "172.18.0.1" dans notre exemple :
Cette IP correspond à notre IP dans le réseau.
Ce qui est vraiment intéressant dans le fait de mesher notre ingress controller est de forcer le mTLS et vérifier qu’il est bien activé entre notre ingress controller et nos déploiements, car ceci nous garantie que la communication est chiffrée dans notre cluster :
linkerd viz tap -n ingress-nginx deploy/ingress-nginx-controller | grep 10.244.0.33 | grep 10.244.0.27
req id=3:1 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true :method=GET :authority=localhost :path=/
rsp id=3:1 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true :status=200 latency=7461µs
end id=3:1 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true duration=21µs response-length=560B
req id=3:5 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true :method=GET :authority=localhost :path=/js
rsp id=3:5 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true :status=200 latency=62679µs
end id=3:5 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true duration=104667µs response-length=1782140B
req id=3:7 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true :method=GET :authority=localhost :path=/api/list
rsp id=3:7 proxy=out src=10.244.0.33:45568 dst=10.244.0.27:8080 tls=true :status=200 latency=41450µs
Finalement, nous concluons donc que même si le choix de LinkerD est de ne pas implémenter spécifiquement d’ingress gateway, ceci est fait pour nous permettre la plus grande souplesse quant au choix de notre ingress controller sans être intrusif. Ce point peut être discutable (et doit être discuté avant de choisir votre service Mesh) car même si les ingress controllers sont effectivement supportés, on regrettera le manque de fonctionnalités natives par rapport à d’autres services Mesh qui proposent une ingress gateway.
Effectivement si la plupart des fonctionnalités d’une ingress gateway peuvent être implémentées en utilisant différentes règles une fois l’ingress gateway meshée, on ne peut pas forcer facilement les comportements (imaginons qu’une règle de traffic entrant ne soit pas en place pour limiter par exemple les endpoints qui peuvent être atteints, alors on peut tous les atteindre, et il est moins simple de voir le comportement à l’entrée de notre cluster, puisqu’il faut analyser les règles en place pour avoir la vue complète de ce qui se passe).
Si on prend l’exemple d’Istio l’ingress gateway nous permet notamment d’atteindre des service virtuels qui permettent d’utiliser directement des fonctionnalités de split de trafic par exemple. L’analyse des règles de routage reste plus simple car nous pouvons directement observer les services virtuels là où avec LinkerD nous devrons en revanche analyser les différentes règles autour des différentes routes utilisées, de manière décentralisée.
Notons par ailleurs que la roadmap mentionne la prise en charge d’une fonctionnalité d’ingress gateway en se basant sur la fonctionnalité existante dans le cas du multi-cluster, ce qui pourrait servir de base pour une ingress gateway dans un mono-cluster.
LinkerD fournit bien entendu des fonctionnalités de sécurité et de routage.
Pour les fonctionnalités de routage, il est maintenant conseillé d’utiliser l’objet HTTPRoute
y compris pour le découpage du trafic.
Pour les fonctionnalités de sécurité réseau il s’agit des objets :
AuthorizationPolicy
ServerAuthorization
MeshTLSAuthentication
NetworkAuthentication
qui vous permettent de mettre en place des polices de sécurité au niveau des accès à vos services ou à vos routes.
Prenons un exemple simple, et créons une politique d’autorisation afin d’interdire tout accès direct à notre service web-svc depuis un pod du cluster.
Nous allons créer une politique d’autorisation qui n’autorise que le compte de service de notre ingress controller à accéder à notre service.
Nous adapterons la documentation afin de rester avec le cas d’utilisation emojivoto
.
Auparavant testons que nous pouvons appeler la méthode "GET" directement sur notre service sans problème :
kubectl run mycurlpod --image=curlimages/curl -- sh -c "curl -v web-svc.emojivoto.svc.cluster.local && sleep infinity"
Nous obtenons bien une réponse si nous regardons les logs de notre pod :
kubectl logs -f mycurlpod
Revenons à notre sujet, les autorisations. Nous pouvons lister grâce au cli la liste des autorisations en place dans notre namespace :
linkerd viz authz -n emojivoto deploy/web
Nous voyons que des polices existent d’ores et déjà (ce sont celles créées par défaut par le service Mesh) :
Il est intéressant de noter sur les resources Server
portent ces polices : default:all-unauthenticated
Nous allons donc créer un nouvel objet Serveur sur lequel nous allons appliquer notre nouvelle police.
kubectl apply -f - <<EOF
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
name: web-server
namespace: emojivoto
spec:
podSelector:
matchLabels:
app: web-svc
port: http
EOF
Nous créons ensuite une police de type ServerAuthorization
associée:
kubectl apply -f - <<EOF
---
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: web-server
labels:
app.kubernetes.io/part-of: emojivoto
app.kubernetes.io/name: web
spec:
server:
name: web-server
client:
meshTLS:
serviceAccounts:
- name: ingress-nginx
EOF
Nous devrions maintenant recevoir une erreur si nous appelons depuis notre pod directement car le service ne peut être appelé que depuis notre ingress-controller, i.e. tout appel interne est proscrit, sauf celui de l’ingress-controller (ou du moins les appels provenant de son compte de service).
Nous pouvons d’ailleurs le vérifier en exécutant la commande suivante:
kubectl exec mycurlpod -- sh -c "curl -v web-svc.emojivoto.svc.cluster.local"
Nous avons bien une erreur 403 car nous ne sommes pas autorisés à accéder en direct.
Pour autant si nous nous rendons sur https://localhost cela fonctionne puisque nous passons bien par l’ingress controller.
LinkerD propose nativement les mécanismes suivants :
Server
de définir quels clients peuvent y accéder Notons que pour l’instant il n’est pas possible nativement de faire de l’authentification JWT par exemple (cependant on peut trouver des issues où la question se pose, et cela sera sûrement implémenté dans le futur).
Toujours dans un souci de concision de nos articles de blogs, nous n’irons pas détailler l’implémentation de chaque mode de déploiement, mais vous pouvez bien entendu consulter la documentation pour cela.
Les mode de déploiment proposés de manière relativement simples avec LinkerD sont les suivants (cf documentation sur le trafic Shifting):
Deux moyens sont proposés pour arriver à nos fins:
Notons que certains type de déploiement, comme le déploiement canari, basé sur Flagger, nécessitent l’utilisation de l’adaptateur SMI.
Comme expliqué en préambule, LinkerD est le pionnier des services Mesh, mais le souhait avoué est de réussir à rester simple en termes d’installation et de configuration. Il propose parfois moins de fonctionnalités que ses concurrents, mais sa maturité n’est plus à prouver.
Le projet dispose d’une grande communauté Open-Source puisqu’il a été confié à la CNCF un an après sa création. Les moyens de s’adresser à la communauté sont nombreux (Slack, Forum, Twitter) et cette communauté semble ouverte à la proposition de nouvelles fonctionnalités.
A l’heure actuelle ce service Mesh ne propose pas une architecture sans sidecar container, ce que nous regrettons un peu avec ce service Mesh, puisque d’autres le proposent, nous permettant ainsi d’alléger les ressources consommées par le service Mesh comme Istio. Ceci ne semble pas encore prévu dans la roadmap de LinkerD
Un des autres avantages lié à une architecture sans sidecar est de ne pas devoir redémarrer tous les pods lors d’une modification qui impacte tout le cluster. Si le modèle avec sidecar permet de ne pas être trop "brutal" au niveau de l’application des configurations et donc de faire un rollout plus progressif lors de la mise en place d’une fonctionnalité impactant tous les pods, lorsque nous avons des centaines ou des milliers de pods dans un cluster cet aspect devient vite pénalisant.
Comme cité plusieurs fois dans cet article, ce service Mesh se veut plus simple d’installation et d’utilisation que les autres services Mesh du marché. Il demeure donc assez simple à opérer, car le nombre d’implémentations pour arriver à un même résultat reste assez réduit, afin de faciliter l’homogénéité entre les utilisations que l’on pourrait avoir avec différentes équipes au sein d’un même cluster.
Le dashboarding reste assez simple et vous permet de voir en un clin d’oeil les services qui peuvent être en peine. Vous pouvez aussi voir facilement la latence pour les appels sur un service, ou encore les routes qui peuvent être engorgées.
Ceci étant dit, même si la simplicité de LinkerD rend l’adoption de ce service Mesh moins compliqué, n’hésitez pas lors de sa mise en place à former les équipes opérationnelles sur son utilisation, car ceci reste toujours une technologie de plus à adopter pour ces équipes.
Le client linkerd
proposé peut vous aider à trouver facilement les configurations appliquées sur votre cluster, ainsi qu’à diagnostiquer les problèmes lorsque quelques chose ne va pas. Il permet aussi d’injecter les sidecar containers comme nous l’avons vu précédemment, ou encore de lancer le dashboard rapidement.
linkerd -- help
vous donnera la liste des commandes proposées, dont certaines sont très utiles en cas de souci, notamment la commande diagnostics
.
Nous ne détaillons pas chaque commande dans cet article, car elles sont suffisamment explicites et documentées pour vous guider dans leur utilisation.
Si vous rencontrez des soucis lors de la mise en place du service Mesh, et que vous pensez que la configuration de LinkerD en elle-même en est la cause, il faut alors observer les journaux du service Mesh lui-même.
L’outil stern peut être bien utile pour pouvoir lire tous les logs d’un namespace, au moment où le problème survient, dans notre cas sur le namespace système de LinkerD.
Pour logguer tous nos pods du service Mesh, nous utiliserons la commande suivante :
stern -n linkerd .*
Vous pouvez aussi changer le niveau des journaux du sidecar-proxy lorsque vous souhaitez voir plus d’informations sur ce qui se passe autour de votre conteneur principal.
Tout d’abord vous pouvez utiliser bien entendu les fonctionnalités natives, et donc accéder au dashboard LinkerD. Vous pouvez aussi si vous avez déjà une stack d’observabilité telle que Prometheus/Grafana y inclure directement les métriques du service Mesh, que cette instance grafana soit dans ou en dehors de votre cluster (cf documentation).
Notons aussi qu’une extension Jaeger est disponible, vous permettant d’analyser avec cet outil de tracing de manière plus commune vos temps de réponse par exemple.
Cet aspect sera abordé dans un article spécifique dédié à l’implémentation d’un service Mesh multi-cluster. Le support de l’implémentation multi-clusters est bien présent dans LinkerD au travers de l’extension multi-cluster.
Si LinkerD propose moins de fonctionnalités que certains autres services Mesh, il demeure le pionnier du concept, et se veut le plus simple d’utilisation. Tout comme pour nos précédents articles, nous ne sommes pas rentrés en détails sur certaines fonctionnalités (injection d’erreurs, retries, …) dans un souci de concision. Cependant la documentation est assez claire et vous pouvez approfondir le sujet assez facilement en suivant les exemples proposés dans celle-ci.
Notons que même si LinkerD, volontairement, ne propose pas un très grand nombre de fonctionnalités, il a tout de même su évoluer pour proposer des implémentations plus légères et plus intuitives au travers du temps.
Si vous souhaitez mettre en place LinkerD au sein de votre plateforme, et que vous avez besoin de support, n’hésitez pas à contacter SoKube, nous nous ferons un plaisir de vous accompagner sur ce voyage vers l’implémentation d’un service Mesh.