從開發到上線,實戰持續交付

「開發者最佳實踐日」是由七牛雲存儲發起並聯合各方小夥伴爲開發者舉辦的系列技術沙龍,關注開發者在實際應用中可能遇到的技術問題。致力於爲敢於創新的開發者們提供行業內最前沿最熱門的技術乾貨,以技術驅動應用創新,讓更多的開發者享受技術帶來的生活樂趣。nginx

本次在1115的杭州開發工具專場就由5位來自不一樣公司的技術負責人帶來了本身的技術體會和心得,下面是七牛首席架構師李道兵帶來的技術分享。程序員

李道兵:在開始以前,我想給你們推薦兩本書,一本叫《精益創業》,一本叫《持續交付0》,《精益創業》講的就是說怎麼樣把一個Idea變成一個事業或者說一個公司,而這個裏面最推崇的,就是整個流程裏面的話,第一個是從代碼怎麼變成服務,第二個步驟,怎麼從一個服務變成去收集用戶的反饋。第三個步驟,從反饋再回到開發流程。sql

此次我講第一個流程,第二個和第三個更可能是一些產品決策的東西,我這裏跳過不講了。
關於第一個流程,有一本更加專一的書,叫《持續交付:發佈可靠軟件的系統方法》,第二本書比較晦澀一點,我仍是推薦前一本。我講講咱們七牛從開發到上線這方面有哪些實踐。docker

最開始咱們提出來一個簡單的或者是很是通用的互聯網的網站架構,最開始進來的是Nginx,nginx把全部請求分紅兩路,一路是靜態文件,Nginx本身服務掉了,另一個是動態服務,用到後面的應用服務器,大概是一個業務邏輯層,業務邏輯層的話,你設計好了,它應該是沒有狀態的,這樣的話,當你的用戶從1萬變成100萬再到1000萬,應用層從一臺直接鋪到一百臺,架構不用作任何變動,在這一層的話,你作好消除狀態、方便水平伸縮就能夠了。數據庫

而後是數據庫,數據就是Mysql的主存架夠,DBA的一些Replita爲(音)的架構,或者說是引入一些數據庫結構,可以Hold住這些東西,好比說如今有1萬的量,當你的量成爲10萬,變成十倍的時候,你怎麼去處理,只要數據庫能Hold住就能夠了。api

第四個要注意的,你的用戶上傳的文件怎麼處理,當你一臺的時候,就是放磁盤,當你有兩三臺機器的時候,就要想兩三個機器的話,用Ithink能夠同步,用戶訪問的話,最多有一百毫秒的級別不可用的,大部分是可用的。另外用一些比較成熟的Hold住這些軟件,可是如今有一些另外的選擇,就是接入公有云存儲,這些工具比較方便的。七牛雲存儲

另外當你的東西稍微大一點,你就要考慮在哪一個換緩存,一個是在Nginx(音)的業務邏輯中間,由於它相對來講很接近出口的地方多了一個緩存層。第二個比較合適的地方,就是在業務邏輯層和數據庫之間,由於數據庫最後是壓垮你網站性能的最後一根稻草,通常從數據庫這個點壓垮的,放在數據庫裏的話,可以大幅度下降數據庫壓力,這一層須要注意的,保持數據一致,避免緩存,還有緩存出現單點故障的時候,怎麼避免後面整個數據庫服務都被壓死,這是須要去考慮的。從Idea變成普通網站,網站上線了,上線以後進入下一個循環,怎麼從網站收集用戶數據,或者說作一些線下訪談,從用戶這裏得到反饋,反饋到開發的階段,開發階段以後再去作一些改動,改動以後再去上線。緩存

對於咱們大部分程序員來說,這裏面臨一個需求,從改動到完成上線,究竟須要花多久,花多久是一回事,另外在改動過程當中,怎麼讓用戶比較穩定地去作這些改動,而咱們將圍繞着這個環節作一些講解。安全

