Як зберегти контейнер Docker після запуску послуг?


156

Я бачив купу навчальних посібників, які, здається, роблять те саме, що я намагаюся зробити, але чомусь мій контейнер Docker виходить. В основному я налаштовую веб-сервер і кілька демонів всередині контейнера Docker. Я завершую частину цього за допомогою скрипту bash під назвою, run-all.shщо я пробігаю CMD у своєму Dockerfile. run-all.shвиглядає так:

service supervisor start
service nginx start

І я запускаю його всередині мого Dockerfile так:

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

Я бачу, що всі сервіси запускаються правильно, коли я запускаю речі вручну (тобто, переходжу до зображення за допомогою -i -t / bin / bash), і все виглядає так, що воно працює правильно, коли я запускаю зображення, але воно виходить один раз це закінчує запуск моїх процесів. Я хотів би, щоб процеси протікали нескінченно, і наскільки я розумію, контейнер повинен постійно працювати, щоб це відбулося. Тим не менш, коли я бігаю docker ps -a, я бачу:

➜  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

Що дає? Чому воно виходить? Я знаю, що я міг просто поставити цикл часу в кінці мого сценарію bash, щоб тримати його, але який правильний спосіб запобігти його виходу?


1
ви піддаєте порти служб зовні (опція -p для запуску докера)? (звичайно, це не завадить їм вийти)
ribamar

1
Я використовував ENTRYPOINT у своєму Dockerfile, і після запуску сценарію, визначеного в ENTRYPOINT (мій сценарій init), він з’явився в журналах, але мій контейнер, здавалося, був вихід. Отже, замість ENTRYPOINT я використовував команду RUN для запуску сценарію, і контейнер все ще працює у фоновому режимі.
ypahalajani

Відповіді:


50

Це не так, як ви повинні конструювати свої контейнери Docker.

Розробляючи контейнер Docker, ви повинні створити його таким чином, що запущений лише один процес (тобто у вас повинен бути один контейнер для Nginx і один для нагляду або додаток, який він працює); крім того, цей процес повинен працювати на першому плані.

Контейнер "вийде", коли сам процес завершиться (у вашому випадку цей процес - ваш скрипт bash).


Однак якщо вам дійсно потрібно (або ви хочете) запустити декілька сервісів у вашому контейнері Docker, подумайте, починаючи з "Базового зображення Docker" , який використовується runitяк псевдоінітальний процес ( runitзалишатиметься в Інтернеті, поки працюватимуть Nginx і Supervisor), який залишатиметься на передньому плані, поки ваші інші процеси роблять свою справу.

У них є багато документів, тому ви повинні мати можливість досягти того, що ви намагаєтеся зробити досить легко.


1
Чи можете ви пояснити, чому мені повинен працювати лише один сервіс? Я можу додати nginx до супервізора, якщо це необхідно, але не впевнений, чому це потрібно.
Елі

3
@Eli Коротка відповідь - це те, як працює Докер. Докер виконуватиме лише один процес (та його діти) на контейнер. Рекомендується, щоб цей процес був фактичним процесом подання заявки (так що, якщо він закінчується, Докер знає), але ви дійсно можете використовувати супервізор як цей процес. Зауважте, що вам доведеться налаштувати супервізор для запуску на передньому плані (тобто не демонізувати), що робиться через --nodaemonопцію.
Томас Орозько

1
@Eli Цей допис у блозі Docker робить випадок, що запуск декількох процесів (і, загалом кажучи, перегляд контейнера як "малого VPS") є неоптимальним. У вашому випадку тема коментаря, ймовірно, буде більш доречною, ніж фактична публікація в блозі.
Томас Орозько

1
Базове зображення Docker - це жахливе рішення для багатьох корпоративних проблем, оскільки мало серйозних компаній використовують ubuntu, віддаючи перевагу замість дерева RHEL / Centos.
Програмний інженер

9
"Мало серйозних компаній" здається невиправданим. Здавалося б, вибір ОС повністю ґрунтується на випадку використання. Будь-яка компанія має багато різних середовищ, включаючи внутрішнє використання розробників, внутрішнє використання співробітників, підтримку продажів, постановку, POC і, нарешті, виробництво (і навіть це невиразний термін). Я не вірю, що ОП згадувало їхню ситуацію використання, (вибачте, що це прискіпливо), але подібний коментар, схоже, є типом, який поширює дуже впевнену інформацію, не маючи аргументів, чому.
Джон Керрелл

155

Якщо ви використовуєте Dockerfile, спробуйте:

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

(Очевидно, що це лише для розробників, вам не потрібно підтримувати контейнер живим, якщо він не працює, наприклад, nginx ...)


5
Я використовував, CMD["sleep", "1d"]але ваше рішення здається кращим
Джордж Плігоропулос,

@GeorgiosPligoropoulos це буде застрявати в цьому рядку; можливо біг у фоновому режимі спрацює
Prashanth Sams

5
Також можна використовувати CMD["sleep", "infinity"].
Ромен

5
або "кішка", але люди можуть сказати, що це зловживання тваринами. xD
lawphotog

Ви можете закінчити сценарій вхідної точки з exec tail -f /dev/nullвикористанням, але використання tailяк точки вступу - неправильна відповідь.
Торстен Бронгер

86

