持續整合是持續佈署的根本,它專注於程式碼的頻繁整合、建置和測試,確保程式碼品質。然而,持續整合本身並不能保證軟體隨時可以佈署到生產環境。持續交付在此基礎上更進一步,強調自動化測試和佈署流程,確保每個透過測試的版本都能夠快速且可靠地佈署。而持續佈署則將自動化推向極致,自動將透過所有測試的版本佈署到生產環境,無需人工干預。微服務架構的興起,為軟體開發帶來了更高的靈活性,但同時也增加了佈署的複雜性。容器技術的出現,特別是 Docker 的普及,有效解決了這個問題,讓微服務的佈署變得更加簡單和可靠。Docker 容器的輕量級特性,使得在單一伺服器上執行多個微服務成為可能,提升了資源利用率。

持續整合、微服務與容器:實務突破

乍看之下,持續佈署(CD)、微服務(MS)與容器像是三個無關的主題。畢竟,DevOps 運動並未規定微服務是持續佈署的必要條件,微服務也不一定要封裝成容器。然而,當這三者結合時,卻開啟了新的大門,等待我們跨越。最近在容器領域的發展以及不可變佈署的概念,使我們能夠克服許多微服務曾經面臨的問題。反過來,它們也讓我們獲得了靈活性和速度,沒有這些,持續佈署就不可能或不具成本效益。

在進一步探討這個思路之前,我們將嘗試正確定義每個術語。

持續整合

要了解持續佈署,我們應該首先定義它的前身:持續整合和持續交付。

在專案開發過程中,整合階段往往是軟體開發生命週期中最痛苦的階段之一。我們會花費數週、數月甚至數年的時間,在不同的團隊中開發各自的應用程式和服務。每個團隊都有自己的需求,並盡力滿足這些需求。雖然定期驗證每個應用程式和服務並不困難,但我們都害怕團隊負責人決定將它們整合到一個獨特的交付成果中的那一刻。根據以往專案的經驗,我們知道整合將會出現問題。我們知道會發現問題、未滿足的依賴關係、介面無法正確通訊,以及經理會感到失望、沮喪和緊張。花費數週甚至數月的時間在這個階段並不罕見。最糟糕的是,在整合階段發現的錯誤可能意味著要回頭重新做幾天或幾週的工作。如果有人問我對整合的感受,我會說這是我最接近永久沮喪的時刻。

那是不同的時代。我們認為那是開發應用程式的「正確」方式。

從那時起,很多事情都發生了變化。極限程式設計(XP)和其他敏捷方法論變得熟悉,自動化測試變得頻繁,持續整合開始流行。如今,我們知道過去開發軟體的方式是錯誤的。這個行業已經向前邁進了一大步。

持續整合的定義

持續整合(CI)通常指的是在開發環境中整合、建置和測試程式碼。它要求開發人員經常將程式碼整合到共用儲存函式庫中。「經常」可以有多種解釋,取決於團隊規模、專案規模和投入編碼的時間。在大多數情況下,這意味著開發人員要麼直接推播到共用儲存函式庫,要麼將程式碼與其合併。無論是推播還是合併,這些動作在大多數情況下都應該至少每天進行幾次。將程式碼提交到共用儲存函式庫還不夠,我們需要有一條Pipeline,至少能夠簽出程式碼並執行與該儲存函式庫對應的程式碼相關的所有測試。Pipeline執行的結果可能是紅色或綠色。有些東西失敗了,或者一切都執行得沒有任何問題。在前一種情況下,最低限度的行動是通知提交程式碼的人。

持續整合Pipeline應該在每次提交或推播時執行。與持續交付不同,持續整合沒有明確定義的目標。說一個應用程式與其他應用程式整合並不能告訴我們太多關於它的生產準備就緒程度。我們不知道還需要多少工作才能達到可以將程式碼交付到生產環境的階段。我們真正追求的是知道某次提交沒有破壞任何現有的測試。儘管如此,CI 在正確實施時是一個巨大的改進。在許多情況下,它是一種很難實施的做法,但是一旦每個人都對此感到舒適,其結果往往非常令人印象深刻。

測試驅動開發的重要性

整合測試需要與實作程式碼一起提交,如果不是更早的話。為了獲得最大利益,我們應該以測試驅動開發(TDD)的方式編寫測試。這樣,不僅測試已經準備好與實作一起提交,而且我們知道它們不是有缺陷的,並且無論我們怎麼做都不會透過。TDD 還帶來了許多其他好處,如果你還沒有採用它,我強烈建議你採用它。你可能想要參考 Technology Conversations 部落格的 Test-Driven Development⁴ 一節。

持續整合的前提條件

測試不是 CI 的唯一前提條件。其中一項最重要的規則是,當Pipeline失敗時,修復問題的優先順序高於任何其他任務。如果這個動作被推遲,下一次執行Pipeline時也會失敗。人們會開始忽略失敗通知,慢慢地,CI 程式就會開始失去其目的。我們越早修復在 CI Pipeline執行期間發現的問題,就越好。如果立即採取糾正措施,關於潛在問題原因的知識仍然是新的(畢竟,從提交到失敗通知只隔了幾分鐘),修復它應該是微不足道的。

持續整合的工作流程

具體細節取決於工具、程式語言、專案等許多因素。最常見的工作流程如下:

  • 推播到程式碼儲存函式庫
  • 靜態分析
  • 預佈署測試

這些步驟共同構成了持續整合的核心流程,為軟體開發團隊提供了快速回饋和提高軟體品質的機制。

持續整合的實踐突破:從程式碼提交到佈署

在軟體開發過程中,持續整合(Continuous Integration, CI)是確保程式碼品質和加快開發速度的關鍵實踐。持續整合的核心思想是頻繁地將程式碼變更合併到主分支,並透過自動化的流程來驗證這些變更。

提交程式碼到版本控制系統

開發人員在獨立的分支上進行功能開發,一旦他們認為自己的工作已經穩定,就會將分支合併到主線(或主幹)。更先進的團隊可能會直接提交到主線。關鍵點是主線分支需要經常接收提交,以保持快速的回饋迴圈。如果數天或數週過去,變更累積,持續整合的效益就會降低。

當提交被偵測到時,CI 工具會監控程式碼倉函式庫,並執行 CI 管道。管道本身由一系列自動化任務組成,按順序或平行執行。管道的結果要麼是某個步驟失敗,要麼是成功並推進到下一個階段。

靜態分析:提升程式碼品質的第一步

靜態分析是在不執行程式的情況下分析軟體。它可以突出可能的編碼錯誤,確保遵循約定的格式。雖然靜態分析的效益有時會被質疑,但實施它的成本非常低,因此沒有理由不使用它。

圖 2-2:持續整合管道中的靜態分析步驟

常見的靜態分析工具包括 Java 的 CheckStyle 和 FindBugs,JavaScript 的 JSLint 和 JSHint,以及支援多種語言的 PMD。靜態分析通常是管道中的第一步,因為它的執行速度非常快。

佈署前的測試:確保程式碼正確性

佈署前的測試是持續整合管道中的關鍵階段。這包括所有不需要將程式碼佈署到伺服器就能執行的測試,如單元測試。如果可以執行功能測試而不佈署程式碼,也應該在這階段執行。

圖 2-3:持續整合管道中的佈署前測試步驟

這些測試相對容易編寫,執行速度快,並且比其他型別的測試(如整合測試和效能測試)提供更大的程式碼覆寫率。

封裝和佈署到測試環境

完成所有可以在不佈署應用程式的情況下進行的驗證後,就該封裝應用程式了。封裝的方法取決於框架和程式語言。例如,在 Java 世界中,會建立 JAR 或 WAR 檔案;對於 JavaScript,可能會最小化程式碼並將其傳送到 CDN 伺服器。

程式碼範例:建立 Docker 容器

FROM java:8
VOLUME /tmp
COPY target/myapp.jar myapp.jar
ENTRYPOINT ["java","-jar","/myapp.jar"]

