Jak utrzymać działający kontener Docker po uruchomieniu usług?

156

Widziałem kilka samouczków, które wydają się robić to samo, co próbuję zrobić, ale z jakiegoś powodu moje kontenery Docker zamykają się. Zasadniczo konfiguruję serwer WWW i kilka demonów w kontenerze Dockera. run-all.shWykonuję ostatnie części tego za pomocą skryptu bash o nazwie, który uruchamiam przez CMD w moim pliku Dockerfile. run-all.shwygląda tak:

service supervisor start
service nginx start

I uruchamiam go w moim pliku Docker w następujący sposób:

CMD ["sh", "/root/credentialize_and_run.sh"]

Widzę, że wszystkie usługi uruchamiają się poprawnie, gdy uruchamiam rzeczy ręcznie (tj. Wchodzę do obrazu za pomocą -i -t / bin / bash) i wszystko wygląda tak, jakby działało poprawnie po uruchomieniu obrazu, ale kończy się raz kończy uruchamianie moich procesów. Chciałbym, aby procesy działały w nieskończoność i, o ile rozumiem, kontener musi działać, aby tak się stało. Niemniej jednak, gdy biegnę docker ps -a, widzę:

➜  docker_test  docker ps -a
CONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS                      PORTS               NAMES
c7706edc4189        some_name/some_repo:blah   "sh /root/run-all.sh   8 minutes ago       Exited (0) 8 minutes ago                        grave_jones

Co daje? Dlaczego to wychodzi? Wiem, że mógłbym po prostu umieścić pętlę while na końcu mojego skryptu bash, aby go utrzymać, ale jaki jest właściwy sposób, aby zapobiec jego zamknięciu?

Eli
źródło
1
czy wystawiasz porty usług na zewnątrz (opcja -p do uruchomienia Dockera)? (oczywiście to nie przeszkodzi im wyjść)
ribamar
1
Używałem ENTRYPOINT w moim pliku Dockerfile i po uruchomieniu skryptu zdefiniowanego w ENTRYPOINT (mój skrypt inicjujący) pojawił się w dziennikach, ale mój kontener wydawał się być zamknięty. Tak więc zamiast ENTRYPOINT użyłem polecenia RUN do uruchomienia skryptu, a kontener nadal działa w tle.
ypahalajani

Odpowiedzi:

50

Tak naprawdę nie należy projektować kontenerów Docker.

Projektując kontener Dockera, powinieneś zbudować go w taki sposób, aby działał tylko jeden proces (tj. Powinieneś mieć jeden kontener dla Nginx i jeden dla nadzorcy lub uruchomionej aplikacji); dodatkowo proces ten powinien przebiegać na pierwszym planie.

Kontener "wyjdzie" po zakończeniu samego procesu (w twoim przypadku tym procesem jest twój skrypt bash).


Jeśli jednak naprawdę potrzebujesz (lub chcesz) uruchomić wiele usług w swoim kontenerze Docker, rozważ rozpoczęcie od „Docker Base Image” , który jest używany runitjako proces pseudo-init ( runitpozostanie w trybie online podczas działania Nginx i Supervisor), który pozostanie na pierwszym planie, podczas gdy inne procesy robią swoje.

Mają pokaźną dokumentację, więc powinieneś być w stanie osiągnąć to, co próbujesz zrobić, w miarę łatwo.

Thomas Orozco
źródło
1
Czy możesz wyjaśnić, dlaczego powinienem mieć uruchomioną tylko jedną usługę? W razie potrzeby mógłbym dodać nginx do przełożonego, ale nie wiem, dlaczego ma to być konieczne.
Eli
3
@Eli Krótka odpowiedź jest taka, że ​​tak działa Docker. Docker będzie uruchamiał tylko jeden proces (i jego elementy podrzędne) na kontener. Zaleca się, aby ten proces był faktycznym procesem aplikacji (aby Docker wiedział, jeśli zakończy się), ale rzeczywiście można użyć nadzorcy jako tego procesu. Zauważ, że będziesz musiał skonfigurować nadzorcę tak, aby działał na pierwszym planie (tj. Nie demonizował), co odbywa się za pomocą --nodaemonopcji.
Thomas Orozco,
1
@Eli Ten post na blogu Dockera pokazuje, że uruchamianie wielu procesów (i ogólnie mówiąc, postrzeganie kontenera jako „małego VPS”) jest nieoptymalne. W twoim przypadku wątek komentarza będzie prawdopodobnie bardziej odpowiedni niż sam wpis na blogu.
Thomas Orozco,
1
Podstawowy obraz Dockera jest okropnym rozwiązaniem wielu problemów korporacyjnych, ponieważ niewiele poważnych firm używa ubuntu, zamiast tego preferuje drzewo RHEL / Centos.
Inżynier oprogramowania
9
„Kilka poważnych firm” wydaje się nie do obrony. Wydaje się, że wybór systemu operacyjnego zależy wyłącznie od przypadku użycia. Każda firma ma wiele różnych środowisk, w tym wewnętrzne wykorzystanie przez programistów, wewnętrzne wykorzystanie pracowników, wsparcie sprzedaży, staging, POC i wreszcie produkcję (a nawet to jest niejasne określenie). Nie sądzę, by OP tak wspominał o ich przypadku użycia (przepraszam, że jestem dziwaczny), ale ten rodzaj komentarza wydaje się być typem, który rozpowszechnia wysoce uparte informacje bez argumentu dlaczego.
John Carrell
155

