規劃 Deployment Manager 以供大規模使用

當「基礎架構即程式碼」系統在沒有規劃的情況下增長超過「Hello World」的範例時,程式碼往往會變得非結構化。非預定設定會採用硬式編碼,維護方便性也會大幅降低。

您可以使用本文件,以更有效率的方式建立大規模的部署作業結構。

此外,您可以在各團隊內強制執行命名慣例和內部最佳做法。本文件適用於具備進階技術知識的讀者,並假定您對 Python、基礎架構、Deployment Manager 和一般的基礎架構即程式碼有基本瞭解。 Google Cloud

事前準備

使用單一程式碼基底的多個環境

對於具有十幾個資源的大規模部署作業來說,標準最佳做法會要求您使用大量外部屬性 (設定參數),以避免您將字串和邏輯硬式編碼到一般範本。因為類似的服務和環境 (例如開發、測試或實際工作環境),多數屬性會部分重複。舉例來說,所有標準服務都會在類似的 LAMP 堆疊上執行。按照這些最佳做法操作會產生大量的設定屬性與重複內容,除了導致維護不易,更會增加人為疏失的可能性。

下表的程式碼範例說明每個部署作業在階層式設定和單一設定之間的差異。資料表摘要列出單一設定中常見的重複內容。如果使用階層式設定,資料表顯示如何在階層中將重複的區段移到較高層級,以避免重複並降低人為疏失的可能性。

範本 階層式設定 (沒有備援功能) 單一設定 (有備援功能)

project_config.py

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP' }

不適用

frontend_config.py

config = {'ServiceName': 'frontend'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'frontend' }

backend_config.py

config = {'ServiceName': 'backend'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'backend' }

db_config.py

config = {'ServiceName': 'db'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'db' }

為了能更妥善的處理大型程式碼基底,請使用具有串聯合併設定屬性的結構化階層配置。如要執行上述作業,請使用多個檔案進行設定,而非使用單一檔案。您也可以搭配輔助函式一起使用,將一部分程式碼基底與貴機構共用。

建立及串聯程式碼階層結構具有下列優點:

  • 將設定拆分為多個檔案時,可改善屬性的結構和易讀性。您也可以避免重複設定。
  • 將階層式合併設計為以邏輯方式串聯值,可建立頂層設定檔,供專案或元件重複使用。
  • 只需定義每個屬性一次 (覆寫除外),也不必處理屬性名稱中的命名空間。
  • 範本會根據適當的變數載入正確的設定,無需瞭解實際環境。

建立程式碼基底階層結構

Deployment Manager 部署作業包含一個 YAML 設定檔或結構定義檔和多個 Python 檔案。這些檔案共同構成部署作業的程式碼基底。Python 檔案提供多種用途。您可以將 Python 檔案做為部署範本、一般程式碼檔案 (輔助類別) 或儲存設定屬性的程式碼檔案使用。

如要建立程式碼基底階層結構,請使用部分 Python 檔案做為設定檔,不要使用標準設定檔。與連結部署作業到單一 YAML 檔案相比,這種方法能享有更大的彈性。

將基礎架構視為真正的程式碼

保持程式碼清楚簡潔的重要原則就是不要重複 (DRY)。每個項目只需定義一次即可。這種方法能讓程式碼基底更加簡潔、更方便審查及驗證,維護作業也更輕鬆。由於您只需要在一個地方修改屬性,因此可降低人為疏失的可能性。

如果是包含較少設定檔數量和最少重複內容的小型程式碼基底,請使用這些準則來建立設定結構,以遵循 DRY 原則。

機構、部門、環境和模組

如要建立清楚簡潔的程式碼基底階層結構,基本原則是使用機構、部門、環境和模組。這些原則為選用且可擴充。如要查看遵循這些原則的範例程式碼基底的階層圖表,請參閱設定階層

下圖顯示的是部署在環境中的模組。設定合併工具會根據使用位置的背景資訊,在每個層級選取適當的設定檔。同時也會自動定義系統和部門。

部署在環境中的模組

