天天看點

鏡像即代碼:基于Packer建構阿裡雲鏡像

什麼是Packer

Packer

HashiCorp

推出的一款工具,旨在提供簡易的方式自動化建構鏡像。通過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通過配置檔案記錄鏡像建構過程中所需的所有細節。如下

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_key

    secret_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場景會用到的一些配置進行必要的說明以供參考,更多參數和樣例詳見

Examples

DevOps常用配置

鏡像标簽(tags)

當所要管理的鏡像達到一定的數量時,對鏡像進行适當的标記就變得很有必要,比如記錄鏡像版本号、鏡像包含的應用類型等。通過為鏡像打上

标簽

是達到上述目的絕佳手段,阿裡雲Builder提供了

tags

參數以支援此類需求。如下配置檔案将為最終生成的鏡像和對應的快照打上

version=v1.0.0

app=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

将前面生成的鏡像檔案導入到阿裡雲。

繼續閱讀