1

[TOC]

TODO: 暂时略过, 后续再补课.

(这部分暂时以截图为主)

https://github.com/geektime-g...

这一整节的内容 pdf, 可以搜索复制文本.

保护你的数据

集群身份认证与用户鉴权

image-20201103095444936

image-20201103095453930

image-20201103095504944

image-20201103095513198

image-20201103095523390

image-20201103095535669

image-20201103095546113

image-20201103095553620

image-20201103095600831

开启并配置 X-Pack 的认证与鉴权

  1. 修改配置文件, 打开认证与授权 elasticsearch.yml

    xpack.security.enabled: true
    # xpack.license.self_generated.type: basic
    # xpack.security.transport.ssl.enabled: true
  2. 启动 ES
  3. 创建默认的用户和分组

    bin/elasticsearch-setup-passwords interactive

    会创建用户:

    • elastic
    • apm_system
    • kibana
    • kibana_system
    • logstash_system
    • beats_system
    • remote_monitoring_user
  4. 配置 Kibana 身份认证 kibana.yml

    elasticsearch.username: "kibana_system"
    elasticsearch.password: "......"
  5. 配置 Logstash 身份认证 logstash.yml

    • 启用 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, 具体见下面的 "集群内部安全通信" 一节.

image-20201103095624616

image-20201103095632690

在 kibana 中 manager 的 security 可以方便图形化地添加 用户/角色

集群内部安全通信

img

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/...

img

默认生成的 CA 文件名: elastic-stack-ca.p12

默认生成的节点证书文件名: elastic-certificates.p12

img

配置节点间通讯

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"

img

集群与外部间的安全通信

img

img

img

↑ kibana 使用的证书格式是 pem, 而之前 es 生成的节点证书(默认文件名: elastic-certificates.p12)是p12 格式, 因此需要使用 openssl 将其转换为 pem 格式.img

img

img

img

↑ 将上述生成的 elastic-stack-ca.zip 解压得到 ca.crt 文件和 ca.key 文件.

配置 config/kibana.yml:

img

由于这里的证书是自签的, kibana启动时会报错, 只是做测试, 可以忽略.

水平扩展 ES 集群

常见的集群部署方式

img

img

img

img

img

img

img

img

img

img

img

Hot & Warm 架构与Shard Filtering

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

当使用 force awareness, 都指定了不存在的 zone, 那么会导致分片(副本)无法分配.

img

分片设计及管理

img

↑ 7.0 之前是默认创建5个主分片...

img

img

img

img

img

img

img

如何对集群进行容量规划

img

img

img

后文有提到说, 搜索类的按照 1:16, 日志类的按照 1:48 ~ 1:96 之间.

img

img

img

img

img

img

img

img

img

img

img

在私有云上管理 ES 集群的一些方法

img

img

img

img

img

img

img

img

在公有云上管理与部署 ES 集群

img

img

img

img

生产环境中的集群运维

生产环境常用配置与上线清单

img

img

img

需配置 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 这两种模式

img

> 官方文档中 Import Elasticsearch configuration 这一节中介绍了一些重要的 ES 参数配置项

img

> 需要对 Linux 主机进行相关设定, 否则在生产环境模式是无法通过检查, 启动报错.

> 具体参见 Bootstrap Checks 一节

img

img

> 这里的内存大小指的是给 JVM 分配的大小, 系统应该额外预留系统的另外一半内存给 Lucene.

> 也就是上面单个节点分配给JVM 31G内存, 那么这台主机实际内存占用在 64GB 左右

img

img

img

img

img

img

监控 ES 集群

img

img

img

img

诊断集群的潜在问题

img

img

img

img

img

img

img

img

解决集群 Yellow 与 Red 的问题

img

img

img

https://github.com/geektime-g...

img

img

img

img

img

提升集群写性能

img

img

img

↓ 文档建模的一些最佳实践

img

img

img

img

img

img

img

一个索引设定的例子

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"
    }
  }
}

提升集群读性能

img

img

img

img

img

img

img

img

集群压力测试

img

img

img

img

img

img

img

img

img

img

img

img

段合并优化及注意事项

img

img

img

img

缓存及使用 Breaker 限制内存使用

img

img

img

img

img

img

img

img

img

img

img

一些运维的相关建议

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

索引生命周期管理

使用 Shrink 与 Rollover API 有效管理序列索引

img

img

img

img

*Demo*

img

imgimg

imgimg

img

Demo

imgimg

img

img

示例1

imgimg

示例2

img

Rollover API

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 /<logs-{now/d}-1> 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-target>/_rollover?dry_run

POST /<rollover-target>/_rollover
{
    "conditions": {
        // 如果时间超过7天,那么自动rollover,也就是使用新的index
        "max_age": "7d",
        // 如果文档的数目超过14000个,那么自动rollover
        "max_docs": 100000,
        // 如果index的大小超过5G(仅针对主分片统计, 副本分片不会被算进来),那么自动rollover
        "max_size": "5gb"
    }
}


// 也可以指定具体新创建的索引的名子
POST /<rollover-target>/_rollover/<target-index>

Path 路径参数

  • <rollover-target> 可以是 alias(索引别名) 或 data stream

    必选参数

    根据不同的 target, rollover 的行为也不一样

    • 若是指向一个单独索引的 alias

      1. 创建新索引
      2. alias 指向新的索引
      3. 从原来的索引上移除 alias
    • 若指向(多个)索引的 alias, 且这些索引中有(且只能是)一个设置了 is_write_index: true

      1. 创建新索引
      2. 对新创建的索引设置 is_write_index: true
      3. 对旧的索引设置 is_write_index: false
> 此时 alias 会同时指向这些索引, 但只会 write 到新建的索引(即此时 `is_write_index: true` 的那个索引), read 依旧是从所有索引中读.
    • 若是"data stream"

      1. 创建新索引
      2. 在 data stream 上将新建的索引添加进去作为 backing index 和 write index
      3. 增加 data stream 属性的 generation 值.
    • <target-index> 可以指定新创建的索引的名字

      可选参数.

      <rollover-target> 是 data stream, 则不支持设置该参数.

      <rollover-target> 是 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 设置新索引的 mapping
    • settings 设置新索引的 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 将索引生命周期按时间顺序划分为以下几个阶段(单向变化)

    1. Hot: 索引还存在着大量的读写操作
    2. Warm:索引不存在写操作,还有被查询的需要
    3. Cold:数据不存在写操作,读操作也不多
    4. Delete:索引不再需要,可以被安全删除
    Hot -> Warm -> Cold -> Delete

    并不要求所有阶段都要设置, 比如 filebeat 创建的 ILM 就只有一个 Hot

    ILM 能做的事:

    • 根据条件修改索引的生命周期

      比如 Cold 阶段的数据存放在性能比较差的 ES 节点上.
    • 定期关闭或删除索引

    img

    img

    img

    img

    img

    img

    img

    img

    img

    img

    img

    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"
            },
            ...
            ...
            ...
            }
          }
        }
      }
    }
    这是我自己敲的, 不能完全确保没敲错

    嘉兴ing
    284 声望24 粉丝

    PHPer@厦门