Table of Contents

Docker: guía rápida de iniciación a la linea de comandos de Docker

Manual rápido y completo de Dockerfile:guia_rapida_de_dockerfile
Manual rápido y completo de Docker Swarm: guia_rapida_de_docker_swarm

Esta guía muestra los comandos más útiles para iniciarte con Docker por linea de comandos.

Mostrar información del motor Docker, imágenes y contenedores

Información sobre Docker

docker system info     # Muestra información sobre Docker (versiones, número de imágenes, contenedores, CPUs disponibles, driver de almacenamiento, etc).
docker system  df -v   # Visualiza información sobre espacio usado por imágenes, contenedores, volúmenes y cache, además de otros datos de interés.
docker system events   # Muestra información de los eventos en docker útil para depuración. health_status, attach, detach, pause, restart, copy, etc.

Listar imágenes de docker

# Muestra una salida truncada y sin mostrar las imágenes intermedias.
docker images 
# Muestra todas las imágenes y la salida es sin truncar.
docker images -a --no-trunc 

Listar contenedores.

# Muestra una lista de los contenedores que están activos.
docker ps
docker container ls
 
# Muestra todos los contenedores, activos y no activos (historial).
docker ps -a
docker container ls  -a

Muestra los procesos que están corriendo en un contenedor

docker top contenedor

Averiguar el código de salida de un contenedor al finalizar

# Este comando se quedará escuchando hasta qe finalice el contenedor, entonces visualizará su codigo de salida.
docker container wait contenedor

docker container wait contenedor1 contenedor2 …

Muestra los mapeos de puertos entre contenedor (nombre o ID) y host anfitrión

docker port contenedor
docker port contenedor1 contenedor2 contenedor3_ID ...

Visualiza información a bajo nivel de las imágenes y contenedores

docker inspect contenedor1 imagen5 contenedor2_ID ...
docker inspect -s contenedor1 contenedor2_ID ... # Permite mostrar SizeRootFs y SizeRw
 
# "SizeRootFs" es el tamaño total de todos los archivos del contenedor, en bytes.
# "SizeRw" es el tamaño de los archivos que se han creado o modificado, si se compara el contenedor con su imagen base. Justo después de la creación, este debería ser cero; a medida que modificas (o creas) archivos, esto aumentará.
 
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' zzz # La opción -f permite especificar un "Go template" para filtrar contenido. El ejemplo mostraría las IPs del contenedor.
 
docker inspect --type=image XXX       # Permite especificar que se pregunta por una imagen (para casos donde contenedor e imagen tienen el mismo nombre).
docker inspect --type=container XXX   # Permite especificar que se pregunta por un contenedor (para casos donde contenedor e imagen tienen el mismo nombre).

Renombrar contenedores

docker rename contenedor12 nuevo_nombre

Pausar los procesos de un contenedor

docker container pause contenedor
docker container unpause contenedor

Mostrar cambios realizados en el sistema de ficheros del contenedor

docker diff contenedor

Copiar ficheros y directorios entre el host y el contenedor en funcionamiento

# Los directorios deben existir en remoto, cp fallará si el directorio destino no existe.
docker container cp contenedor:/carpeta /root/
docker cp /opt/fichero.tar.gz contenedor:/root/Downloads

Arrancar, parar y reiniciar contenedor

docker start contenedor          # Arranca un contenedor no activo.
docker stop conetenedor          # Para un contenedor activo.
docker restart contenedor        # Reinicia el contenedor. El tiempo de espera antes de forzar la parada del contenedor son 10 segundos.
docker restart -t 15 contenedor  # Permite establecer el tiempo de espera antes de forzar su muerte. Útil en casos en los que el proceso necesita de más tiempo para parar limpiamente.

Ejecutar el mismo comando en varios contenedores al mismo tiempo.

docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq) # Visualiza las direcciones IPs de todos los contenedores del host.
 
# Usando ssh (opción 1).
ssh root@X.X.X.X 'docker inspect --format="{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" $(docker ps -aq)'
# Usando ssh (opción 2).
ssh root@X.X.X.X "docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \$(docker ps -aq)"

Eliminar imágenes de Docker

docker images -a                   # Lista todas las imágenes.
docker rmi imagen1 imagen2         # Elimina imágenes específicas.
docker rmi $(docker images -a -q)  # Elimina todas las imágenes del host.
docker images -a | grep "XXX" | awk '{print $3}' | xargs docker rmi  # Elimina las imágenes que sigan un patrón.

Eliminar contenedores

docker rm contenedor           # Elimina el contenedor.
docker rm -v contenedor        # Elimina el contenedor y su volumen si este no ha sido montado con un directorio del host.
docker rm $(docker ps -qa)     # Elimina todos los contenedores (no afecta a los contenedores activos).
docker rm -f $(docker ps -qa)  # Elimina todos los contenedores, incluidos los contenedores activos).
 
# Ejemplo eliminando contenedores por estado estado dead y exited.
docker rm $(docker ps --all -q -f status=dead)   
docker rm $(docker ps --all -q -f status=exited)
 
# Estilo colector de basura (garbage collection), la opción prube elimina contenedores no activos, redes no usadas, imágenes no referenciadas y cache.
# También es posible usar en vez de system (todo), unicamente la eliminación de "network", "volume", "container" y "images" por separado y con varias posibilidades de aplicar filtros.
docker system prune  

Crear / Ejecutar / Arrancar un contenedor Docker

Arrancar un contenedor controlando el uso de procesador.

Disponiendo de 4 núcleos en el host, el contenedor usará unicamente los núcleos 0 y 1.

sudo docker run --cpus=2.0 --cpuset-cpus=0,1 -m=1g alpine

También podemos definir la afinidad para que unos contenedores tengan más afinidad que otros. El número 1024 sería el 100% de los recursos del sistema en cuanto a CPU.

# Ejemplo de un contenedor con el 50% y otros dos con 25%. Pero esos límites se dan únicamente cuando haya escasez de recursos.
docker run --cpu-shares=512 --name contenedor_uso_50 alpine
docker run --cpu-shares=256 --name contenedor_dos_25 alpine
docker run --cpu-shares=256 --name contenedor_tres_25 alpine

Crear / Ejecutar / Arrancar un contenedor Docker: Se indica qué imagen va a ejecutar qué comando y con qué argumentos.

Sintaxis: docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]
 
docker run --name contenedor_sleep alpine top     # Lo ejecuta en primer plano pero no habrá salida en pantalla.
docker run -d --name contenedor_sleep alpine top  # Lo ejecuta en segundo plano (-d). No nos ocupara el prompt como el comando anterior, pero nunca terminará si no intervenimos posteriormente.
docker create --name contenedor_sleep alpine top  # Si no se se desea que el contenedor sea arrancado, se debe usar create como alternativa a "docker run -d" y arrancarlo posteriormente.
 
docker run -a stdout alpine top                     # Salida del comando top en pantalla, pero no hay entrada de teclado.
docker run -a stdin -a stdout -a stdout alpine top  # Salida del comando top en pantalla con entrada de teclado pero no interactivo (falta -it).
docker run -it -a stdin -a stdout alpine top        # Salida y entrada por teclado par interactuar con comandos, en este caso top.
 
# Ejecutar un comando en docker pero usando el PID namespace del host anfitrión (mostrará los procesos del anfitrión).
docker run --pid="host" --cidfile="pid" -it -a stdin -a stdout alpine top
 
--cidfile="docker_id"           # Permite crear un fichero con el ID del contenedor.
--pid="container:contenedor"    # Usa el PID namespace de otro contenedor (nombre / ID). Muy útil para depurar desde otro contenedor, ej. strace.
--pid="host"                    # Usa el PID namespace del anfitrión.
 
# Reiniciar contenedores automáticamente.
 
--restart on-failure[:max-retries]  # Si el comando termina sin exit code 0, se intentará reiniciar las veces configuradas.
--restart always                    # El contenedor será siempre rearrancado.
--restart unless-stopped            # Reinicia el contenedor incluyendo el inicio del servicio docker, excepto si el contenedor fue puesto en un estado detenido antes de que el demonio Docker fuera detenido.

NOTA: El comando docker run crea y ejecuta el contenedor. Se debe tener en cuenta que al terminar el proceso, el contenedor será eliminado. Hay comandos que ejecutan un proceso raíz el cual genera otros procesos que sí se mantienen activos pero el raíz desaparece. En esos casos el contenedor será eliminado también. Por ejemplo de usar nginx, se debería usarse nginx -g 'daemon off;'

Ejecutar un comando en un contenedor (por ejemplo una shell)

docker exec -it contenedor bash

NOTA: Si no se usa el modo interactivo el contenedor ejecutará el comando sh y habrá terminado su trabajo, parándose. Con “-it” siempre se tendrá la posibilidad de arrancarlo de nuevo con “start” / “restart” y usar “attach” para conectarnos al mismo. Para desvincularse (detach) del contenedor sin terminarlo “Ctrl+p, Ctrl+q”

Vincularse al / desvincularse del contenedor (acceder a stdout).

docker run -d --name topdemo alpine /usr/bin/top
docker attach topdemo
# Para desvincularse (detach) del contenedor evitando que este termine, se debe usar "Ctrl+p, Ctrl+q".

Códigos de error de docker

# Enviar código de salida.
docker run busybox /bin/sh -c 'exit 3'; echo $?
# Mostrar código de salida.
docker run busybox foo; echo $?
 
125 Error del demonio Docker.
126 El comando no pudo ser ejecutado.
127 El comando no pudo ser encontrado.

Dejar un contenedor siempre activo con una shell disponible

# Crear el contenedor usando "stdin" y "stdout" para poder vincularse a la shell.
docker run --restart always --name SH_CONTAINER -it -a stdin -a stdout alpine sh
 