咱們把這個過程稱之爲部署,部署的話,咱們之前怎麼玩這個事情的,應該說比較早期的話, 部署順着安裝文檔把這個事情幹完,最先玩BBS或者說早期的其餘網站,咱們都是怎麼來完成部署呢,就是說這個軟件會有一份安裝文檔,告訴你要安裝一個數據庫,建立一個Database,須要有一個表,怎麼安裝,安裝完之後須要改哪些配置,而後這樣就能夠上線了。服務器

而這種方式最大的缺陷是什麼呢,你要作一些改動怎麼辦呢,一個方式是我在線上直接改掉。第二個方式就是把這邊改掉測試完,測試完之後從新部署一遍。對於第一個方式,線上修改最大的麻煩是你的歷史沒有記下來,不少東西容易被遺漏,你改了,另一我的改了,這些改動互相有一些衝突或者說改動以後直接丟掉。

若是你安裝重裝的思路,中間的服務就有一段下線的時間,也很難作到很敏捷的上線,客戶發現上面有一個錯別字,第一個方案確實能夠快速改掉,第二個方案可能須要幾個小時,用戶才能看獲得。

第二個,有一段時間很流行Mysql加上PHP的結構,這個上線很簡單,大部分狀況就是一個FTP或者SFTP,而後上傳上去,用戶刷新頁面,看到一個全新的網站,那這就有一個問題,它的適用面比較窄,PHP很是好用,可是對於JAVA來說的話,甚至Python爲、Ruby都不是很推崇,若是用FTP上傳的話,老的服務可能直接崩掉了,要作版本管理,不然回滾很很差作。

JAVA這邊經常使用的是打包,把整個應用服務作成一個war包,war包傳上去,這個惟一比較麻煩的,假定我已經有100臺服務器,一個一個拷貝,其實也是一件很麻煩的事情,當你有100臺服務器的時候,要想到一個很大的麻煩,你不是全部服務器在線上,有可能有兩臺三臺在癱着,某一天須要的時候從新啓動上線,那麼整個部署管理的話,實際上是一件很麻煩的事情,因此說你會配合一些部署系統。辦法就是作成系統安裝包,製做過程比較累,好比說作成RPF包,能夠很方便地使用,這種大規模部署就沒啥壓力,雅虎之前這麼作的。可是惟一一個麻煩,製做這個包的話,須要不少知識,對於程序員負擔比較大。

另外是capistrano,咱們有一部分東西是在用這個東西,從Ruby社區出來的一個部署組建,用起來也算是比較方便,稍候稍微詳細地講一下。capistrano在某些方面解決的問題不是太好,好比說系統方面解決不太好,因此配置方面的話, 須要一些配合,通常配合的是Pupet或者Salt,配合得很是好。還有一個是最近一兩年很流行的docker,可以大規模地改變你的整個開發和部署流程,如今不少公司在這方面作一些探索,也作一些小規模的應用,尚未把全部流程搬到這個上面來,就咱們如今來看的話,還有一些小缺點,好比說Docker改變了線上運行方式,而這個運行方式的話,自己有缺陷,好比說Docker對於網絡交互能力不夠大,若是是大流量的應用程序,表現不太好,另外也須要改變不少之前的日誌管理,應用程序的管理,須要更多改變,須要整個運維體系一塊兒跟上才能作得很好。

接下來我稍微講一下capistrano,這個標準結構的目錄結構,它有幾個大的目錄,一個是Release目錄,Release目錄下面有不少子目錄,每個子目錄是一長串數字,通常是時間標記,這是2014年9月19號幾點幾分幾秒,整個部署流程是怎麼樣的呢。

第一步,我先給你在服務端上建立這麼一個目錄,建立完這個目錄以後,無論用什麼方法,我把你的一個新的代碼也好,新的部署也好,拖到這個Release目錄裏面去。第二個事情,我在Release目錄裏面,有些東西是公用的,好比說配置文件,公用的部分的話,它會放在share裏面,咱們能夠看到一個配置目錄,一個LOG目錄,一個PID目錄,還有一些system目錄,咱們把公共部分放到這裏以後,咱們用軟連接的方式放進去以後,至關於這個Release目錄裏面就是很完整、可運行的程序了。

最後一步咱們作什麼呢,這裏有一個current目錄,這個是按軟連接到特定的Release目錄,咱們的步驟就變成先把這個Release目錄準備好,準備好了以後,而後把全部的page連接過去,而後第三步是切換Current,從老的Release切換到新的Release,而後再重啓程序,新版程序就直接上線。這個時候你就開始測試,測試若是發現它有問題怎麼辦呢,能夠回滾,回滾超級簡單,我把Current從新遷回上一次Release軟連接,再重起程序,整個回滾就直接完成了,這也是capistrano最大的賣點所在。同時它的整個目錄體系能夠定製的地方很是多,因此用起來,自我定製化能力也比較強,用起來也比較方便。

capistrano可以解決的,就是說你的代碼修改了,怎麼去上線這個流程,也就是說咱們剛纔說的三步步驟裏面,跟程序員最相關的一步,新需求來了,我要把它弄上線,有Bug了,要修復,要弄上線。僅僅作到這樣的話,不太夠,還須要解決一些新的問題,一臺機器在線上,系統盤毀掉了,這個磁盤壞掉了,咱們多久才能把這臺機器恢復出來,恢復出來咱們要作一些什麼事情,咱們要先安裝一個系統,多是LTS版本,同時要在上面建立用戶,要部署SNG(音)的綱要,要調整日誌的一些滾動方式,要調整youdimit(音)文件過多,同時還可能要去安裝它的依賴,還要安裝Ruby的特定版本和依賴包。

要麼你就是維護一份文檔,每次當你要裝一臺新機器的時候,安照這個文檔走一遍,可是最大的問題,這種文檔維護是會丟失的,若是機器不壞,你確定會修改依賴,好比說Ruby有可能從2.0升到2.1,Python可能從2.6升到2.7,在升級過程當中,你會在線上直接操做完了以後,你會把這份文檔很容易忘記修改。

第二個問題,你常常在線上作一些微調的話,實際上你會忘記入庫的東西。第二個問題跟第一個問題很相似的,你的用戶大規模上來了,按照咱們剛纔說的,你的架構自己支持水平伸縮的,很容易伸縮完了,那我就擴容到一百臺,但擴容到一百臺機器以後,就面臨一個問題,你怎麼同時部署一百臺機器,是一臺一臺地走呢,仍是說你有什麼辦法去控制,從一箇中心控制一百臺,同時幫你作一些升級工做。

固然平常運維除了這兩點,還會面臨着不少問題,好比說咱們今年SSL大概前先後後出了好幾回大漏洞,每次大漏洞的時候,你們的處理方式是什麼呢,若是你所有用系統包,又觸發全部的系統包,所有完成整個NGX(音)或者Open SSH的升級工做, NGINX(音)常常是自定義編輯的,NGINX怎麼快速一次性升級完畢,今年Heartblood(音)是一個很好的例子,當這個漏洞剛出現的時候,它的攻擊手法跟漏洞的報告幾乎是同時出現的,也就是說你的機器在公網上多暴露一分鐘,你的用戶密碼就多暴露一分鐘,這個是很典型的例子。

這樣的狀況下,你的一百臺機器,升級一個東西須要多久,意味着你在這方面的話,時間越長,你的損失就越大,固然用戶不會由於這個東西來質疑,可是一個用戶密碼泄露,早晚會給你帶來一些損失。

這些問題有什麼比較好的解決方案呢,咱們用方式,就是puppet和salt都能很好地解決問題,它們的思路比較一致,一個至關於幫你準備好了不少基礎組建,另一個的話,更多的東西須要你本身寫一下。在這方面的話,它們的思路就是幾點。