內容解密:

  1. FROM java:8:使用 Java 8 作為基礎映像。
  2. VOLUME /tmp:掛載 /tmp 目錄,用於存放臨時檔案。
  3. COPY target/myapp.jar myapp.jar:將目標目錄下的 myapp.jar 複製到容器中,並重新命名為 myapp.jar
  4. ENTRYPOINT ["java","-jar","/myapp.jar"]:設定容器的入口點,執行 java -jar /myapp.jar 命令來啟動應用程式。

封裝完成後,將佈署包佈署到測試環境。根據伺服器的容量,可能需要佈署到多台機器上,例如,一台專門用於效能測試,其他機器用於其他需要佈署的測試。

持續整合、交付與佈署的技術突破

持續整合流程的深化實踐

在現代軟體開發流程中,持續整合(CI)扮演著至關重要的角色。一個完整的CI流程涵蓋從程式碼提交到佈署至測試環境的全過程。如圖2-4所示,持續整合流程中的封裝與佈署階段是整個流程的核心環節之一。

佈署後測試:確保整合品質的關鍵步驟

當應用程式或服務被佈署到測試環境後,我們會執行剩餘的測試專案。這些測試包括功能測試、整合測試和效能測試,確保應用程式或服務的整合成功與否。測試工具與技術的選擇取決於多種因素,例如所使用的程式語言和測試框架。就個人經驗而言,採用行為驅動開發(BDD)進行功能測試,並使用Gatling進行效能測試,是相當有效的實踐方式。


#### 程式碼範例:使用Gatling進行效能測試
```scala
import io.gatling.core.Predef._
import io.gatling.http.Predef._

class BasicSimulation extends Simulation {
  val httpProtocol = http
    .baseUrl("http://example.com")
    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")

  val scn = scenario("BasicSimulation")
    .exec(http("request_1")
      .get("/"))

  setUp(
    scn.inject(atOnceUsers(1))
  ).protocols(httpProtocol)
}

內容解密:

此範例展示了一個基本的Gatling效能測試模擬。首先,我們匯入必要的Gatling套件,並定義一個名為BasicSimulation的模擬類別。httpProtocol定義了HTTP請求的基本設定,包括基礎URL和接受的內容型別。scn變數定義了一個場景,包含一個GET請求。最後,setUp方法組態了模擬的執行,注入一個使用者並指定使用的HTTP協定。

從持續整合到持續交付與佈署

持續交付:提升流程的自動化與可靠性

持續交付(CD)與持續整合在流程上非常相似,主要差異在於對流程的信心程度。持續交付強調自動化測試與佈署流程,以確保每次成功的構建都可以直接佈署到生產環境。技術上,每個透過測試的構建都是可佈署的,但是否實際佈署取決於業務或行銷考量。


#### 圖示:持續交付流程
此圖示展示了持續交付流程中自動化測試與佈署的關鍵步驟。

持續佈署:實作完全自動化的佈署流程

持續佈署則更進一步,自動將透過所有驗證的構建佈署到生產環境,無需人工干預。這種做法要求開發團隊對自動化測試與佈署流程有極高的信心,並能快速回應可能出現的問題。


#### 圖示:持續佈署流程
此圖示展示了持續佈署流程中從程式碼提交到自動佈署至生產環境的完整自動化流程。

重點解析與技術挑戰

在實施持續佈署時,需要特別關注資料函式庫變更的相容性問題,確保變更是向後相容的,以避免影響現有系統的運作。此外,選擇適當的測試策略和工具對於確保流程的順暢至關重要。

技術選型的考量

在選擇技術和工具時,應考慮專案的需求、團隊的技術堆疊以及工具的可擴充套件性和維護成本。例如,使用Docker容器化技術可以簡化佈署流程,而Kubernetes則可以用於管理容器化應用程式的佈署和擴充套件。


#### 程式碼範例:使用Dockerfile定義容器化應用程式
```dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install -r requirements.txt

COPY . .

CMD ["python", "app.py"]

內容解密:

此Dockerfile範例定義了一個Python應用程式的容器化過程。首先,從Python 3.9的基礎映像開始,接著設定工作目錄並複製requirements.txt檔案到容器中。然後,安裝所需的Python套件,並將應用程式碼複製到容器中。最後,定義了容器啟動時執行的命令。