# Arrancar el contenedor si no se quiere usar la opción "--restart always".
# docker start SH_CONTAINER
 
# Vincularse a la shell del contenedor.
docker attach SH_CONTAINER
# Para desvincularse (detach) del contenedor impidiendo que este termine "Ctrl+p, Ctrl+q".

Sobrescribir configuración predeterminada de un Dockerfile

Guia de Dockerfile.

Con “docker run” se pueden sobrescribir opciones de un Dockerfile a excepción de FROM, MAINTAINER, RUN y ADD.

CMD puede ser sobrescrito simplemente especificando un comando con “docker run”.

ENTRYPOINT se usa para poder utilizar el contenedor como un binario, por ejemplo para ejecutar versiones concretas de python. En estos casos el parámetro pasado a run sería el parámetro que recibiría el ejhecutable usado en ENTRYPOINT. La opción --entrypoint=“” Sobrescribe ENTRYPOINT. También puede usarse para anularlo y usar la linea de comandos directamente.

docker run -it --entrypoint="" alpine bash

EXPOSE en ficheros Dockerfile permite documentar la exposición de un puerto en el contenedor (pero no hace ningún mapeo), es algo informativo para que los usuarios sepan qué puertos usa el servicio que el contenedor corre. Desde la linea de comandos se tienen varias opciones para indicar una exposición de puerto, aumentar la definida en el Dockerfile y mapear dichos puertos con el anfitrión. A efectos prácticos no hay diferencia entre exponer un puerto en tiempo de ejecución o exponerlo a través de una instrucción en el Dockerfile.

--expose=[] # Expone un puerto o un rango de puertos dentro del contenedor. Agrega más puertos que los establecidos en EXPOSE, pero como la directiva del Dockerfile, no mapea los puertos al host anfitrión.
 
-P     # Mapea todos los puertos expuestos del contenedor con los del host anfitrión.
       # Si no especifica puerto, se asignará uno al azar (/proc/sys/net/ipv4/ip_local_port_range)
 
-p=[]  # Mapea un puerto o rango de puertos expuestos del contenedor con los del host anfitrión: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort.
 
       # Si se usan rangos, el número de puertos debe coincidir.
       # Si se usa un rango para el anfitrión pero un solo puerto expuesto en el contenedor, no se podrá saber a qué puerto fue asignado si no se usa el comando "docker port" para visualizar el mapeo.
 
--link=""   # Enlaza con otro contenedor, normalmente para conectar contenedores consumidores con servidores: <name or id>:alias | <name or id>

ENV variables de entorno, por defecto en linux de manera predeterminada se configuran automáticamente las siguientes variables. HOME USER HOSTNAME PATH (binarios) TERM (si se activa el TTY allocation).

docker exec -it -e "HOME=HACKERMASTER" -e "variable=loquesea" nostalgic_hodgkin env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d07c71d900fa
TERM=xterm         # Sin la opción "-it" no se inicializaría.
HOME=HACKERMASTER
variable=loqueseat

HEALTHCHECK

--health-cmd       # Comando a ejecutar para comprobar que el comando funciona.
--health-interval  # Tiempo entre la ejecución de la comprobación.
--health-retries   # Fallos consecutivos necesarios para informar de la falta de salud.
--health-timeout   # Tiempo máximo de ejecución de una comprobación.
--health-start-period  # Periodo de inicio para que el contenedor se inicialice antes de comenzar la cuenta atrás de health-retries.
--no-healthcheck       # Desactiva cualquier HEALTHCHECK especificado por el contenedor.
 
docker run --name=test --health-cmd "ps aux | grep -i top" -d alpine  top  # Ejecutar top en un contenedor y establecer el check "ps aux | grep -i top".
docker inspect --format='{{.State.Health.Status}}' test                    # Obtener información del estado del proceso (started, healthy, unhealthy, etc).
healthy

TMPFS (mount tmpfs filesystems).

Se utiliza para almacenar información en memoria. Sólo mantiene la información mientras el contenedor esté en ejeución, práctico para almacenar información sensible no cifrada. A diferencia de volúmenes y bind mounts, donde es posible compartir la información entre contenedores, con tmpfs no lo es.

# Los parámetros usan la misma sintaxis que 'mount -t tmpfs -o'
docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image
# Usando la sintaxis de mount.
docker run -d --mount type=tmpfs,destination=/run my_image

USER: Seleccionar el UID/GID del proceso ejecutado dentro del contenedor.

docker run --user 99:99 -it -a stdin -a stdout alpine sh

WORKDIR : Por defecto “/” que es la ruta donde se ejecutarán los comandos.

-w="", --workdir=""

VOLUME : El tema de los volúmenes es muy amplio, se mostrará simplemente lo más básico. Los volúmenes se recomiendan frente a los bind mounts a la hora de almacenar datos. El volumen montado no es eliminado junto con al contenedor. Desde el contenedor y el anfitrión se pueden editar los permisos (los IDs deben existir en ambos naturalmente).

docker volume create volumen1   # Crear un volumen.
docker volume list              # Listar volúmenes.
docker volume inspect volumen1  # Mostrar información detallada del volumen (Fecha de creación, driver, etiquetas, punto de montaje, etc)
docker run --mount source=volumen1,destination=/opt/volumen1 -it alpine sh              # Montar el volumen en el contenedor.
docker run --mount source=volumen1,destination=/opt/volumen1 -it -u $UID:$UID alpine sh # Montar el volumen en el contenedor especificando usuario, en este caso el mismo que ejecuta el comando.

NOTA: La opción para cambiar el WORKDIR puede ser interesante de tener en cuenta al usar volúmenes.

Montar una carpeta del host mediante “bind mount”

Los bind mounts es la forma antigua de guardar datos generados por contenedores, han existido desde los primeros días de Docker y desde hace ya mucho tiempo se recomienda no usarlos y usar los volúmenes. Actualmente están regelados a entornos donde sea necesario acceder a determinada información del host anfitrión. Los bind mounts tienen una funcionalidad limitada y diferente en comparación con los volúmenes. Una limitación de los bind mounts es que NO pueden definir un propietario del directorio montado dentro del contenedor. Es decir, siempre se montará con el UID/GUIs del directorio del host. Eso en cambio si es algo configurable con los volúmenes. Siempre se puede usar desde la linea de comandos la opción --user para arrancar el contenedor con un determinado usuario del host y así poder tener acceso al bind mount desde el contenedor (leer) .

Una diferencia con los volúmenes es que los montajes sobre directorios no vacíos, queda oculto el contenido previamente existente, cosa que no sucede al utilizar los volúmenes.

Cuando se utiliza un bind mount, un archivo o directorio en la máquina anfitriona se monta en un contenedor. El archivo o directorio es referenciado por su ruta absoluta en el host (máquina anfitriona). Por el contrario, cuando se utiliza un volumen, se crea una nueva carpeta dentro del directorio de almacenamiento de Docker en la máquina anfitriona, y Docker gestiona el contenido de ese directorio.

No es necesario que el archivo o directorio ya exista en el host de Docker. Se crea bajo demanda si aún no existe. Los bind mounts son muy eficaces, pero dependen de que el sistema de archivos de la máquina anfitriona tenga una estructura de directorios específica disponible.

# -v puede ser especificada como parámetro varias veces para crear múltiples directorios o mapear directorios con el host anfitrión.
# --mount permite también hacer este tipo de montajes y soporta más opciones (es la opción recomendable). También puede usarse múltiples veces.
# --mount type=bin,src=XX,dst=XX # Monta un directorio del host en el contenedor.
# --mount type=volume,target=XX  # Crea un directorio si no existe en el contenedor.
 
docker run -it -v /root:/root -w /root/ alpine sh    # Monta el directorio /root del host en el directorio /root del contenedor.
 
# NOTA: Al montar directorios del host y no usar volumenes docker genéricos. Estos contenedores no serán borrados al usar "docker container rm -v".
 
docker run -it --mount type=bind,src=/root/,dst=/root -w /root/ alpine sh # Lo mismo que el anterior comando pero con la sintaxis de mount.
 
# Lo mismo que el comando anterior, pero solo se podrá escribir en el directorio especificado con -v (o con --mount), en este caso /root.
docker run --read-only -it -v /root:/root -w /root/ alpine sh
 
 
# Crea el directorio /root/test dentro del contenedor y lo usa como directorio de trabajo para ese comando, en este caso la shell.
docker run -it -v /root/test -w /root/test alpine sh 
# Equivalente al comando anterior pero usando la sintaxis de mount. También creará el directorio /root/test si no existe.
docker run -it --mount type=volume,target=/root/test -w /root/ alpine sh

ARG: Al contruir imágenes las variables pueden ser sobrescritas siempre que haya sido definida en el Dockerfile o bien sea una de las variables estándar.

docker build --build-arg some_variable_name=a_value

Limpiar volúmenes anónimos: Los volúmenes que han sido mapeados con un directorio del sistema anfitrión no son eliminados ya que borraría el directorio del host en al caso.

docker run --rm contenedor

Opciones de seguridad: https://docs.docker.com/engine/reference/run/#security-configuration

Buscar / Descargar imágenes

Los comandos de búsqueda refieren a DockerHub. En DockerHub es posible guardar, compartir y buscar imágenes. Es posible automatizar creación de imágenes y disparadores webhooks para automatizar o notificar la creación de una imagen.

Una imagen automatizada es normalmente de terceros y no oficial. Es la construida automáticamente en DockerHub cuando se realiza un cambio en el repositorio GitHub/Bitbucket configurado como fuente para esa imágen.

Descargar una imagen de Docker

# Sintaxis: docker pull imagen
docker pull alpine

Buscar imágenes en DockerHub (Nombre / Descripción / Estrellas / Oficial / Automatizada)

