基礎設施程式碼的團隊工作流程與最佳實踐

在現代技術環境中,使用程式碼管理基礎設施已成為標準實踐。然而,這種方法不僅是學習新工具或技能,而是徹底改變了團隊設計、建構和管理基礎設施的方式。本文將探討基礎設施即程式碼(Infrastructure as Code, IaC)的團隊工作流程,分析不同角色的職責,以及如何建立高效的交付流程。

專案整合策略與基礎設施程式碼交付

在管理複雜的基礎設施時,不同團隊負責的基礎設施元件之間的整合是一個關鍵挑戰。以下探討幾種主要的整合策略及其應用場景。

應用時整合:動態相依性管理的彈性方案

應用時整合是一種靈活的專案整合模式,它允許基礎設施元件在被應用到環境時動態整合。這種方法的核心優勢在於其適應性和即時性。

以ShopSpinner專案為例,當應用程式基礎設施堆積積疊(application-infrastructure-stack)需要參照由分享網路堆積積疊(shared-network-stack)建立的網路結構時,應用時整合允許這種依賴關係在佈署時動態解析。

應用時整合的關鍵特點:

  1. 動態依賴解析:每次應用基礎設施程式碼時,系統會動態解析並使用最新版本的依賴元件
  2. 標準化介面:依賴元件必須透過標準化方式暴露識別符,確保消費元件能夠可靠地參照它們
  3. 隔離測試能力:使用測試夾具(test fixtures)可以在不依賴實際分享元件的情況下測試單個堆積積疊
# 分享網路堆積積疊暴露的輸出範例
output "private_subnet_ids" {
  value       = aws_subnet.private[*].id
  description = "私有子網路的ID列表,供其他堆積積疊使用"
}

# 應用程式堆積積疊中參照分享網路的方式
data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "shopspinner-terraform-state"
    key    = "shared-network/terraform.tfstate"
    region = "us-west-2"
  }
}

resource "aws_instance" "app_server" {
  # 使用分享網路堆積積疊暴露的子網路ID
  subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0]
  # 其他設定...
}

上面的程式碼展示了應用時整合的實際實作方式。分享網路堆積積疊透過輸出(output)暴露了私有子網路的ID列表,而應用程式堆積積疊則使用terraform_remote_state資料源來取得這些輸出值。這種方式允許應用程式堆積積疊在佈署時動態參照最新版本的網路資源,而不需要硬編碼這些值。

契約測試確保整合可靠性

為了確保這種動態整合的可靠性,團隊應該實施契約測試:

  1. 提供者契約測試:分享網路堆積積疊應該驗證網路結構(如子網路)已被建立,與其識別符透過預期的機制暴露
  2. 消費者驅動契約測試(CDC):依賴分享資源的團隊可以編寫測試,在提供者專案的管道中執行,幫助提供者團隊理解消費者的期望
# 分享網路堆積積疊的契約測試範例
def test_network_exposes_required_outputs():
    # 執行terraform output命令取得輸出
    outputs = subprocess.check_output(["terraform", "output", "-json"]).decode()
    outputs_json = json.loads(outputs)
    
    # 驗證必要的輸出存在與格式正確
    assert "private_subnet_ids" in outputs_json
    assert isinstance(outputs_json["private_subnet_ids"]["value"], list)
    assert len(outputs_json["private_subnet_ids"]["value"]) > 0

這個測試範例展示瞭如何為分享網路堆積積疊建立契約測試。測試確保堆積積疊暴露了必要的輸出(如私有子網路ID),並且這些輸出的格式符合消費者的期望。這種測試應該在分享網路堆積積疊的CI/CD管道中執行,以確保任何更改都不會破壞與消費者的契約。

應用時整合與其他整合模式的比較

應用時整合與其他整合模式(如建構時整合和交付時整合)形成了一個光譜:

  • 建構時整合:在交付週期開始時一次性整合專案,適合穩定的依賴關係
  • 交付時整合:在交付週期的特定點整合專案,平衡了穩定性和靈活性
  • 應用時整合:每次應用程式碼時動態整合,提供最大的靈活性,但可能增加複雜性

應用時整合特別適合伺服器佈建場景,例如在建立新的伺服器例項時應用最新版本的伺服器模組。

使用指令碼包裝基礎設施工具:挑戰與最佳實踐

