Recomendaciones para compilar contenedores

En este artículo se describe un conjunto de recomendaciones para compilar contenedores. Estas recomendaciones cubren una amplia gama de objetivos, que van desde reducir el tiempo de compilación hasta crear imágenes más pequeñas y resilientes, a fin de hacer que la compilación de contenedores sea más sencilla (por ejemplo, con Cloud Build), y que sean más fáciles de ejecutar en Google Kubernetes Engine (GKE).

Estas recomendaciones no son todas igual de importantes. Por ejemplo, podrás ejecutar una carga de trabajo de producción de manera correcta aunque no sigas algunas de ellas, pero otras son indispensables. En particular, la importancia de las recomendaciones sobre seguridad es subjetiva. Su puesta en práctica dependerá de tu entorno y de tus restricciones.

Para sacarle el máximo provecho a este artículo, necesitas tener algo de conocimiento sobre Docker y Kubernetes. Ciertas recomendaciones que se tratan aquí también se aplican a los contenedores de Windows, pero la mayoría supone que trabajas con contenedores de Linux. Puedes encontrar asesoramiento sobre la ejecución y el manejo de contenedores en Recomendaciones para trabajar con contenedores.

Empaqueta una sola aplicación por contenedor

Importancia: ALTA

Cuando comienzas a trabajar con contenedores, un error común es tratarlos como máquinas virtuales que pueden ejecutar diferentes elementos de manera simultánea. Un contenedor puede trabajar de esta manera, pero, si lo hace, reduce la mayoría de las ventajas del modelo. Por ejemplo, pensemos en una pila Apache/MySQL/PHP clásica: podrías verte tentado de ejecutar todos los componentes en un solo contenedor. Sin embargo, la recomendación es que utilices dos o tres contenedores diferentes: unos para Apache, uno para MySQL y posiblemente uno para PHP si estás ejecutando PHP-FPM.

Debido a que un contenedor está diseñado para tener el mismo ciclo de vida que la aplicación que aloja, cada uno de tus contenedores debería contener solamente una aplicación. Cuando se inicia un contenedor, también debería iniciarse la aplicación; cuando la aplicación se detiene, también debería hacerlo el contenedor. En el diagrama a continuación se muestra esta recomendación.

Un diagrama que muestra el proceso de inicio sin una imagen personalizada.

Figura 1. El contenedor de la izquierda se adhiere a la recomendación. El de la derecha, no.

Si tienes varias aplicaciones en un contenedor, podrían tener ciclos de vida diferentes, o estar en estados distintos. Por ejemplo, podrías tener un contenedor que se está ejecutando, pero que tenga un componente principal con fallas o que no responda. Sin una verificación de estado adicional, el sistema de administración general del contenedor (Docker o Kubernetes) no puede diagnosticar si el contenedor está en buen estado. En el caso de Kubernetes, esto significa que tu contenedor no se reiniciará de forma predeterminada si es necesario.

Es posible que veas las siguientes acciones en imágenes públicas, pero no sigas su ejemplo:

  • Utilizar un sistema de administración de procesos como supervisord para administrar una o más aplicaciones en el contenedor.
  • Utilizar una secuencia de comandos de Bash como punto de entrada en el contenedor, y generar diferentes aplicaciones como trabajos en segundo plano. A fin de conocer el uso correcto de la secuencia de comandos de Bash en contenedores, consulta Manejo correcto de PID 1, manejo de señales y procesos zombie.

Manejo correcto de PID 1, manejo de señales y procesos zombie

Importancia: ALTA

Las señales de Linux son la forma principal de controlar el ciclo de vida de procesos dentro del contenedor. En línea con la recomendación anterior, a fin de vincular el ciclo de vida de tu aplicación con el contenedor en el que se encuentra, asegúrate de que maneje correctamente las señales de Linux. La señal más importante de Linux es SIGTERM porque termina un proceso. Es posible que tu aplicación también reciba una señal SIGKILL, que se utiliza para terminar el proceso de forma no ordenada, o una señal SIGINT que se envía cuando ingresas Ctrl+C y generalmente se la trata como a SIGTERM.

