什么是Packer

简单介绍一下自己

Packer 是一个轻量命令行工具, 能在几乎所有主流的操作系统上运行。

在给定一份配置文件的情况下, Packer 能为多种系统架构创建云主机镜像。同时 Packer 自身也能够做到多镜像并发创建, 大大节省了镜像创建过程中的时间成本。

为什么要用 Packer

为什么呢?

当然是因为使用预制的镜像有非常多的好处, 最简单来说,就是能最大程度地保证不同机器上服务的一致性(以经验来看这一点非常重要)。但是在实际使用中, 镜像因其创建/管理的工作单调且复杂, 很多情况下镜像还没有被完全普及。

现有的镜像自动化创建工具, 要么是不好用或不方便, 要么就是学习曲线太高。这些特点导致运维团队投入过多的精力在镜像的使用中, 进而导致工作效率以及敏捷性被阻碍。这就是为什么虽然镜像的工作方式具有非常多的优势,但是却依旧没有被大规模的普及。

Packer 依据单个的配置文件, 能做到流水线式 + 并发的创建镜像,与传统手工操作相比,其 “Infrastructure as Code” 的工作方式也大大减少了失误的概率。

至少在 Packer 官方认为:

Packer brings pre-baked images into the modern age,
unlocking untapped potential and opening new opportunities.

Infrastructure as Code 的工作方式

在这个理念被提出之前, 手工+脚本的方式非常普遍, 手工容易出错, 而脚本本身也要投入很多人力来进行维护。与此同时,一些主流的云服务厂商也在积极寻找更多的可能性。2019年4月, 在我们发布了 terraform-provider-jdcloud插件以后, 目前一些团队在使用 Terraform 的京东云插件, 有的会在 Github 上留下 issues, 有的是通过留言,表示希望能增加更多功能。用户的这些表现都从侧面验证了 “Infrastructure as Code” 工作方式的可靠性和敏捷性。

到了 Packer, 这些特性依旧被保留下来。相较于传统方式,IaC 被认为是: “Modern and Automated” , 同样是一份简单的 json 配置文件,IaC 鼓励开发者开始使用镜像, 同时使用 Packer 自动化、流水线化地管理镜像, 从而减少镜像本身管理带来的负担。

介绍一些日常的使用场景

  • 持续交付 - Packer&Chef&Puppet: Packer 因其自身体积轻量的特点, 使其被直接放到流水线里并成为流水线的一环也变成了一种可能的选择: 在 Chef/Puppet 的配置产生变动的时候触发流水线, 下一环 Packer 负责为新配置生成镜像, 这些镜像可以立刻投入测试, 测试通过后即可部署到生产环境。
  • 混合云的使用: Packer 的一份配置可以为多个云服务商生成镜像, 假设你使用 VMWare 作为开发环境, AWS 作为生产环境, 那么 Packer 能够并发生成两份镜像用于两家云服务商, 从而最大程度地减少两个镜像之间的区别。

详细一些, Packer 还包含有这些优势

  • 对于临时产物的处理上: Packer 能为你创建一些临时资源,比如在没有指定子网的情况下,Packer 能够帮你创建一个临时子网,用于安放云主机。并且在出现错误的时候终止任务,同时自动清理中间产物。而传统方式则需要自己先创建一个临时子网,并且出现错误时还需要手动清理。
  • 在问题的追溯与定位上: 在 Packer 上所有变化都是基于代码的,而代码是可以追溯的,方便快速定位问题并回滚。而在传统方式中,考虑到手动操作的过程可能涉及多人,完整地追出问题并不是一件容易的事儿。
  • 在便捷性与效率上: 由于 Packer 上的操作基于代码,变更的时候操作会非常快;而手动操作的效率则取决于个人的手速了。
  • 在操作的可重复性上: Packer 依据配置文件,随时快速重新操作;而在全手动的情况下, 想要完整的复现一次所有操作并不容易。Packer 上代码的可重复利用也说明你可以用最快的速度再创建一个一模一样的(测试)环境。

立刻开始使用 Packer

安装 Packer

安装 Packer 我们推荐去 Packer官网 下载一个二进制包,解压后直接就可以使用。另外对于 Mac OS X 用户, 也可以使用 HomeBrew 直接进行安装。

$ brew install packer

准备配置文件

