1. 程式人生 > >映象即程式碼:基於Packer構建阿里雲映象

映象即程式碼:基於Packer構建阿里雲映象

開發十年,就只剩下這套架構體系了! >>>   

什麼是Packer

PackerHashiCorp推出的一款工具,旨在提供簡易的方式自動化構建映象。通過Packer,你只需要在配置檔案中指明映象構建所需的基本資訊及期望安裝到映象中的軟體及配置,即可通過自動化指令碼構建所需的映象。由於構建映象的過程被固化成了配置檔案,每一個步驟都清晰可見易於回溯,無需擔心多次構建得到的映象存在不一致。且映象構建配置化後,將為測試和更新映象帶來極大的便利,大大降低運維和管理映象的成本。

在具體介紹Packer的使用方法之前,我們先來看下以前在阿里雲ECS上如何手動建立一個自定義映象。如果對這個流程已非常熟悉,可以直接跳到通過Packer構建映象一節。

注意:後續操作會建立一些收費資源,請注意釋放和清理,如例項、公網IP、快照等。
例項規格和映象會隨著時間的推移不斷更新,本文後續提到的一些規格和映象可能會在未來下線,所以具體操作流程可以根據實際情況選擇不同的規格、映象或者其他例項相關的資源。

手動建立自定義映象

簡單起見,假設我們需要在阿里雲北京地域構建一個CentOS 7.3的映象,其中需要安裝redis,其他方面無特定需求,則整個建立步驟如下所示:

  1. 開啟ECS售賣頁,從上到下依次選擇按量付費 => 華北2(北京) => ecs.t5-lc1m1.small=> 公共映象CentOS 7.3 64位,點選頁面右下方下一步:網路和安全組
  2. 繼續選擇專有網路 => 公網頻寬 => 安全組,點選頁面右下方下一步:系統配置
  3. 繼續選擇祕鑰對,如不存在需要新建祕鑰對,便於後續通過祕鑰連線例項。其餘配置保持預設,點選頁面右下方確認訂單建立例項
  4. 購買流程完成後,可在ECS控制檯華北2(北京)地域看到新建的例項,稍等片刻待例項狀態變成執行中。
  5. 連線並登陸新建立的例項,通過命令列安裝redis。連線方式可參照使用SSH金鑰對連線Linux例項一文。
  6. 安裝完成後回到控制檯例項列表,點選對應例項右側更多 => 磁碟和映象 => 建立自定義映象,等待自定義映象建立完成。
  7. 最後清理不需要的資源,釋放例項、公網IP(如果是彈性公網IP)。如果需要,可以進一步刪除VPC、安全組等僅用於測試的資源。

上述過程實際上簡化了映象內最為關鍵的軟體及其配置部分,實際上該過程會隨著映象內需預裝的軟體及其配置不斷擴充變得愈發複雜。通過人肉保證每一次操作都準確無誤,和之前毫無偏差,會是一件非常困難的事情,更別提之後的維護和更新了。接下來我們將看到如何通過Packer自動化地完成上述映象構建構成。

通過Packer構建映象

如上所述,Packer通過配置檔案記錄映象構建過程中所需的所有細節。如下alicloud.json便是用於完成手動建立自定義映象一節需求所需的配置檔案。

{
  "variables": {
    "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
    "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
  },
  "builders": [{
    "type":"alicloud-ecs",
    "access_key":"{{user `access_key`}}",
    "secret_key":"{{user `secret_key`}}",
    "region":"cn-beijing",
    "image_name":"packer_basic",
    "source_image":"centos_7_03_64_20G_alibase_20170818.vhd",
    "ssh_username":"root",
    "instance_type":"ecs.t5-lc1m1.small",
    "internet_charge_type":"PayByTraffic",
    "io_optimized":"true"
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sleep 30",
      "yum install redis.x86_64 -y"
    ]
  }]
}

