Shell

原文


类型:shell

Packer 的 shell 配置器使得 Packer 可以使用 shell 脚本构建的机器。 Shell 配置器是在计算机上安装和配置软件的最简单方法。

基础用法

HCL2:

provisioner "shell" {
    inline = ["echo foo"]
}

Json:

{
  "type": "shell",
  "inline": ["echo foo"]
}

配置参数

下面列出了可用配置选项的参考。唯一必须的元素是 inlinescript。每个其他选项都是可选的。

以下参数必须选一配置:

  • 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_commandchmod +x { {.Path} }; . { {.EnvVarFile} } && { {.Path} }。 该值是一个模板引擎。因此,您可能在此字段中使用用户变量和模板函数。此外,还有三个可用的额外变量:

    • Path 是要运行的脚本的路径
    • Varsenvironment_vars 的列表(如果已配置)。
    • EnvVarFile 是包含环境变量的文件的路径,如果 use_env_var_filetrue
  • 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_folderremote_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)- 如果配置器完成时间超过配置值(例如 1h10m1s10m),则配置器将超时并失败。

执行命令的例子

对于许多新用户来说,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 服务器的构建器(例如 hypervparallelsqemuvirtualboxvmware),这将被设置为 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 shelldash。如果您的脚本中包含特定于 bash) 的命令,请将 #!/bin/bash -e 放在脚本的顶部。可以在 Ubuntu wiki 的 DashAsBinSh 页面上找到 dash 和 bash 之间的区别。

当我登录时,我的 shell 可以工作,但 shell 配置器失败了

  • 请参阅上面的提示。更有可能的是,您的登录 shell 使用 /bin/bash,而配置程序使用 /bin/sh

使用 apt-getyum 时安装程序挂起

  • 确保在命令中添加 -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"

results matching ""

    No results matching ""