Инструменты пользователя

Инструменты сайта


docker:healthcheck:main

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
docker:healthcheck:main [2023/12/21 23:13]
werwolf [Проверка состояния контейнера в Dockerfile]
docker:healthcheck:main [2023/12/21 23:20] (текущий)
werwolf [Мораль]
Строка 36: Строка 36:
 Она практически никак не отличается от оной в Dockerfile: Она практически никак не отличается от оной в Dockerfile:
  
-YAML +<code ini>
 ... ...
 healthcheck:​ healthcheck:​
Строка 45: Строка 44:
   retries: 3   retries: 3
 ... ...
- +</code>
-| 1 2 3 4 5 6 7 | ... healthcheck:​   test: curl -sS http:%%//​%%127.0.0.1 %%||%% exit 1   interval:​ 5s   timeout:​ 10s   retries:​ 3 ... | +
 ==== Проверка в docker run и service create ==== ==== Проверка в docker run и service create ====
  
 У этих двух синтаксис проверок одинаковый и тоже предсказуемо понятный:​ У этих двух синтаксис проверок одинаковый и тоже предсказуемо понятный:​
  
-Shell +<code bash>
 docker run --health-cmd='​curl -sS http://​127.0.0.1 || exit 1' \ docker run --health-cmd='​curl -sS http://​127.0.0.1 || exit 1' \
     --health-timeout=10s \     --health-timeout=10s \
Строка 59: Строка 55:
     --health-interval=5s \     --health-interval=5s \
     ....     ....
- +</code>
-| 1 2 3 4 5 | docker run --health-cmd='​curl -sS http://127.0.0.1 %%||%% exit 1' \     --health-timeout=10s \     --health-retries=3 \     --health-interval=5s \     .... |+
  
 Кстати,​ если в Dockerfile образе,​ который мы запускаем,​ уже была проверка,​ то на этой стадии её можно переопределить или даже выключить с помощью ''​--no-healthcheck=true''​. Кстати,​ если в Dockerfile образе,​ который мы запускаем,​ уже была проверка,​ то на этой стадии её можно переопределить или даже выключить с помощью ''​--no-healthcheck=true''​.
Строка 71: Строка 66:
  
 server.js server.js
- +<code javascript>​
-JavaScript +
 "use strict";​ "use strict";​
-const http = require%%('%%http'​);​+const http = require('​http'​);​
  
 function createServer () { function createServer () {
-        ​return http.createServer(function (req, res) { + return http.createServer(function (req, res) { 
-                res.writeHead(200, ​%%{'%%Content-Type':​ '​text/​plain'​});​ + res.writeHead(200,​ {'​Content-Type':​ '​text/​plain'​});​ 
-                res.end%%('%%OK\n'​);​ + res.end('​OK\n'​);​ 
-        }).listen(8080);​+ }).listen(8080);​
 } }
  
Строка 87: Строка 80:
  
 http.createServer(function (req, res) { http.createServer(function (req, res) {
-        ​res.writeHead(200, ​%%{'%%Content-Type':​ '​text/​plain'​});​ + res.writeHead(200,​ {'​Content-Type':​ '​text/​plain'​});​ 
-        if (server) { + if (server) { 
-                server.close();​ + server.close();​ 
-                server = null; + server = null; 
-                res.end%%('%%Shutting down...\n'​);​ + res.end('​Shutting down...\n'​);​ 
-        } else { + } else { 
-                server = createServer();​ + server = createServer();​ 
-                res.end%%('%%Starting up...\n'​);​ + res.end('​Starting up...\n'​);​ 
-        }+ }
 }).listen(8081);​ }).listen(8081);​
- +</code>
-| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | "use strict";​ const http = require('​http'​);​   function createServer () { return http.createServer(function (req, res) { res.writeHead(200,​ {'​Content-Type':​ 'text/plain'​});​ res.end('​OK\n'​);​ }).listen(8080);​ }   let server = createServer();​   http.createServer(function (req, res) { res.writeHead(200,​ {'​Content-Type':​ '​text/​plain'​});​ if (server) { server.close();​ server = null; res.end('​Shutting down...\n'​);​ } else { server = createServer();​ res.end('​Starting up...\n'​);​ } }).listen(8081);​ |+
  
 Примерно так: Примерно так:
- +<code bash>
-Shell +
 $ node server.js $ node server.js
 # switch to another terminal # switch to another terminal
