Por que necesito un service mesh o malla de servicio?


Introducción

Esta vez veremos cómo empezar con Istio y por qué necesitamos usar un service mesh.


Istio

Entonces… Podrías estar preguntándote algunas de estas preguntas: ¿por qué Istio? ¿Por qué necesito un service mesh? ¿Cuándo lo necesito? Y quiero ayudarte con algunas respuestas:


¿Por qué necesito un service mesh? Básicamente porque en entornos en la nube no puedes confiar en que la red será confiable el 100% del tiempo, que la latencia será baja, que la red es segura y que el ancho de banda es infinito. El service mesh es simplemente una capa adicional que ayuda a los microservicios a comunicarse entre sí de manera segura y confiable.


¿Cuándo necesito tener uno? Esto puede ser complicado y dependerá de tu entorno, pero el momento en que comiences a experimentar problemas de red entre tus microservicios sería un buen momento para implementarlo. Por supuesto, podría hacerse antes, pero dependerá mucho del proyecto; si puedes empezar temprano, será mejor y más fácil de implementar. Siempre ten en cuenta los beneficios de seguridad adicional, observabilidad y probable mejora del rendimiento.


¿Por qué Istio? Esta será una pequeña serie sobre service meshes para Kubernetes y decidí comenzar con Istio.


En caso de que no estés de acuerdo con mis explicaciones, está bien; esta es una versión resumida y también simplifiqué mucho las cosas. Para una visión más completa, puedes consultar este artículo o este otro, o si deseas una introducción más profunda puedes leer más aquí.


Empecemos

Primero que nada, necesitamos descargar e instalar Istio en nuestro clúster. La forma recomendada de hacerlo es usando Helm (en este caso, utilizaré la alternativa sin Tiller, pero también se podría hacer con helm install. Consulta más información aquí):

$ curl -L https://git.io/getLatestIstio | sh -

Esto descargará y extraerá la última versión, en este caso la 1.0.5 en este momento.


Así que instalemos Istio… solo presta atención a los primeros 3 comandos, luego puedes saltar hasta el final del bloque de código; publico toda la salida porque me gustan los ejemplos completos :)

istio-1.0.5 $ helm template install/kubernetes/helm/istio --name istio --namespace istio-system --set grafana.enabled=true > $HOME/istio.yaml
istio-1.0.5 $ kubectl create namespace istio-system
namespace "istio-system" created

