Compose: ¿Hay alguna forma de retrasar el inicio del contenedor para admitir servicios dependientes con un tiempo de inicio más largo?

Creado en 5 ago. 2014  ·  314Comentarios  ·  Fuente: docker/compose

Tengo un contenedor MySQL que tarda un poco en iniciarse, ya que necesita importar datos.

Tengo un contenedor Alfresco que depende del contenedor MySQL.

En este momento, cuando uso fig, el servicio Alfresco dentro del contenedor Alfresco falla cuando intenta conectarse al contenedor MySQL ... aparentemente porque el servicio MySQL aún no está escuchando.

¿Hay alguna forma de manejar este tipo de problema en la Fig?

Comentario más útil

Sí, me interesaría algo como esto, destinado a publicarlo antes.

El patrón de impacto más pequeño que puedo pensar que solucionaría este caso de uso para nosotros sería el siguiente:

Agregue "esperar" como una nueva clave en fig.yml, con una semántica de valor similar a la del enlace. Docker trataría esto como un requisito previo y esperaría hasta que este contenedor haya salido antes de continuar.

Entonces, mi archivo de Docker se vería así:

db:
  image: tutum/mysql:5.6

initdb:
  build: /path/to/db
  link:
    - db:db
  command: /usr/local/bin/init_db

app:
  link:
    - db:db
  wait:
    - initdb

Al ejecutar la aplicación, iniciará todos los contenedores de enlaces, luego ejecutará el contenedor de espera y solo avanzará al contenedor de la aplicación real una vez que el contenedor de espera (initdb) haya salido. initdb ejecutaría un script que espera a que la base de datos esté disponible, luego ejecuta las inicializaciones / migraciones / lo que sea, luego sale.

De todos modos, esos son mis pensamientos.

Todos 314 comentarios

En el trabajo, envolvemos nuestros servicios dependientes en un script que verifica si el enlace ya está activo. ¡Sé que uno de mis colegas también estaría interesado en esto! Personalmente, creo que es una preocupación a nivel de contenedor esperar a que los servicios estén disponibles, pero puedo estar equivocado :)

Hacemos lo mismo con el envoltorio. Puede ver un ejemplo aquí: https://github.com/dominionenterprises/tol-api-php/blob/master/tests/provisioning/set-env.sh

Sería útil tener un script de punto de entrada que recorra todos los enlaces y espere hasta que funcionen antes de iniciar el comando que se le pasa.

Esto debería estar integrado en Docker, pero la solución está muy lejos. Un contenedor no debe considerarse iniciado hasta que se haya abierto el enlace que expone.

@bfirsh eso es más de lo que imaginaba, pero sería excelente.

Un contenedor no debe considerarse iniciado hasta que se haya abierto el enlace que expone.

Creo que eso es exactamente lo que la gente necesita.

Por ahora, usaré una variación en https://github.com/aanand/docker-wait

Sí, me interesaría algo como esto, destinado a publicarlo antes.

El patrón de impacto más pequeño que puedo pensar que solucionaría este caso de uso para nosotros sería el siguiente:

Agregue "esperar" como una nueva clave en fig.yml, con una semántica de valor similar a la del enlace. Docker trataría esto como un requisito previo y esperaría hasta que este contenedor haya salido antes de continuar.

Entonces, mi archivo de Docker se vería así:

db:
  image: tutum/mysql:5.6

initdb:
  build: /path/to/db
  link:
    - db:db
  command: /usr/local/bin/init_db

app:
  link:
    - db:db
  wait:
    - initdb

Al ejecutar la aplicación, iniciará todos los contenedores de enlaces, luego ejecutará el contenedor de espera y solo avanzará al contenedor de la aplicación real una vez que el contenedor de espera (initdb) haya salido. initdb ejecutaría un script que espera a que la base de datos esté disponible, luego ejecuta las inicializaciones / migraciones / lo que sea, luego sale.

De todos modos, esos son mis pensamientos.

(revisado, ver más abajo)

+1 aquí también. No es muy atractivo tener que hacer esto en los propios comandos.

+1 también. Acabo de encontrarme con este problema. ¡Gran herramienta por cierto, hace mi vida mucho más fácil!

+1 sería genial tener esto.

+1 también. Recientemente me encontré con el mismo conjunto de problemas

+1 también. alguna declaración de dockerguys?

Estoy escribiendo scripts de envoltura como puntos de entrada para sincronizar en este momento, no estoy seguro de si tener un mecanismo en la figura es prudente si tiene otros objetivos para sus contenedores que realizan la orquestación de una manera diferente. Me parece una aplicación muy específica, como tal la responsabilidad de los contenedores que hacen el trabajo.

Después de pensarlo un poco y experimentar, estoy de acuerdo con esto.

Como tal, una aplicación que estoy construyendo tiene básicamente un
función waitfor (host, puerto) que me permite esperar los servicios de la aplicación
depende de (ya sea detectado a través del entorno o explícitamente
configuración a través de opciones cli).

salud
James

James Mills / prológico

E: [email protected]
W: prologic.shortcircuit.net.au

El viernes 22 de agosto de 2014 a las 6:34 p. M., Mark Stuart [email protected]
escribió:

Estoy escribiendo scripts de envoltura como puntos de entrada para sincronizar en este momento,
No estoy seguro de si tener un mecanismo en la figura es prudente si tiene otros objetivos para
sus contenedores que realizan la orquestación de una manera diferente. Parece muy
aplicación específica para mí como tal la responsabilidad de los contenedores
haciendo el trabajo.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/docker/fig/issues/374#issuecomment -53036154.

Sí, aquí se necesitan algunas "dependencias" básicas ...
Entonces, si tiene 20 contenedores, simplemente no debe ejecutar la figura y todo comienza con el orden correcto ...
Sin embargo, también tiene alguna opción de tiempo de espera u otros mecanismos de detección de fallas

Otro +1 aquí. Postgres tarda más que Django en iniciarse, por lo que la base de datos no está allí para el comando de migración sin piratería.

@ahknight es interesante, ¿por qué se ejecuta la migración durante run ?

¿No desea ejecutar migrate durante la fase build ? De esa manera, puede iniciar imágenes nuevas mucho más rápido.

Hay un script de inicio más grande para la aplicación en cuestión, por desgracia. Por ahora, primero estamos haciendo trabajos que no son de base de datos, usando nc -w 1 en un bucle para esperar la base de datos y luego haciendo acciones de base de datos. Funciona, pero me hace sentir sucio (er).

He tenido mucho éxito haciendo este trabajo durante la fase fig build . Tengo un ejemplo de esto con un proyecto de django (todavía un trabajo en progreso): https://github.com/dnephin/readthedocs.org/blob/fig-demo/dockerfiles/database/Dockerfile#L21

No es necesario sondear para el inicio. Aunque hice algo similar con mysql, donde tuve que sondear para el inicio porque el script mysqld init no lo estaba haciendo ya. Este script de inicio de postgres parece ser mucho mejor.

Esto es lo que estaba pensando:

Usando la idea de docker / docker # 7445 podríamos implementar este atributo "wait_for_helth_check" en la figura?
Entonces, ¿sería un problema, no un problema de Docker?

¿Hay alguna forma de hacer que fig compruebe el estado de tcp en el contenedor vinculado? Si es así, creo que este es el camino a seguir. =)

@dnephin, ¿puedes explicar un poco más lo que estás haciendo en Dockerfiles para ayudar con esto?
¿La fase de construcción no puede influir en el tiempo de ejecución?

@docteurklein yo puedo. Arreglé el enlace desde arriba (https://github.com/dnephin/readthedocs.org/blob/fig-demo/dockerfiles/database/Dockerfile#L21)

La idea es que realice todas las operaciones de "configuración" más lentas durante la compilación, para que no tenga que esperar nada durante el inicio del contenedor. En el caso de una base de datos o índice de búsqueda, debería:

  1. iniciar el servicio
  2. crear los usuarios, bases de datos, tablas y datos de accesorios
  3. cerrar el servicio

todo como un solo paso de construcción. Más tarde, cuando fig up el contenedor de la base de datos, está listo para funcionar básicamente de inmediato, y también puede aprovechar la caché de compilación de la ventana acoplable para estas operaciones más lentas.

¡bonito! Gracias :)

@dnephin agradable, no había pensado en eso.

+1 Esto definitivamente es necesario.
En la mayoría de los casos, bastaría con un truco de demora desagradable, pero una solución _real_ sería bienvenida.

¿Podría dar un ejemplo de por qué / cuándo es necesario?

En el caso de uso que tengo, tengo un servidor Elasticsearch y luego un servidor de aplicaciones que se conecta a Elasticsearch. Elasticsearch tarda unos segundos en activarse, por lo que no puedo simplemente hacer un fig up -d porque el servidor de aplicaciones fallará inmediatamente cuando se conecte al servidor Elasticsearch.

Digamos que un contenedor inicia MySQL y el otro inicia una aplicación que necesita MySQL y resulta que la otra aplicación se inicia más rápido. Tenemos fallas transitorias fig up debido a eso.

crane tiene una forma de evitar esto permitiéndole crear grupos que se pueden iniciar individualmente. Entonces puede iniciar el grupo MySQL, esperar 5 segundos y luego iniciar las otras cosas que dependen de él.
Funciona a pequeña escala, pero no es una solución real.

@oskarhane no está seguro de si este "esperar 5 segundos" ayuda, en algunos casos es posible que deba esperar más (o simplemente no puede estar seguro de que no pasará los 5 segundos) ... no es muy seguro confíe en el tiempo de espera.
También tendrías que hacer esto manualmente esperando y cargando el otro grupo, y eso es un poco tonto, fig debería hacer eso por ti = /

@oskarhane , @dacort , @ddossot :

Tiene razón, pero hasta que arreglemos todas las aplicaciones preexistentes para hacer cosas como recuperarse elegantemente de la ausencia de sus recursos críticos (como DB) al inicio (que es una gran cosa ™ pero desafortunadamente rara vez es compatible con marcos), deberíamos usar fig start para iniciar un contenedor individual en un orden determinado, con retrasos, en lugar de fig up .

Puedo ver un script de shell que viene a controlar fig para controlar docker: wink:

Estoy de acuerdo con que esto no esté integrado en la figura, pero algunos consejos sobre las mejores prácticas para esperar la preparación serían buenos

Vi en un código vinculado desde un comentario anterior que se hizo:

while ! exec 6<>/dev/tcp/${MONGO_1_PORT_27017_TCP_ADDR}/${MONGO_1_PORT_27017_TCP_PORT}; do
    echo "$(date) - still trying to connect to mongo at ${TESTING_MONGO_URL}"
    sleep 1
done

En mi caso, sin embargo, no hay una ruta /dev/tcp , tal vez sea una distribución de Linux diferente (?) - Estoy en Ubuntu

En cambio, encontré este método que parece funcionar bien:

until nc -z postgres 5432; do
    echo "$(date) - waiting for postgres..."
    sleep 1
done

Esto parece funcionar, pero no sé lo suficiente sobre esas cosas para saber si es robusto ... ¿Alguien sabe si existe una posible condición de carrera entre el puerto que muestra hasta nc y el servidor de Postgres _ realmente_ capaz de aceptar? comandos?

Sería más feliz si fuera posible invertir la verificación; en lugar de sondear desde los contenedores dependientes, ¿es posible enviar una señal desde el contenedor de destino (es decir, el servidor de postgres) a todos los dependientes?

Tal vez sea una idea tonta, ¿alguien tiene alguna idea?

Los enlaces de

¿Alguien sabe si existe una posible condición de carrera entre el puerto que se muestra en nc y el servidor de postgres realmente capaz de aceptar comandos?

No hay forma de saberlo en el caso general, podría ser cierto para postgres, podría ser falso para otros servicios, que es otro argumento para no hacerlo en la Fig.

@aanand intenté usar su enfoque de imagen acoplable / espera, pero no estoy seguro de lo que está sucediendo. Básicamente, tengo este contenedor "Orientdb" al que se vinculan muchos otros contenedores de aplicaciones NodeJS. Este contenedor orientdb tarda algo de tiempo en comenzar a escuchar en el puerto TCP y esto hace que los otros contenedores obtengan el error "Conexión rechazada".

Esperaba que al vincular el contenedor de espera a Orientdb no vería este error. Pero desafortunadamente todavía lo recibo al azar. Aquí está mi configuración (Docker versión 1.4.1, figura 1.0.1 en una caja de Ubuntu 14.04):

orientdb:
    build: ./Docker/orientdb
    ports:
        -   "2424:2424"
        -   "2480:2480"
wait:
    build: ./Docker/wait
    links:
        - orientdb:orientdb
....
core:
    build:  ./Docker/core
    ports:
        -   "3000:3000"
    links:
        -   orientdb:orientdb
        -   nsqd:nsqd

Se agradece cualquier ayuda. Gracias.

@mindnuts la imagen wait es más una demostración; no es adecuado para su uso en fig.yml . Debe utilizar la misma técnica (sondeo repetido) en su contenedor core para esperar a que comience el contenedor orientdb antes de iniciar el proceso principal.

+1 acaba de comenzar a encontrar esto ya que estoy extrayendo imágenes personalizadas en lugar de compilarlas en fig.yml. La aplicación de nodo falla porque mongodb aún no está listo ...

Acabo de pasar horas depurando por qué se podía acceder a MySQL al iniciar WordPress manualmente con Docker, y por qué estaba fuera de línea al comenzar con la Fig. Solo ahora me di cuenta de que Fig siempre reinicia el contenedor MySQL cada vez que inicio la aplicación, por lo que WordPress entrypoint.sh muere aún no poder conectarse a MySQL.

Agregué mi propio entrypoint.sh anulado que espera 5 segundos antes de ejecutar el entrypoint.sh real. Pero claramente, este es un caso de uso que necesita una solución general, si se supone que es fácil lanzar una combinación de contenedor MySQL + WordPress con Docker / Fig.

por lo que el entrypoint.sh de WordPress muere aún sin poder conectarse a MySQL.

Creo que esto es un problema con el contenedor de WordPress.

Si bien inicialmente era un fanático de esta idea, después de leer https://github.com/docker/docker/issues/7445#issuecomment -56391294, creo que tal característica sería un enfoque incorrecto y, de hecho, fomenta las malas prácticas.

Parece haber dos casos que esta cuestión pretende abordar:

Debe haber un servicio de dependencia disponible para realizar alguna inicialización.

Cualquier inicialización de contenedor debería realizarse durante build . De esa forma, se almacena en caché y no es necesario que todos los usuarios de la imagen repitan el trabajo.

Un servicio de dependencia debe estar disponible para que se pueda abrir una conexión.

La aplicación debería ser realmente resistente a los fallos de conexión y reintentar la conexión.

Supongo que la raíz del problema es que no existen reglas básicas en cuanto a quién es la responsabilidad de esperar a que los servicios estén listos. Pero incluso si lo hubiera, creo que es un poco poco realista esperar que los desarrolladores agreguen reintentos de conexión de base de datos a cada script de inicialización. Estos scripts a menudo son necesarios para preparar volúmenes de datos vacíos que se acaban de montar (por ejemplo, crear la base de datos).

El problema en realidad sería mucho menos molesto si Fig no siempre reiniciara los contenedores vinculados (es decir, el servidor de la base de datos) al reiniciar el contenedor de la aplicación. Realmente no sé por qué hace eso.

El problema en realidad sería mucho menos molesto si Fig no siempre reiniciara los contenedores vinculados (es decir, el servidor de la base de datos) al reiniciar el contenedor de la aplicación. Realmente no sé por qué hace eso.

En realidad, no solo _ reinicia_ contenedores, sino que los _destruye y recrea_, porque es la forma más sencilla de asegurarse de que se recojan los cambios en fig.yml . Eventualmente deberíamos implementar una solución más inteligente que pueda comparar la "configuración actual" con la "configuración deseada" y solo recrear lo que ha cambiado.

Volviendo al problema original, realmente no creo que sea poco realista esperar que los contenedores tengan una lógica de reintento de conexión; es fundamental para diseñar un sistema distribuido que funcione. Si diferentes scripts necesitan compartirlo, debe incluirse en un ejecutable (o en un módulo específico del idioma si no está usando shell), por lo que cada script puede simplemente invocar waitfor db en la parte superior.

@kennu ¿qué pasa con --no-recreate ? / cc @aanand

@aanand me refiero al comentario de irrealismo desde el punto de vista de que Docker Hub ya está lleno de imágenes publicadas que probablemente no manejan el reintento de conexión en sus scripts de inicialización, y que sería una gran empresa lograr que todos lo agreguen. Pero supongo que podría lograrse si Docker Inc publicara algún tipo de pautas / requisitos oficiales.

Personalmente, prefiero mantener los contenedores / imágenes simples y dejar que el sistema subyacente se preocupe por resolver las dependencias. De hecho, es posible que la política de reinicio de Docker ya resuelva todo (si el contenedor de la aplicación no se conecta a la base de datos, se reiniciará y volverá a intentarlo hasta que la base de datos esté disponible).

Pero confiar en la política de reinicio significa que debería estar habilitada de forma predeterminada, o de lo contrario, la gente pasa horas depurando el problema (como acabo de hacer). Por ejemplo, Kubernetes tiene como valor predeterminado RestartPolicyAlways para los pods.

algún progreso en esto? Me gustaría hacerme eco de que esperar que todas las imágenes de la ventana acoplable cambien y que toda la comunidad implemente prácticas de reintento de conexión no es razonable. Fig es una herramienta de orquestación de Docker y el problema radica en el orden en que hace las cosas, por lo que el cambio debe realizarse en Fig, no en Docker ni en la comunidad.