大多數管理基礎設施程式碼的團隊都會建立自定義指令碼來協調和執行基礎設施工具。這些指令碼可能使用Make、Rake、Gradle等建構工具,或者用Bash、Python、PowerShell等語言編寫。然而,這些支援程式碼往往變得與定義基礎設施的程式碼一樣複雜,導致團隊花費大量時間除錯和維護它們。

指令碼處理的常見任務

包裝指令碼通常處理多種任務,包括:

  1. 組態管理:組裝設定引數值,可能需要解析引數層次結構
  2. 依賴解析:解析並取得函式庫、提供者和其他程式碼
  3. 封裝:準備程式碼交付,無論是封裝成工件還是建立或合併分支
  4. 晉升:將程式碼從一個階段移動到下一個階段
  5. 協調:根據依賴關係按正確順序應用不同的堆積積疊和其他基礎設施元素
  6. 執行:執行相關的基礎設施工具,組裝命令列引數和設定檔案
  7. 測試:設定和執行測試,包括設定測試夾具和資料,收集和發布結果

設定值組裝的複雜性

組織和解析設定值可能是包裝指令碼任務中最複雜的之一。考慮ShopSpinner這樣的系統,涉及多個交付環境、多個生產客戶例項和多個基礎設施元件。

即使是簡單的單層設定值集,每個元件、環境和客戶的組合都需要相當多的檔案,而與許多值會重複。

# 一個處理設定層次結構的指令碼範例
#!/bin/bash

# 定義設定檔案路徑
SHARED_CONFIG="./config/shared.json"
ENV_CONFIG="./config/environments/${ENVIRONMENT}.json"
COMPONENT_CONFIG="./config/components/${COMPONENT}.json"
INSTANCE_CONFIG="./config/instances/${CUSTOMER}_${ENVIRONMENT}_${COMPONENT}.json"

# 按優先順序合併設定
jq -s '.[0] * .[1] * .[2] * .[3]' \
  $SHARED_CONFIG $ENV_CONFIG $COMPONENT_CONFIG $INSTANCE_CONFIG > ./config.json

# 使用合併後的設定執行Terraform
terraform apply -var-file=./config.json

這個指令碼展示了處理設定層次結構的複雜性。它從四個不同層次的設定檔案中讀取值:分享設定、環境特定設定、元件特定設定和例項特定設定。使用jq工具,指令碼按照優先順序合併這些設定,然後將結果傳遞給Terraform。

這種引數層次結構雖然靈活,但編碼起來相當複雜。當引入新引數、設定正確值或追蹤和除錯任何給定例項中使用的值時,人們很難理解。

簡化包裝指令碼的策略

玄貓觀察到許多團隊在處理包裝指令碼的錯誤上花費的時間比改進基礎設施程式碼還多。這種情況源於混合和耦合本應分開的關注點。以下是一些需要考慮的領域:

  1. 拆分專案生命週期:單個指令碼不應該處理專案生命週期的建構、晉升和應用階段的任務。為每個活動編寫和使用不同的指令碼。

  2. 分離任務:拆分管理基礎設施所涉及的各種任務,如組裝設定值、封裝程式碼和執行基礎設施工具。定義這些任務之間的整合點,保持它們鬆散耦合。

  3. 解耦專案:協調跨多個專案操作的指令碼應該與執行專案內任務的指令碼分開。並且應該可以單獨執行任何專案的任務。

  4. 保持包裝程式碼無知:指令碼不應該瞭解它們支援的專案。避免將依賴於基礎設施程式碼功能的操作硬編碼到包裝指令碼中。理想的包裝指令碼是通用的,可用於特定形狀的任何基礎設施專案。

# 一個通用的基礎設施堆積積疊應用指令碼
#!/usr/bin/env python3

import argparse
import subprocess
import os
import json

def load_config(stack_name, environment):
    """根據堆積積疊名稱和環境載入設定"""
    # 實作設定載入邏輯
    pass

def apply_stack(stack_name, environment, config):
    """應用基礎設施堆積積疊"""
    # 設定環境變數
    os.environ["TF_VAR_environment"] = environment
    
    # 切換到堆積積疊目錄
    os.chdir(f"./stacks/{stack_name}")
    
    # 初始化Terraform
    subprocess.run(["terraform", "init"], check=True)
    
    # 應用堆積積疊
    subprocess.run([
        "terraform", "apply",
        "-var-file", f"{config['var_file']}",
        "-auto-approve"
    ], check=True)

