Поддержка в рабочем состоянии заданного набора контейнеров является одним из главных преимуществ использования Kubernetes. В этой статье я хотел бы на практике разобраться в этом аспекте, а именно в том, как проверяются живы ли контейнеры и как они перезапускаются в случае необходимости, а также как нам, разработчикам приложений, помочь системе оркестрации в этом деле. Чтобы начать экспериментировать, мне понадобится кластер Kubernetes. Я буду использовать тестовое окружение, предоставляемое Docker Desktop. Для этого всего лишь потребуется активировать Kubernetes в настройках. Как установить Docker Desktop в Windows 10 можно узнать из первой части этой статьи. Для дальнейших экспериментов я создам простой веб-сервис на основе шаблона ASP.NET Core Web API. Добавлю через Package Manager пакет для генерации случайного текста командой Install-Package Lorem.Universal.Net -Version 3.0.69 Изменю файл Program.cs Добавлю в проект класс LoremService, который будет возвращать случайно сгенерированный текст В классе Startup зарегистрирую созданный сервис Заменю созданный автоматически контроллер на LoremController И наконец, добавлю файл Dockerfile с инструкциями для создания образа с приложением.  За основу взят образ, оптимизированный для запуска приложений ASP.NET с версией .NET 5. В результате у меня получилась следующая структура Подготовлю релиз приложения dotnet publish -c Release -r linux-x64 Создам образ, используя ранее подготовленный Dockerfile. Выполню команду из папки, где непосредственно находится этот файл  docker build -t sasha654/webapiliveness . И наконец, отправлю образ в Docker Hub docker push sasha654/webapiliveness Я разместил образ в общедоступном репозитории и теперь его можно получить с любого устройства. Но если вы собираетесь проделать то же самое в своем репозитории Docker Hub, то очевидно, необходимо подставить вместо префикса sasha654 свой Docker ID, полученный при регистрации. Прежде чем разворачивать приложение в кластере Kubernetes, я проверю, что на данном этапе все сделано правильно и запущу контейнер из созданного образа непосредственно через Docker docker run -p 8080:80 -d sasha654/webapiliveness И отправлю запрос curl http://localhost:8080/api/lorem Отлично! Теперь остановлю и удалю этот, уже ненужный, контейнер. Основной единицей развертывания в Kubernetes, является Pod – это группа, состоящая из одного (чаще) или нескольких (гораздо реже) контейнеров. Поды почти никогда не создаются в кластере вручную. И в данном примере я создам другой ресурс Kubernetes – ReplicaSet, который в свою очередь будет создавать поды и следить за ними. Манифест для ReplicaSet содержит инструкции для запуска 3 экземпляров подов на основе ранее опубликованного образа sasha654/webapiliveness. Созданным экземплярам будет присвоена метка api: loremapi. Применю команду kubectl create -f kuber-rs.yaml, а после выведу на экран информацию о созданных ресурсах командами kubectl get rs и kubectl get pods --show-labels  Чтобы получить доступ к работающим приложениям извне кластера, я создам ресурс типа Service LoadBalancer с помощью следующего манифеста.  Здесь важно отметить, что служба будет предоставлять доступ к подам на основе селектора, указанного в разделе spec. Информация о созданном сервисе kubectl get svc Теперь через браузер или Postman можно посылать запросы на адрес службы http://localhost:8080/api/lorem. А служба будет перенаправлять запрос к одному из относящихся к ней подов. Здесь я бы хотел рассказать о сохранение сессий. При первом запросе к службе выбирается случайный под с которым устанавливается постоянное подключение. Соответственно все последующие запросы, принадлежащие этому подключению, будут отправляться в этот же под. И так до тех пор, пока подключение не будет закрыто. А для того чтобы убедиться, что каждый запрос все таки отправляется на случайно выбранный под, я установил в файле Program.cs значение для свойства KeepAliveTimeout равное 1 секунде, вместо 2 минут, принятых по умолчанию. В итоге, если отправлять запросы реже одной секунды, то каждый раз подключение будет принудительно закрываться, тем самым для обработки каждого нового запроса будет происходить новый выбор пода. Теперь если произойдет сбой главного процесса в каком-либо из контейнеров, например, из-за ошибки в приложении, которая время от времени приводит к падению, то Kubernetes перезапустит его. Если Pod пропадет, например, из-за возникшей неисправности узла кластера, то Kubernetes создаст новый Pod. Так, если я удалю вручную один из подов kubectl delete pod myrs-jqjsp, то почти сразу будет создан автоматически новый, тем самым будет восстановлен баланс между желаемым и фактическим количеством экземпляров. Удалю все ресурсы из кластера kubectl delete all --all Таким образом, даже не делая ничего в самом приложении, Kuberntes значительно повышает выживаемость. Но бывают такие сценарии, когда приложение перестает корректно работать без падения процесса, а до тех пор пока процесс выполняется, Kubernetes будет считать, что все в порядке. И здесь я, как разработчик, должен позаботиться, чтобы известить Kubernetes, что приложение перестало работать должным образом и тем самым вынудить систему произвести перезапуск. На сколько мне известно, есть 3 таких способа. Проверка выполнения произвольной команды с помощью инструкции exec. Если команда по завершение возвращает 0, то все в порядке. Проверка открытия TCP-порта. Если подключение установлено, то все в порядке. Проверка отправкой GET-запроса по указанному маршруту. Если ответ получен и код ответа не указывает на ошибку, то, как вы уже догадались, все в порядке. Поскольку в примере используется веб-приложение, я исследую проверку с помощью GET-запроса. Но перед тем как идти дальше я зафиксирую текущий образ на Docker Hub с тегом 1, т.к. совсем скоро я изменю веб-приложение и сделаю 2 версию образа. Для следующей демонстрации я изменю код класса LoremService таким образом, чтобы после пяти запросов к API, приложение становилось неисправным, тем самым провоцируя Kubernetes перезапускать под с этим приложением. Добавлю новый контроллер HealthController, который на GET-запрос будет возвращать состояние приложения. Как было описано ранее, опубликую новую версию и зафиксирую образ на Docker Hub, но уже с тегом 2. Создам новый манифест ReplicaSet с проверкой состояния. В основном, этот манифест отличается от предыдущего тем, что будет создаваться всего 1 экземпляр пода, а также новым разделом livenessProbe, где указан путь по которому будет обращаться Kubernetes для проверки живости приложения. Манифест службы оставлю без изменений. Как и ранее, создам ресурсы ReplicaSet и Service. После выполню более 5 запросов http://localhost:8080/api/lorem и тогда получу  результат с ошибкой. Через некоторое время единственный под будет перезапущен и снова будет готов обрабатывать запросы. Взгляну подробнее на отчет, полученный командой kubectl describe pod myrs-787w2  Этот простой пример проверки жизни показал насколько это мощное и полезное средство для обеспечения восстановления и устойчивости приложений в целом. Я не буду рассматривать подробно, но упомяну еще об одной важной проверке, которую может выполнять Kebernetes - проверка готовности (Readiness). Иногда я не хочу, чтобы только что запущенное приложение немедленно начинало принимать запросы, т. к. в некоторых сценариях для корректной работы необходимо выполнить загрузку больших данных из внешнего источника или выполнять какую-либо другую длительную операцию. На основе периодической проверки готовности определяется, должен ли конкретный под получать клиентские запросы на обработку или нет. Но в отличие от проверки на живость, если под не проходит проверку готовности, Kubernetes не убивает и не перезапускает его. В завершении упомяну, что ASP.NET предоставляет ПО промежуточного слоя Microsoft.AspNetCore.Diagnostics.HealthChecks, упрощающее создание сценариев проверки. В частности имеются функции, которые позволяют проверить внешние ресурсы, например, СУБД SQL Server или удаленный API. Здесь находится репозиторий с проектом. Разработчик