Ротация логов для ELK (Elasticsearch - Logstash - Kibana) - Пример работы с RESTful Elasticsearch из PowerShell

imageВ настоящее время все больше и больше продуктов содержат API для интеграции с другими продуктами. Хорошие разработчики делают этот APIRESTful, то есть разработанный по принципам REST. Что бы работать с подобными сервисами в PoweShell 3.0 ввели cmdlet Invoke-RESTMethod. Данный cmdlet позволяет легко делать как привычные HTTP (GET-POST) запросы, так и запросы по паттерну CRUD (POST-GET-PUT-DELETE), а также другие WebRequestMethod. Я уже пользовался данным cmdlet’ом в статье о интеграции DPM с системой ServiceDesk, а сейчас хотел бы показать как можно пользоваться данным cmdlet’ом более полно, получая от него данные и пользуясь этими данными. Мои статьи рассчитаны на таких же неискушенных в PowerShell администраторов как и я, потому не следует ждать тут каких либо откровений для опытных скриптописателей.

Так как давать абстрактный пример не хочется (не люблю абстрактных примеров), я буду реализовывать задачу из заголовка – ротацию логов для ELK. В статье о ELK и сборе логов я упустил этот момент и вот сейчас готов его восполнить. Хранить огромное количество логов за продолжительный промежуток времени я считаю довольно бессмысленным (какова актуальность логов двухнедельной давности?). По этому логи старше N дней нужно удалять из Elacticsearch. Изучив документацию по Elasticsearch я убедился, что он вполне себе RESTful и в наличии есть все необходимые методы для управления индексами поискового движка.

Для того чтобы получить список всех индексов Elasticsearch, необходимо выполнить GET запрос по адресу:

http://{you elasicsearech server}:9200/_cat/indices?v

Попробуем воспользоваться cmdlet’ом Invoke-RESTMethod:

$result = Invoke-RestMethod -Method Get -Uri "http://{you elasicsearech server}:9200/_cat/indices?v"

Получаем примерно такой вывод:

PS C:\Windows\system32> $result
health index               pri rep docs.count docs.deleted store.size pri.store.size 
green  logstash-2014.10.13   5   0   13569640            0     16.7gb         16.7gb 
green  logstash-2014.10.14   5   0    4348141            0      5.5gb          5.5gb 
green  .marvel-kibana        5   0          1            1      3.5kb          3.5kb

Однако работать с таким $result неудобно, потому как это просто строка, в чем мы можем убедиться воспользовавшись методом GetType()

PS C:\Users\nikitin.ae\Documents> $result.GetType()

IsPublic IsSerial Name    BaseType
-------- -------- ---- -------- True True String System.Object

Для того чтобы получить от сервера массив, мы добавим cmdlet’у параметр ContentType в котором укажем MIME тип application/json. В результате сервер отдаст нам ответ в формате JSON, который Powershell автоматически приведет к массиву объектов PSCustomObject.

Итак наш новый запрос будет выглядеть так:

$result = Invoke-RestMethod -Method Get -Uri "http://{you elasicsearech server}:9200/_cat/indices?v" -ContentType 'application/json'

А результат можно увидеть воспользовавшись методом любого объекта Powershell GetType()

PS C:\Users\nikitin.ae\Documents> $result.GetType()

IsPublic IsSerial Name     BaseType
-------- -------- ---- -------- True True Object[] System.Array

Тип PSCustomObject очень похож на Hashtable, и по имени свойства объекта мы можем получить доступ к данным.

Для обхода всего массива мы можем воспользоваться foreach. Так как нас интересуют только индексы с именем, начинающимся на “logstash-” то будет уместным отфильтровать индексы, которые имеют отличное от этого имя. Ну и наконец в имени индекса, которое создает logstash содержится дата, нам нужно ее распознать и привести к типу datetime, чтобы потом можно было бы сравнить с текущим и получить разницу в днях.

Как итог у меня получился вот такой скрипт:

$result = Invoke-RestMethod -Method Get -Uri "http://{your elasticsearch server}:9200/_cat/indices?v" -ContentType 'application/json'

$today = [datetime]::Today
foreach($r in $result){
    if($r.index.StartsWith('logstash-') -eq $true){    
        $date = [datetime]::Parse($r.index.SubString(9))
        if(($today-$date).Days -gt 3) {    
            $delindex = $r.index
            $uri = "http://{your elasticsearch server}:9200/"+$delindex+"?pretty"
            Invoke-RestMethod -Method Delete -Uri $uri -ContentType 'application/json'
        }
    }
}

В переменной $today сохраняется текущая дата (без времени). А в строке if(($today-$date).Days –gt 3) разница разница в днях сравнивается со значением 3 (вы можете использовать удобное вам значение дней) и удаляются только те индексы, которые старше 3 дней. Удаление производится все тем же cmdlet’ом Invoke-RESTMethod, однако метод называется Delete (один из методов CRUD) который корректно обрабатывается сервером Elasticsearch и вызывает удаление индекса, который мы указываем в параметре Uri.

Сохранив данный скрипт, вы можете вызывать его по расписанию каждый день с помощью Task Schelduler или, например, с помощью System Center Orchestrator, как это сделал я.

Только один комментарий Комментировать

  1. vit /

    Невозможно вызвать метод для выражения со значением NULL.
    строка:4 знак:8
    + if($r.index.StartsWith('logstash-') -eq $true){
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Добавить комментарий