以下是運滿滿K8s容器化進程記錄,摘抄一下,方便以后查閱。
背景介紹
運滿滿自開始微服務改造以來,線上線下已有數千個微服務的 Java 實例在運行中。這些 Java 實例部署在數百台雲服務器或虛機上,除少數訪問量較高的關鍵應用外,大部分實例均混合部署。
這些實例的管理,采用自研平台結合開源軟件的方式,已實現通過平台頁面按鈕菜單執行打包、部署、啟動、停止以及回滾指定的版本等基本功能,取得了不錯的效果。但仍然存在如下幾個痛點:
-
實例間資源隔離,尤其在高峰期或故障期間,單服務器上不同實例間 CPU 和內存資源的爭搶特別明顯。
-
線上某個應用實例異常時需要人工干預,導致較長的故障時間。
-
大批服務端應用新版上線后,如網站關鍵功能故障,需要針對每個應用,選擇對應的版本,執行回滾操作,整個過程耗時較長。
-
線下 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。
難點和基本原則
-
線上服務必須在不間斷提供服務的情況下遷移,每個應用按比例切分流量,在確保穩定性的前提下遷移到 K8S 集群中。
-
DEV 環境可批量上線,QA 和 Production 環境上線需要考慮各應用的版本依賴關系。
-
初期只上無狀態的應用。
-
對研發 /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 集群的性能,部署好應用之后做一次壓測很有必要。
原文地址:https://mp.weixin.qq.com/s?__biz=MzIzNjUxMzk2NQ==&mid=2247490483&idx=1&sn=af59ead3956cdb07e00d0504660da3b7&chksm=e8d7e471dfa06d6719f287f560907298884c5084ab330c28ff2d610e8c44c4ccff1585fd5ce3&scene=27#wechat_redirect