Skip to content

Prácticas recomendadas para crear imágenes de contenedores

Introducción

En el mundo actual del desarrollo y despliegue de aplicaciones, los contenedores se han consolidado como una tecnología clave para lograr portabilidad, escalabilidad y eficiencia operativa. Sin embargo, el verdadero valor de los contenedores comienza en el momento en que se construyen las imágenes que los definen. Una imagen mal diseñada puede derivar en problemas de seguridad, rendimiento deficiente, dificultades de mantenimiento e incluso costos innecesarios en ambientes productivos.

Por ello, adoptar prácticas recomendadas para la creación de imágenes de contenedores no solo mejora la calidad técnica del software, sino que también facilita su integración en flujos DevOps, garantiza mayor seguridad y contribuye a una operación más predecible y estable. En esta sección, exploraremos los principios fundamentales y las recomendaciones prácticas para construir imágenes eficientes, seguras y alineadas con los estándares modernos de la industria.

Objetivo General

Proporcionar un conjunto de lineamientos y buenas prácticas para la creación de imágenes de contenedores eficientes, seguras y optimizadas, que faciliten su integración en procesos de desarrollo, pruebas y despliegue continuo, garantizando un funcionamiento predecible y alineado con los estándares modernos de la industria de software.

Prácticas recomendadas

  1. 🧱 Usa imágenes base mínimas y seguras

Opta por imágenes livianas como alpine, distroless o versiones slim de tus lenguajes. Esto reduce la superficie de ataque, acelera el tiempo de construcción y disminuye el tamaño de la imagen.

FROM python:3.12-slim

  1. 🔐 Minimiza los paquetes y dependencias

Instala solo lo que necesitas. Evita instalar herramientas innecesarias como curl, vim, o compiladores si no son requeridos en tiempo de ejecución.

--no-install-recommends en Debian/Ubuntu
dnf install --nodocs en RHEL.

  1. ⚙️ Utiliza múltiples etapas de construcción (multi-stage builds)

Separa el proceso de compilación del entorno de ejecución. Esto permite crear imágenes finales más limpias y seguras. Ejemplo:

# Etapa de construcción
FROM golang:1.21 AS builder
WORKDIR /app

# Copiamos solo los archivos necesarios para la compilación
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# Compilamos la aplicación
RUN go build -o app

# Etapa de ejecución
FROM alpine:latest
WORKDIR /app

# Instala dependencias mínimas si son necesarias (ej: para certificados)
RUN apk --no-cache add ca-certificates

# Copiamos el binario desde la etapa de build
COPY --from=builder /app/app .

# Establecemos el punto de entrada
ENTRYPOINT ["./app"]

  1. 🧼 Limpia archivos temporales y cachés

Después de instalar paquetes o dependencias, elimina archivos temporales, cachés de paquetes, y artefactos de compilación.