在下列清單中,數字代表覆寫順序:

  1. 機構屬性

    這是結構中的最高層級。在這個層級中,您可以儲存命名慣例中使用的設定屬性 (例如 organization_nameorganization_abbreviation),以及您想在所有團隊中共用及強制實行的輔助函式。

  2. 部門屬性

    如果您的結構中有部門,則機構包含部門。您可以在每個部門的設定檔中,分享其他部門未使用的屬性,例如 department_namecost_center

  3. 系統 (專案) 屬性

    每個部門都包含多個系統。系統是妥善定義的軟體堆疊,例如:您的電子商務平台。這並「不是」Google Cloud 專案,而是服務運作的生態系統。

    與高於「系統」的層級相較,您的團隊在系統層級內所享有的自主權高出許多。您可以在這裡針對整個團隊和全系統的參數 (例如 system_namedefault_instance_sizenaming_prefix) 定義輔助函式 (例如 project_name_generator()instance_name_generator()instance_label_generator())。

  4. 環境屬性

    您的系統可能有多個環境,例如 DevTestProd (以及選用的 QAStaging),彼此之間可能非常類似。在理想情況下,這些環境應使用相同的程式碼基底,只有設定層級不同。您可以在環境層級覆寫屬性,例如 ProdQA 設定的 default_instance_size

  5. 模組屬性

    如果您的系統規模很大,請拆分成多個模組,而不要保持為一個大型的單體式區塊。舉例來說,您可以將核心網路和安全性移入獨立區塊。您也可以將後端、前端和資料庫層區分為獨立模組。模組是第三方開發的範本,您只需在其中加入適當的設定。您可以在模組層級定義與特定模組相關的屬性,包括專為覆寫已繼承系統層級屬性的屬性。在系統中,環境層級和模組層級是平行的空間,但在合併程序中,模組會遵循環境。

  6. 特定環境的模組屬性

    某些模組屬性可能也會取決於環境,例如執行個體大小、映像檔和端點。特定環境的模組屬性是最明確的層級,也是串聯合併以覆寫先前定義值的最後一點。

合併設定的輔助類別

config_merger 類別屬於輔助類別,會自動載入適當的設定檔,並將內容合併為單一字典。

如要使用 config_merger 類別,您必須提供下列資訊:

  • 模組名稱。
  • 全域背景資訊,包含環境名稱。

呼叫 ConfigContext 靜態函式會傳回合併的設定字典。

下列程式碼示範如何使用這個類別:

  • module = "frontend" 會指定背景資訊,並載入屬性檔。
  • 系統會自動從 context.properties["envName"] 中挑選環境。
  • 全域設定。

    cc = config_merger.ConfigContext(context.properties, module)
    
    print cc.configs['ServiceName']
    

在後端,本輔助類別必須與設定結構一致、以正確順序載入所有層級,並覆寫適當的設定值。如要變更層級或覆寫順序,請修改設定合併工具類別。

在每日和日常的用途中,通常不需要變更這個類別。一般來說,您可以編輯範本和適當的設定檔,然後使用輸出字典和其中的所有設定。

本範例程式碼基底包含下列三個硬式編碼設定檔:

  • org_config.py
  • department_config.py
  • system_config.py

您可以在初始化存放區時,將機構和部門設定檔建立為符號連結。這些檔案可以位於獨立的程式碼存放區中,因為邏輯上這個程式碼存放區不屬於專案團隊程式碼基底,但與整個機構和部門共用。

設定合併工具也會尋找與結構中其他層級相符的檔案:

  • envs/[environment name].py
  • [environment name]/[module name].py
  • modules/[module name].py

設定檔

Deployment Manager 使用一個設定檔,意即一個特定部署作業使用單一檔案。所有部署作業不能共享單一設定檔。

使用 config-merger 類別時,由於您並未使用這個設定檔,因此設定屬性會從中完全卸離。如果您改為使用一系列 Python 檔案,您在部署作業中可享有更大的彈性。這些檔案也可以與其他部署作業共享。

任何 Python 檔案都可以包含變數,讓您能以結構化的分散式方式儲存設定。最佳做法是使用具有商定結構的字典。設定合併工具會在合併鏈的每個檔案中尋找名為 configs 的字典。獨立的 configs 會合併為同一個。

在合併期間,當路徑和名稱相同的屬性多次出現在字典中時,設定合併工具就會覆寫該屬性。 在某些情況下,這個行為相當實用,例如特定內容值得以覆寫預設值。但在很多情況下,您可能想要避免覆寫屬性。如要避免覆寫屬性,請為屬性新增獨立的命名空間,藉此避免屬性重複。在下列範例中,您會在設定字典中建立額外層級 (亦即建立子字典) 以新增命名空間。

config = {
    'Zip_code': '1234'
    'Count': '3'
    'project_module': {
        'admin': 'Joe',
    }
}

config = {
    'Zip_code': '5555'
    'Count': '5'
    'project_module_prod': {
        'admin': 'Steve',
    }
}

輔助類別和命名慣例

命名慣例是確保能妥善控管 Deployment Manager 基礎架構的最佳方式。請勿使用任何模稜兩可或一般的名稱,例如 my projecttest instance

下列範例是全機構的執行個體命名慣例:

def getInstanceName(self, name):
  return '-'.join(self.configs['Org_level_configs']['Org_Short_Name'],
                  self.configs['Department_level_configs']['Department_Short_Name'],
                  self.configs['System_short_name'],
                  name,
                  self.configs["envName"])

提供輔助函式可讓您輕鬆根據商定慣例來命名每個執行個體。這也會讓程式碼審查更加簡單,因為所有執行個體名稱全都來自這個函式。函式會自動從層級較高的設定中挑選名稱。這種方法有助於避免不必要的輸入。

您可以將這些命名慣例套用到大多數 Google Cloud 資源和標籤。更複雜的函式甚至可以產生一組預設標籤。

範例程式碼基底的資料夾結構

範例程式碼基底的資料夾結構具有彈性且可自訂。 不過,其中有一部分硬式編碼到設定合併工具和 Deployment Manager 結構定義檔,這代表如果您進行任何修改,必須在設定合併工具和結構定義檔中反映相關異動。

├── global
│   ├── configs
│   └── helper
└── systems
    └── my_ecom_system
        ├── configs
        │   ├── dev
        │   ├── envs
        │   ├── modules
        │   ├── prod
        │   └── test
        ├── helper
        └── templates
    

全域資料夾包含不同專案團隊之間共用的檔案。為求簡單起見,設定資料夾會包含機構設定和所有部門的設定檔。在本範例中,部門沒有獨立的輔助類別。您可以在機構或系統層級新增任何輔助類別。

全域資料夾可以位於獨立的 Git 存放區中。您可以從個別系統中參照檔案。您也可以使用符號連結,但在某些作業系統中可能會造成混淆或中斷。

├── configs
│   ├── Department_Data_config.py
│   ├── Department_Finance_config.py
│   ├── Department_RandD_config.py
│   └── org_config.py
└── helper
    ├── config_merger.py
    └── naming_helper.py

系統資料夾包含一或多個不同的系統。系統各自獨立,也不會共用設定。

├── configs
│   ├── dev
│   ├── envs
│   ├── modules
│   ├── prod
│   └── test
├── helper
└── templates

設定資料夾包含這個系統專屬的所有設定檔,也會透過符號連結參照全域設定。

├── department_config.py -> ../../../global/configs/Department_Data_config.py
├── org_config.py -> ../../../global/configs/org_config.py
├── system_config.py
├── dev
│   ├── frontend.py
│   └── project.py
├── prod
│   ├── frontend.py
│   └── project.py
├── test
│   ├── frontend.py
│   └── project.py
├── envs
│   ├── dev.py
│   ├── prod.py
│   └── test.py
└── modules
    ├── frontend.py
    └── project.py

Org_config.py:

config = {
  'Org_level_configs': {
    'Org_Name': 'Sample Inc.',
    'Org_Short_Name': 'sampl',
    'HQ_Address': {
      'City': 'London',
      'Country': 'UK'
    }
  }
}

在輔助資料夾中,您可以加入更多輔助類別並參照全域類別。

├── config_merger.py -> ../../../global/helper/config_merger.py
└── naming_helper.py -> ../../../global/helper/naming_helper.py

在範本資料夾中,您可以儲存或參照 Deployment Manager 範本。這裡也可以使用符號連結。

├── project_creation -> ../../../../../../examples/v2/project_creation
└── simple_frontend.py

最佳做法

下列最佳做法可協助您建立程式碼階層結構。

結構定義檔案

在結構定義檔案中,Deployment Manager 要求列出在部署期間以任何方式使用的每個檔案。加入整個資料夾可讓程式碼更加簡短與通用。

  • 輔助類別:
- path: helper/*.py
  • 設定檔:
- path: configs/*.py
- path: configs/*/*.py
  • 大量 (glob 樣式) 匯入
gcloud config set deployment_manager/glob_imports True

多個部署項目

這個做法最適合包含多個部署的系統,這些部署都使用相同的設定組合,即使位於不同的模組 (例如網路、防火牆、後端、前端) 中也一樣。您可能需要透過其他部署存取這些部署的輸出。您可以在部署完成後查詢部署的輸出,並將查詢結果儲存在設定資料夾中。您可以在合併程序中加入這些設定檔。

符號連結支援 gcloud deployment-manager 指令,並會正確載入連結的檔案。但符號連結不支援所有 OS。

設定階層

下圖為不同層級和層級關係的總覽。每個矩形代表一個屬性檔,檔案名稱以紅色標示。

強調不同層級與層級關係的設定階層。

情境感知合併順序

設定合併工具會根據每個使用檔案中的背景資訊,在每個層級選取適當的設定檔。背景資訊是部署在環境中的模組。本背景資訊會自動定義系統和部門。

下圖中的數字代表階層中的覆寫順序:

覆寫順序圖表

後續步驟