在雲端維運實務中,建立標準化映像是實現不可變基礎架構的關鍵。Packer 專門用於從單一來源配置檔為多平台創建機器映像,達成此目標。本文將聚焦於 Packer 模板的兩個核心實踐:透過變數管理實現配置的動態化,以及針對 Linux 系統執行通用化腳本,確保產出的映像檔具備跨環境部署的純淨狀態與一致性,為高效的自動化部署流程奠定基礎。

Packer 模板組件深入解析:通用化、變數與實踐

Linux 映像的通用化

與 Windows 使用 Sysprep 不同,Linux 映像的通用化通常涉及清除系統日誌、用戶歷史記錄以及移除特定於部署的代理程式。Packer 提供 shell 配置器來執行這些任務。

Linux 通用化範例:

provisioners 區塊的結尾,可以添加一個 shell 配置器來執行通用化命令:

JSON 格式的 Linux 通用化:

{
  // ... 其他 provisioners ...
  "provisioners": [
    {
      "type": "shell",
      "execute_command": "sudo sh -c '{{ .Vars }} {{ .Path }}'",
      "inline": [
        "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
      ]
    }
  ]
}

HCL 格式的 Linux 通用化:

# ... 其他 provisioners ...

provisioner "shell" {
  execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'"
  inline = [
    "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
  ]
}

說明:

  • /usr/sbin/waagent -force -deprovision+user: 這是 Azure Linux 代理程式 (waagent) 的命令,用於移除用戶帳戶和配置資訊,使映像可以被重新部署。
  • export HISTSIZE=0: 清空 shell 的命令歷史記錄。
  • sync: 將所有緩衝的寫入操作寫入磁碟。
  • execute_command 的設定確保了命令以 root 權限執行,並且可以傳遞 Packer 的變數。

variables 區塊:模板的參數化

variables 區塊是一個可選部分,用於定義模板中可以使用的變數。這使得模板更加靈活和可重用,因為您可以避免將硬編碼的值直接寫入模板中,而是透過外部方式(如命令列參數或環境變數)提供這些值。

變數的定義與使用:

  • 定義: 在 variables 區塊中,您可以為每個變數指定一個名稱和一個預設值。預設值可以是字串、數字,或者引用環境變數。
  • 引用: 在模板的其他地方(如 buildersprovisioners 區塊),可以使用 {{ var variable_name }}{{ env ENVIRONMENT_VARIABLE_NAME }} 的語法來引用這些變數。

JSON 格式的 variables 範例:

{
  "variables": {
    "azure_client_id": "{{ env `AZURE_CLIENT_ID` }}",
    "azure_client_secret": "{{ env `AZURE_CLIENT_SECRET` }}",
    "azure_subscription_id": "{{ env `AZURE_SUBSCRIPTION_ID` }}",
    "azure_tenant_id": "{{ env `AZURE_TENANT_ID` }}",
    "vm_size": "Standard_DS2_v2",
    "location": "eastus",
    "image_name": "my-custom-image-{{timestamp}}"
  },
  "builders": [
    {
      "type": "azure-rm",
      "client_id": "{{ var `azure_client_id` }}",
      "client_secret": "{{ var `azure_client_secret` }}",
      "subscription_id": "{{ var `azure_subscription_id` }}",
      "tenant_id": "{{ var `azure_tenant_id` }}",
      "location": "{{ var `location` }}",
      "vm_size": "{{ var `vm_size` }}",
      "image_name": "{{ var `image_name` }}",
      // ... 其他 Azure 特定屬性
    }
  ],
  "provisioners": [
    // ... 配置腳本 ...
  ]
}

HCL 格式的 variables 範例:

variable "azure_client_id" {
  type    = string
  default = env("AZURE_CLIENT_ID")
}

variable "azure_client_secret" {
  type    = string
  default = env("AZURE_CLIENT_SECRET")
}

variable "azure_subscription_id" {
  type    = string
  default = env("AZURE_SUBSCRIPTION_ID")
}

variable "azure_tenant_id" {
  type    = string
  default = env("AZURE_TENANT_ID")
}

variable "vm_size" {
  type    = string
  default = "Standard_DS2_v2"
}

variable "location" {
  type    = string
  default = "eastus"
}

variable "image_name" {
  type    = string
  default = "my-custom-image-${timestamp()}"
}

source "azure-rm" "azurevm" {
  client_id       = var.azure_client_id
  client_secret   = var.azure_client_secret
  subscription_id = var.azure_subscription_id
  tenant_id       = var.azure_tenant_id
  location        = var.location
  vm_size         = var.vm_size
  image_name      = var.image_name
  # ... 其他 Azure 特定屬性
}

build {
  sources = ["source.azure-rm.azurevm"]
  # ... provisioners 區塊 ...
}

