鏡像即代碼:基於Packer構建阿里雲鏡像

什麼是Packer

PackerHashiCorp推出的一款工具,旨在提供簡易的方式自動化構建鏡像。經過Packer,你只須要在配置文件中指明鏡像構建所需的基本信息及指望安裝到鏡像中的軟件及配置,便可經過自動化腳本構建所需的鏡像。因爲構建鏡像的過程被固化成了配置文件,每個步驟都清晰可見易於回溯,無需擔憂屢次構建獲得的鏡像存在不一致。且鏡像構建配置化後,將爲測試和更新鏡像帶來極大的便利,大大下降運維和管理鏡像的成本。html

在具體介紹Packer的使用方法以前,咱們先來看下之前在阿里雲ECS上如何手動建立一個自定義鏡像。若是對這個流程已很是熟悉,能夠直接跳到經過Packer構建鏡像一節。git

注意:後續操做會建立一些收費資源,請注意釋放和清理,如實例、公網IP、快照等。
實例規格和鏡像會隨着時間的推移不斷更新,本文後續提到的一些規格和鏡像可能會在將來下線,因此具體操做流程能夠根據實際狀況選擇不一樣的規格、鏡像或者其餘實例相關的資源。

手動建立自定義鏡像

簡單起見,假設咱們須要在阿里雲北京地域構建一個CentOS 7.3的鏡像,其中須要安裝redis,其餘方面無特定需求,則整個建立步驟以下所示:github

  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自動化地完成上述鏡像構建構成。web

經過Packer構建鏡像

如上所述,Packer經過配置文件記錄鏡像構建過程當中所需的全部細節。以下alicloud.json即是用於完成手動建立自定義鏡像一節需求所需的配置文件。redis

{
  "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"
    ]
  }]
}

其中:shell

  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完成鏡像構建,整個構建過程須要耗費一些時間,構建過程當中產生的日誌以下所示: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 BuilderExamplescentos

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文件須要在線下虛擬化環境安裝完成後,生成對應格式的鏡像文件再導入到阿里雲(當前支持QCOW二、VHD和RAW三種格式的文件導入阿里雲)。若是線下環境爲qemu,可參照使用Packer建立並導入本地鏡像一文。其中包含兩個重要的部分,首先須要使用對應虛擬化環境或軟件對應的Builder,如上文中使用的是Qemu Builder。其次,經過定義Alicloud Import Post-Processor將前面生成的鏡像文件導入到阿里雲。

 

原文連接

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索