好未來學而思網校如何實現1小時內發佈一個新項目

背景

在互聯網高速迭代的浪潮下,相信每位研發人員都經歷過新項目的需求研發、測試驗收、部署上線、功能迭代、甚至項目下線的全過程。其中“需求研發”、“測試驗收”、“功能迭代”、“項目下線”這類環節對於研發人員來說,基本上可以有效的把控,在整個項目的生命週期內,也是投入最多的幾個環節,因此可以提前預估出合理的時間計劃。

這幾個關鍵的環節中,將項目部署上線,往往是最後一個環節。對項目組成員和需求方來說,軟件研發完成以後,投入生產環境,是一個水到渠成的事情,需要立馬完成。

然而在網校大背景下,一個新項目投入到生產環境是一個非常嚴肅的事情,實際上需要經歷的幾個環節如下:

  1. 新域名備案(準備必要的資料)
  2. 服務器申請(機房選擇、單機硬件配置、系統選型、網段規劃、機器命名、容量預估)
  3. 服務器生產(必要軟件初裝、參數校對)
  4. 研發授權(準備dev or guest人員名單、準備機器標籤進行批量授權)
  5. 投入發佈系統(創建發佈項目、填充機器信息、項目人員分工)
  6. 配置網關(集羣選擇、vhost配置、upstream配置)
  7. 域名解析(是否經過全站加速/高防等防護產品、提供解析地址生效)
  8. 若是容量預估不足,還需要再經歷一遍#2~#6(多批生產的機器存在環境配置不一致的隱患)
  9. 如果遇到項目下線,往往域名又被容易忽略掉,造成一些無用且已經備案的域名,不易管理

整個過程最快也要以天爲單位,隔天交付生產;慢的話要數天才能完成生產環境的部署,並且需要跨多個部門協同完成。2020年公益直播高峯前夕,“連軸轉”完成服務擴容的夥伴,尤其銘心刻骨。

因此,爲了解決新項目部署週期長的問題,基礎架構部啓動了兩個項目:《域名收斂》、《容器平臺》。

《域名收斂》項目,主要爲了縮短:#1、#7、#9這3個環節上消耗的時間和精力。

《容器平臺》項目,主要爲了縮短:#2、#3、#4、#5、#6、#8這6個環節上消耗的時間和精力。

並且現在兩個項目已經無縫結合,在同一個平臺上完成。

下面將詳細的介紹,我們如何給開發完成的新項目,在1小時內發佈上線提供便利,實現小時級交付生產,以及當前的項目應用規模。

容器平臺

物理機與虛擬機時代整個新服務上線流程嚴重依賴機房資源、運維部署、多個系統的環境配置、以及性能壓測。

漫長的處理流程

資源申請流程 -〉 資源採購 -〉 機器授權 -〉 服務運行環境配置(環境、網絡) -〉 配置發佈系統 -〉 配置網關/日誌 -〉 性能壓測

硬件資源不足時,需要排隊採購審批,到資源上架初始化過程可能長達數月,漫長的處理流程非常打擊開發人員的熱情。

在資源充足的時候,也需要數天的處理流程。我們需要去解決這些漫長的流程的問題 ,在互聯網時代敏捷性決定了業務的生與死。

我們是如何做到便捷性的呢?

  1. 通過容器的環境一致性
  2. 網校雲的統一調度硬件資源使我們能有效盤活有效固定資產池
  3. 通過打通網關、日誌體系,每位研發老師完全自助的完成所有配置。

應用快速發佈

驅動於git devops的設計,用戶只需要提供git倉庫地址及項目域名,平臺會調用pipeline引擎構建鏡像,通過配置基於git事件的觸發全自動構建鏡像,然後按照模板創建 deployment,service,及ingress的kubernets等應用及資源。流水線作業設計,一氣呵成完成應用發佈與鏡像構建。

與網關進行了自動化打通, 應用部署完成後我們將集羣的網關ingress的地址提交給網關, 觸發網關的upstream更新操作,從而實現了服務的發現以分流。

快速發佈.png

同時與公司發佈系統進行打通,在用戶發佈完應用後,可以獲得一個hook地址,發佈系統設置hook後,在發佈的時候出發k8s的滾動升級,以實現混合部署應用的同步升級。

雲原生的PIPELINE引擎

gitlab事件觸發充分借鑑了K8S 自帶的CI/CD系統Prow的系統設計,實現gitlab的自動事件通知支持:

  1. Tag事件
  2. Push事件
  3. Merge事件

