Edit This Page

Escribiendo Logs con Stackdriver

Antes de seguir leyendo esta página, deberías familiarizarte con el resumen de escritura de logs en Kubernetes.

Nota: Por defecto, Stackdriver recolecta toda la salida estándar de tus contenedores, así como el flujo de la salida de error. Para recolectar cualquier log tu aplicación escribe en un archivo (por ejemplo), ver la estrategia de sidecar en el resumen de escritura de logs en Kubernetes.

Despliegue

Para ingerir logs, debes desplegar el agente de Stackdriver Logging en cada uno de los nodos de tu clúster. Dicho agente configura una instancia de fluentd, donde la configuración se guarda en un ConfigMap y las instancias se gestionan a través de un DaemonSet de Kubernetes. El despliegue actual del ConfigMap y el DaemonSet dentro de tu clúster depende de tu configuración individual del clúster.

Desplegar en un nuevo clúster

Google Kubernetes Engine

Stackdriver es la solución por defecto de escritura de logs para aquellos clústeres desplegados en Google Kubernetes Engine. Stackdriver Logging se despliega por defecto en cada clúster a no ser que se le indique de forma explícita no hacerlo.

Otras plataformas

Para desplegar Stackdriver Logging en un nuevo clúster que estés creando con kube-up.sh, haz lo siguiente:

  1. Configura la variable de entorno KUBE_LOGGING_DESTINATION con el valor gcp.
  2. Si no estás trabajando en GCE, incluye beta.kubernetes.io/fluentd-ds-ready=true en la variable KUBE_NODE_LABELS.

Una vez que tu clúster ha arrancado, cada nodo debería ejecutar un agente de Stackdriver Logging. Los DaemonSet y ConfigMap se configuran como extras. Si no estás usando kube-up.sh, considera la posibilidad de arrancar un clúster sin una solución pre-determinada de escritura de logs y entonces desplegar los agentes de Stackdriver Logging una vez el clúster esté ejecutándose.

Advertencia: El proceso de Stackdriver Logging reporta problemas conocidos en plataformas distintas a Google Kubernetes Engine. Úsalo bajo tu propio riesgo.

Desplegar a un clúster existente

  1. Aplica una etiqueta en cada nodo, si no estaba presente ya.

    El despliegue del agente de Stackdriver Logging utiliza etiquetas de nodo para determinar en qué nodos debería desplegarse. Estas etiquetas fueron introducidas para distinguir entre nodos de Kubernetes de la versión 1.6 o superior. Si el clúster se creó con Stackdriver Logging configurado y el nodo tiene la versión 1.5.X o inferior, ejecutará fluentd como un pod estático. Puesto que un nodo no puede tener más de una instancia de fluentd, aplica únicamente las etiquetas a los nodos que no tienen un pod de fluentd ya desplegado. Puedes confirmar si tu nodo ha sido etiquetado correctamente ejecutando kubectl describe de la siguiente manera:

    kubectl describe node $NODE_NAME
    

    La salida debería ser similar a la siguiente:

    Name:           NODE_NAME
    Role:
    Labels:         beta.kubernetes.io/fluentd-ds-ready=true
    ...
    

    Asegúrate que la salida contiene la etiqueta beta.kubernetes.io/fluentd-ds-ready=true. Si no está presente, puedes añadirla usando el comando kubectl label como se indica:

    kubectl label node $NODE_NAME beta.kubernetes.io/fluentd-ds-ready=true
    
    Nota: Si un nodo falla y tiene que volver a crearse, deberás volver a definir la etiqueta al nuevo nodo. Para facilitar esta tarea, puedes utilizar el parámetro de línea de comandos del Kubelet para aplicar dichas etiquetas cada vez que se arranque un nodo.
  2. Despliega un ConfigMap con la configuración del agente de escritura de logs ejecutando el siguiente comando:

    kubectl apply -f https://k8s.io/examples/debug/fluentd-gcp-configmap.yaml
    

    Este comando crea el ConfigMap en el espacio de nombres default. Puedes descargar el archivo manualmente y cambiarlo antes de crear el objeto ConfigMap.

  3. Despliega el agente DaemonSet de escritura de logs ejecutando el siguiente comando:

    kubectl apply -f https://k8s.io/examples/debug/fluentd-gcp-ds.yaml
    

    Puedes descargar y editar este archivo antes de usarlo igualmente.

Verificar el despliegue de tu agente de escritura de logs

Tras el despliegue del DaemonSet de StackDriver, puedes comprobar el estado de cada uno de los despliegues de los agentes ejecutando el siguiente comando:

kubectl get ds --all-namespaces

Si tienes 3 nodos en el clúster, la salida debería ser similar a esta:

NAMESPACE     NAME               DESIRED   CURRENT   READY     NODE-SELECTOR                              AGE
...
default       fluentd-gcp-v2.0   3         3         3         beta.kubernetes.io/fluentd-ds-ready=true   5m
...

Para comprender cómo funciona Stackdriver, considera la siguiente especificación de un generador de logs sintéticos counter-pod.yaml:

debug/counter-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

Esta especificación de pod tiene un contenedor que ejecuta una secuencia de comandos bash que escribe el valor de un contador y la fecha y hora cada segundo, de forma indefinida. Vamos a crear este pod en el espacio de nombres por defecto.

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

Puedes observar el pod corriendo:

kubectl get pods
NAME                                           READY     STATUS    RESTARTS   AGE
counter                                        1/1       Running   0          5m

Durante un período de tiempo corto puedes observar que el estado del pod es 'Pending', debido a que el kubelet tiene primero que descargar la imagen del contenedor. Cuando el estado del pod cambia a Running puedes usar el comando kubectl logs para ver la salida de este pod contador.

kubectl logs counter
0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...

Como se describe en el resumen de escritura de logs, este comando visualiza las entradas de logs del archivo de logs del contenedor. Si se termina el contenedor y Kubernetes lo reinicia, todavía puedes acceder a los logs de la ejecución previa del contenedor. Sin embargo, si el pod se desaloja del nodo, los archivos de log se pierden. Vamos a demostrar este comportamiento mediante el borrado del contenedor que ejecuta nuestro contador:

kubectl delete pod counter
pod "counter" deleted

y su posterior re-creación:

kubectl create -f https://k8s.io/examples/debug/counter-pod.yaml
pod/counter created

Tras un tiempo, puedes acceder a los logs del pod contador otra vez:

kubectl logs counter
0: Mon Jan  1 00:01:00 UTC 2001
1: Mon Jan  1 00:01:01 UTC 2001
2: Mon Jan  1 00:01:02 UTC 2001
...

Como era de esperar, únicamente se visualizan las líneas de log recientes. Sin embargo, para una aplicación real seguramente prefieras acceder a los logs de todos los contenedores, especialmente cuando te haga falta depurar problemas. Aquí es donde haber habilitado Stackdriver Logging puede ayudarte.

Ver logs

El agente de Stackdriver Logging asocia metadatos a cada entrada de log, para que puedas usarlos posteriormente en consultas para seleccionar sólo los mensajes que te interesan: por ejemplo, los mensajes de un pod en particular.

Los metadatos más importantes son el tipo de recurso y el nombre del log. El tipo de recurso de un log de contenedor tiene el valor container, que se muestra como GKE Containers en la UI (incluso si el clúster de Kubernetes no está en Google Kubernetes Engine). El nombre de log es el nombre del contenedor, de forma que si tienes un pod con dos contenedores, denominados container_1 y container_2 en la especificación, sus logs tendrán los nombres container_1 y container_2 respectivamente.

Los componentes del sistema tienen el valor compute como tipo de recursos, que se muestra como GCE VM Instance en la UI. Los nombres de log para los componentes del sistema son fijos. Para un nodo de Google Kubernetes Engine, cada entrada de log de cada componente de sistema tiene uno de los siguientes nombres:

  • docker
  • kubelet
  • kube-proxy

Puedes aprender más acerca de cómo visualizar los logs en la página dedicada a Stackdriver.

Uno de los posibles modos de ver los logs es usando el comando de línea de interfaz gcloud logging del SDK de Google Cloud. Este comando usa la sintaxis de filtrado de StackDriver Logging para consultar logs específicos. Por ejemplo, puedes ejecutar el siguiente comando:

gcloud beta logging read 'logName="projects/$YOUR_PROJECT_ID/logs/count"' --format json | jq '.[].textPayload'
...
"2: Mon Jan  1 00:01:02 UTC 2001\n"
"1: Mon Jan  1 00:01:01 UTC 2001\n"
"0: Mon Jan  1 00:01:00 UTC 2001\n"
...
"2: Mon Jan  1 00:00:02 UTC 2001\n"
"1: Mon Jan  1 00:00:01 UTC 2001\n"
"0: Mon Jan  1 00:00:00 UTC 2001\n"

Como puedes observar, muestra los mensajes del contenedor contador tanto de la primera como de la segunda ejecución, a pesar de que el kubelet ya había eliminado los logs del primer contenedor.

Exportar logs

Puedes exportar los logs al Google Cloud Storage o a BigQuery para llevar a cabo un análisis más profundo. Stackdriver Logging ofrece el concepto de destinos, donde puedes especificar el destino de las entradas de logs. Más información disponible en la página de exportación de logs de StackDriver.