esperar que todas las imágenes de Docker cambien y que toda la comunidad implemente prácticas de reintento de conexión no es razonable

No es que una aplicación deba volver a intentarlo debido a la ventana acoplable o la fig. Las aplicaciones deben ser resistentes a las conexiones caídas porque la red no es confiable . Cualquier aplicación ya debería estar construida de esta manera.

Personalmente, no he tenido que implementar reintentos en ninguno de mis contenedores, y tampoco he necesitado ningún retraso o espera en el inicio. Creo que la mayoría de los casos de este problema se incluyen en estas dos categorías (mi uso de "reintentar" probablemente no sea bueno aquí, quise decir más que restablecería una conexión si la conexión se cerró, no necesariamente sondear durante un período de tiempo intentando múltiples veces).

Si se asegura de que toda la inicialización ocurre durante la fase de "compilación" y que las conexiones se restablecen en la siguiente solicitud, no tendrá que volver a intentarlo (o esperar a que comiencen otros contenedores). Si las conexiones se abren con pereza (cuando se realiza la primera solicitud), en lugar de ansiosamente (durante el inicio), sospecho que no necesitará volver a intentarlo en absoluto.

el problema radica en el orden en que [fig] hace las cosas

No veo ninguna mención de eso en esta discusión hasta ahora. Fig ordena el inicio en función de los enlaces especificados en la configuración, por lo que siempre debe iniciar los contenedores en el orden correcto. ¿Puede proporcionar un caso de prueba en el que el pedido sea incorrecto?

Tengo que estar de acuerdo con @dnephin aquí. Claro, sería conveniente si compose / fig pudiera hacer algo de magia y verificar la disponibilidad de los servicios, sin embargo, ¿cuál sería el comportamiento esperado si un servicio no responde? Eso _realmente_ depende de los requisitos de su aplicación / pila. En algunos casos, la pila completa debe destruirse y reemplazarse por una nueva; en otros casos, debe usarse una pila de conmutación por error. Se pueden pensar en muchos otros escenarios.

Compose / Fig no puede tomar estas decisiones, y los servicios de monitoreo deben ser responsabilidad de las aplicaciones que se ejecutan dentro del contenedor.

Me gustaría sugerir que @dnephin simplemente ha tenido suerte. Si bifurca dos procesos en paralelo, uno de los cuales se conectará a un puerto que el otro escuchará, básicamente está introduciendo una condición de carrera; una lotería para ver qué proceso se inicializa más rápido.

También me gustaría repetir el ejemplo de inicialización de WordPress: ejecuta un script de shell de inicio que crea una nueva base de datos si el contenedor MySQL aún no la tiene (esto no se puede hacer al construir la imagen de Docker, ya que depende del volumen de datos montado externamente). Dicho script se vuelve significativamente más complejo si tiene que distinguir los errores genéricos de la base de datos de los errores de "la base de datos aún no está lista" e implementar alguna lógica de reintento sana dentro del script de shell. Considero muy probable que el autor de la imagen nunca pruebe realmente el script de inicio con dicha condición de carrera.

Aún así, la política de reinicio incorporada de Docker proporciona una solución alternativa para esto, si está listo para aceptar que los contenedores no se inician esporádicamente e imprimen regularmente errores en los registros. (Y si recuerdas encenderlo).

Personalmente, haría que las cosas simplemente funcionen, haciendo que Fig autodetecte qué puertos de contenedor están expuestos a un contenedor vinculado, haga ping antes de iniciar el contenedor vinculado (con un tiempo de espera razonable) y, en última instancia, proporcione una configuración para anular / deshabilitar esta funcionalidad.

esto no se puede hacer cuando se crea la imagen de Docker, ya que depende del volumen de datos montado externamente

Cierto. Un enfoque aquí es iniciar solo el contenedor de la base de datos una vez (si es necesario, con un punto de entrada / comando diferente), para inicializar la base de datos o usar un contenedor solo de datos para la base de datos, creado a partir de la misma imagen que el contenedor de la base de datos.

Dicho script se vuelve significativamente más complejo si tiene que distinguir los errores genéricos de la base de datos de los errores de "la base de datos aún no está lista".

Compose / Fig se encontrará con el mismo problema allí; ¿Cómo comprobar si MySQL está activo y _aceptando_ conexiones? (y PostgreSQL, y (_insertar su servicio aquí_)). Además, ¿desde dónde debería ejecutarse el "ping"? ¿Dentro del contenedor que estás comenzando, desde el anfitrión?

Por lo que puedo decir, la imagen oficial de WordPress incluye una verificación para ver si MySQL acepta conexiones en el docker-entrypoint.sh

@thaJeztah "Agregue algo de lógica de reintento simple en PHP para errores de conexión de MySQL" escrito por tianon hace 2 días - Niza. :-) Quién sabe, tal vez esto se convierta en un enfoque estándar después de todo, pero todavía tengo mis dudas, especialmente acerca de que este tipo de implementaciones de reintento están siendo probadas por todos los autores de imágenes.

Acerca del ping del puerto: no puedo decir a primera vista cuál sería la implementación óptima. Supongo que tal vez una simple verificación de conexión desde un contenedor vinculado temporal y reintentar mientras obtiene ECONNREFUSED. Lo que sea que resuelva el 80% (o posiblemente el 99%) de los problemas, para que los usuarios no tengan que resolverlos solos una y otra vez cada vez.

@kennu ¡Ah! Gracias, no sabía que se acababa de agregar recientemente, solo revisé el script ahora debido a esta discusión.

Para ser claros, entiendo los problemas que está teniendo, pero no estoy seguro de que Compose / Fig pueda resolverlos de una manera limpia que funcione para todos (y de manera confiable). Entiendo que muchas imágenes en el registro no tienen "salvaguardas" para manejar estos problemas, pero dudo que sea responsabilidad de Compose / Fig arreglar eso.

Habiendo dicho lo anterior; Creo que sería bueno documentar esto en la sección de mejores prácticas de Dockerfile .

Las personas deben ser conscientes de esto y deben agregarse algunos ejemplos para ilustrar cómo manejar la "interrupción" del servicio. Incluyendo un enlace al artículo de WikiPedia que @dnephin mencionó (y posiblemente otras fuentes) como referencia.

Me encontré con el mismo problema y me gusta esta idea de @kennu

Personally, I would make Things Just Work, by making Fig autodetect which container ports are exposed to a linked container, ping them before starting the linked container (with a sane timeout), and ultimately provide a configuration setting to override/disable this functionality.

Creo que esto resolvería muchos casos de uso típicos, como para mí cuando dependía del contenedor oficial de mongodb .

Estoy de acuerdo con @soupdiver. También estoy teniendo problemas en conjunto con un contenedor mongo, y aunque lo tengo funcionando con un script start.sh, el script no es muy dinámico y agrega otro archivo que necesito mantener en mi repositorio (me gustaría tener solo un Dockerfile y docker-compose.yml en mi repositorio de nodo). Sería bueno si hubiera alguna forma de simplemente hacerlo funcionar, pero creo que algo simple como un temporizador de espera no lo cortará en la mayoría de los casos.

Hacer ping en la OMI no es suficiente, porque la conexión de red básica puede estar disponible, pero el servicio en sí todavía no está listo.
Este es el caso de la imagen MySQL, por ejemplo, usar curl o telnet para la verificación de conexión en los puertos expuestos sería más seguro, aunque no sé si sería suficiente. Pero la mayoría de los contenedores no tienen estas herramientas instaladas de forma predeterminada.

¿Podrían docker o fig manejar estos controles?

¿Podrían docker o fig manejar estos controles?

En resumen: _no_. Por varias razones;

  • Realizar un "ping" desde dentro de un contenedor significaría ejecutar un segundo proceso. Fig / Compose no puede iniciar automáticamente dicho proceso, y no creo que desee que Fig / Compose modifique su contenedor _instalando_ software (como curl o telnet) en él.
  • (Como mencioné en un comentario anterior), cada servicio requiere una forma diferente de verificar si está aceptando conexiones / listo para usar. Algunos servicios pueden necesitar credenciales o certificados para _establecer_ una conexión. Fig / Compose no puede inventar automáticamente cómo hacer eso.

y no creo que desee que Fig / Compose modifique su contenedor instalando software (como curl o telnet) en él.

No, seguro que no.

Fig / Compose no puede inventar automáticamente cómo hacer eso.

No inventar. Estaba pensando más en una instrucción para fig o docker, cómo verificarlo, por ejemplo.

web:
    image: nginx
    link: db
db:
   is_available: "curl DB_TCP_ADDR:DB_TCP_PORT"

El comando telnet se ejecutará en el host de la ventana acoplable, no en el contenedor.
Pero solo estoy pensando en voz alta, sé que esta no es la solución perfecta. Pero la forma actual de usar scripts de verificación personalizados para los contenedores podría mejorarse.

El comando telnet se ejecutaría en el docker-host, no en el contenedor.

Entonces curl o <name a tool that's needed> tendrían que ser instalados en el host. Esto incluso podría tener grandes problemas de seguridad (por ejemplo, alguien quiere ser divertido y usa is_available: "rm -rf /" ). Aparte de eso, poder acceder a la base de datos desde el _host_ no es garantía de que también sea accesible desde el interior del contenedor.

Pero solo estoy pensando en voz alta ...

Lo sé y lo agradezco. Simplemente piense que no hay una forma confiable de automatizar esto, o que serviría para la mayoría de los casos de uso. En muchos casos, terminaría con algo complejo (tome, por ejemplo, el ejemplo curl ; ¿cuánto tiempo debería intentar conectarse? ¿Reintentar?). Tal complejidad es mejor para moverse dentro del contenedor, lo que también sería útil si el contenedor se iniciara con Docker, no con Fig / Compose.

@thaJeztah Estoy totalmente de acuerdo contigo. Y es muy probable que no haya una solución al 100%.

Voy a repetir una sugerencia que hice antes: sería suficiente para mí si pudiera decir en la fig.yml “esperar a que este contenedor salga antes de ejecutar este otro contenedor”.

Esto me permitiría crear un contenedor que sepa cómo esperar todas sus dependencias (verificar puertos, inicializar bases de datos, lo que sea) y requeriría que fig supiera lo menos posible.

Lo vería configurado como algo como:

"" "
aplicación:
Enlaces:
- db: db
prerrequisitos:
- ejecutar este primero

ejecutar esto primero:
Enlaces:
- db: db
"" "

runthisfirst tiene un enlace que significa que la base de datos se inicia para que pueda verificar el acceso. la aplicación solo se ejecutará una vez que runthisfirst haya salido (puntos de bonificación si runthisfirst tiene que salir correctamente).

¿Es esto factible como respuesta?

KJL

El 10 de febrero de 2015, a las 05:28, Tobias Munk [email protected] escribió:

@thaJeztah https://github.com/thaJeztah Estoy totalmente de acuerdo contigo. Y es muy probable que no haya una solución al 100%.

-
Responda a este correo electrónico directamente o véalo en GitHub https://github.com/docker/fig/issues/374#issuecomment -73561930.

Acabo de intentar migrar mis lanzadores de scripts de shell y encontré este problema. Sería bueno incluso agregar una tecla simple de suspensión / espera que solo duerme durante esa cantidad de segundos antes de lanzar el siguiente contenedor.

db:
  image: tutum/mysql:5.6
  sleep: 10
app:
  link:
    - db:db

Realmente no me gusta esto por varias razones.

a) Creo que es el lugar equivocado para esto
b) ¿Cuánto tiempo duermes?
c) ¿Qué pasa si el tiempo de espera no es lo suficientemente largo?

Aparte de los problemas obvios, realmente no creo
la infraestructura debe preocuparse por lo que la aplicación
es y viceversa. IHMO, la aplicación debe estar escrita para ser
más tolerante y / o más inteligente con sus propios requisitos.

Dicho esto, las aplicaciones existentes y las aplicaciones heredadas
necesitará algo, pero probablemente debería ser más largo
las líneas de:

a docker-compose.yml :

db:
  image: tutum/mysql:5.6
app:
  wait: db
  link:
    - db:db

Donde wait espera que los servicios "expuestos" en db estén disponibles.

El problema es ¿cómo se determina eso?

En los casos más simples, espere hasta que pueda abrir con éxito
una conexión tcp o udp a los servicios expuestos.

Esto podría ser excesivo para este problema, pero lo que sería una buena solución es si Docker proporcionara un sistema de activación de eventos en el que pudiera iniciar un activador desde un contenedor que resultara en algún tipo de devolución de llamada en otro contenedor. En el caso de esperar a importar datos a una base de datos MySQL antes de iniciar otro servicio, simplemente monitorear si el puerto estaba disponible no es suficiente.

Tener un script de punto de entrada que establezca una alerta para Docker desde el interior del contenedor (establezca una variable de entorno predefinida, por ejemplo) que desencadenó un evento en otro contenedor (quizás estableciendo la misma variable de entorno sincronizada) permitiría a los scripts en ambos lados saber cuándo ciertos las tareas están completas.

Por supuesto, podríamos configurar nuestro propio servidor de socket u otros medios, pero es tedioso resolver un problema de orquestación de contenedores.

@aanand yo _casi_ tengo algo funcionando usando su enfoque de espera como punto de partida. Sin embargo, hay algo más entre la ejecución de docker-compose y la ejecución de la ventana acoplable donde la primera parece bloquearse mientras que la última funciona a la perfección.

ejemplo docker-compose.yml:

db:
  image: postgres
  ports:
    - "5432"
es:
  image: dockerfile/elasticsearch
  ports:
    - "9200"
wait:
  image: n3llyb0y/wait
  environment:
    PORTS: "5432 9200"
  links:
    - es
    - db

luego usando ...

docker-compose run wait

sin embargo, esto no será así. Los servicios vinculados se inician y parece que estamos a punto de esperar solo a que se ahogue (al menos dentro de mi entorno virtualbox. Llego al bucle nc y obtenemos un solo punto y luego ... nada).

Sin embargo, con los servicios vinculados en ejecución, puedo usar este método (que es esencialmente lo que he estado haciendo para nuestras compilaciones de CI)

docker run -e PORTS="5432 9200" --links service_db_1:wait1 --links service_es_1:wait2 n3llyb0y/wait

Parece que docker-compose run debería funcionar de la misma manera. La diferencia es que cuando usas docker-compose run con la bandera de separación -d no obtienes ningún beneficio de espera ya que los fondos del contenedor de espera y creo (en este momento) que no usar la bandera causa la espera ahogarse con los otros servicios que no están en segundo plano. Voy a echar un vistazo más de cerca

Después de un poco de prueba y error, ¡parece que el enfoque anterior funciona! Es solo que la base de busybox no tiene una utilidad netcat que funcione muy bien. Mi versión modificada de la utilidad @aanand wait funciona contra docker-compose 1.1.0 cuando utilizo docker-compose run <util label> lugar de docker-compose up . Ejemplo de uso en el enlace.

Sin embargo, no estoy seguro de si puede manejar situaciones de encadenamiento según la pregunta original. Probablemente no.

Déjame saber lo que piensas.

Este es un tema muy interesante. Creo que sería muy interesante tener una forma de que un contenedor espere hasta que otro esté listo. Pero como todo el mundo dice, ¿qué significa listo? En mi caso tengo un contenedor para MySQL, otro que gestiona sus copias de seguridad y además se encarga de importar una base de datos inicial, y luego los contenedores para cada app que necesite la base de datos. Es obvio que esperar a que los puertos estén expuestos no es suficiente. Primero se debe iniciar el contenedor mysql y luego el resto debe esperar hasta que el servicio mysql esté listo para usarse, no antes. Para conseguirlo, he necesitado implementar una secuencia de comandos simple que se ejecutará al reiniciar y que utiliza la funcionalidad docker exec . Básicamente, el pseudocódigo sería como:

run mysql
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"show tables\""
run mysql-backup
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"describe my_table\""
run web1
waitUntil "dexec web1 curl localhost:9000 | grep '<h1>Home</h1>'"
run web2
waitUntil "dexec web2 curl localhost:9000 | grep '<h1>Home</h1>'"
run nginx

Donde la función waitUntil tiene un bucle con un tiempo de espera que evalúa el comando docker exec … y verifica si el código de salida es 0.

Con eso aseguro que cada contenedor espera hasta que sus dependencias estén listas para usarse.

Así que creo que podría ser una opción para integrar dentro de la utilidad de redacción. Tal vez algo así, donde wait_until declara una lista de otras dependencias (contenedores) y espera a cada una hasta que responda bien al comando correspondiente (o tal vez con un patrón opcional o expresión regular para verificar si el resultado coincide con algo que esperas, aunque usar el comando grep podría ser suficiente).

mysql:
  image: mysql
  ...
mysql-backup:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "show tables"
  ...
web1:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "describe my_table"
  ...
web2:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "describe my_table"
  ...
nginx:
  links:
   - web1
   - web2
  wait_until:
   - web1: curl localhost:9000 | grep '<h1>Home</h1>'
   - web2: curl localhost:9000 | grep '<h1>Home</h1>'
  ...

¿No es una comida sencilla para el puerto?
http://docs.azk.io/en/azkfilejs/wait.html#

@robsonpeixoto : Esperar el puerto no es suficiente para muchos casos de uso. Por ejemplo, digamos que está sembrando una base de datos con datos al momento de la creación y no desea que el servidor web se inicie y se conecte a él hasta que la operación de datos se haya completado. El puerto estará abierto todo el tiempo para que no bloquee el inicio del servidor web.

