IaC Part 10 Application Runtime

--

我們在Part 3中介紹了Application runtime,它是一個作為將系統各部分組織為三層的模型的一部分。 在此模型中,我們可以組合基礎架構層的資源來提供人們可以在其上部署應用程式的runtime platform。

Application Runtime由我們使用基礎架構管理工具定義和建立的基礎架構堆疊組成,如Part 5 所述,並如下圖所示。

設計和實現Application runtime 基礎設施的起點是了解會使用到它的應用程式。 它們跑甚麼語言和execution stack? 它們會被打包並部署到伺服器、容器中還是作為 FaaS serverless code碼? 它們是部署到單一位置的單一應用程式,還是分佈在叢集中的多個服務? 它們的連接和資料要求是什麼?

這些問題的答案有助於理解Application runtime layer運行應用程式所需的基礎設施資源。Application runtime layer的各個部分對應到Part 3「基礎設施資源」章節中描述的基礎設施平台的各個部分。這些部分將包括基於運算資源的執行環境、基於儲存資源構建的資料管理以及由網路資源組成的連結。

本文總結了每種關係,重點介紹將基礎架構資源組織到Application runtime platform中的方法。 它為後面的章節奠定了基礎,這些章節將更詳細地介紹如何以代碼形式定義和管理這些資源-server as code(part 11)和cluster as code(part 14)。

雲原生和應用程式驅動的基礎設施

雲原生軟體的設計和實作是為了利用現代基礎架構的動態特性。 與舊的軟體不同,雲原生應用程式的instance可以在底層基礎設施之間添加、刪除和轉移。 底層平台動態分配運算和儲存資源,並路由進出應用程式的流量。 該應用程式與監控、日誌記錄、身份驗證和加密等服務無縫整合。

Heroku 的人員闡明了建立在雲端基礎設施上運行的應用程式的12因子方法。 雲原生作為一個短語,通常與 Kubernetes 生態系統連結在一起。

許多企業都會有非雲原生的現有軟體組合。 雖然他們可能會轉換或重寫一些軟體以實現雲原生,但在許多情況下,這樣做的成本並不能與效益相符。 應用程式驅動的基礎架構策略涉及使用現代動態基礎架構為應用程式建立application runtime environments。

團隊為作為serverless code或在容器中運作中的應用程式來提供application runtime。 它們還提供基礎設施來支援現有應用程式。 所有基礎設施都作為代碼進行定義、配置和管理。 可以使用抽象層動態提供應用程式驅動的基礎設施。

Application Runtime Targets

實施應用程式驅動的策略首先要分析應用程式組合的runtime要求。 然後,設計application runtime解決方案來滿足這些要求,實現可重複使用stacks、stack components以及團隊可用來組裝特定應用程式環境的其他元素。

應用程式的可部署部分

應用程式的可部署版本可能涉及不同的元素。 撇開文件和其他metadata不談,應用程式部署的範例包括:

  • Executables
    版本的核心是一個或多個可執行檔案,無論它們是Binary還是interpreted scripts。 我們以將可執行檔使用的libraries和其他檔案視為此類別的成員。
  • Server Configuration
    許多應用程式部署套件都會更改伺服器配置。 這些可以包括Process將運行的使用者帳號、資料夾結構以及對系統設定檔的變更。
  • Data Structure
    當應用程式使用資料庫時,部署時可能會建立或更新架構。 架構的特定版本通常對應於可執行檔的版本,因此最好將它們捆綁並部署在一起。
  • Reference data
    應用程式部署可以用初始資料集載入資料庫或其他storage。 這可以是隨新版本而更改的參考資料,也可以是幫助使用者在首次安裝應用程式時開始使用該應用程式的範例資料。
  • Connectivity
    應用程式部署可以指定網路配置,例如網路port。 它還可能包括用於支援連接的元素,例如用於加密或驗證連接的憑證或金鑰。
  • Configuration parameters
    應用程式部署套件可以設定configuration parameters,無論是透過將設定檔複製到伺服器上,還是將設定推送到Registry中。

我們可以在不同的地方在應用程式和基礎架構之間劃清界線。 可以將所需的library捆綁到應用程式部署套件中,或將其作為基礎架構的一部分進行配置。

例如,Container image通常包含一部分的OS以及將在其上運行的應用程式。 immutable server或immutable stack更進一步,將應用程式和基礎架構組合成一個實體。 另一方面,通常特定應用程式的Infra code提供libraries和configuration files在應用程式套件本身中留沒留下甚麼訊息。

