Dump-init -это простой руководитель процесса и система init, предназначенная для работы в виде PID 1 внутри минимальных контейнерных сред (таких как Docker). Он развернут как небольшой, статически связанный бинар, написанный в C.
Легкие контейнеры популяризировали идею запуска одного процесса или обслуживания без нормальных систем инициации, таких как SystemD или Sysvinit. Тем не менее, пропущение системы init часто приводит к неправильной обработке процессов и сигналов и может привести к таким проблемам, как контейнеры, которые нельзя изящно остановить, или протекающие контейнеры, которые должны были быть уничтожены.
dumb-init позволяет вам просто префикс вашей команды с dumb-init . Он действует как PID 1 и сразу же появляет вашу команду в качестве дочернего процесса, заботясь о правильной обработке и направленных сигналах по мере их получения.
Обычно, когда вы запускаете контейнер Docker, процесс выполнения, который вы выполняете, становится PID 1, давая ему причуды и обязанности, которые поставляются с системой инициализации для контейнера.
Есть две общие проблемы, которые это представляет:
В большинстве случаев сигналы не будут обращаться должным образом.
Ядро Linux применяет специальную обработку сигналов к процессам, которые работают как PID 1.
Когда процессы отправляются сигналом в нормальной системе Linux, ядро сначала проверит на наличие любых пользовательских обработчиков, которые процесс зарегистрировал для этого сигнала, и в противном случае возвращается к поведению по умолчанию (например, убийство процесса на SIGTERM ).
Однако, если процесс, получающий сигнал, является PID 1, он получает специальную обработку ядром; Если он не зарегистрировал обработчик для сигнала, ядро не вернется к поведению по умолчанию, и ничего не произойдет. Другими словами, если ваш процесс явно не обрабатывает эти сигналы, отправка его SIGTERM не будет иметь никакого эффекта.
Распространенным примером является задание CI, которые docker run my-container script : отправка SIGTERM в процесс docker run обычно убивает команду docker run , но оставляйте контейнер, работающий на заднем плане.
Процессы зомби с осиротеими не приобретаются должным образом.
Процесс становится зомби, когда он выходит, и остается зомби, пока его родитель не вызовет некоторый вариант системы системы wait() . Он остается в таблице процессов как «несуществующий» процесс. Как правило, родительский процесс будет сразу же позвонить в wait() и избегать длинных зомби.
Если родитель выходит из -за своего ребенка, ребенок «осиротет» и переплачивается под PID 1. Таким образом, система init отвечает за wait() -инскую на осиротевших процессах зомби.
Конечно, большинство процессов не будут wait() на случайных процессах, которые становятся прикрепленными к ним, поэтому контейнеры часто заканчиваются десятками зомби, укоренившимися в PID 1.
dumb-init dumb-init работает как PID 1, действуя как простая система инициации. Он запускает один процесс, а затем все прокси получили сигналы на сессию, основанную на этом дочернем процессе.
Поскольку ваш фактический процесс больше не является PID 1, когда он получает сигналы от dumb-init , обработатели сигналов по умолчанию будут применяться, и ваш процесс будет вести себя так, как и следовало ожидать. Если ваш процесс умрет, dumb-init также умрет, заботясь о очистке любых других процессов, которые все еще могут остаться.
В своем режиме по умолчанию dumb-init устанавливает сеанс, основанный на ребенке, и отправляет сигналы во всю группу процессов. Это полезно, если у вас есть плохого достоинства ребенка (например, сценарий оболочки), который обычно не будет сигнализировать его детей до смерти.
На самом деле это может быть полезно за пределами контейнеров Docker в регулярных руководителях процессов, таких как Daemontools или Supersord для контроля сценариев оболочки. Обычно сигнал, подобный SIGTERM полученный оболочкой, не направляется в подпроцессы; Вместо этого умирает только процесс оболочки. С тупаемостью, вы можете просто написать сценарии оболочки с глупостью в Шебанге:
#!/usr/bin/dumb-init /bin/sh
my-web-server & # launch a process in the background
my-other-server # launch another process in the foreground
Обычно SIGTERM , отправленный в оболочку, убил бы оболочку, но оставит эти процессы запускаемыми (как фон, так и на переднем плане!). С тупаемостью ваши подпроцессы получат те же сигналы, которые делает ваши оболочки.
Если вы хотите, чтобы сигналы были отправлены только прямым ребенку, вы можете запустить аргумент --single-child или установить переменную среды DUMB_INIT_SETSID=0 при запуске dumb-init . В этом режиме тупой инит полностью прозрачен; Вы можете даже связать несколько вместе (например dumb-init dumb-init echo 'oh, hi' ).
Dump-init позволяет переписать входящие сигналы, прежде чем их обеспечить. Это полезно в тех случаях, когда у вас есть руководитель Docker (например, мезос или Kubernetes), который всегда посылает стандартный сигнал (например, Sigterm). Некоторые приложения требуют другого сигнала остановки, чтобы сделать изящную очистку.
Например, чтобы переписать сигнал Sigterm (номер 15) в Sigquit (номер 3), просто добавьте --rewrite 15:3 в командную строку.
Чтобы полностью отбросить сигнал, вы можете переписать его на специальное число 0 .
При запуске в режиме SETSID в большинстве случаев недостаточно для пересылки SIGTSTP / SIGTTIN / SIGTTOU , поскольку, если процесс не добавил пользовательский обработчик сигналов для этих сигналов, то ядро не будет применять поведение по обработке сигналов по умолчанию (что будет приостановить процесс), так как это является членом орфорированной группы процессов. По этой причине мы устанавливаем переписывание по умолчанию в SIGSTOP из этих трех сигналов. Вы можете отказаться от этого поведения, при желании переписывая сигналы к их первоначальным ценностям.
Одно предостережение с этой функцией: для сигналов управления работой ( SIGTSTP , SIGTTIN , SIGTTOU ), Dump-init всегда приостановит себя после получения сигнала, даже если вы переписываете его на что-то еще.
У вас есть несколько вариантов использования dumb-init :
Многие популярные дистрибутивы Linux (включая Debian (с момента stretch ) и деривативы Debian, такие как Ubuntu (с момента bionic )), теперь содержат тупые пакеты в своих официальных репозиториях.
На Debian Distributions вы можете запустить apt install dumb-init для установки DumpeNit, как вы установили любой другой пакет.
Примечание. Наиболее расколотые версии тупого инимы не связаны статически, в отличие от версий, которые мы предоставляем (см. Другие варианты ниже). Как правило, это прекрасно, но означает, что эти версии тупого инитра, как правило, не будут работать при копировании в другие дистрибуты Linux, в отличие от статически связанных версий, которые мы предоставляем.
Если у вас есть внутренний сервер APT, загрузка .deb на ваш сервер является рекомендуемым способом использования dumb-init . В ваших Dockerfiles вы можете просто apt install dumb-init и это будет доступно.
Пакеты Debian доступны с вкладки GitHub Releseses, или вы можете запустить make builddeb самостоятельно.
.deb вручную (Debian/Ubuntu) Если у вас нет внутреннего сервера APT, вы можете использовать dpkg -i для установки пакета .deb . Вы можете выбрать, как вы получите .deb на свой контейнер (устанавливая каталог или wget -ing, это некоторые варианты).
Одна возможность - со следующими командами в вашем Dockerfile:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.debПоскольку Dump-init выпускается в виде статически связанного двоичного файла, вы обычно можете просто подтолкнуть его к своим изображениям. Вот пример этого в Dockerfile:
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-init Хотя dumb-init полностью написан в C, мы также предоставляем пакет Python, который компилируется и устанавливает бинар. Он может быть установлен из PYPI с помощью pip . Вы захотите сначала установить компилятор C (на Debian/ubuntu, достаточно apt-get install gcc ), а затем просто pip install dumb-init .
По состоянию на 1.2.0 пакет в PYPI доступен в виде предварительно созданного колесного архива и не нужно собирать на обычных распределениях Linux.
После установки внутри вашего контейнера Docker просто префиксуйте свои команды с dumb-init (и убедитесь, что вы используете рекомендуемый синтаксис JSON).
В Dockerfile это хорошая практика, чтобы использовать DumpeEnit в качестве точки входа вашего контейнера. «Точка входа»-это частичная команда, которая готовится к вашей инструкции CMD , что делает ее отличной подходящей для глупости:
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD [ "/my/script" , "--with" , "--args" ] Если вы объявляете точку входа на базовом изображении, любые изображения, которые спускаются от нее, не должны также объявлять глупости. Они могут просто установить CMD , как обычно.
Для интерактивного одноразового использования вы можете просто приготовить его вручную:
$ docker run my_container dumb-init python -c 'while True: pass'
Запуск этой же команды без dumb-init приведет к тому, что не сможет остановить контейнер без SIGKILL , но с dumb-init вы можете отправить ему более гуманные сигналы, такие как SIGTERM .
Важно, чтобы вы использовали синтаксис JSON для CMD и ENTRYPOINT . В противном случае Docker вызывает оболочку, чтобы запустить вашу команду, в результате чего оболочка как PID 1 вместо глупости.
Часто контейнеры хотят выполнять предварительную работу, которую нельзя сделать во время строительства. Например, вы, возможно, захотите сделать шаблон некоторых файлов конфигурации на основе переменных среды.
Лучший способ интегрировать это с Dump-init-это следующее:
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
CMD [ "bash" , "-c" , "do-some-pre-start-thing && exec my-server" ]По-прежнему используя Dump-init в качестве точки входа, у вас всегда есть правильная система init.
Часть exec команды Bash важна, потому что она заменяет процесс Bash на вашем сервере, так что оболочка существует только на мгновение в начале.
Создание глупого бинарника требует рабочего компилятора и заголовков LIBC и по умолчанию для GLIBC.
$ make
Статически скомпилированная глупость составляет более 700 КБ из-за GLIBC, но теперь MUSL-это вариант. На Debian/Ubuntu apt-get install musl-tools
$ CC=musl-gcc make
Когда статически составлен с Musl, бинарный размер составляет около 20 КБ.
Мы используем стандартные соглашения Debian для определения зависимостей для сборки (посмотрите в debian/control ). Легкий способ начать работу apt-get install build-essential devscripts equivs , а затем sudo mk-build-deps -i --remove удалить все отсутствующие зависимости от сборки сборки автоматически. Затем вы можете использовать make builddeb для создания Debian Debian Packages.
Если вы предпочитаете автоматизированную сборку пакета Debian с помощью Docker, просто запустите make builddeb-docker . Это проще, но требует, чтобы у вас работал Docker на вашей машине.