寫了 300000 行基礎設施代碼,學到的五條經驗!

本文為作者 Yevgeniy Brikman 分享了他在 Gruntwork 工作時創建並維護一個超過 30 萬行基礎設施代碼的庫時學到的五條經驗。


寫了 300000 行基礎設施代碼,學到的五條經驗!


  • 視頻地址:https://youtu.be/RTEgE2lcyk4(需科學上網)
  • 《Lessons learned from writing over 300,000 lines of infrastructure code》PPT 下載地址:https://www.slideshare.net/brikis98/lessons-learned-from-writing-over-300000-lines-of-infrastructure-code-120597849

DevOps 仍在石器時代

儘管業界充斥著大量尖端的詞彙——Kubernetes、微服務、服務網格、不可改變的架構、大數據、數據湖等等,但現實是,在真正構建基礎設施時,你完全感覺不到任何現代化。

在我看來,DevOps 就像是這樣:

寫了 300000 行基礎設施代碼,學到的五條經驗!


寫了 300000 行基礎設施代碼,學到的五條經驗!


建立生產級別的基礎設施很難、壓力很大、而且很花時間。非常耗時間。

下面是我對於基礎設施項目的時間估計,來自幾百家與我們合作過的公司的經驗數據:


寫了 300000 行基礎設施代碼,學到的五條經驗!


第一條:生產級基礎設施的檢查列表

DevOps 項目永遠比你預想的時間長。為什麼呢?

