Cómo actualizar el contenedor Docker después de que su imagen haya cambiado

518

Digamos que he sacado la imagen oficial mysql: 5.6.21 .

Desplegué esta imagen creando varios contenedores acoplables.

Estos contenedores han estado funcionando durante algún tiempo hasta que se lanza MySQL 5.6.22. La imagen oficial de mysql: 5.6 se actualiza con la nueva versión, pero mis contenedores aún se ejecutan 5.6.21.

¿Cómo propago los cambios en la imagen (es decir, actualizo la distribución de MySQL) a todos mis contenedores existentes? ¿Cuál es la forma correcta de Docker de hacer esto?

Yaroslav Stavnichiy
fuente

Respuestas:

579

Después de evaluar las respuestas y estudiar el tema que me gustaría resumir.

La forma de Docker para actualizar contenedores parece ser la siguiente:

Los contenedores de aplicaciones no deben almacenar datos de la aplicación . De esta manera, puede reemplazar el contenedor de la aplicación con su versión más nueva en cualquier momento ejecutando algo como esto:

docker pull mysql
docker stop my-mysql-container
docker rm my-mysql-container
docker run --name=my-mysql-container --restart=always \
  -e MYSQL_ROOT_PASSWORD=mypwd -v /my/data/dir:/var/lib/mysql -d mysql

Puede almacenar datos en el host (en el directorio montado como volumen) o en contenedores especiales de solo datos . Leer más al respecto

La actualización de aplicaciones (p. Ej., Con yum / apt-get upgrade) dentro de los contenedores se considera un antipatrón . Se supone que los contenedores de aplicación son inmutables , lo que garantizará un comportamiento reproducible. Algunas imágenes de aplicaciones oficiales (mysql: 5.6 en particular) ni siquiera están diseñadas para autoactualizarse (apt-get upgrade no funcionará).

Me gustaría agradecer a todos los que dieron sus respuestas, para que pudiéramos ver todos los enfoques diferentes.

Yaroslav Stavnichiy
fuente
31
¿Qué pasa si se necesita migración de datos? El nuevo servidor no puede montar los datos porque está en un formato antiguo, necesita saber que está ocurriendo una migración y cambiar la representación de los datos.
Dor Rotman
12
Creo que los diseñadores de imágenes deberían tenerlo en cuenta y permitir el lanzamiento de comandos personalizados (por ejemplo, migración de datos) durante la primera ejecución del contenedor.
Yaroslav Stavnichiy
2
Un artículo relevante: intercityup.com/blog/running-upgrading-mysql-server-docker
Dor Rotman
44
@static_rtti ¿Qué tal docker rename my-mysql-container trash-containerantes de crear el nuevo?
Franklin Yu
44
¿Habría algún comando todo en uno para actualizar el contenedor sin tener que detenerlo manualmente, eliminarlo y volver a crearlo (en función de la nueva imagen que se ha extraído)?
Michaël Perrin
83

No me gusta montar volúmenes como un enlace a un directorio de host, así que se me ocurrió un patrón para actualizar los contenedores acoplables con contenedores administrados por completo. La creación de un nuevo contenedor de Docker --volumes-from <container>le dará al nuevo contenedor con las imágenes actualizadas la propiedad compartida de los volúmenes administrados de Docker.

docker pull mysql
docker create --volumes-from my_mysql_container [...] --name my_mysql_container_tmp mysql

Al no eliminar inmediatamente el original my_mysql_containertodavía, tiene la capacidad de volver al contenedor de trabajo conocido si el contenedor actualizado no tiene los datos correctos o falla una prueba de cordura.

En este punto, generalmente ejecutaré las secuencias de comandos de respaldo que tenga para que el contenedor tenga una red de seguridad en caso de que algo salga mal

docker stop my_mysql_container
docker start my_mysql_container_tmp

Ahora tiene la oportunidad de asegurarse de que los datos que espera que estén en el nuevo contenedor estén allí y realice una verificación de integridad.

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

Los volúmenes de la ventana acoplable se mantendrán mientras los utilice cualquier contenedor, de modo que pueda eliminar el contenedor original de forma segura. Una vez que se retira el contenedor original, el nuevo contenedor puede asumir el mismo nombre del original para hacer que todo sea tan bonito como era para comenzar.

Existen dos ventajas principales al usar este patrón para actualizar los contenedores acoplables. En primer lugar, elimina la necesidad de montar volúmenes en directorios de host al permitir que los volúmenes se transfieran directamente a contenedores actualizados. En segundo lugar, nunca está en una posición donde no haya un contenedor acoplable que funcione; así que si la actualización falla, puede volver fácilmente a cómo funcionaba antes girando nuevamente el contenedor acoplable original.

