天天看點

一個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中國”