====== Docker ======
Herramienta que permite desplegar aplicaciones en contenedores de forma rápida y portable.
* [[https://www.docker.com/|Web oficial]]
Utiliza contenedores e imágenes. En las imágenes se define toda la configuración del software, bibliotecas, etc., todo lo que necesita la aplicación para funcionar. En un contenedor se vuelve realidad.
* Aplicaciones de bolsillo
* Desplegar y escalar aplicaciones
* Destruir y recrear.
Ejemplo de uso:
docker run -d -p 80:80 --name web httpd
Para eliminarlo:
docker rm -fv web
===== Arquitectura de Docker =====
{{ :informatica:sistemas_operativos:virtualizacion:docker:docker-arquitectura.jpg?nolink |}}
===== Imágenes =====
Una imagen es un paquete que contiene toda la configuración necesaria para que funcione el servicio/aplicación.
Las imágenes se componen de capas y son de solo lectura. En la primera capa definimos qué sistema operativo tendremos en la imagen.
Las capas se definen través de un fichero ''Dockerfile'':
FROM centos:7
# Instalación del servidor web Apache
RUN yum -y install httpd
# Definimos el comando que ejecutará Apache. Es importante que se ejecute en primer plano o el contenedor finalizará en el momento en que se haya ejecutado lo que hay en ''CMD''
CMD ["apachectl", "-DFOREGROUND"]
Cuando no se especifica una etiqueta/tag, por defecto se descarga la ''latest''
==== Imágenes oficiales ====
Las imágenes se alojan en el [[https://hub.docker.com/|Docker Hub]]
Para descargarlas:
docker pull nombre-imagen
Para saber las imágenes que tenemos descargadas o en el sistema:
docker images
Para eliminar una imagen:
docker rmi nombre-imagen|identificador-imagen
==== Imágenes propias ====
Creamos un fichero ''Dockerfile'':
FROM centos:latest
RUN yum -y install httpd
Para convertirla en imagen:
docker build --tag nombre-nueva-imagen .
Para ver las capas que se crearon en una imagen:
docker history -H nombre-imagen
==== Operaciones ====
Eliminar imágenes:
docker rmi nombre-imagen
Imágenes huérfanas:
docker images -f dangling=true
Y para eliminarlas:
docker images -f dangling=true -q | xargs docker rmi
Para eliminar las imágenes que no están en uso ni referenciadas también se puede utilizar ''docker image prune''
==== Dockerfile ====
Fichero para la creación de imágenes.
* ''FROM'': imagen de base
* ''RUN'': cualquier comando disponible en Linux
* ''COPY'': copia archivos desde la máquina local hacia la imagen (opción recomendada por Docker).
* ''ADD'': copia archivos desde la máquina local o remota (se puede indicar una URL) hacia la imagen.
* ''ENV'': variables de entorno
* ''WORKDIR'': directorio actual de trabajo en la imagen
* ''EXPOSE'': puerto que se expondrá desde el contenedor.
* ''LABEL'': etiquetas para mostrar alguna información, metadatos.
* ''USER'': indica el usuario que ejecutará la tarea (por defecto es root)
* ''VOLUME'': permite indicar qué datos serán persistentes en la creación del contenedor (volúmenes anónimos).
* ''CMD'': comando o script para mantener un contenedor en ejecución
FROM centos
LABEL version=1.0
LABEL description="This is an apache imagen"
LABEL vendor=yo
RUN yum install httpd -y
WORKDIR /var/www
# No ponemos /var/www/html como destino de la copia porque
# con WORKDIR nos hemos posicionado en /var/www, así que solo
# quedaría indicar html
COPY app html/
# variable valor
ENV contenido prueba
RUN echo "$contenido" > /var/www/html/prueba.html
RUN useradd pepito
# A partir de aquí, todo lo que se ejecuté, será por el
# usuario pepito
USER pepito
RUN echo "$(whoami)" > /tmp/user.html
# Volvemos a ser root
USER root
RUN cp /tmp/user.html /var/www/html/user.html
CMD apachectl -DFOREGROUND
En ''CMD'' solo se puede indicar un comando, así que si necesitamos más, habría que crear un script con todo lo necesario y luego colocar ese script en ''CMD''
Con el Dockerfile creado, para crear la nueva imagen a partir de él:
docker build -t apache .
Si nuestro Dockerfile tiene otro nombre, hay que indicarlo a la hora de crear la imagen:
docker build -t prueba -f mi-dockerfile .
==== .dockerignore ====
Fichero que permite ignorar cualquier cosa que esté en el directorio actual y no incluirlo en una imagen:
fichero-ignorado
==== Multi-Stage Build ====
Permite utilizar varios ''FROM'' en el Dockerfile:
# La imagen de centos ya ocupa unos 200 MB
FROM centos as test
RUN fallocate -l 10M /opt/file1
RUN fallocate -l 20M /opt/file2
RUN fallocate -l 30M /opt/file3
# La imagen de alpine son poco más de 4 MB
FROM alpine
COPY --from=test /opt/file2 /opt/file
Cuando se cree la imagen, tendrá el tamaño de la imagen ''alpine'' y el fichero que hemos copiado de la anterior imagen
==== Buenas prácticas ====
* Un contenedor por servicio
* ''.dockerignore''
* Pocas capas
* Multilínea (''\'') para facilitar la lectura
* Varios argumentos en una sola capa
* No instalar paquetes innecesarios
* Utilizar labels para añadir metadatos a la imagen
===== Contenedores =====
Sería la siguiente capa de una imagen. Ejecución de la imagen. Es de lectura y escritura. Podemos modificar cosas en el contenedor, pero no en la imagen.
Un contenedor contiene:
* Imagen
* Volúmenes
* Redes
==== Creación ====
Para crear un contenedor:
docker run -d -p 80:80 --name mi-contenedor imagen
* ''-d'': ejecuta el contenedor en segundo plano.
* ''-p'': puerto local:puerto de la imagen
* ''%%--%%name'': nombre que tendrá el contenedor
Podemos ver los contenedores en ejecución:
docker ps
Si queremos ver también los que se han detenido o finalizado:
docker ps -a
==== Eliminar ====
docker rm -f nombre-contenedor
Si queremos eliminar todos a la vez:
docker rm -fv $(docker ps -aq)
También se pueden eliminar los contenedores detenidos con ''docker container prune''
==== Renombrar ====
docker rename nombre-antiguo nombre-nuevo
==== Detener ====
docker stop nombre-contenedor|id-contenedor
==== Iniciar ====
docker start nombre-contenedor|id-contenedor
==== Entrar en el contenedor ====
docker exec -it contenedor bash
Si queremos entrar como cierto usuario:
docker exec -u root -it contenedor bash
El usuario tiene que existir en la imagen o en el contenedor.
==== Variables de entorno ====
Si queremos especificar variables de entorno a la hora de crear un contenedor:
docker run -d -e "prueba:1234" --name mi-contenedor imagen
Es útil para imágenes que ya tienen definidas algunas variables:
docker run -d -e "MYSQL_ROOT_PASSWORD=123456" mysql
==== Copiar archivos ====
Si queremos copiar archivos desde nuestro equipo local hacia el contenedor:
docker cp fichero1 mi-contenedor:/ruta/contenedor/fichero1
Para copiar desde el contenedor a nuestra máquina:
docker cp mi-contenedor:/ruta/contenedor/fichero1 fichero1
==== Eliminar contenedores automáticamente ====
Para indicar que el contenedor que queremos crear es temporal y que al finalizar, lo borre:
docker run --rm -ti --name mi-contenedor imagen bash
Cuando salgamos del terminal bash del contenedor, Docker lo eliminará.
==== Recursos consumidos ====
docker stats nombre-contenedor|id-contenedor
Ejemplo de salida:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
9aa067025bbe cloud.tempwin.net 0.00% 121.2MiB / 7.791GiB 1.52% 669MB / 375MB 0B / 0B 146
Para limitar el uso de RAM que podrá hacer un contenedor:
docker run -d --memory "1gb" --name mi-contenedor imagen
Para limitar el número de CPUs:
docker run -d --cpuset-cpus 0-1 --name mi-contendor imagen
Le hemos indicado que ese contenedor solo podrá usar la CPU 0 y la CPU 1
==== Registros ====
Para centrarnos en los registros de un único servicio:
docker logs -t -f
===== Volúmenes =====
Herramienta para almacenar datos del contenedor de forma persistente en nuestra máquina local.
==== Volúmenes de host ====
Residen en una carpeta del sistema de ficheros de la máquina local
docker run -d -v /opt/mysql:/var/lib/mysql -p 3306:3306 --name mi-contenedor mysql
De esa manera indicamos que el directorio ''/var/lib/mysql'' del contenedor va a estar disponible desde el directorio ''/opt/mysql'' de la máquina local.
==== Volúmenes anónimos ====
docker run -d -v /var/lib/mysql -p 3306:3306 --name mi-contenedor mysql
Al no definir en qué carpeta quedará asociada la del contenedor, Docker le dará una al azar.
No es recomendable utilizar este tipo de volúmenes ya que, además de tener nombres difíciles de memorizar, en el momento de eliminar el contenedor, Docker borrará el volumen también.
==== Volúmenes nombrados ====
Volúmenes creados por el usuario.
Para crear un volumen llamado ''mysql-data'':
docker volume create mysql-data
Para eliminarlo:
docker volume rm mysql-data
Para asociarlo a un contenedor:
docker run -d --name mi-contenedor -v mysql-data:/var/lib/mysq -p 3306:3306 mysql
La ventaja de este tipo de volúmenes es que no se borran al eliminar el contenedor
==== Volúmenes huérfanos ====
Se llaman volúmenes //dangling//, no están referenciados por ningún contenedor. Para encontrarlos:
docker volume ls -f dangling=true
Para borrarlos todos de una vez:
docker volume ls -f dangling=true -q | xargs docker volume rm
* ''-q'': muestra el identificador del volumen
===== Redes =====
Con la instalación de Docker se crea una nueva interfaz de red:
$ ip a | grep docker
11: docker0: mtu 1500 qdisc noqueue state DOWN group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
Todos los contenedores que creemos, estarán dentro de esa red:
docker inspect nombre|id-contenedor
Consultamos el valor //IPAddress// para saber la IP que usa el contenedor
La red por defecto se llama **bridge**
Para ver todas las redes:
docker network ls
==== Tipos ====
* **Bridge**
* **Host**: la red de la propia máquina
* **None**: los contenedores que se metan ahí no tendrán red
* **Overlay**
==== Creación ====
docker network create nombre-red
Podemos configurar la subred:
docker network create --subnet 172.120.0.0/24 --gateway 172.120.0.1 nombre-red
==== Inspeccionar ====
docker network inspect nombre-red
==== Eliminación ====
docker network rm nombre-red
Para poder eliminar una red, no debe haber ningún contendor que esté usándola
==== Asignación ====
Si queremos crear un contenedor en una determinada red:
docker run --network nombre-red -d --name mi-contenedor centos
En las redes creadas por el usuario, podemos ver los contenedores por su nombre.
Esto no es posible en la red por defecto de Docker, solo se podría por IP
==== Asignar IP ====
docker run --network mi-red --ip 172.120.0.10 -d --name mi-contendor imagen
==== Conectar contenedor distintas redes ====
docker network connect nombre-red contenedor
De esa manera, indicamos a Docker que ''contenedor'' también está en la red ''nombre-red''
Si queremos desconectarlo de cierta red:
docker network disconnect nombre-red contenedor
===== Docker Compose =====
Herramienta que ayuda a crear aplicaciones multicontenedor. Aplicaciones como aplicaciones web utilizan bases de datos, algún lenguaje de programación y servidor web. Crearíamos un contenedor por cada elemento necesario. En lugar de hacerlo por separado, Docker Compose nos permite definirlo en un único archivo de texto y él gestionará la creación de contenedores, volúmenes, redes, etc.
==== Instalación ====
Actualizar el proceso de instalación manual, el que aquí se muestra está desfasado
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Y le damos permisos de ejecución:
sudo chmod +x /usr/local/bin/docker-compose
* [[https://docs.docker.com/compose/install/|Documentación oficial]]
* [[https://github.com/docker/compose|Repositorio oficial]]
==== Uso ====
El fichero de configuración de Docker Compose se llama ''docker-compose.yml''. El fichero se compone de 4 bloques:
# Obligatorias:
version:
services:
# Opcionales:
volumes:
networks:
docker run -d --name nginx -p 80:80 nginx
Traducido al ''docker-compose.yml'':
version: '3'
services:
web:
image: nginx
container_name: nginx
ports:
- "80:80"
En Docker Compose, los contenedores que se crean se ejecutan en segundo plano, como cuando indicamos la opción ''-d''
Para arrancar todo lo definido en el fichero ''docker-compose.yml'':
docker-compose up -d
Si queremos darle un nombre, en lugar del que defina Docker Compose, lo lanzaremos así:
docker-compose -p nombre-proyecto up -d
Si utilizamos un Docker Compose diferentes al esperado (''docker-compose.yml''), añadimos la opción ''-f'':
docker-compose -f docker-compose-mysql.yml up -d
=== Eliminar contenedores ===
docker-compose down
=== Variables de entorno ===
Podemos definirlas desde el propio fichero ''docker-compose.yml'':
version: '3'
services:
web:
image: mysql
container_name: mysql
ports:
- "3306:3306"
environment:
- "MYSQL_ROOT_PASSWORD=abc123."
O en un fichero externo, por ejemplo, en ''.env'':
MYSQL_ROOT_PASSWORD=abc123.
MYSQL_USER=pepito
Y luego hacemos referencia a él desde ''docker-compose.yaml'':
version: '3'
services:
web:
image: mysql
container_name: mysql
ports:
- "3306:3306"
env_file: .env
=== Volúmenes ===
Volúmenes nombrados:
version: 3
services:
web:
image: nginx
container_name: nginx
ports:
- "80:80"
volumes:
- "mi-volumen:/usr/share/nginx/html"
volumes:
mi-volumen:
Para definir un volumen de host:
version: 3
services:
web:
image: nginx
container_name: nginx
ports:
- "80:80"
volumes:
- "/mi/ruta/:/usr/share/nginx/html"
=== Políticas de reinicio ===
version: '3'
services:
servicio:
...
restart: no
Por defecto, Docker Compose no reinicia los contenedores si se han detenido.
Otras opciones:
* ''always'': el contenedor siempre se reiniciará
* ''on-failure'': el contenedor se reinicia si hubo un fallo
* ''unless-stopped'': el contenedor se reinicia solo si no ha sido detenido
===== Administración =====
==== Docker root dir ====
El directorio raíz de Docker (//Docker Root Dir//) es por defecto ''/var/lib/docker''. Podemos saberlo:
docker info | grep -i "root"
Antes de hacer ningún cambio, detenemos el servicio de Docker:
systemctl stop docker
Para indicar un nuevo directorio, vamos a ''/lib/systemd/system/docker.service'':
ExecStart=/usr/bin/dockerd --data-root /nuevo/docker-root-dir
Recargamos la nueva configuración:
systemctl daemon-reload
Y reiniciamos el servicio de Docker:
systemctl restart docker