經典案例覆盤——運維專家講述如何實現K8S落地

背景介紹

運滿滿自開始微服務改造以來,線上線下已有數千個微服務的Java實例在運行中。這些Java實例部署在數百臺雲服務器或虛機上,除少數訪問量較高的關鍵應用外,大部分實例均混合部署。

這些實例的管理,採用自研平臺結合開源軟件的方式,已實現通過平臺頁面按鈕菜單執行打包、部署、啓動、停止以及回滾指定的版本等基本功能,取得了不錯的效果。但仍然存在如下幾個痛點:

  1. 實例間資源隔離,尤其在高峯期或故障期間,單服務器上不同實例間CPU和內存資源的爭搶特別明顯。
  2. 線上某個應用實例異常時需要人工干預,導致較長的故障時間。
  3. 大批服務端應用新版上線後,如網站關鍵功能故障,需要針對每個應用,選擇對應的版本,執行回滾操作,整個過程耗時較長。
  4. 線下DEV/QA環境頻繁發佈,每次發佈都需要先停止老的版本再發布新的版本,會影響到日常測試。

運滿滿飛速發展的業務,對系統穩定性的要求越來越高,我們急需解決如上問題。

技術調研、選型

最初吸引我們的是容器技術良好的隔離和水平擴展等特性,而Docker的口碑以及幾年前參與的一些Docker項目經驗,使得采用Docker容器技術成了我們的不二選擇。

但我們仍然需要一套容器編排系統,來實現自動化管理Docker容器,大致瞭解下來有3個選項:Kubetnetes(K8S)、swarm、mesos

這3個我們都不熟悉,而這個項目的節奏很緊迫,不允許我們對這3個系統深入瞭解後再做選擇。好在Github有一個統計功能,我們在Github上查到了這3個開源項目的一些基本情況,如下圖:



根據這份統計數據,以及擁有Google公司的光環,我們在很短的時間內確定了使用K8S作爲容器編排管理系統。K8S,這個開源項目號稱可以自動部署、擴展和管理容器應用,並且能解決如下核心問題:

負載均衡 - 一個應用運行多個同樣的容器,內部Service提供了統一的訪問定義,以負載均衡的方式來提供訪問。

服務發現 - Service和Kube-DNS結合,只需要通過固定的Service名稱就可以訪問到對應的容器,不需要獨立尋找使用服務發現組件。

高可用 - K8S會檢查服務的健康狀態,發現異常時會自動嘗試重新啓動服務,保障正常運行。

滾動升級 - 在升級過程中K8S會有規劃的挨個容器滾動升級,把升級帶來的影響降低到最小。

自動伸縮 - 可以配置策略當容器資源使用較高會自動增加新的容器來分擔壓力,當資源使用率降低會回收容器。

快速部署 - 編寫好對應的編排腳本,可以在極短的時間部署一套環境。

資源限制 - 對程序限制最大資源使用量避免搶佔資源遇到事故或壓力也能從容保障基礎服務不受影響。

進一步深入瞭解K8S之後,我們大致確定了會用到如下組件、相關技術和系統:

  • 應用部署 K8S Deployment,HPA;
  • 少量基礎服務 K8S Daemonset, kube-dns;
  • 對外服務暴露 K8S Ingress, Traefik, Service;
  • 網絡插件 Flannel;
  • 監控告警 Heapster, InfluxDB, Grafana, Prometheus;
  • 管理界面 Kubectl, Dashboard, 自研發布管理系統;
  • 製作鏡像 Jenkins, Maven, Docker;
  • 鏡像倉庫 Harbor;
  • 日誌收集 Filebeat, Kafka, ELK。

難點和基本原則

  1. 線上服務必須在不間斷提供服務的情況下遷移,每個應用按比例切分流量,在確保穩定性的前提下遷移到K8S集羣中。
  2. DEV環境可批量上線,QA和Production環境上線需要考慮各應用的版本依賴關係。
  3. 初期只上無狀態的應用。
  4. 對研發/QA的影響最小化(儘量不給繁忙的研發/QA同學增加工作量)。

落地過程剖析

Docker化前後應用發佈流程對比

從下圖中可以看到2個明顯的變化:

  1. 之前部署的是war包、jar包,之後部署的是Docker鏡像(鏡像中包含war包、jar包)。
  2. 之前是先停止再啓動應用進程,發佈過程中服務會中斷,之後是先啓動新版本容器,再停止舊版本容器,發佈過程中應用一直在提供服務。

遷移中的系統架構

當前業務應用主要分爲2種,僅供內部應用調用的RPC服務(Pigeon框架)和對外提供服務的REST API,REST API可進一步細分爲2種,已接入API網關和未接入API網關。其中RPC服務和已接入API網關的應用均有自己的註冊中心,遷移步驟相對簡單,在K8S集羣中啓動對應的應用即可。未接入API網關的應用採用K8S Ingress插件提供對外服務入口,需要一些配置。系統架構如下圖,最終目標是要實現將圖中下方的兩個框內的應用全部遷入K8S集羣中。

Master集羣的高可用

由於公有云的限制,我們主要結合服務商提供的SLB來實現,示意圖如下:

K8S集羣內應用對外提供服務

由於集羣內POD的IP地址動態變化,我們採用 Traefik+Ingress+Nginx+SLB 的方式,來提供一個對外服務的統一入口。Traefik根據HTTP請求的域名和路徑路由到不同的應用服務,Nginx則執行一些複雜的諸如rewrite等操作,SLB提供高可用。架構示意圖如下:

容器內應用初始化

爲了實現同一個鏡像可以兼容運行在DEV、QA、Production等各種環境,必須編寫一個初始化腳本,該腳本被存放在鏡像中。當容器啓動時,從Env變量中讀取當前所在的環境,並創建一系列軟鏈到各環境對應的配置文件以及設置日誌目錄等其他初始化操作,隨後fork一個新進程用於檢測和設置該容器內應用是否已完成正常啓動(配合容器 readiness 探針使用),同時調用應用啓動腳本。
下圖爲容器內通過軟鏈指向不同的環境配置文件:

下圖爲容器內通過軟鏈設置日誌目錄:

K8S 日誌收集

當前應用日誌均以文件形式存放,且單個實例對應多個日誌文件,無法採用K8S官方推薦的日誌方案。同時由於容器的無狀態化,我們必須另想其他辦法保存日誌。目前採用的是將Node上的固定目錄作爲存儲卷掛載到容器內,在容器啓動時通過初始化腳本按照應用名+容器IP生成該容器特定的日誌路徑。爲了便於查看日誌,我們提供3種途徑:

  1. 容器內啓用SSH服務端,發佈管理系統中實現WEBSSH,正常情況下可通過WEB頁面進入容器命令行查看日誌,由於其便利性,推薦首選此方式。
  2. 有些情況下容器會啓動失敗,此時無法進入命令行,可在發佈管理系統中找到日誌的鏈接地址,下載到本機後再查看。
  3. 此外,我們在所有Node上各運行一個Filebeat容器,將Node上收集到的日誌實時發送到Kafka集羣中,經過處理後存儲到ES集羣,以便日後檢索。

下圖爲Node服務器上的日誌目錄結構:

下圖爲Node服務器上共享的日誌下載路徑:

K8S 監控

採用 Heapster+InfluxDB+Grafana 組合,需要注意的是其中InfluxDB用於存放監控數據,需要將數據持久化。在Grafana上製作了不同維度的dashboard,可根據Namespace、Node、應用名進行檢索,可按照CPU、內存、網絡帶寬、硬盤使用量篩選應用,方便故障排查和日常優化。(當然,更好的監控系統是Prometheus,已經在上線的路上。)

下圖爲監控大盤:

下圖爲監控菜單:

下圖爲某應用的監控圖:

Harbor鏡像倉庫

Harbor我們目前採用的是一主多從結構,主庫與打包Jenkins都在線下網絡中,鏡像上傳到主庫後會被自動同步到線下另一個從庫以及線上的從庫中,如下圖所示:

鏡像樹

我們的規劃是構建一顆鏡像樹,所有的應用都基於這顆樹上的基礎鏡像來構建應用鏡像,各應用構建時選擇最相似的基礎鏡像,再增加應用的特殊需求即可。基於此鏡像樹,我們95%以上的應用均無需在Gitlab裏放置Dockerfile,Dockerfile在打包時根據變量自動生成即可,例如:

下圖爲腳本自動生成的某應用Dockerfile:

鏡像樹結構示意圖如下:

當前狀態

容器化:DEV/QA環境的應用已完成Docker化,產品環境中應用約98%已完成Docker化。

系統自愈:應用OOM或其他Crash時,系統能夠自動拉起新的節點以替換故障節點,高級健康檢查暫未開啓(需其他方面配合)。

彈性伸縮:關鍵應用全部開啓彈性伸縮,訪問量高峯期觀察到的效果很好。

滾動發佈:可按指定的比例分批次部署更新應用版本,先更新一批,成功後銷燬一批,依次滾動。

快速回滾:當前僅支持單應用快速回滾,後期如需要增加事務級回滾能力,採用K8S的rollout功能可以方便實現。

一些踩過的坑和建議

  1. 底層操作系統採用CentOS7.x版本,會比較省事。
  2. 阿里雲經典網絡中的ECS無法訪問容器IP,需要先遷移到VPC環境,其他公有云情況類似,重點是能自主添加路由。
  3. 如果有應用級監控的話,從容器內部採集到的Memory,Load Average等信息是底層操作系統的,而不是容器的,這些指標可以依賴專門的容器監控系統。
  4. 要注意ulimit的限制,容器中並沒有對它進行隔離,設置過小的話會遇到一些莫名其妙的問題。
  5. 容器中的root用戶用netstat命令可能看不到其他用戶所創建的進程的owner,如果有一些老式的腳本可能會遇到類似問題。
  6. 如果有一些內部系統需要直接訪問容器的特定端口,headless service挺好用。
  7. Zookeeper有一個單IP連接數60的默認限制,如果沒修改過該參數的話應用遷移到K8S之後可能會遇到此問題。
  8. 產品環境中的某個訪問量大的應用往K8S遷移時,可以先分配較多數量的容器,確保能吃下所有流量,之後再根據監控,用彈性伸縮功能來減掉多餘的容器。
  9. 如果想提前知道K8S集羣的性能,部署好應用之後做一次壓測很有必要。

作者簡介

王春林,就職於運滿滿技術保障部,關注容器、DevOPS等領域。

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