Замечательная функция healthcheck имеется в инструментарии docker, на её основе kubernetes совершает множество действий над подами (контейнерами), например заменяет в реальном времени вышедший из строя контейнер. Я же хочу показать как можно пользоваться этими инструментами вне kubernetes. Для чего? Например что бы связывать контейнеры между собой в определенной последовательности или мониторинга состояния единичного сервиса.
Для начала рассмотрим пример с Dockerfile. Например у меня есть простой сервис который отдает что-то клиенту, скажем, вот такой скрипт на python:
#!/usr/bin/python
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
class HttpProcessor(BaseHTTPRequestHandler):
def do_GET (self):
self.send_response(200)
self.send_header('content-type','text/html')
self.end_headers()
self.wfile.write("<h1>Apps Server</h1><p>")
serv = HTTPServer(("0.0.0.0",8081),HttpProcessor)
serv.serve_forever()
Сервис будет доступен на порту 8081, и при обращении к нему будет отдавать обычный HTML в теле Apps Server. Назовем его server.py Хорошо, теперь напишем Dockerfile для него:
FROM python:2.7-alpine
RUN apk add --update curl && rm -rf /var/cache/apk/*
COPY ./server.py /opt/server.py
WORKDIR /opt
HEALTHCHECK CMD curl --fail http://localhost:8081/ || exit 1
CMD [ "python", "server.py" ]
Вкратце рассмотрим, мы берем за базовый образ python:2.7-alpine, далее добавляем в образ curl для получения результата от сервера, далее копируем файл скрипта в контейнер, назначаем рабочую директорию и добавляем проверку «здоровья» контейнера. По сути это обычный запрос к сервису и если ответ а нет, то статус «здоровья» изменится. Далее запускаем сервер. Собираем образ и запускаем его, смотрим результат:
#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a58c0a4d8bde python-server-prod "python server.py" 5 seconds ago Up 4 seconds (health: starting) python-apps
Для нас ключевое поле (health: starting), что говорит о том что контейнер запустился и ожидает ответа от сервиса, как только сервис вернет ответ, значение поля изменится на healthy. Если в процессе работы приложение перестанет возвращать ответ или завершиться с ошибкой, статус сменится на unhealthy. При этом сам контейнер может продолжать работать, но мы рассматриваем простой пример.
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a58c0a4d8bde python-server-prod "python server.py" About a minute ago Up About a minute (healthy) python-apps
Так же, это применимо и к docker-compose, рассмотрим пример:
version: "3.7"
services:
python-server:
container_name: python-apps
image: python-apps
build: ./apps
restart: always
network_mode: host
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8081/"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
logging:
driver: "json-file"
options:
max-size: "100k"
max-file: "5"
Запускаем и проверяем:
docker-compose ps
Name Command State Ports
------------------------------------------------------------
python-apps python server.py Up (healthy)
Если, нам необходимо не просто запустить какой-то другой контейнер после первого, но при этом не просто запустить а получить именно статус от самого сервиса, необходимо добавить зависимость:
depends_on:
python-apps:
condition: service_healthy
Например мы можем привязать к нашему docker-compose, nginx. Установить зависимость его от контейнера python-apps, то есть контейнер с nginx запуститься только после того как будет стартован python-apps, но тогда мы получим 502 ошибку, так как сам сервис ещё не будет запущен, но контейнер уже получил статус up. В этом случае, мы можем запускать nginx только после того как контейнер с нашим приложением ответил healthy. Это очень удобно например в связке приложения и базы данных, когда контейнер с базой запустился, но сама база ещё не прошла инициализацию, при запуске приложения оно будет выдавать ошибку соединения с базой, таким образом можно решить эту проблему.
А так же это не плохой инструмент мониторинга приложений внутри контейнера, достаточно сказать Zabbix-у или prometheus о статусе и мы можем получать оповещение когда контейнер все ещё продолжает работу, то есть его статус Up, но само приложение не возвращает нужных ответов.