镜像即代码:基于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将前面生成的镜像文件导入到阿里云。

 

原文连接

本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索