升升的NotaBooka



路漫漫其修远兮
吾将上下而求索




开源项目agent-updater源码阅读



开源工具Agent-Updater主要用来批量更新所有机器上的Agent。分为Meta和Updater两部分。
工具作者秦晓辉,代码在这里,相应的视频教程
本文是我个人的源码阅读笔记。

前言

agent-updater可以看做是一个微缩版的部署系统
之所以这么说,是因为它确确实实做到了部署系统应该做的事:上线代码、版本管理、甚至小流量的支持
然而这么说又不太对,就我个人看法,部署系统应该是与公司业务相关的,不同的网络、IDC的选型,不同的服务划分的选型,应该有不同的部署解决方案。
如果说监控系统还可以独立于业务之外的话,部署系统是实在无法与业务分开,因为部署必须要依赖于服务树,服务树是业务架构的划分,业务线与机器之间的对应关系。

整体架构

alt text

  • 项目分为meta和updater两部分
  • meta作为一个服务端,用户在meta上统一进行配置
  • updater作为一个agent,运行在每台客户端上,向上联系meta获取信息,向下管理着机器上的各种agent

Meta

alt text

  • Config模块,主要加载用户配置,来处理各updater对应的agent版本等信息
  • HTTP模块,用来和updater做交互,同时对外提供各updater的状态信息
  • DOWNLOAD模块,用来提供一个文件服务器,提供给updater拉取tar包

Updater

alt text

  • HTTP模块,用来和meta交互,上报本机agent信息,下拉配置信息和tar包
  • cron模块,用来拉取tar包,部署对应agent,同时获取各agent运行信息

配置的加载

这是我学习的第一个golang的项目,之前只是使用过beego,看过beego的部分代码。学习UlricQin的这个项目让我受益良多。感谢UlricQin

  • 配置的加载是用的json格式的文件
  • 读取直接使用json.Unmarshal即可,需提前定义好结构体
  • 放在g包的一个全局变量中,带一把读写锁

    var (
        ConfigFile string
        config     *GlobalConfig
        configLock = new(sync.RWMutex)
    )
    
    func Config() *GlobalConfig {
        configLock.RLock()
        defer configLock.RUnlock()
        return config
    }
    

Heartbeat请求的实现

为了解决同时请求meta,造成meta压力过大的问题,要让updater在第一个心跳请求之前sleep一个随机的时间(0 < t < 心跳周期)。
第一次心跳请求之后,每次请求sleep心跳骤起的时间就可以了。

    func SleepRandomDuration() {
        ns := int64(g.Config().Interval) * time.Second
        // 以当前时间为随机数种子
        r := rand.New(rand.NewSource(time.Now().UnixNano()))
        d := time.Duration(r.Int63n(ns)) * time.Nanosecond
        time.Sleep(d)
    }

缺点

使用updater上报-下拉的方式,诚然减少了服务端压力,然而meta对于各agent的控制力降低,无法实时监控各agent状态,也无法及时获取到agent的各异常信息。

改进

服务端维护一个全量的机器列表,不管用DB、内存还是文件。可定时查看各agent详细情况。

后记

回头想想,使用updater主动连meta的这种方式,确实带来了诸多优点,同时也导致了meta中央控制力的减弱。对agent的状态无法实时掌控,是一个运维工程师无法忍受的。
当然也可以通过其他的方式来弥补这一点,比如定时查询状态等,但如此一来,又违反了我们这样设计的初衷。
真正的部署系统,是应当与服务树、与公司的业务紧密相关的。所以,UlricQin在视频里说“这并不是一个真正的部署系统,因为一个真正的部署系统应该有各种更加复杂的逻辑”。

2015.9.28

升升