前言

网上有一个老哥用 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 官方教程

缘起

用过 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 其实很简单,只是不会的时候觉得难。


  1. https://medium.com/@maros.kukan/automating-golden-image-builds-with-packer-3b1c6010b467

  2. Docker | Packer | HashiCorp Developer

  3. https://developer.hashicorp.com/packer/guides/hcl

  4. Input and Local Variables guide | Packer | HashiCorp Developer

  5. HCL Templates | Packer | HashiCorp Developer

packer 学习笔记的更多相关文章

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  3. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  4. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  5. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  6. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  7. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  8. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

  9. DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记

    今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...

  10. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

随机推荐

  1. Iceberg 待学习链接

    1.Iceberg事务特性解读 https://blog.csdn.net/naisongwen/article/details/123343566 2.FLink全链路时延-测量方式 https:/ ...

  2. flutter-解决长按TextField出现英文(复制粘贴)问题

    第一步 引入依赖 dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter 第二步 在main.dart中添加代码 ...

  3. [HDU5396] Expression 题解

    每次合并两个数,做过石子合并的人都能看出来是区间 dp. 设状态 \(dp_{i,j}\) 表示区间 \([i,j]\) 中合并为一个数的所有情况之和. 那么我们就可以枚举断点 \(k\): \(b_ ...

  4. Linux - centos6忘记root密码怎么办?

    Linux的root密码修改不像Windows的密码修改找回,Windows的登录密码忘记需要介入工具进行解决.CentOS6和CentOS7的密码方法也是不一样的,具体如下 1.开机按esc   2 ...

  5. springboot项目发布war包

    1:修改pom.xml 将"jar"改为"war" <packaging>war</packaging> 将: <dependen ...

  6. Selenium KPI接口 键盘操作

    实现功能:百度搜索框输入selenium->复制内容->sogo搜索框粘贴内容. 首先导入Keys方法 使用格式: driver.findelementbyid("kw" ...

  7. selenium自动化测试入门

    Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案. Selenium是用于自动化控制浏览器做各种操作,打开网页,点击按钮,输入表单等等,可以 ...

  8. wordpress:nginx负载均衡+nignweb服务器+mysql数据库+nfs-lsync+rsync备份

    目录 所有知识结合,注意正式环境慎用: mariadb服务器 NFS服务器配置 web服务器配置 Nginx负载均衡 backup备份服务器配置rsync NFS服务器安装lsync进行实时同步 所有 ...

  9. linux 上安装portainer.io

    inux 上安装portainer.io    1.portainer.io是什么?    2.安装,运行镜像    3.登陆1.portainer.io是什么?Portainer是一个轻量级的Doc ...

  10. 代码块--java进阶day03

    1.代码块 1.局部代码块 定义在方法中的一对大括号,可以提早释放内存,走完{}里的逻辑后就会被释放,在之后的编程中无法使用 2.构造代码块 位置在类中,方法外的{},在构造方法执行的时候,构造代码块 ...