Использование Elasticsearch для временных рядов данных
Elasticsearch предлагает возможности для хранения, управления и поиска данных временных рядов, таких как журналы и метрики. После добавления в Elasticsearch данные можно анализировать и визуализировать с помощью Kibana и других функций Elastic Stack.
Настройка уровней данных
Функция Elasticsearch ILM использует уровни данных, чтобы автоматически перемещать более старые данные на узлы с менее дорогим оборудованием по мере их старения. Это помогает улучшить производительность и снизить затраты на хранение.
Требуются уровни «горячий» и «контент». Уровни «тёплый», «холодный» и «замороженный» являются необязательными.
Используйте узлы высокой производительности на уровнях «горячий» и «тёплый» для более быстрого индексирования и поиска самых последних данных. Используйте более медленные и менее дорогие узлы на уровнях «холодный» и «замороженный» для снижения затрат.
Уровень «контент» обычно не используется для данных временных рядов. Однако он необходим для создания системных индексов и других индексов, которые не являются частью потока данных.
Шаги по настройке уровней данных различаются в зависимости от типа развертывания:
- Войдите в Консоль сервиса Elasticsearch.
- Добавьте или выберите своё развертывание с главной страницы или со страницы развертываний сервиса Elasticsearch.
- В меню развертывания выберите Редактировать развертывание.
- Чтобы включить уровень данных, нажмите Добавить емкость.
Включить автоматическое масштабирование
Автоматическое масштабирование автоматически настраивает емкость вашего развертывания для удовлетворения потребностей в хранении. Чтобы включить автоматическое масштабирование, выберите Автомасштабировать это развертывание на странице Редактировать развертывание. Автоматическое масштабирование доступно только для сервиса Elasticsearch.
Чтобы назначить узел уровню данных, добавьте соответствующий роли узла в файл elasticsearch.yml узла. Изменение ролей существующего узла требует поэтапной перезагрузки.
# Content tier node.roles: [ data_content ] # Hot tier node.roles: [ data_hot ] # Warm tier node.roles: [ data_warm ] # Cold tier node.roles: [ data_cold ] # Frozen tier node.roles: [ data_frozen ]
Рекомендуется использовать выделенные узлы на уровне «замороженный». При необходимости можно назначить другие узлы нескольким уровням.
node.roles: [ data_content, data_hot, data_warm ]
Назначьте узлам любые другие необходимые роли для вашего кластера. Например, небольшой кластер может иметь узлы с несколькими ролями.
node.roles: [ master, ingest, ml, data_hot, transform ]
Зарегистрировать хранилище снимков
Уровни «холодный» и «замороженный» могут использовать поисковые снимки для снижения стоимости локального хранилища.
Чтобы использовать поисковые снимки, необходимо зарегистрировать поддерживаемое хранилище снимков. Шаги по регистрации этого хранилища различаются в зависимости от типа вашего развертывания и поставщика хранилища:
При создании кластера сервис Elasticsearch автоматически регистрирует хранилище по умолчанию found-snapshots. Это хранилище поддерживает поисковые снимки.
Хранилище found-snapshots специфично для вашего кластера. Чтобы использовать хранилище по умолчанию другого кластера, обратитесь к документации Cloud Снимки и восстановление.
Вы также можете использовать любой из следующих типов пользовательских хранилищ с поисковыми снимками:
Используйте любой из следующих типов хранилищ с поисковыми снимками:
Вы также можете использовать альтернативные реализации этих типов хранилищ, например MinIO, если они полностью совместимы. Используйте API анализа хранилищ для анализа пригодности вашего хранилища для использования с поисковыми снимками.
Создать или изменить политику жизненного цикла индекса
Поток данных хранит ваши данные по нескольким базовым индексам. ILM использует политику жизненного цикла индекса, чтобы автоматически перемещать эти индексы по уровням данных.
Если вы используете Fleet или Elastic Agent, отредактируйте одну из встроенных политик жизненного цикла Elasticsearch. Если вы используете пользовательское приложение, создайте свою собственную политику. В любом случае убедитесь, что ваша политика:
- Содержит фазу для каждого уровня данных, который вы настроили.
- Вычисляет порог, или
min_age, для перехода между фазами при ротации. - Использует поисковые снимки на фазах «холодный» и «замороженный», если это необходимо.
- Содержит фазу удаления, если это нужно.
Fleet и Elastic Agent используют следующие встроенные политики жизненного цикла:
-
logs -
metrics -
synthetics
Вы можете настроить эти политики в соответствии со своими требованиями к производительности, отказоустойчивости и хранению.
Чтобы отредактировать политику в Kibana, откройте главное меню и перейдите к Управление стеком > Политики жизненного цикла индексов. Щёлкните по политике, которую вы хотите отредактировать.
Вы также можете использовать API обновления политики жизненного цикла.
resp = client.ilm.put_lifecycle(
name="logs",
policy={
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
},
)
print(resp) const response = await client.ilm.putLifecycle({
name: "logs",
policy: {
phases: {
hot: {
actions: {
rollover: {
max_primary_shard_size: "50gb",
},
},
},
warm: {
min_age: "30d",
actions: {
shrink: {
number_of_shards: 1,
},
forcemerge: {
max_num_segments: 1,
},
},
},
cold: {
min_age: "60d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
frozen: {
min_age: "90d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
delete: {
min_age: "735d",
actions: {
delete: {},
},
},
},
},
});
console.log(response); PUT _ilm/policy/logs
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
}
} Чтобы создать политику в Kibana, откройте главное меню и перейдите к Управление стеком > Политики жизненного цикла индексов. Нажмите Создать политику.
Вы также можете использовать API обновления политики жизненного цикла.
resp = client.ilm.put_lifecycle(
name="my-lifecycle-policy",
policy={
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
},
)
print(resp) const response = await client.ilm.putLifecycle({
name: "my-lifecycle-policy",
policy: {
phases: {
hot: {
actions: {
rollover: {
max_primary_shard_size: "50gb",
},
},
},
warm: {
min_age: "30d",
actions: {
shrink: {
number_of_shards: 1,
},
forcemerge: {
max_num_segments: 1,
},
},
},
cold: {
min_age: "60d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
frozen: {
min_age: "90d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
delete: {
min_age: "735d",
actions: {
delete: {},
},
},
},
},
});
console.log(response); PUT _ilm/policy/my-lifecycle-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
}
} Создать шаблоны компонентов
Если вы используете Fleet или Elastic Agent, перейдите к Поиск и визуализация данных. Fleet и Elastic Agent используют встроенные шаблоны для создания потоков данных.
Если вы используете пользовательское приложение, вам нужно настроить свой собственный поток данных. Для потока данных требуется соответствующий шаблон индекса. В большинстве случаев этот шаблон индекса создаётся с помощью одного или нескольких шаблонов компонентов. Обычно вы используете отдельные шаблоны компонентов для отображений и настроек индексов. Это позволяет повторно использовать шаблоны компонентов в нескольких шаблонах индексов.
При создании шаблонов компонентов включайте:
- Сопоставление типа поля
dateилиdate_nanosдля поля@timestamp. Если вы не укажете сопоставление, Elasticsearch отобразит@timestampкак полеdateс параметрами по умолчанию. - Ваша политика жизненного цикла в настройке индекса
index.lifecycle.name.
Используйте Elastic Common Schema (ECS) при сопоставлении полей. Поля ECS по умолчанию интегрируются с несколькими функциями Elastic Stack.
Если вы не уверены, как сопоставить свои поля, используйте runtime-поля, чтобы извлечь поля из неструктурированного содержимого во время поиска. Например, вы можете индексировать сообщение журнала в поле wildcard и впоследствии извлекать IP-адреса и другие данные из этого поля во время поиска.
Для создания шаблона компонента в Kibana откройте главное меню и перейдите к Управление стеком > Управление индексами. В представлении Шаблоны индексов нажмите Создать шаблон компонента.
Также можно использовать API создания шаблона компонента.
resp = client.cluster.put_component_template(
name="my-mappings",
template={
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "date_optional_time||epoch_millis"
},
"message": {
"type": "wildcard"
}
}
}
},
meta={
"description": "Mappings for @timestamp and message fields",
"my-custom-meta-field": "More arbitrary metadata"
},
)
print(resp)
resp1 = client.cluster.put_component_template(
name="my-settings",
template={
"settings": {
"index.lifecycle.name": "my-lifecycle-policy"
}
},
meta={
"description": "Settings for ILM",
"my-custom-meta-field": "More arbitrary metadata"
},
)
print(resp1) response = client.cluster.put_component_template(
name: 'my-mappings',
body: {
template: {
mappings: {
properties: {
"@timestamp": {
type: 'date',
format: 'date_optional_time||epoch_millis'
},
message: {
type: 'wildcard'
}
}
}
},
_meta: {
description: 'Mappings for @timestamp and message fields',
"my-custom-meta-field": 'More arbitrary metadata'
}
}
)
puts response
response = client.cluster.put_component_template(
name: 'my-settings',
body: {
template: {
settings: {
'index.lifecycle.name' => 'my-lifecycle-policy'
}
},
_meta: {
description: 'Settings for ILM',
"my-custom-meta-field": 'More arbitrary metadata'
}
}
)
puts response const response = await client.cluster.putComponentTemplate({
name: "my-mappings",
template: {
mappings: {
properties: {
"@timestamp": {
type: "date",
format: "date_optional_time||epoch_millis",
},
message: {
type: "wildcard",
},
},
},
},
_meta: {
description: "Mappings for @timestamp and message fields",
"my-custom-meta-field": "More arbitrary metadata",
},
});
console.log(response);
const response1 = await client.cluster.putComponentTemplate({
name: "my-settings",
template: {
settings: {
"index.lifecycle.name": "my-lifecycle-policy",
},
},
_meta: {
description: "Settings for ILM",
"my-custom-meta-field": "More arbitrary metadata",
},
});
console.log(response1); # Creates a component template for mappings
PUT _component_template/my-mappings
{
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "date_optional_time||epoch_millis"
},
"message": {
"type": "wildcard"
}
}
}
},
"_meta": {
"description": "Mappings for @timestamp and message fields",
"my-custom-meta-field": "More arbitrary metadata"
}
}
# Creates a component template for index settings
PUT _component_template/my-settings
{
"template": {
"settings": {
"index.lifecycle.name": "my-lifecycle-policy"
}
},
"_meta": {
"description": "Settings for ILM",
"my-custom-meta-field": "More arbitrary metadata"
}
} Создать шаблон индекса
Используйте свои шаблоны компонентов для создания шаблона индекса. Укажите:
- Один или несколько шаблонов индексов, соответствующих имени потока данных. Рекомендуется использовать нашу схему именования потоков данных.
- Что шаблон поддерживает потоки данных.
- Любые шаблоны компонентов, содержащие ваши сопоставления и настройки индекса.
- Приоритет выше, чем
200, чтобы избежать коллизий с встроенными шаблонами. См. Избегайте коллизий с шаблонами индексов.
Для создания шаблона индекса в Kibana откройте главное меню и перейдите к Управление стеком > Управление индексами. В представлении Шаблоны индексов нажмите Создать шаблон.
Также можно использовать API создания шаблона индекса. Включите объект data_stream, чтобы включить потоки данных.
resp = client.indices.put_index_template(
name="my-index-template",
index_patterns=[
"my-data-stream*"
],
data_stream={},
composed_of=[
"my-mappings",
"my-settings"
],
priority=500,
meta={
"description": "Template for my time series data",
"my-custom-meta-field": "More arbitrary metadata"
},
)
print(resp) response = client.indices.put_index_template(
name: 'my-index-template',
body: {
index_patterns: [
'my-data-stream*'
],
data_stream: {},
composed_of: [
'my-mappings',
'my-settings'
],
priority: 500,
_meta: {
description: 'Template for my time series data',
"my-custom-meta-field": 'More arbitrary metadata'
}
}
)
puts response const response = await client.indices.putIndexTemplate({
name: "my-index-template",
index_patterns: ["my-data-stream*"],
data_stream: {},
composed_of: ["my-mappings", "my-settings"],
priority: 500,
_meta: {
description: "Template for my time series data",
"my-custom-meta-field": "More arbitrary metadata",
},
});
console.log(response); PUT _index_template/my-index-template
{
"index_patterns": ["my-data-stream*"],
"data_stream": { },
"composed_of": [ "my-mappings", "my-settings" ],
"priority": 500,
"_meta": {
"description": "Template for my time series data",
"my-custom-meta-field": "More arbitrary metadata"
}
} Добавление данных в поток данных
Запросы индексирования добавляют документы в поток данных. Эти запросы должны использовать op_type типа create. Документы должны содержать поле @timestamp.
Для автоматического создания потока данных отправьте запрос индексирования, который нацелен на имя потока. Это имя должно совпадать с одним из шаблонов индексов вашего шаблона индекса.
resp = client.bulk(
index="my-data-stream",
operations=[
{
"create": {}
},
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
},
{
"create": {}
},
{
"@timestamp": "2099-05-06T16:25:42.000Z",
"message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638"
}
],
)
print(resp)
resp1 = client.index(
index="my-data-stream",
document={
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
},
)
print(resp1) response = client.bulk(
index: 'my-data-stream',
body: [
{
create: {}
},
{
"@timestamp": '2099-05-06T16:21:15.000Z',
message: '192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736'
},
{
create: {}
},
{
"@timestamp": '2099-05-06T16:25:42.000Z',
message: '192.0.2.255 - - [06/May/2099:16:25:42 +0000] "GET /favicon.ico HTTP/1.0" 200 3638'
}
]
)
puts response
response = client.index(
index: 'my-data-stream',
body: {
"@timestamp": '2099-05-06T16:21:15.000Z',
message: '192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736'
}
)
puts response const response = await client.bulk({
index: "my-data-stream",
operations: [
{
create: {},
},
{
"@timestamp": "2099-05-06T16:21:15.000Z",
message:
'192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736',
},
{
create: {},
},
{
"@timestamp": "2099-05-06T16:25:42.000Z",
message:
'192.0.2.255 - - [06/May/2099:16:25:42 +0000] "GET /favicon.ico HTTP/1.0" 200 3638',
},
],
});
console.log(response);
const response1 = await client.index({
index: "my-data-stream",
document: {
"@timestamp": "2099-05-06T16:21:15.000Z",
message:
'192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736',
},
});
console.log(response1); PUT my-data-stream/_bulk
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:21:15.000Z", "message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736" }
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:25:42.000Z", "message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638" }
POST my-data-stream/_doc
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
} Поиск и визуализация данных
Чтобы изучить и выполнить поиск по данным в Kibana, откройте главное меню и выберите Обзор. См. документацию по Обзору Kibana.
Используйте функцию Панель мониторинга Kibana для визуализации данных в диаграмме, таблице, карте и т. д. См. документацию по Панели мониторинга Kibana.
Также можно выполнить поиск и агрегирование данных с помощью API поиска. Используйте runtime-поля и шаблоны grok, чтобы динамически извлекать данные из сообщений журналов и другого неструктурированного содержимого во время поиска.
resp = client.search(
index="my-data-stream",
runtime_mappings={
"source.ip": {
"type": "ip",
"script": "\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n "
}
},
query={
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-1d/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
fields=[
"*"
],
source=False,
sort=[
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
],
)
print(resp) const response = await client.search({
index: "my-data-stream",
runtime_mappings: {
"source.ip": {
type: "ip",
script:
"\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n ",
},
},
query: {
bool: {
filter: [
{
range: {
"@timestamp": {
gte: "now-1d/d",
lt: "now/d",
},
},
},
{
range: {
"source.ip": {
gte: "192.0.2.0",
lte: "192.0.2.255",
},
},
},
],
},
},
fields: ["*"],
_source: false,
sort: [
{
"@timestamp": "desc",
},
{
"source.ip": "desc",
},
],
});
console.log(response); GET my-data-stream/_search
{
"runtime_mappings": {
"source.ip": {
"type": "ip",
"script": """
String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ "message" ].value)?.sourceip;
if (sourceip != null) emit(sourceip);
"""
}
},
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-1d/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
"fields": [
"*"
],
"_source": false,
"sort": [
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
]
} По умолчанию Elasticsearch-поиски синхронные. Поиски по замороженным данным, длинным временным интервалам или большим наборам данных могут занимать больше времени. Используйте API отправки асинхронного поиска, чтобы запускать поиски в фоновом режиме. Для получения дополнительных параметров поиска см. API поиска.
resp = client.async_search.submit(
index="my-data-stream",
runtime_mappings={
"source.ip": {
"type": "ip",
"script": "\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n "
}
},
query={
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-2y/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
fields=[
"*"
],
source=False,
sort=[
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
],
)
print(resp) const response = await client.asyncSearch.submit({
index: "my-data-stream",
runtime_mappings: {
"source.ip": {
type: "ip",
script:
"\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n ",
},
},
query: {
bool: {
filter: [
{
range: {
"@timestamp": {
gte: "now-2y/d",
lt: "now/d",
},
},
},
{
range: {
"source.ip": {
gte: "192.0.2.0",
lte: "192.0.2.255",
},
},
},
],
},
},
fields: ["*"],
_source: false,
sort: [
{
"@timestamp": "desc",
},
{
"source.ip": "desc",
},
],
});
console.log(response); POST my-data-stream/_async_search
{
"runtime_mappings": {
"source.ip": {
"type": "ip",
"script": """
String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ "message" ].value)?.sourceip;
if (sourceip != null) emit(sourceip);
"""
}
},
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-2y/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
"fields": [
"*"
],
"_source": false,
"sort": [
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
]
}
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/use-elasticsearch-for-time-series-data.html