写好 DevOps 的文档 by Glow CTO Ryan YE - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Glowapp
V2EX    DevOps

写好 DevOps 的文档 by Glow CTO Ryan YE

  •  
  •   Glowapp 2015-11-16 12:13:07 +08:00 4410 次点击
    这是一个创建于 3681 天前的主题,其中的信息可能已经有所发展或是发生改变。
    每个 DevOps 都一个百宝箱,里面放着各种命令行脚本,可以用来自动化各式任务。但若文档不全,即便是脚本的作者,时间一久也不敢随便乱用,毕竟运维的大部分工作是管理生产环境,要是出了错,不是轻描淡写就可以蒙混过关的。写好 DevOps 的文档其实也是一门技术活儿,这里给大家分享一些组织运维脚本及其文档的经验。


    因为行文里有一些附图,我就统一更新在评论上。
    原文地址 http://tech.glowing.com/cn/better-documentation-for-devops/
    Ryan 个人博客 http://yejianye.com/
    我司官网 glowing.com
    我司微信 Glow_Inc
    4 条回复    2015-11-16 15:53:12 +08:00
    Glowapp
        1
    Glowapp  
    OP
       2015-11-16 12:13:50 +08:00
    Fabric 的任务管理与文档
    在以前的文章中,我们曾经介绍过 Glow 使用了 fabric 来执行各种日常管理的任务。 Fabric 提供了非常好用的任务组织以及查阅任务文档的功能。
    Fabric 的主文件一般命名为 fabfile.py ,但任务多了,都写在一个文件里显然很难维护。 Fabric 有一个很实用的特性,就是当 fabfile.py 里导入其他模块时,会自动发现里面的 fabric 任务。利用这个特性,可以把各种任务分类写在不同的模块中,然后在 fabfile.py 中统一导入。比如 Glow 的 DevOps 代码库的结构大概长这个样子


    fabfile.py 里除了一些最核心的任务脚本外,主要就是一些 import 语句



    这样我们就把散落在多个文件里的任务聚集到了一起,我们可以用 fab -l 来列出所有可执行的任务及其描述,其中任务描述来自于对应任务的第一行 docstring 。例如,



    这里可以看到,将任务分写在不同的模块,模块名就起到了 Namespace 的作用。在显示命令列表时,在同一个 Namespace 下的命令被聚集到了一起,很好地起到了任务分类的作用。使用 fab -d [task_name]可以显示该任务完整的 docstring 。规整的 docstring 可以让执行任务的用户清楚地理解其作用及参数用法。我们在写 fabric 任务的 docstring 时,一般分为三个部分
    任务的简单介绍
    任务的参数
    具体用例

    最后一点由为重要,有些任务参数众多,即使读了参数说明,仍会让人有些云里雾里。但几个典型的实际用例,对于用户了解任务的用法会起到至关重要的作用。在下面的例子中,我们展示了 deploy 任务(代码部署)的说明文档



    动态 Docstring

    在 Python 中, docstring 其实就是函数的__doc__的属性,所以我们可以像修改普通变量那样动态修改 docstring ,这给我们生成动态文档或是重用公共的文档提供了可能。例如,我们的 services 模块下有 cycle,start,stop 三个任务,分别用来重启,开始,停止我们的 microservice 。我们当然希望在用 fab -d 来查看任务的文档说明时,同时可以显示所有可用的 microservice 。但 hard-coded 现有的 microservice 是一个愚蠢的做法,这样我们不但需要把同一段文档复制三份,并且每次新增一个 microservice 时还要记得来更新文档。这里我们用 Python 的 decorator 来动态地把可用服务的信息添加到 docstring 中。比如 cycle 任务的定义是这样的



    注意到这里用了 @service_doc 这个 decorator ,它的定义如下



    我们通过 get_available_services 来动态取得当前环境下可用的 microservice (这里我们不关心 get_available_service 是如何实现的),并将其添加到函数的 docstring 之后。这样,当我们查看 cycle 的方法时,所有可用的 microservice 也会显示出来。



    动态外部文档

    除了 docstring ,我们也经常需要写独立的外部文档。在 Glow ,这些文档绝大部分都是用 Markdown 来写的。例如,我们需要写一个介绍生产环境架构的文档,其中肯定会加入生产环境中有哪些服务器,每个服务器的功能描述以及它们的 hostname 。我们可以用手动的方式来写,但每当为生产环境添加新服务器时,我们必须记得更新这份文档。而实际情况是,我们从来不在 AWS 的控制台手动创建服务器,所有的服务器都是由 Ansible 来创建与维护的。也就是说,所有的服务器配置信息及其功能描述都已经存在于 Ansible 的 playbook 中。当我们写外部文档时,应该去引用 Ansible 中的信息,而不是重写手写一遍。
    所以在我们的生产环境文档中会利用 HTML 注释来指定需要外部引用的部分,然后通过执行脚本将这些引用的内容填充至文档里。例如,在我们的生产环境文档中有这样一段



    这里<!-- BEGIN EC2-SERVER-LIST -->和<!-- END EC2-SERVER-LIST -->之间的表格就是一个外部引用,每次 Ansible 更新服务器配置时,会执行一个脚本,它会自动在文档中查找这对标签,并更新其中的内容。这是一个很简单的技术,但对于保持文档与实际环境同步很有帮助。

    小结

    几乎所有人都承认文档的重要性,但真正愿意在文档上花费精力的团队却十分有限。写这篇文章是希望大家能意识到,文档其实也是代码的一部分,也应该遵循 DRY 原则,也可以通过编程的手段来动态生成与自动更新。
    如果各位对这种文档与代码结合的概念有兴趣,你也许听过 Literate programming ,( https://en.wikipedia.org/wiki/Literate_programming )那 Literate programming 与 DevOps 结合会是什么样子,有兴趣的可以看看这个视频(
    oclock
        2
    oclock  
       2015-11-16 13:55:25 +08:00
    All due respect 技术布道和招聘广告还是应该分开
    dreampuf
        3
    dreampuf  
       2015-11-16 14:24:35 +08:00
    更常见的做法是将 fabfile 作为一个文件夹,在其中__init__.py 文件加载相关的 scripts
    adoyle
        4
    adoyle  
       2015-11-16 15:53:12 +08:00
    写得真棒!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1148 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 23:59 PVG 07:59 LAX 15:59 JFK 18:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86