У мене просто була та сама проблема, і я дізнався, що якщо ви використовуєте ваш контейнер із прапором -tта -dпрапором, він продовжує працювати.

docker run -td <image>

Ось що роблять прапори (відповідно docker run --help):

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

Найважливіший - -tпрапор. -dпросто дозволяє запускати контейнер у фоновому режимі.


3
Я не можу це відтворити. Надайте, будь ласка, приклад? Чи є щось конкретне (наприклад: CMD) про Dockerfile, що нам потрібно для того, щоб це працювало?
Матей Сантана

2
Це для мене не вийшло. Я використовував команду, docker logs <image>щоб переконатися, що помилка викликає вихід мого контейнера докера. Статус виходу є, 0а останній вихід - це підтвердження, що мій lighttpdсервер працює:[ ok ] Starting web server: lighttpd.
ob1

Я вже деякий час не працюю з Docker. Тож можливо, що інтерфейс командного рядка змінився і ця команда більше не працює.
arne.z

4
Я можу підтвердити, що це справді працює з останньою версією докера. Якщо ви хочете пізніше приєднатись до цього сеансу, використання -dit також буде працювати.
Джон Гамільтон

1
@ Довгий скрипт не прийме до кінця start.sh tty, додати exec bashабо exec shякщо bash не встановлений. Тоді ви можете використовувати прапор -t
123

43

Причина, яку він закриває, полягає в тому, що сценарій оболонки запускається спочатку як PID 1, і коли це завершено, PID 1 відсутній, і докер працює лише тоді, коли PID 1 є.

Ви можете використовувати супервізор, щоб зробити все, якщо запустити прапор "-n", він не буде демонструватись, тому він залишиться як перший процес:

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

І ваш 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

Тоді ви можете мати стільки інших процесів, скільки хочете, і керівник буде виконувати перезапуск їх, якщо це потрібно.

Таким чином, ви можете використовувати нагляд в тих випадках, коли вам можуть знадобитися nginx і php5-fpm, і не має особливого сенсу їх розділяти.


Де в документах написано, якщо PID 1 закінчує контейнер докера перестає працювати?
8oh8

@ 8oh8 По суті, так працюють простори імен процесів; це не конкретно для Докера настільки, як "річ, що лежить в основі всіх контейнерів". Від 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

ви можете запустити просто catбез будь-яких аргументів, як згадував bro @ Sa'ad, щоб просто тримати контейнер у роботі [насправді нічого не робити, окрім чекання на введення користувача] (плагін Jenkins 'Docker робить те саме)


на додаток до моєї відповіді: але розумійте, що docker-compose (не демонізований) використовується, щоб показати вам робочий процес вашого контейнера, тому це може бути зручно вводити файли журналу ваших запущених служб. ура
Сергій Великанов

1
або cat. Плагін докер для jenkin робить це.
Саад

12

Переконайтеся, що ви додали daemon off;до себе nginx.conf або запускаєте його CMD ["nginx", "-g", "daemon off;"]відповідно до офіційного зображення nginx

Потім використовуйте наступне, щоб запустити як Supervisor як службу, так і nginx як процес переднього плану, який не дозволить контейнеру виходити

service supervisor start && nginx

У деяких випадках вам потрібно буде мати більше одного процесу у вашому контейнері, тому примушування контейнера мати точно один процес не працюватиме і може створювати більше проблем при розгортанні.

Тож вам потрібно зрозуміти компроміси і прийняти своє рішення відповідно.


7

Мотивація:

Немає нічого поганого в запуску декількох процесів всередині докерного контейнера . Якщо ви любите використовувати докер як легку машину - так і нехай. Інші люблять розділяти свої програми на мікросервіси. Мені здається: Стек LAMP в одному контейнері? Просто здорово.

Відповідь:

Дотримуйтесь хорошого базового зображення, як базового зображення phusion . Можуть бути й інші. Будь ласка, прокоментуйте.

І це лише чергове благання керівника. Тому що базовий образ фьюзії забезпечує супервізора, окрім деяких інших речей, таких як налаштування крона та локалізації. Речі, які вам подобаються налаштовувати під час роботи такої невеликої ваги VM. Для того, що це варто, він також забезпечує ssh-з'єднання в контейнер.

Саме зображення фьюзії просто запуститься і продовжить працювати, якщо ви опублікуєте цей базовий оператор запуску докер:

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

Або мертвий простий:

Якщо базове зображення не для вас ... Щоб швидкий CMD не працював, я б припустив щось подібне для bash:

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

Або це для busbox:

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

Це приємно, тому що воно вийде негайно на a docker stop. Просто просто sleepабо catпройде кілька секунд, перш ніж контейнер вийде.


Я налаштував базове зображення centos7 для завантаження PostgreSQL 11. Ви почнете це з виклику / usr / pgsql-11 / bin / pg_ctl, але pg_ctl виходить, коли сервер працює. Ваша пропозиція використовувати пастку спрацювала чудово; це останній рядок мого сценарію pgstartwait.sh
Alchemistmatt

6

Захопіть PID процесу ngnix змінною (наприклад, $ NGNIX_PID) і в кінці файлу точки введення зробіть

wait $NGNIX_PID 

Таким чином, ваш контейнер повинен працювати, поки ngnix живий, коли ngnix зупиняється, контейнер також зупиняється


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.