天天看点

一个purge参数引发的惨案——从线上hbase数据被删事故说起一个purge参数引发的惨案——从线上hbase数据被删事故说起

在写这篇blog前,我的心情久久不能平静,虽然明白运维工作如履薄冰,但没有料到这么一个细小的疏漏会带来如此严重的灾难。这是一起其他公司误用puppet参数引发的事故,但是这个参数我也曾被“坑过”。

一个purge参数引发的惨案——从线上hbase数据被删事故说起一个purge参数引发的惨案——从线上hbase数据被删事故说起

先说说这起事故,在周二下午,安静了一天的某个技术交流群,突然有个惊慌失措的同学在群里说,他拿第三方的puppet hbase module来管理线上hbase集群,结果这个模块在管理数据文件夹时,使用了一个purge参数把几乎所有的线上数据都删完了。他已经和领导汇报了情 况,那边正在紧急讨论处理方案。他在做好打包走人的准备的同时,仍抱有一丝希望来询问我们有没有办法恢复数据,大家纷纷为他献计献策... 我就想起两年前,我第一次尝试使用puppet-apache模块管理apache服务,apache::init类中默认设置了purge_configs参数为true,导致我把apache目录下的所有vhost文件删掉了,万幸的是我是在开发环境发现了这个问题。   那么,我们来看看这个“邪恶”的purge参数是什么样子的:

file {'/var/lib/data_directory':

xxxx => xxx,

......

recurse => true,

purge => true

}

<a target="_blank"></a>

 和其他的编程语言一样,语法检查是基本步骤,因此使用puppet解析器做语法检查是最基础也是必不可少的验证工作。你可以使用puppet parser validate命令来检查某个manifest文件: 例如,我在logserver.pp中的$eth0_netmask变量后面漏掉了逗号:

puppet parser validate logserver.pp

error: could not parse for environment production: syntax error at 'eth0_netmask' at sunfire/manifests/logserver.pp:6:3

 对于erb template,你可以使用 erb -p -x -t '-' $1 | ruby -c命令来做检查。我在route-eth.erb中漏掉了if判断语句的结束标记,此时执行语法检测会发生以下提示:

route-eth.erb:1: syntax error, unexpected '&lt;'

&lt;%= @internal_network %&gt; via &lt;%= @internal_gateway %&gt;

puppet-lint manifests/init.pp

warning: class inheriting from params class on line 339

warning: line has more than 80 characters on line 47

warning: line has more than 80 characters on line 167

 如果你希望检查整个puppet mainifest目录,你需要添加: require 'puppet-lint/tasks/puppet-lint' 到rakefile里,然后运行rake lint即可。 在某些情况下,你不得不关闭某些检查,例如,想要关闭80 character check,你可以如下运行: 

puppet-lint --no-80chars-check /path/to/my/manifest.pp

需要注意的是,puppet-lint仅作代码风格的检查,不能替代语法检查。

  你可以使用rspec来确保所有的模块符合预期。举一个例子,你希望编写一个测试来确保当使用puppet-keystone模块时,keystone包被正确地安装,系统中添加了keystone用户和组,可以编写一个keystone_spec.rb文件来做测试:

it { should contain_package('keystone').with(

'ensure' =&gt; param_hash['package_ensure']

) }

it { should contain_group('keystone').with(

'ensure' =&gt; 'present',

'system' =&gt; true

it { should contain_user('keystone').with(

'gid' =&gt; 'keystone',

 随后执行rspec来验证这些测试能否通过。

如果希望集成到rake命令中去,我们可以在rakefile里添加:

require 'puppetlabs_spec_helper/rake_tasks'

 随后使用以下命令来完成相应的spec执行模块测试:

rake spec # run spec tests in a clean fixtures directory

rake spec_clean # clean up the fixtures directory

rake spec_prep # create the fixtures directory

rake spec_standalone # run spec tests on an existing fixtures directory

因此,在使用从github或者puppetforge下载的module时,阅读readme和测试用例是非常重要的,如果我当时仔细阅读了 apache::init的测试用例,也不会出现所谓被坑的问题,因为人家明明在apache_spec.rb里写有对/etc/apache /sites-enabled目录的测试:

it { should contain_file("/etc/httpd/conf.d").with(

'ensure' =&gt; 'directory',

'recurse' =&gt; 'true',

'purge' =&gt; 'true',

'notify' =&gt; 'class[apache::service]',

'require' =&gt; 'package[httpd]'

)

最终部署逻辑能否上线到生产环境,还需要在开发环境和测试环境进行验证。可以使用目前流行的vagrant,openstack等工具搭建一个测试 平台,调用api创建符合生产环境的集群,通过puppet做软件安装和配置,验证部署逻辑是否符合预期。开发环境和测试环境的不同点在于,测试环境的所 有变更与线上环境完全一致,不允许有任何的人工干预。

至此,一个通过验证的puppet部署逻辑可以release了,打上tag,可以准备发布到线上了。当然不能少了线上变更流程,写下在此次线上变更的详细操作以及回滚机制。

原文发布时间:2014-08-13

本文来自云栖合作伙伴“linux中国”