Строка 116: Строка 106:
 curl 127.0.0.1:​8080 curl 127.0.0.1:​8080
 # OK # OK
- +</​code>​
-| 1 2 3 4 5 6 7 8 9 10 11 12 | $ node server.js # switch to another terminal curl 127.0.0.1:​8080 # OK curl 127.0.0.1:​8081 # Shutting down... curl 127.0.0.1:​8080 # curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused curl 127.0.0.1:​8081 # Starting up... curl 127.0.0.1:​8080 # OK |+
  
 Теперь положим этот server.js в Dockerfile, добавим туда HEALTHCHECK,​ соберём это всё в образ по имени ''​server''​ и запустим:​ Теперь положим этот server.js в Dockerfile, добавим туда HEALTHCHECK,​ соберём это всё в образ по имени ''​server''​ и запустим:​
  
 Dockerfile Dockerfile
- +<code ini>
-INI +
 FROM node FROM node
  
Строка 134: Строка 121:
  
 CMD [ "​node",​ "/​server.js"​ ] CMD [ "​node",​ "/​server.js"​ ]
 +</​code>​
  
-| 1 2 3 4 5 6 7 8 9 | FROM node   COPY server.js /   EXPOSE 8080 8081   HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl -sS 127.0.0.1:​8080 %%|%%%%|%% exit 1   CMD [ "​node",​ "/​server.js"​ ] | +<code bash>
- +
-Shell +
 $ docker build . -t server:​latest $ docker build . -t server:​latest
 # Lots, lots of output # Lots, lots of output
Строка 145: Строка 130:
 $ curl 127.0.0.1:​8080 $ curl 127.0.0.1:​8080
 # OK # OK
- +</​code>​
-| 1 2 3 4 5 6 | $ docker build . -t server:​latest # Lots, lots of output $ docker run -d --rm -p 8080:8080 -p 8081:8081 server # ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b $ curl 127.0.0.1:​8080 # OK |+
  
 Первых трём символов айдишки контейнера — ''​ec3''​ — нам будет достаточно,​ чтобы к нему обратиться,​ так что самое время перейти к проверкам. Первых трём символов айдишки контейнера — ''​ec3''​ — нам будет достаточно,​ чтобы к нему обратиться,​ так что самое время перейти к проверкам.
Строка 154: Строка 138:
 Основная команда для запроса состояния контейнера в Docker — ''​docker inspect''​. Она много чего может рассказать,​ но нам всего-то нужно свойство ''​State.Health'':​ Основная команда для запроса состояния контейнера в Docker — ''​docker inspect''​. Она много чего может рассказать,​ но нам всего-то нужно свойство ''​State.Health'':​
  
-Shell+<code bash> 
 +$ docker build . -t server:​latest 
 +# Lots, lots of output 
 +$ docker run -d --rm -p 8080:8080 -p 8081:8081 server 
 +# ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b 
 +$ curl 127.0.0.1:​8080 
 +# OK 
 +</​code>​
  
-$ docker inspect ec3 | jq '​.[].State.Health'​ 
-#{ 
-#  "​Status":​ "​healthy",​ 
-#  "​FailingStreak":​ 0, 
-#  "​Log":​ [ 
-#    { 
-#      "​Start":​ "​2017-06-27T04:​07:​03.975506353Z",​ 
-#      "​End":​ "​2017-06-27T04:​07:​04.070844091Z",​ 
-#      "​ExitCode":​ 0, 
-#      "​Output":​ "​OK\n"​ 
-#    }, 
-#... 
-} 
  
-| 1 2 3 4 5 6 7 8 9 10 11 12 13 | $ docker inspect ec3 %%|%% jq '​.[].State.Health'​ #{ #​  "​Status":​ "​healthy",​ #​  "​FailingStreak":​ 0, #​  "​Log":​ [ #    { #​      "​Start":​ "​2017-06-27T04:​07:​03.975506353Z",​ #​      "​End":​ "​2017-06-27T04:​07:​04.070844091Z",​ #​      "​ExitCode":​ 0, #​      "​Output":​ "​OK\n"​ #    }, #... } | 
  
 Вполне предсказуемо,​ контейнер — в ‘healthy’ состоянии,​ а в ''​Log''​ даже можно посмотреть,​ чем отзывался сервер на запросы. Но если мы теперь отправим запрос на 8081 и подождём 3*5 секунд (3 проверки подряд),​ то кое-что изменится в интересную сторону. Вполне предсказуемо,​ контейнер — в ‘healthy’ состоянии,​ а в ''​Log''​ даже можно посмотреть,​ чем отзывался сервер на запросы. Но если мы теперь отправим запрос на 8081 и подождём 3*5 секунд (3 проверки подряд),​ то кое-что изменится в интересную сторону.
  
-Shell +<code bash>
 $ curl 127.0.0.1:​8081 $ curl 127.0.0.1:​8081
 # Shutting down... # Shutting down...
Строка 193: Строка 169:
 #  ] #  ]
 #} #}
- +</​code>​
-| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ curl 127.0.0.1:​8081 # Shutting down... # Через 15 секунд $ docker inspect ec3 %%|%% jq '​.[].State.Health'​ #{ #​  "​Status":​ "​unhealthy",​ #​  "​FailingStreak":​ 4, #​  "​Log":​ [ #  ... #   { #​      "​Start":​ "​2017-06-27T04:​16:​27.668441692Z",​ #​      "​End":​ "​2017-06-27T04:​16:​27.740937964Z",​ #​      "​ExitCode":​ 1, #​      "​Output":​ "curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused\n"​ #    } #  ] #} |+
  
 Я промазал мимо 15 секунд и пропустил аж четыре проверки,​ поэтому значение в ''​FailingStreak''​ стало равно четырём. Но в остальном Нострадамус не врал — контейнер перешёл в разряд ‘unhealthy’. Я промазал мимо 15 секунд и пропустил аж четыре проверки,​ поэтому значение в ''​FailingStreak''​ стало равно четырём. Но в остальном Нострадамус не врал — контейнер перешёл в разряд ‘unhealthy’.
Строка 200: Строка 175:
 Правда,​ достаточно лишь одной успешной проверки,​ чтобы контейнер официально воскрес:​ Правда,​ достаточно лишь одной успешной проверки,​ чтобы контейнер официально воскрес:​
  
-Shell +<code bash>
 $ curl 127.0.0.1:​8081 $ curl 127.0.0.1:​8081
 # Starting up... # Starting up...
 $ docker inspect ec3 | jq '​.[].State.Health.Status'​ $ docker inspect ec3 | jq '​.[].State.Health.Status'​
 # "​healthy"​ # "​healthy"​
- +</​code>​
-| 1 2 3 4 | $ curl 127.0.0.1:​8081 # Starting up... $ docker inspect ec3 %%|%% jq '​.[].State.Health.Status'​ # "​healthy"​ | +
 ==== Узнаём статус контейнера из Docker events ==== ==== Узнаём статус контейнера из Docker events ====
  
 Кроме как спрашивать контейнер о его здоровье напрямую,​ можно спросить у его соседей — ''​docker events'':​ Кроме как спрашивать контейнер о его здоровье напрямую,​ можно спросить у его соседей — ''​docker events'':​
  
-Shell +<code bash>
 $ docker events --filter event=health_status $ docker events --filter event=health_status
 # 2017-06-27T00:​23:​03.691677875-04:​00 container health_status:​ healthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server,​ name=eager_swartz) # 2017-06-27T00:​23:​03.691677875-04:​00 container health_status:​ healthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server,​ name=eager_swartz)
 # 2017-06-27T00:​23:​23.998693118-04:​00 container health_status:​ unhealthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server,​ name=eager_swartz) # 2017-06-27T00:​23:​23.998693118-04:​00 container health_status:​ unhealthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server,​ name=eager_swartz)
- +</​code>​
-| 1 2 3 | $ docker events --filter event=health_status # 2017-06-27T00:​23:​03.691677875-04:​00 container health_status:​ healthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server,​ name=eager_swartz) # 2017-06-27T00:​23:​23.998693118-04:​00 container health_status:​ unhealthy ec36579aa452bf683cb17ee44cbab663d148f327be369821ec1df81b7a0e104b (image=server,​ name=eager_swartz) |+
  
 Обычно событий у Docker достаточно много, так что пришлось приглушить ненужные параметром ''​--filter''​. Сама ''​docker events''​ самостоятельно не завершится и будет спамить в консоль до скончания веков. Обычно событий у Docker достаточно много, так что пришлось приглушить ненужные параметром ''​--filter''​. Сама ''​docker events''​ самостоятельно не завершится и будет спамить в консоль до скончания веков.
