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 也会默认将一些环境变量注入到环境中,这将在下面的部分中介绍。envenvironment_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_onshell-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)- 如果配置器完成时间超过配置值(例如 1h10m1s10m),则配置器将超时并失败。

execute_command 详解

对于许多新用户来说,execute_command 令人费解。但是,它提供了一个重要的功能:自定义命令的执行方式。最常见的用例是处理 sudo 密码提示。如果您使用非 POSIX shell,例如 FreeBSD 上的 tcsh,您可能还需要对该参数进行自定义。

Windows Subsystem For Linux

shell-local 配置器的设计理念是允许您在本地操作系统的本机 shell 中运行命令。对于 Windows,我们在默认情况下假定这是 Cmd。然而通过修改配置程序配置中的 execute_commanduse_linux_pathing 选项,可以让 shell local 配置程序使用 Windows Linux 子系统来运行 bash 脚本。

此功能的一个限制是无法使用 inlinecommand 参数;请使用 scriptscripts 参数。

请注意,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 服务器的构建器(例如 hypervparallelsqemuvirtualboxvmware),这将被设置为 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_pathingexecute_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_formatexecute_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_formattempfile_extensionexecute_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"))

results matching ""

    No results matching ""