从安装了cloud-init 的AMI 开始时(这在许多官方Linux 发行版中很常见),我们可以使用cloud-init 的write_files module 将任意文件放入文件系统,只要它们足够小适合 user_data 参数以及所有其他 cloud-init 数据的约束。
与所有 cloud-init 模块一样,我们使用 cloud-init's YAML-based configuration format 配置 write_files,它以单独的一行特殊标记字符串 #cloud-config 开头,后跟 YAML 数据结构。因为 JSON 是 YAML 的子集,我们可以使用 Terraform 的 jsonencode 来生成有效值[1]。
locals {
cloud_config_config = <<-END
#cloud-config
${jsonencode({
write_files = [
{
path = "/etc/example.txt"
permissions = "0644"
owner = "root:root"
encoding = "b64"
content = filebase64("${path.module}/example.txt")
},
]
})}
END
}
当我们设置encoding = "b64" 时,write_files 模块可以接受 base64 格式的数据,因此我们将它与 Terraform 的 filebase64 函数结合使用以包含外部文件的内容。这里也可以使用其他方法,例如使用 Terraform 模板动态生成字符串并使用base64encode 将其编码为文件内容。
如果您可以像上面一样在单个配置文件中表达您希望 cloud-init 执行的所有操作,那么您可以将 local.cloud_config_config 直接分配为您的实例 user_data,cloud-config 应该会在系统上识别并处理它开机:
user_data = local.cloud_config_config
如果您需要将创建文件与其他一些操作(例如运行 shell 脚本)结合起来,您可以使用 cloud-init 的 multipart archive 格式对多个“文件”进行编码以供 cloud-init 处理。 Terraform 有一个 cloudinit 提供程序,其中包含一个数据源,可轻松为 cloud-init 构建多部分存档:
data "cloudinit_config" "example" {
gzip = false
base64_encode = false
part {
content_type = "text/cloud-config"
filename = "cloud-config.yaml"
content = local.cloud_config_config
}
part {
content_type = "text/x-shellscript"
filename = "example.sh"
content = <<-EOF
#!/bin/bash
echo "Hello World"
EOT
}
}
此数据源将在 cloudinit_config.example.rendered 处生成单个字符串,这是一个适合用作 cloud-init 的 user_data 的多部分存档:
user_data = cloudinit_config.example.rendered
EC2 规定 user-data 的最大大小为 64 KB,因此所有编码数据一起必须符合该限制。如果您需要放置接近或超过该限制的大文件,最好使用中间其他系统来传输该文件,例如让 Terraform 将文件写入 Amazon S3 存储桶并将软件放入您的实例使用instance profile 凭据检索该数据。不过,对于用于系统配置的小数据文件来说,这应该不是必需的。
需要注意的是,从 Terraform 和 EC2 的角度来看,user_data 的内容只是一个任意字符串。处理字符串中的任何问题都必须在目标操作系统本身内进行调试,方法是读取 cloud-init 日志以查看它如何解释配置以及尝试执行这些操作时发生了什么。
[1]:我们也可以使用 yamlencode,但在我写这篇文章的时候,这个函数有一个警告,它的确切格式可能会在未来的 Terraform 版本中发生变化,这对于user_data 因为它会导致实例被替换。如果您将来阅读此内容,并且该警告不再出现在 yamldecode 文档中,请考虑改用 yamlencode。