def main():
    parser = argparse.ArgumentParser(description="應用基礎設施堆積積疊")
    parser.add_argument("stack", help="要應用的堆積積疊名稱")
    parser.add_argument("environment", help="目標環境")
    args = parser.parse_args()
    
    config = load_config(args.stack, args.environment)
    apply_stack(args.stack, args.environment, config)

if __name__ == "__main__":
    main()

這個Python指令碼展示了一個更通用、更模組化的方法來包裝基礎設施工具。它接受堆積積疊名稱和環境作為引數,載入適當的設定,然後應用堆積積疊。這種設計遵循了上述簡化策略:

  1. 它專注於單一任務(應用堆積積疊),而不是處理整個生命週期
  2. 它將設定載入和堆積積疊應用分離為不同的函式
  3. 它不包含特定於任何堆積積疊實作的硬編碼邏輯
  4. 它可以用於任何遵循相同目錄結構的堆積積疊

將包裝指令碼視為"真正的"程式碼有助於所有這些工作。使用shellcheck等工具測試和驗證指令碼。將良好軟體設計的規則應用於指令碼,如組合規則、單一責任原則和圍繞領域概念設計。

團隊角色與責任:誰應該編寫基礎設施程式碼?

基礎設施即程式碼徹底改變了團隊設計、建構和管理基礎設施的方式。讓我們探討參與大多數基礎設施系統的角色,以及如何重新分配責任以提高效率。

基礎設施團隊中的關鍵角色

在大多數基礎設施系統中,有幾個關鍵角色參與其中。這些角色通常不是一對一對映到個人—有些人扮演多個角色,有些人與他人分享角色:

  1. 使用者:直接使用基礎設施的人。在許多組織中,應用程式團隊是使用者。
  2. 治理工作者:制定各種領域政策的人,包括安全、法律合規、架構、效能、成本控制和正確性。
  3. 設計師:設計基礎設施的人。在某些組織中,這些人是架構師,可能分為不同的領域,如網路或儲存。
  4. 工具製造者:提供其他團隊用於建構或執行環境的服務、工具和元件的人。
  5. 建構者:建構和更改基礎設施的人。
  6. 測試者:驗證基礎設施的人。這個角色不限於QA(品質分析師)。
  7. 支援:確保系統繼續正確執行並在出現問題時修復它的人。

傳統上,這些角色可能分佈在不同的團隊中,每個團隊負責工作流程的一部分。然而,這種結構往往導致溝通障礙和效率低下。

誰應該編寫基礎設施程式碼?

組織對於誰應該編寫和編輯基礎設施程式碼有不同的答案:

  1. 建構者編寫程式碼:一些組織嘗試保持傳統流程和團隊結構。因此,建構(可能還支援)基礎設施的團隊使用基礎設施即程式碼工具來最佳化其工作。使用者請求環境,建構團隊使用其工具和指令碼為他們建構環境。

  2. 使用者編寫程式碼:許多組織使應用程式團隊能夠定義其應用程式使用的基礎設施。這將使用者需求與解決方案對齊。然而,它要麼需要每個團隊包括具有強大基礎設施專業知識的人,要麼需要簡化定義基礎設施的工具。

  3. 工具製造者編寫程式碼:專業團隊可以建立平台、函式庫和工具,使用者能夠定義他們需要的基礎設施。在這些情況下,使用者傾向於編寫更多設定而非程式碼。工具製造者團隊編寫程式碼與建構者團隊編寫程式碼的區別在於自助服務。

  4. 治理和測試者編寫程式碼:制定政策和標準的人,以及需要確保更改的人,可以建立工具,幫助其他人驗證自己的程式碼。

使用價值流對映改進工作流程

價值流對映是分解交付時間的有用方法,可以瞭解時間去向。透過測量各種活動(包括等待)所花費的時間,可以將改進重點放在最有影響的領域。

  graph LR
    A[需求提交] -->|2天| B[需求審核]
    B -->|3天| C[設計審批]
    C -->|1天| D[基礎設施編碼]
    D -->|4小時| E[程式碼審核]
    E -->|1天| F[測試環境佈署]
    F -->|2小時| G[測試執行]
    G -->|2天| H[生產佈署審批]
    H -->|4小時| I[生產佈署]
 

