
Maillage de service Isstio
Chez Namely, nous utilisons Istio depuis un an maintenant. Il vient alors de partir. Nous avons eu une grande baisse de performances dans le cluster Kubernetes, nous voulions un suivi distribué et avons pris Istio pour exécuter Jaeger et le découvrir. Le maillage de service s'intègre si bien dans notre infrastructure que nous avons décidé d'investir dans cet outil.
J'ai dû souffrir, mais nous l'avons étudié de très près. Ceci est le premier article d'une série où je décrirai comment Istio s'intègre à Kubernetes et ce que nous avons appris sur son travail. Parfois, nous nous promènerons dans les déserts techniques, mais pas très loin. De plus, il y aura plus de messages.
Qu'est-ce que Istio?
Istio est un outil de configuration de maillage de service. Il lit l'état du cluster Kubernetes et met à niveau les proxys L7 (HTTP et gRPC), qui sont implémentés en tant que sidecar dans les pods Kubernetes. Ces sidecar sont des conteneurs Envoy qui lisent la configuration à partir de l'API Istio Pilot (et du service gRPC) et acheminent le trafic à travers celle-ci. Avec le puissant proxy L7 sous le capot, nous pouvons utiliser des métriques, des traces, une logique de nouvelle tentative, un disjoncteur, un équilibrage de charge et des déploiements canaries.
Commençons par le début: Kubernetes
Dans Kubernetes, nous créons sous en utilisant un déploiement ou un StatefulSet. Ou il peut simplement être «vanille» sous sans contrôleur de haut niveau. Ensuite, Kubernetes fait de son mieux pour maintenir l'état souhaité - il crée des pods dans le cluster sur le nœud, s'assure qu'ils démarrent et redémarrent. Lorsqu'un sous est créé, Kubernetes parcourt le cycle de vie de l'API, s'assure que chaque étape réussit, puis crée finalement le sous sur le cluster.
Étapes du cycle de vie de l'API:

Merci Ă Banzai Cloud pour la photo sympa.
L'une des étapes consiste à modifier les webhooks d'admission. Il s'agit d'une partie distincte du cycle de vie de Kubernetes, où les ressources sont personnalisées avant de s'engager dans le référentiel etcd, la source de vérité pour la configuration de Kubernetes. Et ici, Istio fait sa magie.
Modification des webhooks d'admission
Lorsqu'un sous est créé (via kubectl
ou Deployment
), il passe par ce cycle de vie, et les webhooks modificateurs d'accès le changent avant de le diffuser dans le grand monde.
Lors de l'installation d'Istio, l'istio-sidecar-injector est ajouté comme ressource de configuration pour modifier les webhooks:
$ kubectl get mutatingwebhookconfiguration NAME AGE istio-sidecar-injector 87d
Et la configuration:
apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingWebhookConfiguration metadata: labels: app: istio-sidecar-injector chart: sidecarInjectorWebhook-1.0.4 heritage: Tiller name: istio-sidecar-injector webhooks: - clientConfig: caBundle: redacted service: name: istio-sidecar-injector namespace: istio-system path: /inject failurePolicy: Fail name: sidecar-injector.istio.io namespaceSelector: matchLabels: istio-injection: enabled rules: - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods
Il est indiqué ici que Kubernetes doit envoyer tous les événements de création de foyer au service istio-sidecar-injector
istio-system
dans l'espace de noms istio-system
si l'espace de noms a istio-injection=enabled
. L'injecteur comprend deux conteneurs supplémentaires dans PodSpec: un temporaire pour définir des règles de proxy et un pour se procurer lui-même. L'injecteur side-car insère ces conteneurs selon le modèle de la carte de configuration istio-sidecar-injector
. Ce processus est également appelé side-caring.
Dosettes Sidecar
Les side-cars sont les astuces de notre magicien Istio. Istio exécute tout si habilement que de l'extérieur, c'est juste magique, si vous ne connaissez pas les détails. Et il est utile de les connaître si vous devez soudainement déboguer des requêtes réseau.
Conteneurs init et proxy
Kubernetes dispose de conteneurs init initiaux temporaires qui peuvent être exécutés avant les principaux. Ils mettent en commun des ressources, migrent des bases de données ou, comme c'est le cas avec Istio, configurent des règles de réseau.
Istio utilise Envoy pour proxy toutes les demandes de soumissions le long des itinéraires souhaités. Pour ce faire, Istio crée des règles iptables
, et ils envoient le trafic entrant et sortant directement à Envoy, et il projette soigneusement le trafic vers sa destination. Le trafic fait un petit détour, mais vous avez distribué le traçage, les métriques de requête et l'application des règles. Dans ce fichier, à partir du référentiel Istio, vous pouvez voir comment Istio crée des règles iptables.
@jimmysongio a dessiné un excellent diagramme de connexion entre les règles iptables et le proxy Envoy:

Envoy - Envoy Traffic
Envoy reçoit tout le trafic entrant et sortant, donc tout le trafic se déplace généralement à l'intérieur d'Envoy, comme dans le diagramme. Le proxy Istio est un autre conteneur qui est ajouté à tous les pods qui sont modifiés par l'injecteur side-car Istio. Dans ce conteneur, le processus Envoy démarre, qui reçoit tout le trafic du foyer (à quelques exceptions près, comme le trafic de votre cluster Kubernetes).
Le processus Envoy découvre toutes les routes via l'API Envoy v2, qui implémente Istio.
Envoyé et pilote
Envoy lui-même n'a aucune logique pour détecter les pods et les services dans un cluster. Il s'agit d'un plan de données et il a besoin d'un plan de contrôle pour guider. Le paramètre de configuration Envoy demande à l'hôte ou au port de service de recevoir cette configuration via l'API gRPC. Istio, à travers son service Pilot, répond aux exigences de l'API gRPC. Envoy se connecte à cette API sur la base d'une configuration de side-car implémentée via un webhook de modification. L'API possède toutes les règles de trafic qu'Envoy doit découvrir et acheminer pour le cluster. Il s'agit du maillage de service.

Échange de données "sous le <-> pilote"
Pilot se connecte au cluster Kubernetes, lit l'état du cluster et attend les mises à jour. Il surveille les foyers, les services et les points d'extrémité dans le cluster Kubernetes, pour ensuite donner la bonne configuration à tous les side-cars Envoy connectés au Pilot. C'est le pont entre Kubernetes et Envoy.