RUN apt-get update && apt-get install -y \
    build-essential \
 && rm -rf /var/lib/apt/lists/*

  1. 👤 No ejecutes como root

Usar un usuario no privilegiado mejora significativamente la seguridad del contenedor.

RUN useradd -m appuser
USER appuser

  1. 📁 Define claramente WORKDIR, ENTRYPOINT y CMD

Estos tres elementos deben usarse adecuadamente para que la imagen tenga un comportamiento predecible.

WORKDIR /app
COPY . .
ENTRYPOINT ["python"]
CMD ["main.py"]

  1. 🧪 Prueba tus imágenes localmente
docker run --rm tu-imagen
  1. 🔒 Escanea tus imágenes en busca de vulnerabilidades

Utiliza herramientas como Trivy, Grype o Docker Scout para encontrar vulnerabilidades conocidas.

trivy image tu-imagen

  1. 🧾 Usa etiquetas (tags) claras y controladas

Evita depender de la etiqueta latest en producción. Usa versiones semánticas o hashes de commits para rastrear cambios.

  1. 🧰 Usa archivos .dockerignore

Evita copiar archivos innecesarios a la imagen, como .git, node_modules, o archivos temporales.

Ejemplo .dockerignore:

.git
*.log
Dockerfile
README.md

Laboratorio: Prácticas recomendadas para crear imágenes de contenedores

  1. Ingresar al servidor qué contiene al ambiente de laboratorio con credenciales de administración

  2. Crear una carpeta llamada best_practices

    mkdir best-practices
    
    cd best-practices
    

  3. Crear un archivo llamado Dockerfile.multipleruns

    vi Dockerfile.multipleruns
    

  4. Escribir el siguiente contenido

    1
    2
    3
    4
    5
    FROM registry.suse.com/bci/bci-base:15.4
    
    LABEL maintainer=usuario
    
    RUN zypper -n install zip
    

  5. Construya una imagen de prueba en base al Dockerfile creado

    docker build -t best-practices:mruns1 -f Dockerfile.multipleruns .
    

  6. Liste la información de la imagen creada y observe el apartado de Layers

    docker image inspect best-practices:mruns1
    
    docker history best-practices:mruns1
    

  7. Modifique el archivo Dockerfile.multipleruns agregando las siguientes líneas

    RUN zypper -n install wget
    RUN zypper -n install curl
    RUN mkdir /tmp/tools
    RUN touch /tmp/tools/single.id
    

  8. Construya nuevamente la imagen de prueba

    docker build -t best-practices:mruns1 -f Dockerfile.multipleruns .
    

  9. Liste la información de la imagen creada y observe el apartado de Layers

    docker image inspect best-practices:mruns1
    
    docker history best-practices:mruns1
    

  10. Ahora cree el archivo Dockerfile.singlerun con el siguiente contenido

    1
    2
    3
    4
    5
    6
    7
    FROM registry.suse.com/bci/bci-base:15.4
    
    LABEL maintainer=usuario
    
    RUN zypper -n install zip wget curl \
        && mkdir /tmp/tools \
        && touch /tmp/tools/single.id
    

  11. Construya una imagen con el tag best-practices:srun1

    docker build -t best-practices:srun1 -f Dockerfile.singlerun .
    

  12. Liste la información de la imagen creada y observe el apartado de Layers

    docker image inspect best-practices:srun1
    
    docker history best-practices:mruns1
    

  13. Modifique el archivo Dockerfile.singlerun agregando el siguiente contenido

    COPY data.txt /data.txt
    

  14. Cree un archivo vacío llamado data.txt

    touch data.txt
    

  15. Construya nuevamente la imagen pero con el tag best-practices:srun2

    docker build -t best-practices:srun2 -f Dockerfile.singlerun .
    

  16. Liste la información de las imágenes creadas y compare el apartado de Layers con la primera imagen

    docker image inspect best-practices:srun2 best-practices:srun1 \
    --format='{{println .RepoTags}}{{range .RootFS.Layers}}{{println . }}{{end}}'
    

  17. Cree varios archivos y carpetas de prueba vacíos

    mkdir -p bin/libs/example-1.0.0
    mkdir -p node_modules/example-1.0.0
    mkdir -p src/temp
    touch src/temp/.env
    touch src/temp/.local
    mkdir .vscode
    

  18. Mueva el archivo data.txt a la carpeta src

    mv data.txt src/
    

  19. Modifique el archivo Dockerfile.singlerun, replanzando la linea con la instruccion

    COPY data.txt /data.txt
    
    por
    COPY src /src
    

  20. Cree el archivo .dockerignore con el siguiente contenido

    1
    2
    3
    4
    5
    # comment
    bin
    node_modules
    src/temp/*
    .vscode
    

  21. Construya una imagen con el tag best-practices:srun3 -f Dockerfile.singlerun .

    docker build -t best-practices:srun3 -f Dockerfile.singlerun .
    

  22. Iniciar un contenedor con la imagen creada y entrando a la shell

    docker run -it --rm best-practices:srun3 bash
    

  23. Liste los archivos que se encuentran en la carpeta src en la raiz

    ls -la /src
    

  24. Verifique que la carpeta temp no contenga ningún archivo

    ls -la /src/temp
    

  25. Salga de la shell del contenedor

    exit
    

  26. Modifique el archivo Dockerfile.singlerun agregando la siguiente instrucción

    COPY node_modules /node_modules
    

  27. Construir la imagen con el tag best-practices:srun4

    docker build -t best-practices:srun4 -f Dockerfile.singlerun .
    
    Nota: Esta acción debe fallar y generar un error que no encuentra la carpeta.

  28. Modificar el archivo .dockerignore comentando la siguiente línea

    #node_modules
    

  29. Vuelva a construir la imagen

    docker build -t best-practices:srun4 -f Dockerfile.singlerun .
    

  30. Compruebe que los cambios fueron efectuados levantando un contenedor y entrando a la shell

    docker run -it --rm best-practices:srun4 bash
    
    ls -l /
    
    ls -l /node_modules
    

  31. Salga de la shell del contenedor

    exit
    

  32. Cree un nuevo archivo llamado Dockerfile.rootless con el siguiente contenido

    1
    2
    3
    4
    5
    6
    7
    FROM registry.suse.com/bci/bci-base:15.4
    
    LABEL maintainer=usuario
    
    RUN mkdir /usr/src/tools \
             && touch /usr/src/tools/.env \
             && mkdir /deployments
    

  33. Cree una imagen con el tag opensuse-rootless:0.0.1-root

    docker build -t opensuse-rootless:0.0.1-root -f Dockerfile.rootless .
    

  34. Levante un contenedor temporal con esta imagen y entre a la shell

    docker run -it --rm opensuse-rootless:0.0.1-root bash
    

  35. Liste el usuario con el que se levanta el contenedor

    whoami
    
    cat /etc/passwd
    

  36. Obtenga la información de los directorios creados

    ls -l /usr/src
    
    ls -l /usr/src/tools
    
    ls -l /
    
    exit
    

  37. Modifique el archivo Dockerfile.rootless agregando las siguientes instrucciones

     5
     6
     7
     8
     9
    10
    RUN mkdir /usr/src/tools \
             && touch /usr/src/tools/.env \
             && mkdir /deployments \
             && groupadd --sys test -g 1001 \
             && useradd --sys -u 1001 -g test test -m -d /opt/test -s /sbin/nologin
    USER test
    
    Nota: Solo debe agregar las lineas las últimas 3 lineas y modificar el salto de línea.

  38. Cree otra imagen con el tag opensuse-rootless:0.0.1-denied

    docker build -t opensuse-rootless:0.0.1-denied -f Dockerfile.rootless .
    

  39. Iniciar un contenedor temporal con esta imagen y entre a la shell

    docker run -it --rm opensuse-rootless:0.0.1-denied bash
    

  40. Liste el usuario con el que se levanta el contenedor

    whoami
    
    cat /etc/passwd
    

  41. Intente crear un archivo nuevo en la carpeta /usr/src/tools

    touch /usr/src/tools/test.txt
    
    Nota: Debe mostrar un error de permisos denegados.
    exit
    

  42. Modifique el archivo Dockerfile.rootless agregando el siguiente contenido

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    FROM registry.suse.com/bci/bci-base:15.4
    
    LABEL maintainer=usuario
    
    RUN mkdir /usr/src/tools \
             && touch /usr/src/tools/.env \
             && mkdir /deployments \
             && groupadd --sys test -g 1001 \
             && useradd --sys -u 1001 -g test test -m -d /opt/test -s /sbin/nologin \
             && chown -R test:test /usr/src/tools \
             && chown -R test:test /deployments
    
    USER test
    
    Nota: Solo debe agregar las lineas que contienen el comando chown y modificar el salto de línea.

  43. Cree una nueva imagen con el tag opensuse-rootless:0.0.1-pass

    docker build -t opensuse-rootless:0.0.1-pass -f Dockerfile.rootless .
    

  44. Levante un contenedor temporal con esta imagen y entre a la shell

    docker run -it --rm opensuse-rootless:0.0.1-pass bash
    

  45. Vuelva a intentar crear un archivo en /usr/src/tools

    touch /usr/src/tools/test.txt
    
    Nota: Esta prueba debe ser satisfactoria.
    exit
    

Limpieza de ambiente

Eliminando las imágenes creadas

docker image rm -f $(docker images --filter=label=maintainer=usuario -q)

Eliminar el directorio de trabajo

cd ~/ ; rm -fR best-practices