【學長姊帶路】IaC Part 11 -Building Servers as Code

原標《IaC Part 11 -Building Servers as Code》

文/運用雲端服務加速企業數位轉型

IaC最初是作為配置伺服器的一種方式出現的。 系統管理員編寫 shell、batch和 Perl scripts。 CFEngine 率先使用宣告式、冪等(idempotent) DSL 來安裝套件和管理伺服器上的設定文件,CHEF和Puppet也隨後出現。 這些工具假設是我們有既存伺服器 — — 通常是機架中的實體機,有時是使用 VMware 的VM,後來是Cloud Instance。

現在,我們要嘛專注於Infra stack,其中伺服器只是一部分,要嘛我們使用Container cluster,其中伺服器是其底層架構。

但伺服器仍然是大多數Application runtime環境的重要組成部分。 大多數已經存在多年的系統至少在伺服器上運行一些應用程式。 即使是運行cluster的團隊通常也需要為host node建置和運行伺服器。

伺服器比其他類型的基礎設施(例如網路和儲存)更複雜。 它們有更多的移動部件和變化,因此大多數系統團隊仍然花費大量時間陷在作業系統、軟體包和設定檔中。

本文介紹了以代碼形式建置和管理伺服器配置的方法。 它從伺服器的內容(需要配置的內容)和伺服器生命週期(配置活動發生時)開始。 然後轉向伺服器配置代碼和工具的視圖。 本文的主要內容著重於建立server insatnce的不同方法、如何預先建置伺服器以便可以建立多個一致性的instance,以及在整個伺服器生命週期中應用伺服器配置的方法。

下圖將伺服器的生命週期視為具有多個過渡階段,這樣的作法對我們管理伺服器會很有幫助。

上圖顯示的基本生命週期有三個過渡階段:

  1. 建立並配置server instance
  2. 更改現有server instance
  3. 刪除server instance

本文介紹建立和配置伺服器。 Part 12介紹如何對伺服器進行便更,Part 13討論建立和更新可用於建立server instance的server image。

甚麼東西在伺服器裡?

思考伺服器上的各種事物以及它們的來源是必須的。

分解伺服器中事物的一種方法是將其分為軟體、配置和資料。 這些類別(如下表所示)對於瞭解設定管理工具應如何處理特定檔案或檔案集是有用的。

軟體:

應用程式、函式庫和其他代碼。 這不需要是可執行檔; 它幾乎可以是任何靜態檔案,並且不會因系統而異。 Linux 系統上的time zone data就是一個例子。而配置管理的方式是會確保每個相關伺服器上的資訊都是相同的,但 不在乎裡面有什麼。

配置(Configuration):
用於控制系統和(或)應用程式如何運作的檔案。 伺服器之間的內容可能會有所不同,具體取決於伺服器的角色、環境、instance等。 這些檔案作為基礎設施的一部分進行管理,而不是由應用程式本身管理的配置。 例如,如果應用程式具有用於管理使用者設定檔的 UI,則從基礎架構的角度來看,儲存使用者設定檔的資料檔案不會被視為配置檔; 相反,這是資料。 但是,從這個意義上來說,儲存在檔案系統上並由基礎設施管理的應用程式設定檔將被視為配置。而配置管理的方式是會在伺服器上建立檔案內容; 確保其一致。

資料:
由系統和應用程式所產生和更新的檔案。 基礎設施可能對此資料負有一些責任,例如資料的分發、備份或複製。 但基礎設施將檔案的內容視為黑盒子,是不關心它們的內容。 資料庫資料檔案和日誌檔案就是這種意義上的資料範例。而配置管理的對待的方式是它是自然發生和變化; 可能需要保存它,但不會嘗試管理裡面的東西(因為有另一種管理員需要來管理其內部)。

配置檔和資料之間的差異在於自動化工具是否自動管理檔案內的內容。 因此,儘管系統日誌對於基礎架構至關重要,但自動化配置工具仍將其視為資料。 同樣,如果應用程式將其帳號和使用者等資訊儲存在伺服器上的檔案中,則伺服器設定工具會將該檔案視為資料。

東西從哪裡來的?

