[TOC]
TODO: 暂时略过, 后续再补课.
(这部分暂时以截图为主)
https://github.com/geektime-g...
这一整节的内容 pdf, 可以搜索复制文本.
保护你的数据
集群身份认证与用户鉴权
开启并配置 X-Pack 的认证与鉴权
修改配置文件, 打开认证与授权 elasticsearch.yml
xpack.security.enabled: true # xpack.license.self_generated.type: basic # xpack.security.transport.ssl.enabled: true
- 启动 ES
创建默认的用户和分组
bin/elasticsearch-setup-passwords interactive
会创建用户:
- elastic
- apm_system
- kibana
- kibana_system
- logstash_system
- beats_system
- remote_monitoring_user
配置 Kibana 身份认证 kibana.yml
elasticsearch.username: "kibana_system" elasticsearch.password: "......"
-
启用 X-Pack Monitoring(Basic Free)
xpack.monitoring.enabled: true xpack.monitoring.elasticsearch.username: "logstash_system" xpack.monitoring.elasticsearch.password: "...." xpack.monitoring.elasticsearch.hosts: ["http://ip:9200"]
在 logstash 定义 conf 时, 记得加上账号密码
input { elasticsearch { ... user => "logstash_system" password => ..... } } filter { elasticsearch { ... user => "logstash_system" password => ..... } } output { elasticsearch { ... user => "logstash_system" password => "....." #ssl => true #cacert => '/path/to/cert.pem' } }
注意, 若是在生产环境, 启动 ES 时会强制进行 Bootstrap Check, 在开启了 xpack.security.enabled: true 情况下会要求配置 xpack.security.transport.ssl, 具体见下面的 "集群内部安全通信" 一节.
在 kibana 中 manager 的 security 可以方便图形化地添加 用户/角色
集群内部安全通信
ES 内部使用 9300 端口传输
生成节点证书
# 直接一路回车, 然后会在 elasticsearch 的安装目录下生成一个文件: *elastic-stack-ca.p12*
bin/elasticsearch-certutil ca
# 一路回车, 会在 elasticsearch 的安装目录下生成一个文件: *elastic-certificates.p12*
bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12
# 统一将上述生成的证书文件都放到 elasticsearch 安装目录下 certs 目录中
# 若使用 rpm 方式安装, 那么配置文件和 elasticsearch 安装目录不在一起, 此时可以将这些证书移到 /etc/elasticsearch/certs 中
# 注意证书文件的权限, 需确保至少 elasticsearch 可读
mkdir certs
mv *.p12 ./certs
https://www.elastic.co/guide/...
默认生成的 CA 文件名: elastic-stack-ca.p12
默认生成的节点证书文件名: elastic-certificates.p12
配置节点间通讯
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: "certificate"
xpack.security.transport.ssl.keystore.path: "certs/elastic-certificates.p12"
xpack.security.transport.ssl.truststore.path: "certs/elastic-certificates.p12"
集群与外部间的安全通信
↑ kibana 使用的证书格式是 pem, 而之前 es 生成的节点证书(默认文件名: elastic-certificates.p12
)是p12 格式, 因此需要使用 openssl 将其转换为 pem 格式.
↑ 将上述生成的 elastic-stack-ca.zip 解压得到 ca.crt
文件和 ca.key
文件.
配置 config/kibana.yml
:
由于这里的证书是自签的, kibana启动时会报错, 只是做测试, 可以忽略.
水平扩展 ES 集群
常见的集群部署方式
Hot & Warm 架构与Shard Filtering
当使用 force awareness, 都指定了不存在的 zone, 那么会导致分片(副本)无法分配.
分片设计及管理
↑ 7.0 之前是默认创建5个主分片...
如何对集群进行容量规划
后文有提到说, 搜索类的按照 1:16, 日志类的按照 1:48 ~ 1:96 之间.
在私有云上管理 ES 集群的一些方法
在公有云上管理与部署 ES 集群
生产环境中的集群运维
生产环境常用配置与上线清单
需配置 discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes 这几个中至少一个, 比如:
discovery.seed_hosts: ["127.0.0.1"] cluster.initial_master_nodes: ["node-1"]
剩下的 50% 的物理内存要分配给 Lucene 使用.
分配给JVM 的内存建议不要超过 32GB, 超过之后性能反而会下降...
JVM 有 Server 和 Cli 这两种模式
> 官方文档中 Import Elasticsearch configuration 这一节中介绍了一些重要的 ES 参数配置项
> 需要对 Linux 主机进行相关设定, 否则在生产环境模式是无法通过检查, 启动报错.
> 具体参见 Bootstrap Checks 一节
> 这里的内存大小指的是给 JVM 分配的大小, 系统应该额外预留系统的另外一半内存给 Lucene.
> 也就是上面单个节点分配给JVM 31G内存, 那么这台主机实际内存占用在 64GB 左右
监控 ES 集群
诊断集群的潜在问题
解决集群 Yellow 与 Red 的问题
https://github.com/geektime-g...
提升集群写性能
↓ 文档建模的一些最佳实践
一个索引设定的例子
PUT my_test
{
"mappings": {
// 避免不必要的字段索引.
// 必要时可以通过 update by query 索引必要的字段
"dynamic": "false",
"properties": {}
},
"settings": {
"index": {
"routing": {
"allocation": {
// 控制该索引的在每个节点的分片数, 避免数据热点
"total_shards_per_node": "3"
}
},
// 30 秒一次 Refresh
"refresh_interval": "30s",
"number_of_shards": "2",
// 降低 translog 落盘
"translog": {
"sync_interval": "30s",
"durability": "async"
},
"number_of_replicas": "0"
}
}
}
提升集群读性能
集群压力测试
段合并优化及注意事项
缓存及使用 Breaker 限制内存使用
一些运维的相关建议
索引生命周期管理
使用 Shrink 与 Rollover API 有效管理序列索引
*Demo*
Demo
示例1
示例2
Rollover API
- 类似 Log4J 记录日志的方式,索引尺寸或者时间超过一定值后,创建新的
支持的条件判断(满足任一条件)
- 存活时间
max_age
- 最大文档数
max_docs
- 最大的索引大小
max_size
- 存活时间
Rollover API 是一次性的, ES 仅在调用时进行判断并处理, 后续并不会进行监控.
若需要持续监控, 并在满足条件时 rollover 时, 则应使用 ILM(Index Lifecycle Management Policies) 结合使用
这特别适合索引数据量持续增大, 且容易过大的.
- 当调用 Rollover API 时, 若满足条件将会创建新索引, "写别名"(write alias)会被更新为指向新的索引, 后续更新都会被写入新的索引.
Rollover 对于别名指向的索引中显示设置了
is_write_index: true
的, 并不会简单地将别名指向新索引, 而是 rollover 设置了is_write_index: true
的那个旧索引(指向的多个索引中只能有1个设置了该值), 在创建了新的索引后, 将别名新增指向该索引, 并设置 write 到新索引, 同时从所有已存在的索引中 read.示例
PUT my_logs_index-000001 { "aliases": { // configures my_logs_index as the write index for the logs alias "logs": { "is_write_index": true } } } PUT logs/_doc/1 { "message": "a dummy log" } POST logs/_refresh POST /logs/_rollover { "conditions": { "max_docs": "1" } } // newly indexed documents against the logs alias will write to the new index: my_logs_index-000002 PUT logs/_doc/2 { "message": "a newer log" } // 此时 alias 的配置如下: /* { "my_logs_index-000002": { "aliases": { "logs": { "is_write_index": true } } }, "my_logs_index-000001": { "aliases": { "logs": { "is_write_index" : false } } } } */
新创建的索引名
- 如果是对 alias 做 rollover, 并且 alias 指向的已存在的索引的命名格式是
-
加上一个数字( 比如logs-1
), 那么新创建的索引会按照这个模式, 并递增数字序号来创建.新的数字序号是用
0
作填充的 6 位数(不管旧的索引的数字位数是多少位), 比如logs-000002
Rollover API 支持 date math, 因此若旧的索引命名是按照 date math, 那么 rollover 新创建的索引也会是按照 date math 来创建
示例
// 注意, 这里必须用 urlencode 的来作为 uri path, 否则会报错. // 示例, 这里实际创建的索引名是 logs_2016.10.31-1 // PUT /
with URI encoding: PUT /%3Clogs-%7Bnow%2Fd%7D-1%3E { "aliases": { "logs_write": {} } } PUT logs_write/_doc/1 { "message": "a dummy log" } POST logs_write/_refresh // Wait for a day to pass // 如果是在当天执行, 那么这里创建的新索引名会是 logs_2016.10.31-000002 // 如果是隔天, 那么就是 logs_2016.11.01-000002 POST /logs_write/_rollover { "conditions": { "max_docs": "1" } }
为什么要使用 Rollover API
- 不断膨胀的历史数据(特别是时间序列数据)需要加以限制
- rollover 功能可以以紧凑的聚合格式保存旧数据, 仅保存您感兴趣的数据
Rollover API Demo
// 当加上参数 ?dry_run ,那么仅仅是测试, 可以方便地查看是否满足 rollover 条件(及具体哪个条件), 并没有实际执行 rollover 操作.
// 比如 POST //_rollover?dry_run
POST //_rollover
{
"conditions": {
// 如果时间超过7天,那么自动rollover,也就是使用新的index
"max_age": "7d",
// 如果文档的数目超过14000个,那么自动rollover
"max_docs": 100000,
// 如果index的大小超过5G(仅针对主分片统计, 副本分片不会被算进来),那么自动rollover
"max_size": "5gb"
}
}
// 也可以指定具体新创建的索引的名子
POST //_rollover/
Path 路径参数
可以是 alias(索引别名) 或 data stream必选参数
根据不同的 target, rollover 的行为也不一样
若是指向一个单独索引的 alias
- 创建新索引
- alias 指向新的索引
- 从原来的索引上移除 alias
若指向(多个)索引的 alias, 且这些索引中有(且只能是)一个设置了
is_write_index: true
- 创建新索引
- 对新创建的索引设置
is_write_index: true
- 对旧的索引设置
is_write_index: false
> 此时 alias 会同时指向这些索引, 但只会 write 到新建的索引(即此时 `is_write_index: true` 的那个索引), read 依旧是从所有索引中读.
若是"data stream"
- 创建新索引
- 在 data stream 上将新建的索引添加进去作为 backing index 和 write index
- 增加 data stream 属性的 generation 值.
可以指定新创建的索引的名字可选参数.
若
是 data stream, 则不支持设置该参数.若
是 alias, 且对应的索引名不符合规则(以-数字
为结尾, 比如logs-001
是符合规则的), 那么就必须手动指定该参数.命名需满足规则
- 仅小写
- 不能包含
\
,/
,*
,?
,"
,<
,>
,|
,,
、#
- ES 7.0 之前可以包含冒号(
:
),但从 7.0 开始就不支持了 - 不能以
-
,_
,+
作为索引名开头 - 不可能是
.
或..
- 不能超过 255个字节(多字节的要注意)
- 以
.
开头的索引不建议使用(deprecated), 除了隐藏索引( hidden indices)以及插件管理的内部索引外.
建议索引命名格式: xxxxxxx-6位数字
, 比如从 xxxxx-000001
开始
Query 查询参数
dry_run
仅仅是测试是否满足 rollover 条件(包括查看索引名), 并不实际执行 rollover.可选
Request boyd 请求体
aliases
索引别名conditions
rollover 条件max_age
max_docs
max_size
这里的 docs 和 size 都只算主分片的, 并不会受副本分片影响.
mappings
设置新索引的 mappingsettings
设置新索引的 settings
filebeat 自动创建的 ILM 示例中配置的 rollover
PUT _ilm/policy/filebeat
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_age": "30d",
"max_size": "50gb"
},
"set_priority": {
"priority": null
}
}
}
}
}
}
索引全生命周期管理及工具介绍
索引生命周期管理(ILM)特别适合处理时间序列的索引.
时间序列的索引是指:
- 索引中的数据随着时间,持续不断增长.
- 数据量大, 且早期的数据价值越来越低(很少写甚至是只读, 到完全无用)
按照时间序列划分索引
- 管理更方便(比如完整删除一个索引的操作会比 delete_by_query 性能更好)
ILM 将索引生命周期按时间顺序划分为以下几个阶段(单向变化)
- Hot: 索引还存在着大量的读写操作
- Warm:索引不存在写操作,还有被查询的需要
- Cold:数据不存在写操作,读操作也不多
- Delete:索引不再需要,可以被安全删除
Hot -> Warm -> Cold -> Delete并不要求所有阶段都要设置, 比如 filebeat 创建的 ILM 就只有一个 Hot
ILM 能做的事:
根据条件修改索引的生命周期
比如 Cold 阶段的数据存放在性能比较差的 ES 节点上.
- 定期关闭或删除索引
filebeat 的示例
ILM Policy
PUT _ilm/policy/filebeat
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_age": "30d",
"max_size": "50gb"
},
"set_priority": {
"priority": null
}
}
},
"delete": {
"min_age": "180d",
"actions": {
"delete": {
"delete_searchable_snapshot": true
}
}
}
}
}
}
Index Template
PUT /_template/filebeat-7.9.3
{
"index_patterms": [
"filebeat-7.9.3-*"
],
"settings": {
"index": {
"lifecycle": {
"name": "filebeat",
"rollover_alias": "filebeat-7.9.3"
},
"mapping": {
"total_fields": {
"limit": "10000"
}
},
// 将 refresh interval 设置为 5s 以提高性能
"refresh_interval": "5s",
"number_of_shards": "1",
// 这个是我自己加的, 因为我在部署时是单节点的 ES 集群
"number_of_replicas": "0",
"max_docvalue_fields_search": "200",
"query": {
"default_field": [
"message",
"tags",
"agent.ephemeral_id",
"agent.id",
"agent.name",
"agent.type",
"agent.version",
...
...
...
"fields.*"
]
}
}
}
}
PUT _template/filebeat-7.9.3?include_type_name
{
"order": 1,
"index_patterns": [
"filebeat-7.9.3-*"
],
"settings": {
"index": {
"lifecycle": {
"name": "filebeat",
"rollover_alias": "filebeat-7.9.3"
},
"mapping": {
"total_fields": {
"limit": "10000"
}
},
// 将 refresh interval 设置为 5s 以提高性能
"refresh_interval": "5s",
"number_of_shards": "1",
// 这个是我自己加的, 因为我在部署时是单节点的 ES 集群因此将副本分片设为 0
"number_of_replicas": "0",
"max_docvalue_fields_search": "200",
"query": {
"default_field": [
"message",
"tags",
"agent.ephemeral_id",
"agent.id",
"agent.name",
"agent.type",
"agent.version",
"as.organization.name",
// 这里省略大量字段
...
...
...
"fields.*"
]
}
}
},
"mappings": {
"_doc": {
"dynamic": true,
"numeric_detection": false,
"date_detection": false,
"_source": {
"enabled": true,
"includes": [],
"excludes": []
},
"_meta": {
"beat": "filebeat",
"version": "7.9.3"
},
"_routing": {
"required": false
},
"dynamic_templates": [
{
"labels": {
"path_match": "labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"container.labels": {
"path_match": "container.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"dns.answers": {
"path_match": "dns.answers.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"log.syslog": {
"path_match": "log.syslog.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"network.inner": {
"path_match": "network.inner.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"observer.egress": {
"path_match": "observer.egress.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"observer.ingress": {
"path_match": "observer.ingress.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"fields": {
"path_match": "fields.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"docker.container.labels": {
"path_match": "docker.container.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"kubernetes.labels.*": {
"path_match": "kubernetes.labels.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"kubernetes.annotations.*": {
"path_match": "kubernetes.annotations.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"docker.attrs": {
"path_match": "docker.attrs.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"azure.activitylogs.identity.claims.*": {
"path_match": "azure.activitylogs.identity.claims.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "*"
}
},
{
"kibana.log.meta": {
"path_match": "kibana.log.meta.*",
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
},
{
"strings_as_keyword": {
"mapping": {
"ignore_above": 1024,
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"@timestamp": {
"type": "date"
},
...
...
...
}
}
}
}
}
这是我自己敲的, 不能完全确保没敲错