持續交付:雲原生應用的「十二要素」
- 2019 年 11 月 5 日
- 筆記
- 使用聲明式格式來建立自動化,最大限度減少新人加入項目的時間和成本。
- 與底層作業系統形成一個乾淨的合約,為在不同執行環境之間移植提供最大的可能。
- 適於部署在現代的雲平台上,最大限度減少對伺服器和系統管理的需要。
- 最大限度地減少開發環境和生產環境之間的差異,使持續部署實現最大的靈活性。
- 可以水平擴展,但不需要對工具、架構或者開發方式進行重大改變。
獨立系統的架構原則
「獨立系統的架構原則」(https://isa-principles.org/)與「十二要素應用」密切相關,但前者更注重架構方面。這些原則基於微服務,尤其是自包含系統(SCS)的經驗,是一組最佳實踐的集合。
現在讓我們簡要介紹一下這些要素,看看它們如何用來持續部署 Java 應用程式。
- 程式碼庫 :在版本控制系統中跟蹤的一個程式碼庫,會被多次部署 應該將每個 Java 應用程式(或者服務)的程式碼,存儲在一個共享的程式碼倉庫中。部署所使用的配置文件(例如,腳本、Dockerfile 和 Jenkinsfile)也應當與應用程式程式碼存儲在一起。
- 依賴關係 :顯式聲明並隔離依賴項 我們通常會使用構建工具(例如,Maven 或 Gradle)來管理 Java 應用程式中的依賴關係,並且應該在虛擬機(VM)鏡像清單、Dockerfile 或者無伺服器架構的配置文件中,明確指出作業系統方面的依賴項。
- 配置 :在環境中存儲配置 Twelve-Factor App 建議通過環境變數將配置資訊注入應用程式。實際上,許多 Java 開發人員更喜歡使用配置文件來管理這些變數,而且在構建包含密碼的 VM 或者容器時,通過環境變數來指定密碼可能會存在潛在的安全問題。 將非敏感的配置數據存儲在 Spring Cloud Config(基於 Git 或 Consul)等遠程服務中,並且將密碼存儲在 HashiCorp 的 Vault 服務中,既可以滿足 Twelve-Factor 的建議,也符合當前的最佳實踐。
- 支援服務 :將支援服務視為一種附加資源(一般通過網路進行調用) 在構建管道的組件測試中,Java 開發人員習慣用這種方式來代替數據存儲和中間件,例如,使用記憶體資料庫(例如,HSQLDB、Apache Qpid 和 Stubbed Cassandra)或者服務虛擬化(例如,Hoverfly 和 WireMock)框架。
- 構建、發布、運行 :嚴格分離構建和運行階段 對於 Java 等編譯語言,這條規範顯而易見(並且幾乎沒有其他方式可選擇)。值得一提的是,VM 和容器技術的靈活性意味著,你可以使用合理配置的單獨構件來構建、測試和運行應用程式。例如,你可以使用一個完整的 OS、JDK 以及診斷工具,創建一個用於構建和測試的部署構件,也可以在生產環境中僅用 OS 和 JRE 創建一個運行應用程式的構件。 但是,我們將此視為一種反模式,因為只應該有一個構件進入構建管道,即「單一事實來源」。使用多個構件很容易導致開發環境和生產環境的配置不同,當發生問題時難以調試。
- 進程 :將應用程式作為一個或多個無狀態的進程執行 藉助 VM 鏡像、容器鏡像或者無伺服器函數等技術,可以讓構建和運行 Java 微服務應用程式變得更加容易。
- 埠綁定 :通過埠綁定暴露服務 Java 開發人員習慣通過埠暴露應用程式的服務(例如,在 Jetty 或 Apache Tomcat上運行一個應用程式)。
- 並發 :通過進程模型進行伸縮 傳統的 Java 應用程式通常會採用相反的方式,因為運行中的 JVM 就像是一個巨大的「超級進程」,通常會通過添加更多堆記憶體來實現垂直伸縮,或者通過鏡像和負載均衡來實現多個實例的水平伸縮。不過,將 Java 應用程式分解為微服務,然後在VM、容器或無伺服器函數中運行這些服務,也可以實現可伸縮性。無論採用何種方法來實現可伸縮性,都應該在構建管道中進行測試。
- 可處置性 :通過快速啟動和優雅關閉保證最大的穩定性 對於習慣於創建傳統的、長期運行的 Java 應用程式的開發人員而言,這可能需要轉變觀念,因為大部分應用程式的配置和初始化,都是在 JVM 或者應用程式啟動過程中預先載入的。但是現代化的、容器化的應用程式會使用更多的即時(JIT)配置,確保應用程式在關閉期間盡最大努力清理資源和狀態。
- 開發 / 生產環境差異 :儘可能保持開發、預發布和生產環境一致 與傳統的物理機部署相比,將 VM 或容器技術與 VMware、Kubernetes 和 Mesos 等編排技術結合使用,可以降低開發環境和生產環境之間的不同,因為在物理機器環境中,開發或測試機器的底層硬體和作業系統配置可能會有顯著差異。 隨著構件在構建管道中的不斷構建,它會越來越接近真實的環境(例如,單元測試可以在一個記憶體的沙箱中運行)。但是,端到端測試應該儘可能在類似生產環境的環境中進行。
- 日誌 :將日誌視為事件流 Java 與日誌框架之間存在著長期、重要的關係,但是像 Logback 和 Log4j2 等現代的日誌框架,已經可以將日誌流式傳輸到標準輸出或磁碟上。
- 管理進程 :一次性運行管理任務 因為容器和無伺服器函數可以非常簡單地運行 Java 應用程式,所以管理任務可以一次性運行。但是,它們也必須在構建管道內(或者作為其中的一部分)進行測試。
十二要素應用的各項原則,說明了在設計系統時,不僅要了解背後的部署方式,還要積極地利用它。接下來,我們要介紹與此密切相關的一個話題 — 機械同情心。
培養機械同理心
Martin Thompson 和 Dave Farley 多年來一直在討論軟體開發的「機械同理心」。他們的靈感來自一級方程式賽車手 Jackie Stewart 的名言,「要想成為一名賽車手,你不必成為一名工程師,但你必須有機械同理心。」了解賽車的運作方式會讓你成為更好的賽車手,同樣,程式設計師如果理解電腦硬體的工作原理也會有助於編程。你不一定需要獲得電腦科學學位或者成為一名硬體工程師,但是你確實需要了解硬體的工作原理,並在設計軟體時考慮這一點。
架構師坐在象牙塔里繪製 UML 圖的日子已經結束。架構師和開發人員必須通過新的技術,不斷增加實踐經驗和運維經驗。PaaS、CaaS 和函數已經從根本上改變了軟體與硬體交互的方式。實際上,許多現代的 PaaS 平台和基於函數的解決方案,背後都使用了容器技術來提供進程隔離,所以你應該了解以下這些變化 :
- PaaS 和容器技術可以限制對系統資源的訪問,避免開發、運維操作或者資源競爭導致資源耗盡。
- 容器技術(偶爾)會將不正確的可用資源暴露給 JVM(例如,通常暴露給容器化 JVM 應用程式的處理器核心數量,是底層主機的硬體屬性,而不是該容器的數量限制)。
- 在運行 PaaS 時,通常會在作業系統上附加一層抽象層(例如,編排框架、容器技術,以及一個附加的 OS)。
- 與傳統的部署平台相比,PaaS 平台和容器編排、調度框架通常會更頻繁地停止、啟動和移動容器(以及應用程式)。
- 公有雲 PaaS 和容器平台的硬體結構,天生會更為短暫。
- 容器化和無伺服器應用程式,會出現新的安全攻擊漏洞,必須加以關注和處理。
開發人員不應該對這些部署結構的變化感到驚訝,因為新技術自然會帶來一些變化(例如,升級應用程式的 JVM 版本、在容器中部署 Java 應用程式,以及在雲計算平台上運行 Java 應用程式)。通過加強 CD 構建管道中的測試過程,可以預防絕大多數潛在的問題。
面向失敗的設計和持續測試
雲計算為開發人員提供了絕佳的機會,十年前,我們只能夢想像現在一樣,點一下按鈕就可以將硬體組裝起來。但這種基礎設施也帶來了新的挑戰。由於雲計算具有網路化、成本低廉、規模巨大等特點,所以雲平台的性能問題和故障也是不可避免的。
雲平台中的絕大多數 I/O 操作都通過網路進行。例如,看上去是本地的彈性塊存儲通常由一個存儲區域網路(SAN)提供,性能特徵差異很大。如果你在本地電腦上開發一個應用程式,包含三個頻繁訪問資料庫的服務,那麼你就會發現,同樣是訪問 localhost迴環網路和直接訪問 SSD 塊存儲的操作,本地環境和雲平台上的性能差異很大。這甚至可以決定一個項目的成敗。
大多數雲計算的基礎設施天生就是短暫的,與本地環境的硬體相比,也更容易出現故障。結合我們大多數人在設計分散式系統時所遵循的原則,你必須設計出能夠容忍服務消失或者被重新部署的系統。當許多開發人員在考慮如何測試這類故障時,腦海中都會浮現出 Netflix Simian Army 和 Chaos Monkeys 工具。但是,這類測試通常是在生產環境中進行的。在開發 CD 構建管道時,你還需要實現一個有限的但是功能相當的混亂測試,只不過在故障類型和效果方面更加可控。
松耦合的系統通常更容易測試,因為你可以更容易地隔離組件,而高內聚有助於在修復錯誤時,降低尋找問題原因的難度。本節要介紹的主要內容是,持續交付管道必須儘快在實際環境(類似生產環境)中進行部署和測試,並且必須可以模擬和測試性能和故障場景。
本文節選自新書《Java 持續交付》。持續交付為業務的持續發展和整個軟體的交付周期增加了巨大的價值,但是掌握這項技術就意味著普通開發人員要跳出舒適區,學習很多新技能。本書以實踐為基礎,通過大量的實踐指導,幫助Java開發人員掌握針對不同平台的架構設計、自動化品質保證, 以及打包和部署的技巧。左下閱讀原文,掀開名著面紗——「持續精進,交付經典」,前沿技墅不變的承諾!

內容簡介:本書完整介紹了Java軟體開發的整個生命周期,還結合大量的成功實踐經驗,介紹了每個階段可能會使用到的工具和技能。在基礎設施已經極大完善的今天,本書還與時俱進地介紹了如何在Docker、Kubernetes、Cloud、FaaS等新興環境下進行持續集成和持續交付。