Algo como WaitCondition de AWS CloudFormation sería bueno. http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-waitcondition.html

+1 Tengo el mismo problema al usar Docker para probar mis aplicaciones Rails que dependen de MySQL

+1 Yo también tengo este problema. Me gusta la idea de @adrianhurt , donde en realidad proporcionas la condición a evaluar para determinar si la espera se ha completado. De esa manera, todavía tiene un buen yml declarativo y no tiene que tener una definición arbitraria de "listo".

+1

He tenido esta pestaña abierta por un tiempo: http://crosbymichael.com/docker-events.html ... parece relevante

+1

+1 para tiempo de espera simple

+1 para una condición de listo

+1

Estoy resolviendo esto de manera muy confiable a nivel de aplicación desde hace un tiempo, como se recomendó en este hilo.

Solo para darle una idea de cómo se puede implementar esto para MySQL + PHP, aquí está mi código.

De igorw / retry :)

Dado que la red es confiable, las cosas siempre deberían funcionar. Estoy en lo cierto? Para aquellos casos en los que no lo hacen, hay un reintento.

+1

@ schmunk42 Cosas bonitas: me gusta que sea un buen ejemplo tanto de establecer la conexión como de realizar una operación de configuración de base de datos idempotente.

Podría ser bueno crear (algunos) ejemplos básicos para incluirlos en los documentos, para diferentes casos, por ejemplo, NodeJS, Ruby, PHP.

+1, al menos debería proporcionar algunas opciones para agregar un retraso antes de que el contenedor se inicie correctamente.

+1

Cómo resolver problemas cuando intentas conectar servicios que no son tu código.
Por ejemplo, si tiene el servicio Service y la base InfluxDB datos Services requiere InfluxDB y el InfluxDB tiene un inicio lento.

¿Cómo puede docker-compose esperar a que InfluxDB esté listo?

Si el código es mío, puedo resolverlo volviendo a intentarlo. Pero para la tercera aplicación no puedo cambiar el código.

@robsonpeixoto hay algunos ejemplos en este ticket con netcat o formas similares. Puede echar un vistazo a mi ejemplo de MySQL en otro ticket: https://github.com/docker/docker/issues/7445#issuecomment -101523662

Esa es la razón por la que creo que cada contenedor debería tener la capacidad opcional de indicar su propia preparación. Para una base de datos, por ejemplo, quiero esperar hasta que el servicio esté completamente listo, no cuando se crea el proceso. Resuelvo esto con cheques personalizados con docker exec y verificando si puede resolver una consulta simple, por ejemplo.

Algún indicador opcional para docker run para indicar un comando de verificación interno sería genial para luego vincularlo desde otro contenedor usando un indicador especial para el enlace.

Algo como:

$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python app.py

Donde is_ready.sh es una prueba booleana simple que se encarga de decidir cuándo se considera que el contenedor está listo.

+1

@ schmunk42 ¡ buena cita!

+1

+1

+1

+1

+1

+1

+1

+1

+1

En realidad cambié de opinión sobre esto, así que -1

Tiene más sentido que su contenedor verifique si el servicio de terceros está disponible, y esto se hace fácilmente con un pequeño script bash wrapper que usa nc por ejemplo.

Confiar en un retraso es tentador, pero es una mala solución porque:

  • Su contenedor _siempre_ esperará X segundos antes de estar listo.
  • Es posible que X segundos aún no sean suficientes en algunos casos (por ejemplo, E / S o CPU pesadas en el host), por lo que su contenedor aún no es a prueba de fallas.
  • Sin estrategia fallida.

Confiar en escribir un script bash de envoltura es mejor porque:

  • Su contenedor estará listo tan pronto como sea posible.
  • Puede implementar cualquier estrategia de falla, por ejemplo, intentar 10 veces y luego fallar, intentar siempre, etc. ¡Incluso puede implementar el retraso durmiendo antes de intentarlo!

Al leer este hilo, veo que nadie menciona secretos. Estoy tratando de usar un contenedor de solo datos que solicita secretos una vez que se ejecuta. El problema que tengo: si mis secretos tardan demasiado en transmitirse / descifrarse, mi contenedor dependiente falla porque los datos que espera no están allí. Realmente no puedo usar el método de "poner todo en el contenedor antes de ejecutarlo" porque son secretos.

Sé que existe cierta ambigüedad en torno a los contenedores de solo datos en la composición debido al contexto del código de retorno, pero ¿hay una mejor manera de hacer esto?

De manera similar, cambiando de opinión sobre esto, -1. El enfoque de @dnephin es completamente correcto. Si su _aplicación_ depende de un _servicio_, la propia aplicación debería poder manejar la indisponibilidad de ese servicio con elegancia (por ejemplo, restableciendo una conexión). No debería ser un script de envoltura de bash o alguna lógica en Compose o Docker, es responsabilidad de la aplicación en sí. Cualquier cosa que no esté en el nivel de la aplicación también funcionará solo en la inicialización; si ese servicio deja de funcionar, no se ejecutará un script contenedor o algo.

Ahora bien, si pudiéramos lograr que los desarrolladores de aplicaciones / bibliotecas / marcos se dieran cuenta y respaldaran esta responsabilidad, sería fantástico.

Es difícil de hacer teniendo en cuenta los enfoques que tomaría para incluir otros demonios que no se recomiendan. Para mi ejemplo, en el que tengo una aplicación de rieles que intenta conectarse a una base de datos MySQL mientras otra aplicación de rieles está migrando y sembrando la base de datos en el inicio inicial, para que la aplicación de rieles sepa que no intenta usar la base de datos, lo haría tiene que modificar la biblioteca ActiveRecord (no va a suceder) o ejecutar un script que sigue verificando si la base de datos ha sido migrada y sembrada. Pero, ¿cómo puedo saber con certeza sin saber qué datos se supone que deben estar allí y / o tener algún script que se ejecute en el sistema que está sembrando la base de datos para informar al resto que se conecte a él?

Tal vez me esté perdiendo la solución obvia, pero su respuesta de "los desarrolladores deberían poder lidiar con esto en su propio código" se rompe cuando está usando bibliotecas listas para usar y cuando no "se supone" que debe cargar demonios en un contenedor.

@mattwallington ni siquiera estoy seguro de cómo Compose proporcionaría una solución para esa situación ...
Eso también es terriblemente específico, lo que haría aún más difícil de inventar para Compose. Leería algunos de los consejos de @dnephin anteriores sobre inicialización / migración / siembra, ya que eso podría ayudar en su caso.

Creo que no entendiste el punto de mi última línea, muchas bibliotecas disponibles no funcionan _precisamente_ porque no se han construido de manera resistente. No hay una solución mágica que pueda resolver todo esto que Compose pueda implementar.

Entendido. Mi sugerencia anterior, que funcionaría para muchos de estos casos de uso, es tener una variable de entorno compartida entre contenedores. Un lado podría bloquear el sondeo de la variable y el otro podría realizar la acción y luego establecer la variable.

+1 @mattwallington idea de variable de entorno compartida entre contenedores

Gracias. Es simple (a nivel de producto. No tengo idea de lo que se necesitaría desde el lado del desarrollo, ya que no he mirado el código) pero resolvería muchos de estos problemas y probablemente muchos otros, ya que no es específico de este problema.

Pero, ¿cómo puedo saber con certeza sin saber qué datos se supone que deben estar allí y / o tener algún script que se ejecute en el sistema que está sembrando la base de datos para informar al resto que se conecte a él?

@mattwallington : verifique el número de migración en la tabla de esquema. Si el número es correcto, sabrá que se ejecutó la migración.

No debería ser un script de envoltura de bash o alguna lógica en Compose o Docker, es responsabilidad de la aplicación en sí. Cualquier cosa que no esté en el nivel de la aplicación también funcionará solo en la inicialización; si ese servicio deja de funcionar, no se ejecutará un script contenedor o algo.

@ agilgur5 : sí, estoy de acuerdo en que lo manejará la aplicación, pero un script bash es una solución simple para manejar aquellas aplicaciones que no están codificadas de esa manera, por ejemplo, reiniciando la aplicación cuando el servicio no está disponible.

Se pueden argumentar todo el día sobre lo que debería o podría hacerse a nivel de la aplicación, pero en lugar de esperar que todas las aplicaciones del mercado se ocupen de esto y se vuelvan increíbles en la recuperación personal (poco probable), ¿por qué estamos tan en contra de agregar algunas funciones que pueden resolver? este problema para las aplicaciones se ejecuta en la ventana acoplable independientemente de cómo estén escritas las aplicaciones de terceros o lo que DEBEN hacer pero no lo harán. Esto es sobre lo que tenemos control. Resolvamos el problema en lugar de decidir quién debería ser el que lo resuelva, ya que no tenemos control sobre eso.

Estoy de acuerdo con @mattwallington. Puede requerir este esfuerzo adicional para la autorrecuperación a nivel de la aplicación de cada desarrollador de cada imagen de contenedor, pero un gran porcentaje de ellos seguramente serán demasiado ignorantes o simplemente estarán demasiado ocupados para implementarlo y probarlo con cuidado. El resultado final será que algunos contenedores saben cómo recuperarse por sí mismos, mientras que muchos no. Y como usuario, no dispondrá de herramientas para gestionar las que no lo hacen.

Una idea que se me acaba de ocurrir: en lugar de resolver el problema retrasando el inicio del contenedor, Compose podría intentar recuperar el contenedor fallido.

Algo como recover: auto reiniciaría el contenedor fallido 5 veces en 2, 4, 8, 16 y 32 segundos y luego se rendiría por completo.

¿Alguien ha pensado en la noción de un contenedor de dependencia de contenedores?

Por ejemplo:

`` #! yml
db:
imagen: mysql

esperar:
Enlaces:
- db
volúmenes:
- /var/lib/docker.sock:/docker.sock
- $ {PWD} /docker-compose.yml:/docker-compose.yml
comando: docker-compose up -d app

aplicación:
imagen: myuser / myapp
Enlaces:
- db
''

La idea básica aquí es que usted _ resuelve_ el problema de los contenedores que no tienen mecanismos de recuperación automática creando un servicio reutilizable dedicado que podría publicarse en Docker Hub y que todos puedan inyectar en su composición.

Incluso estaría dispuesto a crear un prototipo de un servicio / contenedor / imagen de este tipo y dejar que otros jueguen con esto para ver cómo le va ...

@prologic El problema con una dependencia es, ¿cómo asegurarse de que el servicio con el que desea hablar esté realmente activo?

Su contenedor db podría responder a un ping pero está realizando una limpieza / inicialización previa al lanzamiento de la base de datos antes de que esté disponible para los comandos mysql / psql .

¿Puede esa prueba definirse de una manera configurable y / o suministrarse en un script a un servicio de tipo waitfor reutilizable?

En mi humilde opinión, este es un problema muy común y cada uno tiene sus propios requisitos específicos. Como se comentó anteriormente en este número, creo que la ventana acoplable podría (y debería) proporcionar una forma para que un contenedor especifique un comando simple para verificar su propia preparación. Obviamente, nosotros, como desarrolladores, deberíamos indicar específicamente cómo verificar la preparación de cada contenedor.

Algún indicador opcional para la ejecución de la ventana acoplable para indicar un comando de verificación interno sería genial para luego vincularlo desde otro contenedor usando un indicador especial para el enlace.

Algo como:

$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python 

Donde is_ready.sh es una prueba booleana simple que se encarga de decidir cuándo se considera que el contenedor está listo.

También podría ser un comando para que un contenedor verifique manualmente su preparación.

Donde is_ready.sh es una prueba booleana simple que se encarga de decidir cuándo se considera que el contenedor está listo.

lo que significa que cada desarrollador debe preparar sus imágenes / contenedores para incluir _algo_ que se pueda usar para verificar si el contenedor está listo.

Lo que nos lleva de vuelta al cuadro 1 .; los desarrolladores son los responsables de hacer que sus contenedores sean resistentes a la interrupción del servicio / tiempo de inicio, porque son los únicos que pueden decir "qué" eso significa para su situación.

¿O estoy pasando por alto algo aquí?

Estoy de acuerdo. La responsabilidad recae en el desarrollador / contenedor / servicio

El jueves 30 de julio de 2015, Sebastiaan van Stijn [email protected]
escribió:

Donde is_ready.sh es una prueba booleana simple que está a cargo del
decisión de cuándo se considera que el contenedor está listo.

lo que significa que cada desarrollador debe preparar sus imágenes / contenedores para
incluir _algo_ que se pueda usar para verificar si el contenedor es
Listo.

Lo que nos lleva de vuelta al cuadro 1 .; los desarrolladores son los responsables de
hacer que sus contenedores sean resistentes a la interrupción del servicio / tiempo de inicio, porque
¿Son los únicos que pueden decir "qué" significa eso para su situación?

¿O estoy pasando por alto algo aquí?

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/docker/compose/issues/374#issuecomment -126278215.

James Mills / prológico

E: [email protected]
W: prologic.shortcircuit.net.au

Sí, por supuesto. Para mí, el único que realmente sabe cuando un contenedor está listo es el propio contenedor. Docker no puede saber nada sobre el contenido de un contenedor. Es una caja negra. Lo único que podría hacer es preguntar al contenedor (con una acción personalizada especificada cuando desea ejecutar, como propuse, o cualquier otra forma común de probarlo). Y obviamente el desarrollador es el único que sabe lo que necesita y el contenido de esa caja negra.

¡Sí es cierto!

El jueves 30 de julio de 2015, adrianhurt [email protected] escribió:

Sí, por supuesto. Para mí, el único que realmente sabe cuándo un contenedor es
listo está el propio contenedor. Docker no puede saber nada sobre el contenido de
un contenedor. Es una caja negra. Lo único que podía hacer era pedirle al
contenedor (con una acción personalizada especificada cuando desea ejecutar, como yo
propuesto, o cualquier otra forma común de probarlo). Y obviamente el desarrollador
es el único que sabe lo que necesita y el contenido de esa caja negra.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/docker/compose/issues/374#issuecomment -126285056.

James Mills / prológico

E: [email protected]
W: prologic.shortcircuit.net.au

De acuerdo, por el bien de todo el software en desarrollo o heredado que no puede manejar fallas de red, supongamos que queremos resolver este problema después de todo. No digo que lo hagamos, solo quiero tener una idea de la sintaxis, la semántica y la complejidad.

El conjunto mínimo de requisitos parece ser:

  • Quiero que Compose espere para iniciar un servicio hasta que otro servicio esté "listo".
  • Quiero definir "listo" como "aceptar conexiones TCP en el puerto X", o algo más.

Supongamos también que las comprobaciones de estado no llegarán a Docker por un tiempo.

Me pregunto si podría resolverse en el caso general haciendo posible _esperar a que salgan los contenedores de otro servicio_. Luego, podría escribir su chequeo médico como un servicio más.

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["db_wait"]

db_wait:
  image: netcat
  links: ["db"]
  command: sh -c "while ! nc -w 1 -z db 5432; do sleep 1; done"

db:
  image: postgres

Si desea algún tipo de control de salud personalizado, defínalo en el servicio "esperar". Aquí, db_wait solo saldrá una vez que mytable exista en la base mydb datos

db_wait:
  image: postgres
  links: ["db"]
  command: sh -c "while ! psql --host db --dbname mydb -c "\d mytable"; do sleep 1; done"

Si tiene un script de preparación de base de datos para ejecutar primero, puede hacer que sea lo que debe esperar:

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["prepare_db"]

prepare_db:
  image: prepare_db
  links: ["db"]
  command: ./prepare.sh

db:
  image: postgres

El primer caso (esperar hasta que el contenedor acepte conexiones TCP) puede ser lo suficientemente común como para que valga la pena admitirlo de inmediato.

web:
  image: mywebapp
  links: ["db"]
  wait_for_tcp: ["db:5432"]

db:
  image: postgres

Hay una implicación oculta en todo esto: docker-compose up -d tendría que bloquearse mientras se ejecuta el servicio intermedio de verificación de estado o preparación, para que pueda iniciar los servicios al consumidor una vez que esté listo.

Si. Sin embargo, en mi opinión, la ventana acoplable en sí debería proporcionar una forma de determinar cuándo un contenedor está listo y luego componer podría administrarlo. A continuación, podríamos plantear un nuevo problema directamente en la ventana acoplable. Para mí, podría ser algo como:

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["db"]

db:
  image: postgres
  ready_when: sh -c "while ! psql --host db --dbname mydb -c "\d mytable"; do sleep 1; done"

Entonces, no es necesario crear nuevos servicios como solución alternativa

Convenido. Dale flexibilidad al desarrollador. Además, es posible que detener el contenedor no sea lo que el desarrollador necesita que haga mientras espera. Quizás haya algo de su propio init que deba suceder, pero luego espere a que la base de datos esté lista para las conexiones. Entonces, si se trata de una variable env compartida simple, permite al desarrollador usarla cuando lo necesite.

¿Cómo se actualizaría una variable de entorno compartida? Por lo que yo sé, no se pueden realizar cambios en el conjunto de variables de entorno de un proceso una vez iniciado.

Puede cambiarlos, por ejemplo, en una sesión bash, pero no se propagarán en todas las demás capas.

Para ello, se debe confirmar el cambio y reiniciar el contenedor. Esto es bastante inútil en este escenario.

Es más, incluso si la variable pudiera cambiar, el proceso en el contenedor necesitaría saber para sondearlo, lo que hace que esto no sea una solución para el software heredado para el que aparentemente es esta función.