在上面的價值流圖中,我們可以看到從需求提交到生產佈署的整個流程需要約10天。雖然基礎設施編碼只需要1天,但整個流程中的等待時間(如需求審核、設計審批和生產佈署審批)佔用了大部分時間。

透過這種分析,團隊可以識別出最大的瓶頸(在這個例子中是需求審核和設計審批),並專注於改進這些領域,而不是試圖進一步最佳化已經相對高效的編碼過程。

應用程式碼到基礎設施:最佳實踐與陷阱

對基礎設施進行更改的典型工作流程從分享原始碼函式庫中的程式碼開始。團隊成員將最新版本的程式碼提取到他們的工作環境中,並在那裡編輯它。當他們準備好時,他們將程式碼推播到原始碼函式庫,並將新版本的程式碼應用到各種環境中。

從本地工作站應用程式碼的陷阱

從命令列應用基礎設施程式碼對於沒有其他人使用的基礎設施測試例項可能很有用。但從本地工作環境執行工具會為分享的基礎設施例項帶來問題,無論是生產環境還是交付環境。

主要問題包括:

  1. 版本控制問題:如果某人在應用程式碼前對其本地版本進行更改,但在推播到分享函式庫之前應用了程式碼,那麼其他人就無法存取該版本的程式碼。
  2. 衝突風險:如果應用本地程式碼的人沒有立即推播他們的更改,其他人可能會提取和編輯較舊版本的程式碼。當他們應用該程式碼時,他們會還原第一個人的更改。
  sequenceDiagram
    participant A as 開發者A
    participant Repo as 程式碼倉函式庫
    participant Infra as 基礎設施
    participant B as 開發者B
    
    A->>Repo: 提取程式碼v1
    B->>Repo: 提取程式碼v1
    A->>A: 修改為v2
    A->>Infra: 應用v2
    B->>B: 修改為v3
    B->>Infra: 應用v3 (覆寫v2)
    A->>Repo: 推播v2
    Note over Repo,Infra: 倉函式庫中是v2,但基礎設施是v3
    B->>Repo: 嘗試推播v3 (衝突)

值得注意的是,鎖定解決方案(如Terraform的狀態鎖定)不能防止這種情況。鎖定阻止兩個人同時將他們的程式碼應用到同一個例項。但是,截至目前,鎖定解決方案不會阻止人們應用不同版本的程式碼,只要他們各自等待輪到自己。

因此,對於任何基礎設施例項,程式碼應該始終從同一位置應用。更好的解決方案是使用中央系統來處理分享基礎設施例項。

從中央化服務應用程式碼

可以使用中央化服務將基礎設施程式碼應用到例項,無論是自己託管的應用程式還是第三方服務。該服務從原始碼函式庫或工件函式庫提取程式碼並將其應用到基礎設施,強制執行明確、受控的流程來管理應用哪個版本的程式碼。

  sequenceDiagram
    participant A as 開發者A
    participant B as 開發者B
    participant Repo as 程式碼倉函式庫
    participant CI as CI/CD服務
    participant Infra as 基礎設施
    
    A->>Repo: 提取程式碼v1
    B->>Repo: 提取程式碼v1
    A->>A: 修改為v2
    A->>Repo: 推播v2
    B->>B: 修改為v3
    B->>Repo: 提取最新程式碼
    B->>B: 解決衝突
    B->>Repo: 推播合併後的v4
    Repo->>CI: 觸發管道
    CI->>Infra: 應用v4

中央服務的優勢包括:

  1. 一致性:確保基礎設施工具以一致的方式執行,使用相同版本的工具、指令碼和支援實用程式
  2. 完全自動化:強制團隊自動化整個流程,避免手動步驟
  3. 清晰的版本控制:當出現問題時,很容易看到應用了哪個版本的程式碼並進行修正
  4. 與管道模型對齊:與用於跨環境交付基礎設施程式碼的管道模型很好地對齊

個人基礎設施例項的管理

理想情況下,開發人員應該能夠在推播到分享程式碼函式庫之前測試他們的程式碼更改。這提供了一種方法來確保更改達到預期效果,並且比等待管道將程式碼執行到線上測試階段更快。