持續佈署、微服務與容器的實踐突破

在持續整合的過程中,軟體佈署到生產環境並不一定是必要的,但持續交付和佈署則將生產環境(主要是整合)測試視為絕對必要,並且在持續佈署的情況下,這是完全自動化流程的一部分。由於沒有人工驗證,我們需要盡可能確定佈署到生產環境的軟體能夠正常運作。這並不意味著需要重複所有自動化測試,而是需要執行能夠證明佈署的軟體與系統其他部分整合良好的測試。在其他環境中執行相同的整合測試並不意味著由於某些差異,佈署到生產環境的軟體能夠繼續與系統的其他部分「和平共處」。

在持續佈署的背景下,另一個非常有用的技術是功能開關(Feature Toggles)。由於每個構建都被佈署到生產環境,我們可以使用它們來暫時停用某些功能。例如,我們可能已經完全開發了登入介面,但尚未開發註冊功能。如果讓訪客知道一個需要另一個尚未佈署的功能的功能是不合理的。持續交付透過手動批准哪個構建被佈署到生產環境來解決這個問題,並且會選擇等待。然而,在持續佈署的情況下,這種決策是不存在的,因此功能開關是必不可少的,否則我們就需要延遲與主線的合併,直到所有相關功能都完成。然而,我們已經討論過不斷與主線合併的重要性,這種延遲違背了 CI/CD 的邏輯。雖然還有其他方法可以解決這個問題,但我認為功能開關對於所有選擇應用持續佈署的人來說是不可或缺的。

大多數團隊從持續整合開始,然後慢慢轉向交付和佈署,因為前者是後者的先決條件。在本文中,我們將實踐持續佈署。不要害怕,我們所做的一切都可以輕易修改,以便有暫停和手動干預。例如,我們將直接將容器佈署到生產環境(實際上是模擬生產環境的虛擬機器),而無需經過測試環境。在應用本文中的技術時,您可以輕易選擇在中間新增測試環境。

需要注意的重要一點是,我們討論的流程階段是以特定的順序執行的。這個順序不僅是邏輯上的(例如,我們不能在編譯之前佈署),而且也是按照執行時間的順序排列的。執行時間較短的事情先執行。例如,一般來說,佈署前測試往往比佈署後測試執行得更快。同樣的規則也適用於每個階段內部。如果,例如,在佈署前階段有不同型別的測試,請先執行那些執行速度更快的測試。追求速度的原因是直到我們獲得反饋的時間。我們越早發現提交有問題,就越好。理想情況下,我們應該在轉移到下一個開發任務之前獲得反饋。進行提交,喝杯咖啡,檢視收件箱,如果沒有收到任何表示失敗的憤怒電子郵件,就轉移到下一個任務。

微服務

我們已經在持續佈署的背景下討論了速度。這種速度指的是從新功能的概念產生到完全運作並佈署到生產環境的時間。我們希望能夠快速行動,提供最快的上市時間。如果新功能可以在幾個小時或幾天內交付,業務將比需要幾周或幾個月才能看到好處。實作這種速度有多種方法。例如,我們希望流程盡可能快,以便在失敗的情況下提供快速反饋,並為其他排隊的工作釋放資源。我們的目標是從簽出程式碼到將其佈署到生產環境的時間以分鐘計算,而不是以小時計算。微服務可以幫助實作這一目標。執行整個流程對於龐大的單體應用程式來說往往很慢。同樣適用於測試、封裝和佈署。另一方面,微服務由於體積較小,因此速度更快。需要測試的程式碼更少,需要封裝的程式碼更少,需要佈署的程式碼也更少。

我們不會僅僅因為這個原因就轉向微服務。在後面的章節中,您將會找到一整章專門對微服務進行更深入的研究。目前,需要注意的重要一點是,由於當今競爭對我們提出的要求,微服務可能是我們可以應用的最佳架構型別。

容器