引擎通過向註冊相關事件並監聽, 當事件到來時會根據事件類型分發事件給相應的插件來處理。已經實現了和網校雲容器平臺的無縫連接, 通過在簡單的web配置就可以構建出基於指定Tag或指定Commit的鏡像。

引擎完全運行在容器之上,多插件和PIPELIE、靈活多變的設計讓應用的發佈不會拘泥於特定的運行與構建環境,助力新項目的新環境的快速發佈於迭代。

彈性擴容

以基於horizontal pod autoscalers實現的 集羣應用的自動彈性伸縮, 根據CPU使用量指標, metrics server從kubelet中的cAdvisor組件中獲取cpu的資源消耗,默認情況下,每10s採樣一次,每30s計算一次cpu使用量。當業務高峯來臨, 監控到pod的CPU佔用量超過預定的閾值, 會自動增加服務的副本數量(在設定範圍),反之會減小副本數量,釋放集羣資源,由此實現集羣資源的高效利用。

hpa.png

krt.png

自動化日誌收集

我們採用了filebeat作爲日誌採集工具。

  1. filebeat以daemon set模式運行,保證了每個節點都會有一個filebeat容器運行。
  2. filbeat容器可以採集宿主機以及該宿主機上其他容器的日誌。
  3. 可以在我們的容器平臺,選擇需要收集日誌的應用,即可完成日誌採集。
  4. 通過configMap(配置中心)的方式,對採集配置進行管理

因爲我們已經和網校的日誌中心打通,我們所採集的日誌將均被採集到kafka中,然後落地到日誌中心ES集羣。

域名收斂

一、爲什麼要做域名收斂

隨着網校用戶體量飛速發展,業務越來越多樣化,複雜化,各服務之間的耦合度也越來越高,依賴嚴重,相互影響。爲了減輕依賴,各項目都希望有獨立域名來提供服務,業務方也開始頻繁申請新域名,使網校對外暴露的域名越來越多。這種做法逐漸造成了網校域名氾濫現象。據不完全統計,截止2019年年中,網校申請的域名有數千個,而且以每週數十的速率增長,其中經過網關的域名就已經破千,而且很多域名已經處於無人認領的狀態,沒有人敢確認這些服務是否在線上running,因此也不敢輕易去修改或下線。除此之外,域名的泛濫成災也給網校的研發與運維帶來了很多痛點問題:

  1. 研發人員:
  • 上線困難: 域名申請、域名備案、域名解析、網關創建應用、vhost、upstream、申請後端機器、後端nginx+fpm配置、發佈代碼。週期長(1周~1月),流程冗長,效率極低,研發人員將大量精力耗費在項目上線上。
  • 開發困難: 無論是app、H5、web等客戶端,開發需要適配不同域名的不同接口,代碼臃腫,難以維護,單app端依賴的接口就有涵蓋了近百域名,開發人員已經不堪重負。
  1. 運維人員:
  • 管理維護: 上千的域名維護管理困難,只增不減,很多域名沒有訪問量,無人維護,無人認領。
  • 配置困難: 運維對接各種開發配置需求,造成網關層配置毫無規範,複雜冗長,頻繁修改,出錯率高,風險性大。
  • 安全問題: 暴露域名過多降低了系統的安全性,大量域名需要備案審計、安全掃描,也暴露出了很多安全漏洞。

2020年2月網校公益直播期間,由網校技術委員會發起的‘競品分析’專項進行了多項技術評測指標,其中在服務端域名管控上,我們實施的力度遠遠不及對手,網校的研發和運維人員還沒有形成域名收斂的意識,不僅沒有制定相關的規範文檔,也沒有對應的技術手段實現域名複用。

針對以上這些痛點問題,網校服務端技術委員會、運維團隊、集團安全組攜手發起了《網校域名收斂專項》,項目目的是要解決網校域名氾濫的問題,一方面要對現有的域名進行收斂與規範,另一方面對後續新上線項目流程進行調整與優化,加速推進項目的自動化與標準化上線流程,爲網校雲的建設鋪路。

二、如何實現域名收斂

2.1 域名規劃

那麼該如何實現域名收斂呢?首先我們將網校的服務大致分爲進行了分類,大致如下:

  • 按照服務類型分類:WEB服務與API服務,前者輸出的是html,後者輸出的是json數據。

  • 按照服務範圍分類: 對內服務與對外服務,前者只提供內網或者辦公區服務,後者提供公網服務。

如此一來,就有四種類型的服務: 對外WEB,對內WEB, 對外API,對內API。

