基於容器的全鏈路運維平台實踐
經曆過去 O、靜態化、異地多活、全鏈路壓測、雙 11 等多個高可用項目之後呢,我就會去思考說我們能不能把這些高質量的架構通過產品化的方式,讓阿裏之外的公司也能夠享受到這樣優質的架構,而且不需要踩我們之前所碰到的那些坑。這就是我今天主要給大家介紹的我們做的叫 EWS 的一個產品,以及我們做這個產品當中的一些思考。
首先什麼是 EWS, EWS 是針對互聯網應用提供的係統構建、發布、持續集成、運維管理的一站式解決方案。就可以說當你研發完成之後,所有的上線、運維都可以在這個平台裏麵完成的。
首先我們來看一下,一個通常的運維係統是什麼樣的。
一般來說會用腳本做係統發布,底層的話會用 SCP/FTP 中轉的方式把代碼包傳到服務器上去,然後重啟應用。為了保證發布的一致性,我們可能會批量比對代碼包的 MD5 是不是一致。那監控我們會怎麼做呢,可能就會使用一些像 Zabbix 這種開源方案。
對一些初創公司來說,這套發布方案已經是足夠使用了。但是我們細想一下就會發現很多需求是沒有辦法滿足的。
比如說我搭了一套監控係統,那這套監控係統本身的監控係統由誰來做。假如某一天,我的監控係統掛掉了,那我是不是就變成了一個瞎子。還有當我發布了之後,程序員寫 Bug 是個很正常的事情,我就會需要回滾,那回滾怎麼做,比如我想回到某一天的某一個版本,就像 Mac 上麵的 TimeMachine,我希望回到上周二發布的版本,那這個問題也是沒法解決的。當我們業務上漲之後還會有一些精細化運營的需求,比如灰度發布,AB Test 這樣的需求,顯然這種架構也是沒有辦法支持的。
回到剛才的問題:『我們為什麼要做 EWS』,首先要解釋一下什麼是 EWS ,就是Enterprise Workstation Service,企業工作站服務。我們希望企業的運維工作可以在上麵一站式地完成。
首先我們做了一個 TAE 2.0 的版本給阿裏百川內部使用。它的第一個問題就是不夠自由,什麼叫不夠自由呢,可能有很多用戶以前用過 GAE、SAE 這些 PaaS 化的產品,那它的研發流程是什麼樣的呢?比如我基於 GAE 的 SDK 去開發我的產品,GAE 會提供給我們文件的存儲、緩存、消息中間件服務。但是有個很嚴重的問題是,我是強綁定一個平台的,比如我某天不希望在 GAE 上開發我的產品了,但是我已經綁死了 SDK 這樣我的遷移成本就很高。這是一個不自由的問題。
第二個是成本問題,我們在做百川的時候,用戶購買的是一個容器,而不是一台機器。他是不能登到機器上麵去的,而我們背後怎麼做的呢,簡單來說,我們會創建很多 ECS 的虛擬機,將用戶的程序混合部署在上麵。當時 Docker 發展的還不是非常的完善,我們考慮到用戶的安全問題,有的時候一台機器一個容器這樣部署的。那這樣就帶來了很明顯的成本問題。我有大量的1核1G的機器。在預算有限的情況下,幾千上萬台機器對成本的壓力是很高的。
第三個是不支持多 Region 部署,我們之前是隻支持杭州機房的一個部署。那很多公司近兩年開始嚐試異地多活,就是我的程序在全國各地都有部署,用戶可以就近接入,而且如果我的機房掛掉了,或者某個城市的機房掛掉了,都不影響我的服務。這就要求我們支持多 Region 部署。
第四點就是我們當時做的彈性伸縮它是不夠智能的。為什麼說它不夠智能,因為當你在做一個應用彈性的時候,你需要考慮到很多方麵,比如一個 PHP 的應用,他是一個無狀態的應用,或者像 Hadoop 這種離線計算,他的調度算法是完全不一樣的。第二個是對於有狀態和無狀態的服務,他們的調度也是完全不一樣的,比如無狀態的,我隻要在另外一個地方起一個容器,然後把流量倒過去就可以了,但是對於有狀態的東西來說就沒這麼簡單。
之前我們並沒有充分發揮容器的優勢,容器最大的優勢在於它像一個 Box,一個盒子,我把它放在哪裏都能跑起來,但是他下麵是一個自建機房的機器,還是我買的公有雲的虛擬機,還是一台物理機,這其實並不重要。
那同時,客戶對我們提出新的需求,說在你們上麵開發收到很大的限製和約束,在易用性和安全上麵這是有一個權衡的,我們需要考慮要讓用戶用起來沒有什麼約束,又能保證他的整個代碼是穩定安全的。第二點是我們有越來越多的內部用戶,希望把自己的程序部署在阿裏雲的機房上麵,比如說,我的客戶在深圳那我需要部署到深圳機房,但內部目前並沒有一套全麵的管控係統來幫他做發布和監控的。
那麼我談談我理解的創業公司的第一要務是什麼,首先在互聯網這個行業裏麵,速度是需要非常快的,你和競爭對手同時想到一個 idea,他先做出來了,你沒做出來,那你可能就失敗。對於一個創業公司來說他一定百分之一百投入在他業務上,假設我是一個 CTO,老板問我進度怎麼樣了,我說『給我一個月時間,設計一個高性能高可用的架構,然後在這個架構之上就可以具備水平拓展,異地多活這些能力。』老板說『我們業務沒做起來,別想那麼多。』顯然對於創業公司來說,並沒有必要聘請專門的 SA 和 DBA,請專人來做對成本是巨大的浪費。
那給大家簡單介紹一下產品可以達到什麼樣的效果。
這是使用我們服務的一個應用,規模還是比較大的,有 400 台 ECS 虛擬機,那他發布的流程是什麼樣的呢,他會將文件傳到公司跳板機,再傳到部門跳板機,再批量拷貝覆蓋,重啟應用。那麼問題是他的代碼版本是難以追溯的,回滾也容易搞錯,本來想回滾到這個版本的代碼,結果回滾到另外一個版本去了。另外很多公司的應用因為擔心故障,所以隻能在低峰期發布。因為害怕影響到客戶所以隻能在半夜發布,程序員也因此苦逼了,老是熬夜嘛。還有運維的問題,運維同學寫了一個腳本,腳本比較簡單,隻能做 CPU 內存的監控和報警,但是我的應用裏麵碰到異常情況,我會打一些日誌出來,根據這些日誌篩選出異常做報警,這個也做不到。
在他使用 EWS 之後,他的體驗是怎麼樣的呢?圖中表明了他應用的容器數,他可以進行的操作:重啟、停止、上傳代碼到部署、編譯、曆史版本部署、單文件的發布,配置管理、WebSSH、監控詳情。容器管理的界麵裏,可以看到他的每一個容器這裏都有運行功能狀態、IP、容器配置、占用 CPU 和內存的情況。還可以給容器加標簽,方便管理。在監控圖上可以看到 CPU、內存,還可以監控帶寬。
在 WebSSH 裏,用戶可以無需經曆很長的過程,隻要選擇一個容器,打開 WebSSH,就可以像使用 Shell 一樣使用一個在線的 SSH,隻要點擊一下就可以開始排查問題,這個對他的體驗是非常好的,當然在 WebSSH 中是有非常嚴格的權限校驗的。
那麼對於一個係統來說,核心的關注點應該在哪裏,怎麼構建一個高性能高可用的應用。主要要考慮哪些方麵呢:
- 穩定
- 高性能
- 可擴展(水平擴展)
- 安全
這是我們產品功能的架構圖。
最上麵有防攻擊的一些流量入口,下麵是負載均衡的層,用 Nginx 和 SLB 來做負載均衡,中間是用戶部署的一些容器,底層我們提供了一些比如數據庫、文件存儲還有緩存這樣的一些服務。右邊部分我們包含了從他上傳部署開始、對象管理等等很多功能。
全球有一家因為猴子而出名的公司,叫 NETFLIX,一家在線視頻的公司。為什麼因為猴子出名呢,他們開發了一套非常著名的組件,叫『Chaos Monkey(混世猴王)』,這個猴子在幹嘛呢。猴子啟動之後,會在集群裏隨機挑選幾台機器,然後直接停掉。通過這種方式驗證係統是不是高可用的。檢測在一些機器停電了、機房掛掉的情況下,程序能不能正常運行。全球沒有幾家公司能處理好這種實驗,所以 NETFLIX 在高可用圈是非常出名的。
那麼說到如何保證在我一些機器掛掉的時候,我的服務是可用的。這裏麵有很多細分的思考。
對於無狀態的服務來說,他是需要有水平擴容能力的。我在負載均衡上麵,多放一台機器或者去掉一台機器,他就能起到增加容量能力或者減少的效果。在這個基礎上我們是準備做更加精細化。比如對於 Java 應用來說 我們會監控 JVM 的 GC Log,我們會判別你的 CMS GC 的頻率是不是固定的,有沒有發生Full GC,有沒有出現Stop-The-World,有沒有導致你的服務在抖動,如果你不從 GC Log 來看,而從外麵黑盒的角度去看的話,那在我健康檢查的那個點這個應用是 OK 的,但其實這個應用可能已經是有問題的了。
故障遷移有個很大的難點,就是我們怎麼遷移有狀態的應用。比如說MongoDB或者MySQL這樣的應用,我們怎麼對他做有狀態的遷移,怎麼樣不丟數據,這是一個難點。首先有狀態的服務,在遷移的時候,一定要是停機的。如果不停機的話,會導致你的文件正在寫入的時候被打快照,那文件可能是損壞的,第二,你在寫快照在做,這是永遠追不上的一個過程。我們將有狀態的停機之後,會將數據打包,放到另外一台機器上去,再跑起來。這是一個最簡單的思路,當然中間會有很多問題。第二個解決方法是我們用共享存儲的方式。就是我的存儲其實是在遠端的,那麼通過iSCSI/IP-SAN這樣的協議,就是我這台機器的數據在另外一個台遠端機器上,當這台機器出問題的時候,我隻要起另外一台機器,然後起這個網盤掛載上來,那我就在不做數據遷移的情況下,把有狀態的節點做了一個遷移。
那麼從流量入口的角度來說,我們客戶他的服務會跑在我們接入層的後麵。那我們接入層上麵就要考慮幾個點。第一,帶寬要足夠大,否則會導致一些網絡的擁塞;第二個,我一定要是一個安全的接入層,就是當客戶遭受攻擊的時候,第一客戶的應用不能掛,第二我們自己不能掛,目前我們采用的是阿裏開源的一個 Tengine,fork自 Nginx 的一個負載均衡器,現在同時支持 HTTP 和 HTTPS 的服務。在 4 層上麵我們是支持雲盾的防 DDOS 攻擊,7 層上麵使用 TMD 來解決 CC 攻擊。最後就是我們的接入層要直接回源到用戶的機器,那需要用戶的授權,我們會在他的安全組裏添加一條接入層的白名單。
那麼我們接入層做了這些安全工作以後達到的目的是什麼樣的呢。比如我以前一個店鋪的應用,比如說一些店鋪上新,那我們會去他的首頁他的搜索頁去逛,這個頁麵其實是經常被攻擊的,主要是一些同行攻擊,直接打到我們係統上麵來,可能會是幾百 G 的流量,幾十萬的 QPS 直接打過來,所以這些方麵我們是有很多攻防的經驗的,然後把這種經驗沉澱到我們的產品上麵去。
剛剛提到的係統關注點裏麵有一個就是高性能,那麼我們怎麼評判高性能。
我會采用一些常態化的壓製手段來驗證我的係統到底能支撐多少 QPS 的訪問量。常用的手段有哪些呢,最簡單的是我去造一個請求,比如說 AB,或者 HttpClient 寫的一些工具,我去造這些請求。
另外一種就是說我用線上的流量拷貝一份,比如 TCPCOPY/HTTPCOPY 的方式來拷貝到我的線下服務器上麵,我可以導五台機器的流量到一台上麵,看能不能扛得住這些流量。這是一種流量拷貝的方式。
那麼另外一種方式是說,可能大家在外麵的一些資料上可以看到,就是全鏈路壓測。你會發現當你的係統大了之後,特別是現在比較流行的叫微服務,一個係統可能包含了幾十個微服務,而且它們之間是會互相調用的,那麼這時候你保證一個係統的容量是不夠的,根據木桶原理永遠是最容易垮掉的那個係統最先垮掉,而且整個係統會受它拖累。
那麼全鏈路壓測的目的就是說,我會把一個業務的整個鏈路上所有的係統全部壓到,這個時候我就能保證說比如在雙十一的時候,交易、商品、營銷、店鋪這些係統,我能保證在幾萬筆每秒的下單的情況下都不會掛,但是在以前,可能交易說我能扛住多少筆每秒的下單,商品說我現在能扛住幾百萬每秒的商品查詢,最後你會有疑問說到底雙十一能不能扛住,因為大家給出的標準都是不一樣的。全鏈路壓測就是解決這個問題的。
那麼我們可以看一下我們給用戶提供的一個基於 URL 的壓測。
它大概的參數會有這些:你的應用是什麼,測試時間,測試URL是哪些,這邊可以設權重,還有 HTTP 的 Method,這裏可以增加 URL,參數、Header。然後這裏麵會有兩種方式,一種方式是針對小白用戶,可能我以前沒有太多的性能壓測經驗,那麼我可以根據我業務的 PV 量,抽象計算出我大概需要承擔多少 QPS,那麼另一種就是說我自己定義參數,多少 QPS,多少 RT,點確定,那麼這時候壓測任務就開始了。用戶不需要做額外的事情,他就可以進行壓測了,結束後會產生一個非常詳細的壓測報告,包括比如說 QPS、RT、Load、CPU、內存、網絡還有諸如 JVM 的一些 GC 的情況。
這些工具想讓用戶具備的能力就是讓用戶隨時隨地,想發就發,就是你無論在高峰期還是低峰期,我在高峰期也能做發布,以及隨時隨地,想壓就壓,我在任何情況下,隨時可以對係統做壓測。那麼之前說到了程序員和運維人員這麼苦逼,為什麼老是要半夜兩三點爬起來幹活,就是不敢在高峰期做發布做變更,那麼我們希望把他們解放出來。不用再兩三點爬起來,房間燈也沒有開,對著一個漆黑的SSH的屏幕,覺得自己很苦逼。
這是一種能力,什麼的能力?想發就發,想壓就壓的能力,這種能力,不是每個係統都能具備的。
我們希望未來的互聯網的應用,每個都應該具備這樣的能力。
然後再談一些技術方麵的東西,我們會在客戶的機器上安裝一個 Agent,會有一個 AccessServer 的接入層。當時我們在做這塊通信的時候遇到一個網絡的問題,客戶的係統是在阿裏雲ECS上,我們的係統是在內部機房,出於安全的考慮,我們的機器是不能直接訪問客戶的機器的。可能有人不太清楚是不是我的數據放在阿裏雲上你就可以隨便去看,其實不是這樣的,你會發現我們在做的時候,你不能訪問客戶的任何端口。
那麼我們就反過來,客戶可以請求我們的服務,Agent 在啟動的時候會主動連接 AccessServer,然後中間會建立一條 WebSocket 的長連接,而且這個長連接是帶 SSL 加密的。為什麼要帶 SSL 加密後麵我會提到。那麼這種情況下隻要 Agent 能訪問一個雲服務,基本上能解決 99% 的場景,即使你的服務器不能直接訪問客戶的機器,這個架構也是通用的。
大家知道 WebSocket 它是一個雙向通信的協議,對 Agent 來說會發心跳給服務器做監控,我們會發一些指令比如部署、停止容器、重啟這些指令。整個延遲是比較低的,整個交互是在1ms以內,而且支持高並發,我們一台 4 核 8G 的虛擬機能維持並發5萬個連接。
然後說下異地多活,就是多 Region 部署,這個地方我們有一些思考就是說,第一個版本裏麵都是中心化部署的,所有係統部署在杭州的主站,指令會通過杭州發到青島或者深圳的機器上去,那麼我們當時對哪些係統應該放在 Region 內哪些應該放在 Region 外其實是有一些討論的。最後決定,對於一些交互頻繁的應用,放在 Region內,這樣整個網絡鏈路比較好,整個延遲、成本也比較低。對於一些需要有全局視角的應用,比如說一些全局調度器,它應該是中心化的,並且它大部分是一些控製流不是數據流,所以中心化的網絡帶寬占用不會特別大。
還有一個非常重要的概念就是 Region 自治,什麼是 Region 自治,就是當時我們考慮一種比較極端的情況,當我們的中心節點和 Region 任意一方發生網絡中斷的時候,心跳也上不來,指令也下不去的時候,這時候我們要保證一個事情,雖然 Region 內已經脫離了中心的管控,但是客戶的整個流量、功能、服務都是可以正常進行的,不會因為中心的係統影響客戶 Region 內的服務。整個網絡恢複後所有應用就應該恢複了,期間用戶不應該感知到任何異常。跨地域網絡通信我們是通過接專線來實現高性能。另外一個就是,我們現在在全國有 4 個 Region,分別是北京、青島、杭州、深圳,我們未來可能會有新建 Region 到香港、北美、歐洲等等。那麼我們的技術人員都是比較有追求的,當新建一個 Region 的時候我們希望第一是自動化的,就是一鍵部署,第二我們希望是非常高效的,那麼我們給自己的 SLA 就是說,在半小時內把一個新的 Region 全自動地建立起來,這是我們自動化搭建的一個目標。
那麼下麵一個問題是,如何讓係統可演進?什麼是讓係統可演進,就是說你的係統一定是在不斷發展的,你一定是在不斷更新你的係統、發新的版本,那麼在這個過程中,我們作為一個 Provider,一個服務方,怎麼讓客戶感知不到我們係統的升級,打個比方,我們的Agent會升級,我們可能會針對Docker版本做升級,那麼這些升級客戶度應該是感知不到的,就是所謂的「無縫」嘛。
那麼我們就會做很多事情,特別是這個地方有一個隱含的前提條件就是說,我們沒有辦法在用戶的機器上執行任何的命令。那麼用戶是怎麼把他的機器掛到我們的平台上來的?他是用 curl 命令來安裝我們的 Agent,然後直接注冊上來的,但是隻要連接斷開或是出現了一些意外情況,我們是拿不到用戶的密碼的,也無法登陸他的機器,當然你可以告訴用戶說我能不能上去看一下,但我們不希望出現這種情況出現,我們希望說不管什麼情況都不要騷擾用戶,不要因為我們的升級騷擾用戶。
那麼這裏是我們 Agent 升級的一個流程圖,其實它跟 nginx 的 reload 原理非常像。我們可以看到這裏有一個 V1 版本的 Agent 和一個 V2 版本的,一開始 V1 在工作,那麼我們要升級 Agent 的時候,首先通過 V1 的 Agent 在機器上再起一個 V2 的 Agent,它們兩個用不同的端口,這個時候 V1 的 Agent 會受到一個指令,就是你現在不要再接受新的指令了,新的指令全部給到 V2 上麵,你現在正在執行的指令,因為我有一個中心化的係統,每次新指令發出前會問一下 V1 現在這個容器有沒有指令正在執行,如果有,那麼就等你繼續完成之後再執行,如果說你沒有在執行這個容器的操作,那麼我就直接把指令交給 V2 執行了,隨著時間的流逝,V1 上麵就不再有指令,沒有指令之後,它就可以下線了,那麼這時候 V2 就成為這個機器上唯一的Agent了。所以你會發現這個跟 nginx 的 reload 是非常像的。
那麼這種方式就保證了說我的 Agent 盡管頻繁在升級,但是通道不會中斷,也不會給客戶產生影響。所以從效果來看我們從整個產品上線以來,Agent升級了有大概幾十次,沒有客戶感知到我們這個事情。
下麵是一個安全的問題,在高性能、穩定、高可用之外,安全是一個日益嚴重的問題。你們會發現說今年 315 暴露了一個很大的問題是說,我去了一家咖啡店或者肯德基,然後我連了他家的 WiFi,結果我的個人信息全部泄露了。所以安全是一個平台從誕生之初就應該考慮的,我這裏有一個建議是說,如果你正在使用的產品不是用 https 的,那麼這個產品你今天就可以把它卸載掉了。
那麼在一個雲計算的網絡上麵會有一個很明顯的問題就是說經典網絡下有一個大二層的嗅探問題,什麼意思,就是說雖然我可以通過安全組在路由這一層把我跟其它機器的網絡斷開,但是在二層鏈路層上麵我們其實在一個大二層,所以隻要我在二層上麵做監聽的時候可能會監聽到別人的一些幀,那這個就有可能會造成安全的泄露了。那麼你會發現說我們的服務很多是提供在內網的,也就是說是不暴露在公網的,隻有 ECS 通過內網 IP 才能訪問,但是對於這些服務我們也是全部做了 SSL 加密的,不管是我們前麵說到的 WebSSH,還是 Docker Registry,還有 WebSocket,所有跟客戶交互的鏈路我們全部采用了 SSL 加密,這是一個原則問題。
成本,我們之前提到了我們的 2.0 集群大概有接近 7000 台的虛擬機,容器有上萬個,那麼這 7000 台虛擬機對我們的成本壓力是很大的,我們會想說怎麼樣去削減這個成本。我們會發現第一,很多用戶是沒有流量的,他可能上來用來一次就沒再用了,第二有的用戶流量是很低的,你給他 1 核 1G 他可能根本用不滿,那麼我們就會通過容器和調度的方式去將流量比較小的用戶做超賣,對於付費和 VIP 用戶,我們還是會把資源留給他,用獨占的模式,那麼在經過新平台的一個調度之後我們發現,成本是這樣的。
藍色是老版本,綠色是新版本,左邊是根據機器數的,右邊是根據CPU的,會發現機器數我們現在大概隻需要三四百台,實際減少了 95%,從 CPU 核數來看的話,之前是 10000 多核的規模,現在大概 3000 多核,也是削減了差不多 2/3 的成本。
網絡拓撲我就簡單過一下,我們現在所有跨 Region 的通信全部是走專線,所有公網服務全部在 BGP 機房。
那麼這裏是我個人對公有雲的一些思考,以前機器是我家的,我可能租了一個機櫃,現在隻是說我的機器放在別人家的機房,是這樣嗎?那麼我覺得說,公有雲並不是說隻是以前機器是我的現在租用別人的機器這麼簡單。
公有雲它的整個開發和運維模式和以前完全是兩樣的。
首先第一個,公有雲賦予了你更多的能力,它開放了很多 Resource API 給你,你創建一台機器,收回一台機器,創建一個 Cache,清空一個 Cache,或者是一個文件的存儲,或者是我的CPU分配、網絡帶寬的分配,這些都給了你一個動態調整的能力,這個能力能不能善於運用,那是另外一回事。
第二個他一定是一個利於 DevOps 的一個模式,可能以前我作為一個開發人員,機器是運維給我的,而且下麵網卡是什麼樣就是什麼樣,是不是 SSD,都是他說了算的。開發人員對代碼實際有更多掌控權,那麼現在開發人員可以涉及到運維,我需要什麼樣的配置,我是計算密集型還是 I/O 密集型,我如果隻需要打些 log,那麼我完全可以買更便宜的普通磁盤,我如果放 MySQL 那麼可以買 SSD 雲盤。那麼開發者是接觸代碼最近的一個群體,他對機器配置要求是最明確的。
第三點是資源變得更易於獲得了,你會發現我要一台機器的時候是要走流程,比如說采購、搬遷、上架、檢查有沒有問題,這個流程其實比較長的,可能一個月兩個月。那麼在雲上麵,我什麼時候想要機器就可以直接生產出來使用了,在資源的獲得上麵其實是降低了資源的獲得成本的,但是對於你的經營成本來說是不是一定降低的?我們說不一定,我們會發現現在很多公司他不用公有雲,還是玩自建機房,為什麼?我們通常來說公有雲便宜,但其實不一定的,有可能你自建機房更便宜,這個跟你的業務、配置、帶寬直接相關。那麼後麵就是說我作為一個小團隊,我沒有能力自建機房,我也沒有辦法說買很多的機櫃,這個時候,沒關係,我也可以在雲上麵購買不同地區的機器,我可以自己實現跨城市甚至是跨國的容災,那麼這種能力你在10年前、20年前是沒法想象的。
最後更新:2017-10-25 16:05:09