[譯]SQL不是回避 DevOps 的理由

自動化+一些原則讓測試更有效、發(fā)布周期更短,而且商業(yè)風(fēng)險更低。

by Thomas A.Limoncelli

原文地址:(https://queue.acm.org/detail.cfm?id=3300018)

譯者按:DevOps 實踐火遍大江南北,從一線大廠擴展推行至傳統(tǒng)軟件企業(yè),但涉及數(shù)據(jù)庫時都多少有些為難。顧介紹相關(guān)實踐,以供大家借鑒。

有朋友最近告訴筆者,“我們做不了 DevOps,我們使用了關(guān)系數(shù)據(jù)庫。“筆者聽罷,差點從椅子上摔倒。這個觀點在很多層面都是錯誤的。
“你不了解我的處境!”他拒絕道?!癉evOps 意味著我們需要更高頻率的部署我們軟件的發(fā)布版本!現(xiàn)在我們不能控制部署,我們一年只有很少的時間可以干這個。”

筆者追問朋友當(dāng)前的部署流程。

“每隔幾個月我們會有新版發(fā)布”,他解釋道?!皩⑵洳渴鸬缴a(chǎn)線需要很多工作。由于我們使用 SQL,部署過程會是這樣:第一,我們踢掉所有用戶,關(guān)閉應(yīng)用服務(wù)。下一步,DBA 修改數(shù)據(jù)庫的 schema。一旦他們的工作完成,新版軟件就安裝完畢,可以使用了。整個過程花費好幾個小時,所以我們一般安排在周末進行……(這讓人討厭)。如果升級失敗,我們不得不使用備份的磁帶,將一切恢復(fù)到初始狀態(tài),然后再試一次?!?/p>

他總結(jié)道,“僅是規(guī)劃一次升級部署都需要好幾周的協(xié)商。我們通常都很難達成共識,這也是為什么我們需要在周末來處理升級。每隔幾個月做一次都很痛苦,要面對來自方方面面的壓力!我聽說一些公司每天能做好幾次軟件發(fā)布。如果我們那樣做,我們的應(yīng)用系統(tǒng)就會因為升級而一直處于下線狀態(tài)了!”
這里有很多沒有說清楚的地方。讓我先澄清一些誤解。之后再討論采用一些技術(shù)手段如何讓部署更容易。

首先,DevOps 不是一項技術(shù),它是一套方法論。 關(guān)于 DevOps 最精確的定義是,應(yīng)用敏捷、精益的方法到從源碼到部署的全過程。它是為了更快速的交付價值,或者說是用短的時間讓一個需求特性從想法轉(zhuǎn)變?yōu)樯a(chǎn)系統(tǒng)的功能。更高頻率的發(fā)布意味著減少已經(jīng)就緒的特性等待上線的時間。

DevOps 不需要禁止任何特殊的數(shù)據(jù)庫技術(shù)。反過來,任何技術(shù)也不是采用和不采用 DevOps 的理由,就像使用特殊的語言不是阻礙將敏捷應(yīng)有到項目中的理由。SQL 是常見的理由,但也是經(jīng)不起推敲的。

很理解 DevOps 在一些人頭腦里是如何與少 SQL 數(shù)據(jù)庫關(guān)聯(lián)在一起。在2000年到2010年左右,投資和實踐 DevOps 的公司大部分是大型網(wǎng)站,它們的共識是推行 NoSQL 數(shù)據(jù)庫(鍵值存儲)。然而兩者的聯(lián)系造成了因果的混淆。這些公司還向員工免費提供豐盛的午餐,但我們都同意這不是 DevOps 的前置條件。

第二,我不確定一些人是否可以做 DevOps。 你可以使用 DevOps 的技術(shù)、方法或其他。也就是說,人們已經(jīng)很頻繁的使用這個詞,以致于我想我已經(jīng)無法去有效討論這個問題。

朋友們有一個共識。“看”,他困惑道,“這些部署是危險的。坦白講,每次我們實施的時候都在拿公司的數(shù)據(jù),甚至我的工作在冒險。每隔幾個月做一次已經(jīng)壓力很大了,還需要更頻繁的做嗎?不!先生,那是不負責(zé)任!”

在之前的專欄中提到(小批量原則 ),當(dāng)一件事情是高風(fēng)險的時候,我們會傾向于更少的去嘗試做它。但與常識相悖的是,這種做法恰恰會增加風(fēng)險。下次當(dāng)你做危險的事情時,你應(yīng)該更多的實踐,針對周圍環(huán)境而累積的變化會越來越大,越容易造成因為未知的副作用導(dǎo)致失敗。相應(yīng)的,DevOps采取的做法是,危險的事情應(yīng)該更頻繁的去做。高頻次的做法會盡早暴露大大小小的問題,而不是每年一次的大掃除。它強迫我們將過程自動化,自動測試整個過程,讓過程更流暢,這樣風(fēng)險會降低。它仍人們更多參與到實踐中。實踐出精品。不再回避我們恐懼的東西,幫助我們跨越危險,克服挑戰(zhàn)。像任何經(jīng)歷過術(shù)后恢復(fù)的人一樣,我們不斷的實踐直到它不再痛苦為止。