Строка 227: Строка 197:
 Чтобы начать играться с сервисами,​ мне пришлось временно перевести локальный Docker в Swarm режим через ''​docker swarm init''​. Зато теперь наш ''​server''​ образ можно запускать вот так: Чтобы начать играться с сервисами,​ мне пришлось временно перевести локальный Docker в Swarm режим через ''​docker swarm init''​. Зато теперь наш ''​server''​ образ можно запускать вот так:
  
-Shell +<code bash>
 $ docker service create -p 8080:8080 -p8081:8081 \ $ docker service create -p 8080:8080 -p8081:8081 \
     --name server \     --name server \
Строка 240: Строка 209:
  
 #​ohkvwbsk06vkjyx69434ndqij #​ohkvwbsk06vkjyx69434ndqij
- +</​code>​
-| 1 2 3 4 5 6 7 8 9 10 11 | $ docker service create -p 8080:8080 -p8081:8081 \     --name server \     --health-cmd='​curl -sS 127.0.0.1:​8080'​ \     --health-retries=3 \     --health-interval=5s \     server #unable to pin image server to digest: errors: #denied: requested access to the resource is denied #​unauthorized:​ authentication required   #​ohkvwbsk06vkjyx69434ndqij |+
  
 Оказывается,​ Swarm не очень любит локально созданные образы,​ поэтому-то и выплюнул в консоль несколько ошибок со своим недовольством. Но сервис всё-таки создал и вернул его айдишку:​ Оказывается,​ Swarm не очень любит локально созданные образы,​ поэтому-то и выплюнул в консоль несколько ошибок со своим недовольством. Но сервис всё-таки создал и вернул его айдишку:​
  
-Shell +<code bash>
 docker service ls docker service ls
 #ID            NAME    MODE        REPLICAS ​ IMAGE #ID            NAME    MODE        REPLICAS ​ IMAGE
 #​ohkvwbsk06vk ​ server ​ replicated ​ 1/1       ​server #​ohkvwbsk06vk ​ server ​ replicated ​ 1/1       ​server
- +</code>
-| 1 2 3 | docker service ls #​ID            NAME    MODE        REPLICAS  IMAGE #​ohkvwbsk06vk  server  replicated  1/1       server |+
  
 ''​curl 127.0.0.1:​8080''​ теперь снова будет возвращать OK (я проверял),​ а запрос на порт 8081 временно заткнёт сервер. Но в отличие от первого примера,​ примерно через пол минуты после того, как сервер был явно отключён,​ он снова начнёт отзываться на 8080. Как же так? ''​curl 127.0.0.1:​8080''​ теперь снова будет возвращать OK (я проверял),​ а запрос на порт 8081 временно заткнёт сервер. Но в отличие от первого примера,​ примерно через пол минуты после того, как сервер был явно отключён,​ он снова начнёт отзываться на 8080. Как же так?
Строка 257: Строка 223:
 Прикол в том, что как только мы отключили сервер и его контейнер получил статус unhealthy, это тут же заметил Swarm менеджер и понял, что заявленная конфигурация сервиса больше не выполняется. А так нельзя,​ поэтому он быстренько прибил проблемный сервисный контейнер и заменил его на новый, рабочий. Следы этого можно увидеть,​ посмотрев историю задач для всего сервиса:​ Прикол в том, что как только мы отключили сервер и его контейнер получил статус unhealthy, это тут же заметил Swarm менеджер и понял, что заявленная конфигурация сервиса больше не выполняется. А так нельзя,​ поэтому он быстренько прибил проблемный сервисный контейнер и заменил его на новый, рабочий. Следы этого можно увидеть,​ посмотрев историю задач для всего сервиса:​
  
-Shell +<code bash>
 $ docker service ps server $ docker service ps server
 #ID            NAME          IMAGE   ​NODE ​ DESIRED STATE  CURRENT STATE              ERROR                             PORTS #ID            NAME          IMAGE   ​NODE ​ DESIRED STATE  CURRENT STATE              ERROR                             PORTS
 #​mt67hkhp7ycr ​ server.1 ​     server ​ moby  Running ​       Running 50 seconds ago                                        #​mt67hkhp7ycr ​ server.1 ​     server ​ moby  Running ​       Running 50 seconds ago                                       
 #​pj77brhfhsjm ​  \_ server.1 ​ server ​ moby  Shutdown ​      ​Failed about a minute ago  "task: non-zero exit (137): do…" #​pj77brhfhsjm ​  \_ server.1 ​ server ​ moby  Shutdown ​      ​Failed about a minute ago  "task: non-zero exit (137): do…"