kMaiSmith
fuente
3
¿Por qué no te gusta montar volúmenes de host dentro de un contenedor Docker? (Estoy haciendo precisamente eso, así que estoy interesado en argumentos en contra de hacerlo: -) He montado, por ejemplo: ./postgres-data/:/var/lib/postgres/data- es decir, he montado el directorio host ./postgres-data/, dentro de mi contenedor PostgreSQL.)
KajMagnus
44
@KajMagnus Uso mucho los enjambres de Docker, y me gusta escribir mis contenedores para que funcionen bien en un enjambre. Cuando giro un contenedor en un enjambre, no tengo idea de en qué nodo de enjambre vivirá el contenedor, por lo que no puedo confiar en la ruta del host que contiene los datos que quiero. Dado que Docker 1.9 (creo) los volúmenes se pueden compartir entre los hosts, lo que hace que la actualización y la migración de contenedores sea muy sencilla utilizando el método que describí. Una alternativa sería asegurarse de que algún volumen de red esté montado en todos los nodos de enjambre, pero eso parece un gran dolor de mantenimiento.
kMaiSmith
¡Gracias! Ok, montar volúmenes de host parece algo que yo también quiero evitar ahora. Al menos un poco más tarde si mi aplicación se vuelve popular y necesita escalar a más de un servidor
KajMagnus
32

Solo por proporcionar una respuesta más general (no específica de MySQL) ...

  1. En breve

Sincronice con el registro de imágenes de servicio ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Recree el contenedor si la imagen o el archivo docker-compose han cambiado:

docker-compose up -d
  1. Antecedentes

La administración de imágenes de contenedor es una de las razones para usar docker-compose (consulte https://docs.docker.com/compose/reference/up/ )

Si hay contenedores existentes para un servicio, y la configuración o la imagen del servicio se cambió después de la creación del contenedor, Docker-compose recoge los cambios deteniendo y recreando los contenedores (preservando los volúmenes montados). Para evitar que Compose recoja los cambios, use el indicador --no-recreate.

El aspecto de gestión de datos también está cubierto por docker-compose a través de "volúmenes" externos montados (consulte https://docs.docker.com/compose/compose-file/#volumes ) o contenedor de datos.

Esto deja intactos los posibles problemas de compatibilidad con versiones anteriores y migración de datos, pero estos son problemas "aplicativos", no específicos de Docker, que deben verificarse con las notas y pruebas de la versión ...

Ronan Fauglas
fuente
¿Cómo haces esto con las versiones? ejemplo, la nueva imagen es foo / image: 2 y docker-compose.yml tiene image: foo / image: 1?
dman
GRACIAS. ¡La mejor respuesta!
Mick
Si bien este es seguramente el camino a seguir, se debe tener en cuenta que los cambios realizados en el contenedor se perderán una vez que el contenedor se vuelva a crear. Por lo tanto, es necesario mantener los cambios del contenedor solo dentro de los volúmenes montados.
Petr Bodnár
23

Me gustaría agregar que si desea realizar este proceso automáticamente (descargar, detener y reiniciar un nuevo contenedor con la misma configuración descrita por @Yaroslav) puede usar WatchTower. Un programa que actualiza automáticamente sus contenedores cuando se cambian https://github.com/v2tec/watchtower

Ricardo Polo Jaramillo
fuente
20

Considere para estas respuestas:

  • El nombre de la base de datos es app_schema
  • El nombre del contenedor es app_db
  • La contraseña de root es root123

Cómo actualizar MySQL al almacenar datos de la aplicación dentro del contenedor

Esto se considera una mala práctica , porque si pierde el contenedor, perderá los datos. Aunque es una mala práctica, aquí hay una posible forma de hacerlo:

1) Realice un volcado de la base de datos como SQL:

docker exec app_db sh -c 'exec mysqldump app_schema -uroot -proot123' > database_dump.sql

2) Actualiza la imagen:

docker pull mysql:5.6

3) Actualice el contenedor:

docker rm -f app_db
docker run --name app_db --restart unless-stopped \
-e MYSQL_ROOT_PASSWORD=root123 \
-d mysql:5.6

4) Restaurar el volcado de la base de datos:

docker exec app_db sh -c 'exec mysql -uroot -proot123' < database_dump.sql

Cómo actualizar el contenedor MySQL usando un volumen externo

Usar un volumen externo es una mejor manera de administrar los datos y facilita la actualización de MySQL. Perder el contenedor no perderá ningún dato. Puede usar docker-compose para facilitar la administración de aplicaciones Docker de contenedores múltiples en un solo host:

1) Cree el docker-compose.ymlarchivo para administrar sus aplicaciones:

version: '2'
services:
  app_db:
    image: mysql:5.6
    restart: unless-stopped
    volumes_from: app_db_data
  app_db_data:
    volumes: /my/data/dir:/var/lib/mysql

2) Actualice MySQL (desde la misma carpeta que el docker-compose.ymlarchivo):

docker-compose pull
docker-compose up -d

Nota: el último comando anterior actualizará la imagen MySQL, recreará e iniciará el contenedor con la nueva imagen.

Alexandre V.
fuente
Digamos que tengo una gran base de datos (varios GB), ¿no se podrá acceder a mis datos hasta que se importe toda la base de datos? Eso podría ser un gran "tiempo de inactividad"
hellimac
Como mencionaste docker-compose, ¿funcionará esto? stackoverflow.com/a/31485685/65313
sivabudh
1
volumes_fromLa clave ahora está en desuso (incluso eliminada en la versión 3 del archivo de redacción) a favor de la nueva volumesclave.
Franklin Yu
docker pull image_uri:tag && docker restart container_running_that_imagetrabajó para mi. No hay necesidad de docker-compose pull && docker-compose up -d.
Yuriy Pozniak
16

Respuesta similar a la anterior

docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull
Eddie Jaoude
fuente
1
¡Brillante! Bastante sorprendido de que no obtuviera más votos. Ahora lo único que falta sería activar un reinicio de todos los contenedores que se actualizaron.
sorin
77
Lamentablemente, esto no actualizará el contenedor existente. Esto solo actualizará la imagen extraída, pero el contenedor existente es inmutable y sigue utilizando la imagen original utilizada para crearla. Esto solo funciona si crea un nuevo contenedor a partir de la imagen, pero cualquier contenedor existente aún se basa en la imagen original.
Eric B.
Asombroso. Si necesita extraer la versión específica del contenedor, hágalo así: imágenes acoplables | awk '{print $ 1 ":" $ 2}' | grep -v 'ninguno' | grep -iv 'repo' | xargs -n1 docker pull
rogervila
11

Esto es lo que parece usar docker-composeal crear una costumbre Dockerfile.

  1. Cree primero su Dockerfile personalizado, agregando un número de versión siguiente para diferenciar. Ej: docker build -t imagename:version . Esto almacenará su nueva versión localmente.
  2. correr docker-compose down
  3. Edite su docker-compose.ymlarchivo para reflejar el nuevo nombre de imagen que configuró en el paso 1.
  4. Ejecutar docker-compose up -d. Buscará localmente la imagen y usará la imagen actualizada.

-EDITAR-

Mis pasos anteriores son más detallados de lo que deben ser. He optimizado mi flujo de trabajo al incluir el build: .parámetro en mi archivo docker-compose. Los pasos se ven esto ahora:

  1. Verifique que mi Dockerfile es como quiero que se vea.
  2. Establecer el número de versión de mi nombre de imagen en mi archivo docker-compose.
  3. Si mi imagen aún no está construida: ejecuta docker-compose build
  4. correr docker-compose up -d

No me di cuenta en ese momento, pero Docker-compose es lo suficientemente inteligente como para simplemente actualizar mi contenedor a la nueva imagen con el comando único, en lugar de tener que bajarlo primero.

gdbj
fuente
En una situación real, no puede usar sus propias manos y hacer esos cambios. Su solución no admite formas automáticas para resolver el problema.
Carlos Vázquez Losada
77
¿Estás diciendo que porque mi solución no es automática, no es válida? ¿Es ese un requisito del OP? ¿Y las otras respuestas implican automatización? Muy confundido Y, creo que los votos negativos están perjudicando a otros que vienen aquí. Mi respuesta es 100% válida para la pregunta formulada.
gdbj
gracias por esta respuesta, tampoco me di cuenta de que simplemente podrías correr docker-compose up -dsin necesidad de detener todo primero.
Radicando
4

Si no desea utilizar Docker Compose, puedo recomendarle un portainer . Tiene una función de recreación que le permite recrear un contenedor mientras extrae la última imagen.

beruic
fuente
2

Debe reconstruir todas las imágenes y reiniciar todos los contenedores, o de alguna manera actualizar el software y reiniciar la base de datos. No hay una ruta de actualización sino que la diseñe usted mismo.

