Reinicio/Restart de Contenedores en Docker.
El concepto de “Docker”/”contenedores” brinda una solución técnica usando pocos recursos, permitiendo tener muchos contenedores corriendo sobre el mismo sistema operativo, mantenerlos aislados o conectados según sea la necesidad, permite encapsularlos, un uso mínimo de la memoria, cpu y red. Docker es una de las soluciones mas eficientes que existen pero nada es perfecto, a prueba de fallos/balas y debe ser preparado para posibles fallos.
También existe la posibilidad de que un contenedor/servicio sea detenido por uso excesivo de RAM. Lo veremos en otro post.
Hemos presentado en otros posts como monitorizar Docker y sus contenedores, sus consumos, también existen herramientas (nagios, suricata y otras) para ver monitorizar servicios y en caso de caídas alertarnos o realizar acciones correctivas.
Docker nos brinda algunas opciones para intentar mejorar la calidad del servicio que brinda, aqui presentaremos algunos ejemplos.
Lo primera será generar un incidente controlado en un contenedor, mediante un proceso que se cancela.
En nuestro equipo real creamos un script. Demora 10 segundos y luego sale del proceso indicando un error “1”.
# vi error.sh
#/bin/bash
sleep 10
exit 1
Crearemos ahora una imágen incluyendo este script y que será arrancado en el contenedor que crearemos.
# vi Dockerfile
FROM ubuntu:17.10
ADD error.sh /
CMD /bin/bash /error.sh
Creando un contenedor, aún no lo correremos con el script dentro.
# docker build -t crashing .
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM ubuntu:17.10
17.10: Pulling from library/ubuntu
0bd639347642: Pull complete
15f827925d02: Pull complete
8d4e9883d6b5: Pull complete
c754e879539b: Pull complete
85f5abd03ce7: Pull complete
Digest: sha256:01421c4dccafd6d38272e8299f5a23019b7937bea8cc4e7fdfc1bf266a77f369
Status: Downloaded newer image for ubuntu:17.10
---> 275d79972a86
Step 2/3 : ADD error.sh /
---> 3b0e72214591
Step 3/3 : CMD /bin/bash /error.sh
---> Running in c4c859e1ae36
Removing intermediate container c4c859e1ae36
---> 48acec713458
Successfully built 48acec713458
Successfully tagged crashing:latest
La imágen preparada mide unos 95 mb.
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
crashing latest 48acec713458 50 seconds ago 94.6MB
Ahora la arrancamos, veremos que durante 10 segundos está corriendo y luego se dentendrá.
# docker run --name crash1 crashing
Al ver los contenedores que tenemos/corrieron, vemos nuestro “Crash1”.
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba53134ef75d crashing "/bin/sh -c '/bin/ba…" 53 seconds ago Exited (1) 41 seconds ago crash1
Durante esos 10 segundos podemos ver el contenedor corriendo. Aunque luego desaparece.
# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
ba53134ef75d crash1 0.00% 1.68MiB / 7.677GiB 0.02% 3.44kB / 0B 3.68MB / 0B 3
Si monitorizamos de una manera simple el contenedor, veremos que responde a los pings hasta que se cae (lo que es obvio).
# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.028 ms
Ahora borraremos el contenedor creado para nuestro ejemplo, para poder crearlo nuevamente con una opción de “restart”.
# docker rm crash1
Ahora lo volveremos a correr, pero indicando que en caso de “restart” siempre arranque nuevamente. En este caso ese contenedor funcionará (solo el ping, no hay servicios corriendo), 10 segundos después se parará pero Docker lo volverá a correr eternamente.
# docker run --restart always --name crash1 crashing
Si miramos los pings, veremos que en un momento, podemos llegar a detectar que un paquete no hube respuesta (en este caso el nro 17).
# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=14 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=15 ttl=64 time=0.025 ms
...
64 bytes from 172.17.0.2: icmp_seq=16 ttl=64 time=0.026 ms
64 bytes from 172.17.0.2: icmp_seq=18 ttl=64 time=0.076 ms
64 bytes from 172.17.0.2: icmp_seq=19 ttl=64 time=0.034 ms
La manera correcta de detener nuestro contenedor, que funcionaría eternamente es:
# docker stop crash1
La manera standard en que corren los contenedores es en modo “restart no”, que indica que en caso de un restart del contenedor se detenga y no arranque nuevamente.
# docker run --restart no --name crash1 crashing
Como con otros servicios probaremos una solución mejor, que es que arranque NN veces y si sigue parándose el servicio que efectivamente se detenga el contenedor. En nuestro caso luego de 3 paradas por errores (unos 30 segs después) el contenedor estará parado definitivamente. Recordemos que debemos borrar el contenedor “crash1” antes de crearlo de nuevo con estas opciones.
# docker stop crash1
# docker run --restart on-failure:3 --name crash1 crashing
Con un poco de suerte podemos identificar cuando se apaga el contenedor por el error del script.
# docker logs ebebf498da42
[1]+ Exit 1 docker run --restart on-failure:3 --name crash1 crashing
Podemos correr el contenedor utilizando este comando.
# docker start crash1
Ahora cambiaremos el proceso (script) para finalizarlo correctamente.
# vi noerror.sh
#/bin/bash
sleep 10
exit 0
Creamos la imágen igual que antes, pero ahora usando el script “acaba bien”.
# vi Dockerfile
FROM ubuntu:17.10
ADD noerror.sh /
CMD /bin/bash /noerror.sh
# docker build -t nocrashing .
Al correrlo veremos que luego de los 10 segundos el contenedor se detendrá, porque la condición “–restart on-failure:3” indica que re-arranque el contenedor hasta 3 veces si hay “un fallo”. El script finaliza con “exit 0”, indica una finalización correcta. En este caso solo correrá una ves.
# docker run --restart on-failure:3 --name nocrash nocrashing
Podemos correr el contenedor con el parámetro “–restart always”, en este caso, sea de manera correcta (0) o reportando un error (1), el contenedor arrancará nuevamente eternamente.
# docker run --restart always --name nocrash nocrashing
Existe una opción para rearrancar siempre el contenedor, excepto si fue parado implícitamente.
# docker run --restart unless-stopped --name nocrash nocrashing
Estas opciones del “restart” sirven para definir una política para cada imágen/contenedor que creemos, pero DEBEMOS utilizar herramientas de monitorización en todos los contenedores productivos de todos los datacenters si queremos brindar un servicio profesional en cada servicio.