第一點,全部的配置都應該是入庫的,這個入庫是指什麼呢,好比說線上的OpenSSH應該是哪個版本,我至關於在裏面要作一些鎖定的工做,個人Ruby應該用哪個版本,也應該鎖定的,我須要建立哪些用戶,一個很少、一個很多的,都是在個人配置裏面寫好的。個人用戶,個人公要上傳哪些,公要部署哪些。還有日誌滾動怎麼配置,這些都是入在一個配置庫裏面。同時來說,它們引入一些模板簡化配置,好比說上一臺機器,跟其餘機器同樣,這兩臺同樣須要哪些組建就能夠了,一樣來說的話,好比說這臺機器可能須要Mysql5.0的,另外的一臺機器須要Mysql5.5的,那你就用一個模板,能夠快速在這臺機器上裝5.0,另一臺機器裝5.5,同時管理大量機器,這也是剛纔所說的很重要的問題,就是說我有一百臺機器,要同時讓它們作操做,同時升級組建,怎麼作,利用Pub(音)可以很好地解決。剩下包括依賴的管理,改配置帶來一個問題,你必需要Reload,怎麼來觸發,裏面經過一些依賴,好比說一個服務依賴於一個配置,這個配置修改以後,這個服務的話,自動作一些Reload的工做,這個是Puppet能夠作很好的實驗。

固然cap+Puppet也有一些小問題的,爲了解決這個問題,開發一個新的部署系統。第一個問題來說,程序和配置是分離的,前面咱們提到,用你的capistrano能夠更好地幫你把程序部署上去,可是配置呢,配置通常有一個特色,它不入庫的,由於咱們的配置裏面,常常涉及一些比較偏機密的東西,好比說應用服務器會scret,你的服務器有用戶、有密碼,這類的東西都會在裏面。配置咱們傾向於另一條管理思路,好比說Hubpuppet分發,就帶來一個問題,你的程序更新涉及到配置的更新,程序的更新致使一個配置項含義改變了,或者增長配置項,會有這類的問題,就會帶來一個問題,程序更新咱們能夠很容易,咱們先把配置弄上去,而後再更新程序,再重寫程序,這個流程沒有問題。

問題出在什麼,當你回滾的時候怎麼辦,當你回滾的時候,舊的程序多了一個新的配置,優雅一點的,你的程序能作得很好,你能夠作新的配置,能夠兼容,不優雅一點,舊程序就崩潰掉了,整個回滾就失敗。咱們這個新的整個流程的話,就是說咱們的配置和程序是至關於同時部署的,這跟之前的的capistrano算是小的改進。

第二部來說的話,以前puppet帶來一個問題,你的puppet,十臺機器、一百臺機器沒有問題,能夠扛得住,可是當你的服務規模再上一個臺階的時候,整個中心的性能就不夠了,整個部署的流程,從你通知它部署到它部署完成,可能這個時間拉得很長,這對咱們來說是很不利的。你的危險在公網多暴露一分鐘,就多一分損失。

第三個,當你的機器不少,同時機器數據比較敏感,由於咱們作存儲,那麼開發人員想知道機器的一些狀況,可是又不能容許他登錄的狀況下,咱們就會開發一些輔助程序讓它在不登錄的狀況下了解狀況,監控是一個辦法,可是不能太多太雜,太多太雜的話,開發人員會被迷失在數據裏面,那麼咱們反過來去作的話,當開發人員想知道一個數據的時候,咱們其中一個方法讓你去作,去拿到這些數據。好比說你想知道磁盤的剩餘空間,這是最簡單的需求,另外想知道應用程序日誌有哪些文件,可能想知道分別佔多大的空間。好比說這個程序如今打開了多少個文件描述符,上面總共有多少個連接,這個時候幫助咱們開發人員快速瞭解狀況,同時不用申請登錄權限,這是咱們開發部署系統想去解決的問題,同時也有一些比較絢的東西,可讓咱們幹活更得更爽一點。

