基於 Orbit 的雲原生應用交付基礎原則與良好實踐

img


本文作者:何文強——騰訊雲 CODING 高級架構師。
負責 CODING DevOps產品解決方案架構設計和技術產品佈道以及 CODING 雲原生技術研究與落地實踐。在多個技術大會擔任演講嘉賓,騰訊雲 CODING DevOps 課程認證出品人,騰訊云云原生訓練營核心初創成員。

精通敏捷精益、DevOps 和雲原生領域,技術紮實,視野開闊,格局前瞻;在泛互、教育、工業、政務、金融等多個行業擁有數字化落地規劃和實戰經驗;多年技術開發和團隊管理經驗,目前專注於一站式研發效能平臺的建設和推廣,聚焦於“以應用爲中心“的雲原生的落地與實踐,致力於中國軟件工程能力的提升和改進。

良好的實踐需要遵循一定的原則,通過原則指導的實踐才能行穩致遠。在雲原生應用交付中,可通過 The Twelve-Factor App(應用 12 因素)原則作爲雲原生應用交付實踐的指南。

img
圖5-1

應用 12 因素(圖5-1)主要包括:#1基準代碼;#2依賴;#3配置;#4後端服務;#5構建、發佈、運行;#6進程;#7端口綁定;#8併發;#9易處理;#10開發環境與線上環境等價;#11日誌;#12進程管理。接下來我們將詳細介紹基於這 12 個原則的良好實踐。

基準代碼:一份基準代碼,多份部署

基準代碼和應用之間總是保持一一對應的關係:

● 一旦有多個基準代碼,就不能稱爲一個應用,而是一個分佈式系統。分佈式系統中的每一個組件都是一個應用,每一個應用可以分別使用 12-Factor 進行開發。

● 多個應用共享一份基準代碼是有悖於 12-Factor 原則的。解決方案是將共享的代碼拆分爲獨立的類庫,然後使用 依賴管理 策略去加載它們。

在“一份基準代碼,多份部署”的良好實踐中(圖5-2),爲一個應用的每個模塊創建一個代碼倉庫,選擇 Master 分支作爲基線,並以 Master 分支構建鏡像, Master 分支構建出來的鏡像部署在不同的環境中,即所有環境共享由 master 分支構建出來的鏡像,如 dev 環境、pre-production 環境、production 環境共享同一鏡像。在基準代碼實踐中,不同環境使用的鏡像是同一份,但是不同環境的配置信息不一致,通過鏡像與配置信息實現不同環境的部署。

img

依賴:顯示聲明依賴

大多數編程語言都會提供一個打包系統,用來爲各個類庫提供打包服務,就像 Perl 的 CPAN 或是 Ruby 的 Rubygems 。通過打包系統安裝的類庫可以是系統級的,或僅供某個應用程序使用,部署在相應的目錄中。

12-Factor 規則下的應用程序不會隱式依賴系統級的類庫。 它一定通過依賴清單 ,確切地聲明所有依賴項。此外,在運行過程中通過依賴隔離工具來確保程序不會調用系統中存在但清單中未聲明的依賴項。這一做法會統一應用到生產和開發環境。

在“顯示聲明依賴”的良好實踐中(圖5-3),Python 通過 requirements.txt 顯示聲明所需要的第三方類庫和組件,並記錄這些類庫和組件的所有依賴和精確的版本號;Gradle 通過 build.gradle(Maven 通過 pom.xml)的 dependencies 顯示聲明所需要的第三方類庫和組件的所有依賴和版本號。通過顯示聲明依賴,可以有效減少因錯誤的依賴版本導致的缺陷,提升軟件供應鏈的可見性。

img
圖5-3

配置:在環境中存儲配置

通常,應用的 配置 在不同 部署 (預發佈、生產環境、開發環境等等)間會有很大差異。這其中包括:

● 數據庫,Memcached,以及其他後端服務的配置;

● 第三方服務的證書,如 Amazon S3、企業微信等;

● 每份部署特有的配置,如域名等。

有些應用在代碼中使用常量保存配置,這與 12-Factor 所要求的代碼和配置嚴格分離顯然大相徑庭。配置文件在各部署間存在大幅差異,代碼卻完全一致。