因此我們設計了四個域名與之對應: app.xueersi.com; app.xesv5.com; api.xueersi.com;api.xesv5.com; 這四個域名的作用如下。

  • app.xueersi.com : 主要代理對外WEB應用,面向公網用戶,例如站點首頁。
  • app.xesv5.com : 主要代理對內WEB應用,面向內部用戶,例如admin管理後臺,各種監控平臺,告警平臺等對內系統。
  • api.xueersi.com : 主要代理對外API服務,例如app,ios,pc端調用的api服務,阿里雲、微信等第三方回調api。
  • api.xesv5.com : 主要代理內部api服務,例如用戶數據等中臺類api接口。

收斂域名的作用

2.2 方案制定

2.2.1 服務標籤化

以網校的站點架構特點來說,幾乎所有的服務都由統一的Nginx網關進行反向代理,一個域名對應一個vhost與upstream,通過Nginx的proxy_pass進行路由轉發。爲了實現域名收斂,我們決定對網關層進行技術改造,實現基於收斂域名的動態路由分發,所謂的收斂域名就是指上面提到的四個專用域名app.xueersi.com,app.xesv5.com,api.xueersi.com, api.xesv5.com, 除特殊情況外,儘量讓所有的服務都能夠複用這四個收斂域名(也可以叫做網關的專用’代理域名’)。

如果大家都共用同一個域名,怎麼識別不同的服務呢? 很簡單,根據url來識別, 我們會將url中第一個’/'後面的字段作爲每個項目的標籤,網關層會根據"標籤"識別不同服務,比如新上線一個學習應用,想使用study.xueersi.com, 則業務方可以拿"study"作爲項目標籤,同時複用app.xueersi.com這個域名,當用戶訪問http://app.xueersi.com/study/course/getInfo?a=1時,網關會通過改寫host,upstream,uri來實現以下轉發規則:

  • 改寫upstream:從proxy_pass http://app.xueersi.com 改寫成 proxy_pass http://study.xueersi.com
  • 改寫host : 從 app.xueersi.com 改寫成 study.xueersi.com
  • 改寫uri : 從 /study/course/getInfo?a=1 改寫成 /course/getInfo?a=1

此時對於後端服務來說,用戶請求的是http://app.xueersi.com/study/course/getInfo?a=1,經過網關轉化後,後端真正接收到的請求爲: http://study.xueersi.com/course/getInfo?a=1

同理,其他服務也是類似,複用同一域名,獨佔一個標籤。

2.2.2 上線流程簡化

在服務標籤化之後,服務的暴露形式就從‘域名’依賴轉換成了‘標籤’依賴,那麼對於研發人員則省去了域名申請,域名解析,網關創建vhost等步驟,以部署在kvm的服務爲例,上線流程簡化爲:

image20191225_20589.png

其中開發需要做的只有兩件事情:

  • 域名備案 :從立項到上線之間任意時間備份即可(安全部門硬性要求,公網域名必須備案,私網域名可以不用)。
  • 創建upstream: 通過網關後臺創建即可。

對於接入了網校雲的服務,依賴於k8s的容器運行環境,這種服務的上線會更快,網關已經和k8s進行了打通,只需要在k8s上進行應用創建,選擇好對應的網關集羣,一鍵切流即可上線一個新項目。

2.3 技術實現

從上述的技術方案中不難看出,實現的關鍵點在於網關需要對每一個以收斂域名打頭的請求,按照url進行切割,提取出來請求的"標籤",同時改寫請求的host、upstream、uri,於是在網關層我們開發了dyroute插件實現了上述功能。該插件不僅支持host,url,header,body,ip,referer,cookie等十幾種篩選條件,也支持URL的多段分割和提取,可以按需對host、upstream、uri進行定製。

三、問題與解決

理想很豐滿,現實很骨感。真正推進域名收斂的過程,我們遇到了很多棘手的問題,這裏記錄了主要的幾個。

3.1 path池

按照網關的這套轉換規則會存在一個漏洞,以 app.xueersi.com 爲例,如果有人請求 app.xueersi.com/A 則會間接的訪問到 A.xueersi.com,訪問 app.xueersi.com/B 則會訪問到 B.xueersi.com,也就是他可以任意試探訪問我們的不通服務。你可能會問,app.xueersi.com 的設計初衷不就是代理公網服務嗎?就算試探中了也沒有關係,本來就是面向公網用戶的。網校的域名有一個"不成文"的規則,就是 .xueersi.com 都是對外的,.xesv5.com 都是對內的,但是由於歷史原因,很多域名並沒有準守這套規則,例如 oa.xueersi.com 本來應該是對內的OA系統,但是卻用了 .xueersi.com 的對外域名。如果有用戶通過 app.xueersi.com/oa 發送請求,則可以毫無阻攔的訪問到我們對內的系統。同理,對於本來對外確用了 .xesv5.com 的服務,也會有類似的問題。

針對此問題,我們設計了path池機制,每一個收斂域名需要和一個path池進行綁定,我們會在創建upstream的時候把對應的標籤加入到path池,只有加入到了對應path池的url才能正常通過網關,其他胡亂試探的/A,/B,/C的請求會被網關攔截。

3.2 日誌切割

以 app.xueersi.com 爲例,在網關上會配置一個對應的vhost,當請求到來時Nginx會匹配中這個vhost,也就是所有接入到這個域名的服務都會共用vhost。一個vhost就有一份log文件的問題,如果後期這個域名接入了上百個服務,那這上百個服務的access log全部會記錄到
app.xueersi.com_access.log 這份文件中,勢必會造成單份日誌文件過大的問題,同時會對ELK的搜索造成困擾,用戶很難找到自己服務對應的日誌。

針對此問題,我們採取了一種"變量式"日誌記錄方式,即"access_log /home/nginx/logs/app.xueersi.com_{host}_access.log", 當接入app.xueersi.com的請求到來時,網關層會識別標籤信息並將host進行對應的改寫,到了Nginx的log階段,會識別當前的host變量決定往哪份log文件裏寫日誌。例如 app.xueeris.com/A 則會寫入app.xueersi.com_A_access_log,而 app.xueersi.com/B 則會寫入app.xueersi.com_B_access_log。

3.3 cookie爆炸

當多個服務共用一個域名時另一個棘手的問題是cookie,服務端如果都往app.xueersi.com裏set cookie,一是不同cookie key可能會重複,二是如果沒有規範後期很可能會超出瀏覽器的最大cookie長度限制。

針對此問題,我們從兩方面着手解決。一方面設計了一套cookie管理系統,後端服務如果想要在 app.xueersi.com 裏設置cookie,都必須在管理系統上進行申請,如果該cookie已經有人申請過,則不允許重複申請,cookie管理系統上會記錄每個cookie對應的服務信息。 另一方面對dyroute插件進行改造,在發送響應給客戶端之前,會對cookie進行校驗,不屬於該域名的cookie會被攔截。

3.4 跨域

跨域問題也是令人頭疼的一個問題,集團安全組反饋,這些收斂域名會存在跨域風險,需要在有跨域限制。但是有的服務希望能夠在網關層統一處理跨域請求,有的服務則希望後端自己處理跨域,有的服務希望後端自行處理,有的希望能夠任意origin可以跨域訪問,有的則希望只能‘http_origin’的訪問,甚至會有希望自定義添加allow header頭的業務。要想在一個域名vhost裏cover所有的需求很難做到,我們和業務方以及安全組討論了一個折中方案,即所有接入域名收斂的服務統一在網關層進行跨域處理,網關會允許集團內的域名相互訪問,例如’.xesv5.com,.xueersi.com, .100tal.com’之間的跨域訪問沒有限制,而外部域名則會被攔截。另外我們在allow header裏添加了瀏覽器識別的通用header頭,同時設置有其他變量供業務方選定作爲自定義字段。

3.5 雙活

接入了域名收斂的服務中,有一部分服務是有雙活需求的,但是這些域名是解析到了世紀互聯IDC,無法滿足雙活需求,於是我們將這些域名的公網解析前移到了切流網關上,並利用切流網關的能力實現了雙活需求,默認會直接轉發到世紀互聯, 如果有業務需要轉發到阿里雲會根據提前設定好的路由規則進行阿里雲轉發。

其實攔在我們面前的問題遠遠不止於此,例如前端js動態配置問題,網關插件執行順序問題,多環境、多事業部域名收斂規劃、多協議支持問題、接入全站加速問題、靜態資源cdn緩存問題等等。而且面臨的阻力也非常大,並不是所有人都樂意接受這個事情,特別是針對一些老的服務,沒有人願意去改造,變革總會面臨着走出舒適區,適應新的規則,因此很多人會有抗拒。我們要推動全網校的前端、後端、運維、集團安全以及各部門的項目負責人,制定一套網校級的域名收斂規範,同時將這套歷史經驗推廣到全集團。

經過了半年的努力,我們也取得了一點小小的成果,不僅在網校落地了一套域名收斂規範,以網關和容器爲核心,成功實踐於網校雲平臺,收斂的域名總數也突破500大關。

image2020812_151117.png

我們相信,既然認定了是對的,就一直幹下去,解決麻煩的最好方法就是不怕麻煩,總有一天會看到收穫。

作者簡介

陳朝飛爲好未來PHP/Golang開發高級專

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章