Faça um aplicativo Docker gravar no stdout

49

Estou implantando um aplicativo de terceiros em conformidade com o comunicado de 12 fatores e um dos pontos informa que os logs do aplicativo devem ser impressos em stdout / stderr: o software de cluster pode coletá-lo.

No entanto, o aplicativo pode gravar apenas em arquivos ou syslog. Como imprimo esses logs?

Kolypto
fonte
Eles já estão indo para o syslog. Você pode pegá-los de lá!
Michael Hampton
@ MichaelHampton, parece bom, mas o Docker executa um único processo que pode gravar no stdout, e isso parece combinar dois deles?
Kolypto 28/05
1
Você pode ter um processo daemon usando syslog e ter um processo frontal que imprime?
Qkrijger 28/05
@qkrijger, bom ponto! Agora, se alguém tiver experiência com isso ...?
Kolypto 28/05

Respostas:

107

Uma receita incrível é dada no Dockerfile nginx :

# 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

Simplesmente, o aplicativo pode continuar gravando nele como um arquivo, mas, como resultado, as linhas irão para stdout& stderr!

Kolypto
fonte
2
Este. É. Somente. Brilhante. E versátil.
Spacediver
11
O único problema com isso é quando o que você está executando insiste em se forçar em segundo plano como um processo não raiz. Estou tendo isso com squid3, e então ele tem problemas com permissões /dev/stdout.
precisa
4
Isso nem sempre funciona - por exemplo, se o aplicativo tentar procurar o arquivo, esse método geralmente não funcionará.
Mix24 /
Link é baixo ...
Babken Vardanyan
4
Tudo é um arquivo para a vitória.
precisa
16

Em outra pergunta, matar o processo filho quando o pai sai , recebi a resposta que ajudou a resolver isso.

Dessa forma, configuramos o aplicativo para que ele se registre em um arquivo e continuamente tail -f. Felizmente, tailpode aceitar --pid PID: ele será fechado quando o processo especificado terminar. Colocamos $$lá: PID do shell atual.

Como etapa final, o aplicativo iniciado é execed, o que significa que o shell atual é completamente substituído por esse aplicativo.

O script do corredor,, run.shterá a seguinte aparência:

#! /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: ao usar tail -F, listamos os nomes de arquivos e eles serão lidos mesmo que apareçam mais tarde!

Por fim, o Dockerfile minimalista:

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

Nota: para solucionar um tail -fcomportamento extremamente estranho (que diz "foi substituído por um arquivo remoto. Desistindo desse nome"), tentei outra abordagem: todos os arquivos de log conhecidos são criados e truncados na inicialização: dessa maneira, garanto que eles existem e somente então - siga-os:

#! /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
fonte
Especialmente para o servidor Apache, estou usando logs do Piped ( httpd.apache.org/docs/2.4/logs.html#piped ) e parece que funciona.
Php-coder
Ele resolve problemas semelhantes com o alpine e parece funcionar bem no BusyBox tailsem a opção --pid.
Ryan
a solução alternativa funcionou para mim. extremamente útil, obrigado.
Tamas Kalman
8

Para um processo em segundo plano em um contêiner de docker, por exemplo, conectando-me ao exec em / bin / bash, eu pude usar.

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

Isso envia a saída para o stdout do pid 1, que é o único captador do docker.

Pieter
fonte
1
Essa é a resposta correta. Os logs devem ser enviados ao stdout para o PID1 e, se seu aplicativo estiver sendo executado sob outro PID, ele não será visto por docker logsou kubectl logs. Eu tenho um contêiner que agenda tarefas através do crontab que não está sendo executado como PID1.
leeman24 31/10
6

para nginx você pode ter nginx.confapontando /dev/stderre /dev/stdoutassim

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

e sua Dockerfileentrada deve ser

/usr/sbin/nginx -g 'daemon off;'
Muayyad Alsadi
fonte
Não funciona quando o processo principal não está sendo executado comoroot
Peedee