Dado que en realidad solo estamos hablando de resolver este problema para contenedores heredados en entornos de desarrollo, creo que podría resolverse como una herramienta que se encuentra en la parte superior de compose docker-compose ps -s (lista de servicios en orden de dependencia, # 1077).

Con estos dos comandos, se podría escribir una herramienta que haga algo como esto:

  1. Ejecute docker-compose ps -s para obtener la lista de nombres de servicios en orden de dependencia
  2. Ejecutar docker-compose up -d --no-recreate <first service from the list>
  3. Ejecute el comando "healthcheck" para ese servicio hasta que funcione correctamente o llegue al tiempo de espera. Esto podría ser una solicitud HTTP o una llamada docker exec
  4. Repita 2 y 3 para cada servicio de la lista.
  5. Ejecute docker-compose logs (o no lo haga si se pasa -d )

De esta manera, la configuración "healthcheck" y "wait for" puede ser externa para componer, pero no creo que el desarrollador deba proporcionar nada por encima de lo que se requeriría si se implementara como parte de compose.

¿Alguna razón por la que esto no funcionaría?

Me alegra que hayamos limitado el alcance a los contenedores heredados, eso es mucho más razonable: +1:

@dnephin Creo que es genial desde la perspectiva inmediata de que es flexible y que Compose no tiene que tener algún soporte integrado para contenedores heredados, pero veo un problema inmediato similar al que describió @mattwallington , ¿y si los otros contenedores pueden? ejecutar algunas cosas (por ejemplo, init) antes de conectarse a este servicio? Bloquear ese contenedor _funciona_, pero no es ideal (dicho esto, no estoy seguro de si existe una solución ideal para un contenedor heredado). Eso resolvería mi problema al menos, ¡ahora tengo que encontrar ese boleto!

+1

por poder especificar dependencias en archivos docker-compose ...

... sin usar enlaces (ya que son incompatibles con net = host). Pensé que es el trabajo o al menos la preocupación de una herramienta de administración de contenedores múltiples saber en qué orden deben comenzar las cosas, al igual que el títere tiene su propio árbol de dependencia, pero a veces el usuario sabe mejor y puede anularlo. Al leer todo lo anterior, me parece que la única dificultad es decidir cuándo un contenedor está "activo" para que se pueda iniciar el siguiente contenedor en la cadena de dependencia. Este podría ser exactamente el mismo mecanismo que el mecanismo de enlace (por ahora): cualquier cosa es mejor que nada. Más tarde, la aclaración de "cumplir una dependencia" podría especificarse mediante el archivo docker-compose, por ejemplo: esta dependencia importa que el contenedor se esté ejecutando

depends_on:
  container: foo
  requires: running

o esta dependencia importa que los puertos TCP de los contenedores estén escuchando.

depends_on:
  container: foo
  requires: listening

Decir que es el trabajo de alguna herramienta externa o secuencia de comandos sobre docker-compose equivale a decir que docker-compose no tiene ningún interés o responsabilidad real en la orquestación de ejecutar 2 o más contenedores en la misma máquina. Entonces, ¿cuál es su propósito?

Pensé que es el trabajo o al menos la preocupación de una herramienta de administración de contenedores múltiples saber en qué orden deberían comenzar las cosas

No, no necesariamente. Por las razones expuestas ya varias veces en este hilo, creo que solo hay dos excusas para entregar este trabajo a una herramienta de administración de contenedores:

  1. Está ejecutando imágenes de contenedores listas para usar que no son resistentes a la indisponibilidad de los servicios ascendentes de los que dependen y, por razones técnicas o comerciales, no puede cambiarlas ni ampliarlas.
  2. Está ejecutando software exclusivamente en un entorno de desarrollo, no de producción, y tiene mejores cosas en las que dedicar tiempo que en implementar la resiliencia.

Sin embargo, si tiene control sobre su software y planea implementarlo en producción, no puede confiar en una herramienta externa que simplemente inicia las cosas en el orden correcto, incluso si ha definido cuidadosamente sus condiciones de preparación. No es una solución al problema fundamental, y su sistema se caerá en el momento en que haya un problema en la red. En el mejor de los casos, tendrá que reiniciar automáticamente todos sus contenedores de interfaz web cada vez que eso suceda, y no veo que sea una cantidad aceptable de tiempo de inactividad para nadie.

Estoy de acuerdo con usted en que un software bien escrito hará frente a las interrupciones de la red. Creo que ambos estamos de acuerdo en que no todo el software está bien escrito y los desarrolladores no siempre piensan en todos los casos de uso posibles.

Tal vez quiera ejecutar un contenedor de clientes que ejecute una JVM y otra herramienta de monitoreo para adjuntarlo, tanto en el espacio de nombres PID del host para que uno pueda monitorear al otro y solo tenga soporte y licencia para ejecutar dicha herramienta con la imagen autorizada del proveedor . Si la herramienta de monitoreo monitorea las JVM existentes, entonces importa en qué orden se inician (obviamente). Probablemente hay cientos de casos de uso en los que el orden importa, algunos de los cuales involucran redes (se han mencionado mysql, elasticsearch, clústeres de descubrimiento de servicios), pero algunos involucran otras cosas que se pueden compartir usando los diferentes espacios de nombres de manera efectiva.

Así que definitivamente estoy de acuerdo con el caso de uso (1), ya que en algunas circunstancias simplemente no puede cambiar un contenedor

Pero también tan pronto como una herramienta se ocupa de múltiples cosas, inmediatamente se enfrenta a pedidos. Si ordenar es importante y la única forma de garantizar un pedido es escribir un script bash alrededor de docker-compose para primero hacer algo, y luego subir algo más, docker-compose podría no existir siquiera en la cadena, aparte del hecho JSON / YAML es más bonito que cmdline args.

En mi humilde opinión, en última instancia, una herramienta es útil o no para un montón de casos de uso. Docker-compose es claramente útil para inicios desordenados de varios contenedores en el mismo host. Si suficientes personas y suficientes casos de uso se tratan de pedidos y una herramienta no los aborda, la gente simplemente irá a otro lado para esos casos de uso, lo cual es una pena.

De todos modos, veo que solo estoy repitiendo viejos argumentos, así que me expulsaré de este hilo ahora ...

Este es el mismo argumento que se expresa en miles de hilos sobre tantos temas de desarrollo diferentes "Si todo el código fuera escrito correctamente por todos, no habría necesidad de esto, así que no lo hagamos". Eso equivale a decir: si todas las personas del mundo dejaran de quemar combustibles fósiles o de usar electricidad, podríamos solucionar los problemas que estamos causando a nuestro planeta. Estás en lo correcto. Si todas las aplicaciones hicieran lo que deberían, no estaríamos aquí hablando de esto. Pero vivimos en la tierra. Un lugar que tenía imperfecciones masivas con una especie de seres que tienden a tener que aprender las cosas por las malas. Las mismas personas que solo logran que las empresas despeguen porque construyen un "producto mínimo viable" para rezar para que lleguen a la próxima ronda de financiación o al próximo cliente que algún día les permita crear la versión que desearían. .

El mundo no es y nunca será perfecto, por lo que solo podemos hacer lo que tenemos bajo nuestro propio control. Y en este caso, lo único que tengo bajo mi control es tratar de convencerlos a todos (también conocidos como las personas que desarrollan la herramienta que me encanta y que usaría como loco si solo tuviera esta característica) para construirla una forma en la que pueda manejar el software que existe en el mundo en el que vivimos. No en el que deseamos vivir.

El 31 de julio de 2015, a las 3:42 a.m., Aanand Prasad [email protected] escribió:

Pensé que es el trabajo o al menos la preocupación de una herramienta de administración de contenedores múltiples saber en qué orden deberían comenzar las cosas

No, no necesariamente. Por las razones expuestas ya varias veces en este hilo, creo que solo hay dos excusas para entregar este trabajo a una herramienta de administración de contenedores:

Está ejecutando imágenes de contenedores listas para usar que no son resistentes a la indisponibilidad de los servicios ascendentes de los que dependen y, por razones técnicas o comerciales, no puede cambiarlas ni ampliarlas.

Está ejecutando software exclusivamente en un entorno de desarrollo, no de producción, y tiene mejores cosas en las que dedicar tiempo que en implementar la resiliencia.

Sin embargo, si tiene control sobre su software y planea implementarlo en producción, no puede confiar en una herramienta externa que simplemente inicia las cosas en el orden correcto, incluso si ha definido cuidadosamente sus condiciones de preparación. No es una solución al problema fundamental, y su sistema se caerá en el momento en que haya un problema en la red. En el mejor de los casos, tendrá que reiniciar automáticamente todos sus contenedores de interfaz web cada vez que eso suceda, y no veo que sea una cantidad aceptable de tiempo de inactividad para nadie.

-
Responda a este correo electrónico directamente o véalo en GitHub.

+1 @aanand. Esto no es algo que pueda limitar en su alcance. La función, si se realiza, debe ser algo con lo que la gente pueda contar. Han estado codificando infraestructura "duradera" durante mucho tiempo y se está tardando mucho en convertir a las masas. Lo usarán durante mucho tiempo.

Me gustaría reiterar que no hemos descartado implementar algo para aliviar el problema de las dependencias de tiempo de inicio entre contenedores; esbocé una posible solución ayer, en este mismo hilo .

Pero quiero que estemos en sintonía con respecto a para quién es esta función, qué problemas resolverá, qué problemas _no_ resolverá y en qué medida se puede confiar. Eso es lo que estoy tratando de sentir.

Además, dadas las múltiples definiciones de preparación que existen ("el contenedor ha comenzado" vs. idea de cuántas personas se beneficiarían del soporte listo para usar para cada uno.

Una técnica común para sincronizar servicios es algo así como un "registro de servicios" usando etcd o similar. ¿Qué tal un wait_for_service:

web:
  image: mywebapp
  links: ["db"]
  wait_for_service:
    type: etcd (or consul, or zk)    -- or use swarm type notation
    addr: http://my.etcd.com/
    path: postgres.service

db:
  image: postgres

compose no sabe si el servicio está realmente listo, pero puede buscar los datos en el registro e iniciar el contenedor dependiente basándose en eso. Es responsabilidad de los servicios (como postgres) publicar su disponibilidad en el registro, por lo que para las aplicaciones heredadas, algún tipo de script de ajuste haría eso: inicie la aplicación, observe que el puerto se active y luego publique en el registro.

Hola @aanand.

Estuve discutiendo esto hoy con @bfirsh en los twitters, ya que es algo que he encontrado.

Específicamente, al construir un sistema distribuido a partir de muchos componentes pequeños acoplados, encontré la necesidad de tener pruebas de integración que activaran todas las aplicaciones de la interfaz principal y sus dependencias, luego ejecuté varias pruebas contra ellas antes de derribarlas todas de nuevo.

Esto lleva a que, por ejemplo, Riak tarde un poco en comenzar en comparación con cualquier otra cosa que lo use.

No me opondría especialmente a que todos ustedes digan "los contenedores comienzan asíncronos, lidiar con eso", pero diseñar alrededor de cada servicio exponiendo una verificación de estado consistente al menos contendría "lidiar con eso" en una implementación por servicio en lugar de tener código para hacer frente a reintentar las conexiones en cada aplicación que depende del servicio.

Los servicios que definen su propio chequeo de salud también son beneficiosos para fines de monitoreo.

@elliotcm Estoy de acuerdo en ambos puntos.

Para realizar pruebas en nuestro CI, creamos una pequeña utilidad que se puede usar en un contenedor Docker para esperar a que los servicios vinculados estén listos. Encuentra automáticamente todos los servicios TCP vinculados a partir de sus variables de entorno y repetida y simultáneamente intenta establecer conexiones TCP hasta que tiene éxito o se agota el tiempo de espera.

También escribimos una publicación de blog que describe por qué lo creamos y cómo lo usamos .

@meeee que se ve realmente agradable y útil! ¿Puede mostrarnos ejemplos de cómo gira? en particular en uso con un docker-compose?

Hoy tuve un caso más complicado, en el que estaba iniciando un contenedor mysql por primera vez. El contenedor se inicia a sí mismo cuando se ejecuta por primera vez y reinicia el demonio de la base de datos cuando está configurado. Esto provocó que mis comprobaciones de disponibilidad de puertos se activaran prematuramente y que los contenedores dependientes se iniciaran pero no se conectaran. Esto fue en un entorno de CI, donde queremos tener las cosas configuradas completamente desde cero.

Estoy muy interesado en redactar para admitir algún tipo de comportamiento de espera / verificación, pero existen algunos casos engañosamente complicados. Estoy feliz de participar en las discusiones.

@prologic Todo lo que tiene que hacer es ejecutar el comando waitforservices en un contenedor Docker, dependiendo de otros servicios / contenedores antes de ejecutar su aplicación o pruebas. Encuentra todos los servicios vinculados y se ejecuta hasta que pueda conectarse a ellos o haya pasado un tiempo determinado (60 segundos por defecto). Simplemente ejecute todo el código que depende de otros servicios después de la salida binaria (aunque es posible que desee verificar el estado de salida).

@pugnascotia Su servidor de base de datos podría escuchar en localhost solo mientras se está iniciando; tendrá que exponer algún tipo de indicador de si el contenedor está listo de todos modos. No usamos MySQL, pero waitforservices funciona perfectamente con la imagen oficial de postgres .

@aanand Postgres es un excelente ejemplo para elegir en realidad, porque esperar a que se abra el puerto TCP _no_ es suficiente; si lo hace en este tipo de escenario (docker), a veces recibirá un error como FATAL: the database system is starting up. si su otro contenedor se conecta demasiado rápido después de que se abre la conexión TCP. Entonces, psql parece necesario si quieres estar seguro de que postgres está listo; yo uso select version() pero tal vez haya una alternativa más ligera.

Hay un complemento de ventana acoplable de Maven particular que tiene una implementación de espera interesante. En este momento estoy usando un contenedor bash para iniciar cada servicio, lo que anula el punto de usar compose.

Usando esto como una solución alternativa (no estoy seguro de que sea a prueba de balas):

db:
  image: postgres:9.3
  ports:
    - "5432:5432"
createdbs:
  image: postgres:9.3
  links:
    - db
  command: >
    /bin/bash -c "
      while ! psql --host=db --username=postgres; do sleep 1; done;
      psql --host=db --username=postgres -c 'CREATE DATABASE \"somedatabase\";';
    "

He estado usando métodos similares a @olalonde. Cuando utilizo comillas simples para el comando que se ejecuta después de /bin/bash -c , también puedo utilizar variables de entorno que se reutilizan de enlaces cuando otras aplicaciones, de modo que puedo usar nombres de usuario y contraseñas sin tener que mantenerlos en dos lugares. Esto funciona bien para situaciones en las que tengo un servicio, como una API, que necesita una base de datos para estar activa y tener los datos adecuados arrancados mediante la ejecución de una consulta de verificación para ver si existe una tabla o registro específico. Esto también significa que necesito tener algún tipo de cliente instalado en el contenedor para consultar la base de datos correctamente, pero funciona.

+1 Estoy realmente interesado en esta función.

+1 a dependencias. Estoy de acuerdo en que, en principio, la arquitectura debería ser lo suficientemente robusta para soportar cualquier orden de puesta en marcha. PERO, muchas veces, hacerlo no es práctico.

Parece que el rechazo de esta característica proviene de otros que intentan dictar la arquitectura desde lejos; donde realmente no tienen derecho a hacerlo. Esta característica permitiría trabajar con refactores que consumen mucho tiempo; con poco valor a largo plazo. (Refactorización por el bien de la refactorización).

Sí, dije "trabajar alrededor"; y me siento sucio Pero para mí, componer se trata realmente de permitir que otros sean productivos. Esta sencilla configuración lo permite.

Si esta característica existiera en la herramienta más grande, podría resolver mi problema en un minuto y pasar a agregar valor real. En cambio, me estoy golpeando la cabeza contra una pared tratando de solucionar problemas de orden de inicio con dependencias externas.

@beardface, y todos los demás: ¿Qué característica, en concreto, que permita seguir adelante con el desarrollo de su aplicación?

  1. ¿La capacidad de especificar que el servicio A debe esperar para comenzar hasta que el servicio B haya comenzado? (que, tenga en cuenta, aún no resolverá la condición de carrera cuando un contenedor se inició pero no está listo para aceptar conexiones)
  2. ¿La capacidad de especificar que un servicio A debe esperar para comenzar hasta que el servicio B acepte conexiones? (que, tenga en cuenta, aún no resolverá la condición de carrera cuando un contenedor está escuchando pero no ha terminado de inicializarse, por ejemplo, un contenedor de postgres que crea una base de datos al inicio, para usar el ejemplo de @rarkins )
  3. ¿La capacidad de definir una verificación de estado en el servicio B y especificar que el servicio A debe esperar para comenzar hasta que pase la verificación de estado del servicio B?

@aanand El número 3 es mi voto. Le brinda al desarrollador la flexibilidad de elegir la lógica de verificación de estado en lugar de volver a depender del servicio para decidir cuándo es el momento. Para este caso de uso particular, más libertad de desarrollador es mejor. Imposible anticipar todos los tipos de aplicaciones que se instalarán en contenedores.

+3

Sin embargo, sería bueno tener algunos controles de salud básicos incluidos. Algo como _http 200 ok en el puerto 80_ es tan común que podría valer la pena el esfuerzo.

Sería bueno tener un número 3 con "pilas incluidas" (¿me atrevo a decir todo lo anterior?)

Es decir, capacidad incorporada para el tipo de espera "el contenedor está activo", "el archivo está presente" y "el puerto está abierto" y luego una forma de permitir que las personas definan sus propias comprobaciones de "capa de aplicación".

3 obtiene mi voto

3 es más general 2 es más general 1. Todos preferirán 3, pero 2 o 1 será suficiente para algunos.

Vote por 3

Obviamente la tercera opción. Hay muchos casos solo en esta discusión. Entonces, la primera y la segunda opción serían geniales y muchas personas estarían felices, pero el tema permanecería abierto.

Vote por 3

Vote por 3

Vote por 3. Interesado en probarlo también.

Vote por 3.

Vote por 3. Interesado en probarlo también.

Vote por 3

3

Gracias a todos. Puede dejar de votar ahora, creo que el mensaje es claro.

Me interesaría recibir comentarios sobre el diseño que propuse en https://github.com/docker/compose/issues/374#issuecomment -126312313 - cualquier propuesta de diseño alternativo, junto con discusiones sobre sus fortalezas / debilidades.

El método de conveniencia wait_for_tcp sería útil, pero no me queda claro cómo tener un contenedor separado para hacer la verificación de estado es más fácil que hacerlo en el mismo contenedor como lo describen @olalonde y @mbentley arriba .

¿Qué hay de hacer algo como lo que está haciendo alexec en el complemento docker-maven-plugin? Él diseñó explícitamente su configuración para que fuera similar a docker-compse / fig y, hasta ahora, funcionó muy bien para mis proyectos.

healthChecks:
  pings:
     # check this URL for 200 OK
     - https://localhost:8446/info
     # check another URL with non-default time out, with a pattern, and non checking SSL certificates
     - url: https://localhost:8446/info
       timeout: 60000
       pattern: pattern that must be in the body of the return value
       sslVerify: false
  logPatterns:
     - pattern that must be in log file
     - pattern: another pattern with non-default timeout
       timeout: 30000

Fuente: https://github.com/alexec/docker-maven-plugin/blob/master/USAGE.md

Verificar que la conexión tcp esté activa no es suficiente para saber que la base de datos está iniciada. Creo que es mejor tener algún comando que sea verificar el estado de la base de datos.

@ceagan Así. Los comandos personalizados también serían útiles. p.ej

healthChecks:
  custom:
    # retry this command until it returns success exit code
    - cmd: psql --host=localhost --username=postgres
      sleep: 1s

También creo que checks sería mejor que healthChecks porque no es necesario recordar la convención del caso. También podría ser útil tener wait (cuántos segundos esperar antes de comenzar a ejecutar las verificaciones de estado), attempts (cuántas veces se debe verificar la salud antes de darse por vencido), retire (cuántos segundos esperar antes de renunciar por completo) parámetros.

esto va en la dirección en que lo resolvió el marco de maratón. dependencias básicas más chequeos de salud . como es muy popular para iniciar contenedores docker, vale la pena verificar sus opciones (tiempos de espera, intervalo, códigos de respuesta, etc.) para adoptarlos para componer.

Lo mismo aquí, opción 3.

+1

+1

+1

Me quedaré con un script de espera personalizado, sin embargo, sería muy bueno.

+1

+1

Opciones 3-healthChecks suena bien.

+1

+1

+1

+3.
Para agregar algunos otros pensamientos a la discusión, las opciones que @aanand propuso realmente dicen: el estado de los contenedores es responsabilidad de docker, no de docker-compose. Docker-compose podría implementar todos estos casos de uso de una manera limpia y elegante, si docker proporcionara información de estado. Pero Docker no lo hace. Parece ceñirse a la visión de contenedores sin estado inmediatos, lanzándose tan rápido que este tipo de problemas de sincronización no son importantes.
En mi caso, persigo la idea de poder elegir la mejor arquitectura para mis servicios, para cada caso. Por ejemplo, a veces me gustaría tener varias instancias de MariaDB, cada una de las cuales sirva para una sola aplicación. Otras veces, querría una sola instancia de MariaDB, que sirviera para múltiples aplicaciones. No quiero que Docker me diga qué es lo mejor o qué debería hacer en su lugar. Docker siempre parece tener este tipo de tentaciones;).
Creo que la mejor solución es convencer a Docker de que permita que los contenedores declaren metadatos arbitrarios sobre sí mismos y usar esa función para permitir que docker-compose averigüe si un contenedor se considera "listo" para que otros puedan confiar.
En cuanto al enfoque de "aplicaciones múltiples de base de datos única", me gustaría una definición como:

db:
  image: postgres:9.3
  ports:
    - "5432:5432"
app1:
  image: wordpress
  links:
    - db [WP]
app2:
  image: ghost
  links:
    - db [GST]

Docker Compose lanzaría "db" y preguntaría acerca de sus metadatos (relevantes para Docker Compose). A partir del archivo yml, sabe que "app1" espera que "db" esté "listo para wordpress" (lo que significa que no solo acepta conexiones, sino también los objetos necesarios).

No tengo una solución simple para resolver esta situación. Actualmente lo hago manualmente, en dos pasos: una imagen postgresql-bootstrap personalizada, en la que creo la base de datos y el usuario de la base de datos para acceder a ella; y una imagen personalizada liquibase-postgresql, para generar los objetos de la base de datos a partir de los DDL proporcionados (o extraídos) del contenedor de Wordpress. Solo entonces, puedo iniciar "app1".
Eso me obliga a separar los contenedores en grupos de "infraestructura" y "aplicaciones", si los contenedores de "infraestructura" sirven a diferentes aplicaciones.

Docker Compose quiere ser tan apátrida como el propio Docker. No sé si eso es posible si quiere ser realmente útil.

+1 para la opción 3

+1

+1 para la opción 3.

+1 para la opción 3

+1 para la opción 3

Este problema ha existido por un tiempo, ¿cuál es la solución?

@ bweston92 Creo que el estado es que @aanand propuso una solución anteriormente en este hilo y está buscando

cualquier propuesta de diseño alternativo, junto con discusiones sobre sus fortalezas / debilidades.

Personalmente, creo que la solución propuesta por @aanand tiene mucho sentido. Me parece muy explícito, a la vez que flexible. Cubriría mis necesidades de esperar a que se abra un puerto TCP o simplemente esperar una cantidad fija de tiempo.

Mi caso de uso es solo para pruebas. Mis pruebas fallarán si comienzan antes de que se haya creado la base de datos, así que cambié su comando a bash -c "sleep 2; python manage.py test --keepdb" , así:

db:
    image: postgres:9.5
test:
    build: .
    command: bash -c "sleep 2; python manage.py test --keepdb"
    volumes:
        - ./test_project:/app
    links:
        - db
        - selenium
    environment:
        - EXTERNAL_TEST_SERVER=http://testserver:8000/
        - SELENIUM_HOST=http://selenium:4444/wd/hub
selenium:
    image: selenium/standalone-chrome:2.48.2
    links:
        - testserver
testserver:
    build: .
    command: bash -c "sleep 5; python manage.py testserver 8000 --static"
    volumes:
        - ./test_project:/app
    ports:
      - "8000:8000"
    links:
        - db

para poder ejecutar docker-compose run test sin iniciar la base de datos primero y esperar.

Es difícil saber qué tema en estos días es el lugar "correcto" para votar por una nueva funcionalidad de composición de la ventana acoplable que permite declarar dependencias explícitas, pero considera que mi voto es fuerte. Con la nueva funcionalidad de red de Docker 1.9 y la inminente desaprobación de los enlaces de contenedor a su favor, ahora no hay una buena manera de asegurarse de que el contenedor A se inicie antes que el contenedor B, porque si usa una red definida por el usuario de Docker 1.9, puede ya no especifica enlaces de contenedor. Eso ... está roto.

Estoy de acuerdo. ¿Existe un cronograma para obtener la opción 3? Sería genial tener esto acelerado.

Vale la pena señalar que el orden de dependencia no soluciona este problema. Este problema también se aplica a los enlaces. En muchos casos, un contenedor se inicia lo suficientemente rápido como para que no note el problema, pero el problema sigue ahí.

Lo que se necesita para resolver este problema es una verificación de estado consciente de la aplicación. Una comprobación de estado es básicamente un bucle que reintenta alguna operación hasta que: la operación sea exitosa o se agote el tiempo de espera. En el caso de un servicio HTTP, esto podría estar realizando solicitudes http hasta que obtenga un código 2xx. Para una base de datos, podría conectarse y seleccionar de una tabla.

En cualquier caso, es una aplicación específica, por lo que debe ser definida por el desarrollador. Si tuviéramos que implementar la opción 3 de https://github.com/docker/compose/issues/374#issuecomment -135090543, aún necesitaría implementar esta lógica de verificación de estado.

Ya se ha mencionado varias veces en este número (https://github.com/docker/compose/issues/374#issuecomment-53036154, https://github.com/docker/compose/issues/374#issuecomment-71342299 ), pero para repetir, puede resolver este problema hoy haciendo que su aplicación sea resistente a fallas reintentando una conexión. Debe hacer esto de todos modos para cualquier sistema de producción.

Resulta que la funcionalidad para hacer que su aplicación sea resistente a fallas es efectivamente la misma lógica que una verificación de estado. Entonces, de cualquier manera, aún necesita implementar la misma lógica. La única diferencia sería dónde lo incluyes. Ahora mismo puede incluirlo en su aplicación o en un script de punto de entrada. Con el cambio propuesto, podrá definirlo en el archivo Redactar. De cualquier manera, aún tendrá que implementar una verificación de estado para cada servicio.

¿Existe una ventaja significativa al incluirlo en el archivo de redacción en lugar del script de punto de entrada? Quizás todavía esté en debate.

La gran _desventaja_ de ponerlo en el archivo Compose es que hace que up significativamente más lento.

Con la nueva red podemos hacer que up suceda en paralelo (como hacemos para detener, rm y escalar). Cada contenedor puede iniciarse a la vez, realizar una inicialización y luego esperar a que sus dependencias estén disponibles para continuar. Esto hace que iniciar un entorno sea muy rápido.

Si Compose tiene que esperar a que se complete una verificación de estado, el inicio es efectivamente secuencial. El inicio del contenedor y la inicialización de la aplicación no ocurren en paralelo y todo es más lento.

La mayoría de las aplicaciones tendrán controles de salud debido a que están detrás de LB, monitoreadas externamente, etc. Abrir una nueva no es difícil. Entonces, si redactar lo admite, entonces es una opción que la gente puede usar. No es obligatorio. En el mundo real, la gente tiene que lidiar con una variedad de aplicaciones y esta noción de que de repente todas las aplicaciones pueden hacerse inteligentes es poco realista y poco práctica. Y la lógica de envoltura en el punto de entrada es simplemente fea. Creo que ha habido suficiente demanda en la comunidad para una función y, como puede ver, la opción 3 obtuvo muchos votos.

Enviado desde mi iPhone

El 18 de noviembre de 2015, a las 11:01 a. M., Daniel Nephin [email protected] escribió:

Vale la pena señalar que el orden de dependencia no soluciona este problema. Este problema también se aplica a los enlaces. En muchos casos, un contenedor se inicia lo suficientemente rápido como para que no note el problema, pero el problema sigue ahí.

Lo que se necesita para resolver este problema es una verificación de estado consciente de la aplicación. Una comprobación de estado es básicamente un bucle que reintenta alguna operación hasta que: la operación sea exitosa o se agote el tiempo de espera. En el caso de un servicio HTTP, esto podría estar realizando solicitudes http hasta que obtenga un código 2xx. Para una base de datos, podría conectarse y seleccionar de una tabla.

En cualquier caso, es una aplicación específica, por lo que debe ser definida por el desarrollador. Si tuviéramos que implementar la opción 3 del # 374 (comentario), aún necesitaría implementar esta lógica de verificación de estado.

Ya se ha mencionado varias veces en este número (# 374 (comentario), # 374 (comentario)), pero para repetir, puede resolver este problema hoy haciendo que su aplicación sea resistente a fallas volviendo a intentar una conexión. Debe hacer esto de todos modos para cualquier sistema de producción.

Resulta que la funcionalidad para hacer que su aplicación sea resistente a fallas es efectivamente la misma lógica que una verificación de estado. Entonces, de cualquier manera, aún necesita implementar la misma lógica. La única diferencia sería dónde lo incluyes. Ahora mismo puede incluirlo en su aplicación o en un script de punto de entrada. Con el cambio propuesto, podrá definirlo en el archivo Redactar. De cualquier manera, aún tendrá que implementar una verificación de estado para cada servicio.

¿Existe una ventaja significativa al incluirlo en el archivo de redacción en lugar del script de punto de entrada? Quizás todavía esté en debate.

La gran desventaja de ponerlo en el archivo de Redacción es que se recupera significativamente más lento.

Con la nueva red podemos hacer que suceda en paralelo (como hacemos para detener, rm y escala). Cada contenedor puede iniciarse a la vez, realizar una inicialización y luego esperar a que sus dependencias estén disponibles para continuar. Esto hace que iniciar un entorno sea muy rápido.

Si Compose tiene que esperar a que se complete una verificación de estado, el inicio es efectivamente secuencial. El inicio del contenedor y la inicialización de la aplicación no ocurren en paralelo y todo es más lento.

-
Responda a este correo electrónico directamente o véalo en GitHub.

@dnephin El resultado final es que tiene los mismos scripts de envoltura para todos y cada uno de los servicios. Mi punto es que hay algunas cosas que son tan comunes (como HTTP 200 en 80 y 443 o TCP en 5432) que es una buena idea enviarlas con compose.

Claro, sería genial resolver todo esto en el nivel de la aplicación, pero en realidad solo tendrá control sobre su propia aplicación y no sobre todas las demás partes móviles como la base de datos, la caché o la cola de masajes.

Estoy de acuerdo con @mbdas y @jayfk , y solo

@delfuego : ¿puedes explicar cómo se están desaprobando los enlaces, y especialmente por qué se reemplazan? enlace a algunos documentos / ejemplos es suficiente

@ h17liner : sí, ¡interesante! Gracias

Si bien estoy de acuerdo con @dnephin en que

Ya se ha mencionado varias veces en este número, pero para reiterarlo, puede resolver este problema hoy haciendo que su aplicación sea resistente a fallas volviendo a intentar una conexión. Debe hacer esto de todos modos para cualquier sistema de producción.

No veo que esto sirva para algo como ejecutar pruebas. Si solo estoy probando si un modelo se guarda correctamente en la aplicación Django, no estoy seguro de cuánto sentido tiene agregar resistencia a la conexión de la base de datos.

@delfuego Creo que estabas en el lugar correcto originalmente (# 686) para ese número. Este problema no se trata de pedidos, se trata de un retraso artificial en el inicio (cuando ya existe un pedido). Si bien estas cosas están relacionadas, son cuestiones independientes.

No estoy de acuerdo con los enlaces que no son compatibles con las redes puente creadas por el usuario y que están documentados como obsoletos; en general, no hay pedidos. Por lo tanto, la opción 3 se encarga tanto del pedido como de cuándo comenzar a emitir.

Enviado desde mi iPhone

El 19 de noviembre de 2015, a las 8:14 a. M., Daniel Nephin [email protected] escribió:

@delfuego Creo que estabas en el lugar correcto originalmente (# 686) para ese número. Este problema no se trata de pedidos, se trata de un retraso artificial en el inicio (cuando ya existe un pedido). Si bien estas cosas están relacionadas, son cuestiones independientes.

-
Responda a este correo electrónico directamente o véalo en GitHub.

Me gustaría proponer la opción 4 (algunos pueden decir que en realidad es una variación de 3)
El contenedor no está listo hasta que todos los comandos de inicio terminen con 0 código de salida. Debe ser posible definir estos comandos de inicio en el archivo yml para cada contenedor. Estos comandos se ejecutan como lo haría con "docker exec" contra el contenedor en ejecución. Piense en los métodos setUp () y tearDown () en las pruebas unitarias clásicas. Sí, también podríamos tener comandos de "apagado".
Obviamente, el siguiente contenedor de la jerarquía no se lanza hasta que todos los contenedores de los que depende están listos.
PD Gracias por el gran DockerCon.Eu 2015

Una directiva HEALTHCHECK es mucho más flexible (es decir, se puede usar en cualquier momento posterior) y útil. La configuración debe realizarse dentro del script CMD o (mejor aún) ENTRYPOINT , desmontando mediante el manejo de señales de proceso.

Creo que el meollo del problema aquí es que la gente quiere un solo comando docker-compose up para abrir una pila y todo funciona mágicamente.

En base a todos los comentarios, es evidente que existen muchas soluciones para diferentes casos de uso, pero ninguna "talla única".

Puede ejecutar tareas de "inicialización" con bastante facilidad mediante la ejecución de varios comandos de docker-compose, y creo que este enfoque es el más genérico y flexible.

Por ejemplo, ejecuto un libro de jugadas de Ansible en un contenedor de "agente" con una sola tarea que espera a que el contenedor de mi base de datos (MySQL) se ejecute en el puerto 3306. Este contenedor de "agente" está vinculado al contenedor de mi "base de datos" de forma automática lo inicia cuando se ejecuta lo siguiente:

$ docker-compose run --rm agent
Creating db_1

PLAY [Probe Host] *************************************************************

TASK: [Set facts] *************************************************************
ok: [localhost]

TASK: [Message] ***************************************************************
ok: [localhost] => {
    "msg": "Probing db:3306 with delay=0s and timeout=180s"
}

TASK: [Waiting for host to respond...] ****************************************
ok: [localhost -> 127.0.0.1]

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0

Después de lo cual puedo ejecutar docker-compose up sabiendo que el contenedor db está completamente operativo.

Aquí hay un archivo docker-compose.yml simple que admite esto:

...
...
db:
  image: mysql
  hostname: db
  expose:
    - "3306"
  environment:
    MYSQL_DATABASE: xxx
    MYSQL_USER: xxx
    MYSQL_PASSWORD: xxx
    MYSQL_ROOT_PASSWORD: xxx

agent:
  image: cloudhotspot/ansible
  links:
    - db
  volumes:
    - ../../ansible/probe:/ansible
  environment:
    PROBE_HOST: "db"
    PROBE_PORT: "3306"

El contenedor "agente" ejecuta un libro de jugadas llamado site.yml en el volumen montado /ansible que se muestra a continuación:

- name: Probe Host
  hosts: localhost
  connection: local
  gather_facts: no
  tasks: 
    - name: Set facts
      set_fact: 
        probe_host: "{{ lookup('env','PROBE_HOST') }}"
        probe_port: "{{ lookup('env','PROBE_PORT') }}"
        probe_delay: "{{ lookup('env','PROBE_DELAY') | default(0, true) }}"
        probe_timeout: "{{ lookup('env','PROBE_TIMEOUT') | default (180, true) }}"
    - name: Message
      debug: msg="Probing {{ probe_host }}:{{ probe_port }} with delay={{ probe_delay }}s and timeout={{ probe_timeout}}s"
    - name: Waiting for host to respond...
      local_action: >
        wait_for host={{ probe_host }}
        port={{ probe_port }}
        delay={{ probe_delay }}
        timeout={{ probe_timeout }}
      sudo: false

Una solución para el objetivo único de docker-compose up podría ser introducir una función de "flujo de trabajo" para la composición de la ventana acoplable e incluir un archivo de especificaciones de flujo de trabajo opcional que permita escenarios de orquestación más complejos y controlados al especificar uno o más comandos de composición de la ventana acoplable como "tareas" que deben ejecutarse:

# The default workflow, specified tasks will be run before docker-compose up
# The "up" task is implicit and automatically invoked for the default workflow
# The "up" task is explicit for custom workflows as some workflows may not want docker-compose up
default:
  tasks:
    - run --rm agent
    - up

# Custom workflows that can be invoked via a new docker-compose command option
# This example:
# 1. Runs agent container that waits until database container is up on port 3306
# 2. Runs Django database migrations from app container
# 3. Runs Django collect static task from app container
# 4. Runs test container that runs acceptance tests against linked app container
# Does not execute a docker-compose up afterwards

test:
  tasks:
    - run --rm agent 
    - run --rm app manage.py migrate
    - run --rm app manage.py collectstatic --noinput
    - run --rm test

Hoy logro lo anterior usando Makefiles, que brindan una capacidad de orden superior para definir mis propios flujos de trabajo para diferentes escenarios.

Sería estupendo si se pudiera introducir una función de "flujo de trabajo" o similar en la ventana acoplable componer, lo que proporcionaría una solución muy genérica y flexible para este problema en particular y muchos más.

De acuerdo, el problema es que la gente espera que docker-compose sea suficiente para las implementaciones de producción. Personalmente, creo que pasará mucho tiempo antes de que eso sea factible y Helm parece estar mucho más cerca de ese objetivo.

@olalonde , seguro que nos encantaría componer para estar listos para la producción ... pero lo TOMAREMOS compatible con la funcionalidad importante y existente que, dada la obsolescencia de los enlaces de contenedores, desaparecerá a menos que se repita al nuevo usuario -Modelo de redes creadas. (Nuevamente, tal vez esta solicitud no esté perfectamente alineada con este problema específico; no me queda claro si solo obtener el pedido de inicio del contenedor "pertenece" aquí o en el número 686 ...)

Con una directiva HEALTCHECK Docker, la funcionalidad depends_on puede esperar al inicio del contenedor (sin verificación de estado) o al script de verificación de estado para salir correctamente (código de salida 0 ). Esto es lo más flexible posible (puede definir una lógica arbitraria) y mantiene la lógica de verificación de estado donde pertenece (dentro del contenedor que está marcado).

@delfuego incluso para desarrollo y pruebas, esta funcionalidad sería útil. Personalmente, quiero poder hacer docker-compose run test y hacer que funcione sin tener que abrir los servicios de antemano y esperar manualmente. Aunque esto es posible, solo hace que sea un poco más doloroso comenzar con el proyecto y agrega más formas en las que las pruebas pueden fallar.

+1

Creo que la resolución requiere una vía intermedia: componer nunca podrá dar cuenta de todas las diferentes formas en que las aplicaciones pueden considerarse disponibles o no. La idea de un chequeo de salud significará cosas diferentes para diferentes personas, y puede que no sea tan simple como "¿funciona o no?". En producción, podría derribar un contenedor si mostraba tiempos de respuesta inusualmente largos, incluso si pasaba las comprobaciones HTTP.

Por lo tanto, creo que el soporte básico para respuestas HTTP, puertos abiertos, archivos creados o líneas de registro emitidas debería ser suficiente para el desarrollo. Cualquier cosa más avanzada que eso se convierte casi de inmediato en una aplicación específica. También me gusta la idea de alentar a los desarrolladores a hacer que las partes individuales de sus pilas de aplicaciones sean más sólidas.

@pugnascotia gracias, es un comentario constructivo y un enfoque razonable (¿"lo mejor de ambos mundos"?)

Las soluciones discutidas actualmente no parecen abordar realmente el problema _inicialmente informado_ que es mucho más simple ... que NO es esperar a que un servicio esté disponible, sino esperar a que un servicio SALGA.

Tengo un caso de uso en el que tengo dos contenedores que exponen los mismos puertos. El primero se ejecuta durante 15-60 segundos, luego sale. Entonces debería comenzar el segundo servicio. No hay una forma (obvia?) De hacer esto en componer hoy, ya que detectará el conflicto del puerto y saldrá; ni siquiera 'reiniciar: siempre' es una solución.

Sí, Compose no está diseñado para ese caso de uso. Compose se centra en los entornos de ejecución, no en la creación de canalizaciones. No creo que de eso se trate el problema original informado.

Ha habido algunas solicitudes de funciones más orientadas a la compilación, pero no creo que tengan sentido para componer. Las dos funciones son muy diferentes y tratar de que encajen en el mismo formato de configuración puede generar muchas confusiones y una mala experiencia de usuario.

@ewindisch, su caso de uso puede generalizarse para ejecutar una cadena de trabajos por lotes. Compose es útil para ese caso (a pesar de no estar diseñado para él) porque mantiene dependencias entre servicios, por ejemplo, esas cadenas. Pero no maneja la secuenciación y, en mi humilde opinión, no debería porque Compose está _fuera_ de los contenedores y no tiene idea de qué proceso _dentro_ un contenedor va a hacer.

Esta parte de la documentación de Compose cubre la pregunta de por qué Compose no tiene esta capacidad:

https://docs.docker.com/compose/faq/#how -do-i-get-compose-to-wait-for-my-database-to-be-ready-before-start-my-application

Sin embargo, estas páginas no mencionan el problema en absoluto:

https://docs.docker.com/compose/django/
https://docs.docker.com/compose/rails/
https://docs.docker.com/compose/wordpress/

Como mínimo, estas páginas deben incluir un reconocimiento de que Compose no esperará a que un contenedor de base de datos esté listo. También podrían incluir ejemplos de formas de abordarlo.

@ewindisch En realidad, eso es exactamente lo que estaba proponiendo en https://github.com/docker/compose/issues/374#issuecomment -126312313 - con la hipótesis de que resolver _that_ problem también brinda a los usuarios las herramientas para resolver el problema de pedidos de inicio ( si no el problema de la resiliencia a largo plazo).

Todavía estoy interesado en explorar ese espacio de soluciones, si alguien más lo está.

Yo soy.

Yo también.

+1

+3

+1

+1 para implementar la solución https://github.com/docker/compose/issues/374#issuecomment -126312313.

¡Votaciones masivas!
Actualmente, esto afecta el uso de herramientas que se ejecutan en un contenedor, pero que dependen de los eventos de la ventana acoplable (por ejemplo, jwilder / nginx-proxy). La forma en que lo hago es simplemente docker-componer el oyente manualmente y ejecutar todos los demás contenedores después (lo que estropea toda la belleza de docker-compose como un único punto de entrada).

@meetmatt, ¿ ha intentado ejecutar jwilder / nginx-proxy después? El orden de inicio no debería importar por eso, recogerá los contenedores existentes (en ejecución) cuando se inicie

+1

+1

Realmente me gustaría ver una solución transparente basada en canales. Básicamente como libchan. De esa manera, si consulto una base de datos, la solicitud se almacena en búfer hasta que la base de datos esté lista.

Realmente no creo que el orden de carga sea una solución suficiente en sistemas distribuidos. ¿Qué pasa si, por ejemplo, necesita reiniciar su base de datos pero otros servicios pueden fallar como resultado? Una solución real también manejaría este caso de uso.

Cuente conmigo, ya que la predictibilidad de la canalización de ejecución es clave para lo que hacemos en el trabajo. +1

+1

Debo estar perdiendo algo.

¿Por qué nadie aboga por agregar un "esperar hasta" en la ejecución de la ventana acoplable (el motor de la ventana acoplable en sí)? En todos los casos que se me ocurren, el contenedor dependiente sabe cuándo está "listo", pero la ventana acoplable no lo respeta.

En el caso original (mysql cargando un gran conjunto de datos y al aire libre), el contenedor mysql puede regresar o indicar cuando está listo y el contenedor alfresco no se iniciará hasta entonces.

Me gustaría ejecutar una lógica arbitraria y enviar una señal a la ventana acoplable cuando esté listo, según lo decida yo (por ejemplo, cuando aparece un determinado mensaje en el registro -> señal CONTAINER_UP).

net: "container:[name or id]" ¿por qué no ordenar las puestas en marcha de mis contenedores? Tuve que eliminar links porque quedará obsoleto y quiero que toda la pila use net: "host" red. Desafortunadamente, esto no está permitido con links . ¿Hay otra forma de cambiar el orden de arranque de los contenedores o tengo que compartir volúmenes inútiles entre ellos?

Actualizar:

Acabo de reordenar con volúmenes inútiles en lugar de links :

base:
  build: ./base
  net: "host"
  volumes:
    - /root/lemp_base
phpmyadmin:
  build: ./phpmyadmin
  net: "host"
  volumes_from:
    - base
  volumes:
    - /root/lemp_phpmyadmin
ffmpeg:
  build: ./ffmpeg
  net: "host"
  volumes_from:
    - phpmyadmin
  volumes:
    - /root/lemp_ffmpeg
mariadb:
  build: ./mariadb
  net: "host"
  volumes_from:
    - ffmpeg
  volumes:
    - /root/lemp_mariadb
php:
  build: ./php
  net: "host"
  volumes_from:
    - mariadb
  volumes:
    - /root/lemp_php
nginx:
  build: ./nginx
  net: "host"
  volumes_from:
    - php
  volumes:
    - /root/lemp_nginx

(Borré otros volúmenes compartidos de la pila y otra información como container_name, ports para que parezcan simples).

Si quiero usar con net: "container:base , tengo un mensaje de error en el comando docker-compose build .

ERROR: Service "mariadb" is trying to use the network of "lemp_base", which is not the name of a service or container.

Lo que no me gusta de esta solución es que todos los demás contenedores obtendrán los archivos del servidor web en la carpeta /var/www de base .

EDITAR:
Por alguna razón, esta pila elimina toda la carpeta /var/www al inicio.

Mi humilde opinión es que cualquier mecanismo que termine con Docker Compose conociendo la dependencia entre contenedores va en contra de la Separación de preocupaciones. Docker Compose es responsable de ejecutar los contenedores A y B. Los contenedores A y B son responsables de su propio servicio. Si B depende de A para funcionar correctamente, es responsabilidad de B esperar a que A esté en condiciones de trabajo. Como se ha dicho en la discusión, esto se puede hacer a través del tiempo de espera, reintento o lo que sea, pero este es el problema de B, no Docker Compose ni A. SoC es de suma importancia para la independencia del servicio y el escalado adecuado.

¿Hay algún trabajo en tu idea 3 @aanand ? Sería bueno saber si hay algún progreso, sonaba como un comienzo prometedor que ayudaría a algunos casos de uso muy comunes, incluso si no es una solución perfecta.

+1

+1

Tal vez me equivoque, pero args: buildno: puede ordenar los contenedores en docker-compose.yml versión 2?

Tiendo a estar de acuerdo en que esta es una preocupación que no pertenece a Redactar. @jwilder 's excelente Dockerize acaba de apoyo tiene que esperar a que los contenedores dependientes y se puede especificar el protocolo / puerto que usted está esperando en. Sugeriría que esto se adapte a la mayoría de los casos de uso descritos aquí:

api:
  build: .
  ports:
   - "8000:80"
  expose:
  - "80"

test:
  build: test
  command: dockerize -wait http://api:80 -wait tcp://db:5432 somecommand -some arg -another arg2
  links:
    - api:api

Idealmente, utilizamos la API de eventos de Docker para detectar esto automáticamente, pero eso significaría que todos los contenedores también necesitarían acceso al tiempo de ejecución de Docker, lo que probablemente no sea factible / algo que nos gustaría.

Creo que la espera debería hacerse fuera de la redacción. En mi dev Voy a utilizar híbrido de lo sugerido y @mefellows timercheck.io páginas de estado. Creo que eso me dará exactamente lo que necesito sin tener que usar RabbitMQ o algo similar.

Estamos usando un punto de entrada de script de shell que espera el puerto abierto, con un tiempo de espera de 15 segundos:

#!/usr/bin/env bash

# wait for db to come up before starting tests, as shown in https://github.com/docker/compose/issues/374#issuecomment-126312313
# uses bash instead of netcat, because netcat is less likely to be installed
# strategy from http://superuser.com/a/806331/98716
set -e

echoerr() { echo "$@" 1>&2; }

echoerr wait-for-db: waiting for db:5432

timeout 15 bash <<EOT
while ! (echo > /dev/tcp/db/5432) >/dev/null 2>&1;
    do sleep 1;
done;
EOT
RESULT=$?

if [ $RESULT -eq 0 ]; then
  # sleep another second for so that we don't get a "the database system is starting up" error
  sleep 1
  echoerr wait-for-db: done
else
  echoerr wait-for-db: timeout out after 15 seconds waiting for db:5432
fi

exec "$@"

Esto debería resolverse en el próximo (y aparentemente inminente con la actualización de los documentos) depend_on , ¿sí?

No depends_on solo está ordenando. Para retrasar realmente el inicio de otro contenedor, debería haber alguna forma de detectar cuándo un proceso ha terminado de inicializarse.

Ah, gracias por la aclaración. =)

He escrito una utilidad de línea de comandos pura de bash llamada wait-for-it que se puede incluir en las implementaciones de Docker para ayudar a sincronizar las implementaciones de servicios.

Para mí, no es una buena idea codificar una colección arbitraria de "comprobaciones de disponibilidad". Existen numerosas situaciones que son específicas de un tipo de implementación y nunca se pueden cubrir todas. Solo como ejemplo, en mi aplicación de contenedores múltiples necesito esperar a que aparezca un determinado mensaje de registro en un determinado archivo de registro; solo entonces el servicio de contenedor estará listo.
En cambio, lo que se necesita es un SPI que pueda implementar. Si Docker proporciona algunas implementaciones de ejemplo para los casos de uso más frecuentes (por ejemplo, conexión TCP), está bien. Pero tiene que haber una manera de conectar mi propia funcionalidad y hacer que Docker la llame.
Docker Compose es bastante inútil para mí como un producto completo, si no puedo hacer que mis contenedores funcionen de manera confiable. Por lo tanto, se necesita un "SPI de disponibilidad de servicio de contenedores" estable y uniforme. Y "listo" no debería ser un valor booleano, ya que posiblemente haya más niveles de preparación (como: "ahora puedes leer" y "ahora puedes escribir").

@realulim Buena reseña. Estoy totalmente de acuerdo con la idea de dejarnos definir qué significa el estado "listo" de un servicio a través de complementos. También creo que es una buena idea tener un complemento predeterminado para el complemento que solo verifique que un servicio esté escuchando una conexión http / tcp. Eso cubriría la mayoría de los casos allí mismo.

Esto es lo que se me ocurrió, en el archivo de punto de entrada;

until netcat -z -w 2 database 5432; do sleep 1; done
# do the job here, database host on port 5432 accepts connections

@kulbida ,
Hago algo muy similar con MySQL. "base de datos" en este caso es un enlace en un archivo de redacción.

if [[ "$APP_ENV" == "local" ]]; then
    while ! mysqladmin ping -h database --silent; do
        sleep 1
    done
    # Load in the schema or whatever else is needed here.
fi

Ha habido algunos comentarios en este hilo que afirman que el orden de inicio es solo un subconjunto de la recuperación de errores a nivel de aplicación, que su aplicación debería manejar de todos modos. Me gustaría ofrecer un ejemplo para ilustrar dónde no siempre es así. Considere si algunos servicios dependen de una base de datos agrupada y cada vez que se pierde un quórum debido a un bloqueo, etc., _no_ desea volver a intentarlo automáticamente desde la aplicación. Este podría ser el caso, por ejemplo, si la recuperación de la base de datos requiere algunos pasos manuales y necesita que los servicios permanezcan inequívocamente inactivos hasta que se realicen esos pasos.

Ahora, la lógica de manejo de errores de la aplicación puede ser bastante diferente de la lógica de inicio:

  • Si la base de datos está inactiva porque recién estamos iniciando, espere a que esté disponible.
  • Si la base de datos está inactiva porque se bloqueó, registre un error crítico y muera.

Puede que no sea el escenario más común, pero ocasionalmente ve este patrón. En este caso, la agrupación en clústeres se utiliza para resolver el problema de "la red no es confiable" en el caso general, lo que cambia algunas de las expectativas sobre las condiciones de error que se deben reintentar en la aplicación. Los bloqueos del clúster pueden ser lo suficientemente raros, y reiniciarlos automáticamente puede ser lo suficientemente riesgoso, por lo que es preferible reiniciar los servicios manualmente a reintentarlos en la aplicación. Sospecho que también hay otros escenarios que podrían desafiar las suposiciones sobre cuándo reintentar.

De manera más general, afirmo que el orden de inicio y el manejo de errores no siempre son equivalentes, y que es apropiado que un marco proporcione características (opcionales) para administrar el orden de inicio. Sin embargo, me pregunto si esto pertenece a docker-engine, en lugar de componer. Podría ser necesario cada vez que se inicie la ventana acoplable, independientemente de si se utiliza componer.

Hay una discusión que comienza en el repositorio del motor de Docker en la propuesta https://github.com/docker/docker/issues/21142 para agregar soporte para la verificación de estado. Una vez que este soporte esté disponible, Compose podrá proporcionar una forma de configurarlo y utilizarlo para un inicio diferido.

¿Qué tal usar el sistema de archivos para verificar la existencia de un archivo?

ready_on: /tmp/this_container_is_up_and_ready

De esa manera, depende del desarrollador del contenedor decidir cuándo las cosas están ARRIBA, pero componer puede esperar hasta que el contenedor se declare listo. Es una convención explícita, pero podría agregarse fácilmente como una capa adicional a las imágenes que no tienen ese comportamiento.

El soporte integrado para controles de estado será bueno; Mientras tanto, aquí está el truco que obtuve trabajando en mi configuración local de docker-compose:

    nginx:
        image: nginx:latest
        command: /bin/bash -c "sleep 2 && echo starting && nginx -g 'daemon off;'"
        ...

(En producción, mi aplicación es proxy de algunos servidores ascendentes que ya se están ejecutando usando proxy_pass ; en desarrollo y prueba locales, comienzo instancias de docker de estos, y nginx necesita esperar un poco para que comiencen, de lo contrario se bloquea y muere. La cosa daemon off mantiene nginx en un solo proceso, de lo contrario, Docker detendrá el contenedor tan pronto como el proceso padre genere su demonio secundario).

Solo para agregar mis dos centavos, si está utilizando la herramienta de construcción ANT, viene con soporte incorporado para retrasar la ejecución hasta que se abra un determinado socket.

Nuestro servidor Jenkins CI hace girar los contenedores del proyecto con Docker Compose y luego ejecuta ANT desde el contenedor principal, así:

docker-compose up -d
docker exec -it projectx-fpm-jenkins ant -f /var/www/projectX/build.xml

Esta es la pieza de configuración relevante del archivo docker-compose.yml. Tenga en cuenta que, como se mencionó anteriormente, hacer que fpm dependa de mysql no es suficiente para garantizar que el servicio MySQL estará listo cuando realmente se necesite.

version: '2'
services:
  nginx:
    build: ./docker/nginx
    depends_on:
      - fpm
  fpm:
    build: ./docker/fpm
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=projectx
      - MYSQL_DATABASE=projectx

Pero puede esperarlo durante la tarea ANT:

<!-- other targets... -->

<target name="setup db">
    <!-- wait until the 3306 TCP port in the "mysql" host is open -->
    <waitfor>
        <socket server="mysql" port="3306"/>
    </waitfor>

    <exec executable="php">
        <arg value="${consoledir}/console"/>
        <arg value="doctrine:database:create"/>
        <arg value="--no-interaction"/>
    </exec>
</target>

@kulbida Eso hizo el truco, gracias. Algo un poco más rápido:

while ! nc -w 1 -z db 5432; do sleep 0.1; done

_depends_on_ podría resolver el problema.
De la documentación de docker-compose .
Expresar dependencia entre servicios, que tiene dos efectos:

  1. docker-compose up iniciará los servicios en orden de dependencia. En el siguiente ejemplo, db y redis se iniciarán antes que web.
  2. docker-compose up SERVICE incluirá automáticamente las dependencias de SERVICE. En el siguiente ejemplo, docker-compose up web también creará e iniciará db y redis.

versión 2'
servicios:
web:
construir: .
depende de:
- db
- redis
redis:
imagen: redis
db:
imagen: postgres

@alexch : en una prueba de rendimiento del lado del cliente (

@syamsathyan depends_on no parece ayudar.

@skorokithakis , @kulbida esta es una buena solución. Desafortunadamente, netcat no está disponible de forma predeterminada en ninguno de los servicios que necesito para conectarme a mi base de datos (incluido postgres ). ¿Conoce algún método alternativo?

@nottrobin Me temo que no, lo acabo de instalar en mi imagen: /

@nottrobin mi equipo está trabajando en esto, ¡se lo informará en uno o dos días!

Para aquellos que tienen un bash reciente, existe una solución sin netcat (inspirada en: http://stackoverflow.com/a/19866239/1581069):

while ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/db/5432'; do sleep 0.1; done

o versión menos detallada:

while ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/db/5432' >/dev/null 2>/dev/null; do sleep 0.1; done

@typekpb que funciona perfectamente. ¡Gracias!

Ahora que el soporte de HEALTHCHECK se fusionó en sentido ascendente según https://github.com/docker/docker/pull/23218, esto se puede considerar para determinar cuándo un contenedor está en buen estado antes de comenzar el siguiente en el pedido. La mitad del rompecabezas resuelto :)

Ahora que el soporte de HEALTHCHECK se fusionó en sentido ascendente según la ventana acoplable / ventana acoplable # 23218, esto se puede considerar para determinar cuándo un contenedor está en buen estado antes de comenzar el siguiente en el orden. La mitad del rompecabezas resuelto :)

Se ve bien. ¿Cómo implementarlo en docker-compose.yml ?

Se ve bien. ¿Cómo implementarlo en docker-compose.yml?

La otra pieza del rompecabezas será tener docker-compose watch en busca de contenedores saludables, y usar algo como la sintaxis depends_on mencionada más adelante en este número. Requerirá parches para docker-compose para que todo funcione.

También tenga en cuenta que la función de verificación de estado en Docker no se ha publicado actualmente, por lo que probablemente deba alinearse con un ciclo de lanzamiento de Docker / Docker Compose.

Escribí una biblioteca js que tiene un método .waitForPort() . Tal como se mencionó anteriormente, es posible que esto no funcione para todas las situaciones, pero podría funcionar bien para la mayoría de los casos de uso.
Ver mi blog .

La fusión de HEALTHCHECK es una gran noticia.

Mientras tanto, este documento describe el problema y algunas soluciones.

@pablofmorales No, porque depends_on solo verifica que el contenedor esté arriba.

Algunos demonios necesitan más tiempo para iniciarse y comenzar a escuchar sus puertos y direcciones asignados, sobre todo MySQL.

Sigo pensando que una declaración "READY_ON" sigue siendo la mejor en general. Deja la decisión sobre cuándo algo está listo para el contenedor en sí, independientemente de la imagen, es explícito al participar y la funcionalidad de ruta de recursos (dentro del contenedor) en la API remota de Docker garantiza los cambios mínimos necesarios.

El comportamiento de cuando un contenedor está "arriba" es el único efecto que esto debería tener. Solo informará como "activo" cuando exista el archivo READY_ON.

Creo que este es el 90% del comportamiento que todos han estado discutiendo. Creo que "chequeo de salud" aquí se está fusionando como 2 eventos diferentes, pero tratando de convertirlo en uno. Uno está "listo" para la cadena de eventos al poner en marcha la infraestructura, el otro es "saludable" para que la infraestructura se pueda mantener.

"ready" es un lugar totalmente apropiado para que Docker esté ayudando. En cuanto a "salud", es tan variado en términos de sistemas, creo que depende del contenedor lidiar con eso.

Para una mejor alternativa a healthcheck, es posible que desee buscar algo como containerpilot que cubra no solo la salud, sino también el descubrimiento y monitoreo de servicios. https://github.com/joyent/containerpilot

Sí, esta es una distinción precisa e importante. Sin embargo, ¿cómo escribirán los contenedores ese archivo sin que las imágenes se vuelvan significativamente más complicadas? Me parece que requeriría un script de envoltura para cada contenedor que quiera usar esto.

Bueno, tendrías que iniciar un script para inicializar la instancia de todos modos ... lo último que debe hacer el script es tocar un archivo. Para mí, eso parece mucho más fácil que intentar ejecutar un ejecutivo en una máquina remota para hacer una verificación de estado. Al menos con un archivo táctil, se puede ver, etc., completamente a través de API de forma pasiva sin necesidad de ingresar al contexto del contenedor.

Estoy de acuerdo, pero muchos contenedores no usan un script, simplemente instalan un servicio como Postgres o Redis y dejan que se inicie sin verlo.

En mi caso, estoy usando Kong API Gateway

Antes de ejecutar el contenedor kong, solo verifico si Cassandra está trabajando con este script

while true; do
    CHECK=`kong-database/check`
    if [[ $CHECK =~ "system.dateof" ]]; then
        break
    fi
    sleep 1;
done

el archivo de verificación contiene esto

#!/bin/bash
docker cp cassandra-checker kong-database:/root/
docker exec -i kong-database cqlsh -f /root/cassandra-checker

cassandra-checker es solo una consulta simple

SELECT dateof(now()) FROM system.local ;

Claro, pero la alternativa es una verificación de estado, que requiere un script que tendrías que escribir de todos modos, por lo que no hay diferencia de gastos generales. También es una opción explícita, lo que significa que está indicando que desea este comportamiento. En cuanto a algo que no ejecuta un script, siempre puede tener una verificación de ruta ready_on para un archivo pid o un socket Unix; que no requeriría un guión.

Eso es cierto, tienes razón.

Verificar la existencia de un archivo puede estar bien en muchos casos, pero forzar a los contenedores a usar un script de inicio cuando de otra manera no lo necesitarían es una molestia. ¿Por qué no puede haber también controles para otras condiciones muy simples? Sería especialmente útil esperar hasta que el proceso esté escuchando en un puerto tcp en particular.

Esta idea es opcional, por lo que no hay que forzar nada. De hecho, estás siendo explícito al decir lo que se debe esperar.

La escucha de un puerto tcp puede no ser suficiente para saber cuándo se ha inicializado un contenedor, ya que puede haber un montón de datos de configuración que deben ejecutarse. Demonios, si se conecta a un contenedor de postgres demasiado rápido, incluso a través de tcp, obtendrá un error que indica que la base de datos aún no está lista.

Si te entiendo correctamente, es "opt-in, o de lo contrario no puedes usar esta función". Ergo, si necesito esta función y mi aplicación no usa un archivo pid, me veo obligado a usar un script de inicio.

Para MySQL (el caso del OP), una vez que está escuchando, está listo. Se toman muchas molestias para asegurarse de que eso sea cierto, probablemente en casos como este. Mi opinión es que probablemente haya una lista corta de condiciones que podrían enumerarse de modo que pueda "optar por participar" configurando una verificación preparada contra cualquiera de esas condiciones. No veo ninguna razón por la que deba hacerse de una sola manera.

Para mysql, una vez que está escuchando, no está listo. En el caso de un nodo simple, estará listo, pero si tiene más de un nodo, ciertamente no estará listo todavía. Entiendo lo que quieres decir con "una y sólo una forma", pero creo que, como abstracción básica, es simplemente perfecto. Lo veo más como un lugar donde puede aplicar cualquier herramienta que desee. Demonios, su script podría incluso comunicarse con servicios externos y hacer que verifiquen el contenedor, en cuyo caso sus servicios externos podrían indicarle a su agente de contenedor que escriba el archivo. Flexibilidad ftw.

Si intenta algo en esta lista de "condiciones", SIEMPRE habrá un caso en el que no funcione. Sin embargo, tocar un archivo siempre funcionará, ya que la imagen sabe cuándo cree que está listo (oh, tengo que esperar en otros hosts, necesito que se descarguen los archivos, necesito asegurarme de que $ external_service también esté disponible, amplío correctamente, pero por alguna razón no tengo los permisos correctos para la base de datos, ¿por qué esta imagen es de solo lectura ... etc.

Este tipo de scripts ya existen por todas partes ... demonios, ya ha sido necesario escribir estos scripts porque no hemos tenido una funcionalidad como esta antes. Por lo tanto, introducir un script como este es mínimo, ya que es probable que ya exista un script.

Otro caso probable es que haga que algo como chef o ansible se ejecute contra ese host y luego escriba el archivo.

Si se trata de una verificación del lado de Docker, entonces algo como;

UPCHECK --port=7474 --interval=0.5s --response="Please log in"

Para que conste, creo que la solución de archivos tiene mucho mérito, pero también introduce complejidad.
El 80% del tiempo, la verificación de la respuesta tcp funcionaría bien.

bueno ... supongo:

UPCHECK --file=/tmp/container_is_ready --interval=0.5s --timeout=2m

Es lo mismo.

De hecho, estoy trabajando en una reimplementación de docker-compose que agrega funcionalidad para esperar condiciones específicas. Utiliza libcompose (por lo que no tengo que reconstruir la interacción de la ventana acoplable) y agrega un montón de comandos de configuración para esto. Compruébalo aquí: https://github.com/dansteen/controlled-compose

Tenga en cuenta que el código está terminado, pero estoy esperando que se resuelvan un par de problemas anteriores antes de que pueda usarse realmente.

Goss se puede usar como un calce bastante flexible para retrasar el inicio del contenedor, escribí una publicación de blog que explica cómo esto se puede lograr con un pequeño cambio en su imagen aquí:

Kubernetes tiene el concepto de contenedores de

+1

Creo que es mejor dejar que el servicio que está exponiendo en un contenedor decida si está listo o es capaz de exponer su servicio.

Por ejemplo, una aplicación PHP podría depender de la conexión de MySQL. Entonces, en el ENTRYPOINT del contenedor PHP, escribí algo como esto.

#!/bin/bash
cat << EOF > /tmp/wait_for_mysql.php
<?php
\$connected = false;
while(!\$connected) {
    try{
        \$dbh = new pdo( 
            'mysql:host=mysql:3306;dbname=db_name', 'db_user', 'db_pass',
            array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
        );
        \$connected = true;
    }
    catch(PDOException \$ex){
        error_log("Could not connect to MySQL");
        error_log(\$ex->getMessage());
        error_log("Waiting for MySQL Connection.");
        sleep(5);
    }
}
EOF
php /tmp/wait_for_mysql.php
# Rest of entry point bootstrapping

De esta manera, puedo agregar cualquier lógica para asegurar que las dependencias del servicio que estoy exponiendo, es decir, php, se hayan resuelto.

Nabin Nepal schrieb:

Creo que es mejor dejar que el servicio que está exponiendo en un contenedor decida si está listo o es capaz de exponer su servicio.

Por supuesto, puede codificar este comportamiento en cada contenedor que use su
Contenedor MySql. Pero si algo en su servicio MySql cambia, entonces está
cambiar todos los contenedores dependientes, por no hablar de la codificación repetitiva
necesario en cada uno. Esto no es SECO, no hay contrato estable y por lo tanto
conducen a sistemas frágiles.

Desde el punto de vista de la artesanía del software, debería haber algún tipo de
"Container Readiness SPI", que el desarrollador del contenedor puede implementar. En
en el otro lado debe haber una "API de preparación de contenedores", que el
los servicios pueden depender.

Ulrich

@realulim Acepto que cualquier cambio en el contenedor MySQL debe replicarse o propagarse a todos los contenedores afectados o vinculados.

Sin embargo, si el cambio se trata de parámetros como DB_HOST, DB_NAME, DB_USER y DB_PASSWORD. Estos pueden pasarse como ARG (argumento) y ser compartidos por todos los contenedores relacionados. Si usa docker-compose.yml file, el cambio ocurre en un archivo.

Y estoy totalmente de acuerdo en que tener una API para verificar la preparación del contenedor es la forma real de resolver esto, pero sigo creyendo que el servicio expuesto sería un mejor candidato para declarar esto.

una solución alternativa until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done

@ piotr-s-brainhub De los comentarios anteriores se menciona que tener un puerto abierto no significa que el servicio esté listo.

¿Podemos tener una condición opcional de preparación que pueda ser activada por registros, apertura de puertos o retraso de tiempo? Algo como:

ready_when:
  in_logs: `MySQL init process done`
  ports_open:
  - 3306

Me acabo de dar cuenta de que esperar a que los contenedores de dependencia estén listos se puede implementar fácilmente con herramientas como ansible. ¿Alguien usó ese enfoque? ¿Puede reemplazar fácilmente docker-compose con ansible / chef / puppet? ¿Algún proyecto en github que demuestre este enfoque?

Nota: Entiendo la importancia de escribir un servicio sólido que se pueda ejecutar incluso cuando sus dependencias no estén disponibles en este momento. Ésa no es la cuestión.

Resolví esto hoy en día con una herramienta que escribí: https://github.com/betalo-sweden/await

Puede esperar hasta que una lista determinada de recursos esté disponible y continuar con lo que desea continuar, ya sea yendo al siguiente comando implícitamente o llamándolo explícitamente.

@djui , ¿qué hace

@derekmahar It encuestas. Tiene un tiempo de espera predeterminado de 60 segundos. Cada vez que no puede ver el recurso, simplemente volverá a intentarlo en intervalos de 1 segundo. Actualmente no realiza detección de recursos concurrentes, por lo que es secuencial, pero resultó ser lo suficientemente bueno y se puede arreglar.

Lo uso en el siguiente escenario:

Enciendo una infraestructura de composición de Docker y luego ejecuto un controlador de prueba de integración. El servicio del controlador se inicia solo después de que todos los componentes de la infraestructura estén disponibles, utilizando await; así que await finalmente llama al comando de ejecución del controlador.

Esta es una forma de hacer esto con la nueva directiva HEALTHCHECK de Docker usando make:

https://gist.github.com/mixja/1ed1314525ba4a04807303dad229f2e1

[ACTUALIZACIÓN: esencia actualizada para tratar si el contenedor sale con un código de error, ya que Docker 1.12 informa un tanto estúpidamente el estado de Healthcheck en el contenedor detenido como "comenzando"]

Gracias @mixja , buena solución.

@mixja , ¡buena solución! Esa es exactamente la funcionalidad que esperaría que saliera de la caja. Pero ahora la pregunta es si inicia sus contenedores manualmente, ¿por qué necesita docker-compose?

Para las pruebas, uso https://github.com/avast/docker-compose-gradle-plugin y también usa Docker healthcheck: no más pausas artificiales, compilaciones más rápidas.

@korya : Docker compose no es realmente una herramienta de orquestación, es más una especificación de entorno y una herramienta de administración. Utilizo Make para proporcionar una orquestación de estilo procedimental sobre Docker Compose y Docker (y otras herramientas según sea necesario). La combinación de Make, Docker y Docker Compose es muy poderosa y puede lograr muchos escenarios diferentes con estos componentes básicos.

@mixja bueno, puede que tengas razón. Pero como muchas personas señalaron en este hilo, una funcionalidad de orquestación es muy necesaria en entornos de prueba, y cuando hay docker-compose en su caja de herramientas, es muy tentador requerir este tipo de funcionalidad de docker-compose.

De hecho, según los documentos, "Compose es una herramienta para definir y ejecutar aplicaciones Docker de varios contenedores". Aunque no dice que componer sea una herramienta de orquestación, creo que desde la perspectiva del usuario (por ejemplo, yo mismo) es natural esperar de "una herramienta para definir y ejecutar aplicaciones Docker de múltiples contenedores" para admitir la administración básica de dependencias entre los contenedores administrados fuera de la caja.

No estoy diciendo que la herramienta tenga que soportarlo. Todo lo que digo es que es muy natural esperarlo. De lo contrario, todos tienen que idear formas súper inteligentes de hacerlo. De hecho, usamos un script bash que hace algo similar a lo que hace su archivo MAKE.

@mixja @korya Me gustaría mejorar mi herramienta aguarda y me gustaría pedirle comentarios sobre lo que proporcionan las versiones de Makefile que falta / es más conveniente / habilita más de await .

Parece que la versión healthcheck + make parece ser una vista "global", ningún contenedor conoce el estado global (pero el makefile sí) y await es una vista "local", cada contenedor habilitado lo sabe (solo) lo que necesita saber, similar a depends_on o links . Además, prefiere enviar el contenedor con las herramientas necesarias para la verificación de estado (que a veces es el valor predeterminado, por ejemplo, mysqlshow ) y, de lo contrario, dejar el Dockerfile intacto. Además, parece que usa docker-compose ya no principalmente para la composición sino principalmente para la configuración flexible (por ejemplo, docker-compose up -d mysql debería ser equivalente a docker run -d -e ... -v ... -p ... mysql ).

Hola @djui : probablemente sea un punto de vista filosófico, pero creo que toda la premisa de HEALTHCHECK es promover el comportamiento correcto, es decir, un contenedor puede proporcionar un medio para establecer la salud del contenedor, sin dependencias externas.

Esto no quita de ninguna manera el valor de tener algo externo que verifique la conectividad, sin embargo, normalmente ejecutaría un conjunto de pruebas de aceptación para cubrir esto, ya que desea verificar la conectividad y mucho más (es decir, la funcionalidad de la aplicación). Por supuesto, generalmente no puede ejecutar este nivel de prueba hasta que se haya establecido un entorno completo y el alcance de su herramienta await y otros enfoques que he usado en el pasado (libros de jugadas de Ansible envueltos en un agent container) está realmente enfocado en orquestar correctamente la configuración del entorno (no el objetivo final de las pruebas de aceptación) y hasta ahora era realmente el único enfoque disponible en un mundo Docker.

Con Docker 1.12 ahora tenemos un medio para introspectar el entorno de Docker y la capacidad de usar construcciones bien establecidas (es decir, mecanismos bash / shell) para "esperar" un cierto estado, por supuesto, siempre y cuando nuestros contenedores hayan definido sus propios controles de estado. . Veo más valor en aprovechar las capacidades nativas de la plataforma y alentar a los propietarios de contenedores a definir sus propias verificaciones de estado, en lugar de confiar en el enfoque externo histórico (comencé mi proceso de solicitud, ya no es mi problema) que hemos tenido que hacer recurrir a.

Como analogía relacionada, considere AWS CloudFormation y el concepto de autoescalar grupos y organizar actualizaciones continuas. ¿Cómo sabe CloudFormation si una nueva instancia está "en buen estado" lista para funcionar y si podemos eliminar una instancia anterior y rodar en otra nueva instancia? ¿Escribimos una verificación de estado externa o confiamos en la propia instancia para señalar la salud? La respuesta es la última, significa que el propietario de la instancia puede establecer los criterios de éxito necesarios para su instancia y luego indicar al sistema de orquestación general (es decir, CloudFormation) que la instancia está "en buen estado".

Con respecto a sus comentarios sobre Docker Compose, es una herramienta que puede proporcionar los dos aspectos que menciona. La parte docker-compose.yml es la especificación del entorno de composición del estado deseado, mientras que los distintos comandos docker-compose proporcionan la capacidad de interactuar con el entorno de varias formas. Por ahora necesitamos herramientas de orquestación externas porque, fundamentalmente, docker-compose no realiza la gestión de dependencias entre servicios lo suficientemente bien. Como docker-compose obtiene características como soporte nativo de chequeo de salud, el objetivo de un solo comando docker-compose up será más realista, asumiendo que seremos capaces de especificar, por ejemplo, que un servicio debe estar marcado como saludable antes se considera "activo", lo que significa que nuestros servicios dependientes esperan efectivamente hasta que la dependencia esté sana.

@mixja Gracias por la explicación detallada. Yo creo que

Veo más valor en aprovechar las capacidades nativas de la plataforma

es un buen / el punto principal. Esperando a que Docker Compose aproveche las comprobaciones de estado de forma nativa, ya sea en depende_en o en una nueva clave, aguarde. Solo me pregunto si debería / iría incluso un paso más allá y básicamente derriba los contenedores vinculados si, por ejemplo, se establece --abort-on-container-exit y una verificación de estado durante el tiempo de ejecución establece la etiqueta de verificación de estado en _unhealthy_.

Posible solución temporal para aquellos de ustedes que buscan delay funcionalidad para ejecutar pruebas:

Tengo dos archivos docker-compose yml. Uno es para pruebas y otro para desarrollo. La diferencia está solo en tener sut contenedor en docker-compose.test.yml . sut contenedor ejecuta pytest . Mi objetivo era prueba de funcionamiento docker-compose y si pytest comando en sut envase falla, no se ejecutan desarrollo docker-compose . Esto es lo que se me ocurrió:

# launch test docker-compose; note: I'm starting it with -p argument
docker-compose -f docker-compose.test.yml -p ci up --build -d
# simply get ID of sut container
tests_container_id=$(docker-compose -f docker-compose.test.yml -p ci ps -q sut)
# wait for sut container to finish (pytest will return 0 if all tests passed)
docker wait $tests_container_id
# get exit code of sut container
tests_status=$(docker-compose -f docker-compose.test.yml -p ci ps -q sut | xargs docker inspect -f '{{ .State.ExitCode  }}' | grep -v 0 | wc -l | tr -d ' ')
# print logs if tests didn't pass and return exit code
if [ $tests_status = "1" ] ; then
    docker-compose -f docker-compose.test.yml -p ci logs sut
    return 1
else
    return 0
fi

Ahora puede usar el código anterior en cualquier función de su elección (la mía se llama test ) y hacer algo así:

test
test_result=$?
if [[ $test_result -eq 0 ]] ; then
    docker-compose -f docker-compose.yml up --build -d
fi

Funciona bien para mí, pero todavía estoy deseando ver que docker-compose soporte ese tipo de cosas de forma nativa :)

+1

¿Quizás las cosas que se consideran fuera del núcleo de docker-compose podrían ser compatibles al permitir complementos? De manera similar a la solicitud # 1341, parece que hay una funcionalidad adicional que algunos encontrarían útil, pero que no necesariamente se alinea completamente con la visión actual. Quizás admitir un sistema de complementos como el propuesto por # 3905 proporcionaría una forma de permitir el enfoque de composición en un conjunto básico de capacidades y, si este no es uno, aquellos que lo deseen para su caso de uso particular podrían escribir un complemento para manejar el rendimiento up diferente?

Sería bueno poder hacer que docker-compose actúe como punto de entrada a todos los proyectos que tenemos localmente alrededor de la configuración del entorno de la ventana acoplable, en lugar de tener que agregar un script delante de todos para actuar como el punto de entrada predeterminado en lugar de personas que necesitan recordar ejecutar el script para los casos extraños.

Aquí hay una forma de hacerlo con healthcheck y docker-compose 2.1+ :

version: "2.1"
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    healthcheck:
      test: mysqladmin -uroot -ppassword ping
      interval: 2s
      timeout: 5s
      retries: 30
  web:
    image: nginx:latest # your image
    depends_on:
      db:
        condition: service_healthy

Aquí docker-compose up iniciará el contenedor web solo después de que el contenedor db se considere saludable.

Lo siento si ya se mencionó, pero no creo que se haya publicado una solución completa.

Aquí hay una forma de PostgreSQL.

Gracias @Silex 👍

version: '2.1'
services:
  db:
    image: postgres:9.6.1
    healthcheck:
      test: "pg_isready -h localhost -p 5432 -q -U postgres"
      interval: 3s
      timeout: 5s
      retries: 5

@Silex lamentablemente con la versión "3" y este formato:

    image: nginx:latest # your image
    depends_on:
      db:
        condition: service_healthy

Recibo ERROR: The Compose file './docker-compose.yml' is invalid because: depends_on contains an invalid type, it should be an array

2.1 continúa admitiéndolo y no quedará obsoleto. 3.x es principalmente para el modo de servicios de enjambre (no local).

  From: Vlad Filippov <[email protected]>

Para: docker / compose [email protected]
Cc: mbdas [email protected] ; Mencione menció[email protected]
Enviado: miércoles 8 de marzo de 2017 11:45 a.m.
Asunto: Re: [docker / compose] ¿Hay alguna forma de retrasar el inicio del contenedor para admitir servicios dependientes con un tiempo de inicio más largo (# 374)

@Silex lamentablemente con la versión "3" y este formato: imagen: nginx: última # su imagen
depende de:
db:
condición: service_healthy
Recibo ERROR: El archivo Compose './docker-compose.yml' no es válido porque: services.auth.depends_on contiene un tipo no válido, debería ser una matriz—
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub o silencia el hilo.

2.1 continúa admitiéndolo y no quedará obsoleto. 3.x es principalmente para el modo de servicios de enjambre (no local).

¡Gracias!

@vladikoff : más información sobre la versión 3 en https://github.com/docker/compose/issues/4305

Básicamente, no será compatible, debe hacer que sus contenedores sean tolerantes a fallas en lugar de depender de docker-compose.

Creo que esto se puede cerrar ahora.

Desafortunadamente, la condición ya no es

website:
    depends_on:
      - 'postgres'
    build: .
    ports:
      - '3000'
    volumes:
      - '.:/news_app'
      - 'bundle_data:/bundle'
    entrypoint: ./wait-for-postgres.sh postgres 5432

  postgres:
    image: 'postgres:9.6.2'
    ports:
      - '5432'

esperar-para-postgres.sh:

#!/bin/sh

postgres_host=$1
postgres_port=$2
shift 2
cmd="$@"

# wait for the postgres docker to be running
while ! pg_isready -h $postgres_host -p $postgres_port -q -U postgres; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"

# run the command
exec $cmd

El punto de entrada personalizado @ slava-nikulin es una práctica común, es casi la única forma (nativa de Docker) de cómo puede definir y verificar todas las condiciones que necesita antes de mirar su aplicación en un contenedor.

La verdad es que hubo mucho debate y creo que el soporte 2.x para el soporte condicional para integrarse de forma nativa con los controles de salud y ordenar la puesta en marcha fue un soporte muy necesario. Docker no admite el pod local de contenedores de forma nativa y, cuando lo haga, tendrá que admitir algo similar nuevamente, al igual que kubernetes, por ejemplo, proporciona la semántica.

Docker 3.x es una serie para incorporar el soporte de enjambres en la composición y, por lo tanto, se han eliminado muchas opciones teniendo en cuenta la naturaleza distribuida.

La serie 2.x conserva las características de topología local / de composición original.

Docker tiene que descubrir cómo fusionar estas 2 versiones porque forzar a swarm a componer reduciendo el conjunto de características de compose no es una dirección bienvenida.

El 10 de mayo de 2017, a las 8:15 p.m., Slava Nikulin [email protected] escribió:

Desafortunadamente, la condición ya no se admite en v3. Aquí hay una solución alternativa que encontré:

sitio web:
depende de:
- 'postgres'
construir: .
puertos:
- '3000'
volúmenes:
- '.: / news_app'
- 'bundle_data: / bundle'
punto de entrada: ./wait-for-postgres.sh postgres 5432

postgres:
imagen: ' postgres: 9.6.2 '
puertos:
- '5432'
esperar-para-postgres.sh:

! / bin / sh

postgres_host = $ 1
postgres_port = $ 2
cmd = "$ @"

espere a que se ejecute la ventana acoplable de postgres

mientras ! pg_isready -h $ postgres_host -p $ postgres_port -q -U postgres; hacer

& 2 echo "Postgres no está disponible - durmiendo"
dormir 1
hecho

& 2 echo "Postgres está activo - ejecutando comando"

ejecutar el comando

exec $ cmd
-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub o silencia el hilo.

Pude hacer algo como esto
// start.sh

#!/bin/sh
set -eu

docker volume create --name=gql-sync
echo "Building docker containers"
docker-compose build
echo "Running tests inside docker container"
docker-compose up -d pubsub
docker-compose up -d mongo
docker-compose up -d botms
docker-compose up -d events
docker-compose up -d identity
docker-compose up -d importer
docker-compose run status
docker-compose run testing

exit $?

// status.sh

#!/bin/sh

set -eu pipefail

echo "Attempting to connect to bots"
until $(nc -zv botms 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to events"
until $(nc -zv events 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to identity"
until $(nc -zv identity 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to importer"
until $(nc -zv importer 8080); do
    printf '.'
    sleep 5
done
echo "Was able to connect to all"

exit 0

// en mi docker compose file

  status:
    image: yikaus/alpine-bash
    volumes:
      - "./internals/scripts:/scripts"
    command: "sh /scripts/status.sh"
    depends_on:
      - "mongo"
      - "importer"
      - "events"
      - "identity"
      - "botms"

Tengo un problema similar pero un poco diferente. Tengo que esperar a que MongoDB inicie e inicialice un conjunto de réplicas.
Estoy haciendo todo el procedimiento en Docker. es decir, creación y autenticación de un conjunto de réplicas. Pero tengo otro script de Python en el que tengo que conectarme al nodo principal del conjunto de réplicas. Recibo un error allí.

docker-compose.txt
Dockerfile.txt
y en el script de Python estoy tratando de hacer algo como esto
for x in range(1, 4): client = MongoClient(host='node' + str(x), port=27017, username='admin', password='password') if client.is_primary: print('the client.address is: ' + str(client.address)) print(dbName) print(collectionName) break

Estoy teniendo dificultades para hacerlo, ¿alguien tiene alguna idea?

@patrickml Si no uso docker compose, ¿cómo lo hago con Dockerfile?
Necesito 'cqlsh' para ejecutar mi build_all.cql. Sin embargo, 'cqlsh' no está listo ... tienes que esperar 60 segundos para estar listo.

gato Dockerfile

DESDE store / datastax / dse- servidor: 5.1.8

USUARIO root

EJECUTAR apt-get update
EJECUTAR apt-get install -y vim

AÑADIR db-scripts-2.1.33.2-RFT-01.tar / docker / cms /
COPIA entrypoint.sh /entrypoint.sh

WORKDIR /docker/cms/db-scripts-2.1.33.2/
EJECUTAR cqlsh -f build_all.cql

USUARIO dse

=============

Paso 8/9: EJECUTE cqlsh -f build_all.cql
---> Ejecutando en 08c8a854ebf4
Error de conexión: ('No se puede conectar a ningún servidor', {'127.0.0.1': error (111, "Intenté conectarse a [('127.0.0.1', 9042)]. Último error: Conexión rechazada")})
El comando '/ bin / sh -c cqlsh -f build_all.cql' devolvió un código distinto de cero: 1

Requiere = var-lib-libvirt.mount var-lib-libvirt-images-ram.mount

¿Fue útil esta página
0 / 5 - 0 calificaciones