可以是在建立和設定伺服器時或在改變伺服器時新增構成server instance的軟體、設定和資料。 這些元素有幾個可能的來源(如下圖 ):

  • Base OS
    OS installation image可以是實體磁碟、ISO 檔案或自己做的server image。 OS安裝過程可以選擇可選組件。
  • OS package repositories
    OS安裝或post-installation configuration step可以運行一種工具,從一個或多個儲存庫下載並安裝software package。 OS廠商通常會提供包含他們支援的軟體包的儲存庫。 我們可以使用開源或商業版package的第三方儲存庫。 也可以為內部開發或策劃的套件有一個內部儲存庫。
  • Language、framework與其他平台儲存庫
    除了特定於OS的套件之外,還可以安裝語言或框架的套件,例如 Java函式庫或 Ruby gems。 與OS套件一樣,可以從供應商、第三方或內部團隊管理的儲存庫中存取套件。
  • Nonstandard packages
    一些供應商和內部團隊提供帶有自己的安裝程式的軟體,或者除了運行標準套件管理工具之外還涉及多個步驟。
  • Separate material
    我們可以在應用程式或元件之外新增或修改檔案; 例如,新增使用者帳號或設定本機防火牆規則。

Server Configuration Code

第一代IaC工具專注於自動化伺服器配置。 該領域的一些工具包括:

  • Ansible
  • CFEngine
  • Chef
  • Puppet
  • Saltstack

其中許多工具都使用依照pull configuration pattern安裝在每台伺服器上的代理程式。 它們提供了一個代理,可以將其安裝為服務,也可以透過 cron job定期執行。 其他工具設計為從中央伺服器運行並按照push pattern連接到每個託管伺服器。

我們可以使用這些工具中的大多數來實現pull或push模式。 如果我們使用像 Ansible 這樣專為push模式設計的工具,可以將其預先安裝在伺服器上並從 cron 運作。 另一方面,如果我們使用 Chef 或 Puppet 之類的工具(它提供了與pull模式一起運行的agent),那麼我們可以從中央伺服器執行命令,在每台電腦上登入並執行(用pull)。 因此,該工具不會限制我們使用哪種模式。

許多伺服器配置工具供應商提供儲存庫伺服器來託管配置帶碼。 其中的範例包括 Ansible Tower、Chef Server 和 Puppet master。 它們可能具有附加功能,例如Configuration registry、儀表板、日誌記錄或集中執行。

可以說,選擇提供all-in-one工具生態系統的供應商可以簡化基礎設施團隊的工作。 然而,如果生態系統的元素可以替換為不同的工具,那麼團隊可以選擇最適合他們需求的部分。 例如,如果我們使用與設定Registry整合的多個工具,我們可能會喜歡使用獨立的通用configuration registry,而不是與伺服器設定工具綁定的Registry。

Server Configuration Code Modules

伺服器配置代碼與任何代碼一樣,如果沒有管理好一定隨著時間的推移變得越來越混亂。 這需要紀律和良好的習慣來避免這種情況。 幸運的是,這些工具提供了將代碼組織和建置為不同單元的方法。

大多數伺服器配置工具都允許代碼組織到模組中。 例如,Ansible 有playbook,Chef 將recipes分組到cookbook中,Puppet 有包含manifests的模組。 我們可以單獨組織、版本化和發布這些模組。

角色(Roles)是一種標記一組模組以應用於伺服器並定義其用途的方法。 與其他概念不同,大多數工具供應商似乎都同意使用術語「角色」來表示此目的,而不是供應商自己編的術語。

其中許多工具還支援對其核心資源模型的擴展。 工具的語言提供伺服器資源實體的模型,例如使用者、套件和檔案。 例如,我們可以編寫一個 Chef Lightweight Resource Provider來新增 Java 應用程式資源,用於安裝和設定由開發團隊編寫的應用程式。 這些擴充類似於stack module。

設計Server Configuration Code Modules

我們應該圍繞一個單一的、有凝聚力的關注點來設計和編寫每個伺服器配置代碼模組(例如,每個cookbook或play)。 該建議遵循關注點分離的軟體設計原則。 一種常見的方法是為每個應用程式提供一個單獨的模組。

例如,可以建立一個模組來管理 Tomcat 等應用程式伺服器。 該模組中的代碼將安裝 Tomcat 軟體、運行該軟體的使用者帳號和群組,以及具有日誌和其他檔案正確權限的資料夾。 它將建立 Tomcat 配置文件,包括配置連接埠和設定。 該模組程式碼還將 Tomcat 整合到各種服務中,例如日誌聚合、監控和process management。