部署有一些固定成本。原則上,你需要逐漸將這些固定成本降低。如果增加部署頻率而不降低固定成本,則會傷害業(yè)務(wù),這是不負責(zé)任的。

該文章其余部分描述了兩種實踐,讓你即使使用 SQL也可以快速發(fā)布。實施它們需要開發(fā)者、質(zhì)控、運維走出各自的筒倉,彼此協(xié)作,這也是 DevOps 的實質(zhì)。這樣的結(jié)果是讓業(yè)務(wù)更平滑、更少痛苦、更少壓力。

技術(shù)1:自動 Schema 更新

在這個古老的方法論中,當(dāng)團隊中的專家(通常為 DBA)手工修改 schema 時,任何 schema 的變化都需要整個應(yīng)用系統(tǒng)關(guān)閉。如果你希望實現(xiàn)自動化部署,你需要自動化實現(xiàn) schema 的更新。

因此,應(yīng)用需要管理 schema。每個 schema 的版本都需要記錄。應(yīng)用從 schema V1版開始。這個值存在數(shù)據(jù)庫中(大致為1列的表,該列字段存儲為1)。當(dāng)應(yīng)用啟動時,它需要知道兼容 V1的schema,如果它在數(shù)據(jù)庫中找不到這個版本,它可以拒絕運行。

為了自動更新 schema,下一版本的軟件發(fā)布時需要知道自己要求V2版的 schema,知道 SQL 命令會將 V1版的 schema 升級到 V2版。在啟動時,它看到版本為1,然后運行響應(yīng)的 schema 升級命名,并將存在數(shù)據(jù)庫中的版本號升級為2,之后繼續(xù)運行應(yīng)用。

執(zhí)行這個操作的軟件通常還有一個存儲 SQL schema 升級命令表。這些命令以數(shù)組存儲,索引 n 表示其是支持從Vn-1升級到 Vn。這樣,某個版本找不到也沒有關(guān)系,軟件能將數(shù)據(jù)庫恢復(fù)到任意需要的schema版本。 實際上,如果發(fā)現(xiàn)有沒有初始化的數(shù)據(jù)庫(如在測試環(huán)境中),它會循環(huán)執(zhí)行若干schema 升級,直到獲取到最新的版本。不是每個軟件的發(fā)布都需要升級 schema,因此隔離 schema 和軟件的版本號。

已經(jīng)有一些開源的和商業(yè)的系統(tǒng)實踐了這個過程。他們的一些產(chǎn)品比其他人更復(fù)雜,如支持多語言、多數(shù)據(jù)庫、錯誤處理及是否支持回滾等。從一項關(guān)于”SQL 自動化更新“的研究中,你會發(fā)現(xiàn)更多信息。我最熟悉的是面向.NET 代碼的開源項目 Mayflower和面向 Go 的Goose。

schema 的修改會導(dǎo)致數(shù)據(jù)庫被鎖定幾分鐘甚至幾個小時。這會引起應(yīng)用系統(tǒng)的超時甚至故障?,F(xiàn)代SQL 數(shù)據(jù)庫已經(jīng)減少此類問題了,需要感謝無鎖 schema 升級和在線重建索引的特性。這些特性可以在現(xiàn)在 SQL 數(shù)據(jù)庫產(chǎn)品中找到,包括開源產(chǎn)品如 MariaDB、MySQL、PostgresSQL。查閱相關(guān)文檔,以了解操作時的注意事項。

一旦你的軟件使用了這些技術(shù),采用 CI(持續(xù)集成)會變得非常容易。你的自動化測試環(huán)境可以包含測試使用舊的 schema 的數(shù)據(jù)庫并升級它,然后運行新版軟件。你的 schema 升級過程可以在發(fā)布到生產(chǎn)環(huán)境前,進行數(shù)百次的測試。這會為該過程帶來更多的信心,減少schema升級的危險,解藕DBA本人直接參與到升級。他們可以享受原本屬于他們的周末了。

我對此技術(shù)最感興趣的是,你的schema可以按代碼一起管理了。大幅減少了控制臺上的手工操作,可以在開發(fā)環(huán)境、測試環(huán)境、UAT(用戶驗收測試)環(huán)境、生產(chǎn)環(huán)境中不斷演練整個過程。你可以重復(fù)執(zhí)行這個過程,完善它。既然它是代碼,你可以應(yīng)用代碼管理的實踐和軟件工程的計算去管理它。