Los identificadores de procesos (PID) son identificadores únicos que el kernel de Linux le otorga a cada proceso. Los PID tienen espacio de nombres, lo cual significa que un contenedor tiene su propio conjunto de PID que están asignados en los PID del sistema de alojamiento. El primer proceso que se lanza cuando se inicia un kernel de Linux tiene el PID 1. Para un sistema operativo normal, este proceso es el sistema init, por ejemplo, systemd o SysV. Del mismo modo, el primer proceso que se lanza en un contenedor obtiene el PID 1. Docker y Kubernetes utilizan señales a fin de comunicarse con los procesos dentro de contenedores, en especial para terminarlos. Tanto Docker como Kubernetes solo pueden enviar señales al proceso que tenga PID 1 dentro de un contenedor.

En el contexto de los contenedores, los PID y las señales de Linux crean dos problemas que deben tenerse en cuenta.

Problema 1: Cómo maneja las señales el kernel de Linux

El kernel de Linux maneja las señales de forma diferente para el proceso que tiene PID 1 que para otros procesos. Los controladores de señales no se registran automáticamente para este proceso; esto quiere decir que las señales como SIGTERM o SIGINT no tendrán efectos predeterminados. De forma predeterminada, debes terminar procesos mediante SIGKILL, con lo que evitarás que se produzcan cierres ordenados. Dependiendo de tu aplicación, utilizar SIGKILL puede dar como resultado errores para los usuarios, escrituras interrumpidas (para almacenes de datos) o alertas no deseadas en tu sistema de supervisión.

Problema 2: Cómo manejan los sistemas init clásicos los procesos huérfanos

Los sistemas clásicos init como systemd también se utilizan para quitar (recolectar) procesos zombie huérfanos. Los procesos huérfanos, es decir, aquellos cuyos principales han finalizado, se vuelven a unir al proceso con PID 1, que deberá sacarlos cuando finalicen. Esta tarea la realiza un sistema init normal. Sin embargo, en un contenedor, esta responsabilidad recae en el proceso con PID 1. Si ese proceso no maneja correctamente la recolección, corres el riesgo de quedarte sin memoria o sin otros recursos.

Estos problemas tienen varias soluciones comunes que se detallan en la siguiente sección.

Solución 1: Ejecutar como PID 1 y registrar los controladores de señales

Esta solución solamente sirve para el primer problema. Es válida si tu aplicación genera procesos secundarios de forma controlada (lo que suele suceder) y se evita el segundo problema.

La forma más sencilla de implementar esta solución es iniciar tu proceso con las instrucciones CMD o ENTRYPOINT en tu Dockerfile. Por ejemplo, en el siguiente Dockerfile, nginx es el primer y único proceso que se iniciará.

FROM debian:9

RUN apt-get update && \
    apt-get install -y nginx

EXPOSE 80

CMD [ "nginx", "-g", "daemon off;" ]

A veces, deberás preparar el entorno en tu contenedor para que el proceso se ejecute correctamente. En este caso, la recomendación es hacer que el contenedor lance una secuencia de comandos shell durante el inicio. Esta secuencia de comandos shell tiene la tarea de preparar el entorno y lanzar el proceso principal. Sin embargo, si adoptas este enfoque, la secuencia de comandos shell tendrá PID 1, y no tu proceso, por lo cual debes utilizar el comando exec integrado a fin de iniciar el proceso desde la secuencia de comandos shell. El comando exec reemplaza la secuencia de comandos con el programa que deseas. Entonces, tu proceso hereda PID 1.

Solución 2: Utilizar un sistema init especializado

Como harías en un entorno Linux más clásico, también puedes utilizar un sistema init para abordar esos problemas. No obstante, los sistemas init normales como systemd o SysV son demasiado complejos y grandes para esta finalidad, por lo que te recomendamos usar un sistema init como tini creado especialmente para contenedores.

Si utilizas un sistema init especializado, el proceso de init tiene PID 1 y hace lo siguiente:

  • Registra los controladores de señal correctos.
  • Garantiza que las señales funcionen para tu aplicación.
  • Recoge cualquier proceso zombie eventual.