許多團隊發現根據用途不同地設計伺服器配置模組很有用。 例如,可以將模組分為函式庫模組和應用程式模組(對於 Chef,這將是library cookbooks與application cookbooks)。

函式庫模組管理可由應用程式模組重複使用的核心概念。 以 Tomcat 模組為例,可以編寫該模組來取得參數,因此該模組可用於配置針對不同目的而最佳化的 Tomcat instance。

應用程式模組(也稱為wrapper module)導入一個或多個函式庫模組並設定其參數以實現更具體的目的。 我們可以擁有一個安裝 Tomcat 來執行其產品目錄應用程式的 Servermaker 模組,以及另一個安裝 Tomcat 來執行其客戶管理應用程式的 Servermaker 模組。 這兩個模組都使用安裝 Tomcat 的共享函式庫。

伺服器代碼的版控和升級

與任何代碼一樣,我們應該能夠在將伺服器代碼應用到生產系統之前在環境之間測試和改進伺服器代碼。 一些團隊將所有伺服器代碼模組作為一個單元一起版本化和升級,或者將它們構建在一起,或者在將它們組合起來使用之前單獨地構建和測試它們。

如果將每個模組作為單獨的Unit進行管理,則需要處理它們之間的任何依賴關係。 許多伺服器配置工具可讓我們在descriptor中指定模組的依賴關係。 例如,在 Chef cookbook中,可以在cookbook metadata的 dependent field中列出依賴項。 伺服器配置工具使用此規範下載依賴模模組並將其套用到Server instance。 Server configuration modules的依賴關係管理的工作方式與軟體套件管理系統(例如 RPM 和 Python pip 套件)相同。

我們還需要儲存和管理不同版本的server configuration code。 通常,會有一個應用於生產環境的代版本,而其他版本正在開發和測試。

Server Roles

如前所述,Server Roles是一種定義一組套用於伺服器的configuration module的方法。 Roles還可以設定一些預設參數。 例如,可以建立Application server role:

role: application-server
  server_modules:
    – tomcat
    – monitoring_agent
    – logging_agent
    – network_hardening
  parameters:
    – inbound_port: 8443

將這個Role指派給伺服器會套用 Tomcat、監視和日誌記錄代理程式以及網路強化的設定。 它還為inbound port設定一個參數, network_hardening 模組用來在本地防火牆中open port,同時鎖定其他所有port的參數。

角色(roles)沒有管好可能會變得很亂(一個企業也是如此),因此應該謹慎並一致性地使用它們。 我們可能更喜歡建立細緻度高的角色,將它們組合起來組成特定的伺服器。 可以向特定伺服器指派多個角色,例如Application server、Monitored Server 和 PublicFacing Server。 每個角色都包含少量用於特定用途的伺服器模組。

或者,可能擁有更高等級的角色,其中包括更多模組。 每台伺服器可能只有一個角色,這個角色可能非常具體; 例如,ERP Server 或 Jira Server。

常見的方法是使用role inheritance。 定義一個基本角色,其中包括所有伺服器通用的軟體和配置。 該角色可能具有網路強化、管理使用者帳戶以及用於監視和日誌記錄的代理程式。 然後加入更具體的角色,例如應用程式伺服器或容器主機,包括基本角色,然後添加一些額外的伺服器配置模組和參數:

role: base-role
  server_modules:
    – monitoring_agent
    – logging_agent
    – network_hardening

role: application-server
  include_roles:
    – base_role
  server_modules:
    – tomcat
  parameters:
    – inbound_port: 8443

role: shopping-service-server
  include_roles:
    – application-server
  server_modules:
    – shopping-service-application

此代碼範例定義了層次結構中的三個角色。 shopping-service-server角色繼承了應用程式伺服器角色的所有內容,並加入了一個模組來安裝要部署的特定應用程式。 application-server角色繼承自base-role,增加了Tomcat伺服器和network port configuration。 Base role定義了一組核心的通用configuration modules。

測試伺服器代碼

我們在Part 8解釋了自動化測試和 CD 理論如何應用於基礎設施,Part 9描述了使用Infrs stack來實現這一點的方法。 這兩章中的許多概念都適用於測試伺服器代碼。

漸進式地測試伺服器代碼

代碼設計和測試之間存在著一種自我強化的動態。 為結構清晰的codebase編寫和維護測試變得更加容易。 編寫測試並讓它們全部通過,迫使我們保持乾淨的結構。 將每個變更推送到運行測試的流水線中可以幫助團隊保持持續重構(refactoring)的紀律,以最大限度地減少技術債和設計債。

前面描述的伺服器角色結構由伺服器配置模組組成,這些模組本身可以組織為函式庫模組和應用程式模組,非常適合漸進式測試策略。 一系列的流水現階段可以在不斷增加的整合階段中測試其中的每一個階段,如下圖所示。

每當有人對每個代碼模組提交變更時,都會有一個單獨的階段測試該模組。 伺服器角色也有測試階段。 每當角色使用的模組之一發生變更並通過自己的階段時,伺服器角色階段就會執行測試。 當有人提交角色代碼(role code)的變更(例如新增或刪除模組或更改參數)時,角色測試階段也會運作。

使用伺服器代碼測試甚麼

決定用伺服器代碼測試什麼是很棘手的。 這又回到了測試宣告式代碼是否有任何價值的問題。 伺服器代碼模組,尤其是在設計良好的codebase中,往往較小、簡單且集中。 例如,安裝 Java JVM 的模組可能是一條帶有幾個參數的語法:

package:
  name: java-${JAVA_DISTRIBUTION}
  version: ${JAVA_VERSION}

在實踐中,即使是看似簡單的套件安裝模組也可能有比這更多的代碼,自訂"file paths、configuration files",也許還加上使用者帳號。 但通常沒有太多可測試的東西,只是簡單地重述代碼本身。 測試應關注常見問題、可變結果和代碼組合。

考慮常見問題、實踐中容易出錯的事情以及如何進行健全性檢查。 對於簡單的軟體包安裝,可能需要檢查該命令在預設使用者路徑中是否可用。 因此,測試將呼叫該命令,並確保它已執行:

given command ‘java -version’ {
its(exit_status) { should_be 0 }
}

如果根據傳遞給它的參數,套件可能會產生完全不同的結果,那麼應該編寫測試以確保它在不同情況下代碼行為正確。 對於 JVM 安裝包,可以使用 Java 發行版的不同值來執行測試,例如確保無論選擇哪個發行版,java 指令都可用。

在實踐中,整合的元素越多,測試的價值就越高。 因此,可以為Application server role編寫和運行比該角色包含的模組更多的測試。

當這些模組相互整合和互動時尤其如此。 安裝 Tomcat 的模組不太可能與安裝監控代理程式的模組發生衝突,但安全性強化模組可能會導致問題。 在這種情況下,我們可能希望在Application server role上執行測試,以確認即使在Port被鎖定後Application server也已啟動並接受請求。

如何測試伺服器代碼

大多數自動化伺服器代碼測試都是透過在伺服器或container instance上執行命令並檢查結果來運作的。 這些測試可以斷定資源的存在和狀態,例如套件、檔案、使用者帳號和正在運行的Process。 測試也可能會考察結果; 例如,連接到網路port以證明服務是否會傳回預期結果。 用於測試伺服器條件的熱門工具包括 Inspec、Serverspec 和 Terratest。

測試完整Infra stack的策略包括離線和線上運行測試,線上測試涉及在基礎設施平台上把事物運轉起來。 通常可以使用容器或本機VM離線測試伺服器代碼。 本地作業的基礎設施開發人員可以建立具有最少OS安裝的container instance或本地VM,server configuration code,然後執行測試。

測試伺服器代碼的流水現階段也可以執行相同的操作,在agent上container instance或VM(例如,運行作業的 Jenkins node)。 或者,該階段可以在container cluster上啟動獨立的container instance。 當流水線編排器本身容器化時,這種方法效果很好。

我們可以按照堆疊指南來編排伺服器代碼設定。 這包括在執行測試和報告結果之前編寫設定測試先決條件的腳本,例如container instance。 我們應該執行與從流水線服務進行測試相同的腳本來進行本機測試,以便結果保持一致。

建立新的Server Instance

前面描述的基本伺服器生命週期從建立新的Server Instance開始。 更長的生命週期包括建立和更新server image的步驟,我們可以從中建立server instance,這個我們會在Part 13中提到。 目前,我們的生命週期從建立instance開始,如下圖所示。

完整的伺服器建立流程包括配置server instance,使其完全可供使用。 建立和配置server instance涉及的一些活動包括:

  • 分配基礎設施資源以實例化server instance。 為此,需要從Pool中選擇一台實體伺服器,或選擇一台host server將其作為VM運行。 對於VM,主機上的管理程式軟體分配記憶體和其他資源。 該程序還可以為server instance的磁碟區分配儲存。
  • 安裝OS和初始軟體。 OS可以複製到disk volume上,就像從 AMI 等server image啟動時一樣。 或者,New instance可以執行安裝過程,選擇檔案並將其複製到new instance,可能使用可編寫腳本的安裝程式。 腳本化OS安裝程式的範例包括 Red Hat Kickstart、Solaris JumpStart、Debian Preseed 和 Windowsinstallation answer file。
  • 附加的configuration套用在server instance。 在此步驟中,該程序將按照Part 12中「如何套用伺服器設定代碼」中所述的模式來執行伺服器設定工具。
  • 配置並附加網路資源和Policy。 此過程可以包括將伺服器指派給network address block、建立路由以及新增防火牆規則。
  • 使用服務註冊Server instance。 例如,將新伺服器新增至監控系統。

這些並不是相互排斥的 — 伺服器建立過程可能會使用一種或多種方法來運行這些不同的活動。 可以使用多種機制來觸發server instance的建立。 讓我們逐一回顧這些機制。

手動建立新的server instance

基礎架構平台提供建立新伺服器的工具,通常是 Web UI、命令列工具,有時還包括 GUI 應用程式。 每次建立新伺服器時,都可以選擇所需的選項,包括source image、要指派的資源和網路資訊:

$ jasoncloud server new \
  source-image=stock-linux-4.56 \
  memory=4GB \
  vnet=appservers

雖然使用 UI 和命令列工具來試驗平台很有用,但這並不是創建業務中所需的伺服器的好方法。 前面討論的建立和配置stack的相同原則也適用於伺服器。 手動設定選項容易產生錯誤,並導致伺服器配置不一致、系統不可預測以及維護作業過多。

使用腳本來建立伺服器

我們可以編寫具有一致地建立伺服器的腳本。 此腳本包裝命令列工具或使用基礎設施平台的 API 建立server instance,在腳本代碼或configuration中設定組態選項。 建立伺服器的腳本是可重複使用的、一致的且透明的。 這與Stack的腳本參數模式具有相同的概念。

jasoncloud server new \
  source-image=stock-linux-4.56 \
  memory=4GB \
  vnet=appservers

此腳本與前面的命令列範例相同。 但因為它是在腳本之中,所以團隊中的其他人可以看到其他人如何建立伺服器。 他們可以建立更多伺服器,並確信它們的行為與團隊規範創建的伺服器相同。

在 Terraform 和其他stack management tools出現之前,大多數團隊都編寫了用於建立伺服器的腳本。 我們通常使用設定檔來配置腳本,就像stack configuration模式一樣。 但通常都會花太多時間改進和修復這些腳本。

使用Stack Management Tool來建立伺服器

使用Part 5中討論的stack management tools,我們可以在其他基礎設施資源的背景脈絡中定義伺服器,如下範例所示。 該工具使用平台 API 來建立或更new server instance。

server:
  source_image: stock-linux-4.56
  memory: 4GB
  vnet: ${APPSERVER_VNET}

使用stack management tools建立伺服器如此方便有幾個原因。 一是該工具負責處理我們必須在自己的腳本中實現的邏輯,例如錯誤檢查。 Stack的另一個優點是它可以處理與其他基礎設施元素的整合。 例如,將伺服器附加到Stack中定義的網路結構和儲存。 上面範例中的代碼使用變數 ${APPSERVER_VNET} 設定 vnet 參數,該變數可能會引用在stack code另一部分中定義的網路結構。

配置平台來自動建立伺服器

大多數基礎設施平台可以在特定情況下自動建立新的server instance。 兩個常見的情況是AutoScaling(加入伺服器來處理負載增加)和Auto-recovery(在server instance發生故障時更換成新的)。 通常可以使用stack code來定義它,如以下範例所示。

server_cluster:
  server_instance:
    source_image: stock-linux-4.56
    memory: 4GB
    vnet: ${APPSERVER_VNET}
  scaling_rules:
    min_instances: 3
    max_instances: 9
    scaling_metric: cpu_utilization
    scaling_value_target: 50%
  health_check:
    type: http
    port: 8443
    path: /health
    expected_code: 200
    wait: 90s