istio-1.0.5 $ kubectl apply -f $HOME/istio.yaml
configmap "istio-galley-configuration" created
configmap "istio-statsd-prom-bridge" created
configmap "prometheus" created
configmap "istio-security-custom-resources" created
configmap "istio" created
configmap "istio-sidecar-injector" created
serviceaccount "istio-galley-service-account" created
serviceaccount "istio-egressgateway-service-account" created
serviceaccount "istio-ingressgateway-service-account" created
serviceaccount "istio-mixer-service-account" created
serviceaccount "istio-pilot-service-account" created
serviceaccount "prometheus" created
serviceaccount "istio-cleanup-secrets-service-account" created
clusterrole.rbac.authorization.k8s.io "istio-cleanup-secrets-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-cleanup-secrets-istio-system" created
job.batch "istio-cleanup-secrets" created
serviceaccount "istio-security-post-install-account" created
clusterrole.rbac.authorization.k8s.io "istio-security-post-install-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-security-post-install-role-binding-istio-system" created
job.batch "istio-security-post-install" created
serviceaccount "istio-citadel-service-account" created
serviceaccount "istio-sidecar-injector-service-account" created
customresourcedefinition.apiextensions.k8s.io "virtualservices.networking.istio.io" created
customresourcedefinition.apiextensions.k8s.io "destinationrules.networking.istio.io" created
customresourcedefinition.apiextensions.k8s.io "serviceentries.networking.istio.io" created
customresourcedefinition.apiextensions.k8s.io "gateways.networking.istio.io" created
customresourcedefinition.apiextensions.k8s.io "envoyfilters.networking.istio.io" created
customresourcedefinition.apiextensions.k8s.io "httpapispecbindings.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "httpapispecs.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "quotaspecbindings.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "quotaspecs.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "rules.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "attributemanifests.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "bypasses.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "circonuses.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "deniers.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "fluentds.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "kubernetesenvs.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "listcheckers.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "memquotas.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "noops.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "opas.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "prometheuses.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "rbacs.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "redisquotas.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "servicecontrols.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "signalfxs.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "solarwindses.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "stackdrivers.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "statsds.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "stdios.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "apikeys.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "authorizations.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "checknothings.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "kuberneteses.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "listentries.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "logentries.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "edges.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "metrics.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "quotas.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "reportnothings.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "servicecontrolreports.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "tracespans.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "rbacconfigs.rbac.istio.io" created
customresourcedefinition.apiextensions.k8s.io "serviceroles.rbac.istio.io" created
customresourcedefinition.apiextensions.k8s.io "servicerolebindings.rbac.istio.io" created
customresourcedefinition.apiextensions.k8s.io "adapters.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "instances.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "templates.config.istio.io" created
customresourcedefinition.apiextensions.k8s.io "handlers.config.istio.io" created
clusterrole.rbac.authorization.k8s.io "istio-galley-istio-system" created
clusterrole.rbac.authorization.k8s.io "istio-egressgateway-istio-system" created
clusterrole.rbac.authorization.k8s.io "istio-ingressgateway-istio-system" created
clusterrole.rbac.authorization.k8s.io "istio-mixer-istio-system" created
clusterrole.rbac.authorization.k8s.io "istio-pilot-istio-system" created
clusterrole.rbac.authorization.k8s.io "prometheus-istio-system" created
clusterrole.rbac.authorization.k8s.io "istio-citadel-istio-system" created
clusterrole.rbac.authorization.k8s.io "istio-sidecar-injector-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-galley-admin-role-binding-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-egressgateway-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-ingressgateway-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-mixer-admin-role-binding-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-pilot-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "prometheus-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-citadel-istio-system" created
clusterrolebinding.rbac.authorization.k8s.io "istio-sidecar-injector-admin-role-binding-istio-system" created
service "istio-galley" created
service "istio-egressgateway" created
service "istio-ingressgateway" created
service "istio-policy" created
service "istio-telemetry" created
service "istio-pilot" created
service "prometheus" created
service "istio-citadel" created
service "istio-sidecar-injector" created
deployment.extensions "istio-galley" created
deployment.extensions "istio-egressgateway" created
deployment.extensions "istio-ingressgateway" created
deployment.extensions "istio-policy" created
deployment.extensions "istio-telemetry" created
deployment.extensions "istio-pilot" created
deployment.extensions "prometheus" created
deployment.extensions "istio-citadel" created
deployment.extensions "istio-sidecar-injector" created
gateway.networking.istio.io "istio-autogenerated-k8s-ingress" created
horizontalpodautoscaler.autoscaling "istio-egressgateway" created
horizontalpodautoscaler.autoscaling "istio-ingressgateway" created
horizontalpodautoscaler.autoscaling "istio-policy" created
horizontalpodautoscaler.autoscaling "istio-telemetry" created
horizontalpodautoscaler.autoscaling "istio-pilot" created
mutatingwebhookconfiguration.admissionregistration.k8s.io "istio-sidecar-injector" created
attributemanifest.config.istio.io "istioproxy" created
attributemanifest.config.istio.io "kubernetes" created
stdio.config.istio.io "handler" created
logentry.config.istio.io "accesslog" created
logentry.config.istio.io "tcpaccesslog" created
rule.config.istio.io "stdio" created
rule.config.istio.io "stdiotcp" created
metric.config.istio.io "requestcount" created
metric.config.istio.io "requestduration" created
metric.config.istio.io "requestsize" created
metric.config.istio.io "responsesize" created
metric.config.istio.io "tcpbytesent" created
metric.config.istio.io "tcpbytereceived" created
prometheus.config.istio.io "handler" created
rule.config.istio.io "promhttp" created
rule.config.istio.io "promtcp" created
kubernetesenv.config.istio.io "handler" created
rule.config.istio.io "kubeattrgenrulerule" created
rule.config.istio.io "tcpkubeattrgenrulerule" created
kubernetes.config.istio.io "attributes" created
destinationrule.networking.istio.io "istio-policy" created
destinationrule.networking.istio.io "istio-telemetry" created

¡VAYA! ¿Qué acaba de pasar?

Se crearon muchos recursos nuevos; básicamente, acabamos de generar el manifiesto del chart de Helm y lo aplicamos a nuestro clúster.


Así que veamos qué está en ejecución y qué significa eso:

$ kubectl get pods -n istio-system
NAME                                      READY     STATUS      RESTARTS   AGE
istio-citadel-856f994c58-l96p8            1/1       Running     0          3m
istio-cleanup-secrets-xqqj4               0/1       Completed   0          3m
istio-egressgateway-5649fcf57-7zwkh       1/1       Running     0          3m
istio-galley-7665f65c9c-tzn7d             1/1       Running     0          3m
istio-ingressgateway-6755b9bbf6-bh84r     1/1       Running     0          3m
istio-pilot-56855d999b-c4cp5              2/2       Running     0          3m
istio-policy-6fcb6d655f-9544z             2/2       Running     0          3m
istio-sidecar-injector-768c79f7bf-th8zh   1/1       Running     0          3m
istio-telemetry-664d896cf5-jdcwv          2/2       Running     0          3m
prometheus-76b7745b64-f8jxn               1/1       Running     0          3m

Unos minutos después, casi todo está en funcionamiento, pero ¿qué es todo eso? Istio tiene varios componentes; consulta la siguiente descripción general extraída de GitHub.


Envoy: Proxies sidecar por microservicio para manejar el tráfico de entrada/salida entre servicios en el clúster y desde un servicio a servicios externos. Los proxies forman un mesh de microservicios seguro que proporciona un conjunto rico de funciones como descubrimiento, enrutamiento avanzado de capa 7, circuit breakers, aplicación de políticas y funciones de registro/informe de telemetría.

Nota: El service mesh no es una red superpuesta. Simplifica y mejora cómo los microservicios en una aplicación se comunican entre sí sobre la red proporcionada por la plataforma subyacente.


Mixer: Componente central que es utilizado por los proxies y microservicios para aplicar políticas como autorización, límites de velocidad, cuotas, autenticación, rastreo de solicitudes y recopilación de telemetría.


Pilot: Un componente responsable de configurar los proxies en tiempo de ejecución.


Citadel: Un componente centralizado responsable de la emisión y rotación de certificados.


Node Agent: Un componente por nodo responsable de la emisión y rotación de certificados.


Galley: Componente central para validar, ingerir, agregar, transformar y distribuir configuraciones dentro de Istio.


Bien, se instalaron muchas cosas nuevas, pero ¿cómo sé que está funcionando? Vamos a desplegar una aplicación de prueba y comprobarlo:

$ export PATH="$PATH:~/istio-1.0.5/bin"
istio-1.0.5/samples/bookinfo $ kubectl apply -f <(istioctl kube-inject -f platform/kube/bookinfo.yaml)
service "details" created
deployment.extensions "details-v1" created
service "ratings" created
deployment.extensions "ratings-v1" created
service "reviews" created
deployment.extensions "reviews-v1" created
deployment.extensions "reviews-v2" created
deployment.extensions "reviews-v3" created
service "productpage" created
deployment.extensions "productpage-v1" created

Como podemos ver, cada pod tiene 2 contenedores: el contenedor de la aplicación y istio-proxy. También puedes configurar la inyección automática de sidecar.

$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
details-v1-8bd954dbb-zhrqq        2/2       Running   0          2m
productpage-v1-849c786f96-kpfx9   2/2       Running   0          2m
ratings-v1-68d648d6fd-w68qb       2/2       Running   0          2m
reviews-v1-b4c984bdc-9s6j5        2/2       Running   0          2m
reviews-v2-575446d5db-r6kwc       2/2       Running   0          2m
reviews-v3-74458c4889-kr4wb       2/2       Running   0          2m

Además, todos los servicios están en funcionamiento:

$ kubectl get services
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
details       ClusterIP   10.245.134.179   <none>        9080/TCP   3m
kubernetes    ClusterIP   10.245.0.1       <none>        443/TCP    3d
productpage   ClusterIP   10.245.32.221    <none>        9080/TCP   3m
ratings       ClusterIP   10.245.159.112   <none>        9080/TCP   3m
reviews       ClusterIP   10.245.77.125    <none>        9080/TCP   3m

Pero, ¿cómo accedo a la aplicación?

istio-1.0.5/samples/bookinfo $ kubectl apply -f networking/bookinfo-gateway.yaml
gateway.networking.istio.io "bookinfo-gateway" created
virtualservice.networking.istio.io "bookinfo" created

En Istio, un Gateway configura un balanceador de carga para tráfico HTTP/TCP, operando más comúnmente en el borde del mesh para habilitar el tráfico de entrada para una aplicación (L4-L6).


Después de eso, necesitamos establecer algunas variables de entorno para obtener la IP del balanceador de carga, el puerto, etc.

$ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
$ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
$ export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

curl -o /dev/null -s -w "%{http_code}\n" http://${GATEWAY_URL}/productpage

Si el último comando curl devuelve 200, entonces estamos bien; también puedes navegar a la aplicación con open http://${GATEWAY_URL}/productpage y verás algo como la siguiente imagen: img


También puedes usar Grafana para verificar algunas métricas sobre el uso del servicio, etc. (No tienes que preocuparte por Prometheus ya que está habilitado por defecto). Inicia el port-forward para que no tengamos que exponer Grafana al mundo con: kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=grafana -o jsonpath='{.items[0].metadata.name}') 3000:3000, y luego open http://localhost:3000.


Como consejo general, revisa todas las configuraciones que ofrece Istio; prueba las que creas que podrían ser útiles para tu proyecto y siempre mide y compara.


Notas

  • Ten en cuenta que el pod pilot requiere al menos 4 GB de memoria, por lo que necesitarás al menos un nodo con esa cantidad de memoria.
  • Puedes verificar el estado del balanceador de carga en: Manage -> Networking -> Load balancers. Si todo está bien, tu LB dirá “Healthy”.
  • Grafana no está habilitado por defecto, pero lo habilitamos vía Helm con --set grafana.enabled=true. Si deseas revisar todas las opciones posibles, ve aquí. Si estás usando más de dos opciones --set, recomendaría crear un archivo values.yaml y usar eso en su lugar.
  • Istio es una gran herramienta y debe tratarse con cuidado; hay mucho más que aprender y probar. Aquí solo hemos rascado la superficie.

Próximas publicaciones

  • Más ejemplos usando Istio.
  • Linkerd.
  • Quizás algo de diversión con Golang.
  • Serverless o kubeless, esa es la cuestión.

Errata

Si encuentras algún error o tienes alguna sugerencia, por favor envíame un mensaje para que pueda corregirlo.



No tienes cuenta? Regístrate aqui

Ya registrado? Iniciar sesión a tu cuenta ahora.

Iniciar session con GitHub
Iniciar sesion con Google
  • Comentarios

    Online: 0

Por favor inicie sesión para poder escribir comentarios.

by Gabriel Garrido