Skip to main content

Cómo y cuándo usar etiquetas de Docker / anotaciones de contenedores OCI

Escrito por
blog feature docker labels

3 de noviembre de 2021

0 minutos de lectura

La mayoría de las imágenes de contenedor se crean con Dockerfiles que combinan instrucciones como FROM, RUN, COPY, ENTRYPOINT, entre otras, para crear las capas de una imagen compatible con OCI. Sin embargo, hay una instrucción que se usa sorprendentemente poco: LABEL. En esta publicación, analizaremos las etiquetas («anotaciones» en la especificación de imágenes OCI): qué son, algunos usos estandarizados y prácticas que puedes aplicar para mejorar la seguridad de tus contenedores.

En el resto de este artículo, las llamaremos etiquetas —en lugar de anotaciones—, ya que es el término más usado. ¡Comencemos!

¿Qué son las etiquetas de imagen de Docker?

Las etiquetas de imagen de Docker te permiten agregar metadatos de clave-valor a la imagen. Estos datos no se exponen a los contenedores que se ejecutan a partir de ella, pero son útiles para registrar, por ejemplo, dónde está el código fuente de la imagen, quién brinda soporte o qué compilación de CI la creó.

Explicación de los metadatos de imágenes de Docker / OCI

Si alguna vez creaste un paquete de software, sabes que, por lo general, incluye el software, la configuración y, a veces, datos funcionales, además de metadatos sobre el propio paquete.

Por ejemplo, aunque los archivos Java .jar son básicamente archivos .zip, todos tienen un directorio META-INF de nivel superior que contiene varios archivos y directorios que, según la especificación de la plataforma Java 2, «... la plataforma Java 2 reconoce e interpreta para configurar aplicaciones, extensiones, cargadores de clases y servicios». Si abrimos un archivo .jar creado con la popular herramienta de compilación Maven, normalmente encontraremos, entre otras cosas, un directorio maven con contenido como el pom.xml efectivo de Maven y el pom.properties que se usaron para crear el archivo .jar. (Por si te lo preguntas, “POM” significa Project Object Model [modelo de objetos del proyecto] de Maven).

RPM, APT, NPM y la mayoría de las demás herramientas de empaquetado almacenan metadatos similares. Las herramientas los usan para instalar o ejecutar el software incluido, o bien con fines prácticos en repositorios o sistemas de monitoreo en tiempo de ejecución.

Captura de pantalla de la terminal con el historial de una imagen de Docker que muestra comandos, punto de entrada, usuario, variable de entorno, argumentos, etiquetas y tamaños de imagen.

Las imágenes de contenedor también almacenan metadatos en sus capas. Si consultas el «historial» de una imagen, a menudo verás capas de cero bytes, porque no contienen cambios en el sistema de archivos, sino metadatos que se usan en tiempo de ejecución y que suelen agregarse mediante comandos de Dockerfile:

  • USER: usuario con el que se ejecutará el proceso

  • ENV: variable (y su valor) que se establecerá en el entorno de los procesos

  • ARG: argumento de compilación que se pasa al contenedor de compilación y se usa como una variable de entorno durante la compilación

  • CMD: comando o parámetros para iniciar el proceso (ENTRYPOINT es similar)

  • LABEL: pares de clave-valor que el motor de ejecución no usa

La mayoría de estos elementos son conocidos y se usan en todos los Dockerfiles, pero las etiquetas suelen pasarse por alto porque sus metadatos no son necesarios para ejecutar los contenedores.

Por qué deberías usar etiquetas en las imágenes de contenedor

Hay muchos motivos para usar etiquetas en tus imágenes, como documentar versiones, incluir los datos de contacto de quienes mantienen el proyecto o incluso información sobre el uso en tiempo de ejecución. Uno de los casos de uso más comunes es documentar cómo se creó la imagen, información que puede servir en la cadena de suministro de software del artefacto de imagen.

Tipos de metadatos de etiquetas de Docker / anotaciones de imágenes OCI

Etiquetas estandarizadas