此範例告訴平台保持至少 3個、最多 9個server instance運作。 此平台根據需求新增或刪除instance,以將各個instance的 CPU 使用率保持在接近 50% 的水平。

該定義還包括健康檢查。 此檢查會在連接埠 8443 上向 /health 發出 HTTP request,如果request回船 200 HTTP 回應碼,則認為伺服器運作狀況良好。 如果伺服器在 90 秒後沒有傳回預期的回應碼,平台就會認為伺服器已失敗,因此會刪除它並建立一個new instance。

使用Networked Provisioning Tool建立伺服器

在Part 3中,我們提到了bare-metal cloud,它動態配置硬體伺服器。 執行此操作的過程通常包括以下步驟:

  1. 選擇一個未使用的實體伺服器並觸發其以伺服器韌體支援的「network install」模式啟動(例如,PXE boot)。
  2. 網路安裝程式從簡單的引導OS Image引導伺服器來啟動OS安裝。
  3. 下載OS installation image並將其複製到主硬碟。
  4. 重新啟動伺服器以在設定模式下啟動OS,執行無人看管的腳本化OS安裝程式。

有許多工具可以管理此過程,包括:

  • Crowbar
  • Cobbler
  • FAI, or Fully Automatic Installation
  • Foreman
  • MAAS
  • Rebar
  • Tinkerbell

我們可以boot準備好的server image,而不是啟動OS installation image。 這樣做可以為實行preparing servers的其他一些方法創造機會,如下一節所述。

PS:
FaaS serverless code可以在配置新伺服器方面發揮作用。 我們的平台可以在流程中的不同時間點(建立new instance事前,中與後)運行代碼。 範例包括將安全性原則指派給新伺服器、將其註冊到監視服務或執行伺服器設定工具。

Prebuilding Servers

如前所述,新伺服器有多個內容來源,包括OS安裝、從儲存庫下載的軟體包以及複製到伺服器的自訂設定檔和應用程式檔案。

雖然我們可以在建立伺服器時組裝所有這些內容,但還有多種方法可以提前準備伺服器內容。 這些方法可以優化建立伺服器的過程,使其更快、更簡單,並且更容易建立多個具一致性的伺服器。 以下是執行此操作的幾種方法。 此後的部分解釋了在配置過程之前、中、後套用配置的選項。

Hot-Cloning a Server

大多數基礎設施平台都可以容易複製正在運行的伺服器。 以這種方式hot-cloning伺服器既快速又簡單,並且提供在clone時保持一致的伺服器。

當想要在不干擾伺服器運行的情況下探索伺服器或對其進行故障排除時,clone正在運行的伺服器會非常有用。 但它也存在一些風險。 一是為實驗而製作的生產伺服器副本可能會影響生產環境。 例如,如果複製的應用程式伺服器配置為使用生產資料庫,很可能會意外污染或損壞生產資料。

在生產環境中運行的clone伺服器通常包含來自原始伺服器的歷史數據,例如日誌檔案資訊。 這種資料污染會使故障排除變得混亂。 會導致人們花時間思考錯誤訊息,結果發現這些錯誤訊息並不是來自他們正在除錯的伺服器。

而且clone伺服器並不能真正複製。 在不同時間點從同一parent server複製的兩個伺服器會有所不同,並且無法返回並建立第三個伺服器並完全確定它是否以及如何與其他任何伺服器不同。 clone伺服器是configuration drift的根源。

使用Server Snapshot

我們可以snapshot正在運行的伺服器的快照並用來建立伺服器,而不是透過直接複製正在運行的伺服器來建立新伺服器。 同樣,大多數基礎設施平台都可以輕鬆地向快照伺服器提供命令和 API 呼叫,從而建立static image。 這樣,可以根據需求建立任意數量的伺服器,並確信每台伺服器都與starting image相同。

然而,從運行中伺服器的快照建立伺服器還有許多hot-cloning的其他缺點。 快照可能會受到來自原始伺服器的日誌、配置和其他資料的污染。 從頭開始建立乾淨的server image會更有效。

建立一個乾淨的Server Image

Server Image是從乾淨、已知的來源建立的快照,以便可以建立多個具一致性Server instance。 可以使用與運作中伺服器快照相同的基礎架構平台功能來執行此操作,但原始server instance永遠不會用作整個系統的一部分。 這樣做可以確保每台新伺服器都是乾淨的。 建構Server image的典型過程是:

  1. 從已知來源建立新的server instance,例如OS供應商的installation image,或基礎架構平台為此目的提供的image。
  2. 應用server configuration code或在伺服器上執行其他腳本。 這些可能會在所有伺服器上安裝想要的標準套件和agent,出於安全目的強化配置,並套用所有最新修補程式。
  3. 對伺服器進行快照,建立一個可用作建立多個server instance的source image。 執行此操作可能涉及將快照標記為用於建立新伺服器的image的步驟。 它還可能涉及標記和版控,以幫助管理多個server image的儲存庫。

我們有時將server image稱為golden image。 儘管有些團隊可能會遵標準步驟清單來手動建立這些image,但我們會立即看到自動化此流程、將server image作為代碼管理的效益。 建置和交付server image將是Part 13的主題。

配置一個新的Server Instance

本文描述了伺服器的元素是什麼、它們來自哪裡、建立new server instance的方法以及預先建置server image的價值。 伺服器建立和配置過程的最後一部分是將automated server configuration code套用至新伺服器。在此過程中有幾個點可以執行此操作,如下圖所示。

可以在建立Server image、從Image建立server instance以及伺服器運行時套用配置:

配置Server Image
在建置將用於建立多個server instance的server image時套用設定。 將此視為配置只做一次並能多次使用。 這通常稱為baking a server image。

配置新的server instance
可以在建立new server instance時套用配置。 將此視為能多次性配置。 這有時稱為“frying a server instance”。

配置正在運行的server instance
將設定套用至已在運作的伺服器。 這樣做的一個常見原因是進行變更,例如應用安全性修補程式。 另一個原因是回復在自動化配置之外所做的任何變更以強制一致性。 一些團隊也應用配置來改造現有伺服器; 例如,將Web server變成Application server。

最後一個,配置正在運行的Server instance,通常用於更改伺服器,這是Part 12的主題。前兩個是創建新伺服器時套用Configuration的選項,因此完全屬於本文的範圍。 主要問題是確定為新伺服器套用程式配置的正確時間 — 將其套用(frying)到每個new server instance中,還是將其放在到server image中?

Frying a Server Instance

如前所述,frying server涉及在建立new server instance時應用配置。 我們可以將其發揮到極致,將每個server image保持在最低限度,並將特定內容給予於特定伺服器的所有內容建置到Live image中。 這樣做意味著新伺服器始終擁有最新的變更,包括系統修補程式、最新的軟體包版本和最新的配置選項。 Frying server是交付時間整合的一個例子。

這種方法簡化了Image管理。 Image並不多,基礎架構中使用的硬體和OS版本的每種組合可能只有一個,例如,一個用於 64 位元 Windows 2019,一個用於 32 位元和 64 位元 Ubuntu 18.x。 這些Image不需要經常更新,因為它們沒有太多變化。 無論如何,我們可以在配置每台伺服器時套用最新的補丁。

某些團隊在採用基礎設施自動化的早期就專注於Frying。 設定用於管理伺服器image的工具和流程需要花費大量精力。 我們將這項工作放在待辦事項中,以便在建立核心基礎設施管理後完成。

在其他情況下,Frying是有意義的,因為server變化很大。 例如一家託管公司讓客戶從大量客製化server中進行選擇。 該公司維護極簡的base server image,並在配置每台新伺服器的自動化方面投入更多精力。 建立每個instance時安裝和配置伺服器元素有以下潛在問題:

速度
建置伺服器時發生的活動會增加建立時間。 對於馬上要伺服器上線以處理負載峰值或從故障中恢復來說,增加的時間是一個特殊問題。

效率
配置伺服器通常涉及透過網路從儲存庫下載套件。 這可能既浪費流量又緩慢。 例如,如果需要在短時間內啟動 20 台伺服器,讓每台伺服器下載相同的修補程式和應用程式安裝程式就很浪費資源。

依賴項
配置伺服器通常取決於工件(artifact)儲存庫和其他系統。 如果其中任何一個離線或無法存取,我們將無法建立新伺服器。 在需要快速重建大量伺服器的緊急情況下,這可能會特別痛苦。 在這些情況下,網路設備或儲存庫也可能會關閉,從而需要建立一個複雜的流程圖,其中首先重新啟動或重建哪些系統以使下一組系統能夠依序啟動。

