请选择 进入手机版 | 继续访问电脑版

IT运维管理,ITIL,ITSS,ITSM,ISO20000-ITIL先锋论坛

 找回密码
 微信、QQ、手机号一键注册

扫描二维码登录本站

QQ登录

只需一步,快速开始

艾拓先锋
搜索
查看: 281|回复: 0

你会写DevOps的文档吗?

[复制链接]
来自- 广东广州

参加活动:0

组织活动:12

发表于 2018-8-23 14:53:32 | 显示全部楼层 |阅读模式 来自- 广东广州
本帖最后由 monicazhang 于 2018-8-23 14:53 编辑
& \# r$ X& |$ m# b) m& b5 U/ @# m: R  n. g) \
每个DevOps都一个百宝箱,里面放着各种命令行脚本,可以用来自动化各式任务。但若文档不全,即便是脚本的作者,时间一久也不敢随便乱用,毕竟运维的大部分工作是管理生产环境,要是出了错,不是轻描淡写就可以蒙混过关的。写好 DevOps 的文档其实也是一门技术活儿,这里给大家分享一些组织运维脚本及其文档的经验。
# ]5 y+ g& }+ I7 z* b& {9 X- U
1.png

2 Q0 ?, W1 x6 [- C) W
Fabric的任务管理与文档

0 {! I* H) V8 t2 f& ^0 e6 X
在以前的文章中,我们曾经介绍过Glow使用了fabric来执行各种日常管理的任务。Fabric提供了非常好用的任务组织以及查阅任务文档的功能。

/ K4 s. }' R4 A. D) }/ W
Fabric的主文件一般命名为fabfile.py,但任务多了,都写在一个文件里显然很难维护。Fabric有一个很实用的特性,就是当fabfile.py里导入其他模块时,会自动发现里面的fabric任务。利用这个特性,可以把各种任务分类写在不同的模块中,然后在fabfile.py中统一导入。比如 Glow 的 DevOps 代码库的结构大概长这个样子:

6 D$ Z8 I# ]7 O7 ?- r0 M7 A
$ tree0 s) D; Z4 _6 Q5 _
├── __init__.py" b2 [& C9 M6 `. R4 B) B
├── fabfile.py
. H8 a) e* u% d+ L% M8 L! b6 L├── fab_scripts+ T$ f$ J. I+ D$ t
│   ├── __init__.py
9 O9 l9 e# K$ _7 N& ]│   ├── monitors.py7 c" A/ Y# B- ?% C& O0 L6 b0 c
│   ├── mysql.py, [" ^* `7 r, W/ H. @
│   ├── nginx.py( S. E* J9 X3 y
│   ├── redis.py3 n' c' |3 |4 S% k4 E/ |
│   ├── scaling.py
/ y$ N' |) ^7 g│   ├── services.py

, I+ a# C4 T" i' i/ z1 p
在fabfile.py里除了一些最核心的任务脚本外,主要就是一些import语句

  Y( U. N0 Y$ F# H- l6 B  w
# fabfile.pyfrom fab_scripts import monitors  - g6 R9 k! j  V3 z/ M7 p3 K% H
from fab_scripts import mysql  - c& j$ Q; M5 @
from fab_scripts import nginx  
# e9 z5 T! T! \( qfrom fab_scripts import redis  
# Y" h* t5 f/ a$ v+ ?. Yfrom fab_scripts import scaling  % w. O  p% Q8 N6 q0 T$ Z
from fab_scripts import services  

' ?& a# w3 ~8 n
这样我们就把散落在多个文件里的任务聚集到了一起,我们可以用fab -l来列出所有可执行的任务及其描述,其中任务描述来自于对应任务的第一行docstring。例如,3 {7 t8 ^# ?3 |( i
2 }* V- b) ~3 ]- w9 F
$ fab -lAvailable commands:   monitors.get                Get YAML definition of monitors   monitors.list               List names of all monitors   monitors.mute               Mute specific groups of monitors   monitors.mute_all           Mute all monitors globally   monitors.unmute             Unmute specific groups of monitors   monitors.unmute_all         Unmute all monitors globally   mysql.connection_list       Show number of DB connections group by host   mysql.connection_sources    Show number of DB connections group by process   nginx.turn_off_maintenance  Turn off site maintenance mode.   nginx.turn_on_maintenance   Turn on site maintenance mode. Return 503.   redis.auto_save             Update saving settings for redis instances   redis.start_slave           Set master and start replication.   redis.stop_slave            Stop replication and set its master to none.   scaling.add_servers         Launch more instances in specific server group   scaling.create_image        Create an image for provisioning new instances   scaling.get_latest_tag      Get latest tag of deployed code   services.cycle              Restart application services.   services.start              Start application services.   services.stop               Stop application services.
+ Z& V; ^" c6 u. j9 n& t
这里可以看到,将任务分写在不同的模块,模块名就起到了Namespace的作用。在显示命令列表时,在同一个Namespace下的命令被聚集到了一起,很好地起到了任务分类的作用。使用fab -d [task_name]可以显示该任务完整的docstring。规整的docstring可以让执行任务的用户清楚地理解其作用及参数用法。我们在写fabric任务的docstring时,一般分为三个部分
  _% S) W% V& @6 \! V1 g
