packer 学习笔记
前言
网上有一个老哥用 packer 制作镜像的博客里开篇就提到[1]。
Failure is success in progress.
—— Albert Einstein
不要害怕失败,在用 packer 的过程中你可能会失败很多次。我就经历了 virtualbox 安装失败、启动不了虚拟机,provisioning 阶段失败,ks.cfg 找不到,安装系统的时候失败……
手动创建 Vagrant Base Box 我也经历了十几次失败,打包的镜像不能 add,识别不了系统,SSH连接不上……
参考资料
互联网上有一些好资源:
如果你想了解手动创建 Vagrant Base Box 的过程和注意事项请查看 Vagrant 的官方文档以及theurbanpenguin的视频教程:
如果你想学习 Packer,我推荐你阅读 packer 官方教程。
-
https://developer.hashicorp.com/packer/tutorials/docker-get-started?product_intent=packer
-
感谢 bento 项目,一个教你如何创建 vagrant box 的项目,设置 virtualbox 和设置 vagrant key 的脚本写的很好。可以学到如何组织 packer 的 HCL 文件。
-
Hashicorp 在 Github 上也有一个用 packer 创建 Vagrant Box 的示例: https://github.com/hashicorp-education/ubuntu-24-04。我更推荐参考 Bento 的做法,我认为 Bento 安装 VirtualBox Guest Addition 的方式更好。
缘起
用过 Vagrant 就不会再想重复在虚拟机里装系统了。只需要一条命令就能立刻启动一个刚安装好的虚拟机,这比手动配置虚拟机并安装操作系统要快多了。在 Vagrantfile 里面可以定义 Provision,初次启动虚拟机的时候配置好环境。在 Git 仓库里带上 Vagrantfile,大家就能一键启动一个一模一样的开发环境。
如果你想定制 Vagrant Box,或者你使用的操作系统没有网上公开的 Vagrant Box 可用,你要创建一个 Vagrant Base Box。vagrant package可以将虚拟机打包成一个 Vagrant Box,但是只支持 VirtualBox 和 Hyper-V。vagrant package需要你手动配置虚拟机并安装好操作系统,配置 SSH 等,要么你基于现有的 vagrant box 启动一个机器,做一点修改然后再重新打包。手动配置,必须基于现有的 vagrant box,只支持 virtualbox 和 hyperv,这些都是创建 Vagrant Box 的痛点。
Packer 支持各种机器,通过 HCL 配置文件自动化创建机器的镜像。
我首先通过 Vagrant 的官方文档和 Youtube 上的视频教程,以及网上的各种博客学会了手动创建 Vagrant Base Box。
阅读 Packer 官方教程,通过 Packer Docker 镜像教程学习了 Packer 的基本概念。使用 VirtualBox-iso Builder 和 Packer 的 vagrant 插件,实现一键从 ISO 安装镜像到 Vagrant Box。
Packer 官方教程没有直接讲如何从 ISO 安装镜像到 Virtual Box 的 Vagrant Box。Bento 项目里有各种 Linux 发行版制作 Vagrant Box 的代码,这就是基础设施即代码(IaC)的好处,想知道别人怎么配的,看代码就行了。
使用 Packer 镜像构建过程透明可追溯,避免“黑盒”操作(如手动修改镜像后忘记记录)。写文档记录如何配置环境十分重要,光有如何配置环境的文档还不够,文档作者在写的过程中很容易出现疏漏。如果在环境配置过程中做了某个修改后忘记记录,别人就无法复现你的环境了。又或者有的人文档写得不太好,别人跟着他的文档操作根本不能成功(有的人写文档也没想要别人能成功复现)。换句话说,文档记录的配置过程只是你的一面之词。只有配置代码化,任何人都能通过你给的代码启动一个一模一样的环境。这才算是完全透明可追溯的环境。HCL 配置文件本身就是代码,支持 Git 管理、代码审查和迭代优化。将镜像构建过程代码化(HCL),实现一键生成标准化系统镜像。
从一个 template.pkr.hcl 开始
Docker 启动一个容器镜像比虚拟机快得多,学习 Packer 可以从定制 Docker 容器开始[2]。
// teamplate.pkr.hcl
packer {
required_plugins {
docker = {
version = ">= 1.0.8"
source = "github.com/hashicorp/docker"
}
}
}
source "docker" "ubuntu" {
image = "ubuntu:jammy"
commit = true
}
build {
name = "learn-packer"
sources = [
"source.docker.ubuntu"
]
}
使用 packer 你不需要知道所有的 HCL 语法细节[3]。通常 packer 的配置文件名以.pkr.hcl 结尾。HCL 的语法很简单,主要是块(blocks) 和参数(arguments)。我对 HCL 的了解有限,一对大括号就是一个块,大括号里面的叫做属性也被叫做参数。
packer 块声明一些需要的插件。在命令行下运行 packer init template.pkr.hcl 安装需要的插件。
Each builder has its own unique set of configuration attributes.
source 块是用来配置 builder 的。source 块后面带着的 "docker" 是 build_type,"ubuntu" 是名字。source 块里面有什么属性取决于 build_type。source 就是告诉 packer 从什么地方/文件/基础镜像启动一个新的机器或者容器。source 块是 builder 的配置,有什么配置属性取决于 builder plugin。
builder 负责创建机器并生成镜像。当然这里的机器也可以是容器,镜像也可以就是一个虚拟机的硬盘文件。Builder 插件是用来创建机器和把机器打包成镜像的。
build 块定义具体要 packer 做什么。在 sources 引用之前定义的 source 块。在终端运行 packer build 命令创建镜像。
packer build template.pkr.hcl
实际上你已经学会用 Packer 了,只不过你还需要更复杂的配置。你需要安装系统后配置系统,得到的系统镜像打包成 Vagrant Box 或者 Docker 镜像。
Provison
provision 是动词:为...提供必须品。在计算机里面就是准备环境的意思,为了部署或者开发提供必须要安装的软件和配置,就是 provision。
provisioner 就是安装软件和配置的方式。provisoner 有 shell,ansible 等等。
provisioning 是 provison 的动名词,就是搭建配置 IT 基础设施的意思。provisioning 通俗讲就是搭环境,或者配环境。
HCL 变量
template.pkr.hcl 的每一个属性都是写死的,那还叫什么模板呢?
packer 里的变量其实是常量,可以理解为参数。通过配置参数,使用同一个模板(template)构建不一样的镜像。
在 HCL 文件里面使用变量块声明变量[4]。
variable "docker_image" {
type = string
default = "ubuntu:jammy"
}
在 HCL 文件的其他地方就可以使用这个变量。
source "docker" "ubuntu" {
- image = "ubuntu:jammy"
+ image = var.docker_image
commit = true
}
变量通常有默认值,也可以没有默认值。变量的值是可以通过命令行参数指定的,也可以从配置文件(.pkrvars.hcl)读取变量的值。正是因为属性的值是可以通过变量配置,所以 Packer 的配置文件被称为模板(packer template)。
多文件 Packer 模板
packer build 可以接收一个目录作为参数[5],目录里面以 .pkr.hcl 结尾的文件都会被 packer 分析。
以 Bento 项目为例,将模板分成 3 个文件。
pkr-variables.pkr.hcl
pkr-builder.pkr.hcl
pkr-sources.pkr.hcl
pkr-variables 里面放的全是变量的声明,pkr-sources 里面全是 source 块,pkr-builder 里面是 build 块和 packer 块。各自的文件里面使用 locals 块根据变量来决定使用哪些脚本做 provison。locals 块就是一个根据变量的值做判断和计算地地方,然后给计算得到的结果一个新的名字。
Packer 制作 Vagrant Box 踩坑记录
- ks.cfg 文件的 EOL 必须是 LF('\n')。
又多了一个不用 Windows 写代码的理由。
总结
学习 packer 制作 Vagrant Box 的过程中经历了 virtualbox 不能安装、启动不了虚拟机,provisioning 阶段失败,ks.cfg 找不到,安装过程失败。
我真的有打算要放弃了。但是还有那么一丁点成功的希望,最后折腾了三四天还是成功了,我才明白 “Failure is success in progress” 的含义。
学一个东西带来的收获往往不止是这个学会这个东西本身。在学习的过程中会增长见识,也会有一些心态上的锻炼。学习也是一种修行。与其担心 AI 在各个方面都比我强,担心未来,不如去学一些自己不会的东西,自然就会有收获。
题外话:
原本想写个制作 Vagrant Box 的教程,但是心里很乱。不知道该怎么写起,写一个点就容易越写越深,介绍了 Packer 的好处才发现光顾着介绍使用 Packer 有啥好了。想录一个视频,似乎这也没什么好录的,既然我能在网上学会,别人也能,那我录一个视频也没什么太大的用。干脆写一个学习笔记吧。packer 其实很简单,只是不会的时候觉得难。

浙公网安备 33010602011771号