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
- 🧱 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
- 🔐 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.
- ⚙️ 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"]
- 🧼 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/*
- 👤 No ejecutes como root
Usar un usuario no privilegiado mejora significativamente la seguridad del contenedor.
RUN useradd -m appuser
USER appuser
- 📁 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"]
- 🧪 Prueba tus imágenes localmente
docker run --rm tu-imagen
- 🔒 Escanea tus imágenes en busca de vulnerabilidades
Utiliza herramientas como Trivy, Grype o Docker Scout para encontrar vulnerabilidades conocidas.
trivy image tu-imagen
- 🧾 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.
- 🧰 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
-
Ingresar al servidor qué contiene al ambiente de laboratorio con credenciales de administración
-
Crear una carpeta llamada best_practices
mkdir best-practicescd best-practices -
Crear un archivo llamado Dockerfile.multipleruns
vi Dockerfile.multipleruns -
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 -
Construya una imagen de prueba en base al Dockerfile creado
docker build -t best-practices:mruns1 -f Dockerfile.multipleruns . -
Liste la información de la imagen creada y observe el apartado de Layers
docker image inspect best-practices:mruns1docker history best-practices:mruns1 -
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 -
Construya nuevamente la imagen de prueba
docker build -t best-practices:mruns1 -f Dockerfile.multipleruns . -
Liste la información de la imagen creada y observe el apartado de Layers
docker image inspect best-practices:mruns1docker history best-practices:mruns1 -
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 -
Construya una imagen con el tag best-practices:srun1
docker build -t best-practices:srun1 -f Dockerfile.singlerun . -
Liste la información de la imagen creada y observe el apartado de Layers
docker image inspect best-practices:srun1docker history best-practices:mruns1 -
Modifique el archivo Dockerfile.singlerun agregando el siguiente contenido
COPY data.txt /data.txt -
Cree un archivo vacío llamado data.txt
touch data.txt -
Construya nuevamente la imagen pero con el tag best-practices:srun2
docker build -t best-practices:srun2 -f Dockerfile.singlerun . -
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}}' -
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 -
Mueva el archivo data.txt a la carpeta src
mv data.txt src/ -
Modifique el archivo Dockerfile.singlerun, replanzando la linea con la instruccion
porCOPY data.txt /data.txtCOPY src /src -
Cree el archivo .dockerignore con el siguiente contenido
1 2 3 4 5
# comment bin node_modules src/temp/* .vscode -
Construya una imagen con el tag best-practices:srun3 -f Dockerfile.singlerun .
docker build -t best-practices:srun3 -f Dockerfile.singlerun . -
Iniciar un contenedor con la imagen creada y entrando a la shell
docker run -it --rm best-practices:srun3 bash -
Liste los archivos que se encuentran en la carpeta src en la raiz
ls -la /src -
Verifique que la carpeta temp no contenga ningún archivo
ls -la /src/temp -
Salga de la shell del contenedor
exit -
Modifique el archivo Dockerfile.singlerun agregando la siguiente instrucción
COPY node_modules /node_modules -
Construir la imagen con el tag best-practices:srun4
Nota: Esta acción debe fallar y generar un error que no encuentra la carpeta.docker build -t best-practices:srun4 -f Dockerfile.singlerun . -
Modificar el archivo .dockerignore comentando la siguiente línea
#node_modules -
Vuelva a construir la imagen
docker build -t best-practices:srun4 -f Dockerfile.singlerun . -
Compruebe que los cambios fueron efectuados levantando un contenedor y entrando a la shell
docker run -it --rm best-practices:srun4 bashls -l /ls -l /node_modules -
Salga de la shell del contenedor
exit -
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 -
Cree una imagen con el tag opensuse-rootless:0.0.1-root
docker build -t opensuse-rootless:0.0.1-root -f Dockerfile.rootless . -
Levante un contenedor temporal con esta imagen y entre a la shell
docker run -it --rm opensuse-rootless:0.0.1-root bash -
Liste el usuario con el que se levanta el contenedor
whoamicat /etc/passwd -
Obtenga la información de los directorios creados
ls -l /usr/srcls -l /usr/src/toolsls -l /exit -
Modifique el archivo Dockerfile.rootless agregando las siguientes instrucciones
Nota: Solo debe agregar las lineas las últimas 3 lineas y modificar el salto de línea.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 -
Cree otra imagen con el tag opensuse-rootless:0.0.1-denied
docker build -t opensuse-rootless:0.0.1-denied -f Dockerfile.rootless . -
Iniciar un contenedor temporal con esta imagen y entre a la shell
docker run -it --rm opensuse-rootless:0.0.1-denied bash -
Liste el usuario con el que se levanta el contenedor
whoamicat /etc/passwd -
Intente crear un archivo nuevo en la carpeta /usr/src/tools
Nota: Debe mostrar un error de permisos denegados.touch /usr/src/tools/test.txtexit -
Modifique el archivo Dockerfile.rootless agregando el siguiente contenido
Nota: Solo debe agregar las lineas que contienen el comando chown y modificar el salto de línea.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 -
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 . -
Levante un contenedor temporal con esta imagen y entre a la shell
docker run -it --rm opensuse-rootless:0.0.1-pass bash -
Vuelva a intentar crear un archivo en /usr/src/tools
Nota: Esta prueba debe ser satisfactoria.touch /usr/src/tools/test.txtexit
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