# Buscar imágenes con el nombre "arch"
docker search --no-trunc arch
# Buscar imágenes con el nombre "arch", sin truncar la salida, imágenes con más de 50 estrellas, oficial y no automatizada. Solo muestra 5 resultados.
docker search --filter=stars=50 --filter=is-official=true --filter=is-automated=false --limit 5 --no-trunc arch
 
# Permite obtener la salida en un formato personalizado: .Name/.Description/.StarCount/.IsOfficial/.IsAutomated/
docker search --format "table {{.Name}}\t{{.IsAutomated}}\t{{.IsOfficial}}" arch
NAME                          AUTOMATED   OFFICIAL
base/archlinux                [OK]        
binhex/arch-delugevpn         [OK]        
archlinux                                 [OK]
archlinux/base                            
binhex/arch-rtorrentvpn       [OK]
...

Construir imágenes

El comando build tiene parámetros similares a los de run, por ejemplo de puede configurar afinidad de PCU, establecer ultimits, limitar la memoria ram en el proceso de construcción y muchas otras cosas.

Especificar un fichero en vez de usar el Dockerfile y se asigna un nombre (etiqueta) a la imagen. Si se quieren asignar varias etiquetas también es posible.

docker build -t nombre_imagen -f Dockerfile.debug .

# Construir imagen a partir de un repositorio remoto, este será clonado y usará el directorio raíz del mismo como directorio donde debe estar el Dockerfile.

docker build github.com/creack/docker-firefox

Si se está usando una construcción multietapas, se puede definir una etapa (definida en el FROM mediante “AS”) final en la que se terminará de construir la imagen.

docker build -t nombre_imagen --target etapa_XXX .

Construir una imagen a partir de un contenedor

La creación de imágenes debe hacerse desde ficheros Dockerfile, pero es posible también crear fines imágenes a partir de contenedores. Esto es útil para tareas de depuración de contenedores ya finalizados (y sobrescribir el comando con el que inician) o migrar algunos contenedores con datos a otro host. Los directorios con volúmenes montados no serán copiados a la imagen. Se pueden usar las instrucciones de los Dockerfile (CMD|ENTRYPOINT|ENV|EXPOSE|LABEL|ONBUILD|USER|VOLUME|WORKDIR) para modificar o ampliar la imagen resultante.

En el siguiente ejemplo se agrega información de autor, se agrega/sobrescribe el valor de EXPOSE, se asigna una descripción y por ultimo se indica el contenedor y el nombre de la imagen.

docker commit -a autor1 --change "EXPOSE 80/tcp" -m "imagen a partir de contenedor" 400d657d6848 imagentest/contenedor1

Exportar / Importar el contenido de una o varias imágenes en forma de fichero tar

# Exporta la imagen imagen2 a fichero.tar.
docker save imagen1 imagen2 -o fichero.tar
# Importa en la pila de imágenes, también soporta ficheros gzip, bzip2 y xz.
docker load --input fichero.tar

Exportar / Importar el contenido de un contenedor en forma de fichero tar para crear imágenes

Exporta el sistema de ficheros de un contenedor.
docker container export contenedor -o fichero.tar
# Importa un fichero tgz remoto y crea la imagen.
docker import http://example.com/exampleimage.tgz
# Importa la imagen pero se puede modificar mediante instrucciones Dockerfile usando "--change".
docker import --change "ENV DEBUG=true" /path/to/exampleimage.tgz

Muestra el historial de la imagen docker / Obtener información sobre las instrucciones del Dockerfile que generó esa imagen

Muy útil al permitir obtener el tamaño de cada capa asignada a cada instrucción del Dockerfile.

docker history ansible-alpine:latest

Directorio /var/lib/docker/ en Docker

El directorio “/var/lib/docker/” es gestionado por Docker y no se recomienda manualmente modificar ni eliminar nada.

/var/lib/docker/volumes/ Directorio de volúmenes, en el directorio _data se encuentra todo lo almacenado en ese volumen.

/var/lib/docker/overlay2/ Este directorio contiene las capas de las imágenes. Con “docker inspect” se puede obtener un listado de dichas capas que en conjunto forman la imagen. Cada capa es identificada por un hash y dentro contiene la información pertinente. El fichero “lower” contiene la pila de capas anteriores usadas como base. De ser la primera capa lógicamente le fichero lower no existirá. Mediante el comando “docker inspect” se pueden listas las capas (“GraphDriver” > “Data”: > “LowerDir”), siendo la primera la última capa usada y la última listada la primera usada como base primera (sin fichero lower).

/var/lib/docker/overlay2/l/ Contiene enlaces a los directorios de capas de “/var/lib/docker/overlay2”. Simplemente acortan los IDS para usar el comando mount más cómodamente. El nombre de estos enlaces se encuentra en el mismo directorio que la capa que enlaza, en el archivo “link”.

/var/lib/docker/containers/ En esta carpeta se guardan los directorios referentes a los contenedores, ahí se guardan sus logs (salida estándar), hostname, configuración DNS, configuración mediante ficheros config.v2.json y hostconfig.json (Usados por el comando “docker inspect”).