這個問題也會影響我們如何管理codebase。 我們是否將應用程式代碼與基礎架構代碼放在一起,還是分開? 代碼邊界問題將在Part 18討論到,但一個原則是,將codebase的結構與可部署的部分保持一致通常是一個好主意。

部署套件

應用程式通常被做成deployment packages,Package格式取決於runtime environment的類型。 下表列出了部署套件格式和相關runtimes的範例。

Deployment package format是一種標準,使部署工具或runtimes能夠提取應用程式的各個部分並將它們放在正確的位置。

部署應用程式到伺服器

伺服器,無論是實體或虛擬,都是傳統的runtime platform。 應用程式使用作業系統的打包格式(例如 RPM、.deb 檔案或 Windows MSI)進行打包。 或以language runtime format打包,例如 Ruby gem 或 Java .war檔。 而Container image(如 Docker image)作為一種打包應用程式並將其部署到伺服器的格式而受到歡迎。

以代碼形式定義和設定伺服器是Part 11的主題。該主題與應用程式部署重疊,因為需要決定何時以及如何執行部署命令。

容器中的打包應用程式

容器將作業系統的依賴項pull到應用程式套件(container image)中,如下圖所示。

在容器中包含依賴項使其比一般OS或language packages更大,但具有以下幾個優點:

  • 容器為運行應用程式創建了更一致的環境。 如果沒有容器,應用程式將依賴libraries、configuration、user accounts以及在不同伺服器上可能不同的其他元素。 容器將運行時環境與應用程式及其相依性捆綁在一起。
  • 容器化應用程式基本上與其運行的伺服器隔離,這使我們可以靈活地選擇運行它的位置。
  • 透過將應用程式的OS相關的東西打包在Container image中,可以簡化和標準化對host server的要求。 伺服器只需要安裝container execution tooling。
  • 減少Application runtime environments的可變性可以提高品質保證。 當我們在一種環境中測試container instance時,可以有理由相信它在其他環境中也會表現相同。

部署應用程式到Server Cluster

在container-based application clusters出現之前,人們就已經將應用程式部署到伺服器群上。 通常的模型是擁有一個server cluster,並在每台伺服器上執行一組相同的應用程式。 可以按照與單一伺服器相同的方式打包應用程序,對Pool中的每台伺服器重複部署過程,或許可以使用 Capistrano 或 Fabric 等遠端命令腳本工具(如下圖)。

如果將應用程式部署到多個伺服器,需要決定如何編排部署。 是否立即將應用程式部署到所有伺服器? 執行此操作時是否需要使整個服務離線? 或一次升級一台伺服器? 我們可以利用伺服器的incremental deployment來實現漸進式部署策略,例如blue/green和canary部署模式。

除了將應用程式代碼部署到伺服器上之外,可能還需要部署其他元素,例如資料結構或連線的變更。

部署應用程式到Application Cluster

如我們在Part 3「運算資源」所述,application hosting cluster是執行一個或多個應用程式的server pool。 與Server cluster中每台伺服器運行相同的應用程式集不同,Application cluster中的不同伺服器可以運行不同的application instances groups(如下圖)。

當我們將應用程式部署到叢集時,Scheduler會決定在哪些host server上執行應用程式的instance。 Scheduler可能會改變這種分佈,根據不同的演算法和設定跨host server新增和刪除Application instance。

過去,最受歡迎的Application cluster是Java-based (Tomcat、Websphere、Weblogic、JBoss 等)。 幾年前出現了一波Cluster管理系統,包括 Apache Mesos 和 DC/OS,其中許多系統受到 Google Borg 的啟發。 近年來,專注於orchestrating container instances的系統已經壓垮了應用程式伺服器和cluster orchestrators。

一旦擁有Cluster,在其上部署單一應用程式可能會很簡單。 使用Docker將應用程式打包並推送到Cluster。 但更複雜的應用程式(具有更多移動部件)具有更複雜的要求。

用於將應用程式部署到Cluster的套件

現代應用程式通常涉及跨複雜基礎架構部署的多個流程和元件。 runtime environment 需要知道如何運行這些不同的部分:

  • 要運行的最小和最大instance數量是多少?
  • Runtime應該如何知道何時新增或刪除instance?
  • Runtime如何知道instance是否健康或需要重新啟動?
  • 需要配置哪些storage並將其附加到每個instance?
  • 連接性和安全性要求是什麼?

不同的Runtime platform提供不同的功能,並且許多都有自己的打包和配置格式。 通常,這些平台使用deployment manifest來引用實際的部署工件(例如container image)的部署清單,而不是包含所有可部署部分的歸檔檔案。Cluster application deployment manifests的範例包括:

  • Helm charts for Kubernetes clusters
  • Weave Cloud Kubernetes deployment manifests
  • AWS ECS Services
  • Azure App Service Plans
  • CNAB Cloud Native Application Bundle

不同的deployment manifests與packages在不同層級上工作。 有些專注於單一可部署單元,因此需要每個應用程式的manifests。 有些定義了可部署服務的collection。 根據工具的不同,Collection中的每個服務可能有一個單獨的manifests,其中更高層級的manifests定義了通用元素和整合參數。

ShopSpinner Web 伺服器部署的manifests可能類似於以下範例中所示的偽代碼。

service:
name: webservers
organization: ShopSpinner
version: 1.0.3
application:
name: nginx
container_image:
repository: containers.shopspinner.xyz
path: /images/nginx
tag: 1.0.3
instance:
count_min: 3
count_max: 10
health_port: 443
health_url: /alive
connectivity:
inbound:
id: https_inbound
port: 443
allow_from: $PUBLIC_INTERNET
ssl_cert: $SHOPSPINNER_PUBLIC_SSL_CERT
outbound:
port: 443
allow_to: [ $APPSERVER_APPLICATIONS.https_inbound ]

此範例指定在何處以及如何尋找Container image(container_image block)、要執行的instance數量以及如何檢查其運作狀況。 它也定義了inbound和outbound連接規則。

佈署FaaS Serverless Applications

在Part 3中,我們列出了一些 FaaS Serverless Applications runtime platforms作為一種運算資源。 其中大多數都有自己的格式來定義Application runtime要求,並將它們與代碼和相關片段打包以部署到runtime instance。

編寫 FaaS 應用程式時,代碼運行的任何伺服器或容器的詳細資訊對我們來說都是隱藏的。 但代碼可能需要基礎設施才能運作。 例如,可能需要用於inbound或outbound connection、storage或message queues的網路路由。 我們的 FaaS 框架可與底層基礎架構平台整合,自動配置必要的基礎架構。 或者可能需要在單獨的堆疊定義工具中定義基礎架構元素。 許多stack tool(例如 Terraform 和 CloudFormation)可讓我們將 FaaS 代碼配置宣告為Infra stack的一部分。

Application Data

資料通常是部署和運行應用程式的事後才想到的。 我們提供資料庫和storage volumes,但與基礎設施的許多部分一樣,對它們進行更改非常困難。 更改資料和結構非常耗時、混亂且有風險。

應用程式部署通常涉及建立或變更資料結構,包括在結構變更時轉換現有資料。 更新資料結構應該是應用程式和應用程式部署過程的關注點,而不是基礎設施平台和運行時的關注點。 然而,當基礎設施和其他底層資源發生變化或發生故障時,基礎設施和application runtime services需要支援維護資料。 我們會在Part 19“不斷變化的系統中的資料連續性”章節中講述,以了解應對此挑戰的方法。

Data Schemas and Structures

有些資料儲存是嚴格結構化的,例如 SQL 資料庫,而其他資料儲存是非結構化或schema-less。 嚴格結構化、模式驅動的資料庫強制執行資料結構,拒絕儲存格式不正確的資料。 使用schema-less資料庫的應用程式負責管理資料格式。

新的應用程式版本可能包括對資料結構的變更。 對於schema-driven的資料庫,這涉及更改資料庫中資料結構的定義。 例如,該版本可能會為資料記錄新增一個field,一個典型的例子是將單一「Name」字段拆分為first name、middle和last name的single field。

當資料結構發生變化時,任何現有資料都需要使用任一類型的資料庫轉換為新結構。 如果您要拆分「Name」field,則需要將資料庫中的名稱分割為single field的程序。

更改資料結構和轉換資料稱為schema migration。 應用程式和部署工具可以使用多種工具和library來管理此流程,包括 Flyway、DBDeploy、Liquibase 和 db-migrate。 開發人員可以使用這些工具將增量資料庫變更定義為代碼。 可以將變更簽入版控,並將其打包為版本的一部分。 這樣做有助於確保資料庫架構與部署到instance的應用程式版本保持同步。

團隊可以使用資料庫演進策略來安全、靈活地管理資料和模式的頻繁變更。 這些策略與敏捷軟體工程方法一致,包括 CI 和 CD 以及IaC。

Cloud Native Application Storage Infrastructure

雲端原生基礎架構按需(On-Demand)動態分配給應用程式和服務。 一些平台提供雲原生儲存以及運算和網路。 當系統新增application instance時,它可以自動配置並附加儲存設備。 我們可以在application deployment manifest中指定儲存需求,包括配置時要載入的任何格式或資料(如下參考範例)。

