云時代的運維及基于 ANSIBLE 的自動化運維實踐
云時代的運維
以前的運維那么痛苦,大家卻并未做多大的努力去改變這個現(xiàn)狀,為什么?
因為原來你要自己去建機房、自己去采購、去調(diào)研機房、采購服務器、采購帶寬,中間出了任何問題很大可能都是機房的問題。
在云時代,尤其是在AWS出現(xiàn)之后,很多美國團隊的運維方式發(fā)生了極大的變化。
為什么云時代的運維跟原來的運維不一樣?
首先是從云主機。云主機的出現(xiàn)以前跟機房有模糊地帶的爭議話題全部都抹平了。有了一個供應商專門提供云主機的服務,被上億的用戶驗證。提供的服務其實很簡單,包括一些基本的監(jiān)控,一個部署服務的入口。如果說一個云主機的服務商一大半的用戶使用情況都是很穩(wěn)定的,基本上你碰到不穩(wěn)定的概率是比較低的。其可靠性是比自己建機房、找?guī)捁桃摺?/p>
一個專業(yè)的云主機服務提供商能夠更專業(yè)的去解決基礎設施的問題。你不會再面臨那么多很痛苦的問題了。
另一個本質(zhì)轉變就是彈性運算。點擊一個按紐就能申請到一臺機器,或者是運行API就能拿到一臺機器,在兩分鐘之內(nèi)就能完成整個申請、測試應用甚至上線的過程。以前需要幾個月完成的事情,現(xiàn)在可以在調(diào)用一個API的時間內(nèi)完成。
我個人感覺,是因為基礎設施的變化造成了云時代的運維跟原來的運維有本質(zhì)的區(qū)別。
原來你做的其實是很爛的事情,大家也不會太在意,因為就算哪個環(huán)節(jié)做的不好,你的老板也感覺不到。實際上它帶來的變化就是彈性運算。
當基礎設施是彈性運算之后,實際上所有的服務都可以變成彈性運算。
彈性擴容
初始做產(chǎn)品時你只需要做最小的容量。用戶增長后再去擴容。
灰度上線
過去前置條件很長,長達幾個月甚至無法做。因為前置條件的成本非常低。我們幾乎每做完一個小的特性馬上就去做灰度上線,因為灰度上線的時間越早、發(fā)現(xiàn)問題的時間點也會越早,這個系統(tǒng)穩(wěn)定的時間也會越短。
每天部署
擴容、灰度上線,實際上是一個開發(fā)的過程。整個集群的部署我們每天都會做一些微調(diào),比如增加一個新機房,在早期幾乎是不敢想象的。但是在現(xiàn)在這個年代,這都會越來越輕量。
新增加機房
現(xiàn)在云主機服務商群雄并起,在增加機房之前,你需要做充分的驗證。驗證過程包括部署一整套的東西,在上面跑一些測試用戶。新增加一個機房的成本其實也是很低的。
擴容
擴容的過程包括:
- 申請機器
- 配置DNS
- 部署應用
- 部署監(jiān)控
- 自動測試
- 分配流量
整個過程實際上在自動化運維里面就是一個腳本。通常擴容就是3臺到5臺,即小粒度的方式擴容。3臺到5臺耗時20多分鐘,實際上你也可以認為這個擴容過程就是一個灰度上線或者是灰度測試。每完成一個特性就去做擴容,先灰度上線,上線成功之后就把它切到生產(chǎn)的流量并擴容,同時把老版本的流量或者是容量下掉,就完成了一次擴容過程。
廉價的分配過程
我理解的彈性運算主要是兩點,一是需要的時候才分配。另一個是分配過程很廉價。
這是什么概念呢?過去,分配過程成本高昂,需要有很多的前置條件?,F(xiàn)在的分配過程很廉價,運行一個API,調(diào)用他們的一個API接口,整個分配過程就完成了,包括主機的分配、流量的分配、IP的綁定、域名解析的配置。或者還包括其他的服務,比如存儲服務、load balancer等。
我最早用的服務是AWS,但是現(xiàn)在國內(nèi)的云主機服務商真的做到那么成體系、配套、完整的應該還是沒有的。有一些已經(jīng)有API了,但是跟周邊一些自動部署模塊的整合做的不夠好。
所謂的分配過程很廉價,彈性、擴容、灰度非常方便,兩者結合就形成了彈性預算的重點。
用戶隨時在線擴容
上文提到一個結論:就沒有自動運維就沒有彈性運算。
而用戶隨時在線擴容是另一個概念。不同之處在于,上文是以運維者的角度,或者是以產(chǎn)品服務提供者的角度來講這個問題。
我們服務的目標用戶,早期用戶量非常少只是做驗證沒有付費,用戶量越來越多發(fā)現(xiàn)延遲變大了,查監(jiān)控數(shù)據(jù)發(fā)現(xiàn)每天的在線用戶很多、每天交互次數(shù)很多,現(xiàn)在的通道不足以支撐那么多的用戶,你的通道可能需要去擴展。
以前的做法是打電話給商務說“再多交一點錢,你們再給我多開一臺服務器”。
我覺得那個還是傳統(tǒng)服務,云服務就應該是純自助的。
發(fā)現(xiàn)通道不夠用了就去申請,考慮到主要用戶是在上海、南京,或者在西藏、西安,就需要去根據(jù)具體情況在不同區(qū)域擴張通道。這個事情應該是用戶自己去做,但是他需要我們?nèi)椭俏覀儙椭霓k法是什么?實際上很簡單,我們會看到哪里有資源、哪里缺資源,實際上買資源的過程就是自動擴容的過程。
自動擴容不是由運維來驅(qū)動,而是由使用我們服務的開發(fā)者來驅(qū)動。
自動擴容
每天,我們會通過監(jiān)控系統(tǒng)查看各個模塊、各個組件的情況,一旦到了一定的基準線之后,以前需要人工干預,現(xiàn)在各個環(huán)節(jié)成熟度足夠,實際上不需要人工去干預。例如,做一個配置,每天晚上12點到早上8點,哪一部分東西容量出問題了,就啟動自動擴容過程。
相比之下,就算每天有運維人員盯著,后半夜的工作能力是很弱的。其實很多運維做的事情都可以被標準化,尤其是像擴容這種事情,很多事情可以簡單的通過擴容讓他撐過很長的時間。不管你中間運維程序?qū)懙牟缓?、代碼不好,甚至因為你選的 Erlang 沒有用 Go 的原因,無論什么原因,只要把那條路加寬就可以讓車全部通起來。
舉云巴自己的例子:有一次,系統(tǒng)突然出現(xiàn)瓶頸,超時、丟包,其中有一個集群的壓力很大。我只做了一件事情:把它的容量擴充了一次,從原來是20臺機迅速擴成40臺機。壓力馬上就下去了。
我就跟團隊說“就是這個模塊里面有瓶頸”,我們沒有去看日志、也沒有去抓包。得出這個結論的原因就是發(fā)現(xiàn)入口數(shù)據(jù)一直在增長、出口的數(shù)據(jù)沒有變,調(diào)用依賴模塊的請求數(shù)量沒有增加,入口一直在堆積、出口上不去。很簡單,就說明他這個管子太細了。
#p#
云計算跟企業(yè)服務不同之處
企業(yè)服務出現(xiàn)了問題立馬停下來,但是云服務是不行的,現(xiàn)在可能有成千上萬的用戶正在用你的服務,停一分鐘就會有人開始罵人了,你的電話、你的QQ群、你的郵箱都會爆滿。正確做法是離線的去找問題在哪里。
我們將來想做的事情
把任何模塊的出入口都做很詳細的監(jiān)控,每一次請求甚至是每一個協(xié)議包都做監(jiān)控,我們會找出一些范式。應該進去多少、出去多少,出去哪些到模塊A、哪些到模塊B、哪些到模塊C,我們都有預測。一旦發(fā)現(xiàn)哪個模塊有壓力,馬上做自動擴容。上文提到,有壓力不僅僅是因為容量的問題,也可能是程序有BUG。不管為何,應該先讓它通起來,這是我一直在構思的事情。我們后面也會在這方面投入很大的精力,希望盡快和大家分享。
團隊分工和運維工程師的角色
我們以前開發(fā)團隊的分工是什么呢?就是寫代碼的人寫代碼、測試的人測試、運維的人去敲各種命令去部署。
有一個很有趣的悖論,其實寫代碼的人比測試的人更清楚要怎么去測試,寫代碼的人比運維的人更清楚應該怎么去配置和部署。
實際上我們有過慘痛的教訓,以前做極光推送的時候,每次做升級,我會在辦公室放兩個很大的白板,準備出3支筆,黑色、藍色和紅色,把開發(fā)人員、運維人員、測試人員全部叫在一起,一起寫一個很復雜的作戰(zhàn)地圖。如果一次遷移涉及到9個模塊,就把9個模塊全部畫出來,那個過程有點像話劇的腳本。我先說A你要先做什么事情,做完之后你要運行什么確認之后通知 B同時要告訴我,B要干什么事情,B干完之后也要告訴我通知下一個C,然后你要干什么事情,可能有一個事情需要我們幾個人同時做。
相信大家應該明白,A做了之后B應該在幾秒鐘之內(nèi)立即完成另外的動作,要不然就會出問題。這樣的事情我們每個月會做一次,凌晨2點干到早上5點鐘,還要盯到早上8點才能回去睡覺,非常辛苦。每次運維要問,這個東西到底怎么配置、配置文件到底在哪里、每個項目是什么意思,等等。最后發(fā)現(xiàn)在強求每一個人寫文檔。所有的文檔寫完之后我都要去重寫。
寫文檔的代價很高,所以它的時間成本是很高的,大部分的情況大家未能寫出很好的文檔。就算寫出很好的文檔,運維和測試也需要要花很長時間去看。這三個角色之間溝通的成本非常高,溝通的效果也是非常差的。
怎么去解決這個問題?
答案:Devops。
讓運營來參與開發(fā)。
實際上我們的做法是沒有測試和運維。所有的事情都是由開發(fā)來做。大家的分工會有一點交叉,可能5個人寫一個模塊,這個模塊可能又被分成5個小模塊,可能是做特性A的人可能做了特性A的30%測試,測試用例的代碼和部署的代碼。做特性B的人做70%的特性A測試和一部分特性A的部署,這樣大家可以互相交叉。
你在寫測試和部署程序的時候,你實際上對那個東西的理解可能比寫代碼的人更深刻。因為在整個產(chǎn)品開發(fā)里面寫代碼是最簡單的事情。一個好的產(chǎn)品,寫的東西大概只能占30%,還有其他的因素占到70%。
一個好的產(chǎn)品是由你運維水平來決定的,而不是由你寫代碼的水平來決定的。
實際上部署腳本的代碼量有可能是遠遠超過特性的代碼,寫一個代碼會造成一大片測試腳本/運維腳本和部署腳本的調(diào)整。DevOps 并非我們首創(chuàng),在國外一些團隊已經(jīng)實踐了幾年。他們沒有測試也沒有運維,所有的人又是開發(fā)、又是運維、又是測試。
我們開發(fā)團隊每一個成員,除了寫好特性以外也會寫測試用例、寫部署腳本。我們招人的時候會招全棧工程師,因為你在寫測試腳本和部署腳本的時候會反思你原來寫的東西。
過去經(jīng)常遇到的問題是,運維的人會抱怨“你這個程序怎么寫的、這個配置怎么這么復雜,根本就不知道你在搞什么”。測試的人也會說“你的接口怎么定義的?我完全看不懂,我怎么寫你的測試用例?”
現(xiàn)在,每一個工程師就會經(jīng)常反思,原來寫是否合理、是否考慮到以后將來的維護,這樣反過來又可以提高你編碼過程的質(zhì)量。
理想的部署方式
早期有很多傳統(tǒng)部署工具,比如Puppet,有一個很大的悖論。他們是用一套集群或者是用一套已經(jīng)部署好的東西,在去管理另外一些沒有部署好的東西。這就有一個雞生蛋和蛋生雞的問題。
Puppet本身的集群你怎么去做?原來的做法是運維把機器弄好了、把所有的機器都裝好、配置全都配好,這個過程成本非常高。
我個人比較喜歡的做法是,新機器只需要支持SSH。標準的做法是申請好之后,把key導到部署機器上,調(diào)用一個 API主機申請,用sudo權限即部署完成。配置新機的方式其實好多都申請好了。這和Puppet一類的部署系統(tǒng)比照,它的成本更低,沒有Master,沒有Agent,只要在這臺機器上可以連上目標機就可以了。
#p#
Ansible概述
找到 Ansible 的過程
最早我們用 SSH 寫很多腳本,要用 SSH 連過去,也是在某一臺機器上執(zhí)行,不用在目標機上登陸。這種做法在相當一段時間內(nèi)是我們實際使用的手段,它實際上比 Puppet 有效。但是它有一些問題:管理成本高、腳本會越來越多。部署的過程有很多的基礎部件需要反復部署,幾乎是沒法管理。
后來我們用了 RunDeck,它有界面、有一定的管理能力。我們還用過 Fabric,即批量執(zhí)行命令,能做到類似部署的事情。但是,目標機規(guī)模大了之后僅有管理的能力是不夠的。后來我們又調(diào)研過 Salt,不認為有太大的差別。
選擇 Ansible 主要因為豐富的相關支持,包括很多現(xiàn)有的組件和模塊和開源的 Ansible 部署和腳本。我們的團隊不喜歡糾結。我們發(fā)現(xiàn) Ansible 沒有太本質(zhì)的區(qū)別,就開始用起來。如果沒有明確理由,我們就憑感覺選一個用。
Ansible 是通過 SSH 連接到目標服務器,上面這兩個東西是我想了很久,我希望去擁有的特性。一個是完整的集群,只需要有一個 inventory 去定義,寫出清單就可以定義集群。每一個角色的具體功能有若干 playbooks 來定義。
Ansible Inventory
這是一個 Inventory 的虛擬例子。這定義了兩個角色,一個是 web-group,一個是 db-group。
Playbook的詳細案例。我們用的是 Ubuntu 12.04,所以先用 Nginx 的 PPA。
Ansible 的開發(fā)過程是寫大量 Playbooks?,F(xiàn)在 Ansible 支持的有251個模塊,特別是對于云服務的支持。像 AWS、Docker、Rackspace、OpenStack,部署腳本都放在一個子目錄下。這就意味著把別人寫的腳本拿過來,或者把別人寫定義的 Playbook拿過來非常容易?,F(xiàn)在關于 Ansible 的開源腳本數(shù)量龐大,有 3000 多個項目,我相信這數(shù)字會越來越大。因為它的分享方式真的很簡單,只要把目錄拷過去即可。
Demo 視頻
視頻鏈接見此:http://my.tv.sohu.com/us/242861154/77607305.shtml,41分鐘開始。
"這是我們其中一個機房部署的機器,我個人比較喜歡的做法是我會給每個機器都Hostname,我們每增加一臺機器的時候我們會跑若干的腳本,其中就包括去修改跟他在一個主網(wǎng)內(nèi)的所謂Hosts文件,這里面沒有一個IP。這邊我要講一講,比較有趣的地方比如說我這邊有一個nogic,因為我要考慮到負載均衡的問題,每一臺機器上配的配置項不一樣,那你后面就可以帶一些參數(shù)去給他指定不同的配置項,如果沒有指定的話可以寫一些腳本。"
"我給大家看一下我們幾乎每天都要執(zhí)行的命令,就是所謂的 ansible-playbook,我們可以看一下 playbook 的內(nèi)容。我定義了一些角色,這邊我所有的機器都要去運行 common,我們把一些基礎的模塊都刷一遍,每次運行的時候都裝一遍個,避免造成他的依賴不一致。
這是一個最上層的 playbook,因為我的 Roles 又會引用另外一個 Roles,我們把 playbook 也分了幾級這樣可以看的很清楚,我裝redis的時候他就只去依賴于這個,這邊有一個 Emqtt 實際上以來于三個小的 Roles,一個全的 playbook 你可以很清楚看到每一個Roles要做什么事情。
你可以給每一個 Role 命一個別名,我可以把所有有這個 Tags 的Hosts給執(zhí)行一個 ubuntuprotobuf,你只要用這個 Tags。我們在運行一個命令之前,就確認它會在哪一些機器上執(zhí)行,然后去確認一下,他會去運行哪一些命令,你可以把這個去掉,就可以開始執(zhí)行了。我不知道現(xiàn)在能不能執(zhí)行,我還是最好不要執(zhí)行,我給大家看一下吧,這就是我們其中的一個生產(chǎn)環(huán)境,我給大家執(zhí)行看一下,應該沒什么太大問題。他就開始 playemqtt,會收集一些原始數(shù)據(jù)。他收集的數(shù)據(jù)非常多,大家看到這個叫 Gathering facts,你系統(tǒng)的版本、各種平臺的信息全部收集下來,其實收集這個信息就是要判斷你的版本,它會執(zhí)行不同的分支平臺可以幫你做這些事情。還有比如說依賴于你的 IP、依賴于一些其他的信息,比如說你可能有幾個網(wǎng)口,可能你的配置需要用到不同的網(wǎng)口配置,在第一步就會把這些信息全部收集起來。這邊還有一個很重要的是驗證你的目標機器是不是能夠連的上,在這個 Protobuf 過程當中。"
關于作者
張虎,云巴 (yunba.io) 創(chuàng)始人,yunba.io 云后端服務。 JPush 創(chuàng)始人,原CTO。 Oracle VM 創(chuàng)始團隊成員。