如何使用Packer创建一个接受用户数据的自定义Linux AMI

时间:2018-07-02 22:50:49

标签: docker amazon-ec2 terraform packer user-data

目标是创建一个最终用户可以通过包括外部数据库或其他特定于实例的配置进行自定义的AMI。

作为Packer构建的一部分,我创建了一个自定义映像,该映像从回购中提取了一些资产,然后创建了在下次启动时运行脚本的服务。当从此AMI启动实例时,我包括一个用户数据脚本,该脚本仅创建一个prop文件,然后将一些值回显到prop文件中。然后,服务脚本根据用户数据(如果存在)或默认值创建一个道具文件,然后使用这些道具启动资产。

但是,启动实例后,该文件不是由用户数据创建的。我发现一些东西说这是每个实例ID运行一次,但这是一个新实例。

我已经尝试将用户数据作为服务脚本wget http://169.254.169.254/latest/user-data的一部分来提取,并将其与用户数据相关联的状态文件作为打包程序构建过程的一部分来移除,以便在AMI运行时再次运行user_data。实例化rm -Rf /var/lib/cloud/*,在用户数据前加上#cloud-boothook,以便它每次都运行,而不仅仅是第一次启动。但是,不存在来自用户数据脚本的tmp文件,并且资产仅以默认值开头。

打包器build.json

...
{
  "type": "file",
  "source": "../scripts/bootstrap.sh",
  "destination": "/tmp/bootstrap.sh"
},
{
  "type": "file",
  "source": "../scripts/myservice.service",
  "destination": "/tmp/myservice.service"
},
{
  "type": "shell",
  "environment_vars": [
    "REPO_USERNAME={{user `repo_user`}}",
    "REPO_PASSWORD={{user `rep_password`}}"
  ],
  "execute_command": "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'",
  "inline": [
    "cd /tmp",
    "chmod +x bootstrap.sh",
    "./bootstrap.sh"
  ]
}
...

bootstrap.sh

<pull assets>
...
mv /tmp/myservice.service /lib/systemd/system/myservice.service
sudo systemctl enable myservice.service
...

myservice.service

if [ -ne ./user_data_retrieved_flag ]
then
   wget http://169.254.169.254/latest/user-data
   touch user_data_retrieved_flag
fi
if [ -e ./user-data ] && [ -ne ./user_data_processed_flag ]
then
  chmod 755 ./user-data
  ./user-data
  touch user_data_processed_flag
fi
if [ -e /tmp/my_props.properties ]
then
    cat /tmp/my_props
    mv /tmp/my_props /home/ubuntu/my_props
fi
<myResources.start>
...

Terraform

...
resource "aws_instance" "my_instance" {
  ami = "${var.my_custom_ami}"
  ...
  user_data = <<EOF
    #cloud-boothook
    #!/bin/bash
    touch /tmp/my_props
    echo PROP1=${var.prop1} >> /tmp/my_props
    echo PROP2_USER=${var.db_un} >> /tmp/my_props
    chown ubuntu:ubuntu /tmp/my_props
    chmod 777 /tmp/my_props
    EOF
}
...

编辑:为澄清起见,资产确实在运行,因此我知道服务正在成功执行。但是,它们使用的是默认道具,并且configure.sh中提到的标志或应该由用户数据创建的文件都不存在。

1 个答案:

答案 0 :(得分:3)

用户数据脚本通过cloud-init执行,这是一个守护程序,可以在创建/启动时配置实例,而与映像中的内容无关。

这样,如果您想使用用户数据,则可能要确保在映像中安装了cloud-init。这里最简单的选择是从已经安装了cloud-init的现有AMI(例如Amazon Linux,官方Canonical提供的Ubuntu AMI,官方Red Hat提供的Red Hat映像等)简单地创建AMI。或者,您应该能够通过发行版的软件包管理器进行安装。

如果您想以最少的方式在AWS 中没有 cloud-init的情况下执行用户数据脚本(例如,对于无法使用cloud-init的发行版,例如OpenBSD),可以使用类似的方法:

#!/bin/sh

# Ghetto cloud-init to execute EC2 user data scripts

ID="$(curl --silent 169.254.169.254/latest/meta-data/instance-id)"

if [ ! -f /var/lib/cloud/instance/boot-finished ] && [ "$(cat /var/lib/cloud/instance/boot-finished)" != "${ID}" ]; then
  curl --silent 169.254.169.254/latest/user-data -o /tmp/user-data
  chmod +x /tmp/user-data

  /tmp/user-data >> /var/log/cloud-init-output.log 2>&1

  mkdir -p /var/lib/cloud/instance/
  echo "${ID}" > /var/lib/cloud/instance/boot-finished
fi

并将其作为守护程序运行,确保它在引导过程的早期就启动。

这将从EC2元数据端点中抓取用户数据脚本,然后执行它,将输出写入到cloud-init的典型日志位置,然后确保仅在首次启动时使用它检查的信号量文件来执行在运行之前。