在“在環境中存儲配置”的良好實踐中(圖5-4),將配置管理信息存儲在 Git 倉庫中,對配置進行版本化管理,不同環境的配置通過不同分支進行區分和管理,如 reviews 模塊中,每個環境創建一個配置管理分支,如 test-folder-ref 分支存儲 dev 環境的配置信息、test-base 分支存儲 pre-production 環境的配置信息、master 分支存儲 production 環境的配置信息,實現應用配置與運行環境的對應。

img*
圖5-4*

在“在環境中存儲配置”的良好實踐中(圖5-4),應將應用的配置存儲於環境變量中。在 Kubernetes 應用中,可通過 ConfigMap 和 Secret 對象的 Key-Value 存儲配置信息,爲不同環境創建不同的 ConfigMap 和 Secret,不同環境的 ConfigMap 和 Secret 的 Key 相同,但是 Value 不同;在容器環境變量中引用 ConfigMap 和 Secret 的 Key,實現不同環境不同 Value 的注入,並在應用啓動時,加載注入到容器環境的 Key,獲取到對應環境的 Value。通過將應用的配置存儲於環境變量中,可以有效管理不同環境的配置。

後端服務:把後端服務當做附加資源

後端服務是指程序運行所需要的通過網絡調用的各種服務,如數據庫(MySQL),消息/隊列系統(RabbitMQ),SMTP 郵件發送服務(Postfix),以及緩存系統(Redis)。

12-Factor 應用不會區別對待本地或第三方服務。 對應用程序而言,兩種都是附加資源,通過一個 url 或是其他存儲在配置中的服務定位/服務證書來獲取數據。12-Factor 應用的任意 部署 ,都應該可以在不進行任何代碼改動的情況下,將本地 MySQL 數據庫換成第三方服務(例如 Amazon RDS)。

在“把後端資源當做附加資源”的良好實踐中(圖5-5),Review 服務所依賴的 MySQL 服務和 Redis 服務都是獨立部署的,應用服務和附屬的後端服務保持松耦合,通過協議的方式進行訪問(MySQL 通過 MySQL 協議,Redis 通過 Redis 協議)。通過“把後端服務當做附加資源”,部署可以按需加載或卸載資源,如 MySQL 服務異常,可從備份中快速恢復,卸載當前數據庫,然後加載新的數據庫,而不需要修改代碼。

img
圖5-5

構建、發佈、運行:嚴格分離構建和運行

基準代碼 轉化爲一份部署(非開發環境)需要以下三個階段(圖5-6-1):

● 構建階段 是指將代碼倉庫轉化爲可執行包的過程。構建時會使用指定版本的代碼,獲取和打包 依賴項,編譯成二進制文件和資源文件。

● 發佈階段 會將構建的結果和當前部署所需配置相結合,並能夠立刻在運行環境中投入使用。

● 運行階段 (或者說“運行時”)是指針對選定的發佈版本,在執行環境中啓動一系列應用程序 進程。

img
圖5-6-1

在“嚴格分離構建和運行”的良好實踐中(圖5-6-2),代碼提交到 CODING 代碼庫後,通過 CODING 持續集成將源代碼打包構建爲二進制文件,將二進制文件存儲在 CODING 製品庫中,二進制文件和配置信息組成發佈包,通過 CODING 持續部署選擇指定發佈包進行程序發佈。通過“嚴格分離構建和運行”,可以更好的實現職責和關注點的分離,同時將代碼打包成二進制文件是一種不可變基礎設施的體現,禁止通過 patch 等方式修改運行時的代碼,有利於代碼的同步和二進制文件的一致性和完整性。

img
圖5-6-2

進程:以一個或多個無狀態進程運行應用

運行環境中,應用程序通常是以一個和多個進程運行的。

無狀態應用程序是一種應用程序,它不會保存在一個會話中生成的客戶端數據,以便在與該客戶端的下一個會話中使用。每個會話都像第一次一樣進行,響應不依賴於前一個會話的數據。相反,有狀態應用程序保存有關每個客戶端會話的數據,並在客戶端下次發出請求時使用該數據。

12-Factor 應用的進程必須無狀態且無共享。任何需要持久化的數據都要存儲在後端服務內,比如數據庫。