前面提到整個開發到部署上去的話,部署怎麼去回滾,怎麼去伸縮的問題,接下來帶來另一個問題,你怎麼知道你部署上去的是對的,就是說可能開發一個新功能部署上去,部署上去以後,而後砸了,用戶要來抱怨你的老闆,你的老闆把你罵一頓,怎麼去減小這種狀況呢,那麼咱們須要一些測試環節或者持續集成的環節,固然你也能夠引入一些很縱線的測試,好比說像微軟的Office,一到兩年發行一個版本,以前有很長的測試流程。咱們是作互聯網的,互聯網絕對不但願一連串發佈一個版本,咱們但願一天甚至一天內有屢次發佈,這是互聯網企業所推崇的部署模式。在頻繁發佈的時候,怎麼避免出現愚蠢的錯誤,那就是你的持續集成幫上你的不少忙,持續集成的話,就是說你寫了不少單元測試、集成測試,在適當的時候,幫你在你的代碼上跑一遍,保證你的代碼是正確的。

咱們的持續集成通常提交在兩個階段,提交了一個修改,這個修改的話,咱們是在Github上管理代碼,就是ProRequst爲,提交以後觸一個單元測試的工做,通常控制在一分鐘到五分鐘以內完成,一分鐘到五分鐘以內以後,咱們知道此次修改是否會引起新的Bug,有沒有會觸發Bug這類的事情,這個Pull request,若是是安全的,有同時幫你Review了,我認爲能夠合併了,若是這個不過,首先想到改這個東西,好比說咱們有大量圖片的轉碼工做。圖片一個很噁心的地方在於有大量的圖片格式,每種圖片格式又有不少種擴展,有標準的,也有非標準的,引入大量的樣本在測試程序當中測試,這個就能讓咱們避免在修改的時候,不當心觸動了哪些環節致使問題,咱們就知道哪兒改錯了,從新來。

發佈以前作一個更加大規模的測試,大概控制在十幾分鐘的樣子,這樣的測試,就可以保證放上去的版本不會有太大的問題,Jenkins上面裝了一些比較好用的插件,我可以知道你的整個質量變化狀況,這個模塊的單元測試數量,是否是隨着提交次數在慢慢增加,或者說測試覆蓋率是否是穩定在一個可接收的範圍,這是Jenkins能夠幫你帶來的東西,做爲你的老闆,看着Jenkins,大家真的有在添加測試,同時來說,也能知道測試覆蓋率在什麼樣的水平,這樣對於數據公開是一件很好的事。

咱們再從頭看一下你的工具鏈,從開發上線流程怎麼作的,第一個是提交issue,順着一個Issue開始往前作,修改代碼,提交PR,而後持續集成經過,合併PR,而後持續集成,經過以後就是capistrano部署,部署完成以後,立刻在線上作一次檢驗,若是成功了沒事,若是失敗就快速作一些回滾,這樣對於咱們大部分的程序,均可以作相似這樣的事情。

裏邊的話,有些事情的話比較容易忽略,有必要作一下的,你會用不少第三方的工具,好比說咱們會用Redmark(音),會用Jenkins,這些程序要不要入庫,我我的建議入庫,入庫以後,修改時比較容易,同時可以整合到你整個的發佈渠道里面,就是你發佈渠道變成一個很一致的,就是同一套手法來作這個發佈。第二個,前面說的,現場配置不要入代碼庫,軟件和配置作分離,可以,幫你方便不少,開發人員沒必要了解線上實際的IT五配置狀況,好比說沒必要實際瞭解數據庫究竟在哪兒,數據庫的用戶名和密碼是什麼,開發人員都不須要了解,密碼私要之類,即便是你的部署也不要入庫,它能支持一些模板,能方便你經過模板替換掉這些的話,你的配置模板只是是這兒須要一個密碼,這個密碼是哪個密碼,而不須要真正把密碼寫進去,避免別人經過攻擊你的版本管理庫的方式得到密碼信息。

