zoukankan      html  css  js  c++  java
  • 一个purge参数引发的惨案——从线上hbase数据被删事故说起

    在写这篇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:6:3
     
    对于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,可以准备发布到线上了。当然不能少了线上变更流程,写下在此次线上变更的详细操作以及回滚机制。

    尾声

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

  • 相关阅读:
    LVS的DR模式测试案例<仅个人记录>
    awk命令小结
    iptables命令提取总结,包含扩展模块<取自朱双印博客>
    如何配置nginx屏蔽恶意域名解析指向《包含隐藏nginx版本号》
    CentOS升级OpenSSL至OpenSSL 1.1.0f版本<其中有遇到libcrypto.so的问题>
    U-Mail企业邮箱如何导入授权文件
    Linux花生壳使用篇
    windows远程桌面连接时,显示发生身份验证错误,给函数提供的身份无效
    批量屏蔽符合条件的IP地址,支持添加白名单,IP段,增量,大于指定次数的IP
    rsync 定时备份<crontab+backrsync.sh> 简陋版
  • 原文地址:https://www.cnblogs.com/yuxc/p/3909066.html
Copyright © 2011-2022 走看看