團隊可以做幾件事來使推播前測試程式碼更改變得更容易:

  1. 個人例項建立:確保每個處理基礎設施程式碼的人都可以建立自己的基礎設施例項
  2. 保持基礎設施元件小型:這是本章中的三個核心實踐之一。應該能夠獨立啟動系統的任何元件
  3. 使用相同的工具和指令碼:人們應該使用與分享例項相同的工具和指令碼來應用和測試他們的基礎設施例項
# 個人開發環境設定指令碼範例
#!/bin/bash

# 設定唯一的開發者環境ID
DEV_ID=$(whoami)
export TF_VAR_environment="dev-${DEV_ID}"

# 確保使用隔離的狀態檔案
export TF_CLI_ARGS="-state=terraform.${DEV_ID}.tfstate"

# 應用基礎設施程式碼
terraform init
terraform apply -auto-approve

echo "個人開發環境 ${TF_VAR_environment} 已建立"
echo "使用以下命令銷毀環境:"
echo "TF_VAR_environment=${TF_VAR_environment} TF_CLI_ARGS=\"${TF_CLI_ARGS}\" terraform destroy"

這個指令碼展示瞭如何為開發人員建立個人基礎設施例項。它使用開發人員的使用者名建立一個唯一的環境ID,設定Terraform使用隔離的狀態檔案,然後應用基礎設施程式碼。這種方法允許每個開發人員擁有自己的隔離環境,而不會干擾其他人的工作。

原始碼分支在工作流程中的應用

分支是分享原始碼函式庫的強大功能,使人們更容易對程式碼函式庫的不同副本(分支)進行更改,然後在準備好時整合他們的工作。在基礎設施即程式碼的背景下,有幾個值得強調的分支策略區別。

生產路徑模式與整合模式

團隊使用生產路徑分支模式來管理哪些版本的程式碼應用於環境。典型的生產路徑模式包括發布分支和環境分支。

整合模式描述了處理程式碼函式庫的人管理何時以及如何整合他們工作的方式。大多數團隊使用主線整合模式,無論是使用功能分支還是持續整合。

具體的模式或策略不如使用方式重要。團隊有效使用分支的最重要因素是整合頻率,即每個人將所有程式碼合併到中央函式庫的同一(主)分支的頻率。DORA Accelerate研究發現,團隊內所有程式碼的更頻繁整合與更高的商業績效相關。他們的結果表明,團隊中的每個人應該至少每天將所有程式碼整合在一起。

  gitGraph
    commit
    branch feature-a
    checkout feature-a
    commit
    commit
    checkout main
    merge feature-a
    branch feature-b
    checkout feature-b
    commit
    commit
    checkout main
    merge feature-b
    branch feature-c
    checkout feature-c
    commit
    checkout main
    merge feature-c

上圖展示了一個使用功能分支但保持頻繁整合的工作流程。每個功能分支都相對短暫,並在完成後立即合併回主分支。這種方法確保了團隊成員的工作經常整合,減少了長期分支可能導致的合併衝突和整合問題。

合併不等於整合

人們有時會將構建伺服器自動在分支上執行測試與持續整合混淆。持續整合的實踐,以及與更高團隊績效的相關性,是根據完全整合程式碼函式庫中每個人正在處理的所有更改。

雖然使用功能分支模式的人可能會頻繁地將當前主分支合併到自己的分支,但他們通常不會將自己的工作整合回主分支,直到他們完成功能。如果其他人以相同的方式在自己的功能分支上工作,那麼程式碼只有在每個人完成功能並將更改合併到主分支後才會完全整合。

整合涉及雙向合併—個人將自己的更改合併到主分支,以及將主分支合併回自己的分支或程式碼的本地副本。因此,持續整合意味著每個人在工作時都這樣做,至少每天一次。

團隊責任重組:提高效率的關鍵

傳統的基礎設施管理方法通常將責任嚴格分配給不同的團隊,導致溝通障礙和效率低下。基礎設施即程式碼提供了重新思考這些責任分配的機會。

責任重組的方法

