Por que necesito un service mesh o malla de servicio?
- 26 kubernetes
- 2 istio
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:
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 archivovalues.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.
-
Comentarios
Online: 0
Por favor inicie sesión para poder escribir comentarios.