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

 
 

0. 一个purge参数引发的事故

故事要从周二下午说起,安静了一天的某技术交流群,突然有个惊慌失措的同学在群里说,他直接使用了第三方的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
}
 
解释一下,file是puppet的默认resource type,用于管理文件或者文件夹。在管理文件夹时,只有当设置了recurse为true的情况下,purge参数才会生效。这段逻辑的意思是要清空/var/lib/data_directory目录下所有非puppet管理的文件,purge参数通常的目的是清理管理目录以及防止被他人添加恶意文件。但也因为这段逻辑,就把hbase数据目录下的文件全部清空了。
 
从这次事故中,我们可以看到很多问题:
 
1. 部署代码的上线居然没有通过开发和测试环境的验证
2. 使用第三方模块时,竟然不阅读源码或者README文件,也没有运行测试
3. 上线没有审批流程,上线负责人的失职
4. .....
 
 
首先,有一个观念需要矫正,有些人认为部署逻辑不属于开发范畴,往往编写后就直接上线,其实只要涉及到代码的变更,无论是业务逻辑还是部署逻辑,都需要通过开发环境和测试环境的验证。那么如何做好部署逻辑的验证工作?
 
对于编写puppet来实现部署逻辑的工程师来说:少一个花括号或者分号,就可能导致代码无法运行;遗漏某个class或者某个参数就会使节点无法到达期望的状态;错误的执行顺序,甚至可能会导致系统崩溃或者网络不可达。为如何保证所编写的manifests符合你的预期?
 

1. 语法检查

 
和其他的编程语言一样,语法检查是基本步骤,因此使用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::
 
对于erb template,你可以使用 erb -P -x -T '-' $1 | ruby -c命令来做检查。
我在route-eth.erb中漏掉了if判断语句的结束标记,此时执行语法检测会发生以下提示:
route-eth.erb:1: syntax error, unexpected '<'
<%= @internal_network %> via <%= @internal_gateway %>
^
 

2. 代码风格检查

 
每个语言都有一套规范的语法风格指南,puppet也不例外:https://docs.puppetlabs.com/guides/style_guide.htm
我们可以使用Github的Tim sharpe所开发的puppet-lint工具来分析你所写的manifests文件。
 
例如检查一个manifests文件:
 
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仅作代码风格的检查,不能替代语法检查。
 
 

3. 模块测试

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

      it { should contain_package('keystone').with(
'ensure' => param_hash['package_ensure']
) } it { should contain_group('keystone').with(
'ensure' => 'present',
'system' => true
) }
it { should contain_user('keystone').with(
'ensure' => 'present',
'gid' => 'keystone',
'system' => true
) }

随后执行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' => 'directory',
'recurse' => 'true',
'purge' => 'true',
'notify' => 'Class[Apache::Service]',
'require' => 'Package[httpd]'
)
}

所以说,其实并不存在坑,只是因为在使用他人编写的模块前没有去阅读其文档和测试,完全可以避免的。

4.开发环境和测试环境的验证

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

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

尾声

故事的尾声,我想告诉大家不幸中的万幸,那个可怜的同学,最终找回了大部分的数据。

