Русский
Русский
English
Статистика
Реклама

Жизнь .NET приложения в Kubernetes

Поддержка в рабочем состояние заданного набора контейнеров является одним из главных преимуществ использования Kubernetes

В этой статье я хотел бы на практике разобраться в этом аспекте, а именно в том, как проверяются живы ли контейнеры и как они перезапускаются в случае необходимости, а также как нам, разработчикам приложений, помочь системе оркестрации в этом деле

Чтобы начать экспериментировать, мне понадобится кластер Kubernetes. Я буду использовать тестовое окружение, предоставляемое Docker Desktop. Для этого всего лишь потребуется активировать Kubernetes в настройках

Как установить Docker Desktop в Windows 10 можно узнать из первой части этой статьи

Создание Web API

Для дальнейших экспериментов я создам простой веб-сервис на основе шаблона ASP.NET Core Web API

Новый проект с названием WebApiLivenessНовый проект с названием WebApiLiveness

Добавлю через Package Manager пакет для генерации случайного текста командой Install-Package Lorem.Universal.Net -Version 3.0.69

Изменю файл Program.cs

using Microsoft.AspNetCore.Hosting;using Microsoft.Extensions.Hosting;using System;namespace WebApiLiveness{    public class Program    {        private static int _port = 80;        private static TimeSpan _kaTimeout = TimeSpan.FromSeconds(1);        public static void Main(string[] args)        {            CreateAndRunHost(args);        }        public static void CreateAndRunHost(string[] args)        {            var host = Host                .CreateDefaultBuilder(args)                .ConfigureWebHostDefaults(webBuilder =>                {                    webBuilder                        .UseKestrel(options =>                         {                            options.ListenAnyIP(_port);                            options.Limits.KeepAliveTimeout = _kaTimeout;                        })                        .UseStartup<Startup>();                })                .Build();            host.Run();        }    }}

Добавлю в проект класс LoremService, который будет возвращать случайно сгенерированный текст

using LoremNET;namespace WebApiLiveness.Services{    public class LoremService    {        private int _wordCountMin = 7;        private int _wordCountMax = 12;        public string GetSentence()        {            var sentence = Lorem.Sentence(_wordCountMin, _wordCountMax);            return sentence;        }    }}

В классе Startup зарегистрирую созданный сервис

public void ConfigureServices(IServiceCollection services){    services.AddSingleton<LoremService>();    services.AddControllers();}

Заменю созданный автоматически контроллер на LoremController

using Microsoft.AspNetCore.Mvc;using System;using System.Net;using WebApiLiveness.Services;using Env = System.Environment;namespace WebApiLiveness.Controllers{  [ApiController]  [Route("api/[controller]")]  public class LoremController : ControllerBase  {    private readonly LoremService _loremService;    public LoremController(LoremService loremService)    {        _loremService = loremService;    }    //GET api/lorem    [HttpGet]    public ActionResult<string> Get()    {      try      {          var localIp = Request.HttpContext.Connection.LocalIpAddress;          var loremText = _loremService.GetSentence();          var result =            $"{Env.MachineName} ({localIp}){Env.NewLine}{loremText}";          return result;      }      catch (Exception)      {          return new StatusCodeResult(            (int)HttpStatusCode.ServiceUnavailable);      }    }  }}

И наконец, добавлю файл Dockerfile с инструкциями для создания образа с приложением. За основу взят образ, оптимизированный для запуска приложений ASP.NET с версией .NET 5

FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slimCOPY bin/Release/net5.0/linux-x64/publish/ App/WORKDIR /AppENTRYPOINT ["dotnet", "WebApiLiveness.dll"]

В результате у меня получилась следующая структура

Структура проектаСтруктура проекта

Создание и публикация образа

Подготовлю релиз приложения 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

Основной единицей развертывания в Kubernetes, является Pod это группа, состоящая из одного (чаще) или нескольких (гораздо реже) контейнеров

Поды почти никогда не создаются в кластере вручную. И в данном примере я создам другой ресурс Kubernetes ReplicaSet, который в свою очередь будет создавать поды и следить за ними

Манифест для ReplicaSet содержит инструкции для запуска 3 экземпляров подов на основе ранее опубликованного образа sasha654/webapiliveness. Созданным экземплярам будет присвоена метка api: loremapi

apiVersion: apps/v1kind: ReplicaSetmetadata:  name: myrsspec:  replicas: 3  selector:    matchLabels:      api: loremapi  template:    metadata:      labels:        api: loremapi    spec:      containers:      - name: webapiliveness        image: sasha654/webapiliveness

Применю команду kubectl create -f kuber-rs.yaml, а после выведу на экран информацию о созданных ресурсах командами kubectl get rs и kubectl get pods --show-labels

Чтобы получить доступ к работающим приложениям извне кластера, я создам ресурс типа Service LoadBalancer с помощью следующего манифеста. Здесь важно отметить, что служба будет предоставлять доступ к подам на основе селектора, указанного в разделе spec

apiVersion: v1kind: Servicemetadata:  name: mylbspec:  type: LoadBalancer  selector:    api: loremapi  ports:  - port: 8080    targetPort: 80

Информация о созданном сервисе 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 перезапускать под с этим приложением

using LoremNET;using System;namespace WebApiLiveness.Services{    public class LoremService    {        private int _wordCountMin = 7;        private int _wordCountMax = 12;        private int _numRequestBeforeError = 5;        private int _requestCounter = 0;        public LoremService()        {            IsOk = true;        }        public bool IsOk { get; private set; }        public string GetSentence()        {            if (_requestCounter < _numRequestBeforeError)            {                _requestCounter++;                var sentence = Lorem.Sentence(                    _wordCountMin, _wordCountMax);                return sentence;            }            else            {                IsOk = false;                throw new InvalidOperationException(                    $"{nameof(LoremService)} not available");            }        }    }}

Добавлю новый контроллер HealthController, который на GET-запрос будет возвращать состояние приложения

using Microsoft.AspNetCore.Mvc;using System.Net;using WebApiLiveness.Services;namespace WebApiLiveness.Controllers{    [ApiController]    [Route("api/[controller]")]    public class HealthController    {        private readonly LoremService _loremService;        public HealthController(LoremService loremService)        {            _loremService = loremService;        }        //GET api/health        [HttpGet]        public StatusCodeResult Get()        {            if (_loremService.IsOk)            {                return new OkResult();            }            else            {                return new StatusCodeResult(                    (int)HttpStatusCode.ServiceUnavailable);            }        }    }}

Как было описано ранее, опубликую новую версию и зафиксирую образ на Docker Hub, но уже с тегом 2

Создам новый манифест ReplicaSet с проверкой состояния. В основном, этот манифест отличается от предыдущего тем, что будет создаваться всего 1 экземпляр пода, а также новым разделом livenessProbe, где указан путь по которому будет обращаться Kubernetes для проверки живости приложения

apiVersion: apps/v1kind: ReplicaSetmetadata:  name: myrsspec:  replicas: 1  selector:    matchLabels:      api: loremapi  template:    metadata:      labels:        api: loremapi    spec:      containers:      - name: webapiliveness        image: sasha654/webapiliveness:2        livenessProbe:          httpGet:            path: /api/health            port: 80          initialDelaySeconds: 10          periodSeconds: 3

Манифест службы оставлю без изменений. Как и ранее, создам ресурсы 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

Здесь находится репозиторий с проектом

Источник: habr.com
К списку статей
Опубликовано: 03.01.2021 20:22:24
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Net

Kubernetes

Liveness

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru