云計算最近幾年已經火得不行,云原生(Cloud Native)這個概念又來了,如果上云不“原生”,那就等于白上云。究竟什么是云原生?云原生有何優勢?怎么從“不原生”一步一步做到云原生?本文將給出切實可行的云原生落地指南。
我們先從云計算說起。在云計算普及之前,一個應用想要發布到互聯網,就需要企業自己先買幾臺服務器,找一個IDC機房,租幾個機架,把服務器放進去。接下來就是裝linux系統,部署應用。我們就假定用JAVA寫了Web應用,怎么部署上去呢?先配置Tomcat服務器,在把編譯好的war包上傳到服務器,有用FTP的,安全意識高一點的會選SCP,然后配置Nginx、MySQL這些服務,最后一通調試,把應用跑起來,就算齊活。
這種物理機配合自搭網絡環境、自搭Linux、自配環境的方式,有很多缺點,但最主要的問題有這么幾個:
- 擴容和維護難,因為是物理機,需要先采購后跑機房,遇到雙十一業務量猛增是來不及擴容的,用行話講就是計算資源缺乏彈性;
- 安全性太差,熟悉Linux的運維工程師本來就少,精通SELinux的更是鳳毛麟角,而且,系統安全性僅僅是一個方面,應用部署的權限、流程造成的安全漏洞更大;
- 部署流程不規范,開發、測試和運維脫節,缺少自動化測試和部署,無法快速迭代業務。
解決方案是上云。上云不能解決所有問題,但部分解決了前兩個問題:
- 云服務商提供的是虛擬機,比起物理機,虛擬機的創建、維護、銷毀比物理機簡單了太多,且隨時可以擴容,很大程度上解決了“彈性計算”的問題,至于彈性程度有多大,還得看應用的架構和設計水平;
- 和絕大多數中小企業相比,云服務商在網絡和服務器安全方面要強若干個數量級。只要選擇合適的官方鏡像,配合防火墻規則,系統級別的安全問題大大減少,可以將重點放到應用本身的安全性上。
但是如果僅僅滿足上云,把物理機換成虛擬機,把物理網換成虛擬專用網(VPC),是遠遠不夠的。這些是計算資源和網絡資源層面的簡化。應用方面,如果延續舊的一套開發、測試、部署流程,做不到快速迭代。
要做到快速迭代,敏捷開發,就需要DevOps,即開發運維由一個團隊負責,開發階段,就要把部署、運維的工作考慮進去,而不是發布一個war包或者jar包后扔給運維不管了。
重開發、輕部署,直接后果就是缺少自動化發布流程。想要把部署規范化,就需要整體考慮一系列問題。
還是以Java應用為例,如果是手動部署,那么就上傳war包到服務器,覆蓋原有的版本,重啟Tomcat,再測試。如果發現有嚴重問題要回滾怎么辦?把老版本再傳一遍,然后重啟Tomcat。
手動部署,每次部署都是一次生死考驗,所以最好不要安排在半夜,以免手抖敲錯了命令,本來中斷10分鐘的服務,變成了災備恢復,中斷3天。
稍微靠譜一點的是寫腳本部署,但各家寫出來的腳本都有各家特色,不通用,不易維護,所以更好的方式是用成熟的部署方案,比如Ansible,把腳本標準化,比如做成藍綠發布,把服務器分組,比如A、B兩組,先把流量切到B組,升級A組服務器,有問題就回滾,沒問題了,再把流量切到A組,升級B組服務器,最后,恢復正常流量,整個部署完成。
但是回滾這個事情,遠沒有那么簡單。做過開發的同學都知道,升級新版本,一般要加配置,改配置,如果回滾到舊版本,忘了把配置改回去,那舊版本可能也不能正常工作。
上云,除了物理變虛擬,簡化運維外,最重要的特點——彈性計算,一定要充分利用。
理論指導實踐,實踐完善理論。如果我們分析大多數基于互聯網的應用,可以看到,一個應用,通常用到的資源如下:
- 存儲資源,通常對應著一個或多個數據庫,例如MySQL、Oracle、PostgreSQL等;
- 計算資源,以Java應用為例,就是一個或多個Web應用,跑在Tomcat,或者通過SpringBoot自帶嵌入式服務器;
- 網關,通常是Nginx之類的服務器,對外作為統一的服務入口,對內提供反向代理;
- 其他支撐業務的組件,例如,為提升應用性能采用的redis集群作為緩存,應用內部各組件通信使用的消息系統如Kafka等,以及隨著業務不斷擴大增加的ES集群、日志分析和處理的集群等。
上云后,云服務商通常都提供托管的數據庫,以及大規模存儲系統(S3),可解決存儲資源問題。通過云服務商提供的負載均衡(Load Balancer),也無需自行部署Nginx等網關,免去了運維的問題。各種標準的業務組件如Redis、Kafka等,均可直接租用云服務商提供的資源。
我們重點討論計算資源,也就是云上的虛擬機資源。對于應用來說,可以設計成有狀態和無狀態兩種。一個應用在一臺虛擬機內跑著,如果有本地文件的修改,它就是有狀態的。有狀態的應用既不利于擴展,也不利于部署。反過來,如果一個應用在運行期數據總是存在數據庫或者緩存集群,本地文件無任何修改,它就是無狀態的。
無狀態的應用對應的虛擬機實際上就是不變的計算資源。這里的“不變”非常重要,它是指,一臺虛擬機通過一個固定的鏡像(預先內置好必要的支持環境,如JRE等)啟動后,部署一個應用(對應一個war包或者jar包),該虛擬機狀態就不再變化了,直接運行到銷毀。
有的同學會問:如果給這臺虛擬機重新部署一個新的應用版本,它的狀態不就發生了變化?
確實如此。為了確保虛擬機的不變性,一旦啟動后部署了某個版本,就不允許再重新部署。這樣一來,對虛擬機這種計算資源來說,就具有了不變性。不變性意味著某個虛擬機上的應用版本是確定的,與之打包的配置文件是確定的,不存在今天是版本1,明天變成版本2,后天回滾到版本1的情況。
計算資源不變,能確保啟動一臺虛擬機,對應發布的應用版本和配置是確定的且不變的,對于運維、排錯非常重要。
那么如何在保持計算資源不變的前提下發布新版本?
我們以AWS的CodeDeploy服務為例,假設一組正在運行的某應用v1集群包含3臺虛擬機:
現在,我們要把應用從v1升級到v2,絕不能直接把現有的3臺虛擬機的應用直接升級,而是由CodeDeploy服務啟動3臺新的一模一樣的虛擬機,只是部署的應用是v2。現在,一共有6臺虛擬機,其中3臺運行v1版本,另外3臺運行v2版本,但此刻負載均衡控制的網絡流量仍然導向v1集群,用戶感受不到任何變化:
v2集群部署成功后,做一些自動化冒煙測試和內網測試,如果有問題,直接銷毀,相當于本次部署失敗,但無需回滾。如果沒有問題,通過負載均衡把流量從v1集群切到v2,用戶可無感知地直接訪問v2版本:
穩定一段時間(例如15分鐘)后,銷毀v1集群。至此,整個升級完成。
上述的藍綠部署就是CodeDeploy的一種標準部署流程。CodeDeploy也支持灰度發布,適用于更大規模的應用。
把計算資源不可變應用到部署上,實際上是充分利用了彈性計算這個優勢,短時間創建和銷毀虛擬機,只有上云才能做到,并且針對云計算,把部署流程變得更加簡單可靠,每天發幾個版本甚至幾十、幾百個版本都變得可能,DevOps能落地,有點“云原生”的味道了。
說到AWS的CodeDeploy,最早我使用AWS時,對于它的計費采用Reserved Instance預付模型感到很不理解,租用一臺虛擬機,按國內阿里云、騰訊云包年包月預付享折扣不是更直觀嗎?如果僅僅把上云變成租用虛擬機,那就完全喪失了彈性計算的優勢,相當于租用了一臺虛擬機在里面自己折騰。AWS的Reserved Instance計費并不綁定某一臺虛擬機,而是一種規格的虛擬機。
我們還是舉例說明,如果我們有1臺2v4G規格的虛擬機,并購買了1年的Reserved Instance,那么,我隨時可以銷毀這臺虛擬機,并重新創建一臺同樣規格的新的虛擬機,Reserved Instance計費會自動匹配到新的虛擬機上,這樣才能實現計算資源不變,頻繁實施藍綠部署,真正把部署變成一種云服務。最近阿里云終于推出了節省計劃的付費模式,有點真正的云計算的付費味道了,但是騰訊云、華為云還停留在包年包月和按量付費這兩種原始租賃模型。
講了這么多自動化部署,實際上一個指導思想就是如何充分利用云的彈性計算資源。從充分利用云的彈性資源為出發點,設計一整套開發、部署、測試的流程,就是云原生。彈性資源利用得越充分,云原生的“濃度”就越高,就越容易實施小步快跑的快速迭代。
那么虛擬機是不是彈性最好的計算資源呢?從應用的角度看,顯然容器是一種比虛擬機更具彈性,更加抽象,也更容易部署的計算資源。
容器和虛擬機相比,它實際上是一種資源隔離的進程,運行在容器中的應用比獨占一個虛擬機消耗的資源更少,啟動速度更快。此外,容器的鏡像包含了完整的運行時環境,部署的時候,無需考慮任何額外的組件,比其他任何部署方式都簡單。使用容器,開發部署流程就變成了開發,生成鏡像,推送至Docker Hub或云服務商提供的Registry,直接啟動容器,整個過程大大簡化。
使用容器比使用CodeDeploy部署還要更加簡單,因為CodeDeploy需要在虛擬機鏡像中預置Agent,由于沒有統一的發布標準,還需要配置CodeDeploy,告訴它去哪拉取最新版本,這又涉及到一系列權限配置。而容器作為標準的部署方案,連發布系統都以Registry對各個鏡像版本進行了有效管理,所以部署非常簡單。
容器作為一種彈性計算資源,也應遵循計算不變性,即不要給容器掛載可變的存儲卷。一組不變的容器集群才能更容易地升級。容器的運行方式本身就遵循了不變性原則,因為通過一個鏡像啟動一個容器,在運行過程中,是不可能換一個鏡像的。容器本身也強烈不建議應用寫入數據到文件系統,因為重啟后這些修改將全部丟失。
容器的啟動和銷毀非常容易,不過,容器的管理卻并不簡單。容器的管理涉及到創建、調度、彈性擴容、負載均衡、故障檢測等等,Kubernetes作為事實上的容器編排標準平臺,已經成為各個云服務商的標配。
如果要直接使用K8s,在云環境中首先要有一組虛擬機資源作為底層資源,然后搭建K8s環境,定義好容器編排并啟動容器。云服務商幾乎都提供托管的K8s服務,但直接管理K8s仍然需要非常熟悉K8s的工程師。
還有一種更簡單的使用容器的方式,即完全將底層虛擬機和K8s托管給云服務商,企業客戶只需關心如何部署容器,底層的K8s和虛擬機對企業不可見或者無需關心。AWS的Elastic Container和阿里云的彈性容器均為此類服務。對于中小規模的應用來說,計算資源直接使用容器,再配合云服務商提供的負載均衡,托管的數據庫、消息系統、日志系統等組件服務,應該是目前最“云原生”的一種方案。
最后,我們總結一下云原生的特點:
所謂云原生,就是在上云的過程中,充分發揮云平臺的彈性計算、彈性存儲的優勢,盡量把應用設計成適合云計算的架構,把部署設計成簡單易用的流程,這樣才能實現業務快速上線,快速迭代。
云原生是一個大方向,在上云的過程中,逐步改造應用架構和部署流程,從手動往自動轉,逐步增加計算資源的彈性,就能把云原生一步步落地。