在开始之前,你需要准备一份配置文件jdcloud.json,在配置文件里给出相应的参数。

配置文件的模板如下:

{
   "builders": [
     {
 
       # 服务商与身份信息
       "type":             "jdcloud",
       "access_key":       "<Your access_key>",
       "secret_key":       "<Your secret_key>",
       # 云主机规格信息
      "image_id":         "<Base Image Id>",
      "region_id":        "cn-north-1",
      "az":               "cn-north-1c",
      "instance_name":    "packer_demo",
      "instance_type":    "g.n2.medium",
      "ssh_password":     "DevOps2018",
      "image_name":       "packer_image_demo",
      "subnet_id":        "subnet-jo6e38sdli",

      # 登录设置(不用改)
      "communicator":     "ssh",
      "ssh_username":     "root"
    }
  ],

  "provisioners": [
    {
      "type": "shell",
      "inline": [

        # 云主机运行的脚本信息
        "sleep 30",
        "sudo apt update",
        "sudo apt install nginx -y"
      ]
    }
  ]
}
  • 服务商与身份信息 : 服务提供商-jdcloud, 其次你需要提供你的AccessKey/SecretKey 向我们说明你的身份。
  • 云主机规格信息 :
    这里包含:

云主机/地域/可用区/机型与规格

基础镜像: 可以是京东云官方提供的 Centos/Ubuntu,也可以是你的私人镜像,将它的ID填写在这里即可。

子网信息: 可以为空,如果为空的话,我们会为你创建一个临时的子网。

登录密码: 在这里我们选择使用密码来登录,在这儿还有另一个示例,会告诉用户如何使用秘钥的方式来创建云主机镜像。

  • 运行命令: 在这里作为示例,我们选择安装一个 nginx。

使用配置文件开始创建镜像

bash
~ $ packer build jdcloud.json 
 
 ==> jdcloud: Validating parameters...
     jdcloud: validating your subnet:subnet-xxx
     jdcloud: subnet found: Packer 测试子网
     jdcloud: validating your base image:img-1iubdz7gzu
     jdcloud: image found:Ubuntu 16.04 64位
     jdcloud: Password detected, we are going to login with this password :)

==> jdcloud: Creating instances
    jdcloud: Creating public-ip
    jdcloud: Associating public-ip with instance
    jdcloud: Hi, we have created the instance, its name=packer_demo , its id=i-xxxx, and its eip=116.196.xx.xx  :)

==> jdcloud: Using ssh communicator to connect: 116.196.xx.xx
==> jdcloud: Waiting for SSH to become available...
==> jdcloud: Connected to SSH!
==> jdcloud: Provisioning with shell script
==> jdcloud: Stopping this instance
    jdcloud: Instance has been stopped :)
==> jdcloud: Creating images
Build 'jdcloud' finished.


==> Builds finished. The artifacts of successful builds are:
--> jdcloud: A VMImage was created: img-riggr2xxx

在上面的创建过程中, 我们可以看到, 镜像的创建过程大体可以分成四个阶段:

  1. 阶段-1 参数验证阶段: 在这个阶段里我们将要去验证参数的有效性,包括是否指定子网,需不需要创建临时子网,给出的运行镜像是否存在,是否指定使用密码登录或指定密钥登录。
  2. 阶段-2 创建云主机阶段: 在这个阶段我们按照给出的云主机规格创建出一台云主机,并创建出一个公网IP, 用于稍后登录这台云主机执行命令。
  3. 阶段-3 执行命令阶段: 命令的输出都会打印在这里。
  4. 阶段-4 创建镜像阶段: Packer 会将这台云主机创建出一个镜像,并保存到镜像仓库里, 在上面我们可以看到对应的镜像ID为:img-riggr2xxx。

创建出来的新镜像,用户可以拿来手动创建云主机,也可以通过 terraform 自动创建云主机。

更进一步地,如果考虑到服务的多地域性,用户可能会希望为每个地域都创建出各自的专属镜像。这个时候,只需要在配置文件后面追加出其余地域的配置信息,Packer 就能在一次并发内完成所有镜像的创建,很大程度的提升了镜像创建的效率。

Packer 以其 “Infrastructure as Code” 的工作方式,在帮助用户降低失误故障风险的同时,提高了持续交付效率和业务可用性,解决了传统镜像创建方式在跨云平台环境下的诸多弊端。