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.
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:
- Docker también elimina los volúmenes sin un nombre asociado con el contenedor, lo que puede matar a su datos
- 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:)
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