2015-08-05 8 views
16

Sto eseguendo un'applicazione Java dropwizard in un contenitore mobile utilizzando l'immagine java:7u79 basata su debian/jessie.SIGTERM non ricevuto da processo java utilizzando 'docker stop' e l'immagine java ufficiale

La mia applicazione Java gestisce il segnale SIGTERM allo spegnimento con garbo. La gestione di SIGTERM funziona perfettamente quando eseguo l'applicazione senza Docker.

Quando lo si esegue in un contenitore Docker, SIGTERM non raggiunge l'applicazione Java quando si emette un comando docker stop. Uccide il processo bruscamente dopo 10 secondi.

mio Dockerfile:

FROM java:7u79 

COPY dropwizard-example-1.0.0.jar /opt/dropwizard/ 
COPY example.keystore /opt/dropwizard/ 
COPY example.yml /opt/dropwizard/ 

WORKDIR /opt/dropwizard 

RUN java -jar dropwizard-example-1.0.0.jar db migrate /opt/dropwizard/example.yml 

CMD java -jar dropwizard-example-1.0.0.jar server /opt/dropwizard/example.yml 

EXPOSE 8080 8081 

Cosa c'è di sbagliato con questo Dockerfile? C'è un altro modo per affrontare questo problema?

risposta

24

Supponendo si lancia un servizio Java definendo la seguente nel Dockerfile:

CMD java -jar ... 

Quando si inserisce ora il contenitore e elencare i processi per esempio da docker exec -it <containerName> ps AHf (non ho provato che con l'java ma con l'immagine ubuntu) si vede che il processo di Java non è il processo principale (non il processo con PID 1), ma un processo figlio di un processo /bin/sh:

UID  PID PPID C STIME TTY   TIME CMD 
root   1  0 0 18:27 ?  00:00:00 /bin/sh -c java -jar ... 
root   8  1 0 18:27 ?  00:00:00 java -jar ... 

Quindi fondamentalmente si ha una shell Linux che è il processo principale con PID 1 che ha un processo figlio (Java) con PID 8.

Per ottenere il corretto funzionamento del segnale, si dovrebbe evitare il processo padre shell. Questo può essere fatto usando il comando integrato della shell exec. Ciò farà sì che il bambino elabori il processo genitore. Quindi alla fine l'ex processo genitore non esiste più. E il processo bambino diventa il processo con il PID 1. Prova quanto segue nel Dockerfile:

CMD exec java -jar ... 

il processo di quotazione, allora dovrebbe mostrare qualcosa del tipo:

UID  PID PPID C STIME TTY   TIME CMD 
root   1  0 0 18:30 ?  00:00:00 java -jar ... 

Ora avete solo quella di processo con PID 1. Generalmente una buona pratica è che i container docker contengano solo un processo - quello con PID 1 (o se hai davvero bisogno di più processi allora dovresti usare ad esempio supervisord come PID 1 che a sua volta si occupa della gestione dei segnali per i suoi processi figli).

Con questa configurazione, lo SIGTERM verrà gestito direttamente dal processo Java. Non c'è più nessun processo shell in grado di interrompere la gestione del segnale.

EDIT:

Lo stesso effetto exec potrebbe essere realizzato utilizzando un diverso CMD sintassi che lo fa implicitamente (grazie a Andy per il suo commento):

CMD ["java", "-jar", "..."] 
+1

Questa è una buona risposta, ma potrebbe essere ancora meglio se hanno usato '' cmd' o ENTRYPOINT' con il formato _exec_, ad esempio 'ENTRYPOINT [" java "," - jar "," ... "]' https://docs.docker.com/reference/builder/#entrypoint – Andy

+0

Sì, grazie per averlo indicato . Ho aggiornato la mia risposta di conseguenza. – h3nrik

+2

'CMD [" java "," -jar ", ...]' non funzionava con le variabili 'ENV', ma' CMD exec java -jar' ha risolto il mio problema :) –

2

@ risposta h3nrik è giusto ma a volte hai davvero bisogno di usare uno script per configurare il lancio.Basta usare il comando exec per fare il trucco nella maggior parte dei casi:

#!/bin/sh 

#--- Preparations 

exec java -jar ... 

vedere questo meraviglioso blog post