配置文件那些事

深夜的办公室灯火通明,为了今晚的大版本上线,同事们已经连续奋战了几十个日日夜夜。"但愿今晚平安上线,接下来就能休息几天了",小明这样想着,打开终端登录服务器,等待操作时间到来。

作为一名运维工程师,这样的版本升级小明经历过无数次,况且在之前进行过数次模拟升级,这次想必也不会出什么太大的问题。

升级时间到了,小明深吸一口气,手指开始在键盘上飞舞,停止进程、备份文件、更新程序、启动、验证,一系列步骤有条不紊的进行着……

“小明,为什么我这里vi打开文件是乱码?”小明停下手中的事情,循声望去,原来是开发人员小刚。“把你的终端编码改一下” “在哪里改?” “就在设置那里” “找不到啊,过来帮忙看看啦” 小明无奈起身,走到小刚身边,教他修改终端字符编码……

同事们都在紧张的忙碌着,墙上挂钟那红色的指针也在不知疲惫的一圈又一圈的行进。。。

“警告警告,XX服务器 /data 占用率 100%,请及时处理。”小明赶紧登录检查,发现该目录下的一个 log 占了90%的磁盘空间,里面的 xx.debug.log 占用达几十个G。看来是有人打开了调试模式,小明一边清理日志,一边在群里发问。
原来是小刚验证的时候发现逻辑有点对不上,然后把调试模式打开了,大量的信息很快就把磁盘空间撑爆。处理完这个问题,小明继续之前的工作。。。

“小明,我加了个配置项,需要同步一下全部服务器。”小刚走了过来,“好,没问题。”小明平静的说到,心想这家伙又来搞事。

过了一会,小刚又过来了:“不好意思啊,刚刚那个配置项要改一下,改成xxx。” 小明有点想骂人了,但还是平淡的说:“要改成什么?”

……

配置文件那些痛

故事有些夸大的成份,但里面的场景确实有发生过,相信很多运维同学也是深有体会。维护一套系统,少不了的就是配置文件,故事里修改配置的几大痛点就是我想要解决的:

  • 连接服务器的终端字符编码不一,配置内容可能显示乱码
  • 不同的人修改了配置项,不知道改了什么内容
  • 修改了配置项后如何同步
  • 同步后又反悔了,怎样快速改回之前的内容

如果我们提供一个统一的修改入口,那么编码不一的问题是不是就解决了?
有人修改了内容,我们就保存一个快照;后面若是反悔,可以马上使用这个快照,是不是同时满足了保存修改和修改历史?
快速同步,从主控端分发过去行不行?

我看行!

配置文件管理

使用B/S架构来做配置管理是很自然的事情,实现起来也比较简单:

  • 浏览器作为统一的修改入口
  • 后端保存文件的修改历史,每一次修改都生成一个新的修订号
  • 同步到相关服务器就是将配置内容下发

那么问题又来了:
单个配置文件管理起来不难,要是多个不同的文件呢?多个文件我只改了其中一个呢?新增加一个文件又不想改动原来的呢?

配置文件包

上述问题,我们可以引入“包”这个概念,一个“包”里可以有一个或多个配置文件,我们对“包”作一个快照,也就是历史版本,那么问题就迎刃而解。

比如可以设计成下图的样子:

配置文件包

A B C 代表3个配置文件包,里面的 a.con b.conf aa.txt 就是具体的文件,数字代表修订号。这样就保存了修改历史,方便回溯,对应的数据模型用 django 的 models 来表示:

class ConfRevision(models.Model):
    name = models.CharField(max_length=48, help_text='名称')
    revision = models.PositiveIntegerField(help_text='修订号')
    is_default = models.BooleanField(default=False, help_text='是否默认')
    created = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    description = models.CharField(max_length=120, blank=True, null=True)

class ConfDetail(models.Model):
    FILE_TYPE = (
        ('ini', 'ini'),
        ('conf', 'conf'),
        ('json', 'json'),
        ('yml', 'yml'),
        ('toml', 'toml'),
    )
    name = models.CharField(max_length=48, help_text='配置文件名称')
    type = models.CharField(max_length=4, choices=FILE_TYPE)
    content = models.TextField(null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    description = models.CharField(max_length=120, blank=True, null=True)
    rev = models.ForeignKey(ConfRevision, on_delete=models.DO_NOTHING)

内容下发

有句名言如是说:“手里拿着锤子,看什么都像是钉子”。

ansible 作为运维利器简直是深得我心,有了这把锤子,很多操作都是经由它去完成的,比如文件下发、备份、系统操作等。一开始就是想着用ansible去同步全部主机的内容,但是一些情况下会有问题:

  • 假如当前有个待同步的主机宕机,本次的同步会漏掉一部分主机
  • 资源抢占问题:多个人同时修改,如果后面的修改先同步到目标主机,而前面的修改延迟到达,导致内容覆盖,本来应该全网统一的内容在个别机器上不一致

为了尽量避免上面情况发生,我们需要另一种方式,即使出现极端情况也能保证配置文件全网一致性:
在每台主机上启动一个代理,从一个中心存储拉取内容,当中心存储的内容变化时通知代理。

分析配置文件,全部是文本内容,很方便使用 key-value 存储,进而想到业界知名的 k-v 存储redis,然而数据持久化并非redis擅长,排除。

etcd 倒是一个不错的选择,满足我们所有要求:

  • 支持 key-value
  • 分布式、强一致性
  • 支持发布-订阅模式

那么决定是他了。

配置同步代理

主机上的代理程序要简单、支持etcd、少依赖,有现成的 confd 就不用重新造轮子了。

功能实现

当收到新增请求(因为做了版本管理,所以每次的修改请求都是创建一个新的版本),后台的处理流程:

  1. 校验数据:非空校验、内容格式校验
  2. 写入数据库,同时将内容写入etcd
  3. 主机上的 confd 监听到对应 key 的内容变化,同步内容到本地

若要回退,只需要将对应的历史版本内容写入etcd
而且记录历史版本,搞事的人就无法甩锅,大家可以尽情的嘲讽他~~~

这里实现了全网配置统一,但是如果有某个新功能需要个性化的配置,又该怎么办呢?欲知方法如何,且听下回分解。

posted @ 2019-12-21 21:29  葡萄不吐皮  阅读(310)  评论(0编辑  收藏  举报