Hacer que una aplicación Docker escriba en stdout

49

Estoy implementando una aplicación de terceros de conformidad con el aviso de 12 factores , y uno de los puntos dice que los registros de la aplicación deben imprimirse en stdout / stderr: luego el software de agrupación puede recopilarlo.

Sin embargo, la aplicación solo puede escribir en archivos o syslog. ¿Cómo imprimo estos registros en su lugar?

Kolypto
fuente
Ya van a syslog. ¡Puedes recogerlos desde allí!
Michael Hampton
@MichaelHampton, parece estar bien, pero Docker ejecuta un solo proceso que puede escribir en stdout, ¿y esto suena como combinar dos de ellos?
Kolypto
1
¿Puede hacer que un proceso demonio use syslog y tenga un proceso frontal que imprima?
qkrijger
@qkrijger, buen punto! Ahora, si alguien tiene experiencia con eso ...?
Kolypto

Respuestas:

107

En el archivo Dockerfile de nginx se ofrece una receta increíble :

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

Simplemente, la aplicación puede continuar escribiéndole como un archivo, pero como resultado las líneas irán a stdout& stderr!

Kolypto
fuente
2
Esta. Es. Sólo. Brillante. Y versátil
spacediver
11
Lo único que tiene un problema con esto es cuando lo que está ejecutando insiste en bifurcarse en segundo plano como un proceso no root. Estoy teniendo esto squid3, y luego tiene problemas con los permisos /dev/stdout.
mikepurvis
44
Esto no siempre funciona; por ejemplo, si la aplicación intenta buscar el archivo, este método generalmente no funcionará.
mixja
Link is down ...
Babken Vardanyan
44
Todo es un archivo para la victoria.
RubberDuck
16

En otra pregunta, Matar proceso infantil cuando el padre sale , obtuve la respuesta que ayudó a resolver esto.

De esta manera, configuramos la aplicación para que se registre en un archivo y continuamente tail -f. Afortunadamente, tailpuede aceptar --pid PID: saldrá cuando finalice el proceso especificado. Ponemos $$allí: PID del shell actual.

Como paso final, la aplicación iniciada se execedita, lo que significa que el shell actual se reemplaza completamente con esa aplicación.

El script del corredor run.sh, se verá así:

#! /usr/bin/env bash
set -eu

rm -rf /var/log/my-application.log
tail --pid $$ -F /var/log/my-application.log &

exec /path/to/my-application --logfile /var/log/my-application.log

NOTA: al usar tail -F, enumeramos los nombres de los archivos, ¡y los leerá incluso si aparecen más tarde!

Finalmente, el minimalista Dockerfile:

FROM ubuntu
ADD run.sh /root/run.sh
CMD ['/root/run.sh']

Nota: para solucionar un tail -fcomportamiento extremadamente extraño (que dice "ha sido reemplazado por un archivo remoto. Renunciar a este nombre") probé otro enfoque: todos los archivos de registro conocidos se crean y truncan al inicio: de esta manera me aseguro de que existan , y solo entonces - seguirlos:

#! /usr/bin/env bash
set -eu

LOGS=/var/log/myapp/

( umask 0 && truncate -s0 $LOGS/http.{access,error}.log )
tail --pid $$ -n0 -F $LOGS/* &

exec /usr/sbin/apache2 -DFOREGROUND
Kolypto
fuente
Especialmente para el servidor Apache, estoy usando registros de Piped ( httpd.apache.org/docs/2.4/logs.html#piped ) y parece que funciona.
php-coder
Resuelve problemas similares con alpine y parece funcionar bien en BusyBox tailsin la opción --pid.
Ryan
la solución me funcionó. extremadamente útil, gracias.
Tamas Kalman
8

Para un proceso en segundo plano en un contenedor acoplable, por ejemplo, conectando con exec a / bin / bash pude usar.

echo "test log1" >> /proc/1/fd/1

Esto envía la salida al stdout de pid 1, que es el que recoge una ventana acoplable.

Pieter
fuente
1
Esta es la respuesta correcta. Los registros deben enviarse a stdout para PID1 y si su aplicación se ejecuta bajo otro PID, no será visto por docker logso kubectl logs. Tengo un contenedor que programa la tarea a través de crontab que no se ejecuta como PID1.
leeman24
6

para nginx puedes tener nginx.confapuntando /dev/stderry /dev/stdoutasí

user  nginx;
worker_processes  4;
error_log  /dev/stderr;
http {
    access_log  /dev/stdout  main;
...

y tu Dockerfileentrada debe ser

/usr/sbin/nginx -g 'daemon off;'
Muayyad Alsadi
fuente
No funciona cuando el proceso maestro no se está ejecutando comoroot
peedee