Baking Server Images

伺服器建立光譜的另一端是將幾乎所有內容配置到server image中。 建置新伺服器就會非常快速且簡單,因為只需要套用特定於instance的配置。 Baking Server Images是建置時整合的一個範例。

從Frying的缺點可以看出Baking的優點。 將 Configuration baking到server image中特別適合使用大量類似server instance的系統,以及當需要能夠頻繁、快速地建立伺服器時。

Baking server image的一項挑戰是,我們需要設定工具和自動化流程,以便輕鬆更新和推出新版本。 例如,如果針對OS或嵌入到server image中的關鍵軟體包發布了重要的安全補丁,我們希望能夠建立new image並將其快速部署到現有伺服器,同時將中斷降至最低。 Part 13介紹如何執行此操作。

Baking server image的另一個問題是速度。 即使有成熟的自動化更新image流程,建置和發布new image也可能需要很長時間(通常為 10 到 60 分鐘)。 我們可以透過將變更推送到正在運行的伺服器或透過結合Frying和baking的流程來緩解這種情況。

將Baking與Frying組合

決定何時是應用特定配置的正確時間的主要考量因素是所需的時間和更改的頻率。 應用時間較長且更改頻率較低的內容很適合baking server image。 例如,可以在server image上安裝應用程式伺服器軟體,從而更快地啟動多個server instance並在此過程中節省網路頻寬。

在這種權衡的另一面,安裝速度更快或更改更頻繁的東西最好是Frying。 內部開發的應用程式就是一個例子。 按需(On-demand)啟動新伺服器的最常見用例之一是將測試作為軟體發布過程的一部分。

高生產力的開發團隊每天可能會推出十幾個或更多應用程式的新版本,並依靠 CI 流程自動部署和測試每個版本。 對於這種工作節奏來說,為每個新應用程式建立產生new server image太慢,因此在建立測試伺服器時部署應用程式會更有效率。

團隊將baking和frying結合起來的另一種方法是盡可能多地baking server image,但Frying更新的版本。 團隊可能會以較慢的速度baking updated server image(例如,每週或每月)。 當他們需要更新通常baking到image上的內容時,例如安全性修補程式或配置改進,他們可以將其放入伺服器創建過程中,將其放在baked image之上。

當需要baking updated image時,他們會將updated溶入到image中並將其從建立instance過程中刪除。 這種方法可以讓團隊快速融入新的變更,同時減少overhead。 團隊經常將其與持續應用代碼結合使用來更新現有伺服器,而無需重建它們。

建立伺服器時套用Server configuration

大多數用於以前面討論的方式建立伺服器的工具,無論是命令列工具、平台 API call還是stack management tool,都提供了套用server configuration code的方法。 例如,stack management tool應該具有支援流行工具或在new server instance上執行命令的語法,如以下範例所示。

server:
  source_image: stock-linux-4.56
  memory: 4GB
  vnet: ${APPSERVER_VNET}
  configure:
    tool: servermaker
    code_repo: servermaker.shopspinner.jas
    server_role: appserver
    parameters:
      app_name: catalog_service
      app_version: 4.5.6

此代碼執行 Servermaker 工具,向其傳遞託管的server configuration code的伺服器主機名稱、套用於伺服器的角色 (appserver) 以及傳遞給伺服器配置代碼的一些參數(app_name 和 app_version)。

有些工具還允許我們將伺服器配置代碼直接嵌入到stack code或要執行的 shell command中。 使用它來實現server configuration logic可能很吸引人,並且對於簡單的需求,這可能沒問題。 但在大多數情況下,該代碼的大小和複雜性都會隨時間的推移而增加。 因此,最好提取代碼以保持codebase的整潔和可維護性。

結論

本文涵蓋了建立和配置新伺服器的各個面向。 伺服器上的事項類型包括軟體、配置和資料。 這些通常取自base OS installation和各個儲存庫的軟體包,包括由OS和language vendors、第三方和內部團隊管理的軟體包。 我們通常會透過組合server image中的內容並使用伺服器配置工具來套用其他套件和配置來建立伺服器。

※本文由 運用雲端服務加速企業數位轉型 授權勿任意轉載,原文《IaC Part 11 -Building Servers as Code》

瀏覽 39 次

覺得不錯的話就分享出去吧!

發佈留言

Back to top button