其中:

  1. variables中定義了builders中會用到的兩個變數access_keysecret_key。 這兩個變數的值取自執行時的環境變數,這是為了防止意外將AK寫到配置檔案中造成遺漏。
  2. builders中表明使用的是Alicloud Image Builder。該Builder用於在阿里雲上建立自定義映象。其他則為建立映象所需要的一些資訊,包括AK、地域、映象名稱、源映象、登陸名、例項規格、公網計費方式和IO優化。
  3. provisioners定義了需要在例項內執行的操作。這裡用到Shell Provisioner,表示在連線例項後執行一段shell指令碼安裝redis。

安裝Packer的過程詳見官網Getting Started,此處不再贅述。假設Packer已經安裝成功,執行packer build alicloud.json完成映象構建,整個構建過程需要耗費一些時間,構建過程中產生的日誌如下所示:

alicloud-ecs output will be in this color.

==> alicloud-ecs: Prevalidating image name...
    alicloud-ecs: Found image ID: centos_7_03_64_20G_alibase_20170818.vhd
==> alicloud-ecs: Creating temporary keypair: packer_xxx
==> alicloud-ecs: Creating vpc
==> alicloud-ecs: Creating vswitch...
==> alicloud-ecs: Creating security groups...
==> alicloud-ecs: Creating instance.
==> alicloud-ecs: Allocating eip
==> alicloud-ecs: Allocated eip xxx
    alicloud-ecs: Attach keypair packer_xxx to instance: i-xxx
==> alicloud-ecs: Starting instance: i-xxx
==> alicloud-ecs: Using ssh communicator to connect: ***
==> alicloud-ecs: Waiting for SSH to become available...
==> alicloud-ecs: Connected to SSH!
==> alicloud-ecs: Provisioning with shell script: /var/folders/k_/nv2r4drx3bs08l6tcx06ndb40000gn/T/packer-shell260049331
    alicloud-ecs: Loaded plugins: fastestmirror
    alicloud-ecs: Determining fastest mirrors
    alicloud-ecs: Resolving Dependencies
    alicloud-ecs: --> Running transaction check
    alicloud-ecs: ---> Package redis.x86_64 0:3.2.12-2.el7 will be installed
    alicloud-ecs: --> Processing Dependency: libjemalloc.so.1()(64bit) for package: redis-3.2.12-2.el7.x86_64
    alicloud-ecs: --> Running transaction check
    alicloud-ecs: ---> Package jemalloc.x86_64 0:3.6.0-1.el7 will be installed
    alicloud-ecs: --> Finished Dependency Resolution
    alicloud-ecs:
    alicloud-ecs: Dependencies Resolved
    alicloud-ecs:
    alicloud-ecs: ================================================================================
    alicloud-ecs:  Package           Arch            Version                  Repository     Size
    alicloud-ecs: ================================================================================
    alicloud-ecs: Installing:
    alicloud-ecs:  redis             x86_64          3.2.12-2.el7             epel          544 k
    alicloud-ecs: Installing for dependencies:
    alicloud-ecs:  jemalloc          x86_64          3.6.0-1.el7              epel          105 k
    alicloud-ecs:
    alicloud-ecs: Transaction Summary
    alicloud-ecs: ================================================================================
    alicloud-ecs: Install  1 Package (+1 Dependent package)
    alicloud-ecs:
    alicloud-ecs: Total download size: 648 k
    alicloud-ecs: Installed size: 1.7 M
    alicloud-ecs: Downloading packages:
    alicloud-ecs: --------------------------------------------------------------------------------
    alicloud-ecs: Total                                              2.2 MB/s | 648 kB  00:00
    alicloud-ecs: Running transaction check
    alicloud-ecs: Running transaction test
    alicloud-ecs: Transaction test succeeded
    alicloud-ecs: Running transaction
    alicloud-ecs:   Installing : jemalloc-3.6.0-1.el7.x86_64                                  1/2
    alicloud-ecs:   Installing : redis-3.2.12-2.el7.x86_64                                    2/2
    alicloud-ecs:   Verifying  : redis-3.2.12-2.el7.x86_64                                    1/2
    alicloud-ecs:   Verifying  : jemalloc-3.6.0-1.el7.x86_64                                  2/2
    alicloud-ecs:
    alicloud-ecs: Installed:
    alicloud-ecs:   redis.x86_64 0:3.2.12-2.el7
    alicloud-ecs:
    alicloud-ecs: Dependency Installed:
    alicloud-ecs:   jemalloc.x86_64 0:3.6.0-1.el7
    alicloud-ecs:
    alicloud-ecs: Complete!