seanmcl
fuente
¿Qué quiere decir exactamente al reiniciar los contenedores? Hay un docker restartcomando, pero no estoy seguro de que recoja los cambios de imagen. ¿Y qué pasa con mis datos dentro de los contenedores?
Yaroslav Stavnichiy
1
Lo siento, no me refería a reiniciar la ventana acoplable. Me refiero a docker rm -f CONTANER; la ventana acoplable ejecuta NEW_IMAGE. Los datos en su contenedor sql desaparecerán. Es por eso que las personas generalmente usan volúmenes para almacenar los datos.
seanmcl
Si tiene todos sus datos montados en volúmenes en contenedores separados o en una máquina host, nas @seanmcl dijo que simplemente cree nuevos contenedores con nuevos mysql conectados a los mismos datos. Si no lo hizo (debería), pero puede usar el comando docker exec disponible en docker 1.3 para actualizar mysql y reiniciarlo dentro del contenedor.
Usman Ismail el
2

Tomado de http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

Puede actualizar todas sus imágenes existentes utilizando la siguiente tubería de comando:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull
gvlx
fuente
66
Esto actualizará las imágenes, pero no el contenedor. El contenedor es inmutable y su imagen base no se puede cambiar sin crear un nuevo contenedor a partir de una imagen actualizada.
Eric B.
2

Asegúrese de estar utilizando volúmenes para todos los datos persistentes (configuración, registros o datos de la aplicación) que almacena en los contenedores relacionados con el estado de los procesos dentro de ese contenedor. Actualice su Dockerfile y reconstruya la imagen con los cambios que desea, y reinicie los contenedores con sus volúmenes montados en su lugar apropiado.

Daniel Dinnyes
fuente
1

Esto es algo con lo que también he estado luchando por mis propias imágenes. Tengo un entorno de servidor desde el que creo una imagen Docker. Cuando actualizo el servidor, me gustaría que todos los usuarios que ejecutan contenedores basados ​​en mi imagen de Docker puedan actualizar al último servidor.

Idealmente, preferiría generar una nueva versión de la imagen de Docker y que todos los contenedores basados ​​en una versión anterior de esa imagen se actualicen automáticamente a la nueva imagen "en su lugar". Pero este mecanismo no parece existir.

Entonces, el siguiente mejor diseño que he podido encontrar hasta ahora es proporcionar una forma de actualizar el contenedor en sí, de forma similar a cómo una aplicación de escritorio busca actualizaciones y luego se actualiza a sí misma. En mi caso, esto probablemente significará crear un script que involucre Git extrae de una etiqueta conocida.

La imagen / contenedor en realidad no cambia, pero las "partes internas" de ese contenedor cambian. Podrías imaginar hacer lo mismo con apt-get, yum o lo que sea apropiado para tu entorno. Junto con esto, actualizaría myserver: última imagen en el registro para que cualquier contenedor nuevo se base en la última imagen.

Me interesaría saber si existe alguna técnica anterior que aborde este escenario.

bjlevine
fuente
77
Va en contra del concepto de infraestructura inmutable y algunos de sus beneficios. Puede probar su aplicación / entorno para ver si funciona y no está garantizado si actualiza los componentes en su interior. Dividir el código del contenedor de los datos de la configuración le permite actualizar, probar que estamos trabajando actualmente e implementarlo en producción, sabiendo que no hay una línea de código diferente desde la imagen probada hasta la de producción. De todos modos, el sistema le permite administrarlo como usted dice, es su elección.
gmuslera
Muy buen punto gmuslera. Convino en que es un antipatrón actualizar las 'partes internas' de un contenedor acoplable existente.
bjlevine
Entonces, ¿cuál es la mejor solución para actualizar automáticamente el contenedor de Docker en función de las actualizaciones de imágenes de Docker para entregar las actualizaciones a todos los contenedores sin esfuerzo?
Tarek Salem
1

Actualizar

Esto es principalmente para consultar el contenedor para que no se actualice, ya que construir imágenes es la forma de hacerlo

Tuve el mismo problema, así que creé docker-run , una herramienta de línea de comandos muy simple que se ejecuta dentro de un contenedor de docker para actualizar paquetes en otros contenedores en ejecución.

Utiliza docker-py para comunicarse con contenedores de docker en ejecución y actualizar paquetes o ejecutar cualquier comando único arbitrario

Ejemplos:

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run exec

de forma predeterminada, esto ejecutará el datecomando en todos los contenedores en ejecución y devolverá resultados, pero puede emitir cualquier comando, por ejemplodocker-run exec "uname -a"

Para actualizar paquetes (actualmente solo usa apt-get):

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run update

Puede crear un alias y usarlo como una línea de comando normal, por ejemplo

alias docker-run='docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run'

iTech
fuente
¿Es esta una buena idea? (Si lo hace apt update; apt upgrade, la imagen crecerá).
ctrl-alt-delor
La imagen de @yaroslav es una mejor solución a este problema. Lo anterior no es realmente la manera Docker de hacer las cosas.
Joost van der Laan