Du pilote au Kubernetes
Lorsque des pods, des services ou des points de terminaison sont créés ou mis à jour dans Kubernetes, Pilot en prend connaissance et envoie la configuration nécessaire à toutes les instances Envoy connectées.
Quelle configuration est envoyée?
Quelle configuration l'Envoy obtient-il de l'Istio Pilot?
Par défaut, Kubernetes résout vos problèmes de réseau avec un service (service) qui gère les endpoint
. La liste des noeuds finaux peut ĂŞtre ouverte avec la commande:
kubectl get endpoints
Il s'agit d'une liste de tous les IP et ports du cluster et de leurs adresses (il s'agit généralement de pods créés à partir d'un déploiement). Istio est important à connaître pour configurer et envoyer des données d'itinéraire à Envoy.
Services, auditeurs et itinéraires
Lorsque vous créez un service dans un cluster Kubernetes, vous incluez des raccourcis par lesquels tous les pods appropriés seront sélectionnés. Lorsque vous envoyez du trafic vers l'IP du service, Kubernetes sélectionne le trafic pour ce trafic. Par exemple, la commande
curl my-service.default.svc.cluster.local:3000
Tout d'abord, il trouvera l'IP virtuelle affectée au my-service
dans l'espace de noms default
, et cette IP transmettra le trafic à un sous-correspondant à l'étiquette de service.
Istio et Envoy modifient légèrement cette logique. Istio configure Envoy en fonction des services et des points de terminaison du cluster Kubernetes et utilise les fonctions de routage intelligent et d'équilibrage de charge d'Envoy pour contourner le service Kubernetes. Au lieu de mandater une adresse IP à la fois, Envoy se connecte directement au foyer IP. Pour ce faire, Istio mappe la configuration Kubernetes à la configuration Envoy .
Les termes Kubernetes, Istio et Envoy sont légèrement différents, et on ne sait pas immédiatement ce qu'ils mangent.
Les services
Un service dans Kubernetes est mappé à un cluster dans Envoy. Le cluster Envoy contient une liste de points de terminaison , c'est-à -dire l'IP (ou les noms d'hôte) des instances de traitement des demandes. Pour afficher la liste des clusters configurés dans le sidecar-pod istioctl proxy-config cluster < >
, exécutez le istioctl proxy-config cluster < >
. Cette commande montre l'état actuel des choses en termes de foyer. Voici un exemple de l'un de nos environnements:
$ istioctl proxy-config cluster taxparams-6777cf899c-wwhr7 -n applications SERVICE FQDN PORT SUBSET DIRECTION TYPE BlackHoleCluster - - - STATIC accounts-grpc-gw.applications.svc.cluster.local 80 - outbound EDS accounts-grpc-public.applications.svc.cluster.local 50051 - outbound EDS addressvalidator.applications.svc.cluster.local 50051 - outbound EDS
Tous les mĂŞmes services sont dans cet espace de noms:
$ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) accounts-grpc-gw ClusterIP 10.3.0.91 <none> 80/TCP accounts-grpc-public ClusterIP 10.3.0.202 <none> 50051/TCP addressvalidator ClusterIP 10.3.0.56 <none> 50051/TCP
Comment Istio sait-il quel protocole utilise le service? Configure les protocoles pour les manifestes de service par le champ de name
dans l'entrée de port.
$ kubectl get service accounts-grpc-public -o yaml apiVersion: v1 kind: Service metadata: name: accounts-grpc-public spec: ports: - name: grpc port: 50051 protocol: TCP targetPort: 50051
S'il y a grpc
ou le préfixe grpc-
, Istio configurera le protocole HTTP2 pour le service. Nous avons appris par une expérience amère comment Istio utilise le nom du port lorsque les configurations de proxy sont corrompues car elles ne spécifient pas les préfixes http ou grpc ...
Si vous utilisez kubectl et la page de redirection de port d'administration dans Envoy, vous pouvez voir que les points de terminaison account-grpc-public sont implémentés par Pilot en tant que cluster dans Envoy avec le protocole HTTP2. Cela confirme nos hypothèses:
$ kubectl -n applications port-forward otherpod-dc56885ff-dqc6t 15000:15000 & $ curl http://localhost:15000/config_dump | yq r - ... - cluster: circuit_breakers: thresholds: - {} connect_timeout: 1s eds_cluster_config: eds_config: ads: {} service_name: outbound|50051||accounts-grpc-public.applications.svc.cluster.local http2_protocol_options: max_concurrent_streams: 1073741824 name: outbound|50051||accounts-grpc-public.applications.svc.cluster.local type: EDS ...
Le port 15000 est la page d'administration d'Envoy , disponible sur chaque side-car.
Auditeurs
Les auditeurs reconnaissent les points de terminaison Kubernetes pour transmettre le trafic aux foyers. Le service de vérification d'adresse a un point final ici:
$ kubectl get ep addressvalidator -o yaml apiVersion: v1 kind: Endpoints metadata: name: addressvalidator subsets: - addresses: - ip: 10.2.26.243 nodeName: ip-10-205-35-230.ec2.internal targetRef: kind: Pod name: addressvalidator-64885ccb76-87l4d namespace: applications ports: - name: grpc port: 50051 protocol: TCP
Par conséquent, le foyer de vérification d'adresse a un écouteur sur le port 50051:
$ kubectl -n applications port-forward addressvalidator-64885ccb76-87l4d 15000:15000 & $ curl http://localhost:15000/config_dump | yq r - ... dynamic_active_listeners: - version_info: 2019-01-13T18:39:43Z/651 listener: name: 10.2.26.243_50051 address: socket_address: address: 10.2.26.243 port_value: 50051 filter_chains: - filter_chain_match: transport_protocol: raw_buffer ...
Itinéraires
Dans Istio, au lieu de l'objet Kubernetes Ingress standard, une ressource personnalisée plus abstraite et efficace est VirtualService
- VirtualService
. VirtualService mappe les routes vers les clusters en amont en les liant Ă la passerelle. Voici comment utiliser Kubernetes Ingress avec un contrĂ´leur Ingress.
Chez Namely, nous utilisons la passerelle Istio Ingress-Gateway pour tout le trafic interne GRPC:
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: grpc-gateway spec: selector: istio: ingressgateway servers: - hosts: - '*' port: name: http2 number: 80 protocol: HTTP2 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: grpc-gateway spec: gateways: - grpc-gateway hosts: - '*' http: - match: - uri: prefix: /namely.address_validator.AddressValidator retries: attempts: 3 perTryTimeout: 2s route: - destination: host: addressvalidator port: number: 50051
À première vue, vous ne comprendrez rien dans l'exemple. Il n'est pas visible ici, mais le déploiement Istio-IngressGateway enregistre les points de terminaison nécessaires en fonction du istio: ingressgateway
. Dans cet exemple, IngressGateway achemine le trafic pour tous les domaines sur le port 80 via HTTP2. VirtualService implémente des routes pour cette passerelle, correspond au préfixe /namely.address_validator.AddressValidator
et transmet l' addressvalidator
via le port 50051 au service en amont addressvalidator
une règle de nouvelle tentative en deux secondes.
Si nous redirigeons le port pod d'Istio-IngressGateway et voyons la configuration d'Envoy, nous verrons ce que fait VirtualService:
$ kubectl -n istio-system port-forward istio-ingressgateway-7477597868-rldb5 15000 ... - match: prefix: /namely.address_validator.AddressValidator route: cluster: outbound|50051||addressvalidator.applications.svc.cluster.local timeout: 0s retry_policy: retry_on: 5xx,connect-failure,refused-stream num_retries: 3 per_try_timeout: 2s max_grpc_timeout: 0s decorator: operation: addressvalidator.applications.svc.cluster.local:50051/namely.address_validator.AddressValidator* ...
Ce que nous avons googlé en creusant à Istio
L'erreur 503 ou 404 se produit
Les raisons sont différentes, mais généralement ce sont:
- Les applications Sidecar ne peuvent pas contacter Pilot (vérifiez que Pilot est en cours d'exécution).
- Le manifeste du service Kubernetes a un protocole non valide.
- La configuration VirtualService / Envoy écrit la route vers le mauvais cluster en amont. Commencez avec le service Edge, là où vous attendez du trafic entrant, et examinez les journaux Envoy. Ou utilisez quelque chose comme Jaeger pour trouver des erreurs.
Que signifie NR / UH / UF dans les journaux de proxy Istio?
- NR - Pas de route.
- UH - Upstream Unhealthy (inoperable upstream).
- UF - Upstream Failure (défaillance en amont).
En savoir plus sur le site Web d'Envoy .
Concernant la haute disponibilité avec Istio
- Ajoutez NodeAffinity aux composants Istio pour répartir uniformément les foyers sur différentes zones de disponibilité et augmenter le nombre minimum de répliques.
- Lancez la nouvelle version de Kubernetes avec la fonction de mise à l'échelle horizontale des pods. Les foyers les plus importants évolueront en fonction de la charge.
Pourquoi la cronjob ne se termine-t-elle pas?
Une fois la charge de travail principale terminée, le conteneur de sidecar continue de fonctionner. Pour contourner le problème, désactivez sidecar dans cronjobs en ajoutant l'annotation sidecar.istio.io/inject: “false”
Ă PodSpec.
Comment installer Istio?
Nous utilisons Spinnaker pour les déploiements, mais nous prenons généralement les derniers graphiques Helm, les helm template -f values.yml
, utilisons le helm template -f values.yml
et helm template -f values.yml
fichiers sur Github pour voir les changements avant de les appliquer via kubectl apply -f -
. Ceci afin de ne pas modifier accidentellement le CRD ou l'API dans différentes versions.
Merci à Bobby Tables et Michael Hamrah d' avoir aidé à écrire ce message.