有幾種方法可以重新分配基礎設施管理的責任:

  1. 產品團隊自主權:將更多責任轉移給產品團隊,使他們能夠自行管理基礎設施。這減少了依賴專業團隊的需求,加快了交付速度。

  2. 平台團隊支援:建立平台團隊,為產品團隊提供自助服務工具和平台。平台團隊專注於建立和改進工具,而不是直接處理基礎設施請求。

  3. 嵌入式工作者:將基礎設施工作者嵌入到產品團隊中,提供專業知識同時保持與業務目標的密切聯絡。

  4. 社群實踐:建立跨團隊社群,分享知識、最佳實踐和工具。這促進了標準化和創新,同時保持了團隊的自主權。

  graph TD
    A[傳統模型] --> B[請求基礎設施]
    B --> C[基礎設施團隊審核]
    C --> D[基礎設施團隊實施]
    D --> E[交付給產品團隊]
    
    F[責任重組模型] --> G[產品團隊定義基礎設施需求]
    G --> H[使用平台團隊提供的工具]
    H --> I[自助服務佈署]
    I --> J[平台團隊提供支援]

上圖對比了傳統模型和責任重組模型。在傳統模型中,產品團隊必須透過基礎設施團隊請求和接收基礎設施。在責任重組模型中,產品團隊使用平台團隊提供的工具自行佈署基礎設施,平台團隊專注於提供支援和改進工具。

衡量工作流程的有效性

衡量團隊工作流程的有效性是持續改進的關鍵。Accelerate研究中的四個關鍵指標是決定如何衡量團隊有效性的良好基礎:

  1. 佈署頻率:團隊向生產環境成功佈署的頻率
  2. 變更前置時間:從程式碼提交到成功在生產中執行所需的時間
  3. 還原服務時間:從生產事件中還原所需的時間
  4. 變更失敗率:導致生產故障或需要修復的變更百分比

這些指標可以用來建立SLI(服務級別指標)、SLO(服務級別目標)和SLA(服務級別協定)。

# 計算佈署頻率和變更前置時間的指令碼範例
import pandas as pd
from datetime import datetime, timedelta

# 假設我們有一個包含佈署資料的CSV檔案
deployments = pd.read_csv('deployments.csv')
deployments['timestamp'] = pd.to_datetime(deployments['timestamp'])

# 計算過去30天的佈署頻率
now = datetime.now()
thirty_days_ago = now - timedelta(days=30)
recent_deployments = deployments[deployments['timestamp'] > thirty_days_ago]
deployment_frequency = len(recent_deployments) / 30  # 每天平均佈署次數

# 計算變更前置時間
deployments['lead_time'] = deployments['production_timestamp'] - deployments['commit_timestamp']
average_lead_time = deployments['lead_time'].mean()