一个purge参数引发的惨案——从线上hbase数据被删事故说起的更多相关文章

  1. 【MySQL】实现线上千万数据表添加字段操作以及缓存刷新

    需求背景: 由于业务需求,需要在线上用户表添加渠道字段,用于区分不同渠道注册的用户,目前该表有20+个字段,8个索引 线上用户数据大概1500W左右,需要不停机增加数据库字段,同时需要刷新Redis缓 ...

  2. Redis中一个String类型引发的惨案

    ​      曾经看到这么一个案例,有一个团队需要开发一个图片存储系统,要求这个系统能快速记录图片ID和图片存储对象ID,同时还需要能够根据图片的ID快速找到图片存储对象ID.我们假设用10位数来表示 ...

  3. 一个随意list引发的惨案(java到底是值传递还是引用 传递?)

    前两天写了一个递归,因为太年轻,把一个递归方法需要用到的list定义该递归方法外了,结果开始断点测试的时候有点小问题 ,然后上线之后因为数据量太多导致了一个java.util.ConcurrentMo ...

  4. 一个 curl 配置引发的惨案

    问题 这两天想装新版本的 node,发现 nvm 一直报下面这个错误.我反复 Google 了,但是并没有找到一条我能用的. 痛苦 我起初一直怀疑是我用的 zsh-nvm 抽疯,所以今天有空就把它还有 ...

  5. charles抓取线上接口数据替换为本地json格式数据

    最近要做下拉刷新,无奈测试服务器的测试数据太少,没有足够的数据做下拉刷新,所以用charles抓取了测试服务器的接口,然后在伪造了很多数据返回到我的电脑上,下面来说说使用方法: 第一步: 安装FQ软件 ...

  6. gor实现线上HTTP流量复制压测引流

    一.使用背景 gor 是一款go语言实现的简单的http流量复制工具,它的主要目的是使你的生产环境HTTP真实流量在测试环境和预发布环境重现.只需要在 代理例如nginx入口服务器上执行一个进程,就可 ...

  7. TCPCopy 线上流量复制工具

    TCPCopy是一种重放TCP流的工具,使用真实环境来测试互联网服务器上的应用程序. 一.描述: 虽然真实的实时流量对于Internet服务器应用程序的测试很重要,但是由于生产环境中的情况很负责,测试 ...

  8. 线上bug的解决方案--带来的全新架构设计

    缘由 本人从事游戏开发很多年一直都是游戏服务器端开发. 因为个人原因吧,一直在小型公司,或者叫创业型团队工作吧.这样的环境下不得不逼迫我需要什么都会,什么做. 但是自我感觉好像什么都不精通..... ...

  9. 线上bug分析

    昨天下午大神把组内几十号人召集在一起开Online bug分析大会,主要是针对近期线上事故从事故原因和解决方案两个维度来分析. 对金融软件来说,每一次的线上事故都有可能给公司带来重大的损失,少扣了用户 ...

随机推荐

  1. iOS字符串为空的判断

    //判断字符串 -(BOOL) isValidString:(id)input { if (!input) { return NO; } if ((NSNull *)input == [NSNull ...

  2. 按enter执行click或者搜索问题

    最近需要将按按钮搜索的功能,改为支持按enter回车键,搜索. 实现按enter,执行click事件还是比较简单的. //按回车搜索用户 $(window).keydown(function (eve ...

  3. 20145225唐振远 实验二 "Java面向对象程序设计"

    20145225<Java程序设计> 实验二 Java面向对象程序设计 实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S ...

  4. 20145225《Java程序设计》 第4周学习总结

    20145225<Java程序设计> 第4周学习总结 教材学习内容总结 第六章 继承与多态 6.1继承 继承共同行为:存在着重复,可把相同的程序代码提升(pull up)为父类.exten ...

  5. SSE:服务器发送事件,使用长链接进行通讯

    概述 传统的网页都是浏览器向服务器“查询”数据,但是很多场合,最有效的方式是服务器向浏览器“发送”数据.比如,每当收到新的电子邮件,服务器就向浏览器发送一个“通知”,这要比浏览器按时向服务器查询(po ...

  6. dos 加用户

    net user lipeng 1qaz3EDC /addnet user zhangnan 1qaz3EDC /addnet localgroup "Remote Desktop User ...

  7. 安卓奇葩问题之:设置webView超时

    我只想说:what a fucking day! 今天要做一个webView的超时功能,于是开始百度,一看貌似很简单啊,于是开始copy了下面的代码. import java.util.Timer; ...

  8. Myeclipse/eclipse的Web project改写成Maven项目

    1.导入或建立web项目时加上maven的设置 2.加入如下代码:<dependencies>标签下加入红色部分 <dependencies> <dependency&g ...

  9. CSS3 线性渐变(linear-gradient) 兼容IE8,IE9

    一.线性渐变在 Mozilla 下的应用     语法: -moz-linear-gradient( [<point> || <angle>,]? <stop>, ...

  10. Excel VBA自动添加证书(二)

    继续上次没有写完的随笔,本来是很想一次性写完的,但是到中午一点了还没有吃东西,其实饿的不行了,还好写博客时会自动保存,中间电脑实然蓝屏,花了二个多小时写的没有点击保存,吓我一下,以为会全没了. 前面讲 ...