Jeśli używasz pliku Dockerfile, spróbuj:

ENTRYPOINT ["tail", "-f", "/dev/null"]

(Oczywiście jest to tylko do celów deweloperskich, nie powinno być konieczne utrzymywanie kontenera przy życiu, chyba że działa w nim proces, np. Nginx ...)

Pociski miękkie
źródło
5
Używałem CMD["sleep", "1d"]ale rozwiązanie wydaje się lepsze
George Pligoropoulos
@GeorgiosPligoropoulos to utknie w tej linii; może działanie w tle zadziała
Prashanth Sams
5
Można również użyć CMD["sleep", "infinity"].
Romain
5
lub „kot”, ale ludzie mogą powiedzieć, że to znęcanie się nad zwierzętami. xD
lawphotog
Możesz zakończyć skrypt punktu wejścia, exec tail -f /dev/nullale użycie go tailjako punktu wejścia jest błędną odpowiedzią.
Torsten Bronger
86

Po prostu miałem ten sam problem i odkryłem, że jeśli używasz swojego kontenera z flagą -ti -d, to nadal działa.

docker run -td <image>

Oto, co robią flagi (według docker run --help):

-d, --detach=false         Run container in background and print container ID
-t, --tty=false            Allocate a pseudo-TTY

Najważniejszym z nich jest -tflaga. -dpo prostu pozwala uruchomić kontener w tle.

arne.z
źródło
3
Nie mogę tego odtworzyć. Czy mógłby Pan podać przykład? Czy jest coś konkretnego (np .: CMD) na temat Dockerfile, którego potrzebujemy, aby to zadziałało?
Matheus Santana
2
To nie zadziałało dla mnie. Użyłem polecenia, docker logs <image>aby upewnić się, że to błąd, który powoduje zamknięcie mojego kontenera Docker. Status wyjścia to, 0a ostatnie wyjście to potwierdzenie, że mój lighttpdserwer działa:[ ok ] Starting web server: lighttpd.
ob1
Od jakiegoś czasu nie pracuję z Dockerem. Jest więc możliwe, że interfejs wiersza poleceń się zmienił i to polecenie już nie działa.
arne.z
4
Mogę potwierdzić, że to rzeczywiście działa z najnowszą wersją dockera. Jeśli chcesz później dołączyć do tej sesji, zadziała również opcja -dit.
John Hamilton,
1
@Long skrypt nie przyjmie tty, add exec bashlub exec shjeśli bash nie jest zainstalowany, do końca start.sh. Następnie możesz użyć flagi -t
123
43

Przyczyną zakończenia jest to, że skrypt powłoki jest uruchamiany jako pierwszy jako PID 1, a po zakończeniu PID 1 znika, a docker działa tylko wtedy, gdy PID 1 jest.

Możesz użyć przełożonego do zrobienia wszystkiego, jeśli zostanie uruchomiony z flagą „-n”, powiedziano mu, aby nie demonizował, więc pozostanie jako pierwszy proces:

CMD ["/usr/bin/supervisord", "-n"]

I twój supervisord.conf:

[supervisord]
nodaemon=true

[program:startup]
priority=1
command=/root/credentialize_and_run.sh
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=false
startsecs=0

[program:nginx]
priority=10
command=nginx -g "daemon off;"
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.log
autorestart=true

Wtedy możesz mieć tyle innych procesów, ile chcesz, a przełożony zajmie się ich ponownym uruchomieniem, jeśli zajdzie taka potrzeba.

W ten sposób możesz użyć supervisord w przypadkach, w których możesz potrzebować nginx i php5-fpm i nie ma sensu ich rozdzielać.