application:
name: db_cluster
compute_instance:
memory: 2 GB
container_image: db_cluster_node_application
storage_volume:
size: 50 GB
volume_image: db_cluster_node_volume

這個簡單的範例定義了如何為動態擴展的DB Cluster建立節點。 對於每個node instance,平台將使用資料庫軟體建立container instance,並附加從使用empty database segment初始化的Image複製的磁碟區。 當Instance啟動時,它將連接到叢集並將資料同步到其本地磁碟區。

Application Connectivity

除了執行代碼的運算資源和保存資料的儲存資源之外,應用程式還需要網路來實現inbound和outbound連線。 伺服器導向的應用程式套件(例如 Web server)可能會設定網路連接埠並為inbound連線新增加密金鑰。 但傳統上,他們依賴某人在伺服器外部單獨配置基礎架構。

您可以定義和管理addressing、routing、naming、firewall rules和類似問題,作為Infra stack project的一部分,然後將應用程式部署到產生的基礎架構中。 一種更雲原生的網路方法是將網路需求定義為application deployment manifest的一部分,並讓應用程式在runtime動態分配資源。

以下範例呈現了這一點:

application:
name: nginx
connectivity:
inbound:
id: https_inbound
port: 443
allow_from: $PUBLIC_INTERNET
ssl_cert: $SHOPSPINNER_PUBLIC_SSL_CERT
outbound:
port: 443
allow_to: [ $APPSERVER_APPLICATIONS.https_inbound ]

此範例定義inbound和outbound connection,並引用系統的其他部分。 這些是Internet(大概是Gateway)和應用程式伺服器的inbound HTTPS port,使用自己的deployment manifests定義並部署到同一集群。

Application runtimes為應用程式提供許多通用服務。 其中許多服務都是Service discovery的類型。

Service discovery

在基礎架構中運行的應用程式和服務通常需要知道如何找到其他應用程式和服務。 例如,前端 Web 應用程式可以向後端服務發送請求以處理使用者的交易。

在靜態環境中做到這一點並不困難。 應用程式可能會使用其他服務的已知主機名稱,這些主機名稱可能會保存在根據需要更新的設定檔中。

但是,對於動態基礎架構(服務和伺服器的位置是變動的),需要一種更快回應的方式來尋找服務。

一些流行的Discovery機制是:

Hardcoded IP addresses
為每個服務分配IP位址。 例如,監控伺服器始終運行在192.168.1.10上。 如果需要更改位址或執行服務的多個instance(例如,為了主要升級的rollout),則需要重建和重新部署應用程式。

Hostfile entries
使用server configuration在每台伺服器上產生 /etc/hosts 檔(或等效檔案),將服務名稱對應到目前 IP 位址。 這種方法是 DNS 的更混亂的替代方案,但有時它可以用於解決舊版 DNS 實作。

DNS
使用 DNS entries將服務名稱對應到其目前 IP 位址,可以使用由代碼管理的 DNS entries,也可以使用 DDNS(Dynamic DNS)。 DNS 是一個成熟的、得到良好支援的解決方案。

Resource tags
基礎設施資源被標記以呈現它們提供什麼服務以及環境等背景脈絡。 Discovery涉及使用平台 API 尋找具有相關標籤的資源。 應注意避免將應用程式代碼耦合到基礎設施平台。

Configuration registry
Application instance可以在集中式registry中維護目前的連線詳細資料,以便其他應用程式可以找到它。 當需要比IP address更多的資訊時,這可能會有所幫助; 例如,健康或其他狀態呈現。

Sidecar
每個Application instance旁邊運行一個獨立的Process。 應用程式可以使用 sidecar 作為outbound connection的proxy、inbound connection的gateway或作為lookup service。 Sidecar 需要某種方法來自行進行network discovery。 此方法可能是其他Discovery機制之一,也可能使用不同的通訊協定。Sidecar 通常是Service Mesh的一部分,並且通常提供的不僅僅是Service Discovery。 例如,sidecar 可以處理身份驗證、加密、日誌記錄和監控。

API Gateway
API Gateway是定義路由和endpoint的集中式 HTTP 服務。 它通常還會有更多的服務; 例如,身份驗證、加密、日誌記錄和監控。 換句話說,API gateway與 sidecar 沒有什麼不同,只是一個是集中式一個是分散式的。

--

--

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

我們協助您駕馭名為"雲端運算"的怪獸,馴服它為您所用。諮詢請來信jason.kao@suros.com.tw.