前面講了上線流程,可是這個上線流程對咱們來說太魯莽了一點,真正出婁子的時候,真察覺以前,用戶在抱怨了,那麼咱們須要一個測試環境,測試環境至關於單獨找一到兩臺機器,在線上系統徹底獨立,在部署以前的話,先部署到測試環境,而後先走一遍,而後再往線上去發。這個測試環境惟一須要注意的,我也常常看到有人犯的錯誤,好比說你有一個Web APP,我有一個Secret Token的概念,用在你的Cookie的簽名,你的CSIF的設計上面,都會用到這個Secret token,你的測試環境和正式環境都用同一個Secret token的話,可能用戶根據你在測試環境裏面,註冊一些特殊賬號,而後再把這個cookie session挪到正式環境來用,這樣致使用戶環境泄露的問題,這是安全方面的東西。

測試環境的話,可以解決大部分問題,相信不少互聯網公司也在用這些東西。第二個東西的話就是小入口,小入口概念的話,你在測試環境可能不出問題,可是在正式環境有可能出問題,正式環境有更加龐大的數據,或者說它的整個環境有些小差別形成的,那麼咱們的作法是弄了一個小入口,它跟正式環境是共享數據庫和存儲,固然緩存這方面要看不一樣狀況,有的時候咱們選擇共用,有的緩存選擇獨立緩存。部署的時候,再部署一個小Look,測試沒問題了以後,再開始進行灰以及全量的部署。像Look的話,對咱們的幫助,不只在部署方面作一個灰度部署階段。由於目前只是影響測試用戶,同時還有一個好處,小用戶流量和壓力很是低,就意味着你能夠作很生動的調試,好比說你在線上機器裏上,你是不敢用的,咱們也徹底杜絕你們使用這種方式,由於Stris(音)會極大的下降你的服務端的性能,可是在小入口方面的話,像GBB這些東西都是能夠上去使用的。像你在你的大部分狀況下,因爲多包太嚴重,根本拿不到一連串的數據包,漱口那邊的話就能夠拿到請求鏈上全部的包,幫你作一個調試方面的工做,小入口的引入,對咱們的幫助也是很大的。

前面有些問題的話,是我沒提到的,最後一個小頁講一下這些問題,第一個問題是看capistrano最先的設計,是針對於Ruby、Python的語言,這些語言的特色,最大的特色是不須要編譯,把源碼直接扔到服務端就能夠了,再去服務端編譯太詭異了,通常在中間環節先編譯好,再推送上去,咱們本身寫了capistranoSC、NGINX的東西,整個流程就變成從GitHub(音)拖到NGINX,編譯好了,再經過capistrano部署數出去,而不是像capistrano能見的範例裏面能夠直接推送原碼。

第二個,若是你是推送十臺一百臺機器的話,你的服務器能扛得住,二十兆的包,一百臺的話是2G,2G的話,我在千兆網上面二十秒就跑完了,可是若是服務器倒打一千臺的時候,二十秒就變成二百秒,你的服務端整個跑馬,這個事情咱們就作存儲的還有經驗了,直接推送到內網,下載的只有,至關於從境像分發的一個概念,分發能夠快不少。

而後第二個,就是說咱們的不少機器是所謂村內網的機器,在部署的時候,不能訪問公網,不少在升級方面不是跟方便,像咱們最簡單就是用了一些外國的源代理成國內的源,其餘互聯網服務咱們都作一些適當的代理,可以主動發起,解決這些機器不能訪問公網的問題,我想說的就是這一些,謝謝你們。

更多技術內容請訪問本次活動的原文連接:http://blog.qiniu.com/?p=633

相關文章
相關標籤/搜索