Puedes utilizar esta solución en Docker mediante la opción --init del comando docker run. Para utilizar esta solución en Kubernetes, debes instalar el sistema init en la imagen del contenedor y utilizarlo como punto de entrada del contenedor.

Optimizar para la caché de compilación de Docker

Importancia: ALTA

La caché de compilación de Docker puede acelerar ampliamente la compilación de imágenes de contenedor. Las imágenes se compilan capa por capa, y en un Dockerfile cada instrucción crea una capa en la imagen resultante. Durante la compilación, cuando sea posible, Docker reutiliza una capa de una compilación previa y omite un paso potencialmente costoso. Docker puede utilizar su caché de compilación únicamente si todos los pasos de compilación anteriores la utilizaron. Si bien este comportamiento generalmente es positivo, ya que agiliza las compilaciones, debes tener en cuenta algunas cuestiones.

Por ejemplo, para aprovechar al máximo la caché de compilación de Docker, debes colocar los pasos de compilación que cambian a menudo en la parte inferior del Dockerfile. Si los colocas en la parte superior, Docker no podrá utilizar su caché de compilación para los otros pasos de compilación que cambian con menos frecuencia. Debido a que generalmente se compila una nueva imagen de Docker para cada nueva versión de tu código fuente, agrega el código fuente a la imagen lo más tarde posible en el Dockerfile. En el siguiente diagrama, podrás ver que si modificas el STEP 1, Docker puede volver a utilizar solamente las capas del paso FROM debian:9. Sin embargo, si cambias STEP 3, Docker puede volver a utilizar las capas de STEP 1 y STEP 2.

Ejemplos de cómo utilizar la caché de compilación de Docker

Figura 2. Ejemplos de cómo utilizar la caché de compilación de Docker. Las capas que se pueden volver a utilizar figuran en verde. En rojo, las que deben volver a crearse.

La reutilización de capas tiene otra consecuencia: si un paso de compilación se basa en cualquier tipo de caché almacenada en el sistema de archivos local, esta debe generarse en el mismo paso de compilación. Si esta caché no se genera, tu paso de compilación podría ejecutarse con una caché desactualizada proveniente de una compilación anterior. Este comportamiento se ve generalmente con administradores de paquetes como apt o yum: debes actualizar tus repositorios en el mismo comando RUN que la instalación de tu paquete.

Si cambias el segundo paso RUN en el siguiente Dockerfile, el comando apt-get update no se vuelve a ejecutar, lo que te deja con una caché apt desactualizada.

FROM debian:9

RUN apt-get update
RUN apt-get install -y nginx

En cambio, combina dos comandos en un solo paso RUN:

FROM debian:9

RUN apt-get update && \
    apt-get install -y nginx

Quita herramientas innecesarias

Importancia: MEDIA

A fin de proteger tus aplicaciones de los atacantes, intenta reducir la superficie de ataque de tu aplicación; para ello, quita todas las herramientas innecesarias. Por ejemplo, quita herramientas como netcat, que puedes utilizar para crear un shell inverso dentro de tu sistema. Si netcat no está presente dentro del contenedor, el atacante deberá encontrar otra manera.

Esta recomendación se aplica para cualquier carga de trabajo, incluso si no está dentro de un contenedor. La diferencia es que es mucho más simple de implementar con contenedores que con las clásicas máquinas virtuales o los servidores de código abierto.

Algunas de estas herramientas pueden ser útiles para la depuración. Por ejemplo, si sigues esta recomendación al pie de la letra, los registros exhaustivos, el rastreo, la creación de perfiles y los sistemas de Administración del rendimiento de las aplicaciones se vuelven casi obligatorios. De hecho, ya no puedes confiar en las herramientas de depuración locales, ya que generalmente tienen muchos privilegios.

Contenido del sistema de archivos

