Получение уведомления о том, что контейнеры Docker не используются, является одним из худших способов провести ночь. В сегодняшней статье мы обсудим, как использовать политику перезапуска Docker для автоматического перезапуска контейнеров и предотвращения таких ночных уведомлений.
Что происходит при сбое приложения?
Прежде чем мы начнем с политики перезапуска Docker, давайте немного разберемся, как Docker ведет себя при сбое приложения. Чтобы облегчить это, мы создадим контейнер Docker, который выполняет простой сценарий bash с именем crash.sh .
|
1
2
3
|
#/bin/bashsleep 30exit 1 |
Вышеприведенный скрипт прост; при запуске он будет sleep 30 секунд, а затем выйдет с кодом выхода 1 указывающим на ошибку.
Сборка и запуск пользовательского контейнера
Чтобы запустить этот скрипт в контейнере, нам нужно создать собственный контейнер Docker, который включает в crash.sh скрипт crash.sh . Чтобы создать собственный контейнер, нам сначала нужно создать простой Dockerfile .
|
1
|
$ vi Dockerfile |
Dockerfile будет содержать следующие три строки:
|
1
2
3
|
FROM ubuntu:14.04ADD crash.sh /CMD /bin/bash /crash.sh |
Приведенный выше Dockerfile создаст контейнер на основе последней версии ubuntu:14.04 . Он также добавит скрипт crash.sh в каталог / контейнера. Последняя строка сообщает Docker, что нужно запускать скрипт crash.sh при crash.sh контейнера.
Dockerfile , теперь мы можем собрать наш пользовательский контейнер с помощью команды docker build .
|
01
02
03
04
05
06
07
08
09
10
11
12
|
$ sudo docker build -t testing_restarts ./Sending build context to Docker daemon 3.072 kBStep 1 : FROM ubuntu:14.04 ---> e36c55082fa6Step 2 : ADD crash.sh / ---> eb6057d904efRemoving intermediate container 5199db00ba76Step 3 : CMD /bin/bash /crash.sh ---> Running in 01e6f5e12c3f ---> 0e2f4ac52f19Removing intermediate container 01e6f5e12c3fSuccessfully built 0e2f4ac52f19 |
Эта команда построения создала образ Docker с testing_restarts именем testing_restarts . Теперь мы можем запустить контейнер, используя образ testing_restarts , выполнив testing_restarts docker run .
|
1
2
|
$ sudo docker run -d --name testing_restarts testing_restartsa35bb16634a029039c8b34dddba41854e9a5b95222d32e3ca5624787c4c8914a |
Из вышесказанного видно, что Docker смог запустить контейнер с именем testing_restarts . Давайте проверим состояние этого контейнера, запустив docker ps .
|
1
2
|
$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS |
Команда docker ps не показывает никаких запущенных контейнеров. Причина этого заключается в том, что docker ps по умолчанию показывает только запущенные контейнеры. Давайте посмотрим на работающие и не работающие контейнеры, используя флаг -a .
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa35bb16634a0 testing_restarts "/bin/sh -c '/bin/bas" 9 minutes ago Exited (1) 8 minutes ago |
С результатами docker ps мы видим, что при выходе из приложения в контейнере Docker этот контейнер также останавливается. Это означает, что по умолчанию, если приложение, работающее в контейнере, аварийно завершает работу, контейнер останавливается, и этот контейнер останется остановленным, пока кто-то или что-то не перезапустит его.
! Новый призыв к действию
Изменение поведения докера по умолчанию
Можно автоматически перезапустить разбитые контейнеры, указав политику перезапуска при запуске контейнера. Чтобы лучше понять политики перезапуска, давайте посмотрим, что происходит, когда мы используем политику перезапуска always с этим же контейнером.
|
1
2
|
$ sudo docker run -d --name testing_restarts --restart always testing_restarts8320e96172e4403cf6527df538fb7054accf3a55513deb12bb6a5535177c1f19 |
В приведенной выше команде мы указали, что Docker должен применять политику always перезапуска к этому контейнеру с помощью флага --restart . Давайте посмотрим, как это повлияет на наш контейнер, выполнив docker ps снова.
|
1
2
3
|
$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8320e96172e4 testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 21 seconds |
На этот раз мы видим, что контейнер запущен и работает, но только в течение 21 секунды. Если мы снова запустим docker ps , мы увидим что-то интересное.
|
1
2
3
|
$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8320e96172e4 testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 19 seconds |
Второй запуск показывает, что контейнер был только в течение 19 секунд. Это означает, что даже если наше приложение ( crash.sh ) продолжает выходить с ошибкой, Docker постоянно перезапускает контейнер при каждом выходе.
Теперь, когда мы понимаем, как политики перезапуска могут использоваться для изменения поведения Docker по умолчанию, давайте посмотрим, какие политики перезапуска доступны в Docker.
Политика перезапуска докера
В настоящее время Docker имеет четыре политики перезапуска:
-
no -
on-failure -
unless-stopped -
always
Политика no является политикой перезапуска по умолчанию и просто не перезапускает контейнер ни при каких обстоятельствах.
Перезапуск при неудаче, но остановка при успехе
Политика on-failure немного интересна, поскольку она позволяет Docker перезапустить контейнер, если код завершения указывает на ошибку, но не если код выхода указывает на успех. Вы также можете указать максимальное количество раз, когда Docker автоматически перезапускает контейнер.
Давайте попробуем эту политику перезапуска с нашим контейнером testing_restarts и установим ограничение в 5 перезапусков.
|
1
2
|
$ sudo docker run -d --name testing_restarts --restart on-failure:5 testing_restarts85ff2f096bac9965a9b8cffbb73c1642bf7b64a2173bbd145961231861b95819 |
Если мы запустим docker ps в течение минуты после запуска контейнера, мы увидим, что контейнер запущен и был недавно запущен.
|
1
2
3
|
$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES85ff2f096bac testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 8 seconds |
Однако это не будет верно, если мы запустим команду docker ps 3 минуты после запуска контейнера.
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES85ff2f096bac testing_restarts "/bin/sh -c '/bin/bas" 3 minutes ago Exited (1) 20 seconds ago |
Из вышесказанного видно, что через 3 минуты контейнер останавливается. Это связано с тем, что контейнер был перезапущен больше, чем наша настройка max-retries .
С успехом
Преимущество при on-failures состоит в том, что, когда приложение завершает работу с успешным кодом завершения, контейнер не будет перезапущен. Давайте посмотрим на это в действии, сделав небольшое изменение в скрипте crash.sh .
|
1
|
$ vi crash.sh |
Изменением будет установка кода выхода на 0 .
|
1
2
3
|
#/bin/bashsleep 30exit 0 |
Установив скрипт для выхода с кодом выхода 0 , мы удалим индикатор ошибки из скрипта. То есть, насколько Docker может сказать, этот скрипт будет успешно выполняться каждый раз.
С измененным сценарием нам нужно будет перестроить контейнер, прежде чем мы сможем запустить его снова.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
$ sudo docker build -t testing_restarts ./Sending build context to Docker daemon 3.072 kBStep 1 : FROM ubuntu:14.04 ---> e36c55082fa6Step 2 : ADD crash.sh / ---> a4e7e4ad968fRemoving intermediate container 88115fe05456Step 3 : CMD /bin/bash /crash.sh ---> Running in fc8bbaffd9b9 ---> 8aaa3d99f432Removing intermediate container fc8bbaffd9b9Successfully built 8aaa3d99f432 |
С восстановлением образа контейнера, давайте снова запустим этот контейнер с теми же настройками при on-failures и max-retries .
|
1
2
|
$ sudo docker run -d --name testing_restarts --restart on-failure:5 testing_restartsf0052e0c509dfc1c1b112c3b3717c23bc66db980f222144ca1c9a6b51cabdc19 |
На этот раз, когда мы выполняем docker ps -a , мы должны увидеть разные результаты.
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESf0052e0c509d testing_restarts "/bin/sh -c '/bin/bas" 41 seconds ago Exited (0) 11 seconds ago |
Поскольку сценарий crash.sh с успешным кодом выхода ( 0 ), Docker воспринял это как успешное и не перезапустил контейнер.
Всегда перезапускать контейнер
Если мы хотим, чтобы контейнер перезапускался независимо от кода выхода, у нас есть пара политик перезапуска, которые мы могли бы использовать:
-
always -
unless-stopped
Политика always перезапуска говорит Docker перезапускать контейнер при любых обстоятельствах. Мы экспериментировали с политикой always перезапуска ранее, но давайте посмотрим, что произойдет, когда мы перезапустим текущий контейнер с политикой always перезапуска.
|
1
2
|
$ sudo docker run -d --name testing_restarts --restart always testing_restarts676f12c9cd4cac7d3dd84d8b70734119ef956b3e5100b2449197c2352f3c4a55 |
Если мы подождем несколько минут и снова запустим docker ps -a , мы увидим, что контейнер был перезапущен даже после успешного завершения кода завершения.
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9afad0ccd068 testing_restarts "/bin/sh -c '/bin/bas" 4 minutes ago Up 22 seconds |
Что хорошо в политике always перезапуска, так это то, что даже если наш хост Docker потерпит крах при загрузке, служба Docker перезапустит наш контейнер. Давайте посмотрим на это в действии, чтобы полностью оценить, почему это полезно.
|
1
|
$ sudo reboot |
По умолчанию или даже при on-failures наш контейнер не будет работать при перезагрузке. Что, в зависимости от того, какую задачу выполняет контейнер, может быть проблематичным.
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES676f12c9cd4c testing_restarts "/bin/sh -c '/bin/bas" 9 minutes ago Up 2 seconds |
С политикой always перезапуска это не так. Политика always перезапуска всегда перезапускает контейнер. Это верно, даже если контейнер был остановлен перед перезагрузкой. Давайте посмотрим на этот сценарий в действии.
|
1
2
3
|
$ sudo docker stop testing_restartstesting_restarts$ sudo reboot |
Перед перезагрузкой нашей системы мы просто остановили контейнер. Это означает, что контейнер все еще там, но не работает. Однако после перезагрузки системы контейнер будет работать.
|
1
2
3
|
$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES676f12c9cd4c testing_restarts "/bin/sh -c '/bin/bas" 11 minutes ago Up 24 seconds |
Причиной запуска нашего контейнера после перезагрузки является политика always . При каждом перезапуске службы Docker контейнеры, использующие политику always будут перезапущены независимо от того, были они запущены или сейчас.
Проблема в том, что перезапуск контейнера, который был ранее остановлен после перезагрузки, может быть немного проблематичным. Что если наш контейнер был остановлен по уважительной причине или, что еще хуже, что если контейнер устарел?
Решением для этого является политика перезапуска без unless-stopped .
Останавливаться только когда остановлен Docker
Политика перезапуска без остановок ведет себя так же, как и always с одним исключением. Когда контейнер останавливается и сервер перезагружается или перезапускается служба Docker, контейнер перезапускаться не будет.
Давайте посмотрим на это в действии, запустив контейнер с политикой unless-stopped и повторив наш последний пример.
|
1
2
|
$ sudo docker run -d --name testing_restarts --restart unless-stopped testing_restartsfec5be52b9559b4f6421b10fe41c9c1dc3a16ff838c25d74238c5892f2b0b36 |
Запустив контейнер, давайте остановим его и снова перезагрузим систему.
|
1
2
3
|
$ sudo docker stop testing_restartstesting_restarts$ sudo reboot |
На этот раз, когда система перезапустится, мы должны увидеть, что контейнер находится в остановленном состоянии.
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESfec5be52b955 testing_restarts "/bin/sh -c '/bin/bas" 2 minutes ago Exited (137) About a minute ago |
Один из важных моментов с параметром » unless-stopped заключается в том, что, если контейнер работал до перезагрузки, он будет перезапущен после перезапуска системы. Мы можем увидеть это в действии, перезапустив наш контейнер и перезагрузив систему снова.
|
1
2
3
|
$ sudo docker start testing_restartstesting_restarts$ sudo reboot |
После этой перезагрузки контейнер должен работать.
|
1
2
3
|
$ sudo docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESfec5be52b955 testing_restarts "/bin/sh -c '/bin/bas" 5 minutes ago Up 13 seconds testing_restarts |
Разница между always и unless-stopped может быть небольшой, но в некоторых средах это небольшое различие может быть критическим решением.
Выбор лучшей политики перезапуска
При выборе наилучшей политики перезапуска важно помнить, какой тип рабочей нагрузки выполняет контейнер.
Например, экземпляр Redis может быть критическим компонентом в вашей среде, который должен иметь политику always или unless-stopped . С другой стороны, приложение пакетной обработки может нуждаться в перезапуске до успешного завершения процесса. В этом случае имеет смысл использовать политику при on-failures .
В любом случае, с помощью политики перезапуска Docker вы можете быть уверены, что в следующий раз, когда хост Docker загадочно перезагрузится в 3 часа ночи, ваши контейнеры будут перезапущены.
| Ссылка: | Обеспечение того, чтобы контейнеры всегда выполнялись с политикой перезапуска Docker от нашего партнера JCG Бена Кейна в блоге Codeship Blog . |