在容器變得普遍之前,微服務的佈署很痛苦。另一方面,單體應用程式相對簡單處理。例如,我們會建立一個單一的工件(JAR、WAR、DLL 等),將其佈署到伺服器,並確保所有需要的可執行檔和函式庫(例如 JDK)都存在。這個過程大多數時候是標準化的,需要考慮的事情相對較少。一個微服務同樣簡單,但當它們的數量乘以十、百甚至千時,事情就開始變得複雜了。它們可能使用不同版本的依賴項,不同的框架,不同的應用伺服器等等。我們需要考慮的事情數量開始呈指數級增長。畢竟,微服務背後的一個原因是能夠為工作選擇最佳工具。一個可能用 GoLang 寫得更好,而另一個可能更適合 NodeJS。一個可能使用 JDK 7,而另一個可能需要 JDK 8。安裝和維護所有這些可能會很快讓伺服器變成垃圾場,讓負責管理它們的人發瘋。在那之前,大多數人採用的解決方案是什麼?盡可能標準化。每個人都應該只使用 JDK…

持續佈署、微服務與容器的技術突破

在軟體開發的歷史長河中,我們不斷追求更高效、更可靠的開發與佈署方式。從早期的單體式應用到如今的微服務架構,從虛擬機器到容器的崛起,每一步進化都標誌著技術的重大突破。本文將探討持續佈署、微服務和容器這三者的協同作用,以及它們如何共同推動現代軟體開發的革新。

容器技術的崛起

在微服務架構興起之初,開發者們面臨著諸多挑戰。傳統的開發模式強調標準化與一致性,卻扼殺了創新。開發者們試圖將過去在單體式應用中學到的經驗套用到微服務的佈署上,將後端服務執行在7×24小時的模式下,前端則統一使用JSP,並將公共程式碼放在分享函式庫中。然而,這種做法忽略了微服務本質上與單體式應用的不同。

直到容器技術的普及,尤其是Docker的出現,才徹底改變了這一局面。Docker讓容器變得普及且易於使用,解決了過去虛擬機器資源佔用大、佈署複雜的問題。

什麼是容器?

容器是一種隔離且不可變的映像檔,提供特定的功能,通常透過API存取。它們確保軟體在任何環境下都能可靠執行,無論是在開發者的筆記型電腦上,還是在測試或生產伺服器上。容器透過自給自足和不可變性實作這一目標。

傳統佈署方式將應用程式構件佈署到現有的節點上,期望其他一切都已就緒,例如應用程式伺服器、組態檔和依賴項等。容器則不同,它們包含了軟體執行所需的一切,從二進位檔案、應用程式伺服器到執行時依賴項和作業系統套件。這使得容器比虛擬機器更輕量,特別是在微服務架構下,可以在單一實體伺服器上執行更多的容器。

虛擬機器與容器的資源利用比較

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 虛擬機器與容器的資源利用比較

rectangle "執行" as node1
rectangle "包含" as node2
rectangle "直接執行" as node3
rectangle "分享" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

此圖示說明瞭虛擬機器和容器在資源利用上的差異。虛擬機器各自包含獨立的作業系統,而容器則分享主機的作業系統。

持續佈署、微服務與容器的協同效應

持續佈署、微服務和容器被譽為技術史上的「三劍客」。持續佈署提供了持續自動化的回饋機制,提高了軟體交付的品質並縮短了上市時間。微服務帶來了更大的決策自由度,加快了開發速度,並使擴充套件服務變得更加容易。容器則解決了佈署過程中的諸多問題,尤其是在微服務架構下,大大提高了可靠性。

內容解密:

  1. 持續佈署:自動化軟體交付流程,確保快速回饋與高品質交付。
  2. 微服務:提供靈活的架構選擇,加速開發與擴充套件。
  3. 容器:透過隔離與不可變性,確保軟體在任何環境下的穩定執行。

結合這三者,我們可以實作頻繁且快速的佈署、全自動化流程、零停機時間、隨時可回復的機制,以及跨環境的一致可靠性。同時,它們還能實作無縫擴充套件和自癒系統,進一步提升系統的穩定性和可用性。