- +</​code>​
-| 1 2 3 4 | $ docker service ps server #​ID            NAME          IMAGE   NODE  DESIRED STATE  CURRENT STATE              ERROR                             PORTS #​mt67hkhp7ycr  server.1      server  moby  Running        Running 50 seconds ago                                       #​pj77brhfhsjm   \_ server.1  server  moby  Shutdown       Failed about a minute ago  "​task:​ non-zero exit (137): do…" |+
  
 Маленькая предыстория:​ для каждого контейнера в сервисе Swarm создаёт задачу. То есть сначала идёт задача,​ а она уже приводит к контейнеру. Наш первый контейнер «упал»,​ поэтому менеджер создал новую задачу,​ и она привела к новому контейнеру. Но старая задача осталась в истории! ''​docker service ps''​ показывает всё цепочку смертей и реинкарнаций задач, и в нашем случае видно, что самая старая задача с айди ''​pj77brhfhsjm''​ помечена как упавшая,​ и через ''​docker inspect''​ можно узнать почему:​ Маленькая предыстория:​ для каждого контейнера в сервисе Swarm создаёт задачу. То есть сначала идёт задача,​ а она уже приводит к контейнеру. Наш первый контейнер «упал»,​ поэтому менеджер создал новую задачу,​ и она привела к новому контейнеру. Но старая задача осталась в истории! ''​docker service ps''​ показывает всё цепочку смертей и реинкарнаций задач, и в нашем случае видно, что самая старая задача с айди ''​pj77brhfhsjm''​ помечена как упавшая,​ и через ''​docker inspect''​ можно узнать почему:​
  
-Shell +<code bash>
 $ docker inspect pj77 | jq '​.[].Status.Err'​ $ docker inspect pj77 | jq '​.[].Status.Err'​
 # "task: non-zero exit (137): dockerexec: unhealthy container"​ # "task: non-zero exit (137): dockerexec: unhealthy container"​
- +</​code>​
-| 1 2 | $ docker inspect pj77 %%|%% jq '​.[].Status.Err'​ # "task: non-zero exit (137): dockerexec: unhealthy container"​ |+
  
 «Unhealthy container»,​ вот почему. «Unhealthy container»,​ вот почему.
  
-===== Мораль ===== 
- 
-Проверки состояния контейнеров в Docker — это возможность регулярно запускать сторонние скрипты и экзешники внутри контейнера,​ чтобы узнать,​ достаточно ли живо его содержимое. В однохостовом режиме докер просто пометит проблемные контейнеры как unhealthy и сгенерирует health_status событие. В облачном же режиме он ещё и отключит этот контейнер и заменит его на новый, всё ещё здоровый. Быстро и автоматически. 
- 
-==== Поделиться ссылкой:​ ==== 
- 
-  * [[https://​dotsandbrackets.com/​docker-health-check-ru/?​share=facebook&​amp;​nb=1|Нажмите,​ чтобы открыть на Facebook (Открывается в новом окне)]] 
-  * [[https://​dotsandbrackets.com/​docker-health-check-ru/?​share=twitter&​amp;​nb=1|Нажмите,​ чтобы поделиться на Twitter (Открывается в новом окне)]] 
-  * [[https://​dotsandbrackets.com/​docker-health-check-ru/?​share=linkedin&​amp;​nb=1|Нажмите,​ чтобы поделиться на LinkedIn (Открывается в новом окне)]] 
-  * [[https://​dotsandbrackets.com/​docker-health-check-ru/?​share=telegram&​amp;​nb=1|Нажмите,​ чтобы поделиться в Telegram (Открывается в новом окне)]] 
  
-Метки[[https://​dotsandbrackets.com/​tag/​container/​|container]][[https://​dotsandbrackets.com/​tag/​docker/​|docker]][[https://​dotsandbrackets.com/​tag/​monitoring/​|monitoring]][[https://​dotsandbrackets.com/​tag/​quick-guide/​|quick guide]] 
docker/healthcheck/main.1703189580.txt.gz · Последние изменения: 2023/12/21 23:13 — werwolf