Local Shell 配置器
类型:shell-local
shell-local
将在运行 Packer 的机器上运行您选择的 shell 脚本 - 换句话说,shell-local
将在您的构建服务器或桌面等上运行 shell 脚本,而不是由 Packer 创建的远程/来宾机器。
远程 shell 配置器可以在远程机器上执行 shell 脚本。
基础用法
HCL2:
source "file" "example" {
content = "example content"
}
build {
source "source.file.example" {
target = "./test_artifact.txt"
}
provisioner "shell-local" {
inline = ["echo foo"]
}
}
Json:
{
"builders": [
{
"type": "file",
"name": "example",
"target": "./test_artifact.txt",
"content": "example content"
}
],
"provisioners": [
{
"type": "shell-local",
"inline": ["echo foo"]
}
]
}
配置参数
下面列出了可用配置选项的参考。唯一必须的元素是 command
。
以下参数必须选一配置:
command
(string)- 要执行的单个命令。它将被写入临时文件并使用下面的execute_command
调用运行。如果您正在 AWS、Azure、Google Compute 或 OpenStack 上构建 Windows VM,并且想要访问 Packer 用于通过 WinRM 连接到实例的生成密码,您可以使用模板变量{ {.WinRMPassword} }
来将之设置成一个环境变量。inline
([]string)- 一组要执行的命令。这些命令由换行符连接起来并变成一个文件,因此它们都在相同的上下文中执行。这允许您在一个命令中更改目录,并在下一个命令中使用目录中的内容,依此类推。inline
脚本是在运行 Packer 的机器中完成简单任务的最简单方法。script
(string) - 要执行的脚本的路径。此路径可以是绝对路径或相对路径。如果是相对路径,就是相对于Packer执行时的工作目录。scripts
([]string)- 要执行的脚本数组。脚本将按照指定的顺序执行。每个脚本都是独立执行的,因此一个脚本中的变量等状态不会传递到下一个脚本。
选填参数:
env
(map(string)) - 在execute_command
之前注入的键/值对映射。 Packer 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。env
与environment_vars
冲突的部分会覆盖后者的设置。environment_vars
([]string) - 在execute_command
之前注入的键/值对数组。格式应为key=value
。 Packer 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。如果您正在 AWS、Azure、Google Compute 或 OpenStack 上构建 Windows VM,并且想要访问 Packer 用于通过 WinRM 连接到实例的生成密码,您可以使用模板变量{ {.WinRMPassword} }
来将之设置为一个环境变量。例如:"environment_vars":"WINRMPASS={ {.WinRMPassword} }"
env_var_format
(string) - 当我们解析您提供的environment_vars
时,该参数会为我们提供一个字符串模板以确保我们正确设置环境变量。默认情况下,在 Windows 主机上,此格式为set %s=%s &&
,在 Unix 上,它是%s='%s'
。您可能不需要更改此格式,但您可以在下面查看一些需要修改该参数的用法示例。execute_command
([]string) - 用于执行脚本的命令。默认在 Unix 上是["/bin/sh", "-c", "{ {.Vars} }", "{ {.Script} }"]
,在 Windows 上是["cmd", "/c", "{ {.Vars} }", "{ {.Script} }"]
。该参数的值是一个模板引擎。有两个可用变量:Scripts
,代表要运行的脚本的路径,以及Vars
,代表environment_vars
的列表(如果已配置)。如果您选择设置此选项,请确保数组中的第一个元素是您要使用的 shell 程序(例如,"sh"),数组中后面的元素必须是
{ {.Script} }
。该参数为您提供了很大的灵活性。您可以选择提供自己的 shell 程序,例如
"/usr/local/bin/zsh"
甚至"powershell.exe"
。然而,能力越大,责任越大 - 这些命令不受官方支持,如果您使用不同于默认的 shell,环境变量之类的功能可能无法工作。为了向后兼容,您也可以使用
{ {.Command} }
,但它的解码方式与{ {.Script} }
相同。为了清楚起见,我们建议使用{ {.Script} }
,因为即使您只设置了一个要运行的命令,Packer 也会将其写入一个临时文件,然后将其作为脚本运行。如果您正在 AWS、Azure、Google Compute 或 OpenStack 上构建 Windows VM,并且想要访问 Packer 用于通过 WinRM 连接到实例的生成密码,您可以使用模板变量
{ {.WinRMPassword} }
来将之设置为一个环境变量。inline_shebang
(string) - 运行inline
指定的命令时使用的 shebang 值。默认为/bin/sh -e
。如果您不使用inline
,则此配置无效。重要提示:如果您对此进行自定义,请务必包含类似-e
标志的内容,否则个别步骤失败不会使配置器失败。only_on
([]string) - 这是一个操作系统运行时数组,shell-local 将在其中执行。这允许您仅在特定操作系统上执行shell-local
。默认情况下,如果未设置only_on
,shell-local
将始终运行。use_linux_pathing
(bool) - 这仅与 Windows 主机相关。如果您在启用了 Windows Subsystem for Linux 功能的 Windows 环境中运行 Packer,并且想要调用 bash 脚本而不是调用 Cmd 脚本,则需要将此标志设置为true
;它告诉 Packer 为您的脚本使用 Linux 子系统路径,而不是 Windows 路径。 (例如/mnt/c/path/to/your/file
而不是C:/path/to/your/file
)。有关如何使用此功能的更多指导,请参阅下面的示例。如果您不在 Windows 主机上,或者您不打算使用 shell-local 配置器来运行 bash 脚本,请忽略此选项。valid_exit_codes
([]int) - 脚本的有效退出代码。默认为 0。
所有配置器共有的参数:
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
详解
对于许多新用户来说,execute_command
令人费解。但是,它提供了一个重要的功能:自定义命令的执行方式。最常见的用例是处理 sudo
密码提示。如果您使用非 POSIX shell,例如 FreeBSD 上的 tcsh
,您可能还需要对该参数进行自定义。
Windows Subsystem For Linux
shell-local 配置器的设计理念是允许您在本地操作系统的本机 shell 中运行命令。对于 Windows,我们在默认情况下假定这是 Cmd。然而通过修改配置程序配置中的 execute_command
和 use_linux_pathing
选项,可以让 shell local 配置程序使用 Windows Linux 子系统来运行 bash 脚本。
此功能的一个限制是无法使用 inline
和 command
参数;请使用 script
或 scripts
参数。
请注意,WSL 是测试版功能,不保证此工具能按您预期的方式工作。
HCL2:
source "null" "example" {
communicator = "none"
}
build {
sources = [
"source.null.example"
]
provisioner "shell-local"{
environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
scripts = ["C:/Users/me/scripts/example_bash.sh"]
}
provisioner "shell-local"{
environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
script = "C:/Users/me/scripts/example_bash.sh"
}
}
Json:
{
"builders": [
{
"type": "null",
"communicator": "none"
}
],
"provisioners": [
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
"execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
"use_linux_pathing": true,
"scripts": ["C:/Users/me/scripts/example_bash.sh"]
},
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
"execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
"use_linux_pathing": true,
"script": "C:/Users/me/scripts/example_bash.sh"
}
]
}
默认的环境变量
除了能够使用 environment_vars
配置自定义环境变量外,配置器还会自动定义某些常用的环境变量:
PACKER_BUILD_NAME
值为 Packer 正在运行的构建的名称。这在 Packer 进行多个构建并且您希望将它们彼此能有所区分时最有用。PACKER_BUILDER_TYPE
是用于创建运行脚本的机器的构建器类型。如果您只想在使用特定构建器构建的系统上运行脚本的特定部分,这将很有用。PACKER_HTTP_ADDR
如果使用为文件传输提供 HTTP 服务器的构建器(例如hyperv
、parallels
、qemu
、virtualbox
和vmware
),这将被设置为 HTTP 服务器地址。您可以在配置器中使用此地址通过 HTTP 下载大文件。如果您在使用默认文件配置器时遇到较慢的速度,这可能很有用。使用winrm
通信器的file
配置器可能会遇到这些类型的困难。
编写安全的脚本
无论您是使用 inline
参数,还是将其传递给 script
或是 scripts
,重要的是要了解有关 shell-local 配置器如何运行的原理才能安全又轻松地运行该配置器。理解本配置器的运行原理会在这个过程中为你节省很多时间。
每次构建都会运行一次
您传递给 shell local
的脚本在每个构建器上都会运行一次。这意味着如果您有一个 amazon-ebs
构建器和一个 docker
构建器,您的脚本将运行两次。如果您有 3 个构建器,它将运行 3 次,每个构建器一次。
必须设置退出代码
如果任何配置器失败,Packer 程序的构建将会停止并清除所有临时制品。
对于 shell 脚本,这意味着脚本必须以代码 0
退出。必要时必须小心的调用 exit 0
。
使用样例
Windows 主机
在 Windows 主机上运行一个 .cmd
文件的例子:
HCL2:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest1"]
scripts = ["./scripts/test_cmd.cmd"]
}
Json:
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest1"],
"scripts": ["./scripts/test_cmd.cmd"]
}
假设 test_cmd.cmd
的内容是:
echo %SHELLLOCALTEST%
在 Windows 上使用 inline
命令运行该脚本,需要定制化 tempfile_extension
:
HCL2:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest2"],
tempfile_extension = ".cmd",
inline = [echo "%SHELLLOCALTEST%"]
}
Json:
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest2"],
"tempfile_extension": ".cmd",
"inline": ["echo %SHELLLOCALTEST%"]
}
在 Windows 上使用 WSL 运行 bash 命令的示例,所需 use_linux_pathing
和 execute_command
进行定制化:
HCL2:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest3"],
execute_command = ["bash", "-c", "{{.Vars}} {{.Script}}"]
use_linux_pathing = true
script = "./scripts/example_bash.sh"
}
Json:
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest3"],
"execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"],
"use_linux_pathing": true,
"script": "./scripts/example_bash.sh"
}
example_bash.sh
的内容:
#!/bin/bash
echo $SHELLLOCALTEST
在 Windows 上运行 PowerShell 脚本的示例,需要对 env_var_format
和 execute_command
进行定制化:
HCL2:
provisioner "shell-local" {
environment_vars = ["SHELLLOCALTEST=ShellTest4"]
execute_command = ["powershell.exe", "{{.Vars}} {{.Script}}"]
env_var_format = "$env:%s=\"%s\"; "
script = "./scripts/example_ps.ps1"
}
Json:
{
"type": "shell-local",
"environment_vars": ["SHELLLOCALTEST=ShellTest4"],
"execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
"env_var_format": "$env:%s=\"%s\"; ",
"script": "./scripts/example_ps.ps1"
}
在 Windows 上以 inline
方式运行 PowerShell 脚本的示例,需要对 env_var_format
、tempfile_extension
和 execute_command
进行定制化:
HCL2:
provisioner "shell-local" {
tempfile_extension = ".ps1"
environment_vars = ["SHELLLOCALTEST=ShellTest5"]
execute_command = ["powershell.exe", "{{.Vars}} {{.Script}}"]
env_var_format = "$env:%s=\"%s\"; "
inline = ["write-output $env:SHELLLOCALTEST"]
}
Json:
{
"type": "shell-local",
"tempfile_extension": ".ps1",
"environment_vars": ["SHELLLOCALTEST=ShellTest5"],
"execute_command": ["powershell.exe", "{{.Vars}} {{.Script}}"],
"env_var_format": "$env:%s=\"%s\"; ",
"inline": ["write-output $env:SHELLLOCALTEST"]
}
Unix 主机
在 Unix 主机上运行脚本的例子:
HCL2:
provisioner "shell-local" {
environment_vars = ["PROVISIONERTEST=ProvisionerTest1"]
scripts = ["./scripts/example_bash.sh"]
}
Json:
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest1"],
"scripts": ["./scripts/example_bash.sh"]
}
在 Unix 主机上用 inline
运行脚本的例子:
HCL2:
provisioner "shell-local" {
environment_vars = ["PROVISIONERTEST=ProvisionerTest2"]
inline = ["echo hello", "echo $PROVISIONERTEST"]
}
Json:
{
"type": "shell-local",
"environment_vars": ["PROVISIONERTEST=ProvisionerTest2"],
"inline": ["echo hello", "echo $PROVISIONERTEST"]
}
在 Unix 上运行 Python 脚本的例子:
HCL2:
provisioner "shell-local" {
script = "hello.py"
environment_vars = ["HELLO_USER=packeruser"]
execute_command = [
"/bin/sh",
"-c",
"{{.Vars}} /usr/local/bin/python {{.Script}}"
]
}
Json:
{
"type": "shell-local",
"script": "hello.py",
"environment_vars": ["HELLO_USER=packeruser"],
"execute_command": [
"/bin/sh",
"-c",
"{{.Vars}} /usr/local/bin/python {{.Script}}"
]
}
hello.py
的内容如下:
import os
print('Hello, %s!' % os.getenv("HELLO_USER"))