¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
Conoce nuestra marca.¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
Conoce nuestra marca.dev
Andrés Navidad 24/10/2019 Cargando comentarios…
Desde hace un tiempo hasta ahora, el auge de las nubes públicas es cada vez mayor.
Se ha escrito mucho sobre arquitecturas de microservicios en anteriores posts y sobre las tecnologías que trataremos en este post (Kubernetes, Istio) así que el objetivo de este post es, con todos esos conceptos descritos anteriormente, ver qué posibilidades existen en Google Cloud Platform (GCP).
Parece casi impensable, a día de hoy, diseñar una arquitectura de microservicios sin el concepto de contenedor. Aunque existen otras tecnologías de contenedores, Docker, probablemente, sea la más difundida y soportada.
Y, en paralelo al crecimiento de contenedores ha ido el crecimiento de tecnologías de gestión de contenedores. En este caso, Kubernetes se ha impuesto al resto de tecnologías (Docker Swarm, Mesos).
Existen soluciones empresariales basadas en Kubernetes que apoyan esta tendencia: RedHat con OpenShift o Mesosphere que, aunque de manera muy disimulada, ha puesto el foco en Kubernetes.
Kubernetes fue diseñado por Google y posteriormente donado a la Cloud Native Computing Foundation. En su desarrollo han intervenido, además de Google, empresas como RedHat, Huawei, Microsoft o VMWare, entre otras.
A través de Google Kubernetes Engine (GKE), Google ofrece su servicio gestionado de Kubernetes, haciéndonos la vida más fácil para que nos centremos en la parte más creativa, aportando más valor en mucho menos tiempo.
gcloud beta container --project projectId-GCP clusters create demo-cluster \
--zone "us-central1-b" \
--no-enable-basic-auth \
--cluster-version "1.12.8-gke.10" \
--num-nodes "3" \
--machine-type "n1-standard-2" \
--disk-type "pd-standard" \
--disk-size "20" \
--image-type "COS" \
--default-max-pods-per-node "30" \
--metadata disable-legacy-endpoints=true \
--scopes "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/
logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/
auth/servicecontrol",
"https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/
auth/trace.append" \
--preemptible \
--no-enable-autoupgrade \
--no-issue-client-certificate \
--enable-stackdriver-kubernetes \
--enable-ip-alias \
--network "projects/poc-microservices-241815/global/networks/default" \
--subnetwork "projects/poc-microservices-241815/regions/us-central1/subnetworks/default" \
--addons HorizontalPodAutoscaling,HttpLoadBalancing,Istio \
--istio-config auth=MTLS_PERMISSIVE \
--enable-autorepair
La instalación de Istio en GKE tan solo consiste en añadir el add-ons de Istio y configurar la seguridad a MTLS_PERMISIVE. Esto permite a los servicios aceptar tráfico cifrado y descifrado y, por defecto, lo envía descifrado.
Es una práctica recomendada que el entorno de producción esté en un cluster de Kubernetes independiente y el resto de entornos (test, cua, prepro) en otro cluster.
En este post vamos a crear un único cluster con dos entornos (test y cua). Kubernetes permite esa simulación de entornos a través de namespaces.
Para ello, una vez creado el cluster en el paso anterior procedemos a crear los namespaces:
kubectl apply -f - <<EOF
{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": {
"name": "test",
"labels": {
"name": "test"
}
}
}
EOF
kubectl apply -f - <<EOF
{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": {
"name": "cua",
"labels": {
"name": "cua"
}
}
}
EOF
De esta manera tendremos en nuestro cluster un namespace ‘test’ y otro llamado ‘cua’ que harán la función de dos entornos independientes.
Como veremos más adelante, los servicios desplegados en un namespace no son visibles desde otro namespace.Ahora, para aprovechar las ventajas que nos ofrece Istio, debemos decirle que todos los contendores desplegados en ese namespace deben ser proxificados.
Para hacer esto posible, Istio utiliza el patrón sidecar. Puedes ver en qué consiste este patrón en este post.
kubectl label namespace test istio-injection=enabled
kubectl label namespace cua istio-injection=enabled
Todo los microservicios en goodly están realizados con spring-boot. Utilizamos maven como herramienta de construcción maven.
Hemos generado un arquetipo de maven que permite estandarizar toda la estructura de nuestros microservicios.
Nuestro microservicio expone los siguientes endpoints:
Para desacoplar la configuración del microservicio del propio contenedor utilizamos configmaps. Para ello, usamos spring-cloud-kubernetes, en concreto el módulo spring-cloud-kubernetes-config.
De esta manera, podemos seguir manteniendo el mismo código para cargar la configuración que cuando lo hacíamos a través de ficheros de configuración según profiles de spring.
Para cada entorno tendremos un configmap independiente que configurará las properties que sean necesarias.
k8s_configmap.test.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: employee-ms
namespace: test
data:
application.yaml: |-
environment:
current: Current environment is kubernetes test
De esta manera cuando nuestro microservicio arranque leerá la configuración de este configmap.
2019-08-12 08:16:36,653 [main] INFO org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration - Located property source: CompositePropertySource {name='composite-configmap', propertySources=[ConfigMapPropertySource {name='configmap.employee-ms.test}]}
Gestionar los logs en una arquitectura de microservicios y tenerlos centralizados en un único lugar es algo para lo que hay muchas aproximaciones, pero aquí veremos cómo trabajando con GCP todo se simplifica.
En este caso, todos los logs de nuestros microservicios están centralizados en el servicio de Google Stackdriver Logging. De esta manera, podemos acceder rápidamente a ellos y crear métricas en base a la serie de criterios que más nos interese.
Tenemos diferentes niveles de selección posibles:
Como punto a tener en cuenta, los logs de nuestros microservicios tardan entre 30-60 segundos en estar disponibles en Stackdriver.
Cada uno de los microservicios tiene dos ficheros que son imprescindibles.
ARG JAR_NAME=localimage:latest
FROM alpine
RUN apk update && apk add openjdk8
ENV JAVA_VERSION 1.8
ENV APP_HOME /microservice
RUN mkdir $APP_HOME && adduser -S -D -H java
COPY Docker/scripts $APP_HOME
ADD target/$JAR_NAME $APP_HOME
RUN chown -R java $APP_HOME
WORKDIR $APP_HOME
EXPOSE 8080
USER java
CMD ["./start.sh"]
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: employee-ms
name: employee-ms
spec:
progressDeadlineSeconds: 600
replicas: 1
selector:
matchLabels:
run: employee-ms
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
labels:
run: employee-ms
spec:
containers:
- image: gcr.io/poc-microservices-241815/employee-ms:v1.0.0
imagePullPolicy: Always
name: employee-ms
ports:
- containerPort: 8080
protocol: TCP
resources:
requests:
cpu: 500m
memory: 1024Mi
limits:
cpu: 1000m
memory: 2048Mi
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
restartPolicy: Always
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
status: {}
apiVersion: v1
kind: Service
metadata:
labels:
run: employee-ms
name: employee-ms-service
spec:
type: ClusterIP
selector:
run: employee-ms
ports:
- protocol: TCP
port: 80
targetPort: 8080
<h2 class="block block-header h--h20-175-500 left add-last-dot">Construye y despliega tus microservicios</h2>
Lo primero que haremos será generar el empaquetamiento de nuestro microservicio. Al tratarse de una aplicación spring-boot el resultado será un jar.
```undefined
mvn clean package -DskipTests
Posteriormente, construimos la imagen de docker.
MICROSERVICE_NAME=employee-ms
VERSION=1.0.0
docker build -t $MICROSERVICE_NAME:$VERSION --build-arg JAR_NAME=$MICROSERVICE_NAME-$VERSION.jar -f Dockerfile .
Finalmente, creamos un tag de la imagen y hacemos push para subirla a Google Container Repository.
docker tag $MICROSERVICE_NAME:$VERSION gcr.io/<your-google-projectId>/$MICROSERVICE_NAME:$VERSION
docker push gcr.io/<your-google-projectId>/$MICROSERVICE_NAME:$VERSION
Estamos ya en disposición de desplegar nuestro microservicio y su configuración en el namespace test.
kubectl apply -f k8s_configmap.test.yaml
kubectl apply -f k8s_manifest.yaml -n test
¿Por qué en el configmap no hay que especificar el namespace y en Deployment sí?
En el configmap ya le estamos diciendo internamente, a través de ‘metadata.namespace: test’, que ese configmap va a ese namespace y, sin embargo, el manifest no lo lleva incluido porque queremos que sea válido, tanto para poder desplegar en test como en cua.
Hacer una promoción de entorno de un microservicio que tenemos en test, sería tan sencillo como:
kubectl apply -f k8s_configmap.cua.yaml
kubectl apply -f k8s_manifest.yaml -n cua
Arquitectura según la situación actual:
Tenemos un microservicio desplegado en dos entornos que no es visible más que a otros microservicios que están desplegados en ese mismo namespace.
¿Qué pasaría si tengo que dar visibilidad hacía el mundo exterior? En este caso, Istio, con todas sus posibilidades de traffic management, nos simplifica la vida.
Por defecto, Istio nos ofrece un servicio ingressgateway que tiene vinculado una IP externa. Ahora, hay que definir cómo, a través de esa IP, llegamos a los microservicios que nos interesen.
k8s_istio-global-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: goodly-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
kubectl apply -f k8s_istio-global-gateway.yaml
Tenemos que crear tantos virtual services como namespace tengamos que conectan nuestro gateway con los servicios que hay desplegados en cada entorno.
k8s_istio-virtual-service.test.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: goodly-vs
namespace: test
spec:
hosts:
- anavidad-eval-test.apigee.net #Your domain here
gateways:
- goodly-gateway.istio-system.svc.cluster.local
http:
- match:
- uri:
prefix: /employee-ms
route:
- destination:
host: employee-ms
port:
number: 80
kubectl apply -f k8s_istio-virtual-service.test.yaml
Analicemos qué hace este virtual service poco a poco:
Hacemos lo propio para el namespace cua.
k8s_istio-virtual-service.cua.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: goodly-vs
namespace: cua
spec:
hosts:
- anavidad-eval-cua.apigee.net #Your domain here
gateways:
- goodly-gateway.istio-system.svc.cluster.local
http:
- match:
- uri:
prefix: /employee-ms
route:
- destination:
host: employee-ms
port:
number: 80
kubectl apply -f k8s_istio-virtual-service.cua.yaml
De esta manera la arquitectura final queda así:
Gracias a este post hemos visto cómo tener una primera aproximación a una arquitectura de microservicios en GKE.
Hay ciertos puntos que no se han abarcado en este post: la apificación de estos microservicios desplegados en GKE a través de Apigee, donde se definirán las políticas seguridad y de caché, entre otras.
Tampoco la integración continua y el despliegue automático de microservicios en Kubernetes, que es una parte fundamental para hacer que esta arquitectura sea viable. Estos temas los trataremos más adelante en futuros posts.
Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.
Cuéntanos qué te parece.