Logs de Docker

En Docker podemos diferenciar tres tipos de logs. Los generados por Docker diseñados para ser visualizados mediante journalctl o ficheros de logs, con información relevante sobre algunos eventos generados por el propio servicio Docker y comandos enviados al demonio a través de la API remota de Docker. Dependiendo de su sistema operativo, el archivo de registro del demonio Docker se almacena en diferentes ubicaciones.

Otro tipo de registros serían los generados mediante eventos y orientados a depuración, los cuales están accesibles desde el comando “docker system event” y por último, los propios logs generados por las aplicaciones que corren en el contenedor.

Logs de Docker Por norma se encuentran en /var/log/ y/o a través de journalctl -u docker.

Logs de eventos de Docker (en tiempo real)

El comando “docker system events” permite obtener registros en tiempo real de determinados eventos. Muy útil para depurar que está pasando por debajo con el entorno Docker. También permite usar el parámetro --since para especificar logs a partir de un determinado momento.

docker system events --since '2018-03-01'

Logs generados por las aplicaciones que corren los contenedores

Al usar terminales interactivas como proceso, los logs registrarán absolutamente todo lo que se muestra en la pantalla, los comandos introducidos y su salida. Si no se especifica ningún driver de log se usa json-file, el cual escribir los logs en formato json en el sistema de ficheros anfitrión.

# --log-driver=[none|json-file|syslog|journald|gelf|fluentd|awslogs|splunk|etwlogs|gcplogs|logentries]
docker run -it -a stdin -a stdout --log-driver=journald alpine sh # Ejemplo enviando los logs de un proceso shell a journald (systemd).

Los logs pueden ser siempre visualizados de manera amigable (menos cuando se usa --log-driver=none) desde la opción “logs” de docker, también cuando el contenedor ha terminado y no está en funcionamiento.El formato de salida es siempre amigable al margen del formato elegido en --log-driver.

docker logs 76311d2c554d
docker logs -t 76311d2c554d      # Indica la fecha y hora.
docker logs -f -t 76311d2c554d   # Indica la fecha y hora y queda a la espera de nuevas lineas de logs (-f).

NOTA: Depende del tipo de entornos, los contenedores pueden generar ficheros de logs muy grandes. Estos ficheros y su espacio no son mostrados en los comandos docker como “docker system df -v”, siendo recomendable limitar su tamaño / configurar rotaciones. Para ello puede configurarse en el fichero /etc/docker/daemon.json un límite de tamaño y número de rotaciones (mínimo una rotación). En el ejemplo mostrado el límite será de 1Gb, generando un fichero XXX-json.1 con los registros antiguos que hagan superar al fichero de log principal el tamaño de 1Gbyte.

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "1G",
    "max-file": "1" 
  }
}

Alpine: Paquetería

cat /etc/alpine-release   # Mostrar la version de Alpine.
cat /etc/apk/repositories # Repositorios.
 
apk update      # Actualizar indice de repositorios.
apk upgrade     # Actualizar todos los paquetes de Alpine.
apk -U upgrade  # Actualliza índice de repositorios y posteriormente los paquetes del sistema.
apk upgrade <paquete1> <paquete2> # Actualiza determinados paquetes.
 
apk add <paquete1> <paquete2>                 # Instalar paquete de los repositorios.
apk add --allow-untrusted /path/to/file.apk   # Instalar paquetes en local.
 
# Instalar un paquete desde otro repositorio que no esté en "/etc/apk/repositories"
apk add paquete1 --update-cache \
                 --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ \
                 --allow-untrusted
 
apk del <paquete1> <paquete2>                 # Desinstalar paquete.
 
NOTA: Por defecto crea los ficheros de configuración nuevos ".apk-new".
 
# Buscar paquetes.
 
apk search -v
apk search -v 'acf*'
apk search -v --description 'NTP'
 
apk list      # Muestra los paquetes disponibles a ser instalados (apk list | wc -l debería ser el mimo número de paquetes que "apk update" anuncia).
apk info -vv      # Muestra la lista de paquetes instalados.
apk info paquete  # Muestra información sobre un paquete.

Tipos de redes en Docker

Por defecto, todas las instalaciones de Docker vienen con tres redes creadas por defecto, bridge, host y none.

bridge: Si no especifica un controlador, la red predeterminada bridge es el tipo de red usado. Las redes puente se utilizan normalmente cuando sus aplicaciones se ejecutan en contenedores independientes que necesitan comunicarse. Son las recomendadas cuando se necesita que varios contenedores se comuniquen en el mismo host Docker. Por defecto en entornos Docker siempre hay una red bridge considerada la predeterminada.Esta red bridge predeterminada no tiene tantas opciones como las redes bridge creadas explícitamente por el usuario, las cuales disfrutan de resolución DNS, conexión / desconexión a otras redes en caliente y otra serie de ventajas. Para eliminar un contenedor de la red puente predeterminada, es necesario detener el contenedor y volver a crearlo con diferentes opciones de red. La configuración de la red puente por defecto ocurre fuera de Docker mismo, y requiere un reinicio del servicio.

