Preparar una aplicación para Cloud Service Mesh


Cloud Service Mesh es una herramienta muy útil para gestionar y monitorizar aplicaciones distribuidas. Para sacar el máximo partido a Cloud Service Mesh, es útil conocer sus abstracciones subyacentes, como los contenedores y Kubernetes. En este tutorial se explica cómo preparar una aplicación para Cloud Service Mesh, desde el código fuente hasta un contenedor que se ejecuta en GKE, justo antes de instalar Cloud Service Mesh.

Si ya conoces los conceptos de Kubernetes y de malla de servicios, puedes saltarte este tutorial e ir directamente a la guía de instalación de Cloud Service Mesh.

Objetivos

  1. Descubre una aplicación sencilla de "Hola, mundo" con varios servicios.
  2. Ejecutar la aplicación desde la fuente
  3. Conteneriza la aplicación.
  4. Crea un clúster de Kubernetes.
  5. Despliega los contenedores en el clúster.

Antes de empezar

Sigue estos pasos para habilitar la API Cloud Service Mesh:
  1. Ve a la página de Kubernetes Engine en la Google Cloud consola.
  2. Crea o selecciona un proyecto.
  3. Espera a que la API y los servicios relacionados se habiliten. Este proceso puede tardar varios minutos.
  4. Verify that billing is enabled for your Google Cloud project.

En este tutorial se usa Cloud Shell, que aprovisiona una máquina virtual g1-small de Compute Engine que ejecuta un sistema operativo Linux basado en Debian.

Preparar Cloud Shell

Las ventajas de usar Cloud Shell son las siguientes:

  • Los entornos de desarrollo de Python 2 y Python 3 (incluido virtualenv) están configurados.
  • Las herramientas de línea de comandos gcloud, docker, git y kubectl que se usan en este tutorial ya están instaladas.
  • Puedes elegir entre los siguientes editores de texto:

    • Editor de código, al que puedes acceder haciendo clic en en la parte superior de la ventana de Cloud Shell.

    • Emacs, Vim o Nano, a los que puedes acceder desde la línea de comandos de Cloud Shell.

In the Google Cloud console, activate Cloud Shell.

Activate Cloud Shell

Descargar el código de ejemplo

  1. Descarga el código fuente de helloserver:

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    
  2. Cambia al directorio del código de ejemplo:

    cd anthos-service-mesh-samples/docs/helloserver
    

Explorar la aplicación multiservicio

La aplicación de ejemplo está escrita en Python y tiene dos componentes que se comunican mediante REST:

  • server: un servidor sencillo con un endpoint GET, / , que imprime "hola mundo" en la consola.
  • loadgen: una secuencia de comandos que envía tráfico a server, con un número configurable de solicitudes por segundo (RPS).

aplicación de ejemplo

Ejecutar la aplicación desde la fuente

