為增加本文的代入感,本譯文將以原作者第一稱視角闡述Jesse Newland的經歷,請讀者知悉。
去年,GitHub已經改進了Ruby on Rails應用的基礎設施,該應用負責運行github.com和api.github.com。最近我們實現了一個重要里程碑,即:所有Web和API請求都由Kubernetes集群中運行的容器來處理,這些集群都部署在了我們的metal云上。將一個重要的應用遷移到Kubernetes是一個非常有趣的挑戰,所以我們非常激動可以向你分享我們學到的東西。
為什么要改變?
在做出這次遷移之前,我們主要的Ruby on Rails應用(我們稱之為github/github)的配置和它8年前沒什么兩樣:即由一個名叫God的Ruby進程管理器管理著Unicorn(獨角獸)進程,而該Ruby進程管理器則運行在受Puppet管理的許多服務器上。類似地,chatops部署的工作方式和它剛被引入時差不多:即Capistrano與每個前端服務器建立SSH連接,然后更新代碼并重啟應用進程。當峰值請求負載超過可用的前端CPU能力時,GitHub網站可靠性工程師會分配額外的能力并添加到有效的前端服務器池中。
雖然我們基本的生產方式這些年并未改變多少,但GitHub本身卻發生了巨大的變化,包括:新特性、更大的軟件社區、更多的GitHub用戶以及更高的每秒請求數。隨著我們不斷地發展,這一方式開始出現問題。許多團隊希望將其負責的功能從這個龐大的應用中提取到一個更小的服務中,以便該服務可以單獨運行或部署。隨著運行的服務量增大,網站可靠性工程師團隊開始讓幾十個其它的應用支持與前面提到的配置相類似的配置。這增加了我們在服務器維護、分配和其它工作上花費的時間,而這些工作與改善GitHub整體的體驗并無直接關系。新服務往往需要幾天、幾周甚至幾個月的時間來部署,這取決于其復雜程度和網站可靠性工程師團隊是不是有空。隨著時間的推移,我們發現,這一方式不能給工程師帶來足夠的靈活性,以使他們繼續打造世界級的服務。工程師們需要一個可以讓他們實驗、部署和擴展新服務的自助服務平臺。同時我們也需要這樣的平臺來滿足核心Ruby on Rails應用的需求,從而使工程師或機器人能在幾秒鐘之內(而不是幾小時、幾周甚至更長時間)分配額外的計算資源,從而應對需求上的變化。
為了應對這些需求,網站可靠性工程師、平臺和開發者體驗團隊啟動了一個共同的項目,在這個項目中,我們最開始只是對容器編排平臺進行評估,而到今天,我們已取得這樣的成就,即:每天能將支撐github.com和api.github.com運行的代碼往Kubernetes集群部署幾十次。本文旨在概述一下這個過程中涉及的工作。
為什么是Kubernetes?
作為對“平臺即服務”工具當前局勢評估的一部分,我們對Kubernetes做了近距離考查。Kubernetes是一個谷歌的項目,它自稱是一個開源的系統,用來自動部署、擴展和管理容器化的應用。Kubernetes一些優點使它從眾多平臺中脫穎而出,例如:支撐該項目的活躍的開源社區、首次運行的體驗(這使我們能夠在最初實驗的最初幾個小時內部署小型的集群和應用)、以及大量與刺激其設計的體驗有關的信息。
這些實驗的范圍迅速擴大:我們組建了一個小項目,來構建Kubernetes集群和部署工具,以便在接下來的黑客(hack)周獲得一些有關該平臺實際的體驗。我們在這個項目的體驗以及使用過它的工程師的反饋都極其正面。是時候將實驗擴大了,所以我們開始計劃一個更大的上線活動。
為什么要從github/github開始?
在本項目的初期,我們做了一個慎重的決定,那就是將遷移的目標設定為某個關鍵的負載:即github/github。許多因素促使我們做出這個決定,但其中尤為突出的是:
我們知道,貫穿GitHub深入了解這個應用會對整個遷移過程有幫助。 我們需要自助服務容量擴展工具來應對持續性的增長。我們需要確保我們養成的習慣和模式不僅適合大型應用,還同樣適合小規模服務。我們希望更好地讓應用不受開發、過渡、生產、企業和其它環境的差異所影響。我們知道,成功遷移一個關鍵且高度引人注目的負載可以鼓勵在GitHub更大范圍地采用Kubernetes。考慮到選擇遷移的負載的關鍵性,我們需要在引入任何生產流量之前,樹立高度的運營信心。
通過審查實驗室快速迭代和樹立信心
作為此次遷移的一部分,我們使用了如Pods、Deployments和Services之類的Kubernetes基本單元設計了目前前端服務器所提供的服務的替代品,制作了其原型并進行了驗證。這個新設計的某些驗證可以通過在容器內運行github/github現有的測試套件完成,而不是在同前端服務器配置相似的服務器上完成。然而,我們仍然需要觀察這個容器作為大量Kubernetes資源的一部分有怎樣的表現。很快我們就清楚發現,我們必須得有一個環境,能夠支持對Kubernetes和我們要運行的服務進行探索性測試。
大約在同一時間,我們發現,現有的github/github pull request的探索性測試模式已經開始出現痛點增長的跡象。隨著部署速度以及項目工作的工程師的數量的增加,使用幾個額外的部署環境來驗證向github/github發出pull request的頻率也在增加。通常,在峰值工作時間內,少量功能完備的部署環境已經被預定了,這就減緩了部署pull request的過程。工程師經常要求能夠在“分支實驗室”上測試更多的生產子系統。雖然分支實驗室允許許多工程師進行并發部署,但它只給每個部署單獨啟動了一個Unicorn進程,這意味著“分支實驗室”僅在測試API和UI變化時才有用。這些需求重疊的很多,所以我們將這些項目結合起來,并采用了一個由Kubernetes專為github/github開發的新部署環境,名為“審查實驗室”。
在構建審查實驗室的過程中,我們交付了不少子項目,每個子項目都可以在各自單獨的博客文章中進行介紹。前前后后,我們共交付了:
一個在AWS VPC中運行的Kubernetes集群,該VPC由Terraform和kops共同管理。一系列短暫運行Kubernetes集群的Bash集成測試。在項目初期我們大量使用這些測試,以樹立對Kubernetes的信心。github/github的Dockerfile文件。增強內部CI平臺,以支持將容器構建和發布到容器注冊表。50個Kubernetes資源的YAML陳述,它們檢查后被加入到github/github。增強內部部署應用,以支持將Kubernetes資源從存儲庫部署到Kubernetes命名空間,以及從內部secret存儲庫創建Kubernetes secrets。一個將haproxy和consul-template組合在一起的服務,以便將Unicorn pod的流量路由到現有服務,并在那里發布服務信息。一個讀取Kubernetes事件并將異常事件發送到內部錯誤跟蹤系統的服務。一個名為kube-me的兼容chatops-rpc的服務,它會通過聊天向用戶公開一組有限的kubectl命令。最終成果是一個基于聊天的界面,它可以為任何pull request創建GitHub的單獨部署。一旦pull request通過了所有必需的CI作業,用戶就可以將該pull request部署到審查實驗室,如下所示:
jnewland
.deploy https://github.com/github/github/pull/4815162342 to review-lab
用戶說:部署https://github.com/github/github/pull/4815162342 到審查實驗室
Hubot
@jnewland's review-lab deployment of github/add-pre-stop-hook (00cafefe) is done! (12 ConfigMaps, 17 Deployments, 1 Ingress, 1 Namespace, 6 Secrets, and 23 Services)(77.62s) your lab is available at https://jnewland.review-lab.github.com
答復用戶:@ jnewland的審查實驗室部署github/add-pre-stop-hook(00cafefe)已經完成! (共12個ConfigMap、17個Deployment、1個Ingress、1個Namespace、6個Secrets和23個Service)(用時77.62秒),點擊https://jnewland.review-lab.github.com 即可使用。
像分支實驗室一樣,實驗室在上次部署之后的一天內即被清理干凈。由于每個實驗室都是在自己的Kubernetes命名空間中創建的,因此清理命名空間與刪除命名空間一樣簡單,部署系統在必要時會自動執行這些動作。
審查實驗室是一個成功的項目,產生了一些積極的結果。在將該環境普遍開放給工程師之前,它充當Kubernetes集群設計的基本試驗場和原型環境,以及Kubernetes資源設計和配置的基本試驗場和原型環境,現在github/github Unicorn工作負載由Kubernetes資源來描述。發布后,大量工程師被迫適應新的部署風格,一些感興趣的工程師提供了反饋,而一些沒有注意到任何變化的工程師仍在正常使用,這使我們逐步建立了信心。就在最近,我們觀察了高可用性團隊中一些工程師使用審查實驗室的情況,他們使用審查實驗室來實驗與Unicorn的互動,并通過將某個新實驗子系統部署到共享實驗室來實驗該子系統的表現。這種環境能使工程師以自助服務的方式實驗和解決問題,我們對此感到非常高興。
每周向分支實驗室和審查實驗室的部署量
Metal上的Kubernetes
隨著審查實驗室的交付,我們的注意力轉移到了github.com。為了滿足旗艦服務的性能和可靠性要求(旗艦服務依賴于低延遲來訪問其它數據服務),我們需要構建Kubernetes基礎設施來支持在物理數據中心和POP中運行的metal云。這次的工作又有近十幾個子項目:
這篇關于容器聯網的及時而全面的帖子幫助我們選擇了Calico網絡提供商,它提供了我們在ipip模式下快速交付集群所需的開箱即用功能,同時讓我們之后能夠靈活地與網絡基礎設施進行對等互連。經過對@kelseyhightower的“艱難但必不可少的Kubernetes”的不少于十幾次的拜讀,我們將一些手動分配的服務器組裝到了某臨時的Kubernetes集群中,該集群通過了與我們用來鍛煉AWS集群相同的一套集成測試。我們構建了一個小工具,來為每個集群生成CA和配置,它們的格式可被內部Puppet和secret系統使用。我們Puppet化了兩個實例角色(Kubernetes節點和Kubernetes apiservers)的配置,并允許用戶提供已配置的集群的名稱于分配時加入。我們構建了一個小型Go服務來使用容器日志,并將元數據按鍵/值的格式附加(append)到每一行,并將它們發送到主機的本地syslog端點。我們加強了GLB,即內部負載均衡服務,以支持Kubernetes NodePort服務。所有這些努力產生了一個集群,并通過了我們的內部驗收測試。鑒于此,我們相信,同一套輸入(即由審查實驗室使用的Kubernetes資源)、同一組數據(即通過VPN與審查實驗室連接的網絡服務)以及同樣的工具會產生類似的結果。在不到一周的時間內,即便其中大部分時間用于內部溝通和對那些對遷移有重大影響的事件進行排序,我們仍能將整個工作負載從運行在AWS上的Kubernetes集群遷移到數據中心內的Kubernetes集群。
提高信心
憑借在metal云上搭建Kubernetes集群的成功和可重復的模式,現在我們可以對用Unicorn部署替代當前前端服務器池的能力充滿信心了。在GitHub,工程師及其團隊通常會通過創建Flipper特性來驗證新功能,然后在其可行后立即選擇該功能。在加強部署系統后,我們將一套新的Kubernetes資源部署到與現有的生產服務器并行的github-production命名空間,并加強GLB,以支持通過受Flipper影響的cookie將員工請求路由到不同的后端。因此員工可以通過在任務控制欄中選擇某按鈕進入Kubernetes實驗后端:
內部用戶的負載幫助我們找到問題、修復錯誤,并在生產中開始習慣Kubernetes了。在此期間,我們通過模擬將來要執行的程序、編寫運行手冊和執行故障測試來努力提高信心。我們還將少量生產流量路由到此集群,以確認我們對負載下的性能和可靠性的假設。我們從每秒100個請求開始,并將其擴大到github.com和api.github.com10%的請求。經過這些模擬后,我們暫停了一下,重新評估了全面遷移的風險。
集群組
幾個故障測試產生了始料未及的結果。尤其是,一個模擬單個apiserver節點的故障的測試對運行的工作負載的可用性產生了負面影響,從而中斷了集群。對這些測試結果的調查并沒有產生確鑿的結果,但是幫助我們確定中斷可能與連接到Kubernetes apiserver的各種客戶端之間的交互有關(如calico-agent,kubelet,kube-proxy和kube-controller-manager)和確定內部負載均衡器在apiserver節點故障期間的行為。鑒于觀察到Kubernetes集群降級可能會中斷服務,我們開始考慮在每個站點的多個集群上運行旗艦應用,并自動將請求從不正常的集群轉移到其他正常集群。
我們的路線圖已包含類似的工作,以支持將此應用部署到多個獨立運行的站點。同時,這種方法其它積極的折衷 - 包括為低中斷集群升級提供可行的故事、將集群與現有故障域(如共享網絡和電源設備)相關聯 - 支持我們繼續走這條路線。我們最終決定使用某個設計,它利用了部署系統支持部署到多個“分區”的功能,并且通過自定義的Kubernetes資源注釋來增強該設計,以支持集群特定的配置。所以我們放棄了現有的聯合解決方案,轉而使用另一個方法,因為該方法能夠利用已經存在于部署系統中的業務邏輯。
從10%到100%
隨著集群組的實施,我們逐漸將前端服務器轉換為Kubernetes節點,并增加路由到Kubernetes的流量百分比。除了一些其他負責的設計 團隊,我們在短短一個多月內完成了前端轉型,同時將性能和錯誤率保持在目標范圍內。
Web請求百分比:Kubernetes/github-fe (%)
在這次遷移期間,有個問題一直延續到今天:在高負載和/或高比率容器抖動的時候,一些Kubernetes節點會引起內核崩潰和重啟。雖然我們對此不滿意,并且正在繼續高度重視和調查該問題,但讓我們高興的是,Kubernetes能夠自動繞過這些故障,并繼續在目標錯誤率范圍內提供流量。我們已經通過echo c> / proc / sysrq-trigger執行了一些模擬內核崩潰的故障測試,而且發現這可以作為我們故障測試模式的有用補充。
接下來該做什么?
將此應用遷移到Kubernetes,我們深受鼓舞,并期待后續做更多的遷移。雖然我們故意將首次遷移的范圍限定于于無狀態的工作負載,但能夠在Kubernetes上嘗試運行有狀態的服務仍令人激動不已。
在本項目的最后階段,我們還交付了一個工作流程,用于將新的應用和服務部署到類似的Kubernetes集群組中。在過去幾個月中,工程師已經將數十個應用部署到了這個集群。以前,這些應用中每一個都需要網站可靠性工程師進行配置管理和分配支持。通過自助服務應用分配工作流程,網站可靠性工程師可以將更多的時間投入到為組織其它部門提供基礎設施產品,以支持我們的最佳實踐,以及為每個人提供更快、更有彈性的GitHub體驗。
鳴謝
我們衷心感謝整個Kubernetes團隊提供的軟件、文字和指導。我也要感謝以下GitHub用戶在本項目上所做出杰出貢獻:
和我們一起工作!
想幫助GitHub網站可靠性工程師團隊解決像這樣有趣的問題嗎? 歡迎你加入我們。請通過這里申請!