技術(shù)2:針對多 schema 編碼

在分布式計算環(huán)境中,如何升級數(shù)據(jù)庫的 schema 呢?

典型的網(wǎng)站系統(tǒng)前端是負載均衡服務(wù),后端運行相同軟件的多個實例或副本。每個實例都承擔(dān)一部分 HTTP 負載,也訪問相同的數(shù)據(jù)庫實例。

如果軟件和數(shù)據(jù)庫 schema 緊密耦合,而軟件升級又需要數(shù)據(jù)升級 schema時,操作會變得難以操作。如果你先改變schema,應(yīng)用實例會故障或者至少會因此產(chǎn)生混亂;你需要盡可能快的升級實例,但是其實你已經(jīng)輸了這場游戲,因為你已經(jīng)在承受怒火。

為什么不限升級應(yīng)用實例呢?!悲劇的是,如果你逐一升級應(yīng)用實例,最新升級的實例會不能啟動,因為它檢測到的是錯誤的 schema。只有 schema 與應(yīng)用系統(tǒng)匹配時,你才能應(yīng)用拉起,而也在這個時候停機時間才結(jié)束。

最直接的解決方案是,無視現(xiàn)實規(guī)律,改變數(shù)據(jù)庫的 schema,并同時升級所有應(yīng)用實例的版本。如果條件真的允許你這樣做,一切真可以這樣搞定。

遺憾的是,ACM有套策略來應(yīng)對現(xiàn)實法則,同樣大部分企業(yè)雇主也類似。這也是為什么傳統(tǒng)方法是關(guān)閉整個應(yīng)用,升級所有東西,然后再重新上線。這種最佳實踐延續(xù)直至IEEE 的朋友核算了如何暫停的過程。

無論是違法現(xiàn)實規(guī)律還是計劃的停機,每次停機都會引入更大的問題:你完成了許多的獨立系統(tǒng)變更,但只有讓系統(tǒng)重新運行后才知道它們是否都還正常。同樣也不知道這些累計的改變會引發(fā)什么樣的破壞。

大爆炸式升級變更是很危險的,而每次僅做一個變更,并驗證這個變更,就會會降低風(fēng)險。如果一次執(zhí)行多個變更,此時碰到問題,必須逐一運行程序以確定哪個變更引起的問題。如果每次只做一個變更,即使碰到問題,排查會很簡單,也很容易回退變更。

即使是像谷歌這樣的擁有極其復(fù)雜成熟的測試技術(shù)和方法,如果不能理解預(yù)發(fā)布環(huán)境和生產(chǎn)環(huán)境間細微的差別也可能會導(dǎo)致部署失敗。他們采用“金絲雀”發(fā)布形式:升級其中一個實例,然后觀察是否運行是否正常,如果沒有問題,則繼續(xù)逐漸而有序的升級其余的實例。這其實不是一項測試技術(shù),而是應(yīng)對測試不充分而采取的保障策略。需要說明的是,不是因為測試人員不優(yōu)秀,而是沒有人是完美的。金絲雀技術(shù)目前已經(jīng)是產(chǎn)業(yè)界流行的最佳實踐,而且已經(jīng)被嵌入到 Kubernetes系統(tǒng)中。(金絲雀一詞來源于煤礦業(yè)中金絲雀。煤礦礦工通常會帶著鳥進礦,一般會選擇金絲雀,因為這種鳥對人體有害的氣體非常敏感,如果在礦里有鳥死亡,則被視為危險的信號,提醒大家要撤離。

由于軟件引起的問題通常與特定的 schema 相關(guān),解決方案就是松耦合。在設(shè)計階段去解除耦合,讓軟件可以同時支持多個版本的 schema,以實現(xiàn)獨立激活或者回滾。

第一階段是編碼是不再假定表中的字段。用 SQL 術(shù)語說就是,SELECT 語句需要精確指出需要的字段,而不是泛泛的使用 SELECT *。如果需要使用 SELECT *,則不要假定字段按特定的順序排列。 LAST_NAME也許今天是第三個字段,但明天就不一定了。

基于這個原則,從 schema 中刪除字段也會簡單一些。新版部署后不使用相關(guān)字段接客,一切都可以正常運轉(zhuǎn)。在所有實例都升級至新版后,再變更schema。實際上,冗余的字段可以忽略先,稍后再移除他們,甚至等到下次 schema 變更時再進行清理工作。

添加字段也會變得簡單,即在第一個實例部署前在 schema 中添加新字段即可。如前所述,我們采用技術(shù)1(應(yīng)用管理自己的 schema),部署一個新發(fā)布,它會修改 schema,但不會使用這個新字段。由于合適的事務(wù)鎖會控制住并行處理,第一個新升級的實例重啟后會更新 schema。如果有問題,則金絲雀會宕掉。你就可以修復(fù)軟件,然后實驗新的金絲雀。而回退 schema 的變更也是一個可選的方案。

由于 schema 和軟件結(jié)構(gòu),開發(fā)者可以按需啟動新字段。過去的升級需要考慮多個團隊的需求后才能確定一個維護窗口,而現(xiàn)在流程解耦,所有相關(guān)方可以有序的工作,而不必相互鎖定。

更多復(fù)雜的變更需要更多的規(guī)劃。當(dāng)拆分字段,移除字段,添加字段等等,收益才真正體現(xiàn)。

第一,軟件必須按同時支持新舊 schema 的需求進行編寫,更為重要的是必須能處理變化階段。假定你需要從存儲人全名的字段遷移至拆分為幾個單個字段(第一個名,中間名,最后的名,頭銜等)。軟件需要檢查哪個字段存在,且可用。在數(shù)據(jù)庫轉(zhuǎn)換過程中,它必須可用正常運行,這個過程中兩套字段都會存在。一旦兩套字段都存在時,一個批處理任務(wù)會將全名拆解為多個部分,然后將原字段置為 NULL 值。代碼需要處理這種特殊場景,即有些行已經(jīng)完成轉(zhuǎn)換,而有些還沒有完成轉(zhuǎn)換。

處理這個過程方案可稱之為“五階段在線 schema 變更”。它有很多階段,包括創(chuàng)建新字段,升級軟件,遷移數(shù)據(jù),移除舊字段。它又被成為McHenry 技術(shù)(詳見《云系統(tǒng)管理實踐》),或者叫《Expand/Contract in Release It!: Design and Deploy Production》)