在“以一個或多個無狀態進程運行應用”的良好實踐中(圖5-7),在應用設計時需要將應用設計爲無狀態應用程序,同時在 Kubernetes 中採用 Deployment 對象進行聲明式部署。

img
圖5-7

端口綁定:通過端口綁定提供服務

12-Factor 應用完全自我加載 而不依賴於任何網絡服務器就可以創建一個面向網絡的服務。互聯網應用通過端口綁定來提供服務 ,並監聽發送至該端口的請求。

將網絡服務器類庫通過依賴聲明載入應用。例如,Python 的 Tornado, Ruby 的Thin , Java 以及其他基於 JVM 語言的 Jetty。完全由用戶端 ,確切的說應該是應用的代碼發起請求和運行環境約定好綁定的端口即可處理這些請求。

在“通過端口綁定提供服務”的良好實踐中,應在 Dockerfile 中指定端口,該端口與應用綁定的端口一致,在鏡像構建時,會將 Dockerfile 中指定的端口進行暴露和通信。應用訪問可直接通過 Dockerfile 構建的鏡像暴露的端口進行訪問。在雲原生環境下,可在 Kubernetes 的 yaml 文件中的 containerPort 綁定 Dockerfile 中暴露的端口,實現 Kubernetes 資源與 Docker 鏡像端口的綁定。端口綁定這種方式也意味着一個應用可以成爲另外一個應用的 後端服務 ,調用方將服務方提供的相應 URL 當作資源存入配置以備將來調用

img
圖5-8

併發:通過進程模型進行擴展

任何計算機程序,一旦啓動,就會生成一個或多個進程(圖5-9-1)。互聯網應用採用多種進程運行方式。

在 12-factor 應用中,進程是一等公民。12-Factor 應用的進程主要借鑑於 unix 守護進程模。12-Factor 應用的進程所具備的無共享,水平分區的特性 意味着添加併發會變得簡單而穩妥。這些進程的類型以及每個類型中進程的數量就被稱作進程構成

img
圖5-9-1

在“通過進程模型進行擴展”的良好實踐中(圖5-9-2),面對併發,提倡通過水平擴展進程的數量(增加應用的副本數量,即部署多個相同資源的進程),而非垂直擴展進程的容量(如增加單一進程的CPU、內存等資源)。在雲原生應用中,可充分利用Kubernetes的水平縮擴容的能力。通過設置repilcas的值來調整副本的數量,這種水平(橫向)擴展進程的方式讓併發變得簡單、高效和安全。

img
圖5-9-2

易處理:快速啓動和優雅終止可最大化健壯性

12-Factor 應用的進程是易處理(disposable)的,意思是說它們可以瞬間開啓或停止。 這有利於快速、彈性的伸縮應用,迅速部署變化的代碼或配置 ,穩健的部署應用。

進程應當追求最小啓動時間 。理想狀態下,進程從敲下命令到真正啓動並等待請求的時間應該只需很短的時間。更少的啓動時間提供了更敏捷的發佈以及擴展過程,此外還增加了健壯性,因爲進程管理器可以在授權情形下容易的將進程搬到新的物理機器上。

進程一旦接收終止信號(SIGTERM) 就會優雅的終止 。就網絡進程而言,優雅終止是指停止監聽服務的端口,即拒絕所有新的請求,並繼續執行當前已接收的請求,然後退出。

快速啓動良好實踐

在“快速啓動”的良好實踐中(圖5-10),採用 Docker 鏡像方式進行應用打包,Docker 鏡像中包含應用本身及其所有的運行時依賴,能夠快速複製到新環境中,並能夠快速進行應用的部署。同時,Docker 鏡像具備強大的跨平臺和可移植性,可以快速將進程遷移到新的服務器上。

優雅終止良好實踐

在“優雅停機”良好實踐中(圖5-10),有兩個層面可進行設置,第一個層面是應用層面,若採用 Springboot 框架開發的應用,可在 Springboot 中進行優雅停機參數配置,通過設置 shutdown: graceful 即可實現應用的優雅終止。第二個層面是 Kubernetes 層面,在 Kubernetes yaml 配置中,可以通過 lifecycle 生命週期的 preStop 鉤子,設置 terminationGracePeriodSeconds,即可實現容器鏡像的優雅終止。通過在應用配置層面和 Kubernetes yaml 中的鏡像層面進行優雅終止設置,可實現應用優雅停機。