Configurar los agentes de Stackdriver Logging

En ocasiones la instalación por defecto de Stackdriver Logging puede que no se ajuste a tus necesidades, por ejemplo:

  • Puede que quieras añadir más recursos porque el rendimiento por defecto no encaja con tus necesidades.
  • Puede que quieras añadir un parseo adicional para extraer más metadatos de tus mensajes de log, como la severidad o referencias al código fuente.
  • Puede que quieras enviar los logs no sólo a Stackdriver o sólo enviarlos a Stackdriver parcialmente.

En cualquiera de estos casos, necesitas poder cambiar los parámetros del DaemonSet y el ConfigMap.

Prerequisitos

Si estás usando GKE y Stackdriver Logging está habilitado en tu clúster, no puedes cambiar su configuración, porque ya está gestionada por GKE. Sin embargo, puedes deshabilitar la integración por defecto y desplegar la tuya propia.

Nota: Tendrás que mantener y dar soporte tú mismo a la nueva configuración desplegada: actualizar la imagen y la configuración, ajustar los recuros y todo eso.

Para deshabilitar la integración por defecto, usa el siguiente comando:

gcloud beta container clusters update --logging-service=none CLUSTER

Puedes encontrar notas acerca de cómo instalar los agentes de Stackdriver Logging en un clúster ya ejecutándose en la sección de despliegue.

Cambiar los parámetros del DaemonSet

Cuando tienes un DaemonSet de Stackdriver Logging en tu clúster, puedes simplemente modificar el campo template en su especificación, y el controlador del daemonset actualizará los pods por ti. Por ejemplo, asumamos que acabas de instalar el Stackdriver Logging como se describe arriba. Ahora quieres cambiar el límite de memoria que se le asigna a fluentd para poder procesar más logs de forma segura.

Obtén la especificación del DaemonSet que corre en tu clúster:

kubectl get ds fluentd-gcp-v2.0 --namespace kube-system -o yaml > fluentd-gcp-ds.yaml

A continuación, edita los requisitos del recurso en el spec y actualiza el objeto DaemonSet en el apiserver usando el siguiente comando:

kubectl replace -f fluentd-gcp-ds.yaml

Tras un tiempo, los pods de agente de Stackdriver Logging se reiniciarán con la nueva configuración.

Cambiar los parámetros de fluentd

La configuración de Fluentd se almacena en un objeto ConfigMap. Realmente se trata de un conjunto de archivos de configuración que se combinan conjuntamente. Puedes aprender acerca de la configuración de fluentd en el sitio oficial.

Imagina que quieres añadir una nueva lógica de parseo a la configuración actual, de forma que fluentd pueda entender el formato de logs por defecto de Python. Un filtro apropiado de fluentd para conseguirlo sería:

<filter reform.**>
  type parser
  format /^(?<severity>\w):(?<logger_name>\w):(?<log>.*)/
  reserve_data true
  suppress_parse_error_log true
  key_name log
</filter>

Ahora tienes que añadirlo a la configuración actual y que los agentes de Stackdriver Logging la usen. Para ello, obtén la versión actual del ConfigMap de Stackdriver Logging de tu clúster ejecutando el siguiente comando:

kubectl get cm fluentd-gcp-config --namespace kube-system -o yaml > fluentd-gcp-configmap.yaml

Luego, como valor de la clave containers.input.conf, inserta un nuevo filtro justo después de la sección source.

Nota: El orden es importante.

Actualizar el ConfigMap en el apiserver es más complicado que actualizar el DaemonSet. Es mejor considerar que un ConfigMap es inmutable. Así, para poder actualizar la configuración, deberías crear un nuevo ConfigMap con otro nombre y cambiar el DaemonSet para que apunte al nuevo siguiendo la guía de arriba.

Añadir plugins de fluentd

Fluentd está desarrollado en Ruby y permite extender sus capacidades mediante el uso de plugins. Si quieres usar un plugin que no está incluido en la imagen por defecto del contenedor de Stackdriver Logging, debes construir tu propia imagen. Imagina que quieres añadir un destino Kafka para aquellos mensajes de un contenedor en particular para poder procesarlos posteriormente. Puedes reusar los fuentes de imagen de contenedor con algunos pequeños cambios:

  • Cambia el archivo Makefile para que apunte a tu repositorio de contenedores, ej. PREFIX=gcr.io/<your-project-id>.
  • Añade tu dependencia al archivo Gemfile, por ejemplo gem 'fluent-plugin-kafka'.

Luego, ejecuta make build push desde ese directorio. Cuando el DaemonSet haya tomado los cambios de la nueva imagen, podrás usar el plugin que has indicado en la configuración de fluentd.