print(f"佈署頻率: {deployment_frequency:.2f}

防止設定漂移的策略與實踐

在前面的內容中,我們討論了設定漂移的危險性,這種現象會導致原本相似的基礎設施元素隨著時間推移而變得不一致。設定漂移通常發生在團隊使用基礎設施編碼工具來自動化部分傳統工作方式,而非完全適應新的工作模式時。以下是幾種可以在工作流程中避免設定漂移的方法。

最小化自動化延遲

自動化延遲是指兩次執行自動化流程(如應用基礎設施程式碼)之間經過的時間。上次執行流程至今的時間越長,流程失敗的可能性就越大。即使沒有人刻意進行變更,系統也會隨著時間而改變。

即使程式碼本身沒有變化,長時間隔後重新應用它仍可能因為各種原因而失敗:

  • 有人可能修改了系統的其他部分(如某個依賴項),這種變更只有在重新應用你的程式碼時才會導致問題
  • 用於應用程式碼的工具或服務的升級或設定變更可能與你的程式碼不相容
  • 應用未變更的程式碼可能仍會引入傳遞性依賴項的更新,例如作業系統套件
  • 有人可能進行了手動修復或改進,但忘記將其整合回程式碼中,重新應用程式碼會還原這些修復

自動化延遲的推論是:應用基礎設施程式碼的頻率越高,失敗的可能性就越低。當失敗確實發生時,你可以更快地發現原因,因為自上次成功執行以來變化較少。

避免臨時性應用

一些團隊從「鐵器時代」的工作方式中延續下來的習慣是隻在需要進行特定變更時才應用程式碼。他們可能只使用基礎設施程式碼來設定新的基礎設施,而不用於對現有系統進行變更。或者,他們可能編寫並應用基礎設施程式碼來對系統的特定部分進行臨時性變更。例如,他們為其中一個應用程式伺服器編寫一次性的設定變更程式碼。

即使團隊使用程式碼進行變更,並將程式碼應用於所有例項,有時他們可能只在對程式碼進行變更時才應用程式碼。這些習慣可能導致設定漂移或自動化延遲。

持續應用程式碼

消除設定漂移的核心策略是持續將基礎設施程式碼應用於例項,即使程式碼本身沒有變更。許多伺服器設定工具,包括Chef和Puppet,都設計為按計劃重新應用設定,通常是每小時一次。

GitOps方法論涉及從原始碼分支援續將程式碼應用到每個環境。你應該能夠使用中央服務來應用程式碼,以持續重新應用程式碼到每個例項。

不可變基礎設施

不可變基礎設施以不同的方式解決設定漂移問題。與頻繁地將設定程式碼應用於基礎設施例項不同,你只在建立例項時應用一次。當程式碼變更時,你建立一個新例項並用它替換舊例項。

透過建立新例項來進行變更需要複雜的技術來避免停機時間,並且可能不適用於所有使用場景。自動化延遲仍然是一個潛在問題,因此使用不可變基礎設施的團隊傾向於頻繁重建例項,就像鳳凰伺服器一樣。

鳳凰伺服器(Phoenix Server)是一種經常被重建的伺服器,目的是確保設定過程的可重複性。這種概念也可以應用於其他基礎設施結構,包括基礎設施堆積積疊。這種方法的核心理念是:定期完全重建系統,而不是持續修補現有系統,從而確保系統始終與其定義的程式碼保持一致。

GitOps工作流程

GitOps是基礎設施即程式碼的一種變體,涉及從原始碼分支援續同步程式碼到環境。GitOps強調將系統定義為程式碼。

GitOps並不規定測試和交付基礎設施程式碼的方法,但它與使用管道交付程式碼的方式相容。然而,GitOps不鼓勵使用交付成品,而是透過將程式碼變更合併到原始碼分支來推動程式碼變更。

GitOps的另一個關鍵元素是持續將程式碼同步到系統。與其讓構建伺服器作業或管道階段在程式碼變更時應用程式碼,GitOps使用一個服務持續比較程式碼與系統,從而減少設定漂移。

有些團隊將其流程描述為GitOps,但只實施了環境分支實踐,而沒有持續將程式碼同步到環境。這很容易導致臨時性變更流程,以及為每個環境複製、貼上和編輯程式碼變更的不良習慣,符合複製貼上反模式。

GitOps的核心價值在於它建立了一個明確的、以Git為中心的工作流程,使基礎設施變更變得可追蹤、可稽核與可回復。它的主要優勢包括:

  1. 單一事實來源:Git儲存函式庫成為系統狀態的權威來源
  2. 自動化同步:系統狀態自動與Git儲存函式庫保持同步
  3. 可見性與稽核:所有變更都記錄在Git歷史中
  4. 回復能力:可以輕鬆回復到先前的已知良好狀態

這種方法特別適合Kubernetes等宣告式系統,但原則上可以應用於任何可以用程式碼定義的基礎設施。

根據管道的工作流程中的治理

治理對大多陣列織來説都是一個關注點,特別是較大的組織和那些在金融和醫療保健等受監管行業工作的組織。有些人將治理視為一個負面詞彙,認為它意味著為有用的工作增加不必要的摩擦。但治理只是確保事情按照組織的政策負責任地完成。

在基礎設施即程式碼的環境中,治理不應該被視為一種阻礙,而應該被視為一種確保系統安全、合規和可靠的方式。透過將治理要求編碼到管道中,組織可以實作「合規即程式碼」的理念,使合規成為開發過程的自然組成部分,而不是事後的檢查。這種方法不僅提高了效率,還增強了整體安全性和合規性。

持續同步與設定漂移預防

持續同步是防止設定漂移的關鍵策略。當系統設定與其定義的程式碼持續比較和同步時,任何未經授權或意外的變更都會被自動糾正。這種方法不僅確保了系統的一致性,還提供了一種自動修復機制,使系統能夠從故障中還原。

在實踐中,這通常涉及設定一個持續執行的服務,該服務監視Git儲存函式庫中的變更,並自動將這些變更應用到目標環境。同時,它還會檢測環境中的任何偏差,並將其調整回與程式碼定義一致的狀態。

這種雙向同步機制是GitOps和其他現代基礎設施管理方法的核心,它確保了系統的實際狀態始終與其預期狀態保持一致,從而大減少了設定漂移的風險。