第一個原因就是:剪牛鬃。(https://seths.blog/2005/03/dont_shave_that/。簡單來說,“剪牛鬃”指的是完成一項任務所需的一系列動作中的最後一步,比如:今天我需要給車打蠟,但洗車的水龍頭壞了,得去五金店買個新的,但五金店在橋對面,但我沒有通行證又不想交過橋費……不過我可以借鄰居的通行證,但我兒子借了他的靠墊,要不先還給他靠墊他是不會借給我通行證的……但靠墊上的一個小東西掉了,我需要一些牛鬃毛才能修好……於是第二天你就去剪牛鬃了,這樣你才能去給車打蠟。)下面這張來自美劇《馬爾科姆的一家》的動圖很好地說明了這一點:


寫了 300000 行基礎設施代碼,學到的五條經驗!


第二個原因就是,打造生產級基礎設施(即你的公司可以依賴的基礎設施)涉及大量的小細節。絕大多數開發者都不知道這些細節,所以在估算項目時,你通常會漏掉很多重要且耗時間的細節問題。

為了避免這個問題,每次在構建新的基礎設施時,應當使用下面的檢查列表:


寫了 300000 行基礎設施代碼,學到的五條經驗!


並不是每個基礎設施都需要列表中的每一項,但應當認真檢查每一項,找出哪些你應當實現,哪些應當忽略,以及原因。


第二條:工具集

2018 年我們在 Gruntwork 用來構建和管理基礎設施的主要工具有:


寫了 300000 行基礎設施代碼,學到的五條經驗!


  1. Terraform:我們使用 Terraform 來管理所有基本的基礎設施,包括網絡、負載均衡器、用戶、權限和所有服務器。
  2. Packer:我們使用Packer來定義並構建服務器上運行的虛擬機的映像。
  3. Docker:我們的一些服務器組成集群,在集群上我們通過Docker容器運行應用程序。我們使用的主要Docker集群工具是Kubernetes、ECS和Fargate。

所有這些工具都很有用,但這並不是重點。真正的重點是,僅有工具本身是不夠的。你還需要改變團隊的行為模式。

具體來說,如果你的團隊不習慣使用那些工具,或者沒有足夠的時間學習那些工具,那麼即使世界上最好的工具也無法為你的團隊提供一絲一毫幫助。因此,主要的經驗是:以代碼的方式使用基礎設施(infrastructure as code)是一項重要的投入。一定的前期投入是不可避免的,只有這樣才能讓它運轉起來,但如果正確地投入了時間和精力,長期來看你會獲得巨大的收益。


第三條:大型模塊是有害的

以代碼方式使用基礎設施(infrstructure as code)的新手們經常會把所有基礎設施的所有環境(dev、stage、prod等)都放在一個文件中,或者放在一組文件中但作為一個單元來部署。這是個很糟糕的習慣。

下面是幾條缺陷:

  1. 速度慢。如果所有基礎設施都在一個地方定義,那麼運行任何命令都會花很長時間。我們遇到過許多公司中,terraform plan 需要花五六分鐘時間!
  2. 不安全。如果所有基礎設施都在同一個地方管理,那麼做任何修改都需要擁有訪問一切的權限。也就是說,每個用戶都必須是管理員,這同樣是個非常糟糕的想法。
  3. 高風險。把所有雞蛋放在一個籃子裡,那麼任何地方出錯都會破壞一切。可能你只想對dev裡的前端app做一點微小改動,但一個輸入錯誤或運行錯誤的命令,就可能會刪掉生產數據庫。、
  4. 難理解。同一個地方的代碼越多,別人就越難看懂。如果所有代碼都在同一個地方,那麼你無法理解的部分早晚會坑你。
  5. 難測試。基礎設施的代碼很難測試,大量基礎設施的代碼幾乎不可能測試。我們稍後會討論著一點。
  6. 難審查。一些命令(如terraform plan)的輸出結果變得毫無意義,因為根本沒人會查看幾千條plan的輸出。而且,進一步會讓代碼審查變得毫無意義:


寫了 300000 行基礎設施代碼,學到的五條經驗!


簡單來說,你應當將代碼寫成小型、獨立、可重用、可組合的模塊。這也不是什麼新觀點或有爭議的觀點了。你肯定已經聽過上千次了,只不過是在不同的領域:

“只做一件事並把它做好。”——Unix 的哲學“函數的第一條規則就是它們應當很小。第二條規則就是它們應該比第一條更小。”——《代碼整潔之道》


第四條:沒有自動化測試的基礎設施無法使用

如果你的基礎設施代碼沒有自動化測試,那就沒辦法使用。只不過你還沒意識到罷了。雖然這麼說,但測試基礎設施代碼非常困難。你沒有真正的“localhost”可以使用(例如,你不能把 AWS 上的 VPC 部署到自己的筆記本電腦上),也沒有真正的“單元測試”(例如,你沒辦法把 Terraform 的代碼與“外界”隔離開來,因為 Terraform 的功能就是要與外部交流)。

因此,要想恰當地測試你的基礎設施代碼,通常都要將其部署到一個真實的環境,運行真實的基礎設施,驗證它能實現你想要的功能,最後全部拆毀(這種風格的測試可以參見 Terratest(https://blog.gruntwork.io/open-sourcing-terratest-a-swiss-army-knife-for-testing-infrastructure-code-5d883336fcd5),這是個開源庫,包含了一些用來測試 Terraform、Packer 和 Docker 代碼的工具,支持 AWS、GCP 和 Kubernetes API,可以在本地執行命令,也可以通過 SSH 在遠程服務器上執行命令,還有去多其他功能)。這意味著,在基礎設施測試中,一些術語的定義需要稍作修改:


寫了 300000 行基礎設施代碼,學到的五條經驗!


  • 單元測試:部署並測試一種類型的基礎設施中的一個或幾個緊密相關的模塊(例如,測試一個數據庫中的模塊)。
  • 集成測試:
    部署並測試來自不同類型的基礎設施中的多個模塊,驗證它們能正確地合作(例如,測試某個數據庫的模塊和某個Web服務的模塊)。
  • 端到端(e2e)測試:部署並測試整個架構。

注意圖示是個金字塔,也就是說,裡面有很多單元測試,幾個集成測試,和非常少的 e2e 測試。為什麼?因為每種測試的執行時間如下:


寫了 300000 行基礎設施代碼,學到的五條經驗!


基礎設施的測試非常慢,特別是在進入金字塔頂端之後,所以應當儘可能多地在金字塔底層抓出 Bug。也就是說:

  1. 構建小型、簡單、獨立的模塊(還記得第三條嗎?)併為之編寫許多單元測試,確保它們能正確工作。
  2. 將小型、簡單、測試過的模塊組合成更復雜的基礎設施,並通過幾個集成測試和 e2e 測試進行測試。


第五條:發佈過程

現在我們總結一下。從今以後,構建和管理基礎設施的過程應當如下:

  1. 仔細閱讀“生產級基礎設施檢查列表”確保構建的方向正確。
  2. 使用工具實現通過代碼方式使用基礎設施,如 Terraform、Packer、Docker 等。確保團隊有足夠的時間掌握這些工具(參見“DevOps資源”(https://gruntwork.io/devops-resources/))。
  3. 寫代碼時要寫成小型、獨立、可組合的模塊(或使用 Infrastructure as Code Library(https://gruntwork.io/infrastructure-as-code-library/)中提供的現成的模塊)。
  4. 使用 Terratest 為模塊編寫自動化測試。
  5. 提交拉取請求,讓別人審查代碼。
  6. 發佈代碼的新版本。
  7. 將新版本的代碼從一個環境提升到下一個環境。


寫了 300000 行基礎設施代碼,學到的五條經驗!

寫在最後

get最新最全的IT技能,免費領取各種編程資料(Java、python、前端、大數據、區塊鏈....)
歡迎關注公眾號【傳智播客博學谷】,小谷等你來!


分享到:


相關文章: