炫一下(北京)科技有限公司2011年8月成立於北京,是移動短視頻娛樂分享應用和移動視頻技術服務提供商。旗下擁有「秒拍」、「小咖秀」、「一直播」三款移動視頻產品 [1-2] 。 2016年11月21日,旗下擁有秒拍、小咖秀、一直播的一下科技宣佈完成5億美圓E輪融資,該輪融資也創下了國內移動視頻行業的單輪融資金額最高紀錄。 [3] 根據一下科技公佈的數據,截至2016年11月21日,秒拍與小咖秀的日播放量峯值已突破25億次,日均上傳量突破150萬條,日均覆蓋用戶超過 7000萬 [2] 。java
一下科技的吳飛羣,感謝大佬的文檔支持。node
隨着公司業務規模的不斷變大, 爲了可以快速迭代, 公司的後端架構也逐漸地從傳統架構模型轉向了微服務架構. 公司主要開發語言是Java, Golang, PHP.大部分的服務都運行在公有云的虛擬主機上mysql
因爲測試環境之間也須要互相調用, 常常聯調, 因爲一些歷史緣由, 不是全部的服務都有穩定的測試環境, 這給測試人員帶來了很多的麻煩, 常常會用線上的部分機器進行調試, 存在很多的潛在風險.git
爲了方便管理, 每一個服務都是分配的單獨的服務器進行部署, 因爲產品用戶的使用習慣, 公司大部分的服務的資源使用都有高峯低估的特色, 爲了保障服務的資源充足, 咱們核心的服務高峯的時候也會控制資源使用率, 因此大部分服務在平時的資源使用率都是很低的, 形成了資源的大量浪費.github
業務高峯期的時候常常須要擴容機器, 擴容須要開發申請, 運維審批, 部署服務, 測試確認等不少環節, 一個服務的擴容最快也得須要半個小時才能完成, 半個小時對於很對關鍵服務來講實際上是很長的, 是沒法忍受的.golang
後端系統在向微服務演進的過程當中, 不一樣的後端小團隊出現了編程語言, 框架等的多元化, 以前存在的部署系統不能充分知足多語言多框架的部署需求.(以前大部分是PHP)web
這一部分主要跟你們分享的是咱們怎樣把運行在傳統虛擬機/物理機上的服務逐漸接入到k8s上的.redis
在進行容器化改造以前, 咱們的運維平臺已經具備了代碼發佈的功能, 咱們爲不一樣編程語言的項目制定了部署規範, 好比部署路徑, 日誌路徑, 啓停方式, 回調腳本等. 每個服務要接入部署系統都須要在平臺上提工單申請,工單信息大體以下:spring
經過上面的工單, 足夠能夠收集運維關心的服務信息, 這些信息都會記錄到咱們的運維平臺上.容器集羣搭建完成之後, 開發人員只須要爲該項目申請容器部署便可, 構建的過程能夠複用以前的, 不用作任何改動, 只須要在構建完成之後製做相應的鏡像推送到內部的docker倉庫中。sql
集羣內訪問/負載均衡, 若是鏈接該項目的服務都已經部署到了容器中, 那麼該服務選擇集羣內訪問的方式就行, 也就是採用clusterIP的方式, 若是該服務是對公網的, 或者訪問該服務的客戶端部署在傳統VM/物理機上, 這時候就須要一個負載均衡了.這裏的負載均衡是公有云提供的LoadBalancer. 開發人員提交工單, 運維人員處理完工單之後這個項目基本上就能夠經過部署系統部署到容器了.運維平臺經過調用K8s集羣的接口來建立相應的service, deployment, HPA等. 固然這只是大概的流程, 具體實施過程當中還須要作好開發人員的容器知識的培訓和編寫相關基礎文檔.
推送代碼, 代碼倉庫使用的是內部搭建的gitlab倉庫
運維平臺是以項目爲中心而設計開發的, 上面說了每一個項目要想接入部署系統都會提交發布申請工單,提交完之後項目和該項目的代碼倉庫的綁定關係就會存儲到平臺上,而後開發就能從平臺上提交容器的發佈工單, 以下圖是開發者提交發布申請的表單: 3.咱們的項目構建採用的是Jenkins, 跟大部分使用Jenkins的方式不太同樣, 咱們經過Jenkins的API來觸發構建, Jenkins對於開發人員是不可見的. 咱們會在Jenkins上提早建立不一樣編程語言的任務模板, 開發提交發布工單之後, 運維平臺會經過調用Jenkins的API複製模板建立一個和該項目在運維平臺上名字一致的Job, 並配置好相關的參數信息, 好比git地址, 分支/tag名稱, 構建的shell等, 而後觸發構建任務. 這裏有2個點須要給你們說明一下, 一個就是咱們以前對服務的標準化之後, 開發人員不須要編寫Dockerfile, 而是採用通用的dockerfile, 替換一下相關的變量便可, 這些工做在構建的時候自動觸發; 二是咱們採用gitlab上的tag來做爲每次部署的版本號.咱們沒有采用commitID是由於它可讀性較差, 開發每次提交發布之後, 咱們平臺會用時間戳+用戶ID去gitlab上建立tag, 同時這個tag也會做爲該服務鏡像的一部分.
mvn clean;
mvn package -Dmaven.test.skip;
sed s/service-name.jar/${JOB_NAME}/g /template/Dockerfile > Dockerfile
cp target/*.jar ${JOB_NAME}
docker build -t inner.hub.com/${JOB_NAME}:#tag# .
docker push inner.hub.com/${JOB_NAME}:#tag#
複製代碼
Jenkins配置了gitlab的互信密鑰, 能夠拉取全部項目的代碼.
平臺觸發Jenkins構建任務之後, 就會把該次構建的容器image推送到內部的hub中.
測試的k8s集羣開發和測試能夠隨意發佈, 線上的是須要測試人員確認經過之後才能夠上線的, 這裏有一個點是線上環境上線的鏡像是已經上線到測試環境的相同鏡像.
上線到k8s, 開發人員從運維平臺上點擊按鈕觸發, 運維平臺調用k8s的接口經過替換容器的image地址來實現發佈/回滾.
運維平臺上提供的容器相關的功能. 咱們的運維平臺是以項目爲中心來設計的, 全部的其餘資源都會跟項目綁定, 好比服務器, 數據庫, 容器資源, 負責人等. 此外平臺還提供了權限的管理, 不一樣的人員對於項目有不一樣的權限, 基本上每一個開發對於項目的常規操做咱們都會集成到平臺上. 容器這塊咱們沒有使用官方的dashboard, 也是基於API來集成到運維平臺的.
在沒有采用容器以前, 咱們已經用ELK來解決日誌的問題了, 咱們主要有2種使用方式:
當服務的日誌量不是很大的時候咱們採用的是程序直接以json的形式寫日誌到redis的list中, 而後咱們使用logstash發送到ES中, 而後從kibana上就能夠查看了. 當服務的日誌量較大的時候, 咱們採用寫文件的方式, 而後採用filebeat發送到kafka集羣, 而後採用logstash發送到ES集羣
採用k8s容器之後, 因爲磁盤限制這塊不是很完善, 咱們要求全部的服務都採用輸出到redis的方式或者標準輸出的方式來打日誌, 這樣本地磁盤也就不用考慮限制了.這裏有一點要說明一下:
因爲全部服務都直接採用redis的方式後, redis很容易成爲瓶頸, 若是redis掛了極可能會影響線上業務, 基於這一點, 咱們讓公司的Golang工程師幫忙開發了一個redis -> kafka的一個代理服務, 這樣的話業務不用修改任何代碼, 仍是用redis的方式輸出日誌, 只不事後面實際上是輸出到了kafka集羣了, 代理服務是無狀態的, 能夠無限擴容, 這樣性能問題也就解決了.
在接入容器以前咱們已經採用了prometheus來監控咱們的服務器和業務, k8s對prometheus的支持很完美, 因此咱們這塊能很快適應, 業務代碼也不用怎麼修改. 這裏有幾個點:
咱們在k8s上部署了prometheus來專門監控全部業務的Pod狀態, 好比Pod的HPA使用率, Pod的不可用數量等.
因爲業務監控須要採集的數據巨大, 多個服務若是公用一個prometheus抓取端的話會有性能問題, 咱們採用了爲每個服務啓動一個prometheus抓取端的辦法來解決這個問題,每一個服務單獨的prometheus抓取端經過配置文件中的正則匹配來控制只抓取該服務的數據.
看板採用的是grafna.服務註冊到運維平臺的時候也會自動經過grafna的接口建立相應的看板項目.
報警發送的話咱們本身有封裝的釘釘/企業微信/郵件/短信等功能的接口.
spring boot1.5 項目接入prometheus相關實例參考 github.com/hellorocky/…
公司用到服務發現的主要是Java的spring項目, 因此採用的是Eureka, 咱們以前在沒有采用容器的時候就已經有了一套eureka註冊中心, 因爲pod的網絡和虛擬機的網絡是直接互通的, 服務遷移到容器中之後咱們依然採用的以前的一套註冊中心.
服務遷移到容器的時候咱們就要求開發在開發代碼的時候生產和測試是同一個jar包, 根據啓動參數的不一樣來選擇環境, 這樣的話就能夠實現構建一次, 生產/測試處處運行了. 配置中心這塊咱們採用的是apollo, 這塊也是經過調用apollo自己的API來集成到運維平臺的, 作到了從平臺上修改/編輯配置等.
這個參數是k8s配置服務的健康檢查的時候使用的, 咱們知道, k8s的健康檢查有2種, 一種是存活檢查, 當這個檢查失敗的時候, k8s會嘗試重啓該pod, 另外一種是就緒檢查,當這個檢查失敗的時候, k8s會把該pod從service上摘下來, 還有一個點是很重要的, 好比我在k8s上部署了一個Java項目, 這個項目啓動時間大概是30秒, 那麼我就須要配置上面的這個參數,pod啓動的時候讓健康檢查的探針30秒之後再執行檢查,實際場景中, 一個普通的springboot項目啓動要花費40秒左右(limit爲4核), 因此這個值對於Java來講至少須要配置成60才能夠, 否則常常會出現新部署的pod一直重啓.
在實際生產環境中使用spring框架,因爲服務更新過程當中,服務容器被直接終止,因爲eureka server有緩存, 部分請求仍然被分發到終止的容器,雖然有重試機制, 可是高併發狀況下也常常會遇到接口調用超時/失敗的狀況,爲了減小這些錯誤, 能夠在容器退出前主動從eureka上註銷這個節點, 等待2倍左右的eureka檢測時間(2*5), 這須要開發提供接口並將調用接口的腳本添加到prestop中, 參考: curl http://127.0.0.1/debug/stop/eurekaClient;sleep 10
實際工做中遇到過一個服務無論怎麼配置, 老是會莫名奇怪地出現重啓的現象, 看日誌也沒有看出不來, 後來問題找到了, 是健康檢查配置的問題, 最開始使用k8s的時候咱們給每一個服務配置健康檢查的超時時間是1秒, 失敗1次就算失敗; 後來跟業務溝通了一下, 他們的服務原本就是響應慢, 好吧, 咱們只好修改了一下超時時間, 調大了一點, 失敗次數也改爲了3次. 連續3次失敗纔算失敗.
因爲JVM默認狀況下獲取的是宿主機系統的參數,因此致使JVM獲取的GC線程數和分配的heapsize不是咱們想要的, 咱們是經過調整JVM啓動參數來解決這些問題的, 參考: java -server -XX:ParallelGCThreads=4 -Xmx2g -Xms2g -jar service.jar, 固然也能夠經過其餘的方式來修改.
Q: prestop hook的參考地址能給個外網地址看嘛? A: 這個看官方的文檔就行吧, 我這個場景裏只是用了一個curl, 讓開發提供一個接口就行. 具體prestop hook官方文檔上有詳細的舉例.
Q: apollo配置中心,配置怎麼落到服務裏的,或者容器裏? A: 咱們這邊大部分是Java項目, 使用的官方提供的SDK, 具體你能夠看下apollo的使用文檔.
Q: 日誌怎麼採集和展現?用什麼方案? A: ELK, 採集日誌主要是程序直接輸出到redis, 這點有些不同.
Q: CD的配置是怎麼管理的? A: 相關的上線配置都是存在運維平臺上, 服務的配置使用的apollo配置中心.
Q: ELK是部署成sidecar模式集成採集器仍是日誌輸出到外部存儲?日誌怎麼收集怎麼清洗的? A: 上面說了, 程序直接輸出到redis. 而後直接存儲到了ES中.
Q: redis和mysql之類的組件也都完成了容器化嗎?作的是集羣嗎? 怎麼樣的形式? A: 沒有, 咱們暫時推廣的是無狀態的接口, 有狀態的暫時沒有進行容器化.
Q: 關於war tomcat運行方式,如何指定不一樣環境的配置文件可否像 jar在啓動的時候指定環境配置文件,在tomcat裏的catalina.sh裏指定配置文件。 A: 這個不是很清楚, 公司的服務都是springboot的, 都是jar包這種形式的了.
Q: k8s的hpa組件是原生的嗎,只根據cpu內存來進行伸縮,有沒有出現過什麼問題 A: 是原生的, 出現過的問題是, 以前都只採用了一個緯度的擴容(CPU), 後來發現該pod常常OOM, 後來加上了內存的擴容, Java服務例外.
Q: Prometheus 數據怎麼保存的,每一個實例都存在本地目錄嗎? A: 咱們有專門的Node節點來跑prometheus pod經過node label調度, 採用的本地SSD磁盤, 每一個服務一個目錄, 大概這個樣子.
Q: 還有就是還有就是日誌部分 如今redis是瓶頸嗎 redis也是集羣? A: 分享的時候提到了, redis是瓶頸, 後來公司golang工程師搞了一個reids--> kafka的代理服務, 採用的redis協議, 可是直接寫入到了kafka, 解決了性能問題.
Q: 網絡組件用的什麼,容器和虛機是大二層嗎? A: 容器和虛機是互通的,網絡組件是用的Flannel, 這塊主要是公有云提供商負責維護的, 咱們其實這塊接觸的很少. 性能的話這塊我也會答不了.
Q: 請問FileBeat是直接在容器裏獲取文本的嗎? A: 容器中咱們沒有用到filebeat.
Q: pod有配request 和 limit 嗎,配了的話怎麼和jvm 參數打通?謝謝 A: 配置了, 這塊暫時都是顯性指定參數, 沒有作到自動感知, 也就沒有打通.
Q: 請問集羣的配額限制是從什麼角度作的 ,是pod層面仍是namespace層面 A: 目前只配置了pod的request和limit, 基於namespace的配額暫時沒有配置, 目前沒有遇到相關的問題.
Q: prometeus也是k8s管理嗎,配置文件他的配置文件怎麼管理的 A: 這塊咱們寫了一個簡單的服務部署到了k8s的master節點上, 當一個服務接入k8s上之後, 運維平臺會去掉這個服務的接口, 就會建立一個prometheus server專門抓取該服務的監控數據.經過prometheus的配置能夠作到只匹配該服務, 咱們這邊是每一個服務配置一個單獨的prometheus server抓取端.