Получение уведомления о том, что контейнеры Docker не используются, является одним из худших способов провести ночь. В сегодняшней статье мы обсудим, как использовать политику перезапуска Docker для автоматического перезапуска контейнеров и предотвращения таких ночных уведомлений.
Что происходит при сбое приложения?
Прежде чем мы начнем с политики перезапуска Docker, давайте немного разберемся, как Docker ведет себя при сбое приложения. Чтобы облегчить это, мы создадим контейнер Docker, который выполняет простой сценарий bash с именем crash.sh
.
1
2
3
|
#/bin/bash sleep 30 exit 1 |
Вышеприведенный скрипт прост; при запуске он будет sleep
30
секунд, а затем выйдет с кодом выхода 1
указывающим на ошибку.
Сборка и запуск пользовательского контейнера
Чтобы запустить этот скрипт в контейнере, нам нужно создать собственный контейнер Docker, который включает в crash.sh
скрипт crash.sh
. Чтобы создать собственный контейнер, нам сначала нужно создать простой Dockerfile
.
1
|
$ vi Dockerfile |
Dockerfile
будет содержать следующие три строки:
1
2
3
|
FROM ubuntu: 14.04 ADD 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 kB Step 1 : FROM ubuntu: 14.04 ---> e36c55082fa6 Step 2 : ADD crash.sh / ---> eb6057d904ef Removing intermediate container 5199db00ba76 Step 3 : CMD /bin/bash /crash.sh ---> Running in 01e6f5e12c3f ---> 0e2f4ac52f19 Removing intermediate container 01e6f5e12c3f Successfully built 0e2f4ac52f19 |
Эта команда построения создала образ Docker с testing_restarts
именем testing_restarts
. Теперь мы можем запустить контейнер, используя образ testing_restarts
, выполнив testing_restarts
docker run
.
1
2
|
$ sudo docker run -d --name testing_restarts testing_restarts a35bb16634a029039c8b34dddba41854e9a5b95222d32e3ca5624787c4c8914a |
Из вышесказанного видно, что Docker смог запустить контейнер с именем testing_restarts
. Давайте проверим состояние этого контейнера, запустив docker ps
.
1
2
|
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS |
Команда docker ps
не показывает никаких запущенных контейнеров. Причина этого заключается в том, что docker ps
по умолчанию показывает только запущенные контейнеры. Давайте посмотрим на работающие и не работающие контейнеры, используя флаг -a
.
1
2
3
|
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a35bb16634a0 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_restarts 8320e96172e4403cf6527df538fb7054accf3a55513deb12bb6a5535177c1f19 |
В приведенной выше команде мы указали, что Docker должен применять политику always
перезапуска к этому контейнеру с помощью флага --restart
. Давайте посмотрим, как это повлияет на наш контейнер, выполнив docker ps
снова.
1
2
3
|
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8320e96172e4 testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 21 seconds |
На этот раз мы видим, что контейнер запущен и работает, но только в течение 21
секунды. Если мы снова запустим docker ps
, мы увидим что-то интересное.
1
2
3
|
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8320e96172e4 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_restarts 85ff2f096bac9965a9b8cffbb73c1642bf7b64a2173bbd145961231861b95819 |
Если мы запустим docker ps
в течение минуты после запуска контейнера, мы увидим, что контейнер запущен и был недавно запущен.
1
2
3
|
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 85ff2f096bac testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 8 seconds |
Однако это не будет верно, если мы запустим команду docker ps
3
минуты после запуска контейнера.
1
2
3
|
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 85ff2f096bac 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/bash sleep 30 exit 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 kB Step 1 : FROM ubuntu: 14.04 ---> e36c55082fa6 Step 2 : ADD crash.sh / ---> a4e7e4ad968f Removing intermediate container 88115fe05456 Step 3 : CMD /bin/bash /crash.sh ---> Running in fc8bbaffd9b9 ---> 8aaa3d99f432 Removing intermediate container fc8bbaffd9b9 Successfully built 8aaa3d99f432 |
С восстановлением образа контейнера, давайте снова запустим этот контейнер с теми же настройками при on-failures
и max-retries
.
1
2
|
$ sudo docker run -d --name testing_restarts --restart on-failure: 5 testing_restarts f0052e0c509dfc1c1b112c3b3717c23bc66db980f222144ca1c9a6b51cabdc19 |
На этот раз, когда мы выполняем docker ps -a
, мы должны увидеть разные результаты.
1
2
3
|
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f0052e0c509d 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_restarts 676f12c9cd4cac7d3dd84d8b70734119ef956b3e5100b2449197c2352f3c4a55 |
Если мы подождем несколько минут и снова запустим docker ps -a
, мы увидим, что контейнер был перезапущен даже после успешного завершения кода завершения.
1
2
3
|
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9afad0ccd068 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 -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 676f12c9cd4c testing_restarts "/bin/sh -c '/bin/bas" 9 minutes ago Up 2 seconds |
С политикой always
перезапуска это не так. Политика always
перезапуска всегда перезапускает контейнер. Это верно, даже если контейнер был остановлен перед перезагрузкой. Давайте посмотрим на этот сценарий в действии.
1
2
3
|
$ sudo docker stop testing_restarts testing_restarts $ sudo reboot |
Перед перезагрузкой нашей системы мы просто остановили контейнер. Это означает, что контейнер все еще там, но не работает. Однако после перезагрузки системы контейнер будет работать.
1
2
3
|
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 676f12c9cd4c 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_restarts fec5be52b9559b4f6421b10fe41c9c1dc3a16ff838c25d74238c5892f2b0b36 |
Запустив контейнер, давайте остановим его и снова перезагрузим систему.
1
2
3
|
$ sudo docker stop testing_restarts testing_restarts $ sudo reboot |
На этот раз, когда система перезапустится, мы должны увидеть, что контейнер находится в остановленном состоянии.
1
2
3
|
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fec5be52b955 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_restarts testing_restarts $ sudo reboot |
После этой перезагрузки контейнер должен работать.
1
2
3
|
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fec5be52b955 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 . |