phazei
źródło
Gdzie w dokumentacji jest napisane, czy PID 1 kończy się, kontener docker przestaje działać?
8oh8
@ 8oh8 Tak właśnie działają przestrzenie nazw procesów; nie jest specyficzna dla platformy Docker, ale „rzecz leżąca u podstaw wszystkich kontenerów”. Z man7.org/linux/man-pages/man7/pid_namespaces.7.html :If the "init" process of a PID namespace terminates, the kernel terminates all of the processes in the namespace via a SIGKILL signal. This behavior reflects the fact that the "init" process is essential for the correct operation of a PID namespace.
dannysauer,
40

możesz działać catbez żadnych argumentów, jak wspomniał bro @ Sa'ad, aby po prostu utrzymać działający kontener [właściwie nie robiąc nic poza czekaniem na dane wejściowe użytkownika] (wtyczka Jenkinsa Docker robi to samo)

Serge Velikanov
źródło
oprócz mojej odpowiedzi: ale zrozum, że docker-compose (nie demonizowany) jest używany do pokazania przepływu pracy twojego kontenera, więc może być przydatne dostosowanie plików dziennika uruchomionych usług. okrzyki
Serge Velikanov
1
lub cat. jenkin's Docker plugin robi to.
Sa'ad
12

Upewnij się, że dodajesz daemon off; do siebie plik nginx.conf lub uruchom go CMD ["nginx", "-g", "daemon off;"]zgodnie z oficjalnym obrazem nginx

Następnie użyj poniższego, aby uruchomić zarówno nadzorcę jako usługę, jak i nginx jako proces pierwszego planu, który zapobiegnie zamknięciu kontenera

service supervisor start && nginx

W niektórych przypadkach będziesz musiał mieć więcej niż jeden proces w swoim kontenerze, więc wymuszenie, aby kontener miał dokładnie jeden proces, nie zadziała i może spowodować więcej problemów podczas wdrażania.

Musisz więc zrozumieć kompromisy i podjąć odpowiednią decyzję.

iTech
źródło
7

Motywacja:

Nie ma nic złego w uruchamianiu wielu procesów w kontenerze Dockera . Jeśli ktoś lubi używać dockera jako lekkiej maszyny wirtualnej - niech tak będzie. Inni lubią dzielić swoje aplikacje na mikrousługi. Ja myślę: stos LAMP w jednym pojemniku? Po prostu świetnie.

Odpowiedź:

Trzymaj się dobrego obrazu podstawowego, takiego jak obraz podstawowy fuzji . Mogą być inni. Proszę skomentuj.

A to tylko kolejna prośba o przełożonego. Ponieważ obraz bazowy phusion zapewnia nadzorcę oprócz kilku innych rzeczy, takich jak cron i konfiguracja ustawień regionalnych. Rzeczy, które lubisz mieć konfigurację podczas uruchamiania tak lekkiej maszyny wirtualnej. Na co warto zwrócić uwagę, zapewnia również połączenia ssh do kontenera.

Sam obraz phusion po prostu się uruchomi i będzie działał, jeśli wydasz tę podstawową instrukcję uruchamiania dockera:

moin@stretchDEV:~$ docker run -d phusion/baseimage
521e8a12f6ff844fb142d0e2587ed33cdc82b70aa64cce07ed6c0226d857b367
moin@stretchDEV:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS
521e8a12f6ff        phusion/baseimage   "/sbin/my_init"     12 seconds ago      Up 11 seconds

Lub banalnie proste:

Jeśli obraz podstawowy nie jest dla ciebie ... Aby szybki CMD mógł go uruchomić, przypuszczałbym coś takiego dla basha:

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"

Lub to dla busybox:

CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

To jest miłe, ponieważ wyjdzie natychmiast na docker stop. Po prostu zwykły sleeplub catpotrwa kilka sekund, zanim kontener zostanie zamknięty.

itsafire
źródło
Dostosowałem obraz bazowy centos7 do ładowania PostgreSQL 11. Zaczynasz od wywołania / usr / pgsql-11 / bin / pg_ctl, ale pg_ctl kończy pracę, gdy serwer jest uruchomiony. Twoja sugestia użycia pułapki zadziałała świetnie; to ostatnia linia mojego skryptu pgstartwait.sh
Alchemistmatt
6

Przechwyć PID procesu ngnix w zmiennej (na przykład $ NGNIX_PID) i na końcu pliku punktu wejścia wykonaj

wait $NGNIX_PID 

W ten sposób twój kontener powinien działać, dopóki ngnix nie będzie żył, kiedy ngnix się zatrzyma, kontener również się zatrzyma

user2825611
źródło