host: En el caso de los contenedores autónomos, elimina el aislamiento de red entre el contenedor y el host Docker, y utiliza la red del host directamente. Se recomienda su uso cuando la pila de red no debe estar aislada del host Docker, pero se quiere que otros aspectos del contenedor estén aislados.

overlay: Las redes superpuestas (overlay) conectan varios demonios Docker juntos y permiten que los servicios del enjambre se comuniquen entre sí. También puede utilizar las redes superpuestas para facilitar la comunicación entre un servicio de enjambre y un contenedor autónomo, o entre dos contenedores autónomos en diferentes daemons Docker. Esta estrategia elimina la necesidad de realizar un enrutamiento a nivel de sistema operativo entre estos contenedores.

macvlan: Este tipo de redes permiten asignar una dirección MAC a un contenedor, haciéndolo aparecer como un dispositivo físico en su red. Se usan para comunicar el mundo docker con cualquier otra red / dispositivo externo, por ejemplo que los contenedores estén dentro de la misma red que el host. El demonio Docker enruta el tráfico a los contenedores por sus direcciones MAC. El uso del controlador macvlan es a veces la mejor opción cuando se trata de aplicaciones heredadas que esperan estar directamente conectadas a la red física, en lugar de ser enrutadas a través de la pila de red del host. Son las recomendadas cunado se necesita que los contenedores parezcan hosts físicos en la red, cada uno con una dirección MAC única. Para que este tipo de redes funcione necesitamos que la tarjeta de red del host esté en modo promiscuo,

ninguno:Desactiva todas las redes en el contenedor, los contenedores no tendrá interfaz de red, solo loopback. Normalmente se utiliza junto con un controlador de red personalizado. none no está disponible para los servicios de enjambre.

Plugins de red: Los complementos de red de terceros permiten integrar Docker con pilas de red especializadas. Puedes instalar y utilizar plugins de red de terceros con Docker. Estos plugins están disponibles en Docker Hub o en proveedores de terceros.

docker network create -d overlay XXX     # Crea una red del tipo overlay.
docker nework rm XXX                     # Elimina la red XXX.
docker network ls                        # Lista las redes del host.
docker network prune                     # Borra las redes que no estén siendo usada por ningún contenedor.
docker network connect XXX container1    # Conecta un contenedor a la red XXX.
docker network disconnect XXX container1 # Desconecta un contenedor a la red XXX.
 
docker network inspect XXXX      # Muestra información sobre la red. La información sobre contenedores siempre es relativa al host
                                 # Por ejemplo en redes overlay NO se mostrarán los contenedores de otros hosts.
                                 # Sí se muestran la IP de los nodos (Peers) que implementan dicha red.

Entendiendo el funcionamiento de la red bridge y su relación con las interfaces del host

La interfaz docker0 hace referencia a esa red puente predeterminada de la que se hablo anteriormente. Cada contenedor tiene una interfaz que se conectada a esa red y es por tanto accesible desde el host en el que se ejecuta el contenedor. El contenedor o contenedores usan dicha interfaz docker0 como ruta predeterminada para salir a internet. Veamos un ejemplo práctico para entender mejor como funciona.

# Se crea un contenedor y se consultan sus interfaces: eth0@if68308 coincidirá con alguna interfaz de red creada en el host.
docker run -it alpine sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
68307: eth0@if68308: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:1f:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.31.0.2/24 brd 172.31.0.255 scope global eth0
       valid_lft forever preferred_lft forever
 
# La puerta de enlace predeterminada del contenedor coincidirá con la IP que tiene la interfaz puente docker0.
/ # ip route
default via 172.31.0.1 dev eth0    
172.31.0.0/24 dev eth0 scope link  src 172.31.0.2
 
 
# Sobre el mismo host donde corre el contenedor creado anteriormente se visualizan los espacios de nombres de red.
# El espacio cd13b758f285 se creó al momento de crear el contenedor.
cd /var/run
ln -s /var/run/docker/netns netns
ip netns
 
cd13b758f285 (id: 0) 
default
 
 
# Se consulta la configuración de interfaces del espacio de red que está usando el contenedor: cd13b758f285. 
# Como se puede apreciar es la misma salida que al ejecutar ip addr sobre el contenedor.
ip netns exec cd13b758f285 ip addr
 
 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
68307: eth0@if68308: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:1f:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.31.0.2/24 brd 172.31.0.255 scope global eth0
       valid_lft forever preferred_lft forever
 
 