8 z& Y/ Q- _* A0 J
  • 任务的简单介绍
  • 任务的参数
  • 具体用例
    3 n  N- U  h; P2 |
最后一点由为重要,有些任务参数众多,即使读了参数说明,仍会让人有些云里雾里。但几个典型的实际用例,对于用户了解任务的用法会起到至关重要的作用。在下面的例子中,我们展示了deploy任务(代码部署)的说明文档

  f5 _- C' S/ x; o, z
$ fab -d deploy
9 `. ^/ T4 V% Z$ j/ X/ `" vDisplaying detailed information for task 'deploy':7 A3 `) G+ t! _1 s9 q
! B$ V$ F: U% t, s5 ~$ U1 q/ _1 X7 \
   Deploy code to targeted server_group.
& h+ u" a" m0 K' N0 [$ p   You need put ansible vault password at ~/.ansible_vault_passwd directory, 9 j7 M) f4 T' i+ Q  F6 y
   otherwise you would be prompt to enter vault password.7 `! H/ g# a7 D( E
9 C" Q7 z0 u5 z9 F* q- E+ M2 Z9 A/ ~
   Args:/ G. H* F( Q$ m
       server_group: Possible values include prod, stage% k% _" g+ T! v
       release_tags: A list of release tags to be pushed
2 N5 s; v+ a0 k  D
/ ^: D+ v2 j3 x" `   Examples:        # Deploy to stage
" ^8 L9 F  q6 u4 Q1 a. ]       fab deploy:stage,glow_stage_1446102452        # Deploy to production+ @# ^2 Z% _- a2 a
       fab deploy:prod,glow_prod_1446102452        # Deploy multiple repo at once
1 r( S& l6 x) r       fab deploy:prod,glow_prod_1446102452,nurture_prod_1445102467

