由于沒有方便的監(jiān)控和運維工具,導致排查問題的效率低,使得在系統遇到問題的時候排查困難,耗時過長。

??? ?開發(fā)新功能

開發(fā)新任務的同時,我們需要修復原有系統的性能問題。

?PHP monolithic架構圖

每臺服務器部署相同的服務端PHP代碼,由PHP-fpm解釋執(zhí)行,通過Nginx進行反向代理。

華爾街見聞微服務架構設計

因此,在2016年11月至2017年3月,我們采用微服務架構啟動重構,嘗試解決一部分上述問題,在伸縮性上能以服務為單位進行拓容,同時,這一設計會在某些方面增大我們的開發(fā)成本和運維成本。

??? ?錯誤排查復雜

很顯然,以前在單體應用中能直接登錄服務器,查看出錯日志,現在錯誤散落在不同的服務中,為我們的錯誤排查帶來了困難。

??? ?日志源增加

如何把服務的日志收集并分析。

??? ?基礎設施增加

每個服務有互相獨立的MySQL、Redis,公共服務方面需要高可用的服務發(fā)現,調用鏈路分析,日志收集儲存設施等。

技術選型

微服務架構圖

每臺服務器上均衡地部署服務,LB接受用戶的請求,將請求轉發(fā)到API gateway,API gateway向服務發(fā)現查詢具體服務的IP和端口,服務執(zhí)行完業(yè)務邏輯后向上返回數據。

服務框架

我們選擇golang作為我們的后端開發(fā)語言。

??? ?golang在性能和開發(fā)效率上有很好的平衡,語法上很簡單,并發(fā)編程簡單高效,基礎庫健全。

??? ?調試工具強大

自帶一些pprof包可以profile當前程序的CPU消耗、內存占用、鎖狀態(tài)、channel阻塞等,非常便利我們定位問題。

??? ?有一些優(yōu)秀的微服務框架

我們選用go-micro作為開發(fā)框架,里面包含幾乎所有微服務組件,并且支持非常好的拓展性,通過接口的設計方式,讓我們可以拓展一些自己的組件,如服務發(fā)現、傳輸協議等。

??? ?golang在華爾街見聞已經有過比較多的應用,工程師使用golang開發(fā)幾乎0學習成本。

服務拆分

拆分的原則是通過服務功能劃分,盡量避免雙向依賴。我們拆分出了13個服務,包括用戶、內容、實時新聞、評論、搜索、商城、支付、三方代理等服務。

服務間通信

服務間使用protobuf協議對數據進行編碼,使用UDP作為傳輸協議。

服務發(fā)現

Etcd搭建多節(jié)點高可用的服務發(fā)現。

服務保護

我們選擇Hystrix作為服務保護以及服務降級的方案。

每個服務開發(fā)者,需要定義自己服務接口的并發(fā)量、超時時間以及fallback方法。

部署方案

選擇Kubernetes

* Docker Swarm

這是我們最先選擇的方案,因為Docker 1.12之后已經將Swarm功能集成到Docker En-gine,能以最少的配置啟動Docker集群。經過簡化和設計的控制臺API,方便地管理集群、調整服務如控制服務的數量、CPU、內存限制等。往集群內加入機器非常簡單,只需要運行一條命令即可。使用manager-worker架構,manager作為調度節(jié)點,支持高可用。

但遇到了非常致命的問題,比如頻繁更新服務的時候會出現服務訪問不到,某服務的負載均衡后掛載的服務IP是其它服務的,服務之間的通信有幾率出現超時問題,歸根結底,還是社區(qū)正在不斷完善swarm,有很多不穩(wěn)定的地方,網絡方面沒有進行優(yōu)化。

* Kubernetes

這是谷歌主導的服務編排工具,它支持Docker,相比Docker Swarm來說,它的概念更多,分層更細。功能方面多于Docker Swarm,支持一些高級功能如秘鑰管理、配置管理、自動拓容等。在生產環(huán)境的應用比較廣泛,穩(wěn)定性更高。

* 裸機部署

裸機部署是我們的一個備案,考慮到以上兩個方案在當時沒有具體線上實施的經驗,所以如果Docker Swarm和Kubernetes都沒有成功,我們直接裸機部署。

裸機部署的需要解決單機端口沖突,如果一個服務在一個服務器上最多只部署一個,那么可以通過寫腳本,并劃分服務器角色的方式進行部署,利用ansible可以定義user服務集群、content服務集群、comment服務集群等,通過分發(fā)二進制文件的方式讓服務啟動,這樣的方案要考慮到服務更新、服務重啟、服務刪除等邏輯,同一時間只有部分節(jié)點更新,在服務未更新成功的時候流量暫時不能打到正在更新的節(jié)點。

準備工作

代碼托管
由于之前使用github開發(fā)人員的代碼提交在有翻墻工具的幫助下速度依然不是很理想,我們自建了Gitlab倉庫,自此開發(fā)過上了幸福的生活。

容器化

swarm和kubernetes是基于docker快速創(chuàng)建刪除服務,通過增加容器為服務拓容,縮減容器為服務縮小規(guī)模,所以所有項目必須要構建docker鏡像。按項目類型劃分,我們遇到3種鏡像打包情況。

1.?? ?后端項目

后端服務90%是golang項目,針對golang的鏡像,我們采取將golang項目編譯成可執(zhí)行文件,基于最小的alpine鏡像打包入docker,這里遇到過一個問題,就是alpine里缺少openssl的證書,無法支持https,我們自定義了新的基礎鏡像,不僅將證書文件打入鏡像,同時為了線上調試方便,增加了tcpdump、strace、bash等工具,在初期調試容器間通信問題時發(fā)揮重要的作用。

2.?? ?前端靜態(tài)文件

見聞的后臺以及m站基于Vue,編譯后生成的靜態(tài)文件打入鏡像,通過nginx訪問。 為了支持HTTP2,我們打入nginx鏡像缺少的證書。

3.?? ?服務端渲染

主站PC站基于nodejs、Vue實現服務端渲染,所以不僅需要依賴nodejs,而且需要利用pm2進行nodejs生命周期的管理。為了加速線上鏡像構建的速度,我們利用taobao源https://registry.npm.taobao.org進行加速, 并且將一些常見的npm依賴打入了基礎鏡像,避免每次都需要重新下載,鏡像打包從開始的3分鐘縮減到1.5分鐘。

三類鏡像結構

持續(xù)集成

我們利用Gitlab CI配置了測試、鏡像構建、鏡像發(fā)布、自動部署等流程,后端服務從提交代碼到測試分支到測試環(huán)境自動部署完成花費1.5分鐘,前端服務平均為2.5分鐘。

CI任務中的test->build->docker->deploy流程

云平臺的選擇

最終,我們選擇了騰訊云的容器服務,主要基于以下幾點考慮:

??? ?騰訊云的容器服務是在騰訊云的Iaas上為每個用戶構建容器集群,騰訊云提供的微服務架構和持續(xù)集成與交付的應用場景基本滿足了我們的述求。

??? ?騰訊云的容器服務是基于Kubernetes實現的,支持完全的kubernetes能力。

??? ?騰訊云在Kubernetes上實現了他們的存儲、負載均衡等產品的插件、復用了騰訊云本身平臺的監(jiān)控、日志等能力。減少了我們接入和開發(fā)的成本。

服務在騰訊云的應用

我們將我們的應用重構成微服務的架構,每個微服務部署成騰訊云容器服務上的一個服務,前端接入通過一個負載均衡。后端服務間可互相訪問。

服務器安全方面,內部服務器通過VPC進行網絡隔離,將網絡劃分為生產環(huán)境、測試環(huán)境,在生產環(huán)境中又劃分backend子網和data子網,設定子網之間的訪問規(guī)則。

為了禁止內部服務器的外網訪問,不給內部服務器分配外網IP,僅通過跳板機訪問。

性能對比

利用locust模擬線上請求的比例,利用2臺16核的壓測機在內網對10臺16C32G的機器上的服務進行壓測,達到1w/s QPS以上,并且服務的負載并沒達到極限,這已經是之前PHP生產環(huán)境20+臺16C32G服務器能達到的QPS。

線上調用追蹤
通過追蹤API調用鏈的流向與耗時,我們可以找出性能的瓶頸。我們通過zipkin實際優(yōu)化了幾種情況:
??? ?服務調用冗余
當拉取文章列表的時候,我們需要拉取文章對應的作者信息,開始的時候我們使用拉取單個作者信息的方式,后來性能調優(yōu)階段,我們將其改為批量拉取作者列表,減少RPC的冗余。
??? ?服務耗時長
對于有些本身就比較耗時并且對即時性不是那么苛刻的計算服務,我們?yōu)榱吮WC服務的響應時間,會適量地加上緩存。

監(jiān)控與報警
由從外部系統表征到內部日志,我們將監(jiān)控分為API健康,程序錯誤報警,以及服務器/容器負載。
排查問題的流程一般有兩種情況,一種是用戶發(fā)現問題,申報問題,開發(fā)人員跟進問題;一種是我們的監(jiān)控優(yōu)先發(fā)現問題,開發(fā)人員在用戶反饋前跟進并修復。在報警方面,我們通過為監(jiān)控系統謹慎設置報警閾值,當觸發(fā)報警時,開發(fā)人員會收到郵件。
這里我們在報警的定義上有過思考,即什么樣的報警算是有意義的?我們遇到過每天10幾條重復的報警,通常開發(fā)人員開始時會對報警非常重視,當重復的報警一再出現,漸漸失去了對報警的關注。所以我們有解除一些不必要的報警,并且對剩余一些報警進行調查,甚至有些警報是因為監(jiān)控工具本身的不準確引起的。

API健康
我們設置默認的時間區(qū)間是5分鐘
??? ?統計API五分鐘內平均QPS
??? ?API 98%以內的延遲分布
??? ?QPS最高的前10的API
??? ?API的返回碼的分布

程序錯誤報警
后端程序內接入Sentry日志報警系統,golang程序捕獲panic日志以及error日志,并發(fā)送報警郵件。

服務器/容器負載
通過在服務器上運行telegraf daemon進程,收集服務器metrics并發(fā)送給influxdb,使用Grafana作為前端面板,對服務器負載以及容器的平均CPU、內存占用率進行監(jiān)控。

結束語
本文介紹了華爾街見聞通過重構和服務容器的重新部署,實踐微服務架構的情況。經過幾個月的開發(fā)測試,我們不僅完成了線上服務從PHP到Golang的轉型,更在服務的穩(wěn)定性上經歷了考驗,支撐了幾次重大新聞的高流量。
在開發(fā)流程上,搭建了完善的自動化工具,減少了人工操作的重復性和誤操作概率。
在運維方面,由于監(jiān)控系統對系統完整的監(jiān)控,與Kubernetes健全的上線、下線、回滾、拓容功能配合,能以極快的速度處理線上問題。

分享到

zhangnn

相關推薦