OWL--监控系统实战七上线运行

  本篇文章就是该系列的最后一篇文章了,来的有点晚。本来计划这篇文章是和之前的几篇文章一起发布的,由于那个周末两天时间连续写了好几篇写的有点累了(也比较晚了)最后这篇还没写完,所以就只发布了前面的六篇。
主要说一下线上部署的事儿,以及说一下最后的一些改动和优化吧。

最后的小BUG

  • 这个是一个什么问题呢?就是一个官网所说的登陆失败的问题,当然他们早已更新,只是我们的二次开发是基于8月1号左右的所以我们的static模块还是之前的代码。
    • 就导致苹果内置的浏览器,还有其他版本的google浏览器无法登陆。
    • 为啥快上线了才发现呢?因为我自己开发测试都没事儿,使用的google版本56.0.2924.87 (64-bit)
    • 这个只需要把最新的static模块代码下载下来替换即可解决 owl commits

样式的小改动

  1. 虽然owl的css,js做了一下压缩,logo做了一些加密在里面,但想改动还是可以的
    owl 架构图

  2. 可以通过google浏览器还原css如下,js同样也是很清楚了(需要改代码,就拷贝下面格式好的代码修改即可
    owl 架构图

最后的优化

  • 原由:如果认真看过实战五二次开发就会发现一个可以优化的点

    • 用户下面的进程是不定的,所以后面想保存一个总值是有误差的,最后解决是保存了pid级别的metric信息
      owl 架构图
    • 虽然问题是解决了,也能正常运行,就是导致metrics数量有点大,作为一个监控系统是我们不想看到的(IO是很珍贵的资源!)
  • 优化改造

    1. 目的(最优结果)就是保存用户维度的数据,不保存所有进程级别的数据
    2. 为什么我们要去保存进程级别的数据?因为有进程差异情况
    3. 那为什么保存进程级别的数据就没有差异的情况了呢?因为我们的metric就是进程级别的,在agent.go里面可以看出,他是把上一次的数据metrics记录了下来,如果查询不到上一次的记录,该流程下面就不会被执行,即差异的结果就会被过滤。
    4. 我们要保存用户级别的数据,那就把这个差异的结果去掉(自己做缓存保存上一次的结果,然后做减法不就行了)
  • 具体实现

    1. 一个线程安全的Map,用来做缓存保存上一次的结果。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      var pidDataHis =  newThreadSafeMap()	//pid 上一次的历史数据,KEY:PID+唯一NAME值

      // 线程安全的map
      type threadSafeMap struct {
      sync.RWMutex
      Map map[string]uint64
      }

      func newThreadSafeMap() *threadSafeMap {
      tsMap := new(threadSafeMap)
      tsMap.Map = make(map[string]uint64)
      return tsMap
      }

      func (tsMap *threadSafeMap) read(key string) (uint64,bool) {
      tsMap.RLock()
      value , ok := tsMap.Map[key]
      tsMap.RUnlock()
      return value,ok
      }

      func (tsMap *threadSafeMap) write(key string, value uint64) {
      tsMap.Lock()
      tsMap.Map[key] = value
      tsMap.Unlock()
      }
    2. 提供一个方法,该方法就获取该时间和上一次时间值的差值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // pid + uuid:组成Map 的 key ,保证唯一性
      // currValue:当前值
      func GetDValue( pid string, uuid string, currValue uint64 ) (uint64,bool) {
      history, ok := pidDataHis.read(pid+uuid)
      pidDataHis.write(pid+uuid, currValue)
      if ok {
      return currValue - history , ok
      }
      return 0 , false;
      }
    3. 我们在处理值得时候就可以直接获取差值就行(调用上面的方法)

    4. 然后就把结果放入到openTSDB了,但是DataType写什么呢?我们已经处理成一个结果了,最终的数据只需要:结果值/Cycle就行,但是并没有我们需要的。只有三种
      DataType

    5. 我们就增加一种DataType呗,取名为REDUCE

      1
      2
      case "REDUCE", "reduce":
      tsd.Value = tsd.Value / float64(tsd.Cycle)
    6. 增加了DataType,MySQL里面也得对应修改一下

      1
      2
      3
      ALTER TABLE `metric`
      CHANGE COLUMN `dt` `dt` ENUM('GAUGE','DERIVE','COUNTER','REDUCE') NOT NULL COLLATE 'utf8_unicode_ci' AFTER `name`;
      SET FOREIGN_KEY_CHECKS = 1;

数据初始化(清除数据)

  • 在自己测试的时候,或者线上的环境由于之前的问题,然后进行了改动,留了太多无效的metric。
  • 个人有点强迫症,打算清掉之前的数据,步骤就是清除MySQ和HBase的数据。
  1. 清除MySQ数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    mysql> use owl
    Database changed
    mysql> source /usr/local/src/owl/scripts/db_schema/owl.sql;
    ####插入默认管理员用户
    mysql> INSERT INTO `user` (username, password, role, phone, mail, weixin, status) VALUES ('admin', '21232f297a57a5a743894a0e4a801fc3', '1', '', '', '', '1');

    #####记得添加我们新增的DataType
    ALTER TABLE `metric`
    CHANGE COLUMN `dt` `dt` ENUM('GAUGE','DERIVE','COUNTER','REDUCE') NOT NULL COLLATE 'utf8_unicode_ci' AFTER `name`;
    SET FOREIGN_KEY_CHECKS = 1;
  2. 清除HBase数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ### 就是删了,重建
    disable 'tsdb-uid'
    disable 'tsdb'
    disable 'tsdb-tree'
    disable 'tsdb-meta'

    drop 'tsdb-uid'
    drop 'tsdb'
    drop 'tsdb-tree'
    drop 'tsdb-meta'

    create 'tsdb-uid',
    {NAME => 'id', COMPRESSION => 'NONE', BLOOMFILTER => 'ROW'},
    {NAME => 'name', COMPRESSION => 'NONE', BLOOMFILTER => 'ROW'}
    create 'tsdb',
    {NAME => 't', VERSIONS => 1, COMPRESSION => 'NONE', BLOOMFILTER => 'ROW'}
    create 'tsdb-tree',
    {NAME => 't', VERSIONS => 1, COMPRESSION => 'NONE', BLOOMFILTER => 'ROW'}
    create 'tsdb-meta',
    {NAME => 'name', COMPRESSION => 'NONE', BLOOMFILTER => 'ROW'}
  3. 注意
    如果openTSDB还在运行,owl等服务还在写入数据的时候去做数据清理(删表等操作),会报如下错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    18:20:42.819 ERROR [CompactionQueue.call] - Failed to read a row to re-compact
    org.hbase.async.TableNotFoundException: "tsdb"
    at org.hbase.async.HBaseClient$7.call(HBaseClient.java:2315) ~[asynchbase-1.7.0.jar:na]
    at org.hbase.async.HBaseClient$7.call(HBaseClient.java:2312) ~[asynchbase-1.7.0.jar:na]
    at com.stumbleupon.async.Deferred.doCall(Deferred.java:1278) [async-1.4.0.jar:na]
    at com.stumbleupon.async.Deferred.runCallbacks(Deferred.java:1257) [async-1.4.0.jar:na]
    at com.stumbleupon.async.Deferred.callback(Deferred.java:1005) [async-1.4.0.jar:na]
    at org.hbase.async.HBaseRpc.callback(HBaseRpc.java:698) [asynchbase-1.7.0.jar:na]
    at org.hbase.async.RegionClient.decode(RegionClient.java:1509) [asynchbase-1.7.0.jar:na]
    at org.hbase.async.RegionClient.decode(RegionClient.java:88) [asynchbase-1.7.0.jar:na]
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:500) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:435) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.9.4.Final.jar:na]
    at org.hbase.async.RegionClient.handleUpstream(RegionClient.java:1206) [asynchbase-1.7.0.jar:na]
    at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.SimpleChannelHandler.messageReceived(SimpleChannelHandler.java:142) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler.handleUpstream(IdleStateAwareChannelHandler.java:36) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.handler.timeout.IdleStateHandler.messageReceived(IdleStateHandler.java:294) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) [netty-3.9.4.Final.jar:na]
    at org.hbase.async.HBaseClient$RegionClientPipeline.sendUpstream(HBaseClient.java:3108) [asynchbase-1.7.0.jar:na]
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:88) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:108) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:318) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:89) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) [netty-3.9.4.Final.jar:na]
    at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42) [netty-3.9.4.Final.jar:na]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_141]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_141]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_141]
  • 所以我们在做数据清楚的时候最好关掉服务,如果不想关掉服务记得做完初始化操作后回来重启一下openTSDB(有可能你获取不到数据了,因为HBaseClient: Lost connection with the .META. region)
    1
    2
    3
    4
    5
    2017-09-07 18:25:37,220 INFO  [AsyncHBase I/O Worker #13] HBaseClient: Closing idle connection to HBase region server: [id: 0xe8cfafdd, /10.134.81.55:34726 => /10.134.81.48:16020]
    2017-09-07 18:25:37,220 INFO [AsyncHBase I/O Worker #13] HBaseClient: Channel [id: 0xe8cfafdd, /10.134.81.55:34726 => /10.134.81.48:16020] is disconnecting: [id: 0xe8cfafdd, /10.134.81.55:34726 => /10.134.81.48:16020] CLOSE
    2017-09-07 18:25:55,720 INFO [AsyncHBase I/O Worker #17] HBaseClient: Closing idle connection to HBase region server: [id: 0x33948b1b, /10.134.81.55:39507 => /10.134.81.52:16020]
    2017-09-07 18:25:55,720 INFO [AsyncHBase I/O Worker #17] HBaseClient: Channel [id: 0x33948b1b, /10.134.81.55:39507 => /10.134.81.52:16020] is disconnecting: [id: 0x33948b1b, /10.134.81.55:39507 => /10.134.81.52:16020] CLOSE
    2017-09-07 18:25:55,721 INFO [AsyncHBase I/O Worker #17] HBaseClient: Lost connection with the .META. region

上线部署

  1. 上线的规划因为线上机器目前大约200台,当时每台机器的metrics约13万(当然后面优化后约1000),单个repeater写入:200*13万=2000多万呢

  2. 所以单节点肯定是不行的,通过之前的结构图以及阅读代码后理解下图红圈的都可以做成集群的模式

    • owl 架构图
    • api也是可以多节点,owl作者说后面补上auto_build_index只需一台设置为true
    • 部署方式一对一(同一机器同时部署他们)openTSDB/repeater/inspector/api
    • 考虑到后期还有更多的metric收集,以及节点的增加,openTSDB/repeater/inspector/api在6台服务器上都部署
    • HBase使用的之前的集群(10多个节点)
  3. 配置文件openTSDB/repeater/inspector/api指定为127.0.0.1,agent自定义配置分发)

    • openTSDB 6个节点的配置文件都一样,只需设置tsd.storage.hbase.zk_quorum
    • repeater repeater.conf,opentsdb_addr指定为本服务器的opentsdb

      1
      2
      3
      4
      5
      6
      7
      8
      backend=opentsdb
      tcp_bind=0.0.0.0:10040

      opentsdb_addr=127.0.0.1:4242
      repeater_addr=

      max_packet_size=4096
      buffer_size=1048576
    • inspector inspector.conf 指定controller地址,本地的openTSDB

      1
      2
      3
      4
      5
      6
      7
      8
      9
      controller_addr=10.xx.xx.xx:10050

      max_packet_size=40960
      max_task_buffer=4096
      max_result_buffer=4096
      worker_count=5

      tsdb_addr=127.0.0.1:4242
      tsdb_timeout=30
    • api api.conf 指定MySQL地址,本地的openTSDB

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      http_bind=0.0.0.0:10060

      mysql_addr=10.xx.xx.xx:3306
      mysql_user=user
      mysql_dbname=owl
      mysql_password=pass
      mysql_max_conn=20
      mysql_max_idle_conn=5

      secret_key=b690dfddeb13156b6b88xxxxxxxxxxxxx3df1d285576e843c8zzzzzzzzzzzzz

      opentsdb_addr=127.0.0.1:4242
      opentsdb_timeout=10

      auto_build_metric_tag_index=false|true【自己注意配置】
      auto_build_interval=10
    • agent agent.conf,opentsdb_addr指定为需要分发到的opentsdb服务器地址,cfc地址

      1
      2
      3
      4
      5
      6
      7
      8
      9
      tcp_bind=127.0.0.1:10010

      cfc_addr=10.xx.xx.xx:10020
      repeater_addr=10.xx.zz.yy:10040

      buffer_size=1000000
      max_packet_size=4096
      update_pid_len=86400
      network_card=bond0
  4. 日志级别的配置,按照需求设置吧,我们调通后设置的为4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #日志设置
    #Emergency -> 0
    #Alert -> 1
    #Critical -> 2
    #Error -> 3
    #Warning -> 4
    #Notice -> 5
    #Info -> 6
    #Debug -> 7
  5. 因为api我们也设置了6个实例,所以在nginx那边就要做一下反向代理
    nginx 反向代理

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器