# Docker en DAW

## Uso de docker en DAW

## Virtualización: introducción

### Virtualización tradicional

* Es una abstracción de la capa de hardware
* Despliegues más sencillos
* Ahorro costes
* Aislamiento
* ...

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-e0124a90dbae3054112fb8c05f791498b66d29a3%2Ftraditional-arquitecture.png?alt=media)

### Hypervisor

* Es un monitor que orquesta el acceso de varios SO a los recursos de un servidor físico. ![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-82d0dec4ab792c81ea7a675d5516b4ec6b123936%2Fhypervisor-types.png?alt=media)

### Hypervisor type 1

* Nativo o Bare Metal Hypervisor
* Corre directamente en el hardware de la máquina, hacen la función de HAL (Hardware Abstraction Layer)
* Ej: VMWare ESXI, Microsoft Hyper-V, Citrix/Xen Server

### Hypervisor type 2

* Host OS Hypervisor.
* Corre sobre el sistema operativo, como una aplicación más.
* Ej: VMware Workstation, VMware Player, VirtualBox, Parallel Desktop (MAC), ¿KVM?
* No es adecuado cuando hay un workload elevado: Active Directory, bbdd...
* Adecuados para entornos de test
  * Más baratos
  * Instalación más sencilla

### Contenedores

* Son una abstracción de la capa de aplicación

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-ffff49d83de871301446f5d5fe42b0b3883d7d28%2Fcontainers-arquitecture.png?alt=media)

### Ventajas contenedores

* Más ligeros
  * Portabilidad
    * Contrato entre el sysadmin y el developer
    * Despliegues más rápidos
  * Mas eficientes -> menor coste
* Son efímeros

### Desventajas

* Menor seguridad y aislamiento
  * Depende del host
* Snapshots
* Migraciones en caliente (VMWare vMotion)
* Son efímeros

### Google Trends

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-baee79cb3278de6ca759514dace7580ddf2ae3c9%2Fgoogle-trends.png?alt=media)

## Docker

### Qué es Docker

Herramienta **open-source** que nos permite realizar una **virtualización ligera**, con la que poder **empaquetar entornos y aplicaciones** que posteriormente podremos **desplegar** en cualquier sistema que disponga de esta tecnología

### Objetos de Docker

* Los objetos principales en Docker son las **imágenes**, los **contenedores** y los **servicios**
* Hay otros conceptos relacionados con ellos como **volúmenes**, **registro de imágenes** que los veremos conforme los necesitemos.

### Imagen

* Plantilla que define todas las dependencias de mi aplicación
* Es habitual que las imágenes se creen en base a otras (herencia)
* Ejemplo:

```
FROM ubuntu
MAINTAINER username (email@domain.com)
RUN apt-get update
RUN apt-get install -y nginx
CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
```

### Contenedor

* Instancia ejecutable de una imagen
* Son **efímeros**, la persistencia se logra mediante el uso de **volúmenes**
* Cada contenedor se ejecuta en un entorno aislado (podemos controlar el nivel de aislamiento):
  * variables de entorno
  * Volúmenes montados
  * Interfaces de red
* Podemos crear una imagen a partir de un estado del contenedor.

### Servicios

* Los servicios permiten escalar contenedores a través de múltiples demonios de Docker, los cuales trabajarán conjuntamente como un enjambre (swarm).

### Desarrollo en Docker

* Un contenedor -> Un proceso
  * Mejor escalabilidad
  * Mejor reutilización
  * Actualizaciones

### Docker en Linux (I)

* En Linux Docker no es virtualizado, no hay un hipervisor.
* Los procesos que corren dentro de un contenedor de docker se ejecutan con el mismo kernel que la máquina anfitrión.

### Docker en Linux (II)

* Linux aisla los procesos, ya sean los propios de la máquina anfitrión o procesos de otros contenedores.
* Controla los recursos que se le asignan a contenedores pero sin la penalización de rendimiento de los sistemas virtualizados.