img
圖5-10

開發環境與線上環境等價:儘可能保持開發環境,預發佈環境,線上環境相同。

從以往經驗來看,開發環境(即開發人員的本地 部署)和線上環境(外部用戶訪問的真實部署)之間存在着很多差異。這些差異表現在以下三個方面:

時間差異: 開發人員正在編寫的代碼可能需要幾天,幾周,甚至幾個月纔會上線。

人員差異: 開發人員編寫代碼,運維人員部署代碼。

工具差異: 開發人員或許使用 Nginx,SQLite,OS X,而線上環境使用 Apache,MySQL 以及 Linux。

12-Factor 應用想要做到 持續部署 就必須縮小本地與線上差異

傳統應用 12-factor 應用
每次部署間隔 數週 幾小時
開發人員 vs 運維人員 不同的人 相同的人
開發環境 vs 線上環境 不同 儘量接近

在“儘可能保持開發環境,預發環境,線上環境相同”的良好實踐中(圖5-11)。在時間差異上,通過 CODING CI/CD 流水線縮短部署實踐,並且可以採用自動化的方式,加速應用的交付速度,降低每次部署間隔實踐;在人員差異上,採用誰開發,誰構建,誰運行原則,通過設置開發、構建、發佈運行權限,實現開發運維一體化,授權團隊具備端到端交付能力;在工具差異上,反對在不同環境中使用不同的後端服務,盡最大努力消除使用上的差異,同時使用 IaC(基礎設施即代碼)工具(如Terraform)進行各類環境資源的創建和維護,並將 IaC 配置信息存儲到代碼庫中,在 CI/CD 根據 IaC 配置自動創建環境資源,實現各類環境的等價。

img
圖5-11

日誌:把日誌當做事件流

日誌使得應用程序運行的動作變得透明。在基於服務器的環境中,日誌通常被寫在硬盤的一個文件裏,但這只是一種輸出格式。

日誌應該是事件流的彙總,將所有運行中進程和後端服務的輸出流按照時間順序收集起來。儘管在回溯問題時可能需要看很多行,日誌最原始的格式確實是一個事件一行。日誌沒有確定開始和結束,但隨着應用在運行會持續的增加。

12-factor 應用本身從不考慮存儲自己的輸出流。 不應該試圖去寫或者管理日誌文件。相反,每一個運行的進程都會直接的標準輸出(stdout)事件流。開發環境中,開發人員可以通過這些數據流,實時在終端看到應用的活動。

在“把日誌當做事件流”的良好實踐中(圖5-12),應該使用 logstash 或 Loki 等工具以事件流的方式對日誌進行輸出,並將輸出的事件流發送到 Splunk,ElasticSeach 等日誌索引及分析系統中,統一對日誌進行存儲和檢索(圖5-12的良好實踐)。日誌信息不應該以文件的形式存儲在運行節點的磁盤上(圖5-12的不良實踐)。

img
圖5-12

管理進程 後臺管理任務當做一次性進程運行

進程構成(process formation)是指用來處理應用的常規業務(比如處理 web 請求)的一組進程。與此不同,開發人員經常希望執行一些管理或維護應用的一次性任務,例如:

● 運行數據移植,如數據庫定期備份。

● 運行一個控制檯 ,或是其他命令,如定期線上數據庫狀態檢查。

● 運行一些提交到代碼倉庫的一次性腳本。如應用部署前運行數據庫腳本

在“後臺管理任務當做一次性進行運行”的良好實踐中,應充分利用 Kubernetes 的 Job 和 CornJob 對象(圖5-13)。對於只執行一次的後臺管理任務,如應用部署前進行數據庫表結構和表數據的導入,可以使用 Kubernetes Job 對象進行一次性進程的管理;對於重複性的後臺管理任務,如每日凌晨兩點對數據庫進行備份,可以使用 Kubernetes CronJob 對象進行重複性進程的管理,通過設置 CrobJob 中的 schedule 的 Cron 表達式,即可實現重複性後臺管理任務的執行。

img
圖5-13

點擊這裏,前往 CODING 體驗

img

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