Shell
类型:shell
Packer 的 shell 配置器使得 Packer 可以使用 shell 脚本构建的机器。 Shell 配置器是在计算机上安装和配置软件的最简单方法。
基础用法
HCL2:
provisioner "shell" {
inline = ["echo foo"]
}
Json:
{
"type": "shell",
"inline": ["echo foo"]
}
配置参数
下面列出了可用配置选项的参考。唯一必须的元素是 inline 或 script。每个其他选项都是可选的。
以下参数必须选一配置:
inline(array of string)- 这是一组要执行的命令。这些命令由换行符连接起来并变成一个文件,因此它们都在相同的上下文中执行。这使得您可以在一个命令中更改目录,并在下一个命令中使用目录中的内容,依此类推。内联脚本是在机器内完成简单任务的最简单方法。script(string) - 要上传到机器并执行的脚本的路径。此路径可以是绝对路径或相对路径。如果是相对路径,就是相对于Packer执行时的工作目录。scripts(array of string)- 要执行的脚本数组。脚本将按照指定的顺序上传和执行。每个脚本都是独立执行的,因此一个脚本中的变量等状态不会传递到下一个脚本。
可选参数:
binary(boolean) - 如果为true,则指定脚本是二进制文件,因此 Packer 不应将 Windows 行结尾转换为 Unix 行结尾(如果有的话)。默认为false。valid_exit_codes(整数列表)- 脚本的有效返回值。默认情况下全部是 0。env(map(string))- 在execute_command之前注入的键/值对映射。 Packer 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。重复的env设置会覆盖environment_vars中的设置。environment_vars([]string)- 在execute_command之前注入的键/值对数组。格式应为key=value。 Packer 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。env_var_format(string) - 当我们解析您提供的environment_vars时,该参数会为我们提供一个字符串模板以确保我们正确设置环境变量。默认值为"%s='%s' "。与use_env_var_file结合使用时,默认值为"export %s='%s'\n"use_env_var_file(boolean) - 如果为true,Packer 会将您的环境变量写入临时文件并从该文件获取它们,而不是在我们的execute_command中内联声明它们。默认的execute_command将是chmod +x { {.Path} }; . { {.EnvVarFile} } && { {.Path} }。在大多数情况下,此选项是不必要的,但如果您在自定义execute_command中有额外的引号,那么这可能是正确执行脚本所必需的。默认值:false。execute_command(string)- 用于执行脚本的命令。 默认值为chmod +x { { .Path } }; { { .Vars } } { { .Path } },除非 用户设置了"use_env_var_file": true- 在这种情况下,默认execute_command是chmod +x { {.Path} }; . { {.EnvVarFile} } && { {.Path} }。 该值是一个模板引擎。因此,您可能在此字段中使用用户变量和模板函数。此外,还有三个可用的额外变量:Path是要运行的脚本的路径Vars是environment_vars的列表(如果已配置)。EnvVarFile是包含环境变量的文件的路径,如果use_env_var_file是true
expect_disconnect(boolean) - 默认为false。当为true时,允许服务器断开与 Packer 的连接而不抛出错误。如果您重新启动 SSH 服务器或重新启动主机,则连接可能会断开。inline_shebang(string) - 运行inline指定的命令时使用的 shebang 值。默认值为/bin/sh -e。如果您不使用inline,则此配置无效。重要提示:如果您对此进行自定义,请务必包含类似-e标志的内容,否则个别步骤失败不会引发配置器失败。remote_folder(string) - 上传的脚本将保存在机器上的文件夹。默认值为/tmp。remote_file(string) - 上传的脚本在机器上的文件名。默认值为script_nnn.sh。remote_path(string) - 上传脚本在远程机器上的完整路径。默认值是remote_folder/remote_file,如果设置此选项将覆盖remote_folder和remote_file。skip_clean(boolean) - 如果为true,则指定上传到系统的脚本不会被 Packer 删除。默认值为false(从系统中清理脚本)。start_retry_timeout(string) - 尝试启动远程进程的时间量。默认情况下,默认值为5m或 5 分钟。存在此设置是为了应对 SSH 可能重新启动的时间,例如系统重新启动。如果重新启动需要更长的时间,请将此设置为更高的值。pause_after(string) - 配置 shell 脚本后等待的时间,如果前面的所有步骤都成功,则暂停。
所有配置器共有的参数:
pause_before(duration) - 执行前休眠一段时间。max_retries(int) - provisioner 在失败的情况下重试的最大次数。默认为零 (0)。零表示不会重试错误。only(array of string) - 只运行列表中指定的的配置器程序。override(object) - 使用不同设置覆盖指定配置器的配置,例如:
HCL2:
source "null" "example1" {
communicator = "none"
}
source "null" "example2" {
communicator = "none"
}
build {
sources = ["source.null.example1", "source.null.example2"]
provisioner "shell-local" {
inline = ["echo not overridden"]
override = {
example1 = {
inline = ["echo yes overridden"]
}
}
}
}
Json:
{
"builders": [
{
"type": "null",
"name": "example1",
"communicator": "none"
},
{
"type": "null",
"name": "example2",
"communicator": "none"
}
],
"provisioners": [
{
"type": "shell-local",
"inline": ["echo not overridden"],
"override": {
"example1": {
"inline": ["echo yes overridden"]
}
}
}
]
}
timeout(duration)- 如果配置器完成时间超过配置值(例如1h10m1s或10m),则配置器将超时并失败。
执行命令的例子
对于许多新用户来说,execute_command 令人费解。但是,它提供了一个重要的功能:自定义命令的执行方式。最常见的用例是处理 sudo 密码提示。如果您使用非 POSIX shell,例如 FreeBSD 上的 tcsh,您可能还需要自定义该参数。
Sudo 的例子
某些操作系统默认为非根用户。例如,如果您以 ubuntu 身份登录并且使用密码 packer 执行 sudo,那么您需要将 execute_command 更改为:
"echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'"
-S 标志告诉 sudo 从 stdin 读取密码,在本例中,它是通过管道输入了 packer 这个值。
如果您的环境变量包含空格或单引号,则上述示例将不起作用;这种情况下尝试删除单引号:
"echo 'packer' | sudo -S env {{ .Vars }} {{ .Path }}"
通过这样配置 execute_command,您的脚本可以以 root 权限运行,而不必担心密码提示。
FreeBSD 的例子
FreeBSD 的默认 shell 是 tcsh,它偏离了 POSIX 语义。为了让 Packer 程序传递环境变量,您需要将 execute_command 更改为:
chmod +x {{ .Path }}; env {{ .Vars }} {{ .Path }}
注意在 { { .Vars } } 之前添加了 env。
默认的环境变量
除了能够使用 environment_vars 配置自定义环境变量外,配置器还会自动定义某些常用的环境变量:
PACKER_BUILD_NAME值为 Packer 正在运行的构建的名称。这在 Packer 进行多个构建并且您希望将它们彼此能有所区分时最有用。PACKER_BUILDER_TYPE是用于创建运行脚本的机器的构建器类型。如果您只想在使用特定构建器构建的系统上运行脚本的特定部分,这将很有用。PACKER_HTTP_ADDR如果使用为文件传输提供 HTTP 服务器的构建器(例如hyperv、parallels、qemu、virtualbox和vmware),这将被设置为 HTTP 服务器地址。您可以在配置器中使用此地址通过 HTTP 下载大文件。如果您在使用默认文件配置器时遇到较慢的速度,这可能很有用。使用winrm通信器的file配置器可能会遇到这些类型的困难。
处理重启
配置器有时需要重启,通常是在更新操作系统时。 Packer 的 shell 配置器可以处理重启的情况。
Packer 通过重试执行脚本一段时间直到超时失败来处理此问题。这将允许机器有时间启动并准备好运行脚本。 配置器等待的时间是使用 start_retry_timeout 配置的,默认为几分钟。
有时,当执行诸如 reboot 之类的命令时,shell 脚本将返回并且 Packer 将在 SSH 实际退出并重新启动机器之前开始执行下一个命令。为此,使用 pause_before 让 Packer 在执行下一个脚本之前等待:
HCL2:
provisioner "shell" {
script = "script.sh"
pause_before = "10s"
timeout = "10s"
}
Json:
{
"type": "shell",
"script": "script.sh",
"pause_before": "10s",
"timeout": "10s"
}
某些操作系统配置无法在重启时正确终止所有网络连接,导致配置器在主机重启后挂起。在这种情况下,请确保在重启时或在 shell 脚本中关闭网络接口。例如,在 Gentoo 上:
/etc/init.d/net.eth0 stop
SSH Agent Forwarding
一些配置器需要从 Packer 实例中连接到远程 SSH 服务器。下面的示例是使用客户端上的 openssh 从私有 git 存储库中提取代码。确保您正在运行 ssh-agent 并使用 ssh-add /path/to/key 将您的 git repo SSH 密钥添加到其中。当 Packer 实例需要访问 SSH 密钥时,代理会将请求转发回您的 ssh-agent。
注意:当通过 git 进行配置时,您应该将 git 服务器密钥添加到 ~/.ssh/known_hosts 文件中,否则 git 命令可能会挂起等待输入。这可以通过文件配置器复制文件(更安全)或使用 ssh-keyscan 填充文件(不太安全)来完成。后者访问 github 的一个例子是:
HCL2:
provisioner "shell" {
inline = [
"sudo apt-get install -y git",
"ssh-keyscan github.com >> ~/.ssh/known_hosts",
"git clone git@github.com:exampleorg/myprivaterepo.git"
]
}
Json:
{
"type": "shell",
"inline": [
"sudo apt-get install -y git",
"ssh-keyscan github.com >> ~/.ssh/known_hosts",
"git clone git@github.com:exampleorg/myprivaterepo.git"
]
}
故障排查
我的 shell 脚本在 Ubuntu 上不能正常工作!
- 在 Ubuntu 上,
/bin/sh shell是 dash。如果您的脚本中包含特定于 bash) 的命令,请将#!/bin/bash -e放在脚本的顶部。可以在 Ubuntu wiki 的 DashAsBinSh 页面上找到 dash 和 bash 之间的区别。
当我登录时,我的 shell 可以工作,但 shell 配置器失败了
- 请参阅上面的提示。更有可能的是,您的登录 shell 使用
/bin/bash,而配置程序使用/bin/sh。
使用 apt-get 或 yum 时安装程序挂起
- 确保在命令中添加
-y以防止它要求用户输入才能继续执行。
我如何知道我的 shell 脚本在做什么?
- 将
-x标志添加到脚本顶部的 shebang (#!/bin/sh -x) 将在脚本语句执行时回显脚本语句。
我的构建并不总是一样
- 一些发行版在其他核心服务之前启动 SSH 守护进程,这会产生竞争条件。您的第一个配置器可以阻塞配置直至 SSH 守护进程完全启动后再运行。
HCL2:
provisioner "shell" {
inline = ["sleep 10"]
}
Json:
{
"type": "shell",
"inline": ["sleep 10"]
}
环境变量中使用双引号
Packer 会为您管理双引号,因此您不必担心。以下是 Packer 模板输入的示例以及您应该期望得到的内容:
HCL2:
provisioner "shell" {
environment_vars = [
"FOO=foo",
"BAR=bar's",
"BAZ=baz=baz",
"QUX==qux",
"FOOBAR=foo bar",
"FOOBARBAZ='foo bar baz'",
"QUX2=\"qux\""
]
inline = [
"echo \"FOO is $FOO\"",
"echo \"BAR is $BAR\"",
"echo \"BAZ is $BAZ\"",
"echo \"QUX is $QUX\"",
"echo \"FOOBAR is $FOOBAR\"",
"echo \"FOOBARBAZ is $FOOBARBAZ\"",
"echo \"QUX2 is $QUX2\""
]
}
Json:
"provisioners": [
{
"type": "shell",
"environment_vars": ["FOO=foo",
"BAR=bar's",
"BAZ=baz=baz",
"QUX==qux",
"FOOBAR=foo bar",
"FOOBARBAZ='foo bar baz'",
"QUX2=\"qux\""],
"inline": ["echo \"FOO is $FOO\"",
"echo \"BAR is $BAR\"",
"echo \"BAZ is $BAZ\"",
"echo \"QUX is $QUX\"",
"echo \"FOOBAR is $FOOBAR\"",
"echo \"FOOBARBAZ is $FOOBARBAZ\"",
"echo \"QUX2 is $QUX2\""]
}
]
输出:
docker: FOO is foo
docker: BAR is bar's
docker: BAZ is baz=baz
docker: QUX is =qux
docker: FOOBAR is foo bar
docker: FOOBARBAZ is 'foo bar baz'
docker: QUX2 is "qux"