### Docker en Windows

* Actualmente docker puede usarse en Windows
* De las múltiples maneras posibles, la mejor es Docker Desktop
* Windows puede crear contenedores Windows o contenedores Linux, pero no simultaneamente. Debemos decirle si vamos a usar contenedores de un tipo u otro.
* El uso de contendeores linux requiere instalar LSW, Linux Subsystem for Windows. Es decir, poner un kernel linux a disposición de docker.

## Instalación

* Dejo estas notas aunque no explicamos nada. Nosotros ya tenemos instalado docker.

### Versiones de Docker

* Hasta el 2019 había dos productos:
  * Docker Enterprise Edition
  * Docker Community Edition
* La versión Enterprise [la compró la empresa Mirantis](https://www.mirantis.com/software/mirantis-kubernetes-engine/)
* Nos centraremos en la versión libre, que es la única a la que hace referencia ahora la [web de Docker](https://www.docker.com/)

### Tipos de instalación

* Ir a la [web de Docker](https://docs.docker.com/get-docker/)
  * Se puede instalar el Docker Desktop en Windows
  * Se puede instalar el Docker Engine en Linux
* Se puede desplegar una imagen mediante Vagrant que tenga todo
* Se puede usar una OVA.

### Instalación en Linux

* Lo más usual es usar imágenes basadas en Linux <https://docs.docker.com/engine/install/ubuntu/>
* El proceso habitual de instalación es:
  * Añadir el repositorio de docker
  * Instalar Docker Engine
  * Configurar usuarios para uso de docker (sin privilegios root). Ver [Linux PostInstall](https://docs.docker.com/engine/install/linux-postinstall/)
  * Instalar [Docker Compose](https://docs.docker.com/compose/install/)

### Trabajar con Docker

* Utilizaremos la terminal (cliente docker)
* Utilizaremos Visual Studio Code
  * Terminal integrada
  * Extensión Docker
  * Debug dentro de contenedores

## Comandos básicos

### Contenedores

* Ejecutar un contenedor nuevo:
  * *docker run*
  * *docker container run*
* Iniciar un contenedor existente:
  * *docker start*
  * *docker container start*
* Parar un contenedor:
  * *docker stop*
  * *docker container stop*
* Borrar contenedor
  * *docker rm*
  * *docker container rm*
* Inspeccionar contenedor
  * *docker inspect*
  * *docker container inspect*
* Ejecutar comando en un contenedor:
  * *docker exec*
  * *docker container exec*
* Ver logs:
  * *docker logs*
  * *docker container logs*

### Imágenes

* Ver listado de imágenes
  * *docker images*
* Borrar una imágen
  * *docker rmi*
* Descargar imágen:
  * *docker pull*
* Publicar imágen:
  * *docker push*

### Descargar imágenes

* Traemos la última versión de la imagen de redis:

```
docker pull --help
docker pull redis
```

### Listado de imáges locales

```
docker images
```

* Aparecen datos útiles:
  * Órigen de la imágen
  * Versión
  * Tamaño que ocupa
  * Fecha de creación

### Ejecución imágen (I)

* Recordemos que una imagen en ejecución es un contenedor

```
docker run redis
docker  ps
```

CTRL + D para pararlo y podemos comprobar que ya no está

### Ejecución imagen (II)

* Mejor ejecutar en modo detached:

```
docker run -d redis
docker  stop  <container-id>
```

Si queremos arrancarlo de nuevo (no sabemos id)

```
docker ps  -a
docker start <container-id>
```

### Configuración de redes

* Por defecto, no es necesario especificarlo. Los contenedores se comunican entre sí.
* Son las más utilizadas
* Déjemos que docker se encargue de todo :-)
* Ejecutar comandos:

  ```
  ip  address
  docker network ls
  docker network inspect bridge
  ```

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-65fcfe43b9c35311c19040fce9bdf296498aacb8%2Fdocker-bridge-network.jpg?alt=media)

### Debug de un contenedor

* La forma más evidente es entrar a la terminal del contenedor:
  * Variables de entorno
  * Estructura ficheros
  * pocos comandos disponibles!

```
docker exec -it <name  or container-id> /bin/bash
```

### Ejercicio

* Comprueba que efectivamente los contenedores se pueden comunicar entre sí.

### Ejercicio - solución

* Accede al fichero /etc/host de cada contenedor para ver su ip
* Haz un ping entre contenedores

  ```
  docker exec -it <container-name> /bin/bash
  apt update
  apt install iputils-ping
  # para ver la ip también podíamos haber utilizado el comando ip
  # apt install iproute2
  # ip address
  ping <ip-container>
  ```

### Logs de un contenedor

```
docker logs <name or container-id>
```

* Los contenedores tienen un nombre aleatorio, pero se puede dar de forma explícita:

```
docker run -p5000:6379 --name  redis-new -d redis
docker run -p5001:6379 --name  redis-old -d redis:4.0
```

### Ejercicio

* Comprueba el estado de imágenes y contenedores de tu equipo
* Elimina todas las imágenes
* Elimina todos los contenedores (también los parados)

### Solución

* Estado actual:

  ```
  docker ps
  docker ps -a
  docker  images
  ```
* Borrar imágenes:

  ```
  docker rmi $(docker images -a -q)
  ```
* Borrar contenedores:

  ```
  docker rm $(docker ps -a -q)
  ```

### Ejercicio

* Descarga **redis:latest** y comprueba su versión
* Descarga esa versión específica (redis:x.y.z)
* Comprueba que tienes dos imágenes al hacer un listado pero que:
  * Ambas ocupan el mismo tamaño
  * Son idénticas:
    * Mismo **IMAGE ID**
    * No se ha producido ninguna descarga de layers adicionales.

### Solución

* Ejecutar contenedor latest y ver versión

  ```
  docker run --name redis-new -d redis
  docker inspect redis-new|grep -i version
  docker exec -it redis-new env
  ```
* Descargar versión actual y comprobar que ambas son idénticas

  ```
  docker pull redis:6.2.5 # en mi caso
  docker images
  ```

### Versiones imágenes

* No es aconsejable utilizar imágenes con etiqueta latest
  * Se pueden producir breaking changes
  * No está claro el versionado de la imágen.

## Docker Hub

### Registros de imágenes

* Podemos crear una imagen de cero pero lo normal es usar o partir de una ya creada.
* Hay un registro oficial de imágenes proporcionado por Docker: [Docker Hub](https://hub.docker.com)
* También podemos crear nuestro propio servicio.

### ¿Qué es Docker hub?

* Un **repositorio de imágenes**: descarga y publicación.
  * Repositorio de **imágenes oficiales de Docker**, de alta calidad.
  * Repositorio de **imágenes verificadas publicadas por terceros**.

### ¿Que más ofrece?

* **Gestor de equipos y organizaciones**: acceso a repositorios privados.
* **Autobuilds**: crea nuevas versiones de imágenes en base a cambios en repos de Github/Bitbucket
* **Webhooks**: Ejecuta acciones después para integrar DockerHub con otros servicios

### Limitaciones descargas

* Desde final del 2020 Docker impuso limitaciones en el uso de su registro.
  * 100 descargas de imágenes cada 6 horas para usuarios anónimos (por IP)
  * 200 descargas de imágenes cada 6 horas para usuarios autenticados
  * [Cuentas **pro** y **team**](https://www.docker.com/pricing) para aumentar los límites.
  * [Cómo saber mi rate limit actual](https://docs.docker.com/docker-hub/download-rate-limit/)
* Conclusión: ¡¡¡Debemos hacer ***docker login***!!!

### Autenticación

* Crear cuenta en [Docker Hub](https://hub.docker.com/)
* Hacer login desde consola

  ```
  docker login
  ```
* Se crea un fichero de configuración en *$HOME/.docker/config.json*
* Las siguientes veces que nos autentiquemos, al hacer *docker login* leerá directamente el fichero
* Observa que si cerramos la sesión la entrada *auths* del fichero *config.json* queda vacía

### Workflows

* Si queremos un repositorio compartido entre varios usuarios necesitamos crear una organización
* Desde Junio 2021 los [autobuilds](https://docs.docker.com/docker-hub/builds/) son de pago
  * Si usamos GitHub podemos usar **GitHub Actions**

### Nuestro propio registro

* Docker viene configurado por defecto para buscar las imágenes de Docker Hub
* Es posible usar nuestro propio registro:
  * Podemos usar la misma **implementación vanilla del Docker registry** que usa Docker Hub
  * Otros más avanzados como Harbor (Open Source, era VMWare), Artifactory (JFrog), Nexus (Sonatype), etc.

### Otros registros

* Docker también ofrece una versión enterprise de su registro llamada Docker Trusted Registry (DTR)
* Google Container Registry (GCR), Amazon Elastic Container Registry (ECR), Azure Container Registry (ACR), Quay (Redhat, versión On-Prem y versión cloud), Gitlab Container Registry, Github Packages, etc.

### Repositorios

* Pueden ser públicos o privados (1 gratis)
* Se crean desde Docker Hub
* Cada repo puede tener una o varias imágenes, en función de la tag
* Las imágenes se publican mediante el comando

  ```
  docker push <hub-user>/<repo-name>:<tag>
  ```
* Se pueden consultar mediante `docker search <keyword>`
  * Lo más habitual es hacerlo vía web, no con consola

## Prácticas DockerHub

### Práctica 1

* **redis y redis-commander dockerizados**
  * Evitamos problemas de dependencias
  * las máquinas host se quedan "limpias"

### redis-comander

* [Paquete de npm](https://www.npmjs.com/package/redis-commander) para acceder a Redis desde el navegador
* Instalación:
  * Debemos instalar nodejs, la versión de la distribución de Ubuntu es antigua (v10)
  * Instalamos usando el PPA oficial

```
sudo curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt-get install -y nodejs
node --version
sudo npm i -g redis-commander
redis-commander
```

### Comunicación entre contenedores

* Queremos que se comuniquen entre sí utilizando **redes bridge**:
  * **default**: las que utilizabamos hasta ahora
  * **user defined**:
    * Interacionan solo los contenedores definidos en esta red
    * Hay resolución de nombres

### User defined vs default bridge

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-a7556deac51a7a238b7b9c8378197b139208392b%2Fcustom-network.jpg?alt=media) ![bridge](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-65fcfe43b9c35311c19040fce9bdf296498aacb8%2Fdocker-bridge-network.jpg?alt=media)

### Buscar imagen en Docker Hub

* Opciones
  * Construir nuestra propia imagen (todavía no sabemos)
  * Utilizar una imagen ya preparada en Docker Hub:
    * Menos propenso a errores
    * Más rápido
  * Modificar una imagen ya preparada:
    * ¿Dockerfile? ¿FROM?

### User defined network

* Creación de red:

```
# docker network rm my-net
docker network create my-net
```

* Uso de red:

```
docker create --name my-nginx \
  --network my-net \
  --publish 8080:80 \
  nginx:latest
```

### Solución

```
docker network create redis-net
docker run --rm --name redis --network redis-net -d redis
docker run --rm --name redis-commander --network redis-net  -d \
  --env REDIS_HOSTS=redis \
  -p 8081:8081 \
  rediscommander/redis-commander:latest
```

* Comprueba que efectivamente funciona (*docker log*)
* Entra a uno de los dos contenedores y comprueba el ping por *name*

### Práctica 2

* Monta un escenario como el anterior con MySQL o MariaDB y phpMyAdmin

### Práctica 3

* Comprueba las versiones de imágenes de httpd
  * ¿Vía web?
  * ¿Vía docker search?

### Práctica 3 - opciones

* *docker search* no nos sirve
* *Vía web* aunque es algo "laborioso"
* Prueba y error con la versión que queremos
* Usando [hub tool](https://www.docker.com/blog/docker-hub-experimental-cli-tool/)

```
  hub-tool tag ls <repo-name>
```

### Práctica 4

* Ejecuta una imagen 2.2 de Apache en DockerHub y modifica el index.html para que aparezca HolaMundo
  * ¿Cómo has modificado el index.html?

### Práctica 4 - opciones

* Instalando en el contenedor un editor y entrando mediante:

```
docker exec -it <container> bash
```

* Mediante el [comando cp de Docker](https://docs.docker.com/engine/reference/commandline/cp/)
* Mediante el plugin Docker de Visual Studio Code (lo más sencillo)

### Práctica 5

* Imagina que hay un bug importante en Apache que está arreglado en la versión 2.4
* Actualiza la versión de nuestra aplicación HolaMundo anterior a Apache2.4

### Práctica 5 - opciones

* Las dos opciones más adecuadas serían:
  * Generar una nueva imagen de nuestra aplicación
    * No sabemos hacerlo todavía
    * Realmente tampoco lo habíamos hecho, utilizabamos directamente la imagen de Apache
  * Buscar persistencia de algún modo en nuestro contenedor efímero.
    * Usámos volúmenes o bind-mounts

### Volúmenes

* Docker gestiona el volumen de forma transparente

```
  docker-volume ls
  docker volume  rm <volume-id>
  docker volume inspect <volume-id>
```

* Ejemplo con nginx:

```
  docker run -d --name=nginx -v nginx-vol:/usr/share/nginx/html nginx:latest
```

### Bind mounts

* El volumen se mapea a un directorio físico acccesible no solo por Docker.
* Si el directorio no existe, se crea.

```
docker run -d --name=nginx -v ./nginx-web:/usr/share/nginx/html nginx:latest
```

## Construcción imágenes en Docker (1)

### Objetivos

* Aprender a crear ficheros Dockerfile
* Aprender a crear y publicar imágenes
* Entender el concepto de layers en imágenes.

### ¿Qué es un dockerfile?

* Plantilla en texto plano que define las dependencias de mi aplicación y la imagen.
* Cada línea del fichero Dockerfile contiene una serie de comandos que generan una capa en la imágen
  * Se ejecutan de manera secuencial
  * Existe una caché que funciona por cada línea o capa.
* Es habitual que las imágenes se creen en base a otras (herencia)

### Concepto de capas

* Una imágen es un conjunto de capas de solo lectura, generadas por el Dockerfile
* ¿Qué es un contenedor?
  * Una imagen en ejecución
  * Una imágen con una capa de lectura/escritura encima del resto de capas llamada **container layer**
* Cualquier cambio que hagamos en un contenedor, se lleva a cabo en la **container layer**

### Ejemplo

* Vamos a crear una imagen que visualice el contenido de un fichero al ejecutar el contenedor
* Crea un directorio y coloca un fichero *holaMundo.txt* con el texto *¡Hola Mundo!*
* Crea un fichero *Dockerfile* en el mismo directorio con el siguiente contenido:

```
FROM ubuntu:latest
RUN mkdir -p /app
COPY holaMundo.txt /app/holaMundo.txt
RUN chmod 600 /app/holaMundo.txt
CMD cat /app/holaMundo.txt
```

* Creamos la imagen y etiquetamos:

```
  docker build -t <dockerHubUserName>/holaMundo .
  docker tag  <dockerHubUserName>/holaMundo:1
  docker tag  <dockerHubUserName>/holaMundo:1.0
  docker tag  <dockerHubUserName>/holaMundo:1.0.0
  docker image ls
```

* Ejecutamos la imagen:

  ```
  docker run <dockerHubUserName>/holaMundo
  ```
* Subimos la imagen con todas sus tags

  ```
  docker push -a <dockerHubUserName>/holaMundo
  ```

### Análisis Dockerfile

* FROM nos sirve para partir de una imagen previa
* RUN: ejecuta comandos
* COPY: copia ficheros de nuestro contexto a la imagen
* CMD: Ejecuta un comando al iniciar el contenedor
* Más info en las [referencias de Dockerfile](https://docs.docker.com/engine/reference/builder/)

### Explorar capas

* Podemos ver las capas también mediante *docker inspect* y *docker history*
  * No todos los pasos generan una nueva capa, algunos comandos solo alteran configuración (CMD, ENV, ENTRYPOINT, EXPOSE, etc.).
* La herramienta Dive nos sirve para explorar con más detalle las capas de las imágenes de Docker <https://github.com/wagoodman/dive>

### Cache

* Las líneas del Dockerfile en principio se cachean
* Si se produce un MISS ya no se usa más caché en esa compilación
  * Escribir las líneas más "frecuentes primero"
  * Si hay líneas "con dependencias", ej apt-get update y apt-install juntas.
* Si no queremos caché (ni para *FROM image*):

```
docker build --no-cache --pull -t myApp .
```

## PRACTICAS IMÁGENES

### PRACTICA 1

* Crea una imagen que se base en Ubuntu y que permita:
  * Editar ficheros con vim
  * Ejecutar el comando ping

### Solución

* Observa el *-y* para evitar la parte interactiva del comando *apt-get install*

```
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y vim iputils-ping
```

* Comprueba con dive los recursos utilizados

```
docker build -t test .
dive test
```

### Mejor solución

```
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends \
  vim \
  iputils-ping \
  && rm -r /var/lib/apt/lists/*;
```

### ¿Qué falla aquí?

```
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends \
  vim \
  iputils-ping
RUN rm -r /var/lib/apt/lists/*; \
```

### PRÁCTICA 2

* Crea una imagen a partir de las instrucciones del ejercicio anterior (vim + ping) pero con el comando [docker commit](https://docs.docker.com/engine/reference/commandline/commit/)
* Verifica la imagen (capas y tamaño)

### PRÁCTICA 3

* Vamos a crear una versión v2.0 de nuestro holaMundo, que en vez de coger el fichero de local lo coja de una URL mediante el comando [ADD](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy)
* Compila la imagen y ejecútala
* Cambiaremos los datos del fichero apuntado por la URL
* Compila de nuevo la imagen y ejecuta otra vez
* ¿Qué pasa? ¿Comó lo solucionas?

## Construcción imágenes en Docker (2)

### Almacenamiento

* En docker, las imágenes se construyen a base de capas
* Cada capa contiene únicamente las diferencias respecto a la capa padre.
* Docker utiliza mecanismos de union filesystems para montar en una carpeta la combinación de las distintas capas.

### Almacenamiento

* Al crear un contenedor, Docker añade una capa adicional (la capa de contenedor), que es la única sobre la que es posible escribir.
* El contenedor modifica aparentemente la imagen base, como si tuviera una copia real, pero únicamente está modificando esta última capa.
* Podemos crear múltiples contenedores sobre una misma imagen, reutilizando todas las capas excepto la capa de contenedor.
* Al destruir un contenedor, esta capa con las modificaciones se destruye.

### Tamaño de las imágenes

* Borrar un archivo en un paso del Dockerfile no elimina ese archivo de las capas anteriores de la imagen.

  * El archivo sigue presente, pero no es accesible desde el contenedor.
  * Es la forma de comportarse de los union filesystems

  ```
   FROM ubuntu
   RUN apt-get install alguna-herramienta
   RUN algo-que-utiliza-la-herramienta para compilar o hacer algo
   RUN apt-get remove alguna-herramienta
  ```

### Build Context

* Al hacer un build se envían al daemon docker los ficheros de la ruta especificada como contexto:

```
# En este caso el contexto es ".", el directorio actual:
$ docker build -t myimage -f Dockerfile .
```

* Es recomendable usar una carpeta separada para almacenar el contexto.
* Podemos crear un fichero *.dockerignore*

```
# contexto vacío, si no necesitamos añadir ficheros al contenedor, mediante "-"
docker build -t myimage -f Dockerfile -
```

### Tipos de imágenes

* **Used**: Las que aparecen al hacer un *docker ps -a*
* **Unused**: Las que no aparecen (se usaron en su momento pero se ha borrado el contenedor)
* **Dangling images**: Imágenes que se crean sin nombre, y se muestran como **\<none>**.
  * Útil cuando estamos haciendo pruebas de compilación

### Gestión de espacio

* Conforme vamos usando docker, descargando imágenes... empezamos a ocupar espacio
* Comprobar el espacio usado:

```
docker system df 
```

* Eliminación de imágenes:

```
  docker system prune  -a #  unused  y  dangling images
  docker system prune # dangling images
```

### Multistage builds

* Se crea la primera imagen que sirve para obtener lo que se usa en el segundo stage (ver --from=n)
* Todo lo que no se usa queda fuera del último stage que es el definitivo
  * Útil para entorno de compilación
  * Se reduce el tamaño final de la imagen al quedarnos solo con el ejecutable

### Ejemplo multistage

```
# syntax=docker/dockerfile:1
FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]  
```

## Docker Compose

### Qué es Docker Compose

* Herramienta para definir y ejecutar aplicaciones con varios contenedores Docker.
* Se definen los contenedores (servicios) de la aplicación mediante un fichero YAML.
* Se levantan mediante el comando `docker-compose up`

### Prueba de uso

* [Referencia de uso](https://docs.docker.com/compose/compose-file/compose-file-v3/)
* Vamos a analizar un caso sencillo con 3 contenedores:
  * web server con php
  * db con MySQL
  * phpMyAdmin
* Clona [este repositorio](https://github.com/juanda99/practica-docker-compose-php/blob/main/docker-compose.yml)

### Docker-compose con Image

* Óptimo para producción, más rápido que un build y todo *empaquetado*
* Crea una imagen de Apache con un HolaMundo
* Ejecútala mediante docker-compose.yml

### Proxy inverso

* Para dar un servicio externo, en web el puerto es el 80
* ¿Cómo hacemos para que los servicios anteriores funcionen todos en el 80?
  * Proxy inverso en función de la url
  * Similar a un virtual host de apache pero que se configura solo en función de las peticiones que le llegan al docker daemon
  * Usaremos [nginx-proxy](https://github.com/nginx-proxy/nginx-proxy) o [traefik](https://github.com/traefik/traefik)

### Práctica ODOO

* Seleccionar en DockerHub una imagen de Odoo
* Desplegar la imagen con la bbdd adecuada como servicios (usando *docker-compose.yml*)

### Práctica Dolibarr

* Seleccionar en DockerHub una imagen de Dolilbarr
* Desplegar la imagen con la bbdd adecuada como servicios (usando *docker-compose.yml*)

### Práctica Wordpress

* En la web de Docker hay [ejemplos de Wordpress, Django o Rails](https://docs.docker.com/samples/wordpress/)
* [Repo con wp-cli, copias de seguridad o caché](https://github.com/juanda99/wordpress-docker)

### Mooodle

* [Ver ejemplo de Catedu](https://github.com/catedu/moodle-docker-production)

### Problema de espera entre contenedores

* Una buena forma es usar [dockerize](https://github.com/jwilder/dockerize)
  * [Ejemplo de uso](https://github.com/catedu/moodle-docker-production/blob/master/apache/3.7.6/docker-entrypoint.sh#L101)
  * Instalación:

```
RUN curl -o dockerize.tar.gz -fSL "https://github.com/jwilder/dockerize/releases/download/${DOCKERIZE_VERSION}/dockerize-linux-amd64-${DOCKERIZE_VERSION}.tar.gz"; \
	tar -xf dockerize.tar.gz -C /usr/local/bin; \
	rm dockerize.tar.gz
```

### crontab

* ¿Añadir un servicio de crontab a nuestro contenedor?
  * ¡¡¡no!!! Filosofía docker: 1 contenedor = 1 servicio
* Podemos crear un crontab en el host con comandos como:

  ```
  docker exec -it <containerId> <command> 
  docker run --rm <containerId> <command>
  ```
* Usar soluciones ya preparadas: [Ofelia](https://github.com/mcuadros/ofelia) ![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-ae2d070b4feb0ab3e20d147626dabef884c75eae%2Fofelia.gif?alt=media)

### Práctica copias de seguridad

* Propongo uso de [rsnapshot](https://rsnapshot.org/)
  * rsync para hacer instantáneas con hard links
* Interfaz gráfico para su gestión
* Y para hacer deploy en Docker
* ¿Existe esto?
  * [ELKAR backup](https://www.elkarbackup.org/)

## Monitorización

### Visión general

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-d7190274da17cd3d7dee56f08ca53e4d75ae0afc%2Fqueue.png?alt=media) ![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-68a784616adadeeca1d3c43a3a049de2d3f44a09%2Fhtop.png?alt=media)

### Datos en docker

* Ver uso contenedores

```
docker stats --no-stream
```

* Procesos en ejecución

  ```
  docker  top <container_id>
  ```
* Consultas al demonio de docker
* Consultas específicas por contenedor

```
docker exec -it <container-id> top
```

* Configuraciones intrínsecas a los servicios (por ej. mod\_status en Apache)

### Gestión de la memoria

* El kernel si no tiene suficiente memoria arroja un **OOME (Out of memory exception)**.
  * Empieza a matar procesos para liberar memoria
  * Puede tirar todo el sistema si mata el proceso equivocado (por ej. el demonio de Docker)
* Docker ajusta la prioridad de OOM del demonio para reducir la probabilidad de recibir un kill.

### Gestion de memoria de los contenedores

* La prioridad de los contenedores no se debe ajustar.
  * El host debe tener suficiente memoria
  * Para evitar errores se debe **limitar el uso de memoria de los contenedores**

### Ejemplo configuración

* La configuración cambia bastante entre versiones de docker-compose

```
services:
  service:
    image: nginx
    deploy:
        resources:
            limits:
              cpus: 0.50
              memory: 512M
            reservations:
              cpus: 0.25
              memory: 128M
```

## Workflow con docker

### Desarrollo con Vagrant

* Buena opción si trabajamos en equipos Windows y no queremos preocuparnos de despliegues
* Descargamos Vagrant
* Configuramos Vagrant (ver después) o hacemos un git clone de <https://github.com/juanda99/vagrant-deploy-virtualbox-docker>

### Configuración Vagrant-Virtual Box

* Nos situamos en un directorio e inicializamos mediante `vagrant init`
* Modificamos el fichero Vagrantfile creado con algo como:

  ```
  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  #config.vm.box = "base"
  config.vm.box = "bento/ubuntu-20.04"
  config.vm.network :forwarded_port, host: 8000, guest: 8000
  # require plugin https://github.com/leighmcculloch/vagrant-docker-compose
  config.vagrant.plugins = "vagrant-docker-compose"
  # install docker and docker-compose
  config.vm.provision :docker
  config.vm.provision :docker_compose, yml: "/vagrant/docker-compose.yml", rebuild: true, run: "always"
  ```
* Añadimos servicios mediante docker-compose cuyo punto de entrada se mapee al host en el 8000

### Workflow en local

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-f75a2affcf32ec96d5d23747100e2a5cb825454d%2Fdev-docker-workflow.png?alt=media)

### Workflow global

![](https://1922628619-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LTSsTmsYkC1i_YUBBuZ%2Fuploads%2Fgit-blob-333d7ecce95243ad34193bb57a4ea956417aa64b%2Fci-cd-with-docker.png?alt=media)

* Development
* CI/CD
* Deployment