==> alicloud-ecs: Stopping instance: i-xxx
==> alicloud-ecs: Waiting instance stopped: i-xxx
==> alicloud-ecs: Creating image: packer_basic
    alicloud-ecs: Detach keypair packer_xxx from instance: i-xxx
==> alicloud-ecs: Cleaning up 'EIP'
==> alicloud-ecs: Cleaning up 'instance'
==> alicloud-ecs: Cleaning up 'security group'
==> alicloud-ecs: Cleaning up 'vSwitch'
==> alicloud-ecs: Cleaning up 'VPC'
==> alicloud-ecs: Deleting temporary keypair...
Build 'alicloud-ecs' finished.

==> Builds finished. The artifacts of successful builds are:
--> alicloud-ecs: Alicloud images were created:

cn-beijing: m-xxx

上述日誌較為完整得給出了Packer構建過程中執行的每一個步驟:從校驗引數、建立臨時資源、預安裝軟體、建立目標資源到最後的釋放臨時資源,所有的過程一氣呵成,而這僅僅只需預裝好Packer以及定義好相應的配置檔案。接下來將針對一些實際DevOps場景會用到的一些配置進行必要的說明以供參考,更多引數和樣例詳見Alicloud Image BuilderExamples

DevOps常用配置

映象標籤(tags)

當所要管理的映象達到一定的數量時,對映象進行適當的標記就變得很有必要,比如記錄映象版本號、映象包含的應用型別等。通過為映象打上標籤是達到上述目的絕佳手段,阿里雲Builder提供了tags引數以支援此類需求。如下配置檔案將為最終生成的映象和對應的快照打上version=v1.0.0app=web兩個標籤。

{
  "variables": {
    "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
    "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
  },
  "builders": [{
    "type":"alicloud-ecs",
    "access_key":"{{user `access_key`}}",
    "secret_key":"{{user `secret_key`}}",
    "region":"cn-beijing",
    "image_name":"packer_basic",
    "source_image":"centos_7_03_64_20G_alibase_20170818.vhd",
    "ssh_username":"root",
    "instance_type":"ecs.t5-lc1m1.small",
    "internet_charge_type":"PayByTraffic",
    "io_optimized":"true",
    "tags": {
      "version": "v1.0.0",
      "app": "web"
    }
  }]
}

控制檯映象列表頁面和API DescribeImages均支援查詢映象時返回標籤以及根據標籤過濾映象。但為映象打上標籤真正強大的地方在於能夠和Terraform一起為標準化的DevOps流程提供支援。Terraform的內容超出了本文討論的範疇,在此不再展開。這裡推薦Alibaba Cloud DevOps tutorials系列教程,其中講解了一些企業DevOps過程中的最佳實踐,其中涉及Terraform和Packer的內容參見Continuous Delivery一節。

讓映象只包含系統盤(image_ignore_data_disks)

預設情況下Packer直接從例項建立映象,而從例項建立映象時如果包含資料盤,則映象會同時包含資料盤快照。在構建過程中建立包含資料盤的例項通常有兩種方式:一是通過image_disk_mappings設定資料盤相關引數,二是選擇預設帶有資料盤的例項規格。其中後者涉及的規格包含的資料盤大多為本地盤,如ecs.d1ne.2xlarge,而本地盤當前並不支援建立快照,進而也無法直接通過此類例項建立映象。即便如此,很多場景下為了滿足某些方面的效能需求,使用者依然會選擇這類例項規格,但實際上資料盤部分並不是必須的。此時就可以在配置檔案中加上"image_ignore_data_disks": "true"實現只基於系統盤來建立映象。

