Статьи

Обеспечение работы контейнеров всегда с политикой перезапуска Docker

Получение уведомления о том, что контейнеры 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 .