📘 Docker: La guía "re"finitiva
⚠️ ⚠️ ⚠️ Esta guía está desordenada, es el resultado de varios cursos, investigación por mi cuenta e ir apuntando pasos. La idea es ir ordenándola poco a poco. Puede contener errores, repeticiones y no ser exacta.
Componentes
- Docker Host: Máquina que ejecutará docker.
- Docker CLI: Cliente con el que conectamos y administramos dockers.
- Rest API: Permite administrar docker con peticiones GET/POST.
- Docker Daemon: El servidor de docker.
Contenedores
Un contenedor es una capa donde se ejecutan las imágenes.
Las imágenes son la base del sistema, se las supone stateless (sin estado) y se usan para desplegar varios contenedores. Una imagen es de sólo lectura mientras que el contenedor permite escritura.
Un contenedor es temporal. Si queremos guardar los cambios necesitamos un volumen de datos. Además de RAM y procesador, un contenedor puede emplear: Imágenes, volúmenes y redes.
Los volúmenes permiten almacenar información a los contenedores. Esta información no debería almacenarse directamente en el contenedor, ya que de borrarse, se eliminarían los datos.
Un contenedor puede tener acceso a todos los procesadores y RAM del host (por defecto) o se pueden establecer límites.
Pueden crearse varias redes para relacionar unos contenedores con otros. Un docker puede estar en una o varias redes.
Ejemplo: Lanzar el servidor de BBDD MariaDB oficial
docker run --detach --name maria1 --env MARIADB_USER=unusuario \
--env MARIADB_PASSWORD=unacontrasena \
--env MARIADB_ROOT_PASSWORD=clavederoot \
mariadb:latest
Asociar una carpeta local a una carpeta del contenedor
Por ejemplo: /tmp/mariadatos
al /var/lib/mysql
del docker
Este ejemplo crea un contenedor con la última versión de mariadb y por medio de las variables de entorno que permite su imagen, le pasa un usuario, una clave, una clave de administrador y asocia una carpeta de nuestro host dentro del contenedor.
docker run -v /tmp/mariadatos:/var/lib/mysql/ --detach \
--name maria2 --env MARIADB_USER=unusuario \
--env MARIADB_PASSWORD=unacontrasena \
--env MARIADB_ROOT_PASSWORD=clavederoot \
mariadb:latest
Comando run
:
Descarga una imagen (si no existe) y crea e inicia el contenedor.
Opción --detach
:
Libera la terminal, no nos deja la consola bloqueada, lo pasa a segundo plano.
Pasar una variable de entorno:
Leyenda: -e: Environment.
Asociar carpeta del contenedor a un volumen
docker run -v VOLUMENNUEVO:/var/lib/mysql/ \
--detach --name maria3 \
--env MARIADB_USER=unusuario \
--env MARIADB_PASSWORD=unacontrasena \
--env MARIADB_ROOT_PASSWORD=clavederoot \
mariadb:latest
Volúmenes
Existen tres tipos básicos de volúmenes:
- Volúmenes Host.
- Volúmenes anónimos.
- Volúmenes nombrados.
Por defecto se guardan en: /var/lib/docker/volumes
Conviene acceder a ellos sólo con herramientas de docker. https://docs.docker.com/storage/volumes/
Comandos útiles (se verán de nuevo en los ejemplos)
Por defecto emplearemos el driver local
para los volúmenes, sin embargo hay otras opciones como flocker o convoy con opciones interesantes. Por ejemplo: Sacar snapshots del volumen de datos.
Se permiten pasar opciones a volume
para especificar tamaño (o guardar el volumen de forma volátil con tmpfs)
Nombrado
Para acceder a los contendores o bien podemos referirnos por su nombre (crea un nombre aleatorio si no le especificamos uno con --name
) o por su CONTAINER ID
. En el caso del CONTAINER ID
podemos escribir tan sólo los dos o tres primeros caracteres (siempre que no haya otro contenedor en el que coincidan estos y con ello se pueda inferir inequívocamente el contenedor referenciado).
Ejemplos de comandos básicos
En CONTENEDOR puede valer el nombre asignado o los primeros dígitos/letras de su identificador ¿Cuantos caracteres hay que poner del identificador? Fácil, tantos como permitan identificarlo de forma única.
Listar volúmenes:
Listar imágenes:
Listar contenedores (en ejecución). Opciones:
Listar contenedores (en ejecución y finalizados). Opciones:
Parar un contenedor:
Inciar un contenedor (que esté previamente creado y exista):
Volúmenes. Creación explícita
Volúmenes. Ver información
Asociar un volumen a un contenedor (en la creación del contenedor)
Mapear/Redirigir un puerto
Si necesitamos asignar un puerto del anfitrión al contenedor, podemos hacerlo con la opción: -p PUERTO_ANFITRIÓN:PUERTO_INVITADO
.
Borrar un contenedor
Borrar un volumen
Al borrar un contenedor, por defecto, no se borra el volumen asociado.
Borrar una imagen
Al borrar un contenedor, por defecto, no se borra la imagen asociada a partir de la cual se ha hecho el contenedor.
Salvo que forcemos el borrado, no se puede borrar una imagen si tiene asociado algún contenedor.
Ejecutando un comando dentro del contenedor
Una de los casos más típicos es ejecutar un bash para cambiar o consultar algo dentro del contenedor.
Crear el contenedor y que nos devuelva una terminal:
-it
: nos conecta (attach) a una sesión interactiva de forma que podamos ver e interactuar con ella).run
: Descarga la imagen, crea el contenedor y lo arranca.
Conectarnos a una terminal de un contenedor ya creado:
exec
: Ejecuta el comando /bin/bash en el CONTENEDOR.
En el caso que no necesitemos una sesión interactiva, podemos ejecutar un comando directamente:
La diferencia entre docker run
y docker exec
es que docker exec
ejecuta el comando que le especifiquemos en un contenedor ya creado y en ejecución, mientras que docker run
crea y ejecuta un contenedor temporal, ejecuta el comando y para el contenedor cuando acaba.
Bajar la imagen
Este comando baja la imagen sin usarla en ningún contenedor. Con posterioridad podrá ser utilizada.
Archivo Dockerfile
Ejemplo Dockerfile:
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ESXPOSE 3000
CMD ["node", "server.js"]
FROM
-
Inicia una nueva etapa de construcción:
FROM <image>[:<tag>][AS <NAME>]
-
AS <NAME>
nombra la etapa de compilación. - Puede aparecer varias veces (varias imágenes o indicar dependencia).
- Los tags son opcionales, por defecto
latest
.
Se pueden tener múltiples fases de construcción (multistage). Por ejemplo: La primera baja de un repo, baja dependencias, ejecuta maven/ant/compila y la segunda con el resultado generado (por ejemplo un jar) puede construir una imagen mínima con ese jar y una MV de java para su ejecución.
Es decir, podríamos en este ejemplo tener una máquina para compilar un programa (con todas las dependencias nesarias) y otra con las dependencias mínimas para ejecutar ese programa.
RUN
Ejecuta un comando en la capa actual. Por defecto se emplea:
- En GNU/Linux: /bin/sh -c
- En Microsoft Windows: cmd /S /C
COPY/ADD
COPY/ADD [--chown=<user>:<group>] <src> ... <dest>
<src>
: El origen del archivo.-
<dest>
: Indica la ruta dentro del contenedor. -
GNU/Linux admite parámetro
chown
. -
ADD
permite copiar un archivo desde una URL. Si src es un archivo comprimido, lo descomprime en destino.
ENTRYPOINT
El ejecutable del contenedor
Ejemplo: ENTRYPOINT ["executable","param1","param2"]
Si luego hay un CMD, se le pasa al entrypoint.
CMD
Sólo puede haber un CMD en el Dockerfile
. Si hay más de uno, sólo se aplicará el último.
Indica el comando que levanta el servicio. Debe levantarse en primer plano para mantener vivo el contenedor.
Ejemplo: CMD ["executable","param1","param2"]
ENV
Sirve para indicar variables de entorno con valores por defecto. El usuario puede cambiar estas variables para personalizar los ajustes a su gusto.
Otros
El archivo .dockerignore
permite que no se copien al contenedor los archivos y/o directorios especificados como no necesarios para acelerar el proceso de construcción.
Contenedores
- Variables de entorno:
-e
- Limitar recursos
Limitar memoria:
Limitar al procesador 0 (el primero):
Renombrar un docker
Copiar archivos desde/hacia contenedor/host
Docker logs
Nos va a permitir ver los logs de un contenedor.
- -f
permite verlos en tiempo real
- -t
añade el timestamp al log (aunque la aplicación de log no lo haga). Útil para tiempos sincronizados.
Crear unha imagen a partir de un contenedor
Construir una imagen
Docker commit
Permite crear una imagen a partir de un contenedor.
Ejecutar una imagen en un contenedor
Imágenes propias
Si creas una cuenta en dockerhub puedes subir tu imagen:
Con eso puedes bajar la imagen desde cualquier sitio:
En base a una imagen se pueden crear varios contenedores, por ejemplo:
Cuando eliminamos un contenedor, su sistema de archivos e información también son eliminadas.
Si se quieren guardar los datos al eliminar el docker, debemos guardar los volúmenes de docker (normalmente se crean mediante opción de forma explícita o se puede asociar un directorio).
También se pueden crear redes para comunicar distintos dockers entre ellas.
Podemos utilizar docker-compose
(o docker compose
, dependiendo de la versión) cuando queremos que varios contenedores de docker se inicien al mismo tiempo (y además establecer dependencias entre ellos).
Para administrar muchos contenedores, podemos emplear orquestadores (por ejemplo Kubernetes).
Ejemplos útiles de dockers: - Imagen de gitlab: https://docs.gitlab.com/ee/install/docker.html - Imagen de tensorflow/serving: https://www.tensorflow.org/install/docker?hl=es-419
Pets vs cattle (mascotas versus ganado)
Si un servidor se cae, debería ser reemplazo automáticamente y de forma transparente al usuario (los servidores son ganado).
Para esto ayuda tener la aplicación distribuída en microservicios.
Si la caída de un servidor origina un problema probablemente tengas una mascota, puesto que seguramente conozcas a ese servidor por su nombre: Afrodita, Zeus, MrBurns... lo mantengas y arregles cuando falla y planifiques su mantenimiento como pieza crítica en tu organización.
Si por el contrario tienes un grupo de servidores sin nombre o con nombres: svr1, svr2... del modo en que se ponen etiquetas en las orejas del ganado, a los que aplicas configuraciones automáticas (probablemente a todos las mismas) cuando uno cae, simplemente lo reemplazas por otro, como el ganado. Ejemplos de esto pueden ser: Clusters NO-SQL, servidores de bigdata, clusters de búsqueda, etc.
Las organizaciones que poseen hardware en sus instalaciones, tienen hoy día varios servidores en su rack y necesitan automatizar su instalación, configuración y uso. Existen herrameintas como Kubernetes, Pupper, Chef o Ansible que permiten automatizar y sacarle más rendimiento a nuestro ganado.
Más información:
- https://www.hava.io/blog/cattle-vs-pets-devops-explained
- https://www.ilimit.com/blog/orquestracion-cloud-pets-cattles/
Imágenes de docker
- Docker Hub
- Dockerfile: From, Run, Copy, Add, Entrypoint, Cmd y .dockerignore
- Danling Images: Imágenes no referenciadas, sin nombre o tag pero ocupan espacio.
Redes en docker
Existen 5 tipos de driver: - none - bridge - host - macvlan - ipvlan - overlay
https://docs.docker.com/network/
Por defecto se usa bridge (crea el dispositivo: docker0).\ No se puede hacer ping por nombre de contenedor (habrá que poner la IP).
docker network create NOMBRE --subnet 192.168.0.0/24 --gateway 192.168.0.1
docker network ls
docker run -d (--network NOMBRE) nginx (--ip 192.168.0.200) .
En las redes que nosotros creamos se permite hacer ping por nombre de host.
Añade contenedor a una red:
Estadísticas
Nos permiten ver estadísticas del contenedor: RAM, procesador en uso, uso de red y si tienen algún límite de uso de RAM.
Docker Compose ó docker-compose
Para organizar/orquestar contenedores. Crear contenedores que trabajen juntos. Es decir, aplicaciones multicontenedor para deplegar rápidamente. Usa docker-compose.yml. Emplea formato YAML.
Etiquetas en docker-compose.yml
- version
- services
- volumes
- networks
Moviendo dockers
-
Opción 1: Exportando e importando
-
Opción 2: Migrando la imagen
-
Opción 3: Salvando y creando imágenes
-
Opción 4: Hacer backup del volumen de datos y restaurarlo
Si queremos mover varios todos los dockers, otra opción sería copiar el directorio de trabajo de docker:
/var/lib/docker
Habría que tener en cuenta que debemos:
- Tener la misma versión de docker en los host de origen y destino.
- Preservar los permisos y propietarios de archivos y carpetas.
- Parar el servicio docker antes de copiar nada:
service socker stop
- Si hay otras rutas a carpetas, deben existir, tener los permisos adecuados y asegurarnos que no dan otros problemas.
Lo lógico en estos casos es planificar la migración, probarla y ejecutar finalmente un script de migración con los pasos necesarios para minimizar el tiempo de esta migración.
Extensiones para VSCODE
- Una extensión para gobernarlas a todas: Remote development
- https://code.visualstudio.com/remote/advancedcontainers/develop-remote-host
- https://code.visualstudio.com/docs/devcontainers/containers#installation
- https://code.visualstudio.com/docs/remote/ssh#_connect-to-a-remote-host
Contenedores interesantes para probar
- rednode
- jenkins
- tensorflow
Ejemplo con docker compose. Moodle:
Bajar el docker-compose.yml
de moodle hecho por bitnami.
curl -sSL https://raw.githubusercontent.com/bitnami/containers/main/bitnami/moodle/docker-compose.yml > docker-compose.yml
Lanzalo:
Herramientas relacionadas con dockers
- Docker Desktop: Cliente oficial de docker en modo gráfico. Para Windows/Linux/Mac. Es de pago para empresas grandes.
- Portainer.io: Panel de control web para contenedores (de pago). Gratis 5 contenedores con registro previo
- Podman.io: "Sustituto" de docker. Se ejecuta como programa (y no demonio o servicio). Con o sin root ejecuta los contenedores usando libpod (imágenes, volúmenes, contenedores "pod"). También tiene su versión podman desktop, que es opensource. Mantenido por RedHat. Herramientas relacionadas: Buildah, Skopeo. https://www.redhat.com/es/topics/containers/what-is-podman
- Rancher Desktop: Cliente open-source para gestión de Containers y Kubernetes para Windows, Linux y Mac.
- vaultproject.io: Hashicorp vault. Permite almacenar credenciales y usarlas como parte del proceso CD/CI para no tener que almacenarlas en el repositorio directamente.
PLONK Stack
Consiste en los siguientes elementos:
-
Prometheus.io: Monitorea y gestiona alertas. Soporta métricas en tiempo real y usa una BBDD con series temporales.
-
Linux:
-
OpenFaaS: "Function as a service". Haciendo uso de una arquitectura de microservicios podemos desarrollar, ejecutar y administrar funcionalidades en una aplicación olvidándonos de la parte de la infraestructura ("serverless"). Ejemplos en los que se puede aplicar esta técnica son: procesos batch (por lotes), procesamiento en stream (flujo), procesos ETL, IoT (Internet de las cosas), apps para móviles, aplicaciones web, APIs, etc. La diferencia con PaaS es que el desarrollador/admnistrador ni siquiera debe preocuparse por el escalado o aumentar los servidores.
-
NATS: Mensajería de alto rendimiento. Comunica sistemas/procesos/servicios. Partes: NATS Core, Jet Stream.
-
Kubernetes: Orquestador para desplegar los contenedores en servidores.
Más información: https://www.openfaas.com/blog/plonk-stack/