Skip to content

8. Construir nuestras propias imágenes

Temas del programa: 4.1.2 y 4.1.3

De una imagen existente a una imagen propia

Section titled “De una imagen existente a una imagen propia”

Hasta ahora utilizamos imágenes que otras personas construyeron

Registry
│
▼
Imagen existente
│
▼
Contenedor

Ahora construiremos una imagen para nuestra aplicación

Código
│
▼
Dockerfile
│
▼
Imagen propia
│
▼
Contenedor

Un Dockerfile es un archivo de texto que describe instrucciones para construir una imagen de contenedor

Por ejemplo puede indicar

  • Qué imagen se utilizará como base
  • En qué directorio trabajar
  • Qué archivos copiar
  • Qué dependencias instalar
  • Qué comando ejecutar al iniciar el contenedor
RECETA
1. Seleccionar ingredientes
2. Preparar los ingredientes
3. Seguir instrucciones
4. Obtener un resultado
DOCKERFILE
FROM
WORKDIR
COPY
RUN
CMD

Cada instrucción tiene una función específica durante la construcción o ejecución

Archivo hola.php

<?php
echo "Hola desde PHP en un contenedor" . PHP_EOL;

Archivo Dockerfile

FROM php:8.3-cli
WORKDIR /app
COPY hola.php .
CMD ["php", "hola.php"]

La imagen base incluye PHP CLI y el Dockerfile agrega nuestro archivo y define el comando predeterminado

FROM php:8.3-cli

Indica la imagen base de la que partirá la construcción

Imagen base
php:8.3-cli
│
▼
Nuestra imagen

La imagen base ya contiene archivos y componentes necesarios para ejecutar PHP CLI

Conviene elegir una versión compatible con la aplicación y revisar su mantenimiento

WORKDIR /app

Establece el directorio de trabajo para las instrucciones posteriores que lo utilicen

Contenedor
/
├── usr
├── etc
└── app
└── hola.php

Docker crea el directorio si es necesario durante la construcción

COPY hola.php .

Copia el archivo desde el contexto de construcción al directorio de trabajo de la imagen

Equipo de desarrollo
│
│ COPY
▼
Imagen
/app/hola.php

La ruta de origen se interpreta dentro del contexto de construcción

RUN comando

Ejecuta una instrucción durante la construcción de la imagen

Por ejemplo en una imagen basada en Debian o Ubuntu

RUN apt-get update

No es un comando que se repita automáticamente cada vez que se inicia el contenedor

La sintaxis y los paquetes disponibles dependen de la imagen base

CMD ["php", "hola.php"]

Define el comando predeterminado que se ejecutará al iniciar el contenedor si no se proporciona otro comando que lo sustituya

Imagen
│
▼
docker run
│
▼
php hola.php

La forma de lista JSON suele ser preferible para ejecutar directamente el programa y gestionar señales correctamente

RUNCMD
Se ejecuta durante la construcciónDefine el comando o parámetros predeterminados de ejecución
Contribuye a construir la imagenSe utiliza al iniciar el contenedor
Puede generar una capa de imagenNo genera por sí mismo una capa de archivos durante la construcción

No deben utilizarse como si fueran instrucciones equivalentes

Desde el directorio que contiene el Dockerfile y hola.php

Terminal window
docker build -t hola-php:1.0 .

Significado

docker build
Construye una imagen
-t hola-php:1.0
Asigna nombre y etiqueta
.
Utiliza el directorio actual como contexto

La construcción puede descargar la imagen base si no está disponible localmente

Terminal window
docker run --rm hola-php:1.0

Salida ilustrativa

Hola desde PHP en un contenedor

El comando inicia el proceso definido por CMD

Como se utiliza --rm el contenedor se elimina al terminar

La imagen hola-php:1.0 permanece disponible localmente

Una etiqueta permite identificar una versión o variante de una imagen

hola-php:1.0
hola-php:1.1
hola-php:2.0

No se debe asumir que una etiqueta garantiza por sí sola que el contenido nunca cambiará

Para identificar contenido de manera más precisa existen referencias por digest

Las imágenes se construyen mediante capas y metadatos

┌─────────────────────────────┐
│ Archivos de la aplicación │
├─────────────────────────────┤
│ Dependencias │
├─────────────────────────────┤
│ PHP │
├─────────────────────────────┤
│ Archivos de la imagen base │
└─────────────────────────────┘

Las capas permiten reutilizar contenido compartido y optimizar construcciones y descargas

Si una instrucción cambia puede invalidarse parte de la caché de construcción

Menos eficiente para muchos proyectos

COPY . .
RUN composer install

Una alternativa común

COPY composer.json composer.lock ./
RUN composer install
COPY . .

Así los archivos de dependencias pueden aprovechar la caché cuando no cambian

La estrategia correcta depende del sistema de construcción y de la aplicación

Es un archivo que indica qué rutas deben excluirse del contexto de construcción enviado a Docker

Ejemplo

.git
.env
.env.*
node_modules
vendor
storage/logs

La lista debe adaptarse al proyecto

No conviene excluir archivos que sean necesarios para construir correctamente la imagen

El contexto de construcción puede incluir

Código
Dependencias locales
Archivos temporales
Credenciales
Historial Git
Registros

Un .dockerignore adecuado reduce archivos innecesarios y ayuda a evitar que información sensible se incorpore accidentalmente al contexto

No es un mecanismo suficiente para proteger secretos por sí solo

CÓDIGO
app/
routes/
controllers/
CONFIGURACIÓN
APP_ENV
DB_HOST
DB_PORT
SECRETOS
DB_PASSWORD
API_KEY
APP_KEY

La configuración debe separarse del código siempre que sea posible

Los secretos no deben quedar incrustados en el Dockerfile ni en las capas de la imagen

FROM php:latest
ENV DB_PASSWORD=ClaveReal
COPY . /var/www/html
RUN chmod -R 777 /var/www/html
CMD ["php", "-S", "0.0.0.0:8000"]

Problemas que debemos reconocer

  • La etiqueta latest puede cambiar
  • Se incorpora una contraseña a la imagen
  • Se conceden permisos excesivos
  • No se documentan claramente las dependencias
  • El servidor integrado de PHP no debe asumirse apropiado para producción
Previsibilidad

Elegir versiones compatibles y mantenerlas actualizadas

Menor superficie

Incluir solo componentes necesarios

Privilegios mínimos

Ejecutar con la identidad adecuada

Las decisiones deben responder a los requisitos reales de la aplicación

¿Qué significa ejecutar como usuario no root?

Section titled “¿Qué significa ejecutar como usuario no root?”

En Linux el usuario root tiene privilegios muy elevados

Cuando sea compatible con la aplicación podemos configurar un usuario específico

USER app

El usuario debe existir y tener permisos suficientes para ejecutar el programa y acceder a sus archivos

No se debe asumir que todas las imágenes incluyen un usuario llamado app

Algunas aplicaciones necesitan herramientas para construir pero no para ejecutarse

Una construcción por etapas permite separar ambos entornos

Etapa de construcción
Compiladores y herramientas
│
▼
Artefactos generados
│
▼
Etapa de ejecución
Solo componentes necesarios

Esta técnica puede reducir el tamaño de la imagen final y la cantidad de herramientas disponibles en ejecución

Su implementación depende del lenguaje y del proceso de construcción

Continúa con 9. Docker Compose.