Reconstruir contenedor de Docker en cambios de archivo


Para ejecutar un ASP.NET Core application, generé un dockerfile que construye la aplicación y copys el código fuente en el contenedor, que es obtenido por Git usando Jenkins. Así que en mi espacio de trabajo, hago lo siguiente en el dockerfile:

WORKDIR /app
COPY src src

Mientras Jenkins actualiza los archivos de mi host correctamente con Git, Docker no aplica esto a mi imagen.

Mi script básico para construir:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)

if [ "$containerRunning" == "true" ]; then
        docker stop $containerName
        docker start $containerName
else
        docker run -d -p 5000:5000 --name $containerName $imageName
fi

Probé diferentes cosas como --rm y --no-cache parámetro para docker run y también detener / eliminar el contenedor antes de el nuevo es build. No estoy seguro de lo que estoy haciendo mal aquí. Parece que docker está actualizando la imagen correctamente, ya que la llamada de COPY src src resultaría en un id de capa y ninguna llamada de caché:

Step 6 : COPY src src
 ---> 382ef210d8fd

¿Cuál es la forma recomendada de actualizar un contenedor?

Mi escenario típico sería: La aplicación se está ejecutando en el servidor en un contenedor Docker. Ahora se actualizan partes de la aplicación, por ejemplo, modificando un archivo. Ahora el contenedor debe ejecutar la nueva versión. Docker parece recomendar construir una nueva imagen en lugar de modificar un contenedor existente, por lo que creo que la forma general de reconstruir como yo lo hago es correcta, pero algunos detalles en la implementación deben mejorarse.

Author: Lion, 2016-12-25

1 answers

Después de algunas investigaciones y pruebas, descubrí que tenía algunos malentendidos sobre la vida útil de los contenedores Docker. Simplemente reiniciar un contenedor no hace que Docker use una nueva imagen, cuando la imagen se reconstruyó mientras tanto. En su lugar, Docker solo busca la imagen antes de que ejecute el contenedor. Así que el estado después de ejecutar un contenedor es persistente.

Por qué se requiere eliminar

Por lo tanto, reconstruir y reiniciar no es suficiente. Pensé contenedores funciona como un servicio: Detener el servicio, hacer sus cambios, reiniciarlo y se aplicarían. Ese fue mi mayor error.

Debido a que los contenedores son permanentes, primero debe eliminarlos usando docker rm <ContainerName>. Después de retirar un contenedor, no puede simplemente iniciarlo con docker start. Esto debe hacerse usando docker run, que a su vez utiliza la imagen más reciente para crear una nueva instancia de contenedor.

Los contenedores deben ser lo más independientes posible

Con este conocimiento, es comprensible por qué almacenar datos en contenedores es calificado como mala práctica y Docker recomienda data volumes/mounting host directorys en su lugar: Dado que un contenedor tiene que ser destruido para actualizar las aplicaciones, los datos almacenados en su interior también se perderían. Esto causa trabajo adicional para apagar los servicios, los datos de copia de seguridad, etc.

Así que es una solución inteligente para excluir esos datos completamente del contenedor: No tenemos que preocuparnos por nuestros datos, cuando se almacenan de forma segura en el el host y el contenedor solo contienen la aplicación en sí.

¿Por qué -rf no puede realmente ayudarle

El comando docker run, tiene un interruptor Clean up llamado -rf. Detendrá el comportamiento de mantener contenedores docker permanentemente. Usando -rf, Docker destruirá el contenedor después de que se haya salido. Pero este interruptor tiene dos problemas:

  1. Docker también elimina los volúmenes sin un nombre asociado con el contenedor, lo que puede matar a su datos
  2. Usando esta opción, no es posible ejecutar contenedores en segundo plano usando -d switch

Mientras que el interruptor -rf es una buena opción para ahorrar trabajo durante el desarrollo para pruebas rápidas, es menos adecuado en producción. Especialmente debido a la falta de la opción de ejecutar un contenedor en segundo plano, que en su mayoría sería necesaria.

Cómo extraer un recipiente

Podemos eludir esas limitaciones simplemente eliminando la contenedor:

docker rm --force <ContainerName>

El interruptor --force (o -f) que usa SIGKILL en contenedores en ejecución. En su lugar, también podría detener el contenedor antes de:

docker stop <ContainerName>
docker rm <ContainerName>

Ambos son iguales. docker stop también está usando SIGTERM. Pero usar --force switch acortará el script, especialmente cuando se usan servidores CI: docker stop lanza un error si el contenedor no se está ejecutando. Esto causaría que Jenkins y muchos otros servidores CI consideraran la compilación erróneamente como fallida. Para arreglar esto, tienes que comprobar primero si el contenedor se está ejecutando como lo hice en la pregunta (ver variable containerRunning).

Script completo para reconstruir un contenedor Docker

De acuerdo con este nuevo conocimiento, arreglé mi escritura de la siguiente manera: {[21]]}

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

echo Delete old container...
docker rm -f $containerName

echo Run new container...
docker run -d -p 5000:5000 --name $containerName $imageName

Esto funciona perfectamente:)

 60
Author: Lion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-24 11:00:10