# Se consultan las instancias de puentes en el host. En este caso la interfaz puente docker0 está conectada con la interfaz virtual veth47cd93c.
brctl show
 
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242cb1bbacf	no		veth47cd93c  veth47cd93c.
 
 
# Se consulta las interfaces e IPs del host para poder ver la relación entre la nueva interfaz virtual veth47cd93c y la interfaz eth0 del contenedor.
ip addr
 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:af:69:96 brd ff:ff:ff:ff:ff:ff
    inet 10.0.201.26/21 brd 10.0.207.255 scope global ens160
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:feaf:6996/64 scope link 
       valid_lft forever preferred_lft forever
15: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:cb:1b:ba:cf brd ff:ff:ff:ff:ff:ff
    inet 172.31.0.1/24 brd 172.31.0.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:cbff:fe1b:bacf/64 scope link 
       valid_lft forever preferred_lft forever
68308: veth47cd93c@if68307: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 76:10:01:2f:12:8d brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::7410:1ff:fe2f:128d/64 scope link 
       valid_lft forever preferred_lft forever

La interfaz eth0@if68308, del contenedor se refiere a la interfaz virtual del host veth47cd93c@if68307, la cual fue creada al arrancar el contenedor en la red predeterminada. A su vez, como vimos con el comando brctl, está conectada a la interfaz bridge docker0 para poder comunicarse con el host y salir a internet.

De no usarse la red puente predeterminada de Docker y haber creado redes puente explícitamente, la interfaz docker0 no se usaría, si no que se crearía una interfaz puente con nombre “br-xxxxx”. Las redes virtuales veth siempre serían creadas ya que coincidirían con las interfaces de los contenedores creados en dicha red, las cuales conectan a la interfaz puente “br-xxxxx”.

Por lo tanto una red puente unicamente tendrá una interfaz “br-xxxx” en el host, pero el host tendrá tantas interfaces veth como contenedores se tengan en dicha red (suponiendo que solo tienen una interfaz). Las interfaces virtuales en el host, el espacio de red y la conexión con la interfaz puente docker0 o br-XXXXX desparecen cuando el contenedor deja de estar activo.

SBOM de las imagenes de Docker

Una lista de materiales de software (SBOM) enumera todos los componentes que conforman un software (o que se utilizaron para construirlo). En el caso de imágenes de contenedores, esto incluye paquetes instalados (por ejemplo la bash, los certificados ca, git, etc) junto con sus dependencias, por ejemplo, Log4j. Cada herramienta SBOM puede incluir en su salida diferentes subconjuntos de esta información o incluso más detalles, como versiones de los componentes y su fuente. En Docker se puede usar el siguiente plugin para generar reportes sbom, muy utiles a la hora de buscar aplicaciones vulnerables. Aunque si se quiere obtener más información se recomienda el uso de syft directamente.

curl -sSfL https://raw.githubusercontent.com/docker/sbom-cli-plugin/main/install.sh | sh -s --
docker sbom  XXXX

Ejemplo.

docker sbom zricethezav/gitleaks
Syft v0.43.0
 ✔ Loaded image            
 ✔ Parsed image            
 ✔ Cataloged packages      [49 packages]
NAME                                VERSION                             TYPE      
alpine-baselayout                   3.2.0-r16                           apk        
alpine-keys                         2.3-r1                              apk        
apk-tools                           2.12.7-r0                           apk        
bash                                5.1.4-r0                            apk        
brotli-libs                         1.0.9-r5                            apk        
busybox                             1.33.1-r3                           apk        
ca-certificates                     20191127-r5                         apk        
ca-certificates-bundle              20191127-r5                         apk        
expat                               2.4.1-r0                            apk        
git                                 2.32.0-r0                           apk        
github.com/fsnotify/fsnotify        v1.4.9                              go-module  
github.com/gitleaks/go-gitdiff      v0.7.4                              go-module  
github.com/hashicorp/hcl            v1.0.0                              go-module  
github.com/magiconair/properties    v1.8.5                              go-module  

Usando diferentes opciones de formato de salida se puede obtener más información, por ejemplo nombre, licencia, referencias externas, autor, etc.

docker sbom --format spdx zricethezav/gitleaks
Syft v0.43.0
 ✔ Loaded image            
 ✔ Parsed image            
 ✔ Cataloged packages      [49 packages]
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: zricethezav/gitleaks-latest
DocumentNamespace: https://anchore.com/syft/image/zricethezav/gitleaks-latest-cafe4ad2-56b1-4618-8193-3c159f5832de
LicenseListVersion: 3.16
Creator: Organization: Anchore, Inc
Creator: Tool: syft-[not provided]
Created: 2022-04-20T08:48:22Z
 
##### Package: alpine-baselayout
 
PackageName: alpine-baselayout
SPDXID: SPDXRef-Package-apk-alpine-baselayout-ed18f2a986e77aab
PackageVersion: 3.2.0-r16
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageLicenseConcluded: GPL-2.0-only
PackageLicenseDeclared: GPL-2.0-only
PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*
ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:*
ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine_baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*
ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine_baselayout:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:*
ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*
ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:*
ExternalRef: PACKAGE_MANAGER purl pkg:alpine/alpine-baselayout@3.2.0-r16?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.14.2
...