大数据运维及安全

集中式日志分析平台 - ELK Stack - 关于ES的节点监

2017-07-11  本文已影响110人  大数据之心

在运维 ES 集群的过程中,我们一般使用 node stats 接口获取关键指标, node-stats API 可以通过如下命令执行:

node-stats API 可以通过如下命令执行:

GET _nodes/stats

从输出的顶部开始,我们看到集群名称和我们的第一个节点:

{
   "cluster_name": "elasticsearch_zach",
   "nodes": {
      "UNr6ZMf5Qk-YCPA_L18BOQ": {
         "timestamp": 1408474151742,
         "name": "Zach",
         "transport_address": "inet[zacharys-air/192.168.1.131:9300]",
         "host": "zacharys-air",
         "ip": [
            "inet[zacharys-air/192.168.1.131:9300]",
            "NONE"
         ],
...

节点以哈希表列出,key 为节点的UUID。 显示有关节点网络属性的一些信息(如传输地址和主机)。 当节点没有正确加入集群的时候,这些值对于调试发现问题非常有用:通常会看到使用了错误的端口,或者该节点绑定到错误的IP地址/接口。

indices 信息块

indices 信息块列出该节点上所有 indices 的聚合信息:

    "indices": {
        "docs": {
           "count": 6163666,
           "deleted": 0
        },
        "store": {
           "size_in_bytes": 2301398179,
           "throttle_time_in_millis": 122850
        },

返回结果可以分为 2 块:

        "indexing": {
           "index_total": 803441,
           "index_time_in_millis": 367654,
           "index_current": 99,
           "delete_total": 0,
           "delete_time_in_millis": 0,
           "delete_current": 0
        },
        "get": {
           "total": 6,
           "time_in_millis": 2,
           "exists_total": 5,
           "exists_time_in_millis": 2,
           "missing_total": 1,
           "missing_time_in_millis": 0,
           "current": 0
        },
        "search": {
           "open_contexts": 0,
           "query_total": 123,
           "query_time_in_millis": 531,
           "query_current": 0,
           "fetch_total": 3,
           "fetch_time_in_millis": 55,
           "fetch_current": 0
        },
        "merges": {
           "current": 0,
           "current_docs": 0,
           "current_size_in_bytes": 0,
           "total": 1128,
           "total_time_in_millis": 21338523,
           "total_docs": 7241313,
           "total_size_in_bytes": 5724869463
        },
        "filter_cache": {
           "memory_size_in_bytes": 48,
           "evictions": 0
        },
        "fielddata": {
           "memory_size_in_bytes": 0,
           "evictions": 0
        },
        "segments": {
           "count": 319,
           "memory_in_bytes": 65812120
        },
        ...

OS 和 Process 信息块

OSProcess 信息块描述的指标比较简单通用,无需详细解释。 OS 信息块描述的是整个 OS,而 Process 信息块 展示的是 Elasticsearch JVM 进程相关指标。

这些都是非常有价值的指标,但是我们通常都会在其他监控平台进行采集和展示:

JVM 信息块

jvm 信息块包括一些 ElasticSearch JVM 进程的重要指标。更重要的是包含了 GC 相关的详细指标,这对观察集群稳定性非常必要。

Garbage Collection Primer

在描述指标之前,非常有必要插入下 GC 相关的知识和对 ES 本身的影响。如果您非常了解 JVM GC,可以直接跳过。

Java 是一种 garbage-collected 语言,意味着开发者不需要关心内存的分配和回收。

当内存被分配至 JVM 进程,会被分配到叫做 heap 的大 chunk。JVM 会把 heap 划分为两大块,根据 generations

当对象被实例化时,它被分配至新生代。 当新生代空间满时,会发生 YGC。 仍然需要存在的对象会被移动到其中的一个 suvive 空间中,并且"dead"对象会在这个过程被移除。 如果一个对象在若干个 YGC 中幸存下来,那么它就会晋升到老生代。

在老生代中也会发生类似的过程:当空间变满时,垃圾回收开始,"dead"对象被删除。

然而,GC 过程是有代价的。 不论是 YGC 还是 FGC 都有 "Stop-the-World" 的阶段。 在此期间,JVM会直接停止执行程序,以便跟踪对象 graph 并收集 "dead" 对象。 在 "Stop-the-World" 阶段任何事情都不会发生。 请求不得到服务,ping 不受到响应,shards 不被重定位。 对于 ES 来说,世界确实停止了。

这对新生代来说不是什么大问题;其小尺寸意味着 GC 会被快速执行。 但是老生代的问题还是比较大的,而这里如果存在缓慢的 GC 可能意味着1秒甚至150秒的暂停,这对服务器软件是不可接受的。

JVM中的垃圾回收器是非常复杂的算法,并且可以很好地缩短停顿时间。 和 ES 在 GC 方面做了很深的优化,通过智能地内部重用对象,重用网络缓冲区,并默认启用[docvalues]。 但是最终,GC 频率和持续时间仍是需要关注的指标,因为它是群集不稳定性的首要原因。

经常遇到长 GC 的集群将是一个负载较重的内存不足的集群。 这些长时间的 GC 将使节点短暂地离开集群。 这种不稳定性导致 shards 频繁被 realocate,因为 Elasticsearch 尝试保持集群平衡和足够可用的副本。 这反过来又增加了网络流量和磁盘 I /O,而同时集群也在尝试维护正常的 indexing 和 query 负载。

总而言之, 长时间的 GC 应该被重视并尽可能的优化。

因为 GC 对 ES 集群至关重要,我们应该密切关注 node-stats API 的 GC 信息块:

        "jvm": {
            "timestamp": 1408556438203,
            "uptime_in_millis": 14457,
            "mem": {
               "heap_used_in_bytes": 457252160,
               "heap_used_percent": 44,
               "heap_committed_in_bytes": 1038876672,
               "heap_max_in_bytes": 1038876672,
               "non_heap_used_in_bytes": 38680680,
               "non_heap_committed_in_bytes": 38993920,

如果节点的堆内存长时间 >=85%,那就更严重。水位线处于 90–95%会非常恐怖的触发 10–30 秒的 GC,甚至是 OOM。

   "pools": {
      "young": {
         "used_in_bytes": 138467752,
         "max_in_bytes": 279183360,
         "peak_used_in_bytes": 279183360,
         "peak_max_in_bytes": 279183360
      },
      "survivor": {
         "used_in_bytes": 34865152,
         "max_in_bytes": 34865152,
         "peak_used_in_bytes": 34865152,
         "peak_max_in_bytes": 34865152
      },
      "old": {
         "used_in_bytes": 283919256,
         "max_in_bytes": 724828160,
         "peak_used_in_bytes": 283919256,
         "peak_max_in_bytes": 724828160
      }
   }
},
"gc": {
   "collectors": {
      "young": {
         "collection_count": 13,
         "collection_time_in_millis": 923
      },
      "old": {
         "collection_count": 0,
         "collection_time_in_millis": 0
      }
   }
}

Threadpool 信息块

Elasticsearch 在内部维护了线程池。这些线程池进行协作完成工作,在必要的时候传递工作。一般情况下,无需对其进行配置,但是某些情况下获取他们的状态去观察集群的行为是有用的。

ES 大概有一打线程池,具有相同的状态信息输出:

  "index": {
     "threads": 1,
     "queue": 0,
     "active": 0,
     "rejected": 0,
     "largest": 1,
     "completed": 1
  }

每一个线程池都列出了它们被配置的线程数(threads),其中正在处理工作的线程数 (active),和有多少工作单元在队列中排队 (queue)。

如果队列达到了极限,新的工作单元会被拒绝,我们会在 rejected 看到统计信息。这表示集群因为某些资源达到了瓶颈,因为队列满了意味着节点或者集群的处理速度赶不上工作单元的生成速度。

Bulk Rejections(批处理拒绝)

如果遇到了队列拒绝,很大可能是因为 bulk indexing 请求造成的。一旦阈值达到,队列会迅速被填满,新的 bulk 请求会被拒绝。队列拒绝是一种集群友好的策略,避免被打爆。他告诉了运维同学集群的最大能力在哪里,绝对比把数据持续塞进内存队列好的多。增加队列大小并不能带来性能提升,而是隐藏了问题。如果集群只能处理 10K docs/s,不论队列多大,那也只能处理那么多。队列会简单的隐藏性能问题并且加重数据丢失的风险。任何被塞进队列的都是被定义为未被处理的。如果节点宕机,这些请求都会被永久丢失。进一步来说,队列也会吃掉大量的内存所以不建议开太大。

所以在开发应用的时候也需要优雅的处理 queue rejected 导致的问题,当接收到 bulk rejections 相关异常时,必须这么做:

  1. 停止核心线程 3-5 秒;
  2. 从bulk response 中解析出被拒绝的 actions,因为可能 bulk 操作中有大量 action 是成功的;
  3. 再次发送新的 bulk request,去处理被拒绝的 actions;
  4. 如果再次遇到 rejected 重复第1步;

Rejections 并不是 error:他意味着你必须要再次重试;

我们需要关注如下的线程池信息:

FS 和 Network 信息块

往下拉,可以看到 node-stats API 还附带了这些输出信息:free space,data directory paths,disk I/O stats 等等。如果你没有在第三方监控到,那么可以走这里获取。

同样的对于网络信息块,输出如下:

        "transport": {
            "server_open": 13,
            "rx_count": 11696,
            "rx_size_in_bytes": 1525774,
            "tx_count": 10282,
            "tx_size_in_bytes": 1440101928
         },
         "http": {
            "current_open": 4,
            "total_opened": 23
         },

Circuit Breaker 信息块

最后介绍下 Circuit Breaker 信息块:fielddata circuit-breaker 相关统计指标:

         "fielddata_breaker": {
            "maximum_size_in_bytes": 623326003,
            "maximum_size": "594.4mb",
            "estimated_size_in_bytes": 0,
            "estimated_size": "0b",
            "overhead": 1.03,
            "tripped": 0
         }

这段信息告诉了我们 circuit-breaker 的大小(例如,如果 query 尝试使用更多内存,circuit breaker 会在什么阈值跳闸,阻断 query),还告诉了我们 circuit-breaker 阻断的次数以及当前配置的开销。 开销用于弥补估计值的偏差,因为某些 query 比其他 query 更难估计。最重要的指标是tripped如果该值很大或者持续增加,说明 query 需要被优化或者需要更多内存去支撑 query。

上一篇 下一篇

猜你喜欢

热点阅读