La primera parte de esta recomendación trata sobre el contenido de la imagen del contenedor. Mantén la menor cantidad de elementos posibles en tu imagen. Si puedes compilar tu aplicación en un binario único enlazado estáticamente, agregar este binario a la imagen scratch te permite obtener una imagen final que contiene solo tu aplicación y nada más. Si reduces la cantidad de herramientas empaquetadas en tu imagen, reduces lo que un atacante potencial podría hacer en tu contenedor. Para obtener más información, consulta Compila la imagen más pequeña posible.

Seguridad del sistema de archivos

No tener herramientas en tu imagen no es suficiente: debes evitar que los posibles atacantes instalen sus propias herramientas. Puedes combinar los siguientes dos métodos:

  • Evita la ejecución sin permisos de administrador: este método ofrece una primera capa de seguridad y podría impedir, por ejemplo, que los atacantes modifiquen archivos propiedad del administrador que contengan un administrador del paquete integrado en tu imagen (como apt-get o apk). Para que este método funcione, debes inhabilitar o desinstalar el comando sudo. Este tema se trata en mayor profundidad en Evita la ejecución sin permisos de administrador.

  • Inicia el contenedor en modo de solo lectura, que se logra mediante el uso de la marca --read-only del comando docker run o mediante la opción readOnlyRootFilesystem en Kubernetes. Puedes hacer esto en Kubernetes mediante una PodSecurityPolicy.

Compila la imagen más pequeña posible

Importancia: MEDIA

Compilar una imagen pequeña ofrece ventajas como cargas y tiempos de descarga más veloces, lo cual es especialmente importante para el inicio en frío de un pod en Kubernetes: cuanto más pequeña sea la imagen, más rápido podrá descargarla el nodo. No obstante, compilar una imagen pequeña puede ser difícil debido a que es fácil incluir involuntariamente dependencias de compilación o capas no autorizadas en tu imagen final.

Usa la imagen base más pequeña posible

La imagen base es a la que se hace referencia en la instrucción FROM en tu Dockerfile. Todas las demás instrucciones en Dockerfile se basan en esta imagen. Cuanto más pequeña sea la imagen base, más pequeña será la imagen resultante, y más rápido se la podrá descargar. Por ejemplo, la imagen alpine:3.7 es 71 MB más pequeña que la imagen centos:7.

Incluso puedes usar la imagen base de scratch, que es una imagen vacía en la cual puedes compilar tu propio entorno de ejecución. Si tu aplicación es un binario enlazado estáticamente, es muy fácil utilizar la imagen base de scratch:

FROM scratch
COPY mybinary /mybinary
CMD [ "/mybinary" ]

El proyecto distroless te proporciona imágenes base mínimas para diferentes lenguajes. Las imágenes contienen solo dependencias del entorno de ejecución para el lenguaje, pero no incluyen muchas herramientas que podrías esperar en una distribución de Linux (shells, administradores de paquetes, entre otros).

Reduce la cantidad de desorden en tu imagen

A fin de reducir el tamaño de tu imagen, instala solamente lo que se necesite realmente dentro de ella. Puede ser tentador instalar paquetes adicionales y luego quitarlos en un paso posterior. Sin embargo, este enfoque no es suficiente. Debido a que cada instrucción de Dockerfile crea una capa, quitar datos de la imagen en un paso posterior al paso en el que se creó no reduce el tamaño de la imagen total (los datos siguen allí, simplemente están ocultos en una capa más profunda). Considera el siguiente ejemplo:

Dockerfile incorrecto Dockerfile correcto

FROM debian:9
RUN apt-get update && \ apt-get install -y \ [buildpackage] RUN [build my app] RUN apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

FROM debian:9
RUN apt-get update && \ apt-get install -y \ [buildpackage] && \ [build my app] && \ apt-get autoremove --purge \ -y [buildpackage] && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/*

En la versión incorrecta de Dockerfile, el [buildpackage] y los archivos en /var/lib/apt/lists/* siguen existiendo en la capa correspondiente a la primera RUN. Esta capa es parte de la imagen y se la debe subir y descargar con el resto, incluso si no se puede acceder a los datos que contiene en la imagen resultante.

En la versión correcta de Dockerfile, todo se hace en una sola capa que contiene únicamente tu aplicación compilada. El [buildpackage] y los archivos en /var/lib/apt/lists/* no existen en ningún otro lado de la imagen resultante; ni siquiera están ocultos en una capa más profunda.

Si deseas obtener más información sobre las capas de imágenes, consulta Optimización para la caché de compilación de Docker.

Otra buena forma de reducir la cantidad de desorden en tu imagen es utilizar compilaciones de varias etapas (agregadas en Docker 17.05). Las compilaciones de varias etapas te permiten compilar tu aplicación en el primer contenedor de "compilado" y utilizar el resultado en otro contenedor, a la vez que utilizas el mismo Dockerfile.

Proceso de compilado de etapas múltiples de Docker

Figura 3. El proceso de compilación de varias etapa de Docker.

En el siguiente Dockerfile, el binario hello está compilado en el primer contenedor y se inyecta en el segundo. Debido a que el segundo contenedor está basado en scratch, la imagen resultante contiene solamente el binario hello y no el archivo fuente y los archivos de objetos necesarios durante la compilación. El binario debe estar vinculado estáticamente para poder funcionar sin la necesidad de una biblioteca externa en la imagen de scratch.

FROM golang:1.10 as builder

WORKDIR /tmp/go
COPY hello.go ./
RUN CGO_ENABLED=0 go build -a -ldflags '-s' -o hello

FROM scratch
CMD [ "/hello" ]
COPY --from=builder /tmp/go/hello /hello

Intenta crear imágenes con capas comunes

Si debes descargar una imagen de Docker, Docker primero verifica si ya tienes algunas de las capas que están en la imagen. Si las tienes, no las descarga. Esta situación puede suceder si ya descargaste previamente otra imagen con la misma base que la que estás descargando actualmente. El resultado es que la cantidad de datos descargados es mucho menor para la segunda imagen.

En un nivel organizativo, puedes aprovechar esta reducción y brindarle a tus desarrolladores un conjunto de imágenes base comunes y estándares. Tu sistema debe descargar cada imagen base solo una vez. Tras la descarga inicial, solamente se necesitan las capas que hacen que cada imagen sea única. De hecho, cuanto más tengan en común tus imágenes, más rápida será la descarga.

Intenta crear imágenes con capas comunes

Figura 4. Cómo crear imágenes con capas comunes.

Usa el análisis de vulnerabilidades en Container Registry

Importancia: MEDIA

Las vulnerabilidades del software son un problema muy comprendido en el mundo de los servidores sin sistema operativo y máquinas virtuales. Una forma común de tratar estas vulnerabilidades es usar un sistema de inventario centralizado que enumere los paquetes instalados en cada servidor. Para estar informado del momento en el que una vulnerabilidad afecta tus servidores, y para corregirlo como corresponda, suscríbete a las noticias de vulnerabilidad de los sistemas operativos ascendentes.

Sin embargo, debido a que se supone que los contenedores son inmutables (consulta inmutabilidad y condición de no tener estado de los contenedores para obtener más detalles), no les apliques un parche en caso de una vulnerabilidad. Se recomienda volver a compilar la imagen, con los parches incluidos, y volver a implementarla. Los contenedores tienen un ciclo de vida mucho más corto y una identidad mucho menos definida que los servidores. Por lo tanto, utilizar un sistema de inventario centralizado similar es una manera deficiente de detectar vulnerabilidades en contenedores.

Para ayudarte a tratar este problema, Container Registry cuenta con una función de análisis de vulnerabilidades. Cuando está habilitada, esta función identifica vulnerabilidades en los paquetes para las imágenes de tu contenedor. Las imágenes se analizan al momento de su carga en Container Registry y cuando hay una actualización en la base de datos de vulnerabilidades. Puedes actuar con la información reportada por esta función de varias maneras:

  • Crea un trabajo similar a cron que detalle vulnerabilidades y active el proceso para corregirlas, cuando exista una solución.
  • En cuanto se detecte una vulnerabilidad, utiliza la integración de Cloud Pub/Sub para activar el proceso de aplicación de parches que utiliza tu organización.

Te recomendamos automatizar el proceso de aplicación de parches y confiar en la canalización de integración continua existente utilizada al comienzo para compilar la imagen. Si confías en tu canalización de integración continua, podrás implementar automáticamente la imagen fija cuando esté lista. Sin embargo, la mayoría de las personas prefiere un paso de verificación manual antes de la implementación. El proceso a continuación lo logra:

  1. Almacena tus imágenes en Container Registry y habilita el análisis de vulnerabilidades.
  2. Configura un trabajo que obtenga regularmente nuevas vulnerabilidades de Container Registry y activa una recompilación de las imágenes en caso de que sea necesario.
  3. Cuando las nuevas imágenes se compilen, haz que tu sistema de implementación continua las implemente en un entorno en etapa de pruebas.
  4. Verifica manualmente el entorno en etapa de pruebas en busca de problemas.
  5. Si no encuentras ningún problema, activa manualmente la implementación en la producción.

Etiqueta correctamente tus imágenes

Importancia: MEDIA

Las imágenes de Docker generalmente se identifican mediante dos componentes: su nombre y su etiqueta. Por ejemplo, para la imagen google/cloud-sdk:193.0.0, "google/cloud-sdk" es el nombre y "193.0.0" es la etiqueta. La etiqueta "más reciente" se utiliza de forma predeterminada si no proporcionas una en tu comando de Docker. El par de nombre/etiqueta es único en todo momento. Sin embargo, puedes reasignar una etiqueta a una imagen diferente según lo necesites.

Cuando construyes una imagen, depende de ti que la etiquetes correctamente. Utiliza una política de etiquetado coherente. Registra tu política de etiquetado para que los usuarios de la imagen puedan comprenderla fácilmente.

Las imágenes del contenedor son una forma de empaquetar y liberar una pieza de software. Etiquetar la imagen permite a los usuarios identificar una versión específica de tu software a fin de descargarlo. Por este motivo, vincula estrechamente el sistema de etiquetado en imágenes del contenedor con la política de liberación de tu software.

Etiquetar mediante el control de versiones semántico

Una forma común de liberar un software es "etiquetar" (como en el comando git tag) una versión particular del código fuente con un número de versión. La Especificación de control semántico de versiones proporciona una forma clara de manejar los números de las versiones. En este sistema, tu software tiene un número de versión de tres partes: "X.Y.Z", según el cual:

  • X es la versión principal, incrementada solo para cambios de la API incompatibles.
  • Y es la versión secundaria, incrementada para nuevas funciones.
  • Z es la versión de parche, incrementada para correcciones de errores.

Cada incremento en el número de la versión secundaria o de parche debe realizarse por un cambio compatible con versiones anteriores.

Si utilizas este sistema, o uno similar, etiqueta tus imágenes según la política a continuación:

  • La etiqueta "más reciente" se refiere a la imagen más reciente (posiblemente estable). Esta etiqueta se mueve en cuanto se crea una nueva imagen.
  • La etiqueta "X.Y.Z" hace referencia a una versión específica de tu software. No la muevas a otra imagen.
  • La etiqueta "X.Y" hace referencia a la última versión de parche de la rama secundaria de tu software. Se la mueve cuando se lanza una nueva versión del parce.
  • La etiqueta "X" hace referencia a la última versión de parche de la versión secundaria de la rama principal "X". Se la mueve cuando se lanza ya sea una nueva versión del parche o una nueva versión secundaria.

El uso de esta política ofrece a los usuarios la flexibilidad de elegir qué versión de su software quieren utilizar. Pueden elegir una versión "X.Y.Z" específica y tener la seguridad de que la imagen nunca cambiará, o pueden obtener actualizaciones automáticamente si eligen una etiqueta menos específica.

Etiquetar con el hash de confirmación de Git

Si tienes un sistema de entrega continua avanzado y lanzas tu software regularmente, posiblemente no utilices números de versiones como se describe en la Especificación de control semántico de versiones. En este caso, una forma común de manejar números de versiones es utilizar el hash SHA-1 de confirmación de Git (o una versión corta de este) como el número de versión. Por diseño, el hash de confirmación de Git es inmutable y hace referencia a una versión específica de tu software.

Puedes utilizar este hash de confirmación como un número de versión para tu software, pero también como etiqueta de la imagen de Docker compilada desde esta versión específica de tu software. Si haces esto, las imágenes de Docker serán rastreables: como en este caso la etiqueta de la imagen es inmutable, instantáneamente sabes qué versión específica de tu software se está ejecutando dentro de un contenedor determinado. En tu canalización de entrega continua, automatiza la actualización del número de versión utilizado para tus implementaciones.

Considera cuidadosamente si usarás una imagen pública

Importancia: N/A

Una de las grandes ventajas de Docker es la cantidad de imágenes públicas disponibles, para todo tipo de software. Estas imágenes te permiten comenzar con rapidez. Sin embargo, cuando estás diseñando una estrategia de contenedor para tu organización, posiblemente tengas limitaciones que las imágenes públicas no podrán cumplir. A continuación, te presentamos algunos ejemplos de limitaciones que podrían hacer que el uso de imágenes públicas no sea posible:

  • Deseas controlar exactamente lo que está dentro de tus imágenes.
  • No quieres depender de un repositorio externo.
  • Quieres controlar estrictamente las vulnerabilidades en tu entorno de producción.
  • Quieres tener el mismo sistema operativo de base en cada imagen.

La respuesta a todas esas limitaciones es la misma y, lamentablemente, es costosa: debes compilar tus propias imágenes. Compilar tus propias imágenes es factible para un número limitado de ellas, pero esta cantidad tiende a aumentar rápidamente. Para tener alguna posibilidad de administrar un sistema de este tipo a gran escala, considera utilizar las siguientes opciones:

  • Una forma automatizada de compilar imágenes, de forma confiable, incluso para imágenes que apenas se compilan. Los activadores de compilación en Cloud Build son una buena manera de lograrlo.
  • Una imagen base estandarizada. Google proporciona algunas imágenes base que puedes utilizar.
  • Una forma automatizada de propagar actualizaciones en la imagen base a imágenes "secundarias".
  • Una forma de abordar las vulnerabilidades en tus imágenes. Para obtener más información, consulta Usa el análisis de vulnerabilidades en Container Registry.
  • Una forma de obligar a aplicar tus estándares internos a las imágenes creadas por diferentes equipos en tu organización.

Tienes varias herramientas a tu disposición para ayudarte a aplicar políticas en las imágenes que compilas y luego implementas:

  • container-diff puede analizar el contenido de imágenes y comparar dos entre ellas.
  • container-structure-test puede poner a prueba si el contenido de una imagen cumple con un conjunto de reglas que tú definas.
  • Grafeas es una API de metadatos de artefactos, en la cual puedes almacenar metadatos sobre tus imágenes para controlar luego si cumplen con tus políticas.
  • Kubernetes cuenta con controladores de admisión que puedes utilizar para verificar una cantidad de prerequisitos antes de implementar una carga de trabajo en Kubernetes.
  • Kubernetes también cuenta con políticas de seguridad del pod que puedes utilizar a fin de imponer el uso de opciones de seguridad en el clúster.

También tienes la opción de adoptar un sistema híbrido, es decir, utilizar una imagen pública como Debian o Alpine como la imagen base, y compilar el resto sobre esa imagen. O también puedes utilizar imágenes públicas para ciertas imágenes no importantes y compilar tus imágenes en esos casos. No existe una respuesta correcta o incorrecta para esas preguntas, pero debes abordarlas.

Una aclaración sobre licencias

Antes de incluir bibliotecas y paquetes de terceros en tu imagen de Docker, asegúrate de que las licencias respectivas te permitan hacerlo. Las licencias de terceros podrían imponer restricciones sobre la redistribución, que se aplican cuando publicas una imagen de Docker en un registro público.

Pasos siguientes

Prueba otras características de Google Cloud Platform por ti mismo. Revisa nuestros instructivos.

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...