變數的傳遞:

  • 環境變數: 如範例所示,可以直接引用環境變數。這是處理敏感資訊(如認證金鑰)的推薦方式。
  • 命令列參數: 在執行 packer build 命令時,可以使用 -var-var-file 參數來傳遞變數值。例如:
    packer build -var 'vm_size=Standard_D4s_v3' -var-file='myvars.json' template.json
    
    其中 myvars.json 是一個包含變數定義的 JSON 文件。

使用變數可以讓您的 Packer 模板更加靈活,能夠適應不同的部署環境和需求,而無需修改模板本身。

Packer 模板進階應用:變數的靈活運用與實例整合

變數的引用方式

Packer 模板中的變數,無論是透過環境變數、命令列參數,還是直接在模板中定義的預設值,都可以被靈活地引用到 buildersprovisioners 等區塊中。

引用語法:

  • {{env VARIABLE_NAME}}: 引用同名的環境變數。
  • {{var variable_name}}: 引用在 variables 區塊中定義的變數。
  • {{user variable_name}}: 這是另一種引用變數的方式,通常與 packer build 命令的 -var-var-file 參數結合使用,或者在某些情況下用於引用特定於用戶的配置。在實務上,{{var variable_name}}{{user variable_name}} 在很多場景下功能是重疊的,但理解它們的區別有助於更精確地控制變數來源。

builders 區塊中使用變數:

變數的引入使得 builders 區塊的配置更加動態。例如,您可以使用變數來指定虛擬機的大小、部署區域,甚至是映像的名稱。

JSON 範例:

{
  "variables": {
    "vm_size": "Standard_DS2_v2",
    "location": "eastus",
    "image_name": "my-dynamic-image-{{timestamp}}"
  },
  "builders": [
    {
      "type": "azure-rm",
      "vm_size": "{{var `vm_size`}}",
      "location": "{{var `location`}}",
      "image_name": "{{var `image_name`}}",
      // ... 其他 Azure 連接資訊和基礎映像屬性
    }
  ],
  // ... provisioners 區塊 ...
}

HCL 範例:

variable "vm_size" {
  type    = string
  default = "Standard_DS2_v2"
}

variable "location" {
  type    = string
  default = "eastus"
}

variable "image_name" {
  type    = string
  default = "my-dynamic-image-${timestamp()}"
}

source "azure-rm" "azurevm" {
  vm_size    = var.vm_size
  location   = var.location
  image_name = var.image_name
  # ... 其他 Azure 連接資訊和基礎映像屬性
}

build {
  sources = ["source.azure-rm.azurevm"]
  # ... provisioners 區塊 ...
}

provisioners 區塊中使用變數:

變數同樣可以在 provisioners 區塊中使用,這對於配置腳本中的路徑、參數或任何動態值非常有用。

JSON 範例:

{
  "variables": {
    "image_folder": "/data/images"
  },
  "builders": [
    // ... builders 區塊 ...
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "mkdir -p {{var `image_folder`}}",
        "chmod 755 {{var `image_folder`}}"
      ],
      "execute_command": "sudo sh -c '{{ .Vars }} {{ .Path }}'"
    }
    // ... 其他 provisioners ...
  ]
}

HCL 範例:

variable "image_folder" {
  type    = string
  default = "/data/images"
}

# ... source 區塊 ...

build {
  # ... sources ...
  provisioner "shell" {
    inline = [
      "mkdir -p ${var.image_folder}",
      "chmod 755 ${var.image_folder}"
    ]
    execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'"
  }
  # ... 其他 provisioners ...
}

變數的來源與整合:

Packer 支援從多種來源獲取變數,這提供了極大的靈活性:

  • 環境變數: 直接從運行 Packer 的操作系統環境中讀取。
  • 命令列參數: 使用 packer build -var 'key=value'-var-file=path/to/vars.json
  • 模板內預設值: 在 variables 區塊中直接設定。
  • 外部機密管理: 可以整合 HashiCorp Vault 或 Consul 等機密管理工具,安全地獲取敏感資訊,如 API 金鑰或密碼。這通常需要額外的插件或配置。

縱觀現代管理者的多元挑戰,Packer 模板的精進不僅是技術議題,更反映了對效率與品質的系統性追求。從單純的映像檔製作,演進至結合通用化與變數的參數化設計,其核心價值在於將「一次性建構」轉化為「可規模化的數位資產」。這項轉變雖在初期增加了抽象化設計的門檻,卻能大幅降低後續在多環境部署、客製化需求下的維護成本與人為風險,迫使團隊從孤立的腳本思維,提升至可預測、可重現的自動化流程治理層次。

玄貓認為,這種將彈性內建於設計之初的理念,正是 DevOps 文化中「持續交付」與「基礎設施即程式碼」精神的具體實踐。對於追求卓越營運的技術領導者而言,掌握這套方法論不僅是提升部署效率,更是建立團隊技術紀律與長期競爭力的關鍵投資。