設定快照超時時間(wait_snapshot_ready_timeout)

映象依賴於快照,而快照的建立時間依賴於磁碟大小。當磁碟較大時,建立快照所需要的時間也會相應的增加。預設情況下,輪詢快照的超時時間為3600s。如果由於磁碟太大導致超時,則可以通過wait_snapshot_ready_timeout調大超時時間。

通過私網IP連線例項(ssh_private_ip)

預設情況下,Packer建立EIP並繫結到例項上,然後通過EIP對應的公網IP連線例項安裝軟體或執行命令。但在一些場景下,使用者可以直接通過私網IP連線例項,此時公網IP就會顯得多餘。此時可以通過設定"ssh_private_ip": "true",該設定下Packer將不會分配EIP或者公網IP,而是直接嘗試通過私網IP連線例項。

停止例項選項(disable_stop_instance)

預設情況下,Packer在執行完provisioners後,會先停止例項然後建立映象。但如果設定了"disable_stop_instance": "true",Packer將不會主動去停止例項,而是假設配置中提供的指令會自行停止例項,以滿足一些特殊場景,如Sysprep一個Windows例項。Sysprep的一個使用場景可參照修改Windows例項SID以搭建域環境

通過UserData啟用WinRM

出於安全考慮,Windows映象預設關閉了WinRM。但連線Windows例項及之後在例項內部執行命令都依賴於WinRM,所以需要在例項建立時啟用WinRM,而這可以通過UserData來完成。啟用WinRM的Userdata檔案內容詳見winrm_enable_userdata.ps1,Packer則通過配置user_data_file指定UserData檔案路徑。跟WinRM相關的引數還包括"communicator": "winrm""winrm_port": 5985"winrm_username": "Administrator""winrm_password": "Test1234",分別表示通過WinRM連線例項、連線埠為5985、連線時使用Administrator賬戶,密碼採用Test1234。以下配置檔案提供了基於Windows的一個簡單示例:

{
  "variables": {
    "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
    "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
  },
  "builders": [{
    "type":"alicloud-ecs",
    "access_key":"{{user `access_key`}}",
    "secret_key":"{{user `secret_key`}}",
    "region":"cn-beijing",
    "image_name":"packer_test",
    "source_image":"win2008r2_64_ent_sp1_zh-cn_40G_alibase_20181220.vhd",
    "instance_type":"ecs.n1.tiny",
    "io_optimized":"true",
    "internet_charge_type":"PayByTraffic",
    "image_force_delete":"true",
    "communicator": "winrm",
    "winrm_port": 5985,
    "winrm_username": "Administrator",
    "winrm_password": "Test1234",
    "user_data_file": "examples/alicloud/basic/winrm_enable_userdata.ps1"
  }],
  "provisioners": [{
    "type": "powershell",
    "inline": ["dir c:\\"]
  }]
}

其中image_force_delete表示如果已存在同名映象則先刪除,並假定UserData檔案在給定的相對路徑下。provisioners內的指令只用做在例項內呼叫命令的示例,可根據實際情況填寫。

從ISO到阿里雲映象

ISO檔案需要線上下虛擬化環境安裝完成後,生成對應格式的映象檔案再匯入到阿里雲(當前支援QCOW2、VHD和RAW三種格式的檔案匯入阿里雲)。如果線下環境為qemu,可參照使用Packer建立並匯入本地映象一文。其中包含兩個重要的部分,首先需要使用對應虛擬化環境或軟體對應的Builder,如上文中使用的是Qemu Builder。其次,通過定義Alicloud Import Post-Processor將前面生成的映象檔案匯入到阿里雲。

 

原文連結

本文為雲棲社群原創內容,未經