Los metadatos sobre la creación y el origen de las imágenes se usan con tanta frecuencia que el equipo de OCI publica un conjunto estandarizado de claves, todas con el prefijo «org.opencontainers.image.», entre ellas:

  • source: URL para obtener el código fuente con el que se crea la imagen

  • revision: identificador de la revisión del control de versiones del software empaquetado

  • base.digest: resumen (hash) de la imagen en la que se basa esta imagen

  • base.name: referencia de la imagen en la que se basa esta imagen

  • version: versión del software empaquetado

Etiquetas personalizadas

Como son simplemente pares de clave-valor, tu proyecto u organización puede especificar prácticamente cualquier dato. Algunas ideas (todas podrían llevar un prefijo como «com.mycorp.myteam.»):

  • ci-build: URL de la ejecución del proyecto de CI que creó la imagen

  • releasenotes: notas de la versión del software empaquetado

  • healthz: endpoint HTTP para realizar comprobaciones de estado

  • docker.run: ejemplo de un comando de Docker para ejecutar esta imagen

  • k8s.deployment: YAML en Base64 para una implementación de Kubernetes que use esta imagen

Este último ejemplo es interesante porque, sí, puedes almacenar como valor de una clave de etiqueta prácticamente cualquier dato que puedas codificar en Base64. Esto significa que puedes descargar esa imagen y ejecutar algo como lo siguiente para obtener un archivo YAML de ejemplo para la implementación, que luego podrías usar en un clúster de Kubernetes:

$ docker image inspect myimage:tag | jq -r ".[].Config.Labels.\"com.mycorp.myteam.k8s.deployment\"" | base64 -d
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: snyk
  name: snyk
spec:
  replicas: 1
  selector:
    matchLabels:
      app: snyk
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: snyk
    spec:
      containers:
      - image: ericsmalling/snyklabeldemo:m
        name: snyklabeldemo
        resources: {}
status: {}

Incluso podrías canalizarlo directamente a kubectl e implementarlo de inmediato, sin necesidad de gráficos de Helm ni archivos YAML separados en tu repositorio de Git.

docker image inspect myimage:tag | jq -r ".[].Config.Labels.\"com.mycorp.myteam.k8s.deployment\"" | base64 -d | kubectl apply -f - 

deployment.apps/snyk created

Cómo aprovechar las etiquetas de Docker / anotaciones OCI

Como puedes imaginar, las etiquetas pueden incluir cualquier valor al que tenga acceso tu sistema de CI y servir para vincular imágenes o contenedores en ejecución con su código fuente, documentación y más, con solo inspeccionarlas. Por ejemplo, supongamos que tu organización publica imágenes con la etiqueta estándar de OCI org.opencontainers.image.source, que incluye la URL del repositorio de control de código fuente del que proviene la imagen. Si quisieras averiguar qué imágenes de repositorios se están ejecutando en un host de Docker determinado, podrías ejecutar algo como esto:

$ docker inspect $(docker ps -q) --format='{{ .Id }} {{ index .Config.Labels "org.opencontainers.image.source" }}'

17f4ee967870c49d3ffeb1c49973071c99c63377b2f9bbf987f7c3e4a21d331c https://repo.mycorp.com/team-volton/redlion
c958ffc87c2bd5af500d24eff1ccb3ee21992a5cbcb429fafcc651aa182b66ba <no value>

El resultado muestra los ID de dos contenedores en ejecución. Uno tiene la etiqueta, por lo que también se muestra su valor.

La situación se complica un poco en un clúster de Kubernetes, donde probablemente no tendrás acceso a los sockets del motor de contenedores en los nodos del clúster. Lamentablemente, no hay una API para obtener estas etiquetas con kubectl, así que tendremos que buscar una alternativa. El siguiente script de Bash encuentra las imágenes que se ejecutan en el espacio de nombres del contexto actual y luego consulta el registro de imágenes para obtener sus metadatos y mostrar la información de las etiquetas:

$ cat labelgrep.sh
#!/bin/bash
FINDLABEL=$1
FINDVAL=$2

IMAGES=$(kubectl get pods -o json | jq -r ".items[].spec.containers[].image" | uniq)

for i in $IMAGES; do
	VAL=$(regctl image inspect ${i} --format '{{ index .Config.Labels "'${FINDLABEL}'" }}')
	if [[ "$VAL" != "" && ( "$FINDVAL" == "" || "$VAL" == "$FINDVAL") ]]; then
	  echo "[${i}] ${FINDLABEL}=${VAL}"
  fi
done

Ten en cuenta que uso la excelente herramienta regctl del proyecto de código abierto regclient, que permite obtener información sobre imágenes directamente desde un registro, sin tener que descargar una imagen a mi entorno local para inspeccionarla. Esto también me permite ejecutar el script donde quiera, sin necesidad de tener un motor de ejecución de contenedores.

Ahora, ejecutemos este comando en un clúster y busquemos los repositorios de las imágenes que están en ejecución:

$ ./labelgrep.sh org.opencontainers.image.source
[images.mycorp.com/voltron/redlion] org.opencontainers.image.source=https://repo.mycorp.com/team-volton/redlion
[images.mycorp.com/voltron/bluelion] org.opencontainers.image.source=https://repo.mycorp.com/team-volton/bluelion

Como puedes ver, actualmente hay dos imágenes en mi clúster que se ejecutan en pods y tienen la etiqueta org.opencontainers.image.source.

Aunque estos ejemplos son relativamente sencillos, seguro que puedes ampliar estos conceptos para crear tus propios scripts o llamadas a la API según las necesidades de tu organización.

Integración con Snyk

Uno de los aspectos más interesantes desde el punto de vista de la seguridad es que el escaneo de imágenes de Snyk ahora permite vincular automáticamente la imagen escaneada con su Dockerfile mediante las etiquetas de imagen.

La integración del repositorio de código fuente de Snyk ya puede detectar y escanear estáticamente los Dockerfiles que encuentra en tu código, así como las imágenes importadas desde tus registros de contenedores. Sin embargo, hasta hace poco, tenías que gestionar por tu cuenta la correlación entre ambos. Debías agregar manualmente la referencia al Dockerfile en la imagen o implementar tu propia automatización mediante llamadas a la API.

Ahora solo tienes que incluir la etiqueta estándar de OCI org.opencontainers.image.source con la URL del repositorio que contiene el Dockerfile. Cuando se importe la imagen, Snyk se encargará de vincularlos automáticamente.

Página de detalles de la imagen de Docker que muestra la imagen vinculada “docker-imageleric” resaltada con anotaciones rojas.

Ejemplo de un proyecto de escaneo de imágenes vinculado automáticamente

Para terminar

En resumen, las etiquetas y anotaciones de imagen, que a menudo se olvidan, son una herramienta eficaz para insertar metadatos directamente en tus imágenes. Usar claves estandarizadas puede ayudarte no solo a documentar el origen de una imagen, sino también a aprovechar herramientas de implementación y seguridad para comprender mejor el entorno que tienes implementado.

Puedes empezar a aprovechar hoy mismo la función de vinculación automática de imágenes de Snyk: solo agrega la etiqueta estándar de OCI org.opencontainers.image.source a los Dockerfiles que ya se están escaneando en tu cuenta. ¿Todavía no tienes una cuenta de Snyk? Regístrate gratis y empieza a usarla ahora.

Me interesa saber cómo usas las etiquetas. ¿Son ideas nuevas para ti o tus proyectos ya las usan? ¿Qué otras formas interesantes tienes de anotar tus imágenes? ¿Hay alguna integración nueva que te gustaría ver, con Snyk o con otras herramientas? Etiquétame (@ericsmalling) en Twitter; ¡me encantaría conocer tu opinión!

Seguridad de contenedores centrada en los desarrolladores

Snyk encuentra y corrige automáticamente vulnerabilidades en imágenes de contenedores y cargas de trabajo de Kubernetes.