雲端基礎設施堆積積疊:現代架構的核心元件
在現代技術架構中,基礎設施堆積積疊(Infrastructure Stack)已成為建構可靠系統的關鍵元素。基礎設施堆積積疊是指透過自動化工具如Ansible、CloudFormation、Pulumi或Terraform共同定義和管理的資源集合。這些工具讓我們能夠以程式碼方式描述、佈署和維護整個技術環境,實作真正的基礎設施即程式碼(Infrastructure as Code, IaC)。
基礎設施堆積積疊的本質
基礎設施堆積積疊不僅是伺服器和網路裝置的集合,而是一個完整的技術生態系統,包含了運算資源、儲存系統、網路設定、安全政策等多個層面。透過程式碼定義這些元素,我們能夠實作基礎設施的版本控制、一致性佈署和自動化管理。
當玄貓在設計大型系統時,總是將基礎設施視為可程式化的元件,而非固定的實體資源。這種思維轉變是雲端運算帶來的根本變革之一。
雲端運算的標準定義
美國家標準與技術研究院(NIST)提供了雲端運算的權威定義,這個定義已成為業界標準。根據NIST的定義,雲端運算具有五個基本特性:隨需自助服務、廣泛的網路存取、資源池化、快速彈性和可計量的服務。
特別是在基礎設施即服務(IaaS)模型中,NIST定義如下:
「提供給消費者的能力是佈建處理、儲存、網路和其他基本運算資源,使消費者能夠佈署和執行任意軟體,包括作業系統和應用程式。消費者不管理或控制底層雲端基礎設施,但對作業系統、儲存和已佈署的應用程式具有控制權;並可能對選定的網路元件(如主機防火牆)有限的控制權。」
基礎設施堆積積疊與程式碼管理
在實際應用中,基礎設施堆積積疊通常透過宣告式程式碼來定義。這種方法與傳統的命令式程式設計有所不同—我們不是告訴系統「如何」執行任務,而是描述我們想要的「最終狀態」。
以Terraform為例,一個基本的基礎設施堆積積疊可能如下所示:
# 定義AWS提供者
provider "aws" {
region = "us-west-2"
}
# 建立VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "MainVPC"
Environment = "Production"
}
}
# 建立子網路
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
tags = {
Name = "PublicSubnet"
}
}
# 建立安全群組
resource "aws_security_group" "web" {
name = "web-sg"
description = "Allow web traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
這段Terraform程式碼定義了一個基本的AWS網路基礎設施。首先指定使用AWS作為雲端提供者,並設定區域為美國西部(奧勒岡)。接著建立一個主要VPC(Virtual Private Cloud)網路環境,CIDR區塊設為10.0.0.0/16,這提供了65,536個IP位址的空間。在VPC內部,程式碼建立了一個公開子網路,分配了256個IP位址(10.0.1.0/24)。最後,定義了一個安全群組,允許來自任何地方的HTTP流量(埠80)進入,並允許所有出站流量。這個基礎設施堆積積疊雖然簡單,但展示瞭如何以程式碼定義網路架構的核心元素。
基礎設施堆積積疊的優勢
採用基礎設施堆積積疊方法帶來諸多優勢:
一致性與可重複性:消除環境差異,確保開發、測試和生產環境的一致性
版本控制:基礎設施變更可以像應用程式碼一樣進行版本控制和審核
自動化佈署:減少人為錯誤,加速佈署流程
檔案即程式碼:基礎設施程式碼本身就是最好的檔案,永遠保持最新
災難還原:在災難情況下,可以快速重建整個環境
在玄貓的實踐中,發現將基礎設施堆積積疊與CI/CD管道整合是實作真正自動化的關鍵。當程式碼變更推播到版本控制系統時,自動化測試可以驗證基礎設施變更的有效性,然後安全地佈署到目標環境。
不同工具的比較
市場上有多種工具可用於管理基礎設施堆積積疊,每種都有其優缺點:
- Terraform:跨平台支援,強大的狀態管理,豐富的提供者生態系統
- CloudFormation:AWS原生服務,與AWS服務深度整合
- Pulumi:支援多種程式語言(Python、TypeScript等),而非專用DSL
- Ansible:輕量級,無需代理,但主要專注於組態管理而非資源佈建
選擇合適的工具取決於團隊技能、現有技術堆積積疊和特定需求。在多雲環境中,Terraform和Pulumi通常是更靈活的選擇,而在純AWS環境中,CloudFormation可能提供更無縫的體驗。
基礎設施堆積積疊的最佳實踐
在設計和實作基礎設施堆積積疊時,有幾個關鍵的最佳實踐值得遵循:
模組化設計:將基礎設施分解為可重用的模組,提高可維護性和重用性
環境分離:為不同環境(開發、測試、生產)維護獨立的設定
最小許可權原則:確保基礎設施佈署使用最小必要許可權
狀態管理:安全地儲存和管理基礎設施狀態,考慮使用遠端後端
變數引數化:使用變數使設定更加靈活和可重用
輸出管理:定義明確的輸出以便於跨堆積積疊參照和整合
以下是一個展示模組化設計的Terraform範例:
module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
environment = "production"
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
private_subnets = ["10.0.3.0/24", "10.0.4.0/24"]
}
module "security" {
source = "./modules/security"
vpc_id = module.vpc.vpc_id
environment = "production"
}
module "compute" {
source = "./modules/compute"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
security_group_id = module.security.web_sg_id
instance_type = "t3.medium"
instance_count = 3
}
這段Terraform程式碼展示了模組化的基礎設施設計方法。程式碼使用三個不同的模組來組織基礎設施:vpc模組負責網路層面,定義了VPC及其公開和私有子網路;security模組處理安全相關設定,如安全群組;compute模組管理運算資源,如EC2執行個體。每個模組接收特定的輸入引數,例如CIDR區塊、環境名稱和執行個體型別。模組之間透過輸出值相互參照,例如compute模組使用vpc模組輸出的vpc_id和subnet_ids。這種模組化方法提高了程式碼的可重用性和可維護性,同時使基礎設施設定更加清晰和結構化。
基礎設施堆積積疊與雲端平台的關係
基礎設施堆積積疊與底層雲端平台(如AWS、Azure或GCP)緊密相連。雲端平台提供API和服務,而基礎設施堆積積疊工具則提供抽象層,使我們能夠以一致的方式與這些API互動。
這種關係帶來了幾個重要考量:
供應商鎖定:過度依賴特定雲端提供商的專有服務可能導致鎖定
抽象層級:決定使用低階IaaS服務還是高階PaaS/SaaS服務的平衡
成本管理:基礎設施堆積積疊應包含成本最佳化策略和資源生命週期管理
安全合規:確保基礎設施堆積積疊符合組織的安全要求和合規標準
在設計跨雲或混合雲架構時,選擇適當的抽象層級尤為重要。過高的抽象可能限制靈活性,而過低的抽象則可能增加複雜性和維護負擔。
未來趨勢:基礎設施堆積積疊的演進
基礎設施堆積積疊技術正在快速演進,幾個值得關注的趨勢包括:
GitOps模式:將Git作為基礎設施變更的單一事實來源
政策即程式碼:將合規和治理政策編碼為可執行規則
無伺服器基礎設施:減少對傳統IaaS資源的依賴,轉向更高階別的抽象
AI輔助基礎設施管理:利用機器學習最佳化資源設定和故障檢測
隨著這些趨勢的發展,基礎設施堆積積疊將變得更加人工智慧、自動化和自我修復。
基礎設施堆積積疊是現代技術架構的根本,它將硬體資源轉變為可程式化的元件。透過採用基礎設施即程式碼的方法,組織可以實作更高的敏捷性、可靠性和效率。無論是構建簡單的Web應用還是複雜的分散式系統,掌握基礎設施堆積積疊管理都是現代技術專業人員的核心技能。
基礎設施即程式碼:平台與資源管理
基礎設施平台的核心概念
在現代雲端架構中,基礎設施平台是所有系統的基礎層。這些平台提供動態資源管理能力,讓我們能透過API隨需佈建和變更資源。這正是雲端運算的本質定義 - 一個能夠透過程式化方式管理的動態基礎設施。
從歷史角度看,我們已從硬體主導的「鐵器時代」進入「雲端時代」。虛擬化技術解耦了系統與硬體,而雲端則為這些虛擬化資源增加了管理API。這種轉變徹底改變了基礎設施管理方式。
基礎設施平台型別
現今的基礎設施平台大致可分為三類別:
- 公有IaaS雲端服務:AWS、Azure、GCP、Digital Ocean等
- 私有IaaS雲端產品:OpenStack、VMware vCloud、CloudStack等
- 裸機雲端工具:Cobbler、FAI、Foreman等
多雲端策略也有不同變體:
- 混合雲:同時使用私有基礎設施和公有雲端服務,通常用於無法輕易遷移的遺留系統
- 雲端不可知:建構能在多個公有雲平台執行的系統,以避免廠商鎖定
- 多雲:在多個公有雲平台上執行不同應用程式,以利用各平台的優勢
基礎設施資源的型別與管理
基礎設施平台提供三種基本資源:運算、儲存和網路。這些基本資源可以組合成更複雜的複合資源,如資料函式庫服務、負載平衡器等。
運算資源
運算資源負責執行程式碼,常見形式包括:
- 虛擬機器(VM):在實體主機伺服器上執行的虛擬執行環境
- 實體伺服器:按需佈建的裸機伺服器
- 伺服器叢集:作為群組管理的伺服器例項池
- 容器:輕量級的隔離執行環境,如Docker容器
- 應用程式託管叢集:用於佈署和管理多個應用程式的伺服器池,如Kubernetes
- FaaS無伺服器程式碼執行環境:按需執行程式碼的環境,如AWS Lambda
無伺服器概念已超越純程式碼執行,延伸到其他服務。例如,Amazon DynamoDB和Azure Cosmos DB都是無伺服器資料函式庫,使用者無需關心底層伺服器管理。雲端平台也提供專門的應用程式執行環境,如機器學習模型佈署服務。
儲存資源
動態系統需要各種儲存資源,主要包括:
- 區塊儲存:可附加到單一伺服器的虛擬磁碟卷,如AWS EBS
- 物件儲存:可從多處存取的檔案儲存,如Amazon S3
- 網路檔案系統:可掛載到多個運算例項的分享儲存卷
- 結構化資料儲存:各種資料函式庫服務,從關聯式到NoSQL
- 機密管理:用於安全儲存密碼、金鑰等敏感資訊的加密儲存
網路資源
軟體定義網路(SDN)使網路設定更加靈活與安全。現代雲端平台提供的網路資源包括:
- 網路位址區塊:用於分組資源並控制流量路由
- 名稱服務:如DNS專案
- 路由:控制位址區塊間的流量
- 閘道:引導流量進出區塊
- 負載平衡規則:將連線分發到資源池
- 代理:接受連線並轉換或路由它們
- API閘道:處理API非核心方面的HTTP/S代理
- 虛擬私人網路:跨位置連線不同位址區塊
- 直接連線:雲端網路與其他位置間的專用連線
- 網路存取規則:限制或允許網路位置間的流量
- 非同步訊息:用於程式間傳送和接收訊息的佇列
- 快取:分散資料以改善延遲
- 服務網格:動態管理分散式系統連線的去中心化服務網路
零信任安全模型與SDN
零信任安全模型在最低層級保護每個服務、應用程式和資源,與傳統的根據邊界的安全模型不同。這種模型只有透過程式碼定義系統才能實作,因為手動管理每個程式的控制將過於繁重。
在零信任系統中,每個新應用程式都會標註它需要存取的應用程式和服務。平台使用這些資訊自動啟用所需的存取許可權,並確保其他一切都被阻擋。
這種方法的優勢包括:
- 每個應用程式和服務只擁有明確需要的許可權和存取權,遵循最小許可權原則
- 零信任或無邊界安全涉及在需要保護的特定資源上設定範圍較小的障礙和控制
- 系統元素間的安全關係是可見的,便於驗證、稽核和報告
基礎設施即程式碼的核心實踐
為何要將基礎設施定義為程式碼?
雖然可以透過網頁介面或命令列工具佈建基礎設施,但使用程式碼定義基礎設施有諸多優勢:
- 可重用性:程式碼可以重複使用,確保一致性
- 一致性:相同的程式碼總是產生相同的結果
- 透明度:程式碼清晰記錄了基礎設施的設定
- 速度與品質:程式碼化使我們能夠利用速度來提高品質
可以定義為程式碼的內容
基礎設施程式碼可以指定基礎設施元素及其設定方式。可以定義為程式碼的內容包括:
- 基礎設施堆積積疊:從雲端平台佈建的元素集合
- 伺服器設定元素:如套件、檔案、使用者帳戶和服務
- 伺服器角色:應用於單一伺服器例項的伺服器元素集合
- 伺服器映像定義:用於建立多個伺服器例項的映像
- 應用程式套件:定義如何建立可佈署的應用程式成品
- 交付服務設定:包括管道和佈署
- 操作服務設定:如監控檢查
- 驗證規則:包括自動化測試和合規則
基礎設施程式碼語言的選擇
宣告式與命令式程式碼
基礎設施程式碼有兩種主要正規化:
命令式程式碼是一組指定如何實作某事的指令。例如:
import 'cloud-api-library'
network_segment = CloudApi.find_network_segment('private')
app_server = CloudApi.find_server('my_application_server')
if(app_server == null) {
app_server = CloudApi.create_server(
name: 'my_application_server',
image: 'base_linux',
cpu: 2,
ram: '2GB',
network: network_segment
)
// 等待伺服器準備就緒
// 設定伺服器
}
宣告式程式碼指定你想要什麼,而不是如何實作它:
virtual_machine:
name: my_application_server
source_image: 'base_linux'
cpu: 2
ram: 2GB
network: private_network_segment
provision:
provisioner: servermaker
role: tomcat_server
宣告式程式碼將「你想要什麼」與「如何建立它」分離,使程式碼更乾淨直接。
冪等性的重要性
冪等性意味著你可以多次執行程式碼而不改變輸出或結果。這對於安全地持續應用程式碼至關重要。
例如,這個shell指令碼不是冪等的:
echo "spock:*:1010:1010:Spock:/home/spock:/bin/bash" >> /etc/passwd
而冪等的基礎設施工具會確保無論執行多少次,結果都是一致的。
領域特定語言(DSL)與通用程式語言
許多基礎設施工具使用DSL,這些語言專為建模特定領域而設計,使程式碼更容易編寫和理解。
例如,伺服器設定DSL可能看起來像這樣:
package: jdk
package: tomcat
service: tomcat
port: 8443
user: tomcat
group: tomcat
file: /var/lib/tomcat/server.conf
owner: tomcat
group: tomcat
mode: 0644
contents: $TEMPLATE(/src/appserver/tomcat/server.conf.template)
使用通用程式語言(如JavaScript、Python)的優勢包括完善的工具生態系統、強大的IDE支援和測試框架。
基礎設施即程式碼的實施原則
為了輕鬆安全地更新和演進基礎設施系統,需要保持程式碼函式庫的整潔:易於理解、測試、維護和改進。
關鍵實施原則
分離宣告式和命令式程式碼:混合兩種正規化的程式碼是一種設計氣味,表明應該將程式碼分成不同的關注點
將基礎設施程式碼視為真正的程式碼:應用與應用程式碼相同的工程紀律,包括程式碼審查、結對程式設計和自動化測試
使用外部化設定的工具:選擇將基礎設施規範儲存在文字檔案中的工具,而非封閉式工具
在版本控制系統中管理程式碼:這提供了可追溯性、回復能力、關聯性、可見性和可操作性
避免在程式碼中儲存未加密的機密:即使程式碼函式庫是私有的,歷史和修訂也容易洩漏
基礎設施即程式碼的實踐正在不斷演進。我們需要思考在哪些情況下使用宣告式或命令式語言更為合適,以及如何最佳地組織和管理基礎設施程式碼。
透過這些實踐,我們可以建立更加可靠、可維護與安全的基礎設施系統,同時提高團隊的效率和敏捷性。
環境與基礎設施堆積積疊:設計多環境架構的最佳實踐
環境與堆積積疊的關係:相似但不完全相同
環境和基礎設施堆積積疊都是基礎設施資源的集合,但它們的目的和管理方式有所不同。環境是圍繞特定目的(如測試階段或地理區域服務)組織的軟體和基礎設施資源集合。而堆積積疊則是定義和管理基礎設施資源的工具和方法。
簡單來説,我們使用堆積積疊(單一或多個)來實作環境。可以將環境實作為單一堆積積疊,也可以由多個堆積積疊組成。雖然技術上可以在一個堆積積疊中建立多個環境,但這通常不是最佳實踐。
環境的本質與用途
在技術領域中,「環境」這個概念看似簡單,但在不同情境下可能有細微差異。在基礎架構即程式碼的實踐中,環境是一組運作相關的基礎設施資源,支援特定活動如測試或系統執行。
擁有多個環境通常有兩個主要使用案例:
1. 交付環境
最常見的多環境使用案例是支援漸進式軟體發布流程(也稱為生產路徑)。應用程式的特定版本會依序佈署到每個環境,以支援不同的開發和測試活動,最終佈署到生產環境。
例如,一個典型的交付環境序列可能包括:開發環境 → 測試環境 → 預備環境 → 生產環境。
2. 多生產環境
有時需要在生產中執行系統的多個完整獨立副本,原因包括:
容錯能力:如果一個環境失敗,其他環境可以繼續提供服務。這可能涉及容錯移轉流程,將負載從失敗環境轉移出去。雖然成本較高,但執行額外環境可以提供更高程度的容錯能力。
可擴充套件性:可以將工作負載分散到多個環境。這通常按地理位置進行,每個區域有單獨的環境。多環境可同時實作可擴充套件性和容錯能力。
隔離:可能為不同使用者群執行應用程式或服務的多個例項。在不同環境中執行這些例項可以加強隔離,幫助滿足法律或合規要求,並增強客戶信心。
例如,一家電商平台可能為北美、歐洲和亞洲分別設定獨立環境,以確保符合不同地區的資料儲存法規,並能在不同時區安排系統維護時間。
環境、一致性與設定
由於多個環境旨在執行相同系統的例項,每個環境中的基礎設施應保持一致。環境間的一致性是基礎架構即程式碼的主要驅動因素之一。
環境間的差異會帶來不一致行為的風險。在一個環境中測試軟體可能無法發現在另一環境中出現的問題。軟體甚至可能在某些環境中成功佈署,但在其他環境中失敗。
然而,環境間通常需要一些特定差異:
- 測試環境可能比生產環境小
- 不同人員在不同環境中可能有不同許可權
- 不同客戶的環境可能有不同功能和特性
- 至少名稱和ID可能不同(如appserver-test、appserver-stage、appserver-prod)
因此,需要設定環境的某些方面。
環境設計的關鍵考量是測試和交付策略。當相同的基礎設施程式碼應用於每個環境時,在一個環境中測試它會增強它在其他環境中正常工作的信心。但如果基礎設施在各例項間差異很大,就無法獲得這種信心。
構建環境的模式
反模式:多環境堆積積疊
多環境堆積積疊將多個環境的基礎設施定義和管理為單一堆積積疊例項。
例如,如果有三個用於測試和執行應用程式的環境,單個堆積積疊專案包含所有三個環境的程式碼。
動機: 許多人在學習新的堆積積疊工具時建立這種結構,因為將新環境增加到現有專案中似乎很自然。
後果: 當執行工具更新堆積積疊例項時,潛在變更的範圍是堆積積疊中的所有內容。如果程式碼中有錯誤或衝突,例項中的所有內容都很脆弱。
當生產環境與其他環境在同一堆積積疊例項中時,更改其他環境會有導致生產問題的風險。編碼錯誤、意外依賴或工具中的錯誤都可能在你只想更改測試環境時破壞生產環境。
反模式:複製貼上環境
複製貼上環境反模式為每個基礎設施堆積積疊例項使用單獨的堆積積疊原始碼專案。
例如,對於名為test、staging和production的三個環境,每個環境都有單獨的基礎設施專案。透過編輯一個環境中的程式碼然後依次將更改複製到其他環境來進行更改。
動機: 複製貼上環境是維護多個環境的直觀方式。它們避免了多頭堆積積疊反模式的爆炸半徑問題。你還可以輕鬆自定義每個堆積積疊例項。
適用性: 如果你想維護和更改不同的例項,與不擔心程式碼重複或一致性,複製貼上環境可能是合適的。
後果: 維護多個複製貼上環境可能具有挑戰性。當你想進行程式碼更改時,需要將其複製到每個專案。你可能需要單獨測試每個例項,因為更改可能在一個例項中有效,但在另一個例項中無效。
複製貼上環境通常會遭受設定漂移。將複製貼上環境用於交付環境會降低佈署過程的可靠性和測試的有效性,這是由於從一個環境到下一個環境的不一致性造成的。
複製貼上環境在首次設定時可能是一致的,但隨著時間的推移,變化會逐漸出現。
模式:可重用堆積積疊
可重用堆積積疊是用於建立堆積積疊的多個例項的基礎設施原始碼專案。
動機: 建立可重用堆積積疊是為了維護多個一致的基礎設施例項。當你對堆積積疊程式碼進行更改時,可以在一個例項中應用和測試它,然後使用相同的程式碼版本建立或更新多個額外例項。你希望以最少的儀式甚至自動地設定堆積積疊的新例項。
適用性: 可以將可重用堆積積疊用於交付環境或多個生產環境。當環境之間不需要太多變化時,此模式很有用。當環境需要大量自定義時,它的適用性較低。
後果: 從同一專案設定和更新多個堆積積疊的能力增強了可擴充套件性、可靠性和吞吐量。你可以用更少的努力管理更多的例項,以更低的失敗風險進行更改,並更快地將更改推廣到更多系統。
通常需要為不同例項設定堆積積疊的某些方面,即使只是命名。
在將更改應用於業務關鍵基礎設施之前,應該測試堆積積疊專案程式碼。
實作: 將可重用堆積積疊建立為基礎設施堆積積疊專案,然後每次想要建立或更新堆積積疊例項時執行堆積積疊管理工具。使用堆積積疊工具命令的語法告訴它要建立或更新哪個例項。
例如,使用名為stack的虛構命令設定兩個堆積積疊例項:
> stack up env=test --source mystack/src
SUCCESS: stack 'test' created
> stack up env=staging --source mystack/src
SUCCESS: stack 'staging' created
通常,應該使用簡單引數(字元串、數字或在某些情況下列表)來定義堆積積疊例項之間的差異。此外,可重用堆積積疊建立的基礎設施在例項之間不應有太大差異。
使用多個堆積積疊構建環境
可重用堆積積疊模式描述了實作多環境的方法。但對於較大系統,應該將其拆分為多個堆積積疊。例如,如果遵循服務堆積積疊模式,每個服務都有單獨的堆積積疊。
要建立多個環境,為每個環境設定每個服務堆積積疊的例項。例如,使用以下命令構建具有多個堆積積疊的完整環境:
> stack up env=staging --source product_browse_stack/src
SUCCESS: stack 'product_browse-staging' created
> stack up env=staging --source product_search_stack/src
SUCCESS: stack 'product_search-staging' created
> stack up env=staging --source shopping_basket_stack/src
SUCCESS: stack 'shopping_basket-staging' created
環境設計的關鍵考量
在設計多環境架構時,玄貓建議考慮以下幾點:
爆炸半徑控制:將環境分離到不同堆積積疊中,限制變更的影響範圍,特別是保護生產環境
程式碼重用:避免複製貼上方法,使用可重用堆積積疊模式確保一致性和維護效率
設定策略:建立清晰的環境設定策略,只變更真正需要不同的引數
測試與驗證:在推廣到更關鍵環境前,確保在較低環境中徹底測試基礎設施變更
環境隔離:根據安全性、合規性和操作需求決定環境隔離的程度
多環境架構的成功實施需要平衡一致性與靈活性,同時考慮團隊的工作流程和系統的特定需求。透過採用可重用堆積積疊模式並謹慎管理環境間的差異,可以實作高效、可靠的基礎設施管理。
玄貓的基礎設施即程式碼系列:多堆積積疊策略與組態管理
在現代雲端架構中,基礎設施即程式碼(Infrastructure as Code, IaC)已成為管理複雜系統的核心方法。本文將探討如何透過多堆積積疊策略和組態管理來建立更具彈性、可維護的基礎設施系統。
多堆積積疊策略:分割與整合的藝術
基礎設施系統隨著規模增長,單一堆積積疊往往變得難以管理。將系統分割成多個堆積積疊不僅能提高開發效率,也能增強系統的穩定性和可維護性。
為何需要多堆積積疊策略?
當玄貓處理大型基礎設施專案時,發現單一堆積積疊模式存在幾個明顯缺點:
- 變更風險擴大 - 單一堆積積疊中的小改動可能影響整個系統
- 團隊協作受限 - 多人同時修改同一堆積積疊容易造成衝突
- 佈署時間延長 - 大型堆積積疊的佈署和測試時間成倍增加
- 責任界限模糊 - 難以實施明確的服務邊界和責任分配
將系統分割成多個堆積積疊能有效解決這些問題,讓每個堆積積疊專注於特定功能或服務。
堆積積疊分割的最佳實踐
在實務中,玄貓建議根據以下原則進行堆積積疊分割:
- 按功能邊界分割 - 將相關功能組織在同一堆積積疊中
- 考慮變更頻率 - 經常變更的元件應與穩定元件分離
- 注意依賴關係 - 減少堆積積疊間的複雜依賴
- 保持適當規模 - 堆積積疊不應過大或過小,以平衡管理成本
graph TD A[基礎網路堆積積疊] --> B[資料儲存堆積積疊] A --> C[計算資源堆積積疊] B --> D[應用服務堆積積疊] C --> D D --> E[監控與日誌堆積積疊]
堆積積疊間的整合策略
分割堆積積疊後,整合變得至關重要。玄貓在實踐中發現以下整合方法特別有效:
- 輸出值分享 - 堆積積疊可以輸出關鍵值供其他堆積積疊使用
- 設定註冊中心 - 使用中央服務儲存和分享堆積積疊間的設定
- 服務發現機制 - 實作動態服務發現,減少硬編碼依賴
- 跨堆積積疊參考 - 某些工具支援直接參照其他堆積積疊的資源
堆積積疊例項組態管理
可重用堆積積疊是管理大型基礎設施的核心模式。一個堆積積疊專案可用於建立多個環境例項,但這需要有效的組態管理策略。
設定引數的設計原則
玄貓在設計堆積積疊設定時遵循「保持引數簡單」的核心原則:
- 使用簡單引數型別 - 優先使用字串、數字等簡單型別
- 最小化引數量 - 只在確實需要時增加引數
- 避免條件式引數 - 不要使用引數來建立截然不同的基礎設施
當引數變得複雜難以管理時,這通常是重構堆積積疊程式碼的訊號。
使用引數建立唯一識別符號
在多環境佈署中,資源識別符號必須唯一。玄貓通常使用環境引數來確保唯一性:
server:
id: appserver-${environment}
subnet_id: appserver-subnet-${environment}
這種方法允許在不同環境中建立相同結構的資源,同時避免命名衝突。
堆積積疊設定模式比較
在實際工作中,玄貓發現以下幾種設定模式各有優缺點:
1. 指令碼引數模式
將引數值硬編碼在執行堆積積疊工具的指令碼中:
#!/bin/sh
case $1 in
test)
CLUSTER_MINIMUM=1
CLUSTER_MAXIMUM=1
;;
staging)
CLUSTER_MINIMUM=2
CLUSTER_MAXIMUM=3
;;
production)
CLUSTER_MINIMUM=2
CLUSTER_MAXIMUM=6
;;
esac
stack up \
environment=$1 \
cluster_minimum=${CLUSTER_MINIMUM} \
cluster_maximum=${CLUSTER_MAXIMUM}
優點:簡單直接,引數值可追蹤 缺點:指令碼可能變得複雜,不適合管理機密資訊
2. 堆積積疊設定檔案模式
為每個環境建立單獨的設定檔案:
# staging.properties
env = staging
cluster_minimum = 2
cluster_maximum = 3
優點:設定與程式碼分離,易於理解和稽核 缺點:增加新環境需要新檔案,不適合動態建立環境
3. 包裝堆積積疊模式
為每個例項建立單獨的基礎設施堆積積疊專案,匯入分享模組:
module:
name: container_cluster_module
version: 1.23
parameters:
env: test
cluster_minimum: 1
cluster_maximum: 1
優點:可利用模組版本控制和相依性管理功能 缺點:增加了複雜性,可能導致環境間不一致
4. 管道堆積積疊引數模式
在交付管道設定中定義每個例項的引數值:
stage: apply-test-stack
input_artifacts: container_cluster_stack
commands:
unpack ${input_artifacts}
stack up --source ./src environment=test cluster_minimum=1 cluster_maximum=1
stack test environment=test
優點:與現有管道工具整合良好 缺點:將設定與交付過程耦合,可能難以維護
5. 堆積積疊引數註冊中心模式
在中央位置管理堆積積疊例項的引數值:
└── env/
├── test/
│ └── cluster/
│ ├── min = 1
│ └── max = 1
├── staging/
│ └── cluster/
│ ├── min = 2
│ └── max = 3
└── production/
└── cluster/
├── min = 2
└── max = 6
優點:設定與實作分離,可作為設定真相來源 缺點:增加了依賴,可能成為故障點
機密資訊處理策略
基礎設施程式碼經常需要處理密碼、API金鑰等機密資訊。玄貓建議採用以下策略:
1. 加密機密
使用如git-crypt、blackbox等工具在原始碼中安全儲存加密的機密。
2. 無機密授權
利用雲平台的功能,如AWS IAM角色,授予計算服務特權操作許可權,避免使用機密。
3. 執行時機密注入
在執行時注入機密,而非儲存在程式碼中:
- 本地開發:使用不受版本控制的本地檔案
- 無人值守代理:使用代理軟體的機密管理功能
4. 一次性機密
動態建立機密並按需使用,如自動生成資料函式庫密碼並傳遞給應用伺服器。
設定註冊中心實作
對於大型組織,設定註冊中心是管理設定和整合依賴的有效工具。玄貓在實踐中使用過以下幾種實作方式:
1. 基礎設施自動化工具註冊中心
如Chef Infra Server、PuppetDB等工具提供的內建註冊服務。
2. 通用設定註冊中心產品
如Zookeeper、etcd、Consul等獨立的設定註冊中心產品。
3. 平台註冊服務
如AWS SSM Parameter Store等雲平台提供的鍵值儲存服務。
4. 自建設定註冊中心
使用物件儲存、版本控制系統或網路檔案系統構建輕量級設定註冊中心。
持續測試與交付基礎設施
持續測試和交付是基礎架構即程式碼的核心實踐之一。與應用程式開發類別似,基礎設施程式碼也需要嚴格的測試策略。
為何需要持續測試基礎設施?
許多團隊認為基礎設施是一次性建立的,質疑投入自動化測試的價值。然而,玄貓的經驗表明:
- 基礎設施在建立後會經歷大量變更(修補、升級、修復、改進)
- 持續交付消除了傳統的「建立」和「執行」階段區分
- 早期發現問題可減少調查和修復時間,避免技術債務積累
基礎設施測試的挑戰與解決方案
基礎設施測試面臨幾個獨特挑戰:
1. 宣告式程式碼測試價值低
宣告式基礎設施程式碼的測試往往只是重述程式碼本身,價值有限。解決方案:
- 專注於測試變數和條件邏輯
- 測試多個宣告的組合效果
- 測試結果而非宣告
2. 基礎設施測試速度慢
解決方案:
- 將基礎設施分割為更小的可測試單元
- 明確、最小化並隔離依賴
- 採用漸進式測試策略
- 靈活選擇臨時或持久例項
- 結合線上和離線測試
3. 依賴關係複雜化測試
解決方案:
- 使用測試替身(模擬、假物、存根)
- 使用雲端API模擬工具
- 為其他基礎設施元件建立測試替身
漸進式測試策略
玄貓建議採用漸進式測試策略,從簡單、快速的測試開始,逐步擴充套件到更全面的整合測試:
graph LR A[程式碼掃描] --> B[單元測試] B --> C[元件測試] C --> D[整合測試] D --> E[系統測試]
基礎設施測試金字塔
傳統測試金字塔在宣告式基礎設施程式碼中不太適用,基礎設施測試套件可能更像一個菱形:
- 底層:較少的低階單元測試(宣告式程式碼測試價值有限)
- 中層:大量的元件和整合測試(最有價值的測試層)
- 頂層:較少的端對端系統測試(耗時與複雜)
瑞士乳酪測試模型
另一種思考方式是瑞士乳酪模型,每一層測試可能有「漏洞」,但多層組合確保沒有風險能穿透所有層。這種模型強調根據風險而非公式進行測試。
可重用堆積積疊應該是大多數需要管理大型基礎設施的團隊的主力模式。堆積積疊是測試和交付變更的有用單位,它確保每個環境例項都能一致定義和構建。堆積積疊作為變更單位的全面性增強了快速、頻繁地交付變更的能力。
設定應該保持最小化。如果發現不同堆積積疊例項之間差異過大,應考慮將它們定義為不同的堆積積疊。堆積積疊專案應定義在例項間保持一致的堆積積疊形狀,而設定引數則用於必要的自定義。
持續測試和交付基礎設施程式碼是確保系統可靠性和可維護性的關鍵。儘管面臨挑戰,採用適當的測試策略和工具可以顯著提高基礎設施管理的效率和品質。
持續整合與交付:開發可靠的基礎設施佈署管道
在現代軟體開發中,持續整合與交付(CI/CD)已成為確保程式碼品質和快速佈署的關鍵實踐。當我們將這些概念應用到基礎設施程式碼時,就能建立一個可靠、一致與高效的基礎設施佈署流程。本文將探討如何設計基礎設施佈署管道,實作漸進式測試,並確保每次變更都經過完整驗證。
基礎設施佈署管道的核心概念
基礎設施佈署管道是一個自動化系統,負責封裝、整合和將程式碼應用到各環境中。當開發人員將程式碼變更推播到版本控制函式庫時,系統會自動將變更透過一系列階段進行測試和交付。
管道的關鍵特性
自動化流程 - 管道自動化了封裝、提升和應用程式碼與測試的過程。雖然人員可能會審查變更或進行探索性測試,但不應手動執行佈署命令或在執行時選擇設定選項。
一致性保證 - 自動化確保每次執行過程都保持一致,提高測試可靠性,並在基礎設施例項之間建立一致性。
從源頭推播變更 - 每個變更都應從管道的起點推播。如果在管道後期階段發現錯誤,不要在該階段修復並繼續,而是修復程式碼函式庫中的問題並從頭開始推播新的變更。
管道階段的特性
每個管道階段可能執行不同的任務並以不同方式觸發。一個階段的特性包括:
觸發器(Trigger) - 導致階段開始執行的事件。可能是程式碼推播到儲存函式庫時自動執行,或在前一階段成功執行後觸發,也可能由測試人員或發布管理員手動觸發。
活動(Activity) - 階段執行時執行的操作。一個階段可能執行多個動作,例如應用程式碼來設定基礎設施堆積積疊、執行測試,然後銷毀堆積積疊。
審批(Approval) - 階段如何被標記為透過或失敗。系統可能在命令無錯誤執行與自動測試全部透過時自動標記階段為透過(綠色),或由人員在進行探索性測試後批准階段。
輸出(Output) - 階段產生的成品或其他材料,如基礎設施程式碼包或測試報告。
漸進式測試策略
在漸進式測試策略中,早期階段驗證單個元件,而後期階段整合元件並一起測試它們。
元件測試的範圍
一個階段可能為多個元件執行測試,例如一套單元測試。或者,不同元件可能各自有單獨的測試階段。
依賴關係的漸進式整合
系統的許多元素依賴於其他服務。例如,應用伺服器堆積積疊可能連線到身份管理服務來處理使用者認證。要漸進式測試這一點:
- 首先執行一個階段,測試沒有身份管理服務的應用伺服器,可能使用模擬服務代替。
- 後續階段在應用伺服器與身份管理服務的測試例項整合後執行額外測試。
- 生產階段則與生產例項整合。
平台元素的漸進式使用
系統最終可能在基礎設施平台上執行,但你可能夠在離線環境中有效地執行和測試其部分功能:
- 定義網路結構的程式碼需要在雲平台上設定這些結構才能進行有意義的測試。
- 但安裝應用伺服器包的程式碼可能可以在本地虛擬機器甚至容器中測試,而不需要在雲平台上建立虛擬機器。
因此,早期測試階段可能無需使用完整的雲平台即可執行某些元件的測試。
管道軟體與服務選擇
你需要軟體或託管服務來建立管道系統需要做幾件事:
- 提供設定管道階段的方式
- 從不同動作觸發階段,包括自動事件和手動觸發
- 支援階段所需的任何動作,包括應用基礎設施程式碼和執行測試
- 處理階段的成品和其他輸出,包括能夠將它們從一個階段傳遞到下一個階段
- 幫助追蹤和關聯特定版本和例項的程式碼、成品、輸出和基礎設施
管道系統選項
- 構建伺服器:如Jenkins、Team City、Bamboo或GitHub Actions
- CD軟體:如GoCD、ConcourseCI和BuildKite
- SaaS服務:如CircleCI、TravisCI、AppVeyor、Drone和BoxFuse
- 雲平台服務:如AWS CodeBuild/CodePipeline和Azure Pipelines
- 原始碼函式庫服務:如GitHub Actions和GitLab CI/CD
基礎設施即程式碼專用工具
隨著基礎設施即程式碼的發展,一些專門設計的工具也在出現:
Atlantis:幫助管理Terraform專案的提取請求,並為單個例項執行計劃和應用。它不執行測試,但可用於建立處理程式碼審查和基礎設施變更批准的有限管道。
Terraform Cloud:Terraform專用,包括比CI和管道更多的功能(如模組登入檔)。可用於建立將專案程式碼計劃和應用到多個環境的有限管道,但除了使用HashiCorp自己的Sentinel產品進行策略驗證外,不執行其他測試。
WeaveWorks:提供管理Kubernetes叢集的產品和服務,包括使用根據Git分支的管道管理叢集設定和應用變更的工具,這種方法被稱為GitOps。
在生產環境中進行測試
在將發布和變更應用到生產環境之前進行測試是我們行業的重點。隨著系統複雜性和規模的增加,你能在生產環境之外實際檢查的風險範圍縮小。這並不是説在生產前測試變更沒有價值,但認為發布前測試可以全面覆寫風險會導致:
- 在發布前測試上過度投資,遠超過收益遞減點
- 在生產環境測試上投資不足
生產環境外無法複製的特性
生產環境有幾個特性是你無法在生產環境外實際複製的:
資料 - 生產系統可能有更大的資料集,並且無疑會有意外的資料值和組合。
使用者 - 由於數量眾多,使用者在做奇怪事情方面比測試人員更有創意。
流量 - 如果系統有不平凡的流量水平,你無法複製它將定期經歷的活動數量和型別。與在生產環境執行一年相比,為期一週的浸泡測試微不足道。
並發性 - 測試工具可以模擬多個使用者同時使用系統,但無法複製使用者同時做的不尋常組合。
這些特性帶來的兩個挑戰是:它們創造了你無法預測的風險,以及你無法在生產環境之外充分複製以進行測試的條件。
為什麼要在生產環境之外測試?
在生產環境中測試並不能替代在應用變更到生產環境之前測試變更。以下是你實際上可以(也應該!)提前測試的內容:
- 它是否有效?
- 我的程式碼是否執行?
- 它是否以我能預測的方式失敗?
- 它是否以前失敗的方式失敗?
在生產前測試變更解決了已知的未知,即你知道可能出錯的事情。在生產中測試變更解決了未知的未知,即更不可預測的風險。
管理在生產環境中測試的風險
在生產環境中測試會帶來新的風險。以下是幫助管理這些風險的幾點:
監控 - 有效的監控讓你有信心能夠檢測到測試造成的問題,以便快速停止它們。
可觀察性 - 可觀察性讓你能夠詳細瞭解系統內部發生的情況,幫助你快速調查和修復問題,並提高測試品質。
零停機佈署 - 能夠快速無縫地佈署和回復變更有助於降低錯誤風險。
漸進式佈署 - 如果你可以同時執行元件的不同版本,或為不同使用者組設定不同設定,你可以在將變更暴露給使用者之前在生產條件下測試變更。
資料管理 - 生產測試不應對資料進行不適當的更改或暴露敏感資料。你可以維護不會觸發真實世界行動的測試資料記錄。
混沌工程 - 透過故意注入已知型別的故障來降低生產環境中的風險,證明你的緩解系統正常工作。
監控作為測試
監控可以被視為生產中的被動測試。這不是真正的測試,因為你不是採取行動並檢查結果,而是觀察使用者的自然活動並觀察不良結果。
監控應該成為測試策略的一部分,因為它是你為管理系統風險而做的事情組合的一部分。
基礎設施佈署管道是確保程式碼變更安全、可靠地應用到各環境的關鍵工具。透過實施漸進式測試策略,我們可以在早期階段發現問題,並確保元件在整合前已經過充分驗證。
雖然生產前測試至關重要,但我們也不應忽視在生產環境中進行測試的價值。生產環境提供了無法在其他地方複製的真實資料、使用者行為和流量模式,這對於發現潛在問題至關重要。
透過結合適當的工具、流程和實踐,我們可以建立一個既能快速交付變更又能維護系統穩定性的基礎設施佈署流程。這種平衡是現代DevOps實踐的核心,也是成功實施基礎設施即程式碼的關鍵。
應用程式執行環境的佈署與管理
應用程式資料管理
資料管理常是應用程式佈署和執行的後續考量。我們會佈建資料函式庫和儲存卷,但與基礎設施的許多部分一樣,對它們進行變更才是真正的挑戰。變更資料和結構既耗時又混亂,還充滿風險。
應用程式佈署通常涉及建立或變更資料結構,包括在結構變更時轉換現有資料。更新資料結構應該是應用程式和應用程式佈署流程的關注點,而非基礎設施平台和執行環境的責任。然而,基礎設施和應用程式執行服務需要支援在基礎設施和其他底層資源變更或失敗時維護資料的完整性。
資料結構與模式管理
資料儲存可分為嚴格結構化的(如SQL資料函式庫)和無結構或無模式的。嚴格結構化、以模式驅動的資料函式庫對資料強制執行結構,拒絕儲存格式不正確的資料。使用無模式資料函式庫的應用程式則負責自行管理資料格式。
新版應用程式可能包含資料結構的變更。對於模式驅動的資料函式庫,這涉及變更資料函式庫中的資料結構定義。例如,新版本可能在資料記錄中增加新欄位,一個典型的例子是將單一的「姓名」欄位拆分為名、中間名和姓的獨立欄位。
當資料結構變更時,無論是哪種型別的資料函式庫,任何現有資料都需要轉換為新結構。如果你要拆分「姓名」欄位,你需要一個流程將資料函式庫中的名字分割到各自獨立的欄位中。
變更資料結構和轉換資料稱為「模式遷移」(schema migration)。有多種工具和函式庫可供應用程式和佈署工具用來管理這個流程,包括Flyway、Liquibase和db-migrate等。開發人員可以使用這些工具將增量資料函式庫變更定義為程式碼。這些變更可以提交到版本控制系統,並作為發布的一部分封裝。這有助於確保資料函式庫模式與佈署到例項的應用程式版本保持同步。
團隊可以使用資料函式庫演進策略來安全與靈活地管理資料和模式的頻繁變更。這些策略與敏捷軟體工程方法(包括CI和CD)以及基礎設施即程式碼的理念相符。
雲原生應用程式儲存基礎設施
雲原生基礎設施是根據需求動態分配給應用程式和服務的。某些平台除了提供計算和網路外,還提供雲原生儲存。當系統增加應用程式例項時,它可以自動佈建並附加儲存裝置。你可以在應用程式佈署清單中指定儲存需求,包括在佈建時需要的任何格式化或要載入的資料。
以下是一個包含資料儲存的應用程式佈署清單範例:
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
這個簡單的範例定義瞭如何建立動態擴充套件資料函式庫叢集的節點。對於每個節點例項,平台將建立一個包含資料函式庫軟體的容器例項,並附加一個從初始化為空資料函式庫段的映像克隆的磁碟卷。當例項啟動時,它將連線到叢集並將資料同步到其本地卷。
應用程式連線性
除了執行程式碼的計算資源和儲存資料的儲存資源外,應用程式還需要網路來進行入站和出站連線。傳統上,導向伺服器的應用程式套件(如網頁伺服器)可能會設定網路埠並增加密金鑰用於傳入連線,但它們通常依賴於有人單獨設定伺服器外部的基礎設施。
你可以將定址、路由、命名、防火牆規則等類別似問題定義並管理為基礎設施堆積積疊專案的一部分,然後將應用程式佈署到結果基礎設施中。更符合雲原生理唸的網路方法是將網路需求定義為應用程式佈署清單的一部分,並讓應用程式執行環境動態分配資源。
以下是一個連線性設定的範例:
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 ]
這個範例定義了入站和出站連線,參照系統的其他部分。這些包括公共網際網路(可能是一個閘道)和應用程式伺服器的入站HTTPS埠,這些應用程式伺服器使用自己的佈署清單定義並佈署到同一叢集。
服務發現機制
應用程式執行環境為應用程式提供許多常見服務,其中許多是服務發現的型別。
在基礎設施中執行的應用程式和服務通常需要知道如何找到其他應用程式和服務。例如,前端網頁應用程式可能需要向後端服務傳送請求來處理使用者交易。
在靜態環境中,這並不困難。應用程式可能使用其他服務的已知主機名,可能儲存在需要時更新的設定檔中。
但在動態基礎設施中,服務和伺服器的位置是流動的,需要一種更靈活的方式來尋找服務。
以下是幾種流行的發現機制:
硬編碼IP位址
為每個服務分配IP位址。例如,監控伺服器始終在192.168.1.5上執行。如果需要更改地址或執行多個服務例項(例如,為了控制重大升級的推出),則需要重建和重新佈署應用程式。
主機檔案條目
使用伺服器設定生成每台伺服器上的/etc/hosts檔案(或等效檔案),將服務名稱對映到當前IP位址。這種方法比DNS更混亂,但我見過它被用來解決傳統DNS實作的問題。
DNS(網域名稱系統)
使用DNS條目將服務名稱對映到其當前IP位址,可以使用由程式碼管理的DNS條目,或DDNS(動態DNS)。DNS是一種成熟、得到良好支援的解決方案。
資源標籤
基礎設施資源被標記以指示它們提供什麼服務,以及環境等上下文。發現涉及使用平台API查詢具有相關標籤的資源。應注意避免將應用程式碼與基礎設施平台耦合。
設定登入檔
應用程式例項可以在集中式登入檔中維護當前連線詳細訊息,以便其他應用程式可以查詢。當需要比地址更多的訊息時,這可能很有用;例如,健康狀態或其他狀態指示。
邊車模式
一個獨立的程式與每個應用程式例項一起執行。應用程式可以使用邊車作為出站連線的代理、入站連線的閘道或作為查詢服務。邊車本身需要某種方法來進行網路發現。這種方法可以是其他發現機制之一,或者它可能使用不同的通訊協定。邊車通常是服務網格的一部分,並且通常提供的不僅是服務發現。例如,邊車可能處理身份驗證、加密、日誌記錄和監控。
API閘道
API閘道是一個集中式HTTP服務,定義路由和端點。它通常提供更多服務;例如,身份驗證、加密、日誌記錄和監控。換句話説,API閘道與邊車類別似,但是集中式而非分散式。
避免基礎設施、執行環境和應用程式之間的硬界限
理論上,將應用程式執行環境作為一套完整的服務提供給開發人員,使他們免於瞭解底層基礎設施的細節,可能很有用。但實際上,這個模型中的界限比呈現的要模糊得多。不同的人和團隊需要存取不同抽象層級的資源,具有不同程度的控制。因此,你不應該設計和實作具有絕對界限的系統,而應該定義可以不同方式組合和呈現給不同使用者的元件。
伺服器即程式碼的建構
基礎設施即程式碼最初是作為設定伺服器的方式出現的。系統管理員編寫shell、批處理和Perl指令碼。CFEngine開創了使用宣告式、冪等DSL來安裝套件和管理伺服器上的設定檔的先例,Puppet和Chef隨後跟進。這些工具假設你從現有伺服器開始—通常是機架中的實體伺服器,有時是使用VMware的虛擬機器,後來是雲端例項。
現在,我們要麼專注於伺服器只是其中一部分的基礎設施堆積積疊,要麼使用容器叢集,其中伺服器是底層細節。
但伺服器仍然是大多數應用程式執行環境的重要組成部分。存在超過幾年的大多數系統至少在伺服器上執行一些應用程式。即使是執行叢集的團隊通常也需要為主機節點建立和執行伺服器比其他型別的基礎設施(如網路和儲存)更複雜。它們有更多的活動部件和變化,因此大多數系統團隊仍然花費相當多的時間沉浸在作業系統、套件和設定檔中。
伺服器的生命週期
思考伺服器生命週期的一個有用方式是將其視為具有幾個過渡階段:
- 建立和設定伺服器例項
- 變更現有伺服器例項
- 銷毀伺服器例項
伺服器上的內容
將伺服器上的各種內容分為軟體、設定和資料是很有用的。這些類別對於理解設定管理工具應如何處理特設定檔案或檔案集很有幫助。
設定和資料之間的區別在於自動化工具是否自動管理檔案內容。因此,即使系統日誌對基礎設施至關重要,自動設定工具也將其視為資料。同樣,如果應用程式將其使用者的帳戶和偏好等內容儲存在伺服器上的檔案中,伺服器設定工具也將該檔案視為資料。
伺服器上的內容型別
型別 | 描述 | 設定管理如何處理 |
---|---|---|
軟體 | 應用程式、函式庫和其他程式碼。這不一定是可執行檔;它可以是幾乎任何靜態與不傾向於在系統之間變化的檔案。Linux系統上的時區資料檔案就是一個例子。 | 確保它在每個相關伺服器上都相同;不關心內部內容。 |
設定 | 用於控制系統和/或應用程式工作方式的檔案。內容可能因伺服器而異,根據它們的角色、環境、例項等。這些檔案作為基礎設施的一部分進行管理,而不是由應用程式自己管理的設定。例如,如果應用程式有一個用於管理使用者設定檔的UI,儲存使用者設定檔的資料檔案從基礎設施的角度來看不會被視為設定;相反,這將是資料。但是,儲存在檔案系統上並由基礎設施管理的應用程式設定檔在這個意義上會被視為設定。 | 在伺服器上建立檔案內容;確保它是一致的。 |
資料 | 由系統和應用程式生成和更新的檔案。基礎設施可能對這些資料有一些責任,例如分發、備份或複製它。但基礎設施將檔案內容視為黑盒,不關心其內容。資料函式庫資料檔案和日誌檔案是這種意義上的資料的例子。 | 自然發生和變化;可能需要儲存它,但不會嘗試管理內部內容。 |
內容來源
構成伺服器例項的軟體、設定和資料可能在伺服器建立和設定時增加,或在伺服器變更時增加。這些元素有幾個可能的來源:
基本作業系統:作業系統安裝映像可以是實體磁碟、ISO檔案或標準伺服器映像。OS安裝過程可能會選擇可選元件。
OS套件函式庫:OS安裝或安裝後設定步驟可以執行從一個或多個函式庫下載和安裝套件的工具。OS供應商通常提供他
伺服器程式碼化:從建構到佈署的自動化實踐
在現代雲端基礎架構中,伺服器不再是需要精心呵護的「寵物」,而是可以快速建立和替換的「牲畜」。這種思維轉變使得伺服器映像檔(Server Image)的自動化建構和管理變得至關重要。本文將探討如何將伺服器映像檔程式碼化,實作從建構到佈署的全自動化流程。
伺服器映像檔的核心概念
什麼是伺服器映像檔?
伺服器映像檔是一個預先設定好的伺服器範本,包含作業系統、應用程式和設定。它可以被用來快速建立多個相同設定的伺服器例項,確保環境的一致性和可重複性。
在傳統的基礎架構管理中,伺服器往往是手動設定的,這導致了「雪花伺服器」(每台伺服器都是獨特的)的產生。而伺服器映像檔程式碼化則徹底改變了這種局面,它讓我們能夠:
- 以程式碼定義伺服器的完整狀態
- 自動化建構和測試流程
- 實作版本控制和變更追蹤
- 確保環境一致性和可重複性
伺服器映像檔與組態管理的關係
伺服器映像檔與組態管理工具(如Ansible、Chef、Puppet)並非互斥關係,而是互補的技術:
- 伺服器映像檔:預先封裝好的伺服器範本,啟動速度快,一致性高
- 組態管理:動態應用設定,適合處理變化頻繁的元素
兩者結合使用時,可以實作「烘焙」(Baking)與「油炸」(Frying)的平衡:將穩定的基礎元素烘焙到映像檔中,將變化頻繁的元素在伺服器啟動時動態設定。
伺服器映像檔程式碼化的實作策略
映像檔建構管道
伺服器映像檔的建構應該透過自動化管道完成,這個管道通常包含以下階段:
- 程式碼提交觸發:開發人員提交映像檔定義程式碼
- 基礎映像檔選擇:選擇適當的基礎映像檔(如Ubuntu、CentOS等)
- 設定應用:安裝軟體包、應用設定
- 映像檔最佳化:移除不必要的檔案、壓縮映像檔
- 安全掃描:檢查漏洞和安全問題
- 功能測試:確保映像檔能正常工作
- 發布與標記:將映像檔發布到映像檔函式庫並適當標記
映像檔定義工具選擇
市場上有多種工具可用於定義和建構伺服器映像檔:
- Packer:HashiCorp的開放原始碼工具,支援多種平台和設定方法
- Image Builder:AWS提供的映像檔建構服務
- Azure Image Builder:微軟Azure的映像檔建構工具
- Google Cloud Build:Google Cloud的映像檔建構服務
以Packer為例,它允許我們使用JSON或HCL格式定義映像檔,並支援多種「佈建器」(如Shell、Ansible、Chef等)來設定映像檔。
實作範例:使用Packer定義映像檔
source "amazon-ebs" "ubuntu" {
ami_name = "my-app-server-{{timestamp}}"
instance_type = "t2.micro"
region = "us-west-2"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-focal-20.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"] # Canonical
}
ssh_username = "ubuntu"
}
build {
sources = ["source.amazon-ebs.ubuntu"]
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx",
"sudo systemctl enable nginx"
]
}
provisioner "ansible" {
playbook_file = "./playbooks/configure.yml"
}
post-processor "manifest" {
output = "manifest.json"
}
}
這段Packer設定義了一個根據Ubuntu 20.04的Amazon AMI映像檔。它使用了兩個佈建器:Shell和Ansible。Shell佈建器安裝Nginx並啟用它,而Ansible佈建器則應用更複雜的設定。最後,manifest後處理器生成一個包含建構詳情的JSON檔案。
映像檔測試與驗證策略
映像檔測試是確保映像檔品質的關鍵環節,應該包含多個層次:
靜態分析
在建構前對映像檔定義進行檢查:
# 使用packer validate檢查設定
packer validate template.pkr.hcl
# 使用專門的工具檢查安全問題
conftest test template.pkr.hcl
功能測試
建構後驗證映像檔的功能正常:
# 使用InSpec測試映像檔功能
describe package('nginx') do
it { should be_installed }
end
describe service('nginx') do
it { should be_enabled }
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
安全掃描
檢查映像檔中的安全漏洞:
# 使用Clair掃描容器映像檔
docker run -p 5432:5432 -d --name db arminc/clair-db:latest
docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:latest
clair-scanner --ip $(hostname -i) my-app-image:latest
映像檔版本管理與發布策略
版本命名與標記
映像檔應該有清晰的版本命名策略,例如:
- 語義化版本:major.minor.patch(如1.2.3)
- 時間戳:YYYYMMDD-HHMMSS
- Git提交雜湊:git-8a7d3e2
標記應該包含足夠的中繼資料,以便快速識別映像檔的內容和用途:
# AWS AMI標記範例
aws ec2 create-tags \
--resources ami-1234567890abcdef0 \
--tags \
Key=Version,Value=1.2.3 \
Key=Environment,Value=Production \
Key=BuildDate,Value=2023-03-24 \
Key=GitCommit,Value=8a7d3e2
映像檔發布流程
映像檔發布應該遵循漸進式策略:
- 開發環境:初步測試和驗證
- 測試環境:更全面的功能和整合測試
- 預生產環境:模擬生產環境的最終驗證
- 生產環境:正式發布使用
每個環境的發布都應該有明確的驗收標準和回復機制。
不可變伺服器模式的實踐
不可變伺服器(Immutable Server)是一種基礎架構模式,其核心理念是:一旦伺服器佈署完成,就不再對其進行修改。任何變更都透過佈署新的伺服器例項來實作。
不可變伺服器的優勢
- 一致性:所有伺服器都來自相同的映像檔,確保環境一致
- 可預測性:消除了設定漂移,降低了故障排除的複雜性
- 安全性:減少了攻擊面,提高了系統安全性
- 回復簡便:只需切換回舊版本的映像檔即可實作回復
實作不可變伺服器的關鍵技術
- 自動化映像檔建構:如前所述,使用Packer等工具
- 藍綠佈署:同時維護兩個環境,一個執行當前版本,一個執行新版本
- 金絲雀發布:逐步將流量導向新版本,監控效能和錯誤
- 自動擴充套件組:使用AWS Auto Scaling Groups或類別似技術管理伺服器生命週期
藍綠佈署實作範例
# 定義兩個自動擴充套件組,一個藍色,一個綠色
resource "aws_autoscaling_group" "blue" {
name = "app-blue"
launch_configuration = aws_launch_configuration.blue.id
min_size = var.asg_min
max_size = var.asg_max
desired_capacity = var.asg_desired
vpc_zone_identifier = var.subnet_ids
tag {
key = "Name"
value = "app-blue"
propagate_at_launch = true
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "green" {
name = "app-green"
launch_configuration = aws_launch_configuration.green.id
min_size = 0
max_size = var.asg_max
desired_capacity = 0 # 初始為0,佈署時增加
vpc_zone_identifier = var.subnet_ids
tag {
key = "Name"
value = "app-green"
propagate_at_launch = true
}
lifecycle {
create_before_destroy = true
}
}
# 負載平衡器目標組
resource "aws_lb_target_group" "blue" {
name = "app-blue"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
path = "/health"
interval = 30
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 2
}
}
resource "aws_lb_target_group" "green" {
name = "app-green"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
path = "/health"
interval = 30
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 2
}
}
# 負載平衡器監聽器規則
resource "aws_lb_listener_rule" "blue" {
listener_arn = var.lb_listener_arn
priority = 100
action {
type = "forward"
target_group_arn = aws_lb_target_group.blue.arn
}
condition {
path_pattern {
values = ["/*"]
}
}
}
這段Terraform程式碼定義了藍綠佈署的基礎設施。它建立了兩個自動擴充套件組(藍色和綠色),每個都有自己的負載平衡器目標組。初始狀態下,藍色環境處於活動狀態,綠色環境的容量為零。佈署新版本時,我們會:
- 更新綠色環境的啟動設定,使用新的映像檔
- 增加綠色環境的容量
- 測試綠色環境
- 將流量從藍色環境切換到綠色環境
- 減少藍色環境的容量
這種方法允許我們在不中斷服務的情況下佈署新版本,並在發現問題時快速回復。
映像檔安全最佳實踐
基礎映像檔選擇
選擇安全的基礎映像檔是映像檔安全的第一步:
- 使用官方認證的映像檔
- 選擇最小化的映像檔(如Alpine Linux)
- 定期更新基礎映像檔以取得安全補丁
安全強化
映像檔應該進行安全強化:
# 安裝安全更新
apt-get update && apt-get upgrade -y
# 移除不必要的軟體包
apt-get remove -y telnet netcat
# 設定適當的檔案許可權
chmod 600 /etc/ssh/sshd_config
# 停用不必要的服務
systemctl disable avahi-daemon
# 設定防火牆
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow http
ufw enable
持續安全監控
映像檔安全不是一次性工作,需要持續監控:
- 定期掃描映像檔中的漏洞
- 實施自動化補丁管理
- 監控映像檔使用情況和異常行為
映像檔最佳化技術
映像檔大小最佳化
較小的映像檔可以加快佈署速度並減少儲存成本:
# 清理套件管理器快取
apt-get clean
yum clean all
# 移除臨時檔案
rm -rf /tmp/* /var/tmp/*
# 壓縮日誌檔案
find /var/log -type f -exec truncate --size=0 {} \;
# 移除不必要的語言包
apt-get purge -y $(dpkg --list | grep '^ii.*language-pack-[a-z]' | awk '{print $2}' | grep -v 'en')
啟動時間最佳化
減少伺服器啟動時間可以提高系統的彈性和可擴充套件性:
- 預先安裝和設定應用程式
- 最佳化初始化指令碼
- 使用systemd單元檔案而非傳統init指令碼
- 實施平行啟動服務
多雲端和混合雲映像檔策略
在多雲端或混合雲環境中,映像檔管理變得更加複雜。以下是一些策略:
統一映像檔定義
使用支援多平台的工具(如Packer)定義映像檔:
# 同時為AWS和Azure定義映像檔
source "amazon-ebs" "ubuntu" {
# AWS特定設定
}
source "azure-arm" "ubuntu" {
# Azure特定設定
}
build {
sources = [
"source.amazon-ebs.ubuntu",
"source.azure-arm.ubuntu"
]
# 分享的佈建步驟
provisioner "shell" {
# 平台無關的設定
}
}
平台特定最佳化
為每個雲平台進行特定最佳化:
- AWS:最佳化EBS卷和AMI設定
- Azure:利用Azure特有的功能,如代管身份
- GCP:使用GCP特有的網路和儲存功能
跨平台測試
確保映像檔在所有目標平台上正常工作:
# 使用Terratest進行跨平台測試
go test -v ./test/
容器映像檔與伺服器映像檔的比較
容器映像檔(如Docker映像檔)和伺服器映像檔(如AMI)有許多相似之處,但也有重要區別:
相似之處
- 都是預先設定的環境範本
- 都支援版本控制和不可變佈署
- 都可以透過程式碼定義和自動化建構
區別
- 隔離級別:容器分享主機OS核心,伺服器映像檔包含完整OS
- 大小:容器映像檔通常更小(MB級別),伺服器映像檔更大(GB級別)
- 啟動時間:容器啟動更快(秒級),伺服器啟動較慢(分鐘級)
- 生命週期:容器通常短暫,伺服器例項通常長期執行
何時選擇容器映像檔
- 微服務架構
- 需要快速擴充套件的應用
- 開發和測試環境
- 無狀態應用
何時選擇伺服器映像檔
- 需要完整OS隔離的應用
- 有特殊硬體需求的工作負載
- 傳統單體應用
- 有狀態服務(如資料函式庫)
映像檔生命週期管理
映像檔老化策略
隨著時間推移,映像檔會累積並消耗儲存空間。實施老化策略可以管理這一問題:
# 使用AWS CLI清理舊的AMI
aws ec2 describe-images --owners self --query 'Images[?CreationDate<`2023-01-01`].ImageId' --output text | xargs -I {} aws ec2 deregister-image --image-id {}
映像檔更新策略
定期更新映像檔以應用安全補丁和功能改進:
- 計劃性更新:定期(如每月)建構新映像檔
- 事件驅動更新:當發現重大漏洞時立即更新
- 自動化更新:使用CI/CD管道自動檢測和應用更新
映像檔函式倉管理
有效管理映像檔函式庫可以提高效率並降低成本:
- 實施標記和中繼資料策略
- 設定保留政策
- 使用RBAC控制映像檔存取
- 監控映像檔使用情況
實際案例研究:Netflix的映像檔管理
Netflix是伺服器映像檔程式碼化的先驅之一,他們的Aminator工具和Spinnaker平台展示了企業級映像檔管理的最佳實踐。
Netflix的映像檔策略
- Bake and Deploy:Netflix使用「烘焙」策略,將應用程式和依賴項封裝到AMI中
- 不可變基礎架構:一旦佈署,伺服器不會被修改
- 自動化管道:從程式碼提交到生產佈署的全自動化流程
- 金絲雀發布:逐步將流量導向新版本,監控效能和錯誤
關鍵技術
- Spinnaker:多雲端佈署平台
- Aminator:AMI建構工具
- Chaos Monkey:隨機終止伺服器例項以測試系統彈性
學習要點
- 自動化優先:從頭到尾自動化整個流程
- 測試驅動:在每個階段進行全面測試
- 漸進式發布:使用金絲雀發布減少風險
- 彈性設計:設計系統以適應伺服器故障
結語
伺服器映像檔程式碼化代表了基礎架構管理的現代化方向。透過將伺服器定義為程式碼,我們可以實作更高的一致性、可靠性和安全性。無論是使用傳統的虛擬機器映像檔還是容器映像檔,程式碼化的方法都能帶來顯著的營運效益。
隨著雲端技術的不斷發展,伺服器映像檔程式碼化將繼續演進,整合更多的自動化、安全和最佳化技術。對於現代DevOps團隊來説,掌握這些技術不僅是提高效率的方式,更是確保系統可靠性和安全性的關鍵。
透過實施本文討論的策略和最佳實踐,組織可以建立一個強大的伺服器映像檔管理系統,為雲端原生應用提供堅實的基礎。