如何让 Terraform 等待 cloudinit 完成?

How to make Terraform wait for cloudinit to finish?

在我的 Terraform AWS Docker Swarm module 中,我使用 cloud-init 来初始化 EC2 实例。但是,Terraform 表示资源在 cloud-init 完成之前就已准备就绪。有没有办法让它等待 cloud-init 完成 理想情况下 而无需 SSH 连接或使用 null 资源检查要启动的端口。

你的 managers and workers 都使用 template_cloudinit_config。他们还有 ec2:CreateTags.

您可以使用 trajano/terraform-docker-swarm-aws/cloudinit-complete 等 EC2 资源标签来指示 cloudinit 已完成。

您可以将这最后一部分添加到每个以调用标记脚本:

部分{ 文件名 = "tag_complete.sh" 内容 = local.tag_complete_script content_type = "text/x-shellscript" }

并声明tag_complete_script如下:

locals {
  tag_complete_script = <<-EOF
  #!/bin/bash
  instance_id="${TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \
&& curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/instance-id}"
  aws ec2 create-tags --resources "$instance_id" --tags 'Key=trajano/terraform-docker-swarm-aws/cloudinit-complete,Value=true'
  EOF
}

然后用 null_resource,等待标签出现(在我的 phone 上写了这个未经测试和编辑):

resource "null_resource" "wait_for_cloudinit" {
  provisioner "local-exec" {
    command = <<-EOF
    #!/bin/bash
    poll_tags="aws ec2 describe-tags --filters 'Name=resource-id,Values=${join(",", aws_instance.managers[*].id)}' 'Name=key,Values=trajano/terraform-docker-swarm-aws/cloudinit-complete' --output text --query 'Tags[*].Value'"
    expected='${join(",", formatlist("true", aws_instance.managers[*].id))}'
    $tags="$($poll_tags)"
    while [[ "$tags" != "$expected" ]] ; do
      $tags="$($poll_tags)"
    done
    EOF
  }
}

通过这种方式,在 cloudinit 完成后,您可以对 null_resource.wait_for_cloudinit 需要 运行 的任何资源产生依赖。

另一种可能的方法是使用 AWS Systems Manager Run Command,如果您的 AMI 可用的话。

您使用使用 cloud-init status --wait 命令的 Terraform 创建 SSM 文档,然后从本地供应商触发该命令,并等待它完成。这样你就不用去玩tags了,100%确定cloud-init已经完成了。

这是您可以使用 Terraform 创建的文档示例:

resource "aws_ssm_document" "cloud_init_wait" {
  name = "cloud-init-wait"
  document_type = "Command"
  document_format = "YAML"
  content = <<-DOC
    schemaVersion: '2.2'
    description: Wait for cloud init to finish
    mainSteps:
    - action: aws:runShellScript
      name: StopOnLinux
      precondition:
        StringEquals:
        - platformType
        - Linux
      inputs:
        runCommand:
        - cloud-init status --wait
    DOC
}

然后您可以在 EC2 实例块内 或空资源中使用本地供应商 ,直到您需要用它做什么。

供应商或多或少是这样的:

provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]

    command = <<-EOF
    set -Ee -o pipefail
    export AWS_DEFAULT_REGION=${data.aws_region.current.name}

    command_id=$(aws ssm send-command --document-name ${aws_ssm_document.cloud_init_wait.arn} --instance-ids ${self.id} --output text --query "Command.CommandId")
    if ! aws ssm wait command-executed --command-id $command_id --instance-id ${self.id}; then
      echo "Failed to start services on instance ${self.id}!";
      echo "stdout:";
      aws ssm get-command-invocation --command-id $command_id --instance-id ${self.id} --query StandardOutputContent;
      echo "stderr:";
      aws ssm get-command-invocation --command-id $command_id --instance-id ${self.id} --query StandardErrorContent;
      exit 1;
    fi;
    echo "Services started successfully on the new instance with id ${self.id}!"

    EOF
  }