在線 schema 變更的五個階段

  1. 運行的代碼讀寫舊 schema,即從表、視圖中選擇需要的字段。這是初始狀態(tài)。
  2. 擴展:schema 被修改,即增加新字段,但不刪除任何舊字段。沒有代碼需要變更。由于此時新字段沒有被使用,所以如果需要回滾也不會很痛苦。
  3. 代碼被修改以使用schema 中的新字段,并發(fā)布至生產(chǎn)環(huán)境。如果此時發(fā)生回滾,只需回滾至第2階段。這個時候,執(zhí)行數(shù)據(jù)轉(zhuǎn)換操作。
  4. 簽約:訪問不再使用的舊字段的代碼會被清除,且被發(fā)布至生產(chǎn)環(huán)境。如果此時發(fā)生回滾,只需回滾至第3階段。
  5. 從 schema 中移除舊的不再使用的字段。這個階段如果發(fā)生罕見的回滾事件,則數(shù)據(jù)庫可簡單回滾至第4階段。

這些技術(shù)足夠處理在線分布式系統(tǒng)中大部分復(fù)雜的 schema 變更。而且,每個變更都可以獨立回滾。

針對特定場景階段的數(shù)量可以裁剪。如果只是加字段,則階段5可以忽略,因為沒有需要移除的。四、五階段可以合并或重疊。第5階段可以合并至另一次 schema 變更中的第2階段。

基于這些技術(shù),你可以處理最復(fù)雜的 schema 變更而不會有停機時間。

總結(jié)

使用關(guān)系數(shù)據(jù)庫并不妨礙推行 DevOps。自動化 schema 管理加上一些開發(fā)原則就可以強化測試、縮短發(fā)布周期、降低商業(yè)風(fēng)險。

自動化發(fā)布解放了我們。它將痛苦的、鴨梨山大、人工的升級過程,轉(zhuǎn)換為常規(guī)事件,且沒有事故。它減輕了商業(yè)風(fēng)險,但更重要的是,創(chuàng)建了一個更可持續(xù)的工作環(huán)境。

當(dāng)你可以充滿自信的部署新版時,你就可以更高頻率的去發(fā)布。以前需要等數(shù)周甚至數(shù)月的新特性可以更早接觸用戶。問題修復(fù)更快。安全漏洞也及時解決。從而使企業(yè)能為客戶提供更多價值。

相關(guān)文章

關(guān)于作者:Thomas A. Limoncelli

  • 紐約Stack Overflow公司的SRE經(jīng)理
  • 著作:《系統(tǒng)與網(wǎng)絡(luò)管理實踐》、《云系統(tǒng)管理實踐》、《系統(tǒng)管理的時間管理》
  • 博客:http://everythingsysadmin.com/
  • tweet: @YesThatTom
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容