: p9 a- a. f) _1 J/ n( \
动态Docstring
. G- b& [7 a& Y4 J, \6 l+ F
在Python中,docstring其实就是函数的__doc__的属性,所以我们可以像修改普通变量那样动态修改docstring,这给我们生成动态文档或是重用公共的文档提供了可能。例如,我们的services模块下有cycle,start,stop三个任务,分别用来重启,开始,停止我们的microservice。我们当然希望在用fab -d来查看任务的文档说明时,同时可以显示所有可用的microservice。但hard-coded现有的microservice是一个愚蠢的做法,这样我们不但需要把同一段文档复制三份,并且每次新增一个microservice时还要记得来更新文档。这里我们用Python的decorator来动态地把可用服务的信息添加到docstring中。比如cycle任务的定义是这样的:
2 y' {% A7 I& E: S6 z( T
@task@services_docdef cycle(*services, **kwargs):  
. v  s' s' z& e0 {; k1 P   """Restart application services.% C0 S) a5 \6 M. s" }& c- B
7 S- O( l1 q' q+ D2 M
   Args:
) \: j* t7 ?1 m/ a       services: list of services need cycle (separate by comma)
$ t8 ^3 N* Y" D* y( r9 x0 j
  G8 ^8 P& ]2 D& E2 L* n$ w" f9 f8 m   Examples:4 x9 F/ c; V' j7 }9 r
       fab services.cycle:glow-www,glow-forum, Z2 t( E3 _; p2 \! l; x( N, ]/ g
   """
( L& r% {! k2 H  K
注意到这里用了@service_doc这个decorator,它的定义如下:
/ |) L" f* K/ d" ~
def services_doc(func):  
- C/ f: T* L+ h+ m0 ^4 e, ?   services = get_available_services()+ a9 Y# A2 |2 X- k: s8 @
   doc = """
9 J- S* }) I( N5 `& ]4 @   Possible values for services:
5 z; _! z  R8 ]. R" d' U{}
: Q' {1 ?& f4 z   """.format(" ".join(" " * 8 + x for x in services))5 N" n0 V- C" i* q
   func.__doc__ += doc    return func
8 D& l8 U3 W0 j: K; n
我们通过get_available_services来动态取得当前环境下可用的microservice(这里我们不关心get_available_service是如何实现的),并将其添加到函数的docstring之后。这样,当我们查看cycle的方法时,所有可用的microservice也会显示出来。

( G# F( V( }( q8 r& T# e& z( g2 H- D: V$ P4 S
$ fab -d services.cycleDisplaying detailed information for task 'services.cycle':   Restart application services   Args:       services: list of services need cycle (separate by comma)   Examples:       fab services.cycle:glow-www,glow-forum   Possible values for services:       glow-www       glow-user       glow-forum       ...
$ s* I6 e9 N! ~
0 B. g7 U& S2 h$ z" P1 |
动态外部文档
( T7 }6 k9 h. _; k. z- [8 i* c5 S
除了docstring,我们也经常需要写独立的外部文档。在Glow,这些文档绝大部分都是用Markdown来写的。例如,我们需要写一个介绍生产环境架构的文档,其中肯定会加入生产环境中有哪些服务器,每个服务器的功能描述以及它们的hostname。我们可以用手动的方式来写,但每当为生产环境添加新服务器时,我们必须记得更新这份文档。
( L5 @! u+ y% _
, t# c; v- u; T6 C. p) R$ O, w
而实际情况是,我们从来不在AWS的控制台手动创建服务器,所有的服务器都是由Ansible来创建与维护的。也就是说,所有的服务器配置信息及其功能描述都已经存在于Ansible的playbook中。当我们写外部文档时,应该去引用Ansible中的信息,而不是重写手写一遍。
4 p. V& q- S7 s9 j
2.png

5 j+ ?( w2 P: r9 j& h5 D. H) Z5 r8 x5 b
所以在我们的生产环境文档中会利用HTML注释来指定需要外部引用的部分,然后通过执行脚本将这些引用的内容填充至文档里。例如,在我们的生产环境文档中有这样一段:

7 c6 z0 {1 e6 H. T3 ~) [
## EC2 servers<!-- BEGIN EC2-SERVER-LIST -->  | Server group | Instance type | Count | Description           |' m9 b; Y. |; q0 c2 `
|:-------------|:--------------|------:|:----------------------|; h& H7 K9 K- P2 c
| bastion      | t2.small      |     1 | Bastion/Jumper server |
9 F/ e$ J* ~; B3 J% y: u| www          | c3.large      |     4 | Web servers           |
& a) p* A5 {0 o0 ~+ ^  Z...<!-- END EC2-SERVER-LIST -->  
3 g1 F4 U  n9 T) f

4 }% h* e# @- z4 E! l0 `; ]8 I
这里<!-- BEGIN EC2-SERVER-LIST -->和<!-- END EC2-SERVER-LIST -->之间的表格就是一个外部引用,每次Ansible更新服务器配置时,会执行一个脚本,它会自动在文档中查找这对标签,并更新其中的内容。这是一个很简单的技术,但对于保持文档与实际环境同步很有帮助。
# R# a# X1 w/ U
原创:叶剑烨

: n! i; f3 X: w# f1 _8 N" h2 O
: H/ Y$ e1 Z6 P1 N
# J* q! z7 K- e/ C) I' l2 Q8 C  ]3 G# o9 Y! O0 z) U0 g

本版积分规则

选择云运维时代的王牌讲师-长河老师,助你轻松入门ITIL Foundation培训课程

QQ|小黑屋|手机版|Archiver|艾拓先锋网 ( 粤ICP备11099876号-1|网站地图

Baidu

GMT+8, 2019-3-20 03:31 , Processed in 0.584014 second(s), 29 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表