Para familiarizarte con la aplicación de ejemplo, ejecútala en Cloud Shell.

  1. En el directorio sample-apps/helloserver, ejecuta el siguiente comando: server

    python3 server/server.py
    

    Al iniciarse, server muestra lo siguiente:

    INFO:root:Starting server...
    
  2. Abre otra ventana de terminal para poder enviar solicitudes al server. Haz clic en para abrir otra sesión.

  3. Envía una solicitud a server:

    curl http://localhost:8080
    

    server responde:

    Hello World!
    
  4. En el directorio donde hayas descargado el código de ejemplo, cambia al directorio que contiene loadgen:

    cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/loadgen
  5. Crea las siguientes variables de entorno:

    export SERVER_ADDR=http://localhost:8080
    export REQUESTS_PER_SECOND=5
    
  6. Inicio virtualenv:

    virtualenv --python python3 env
    
  7. Activa el entorno virtual:

    source env/bin/activate
    
  8. Instala los requisitos de loadgen:

    pip3 install -r requirements.txt
    
  9. Ejecuta loadgen:

    python3 loadgen.py
    

    Al iniciarse, loadgen muestra un mensaje similar al siguiente:

    Starting loadgen: 2019-05-20 10:44:12.448415
    5 request(s) complete to http://localhost:8080
    

    En la otra ventana de terminal, server escribe mensajes en la consola similares a los siguientes:

    127.0.0.1 - - [21/Jun/2019 14:22:01] "GET / HTTP/1.1" 200 -
    INFO:root:GET request,
    Path: /
    Headers:
    Host: localhost:8080
    User-Agent: python-requests/2.22.0
    Accept-Encoding: gzip, deflate
    Accept: */*
    

    Desde el punto de vista de la red, toda la aplicación se ejecuta ahora en el mismo host. Por este motivo, puedes usar localhost para enviar solicitudes a la server.

  10. Para detener loadgen y server, introduce Ctrl-c en cada ventana de terminal.

  11. En la ventana de terminal loadgen, desactiva el entorno virtual:

    deactivate
    

Contenerizar la aplicación

Para ejecutar la aplicación en GKE, debes empaquetar la aplicación de ejemplo (server y loadgen) en contenedores. Un contenedor es una forma de empaquetar una aplicación de forma que esté aislada del entorno subyacente.

Para contenerizar la aplicación, necesitas un Dockerfile. Un Dockerfile es un archivo de texto que define los comandos necesarios para ensamblar el código fuente de la aplicación y sus dependencias en una imagen de Docker. Una vez que hayas creado la imagen, súbela a un registro de contenedores, como Docker Hub o Container Registry.

La muestra incluye un Dockerfile para server y loadgen con todos los comandos necesarios para crear las imágenes. A continuación, se muestra el Dockerfile de server:

FROM python:3.13-slim as base
FROM base as builder
RUN apt-get -qq update \
    && apt-get install -y --no-install-recommends \
        g++ \
    && rm -rf /var/lib/apt/lists/*

# Enable unbuffered logging
FROM base as final
ENV PYTHONUNBUFFERED=1

RUN apt-get -qq update \
    && apt-get install -y --no-install-recommends \
        wget

WORKDIR /helloserver

# Grab packages from builder
COPY --from=builder /usr/local/lib/python3.* /usr/local/lib/

# Add the application
COPY . .

EXPOSE 8080
ENTRYPOINT [ "python", "server.py" ]
  • El comando FROM python:3-slim as base indica a Docker que use la imagen de Python 3 más reciente como imagen base.
  • El comando COPY . . copia los archivos de origen del directorio de trabajo actual (en este caso, solo server.py) en el sistema de archivos del contenedor.
  • ENTRYPOINT define el comando que se usa para ejecutar el contenedor. En este caso, el comando es casi el mismo que el que usaste para ejecutar server.py desde el código fuente.
  • El comando EXPOSE especifica que server escucha en el puerto 8080. Este comando no expone ningún puerto, pero sirve como documentación para indicar que debes abrir el puerto 8080 cuando ejecutes el contenedor.

Preparar la contenedorización de la aplicación

  1. Define las siguientes variables de entorno. Sustituye PROJECT_ID por el ID de tu proyectoGoogle Cloud .

    export PROJECT_ID="PROJECT_ID"
    export GCR_REPO="asm-ready"

    Usa los valores de PROJECT_ID y GCR_REPO para etiquetar la imagen Docker cuando la compiles y, a continuación, la envíes a tu Container Registry privado.

  2. Define el proyecto Google Cloud predeterminado de Google Cloud CLI.

    gcloud config set project $PROJECT_ID
  3. Define la zona predeterminada de Google Cloud CLI.

    gcloud config set compute/zone us-central1-b
    
  4. Asegúrate de que el servicio Container Registry esté habilitado en tuGoogle Cloud proyecto.

    gcloud services enable containerregistry.googleapis.com
    

Contenedorizar el server

  1. Cambia al directorio donde se encuentra el ejemplo server:

    cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/server/
  2. Crea la imagen con Dockerfile y las variables de entorno que hayas definido anteriormente:

    docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1 .
    

    La bandera -t representa la etiqueta de Docker. Es el nombre de la imagen que usas al implementar el contenedor.

  3. Envía la imagen a Container Registry:

    docker push gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1
    

Contenedorizar el loadgen

  1. Cambia al directorio donde se encuentra el ejemplo loadgen:

    cd ../loadgen
    
  2. Crea la imagen:

    docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1 .
    
  3. Envía la imagen a Container Registry:

    docker push gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1
    

Enumera las imágenes

Obtén una lista de las imágenes del repositorio para confirmar que se han enviado:

gcloud container images list --repository gcr.io/$PROJECT_ID/asm-ready

El comando responde con los nombres de las imágenes que acabas de enviar:

NAME
gcr.io/PROJECT_ID/asm-ready/helloserver
gcr.io/PROJECT_ID/asm-ready/loadgen

Crear un clúster de GKE

Puedes ejecutar estos contenedores en la VM de Cloud Shell con el comando docker run. Sin embargo, en producción, necesitas orquestar los contenedores de una forma más unificada. Por ejemplo, necesitas un sistema que se asegure de que los contenedores siempre estén en funcionamiento y una forma de aumentar la capacidad y de iniciar instancias adicionales de un contenedor para gestionar los aumentos de tráfico.

Puedes usar GKE para ejecutar aplicaciones en contenedores. GKE es una plataforma de orquestación de contenedores que funciona conectando máquinas virtuales en un clúster. Cada VM se denomina nodo. Los clústeres de GKE se basan en el sistema de gestión de clústeres de código abierto de Kubernetes. Kubernetes proporciona los mecanismos a través de los cuales interactúas con tu clúster.

Para crear un clúster de GKE, sigue estos pasos:

  1. Crea el clúster:

    gcloud container clusters create asm-ready \
      --cluster-version latest \
      --machine-type=n1-standard-4 \
      --num-nodes 4
    

    El comando gcloud crea un clúster en el proyecto y la zona que hayas definido anteriormente.Google Cloud Para ejecutar Cloud Service Mesh, te recomendamos que uses al menos 4 nodos y el tipo de máquina n1-standard-4.

    El comando para crear el clúster tarda unos minutos en completarse. Cuando el clúster esté listo, el comando mostrará un mensaje similar al siguiente:

    NAME        LOCATION       MASTER_VERSION  MASTER_IP      MACHINE_TYPE   NODE_VERSION   NUM_NODES  STATUS
    asm-ready  us-central1-b  1.13.5-gke.10   203.0.113.1    n1-standard-2  1.13.5-gke.10  4          RUNNING
    
  2. Proporciona credenciales a la herramienta de línea de comandos kubectl para que puedas usarla para gestionar el clúster:

    gcloud container clusters get-credentials asm-ready
    
  3. Ahora puedes usar kubectl para comunicarte con Kubernetes. Por ejemplo, puede ejecutar el siguiente comando para obtener el estado de los nodos:

    kubectl get nodes
    

    El comando responde con una lista de los nodos, similar a la siguiente:

    NAME                                       STATUS   ROLES    AGE    VERSION
    gke-asm-ready-default-pool-dbeb23dc-1vg0   Ready    <none>   99s    v1.13.6-gke.13
    gke-asm-ready-default-pool-dbeb23dc-36z5   Ready    <none>   100s   v1.13.6-gke.13
    gke-asm-ready-default-pool-dbeb23dc-fj7s   Ready    <none>   99s    v1.13.6-gke.13
    gke-asm-ready-default-pool-dbeb23dc-wbjw   Ready    <none>   99s    v1.13.6-gke.13
    

Conocer los conceptos clave de Kubernetes

En el siguiente diagrama se muestra la aplicación que se ejecuta en GKE:

aplicación en contenedor

Antes de desplegar los contenedores en GKE, te recomendamos que repases algunos conceptos clave de Kubernetes. Al final de este tutorial, se incluyen enlaces para que puedas obtener más información sobre cada concepto.

  • Nodos y clústeres: en GKE, un nodo es una VM. En otras plataformas de Kubernetes, un nodo puede ser una máquina física o virtual. Un clúster es un conjunto de nodos que se pueden tratar como una sola máquina en la que se despliega una aplicación en contenedores.

  • Pods: en Kubernetes, los contenedores se ejecutan en un pod. Un pod es la unidad atómica de Kubernetes. Un pod contiene uno o varios contenedores. Despliega los contenedores server y loadgen en sus respectivos pods. Cuando un pod ejecuta varios contenedores (por ejemplo, un servidor de aplicaciones y un servidor proxy), los contenedores se gestionan como una sola entidad y comparten los recursos del pod.

  • Despliegues: un despliegue es un objeto de Kubernetes que representa un conjunto de pods idénticos. Un Deployment ejecuta varias réplicas de los pods distribuidas entre los nodos de un clúster. Un Deployment sustituye automáticamente cualquier Pod que falle o no responda.

  • Servicio de Kubernetes: al ejecutar el código de la aplicación en GKE, cambia la red entre el loadgen y el server. Cuando ejecutaste los servicios en una VM de Cloud Shell, pudiste enviar solicitudes a server mediante la dirección localhost:8080. Después de desplegar en GKE, los pods se programan para que se ejecuten en los nodos disponibles. De forma predeterminada, no puedes controlar en qué nodo se ejecuta el pod, por lo que los pods no tienen direcciones IP estables.

    Para obtener una dirección IP de server, debes definir una abstracción de red sobre los pods llamada servicio de Kubernetes. Un servicio de Kubernetes proporciona un endpoint de red estable para un conjunto de pods. Hay varios tipos de Servicios. El server usa un LoadBalancer, que expone una dirección IP externa para que puedas acceder al server desde fuera del clúster.

    Kubernetes también tiene un sistema DNS integrado que asigna nombres DNS (por ejemplo, helloserver.default.cluster.local) a los servicios. De esta forma, los pods del clúster pueden acceder a otros pods del clúster con una dirección estable. No puedes usar este nombre de DNS fuera del clúster, por ejemplo, desde Cloud Shell.

Manifiestos de Kubernetes

Cuando ejecutaste la aplicación desde el código fuente, usaste un comando imperativo: python3 server.py

Imperativo significa que se basa en verbos: "haz esto".

Por el contrario, Kubernetes funciona con un modelo declarativo. Esto significa que, en lugar de decirle a Kubernetes exactamente qué debe hacer, le proporcionas el estado deseado. Por ejemplo, Kubernetes inicia y finaliza pods según sea necesario para que el estado real del sistema coincida con el estado deseado.

Especificas el estado deseado en un conjunto de manifiestos o archivos YAML. Un archivo YAML contiene la especificación de uno o varios objetos de Kubernetes.

El ejemplo contiene un archivo YAML para server y loadgen. Cada archivo YAML especifica el estado deseado del objeto Deployment y del servicio de Kubernetes.

Servidor

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloserver
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloserver
  template:
    metadata:
      labels:
        app: helloserver
    spec:
      containers:
      - image: gcr.io/google-samples/istio/helloserver:v0.0.1
        imagePullPolicy: Always
        name: main
      restartPolicy: Always
      terminationGracePeriodSeconds: 5
  • kind indica el tipo de objeto.
  • metadata.name especifica el nombre de la implementación.
  • El primer campo spec contiene una descripción del estado deseado.
  • spec.replicas especifica el número de pods que se quieren.
  • La sección spec.template define una plantilla de pod. En la especificación de los pods se incluye el campo image, que es el nombre de la imagen que se va a extraer de Container Registry.

El Servicio se define de la siguiente manera:

apiVersion: v1
kind: Service
metadata:
  name: hellosvc
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: helloserver
  type: LoadBalancer
  • LoadBalancer: los clientes envían solicitudes a la dirección IP de un balanceador de carga de red, que tiene una dirección IP estable y se puede acceder a él desde fuera del clúster.
  • targetPort: Recuerda que el comando EXPOSE 8080 de Dockerfile no expone ningún puerto. Expón el puerto 8080 para poder acceder al contenedor server fuera del clúster. En este caso, hellosvc.default.cluster.local:80 (nombre abreviado: hellosvc) se asigna al puerto 8080 de la IP del pod helloserver.
  • port: es el número de puerto que usan otros servicios del clúster al enviar solicitudes.

Generador de carga

El objeto Deployment de loadgen.yaml es similar a server.yaml. Una diferencia destacable es que el objeto Deployment contiene una sección llamada env. En esta sección se definen las variables de entorno que requiere loadgen, que ya configuraste cuando ejecutaste la aplicación desde la fuente.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: loadgenerator
spec:
  replicas: 1
  selector:
    matchLabels:
      app: loadgenerator
  template:
    metadata:
      labels:
        app: loadgenerator
    spec:
      containers:
      - env:
        - name: SERVER_ADDR
          value: http://hellosvc:80/
        - name: REQUESTS_PER_SECOND
          value: '10'
        image: gcr.io/google-samples/istio/loadgen:v0.0.1
        imagePullPolicy: Always
        name: main
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 300m
            memory: 256Mi
      restartPolicy: Always
      terminationGracePeriodSeconds: 5

Como loadgen no acepta solicitudes entrantes, el campo type se ha definido como ClusterIP. Este tipo proporciona una dirección IP estable que pueden usar los servicios del clúster, pero la dirección IP no se expone a los clientes externos.

apiVersion: v1
kind: Service
metadata:
  name: loadgensvc
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: loadgenerator
  type: ClusterIP

Desplegar los contenedores en GKE

  1. Cambia al directorio donde se encuentra el ejemplo server:

    cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/server/
  2. Abre server.yaml en un editor de texto.

  3. Sustituye el nombre del campo image por el nombre de tu imagen de Docker.

    image: gcr.io/PROJECT_ID/asm-ready/helloserver:v0.0.1
    

    Sustituye PROJECT_ID por el ID de tu proyecto. Google Cloud

  4. Guarda y cierra server.yaml.

  5. Despliega el archivo YAML en Kubernetes:

    kubectl apply -f server.yaml
    

    Si la acción se realiza correctamente, el comando mostrará lo siguiente:

    deployment.apps/helloserver created
    service/hellosvc created
    

  6. Cambia al directorio en el que se encuentra loadgen.

    cd ../loadgen
    
  7. Abre loadgen.yaml en un editor de texto.

  8. Sustituye el nombre del campo image por el nombre de tu imagen de Docker.

    image: gcr.io/PROJECT_ID/asm-ready/loadgen:v0.0.1
    

    Sustituye PROJECT_ID por el ID de tu proyecto. Google Cloud

  9. Guarda y cierra loadgen.yaml, y cierra el editor de texto.

  10. Despliega el archivo YAML en Kubernetes:

    kubectl apply -f loadgen.yaml
    

    Si la acción se realiza correctamente, el comando mostrará lo siguiente:

    deployment.apps/loadgenerator created
    service/loadgensvc created
    

  11. Comprueba el estado de los pods:

    kubectl get pods
    

    El comando responde con un estado similar al siguiente:

    NAME                             READY   STATUS    RESTARTS   AGE
    helloserver-69b9576d96-mwtcj     1/1     Running   0          58s
    loadgenerator-774dbc46fb-gpbrz   1/1     Running   0          57s
    
  12. Obtén los registros de la aplicación del pod loadgen. Sustituye POD_ID por el identificador de la salida anterior.

    kubectl logs loadgenerator-POD_ID
    
  13. Obtén las direcciones IP externas de hellosvc:

    kubectl get service
    

    La respuesta del comando es similar a la siguiente:

    NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
    hellosvc     LoadBalancer   10.81.15.158   192.0.2.1       80:31127/TCP   33m
    kubernetes   ClusterIP      10.81.0.1      <none>          443/TCP        93m
    loadgensvc   ClusterIP      10.81.15.155   <none>          80/TCP         4m52s
    
  14. Envía una solicitud al hellosvc. Sustituye EXTERNAL_IP por la dirección IP externa de tu hellosvc.

    curl http://EXTERNAL_IP
    

Preparado para Cloud Service Mesh

Ahora la aplicación se ha desplegado en GKE. El loadgen puede usar el DNS de Kubernetes (hellosvc:80) para enviar solicitudes al server, y puedes enviar solicitudes al server con una dirección IP externa. Aunque Kubernetes te ofrece muchas funciones, falta información sobre los servicios:

  • ¿Cómo interactúan los servicios? ¿Qué relación hay entre los servicios? ¿Cómo fluye el tráfico entre los servicios? Sabes que el loadgen envía solicitudes al server, pero imagina que no conoces la aplicación. No puedes responder a estas preguntas consultando la lista de pods en ejecución en GKE.
  • Métricas: ¿cuánto tiempo tarda server en responder a las solicitudes entrantes? ¿Cuántas solicitudes por segundo (SPS) se reciben en server? ¿Hay alguna respuesta de error?
  • Información de seguridad: ¿el tráfico entre loadgen y server es sin cifrar HTTP o mTLS?

Cloud Service Mesh puede responder a estas preguntas. Cloud Service Mesh es una versión gestionada por Google Clouddel proyecto de código abierto Istio. Cloud Service Mesh funciona colocando un proxy adicional Envoy en cada pod. El proxy de Envoy intercepta todo el tráfico entrante y saliente de los contenedores de la aplicación. Esto significa que server y loadgen tienen un proxy de sidecar de Envoy, y todo el tráfico de loadgen a server está mediado por los proxies de Envoy. Las conexiones entre estos proxies de Envoy forman la malla de servicios. Esta arquitectura de malla de servicios proporciona una capa de control sobre Kubernetes.

malla de servicios

Como los proxies de Envoy se ejecutan en sus propios contenedores, puedes instalar Cloud Service Mesh en un clúster de GKE sin tener que hacer cambios sustanciales en el código de tu aplicación. Sin embargo, hay algunas formas clave en las que has preparado la aplicación para instrumentarla con Cloud Service Mesh:

  • Servicios de todos los contenedores: tanto la implementación server como la loadgen tienen un servicio de Kubernetes asociado. Incluso loadgen, que no recibe ninguna solicitud entrante, tiene un servicio.
  • Los puertos de los servicios deben tener un nombre: aunque GKE te permite definir puertos de servicio sin nombre, Cloud Service Mesh requiere que proporciones un nombre para un puerto que coincida con el protocolo del puerto. En el archivo YAML, el puerto de server se llama http porque server usa el protocolo de comunicación HTTP. Si el service usado es gRPC, el puerto se llamará grpc.
  • Los despliegues están etiquetados: esto te permite usar las funciones de gestión del tráfico de Cloud Service Mesh, como dividir el tráfico entre versiones del mismo servicio.

Instalar Cloud Service Mesh

Consulta la guía de instalación de Cloud Service Mesh y sigue las instrucciones para instalar Cloud Service Mesh en tu clúster.

Limpieza

Para evitar que los recursos utilizados en este tutorial se cobren en tu cuenta de Google Cloud, elimina el proyecto que contiene los recursos o conserva el proyecto y elimina los recursos.

Para limpiar, elimina el clúster de GKE. Al eliminar el clúster, se eliminan todos los recursos que lo componen, como las instancias de proceso, los discos y los recursos de red.

gcloud container clusters delete asm-ready

Siguientes pasos