You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1948 lines
63 KiB
1948 lines
63 KiB
---
|
|
weight: 4
|
|
title: "Apuntes de Docker"
|
|
date: 2021-01-23T16:01:50+0100
|
|
draft: false
|
|
summary: "Apuntes de docker"
|
|
categories:
|
|
- notes
|
|
tags:
|
|
- docker
|
|
- traefik
|
|
---
|
|
|
|
{{< image src="/images/docker_logo_wide.jpg" >}}
|
|
|
|
{{< admonition type=warning title="Work in progress" open=true >}}
|
|
Estos apuntes no están completos, (ni de lejos)
|
|
{{< /admonition >}}
|
|
|
|
## Conceptos
|
|
|
|
### El software
|
|
|
|
El software de Docker implementa el estándar OCI (_Open Container
|
|
Initiative_) tan fielmente como les es posible.
|
|
|
|
La OCI especifica:
|
|
|
|
* Imágenes
|
|
* _Runtime containers_
|
|
|
|
runc
|
|
|
|
: Es la implementación para _Docker_ de la especificación de
|
|
_runtime container_ de la OCI.
|
|
|
|
La misión de _runc_ es crear contenedores, puede funcionar por si
|
|
solo via CLI (es un binario) pero funciona a muy bajo nivel.
|
|
|
|
containerd
|
|
|
|
: Es un demonio. Se encarga del ciclo de vida de los contenedores:
|
|
arrancarlos, pararlos, pausarlos, borrarlos.
|
|
|
|
Se encarga también de otras cosas como _pulls_, _volumes_ y
|
|
_networks_. Esta funcionalidad adicional es modular y opcional y
|
|
se introdujo para facilitar el uso de _containerd_ en otros
|
|
proyectos como por ejemplo _Kubernetes_.
|
|
|
|
shim
|
|
|
|
: _shim_ se encarga de dejar conectados los containers con
|
|
_containerd_ una vez que _runc_ termina su misión y muere.
|
|
|
|
### _images_
|
|
|
|
Puede pensarse que una imagen es como un contenedor parado. De hecho
|
|
puedes parar un contenedor y construir una imagen a partir de el. Se
|
|
dice que las imágenes son objetos de _build-time_ y los contenedores
|
|
son objetos de _run-time_
|
|
|
|
Las imágenes se pueden almacenar centralizadas en los _image
|
|
registries_. Por ejemplo _dockerhub_ es un _image registry_,
|
|
seguramente el más popular, pero tu podrías tener tu propio _image
|
|
registry_.
|
|
|
|
{{< admonition type=warning title="Seguridad e Imágenes" state=open >}}
|
|
|
|
Mucho ojo con esto, por que en el [Docker Store](https://hub.docker.com/) nos podemos encontrar todo tipo de imágenes, al fin y al cabo cualquiera puede publicar sus imagenes. No debemos confiar automáticamente en cualquier imagen. Ten cuidado con tus fuentes.
|
|
|
|
__¡No te fies de las etiquetas!__ Son solo eso, etiquetas. Por ejemplo _latest_ se supone que apunta a la última versión de un contenedor pero no hay ningún mecanismo que lo asegure, _alguien_ tiene que ocuparse de que la etiqueta apunte al contenedor correcto.
|
|
|
|
{{< /admonition >}}
|
|
|
|
Las __imágenes oficiales__ suelen residir en un repo de primer nivel dentro de _dockerhub_ y tendrán una url como esta, por ejemplo: <https://hub.docker.com/_/nginx/>
|
|
|
|
|
|
### _containers_
|
|
|
|
Como dijimos arriba, un _container_ es una instancia _runtime_ de una
|
|
imagen. A partir de una imagen podemos crear varios _containers_.
|
|
|
|
Un contenedor es muy parecido a una máquina virtual clásica (como las
|
|
de _Virtual Box_ por ejemplo). La principal diferencia es que son
|
|
mucho más ligeros y rápidos, ya que comparten _kernel_ con la máquina
|
|
_host_. Además suelen basarse en imágenes lo más ligeras posible que
|
|
contienen solo lo imprescindible para que el contenedor cumpla su
|
|
función.
|
|
|
|
### _volumes_
|
|
|
|
### _networks_
|
|
|
|
|
|
## cheatsheet
|
|
|
|
### Imágenes
|
|
|
|
| Comando | Efectos |
|
|
|:--------------------|:---------------------------------------|
|
|
| docker images | Lista las imágenes disponibles |
|
|
| docker ps | Lista los container en ejecución |
|
|
| docker ps -a | Lista todos los container |
|
|
| docker build | construye imagen desde dockerfile |
|
|
| docker history | Muestra la hist. es decir como se hizo |
|
|
| docker inspect | Detalles de la imagen (p.ej. ip) |
|
|
| docker images | Lista todas las imágenes |
|
|
| docker images -a | lista tb las imágenes intermedias |
|
|
| docker images -q | lista solo identificativos |
|
|
| docker system prune | Borra todas las imágenes no usadas |
|
|
| docker pull | descarga una imagen |
|
|
| docker push | sube una imagen a la nube |
|
|
| docker image rm | Borra imágenes, alias `docker rmi` |
|
|
|
|
### Contenedores
|
|
|
|
| Comando | Efectos |
|
|
|:------------------------|:--------------------------------------|
|
|
| docker run | Ejecuta un contenedor |
|
|
| docker run -d | detached |
|
|
| docker run -name | para pasarlo por nombre |
|
|
| docker container rename | para renombrar un contenedor |
|
|
| docker ps | Para ver cont. ejecutandose |
|
|
| docker ps -a | Para verlos todos |
|
|
| docker stop | Para parar uno, `SIGTERM` + `SIGKILL` |
|
|
| docker start | reiniciar un cont. |
|
|
| docker restart | stop + start |
|
|
| docker kill | `SIGKILL` |
|
|
| docker pause | lo pone en pausa |
|
|
| docker unpause | lo saca de la pausa |
|
|
| docker cp | copiar ficheros a y desde cont. |
|
|
| docker exec -it | ejecutar y abrir terminal |
|
|
| docker top | ver estadísticas del cont. |
|
|
| docker stats | para ver más stats. `--no-stream` |
|
|
| docker --rm | borrar cont. al sali |
|
|
| docker container prune | borrar todos los cont. parados |
|
|
|
|
__Ejemplos__:
|
|
|
|
- Lanzar un alpine y abrir un terminal contra el:
|
|
|
|
```bash
|
|
docker run -it --rm alpine /bin/ash
|
|
```
|
|
|
|
- Abrir un terminal contra un contenedor que ya está corriendo:
|
|
|
|
```bash
|
|
docker exec -it <containerId> bash
|
|
```
|
|
|
|
### Volumes
|
|
|
|
| Comando | Efectos |
|
|
|:------------------------|:--------------------------------------|
|
|
| docker volume prune | Borrar todos los `volume` no usados |
|
|
|
|
### Dockerhub
|
|
|
|
| Comando | Efectos |
|
|
|:---------------------|:-------------------------|
|
|
| docker commit | crea imagen desde cont. |
|
|
| docker login | |
|
|
| docker push | |
|
|
| docker search ubuntu | busca imágenes en el hub |
|
|
|
|
|
|
### docker inspect ###
|
|
|
|
```shell
|
|
docker inspect --format '{{ .NetworkSettings.IPAddress }}' CONT_ID
|
|
```
|
|
|
|
** TODO ** Investigar mas el `format`
|
|
|
|
|
|
### docker build ###
|
|
|
|
Como construir una imagen
|
|
|
|
`docker build https://github.com/salvari/repo.git#rama:directorio`
|
|
|
|
Tambien se le puede pasar una url o un directorio en nuestro pc.
|
|
|
|
|
|
## Dockerfile
|
|
|
|
{{< admonition type=info title="Referencias" open=true >}}
|
|
|
|
- [Dockerfile Best Practices with Examples](https://takacsmark.com/dockerfile-tutorial-by-example-dockerfile-best-practices-2018/#overview)
|
|
|
|
{{< /admonition >}}
|
|
|
|
Un _dockerfile_ es un fichero de texto que define una imagen de Docker. Con esto podemos crear nuestras imágenes a medida, ya sea para "dockerizar" una aplicación, para construir un entorno a medida, para implementar un servicio etc. etc.
|
|
|
|
En el [dockerhub](https://hub.docker.com/) tenemos imágenes para hacer prácticamente cualquier cosa, pero en la práctica habrá muchas ocasiones donde queramos cambiar algún detalle o ajustar la imagen a nuestro gusto y necesidades (o simplemente aprender los detalles exactos de la imagen), para todo ello necesitamos saber como funcionan los _dockerfiles_.
|
|
|
|
Un _dockerfile_ de ejemplo:
|
|
|
|
```dockerfile
|
|
FROM python:3
|
|
|
|
WORKDIR /usr/src/app
|
|
|
|
COPY requirements.txt ./
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
COPY . .
|
|
|
|
CMD [ "python", "./your-daemon-or-script.py" ]
|
|
```
|
|
|
|
Un _dockerfile_ funciona por capas (_layers_) que son los bloques de construcción de _docker_. La primera _layer_ siempre es `FROM image_name` que define en que imagen pre-construida se basará nuestra imagen. Podemos definir muchas cosas en un _dockerfile_ desde permisos de usuario hasta _scripts_ de arranque.
|
|
|
|
Asi que:
|
|
|
|
1. Un _dockerfile_ es un fichero de texto que contien las instrucciones que se ejecutarán con `docker build` para construir una imagen Docker
|
|
2. Es un conjunto de instrucciones paso a paso
|
|
3. Se compone de un conjunto de instrucciones estándar (como `FROM`, `ADD`, etc. etc.)
|
|
4. Docker contruirá automáticamente una imagen a partir de esas instrucciones
|
|
|
|
Nota: En _docker_ un contenedor es una imagen con una capa de lectura-escritura en la cima de muchas capas de solo-lectura. Esas capas inferiores se denominan "imágenes intermedias" y se generan cuando se ejecuta el _dockerfile_ durante la etapa de construcción de la imagen.
|
|
|
|
Evidentemente para construir un _dockerfile_ no basta con conocer Docker. Si quiero construir una imagen para proveer un servicio de base de datos, necesito conocer como funciona ese servicio y el proceso de instalación del mismo para "dockerizarlo"
|
|
|
|
|
|
`ADD`
|
|
: Copia un fichero del host al contenedor
|
|
|
|
`CMD`
|
|
: el argumento que pasas por defecto
|
|
|
|
`ENTRYPOINT`
|
|
: el comando que se ejecuta por defecto al arrancar el contenedor
|
|
|
|
`ENV`
|
|
: permite declarar una variable de entorno en el contenedor
|
|
|
|
`EXPOSE`
|
|
: abre un puerto del contenedor
|
|
|
|
`FROM`
|
|
: indica la imagen base que utilizarás para construir tu imagen
|
|
personalizada. Esta opción __es obligatoria__, y además __debe ser
|
|
la primera instrucción__ del Dockerfile.
|
|
|
|
`MAINTAINER`
|
|
: es una valor opcional que te permite indicar quien es el que se encarga de mantener el Dockerfile
|
|
|
|
`ONBUILD`
|
|
: te permite indicar un comando que se ejecutará cuando tu imagen sea utilizada para crear otra imagen.
|
|
|
|
`RUN`
|
|
: ejecuta un comando y guarda el resultado como una nueva capa.
|
|
|
|
`USER`
|
|
: define el usuario por defecto del contenedor
|
|
|
|
`VOLUME`
|
|
: crea un volumen que es compartido por los diferentes contenedores o con el host
|
|
`WORKDIR define el directorio de trabajo para el contenedor.
|
|
|
|
### Ejemplos de dockerfiles
|
|
|
|
#### Simple
|
|
Un *Dockerfile* muy simple para un script de python
|
|
|
|
```dockerfile
|
|
FROM python:3
|
|
|
|
WORKDIR /usr/src/app
|
|
|
|
COPY requirements.txt ./
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
COPY . .
|
|
|
|
CMD [ "python", "./your-daemon-or-script.py" ]
|
|
```
|
|
|
|
#### nginx
|
|
|
|
Este es un ejemplo de *Dockerfile* `multi-stage`. En un *Dockerfile* de tipo `multi-stage` cada `FROM` (puede haber varios) usa la base que quieras para abrir una nueva etapa (*stage*) Se pueden copiar artefactos de una etapa anterior de forma selectiva.
|
|
|
|
- En la primera etapa, que llamaremos `builder` partimos de la imagen `alpine:3.20.1`
|
|
- Instalamos:
|
|
- `pcre`, una biblioteca de expresiones regulares
|
|
- `libxml2`
|
|
- `libxslt`
|
|
- `gcc`, el compilador gnu
|
|
- `make`, la herramienta
|
|
- `libc-dev`
|
|
- `pcre-dev`
|
|
- `zlib-dev`
|
|
- `libxml2-dev`
|
|
- `libxslt-dev`
|
|
- Descargamos el código de *nginx* en el directorio `/tmp` y lo descomprimimos
|
|
- Ejecutamos `autoconfigure` especificando que se instale en `/opt`
|
|
- Compilamos con `make` e instalamos con `make install`
|
|
- Desinstalamos la bibliotecas y borramos la caché (aunque no creo que esto valga para nada, hasta donde yo entiendo la etapa `builder` se descarta)
|
|
|
|
``` dockerfile
|
|
FROM alpine:3.20.1 AS builder
|
|
RUN apk add --update \
|
|
--no-cache \
|
|
pcre~=8.45-r3 \
|
|
libxml2~=2.12.7-r0 \
|
|
libxslt~=1.1.39-r1 \
|
|
gcc~=13.2 \
|
|
make~=4.4 \
|
|
musl-dev~=1.2.5 \
|
|
pcre-dev~=8.45 \
|
|
zlib-dev~=1.3.1 \
|
|
libxml2-dev~=2.12.7 \
|
|
libxslt-dev~=1.1.39 && \
|
|
cd /tmp && \
|
|
wget -q https://github.com/nginx/nginx/archive/master.zip -O nginx.zip && \
|
|
unzip nginx.zip && \
|
|
cd nginx-master && \
|
|
./auto/configure --prefix=/opt/nginx && \
|
|
make && \
|
|
make install && \
|
|
apk del gcc make libc-dev pcre-dev zlib-dev libxml2-dev libxslt-dev && \
|
|
rm -rf /var/cache/apk
|
|
|
|
FROM alpine:3.20.1
|
|
|
|
ARG UID=${UID:-1000}
|
|
ARG GID=${GID:-1000}
|
|
|
|
RUN apk add --update \
|
|
--no-cache \
|
|
pcre~=8.45 \
|
|
libxml2~=2.12.7 \
|
|
libxslt~=1.1.39 \
|
|
tini~=0.19 \
|
|
shadow~=4.15 &&\
|
|
rm -rf /var/cache/apk && \
|
|
groupmod -g $GID www-data && \
|
|
adduser -u $UID -S www-data -G www-data && \
|
|
mkdir /html
|
|
|
|
COPY --from=builder /opt /opt
|
|
|
|
COPY nginx.conf /opt/nginx/conf/nginx.conf
|
|
COPY entrypoint.sh /
|
|
|
|
EXPOSE 8080
|
|
VOLUME /html
|
|
|
|
RUN chown -R www-data:www-data /html && \
|
|
chown -R www-data:www-data /opt/nginx
|
|
|
|
USER www-data
|
|
|
|
|
|
ENTRYPOINT ["tini", "--"]
|
|
CMD ["/bin/sh", "/entrypoint.sh"]
|
|
```
|
|
|
|
La segunda etapa empieza en la linea 24 definiendo un par de variables para establecer en `UID` y el `GID` del usuario `www-data` que usaremos dentro del contenedor `nginx` para ejecutar el servidor.
|
|
|
|
- Instalamos las bibliotecas:
|
|
- `pcre`
|
|
- `libxml2`
|
|
- `libxslt`
|
|
- `tini`, un micro `init` ideal para implementar contenedores ligeros
|
|
- `shadow`, el PAM de Alpine
|
|
- Limpiamos la caché de instalación de paquetes
|
|
- Cambiamos el `GID`para el grupo `www-data`
|
|
- Creamos el usuario `www-data` con el `UID` especificado
|
|
- Creamos el directorio `/html`
|
|
- Copiamos los ejecutables de *nginx* de la etapa anterior
|
|
- Copiamos el `nginx.conf` y el `entrypoint.sh` desde el directorio de trabajo del *Dockerfile*
|
|
- Declaramos el puerto de acceso y el volumen correspondiente a `/html`
|
|
- Ejecutamos como usuario `www-data` el *entrypoint* del contenedor.
|
|
|
|
##### Ficheros auxiliares
|
|
|
|
**entrypoint**
|
|
|
|
El fichero `entrypoint` que hemos usado es muy simple:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
mediaowner=$(ls -ld /html | awk '{print $3}')
|
|
echo "Current /html owner is $mediaowner"
|
|
/opt/nginx/sbin/nginx -g "daemon off;"
|
|
```
|
|
|
|
El fichero `nginx.conf` tampoco es muy complicado. Dejamos secciones comentadas para tener algunas referencias.
|
|
|
|
```nginx
|
|
#user nobody;
|
|
worker_processes 1;
|
|
|
|
#error_log logs/error.log;
|
|
#error_log logs/error.log notice;
|
|
#error_log logs/error.log info;
|
|
|
|
#pid logs/nginx.pid;
|
|
|
|
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
|
|
http {
|
|
include mime.types;
|
|
default_type application/octet-stream;
|
|
|
|
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
# '$status $body_bytes_sent "$http_referer" '
|
|
# '"$http_user_agent" "$http_x_forwarded_for"';
|
|
|
|
#access_log logs/access.log main;
|
|
|
|
sendfile on;
|
|
#tcp_nopush on;
|
|
|
|
#keepalive_timeout 0;
|
|
keepalive_timeout 65;
|
|
|
|
#gzip on;
|
|
|
|
server {
|
|
listen 8080;
|
|
server_name localhost;
|
|
|
|
access_log /dev/stdout;
|
|
error_log /dev/stdout info;
|
|
#charset koi8-r;
|
|
|
|
#access_log logs/host.access.log main;
|
|
|
|
location / {
|
|
root /html;
|
|
index index.html index.htm;
|
|
|
|
include mime.types;
|
|
try_files $uri $uri/ =404;
|
|
}
|
|
|
|
#error_page 404 /404.html;
|
|
|
|
# redirect server error pages to the static page /50x.html
|
|
#
|
|
error_page 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root html;
|
|
}
|
|
|
|
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
|
#
|
|
#location ~ \.php$ {
|
|
# proxy_pass http://127.0.0.1;
|
|
#}
|
|
|
|
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
|
#
|
|
#location ~ \.php$ {
|
|
# root html;
|
|
# fastcgi_pass 127.0.0.1:9000;
|
|
# fastcgi_index index.php;
|
|
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
|
# include fastcgi_params;
|
|
#}
|
|
|
|
# deny access to .htaccess files, if Apache's document root
|
|
# concurs with nginx's one
|
|
#
|
|
#location ~ /\.ht {
|
|
# deny all;
|
|
#}
|
|
}
|
|
|
|
|
|
# another virtual host using mix of IP-, name-, and port-based configuration
|
|
#
|
|
#server {
|
|
# listen 8000;
|
|
# listen somename:8080;
|
|
# server_name somename alias another.alias;
|
|
|
|
# location / {
|
|
# root html;
|
|
# index index.html index.htm;
|
|
# }
|
|
#}
|
|
|
|
|
|
# HTTPS server
|
|
#
|
|
#server {
|
|
# listen 443 ssl;
|
|
# server_name localhost;
|
|
|
|
# ssl_certificate cert.pem;
|
|
# ssl_certificate_key cert.key;
|
|
|
|
# ssl_session_cache shared:SSL:1m;
|
|
# ssl_session_timeout 5m;
|
|
|
|
# ssl_ciphers HIGH:!aNULL:!MD5;
|
|
# ssl_prefer_server_ciphers on;
|
|
|
|
# location / {
|
|
# root html;
|
|
# index index.html index.htm;
|
|
# }
|
|
#}
|
|
|
|
}
|
|
```
|
|
|
|
|
|
## Networks
|
|
|
|
### Bridge
|
|
|
|
```bash
|
|
docker network ls # Vemos las redes creadas
|
|
|
|
docker run -dit --name alpine1 alpine ash
|
|
docker run -dit --name alpine2 alpine ash
|
|
|
|
docker container ls # Vemos los dos alpine
|
|
|
|
docker network inspect bridge # Vemos que se han conectado al bridge
|
|
|
|
docker attach alpine1 # nos conectamos al contenedor
|
|
|
|
ping alpine2 # NO FUNCIONA por nombre
|
|
ping 172.17.0.4 # FUNCIONA por ip
|
|
|
|
# Para hacer un detach ordenado del alpine1 C-p C-q
|
|
```
|
|
|
|
Vamos a crear una red bridge propia (_user defined bridge_):
|
|
|
|
```bash
|
|
docker network create --driver bridge alpine-net # Creamos la red
|
|
docker network ls # Vemos redes existentes
|
|
docker network inspect alpine-net
|
|
|
|
docker run -dit --name alpine1 --network alpine-net alpine ash # Creamos tres alpines en la red bridge alpine-net
|
|
docker run -dit --name alpine2 --network alpine-net alpine ash
|
|
docker run -dit --name alpine3 --network alpine-net alpine ash
|
|
docker run -dit --name alpine4 alpine ash # este alpine4 está en la bridge default
|
|
|
|
docker network connect bridge alpine3 # alpine3 está conectado a dos redes: default y alpine-net
|
|
|
|
docker container ls
|
|
docker inspect alpine-net
|
|
docker inspect bridge
|
|
|
|
docker attach alpine3 # nos conectamos a alpine3
|
|
ping alpine1 # FUNCIONA, en redes definidas funciona
|
|
# por nombre y por IP
|
|
|
|
ping alpine4 # NO FUNCIONA alpine3 y alpine4 se conectan
|
|
# por la default bridge que no resuelve nombres
|
|
# detach C-p C-q
|
|
```
|
|
|
|
Si probamos desde `alpine1` podremos hacer ping a los `alpine2` y
|
|
`alpine3`, por nombre o dirección ip, pero no podremos llegar a
|
|
`alpine4` de inguna manera, ya que está en otra red.
|
|
|
|
----
|
|
__IMPORTANTE__
|
|
Un _docker-compose_ siempre crea una _user defined
|
|
bridge_, aunque no se lo indiquemos.
|
|
|
|
----
|
|
|
|
## _Volumes_ y _Bind Mounts_
|
|
|
|
### Referencias
|
|
|
|
* [Understanding Docker Volumes: Persisting a Grafana Configuration
|
|
File](https://www.datamachines.io/blog/understanding-docker-volume-persisting-a-grafana-configuration-file)
|
|
* [Stackoverflow: docker data volume vs mounted host directory](https://stackoverflow.com/questions/34357252/docker-data-volume-vs-mounted-host-directory)
|
|
* [Docker Storage](https://docs.docker.com/storage/)
|
|
|
|
Ojito con esto por que, para mi, son muy diferentes y no encontré
|
|
ningún sitio que lo dejara claro desde el principio. Se crean con la
|
|
misma opción `-v` o `--volume` en el comando pero __hay diferencias__.
|
|
|
|
### _Volumes_
|
|
|
|
En principio los contenedores son volátiles y eso está bien para
|
|
ciertas cosas se supone que tienes que diseñarlos para que sean muy
|
|
volátiles y que se puedan crear y matar fácilmente, pero en la
|
|
práctica no. Lo normal es que necesites tener datos persistentes, o
|
|
incluso datos compartidos entre varios contenedores.
|
|
|
|
|
|
* Un _volume_ es un espacio de almacenamiento que crea _Docker_
|
|
internamente, le puedes dar nombre y _docker_ lo va a almacenar en
|
|
algún lugar del disco duro del _host_ (en linux tipicamente
|
|
`/var/lib/docker/volumes`)
|
|
* Se pueden crear previamente dándoles un nombre y separarlos de la
|
|
creación del contenedor: `docker volume create grafana-storage`. En
|
|
_docker-compose_ esto supone declararlos como _external_:
|
|
|
|
```docker-compose
|
|
volumes:
|
|
grafana-storage:
|
|
external: true
|
|
```
|
|
|
|
Eso hace que el _docker-compose file_ no sea independiente (no
|
|
parece lo más elegante).
|
|
|
|
* Si los declaramos en el fichero _docker-compose.yml_ se crearán en el
|
|
caso de que no existan (__parece la mejor manera__)
|
|
|
|
```docker-compose
|
|
volumes:
|
|
- web_data:/usr/share/nginx/html:ro
|
|
```
|
|
|
|
O también:
|
|
|
|
```docker-compose
|
|
volumes:
|
|
web_data:
|
|
name: ${VOLUME_ID}
|
|
|
|
services:
|
|
app:
|
|
image: nginx:alpine
|
|
ports:
|
|
- 80:80
|
|
volumes:
|
|
- web_data:/usr/share/nginx/html:ro
|
|
```
|
|
|
|
### _Bind Mounts_
|
|
|
|
En este caso montamos un fichero o directory __del sistema de ficheros
|
|
del host__ en el contenedor.
|
|
|
|
### Diferencias entre _volumes_ y _bind mounts_
|
|
|
|
* Los _bind mounts_ son más simples a la hora de hacer copias de
|
|
seguridad (aunque en linux no veo grandes diferencias)
|
|
* Los _volumes_ se pueden gestionar desde el CLI del _Docker_
|
|
* Los _volumes_ siempre funcionan, en cualquier S.O. así que tus
|
|
_dockerfiles_ o tus _docker compose files_ serán multiplataforma.
|
|
* Los _volumes_ se pueden compartir entre contenedores fácilmente.
|
|
* El almacenamiento de los _volumes_ es teoricamente más facil de
|
|
implementar en plataformas remotas (la nube)
|
|
* Los _bind mounts_ pueden dar problemas con los permisos de usuarios
|
|
(el usuario que se usa en el _container_ quizás no exista en el
|
|
_host_)
|
|
* Los _bind mounts_ son muy dependientes con el S.O. se suelen
|
|
declarar en variables de entorno para intentar desacoplar los
|
|
_dockerfiles_ y los _docker-compose files_ del S.O. que use cada
|
|
uno, por que las rutas de ficheros evidentemente dependen del S.O.
|
|
|
|
### Renombrar un volumen (receta)
|
|
|
|
```bash
|
|
#/bin/bash
|
|
docker volume create --name $2
|
|
docker run --rm -it -v $1:/from -v $2:/to alpine ash -c "cd /from ; cp -av . /to"
|
|
[ $? -eq 0 ] && docker volume rm $1
|
|
```
|
|
|
|
|
|
## _docker-compose_
|
|
|
|
* [doc oficial](https://docs.docker.com/compose/compose-file)
|
|
* [edututorial](https://www.educative.io/blog/docker-compose-tutorial)
|
|
|
|
|
|
Es el siguiente paso en abstracción. Con _docker-compose_ podemos definir y ejecutar aplicaciones compuestas de múltiples contenedores.
|
|
|
|
La definición se especifica mediante un fichero _YAML_ que permite configurar las aplicaciones y también crearlas.
|
|
|
|
Las principales ventajas de _docker-compose_ son:
|
|
|
|
* Múltiples entornos aislados en un unico _host_
|
|
* Preservar los volúmenes de datos cuando se crean los contenedores
|
|
* Se recrean únicamente los contenedores que cambian
|
|
* Orquestar múltiples contenedores que trabajan juntos
|
|
* Permite definir variable y mover orquestaciones entre entornos
|
|
|
|
### Flujo de trabajo
|
|
|
|
1. Definir los entornos de aplicación con un _dockerfile_
|
|
1. Definir los servicios provistos mediante _docker-compose_. Esto permitirá ejecutarlos en un entorno aislado.
|
|
3. Lanzar los servicios ejecutando el _docker-compose_
|
|
|
|
### _docker-compose_ fichero de configuración
|
|
|
|
La estructura básica de un fichero de configuración para _docker-compose_ tiene esta pinta:
|
|
|
|
```dockercompose
|
|
version: 'X'
|
|
|
|
services:
|
|
web:
|
|
build: .
|
|
ports:
|
|
- "5000:5000" # host:container mejor siempre como string
|
|
volumes:
|
|
- .:/code
|
|
redis:
|
|
image: redis
|
|
```
|
|
|
|
Se puede ver más claro con un fichero real:
|
|
|
|
```docker-compose
|
|
version: '3'
|
|
services:
|
|
web:
|
|
# Path to dockerfile.
|
|
# '.' represents the current directory in which
|
|
# docker-compose.yml is present.
|
|
build: .
|
|
|
|
# Mapping of container port to host
|
|
ports:
|
|
- "5000:5000"
|
|
# Mount volume
|
|
volumes:
|
|
- "/usercode/:/code"
|
|
|
|
# Link database container to app container
|
|
# for reachability.
|
|
links:
|
|
- "database:backenddb"
|
|
|
|
database:
|
|
|
|
# image to fetch from docker hub
|
|
image: mysql/mysql-server:5.7
|
|
|
|
# Environment variables for startup script
|
|
# container will use these variables
|
|
# to start the container with these define variables.
|
|
environment:
|
|
- "MYSQL_ROOT_PASSWORD=root"
|
|
- "MYSQL_USER=testuser"
|
|
- "MYSQL_PASSWORD=admin123"
|
|
- "MYSQL_DATABASE=backend"
|
|
# Mount init.sql file to automatically run
|
|
# and create tables for us.
|
|
# everything in docker-entrypoint-initdb.d folder
|
|
# is executed as soon as container is up nd running.
|
|
volumes:
|
|
- "/usercode/db/init.sql:/docker-entrypoint-initdb.d/init.sql"
|
|
```
|
|
|
|
`version: '3'`
|
|
|
|
: Indica la versión del compose-file que estamos usando ([lista de
|
|
versiones y manual de referencia]( https://docs.docker.com/compose/compose-file/))
|
|
|
|
`services`
|
|
|
|
: Esta sección define los diferentes containers que queremos crear, en
|
|
nuestro ejemplo solo habrá dos "web" y "database"
|
|
|
|
`web`
|
|
|
|
: Marca el comienzo de la definición del servicio "web", en este
|
|
ejemplo sería un servicio implementado con _Flask_
|
|
|
|
`build`
|
|
|
|
: Indica el _path_ al _dockerfile_ que define el servicio, en este
|
|
caso es relativo y el "." significa que el _dockerfile_ está en el
|
|
mismo directorio que el fichero _yaml_ del _compose_
|
|
|
|
`ports`
|
|
|
|
: Mapea puertos desde el container a puertos del _host_
|
|
|
|
`volumes`
|
|
|
|
: mapea sistemas de ficheros del host en el contenedor (igual que la
|
|
opción `-v` en _docker_)
|
|
|
|
`links`
|
|
|
|
: Indica "enlaces" entre contenedores, para la _bridge network_
|
|
debemos indicar que servicios pueden acceder a otros
|
|
|
|
`image`
|
|
|
|
: alternativamente al _dockerfile_ podemos especificar que nuestro
|
|
servicio se basa en una imagen pre-construida
|
|
|
|
`enviroment`
|
|
|
|
: Permite especificar una variable de entorno en el contenedor (igual
|
|
que la opción `-e` en _docker_)
|
|
|
|
### Gestión de variables en _docker-compose_
|
|
|
|
Las variables pueden definirse en varios sitios, por orden de
|
|
precedencia serían:
|
|
|
|
1. Compose file (en una sección _enviroment_)
|
|
2. Variables de entorno del sistema (en nuestro intérprete)
|
|
3. _Enviroment file_, es un fichero en el mismo directorio que el
|
|
fichero de _compose_ con el nombre `.env`
|
|
4. En el _dockerfile_
|
|
5. La variable no está definida
|
|
|
|
La opción _Enviroment file_ es un fichero que se llama `.env`, podemos
|
|
usar otro nombre cualquiera incluyendo la directiva `env_file` en el
|
|
fichero _compose_. Por ejemplo:
|
|
|
|
```docker
|
|
env_file:
|
|
- ./secret-stuff.env
|
|
```
|
|
|
|
Se supone que __no__ vas a subir tus secretos a un repo en la nube. El
|
|
fichero `.env` probablemente no deba estar incluido en _git_ en un
|
|
entorno de producción.
|
|
|
|
[Variables, documentación oficial](https://docs.docker.com/compose/environment-variables/)
|
|
|
|
### docker-compose comandos útiles
|
|
|
|
Reconstruir un contenedor
|
|
: Cuando cambiamos la definición de un servicio dentro del fichero
|
|
`docker-compose.yml`, podemos reconstruir ese servicio con el
|
|
comando:
|
|
```bash
|
|
docker-compose up -d --no-deps --build <service>
|
|
```
|
|
Tenemos una opción más moderna que sería el comando:
|
|
```bash
|
|
docker-compose build --no-cache <service>
|
|
```
|
|
|
|
O bien:
|
|
```bash
|
|
docker-compose up -d --no-cache --build <service>
|
|
```
|
|
|
|
|
|
## Mosquitto + Influxdb + Graphana con _docker_ y _docker-compose_
|
|
|
|
[referencia](https://github.com/Nilhcem/home-monitoring-grafana)
|
|
|
|
Creamos un directorio para nuestro proyecto que llamamos `homesensor`
|
|
(por ejemplo)
|
|
|
|
```bash
|
|
mkdir homesensor
|
|
cd homesensor
|
|
```
|
|
|
|
### Mosquitto
|
|
|
|
Creamos un directorio para nuestro container de _Mosquitto_
|
|
|
|
```bash
|
|
mkdir 01_mosquitto
|
|
cd 01_mosquitto
|
|
```
|
|
|
|
#### Configuración de _mosquitto_
|
|
|
|
Creamos un fichero de configuración de *Mosquitto* `mosquitto.conf` con el siguiente
|
|
contenido:
|
|
|
|
```conf
|
|
persistence true
|
|
persistence_location /mosquitto/data/
|
|
log_dest file /mosquitto/log/mosquitto.log
|
|
allow_anonymous false
|
|
password_file /mosquitto/config/users
|
|
```
|
|
|
|
Con este fichero le indicamos a _Mosquitto_:
|
|
|
|
* Qué queremos que tenga persistencia de datos. Así salvará su estado
|
|
a disco cada 30 minutos (configurable), de lo contrario usaría
|
|
exclusivamente la memoria.
|
|
* le indicamos en que _path_ debe guardar los datos persistentes
|
|
(`/mosquitto/data/`)
|
|
* configuramos el fichero de log (`/mosquitto/log/mosquitto.log`)
|
|
* prohibimos usuarios anónimos
|
|
* establecemos el listado de usuarios con su password
|
|
|
|
----
|
|
__Nota__: Puedes echar un ojo al fichero de ejemplo de configuración
|
|
de mosquitto arrancando un contenedor sin ninguna opción de mapeado de
|
|
volúmenes. O consultando la documentación de _mosquitto_. Hay
|
|
muchísimas más opciones de las que proponemos.
|
|
|
|
----
|
|
|
|
Tendremos que preparar también un fichero `users` de usuarios para
|
|
establecer los usuarios y sus password. Podemos crear un fichero de
|
|
usuarios y passwords en claro con el formato:
|
|
|
|
```
|
|
mqttuser:53cret0
|
|
```
|
|
|
|
Y cifrarlo con el comando `mosquitto_passwd -U <filename>`
|
|
|
|
Si solo tenemos un usuario lo podemos crear con el comando
|
|
`mosquitto_passwd -c <filename> <username>`
|
|
|
|
----
|
|
__Nota__:
|
|
|
|
Nos adelantamos a los acontecimientos, pero si no tienes _mosquitto_
|
|
instalado en tu máquina puedes cifrar el fichero de usuarios con un
|
|
contenedor de _mosquitto_ transitorio, que se encargue del cifrado:
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-v `pwd`/users:/mosquitto/config/users \
|
|
eclipse-mosquitto \
|
|
mosquitto_passwd -U /mosquitto/config/users
|
|
```
|
|
----
|
|
|
|
|
|
#### _mosquitto_ en _docker_ desde linea de comandos
|
|
|
|
Una vez creados estos dos ficheros podemos crear un contenedor
|
|
_mosquitto_ con _ podemos hacerlo desde linea de comandos.
|
|
|
|
```bash
|
|
docker run -it -p 1883:1883 \
|
|
-v `pwd`/mosquitto.conf:/mosquitto/config/mosquitto.conf \
|
|
-v `pwd`/users:/mosquitto/config/users eclipse-mosquitto
|
|
```
|
|
|
|
`run`
|
|
|
|
: Ejecutará el contenedor, si la imagen especificada no existe la
|
|
bajará del _dockerhub_
|
|
|
|
`-it`
|
|
|
|
: La opción `i` ejecuta el contenedor en modo interactivo, la opción
|
|
`t` abrira un terminal contra el contenedor.
|
|
|
|
`-p 1883:1883`
|
|
|
|
: mapea el puerto 1883 del contendor al puerto 1883 del host
|
|
|
|
``-v `pwd`/mosquitto.conf:/mosquitto/config/mosquitto.conf``
|
|
|
|
: mapea el fichero `mosquitto.conf` del directorio actual (`pwd`) en
|
|
el volumen del contenedor: `/mosquitto/config/mosquitto.conf`
|
|
|
|
``-v `pwd`/users:/mosquitto/config/users``
|
|
|
|
: Igual que el anterior pero con el fichero `users`
|
|
|
|
`eclipse-mosquitto`
|
|
|
|
: La imagen que vamos a usar para crear nuestro contenedor, si no
|
|
especificamos nada se usará `:latest`, es decir la imagen más
|
|
reciente.
|
|
|
|
Podemos probar esta configuración con los clientes de mqtt (tenemos
|
|
que tener instalado el paquete `mosquitto-clients` en nuestra máquina)
|
|
|
|
En un terminal lanzamos el _subscriber_:
|
|
|
|
```bash
|
|
mosquitto_sub -h localhost -t test -u mqttuser -P 53cret0
|
|
```
|
|
|
|
Y en otro terminal publicamos un mensaje en el _topic_ test:
|
|
|
|
```bash
|
|
mosquitto_pub -h localhost -t test -u mqttuser -P 53cret0 -m "Hola mundo"
|
|
```
|
|
|
|
Comprobaremos que los mensajes se reciben en el primer terminal.
|
|
|
|
|
|
### InfluxDB
|
|
|
|
Vamos a instalar _InfluxDB_ en _docker_. Igual que con _Mosquitto_
|
|
vamos a mapear los ficheros de configuración y los ficheros donde
|
|
realmente guarda los datos la base de datos en nuestro sistema de
|
|
ficheros local (o el del host)
|
|
|
|
Estamos en el directorio `homesensor` y creamos el directorio
|
|
`02_influxdb`
|
|
|
|
```bash
|
|
mkdir 02_influxdb
|
|
cd 02_influxdb
|
|
```
|
|
|
|
Nos bajamos la imagen oficial de _InfluxDB_ con `docker pull influxdb`
|
|
|
|
Podemos ejecutar un contenedor para probar la imagen con
|
|
|
|
```bash
|
|
docker run -d --name influxdbtest -p 8086:8086 influxdb
|
|
```
|
|
|
|
La opción `-d` (_detached_) es opcional, si no la pones verás el log de
|
|
influxdb, pero no libera el terminal.
|
|
|
|
Ahora desde otro terminal (o desde el mismo si usaste `-d`) podemos
|
|
conectarnos al contenedor con uno de los dos comandos siguientes:
|
|
|
|
```bash
|
|
docker exec -it influxdbtest /bin/bash # con un terminal
|
|
docker exec -it influxdbtest /usr/bin/influx # con el cliente de base de datos
|
|
```
|
|
|
|
Dentro del cliente de base de datos podríamos hacer lo que quisieramos, por ejemplo:
|
|
|
|
```sql
|
|
show databases
|
|
create database mydb
|
|
use mydb
|
|
```
|
|
|
|
Como hemos mapeado el puerto 8086 del contenedor al host (nuestro pc)
|
|
también podemos conectarnos a la base de datos con un cliente en
|
|
nuestro pc, con scripts de python, etc. etc.
|
|
|
|
#### Dos maneras de lanzar el contenedor
|
|
|
|
Podemos examinar la historia de la imagen docker `influxdb` con
|
|
`docker history influxdb`(desde un terminal en nuestro pc). Si sale
|
|
muy apelotonada podemos indicarle que no recorte la salida con `docker
|
|
history --no-trunc influxdb`
|
|
|
|
Veremos que se define `ENTRYPOINT ["/entrypoint.sh"]`
|
|
|
|
Podemos investigar el contenido del fichero `entrypoint.sh` abriendo un
|
|
terminal contra el contenedor `influxdbtest`
|
|
|
|
Después de investigar el contenido del fichero `influxdbtest.sh` y el
|
|
contenido del fichero `init-influxdb.sh` veremos que hay dos formas de
|
|
lanzar un contenedor a partir de la imagen `influxdb` (También nos
|
|
podíamos haber leído [esto](https://hub.docker.com/_/influxdb) para
|
|
llegar a lo mismo más rápido)
|
|
|
|
La primera forma de lanzar el contenedor es la que hemos usado hasta
|
|
ahora:
|
|
|
|
```bash
|
|
docker run -d --name influxdbtest -p 8086:8086 influxdb
|
|
```
|
|
|
|
La segunda forma (más interesante para ciertas cosas) sería invocando
|
|
el script `init-influxdb.sh`:
|
|
|
|
```bash
|
|
docker run --name influxdbtest influxdb /init-influxdb.sh
|
|
```
|
|
|
|
Con esta segunda forma de lanzar el contenedor tenemos dos ventajas:
|
|
|
|
* podemos usar variables de entorno y scripts de inicio, como nos
|
|
explican en [la pagina de
|
|
dockerhub](https://hub.docker.com/_/influxdb) que citamos antes.
|
|
* podemos tener un directorio `/docker-entrypoint-initdb.d` en la raiz
|
|
de nuestro contenedor y cualquier script con extensión `.sh` (shell)
|
|
o `.iql` (Influx QL) se ejecutará al arrancar el contenedor.
|
|
|
|
__Pero__ la segunda forma de lanzar el contenedor sólo es práctica
|
|
para crear y configurar una base de datos, no está pensada para lanzar
|
|
un contenedor "de producción"
|
|
|
|
Cuando acabemos de jugar, paramos el contenedor y lo borramos:
|
|
|
|
```bash
|
|
docker stop influxdbtest
|
|
docker rm influxdbtest
|
|
```
|
|
|
|
#### Configuración de _InfluxDB_
|
|
|
|
Vamos a preparar las cosas para lanzar un contenedor transitorio de
|
|
_InfluxDB_ que nos configure la base de datos que queremos usar.
|
|
|
|
Después lanzaremos el contenedor definitivo que usará la base de datos
|
|
configurada por el primero para dejar el servicio _InfluxDB_
|
|
operativo.
|
|
|
|
Lo primero es hacernos un fichero de configuración para _InfluxDB_.
|
|
Hay un truco para que el propio _InfluxDB_ nos escriba el fichero de
|
|
configuración por defecto:
|
|
|
|
```bash
|
|
cd homesensor/02_influxdb
|
|
|
|
docker run --rm influxdb influxd config |tee influxdb.conf
|
|
```
|
|
|
|
Este comando ejecuta un contenedor transitorio basado en la imagen
|
|
`influxdb`, es un contenedor transitorio por que con la opción `--rm`
|
|
le decimos a _docker_ que lo elimine al terminar la ejecución. Este
|
|
contenedor lo unico que hace es devolvernos el contenido del fichero
|
|
de configuración y desaparecer de la existencia.
|
|
|
|
Ahora tendremos un fichero de configuración `influxdb.conf` en nuestro
|
|
directorio `02_influxdb`. Este fichero lo tenemos que mapear o copiar
|
|
al fichero `/etc/influxdb/influxdb.conf` del contenedor (lo vamos a mapear)
|
|
|
|
Crearemos también un volumen en nuestro contenedor que asocie el
|
|
directorio `/var/lib/influxdb` del contenedor al directorio en el
|
|
sistema de ficheros local: `homesensor/data/influxdb` De esta forma
|
|
tendremos los ficheros que contienen la base de datos en el sistema de
|
|
ficheros del host.
|
|
|
|
También vamos a crear un directorio
|
|
`homesensor/02_influxdb/init-scripts` que mapearemos al directorio
|
|
`/docker-entrypoint-initdb.d` del contenedor. En este directorio
|
|
crearemos un script en _Influx QL_ para crear usuarios y políticas de
|
|
retención para nuestra base de datos.
|
|
|
|
En el directorio `homesensor/02_influxdb/init-scripts`, creamos el
|
|
fichero:
|
|
|
|
`influxdb_init.iql`
|
|
|
|
```sql
|
|
CREATE RETENTION POLICY one_week ON homesensor DURATION 168h REPLICATION 1 DEFAULT;
|
|
CREATE RETENTION POLICY one_year ON homesensor DURATION 52w REPLICATION 1;
|
|
CREATE USER telegraf WITH PASSWORD 'secretpass' WITH ALL PRIVILEGES;
|
|
CREATE USER nodered WITH PASSWORD 'secretpass';
|
|
CREATE USER pybridge WITH PASSWORD 'secretpass';
|
|
GRANT ALL ON homesensor TO nodered;
|
|
GRANT ALL ON homesensor TO pybridge;
|
|
```
|
|
|
|
---
|
|
__NOTA___:
|
|
Creamos el usuario telegraf con todos los privilegios, eso lo
|
|
convierte en administrador de _InfluxDB_. De momento lo vamos a dejar
|
|
así para facilitar las pruebas del contenedor _Telegraf_.
|
|
|
|
---
|
|
|
|
¡Vale! Ya tenemos todo listo. Ahora tenemos que hacer dos cosas.
|
|
|
|
1. Ejecutar un contenedor transitorio que va a crear
|
|
* Mediante variables de entorno (opciones `-e`):
|
|
* Un usario administrador de _InfluxDB_
|
|
* Una base de datos `homesensor`
|
|
* Un usuario genérico de la base de datos `homesensor` que se llama `hsuser`.
|
|
* Mediante el script en _InfluxQL_:
|
|
* Un par de políticas de retención para la base de datos `homesensor`
|
|
* Tres usuarios de bases de datos, a los que damos privilegios
|
|
sobre la base de datos `homesensor`:
|
|
* `telegraf`
|
|
* `noderedu`
|
|
* `pybridge`
|
|
2. Una vez configurada toda la base de datos: activar el acceso seguro
|
|
en la configuración y lanzar el contenedor `influxdb` que se va a
|
|
quedar trabajando.
|
|
|
|
Para el primer paso __configurar la base de datos__ el comando es:
|
|
|
|
```bash
|
|
cd homesensor # ASEGURATE de estar en el directorio homesensor
|
|
sudo rm -rf data/influxdb
|
|
|
|
docker run --rm \
|
|
-e INFLUXDB_HTTP_AUTH_ENABLED=true \
|
|
-e INFLUXDB_ADMIN_USER=admin -e INFLUXDB_ADMIN_PASSWORD=s3cr3t0 \
|
|
-e INFLUXDB_DB=homesensor \
|
|
-e INFLUXDB_USER=hsuser -e INFLUXDB_USER_PASSWORD=pr1vad0 \
|
|
-v $PWD/data/influxdb:/var/lib/influxdb \
|
|
-v $PWD/02_influxdb/influxdb.conf:/etc/influxdb/influxdb.conf \
|
|
-v $PWD/02_influxdb/init-scripts:/docker-entrypoint-initdb.d \
|
|
influxdb /init-influxdb.sh
|
|
```
|
|
|
|
----
|
|
__IMPORTANTE__
|
|
|
|
* Si no pones la opción `-e INFLUXDB_HTTP_AUTH_ENABLED=true` los
|
|
comandos de creación de usuarios fallan sin dar error.
|
|
* Cada vez que lances el contenedor de iniciación __ASEGURATE__ de
|
|
borrar previamente el directorio `homesensor/data/influxdb` o
|
|
fallará todo sin dar errores.
|
|
----
|
|
|
|
Para el segundo paso, es __necesario__ habilitar la seguridad en el
|
|
fichero de configuración antes de arrancar el contenedor. En el
|
|
fichero `influxdb.conf` asegurate de cambiar la linea (de _false_ a _true_):
|
|
|
|
```ini
|
|
[http]
|
|
- auth-enabled = false
|
|
+ auth-enabled = true
|
|
```
|
|
|
|
Y por fin, para arrancar el contenedor `influxdb` que se quedará dando
|
|
servicio, el comando es:
|
|
|
|
```bash
|
|
cd homesensor # asegurate de estar en el directorio homesensor
|
|
docker run -d --name influxdb -p 8086:8086 \
|
|
-v $PWD/data/influxdb:/var/lib/influxdb \
|
|
-v $PWD/02_influxdb/influxdb.conf:/etc/influxdb/influxdb.conf \
|
|
influxdb
|
|
```
|
|
|
|
Con el contenedor _influxdb_ disponible podemos conectarnos a la base
|
|
de datos, ya sea desde nuestra máquina (si tenemos el cliente
|
|
instalado en local) o abriendo un terminal al contenedor.
|
|
|
|
Si ejecutamos:
|
|
|
|
```bash
|
|
> show databases
|
|
ERR: unable to parse authentication credentials
|
|
Warning: It is possible this error is due to not setting a database.
|
|
Please set a database with the command "use <database>".
|
|
```
|
|
|
|
Nos da un error de autenticación.
|
|
|
|
Si añadimos usuario y password entramos sin problemas y podemos vere
|
|
que la base de datos `homesensor` y todos los usuarios se han creado
|
|
correctamente.
|
|
|
|
```bash
|
|
influx --username admin --password s3cr3t0
|
|
Connected to http://localhost:8086 version 1.8.3
|
|
InfluxDB shell version: 1.8.3
|
|
> show databases
|
|
name: databases
|
|
name
|
|
----
|
|
homesensor
|
|
_internal
|
|
> show users
|
|
user admin
|
|
---- -----
|
|
admin true
|
|
idbuser false
|
|
telegrafuser false
|
|
nodereduser false
|
|
pybridgeuser false
|
|
|
|
```
|
|
Un ejemplo de continous query para el dia que haga falta:
|
|
|
|
```sql
|
|
CREATE CONTINUOUS QUERY "cq_30m" ON "homesensor"
|
|
BEGIN
|
|
SELECT mean("temperature") AS "mean_temperature",mean("humidity") AS "mean_humidity" \
|
|
INTO "a_year"."acum_ambient"
|
|
FROM "ambient"
|
|
GROUP BY time(30m)
|
|
END;
|
|
|
|
```
|
|
|
|
### Telegraf
|
|
|
|
Preparamos un fichero de configuración de _Telegraf_. _Telegraf_ es de
|
|
la familia _InfluxDB_ así que soporta el mismo truco, para volcar una
|
|
configuración por defecto:
|
|
|
|
```bash
|
|
cd homesensor
|
|
mkdir 03_telegraf
|
|
cd 03_telegraf
|
|
docker run --rm telegraf telegraf config |tee telegraf.conf
|
|
```
|
|
|
|
En _Telegraf_ todo se hace en el fichero de configuración, en el se
|
|
programan las entradas y las salidas.
|
|
|
|
#### Usando `container:network`
|
|
|
|
Este escenario lo pongo por que me pareció un caso interesante. Pero
|
|
no veo que sea posible implementarlo en _docker-compose_ así que no
|
|
será el definitivo.
|
|
|
|
1. Lanzamos el container `influxdb`
|
|
|
|
```bash
|
|
docker run -d --name influxdb -p 8086:8086 \
|
|
-v $PWD/data/influxdb:/var/lib/influxdb \
|
|
-v $PWD/02_influxdb/influxdb.conf:/etc/influxdb/influxdb.conf \
|
|
-v $PWD/02_influxdb/init-scripts:/docker-entrypoint-initdb.d \
|
|
influxdb
|
|
```
|
|
2. Modificamos el fichero de configuración de _Telegraf_ para poner usuario y password.
|
|
|
|
```init
|
|
## HTTP Basic Auth
|
|
username = "telegraf"
|
|
password = "secretpass"
|
|
```
|
|
|
|
3. Lanzamos el contenedor `telegraf`
|
|
|
|
```bash
|
|
docker run -d --name telegraf \
|
|
--network=container:influxdb \
|
|
-v /proc:/host/proc:ro \
|
|
-v $PWD/03_telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro \
|
|
telegraf
|
|
```
|
|
|
|
Si nos conectamos con el cliente `influx` al servidor de bases de
|
|
datos (o nos conectamos al contenedor y ejecutamos el cliente) veremos
|
|
que _Telegraf_ está mandando todas las estadísticas que tiene
|
|
definidas por defecto de nuestro host.
|
|
|
|
Para que todo esto funcione:
|
|
|
|
* Hemos mapeado el directorio `/proc` del linux de nuestro host
|
|
(nuestro pc o servidor) en el directorio `/host/proc` del contenedor
|
|
`telegraf`, así que _Telegraf_ puede leer varias estadísticas de
|
|
nuestro _host_ (es decir nuestro pc) y escribir los distintos
|
|
valores como _measurements_ en la base de datos `telegraf` (es el
|
|
nombre de la base de datos que usa por defecto).
|
|
* El contenedor `telegraf` ha creado esta base de datos que no existía
|
|
gracias a que el usario que le hemos configurado para acceder a la
|
|
base de datos tiene permisos de administración, de lo contrario
|
|
tendríamos que haber creado nosotros la base de datos antes de que
|
|
se conectara.
|
|
* Hemos usado la opción `--network=container:influxdb`, esta opción
|
|
hace que el contenedor `telegraf` se cree en el `network stack` del
|
|
contenedor `influxdb`, en la práctica el _Telegraf_ se cree que está
|
|
en la misma máquina que _InfluxDB_ y se comunica via el interfaz
|
|
_loopback_ Esta opción tan curiosa creo que no se puede implementar
|
|
en _docker-compose_ (al menos yo no he encontrado nada parecido)
|
|
|
|
|
|
### Grafana
|
|
|
|
Grafana se puede configurar a través de su propia interfaz web.
|
|
|
|
|
|
---- __NOTA__:
|
|
|
|
No me aclaro con [la documentación de _Grafana_ para
|
|
_Docker_](https://grafana.com/docs/grafana/latest/installation/docker/),
|
|
la única forma en que he conseguido mapear la base de datos en el
|
|
directorio local `data/grafana` ha sido lanzar el contenedor como
|
|
_root_. No se que riesgos implica eso.
|
|
|
|
----
|
|
|
|
Lanzamos el contenedor:
|
|
|
|
```bash
|
|
docker run --user root -d --name=grafana -p 3000:3000 \
|
|
-v $PWD/data/grafana:/var/lib/grafana \
|
|
grafana/grafana
|
|
```
|
|
|
|
|
|
La primera vez que nos conectamos a grafana tenemos que entrar con el
|
|
usuario `admin` con contraseña `admin`. Nos pedirá cambiar la
|
|
contraseña.
|
|
|
|
|
|
* Nos conectamos a <http://localhost:3000>
|
|
* Configuramos la contraseña del administrador
|
|
* Añadimos InfluxDB como fuente de datos. En este escenario tenemos que
|
|
configurar la dirección IP del contenedor `influxdb` así que
|
|
tendremos que usar algún comando del estilo `docker inspect
|
|
influxdb` o `docker inspect bridge |grep influxdb -A 5` para
|
|
averiguar la dirección IP.
|
|
* __name__ influxdb
|
|
* __query language__ InfluxQL
|
|
* __http url__ <http://ipaddres:8086>
|
|
* __Access__ server
|
|
* __Auth__ Basic auth with credentials
|
|
* __user__ telegraf (con su contraseña)
|
|
* __database__ telegraf, configurar usuario y contraseña
|
|
* __http method__ GET
|
|
* Por ultimo el report de grafana lo importamos de uno que hay
|
|
pre-construido para este escenario con el id 1443
|
|
|
|
[Grafana Data Sources tutorial](https://grafana.com/tutorials/grafana-fundamentals/?utm_source=grafana_gettingstarted#1)
|
|
|
|
|
|
|
|
### Solución completa con _docker-compose_
|
|
|
|
#### _mosquitto_ en _docker-compose_
|
|
|
|
Definiremos el servicio _mosquitto_ en nuestro fichero
|
|
`homesensor/docker-compose.yml`
|
|
|
|
|
|
```docker
|
|
version: '3'
|
|
|
|
services:
|
|
mosquitto:
|
|
image: eclipse-mosquitto
|
|
container_name: mosquitto
|
|
ports:
|
|
- 1883:1883
|
|
volumes:
|
|
- ./01_mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf
|
|
- ./01_mosquitto/users:/mosquitto/config/users
|
|
- ${DATA_DIR}/mosquitto/data:/mosquitto/data
|
|
- ${DATA_DIR}/mosquitto/log:/mosquitto/log
|
|
restart: always
|
|
```
|
|
|
|
Como se puede ver en el fichero `homesensor/docker-compose.yml` hemos
|
|
definido dos volúmenes adicionales para tener accesibles desde el
|
|
_host_ el directorio de datos: `/mosquitto/data` y el directorio de
|
|
logs `/mosquitto/log`
|
|
|
|
El mapeado de los nuevos volúmenes está controlado por una variable
|
|
`DATA_DIR` que podemos pasar desde el entorno de sistema o con un
|
|
fichero `.env` para que apunte, por ejemplo, al directorio
|
|
`homesensor/data` en el host.
|
|
|
|
`.env`
|
|
|
|
```bash
|
|
DATA_DIR=./data
|
|
```
|
|
|
|
|
|
Ahora podemos lanzar el contenedor con `docker-compose up` (asegúrate
|
|
de borrar el contenedor que creamos a mano, y de establecer la
|
|
variable `DATA_DIR` de alguna manera)
|
|
|
|
Una vez lanzado el contenedor podemos hacer pruebas con los clientes
|
|
mqtt instalados y comprobaremos que los datos y el log de _mosquitto_
|
|
se guardan en el host.
|
|
|
|
Otra cosa que podemos comprobar al usar _docker-compose_ es que se
|
|
crea una red específica y segregada para nuestros contenedores.
|
|
|
|
```bash
|
|
docker network ls
|
|
NETWORK ID NAME DRIVER SCOPE
|
|
d48756f76467 bridge bridge local
|
|
315e4570dff1 homesensor_default bridge local
|
|
9b579976258b host host local
|
|
51c50bab6f35 none null local
|
|
|
|
docker inspect homesensor_default
|
|
|
|
.
|
|
.
|
|
.
|
|
"Containers": {
|
|
"39a45dde629a0ab6f834e7c59f0d9785c64ba31b0d3d93c6c8d2622d31607c28": {
|
|
"Name": "cmosquitto",
|
|
"EndpointID": "504ae74b9109e6d87cd2359f7d6b17352388a5a9a48f7bba5aa7ad29fa43977e",
|
|
"MacAddress": "02:42:ac:1b:00:02",
|
|
"IPv4Address": "172.27.0.2/16",
|
|
"IPv6Address": ""
|
|
}
|
|
},
|
|
.
|
|
.
|
|
.
|
|
```
|
|
|
|
Podemos ver que ahora tenemos la red `homesensor_default` y que dentro
|
|
de esa red solo tenemos el contenedor `mosquito`.
|
|
|
|
|
|
|
|
#### _InfluxDB_ en _docker-compose_
|
|
|
|
Añadimos la configuración de _InfluxDB_ a nuestro fichero `dockercompose.yml`
|
|
|
|
#### Grafana en _docker-compose_
|
|
|
|
Añadimos la configuración del servicio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Preparamos directorios y ficheros de trabajo
|
|
|
|
```bash
|
|
mkdir -p ~/work/docker/{traefik,portainer}
|
|
touch ~/work/docker/traefik/{acme.json,traefik.yml}
|
|
chmod 600 ~/work/docker/traefik/acme.json
|
|
```
|
|
|
|
`acme.json` es el fichero que almacenará los certificados HTTPS
|
|
`traefik.yml` es el fichero de configuración de _Traefik_.
|
|
|
|
Un fichero básico para la configuración de _Traefik_ sería:
|
|
|
|
```yml
|
|
api:
|
|
dashboard: true
|
|
insecure: true
|
|
|
|
entryPoints:
|
|
http:
|
|
address: ":80"
|
|
https:
|
|
address: ":443"
|
|
|
|
providers:
|
|
docker:
|
|
endpoint: "unix:///var/run/docker.sock"
|
|
exposedByDefault: false
|
|
|
|
certificatesResolvers:
|
|
http:
|
|
acme:
|
|
email: <my_email@example.com>
|
|
storage: acme.json
|
|
httpChallenge:
|
|
entryPoint: http
|
|
```
|
|
|
|
Un fichero de configuración más completo:
|
|
|
|
```yml
|
|
|
|
version: "3.3"
|
|
|
|
services:
|
|
################################################
|
|
#### Traefik Proxy Setup #####
|
|
###############################################
|
|
traefik:
|
|
image: traefik:v2.0
|
|
restart: always
|
|
container_name: traefik
|
|
ports:
|
|
- "80:80" # <== http
|
|
- "8080:8080" # <== :8080 is where the dashboard runs on
|
|
- "443:443" # <== https
|
|
command:
|
|
#### These are the CLI commands that will configure Traefik and tell it how to work! ####
|
|
## API Settings - https://docs.traefik.io/operations/api/, endpoints - https://docs.traefik.io/operations/api/#endpoints ##
|
|
- --api.insecure=true # <== Enabling insecure api, NOT RECOMMENDED FOR PRODUCTION
|
|
- --api.dashboard=true # <== Enabling the dashboard to view services, middlewares, routers, etc...
|
|
- --api.debug=true # <== Enabling additional endpoints for debugging and profiling
|
|
## Log Settings (options: ERROR, DEBUG, PANIC, FATAL, WARN, INFO) - https://docs.traefik.io/observability/logs/ ##
|
|
- --log.level=DEBUG # <== Setting the level of the logs from traefik
|
|
## Provider Settings - https://docs.traefik.io/providers/docker/#provider-configuration ##
|
|
- --providers.docker=true # <== Enabling docker as the provider for traefik
|
|
- --providers.docker.exposedbydefault=false # <== Don't expose every container to traefik, only expose enabled ones
|
|
- --providers.file.filename=/dynamic.yaml # <== Referring to a dynamic configuration file
|
|
- --providers.docker.network=frontend # <== Operate on the docker network named 'frontend'
|
|
## Entrypoints Settings - https://docs.traefik.io/routing/entrypoints/#configuration ##
|
|
- --entrypoints.web.address=:80 # <== Defining an entrypoint for port :80 named 'web'
|
|
- --entrypoints.web-secured.address=:443 # <== Defining an entrypoint for https on port :443 named 'web-secured'
|
|
## Certificate Settings (Let's Encrypt) - https://docs.traefik.io/https/acme/#configuration-examples ##
|
|
- --certificatesresolvers.mytlschallenge.acme.tlschallenge=true # <== Enable TLS-ALPN-01 to generate and renew ACME certs
|
|
- --certificatesresolvers.mytlschallenge.acme.email=salvari@gmail.com # <== Setting email for certs
|
|
- --certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json # <== Defining acme file to store cert information
|
|
volumes:
|
|
- ./letsencrypt:/letsencrypt # <== Volume for certs (TLS)
|
|
- /var/run/docker.sock:/var/run/docker.sock # <== Volume for docker admin
|
|
- ./dynamic.yaml:/dynamic.yaml # <== Volume for dynamic conf file, **ref: line 27
|
|
networks:
|
|
- frontend # <== Placing traefik on the network named 'frontend', to access containers on this network
|
|
labels:
|
|
#### Labels define the behavior and rules of the traefik proxy for this container ####
|
|
- "traefik.enable=true" # <== Enable traefik on itself to view dashboard and assign subdomain to view it
|
|
- "traefik.http.routers.api.rule=Host(`monitor.comacero.com`)" # <== Setting the domain for the dashboard
|
|
- "traefik.http.routers.api.service=api@internal" # <== Enabling the api to be a service to access
|
|
```
|
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
**LO QUE SIGUE SON NOTAS SUELTAS PENDIENTES DE ORDENAR Y REVISAR**
|
|
|
|
|
|
|
|
## Arrancando mariadb
|
|
|
|
~~~~{bash}
|
|
docker run --name mariadbtest -e MYSQL_ROOT_PASSWORD=mypass -d mariadb
|
|
docker inspect mariadbtest
|
|
~~~~
|
|
|
|
Del segundo comando sacamos la ip,
|
|
|
|
----------
|
|
|
|
**TODO**:hay que mirar los format que soporta el inspect.
|
|
|
|
---------
|
|
|
|
|
|
|
|
|
|
## Cosas variadas
|
|
|
|
El fichero para lanzar traefik que usamos [en el servidor de
|
|
Bricolabs](https://bricolabs.cc/wiki/privado/sysadmin/traefik_e_cryptpad).
|
|
|
|
~~~{yml}
|
|
version: "3.3"
|
|
services:
|
|
app:
|
|
image: "traefik:2.1"
|
|
container_name: "traefik"
|
|
command:
|
|
- "--api.insecure=true"
|
|
- "--providers.docker=true"
|
|
- "--providers.docker.exposedbydefault=false"
|
|
- "--entrypoints.web.address=:80"
|
|
- "--entrypoints.websecure.address=:443"
|
|
ports:
|
|
- "80:9080"
|
|
- "443:9443"
|
|
volumes:
|
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
|
~~~
|
|
|
|
|
|
## networks
|
|
|
|
bridge 172.17.0.0/16
|
|
|
|
Creamos también:
|
|
|
|
~~~~{bash}
|
|
docker network create --subnet 172.20.0.0/16 backend
|
|
docker network create --subnet 172.21.0.0/16 frontend
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
|
|
# Configuración en comacero.com
|
|
|
|
## Networks
|
|
|
|
Vamos a crear dos redes en el docker:
|
|
|
|
~~~~{bash}
|
|
docker network create --subnet 172.20.0.0/16 backend
|
|
docker network create --subnet 172.21.0.0/16 frontend
|
|
~~~~
|
|
|
|
La red `backend` estará aislada del exterior y será solo para que unos
|
|
dockers se comuniquen con los otros.
|
|
|
|
La red `frontend` será la que tenga comunicación con internet. Ya
|
|
veremos como queda pero lo más probable es que todo tenga que pasar
|
|
por el servidor `traefik`.
|
|
|
|
|
|
## Traefik
|
|
|
|
Vamos a hacer pruebas con lo que nos recomiendan en [este
|
|
tutorial](https://www.youtube.com/watch?v=Gk9WER6DunE)
|
|
|
|
El fichero _compose_ para el _Traefik_:
|
|
|
|
```docker-compose.yaml
|
|
|
|
version: "3.3"
|
|
|
|
services:
|
|
################################################
|
|
#### Traefik Proxy Setup #####
|
|
###############################################
|
|
traefik:
|
|
image: traefik:v2.0
|
|
restart: always
|
|
container_name: traefik
|
|
ports:
|
|
- "80:80" # <== http
|
|
- "8080:8080" # <== :8080 is where the dashboard runs on
|
|
- "443:443" # <== https
|
|
command:
|
|
#### These are the CLI commands that will configure Traefik and tell it how to work! ####
|
|
## API Settings - https://docs.traefik.io/operations/api/, endpoints - https://docs.traefik.io/operations/api/#endpoints ##
|
|
- --api.insecure=true # <== Enabling insecure api, NOT RECOMMENDED FOR PRODUCTION
|
|
- --api.dashboard=true # <== Enabling the dashboard to view services, middlewares, routers, etc...
|
|
- --api.debug=true # <== Enabling additional endpoints for debugging and profiling
|
|
## Log Settings (options: ERROR, DEBUG, PANIC, FATAL, WARN, INFO) - https://docs.traefik.io/observability/logs/ ##
|
|
- --log.level=DEBUG # <== Setting the level of the logs from traefik
|
|
## Provider Settings - https://docs.traefik.io/providers/docker/#provider-configuration ##
|
|
- --providers.docker=true # <== Enabling docker as the provider for traefik
|
|
- --providers.docker.exposedbydefault=false # <== Don't expose every container to traefik, only expose enabled ones
|
|
- --providers.file.filename=/dynamic.yaml # <== Referring to a dynamic configuration file
|
|
- --providers.docker.network=frontend # <== Operate on the docker network named 'frontend'
|
|
## Entrypoints Settings - https://docs.traefik.io/routing/entrypoints/#configuration ##
|
|
- --entrypoints.web.address=:80 # <== Defining an entrypoint for port :80 named 'web'
|
|
- --entrypoints.web-secured.address=:443 # <== Defining an entrypoint for https on port :443 named 'web-secured'
|
|
## Certificate Settings (Let's Encrypt) - https://docs.traefik.io/https/acme/#configuration-examples ##
|
|
- --certificatesresolvers.mytlschallenge.acme.tlschallenge=true # <== Enable TLS-ALPN-01 to generate and renew ACME certs
|
|
- --certificatesresolvers.mytlschallenge.acme.email=salvari@gmail.com # <== Setting email for certs
|
|
- --certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json # <== Defining acme file to store cert information
|
|
volumes:
|
|
- ./letsencrypt:/letsencrypt # <== Volume for certs (TLS)
|
|
- /var/run/docker.sock:/var/run/docker.sock # <== Volume for docker admin
|
|
- ./dynamic.yaml:/dynamic.yaml # <== Volume for dynamic conf file, **ref: line 27
|
|
networks:
|
|
- frontend # <== Placing traefik on the network named 'frontend', to access containers on this network
|
|
labels:
|
|
#### Labels define the behavior and rules of the traefik proxy for this container ####
|
|
- "traefik.enable=true" # <== Enable traefik on itself to view dashboard and assign subdomain to view it
|
|
- "traefik.http.routers.api.rule=Host(`monitor.comacero.com`)" # <== Setting the domain for the dashboard
|
|
- "traefik.http.routers.api.service=api@internal" # <== Enabling the api to be a service to access
|
|
```
|
|
|
|
## nginx docker
|
|
|
|
|
|
```shell
|
|
docker run --name docker-nginx -p 8080:80 nginx # mapea el puerto 8080 del host
|
|
|
|
docker exec -it CONTAINER_ID bash # para conectarnos con el terminal
|
|
```
|
|
|
|
Crear un contenedor corriendo nginx con contenido estático
|
|
|
|
Creamos los direcotorios:
|
|
|
|
```
|
|
nginx
|
|
└── shtml
|
|
```
|
|
|
|
Creamos un fichero `nginx/Dockerfile` con estas lineas:
|
|
|
|
``` Dockerfile
|
|
FROM nginx:latest
|
|
COPY shtml /usr/share/nginx/html
|
|
```
|
|
|
|
Y un contenido _html_ estático en el fichero `shtml/index.html`, por ejemplo:
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="docker-nginx" content="nginx-docker Test">
|
|
<title>Plain HTML site using nginx and docker</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
<div class="navbar">
|
|
<a href="https://comacero.gitlab.io/">Blog Comacero</a>
|
|
<a href="https://comacero.gitlab.io/privado/">Comacero Dark</a>
|
|
<a href="https://gitlab.com/pages/">Gitlab Pages</a>
|
|
</div>
|
|
|
|
<h1>Hello World!</h1>
|
|
|
|
<p>
|
|
This is a simple plain-HTML website on Docker.
|
|
</p>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
Ahora creamos el contenedor con `docker build -t static-nginx .` (ojo al punto al final)
|
|
|
|
Y ejecutamos nuestro nuevo contenedor con `docker run --name nginx-static -p 80:80 -d static-nginx`
|
|
|
|
-->
|
|
|
|
## Recetas
|
|
|
|
### Backup de volúmenes
|
|
|
|
Los pasos típicos
|
|
|
|
1. Parar todos los contenedores que usen los volúmenes que queremos salvar
|
|
2. Arrancar un contenedor dedicado para hacer los backups. Tiene que montar los volúmenes que queremos salvaguardar y montar también un volumen (probablemente un _bind-mount_, donde salvaremos los backups)
|
|
3. Hacer los backups
|
|
4. Parar el contenedor de backups
|
|
5. Arrancar los contenedores de producción
|
|
|
|
Un ejemplo:
|
|
|
|
```bash
|
|
docker stop targetContainer
|
|
mkdir ./backup
|
|
docker run --rm --volumes-from targetContainer -v ~/backup:/backup ubuntu bash -c “cd /var/lib/targetCont/content && tar cvf /backup/ghost-site.tar .”
|
|
|
|
```
|
|
|
|
### Ejecutar aplicaciones X11 en un contenedor Docker
|
|
|
|
Supongamos que queremos ejecutar una aplicación X11 en un contenedor y verla en el escritorio del Host.
|
|
|
|
Tenemos dos alternativas, lanzar la aplicación contra el Xdisplay de nuestro ordenador (`echo "$DISPLAY"`) o preparar otro _display_ dedicado a nuestro contenedor.
|
|
|
|
{{< admonition type=tip title="Otras vias" open=false >}}
|
|
|
|
[Aquí](https://www.howtogeek.com/devops/how-to-run-gui-applications-in-a-docker-container/) describen como lanzar aplicaciones via el _X11 socket_ o utilizando VNC. Pero yo creo que las recetas que apunto son más fáciles.
|
|
|
|
{{< /admonition >}}
|
|
|
|
|
|
#### Usar el Xdisplay de nuestro escritorio
|
|
|
|
Las ventajas de esta opción es que la aplicación del contenedor aparecerá simplemente como una ventana más en nuestro escritorio, no deberíamos tener ningún problema a la hora de usar nuestros dispositivos de entrada (ratón, teclado, etc.) y de salida (pantalla básicamente).
|
|
|
|
1. Averiguar nuestro display con `echo "$DISPLAY"`
|
|
2. Autorizar las conexiones a nuestro display, por seguridad están prohibidas
|
|
|
|
```bash
|
|
xhost + # Esto equivale a autorizar todas las conexiones
|
|
# Es mejor NO USAR esta opción
|
|
|
|
xhost +"local:docker@" # Esta opción es mucho más segura
|
|
# solo permite conexiones desde nuestros contenedores
|
|
```
|
|
|
|
3. Lanzar nuestro contenedor estableciendo la variable de entorno `$DISPLAY` y fijando el `net` de tipo `host`:
|
|
|
|
```bash
|
|
docker run --rm --it --net=host -e DISPLAY=$DISPLAY
|
|
```
|
|
|
|
El detalle importante aquí es limitar lo más posible las conexiones autorizadas a nuestro Xdisplay
|
|
|
|
|
|
#### Lanzar otro display dedicado
|
|
|
|
Yo suelo usar _Xephyr_ para hacer estos experimentos. _Xephyr_ es un servidor de display anidado. _Xephyr_ nos da un Xdisplay al que podemos conectar aplicaciones X11 pero que funciona como una ventana dentro de nuestro entorno gráfico (es decir está anidado en el Xdisplay de nuestro escritorio).
|
|
|
|
La ventaja de esta opción es que no abrimos autorizaciones al Xdisplay de nuestro sistema. A cambio tendremos que ver como usa Xephyr nuestro teclado y ratón.
|
|
|
|
1. Instalar _Xephyr_
|
|
```bash
|
|
sudo apt install xserver-xephyr
|
|
```
|
|
|
|
2. Lanzar una ventana de _Xephyr_
|
|
|
|
```bash
|
|
echo "$DISPLAY" # Averiguamos nuestro DISPLAY, que suele ser el 0 o el 1
|
|
# HAY QUE USAR UNO DISTINTO PARA Xephyr
|
|
|
|
Xephyr -ac -screen 800x600 -br -reset -terminate 2> /dev/null :1 & # Arrancamos el Xserver
|
|
```
|
|
|
|
Opciones de Xephyr utilizadas:
|
|
- __-ac__
|
|
|
|
Autorizar conexiones de clientes indiscriminadamente (disable access restrictions)
|
|
- __-screen__
|
|
|
|
Especificar la geometría de la pantalla
|
|
- __-br__
|
|
|
|
La ventana raiz tendrá fondo negro
|
|
|
|
- __-reset__
|
|
|
|
Reset al terminar el último cliente
|
|
|
|
- __-terminate__
|
|
|
|
Finalizar cuando se resetee el servidor
|
|
- __2> /dev/null__
|
|
|
|
Mandar los mensajes de error al limbo (alias NE en nuestro pc)
|
|
- __:1__
|
|
|
|
Arrancar el server en el DISPLAY=1 __TIENE QUE SER DISTINTO DEL DISPLAY DEL SISTEMA__
|
|
|
|
3. Por último lanzamos nuestro contenedor igual que en la primera opción, pero con el Xdisplay correspondiente a _Xephyr_
|
|
|
|
```bash
|
|
docker run --rm --it --net=host -e DISPLAY=":1"
|
|
```
|
|
|
|
|
|
## Referencias
|
|
|
|
* [Getting Started (oficial)](https://docs.docker.com/get-started/)
|
|
* [El tutorial de atareao](https://www.atareao.es/tutorial/docker/)
|
|
* [The smart person guide to Docker](https://www.techrepublic.com/article/docker-the-smart-persons-guide/)
|
|
* [Lista de versiones de compose-file y manual de referencia]( https://docs.docker.com/compose/compose-file/)
|
|
* [Kitematic: un interfaz gráfico para docker](https://kitematic.com/)
|
|
* [Podman, un docker alternativo](https://www.atareao.es/podcast/es-podman-la-alternativa-a-docker/)
|
|
|
|
* [Docker-compose tutorial](https://www.educative.io/blog/docker-compose-tutorial) <-- No está mal
|
|
* [Advanced Docker-compose configuration](https://runnable.com/docker/advanced-docker-compose-configuration)
|
|
* [Docker Compose Tutorial](https://vegibit.com/docker-compose-tutorial/)
|
|
|
|
* [Awesome Compose](https://github.com/docker/awesome-compose)
|
|
|
|
|
|
### influxdb
|
|
* https://thenewstack.io/how-to-setup-influxdb-telegraf-and-grafana-on-docker-part-1/
|
|
* https://lazyadmin.nl/it/installing-grafana-influxdb-and-telegraf-using-docker/
|
|
* https://dev.to/project42/install-grafana-influxdb-telegraf-using-docker-compose-56e9
|
|
|
|
|
|
|
|
### uwsgi and traefik
|
|
|
|
* https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https
|
|
* https://beenje.github.io/blog/posts/running-your-application-over-https-with-traefik/
|
|
* https://dockerquestions.com/2020/03/23/route-to-flask-and-vue-containers-with-traefik/
|
|
* https://www.fullstackpython.com/wsgi-servers.html
|
|
* https://gist.github.com/nknapp/20c7cd89f1f128b8425dd89cbad0b802
|
|
* https://medium.com/@tiangolo/full-stack-modern-web-applications-using-python-flask-docker-swagger-and-more-b6609dedb747
|
|
* https://stackoverflow.com/questions/44639958/nginx-behind-traefik-docker-swarm-mode-real-ip
|
|
|
|
|
|
### nginx and traefik
|
|
* [Static files with nginx and traefik](https://www.simplecto.com/use-traefik-with-nginx-apache-caddyserver-serve-static-files/)
|
|
* [Install traefik](https://www.howtoforge.com/tutorial/ubuntu-docker-traefik-proxy/)
|
|
* [Traefik and worpress, as usual](https://www.digitalocean.com/community/tutorials/how-to-use-traefik-as-a-reverse-proxy-for-docker-containers-on-ubuntu-16-04)
|
|
* [nginx behind traefik question](https://stackoverflow.com/questions/44639958/nginx-behind-traefik-docker-swarm-mode-real-ip)
|
|
|
|
|
|
|
|
* https://docs.ovh.com/gb/en/domains/create_a_dns_zone_for_a_domain_which_is_not_registered_at_ovh/
|
|
|
|
|
|
|
|
### Kibana and Elastic Search
|
|
|
|
* [elastic kibana and docker-compose](https://codingfundas.com/how-to-install-elasticsearch-7-with-kibana-using-docker-compose/index.html)
|
|
* [quick start](https://www.devopsroles.com/quick-start-install-elasticsearch-and-kibana-with-docker/)
|
|
* [docker logs elastic and kibana](https://www.sarulabs.com/post/5/2019-08-12/sending-docker-logs-to-elasticsearch-and-kibana-with-filebeat.html)
|