一個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,可以准備發布到線上了。當然不能少了線上變更流程,寫下在此次線上變更的詳細操作以及回滾機制。

 

尾聲

故事的尾聲,我想告訴大家不幸中的萬幸,那個可憐的同學,最終找回了大部分的數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM